s2/0000755000176200001440000000000014540350305010575 5ustar liggesuserss2/NAMESPACE0000644000176200001440000001250414530411473012021 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("[<-",s2_cell) S3method("[<-",s2_geography) S3method("[[<-",s2_cell) S3method("[[<-",s2_geography) S3method(Math,s2_cell) S3method(Ops,s2_cell) S3method(Summary,s2_cell) S3method(as.character,s2_cell) S3method(as.character,s2_cell_union) S3method(as.character,s2_geography) S3method(as.list,s2_cell) S3method(as_s2_cell,character) S3method(as_s2_cell,integer64) S3method(as_s2_cell,s2_cell) S3method(as_s2_cell,s2_geography) S3method(as_s2_cell,wk_xy) S3method(as_s2_cell_union,character) S3method(as_s2_cell_union,s2_cell) S3method(as_s2_cell_union,s2_cell_union) S3method(as_s2_geography,WKB) S3method(as_s2_geography,blob) S3method(as_s2_geography,character) S3method(as_s2_geography,logical) S3method(as_s2_geography,s2_cell_union) S3method(as_s2_geography,s2_geography) S3method(as_s2_geography,wk_wkb) S3method(as_s2_geography,wk_wkt) S3method(as_s2_geography,wk_xy) S3method(as_s2_lnglat,character) S3method(as_s2_lnglat,default) S3method(as_s2_lnglat,wk_xy) S3method(as_s2_lnglat,wk_xyz) S3method(as_s2_point,character) S3method(as_s2_point,default) S3method(as_s2_point,wk_xy) S3method(as_s2_point,wk_xyz) S3method(as_wkb,s2_geography) S3method(as_wkt,s2_geography) S3method(format,s2_cell) S3method(format,s2_cell_union) S3method(format,s2_geography) S3method(format,s2_point_crs) S3method(is.na,s2_cell) S3method(is.na,s2_cell_union) S3method(is.na,s2_geography) S3method(is.numeric,s2_cell) S3method(plot,s2_cell) S3method(plot,s2_cell_union) S3method(plot,s2_geography) S3method(print,s2_cell_union) S3method(sort,s2_cell) S3method(str,s2_cell_union) S3method(unique,s2_cell) S3method(unlist,s2_cell_union) S3method(wk_crs,s2_geography) S3method(wk_handle,s2_geography) S3method(wk_is_geodesic,s2_geography) S3method(wk_set_crs,s2_geography) S3method(wk_set_geodesic,s2_geography) S3method(wk_writer,s2_geography) export(as_s2_cell) export(as_s2_cell_union) export(as_s2_geography) export(as_s2_lnglat) export(as_s2_point) export(new_s2_cell) export(s2_area) export(s2_as_binary) export(s2_as_text) export(s2_boundary) export(s2_bounds_cap) export(s2_bounds_rect) export(s2_buffer_cells) export(s2_cell) export(s2_cell_area) export(s2_cell_area_approx) export(s2_cell_boundary) export(s2_cell_center) export(s2_cell_child) export(s2_cell_common_ancestor_level) export(s2_cell_common_ancestor_level_agg) export(s2_cell_contains) export(s2_cell_debug_string) export(s2_cell_distance) export(s2_cell_edge_neighbour) export(s2_cell_invalid) export(s2_cell_is_face) export(s2_cell_is_leaf) export(s2_cell_is_valid) export(s2_cell_level) export(s2_cell_max_distance) export(s2_cell_may_intersect) export(s2_cell_parent) export(s2_cell_polygon) export(s2_cell_sentinel) export(s2_cell_to_lnglat) export(s2_cell_union) export(s2_cell_union_contains) export(s2_cell_union_difference) export(s2_cell_union_intersection) export(s2_cell_union_intersects) export(s2_cell_union_normalize) export(s2_cell_union_union) export(s2_cell_vertex) export(s2_centroid) export(s2_centroid_agg) export(s2_closest_edges) export(s2_closest_feature) export(s2_closest_point) export(s2_contains) export(s2_contains_matrix) export(s2_convex_hull) export(s2_convex_hull_agg) export(s2_coverage_union_agg) export(s2_covered_by) export(s2_covered_by_matrix) export(s2_covering_cell_ids) export(s2_covering_cell_ids_agg) export(s2_covers) export(s2_covers_matrix) export(s2_data_cities) export(s2_data_countries) export(s2_data_timezones) export(s2_difference) export(s2_dimension) export(s2_disjoint) export(s2_disjoint_matrix) export(s2_distance) export(s2_distance_matrix) export(s2_dwithin) export(s2_dwithin_matrix) export(s2_earth_radius_meters) export(s2_equals) export(s2_equals_matrix) export(s2_farthest_feature) export(s2_geog_from_text) export(s2_geog_from_wkb) export(s2_geog_point) export(s2_geography) export(s2_geography_writer) export(s2_hemisphere) export(s2_interpolate) export(s2_interpolate_normalized) export(s2_intersection) export(s2_intersects) export(s2_intersects_box) export(s2_intersects_matrix) export(s2_is_collection) export(s2_is_empty) export(s2_is_valid) export(s2_is_valid_detail) export(s2_length) export(s2_lnglat) export(s2_make_line) export(s2_make_polygon) export(s2_max_distance) export(s2_max_distance_matrix) export(s2_may_intersect_matrix) export(s2_minimum_clearance_line_between) export(s2_num_points) export(s2_options) export(s2_perimeter) export(s2_plot) export(s2_point) export(s2_point_crs) export(s2_point_on_surface) export(s2_prepared_dwithin) export(s2_project) export(s2_project_normalized) export(s2_projection_mercator) export(s2_projection_orthographic) export(s2_projection_plate_carree) export(s2_rebuild) export(s2_rebuild_agg) export(s2_simplify) export(s2_snap_distance) export(s2_snap_identity) export(s2_snap_level) export(s2_snap_precision) export(s2_snap_to_grid) export(s2_sym_difference) export(s2_tessellate_tol_default) export(s2_touches) export(s2_touches_matrix) export(s2_trans_lnglat) export(s2_trans_point) export(s2_union) export(s2_union_agg) export(s2_within) export(s2_within_matrix) export(s2_world_plate_carree) export(s2_x) export(s2_y) importFrom(Rcpp,sourceCpp) importFrom(utils,str) importFrom(wk,as_wkb) importFrom(wk,as_wkt) importFrom(wk,wk_crs) importFrom(wk,wk_handle) importFrom(wk,wk_is_geodesic) importFrom(wk,wk_set_crs) importFrom(wk,wk_set_geodesic) importFrom(wk,wk_writer) useDynLib(s2, .registration = TRUE) s2/tools/0000755000176200001440000000000014530411473011740 5ustar liggesuserss2/tools/version.c0000644000176200001440000000015514530411473013572 0ustar liggesusers#include #if OPENSSL_VERSION_NUMBER < 0x10000000L #error OpenSSL version too old #endif s2/tools/winlibs.R0000644000176200001440000000067114530411473013536 0ustar liggesusers# Build against mingw-w64 build of openssl VERSION <- commandArgs(TRUE) if(!file.exists(sprintf("../windows/openssl-%s/include/openssl/ssl.h", VERSION))){ if(getRversion() < "3.3.0") setInternet2() download.file(sprintf("https://github.com/rwinlib/openssl/archive/v%s.zip", VERSION), "lib.zip", quiet = TRUE) dir.create("../windows", showWarnings = FALSE) unzip("lib.zip", exdir = "../windows") unlink("lib.zip") } s2/README.md0000644000176200001440000001760614535041760012074 0ustar liggesusers # s2 ![R-CMD-check](https://github.com/r-spatial/s2/workflows/R-CMD-check/badge.svg) [![codecov](https://codecov.io/gh/r-spatial/s2/branch/master/graph/badge.svg)](https://app.codecov.io/gh/r-spatial/s2) [![CRAN](http://www.r-pkg.org/badges/version/s2)](https://cran.r-project.org/package=s2) [![Downloads](http://cranlogs.r-pkg.org/badges/s2?color=brightgreen)](https://www.r-pkg.org/pkg/s2) The s2 R package provides bindings to Google’s [S2Geometry](http://s2geometry.io) library. The package exposes an API similar to Google’s [BigQuery Geography API](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions), whose functions also operate on spherical geometries. Package [sf](https://cran.r-project.org/package=sf) uses this package by default for nearly all its geometrical operations on objects with ellipsoidal (unprojected) coordinates; in cases where it doesn’t, such as `st_relate()`, it emits a warning. This package is a complete rewrite of an earlier CRAN package s2 with versions up to 0.4-2, for which the sources are found [here](https://github.com/spatstat/s2/). ## Installation You can install the released version of s2 from [CRAN](https://CRAN.R-project.org) with: ``` r install.packages("s2") ``` And the development version from [GitHub](https://github.com/) with: ``` r # install.packages("remotes") remotes::install_github("r-spatial/s2") ``` ## Example The s2 package provides geometry transformers and predicates similar to those found in [GEOS](https://trac.osgeo.org/geos/), except instead of assuming a planar geometry, s2’s functions work in latitude and longitude and assume a spherical geometry: ``` r library(s2) s2_contains( # polygon containing much of the northern hemisphere "POLYGON ((-63.5 44.6, -149.75 61.20, 116.4 40.2, 13.5 52.51, -63.5 44.6))", # ...should contain the north pole "POINT (0 90)" ) #> [1] TRUE ``` The [sf package](https://r-spatial.github.io/sf/) uses s2 for geographic coordinates with `sf::sf_use_s2(TRUE)`, and will become the default after sf version 1.0.0. The sf package also supports creating s2 vectors using `as_s2_geography()`: ``` r library(dplyr) library(sf) nc_s2 <- read_sf(system.file("shape/nc.shp", package = "sf")) %>% mutate(geometry = as_s2_geography(geometry)) %>% as_tibble() %>% select(NAME, geometry) nc_s2 #> # A tibble: 100 × 2 #> NAME geometry #> #> 1 Ashe POLYGON ((-81.4528885 36.2395859, -81.4310379 36.2607193, -81.41… #> 2 Alleghany POLYGON ((-81.1766739 36.4154434, -81.1533661 36.4247398, -81.13… #> 3 Surry POLYGON ((-80.4530106 36.2570877, -80.4353104 36.5510445, -80.61… #> 4 Currituck MULTIPOLYGON (((-75.9419327 36.2943382, -75.9575119 36.2594528, … #> 5 Northampton POLYGON ((-77.1419601 36.4170647, -77.1393204 36.4564781, -77.12… #> 6 Hertford POLYGON ((-76.7074966 36.2661324, -76.7413483 36.3151665, -76.92… #> 7 Camden POLYGON ((-76.0173492 36.3377304, -76.0328751 36.3359756, -76.04… #> 8 Gates POLYGON ((-76.46035 36.3738976, -76.5024643 36.4522858, -76.4983… #> 9 Warren POLYGON ((-78.1347198 36.2365837, -78.1096268 36.2135086, -78.05… #> 10 Stokes POLYGON ((-80.0240555 36.5450249, -80.0480957 36.5471344, -80.43… #> # … with 90 more rows ``` Use accessors to extract information about geometries: ``` r nc_s2 %>% mutate( area = s2_area(geometry), perimeter = s2_perimeter(geometry) ) #> # A tibble: 100 × 4 #> NAME geometry area perimeter #> #> 1 Ashe POLYGON ((-81.4528885 36.2395859, -81.4310379 3… 1.14e9 141627. #> 2 Alleghany POLYGON ((-81.1766739 36.4154434, -81.1533661 3… 6.11e8 119876. #> 3 Surry POLYGON ((-80.4530106 36.2570877, -80.4353104 3… 1.42e9 160458. #> 4 Currituck MULTIPOLYGON (((-75.9419327 36.2943382, -75.957… 6.94e8 301644. #> 5 Northampton POLYGON ((-77.1419601 36.4170647, -77.1393204 3… 1.52e9 211794. #> 6 Hertford POLYGON ((-76.7074966 36.2661324, -76.7413483 3… 9.68e8 160780. #> 7 Camden POLYGON ((-76.0173492 36.3377304, -76.0328751 3… 6.16e8 150430. #> 8 Gates POLYGON ((-76.46035 36.3738976, -76.5024643 36.… 9.03e8 123170. #> 9 Warren POLYGON ((-78.1347198 36.2365837, -78.1096268 3… 1.18e9 141073. #> 10 Stokes POLYGON ((-80.0240555 36.5450249, -80.0480957 3… 1.23e9 140583. #> # … with 90 more rows ``` Use predicates to subset vectors: ``` r nc_s2 %>% filter(s2_contains(geometry, "POINT (-80.9313 35.6196)")) #> # A tibble: 1 × 2 #> NAME geometry #> #> 1 Catawba POLYGON ((-80.9312744 35.6195908, -81.0035782 35.6970558, -81.0547791… ``` Use transformers to create new geometries: ``` r nc_s2 %>% mutate(geometry = s2_boundary(geometry)) #> # A tibble: 100 × 2 #> NAME geometry #> #> 1 Ashe LINESTRING (-81.4528885 36.2395859, -81.4310379 36.2607193, -81.… #> 2 Alleghany LINESTRING (-81.1766739 36.4154434, -81.1533661 36.4247398, -81.… #> 3 Surry LINESTRING (-80.4530106 36.2570877, -80.4353104 36.5510445, -80.… #> 4 Currituck MULTILINESTRING ((-75.9419327 36.2943382, -75.9575119 36.2594528… #> 5 Northampton LINESTRING (-77.1419601 36.4170647, -77.1393204 36.4564781, -77.… #> 6 Hertford LINESTRING (-76.7074966 36.2661324, -76.7413483 36.3151665, -76.… #> 7 Camden LINESTRING (-76.0173492 36.3377304, -76.0328751 36.3359756, -76.… #> 8 Gates LINESTRING (-76.46035 36.3738976, -76.5024643 36.4522858, -76.49… #> 9 Warren LINESTRING (-78.1347198 36.2365837, -78.1096268 36.2135086, -78.… #> 10 Stokes LINESTRING (-80.0240555 36.5450249, -80.0480957 36.5471344, -80.… #> # … with 90 more rows ``` Finally, use the WKB or WKT exporters to export to sf or some other package: ``` r nc_s2 %>% mutate(geometry = st_as_sfc(s2_as_binary(geometry))) %>% st_as_sf() #> Simple feature collection with 100 features and 1 field #> Geometry type: GEOMETRY #> Dimension: XY #> Bounding box: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965 #> CRS: NA #> # A tibble: 100 × 2 #> NAME geometry #> #> 1 Ashe POLYGON ((-81.45289 36.23959, -81.43104 36.26072, -81.41233 36.2… #> 2 Alleghany POLYGON ((-81.17667 36.41544, -81.15337 36.42474, -81.1384 36.41… #> 3 Surry POLYGON ((-80.45301 36.25709, -80.43531 36.55104, -80.61105 36.5… #> 4 Currituck MULTIPOLYGON (((-75.94193 36.29434, -75.95751 36.25945, -75.9137… #> 5 Northampton POLYGON ((-77.14196 36.41706, -77.13932 36.45648, -77.12733 36.4… #> 6 Hertford POLYGON ((-76.7075 36.26613, -76.74135 36.31517, -76.92408 36.39… #> 7 Camden POLYGON ((-76.01735 36.33773, -76.03288 36.33598, -76.04395 36.3… #> 8 Gates POLYGON ((-76.46035 36.3739, -76.50246 36.45229, -76.49834 36.50… #> 9 Warren POLYGON ((-78.13472 36.23658, -78.10963 36.21351, -78.05835 36.2… #> 10 Stokes POLYGON ((-80.02406 36.54502, -80.0481 36.54713, -80.43531 36.55… #> # … with 90 more rows ``` ## Acknowledgment This project gratefully acknowledges financial [support](https://www.r-consortium.org/projects) from the s2/data/0000755000176200001440000000000014530411473011511 5ustar liggesuserss2/data/s2_data_tbl_countries.rda0000644000176200001440000040323314530411473016457 0ustar liggesusersBZh91AY&SYV`%@7}֨[L3٭eJ֕Usef7wivtehq^F=J{)ZUihuݴWv'lzwu7\fm׹緋޻ޭף\{:B1VG9{TSrwCnΥ\D]{{ɽrz.׽҅]u)ק';Vrw[w;jvz޳7kIuR'éR@ A=]z=;fWC.uպǻevO'Tz׬z޺fӥ9LW{x;v˅Z CasSRlB刚&1˷T]sݗs8mݹ-57׾{6sۦg/^w;}ek93}g (nm݅vvN͉+KсagR;۝Z^;ۻ)u8єb%lf ht S@#빀)@:ݙwVkl ]SmE4ѩ1ʛ.Cff[[]tڷZlQBEU%lUm,FݶZ)*vDE0K-u-ΧLA\MzπPhR}%-lQMMr٪ujJV:R,`WjʅEiHDIMQ*m]jTZm֢mpҦ-d:TKF]v#lܫ +fXJ4hba@44Mѓ@4&OL2``##144(5D&&4`0#LL&A110&h 4AA ɦ i&# LOMM4ějzd4)2#24FT1I&LhdhL M4'ɩS2#M=F4Iɡ#4OSi=4SzDƐI"&L24i4 #1M4ɀdƓd?@hddѓLCh#dlF0Ѫ Id!F@@DW$ d3330C Wʚ7Z@LK wB.a&{N2d99l̀̀fB&0%;{iP%IT4 31.OB&%O>IrdCE2C*h ⨲C dw9("L}Z!8T+ 5UtP\ Ü̆`ff3d0̙`,fC332Z62Dp̙ ̻e*n&]Vd@)ʔʱEp(8 aU6E12n$I"CAD g-nwrJ7,^MG(T5#))$O‰oPJhKoɑF:2{kZb(M\|#`!IƥEal'cʁI0 1Mspʄ/z3eJ1AV=4J L'MW8I[FL>+*Rf)Rju/! ɆIa0d̙ L L2a!!=2@ϝ8a2aC0&d 003202f2Ha s !0 jp#!1`. &I$ N [P0 O $Np 0QX=-}CX*'|9qScM坋a8]xYSk'yoxQqNc`= 5¹6GȄ2a)C ep?0$vCqk[..0Qio7IpGQ`nX[;Äo +~K-|0p̽`FcG7Q,#j>Gt PaLUbF>IeEl#gHit?iVҝH6e&c^ Psu8w­onKӛ=A( ^8~w̖!HmG.*1+p.g&^oͯ>)̼]0[*C[YCZ6!}”f8n7NV5K 8%3Mx.8,ɂ&cu1 T:]P`f:(N PЌ]Y$$+lN l;39 fa9Ü3Ng3ɆLrs`fag9 !ăf`s93!{܇sC3 330a9g8wlίŘ1hNpth(}mp"hSD4A32˜S10L%!E !Ea$$ W @ Br$R!)WZ Vzk}pzT8Y /zîBÜpp?7KtbųMP\h]d.zs)ϚJEl{E2/D?VymFV<_g'FC31X#vG4L@Pa3ݥU[sG,@TUHh|9ČA9g[, <9m>f9dD,<S8L|Zݲ3@ǶKY o9U)_G:K>Oщ ǝջiWnsѠuؠ;@ J/1t"6,`c:*#FTx R b؏#GHhP4:~DTgd)|FEf:EZ=_t-2<|F b%6,t! acwW uo}'K9"s1'+G'Lb%)ՄƠ$v؟OLb&%mr?Kj l':)/ >B3 Eme`VoBhmγhHa A.Ąjht&7uӸ8,XiLb2*3ɗ,s*tb؎`1 և;QLd%0 s)/}1Kd%=EJ9t\pUGɼ * pBI8L;~Иe1}fY\J̏JSwF+A!$Pد|=d'_p&=EC@y*D??K bN= wJɳ?!gm~Ft{)y81`-. sAA):)>]%@]$~EoCF*SW%k*,b?Bca'#Q׾'=g\H\|Mޒ|h< 6s[f}rnq cs{+*pH ǟs5ae Z)OiM_8#* ^Dr kԉc̏$gbc#ݎ"DH&ܞC1dfB޳"s/Žw"ƙzd&| m \pmNf'"D-9VhvKmgS^T}y^Za5$2Go ))/Z_o["],{'OVÈ #YVSZKr1nUO7Z屆#mILي`luV=Qw8J9mB`F}:LB L_?D! L1L aC_bQ2h b`:m77Fԕa3(²J ަ]Vk"{=d}a4ߟ"GB"tO +>+ *2y=h(o#H+bdO/ؗN0ul,ܜd}9Ē<³,qbޑ}=|{ޥٙ[谱#kIl6L ,Ês' PM : +uH#`OnTYHw)+C]4kz$hɇ*&_ldf%M) 19T4FHNj&\nDpqϐᐚ:5XQ6A d!4$r*J A\Qi =%uAݱ4B,4IJJwF, (C&n _:6Rͽ \1ьlն4! PyĘ3 )SP?=U&C:=P?g4fONyl=ak%mJb1}p5 O}ƷpRk$"$ (_Q/pdyYZ'pv5! mjGz_ۼyEJӞt|{- dgP%@h@a|Ko:c O+A[glEYبr7TԿ^j&piBԔe 嶇 3IoN)i8N 3T](2GG9+O2"ѯBIFXRO`P1YGBp&3`d ; ֘(祏 ,q%)%M`o-Ƅ-ԒYi[BZJ5ƘySFK0rЙQ?)`l*^rd8)iL@כBq_q&k%x)AKOq W0w JUj@Hݐv~lTY &=DK'\aN!ú?ﻔP F2eϒsE},s߷5LDJȁaXw`:߮CֳÆnfkCbzG#Ŧbeۼgͧsަg"b $b.TLc3j^{Dv{?7]>^˞ FqFFI9PWxw1\ɄжVon;r䒉 T8 D/e=D>c X%V)%:,I)h4}HMsa26T[FQ>WUyZ3+qRsH5c^sTG{8}ѱ1Ǜ̼fHt׫}辸oovcbXtR.s3ƐJPF,S{IESd8򧠈lL F'˯F,ZA;Ǚט#!}vmH+U]gv60D/Et3j 67k4Z'l)B2Iax1.W>(C{fC…`W)мƼ;<zr:׳[kS ]u?|!? M ӁG 5ʒp|bpꪟSiT~>pݪ̓#)WT`(,@.YّeXZ]8^3ٚVFTr ڝrr zphKRF,D)[N*-a1 4#]R 諭5IV_ g e~z& 7b0ؕ))dY jH66^!аfe&85W=ϜیITyz;$5Iccֵs@x4Qp0BnaC4 O>lL8#bJӾE}hH!$_ˍK`lɬܘ{ e tL|7N(%7IKl<U(e<1q\'{~8BB`{z4X4)3{N!vۍC0㛶c$8)yѧMHD1JU;"YmjbW5x~& Y>,Q듈2, .4U˕j`Pb lY YC'jAZͤ_í 8A]=IEI@i0&D|nd2l;wlse Q|q/l_siRL#[hO~)xpmcF){`jx9@ ▫EDXo+I $ k*fe1U {ARoHv/]ε@IsT#-"νϯOQnkrО{6)$2)8ZE_0v&FT28ykd 7r: 7_PLSY~iCMޤ2|r5K-$d 5\ϗkvY-Cu/|LѬ\#J)ܧyr=ȭAHO-X5ۙ֋| , ]{c%؟wx,=d5+Ջ4+334%>.|.p6 MJqqz;E֔يR"h=un@UoiKwu+qj(ԑQ-\q~GxECW} g7="ɯ,Gh,EJ#F[-wcVE56d#Q?z3Q'$psf̏i: qwx9Àw`K]K$ Â#KOfʖ;/=SrL= Co ddĦf1Oxx9"SC󋴻{e7?'p_!S veʾ57=~7'|b:xbjC:Ng }S i0Ҏ,3^&A486dQ%,S}[ W8_R',(۴i WMhְ_,OBWBL TLb+B|Et?Qp'c$;]4ϡE"s%KGX³t.Dš6J~QmTywé˝+Ra}nȡ)1 g% (vz.]C#N'3fZr- .#@]Hآ9B}I9q2$`xu}63)펶F,Y̏_N lߋi~&?&nSQxd>p~ ʼlw9`ֶ̂EϪ6UTLLyHB$+}xnI$"1Ec&O 5;=<=̩8#t3fI >#ttR=⼋+żz3lI fcLWIozI'cLVnrۉu42-l]җa Y7,eBd&~ ՟Ϸ1z1Ǽ_?O졑TC{h:+Ѩ<&eFmBeZR3ls]usȨttܔ#F)[,ãt+<#Y} ?%#~.P7 ߤQ792:] !HZnӘ}^ ;1wU%EwTn `9&$;(t5&Lʎ[6%lU]ra%0 v.IR(ۚ>^ʌ翴$e dHN.{ܖ*?06{=_.^ SmF :ms0¹_rI'q Q2A6H%:*3;_ I}WǦ638Pߊ\m:A^ky-xz&1e#&"LdZ)_H!eszim0#ذE8\{Q%KA-aX݊{;nGW>*]K )1ww lzT'Pd|aLq)Hz>Kvi?2J47$BXL+CF]_=%X !6qiM{,_x=͏-LД4i14=ٰO9[u0Gxb"nssxi,EFE"nQ>a5)}(>B9!-FF.S W}IFy"jjr eD&|3.]y=M! c3Oر̴~yƥmzKYpCճc{[ pY# Ú^4`We>!&gf2r^Բba3ŝ8 [jЇMi!;9& 0fD%8s?'#Aɢe{+|}ZRqS&uʧsPUtisM6* .ɍ-#k/umoߦc_/?_~FY"H.ZS\Iԋq>[l|n;1Xį.#Hl*Z˙"$C!DlHFSi6f PYp!X%IeqQ}x+ +W3| rٵNӲ'{ӝOGPrtTCyZJQUsKl #ߏೄ>&LƋۥuz' ڷA;oc{?dC"_=F{fK"{(1=F38]ZoSm`$WQW5R^@v'sPU )ďi;G] G%О3.4w /ԎKL>K:vJf;r K]1Lm âf,οUv amEs>=U~~ F^oueRT8cNz//"?Dƕ 80zs~IyЯ):i=п)HP Q6%3w8"zRx: jq$рiL0[VES|,gl1Rz0iyͅvf?ԓ|)ˌM &F@&osndf(6]]’pR3Tg=|t"B!Gl F NVLmR>u~ kϦd+*FWTHWGD!Rv5\>ճ}w E-wɎ[P"E~ȳȼ ,,(ᑷs~jP&&way^C^\spa+Ndm֎Mٙ2ڙ??mU5Wg%{b3#ۙl*V6ެP" LTs]#kQAQ'Qc4f>G;55cɹHchKn*De(%oa*:h,VcZ/vjS_Yl ^ -,F A@ 7)8*R]$ q[yctٴ@ }rxq'#ݖhs<HI pjnLQCH1q+(PJJ)?O Ƨ&Kz(K#*Bv"9,H$G\.7cDVZl@e{s喖<H8#n~xZ/PL3HP4c4UM2a Ufcz?s+gdgQ軈NOPST] ze OW &P{0%1\5L:9 ]دt`3aa GFpSo#& mL\\]ƭzb–BSk~HyMq&0%*z.W҆'n%Ǽ|XX+U2h AxtW(v>~U|^ryBY3f`n/w0lJf p(G_z|*k}g |sw]4yfpvN\_KQe_CL!$"ZTp(z[!J S2|`tyrvhJ04Y<W?2ǓHcIeD!_0'5}/)۱>|/>Ӂ/jDsAKlbFAʎea1{Ş#ڱCI$L!wʹu `|1 m|/%u℁Jji٨RJ[R;)6viZ aTz f0=jkxN[d=-q$GIpA(gsz~xd69j;/DYsWb1vDT>0:0wu'Rqo|YYk$ԱKt/c|.!НWشwp'xwWO`!ĜLò+Nqs^{.3f:MG(j=5kcn( e]{S @e/qdG#F9lLv 㵾?NE90hٖʁZyNr'NEIi*Ubc U3#2@ Urj`]8<mOجb#;}7R,6Fl~MC(|})bT|T\n'BAFzo 6щ"?lZƮ&CB6SMDԫq|H yNknl s~Sb6Xҿҷsz"B8$JK+~%36>Cx &㻉5^ ЧS1L开.Gh<)n:]?DITiU:qD-WҊּ~(x=D%4,Ao9c6zE5eQ ȅ;O8UGfscV / 9Uyy[`c$o?b^BM.H05gibHN:ۖ x/CJzW$|TSؔE1ot*òA͎T@/8CcG4 6ar FK!5lnGT{2%=%~TӪyw)txd?uhU?;CTNX뉕pw[{BoMVВ2WZ ժ;؛t'Ms.FhU0Ca>X]<*a8IS6 n.+&Dq^؞v u-瓷J`wע}LH*&%%ۮ`K =|(LCti˪-O9_G`- (ܫ|TwrGC&l߹6{O:T+6lo~7'j4$0~ek3<9a؞m(2ϹM.L95.)F< sp%XCw:x+].Q(%ѳXyő<+jIƗ!(QEw)~.R%B5gԵ=>For).4*oW$׽ѯ;j4׭M-t!O@z+Wp]T_3rYؼP>,ⷯC66ˁ46H\˝(&92{;>Ѥ(2ZIB]챽ɣFƭ¤ '/aAF qYeߧʡAϚWC%ۧL_!=6FfIɃ ff??KQmݯҎV&%yr iԆ|[)DvD-/2Ӕn§:5hҾC<Vf 4=9flИ<]g ۚ^)ѺK,g G\+sy6N}wI=e;^ZLzD\ѻyp; T"4{"k5P6|΋fn js@1"jjst3Y0Gp #\Οxn3%#Z@Ez7b5Ir[Zc4Bb72C\ 1,]&˨1RK1qSkgED{eƍ} %AG)@߱vX>_޼j3$!Jl};`n~b@S ĴAvto6C/J,>͘%Ke뇵"QX:!^ފR-m5| w~\υdGȪ0xMPY |h$/юbU3F(pT!ZPPcse85KajrGBT IRRK)rRnQnx>w]{2Y"vh{4cU_FG2lۙ%N`c@I5'C::P팽)jHD&"u;5OY(q;6VG6̯y\lםhdݯ$jLǀs ۟@c ^bBJUCB%eCM)EE7p:6K 98iT0< Jz;5pNp<$Ͼg8G eթϕ~*sw1—:u&jSHHRG)L0CL)5쉻!Afפkxu{_뻼%%{TED)nθX)۱xЮeX.Vcb I^GS;68  I"+5L&#Bk}hb32bpLXKy5ѱ]Ÿ1g_ [6?UM%!tI[#'=cL3¿Zs/)7,,STE5L(A<$_(D#Yࢶ2v8ϜSzezM)%uxV,%2&鳇.EwMNK-4+qQX6Xը8_*P yY(_vpBOjy\+4M {ie)e T\foZl 윴x]"|\ypܵ'$-Ya9Ej}3QOd>&CH7py42mܥIY_~>#9 >.,p6K]%tΟlp1Bbn]E:tzܦݯo*|`va]oC*I 2%%4N gP/rm.>EKm  r#YAqDkQ "Y>v*zҼ6ڊC tq.EzK6f+|F᫃h(1M=1_ȟ"-?Wyv´i4B\-m Zw(|c|]AhCogɊu0y?QQKѾ̭ZJ Ho+9?8 9ԤěS Q7̝um HtT|!:5n2M/Df2"ӄPJj3̇첣.MHU?}s)T|>(BV aI^$2oq,aqwBrl 8CI/4d}f* V`ē?g<wSdg~&zw4[B1/{lgx(,TNm4`m̌5O/Ö*lePf>V y ,R PnkW0l\gŧ[C8?QxہHaOsI|w g^T{`5cy>cg>N*+% |]e(;(n2a _9rts^ͧI 8x%JI$t}Lv.wyҐK!-X|DM{1.n5N7f '=i6*?ğy;VGB(c0A=M36/=C4C+k:P}'5?bZe/G _ٿ&Erw|{cX3Q~wsxO0pY8īv9?7b!!O@|u?s_b4#m{<&~KV/&e'@#.,0nylVV<0Zfn/!Nt:<S2%qX"((s*1ξZ% z6D9<VGc cr TG{Ei%03bN 'CvtRa?xyfpq`q1E鱙ۇ9@אUwzs6Ѳ&wq"%2 ioRbbMC gSZrʝ"`kK姾W Ny$foo,ep 1QnB*A&z_ո;dnZ%2+4;3Tp [Ƹ/C5"}%Ĥ\#SgI2r^&,;(tLJb:u_Loal>ԽINDjuLd_ˏ819agFMU]mCrZ=.|Ck:J_=8gB`+6-gYg ޤoT`υ4vHCj=iWLL-{)E[̗GԠMM&C30} 0)7?K0O2xaxߒsD2,N{3gEZfvQOL,Ck[T+7~r{QU Ci*cj5e/,$@?k~0pyH?YPOd+}5nva} Q{7zߝ;[}驗048uxoHݒH "АGQmOÛ{c.H(M4>i]8\?YW/29FeE?[dP{ċJ|apJ=MZ2M(ԐIKsvY\@^OX't4T]6E? @a˸X5 %6}IV]jf\;ȏ)I!/Ïz*D,I;2GP'@3Ko0Ӣ1Q\0q _!Qx'J*KEq M٥hoqXF0\UgNӬ,a Ma1b_ 9z樲.}|XHsZ+둧:}` 3ØD”qor}:WȲ&m}8: "z=h YI)X7 ۵ѭ p .A5Mq9M(d5~(uisHkە@U#7exs`0Θt`62au1`+u AQy0LmPXWF ZYZ hP7; y/F\w/Kq=K_O$>`T۳ ap/5ĮZ8]hXzi= j6;~D/'<J?7yK_CS£~F?Ms~Ɇ\tj:şM?ɦ|t_^ğmG׸pGSfv#5*~xQ_1;;_Mmϸ$T|LG:gl/,j]B,swD-КÐci kxW3]fv}[n$6 bLG=V5BeDLSAlovĢ9 Z|{|]4R&}[;["1}4fRLT3*VB  jl7S]Cy?9T/ !͘q.?eԛr?=#Ѿl$-c=G d\>ѳW!t$tWuH[cXPqPsQr5VU2j:]XDvb;R9WҶm$vIKIJ;4ny3ēNs[(+V2?pkmP}(y:g 9Sii7СS WH#A[IiM0Ě֩wb+G #m+F~Н"(3q*&l&j8U7vfAYjJ9zC[1-(*O}R%TXt]h.z pVBOLܞht7}0[l3ջѭB+zK!4(^DZ!""H2;eSb~7tdmre nGY;êBS*$c'axʓ^:8/Yyt .r* 'V'l#_MsyJ=p= 6YT4Sp1͢&RʙC:\|uЫ"L$q.q+\1x}1j~hC%mH-of$F(Y`FhsUnks;Aah'&|1'EkFޮ{~%^x]y<7ӊ̑^7E'Ur(?y@4>KߋEUzt 5yM%akհM@F ўBN!8 xkF $WEÆq-jkZcAZ>Q4PE{%,>yɫ_ƪjiy iƆCN~6lGz[vv= bpR2r8y-x/z,~ЩT!Pp՗հt4\;7_D8mmΆice0UwX0kcfI1bc(0dU&wY(,y4rڭFDw(ֻvv[ioEUs?eSG:JO$"ԙn0Р=kxu\,Z.}(u+&>,835aaT \V%F'H1nL .a 2x#3%d#;l'8\sR Gt=(8v1o`쓛f-ͭ$3~:  0tO :g-w\q; N+s7z?<1bv7`s7hitꂁ`B(drB b  fn䳳X]7!g"}7Y@n Ȩ6,d˄;bY/_l[|>"L`;v{rqNf _j(wYjW4 J\{q>GTb2TėR,!tLuYq_xwo "{܋ #bRxsjo؆FIcw"TUNa|t/+3dP(&9?}/_ST eXrR<ɳ}cڞ.QY:f RV5;?ɵm8JV&ۍH\q~-zqh2Osy(ԛVzNeO[u^ÈĐ#6epus5E/(1p7H v; VˇhY]i:83x g~yt`"5EKƅ|$EZ^D֟RGaٮ \%NTND-PD*;b/H4[+՟c<1^pibkQK:3b$>Gxd9uc | @b~|FV*3GMvoi^4 NEqdmrSrt9"QZ]\>%ӲAO Eb$+U7WޕY woQG1vH-(~.3%F=otIpQ'0;)Z $C1BSׯ7&rTzQȳ5sA!K]^e7mRNnu'9 RPIL0  w j-&P@%Y|Ry>[{Ǜ*c1#daǀw5]ᓖb#f%êCt}yedTp\2ԙ#ccxGTe|a10 V}ߙ/3G+nqmx \M \ûμد,PlH%ҕM~A՟TL~s;]{z[?@B_p "չNkox\8ؙϦŽ!y=DEZ;oc|EeP;7 %8+R[]j:Hgw9 THY)XKgFwCxS*a0R?ÄbeHg-9  ?Єj:kh4 *|o.TљԓnE=4"^K~b&>3@F_rfZml=C5.58UϬQN52v(&R Sf0zjB^G\DtAV[(Ɣ ϱZψ9}RhT;H%Ъ?;-sr 0r1a CV"r`MV'\r)Bg1f I dǢ c2,:gܚ{S 웜B ]+&!w.l,w]ӛ3 8y3B&)(T2 .%;cNAv:o{r7S.O1(urY\_XYԅ֔^,rȘM4se2D"@7vTBGJ:t[I^oü5}*zs$I,Sr.6LqOH@@sHvL J7[i)ܟ]E>qPU{\Ha8s(`Dz<23G몏PV5?QY)* z eyӮW &5֕wЅIY?oJ~ #:pf ~Pwuh>J'R:IS*4(ރ0W#5n_ғhJ/P>ghN4B}PY5T`x$( YHZ5 |D_e >VFR;85E2)ѮڸEGSUt45Kw{,熌:igO;"]Jx)q)yXFp#rT\V0:hBwruFsL_<3/4p<(HL]ĖPr"8,Z; k|S`DT!Pg}/\1 (4CV)~jaOG|id7xjD%[PE2,Xoҹs]Zq~,hζt˽m[f\L|aM) ɝr\X=0`ֳy.3m!~2¾i3tܡQ4DdbجCBLO«1p'7*͌bS9XPxH4gw=u3quƞ1TN_ :^d,E躗]8 h>fN+k{,|=]J>.ƒX.Ʀ^vQwFڕQ1v'S_0aMˉ8%m[, vȸK| V?LScJIB} r b?%noŋYWȅ>9kAHf227Vdm:cIvC2v2$iI! ͖gslbJk .] eRi?*}p&|¨7J8/&Q5=>p/l-P(u9/!ct(Uj2\n\{RʮC bVe^Ĺ6(kyOpЊIk"m.*Rv7Z|㤹Ɖh2dŹd2P9:q#\9X$%C<<U;EsS\}(Ŏ-@%ALMq#Gۏ!#ʐtvCC87iQtiR u9~$8L{PUVٖyp8uPCe|zY㚒EGO&H R0&`Uۍ:Y=g$|s =ԽZ7BԆؐ"fҕć0PR =77wmStzI@K6v^X2Ξ'J _<]uFfH. (0gĈ*Ύ@`J[avlg2`4Jd00TdZMPؙHj  ._'%V%r0wAHǷtW KK+vJ!YD,XZf+|=7t>,3(!.A8 G$sb_ߖ6r̖0MOqh X\=hR[ģt +_ ^Ipاj;6`ƔAUJ UJY] M(De-eihw?}, R \gR>(iG[J976Al[;o0]S]˘>yq[VQ4V<@=n*2QE}`zʌpoz|8oFpԺ2mm;Yj`|&d`'f۫7rH҇\/ՈpR "+F Q+ȟ;etgbWK 9T\bLoHgf$t4L\JĸV)-MyfL941[ૢqm&n}n& ^GNw-}M~ik5t]pF`̍|q#O>; />YMzl>VgQ?MP>z|Wԍ}RNV`p_o*j#T^@( }Z>'ʮj\oو&oxfT~@t̐l~\~x!h,=Ukjk=5tM0) I;"(#bR _jaAA |5?1Xj%O)iڇFTzKI#.$  J^ SU8r;9imFbd8ЙTƅUɂSu;#ǂB\y"X9]̄&ڟGU)'${r^42Ώ0dL*a C plDP G Fa^JkB" wE/8 4§Ŵ?$OS'+HKWfH-x** 3abJ 4PtptnȚHeZ, hPVE~L0ێWOt[Q344+9g7lZ̄9ٌqt~~?N)iڔ^K^mM~ Q'56cMF 䍍@e3E"ͳAH῕%Nu崪ūV_*%ix5.ؖ֞⠩'ypaa4$ Ԕٝ~X\<*3V0E>;DnzKbEⲧ,wM!=_0}Gڰ$afMa fg V?xV$cxnis~|>1dԹL9pT}LGb8sL1GOV\x=w 1vcJNXAe{a2.m;-g3p*(GgAGܪ 4Ф`Z21\gJKR%53caZ?)i6leRFW4)j~ًlYWC&I$GjD;,ACHPwK B1/YU&LQ4^EMv9c AfCfGvdmlM&2t\P뻗akMk6:uhݸvMjGcAl 3%2<̋^+^kC4CрjW ]k:BIGe{͈4=!2xGrfS}PqMqsg= 쇓p-z\\D5BtVcti( < }ESmV!46~yxZ4 p9C!̱Iod;ȱch|K42( lo{^5~x{@S]b\V ^Fq%뭧uXϏ厂diE[/h3#%pHX .'.DIq#YsZ,u` NM9O~FD$R˧xr%<'P!jes@6U}ԒX(땚hMYL|γ:")Ԑ1c"d Mq א?jӄ2" 6jnLdzK&f{Y{(s!Ѩؗlo x?ʞGY;={w\Ym]fʖn/fpZ38wjѬ'Brk-Xv5r>vM?uUb~{ܗ# wk[.6'6mU=U"8p9g-ڸ+k=J/BĈc#),YA>> S2f&% e?/1JFwd_9X6x[ԫ8:#v>SKk@x¦P-6KmD"\ ǘzCkFGC"6J$,(.;@x1fjoElv7,S={&VJXKmW+v`j?ammoC#W3eB+C{\7J'@8ڊ$P%vdwL< ok{O Z2q9b^3n[d?f!,.,FЗxeP `_pO lGziFE!])-k)? Z zFu ^E ^X[P2S OK =R.f1$Zv@ٔ1Q5peHMqM*lW|u 03ts3f 96νMѳbՓWno/VO?ޢHuj]ѠA<niTrAǐjxHx@p[%~3Diz-3 MWEQy7eZ P]k2C#P fS`]WZc%~PȘ̐|N*ӽM"`fǒ'xo\k1 QyȆI.ߗ>7 `l>eT3Ҿ9ãˠnhS/A|=!j嘠.GR`6W!M?>ϛ[3X*R^P;Oxv౜w/5jġNq}H`޳4utBƐ#}v[s{,L<{v (Tce'Ol W1!BqTrABnq h =Ko~2 ̧8b5qDrHsFBvHA8mDFLh}4K\ݙSB˞ZaN1QXKӄ\NCK`HW(:$LS;pHetN*W@0U()Ρnv|A}Y~skM` F@-8w+}|/$ X%$" BXF!sU0L7FR`Ę6cMxu&3 l76EgS0!~:?\o/Ÿ 2&F"hYALW>: ?nk>JUB2TPB _MB/c2Aӵ +!o"w+aQHVa"B izUGj8 ": op]~s,?7e}D(MȌf nDIFv5W |\HCNR!]V1D#Fܛ"1NyЕ5+ ٲ3$ j%ӹjPA)rM GùV+de$S6tp"vDBE}o Ӕ`&t! Xtk 4(P",r]yQBhG:|'th3]FIiTiAx3bǿďaPE?>:Nr  ;U x6JљwBCS 4fj0|+D<>ngxund j(:3#(oWй*+H(X hHBNMcwG@i.u}&8ENف&UD0r س٭L}}Cqۘ驏x3aT U5 r*a궃ㄋQF4V%v=:8j78?Ts;QqojzBeD@8bxM@o˟Kt ?v]췕l]#99Ɉ20:"^(8|V< hêLFrb\k|xC_ ?o[p'UDJq֭pn 2+ w/8 وY1"kJSҰ#?yR q c|ϯmkЛV'3nlyXTN.RTR|"4 7[3UE/;5١=[7a,4WH n ?9F_G1hn:[5j7 FsS)uF՚A[7S4-Sߊv+h)9lFچÕщ+&2ujɴʌ%ZqxUgz-2lLտ.O#k:~۹Ph>!lAi b+@ 5x88zHxȽv!&l[fwɷ)DMa)G 1b] !2&]wpNL>ox[|\y- J =NY.~1~bMT|y~A_= @c[xNFLOi&_C!JNlW(N#X@tPAWx`ʼƻqttD1G:Ot4vUN9g3QXSXXZG2Ҡ8 K 0BDOjCd%| ʹe¸"ҚGDp)w]PbbDШ(]Se>iJK-7ty`aQM !

Tz^_\Q;[ccgN:qD}Fr: @ďB$ [iW3^L{5?~\9#y3?PwdidKZ # D"(HWqy@A3 uW@*Oid>gfs~!Op@8wJ/OaTZBqx6fH)WI eDicz8 ,S҃px%CDJ3Ha#G("8M#_(/@5aR.r]}B?Ta0 "8`R /wHVAF+G%!ˣ8GHFij*O $8Đ3$\N.E bG.FxN Ȓ z r Dqŋp̃/agtyN#0mmЀ?)`QBB\­L*FS9%ȝ {ZJE/X8 wz~L!0:F'wRH~-tihcx*M< CA_W(LRӦb ~:>)aca㪻'5&ᶑs`oy"!5-x%ΆΧdJX=u~;#AĐ_7hx&+7/ "@)i\m Bɝl٪o,AӡJdELAޤ&lDI3(G!ڬEAA;I[Bk"_>J5†B$.D;yJAobyc0R h (WǾ?H%bFȻo] *-EwT; B!P; :ba=s[|,EGh:k_vCm1~(@V#!H|!Q.Dx.9B/ U1 7ɽРJ*1q 4 pA$v#VAԺ7Ggwcpi9E$E^aa.?tv_Y3側b=xCwd^']u$U}Ti#'-$@[Eb,A99m(W8Ŋeָ/p6Q>&pOb#< ؅hA* ERux. 4\BrciۂzBg:#rD`dw s6]^A@8G#y-|S} L>-0y+Rot1sd/N&vHkL0Tj}*~8@$}ĽLߊs*!Cs4QثdP9;v+q+3>;CbQBP@ZPxeaBC4f9z`xGIDNIR?NP}1 JU AC%M80I-Bq|=h՘{T;p j|HG2Uz N3<.3Dt>LZ// tQ[ %h7Ȭ&itQ2|J0iP|x⚂̱멯|سZHFqHd?D7&ע| ;/(5r9  ,؍\(Sg r+A5cMOoY֞M % 3${EZ"KSJ6JXJ.Ep0yWZrj{8 zDÂtAtf \&8lOL;']Ai E*Ea%pPq.65!F׷O y(9Exa+{i׈j[=b(4kT -IoB&}b.֠EM$6ec3" D4)'-!"k[gidXDT Ϛ6V=\"#i3k}qtk_6crnHKl1lN|>9X'vXM 0z9=.\OEBs U .zU }<گ"h岏3֋-a0E5e-P< HR.=՚9. hՉ c$7^.cwLΧF)8T}9\bSJ\g S<1CP KpNҡh'Oa!ZS94 "2׽9DpL;fXL o &H } |z(=!u;"H Wp2e+UO3hC21\Jf1[aDDrUG0~.X~MwVD9Fg +UÎTQs"$F-$ FV-mBP89SrGٴAO@H2sRNln 7wO93:%$F@|]QmgyަuFLLurWvۺ.^L7b*\Za>5W P*~E^xUzbru"D<.S *1B)TmW`tM :D]0",&B\ɗO{E;@/\"o39 ;sPǝ㛣:ÀTi3 Z2 Wgp19AJ>naG1RQ"~Y4nF:^n6 T]5LZ5C,?Tw4ˤx~8|U1=ݾR3OP0_ U5JvVw⒢|?sy猋?ECȂmc%,dR T .2PhǥJ]@ &##N R[ Y,͚ CD"80THmqE& AM!O9PpkAw+;70Gزxz rD•ݦ1d&5p'L~ S6I?< %ϥ0'q-(LV@g"PggĘvP7ae~;x5Z,2D?ZbP^9uOmY3d2?K'aQ:؄^"lvo+ ̧AX@|:P4q @`T_A͖=P?XUF7ό:rV"YfyvW]!>uv`+X5Ξ/0-M}rvo Z"A_b@hOT΍aCJmӼ _At2@L8SՅZ´TPA,H@ ;"o{=nT  | > c qZ(sa.!j s؝D `/#Eߖ:ľqRY X)IIn]ua%i /": N6zED^s6'EDx6,jS2Ga{$AKsx<(ބ%V3B cld .^U.DV0AћL Dl#VAb}qa(;FO7ĉ+1a~:h:3 ]P;xv ȓP p#K9#l77w=5KVwؑL`Dgp6ك +&& V-Q/[ĴaHNA93E, UmSUB`)0bL  K t$cq-~eA=B$( F"NuBNҠZ`wo+qbwT2C;9xL*72)MH 72PI1xS7T Õ1̼j0-ob)&q- x5c$ÚA7A9QaH8@8VpN |` M V|$M$HtceSJ,/(¬It>p xcWe"#P]U\ÍzJ)wZ 3tw  -FHӲ[hQ>G^C9DqFX[s8'B[`B!,#Kv#:%U2ϔClbx>Q(PTqygӜ^ZG DWT?hD5lw pާ_vXz ]aաXʒN=@M1JU}A[[Qr n@r܄im_Zpi}360fda.#Nh`ft[3 Ni.0iCA}بWEa, R!"8D8h|D8 G\Lo7<'gu퉘BȔVyЫǿC֭7…8^,.R"$8)޶/@ĐS-^%ǺL@ ^bN[tɘ,vy Ȑ5WM:@ dj`TF`)r8 ߮xTTib#~@!oq"5eDvH0 {?l@s&Pb.x'H)3AӬ\"$|秶-"%]Y/z>H/yܓg:): X)XWMƆCJ;F[-oP^1ДF BdY; Ĉqү}*VT`Psؤ\)"k * ~SpretC¡@f|j]Y\s 0`v0$%Si hW>!WgַfomveT{ NthH,>b" cD"f=bx?FVrv[{Ri0f.PA7#Lotu¬pEZfivpEX¨2p5As[%VH%+M j#^OO A) ,`S!$,Dvl!L4(ڤ/Ml]%Ժ"b#ч0e.ڏ<[QUoh&BhB;rP"3sc.!ѼQ&Ln VQEL q@uq֡Xt`X- 6_/5O]zX"-J_[RLzU8ۼlR#fBPvh˲̆`tWpqf4l9[|Ք%8>L@!meHۛ94a kf 2`I`!0&fD@gb ߻gng i)*9RGTI,ZmKNûWd[[nF:+0{@/v.&6{k%5y #d9z!(mAl6R0W4y-BRTHHhZp)g5ܬ55y%iMA.s\`jńNywocr%$O;C I'وd_DQhvG u1vV7<k0?~qPz3$(22#,w"#?hs"pp l[1ص7amwm[ ݛ_˥숋'KY 9?v;ѷ|}l^=ri;)O-6yx}i\?(ChM"Xô%/bEBnF6Q{(Zo:8+:07 /CrKl!?m-Hע?1-C X¯m*ɺMB mq(0O%=x)2_#IwWt% e]IŊW 9CދN˴4_ RWĐ\g-5Px-kQ)7sV;mKII;&GE1Œ,}{j2o3V;q ֆ n)2=qgyL VI~ 7oP,6W{ 1;ҲeB7I´G9*˟e=u ځ?v?7Wo)4fڰ?t :`CюUhz[&X\EkJmnUh쯋dm_7zXÔVW맺7dTОeF4wv½qeTlKýBWU0Ays,>/Ckz̋Ώ4kR^gl=I30$\,8奚=۩rAÔC!FasOk'K\Hf.SCj/}Rf>K Ӥ&,#.wZK,5A.g1SuTٚtJhsN4{U54A]t0)Li qIl]tY(? mخwG5Wfzb+fU]j}̩l1/cB۷GDqurbbJarZ=- =!MXlaD0gVu&fhxeȂGq^|%>ԕ%Ld5^L*Y65bۛ20FTBOwQאPT:Qx0$kspdoR Y^8ybE49u$ m“]vpyK\ٷ0D3gxyW |gSg &c_q b&%!q[3ƏcgB,wmKLwN=T}kcM1 먇7I+e d4yi"'2G>19sn]99-~p[ m'MQi+=o_tt;/jj7֯7Y׎enn+iJV.DU-^2*we ҪjZO<#. &]-fUmR~mx)O`k}@o~[bBɊte'*mSox|u'iӻ4R%6u]yUOm79V}w6;Pa\AջBa8 lS \s^ONA1ݱ0jC?zfK}1e9 Wx 6v8xX xtMC]g;=C2"Ui ]FFzj^rD5gj ! Da4u0/v9/?a `0(3IyHYz #6r?3pǂȎːm6GjUo2FOv{U棬[$x bT_,)qIod S !LID[St9`RX). 8]ji~! dG<"9W&{ Imm`k4)+'c mcz\욘♰ZE䔙86,mCr,ڬzQ<y] MU>9!@\v֌ڡD}H:cpn]G_ *H:܆d{[yEQbpzhO`_F)!j%Mήw7ept/OMv4A7*agVߍDoz(ՐJ߯csYv2^="`k}IG\,' )ngr FWG@RWC[i4'\_ˀcj8wWCn&VMB\;5)>h7FD `ʻOs0}#Uu^#Sw g(:܁ņ{#WS~:"OoiquhM TEGhܤ<%i'@Brp{hCI!}̊$R'O]EN@ԛI/Z2]FCmunTZ&s^h$$NMnxPV 3y(\O@~D|tNfdɝE W˸b!Y]*^HRDh[0O'hz Dg5Sx'XUPЍRZު5e^T[~7[N3)n-/Of~}?BRzJ";:TA JnR$sxٜ),²ڏ G1yưVn*g7LϞn(m# GiɻtoSc*?ZJYKuŔd"#3mi^GN捏_GnWk_9ua݉}gkeG#{{ OY:X%A&Rw:[Dx,G3r+ڻ㕢o/.0lOjwf, YӢp}mpxIOo9'8sX\(/H !0P疂њJ&y0:Wk|_2~F#1qZ\B2탼fL cgwrXac+ꢯ~]btNN0!x*<0[W%BX5wlHrG(òKJ ֽ+dg,6*&9T~<^O¤w$/Rt ,ǨF&\Oqe 4U>J?7s&*~ڟ3^am+5r~n J_ g5w+2QU$fv-2Rgt#AIELʛy+ %햸s*/I_kYB|{?|v&U ̱-~gȞ-= yҫ6ܷsROŕVo*97\T\ZS/rh[!m󯗽jb'wv(%ĎӪ2wNergӘťZ;Ltŵ"+=8T֛BƍK S׻'P7W֔\@S#6~Kwbh17GZbWb-^[<:d6!W}'̄,A܂?WyS?aI?躑|:5q?R'nRXEʸɿ,KQO]R=Ge;w شAj[E{.b6c"*z>ԹmC"Qn.a ISdaofL,üj< ;EGuY *0S:; ?XX 7ކEO_5D 4R$\N+O/D*e?[zÒfe CįjסC3Rh/yF4ygGi`z >y]۶8Ǎ]^\ .o XgQE6IdErl4~p>󽔏Y*  5NOO v˚[L_W,iPbh (]5%ܬQd4Yuv'gQ#4VIHY5rP6U5'3;(xHn] rIhUwH 5.@kheC̬^5GSžA"indX"Izh gdmqg][nډ O"Nc?//ݬG=i66mYҖP~j#)/r[ī"6;ZȖHR6.|9R^+R>DV[/Kɹ#{0TcRCZm\пME)m3ϸp2U 2LCy2@\L stLdO&#ab&neS1l*%֗!@2+퍩;Gd,ikwSU(iC=#CTM 8f[y1ZSkm̶OF{!\)öL?lOKzLB]"hmy0٥',waųNl.k[IOGyej8-N7]w!_Ras\iޙyX6Wo}]?ol"`Ŭ+Ug7ZGon Fj!HsE NTN!0_Ee k4+ek}xv;Ǒn1iq3Y;r%~jm zͰ7gbB:%ps 1c'LtS3Evc߲n#tZ++jB$cXb 8l >ZxQ`Ad4G4D0FvbvF;EHത q# dZ4P^L>-K2.[tR Uf찤QNu =w>CԻ&zPvVjUr2]4 *X&oFA%H#EȢ!GjСMH$1az+ ߠ5RN:]9wOLguyEr_G b(3&{pG (VFF^ng($وZJ\!~#guRciYu+juwaO;Ϩv{xnY)͐Ybc`FH[bƀD(bQ'{`rICY FmIc(C/DGG1ǣdÔI?r\MM' ܕԬLjtH>OkGLW;4u<*#own\ƙ9wN'xB/"]ѫءU yRۙѐԖLtI }G߼d^8mؼЎ/SH^nc^#X82Y2tA%Wp%$$ tk\Ico iKQ 23,ZNնռxN$+fgq*)W ̉5VF 0[j>P0-~s)}iԞ +<5`S=ZB@{Ohl+^G&X抚Dy.i]%tN%l]BKFNtlM\3[w,'_,1;1U_jªc l8tH7|u) f3}'c cj}W֎oRASv/aj໙:ÝG=|sܴm~C~KybSd?Ѧ*fLoD=\8t؅ӱZݛ]t0"GGᖾ[96IأI7bO*CGmFv_o\! P*g}%U0rx! +S($clrCxWBfg0SXr#(`;{C(`&=gөPl+6Dܹ`ξ+2%=%G{m)[Jl+tX5"D;]c ӄ)ۉ|qWvoY(,+,qUؒ!*N^_K[|F48'mr*]BqmzTk~-2)/ Ai̱tᗣqDcCi8-|1D/A2lΓ<j8XֆZ9N5QvV"l/"%pw[3Bg:No9Jb,LmUInm.JJe~?ḚO~0)_eC K_HhyOl_l1{E"9Ypf+joI-ḵݪoOi3i{g3was(\9$mMQ7Ӥuxؽ& $L`vUw"4j,\}Ճ JG"c7999I=6:<ҐEIpJ>,b~iAL"e[j~_&}(C t(HD Ǒ`n׷g[4*哴fz}(p[jΗ֢;^ZYWWFL<ńkx mbne3=&ʸe OKp2cr2?g<&,eY@`mvD_!y&<dm4dduJYr2FH#._sRi#{nm6Nm4bd`l7#q:ϬuݏoO״u] A<8kIr9RW\S/\?zNfw?R@"O7(<7깺%oE}Wx:Ț܋{{kdb\Iz{WS!A$& hOH}-Z$y*Ge˰tIsU)ƙPi=|3{ =[{!x;/R;*跋[#q8]pG^ kP]T1 qg]{-FY sd%ovU.T՛fj(?Uo$bs [l-2!<>BN?݃UF&}ֱ?~ ;㲺a[<)oͲN|;ӫHv&Ȇl@٬d` /F' 4&Y+JzWPz^ҺR&/:jf;S| CE ƿ']8{['nN;_K+cYЖF沱 #GOnD.zwf|5&3z~m. N7î?DL "@yx1 dm}|60aĬGx^p+X 9ǹfkm'r(.4o_)LNi> ~=r2DLZe܄g(Fϟ ^1BuYuso4į̌7qr.dhS^"ĦYRU7x.~ WG/0^hf.|v g(CW;Y)2RLbWtZOK(XEszK8` p01[@c`NG]EMUڟoI5|{tGGztYh2T8zOux:~VQ8n*R3o`[?Gnf8yrQՖs|0.Rj3DY=8sQLѫ|7hÞFgYސ$G,hCr6.eC:rAP]Pf y}_ED>}t;FK7)<_{y1fOIZ6yZn>Aqϼ?5aJ9f@C^}cm?|XGh[( 1XbihY?.gqFO::Dt6MKb ՛iW $gç {iASǃ10~1/\[K1[ᡖQ1tT{cjwgz\"#.W;A)^.҉>CAI4P~zoCdf )v"@Qk=AB;ZR.v<`z>env6'ޚDt6DqN6tw6GH%OO:s9A^NRB]M ĊȻ2;9{*qgDϢ-^jD/viOʛ؛]&mݲPz Q܏c8ߨ4<5Jw$lZǞ쵾 ʙœ5̀*;bncTk:fIuW?-;`ܠ!VsH$r?%VBDlBJ\ OhD#r_ZWPR"I=WN8ԢQy^:8MAۥ~_\#z \/{>L/iD>>I{ٴAe(Sv_|Ku`H.t2‚TDb*Kb8)ΖD{DYăp9RI(ol٘mWq_М-ʞ׎ WPTͣnkhCpMg?k86wK&]{{]އH/S6Fd|*9Jc[l-dJOǿ EY6u.2$Z)7w.5ɄXK@!Xp qHBFLh؋39}}:|'lsc˕?haS }oܜ,"|$"!LHNfH3Y#NTu=>%˺.DUoU~2/f'2d-_4!c[;Cv asu]in}R&Ǥ0y~;^%S\~L}DK MyŊ5=$^IbqƸ_q\!9p=r9i_P{yl?Jx=~kCۢ ɟF]P0KvS ea//#gvp6.[}ͫQvuVwEBIu9KIX- .nc2ri.>q520dxb;^Z[tS3>k7֌5o«ߤ+ɬ 8u 롨2G-$t' = [q1i˾޸]oqťw[X0Eq)X4%4.CR#'}U€2sxOQaV+9\h*'?VFcZz$G ?IH_8J^-ޏ|L"m YZN|;RhdQE>PҔ|QFYX8T֜;y͓֚}j}5JC/'ĮmҥssW7 R2L[L5XamjbB;qX kv[vZjfvX)-B;DKF,hnāSYojabnWxF9Mꂮ:Ջy-ދ%eM'#E@q7aFF?Pq'IrLt9޳P=fqzQFu쥴ƣ|@:HI<}eKѴфbݲu$g'8k+㦷X':GeQ®)5LyFJMlC+{"N&D|ۧ&7}ʣ*@Rwپ?.旒8By>9} #z_\K种3\xIu(vu} f>'Y¿݃bi5$hI XI cni5}I >0Oh&:#PpX3ȱ:"s=t) PaF۫g>|z~[YCv:LL7lD-~|fUeed ju 32~ Xh#9tuP*Q"d4:roq/(XNb@۩1AٗÆB~IrFlZOgPDI|M 8rdyFnn̽֯MBZu11ep%;5N­dBorihU6nm5T=%u:5s!c(ʭwK zKڿ / K;_l5?^G0Hvߎs~$KHG-'\wJ/&RwpZJuh5W0Wr O}==+'E]n[hi{;3)L(זXV'?EPi{z7 ܫ}Yd:whDšUm`jhgm 94>NJN8y,0g!A}B+W5]>n[5㗝V 7UauT͔sಥ+N[Ś4׏=1-򥧓G=# a6V¾Gx(}3Wh%7v]vkq|Q 9mE & ařWlϵ(Hq3i%J.NfMgs8XZM z]RDZ*cG#Nnm57ouL8 )5n5uapEYx5hA[2̼qKuYQuğz[ޕı,&p#Ʉgq7BRl*Qp;4 E8E""  c7*9Zl;#Z ,].s$'Μ$C}rĊİGnB74&K"ME昪0՗)ʟxs Ƕ'呃oVvtW^8I?_Sg2ju"OL~Y5XzqīyO _xaBe P!p䝆6FoԄAàٸ7wjk;'' qЇ5e(IZ}-m@D0WKtǰBwRq}Mb%ˣZ! XܨaIi"0ۆ+-o/9e% A9ΩiN0%`lX [,3O@NیoPn$ORfLX4 -5ωh{t'$+$}&p罙,}! $Q X{ ~joa}>Y?=FԋcsQ(#m$MM* 83aP't֍#v\ki)W*z0F Z52~N_DC3ac&#v 6G4Av qMGRveY40݆C@DrFm %K/qwO ߮;_+p}($Jf`Q5#9'*F=Cg1Cr~b}Wmtg{.}S޸+`/ئl{*$#jb"/fW O -'PjNA7*Cm(fxkGn9 W:3Uj' ͜r _59r_I#Rd;/Hl:,ƥ"fjjuQC1t2OpX5`q97*nF8#頫-p0!CS4Vtd8}v^%E1CU;3’qYD$tb͖I^1 鷦+hMˉ9M0c:cG֯my2 0ؑl}y`lg,w5"e9bGSSHTES`ɲrsh6!IvǥXĿܶ*RP҃;OSQ| ltʌq+%6ͱƩvJqК"C*eMqec>P]M).rV3uO(=_F\Os0%VWMVuFUYJ@ g6(KX漺9Jlp'oW1`-X?*jEбw}imIsn,(>Ds9q3Ձg} A8*vso۵ SN^QyBDdJ?#1d 08Ht$*{282Y硨X.b\0GE6i C\0 xk9̅A+Ni@d>Gx[ف'9tq1&<3w߸eW7t >wС`:m/s jB}pxǔf G$~ ( jY)F/Glx_n(I86_R9Ia7 pjUmw Xҩ!`m H^#0W8'X1֤)w稱#e=6W\l_Q~7%*Lnn%"r7#g T/|$z*vpٓT.cti,Ų;vSTPGv(xQ9Dlf[B#W} |(Zb.P/O{70wl V.,m.F~0:<E*TXzpk T$̕JY32-[riI9Avo7z-VTr)/]bR|#&.ly>UkR8yWbfM$AgKi7< 7+<=1֟)˵5Vp8FalN0]':;e9h6! ۼ,ŌaBhs# ~=t 8.O}uͦ'%śLWs鵿0qEʍ7i'ÀTROW~W+^F +?ӫ{ )r+gN2?/u=*IEk,C 0))%H"P:A~8^g<]uE Ua Xyjaa^y٫9Ǻ=#uᕢ8K3e!޿0nEt/4-df0^jdpJozǴ4W~Eҗ:4*Bņ02)E݊y0|[mBA،D :JajU2W>yYg;xo#&T Cw_O̵(`FɺL^ni0Ma0M '[[-b߷Q1L Fm7lZ4q_[: oYnOD"됬EdiJOgI2%A݉,?^|wzc7i3y$k*~燈1o8>f/-qi^w7K w)͙,q`Nq8Yutr2t{@O,ރ<&[>XWPd6uc0lɲ|N/g3 zNcqHR&Q3uƼ}!W}*y5U~))QަuS wTR@75qRxd 7C)_.~ .ɩRq™P3qATIتC4 6bgcY2C}QC>xͿ]JoKu EkMUN{^3<ڑMџXͮȕ.1/@nf3|tnj\ ޮJ+JH_k.0!vׅp`UA\cgG>_p ˃&bMȹH(gQi~T׷3t1vT P_rɅ2Gm A( Ԃ@tPMs\cʯ$ȳ>A$h/"| &tC6"5N&Y@o} G2}S ra#^\Ԉ v MR<gEOxt&xK;d ݇vv$cJG=FԹ![MjqWdGO,zjw ( )\߫Lk4zakJy̐f}1 ɭ7h)#e˦UD8 wܰF$+W" >8SHorGؔ7;'tLW\Dy&b V@Ggf3ƪIo3 YwsLxvuu]z_j4s U,Ouwwn_޺wש.Iɕe{21;㏀3nQ׎yd=-"j3J,e.nN-k4ҥKh?\*#:iz"JiWC_%qv^[tUE@rO26p@DR]QiƠ@)ht @KOfК@{Z]0kLD5X~kFӔ7=+^!{-!sCok=.W ~Fw;ᒄ4qI&N%s/r۟Aa{ m ,v$Օ,iBƓY]\F(?${F:'aQix7 B-ŧ6$ǩ+vh[ Y|Y$Sɯ$ff1QgNYYb̩M1-35<ێƚϠyyb6ۛxO0T揭Qʍ=5 _?zbF=sF ͘A?w9A"ϼ9ԧ͟$5фm.P&f 98& ,6ă#wrJX| u'ka[Z)|=# K@~QY$L&z abaGWw_\Ùԃa[5#mEYo; T}2"VD,l *euN JA#B!WIY-$gwGũWȅ'(`qm\v|# ay2Z8uR+?Ch)؇YjFƩcrW<mc..tr#HX[Ό_EWŦKAIEon9|aS%,}JW=rj<ᚓ)S|譍-Ѫq VwkB<'O/tEdwcPC%$;g;YhrngA\5]ºo\#ĿѠW,dsy1i:,^@nb2}MMNك<:' .yZdW%/(MM6s+H, "<4źsngg~dpْdʱ!ax&^`sHQQ}nYD%ik9T0:w&K~犁 REj?:ItwuBri.Io/GΚԭHHb71= sxlf噺J:j2 LG$b\T'S1v<6΋y+vr~p;o>(tLɧ΃UTPܺnS(x]>~|e9*Ԥqe>g#ex]͝'ZJ)P0{^crid2k3,20Gz*t*j>KHxY~lAr4!%N3J?C[)2J&כUjQjݘ-ԘkQկIܧ LN5ѫdNՑ}.|Oʌ9+s(/w_'^TeU-X/Pml0Q0M@#0Yb' Ƭ3/WԂ51˒98x:4`r (Œ:hM"֐2(\-~$PT9v}$Ahy|*%jʼnZ \e.f&<ƤҞ'h^dqԉF`^O+WhfNpy?-y<,pzŋ'%*6(±F577PMK鈭M +7 BqcU+"QMиt,? kI(v`^3l8d,~sY~d;a|vѐ -hq Ӂ,Z z; 'w:?|vNnt~jĜfQ@oM#kϢC[(y<_OdBLK L~%Y/WM*\K"᠌F"!=e3S)i=gГ|Gվm7\cBt CYhcjek/8wiVF~7hØm+U&Ce-1tѴT̿;b-e?3QnbNmSK${K="pۏ.sM;t7ٳvЯa~ןYy6瞝+q:[@êy{_]l]?ɜ$18FhPMQ YUҳ)vm'-_VY7;TX:)6xj"լH3M&.a84hq*PQ40ڭg,[A Q /vϒ6 /VFQ;r tY/< 0mEm &gdpLe2׮7FY~3-β9VW泀JK'O~J׎vhO*O* v{ds_!G S}tfaj]tΥHQ>+֧ă؂74j12PƐqzWf(&|/`"%ez&iHJps jw}1۫;s8&F]^}i'9OKTe3>TC #XWC6Z#bvI ˏ`.YmxOOB)DBB蹜wleUo:6}O ﮮcq|LG'Iy̌x3aṖGrOgC+Q6W|0.<8<|vߘrTY IgV80TcK3ӌW2Ԋ'η^L*cI9^/#i/mVrō̑u?=u+;*!Ԅa]=(Q׶h35<] ,œwOjd^{Fi_C<:)1li9pQ=+|fNpϐj\wJM u ʢSN I%*|şH gpIY4.0J=b]RL_}'\뮪{y[7 =k}/=u&f?jIf,YJS9Z_H]P͔RG.ÉͿ[[e(Kr!nTbH8T@䐗1tU_;brX 9-5ɳ%q.,MDHb yZgvvB{r@㠱t04V nWڧ,/8!!nL/5KԄ#:p`$)Q\Q ̲q? #{ڹH% |yi٤PAR6:9+yĢbF!&#alkĶ%  UA6k^~WϨ<3; M pYNh#E<+ is cUj㈸y,ffwS&LqMQR2C|Б?)C%>\NROHp}kUt4Z>xJ6;Q  8ɐ0̙ 0[ TvCByqnQۢAۦMܠ)Y⎹71Ͻsi8؃Ca_sY_d%,ŵ3 Ln7Ga|ELxhL.'.萲ݰGuYc3r3JKH%m[ewF)vA+6=ƀ\EVjw y6/dk=$dFT_HRC\:1`l_{Ri'|zcotgGA<pb+?Մ]d D} qU" a[Q>9~+*n'٩,Zo4ݎEvB/!a$WT4<ƥ8:m7 cUuS4p٣Ҹ*~ږڑ =,HJ`Mspf>^Y^sz),dwNJ/("M ɞIl>XC礪e9#ݴm\+=Ѷ%邭%1  a7~"kPIL )e+9! Y+%vMȈ|o P"&5u,iq7|c}71 eBXsRq XhZ5rB="Bv1G:)XE7юeyFmq ~7 7F(1+IJ3kiwVy]!89uxZtg=}(i1'Qr.G*'[|!iĐמ?ya}lycLi>4T|Df#OK*(lEӵ$.!&/CqҽPW >"Qĭx44c}Eh2S {:VYf)K?_J *UqT''DCHxoPlsh{xނrj:P @LL[ky"2r,*׸>sYN5[>IIY/ƃ덱{G?T5 QzqHuN+~)Xyqv8:C,apÊ!(h@ ܼs 7?e%\GTx#W)}_2<"`Ìr@~_Ρ\9j`IJSt&HXDFt:fuzIGtI.:#Fyf.IOrZ5}j bձ7.G%ۻLT N͢vTUUpR_yeJy|k%)<9KgnwR)H7dw 7>(%"|o 6E1pp0Phc:h.iIjQZDġghb66UrO(߿&Uv,ܞX͋VyP5~2&l[|OIE0[^2 `Q6P4 t%+#Flx?Hd?׽J颍%#lRߢ ʒx)БOWGW)'P3#2 ?<̍:I" s}u\񿏃5OsFo[[G?|W|3$Dv5c)itPoxd*qK(ˡl[sد)Rthj,WzX4VIȮ ;(T̥59 MrU{!8ܔ #d^f$_8jFZvP'0t6K6"l mWΗ0  FkjcA^m*:?`#Zt2+ncY?u_1GfN _̡˩:W=5Jb x\Mi:[eijcx0K3Jm"22%Oi?ˑAdJz; :~#qp %}eBFj L:yutGZ1i8X - ^YQ BEQ;Z )4"I\a*8<9NXVXֲ[DO\4?+zPҶ(^*U0Fsf{q/R(S~/4~ sJSOtGjTZ8+^hTnF UZW^ƯOE bPX=!14Yo>Ck؊:/Q(pGb)<:ۅ,Es_cՊ5en ņ0(ZpRY{zǓzS{ޞ'ʎVsZdߟʨ§"_Ҁ%BdVM=9^IAi% 3` -+5aF| j$8CAۜ W6 `;Nqe0̹W FmR[ω,IMڳr-&o+2VZ1cM=5ے`#,|!4O`́* DUcj Tu0={2_%:®zlAF bF2t ߉"[=5tBIЦA2hX+Gn\<$h"U׼JV%s\jODZQmfoZ͂I\T2L LA;+ {jqj,G!tbh#u9VeefO[f$G8}xF#8C%Ei"~P# )s@DP09<>a7o'ngU)5D.\y5lџ`@ev3%PD3 UF@tipOaxrU /p(33yOU26eVQŌi./xLk|sZvX +P2x \^fGnpWRIec֪5V(oΪ\$0]|hRQĶxrE_9;¹"Y=4%P{X; G 9$zp$IXKheǼ !rmb5 сPs)$UqR-9A|kJ=T@B_]L<|$*"+}aAsn5%lb( 7 àdZЃ ~"GG.q s&7:>? z<*h5=tp)>ŨU,]6U5)F?Nk} t%{&w'y8t45!4S-.Z z6g4:/=lCl0 !=p.a+ ˑE[G|gng8鼁89%A|9v6'7JzW{9m'\g^*b`vY A?e0cQXЅ%l&C&L8˂)].&xW/Suj),Qؚ<ѿ1d*HgQgT=ZL]z2U&zx7ϙgnG٪~ վXȘMxlZ*WEWߚtvfeY佥Ǫ H(7 =ˈDrtkv83׫99Ng uih2'@uJX1n΍w]dݮvS^[ǽ2eCV$Yfz6s5=>aXM,]&.VjQ"L)[PT}atɣ3}Źp΢n'mz4l>m3x¾4 bXP=SvXL6Ecj?X!R?]վ<\ ;ءȑ2\򴋗{Fx1i ux,e+ jtpYޟ4hsHx{<|gN<,΃>v#eD0pt`J$pR:HEiEhe73[˘A jGho^,SRXdwXԧW ^v(| 5 ;^O1}7t(?p.9Z}[ut| rB =,/R#Pdd0́3033!&fffO5"i H%FN*9ܹkoG~rCvrdmB c1 5gN< kM ͙|R H5}&R)$,*یk+(!/hXSgdcsBMKVX AF}kau_a~;(ئчDe"e㊹ˉ A3*#Jh9 h%ޘ,ۊ16M_#\Laހ{gm;gĻ싕$ ɜybVz*Ǐ{1l2GCn)4#SM1&O9`cD5xhfhIVP=K_1MGB`J;gؔ}٭!41h _^/yE:Fj<|M^ >3"T/aq¦b*q⻒t*91xby _-b%%. 8Or4H:h(vCj!: I>W1aI2x([L;1Yi!p.1· #g' zD&J|}躼wد\! 7 AN3UZX-×@FP=?h.G4TpVΉ9i?ǴUOM `w 8d{(aĞ2x--k׺}Gӄr {bfYW玃Fstpzf[u?6Ίz coDz)kV4Gw<E3~^kj8(!ۂߩ9Аq9,:ҊA k`3Els\s"mNJp)d蕐J_i >_(%#>65 hL7Q  HM18 5lҁ8c;V,(G8| #2(( b{*92T~IPTDfLJثz)EU%PֆeRɛ$QTHy'2m38irVBwg1E z1{yG[1ހ^ΩcYnoNHgD7o*U0qǦiї$c4V|Fn$;+;_MfdQ־:# 7 | _.VUМs , ?SE.GW5;[Lɳ#&%ĖTD:*/YjOA6z=n̞о{Yk 1~?@5dL`d5Pv"ǫ_ 2l S]6 hJȃ{Z[5{̬Jg˂Gs9`D)rF}'=$$5yhGrvRm1l0k#[*=e }?-߀Zܷ=ȎͺRI7gv;AFZK{h1mex\Ox1#ohr׏%?|餓Ok?!Ow{&&?jr>ݡFDPo Crk߈@ot(.*Zĕ' wYKX#Y:. O.ͣcx=x]S$ux)LG>XE=DgWP)Duޥ_|Aɽ!}$`u)- aFesysUi>  m~Q, Cek>Lq- fu"XUbZ}ٮZ4#V#;TI7󎾜H`4E1k46R"h;Up,c e ? Jj;b ˔'Lڗf(1i2*6h-IHdV7=궞^z29m b>mZ(ЌM0džpfrGhO#wkT{5\3e׌ N^f!*ULH*cOɁ8ϚlS*XYt{ 9Dhe픋+_h"3bv!%04rk&fÒ,WTWi/6wsLqsjEulXEI̩ mE+DO%!DN;J֥]7 ϼls}̍\Q)ICK0yTuf㾄2f9?3a+]bNphk[5]:Q(z3X=$0?G 7g'yBaeN6? ly(s^M3AI!uTJTe;B?Ա \<; &x˂^Xa})+'_KvJEKMdm)&aG 6bHbsGN&+B#'1HdA!2'3 E/FYI)IyJ]53*rSkkt[~`E]}rsT1r\'%B~[hU<:|Z bW8y<;և*hM+VI7:EP? XN#9EӪB !r8MlmZZh4Ysđ;+6å{O>|6ч3ee :c^ ӏE݈>HZ. eJ*X9[fnj0V3CvQT~&-].']^}M l_b$hWfvN瞺 t~iN=>IC WN; *BQzeFjN,6'kY ֌M*Sܳ.J|_>ѮjI"?Ke"l`X!G9^%tuw\"0yAdUvLBit|Q8~Y)q<$ء%Kh,QLLRao>`hUQIQ" Vj=bh*O$}d)h+0=eh1;Zͧ܋K1HqO˜_;9z(^B#55i(,_t#aTz,K+M}~?ZRQJ朞2 ɰJ5q35SB27efo\5:SᛔHomߟ$36ARUN)Šth4A3FB'ǬsJSƌTOR&%Ȯ.ߛ\"#6Y8K\wc[+#_HK!d2"c/ =_qY6Ca 7KB̚iȼ2;/W_ap=c n UB,crE. mgB]6=eak7%;HP`RAJqjRK+jwJJkAw68ýKDRGXG.V/d  5 qzP7e("nuҀtdK4xZa>q Sze{d`31ZmF]˿<+kU*5(8mQssXohhր?82=T}0/{+z҉&/1bTSeφT}77X8IXB? $&ߎlLNuTc!GUt!Q:#L<' a/Meb/8I'Ap5#4XբAB5f?`=4. LAx?BAbu/ffѵ%'j{u>oZ!5Gfst^ez>_XF0i썹k 2=-+f9UAs [w/wT@Yb>zCޘo.Ukw"E1(j닌kHC묋* EPgP1JSzkGD*U ċܻC\֒+|Bjf>xM>ô GpoT8HsZ% 0$@߻;)3\Z0UVRYGq]3 ,x+Ow#˃df 0TVU&{GYwqkh -v¾ȉ@VԞͿʿ o De{Q iIӨy7z`zq:& %7mc|d Z*Y}dNպ/磑]׽93aM@SŸt\3Lj=SőFGL0zGՌCfyYM~WdgTu_VuQO˼Q%3כϸ֝Asb*si5mrŦTzsܢx[JI+ʭU2\MtQ/t#x9fY`W j:[ݬf#Ipeؕ?4pg{_QC/ym.Ed1]Q8Y&c>2IФ\Z6\=`i97XBb֤;^,pPw&ʹo01<3ߑSqT ru\JQb2o[qy`,؀  $ dJ.lHA^7j)6j6KJ 3{;y?%J:AȘ:?tr;}M,Lu3$wp9ӚA]"$QN vIbr~z{xNkE>-DcQPg O4΍hcr;w4IN@xm9igl9QX|4⏕xΔ|<-u? Yf9^SqF]3ҫfk{YYR+ml}u'ɺe& :.Ux+N˓)2Iޭ(cHJ{{X:)WbujY>]|u`x6Є8咾: vrQv\`eE}4rJ ^O@*#dLs ){e;BaF/],(=$0WA3mD+,w,VR䯎#xOԻтϛO1&)y|/P1\t$g*Vo& o O`W P'XOZ&nQzP__0~chЏrs)T΂*My2` A0\zZ=YPcʪ(,µ 118DgN"7࢈V!DrY~fUCf|i$}r #Wio&}7K P@:{&R fȈo]EÔ_v2Qg̾mAqs.Bb57lKޖlD̻0;+0L`eJ*x p^eWbM.g J4.Fg2Uo {&tˆ/Z6d.&1A޹A *6\_gDx0S'D:cGzX$:tWfi-{6.W>nY4W~?ZhX;Pn_Ώ4)>Wb \ &W112[@ZJ˓`v</.jA71lI:K06W CU,C|S8UL/oFy8\,>ZWR:a "^iqX4y.p#⁝w R[: j{ae('ᇧ)C}Xc7Nxxj#O{ {[缋N/TbgCivZ^L^%@cBf}jO0UjAaEXdnyS;.oco44*:`|*g wQC3xEvÐ^15~sWNNNF i(#jəo; NF&՞J*6^rL0b}`if}NJ9(._6);ý\%$Mu-_>}}^;$ GSn͛)xwi㉏^XuOo&5'umDxΛEDwHH v`!ҋ._aVgvSS HjM0JLWMX~)2JmQ8@'bsd=AtZ׺<ǎFwvk:8\Eq yzC$kyQ"i ф]\ftOMూJ}h8yn]$n޲PEH04 B$X4Ǎoҍ%;V( cū?a1<=p@N aI}|fK¨ nM-S!g2zTo[,(Y0&RxtSITsU4!`_JzCR+fɁ+Q3x;l常aM.h+OHZRL][keӧc't,&ǖ)-D&ĜU-! Le Եe/>._uMF*4\ERL" ,ɂDsHzJZ ?HםbG~B.CMb䒕3Dlsf)7_XS{x;]VK7W}0;YM߹`:八Y;7_$̓5K]Ï|Iec?ó3΍w$lеUS.oL~IJ#{,TaS_>-QHK4¨Fڌ)-E6pvME &yI4>ed¤ ࡩ5G낞ccBCO,G7Hϙ@ 8&5v a>p8"5c݌_}Lą-?R8+͍Z~cُ25șȿl{ʤy$6$3:]VܕݤS_\\KlƄtX_,48fg"dCl< .. ])D[/wF M]y<V\ 1qL:{6&4qc(>Iϋ1+"t %NNڷJ0e VꥋܣH~XR k P+sޖ! '1YC$|$'WCN_m`E.ΣxҚch&K%HJv猕">pfZ$}1'5BJ~5uKF}Q8WVIDQzUtt$lJ3\.Ij5Bډk23c0rCƂfM$[lg o3'Gr 0A=cțZQ-,%_Q~sEG_3ldY?9;*~x''EWmPQ#.HGV:u0~a g ^UjS;q7` ޒ$&  jUA [d1N%նK#q#PjkYKXM2}Y׌9[F/D\)$"5wnu]Ymo.#*Dti^6<_,9v;JI>ZbX\1p!S` (¤.ȺnU#PoeTٍl)j5o=e &35^\7g]6?b:IDmį"e4'<& w#Ԙ ݃/D U] Xv\3)gݬZ5IEM5>#sZ&jn>f{^5^fi Jn w;92a]Y@h*! ߽LG/{U%RV$Lt*(Hz@Ğf #QȆE f;ͩ$0ϋbX~Sc]Rʯn--X{cXEx 3nC= 1l-[y PfV ֐Fnw~Z=tWa[%oaU9vn¦9\ji,vs}d4M9_4XŒ c\8mzxkتԣm 348Ekzc7Gs] I7)N X4UJlX)䉸F眺 DŽrmۣNa)uy'| 5&aIYlLG(~"jq<ZB7[umPFRʅ7;,0N+,a\_ QR)'z9%%(k:zϼ-Q0mRޕ\mt7rp޼YLqt$!e:tH叀:>!4+>ࡏV/I{FG$1H ?# 1M7*~Oĵ#PaZ"c?lbn^k>0uy;cITfبa8t%jq^-ƒWڢ,TWT6ZaUwNPPWuʟ]Y3~N]ꍙFrIрb \O]sHzYfLI9u bV]ozƳrr̢]X5a xsEttD֝##ť#9k0ApҡB97nŧ_ؤ_賓YxF_jA}pCv=ˢ+Tm:cDTM.7,/\BK iО-3Z6`A42CZ>&<mȳQ,kZU;SBlꋬr `U\SZcn]sC6-r| rvj-q[R0m%8qa殽g>ׇ{ƃб# x&Q50^t!K':zrir%;Gqb!F9/%m2Z&.5aP8=\86=$q2x ɋp!#]rsqxMEa) OUy"Z(y?2O_#` Y튵X&I2TG^6A$M w  ˒`u ݂Y;;+#h<-C5 9MWMx"QxQGéANσTep%oRC9F.t -ؖBfNVs;9J{NmrX1-\VѴݏȌS1=*rYo*dז=- ѭJdyţU`dXVkwn2Noҍ#;4+GI #cir(sMPե=3 |Bm{5(2Àng"8L=(fw#1YXҕzx%uZKJ,nEs&t=ّg'V2&B53ﯛ)O{Ge)ɉEp&նy_vV~'t3gWݷ}zsT9?};GvY]9#m2Nw6**Lz1"CT.eCS:Ma7wGCRнS1p:d=0?[0$hK:bdhS$i#} 8w Fr[ߘ,{;z"CcOU lLRYJ$Kj y:EV89ӌ5̏zfb;yeMGŲV.u*2mS"JHIg ̄9La8N_ާ5$ΟWpȃ :^X&7—(d|L@p֘ŷRQ$dlaIk쒮QfHyDdƊ#O4{!՟%n溘k[#?W43)-%c>X}N$mv@P^'>OZLX tӲJ[2J|5v$ pKޢꡞ9U"XSɔK8sq!\7Sm =än)XGwb{"x/+?Kdskɹcʞ_^QSL[ԞІQ5R]&a ٸPO (sS9nPŋ6Yޙf|TB g3zy6W7ٴuHD\;r(-]#$͟r b1iXb1h &aãdKye6$@wGVf1@]ѓ5!)*TR> S|k`]d&KQ(f'LȢeNل8+.mb[ƞc8f RBbyFۋ1x:](E ޞn=mFw_2SV:܉R!@Wc.ļx+r!FgD)% 9Z}nP!mĚ&B=wSb 2&^V)LVf2z7+>LɼҧJ(!EXc.q773Ƙϒ:~c鎳CI(r<8\wwՙ0^Ò]GM%FmPgbo~yƟX(TceE+GZ*7"JN?NI@OXB;j(w>&z] EǥϪ2a'XWR MDȹ9ؔ]HQ:v$YJV;Z?z5 @V] La2ҊK~"ɣ!͝}+x~ʝA16m,=UCak9odԱ~Cq `0F}#s1iSTLW3T#>X|rOBklmClbfMޯ$;F[dmlヒK#Ɋu?%([XVʨk4ự)]B6Ho  pqNS טC=ݡ9*oN? .׃r:C^dtW{ɿP}Ffa8Nܶ0cmr&䰔rӮ57Y6oJiZ2a}foR+\AQ62Exyao’nF,`xӬV h1)8G~4,28&< ngsW!LUa2X _$uLEhWq=Q3}"$kQkjwJ|dRni0P䙌YTAB!QOcLQb|c`fےC7etNy7n:Zm T&by̌ωD|1#CE$%vH,:V'J;=bO&_GN}Uy/\41]mb9:yQC$jDv}NƮBPzbYz?Yrg{gRQoοXN\HޓFդcU1A΍F̍ٝw]68ȻR9H]áVYkF(܅[aq Y%qh;R peKRȥ SȺE@祔H+SSSB|Q8E'L=4a"F%W'] xyٔG_ P˛Fޡ/&j Wл~2-E|aJnH&ܔī1ݯ&Jcc[&P/R}wb]+RkA+*L}RXTi^._PIx"n?۽g0ⰿfHb߯vt-za InK@QUEx.7XUfHO^yIu{(bCz6}@u w^NhN I `b<HfRu5HGb+rAl 8Y(!߷1% /h᫱>a4#0=ϣG`6;:Qf5A 2.Ft%?(.q#K M4fkL_ + 5*$%0o!n])2C"9," ҕ`H|W 0 =Gʍ D0(RaD&KGRɭERFrX"Ny=yU,p䫏jIg2~Kv;.O@UP)x>#z)|õ_:E/'+ t/$y3 3lH>uoƇCH!UbSaS{Pc;P5=yK;] 2Xc_؝o/<8MrtWYJjV_gkc8gB0xXpÕ H͠e 6T;epjW +~z`Kd 4A}}dbȈu6*&vZA*j>q.diiDپ I/#*ĕ;$‹=ӟDuAbt'>|GJ<"rBͣI)%:=YƦI!O da 1ҵ0, M~dO/;ج h2LzoH8:Ph/7ZvG?d8LlfV5#G72gm YbK[}wp7֯&z8F~ԍ0;D׏a5ԕ%lYvp>6%lfOX*ǙEYK,a|dMW7'KdY8/msTExǦŏ r匄 +`Dlͱ♌{y -sr3IsMb7rZ2(H3D*TIsJ똩11e e/Rrr$~z3TFG_ cՇ%}n- ђ/" bh<;_E ?t厕89bX[ 7nw#HČ1ۄ7`-;üXek ^Fbu-whL3ZbrKsfsj+ lwP:X1V7q*qv |Qs<_L|^C =сhHyl-ȬT2rF#&V;DzŔѕ6I0Bj*Ûf1D&d9iފ;E.廙&rqꐶ(r(1s,] /oFۓ :#P_ l{ ܇I9=k9'W;x azLHQd,_AQe?e u(Ƙʰh&b/u4Q,lg)&8<ĽXw/k1_Y|h%^4Fw9"֧XWqċ}%iΛڳ<(-m(%-6LD~g * ܝa-M -(ѩ\8@|E҅Pܿ`;G[Vhd+Z_`PH.#B)+MeF=0~-࿕CV[`6ooF@qYoXd*PhK)q$3O-Tf|DwI09)~q锁PE|DMq>go!]q7yL.>"4NcMF1'}3P߱I[13GG;NI/{-5X &R;) v&Hs:? 4KFnd:*^[d6F4Xz |dz\WǵSG)lDZ2v剗 (dxȣ0)0}<4No' 0MlӠtWڣSMWVkjpq?R e&YLqrⅥ]E`jc(N2~t7Ue ?3| EZ*{xCJeZtXц!F7;1m ~Fb->>]):bֶ4&䗓'oKшb cV#Ƒw%\qQS2M 3(_4ccv9j}>MOWeSeon`cP0&pk\4ܩ,: K I`b=؛Q;~Hܼc0cԜy M4@=:Yc#eP\I=HB? >p!JR5c?qިK ihV!^,szӿ~? 1 ee]*8v֖pͬe1/"\h|}h澃g)aZ(dZaǴ$`0dDA"$5FXnĒ:&SY ȭFr) y@&^ A7;f#?hv,fq{Ji7"3[hF3QO$h~ږaj*0"Jrg309$[Ѹm!zȾ Ѭ c/)#zED!ˍwΠ@inS9oe,ӫ:0etp, 4K@͍+S#Xc eOn0b 0QT< 28wEU+FI7,>i>g֏˙c'{mߪ;b&wY)Gj&rZCҞƠFdZs#3(${Ji0dV:Ɏ#:I5[2`ǯBX5P#2P&1b po\rbc5gӲoZ^Atym1f+QˍsrcQgd2gMjI+q-9o׊TVWmW[NYA9TQț\9g&XM29StYaPHq,)#0z?`/[(s!j]/ 4&ZP"l4حq8בp\dܴ*T]P["`=$0wW-ǹ_YmWhڋ4E'Ł 2cK[WL*2uV_mX4ƾ_YJBl H2{l>`"~Zq"~GR/F+aDy%rYHri+ÁrG*hHS* }i֛(Lmf {xLm.CQ| Q|ȖИ6QjȬ+Xs ^pL}^!hʮfV!Lb-dgRNa/ՂwLf]E0FdYyޘLMNߤf{gLi,6ܽ%?\ko=JB6g,#^)g% N%;W~?rx n"LjAޱkMJ#Εyu#FM +˞sySmcvDW`v t>$0sHfLr`^nvTQ qF b d<#- HEJ϶zIbdxY DGԜ7;#-(ɱp'D3 B<:rXdrV㜴&c n 8F97x" ٣j2 ØUf9OX51-[}!_^(mfμfs7vQzgGlN} gR$'BU*FzLhm-梅 α>?±d-]C(yb۝6+T6MF\:v,uPdwV6-^b6sSfyrp G2qTgPNW~C^kJ%t5035,x}a;~1u`Z)1[GIQI kkloԌ̡9m@js/=xS큣HE85b[6N#6+e"{/΂ d\CN09$8~eɣ7v^DQ`j-ȻHi`m*Fa+M6e>\qJPpf{##Fv]&\W`嘖k䚴Y&}x||L 1l]GN'g@J܋G}:%Ρ"3k5*r((I-6@uIk/ĊlUrxFAIA+Z$Uo'B#$O /PJsTuBXsDy#R﹂V*Gj 8PwMpv)(>F$7'GZ4 b?If^=9`}buΈ Z> BDR'efdsG,VH69ElHNUF =ˍv,oYa0D>R L42Ɵѻ]j,b C,K 9OG?p'dZF;+hGb!"<ѤZ)~)2{һ:u޹8EUmdX 8gCc:)|I FOb`dg5AQLjSIz'Ʃ.c'2X3BImUǒ ⏲3ĻiBK*Imdh]~NAgq8(on_Q<퍄k^K~AR5H{)|&iS_"M GOT C~odn|g: i2Fa֜^PFL '8 S43(VMEOl1 k HhONk:lRrIiXI[QXd \$Y`t Q2z#cdNw$\'ߕ"Kk ~4F4;~̝a ia4ju$Ay۞ʒwl)vf5!$ro%I"|9%Ir ^#;Ey-3NU3W]\H^؛29TsUdq˰"dzJ[3jY }j8芚`,tqW H?U6D)Ί}ȍ8lc pF'N)gQ_HrKyzQOzF&zI‡C-l_3U=);^ 'וQ&8+S.~JoП&DpW&<7#඙G$!c/hF}R; v(=~ڇ$7iM^`mLD[$LtE;QẠ[ɘ4#Ѵ'6`noRU"xqx=pijJD`KYd2Y@T5vp=6kݎƖR uhJE8Ip ,sL"^\샚 ef!n42k(C]xm pq\,6؊Y(|P>c |׀Ve'5kNm߱tמMاa϶C2*QêhW>hum ˸=s 3ܿl*RΦ48"[,d1~ܐhk=U1Kc(2 "ML@ Cb5K'5}"ϿmyL%RVO> S2ClҘL᭄TsWc纙_abƷ,zAo CCKuO! dN=rc r1lXI'1o{McYWGab4T?El'܍Yrл[OFj"H(}}yhG*yQNJNnܱ G㐡JBP?"f&q&War]C^7 ڪqhD]Hiu}Ms\XzRTyMJ y::,u)J0QyNֆ6h&IӥUo`;n$Anoӆ O?EZ,q86RGn!Ly'=eIm ;`le8“c@pjHRTӀ%y_I)\̘'(CANm5gH{}Ί+E"Qh,xa1@^t:wqqjAɳh: cCI &429^Cش~9hWd#LiCT`dxH\}G*P\#f*DSg62Gn0ޢlH=S^^:.Y;cL5pz6FVJ5==N'#jf0hcuJ"ǵyh“8j\:}BHsSXz^gδ_ѧi2cnbմظ#MjS"#`NNt,쒭Ƒw6Ky/IdOGYI#_Ss(2~Qv2|+Jl:]ux[H,V)X܂Y*tDkdžLUDBXE>[>j+&v n"LF,\&(QzXrDCzCvss쑏mW)O[86In{mѱ6 * =6cٮy똊RB /T)cNwϙB2|_+Ie#Yv q[G^r]ŲCAu% O=; x# km" w }VCBs] )%dޗ&<J-:1Nd+`8~j2Y!z_*?/XcF-&QTtq-誾3`wZ-p}}+ezŵԩ@NʆW^vQ[4V rwdA>z91g#Hti5Q}HfY${2Nys8 bI "foh%zlӊQ4õؠJGts;\d>0gɮᬦĚ\F/7Dd;I}-*u𲚼] L:.K d\?Xpa%dL4U,㻫8\+}%3Rw*)hTbnšiOim2qTQpezHƘnxUy7풎T$,ͽ$[ngӕ5bTʲț} vo>b>"e 6~]ahltRu&5cp|47n( 䐊Ti Rd'תz EK !`m>!PtV@ȑBI9J ,aC>xgK7,|*\=$Qʦ E_DlaG/+3T$p xK_Ez3GRpl5e:ř 9a@sX'S׺щdy'_hի;"9kPHG";%5,wqjTSI~ ~!B\H+Ʒ*[! ,LcefVIUjf`hjG5;ɘtZ#vZvִV.NKE@E*x=LJ{4GN?~Y .T0gYN!38AiP2xh!zAQ1:}VܺA̽-?8TPKRZPpjGK=ځ*@4Ꙅg&`9`LJHKo෡x=a@,~95YQta8`L"E*6g`/-`I>bCX}SC܇ Ȼ ՖzՖHXK 3C\  ٤` $YnvP7ǔ8Q#c93#tg+TU2Bm zPu[Vdg 8dHHcϻD(3aiA6 r"K;>/~%1A $d;-?&p]?~M 0ЇFwH>9%=H5{d`B?o% 4~^O:sҊ,vu9#d+q ^/*ZsKDi8o¡D45sHKZcr !\]χM{/ë ?~wC2MWS3ƛU\<;E rOh:h CrpDtIyem^B;5 7s1 bWA` 0eϚه)q^tiodKY>[$V4qOe(ݝ֚J0!1~!4Nł hr$̯3k9oX )AH*^ ` EZ"&ܧZ('n$t O8G HX{f)Ь9qW>|KgY,a,pTQ oFVU/uAn@`˘^ALӦTU1P4]h*N0%7s/_epXd>- WY2`"MjC~XF 紿7JA!yMJBLg~ii% D]ˏ ܧ9*P3[,^Ҡ X(C2*ʱ8'tҀ?Ȕ>H-y`78ה[Fxa+Ǎ3@uume/۩C$:~R'ޒ?*Lr[fT)U'>\O"~`5IOZxU~PT@@fNJ`|9T&iאQ?~/,SEQG<;B;B~ӆQxo6W5 &/bzM)יYb=TxEaKEUX&"}_JLZ4P/1^\hX[Y[`ƾԣx0MAQE_w"BP4"Q{F"y~Qc' Z9$% /Լf d{5,lص{HMN2 0!fjS+L$%$z1m[+fCNC〾j?'q{/);;ǵPPbG6D0%|  Br}$:HMdPYC2k#`~0!rIH9natk{SN}R+(8_LuFXC=Z-{,k*0+|^hxLb#zSSJXm4U"N,1[| 1$.4r >^3,";!\-cIlRIA;<qfu1C +A`."4)|[SBGGC->3!{ VcsC}LU~v_V&5-ח{9l-&Gq2,t!Pef6SIkCn <3|Gd"wRSn ]6"y!IB Ѣ/W*@8dGˉ`vT N12t-L^fRO9dz en&ѩ~B?+4-R p>wl1%=:z !S+oщF{hؑ N p` tᚺ( A^X@=!Y0j#0} r*doSȨdt]jo"_x1Vns %L0X? YAJ}DH)'B (}u4G3OiF?ۃ'.v*%QP)57X`O=! %q_ZX`;>)Af.#K5żB)CrX'}= S0(xs;+Z,͌^9^CpOtJ 366yQ>,.rs$ hlq U\5DG>">HQSBnO0Ȃaa>;@TSe7mοCgEb H|wYV UNHϨ"}i}]$QBuQ#_Y2hVl1V[WH3l`mhl#ߍ^Eo=v>:P|5ꨴn_U$jı/?epa4Gгk21 SYWee Rmr'"J錃y4g;ܳ$yf*f~}H \K=PeU2A 6f#.T~D"MK8+u&s÷n-("SzH0ʢF^"g?s++ 'VfPjpk*o(R0%`ئ<,$Lp@gφFgwv ;`.@`5:?@xg7$BLIp4>9$RyZ A,#E-uEcuCV7UDIz0q8".CRt3Nt5bf* +DZU,hʮ@p& wj~uk”P{bÊu"J+%&_ s^Oj{BΓV-Jc8sJ^ZEx0b_9D`Q(8t謊 a|\~zAiSw#6Ҡ8&އ¬'3@Q-H<)³T0ȋp}| B`%GUwU!|zB~ڔຆEoJ~{ ,Tp1 Br&GpׯnY4S-0p=k`$zMgCC"E`m1@5A_$W?t7R#_$\h{gL-arDffplkz'&{V!#ZC%ex>afA@_-c$+!$١a;ao|Y>%Vp*,XF}lv;*î-JF|sQ6)#3%8 Zƞs y!16e],QZ?t1PC{( #Uktxx Fw8@C1 5ߝz2&m/ ;AW^{AO #2?t.pRH382AqjQɧnb h"K79pw ʬq ~/ ךćsFa͊ hےl+/|oSJ@R(jR.}ӱ(LN$UQOl!jA#`q쎍D ]Dz37kɟMCHqN'| PNvHIXTL6A}-q@F`(q$?pFOr_s^,hZ3AMxO xܸwԥv{( qX6HN/S#`>Z?Tx1-`h/{_`'h2ET,$S48$Rv+Fl< Y3XD!!f>+|îKbarFӉ^]*< iE$;)wsR}`5?bYh5Qpݻyu%`^c;gDrrn`ke-2_;(2ҊfgTz_2Dd/SOd9Ch|j&ɹfm $P<`b٧%DqzM>GЖBb' K>+p>y:ۧ選1H0PT Χ:G|:dcBP~M>w{CVm9_rU19iOK2Qi;I DHf~7 GlBvi[$oadRz,r:r.וzӬpZݸ \Aqǭ$$`ro2wӉ0cb(,s-aQ}BbY#T?d6 73 ' n\qJ?"ư"3Zv J +DħZj*t9S_ :2mTg+c9|_`:%E"y%S }]~Is5H joe#i׍) S.kU>,rq*7,{T`g |USJK|XtG(qg OZwk*A#*[ oMjDQ/=tx' FW6RrM9<=y{:eH;3/cN *W0ח8Bf3 r^dot*lf(&1㫖?v#,7VW1zTi(~>0Z"wHQE&L-(#H><r3k?FQџ_kLwܻ&^uGi-EV9k&Ei;_{TQOʋ>tCAY?Q ~HW,d6}'rvK:6ڞt:aښ~ Xhf =cPC⁂O}յt60h?&5 Q7y֜a 9>VWF# MmfSЎIYj;*)F'd?d w9=3!?e $}8zr瘩N_Ѩ:Q]S6솿bt`V/?D&)K6pqQBxĕ Һ^iB^6gf_В6xz FaZt0bCzYLַ!dUt.2k'P j\>DvFc7| -nYUI-e6&c)Xp'ډ*x6Φt/h( Ysj|4d]El ,;*` t֬e;?f.QsSZJ zI?8St07&tbVV/3f=2@ȳjm6=N-ۢiDo)mV]z y+kCO; n' a~[k60L+%[= 0P lCP iޫLN[W6DtX |`w6B4b}%+Ƈ{2) i#b ]iMdR W\ݲ۹*@ZAAxD%R[Â4 i7.#C}űL]`3PU㣓̌7r*S" ȸ@#zFr?`/*@bE+dD\nx3`ϵС HmQ=A ~t] [~+&a=z4}:L ]>)7i)Cݼpy1P|Mz3y]ErRzMLn-OE?֜XNz+ L..Hx˿\p٢{?Qՠq*-Ǡ !1CǴA_ڍ ({C${[fI1; y&>aFgЄ בk&۫9q1DfN7&·B GW.j z=C2k& qp췙}sE-gECJ Ź0/lGk_*f!5d&i +HUSF2)K1. !h[|W⤾MXE}FohךYa3"]g,buݬ+?-UV/aeN%v#^Zߣ<} ;:ѯ=G:k>]8"Gy|uae:?mI|lˆ3v,pe»ԓo<@8MDY } uD2O}<G2{#$uݠtM跨f,0Eʧs%Q@9P[UL$XJLtrG8S(sG6b s|B(Wq?‡-" E;Gs`>HѲ}}R'8.aZjT|[ىՅ ;> fL8\BND%{”ַ5)*A)\揜)\ʊdqLgpɁcC] 1#;QÅ16_q06,W*LNO&71/_1.بeȘ1AE{/=o;Qs2Jq*UFNPl)qcȊ&j(IwپsHV!ZO !NDSMcRTF~ j]UN-P*p87>Q#%U /JfQt̺Zw2!rxʶ>=:6SJp%)J[Vsד5Ŋ9qE6rS`hb,QCߏeHB೯QZb>sy{5Ra$8Y9<Ʒ0c ]|e΍{eS`22qmBpON6r1;tkv.Ic+CEУvNP-(}^ uSPXod?0bWj ᰾c:3)yK_\ =O f˓w ܑ9X8MKhy.V,f0yNk[׵I160h8oE\u Ԡl GrSaO@Q6huPc 2&5 5dIbw{1h_I$mYFt%a^L0ԘjLr u+Wq(QQ0\"3{mn|ޞAstAe";USVhUFD+]w}װLDw`_?0:|}v*+vtY LteVBzIg\eQ*6j_/kXO/RwKC^ ?_"ZG'}0]C$8mZkHrr`QlivB3/`[v @P̻*Q7b w\< ha%qB%AHpwU>N`a(T;sFF+Ha|bxZYC@ȪCR~B{X7 E{0aћe˥Kz[l&<}_ 4/yR7?2wBNR(h3tN Nhq sd{fFp┾9OJU_F–*RPZmɿ]LfGcpQdq=f KD5ē40T+kFNMX>Lo/nM&{<1uX{R^R1tk|L9}8M!6s<]iXl.|:;T) )K`o9AݱԒ15I65YUJťczhWE2Yq!qA ]j|yd6 XZO:d:M7pAdTަ64ml `}v;Wbpd3R 4&09EXLwc]Z|?M$I@tN_x:z.12a}n#CN*U K,TϊvX<ԗizNq ŚGt= Gt%Lizi7ijfVS^_uP%UF ܊=%ipr$ѹɳVr =bK|)3ўÞ*׋#8Q(R+~u]ܤd<ajڵ#! \#V=]mjhϣ_ U ]]d8lVVTy,h}<<qWu[D<á̢͖V?x@ 4o8S\ECa$:åPf?I;l wzjx#OΞ|<ύǬaqCN 6..ŖaVIw!1K4HpeO!rKD0O1!cͷb?@"˳&#|ix#Rp::g 7å>mpcH'A!s8'PbS$!Vb_/7Sc~ VpD}FQNgy "NjۏWvfX.G[cLJm+X6+R>DϊT |=]$ЧNч$t S2urΥp<7ۮaL[peVnOf),֬Ob%{X且ц#;b6f&f4Rh]bi1{2W}y1?54-,))E2w,Ukғ au|ꄳ&lj]yy9,ː{#(+P:~k6;2#aFjs,3]8dKjЂԺsq H{UM7V]0Fc3LPbJ↣}1;4mE]ZMZ%^4ٱ*~+HE.~-Seiε\VB>XXJ Wt2F(\닇rmeK_< ).Qo)()vD Rrq[㹩 WgČӇRI[2ƫ*otLH {=tVZ>K-ivi-T#tVCbm1mzWa -RSyʜ**O9afƸDضm,7n M99 ɇ7Vh %Cd#opHdz!àp E\}8-4tvCM;>S<j.rۜ;裻ݜ-YWǛ :Z[­tQްt!?T^9c,X*;ɼa7]F|ՐN\׎ٿ9(u(RgFi5p'#<Ĉi"rcr!F\b('( x÷`r/ɋL,dN%mr:]!'o|tgW|\*z^,;? 4_H @VJ.xl9,톚=b˙2fMA7Fy_C۴rVCxz'8 QOC!̜HM@ǠMPr{>6'(JsM e9XޜEx,jtYZ ǓI2F a3$a !;zy|6ysjN~b[OɌ&ބW5TN1[VL[> ;Ū8~7Gpcy284g0ds#Fa(R=eOONu֬E ņ\Ŀt "/ lz`첬q)P v H[.Sr^i="aK-[F^#=BnQsutHtÃ? _!i\4ɳIr%2) a62Hҕ2 `yE%^'hd 5Fo4ţ 2Zޚ&'\ w]V۩p3y&NLwܙ ӖkT^q в|-7d1 C)qә#~븄r9Ln W&BbהּQ< _5a;23 f 4aJȠFty$G=xy8gdhZ\Y* kY!IdZgC#@n|J!qrGwuϪ(%ԺC#!Y$-O6ɓau_n]7Oj-֟xlT(|ps <;zX1X+ +YoOMiR }ڒR(=koʝRK tC`SxI8K a;6ӢnxT\>XE".'Wckw*6Y,)+\-`}V(%ݝԢ+LIVf M:pC,qL=6SQXTZE.f| ~z6b cp\d413RGyM*E4|Bz j'"R>HjZ7dH > H&@t^^p–UoCAFVcc@GG7q$Fḍ..RhK~^2gkBIHKHz\/G|M#!,x6I1u2Im 5n/(S`sʿ'"l{kjF(H NIݨ$h])EulV ۨ7/z(CclQIò Đ%I=AAG^W$,4T>m'-Q\zٟ krWC1 }x*z-DMYbx&:$q31E|Y/$A ቢN*<G {H=vPQ!C}1c0՝ 2$xR”A ג&Zry1Oy8 g&/l4ݱ*-)Eiu٣\=lZy^2{^UM^^(eu˴2ԿL"v~r]`fnbۡ9;ZDkRD IA4t۳:*tC4Q`sy,ۥ[9˃IB17|Z7`elԓl LUfCtC0S0iX(,YX*Sg~cb]b5Lźa½IW䴬}y.dDz(Fy n%o,e~u?cjad 2c(nWBŎ$Q`3;C%T>5is~izg/ƄP1Lr.-> 5gݒ1m88 ;gͲ,C?8~E,L De 5˪nZnA*XmmqHm$QnqґuтVATTsR RreK~j۹e?33>R'Z_1 ~SY#Ř"s.*[fi>k02-Uvt_ #|yY㩊2@X҅2 p_Đ㘀:E@V0E|oqEUȲk2 6~3w] D`LiXGg psFBf28UioM)l~8scL,'eh2ό3˯*jH~]p?dX;'x R*[Vm] !5`֦eJџeZb21:`*KY`ZSHP ^rDݜ*6L&/άu:.q$k& NX3{65Ѓ@5b"zr1v ȪO|[h1?5DS\JLO,KPUB-e!iZrt}3nG~tL&~\Q_͊"H0% -#XqeÚ+MMդXYK/ 5]cCO/CJHU' Mizg#LMya<.Jnn~r2kIpsK oյgF(Gg?5_xLeBҶ7LMȳfCs݌m%X2kk&YU9P6!^H./oVRDX?30/b/R;',hVQG[QN;H5ʓK/fB]okQ6C8mM|I~Fh! 6)ZSAAKվŖKXHp7Q:HAm2lEJJ=ʢ` !Y?0Cڧ)Қ2^|!( ~ B} Di$s$F2Do֜R3R,f(ĉuHӰ*0r3{~g2$d#_U`^Ufմ1BLp #d WY=6 F+I1ͱōTHzRdh(3dߎq \$+==R{sw5]vGt]ǎf014q]'%t b OoYVYay-q3NuZjN~cb|GX.R/sL_ɷZem I}3,'!f+%*[ Q+/"t/H_|WWx~%w2? ⍤ةmH4ņuW"WJ,C4oP8! ֱuRM]mX/{Aj}|/e۠ o1զ;&"L* wFW96$ RJJ'.[#+L۝ k_\Q$~+˚79$ ga2: \|r~хOF"q=j =S_)̓%inܲK="w$Q7Bu9]R*\-Oe+⾱vc.'ˋp'AqJ#UG4F)=Y,f청sFu\;9R0EIJl87%&Jܭu]9DU^J F9Ϥz'þ7?.eX;qs M>?6$Bf<A+@i 䥜Ix{|ӌ$S,W~]>nL|7a2`D=4::i. aCQ?d]kr{ĨmTN./yEG$"*W z7$(T{?9tsM‰#Hn-l8SF%?Xi0Ȳ,+F3Lj@33 %e{c17%!+{DrLɶƵ1 )ړT(ݳFnUrF؊xBF\G}8tΘLeny+XDgBD"F–~aևiI}(TJa\YtE/8 {FaoMOKm9(ˋ`݉w!:^z#ں e5!9ㆯj κF4 +WtR*8.z~d$FyH㐎(JUAl'6G' hX^l wX..yhS?ᅤ1LB6r(nRK#r٪t06_ Z1?N-W, 96/rO`\JsH $p%6h jGmRz2 :@UlѮ;+d-OgK:NVxda2ScHO~"aI8J^nV<tv˸GiMտdpfGF+հv92Ɍ\5Lب^lF9#"q}(1Y>(Ugт?G -Bo\ԏx?o7d$xaX?G8sbJ?ԨѱzdɽTo6i-ցÉh3-ێ]tG&ѷr!UpɵuiʤIԶ89xԱ56$fL5eO蹑`eo ։1A1;&XĹVF&y?)~Lvse}`gekmHM! KOOs._\蓚L%V@&=ku`20jۑ^lj㠨#ǣ3a&`5?!JKY{g?5Lt2ً9a8JF-[aY͹+4]N|>it#7M8pE cdq0s VY8)`Xb Q;1[Uc(̡>Voc (Ց S;uhhNBRHە4FJ9CI7,Bx%T6@Op%lOb\chq@KaL̗(UZ̥M.mI(W c76Ejf96̼}^-Uqt۩UV3s$nRC 0ϩ-YQd`øsԃ,w`0#&z B$IɝNy5>#X1si3& ikB\eWWK+2ۢ5 wvb Ki7f]mM,g .{R$j2QBk~yE.VjcrU?Wo#)YtFf0RT` Ո==cD)CW YFcC0S9.5'C[ ug8VV,CE7GaL$۲ ]ptxDr>aφB3 !3xi{;bU7%NZ$1?W 0ɽ..&NQkviZnw'g^\;Jn(rs(C?2"mR` [ސQ%̜*N0Yyb0{~qH.!"Ȱ)*dIpř5$|DW7^϶&xh2.•`Y|>FslXF^ҋwXrzG!!É5DYq/s5qęaEuSot2ʺX*Ž}3lf*ᷜhr_`{q:f2yHF^qDT1 BvIA@lËYA:2i?3=h-h.wD8&cE-#ʒdTL AӬ?f>UI_؍vlܠRN9s\o"?>Q^& tV r tdtB>VqDڷ@kyv=Tn5+xU_pda(wGbX$@./2v"n )_ Ss\tYM>jB%_))>CC^4ޥRfn8:&)dQC+FHù5iXB[Ofih-S&3"9XB"6㠠WWz,<8wN܁0N˹"m0Jp .nKJrɗ*s39 t ,+`\p 5'Q^ػ)"^JW0؉+ԙv S.T"UP2'v T~(v~Cs䬙Ɨ,k2` S;j6ĩfmgFWpR:iA- G3<RrEs=Ig'_:}>1;bWvHkW8hlFjAt)d5B;eNxm7Yv̭e*.f I&JgHrMjEyμ#qߋnXI&nU{:Nh@yNG{82^ud.4ď&x &% `Fz8b(K3դsEܴ70gTd@{SjMj"c#lsV8=M]%owg_.:7GJd:Ӈ]Kus1M[ × Qd*náՑ?$*㈢!z21tfD>2;H . ɲ1i60dYFG8л;`SMzqy}2>mȯ1NӧB}8K5672=D'3I/$t`S L*j3R g_s$x7Crt柭D?@cq3%()l(4-DuǿpRnCOBXsZD:ԹJ?Ȓ/N:6\q_C q94:v½˯O{n \KaMFySj1.=ϋ-hlzLqi͙[E8ޝWH{ƭI."=86a !H܉#ȴ.{w*6 XV$ "r> t\s&Xf98Nˮ-n,i6w[f>wK+^c_7S|m8HGcp/nhcedF SRY|zO2LV9&ɻ'$.ԟ9M|Sv =MKKZ}Hsf\yfY<fЖM M3 xaULL/vI8؟7)1O+"E5,k[y NS0A3"f,/("|fFژs$$P5m \dP(O2iW V\x|0Ru$\)'z׵1[pG^~þwS̻yÒ@NZ ]u'p84CZu]ܒߟg;):I?t(K cОDv'Mмc,sf%t\$;[e5lĎHZlPQ*LX]-ne*KDzǖU^8.npo7ۛL+/a֌2t3c] [9.;bZN?D#Kx#@b&`I.yA&L!fAgŔc(馰?;R Hŏ<Nٕe`arܯG Ͳ[y!NnRRj)c;YowRkD]^i$ߩj=sM!lZFFs%q)S=HHl*Ӡ.bSsl9EY': /}KC{ju_5s%j=^%zW'(5Ev(\"hќQ{UN4& c.-ϠȈvDdDyM=`dqYc`4*UEYK&X d h\i19mӉ:fIdz6Ro$CB.eG`b3b6I %8Ct<;A7GOl0/\U22D[1rҸ+CKD^ xz}͎?&C|Axí֣E[HsMh{UډBYxȽG3 Z2O$NKȿ DMyZA֨fkv=ëKROm̱۪t'BF{ވ CĆ$TBZN}]'$; ̿5nsӌAwFVqWB[ؕ];g c21m)xEYG{ןGX̡D22{Kͱi@D콨/" 1taFSic$G.3 G,ܑj8 |hsEP޹CW24O1`o@ŸѾctyNik4#FYr2^A}jKzt=f1!(e̱_{gk6uC^&F2aI* b";ڍ,*)V0HܣxA#ZcZ7~ˌ-%sc\擻#Z(VW׸K]%BjB9+)2c/eWkgL̓=P Q8J]#a5E_x&dEǼb܎с!,Z0:vU8zcQÆ1x|~Lpޗ$e&T F U j$pLx0P@"p> ;Hw/*rg4_銈<Kn<^zzAF8R;tVJD}<ȗ+i1,X!/Czo]Ee8I|]9IaL?"@U0Y5C)EVD4ǣUgtLg gU>INY- s45IMoM3'#U8 JG&6,;h`\T[63(=HQ[?u? ԅRTe ҍE̠tǨ!I]aD25>P1r&$#X\xlӐ5;!zU^J|}eǵ|_>I*C(s"W$lwEZzań rUxX±:ߊc(癶DzϲHGcƩV˞c#H"0.ڿ6B}&}rzbܱ&j\ɚ[ıyxB1\ [!-[1AĬ?7яBLCcryAݹj9>y-{. :o%<*ͬȠҡ,_h.# 5kr,MC(4j=+R)9Hf̱OD]!w ^E{GBkS-e12@I&1uqsd 'kne*{ǪNpFGs -enRl5Yf%+i;" mHJ JhByS䅙S1܂@Mq.])@2O+'fk ɝ"A6-"KM'oڝ?}^ DcԵhG˿B}-"˟-y7{B4ҌS{kXdRfD-8mt #CTWW'[o+DkIfV'# i`5$žk3xN@԰R~܏D"23YX*#TmTң8f(6 rX 1.y8zaX884A NkqEp _J><*<7-?} b||߷d5~L3jLJ%f<$ItiFWx5(J,Rzt˟CΏ2q?[e ,xr"N`:{ac4k'T+ܓ1׾Z) :< F o dcDTe\7(磄.F0?7UlՔU13%īWN;&#AbL*t-U[W!g:gI4U)ԝM[WF4]lȍsF))BZ୷)A8p=h|2doE;cQkf9|`Y1ՆPiLmlz -ct^0?9FN]>Ge1'OY[S^{Pd` 9x:fdl} r!>r~:(^~-a~7j,0V~y8ֻZUwɳ)~K1 d-RV ńH(1al]GD"1Dmc+DF/ y̜Ƴwc+H'le0rAX M$|5=vzcnrG1r #S3Dߺ7;,AIcϙlz)4G=HԶT7??#"+mjEcR1*n?vk;Fj̹<09_MZz\ | %.o,zS$sEGPw$S fPs2/data/s2_data_tbl_timezones.rda0000644000176200001440000171530514530411473016470 0ustar liggesusersBZh91AY&SYL!{yu tX"5vj)%]t$ ;j(R4ml}t`@OȀ hѠ5=7딡5SگJT޵R 8yE '3(f49`;޶hyRTЀP@R8)yޕv4 @s H(@nc@Z VRݵ]IUDF@.@ $۲e ȁBvQ{E  @0Us5Jn@r ^NOXw(QRvˈ@.hh8\h H^OJMB$^Q.`3c#c{m:;iTz7:'ovTAAr&VteN0GtХ;,m$=᪭C({j`zxwO2W0Fy@\N ^e!{gxyx\HP4QDA7yE{aT5H=Q`=7;Chݟm/FʫOuM>g P HV{-n^v6{5ֺw|H$ (4[@(PP ]P4 @HPP( = *@X|\Фƌ4=g\>0:uPK3@:e^0uޯZD:@$PP: 4րQ`tt@T0`(+LUP6@U$ ljP42RUBzr)IT$  @0l( mT6U":p( -&$*I `@{`4(֠h( ӳH$UNXȄ(PD٠E"0(vpU@(eT@h!TR)tp P@(3c@PZ%J")i Iق 0((, N JZJ!tր$k @))j)4+eE%TH@hhlh PCT4(@Tt7`4ـ (&[lP(4"M&RM@b4` 5, P5C@4ɌF(9Bje wt@["C(@PPCc()D JPj-P#@ D@M t4 JwrF}x(+`(I@ցJY 4*(VիCB)" ")@4mBh WACX(P4ZBӀ@4^ʔv¨!KL@ *Uݸ U`zkmҀBHP P@@v.`i(P( 4PP(((P B{j`IHlٌ."-PVZUu…a@ZPK fP $(%ԣ@J$zvPdi `@HJ m  U*2EM@G@g5tґ HR(EZjȠ @҃a G3E :j((%H(  30.Brj4 @uѬ [jPP:@P@]t0( SP( UZ R P RBPٻig!!Sl:$ %)pT](G6MӮmH@4 UTU;`UOFL&`M2 dh ihi  4iɁ2` &2  5T4&M@0LhLLba4т`L0 MLF!4&&*`@ 242&ɓ M5%ID=JRɥ!Ğ˽JSho)r bIBB 0k[Z&hR^b"!WuVA\()Wjő(zVJU VR_=X[ӥ+%S[TFG?KBI JR0eRa2En¬IB`Z> $آ+ZLA)TJSpgE,Ee+QK ,M))AZSDU DUKKc/THCM&/ 7RWPd6dRwgl:ŬODh^Ւ`RB "ROR&T P*UTV%*PKN g=d؛UNbuOu$ؔQ!RPTAOe*J!(BP6 .r^J4HR 3HJ!/ J;55ٴT]BU-m ]Tѽ%m(q) LUI3I* l?ѣ5`ωo6~ Z͸xuR.X@p2 5`H-CnHUẇK!N͝$DM )'jƠ^e;" S2]jڧЅ.xB!MqB-\DQVunYܓKKzpkj*G^%8-6[AtN 5Evܯlzb/<(t 9E&ߑޝrY~GK,plX/7~S`Ys_x >!]F=U1 7b~:DX'i 3^ ukˉjli~ѝG lժѣރ ך\XuLJnHEJ^BQOudW|+ سHFGB!G#[7`yyDcl(hjGGHxV! I?*W-N=+6tď^-t)T:G? ,}l]^>b_^..fhfq6؂)ET&ydڴ]ҟ' \XiZJXg+hAq/.AjޖyPt"S.jRܲ]R],ͱV[_l\"B,b,b^l絪f]k6)ʻa(K?Br7lk.+^k)lob/M!7tD^u16dR|'U,~ڛ"T)pi/b?Z^3k":U>׊XE8TW%CI#{N ZU *Ts(s͑ϱ~C_ l,T땥?Xr!P\KReuZXDƀVm[kEVۗ$U4eJvSbWUBSJlCĊ8v7kT1D4 ) M\KVSc]_<闍:t%!QVa2lUiosv~ZTޅTQJP3ROA))JBRajPV >S׆=ZjSD0ەJBNSCX-Hbi*ih0Ƽ U]r( ZK.ұHQ琘CJAM L86+js)JBQIMUUP2SE&RdJvUJ҉tHHE(FiSNIa+JJ) Q)!H^*3E SUD J(uB:T+. )+Il(PA eaT7 ؚRƘZneMEjO QNmLRB E!%'[RiJ[ .8I>rJsma4aBi(ՐInsE 3`V5-!RD!HksS,g5Vֵ4B5QIm+(%+E؇J\JO%%PƠʥ!!b* {lBU ?p!y!ؒRJJ, M:4,:]UH!Q ,CyJ9t$ӠoKTh% 1)JR1JB>Ρ(u-*rԧ%zk( RJSD&TJcЪ < ]1 \OƂߐP692pȥCfU;4$r@ґN,g%䪄QX)! ^TDSbސAU"I3vqYaLj4AJ$̈B0BRB&*,E(U BMfV5^fSG01-y|SB yiAKsIo ) [JBjU] q䴖=x]Cho _9{ K9M6)Z %A!Iv Oµ*/#Y5]:!e"7tme\ߊ IshC´U bLZ)/AF Yc?U_<-r*D%Ia K՜oD ڰh^ :T=jSB] Zt*zTAC@I?oUEh`J% g2Jrҭ! IA3}͋jUGqЂ(b+u}<%[b0.%cZxvyqF[e(JiK1 )#|[qe;& *&GzPB*B(ӷYJHDQ (PB WG"DR!"kKW%iJ)ICijZҏ)CpT Z*=$ՂH)ܖQ,R`(G8-ȢPЧU0HSzXhM>43šS(!JhI %)CA4C?E)TΕV,OLYThz,*M!5D0,h?R0)%-AЭ)vTJJ!B"hzڼ5RGv唒C[b7D+8RSAIE6v٭9̊4&E5fY&HA$`Xйy(ڇ]J)O~Nt.0 Z3бM>FN}*#_38R_wU+b`w=3 jW:&.E)3o9QNs4Z[קq+QH#oUncZ7ƴYE_S)З[(U j[-/_`Rmpv5QxE,{(ƧQzOPK BbYaq4i' 3BqoHB(?󉰇" z6Վ<<@ippN(eDƑEP2CJ84Vն]e{*L1TW @Oe;&!"FTR!1K6QGR` {-?QZPyoXPYFbq=@ݷۯ,~O{EfƲ x`5ӌu1BڕV#nhO!# sZ47.ym/#r7,o (az+ޱk{Ϋ9K_tc$>tUP^wYZ3_"uQh> AhR86hu5ezL~2;kbK -}Y_Fc_=7Yz=zu~qR;k!%ײcQUr(.KKֵ|t7lc_{ ]^M:N+D8N+?})"ECՏ0jsqobRԳ xxemZǠpQ2UҮO/OQ_ǀT̍t'=eBtY5ilnzUjTX![7Jn&NWT()5aXh%&&S XwyTbs)GsTn9 [~JɛCI삏*K06QbqhlsM4PK=U-FX-TNѤ3VHVbR˟,R]5l_k, fUkTK.ϹUهڤ#>H+ mg,+~߃/Bԥ򄑆 $T&PRL+l3H5se>\IbMsiFko{;\w\Y(gS[~inΨ$P3]dMIw].KbK/Y0{(F˰:EA =7-E\Q1(}vM3 Z6Ӝٴ. Qޟ307},e:R=-*ϖz{|,+rQ(g2mSr_j]#Zc);풺:kCCr&QcNџo|  ?"3vHpYtWnOVeK@H!a`cbNl VS[Q/_Hwt™nj7G ^qMybE &vG Q0HJ14q+z&Cg,%^)38DR* 41$#=#gj=A4bq`iP,B:9{ aZ^;16 סaT_!]tNB+S_"~>4v{sݨ~ t5Y PzֶJ >`RAʼfG%]]|1RtSD D[ ?Gx]$ɼa_NA yۛ9/%3PX^YK"rrkT) lN\֗#YQ$]U̢Ժn3GWfN>׾E6QkZ-(iMͲeҵW[d? {)6Epț:7a^wg^c0`5U_[N_֩7A>a '(4_W-~{o]F,b5wJ* 1p},[.Rݲl~g/̱^gt_ 5S:$^fh\0/=F 죿oʧHZ]Ik|bddeRQG'8f2jBo k'1Hu+[tS8iҹڴ'y4#s_N67$%JC@Z l)@edQƺN)EF()i¿WK($%~UM2Wie;vީ(7( CDF1xoG 8IfVGpj6ٚ(4W{i;=?o53,mo!-ĵUt=7#M3N>VMuQ饮X^"շ ZɨK.e.)I9uE6W<)eٍ^VuQD+U}^ԳDZNQӔ&^^ Kj)o㘥CQ2lCF2 Kْ Qh}o뀇qygARq/٧Ipe{Nݓ wi._[kF%KAjŦXg_&޲nmr+ /1d(ךO}&I3H_s\'EQ"#>uJcVn,f]гz{,@~4]M~P* gH2W;[\Y*L8L{Is$I_}9)Mgm )X`Bt_F}8X:uJ " /Kpʓc{XD.b"I[WM3q-FЅ'1 Le+#|2b5[ѩL@-2iҠ$QtU_ù^0 s;Ed u.5OeP1Kp7ħ-unQM_苽SbսYfC2߱ZA]Ԗxm^ hk X42˱ Rq;x&UoFLiLcx$ucv TTe)|[[' =9PR È5lU%D.]R[_:koWfo~+hɼ*BJW6.,vtCX21FX&ʫNoug/4r/}%NnwI8P^WYJQPTv-*T_R*ʉP|]FH'<)PlJ.ksZD6FJ7ȿ_O};!O1xHؾT=XXev͠XU:<+OQh/-%M*vtiGm{+5w ƨʯ#wO -t9:ψ$xQ1/?T} Fak\O]VͿ{b2caf ag.uF9D!RF ;8r]1'jAt@^`QT\qx#U>-2t)\ʬqV2*.YaCZ2 ^v.(U2i3- l> A;zN)WRiNkB=!Mz-bt Un˽nu ȍjrWg\ןX(vu%6Z;Px Jxvj;V@}侔q(<\`6J~gqg!lW`Jxe+bP\CKZemI%uˠaJj%3KS*t1Lod Ԃ1jt(8-r$d(Z8/`'H2e,::Ə}۹v>tZ=EfBrRⲌ=J0tE3v`RTƇ-E}oꖅY0ҫQj3l{g:Xɨ.޶EǨQkPSV>!IDJ^~4t5zGߝĒ*\VaE}xLc4"zZEir}y ܗk)q5.]vЗߦg ?`%W[S8'>nݥc+GA?Zs 0םz{n*PW>Z{,ӫ%>ВQ;C=Z7#r-rp,܎Ɇv 76a˯]+M%6ZiѢ+G_EXqbS0kZӤ{' @mxB RCE(~ %kM$[}CUuQ&k;CCtovE)좞ʲoyfKxRkp,bאZϺGSWR`dVUlY*b<6ګ18O/OoG~\en2:=3lH8.5%ORQG]sR_SYؒP|jo캖иȯ(z(_=Z>ܱ-H݌UkSD93lSwK n.Vh}Ib^;d`YY'uE9'Ԩ)K)4;He=b]#98KI5q8LQCU]OE :2di@~r !az;1 =œl|V#n.2 ҠE_H*>9J@au .i 1/3). <\Z{l;w! 1*݂БFN\|F%ͣnݲN/6 RjAuoRj[R6:ϙRTƽWoԔm9gŇ_SO/ An,\v*[8.l3O5[%{ r4dBw&Me? zK2[shaue})uKDF[6͎xT6oQƦBK4rf 60 j l. 2hg 9]`4o89V-y/8y9hJ׎|NaUi^(%l\ ب{PI3HC[0BȤR-WlwLA7P"1 2ֺ'ԆN&WFSeqAsUͪTיWgIBi(sN`PcPD['&͖!ҳ׵Xeݺo0EE/MIflLfQenM.Pf$_f|H9"bCq`q E(^o/QE]#MG%Vz'ps/Oii'{{c,ٞY6%ܿOyWGEI_G0;k[H:h O]'ϔ?6ypE?Yػ.C kK8#fU*˒ڣbi) } ȶJN').nRn{IʁO-} %BO'W]G0E=&5Zad8}I?2ݍ8Cyo=ZybEøiE#%F% H;MuYW#[AcqGN&?8sWȗAx,=Ne_L箒PT*j5Sͤ=r=[1)&μ,-ziWQxD6N"5O8+sz6k^Sw\R金iU5;MJ:иK XCG"%*ٯ)fyAt-S]}I0+B56T^4xL儒 aBȼ4~Q}*z cc1<_\+V)ҟ8H>%zbY:5֕3lA;u w6x63b@^CCF=gJ}"lTv!YLdU"C\R)FDK. iTK:x3 ~x%yXe`D Rxի6*vkzlM>%J†䳌I~7Gg;ώ4R/)H#K+;fM{5k6cؼL# C:lZ1fRYT:YR*S&+#c&dQNDlCh~& 6Ů{z @7K*SdAPq*K$ʐXۺ+6MUTa?C6G2J 5{Kt#|iaI<~\[&)YՌfXgo)/&|WUhi]>K ܧ,A Y{/U =S;ŭ{rK6 @:.0_D%Wr|@fHR8vWZJe2fmF^(~.Y7|/;.'ڲӀVsjT4;2c慳Aw{$,/o޲&DE7nQ"p6e00pjүѬ% 7跥.oZ#$B*m9UQOϳZgQJ׮DLXfHyKp7:a_ܜZ1jR#֪[s* (LzU>eҸҬ|*Uu=D\˿ߢߣE}^vnZ?DS{lP[pzDVE<-ȼ4 IOJDe[I%\glOT%6n|'RoYG)~k5 ~m4E i`$ҩ nZIBeGPyFٿ:JGz ث KwԞ,ɻ`- %|'e5z⎖ $$,qT:ިC[]UMN*RY˽Hq 6yM:+ēydzvDu?We{6ۻJ+YBg` W6)Ⱦ~GDm촌铽nh7Egv iHW|vq*wSRY0FT|I8m-nkkzs3/}j"v=5 ]\EZBZ4,jiXV4Zb쫼f^ii_l|IťLdR \$m]bŨhQk1z88u{fji&]iiyh}^A~dqSoI~fpQKU,Mdߥ+)M/ QOB-vRWDP<Ǩ۳zNW`C-.l%ˮ7R ? Gڔt^Zz_kvv<ֱ8&Q8SD ՁGaM)dEB!ykۻU =!Ghj+i?[Cr. ҟy ѭUlHxR-zU+ ԦK k$M-״b5zC<9xeeea笰$ϔ F'|7X滐n{r\ʭCoFEj6G?Ac-pЧ?n;{a:~j?G+qZ3Y۲\p .!  аcBׯ&u=U~um;,?OdDJ iN#ӈ=B~__"ۉk}!xYfӧqZh۲g.+IVÊQ4otiVYd).a\/Kl$6yŁቒ H \ x5imϭ~Z-7߳un*,B6=U!oT.W7w}\b_dPHS|Ieĭ4C׶q]o_%ojU@N͡KeMV r⨥!BeW5Ea9֟e / _s\k,"PtVc%Lvk|fUogJ>hpNܹƒL_Cx]ii ~$Lk`ijn,/E[a֝BxfJfD3*ZSa[B:/X)9eu )"M8qyXh6+/4fK(j_OxM2w|,$4 9ߓOvb\|ח̫eKaGXUL-!"5hx6GMEj񩦚qѰ= ['%ZD"su+[hfZ[YB{Þ̹A=Z+Jmgu THðQ 4)Wȅ2)kuEb =F d/T%٪5*Uz"k/Q2B 77Oy>O>b>큉e-梖-Ƽ[7 g8+̓ᵎbr_=k͖54m |qeǾ:4ŕu&"&Yl_'cqSFhR/:|\QlcP),ͲDТ`ΊҪQG=HRMʚ)yi,oúzM@۷G۝,">!}5WQv?鿶O@'~rU9 /BӛNt\[%,QY Nz-XJv >wHD44QaKA+ [`|(6p~,%QnL{{1p }h3M;fuMfx_vB" 2e5؜hq~) _T&TP~XG|"(,CX2P 3ުLdp:D\nf^Qtǔ+xXSkr'Ie]0UQ^9m?EDE>["=s{zRYBftܸ}isˡUDY<,#RcAr/ .܅2ա| t_+Z7*F Ь.b!&(s=w, MřXc S6v[W=5 hú[4p4* u嬩3$RJA0i4yOXP%]'J%]Q+G%SM?uQḎ.v. E=̔j O=}-~QO1Kr^2A}RA˫G|GSU䓊1oIS 1PPM+R W$f2&p]Ѵq<@L(CY2/',gQCahFfF9?Չqx:Wiݡ{!WmVn!4qւ%vSXksm}3]?ۺǭ j]\]DM/Mjմ*ceQ_Iwg|\@k?Z;Un40؅u;TWmY)t$k5OO~LM:.#L[~(HcMj'[뒚i//o%OgSC~B3LAOf2BI|^165. 4sZcpSe]^3bةQw!Fj_O3 B*2zԍxRJ8 U]8#")(T\eUO殿uazUţ{dVw۷823l312x4E Zkzr__yMNkvCZ:6ʪKFLO{I|/ } 3!YAˉ0dESc:UHʊT!*ou/QLcME1YC:'5ۓ2<_ѢΞ(uÅSɍwH> q̼6'6p)4j H0$8c "_cdWyP*χu /X5N ̌sA%d7}UWa.Ě\v(:C9P|z>m WwUt9+MȾ SYJt_Q AT :h_-vO:0dIeoX@nsJ_ˬI[|߿q&FywFK۾<Ҵ\6 5!nM12<ӵص-YcOokIk/G]>+"$ KuBw_uњiߢMEVPr6Ϧ+o1uWa[wi$plY=:Oqøj]Yyse"CR dno" 1 mTYfОR7ĩ$5A=L=7&;C (c6ϵ|K}@Gn#U7iRT1^֙cpM,iH>TlʱZ[IOuٓkCˣT١p]E5[)ADKEkJykAS~Ml=ZWNXg9 [bYVi]h]R%,yvI~ 祜6nQ{zˣDl5Ol%{ qJ YLL)7 $ %(j+U,*%T*Jҵgv -_NZ^yذ f/ucbZBSѨ(QkmQs9o%2xcrPTʏQ$7ťUӛ:v$NAbeqڥ+JWCή)"|6*Ʈe]Ug ~b7 jޮ/@/UL?|4 eU645 Ļ+IN;MJ`:gՋc nMM88HCũGK<#X*,5F­S^cßѧB:Hb71D@ڈq^G9vu"0Kߎx-8Z79% @EgEI5BY|ډyE"fD%d1Յ[ʃ{&iįԂ-( m!V5~{% Y=9-ة$֪V ϪS(yk3%轜ZHm8j6KY~>1$ՖˣiQ笸==LI'{f{.԰K@c{i-FxmH9DTw\-YZN#&yMTZ)m q9t ysX}e3 _0i+fW"Ƽ=AH2mDRBD0Jxڛ>mۥlGNVe-.QbإUIMJ zk5i# Z=摒]`e|梾Uё`եC6eOe 5?J'Es %5~pQlROOe]%S)>E-\u^ p+i{}j)anVE.Ƌh1`YrH 5^%Π|^rXA/1xZ0 X`f=n]ZM_uԕ-S*o(|@lr}j%RAuɕoڦХfe "zPy^SbгyۋʔΝ̛-F0è/EEҘeVRxe¼xMj^L|OqYibWOlnߢMKN62'K:aR:j묻՛j9祐 OʼnSH?__7N"SvyL1cbV3ۍ*ho7ʤ Tă1.ΩP$jES uzH<}r lc% <)zwDt6uY&OK:]i磵޸6-1-jzzˌUtN0RZ)FD- J ҐaU5㴫a=-nh;Q 4Tl QKV_ z5h8o4[h9ߨZ}(t=gB_ V`a4 % /o&l_ rz7rY>DT7r5JskEBt2IR\W%YPlBkǖ]U'IlҌP|1.r)F%}G m/+7bU6lj$JS.RקKA,H)d]j3`9iRVK 0ӷ'l?U~˾U-=f{DtٜW5= SK1٬WQNPdEc YB=>ũS:ae^Sy2f x󥜰C79~Rti`2bǕ-v%j[c.hm*-a"WEk |m5~vߙ%VjK"i'vY9&Lsc]DmW@DD_6jR1X)4p onzk\QMQxgnZ.0L{"X].y ?e{RKM`t"4Qm0gǀr2<ؖ[JBJE *Q%U}Vя{QQ~K#v\zW|13GT6ߙ2,*+~`fUEƺ*]8R)3 nҲW{oI[Z%"n__hZ*X2W5}bEֆW# "bt.kSa xH*r=f! q WԀDSNY{](N.HFu!ơ`6a?ֵVElƚOؒ '؍{8;,i$%4]4yXFU$vRFZcQuϠ؝Zu:m%ΕlZ8LDLID$_PC'L@ JQbΖxN/^ZAM!>$DO:tl#v0IȰnb˹ !ߪ-Ak]'Žˠơ4 +(/SHEpV1WNtG伵MI5'ƥ'H:d+.3m"w`3kf}NO0kdU}ZM(cvt:7.JLjž3*eHºBп{ƗQ#,Gݾ%hȃ9=I#F~.){ rbf\!U^co j)(9.Ώ=䮻GA^/,"lxþ˲/eYV6UpBAKh*M&:cyo}&SkE 9̓8<'^U}{尯xq1m^]=^ț(n2x"Y.2m'mհylccò2d}TSbۏXE;fR&BXUT&]Ka`MbHUr( V=K&1nއ Q[WHVWI@9k'-Z*o6YV( Y&k=~}~`IKf'uHUؔȴ+΢|^rK\lz1z;,(p۾ m+y SF"b 5j2Kz$ 8}6&ͭ2[ӆX2ȟ RN,zܶfcRJ`6903 (fع)$5Nhx#ggFK:n FoT49Vn1a*+ C ).[A'f%m~Q.*VUu՜^4bݾ3ϵN97Jˬ4/ {kO!RqS/U-(uUeUladūdՂ{вd%EVY H{ 쥽nI7n y3ʇp2}nTր%UCb=\Y!S-wO%<~rfz%MжW[%ekH=t%Ч^& :?gi`اm'*-z6iTW#XҪjb_WG=9zK^’OYPlkXJI%(/|ω|?/Z ZRK PC껎|N+и>sߦ}JAk2  {+$WtA=J촊V=d CK)ϝ07r:օP^W4Ovت2\Ґ&,d+ _K ]q-GȌ?cѮ8BQ?߆ ՞"iƃuQ&n^nj.BޢӯG/"xu{Ln/X$GPʕ Iyw}%;di;y]DyH)J%9|'=l'0ʚatkUwƣ3vpR*bȝK-&St\m ;]^.'LiTcWK;NJ\t(}'tPbڴv |qM-CYUDiT6eRWvD`[~|6LZVվq0'7jóKiԥ7fxսgRf%JiB;*D+(c-~(]Z"̬A>g8 b2S:֗⢮}.*֙dmvK[B[7*BdUJ1{Zt(!% v9{$Rt."R)4Ц uk~j>Z.8w]Nihn%."o+F%ǰSKÌK"Bagq_u&cNhIX$^qU6a+*`vxi/HQý?GYO(+A,@"ۡz_H!Wːj#Lw*Xغ<Ŷk;dޒȞ*w͘ޭfCǢptC7ȡǥ%t(m5>@h &Xu兦׮YI8s 5ar]#%*&NaArnX yjb;b6y|i-5nQg-S,}]Tޡ~ 20./^?3f"45//8g0|?dߓ˕JNͼ"lߓݘ;Q;VPlY:=!لt2jІ փXV8_u_.{gcK]u*cWlo^w(F˳^^i7 ̾G q-/i3NC{MlwKoW>;Kcxy+iҼU*ƒ8mA,b}#l!(c]eULsh>?j7n?5Ssq,c! Kȧv|Sa4\ˎg - vaGF9릟p)%GZҵ8"TK-JM(ۥyCuXUW6 ٟ[&aA"_㰎DCT }Y6 Ғ)NBHҐ;*t_i ZiT@WQ3'P#&|U!R)L3&{,lkƮuVѲ3%q!cJYt%C0|wTH1~jq(mn{~V2) -4(XQ4UCuͥZS.vZRsISD3)37aVH"5$$Z`8VQOJnV*nzK !L *}M(^d5B MT)RHx).<-qU|OvsХʵj)p0QQT");WT"QC+Y[o~'W,^c /["=GL`M2h552qpau)(zι5E/IAZ\\fr&YobŔlS[EpiMh9iSg2QNI9ئnNهZj{TeͽUVgVcVь5W+\ ]p ly3QcIRERvEYEn3?X/[>V"Z2+Ӭ:x?^*D5~x,~D^BAB R"h$V{PbD\Uk.;mECU"?ScdK*&+gKXC8Qbdh،m,NB.J%6k^2 ~*UEF~5lCЗ‚ *3>i+7D7׀`z-ZiѪR)J` cPCQJkS^5rUŒX H)8OiHn\;,ʐ_ՔtH C[ߞ49\F{{K-wJwxYwXM..CYU[ )8,lh7-*l퍓WW݂w;wP_=+*iIWRzv!+[|'8 |1cqO[{6o4XGSyEٝB,"RE<Nѳ-9&L:-wVq[6ޚ|6+g_a$8XǺ.)Y"^Q,#e8qR|&+ #s2I)*]EU^Mݴjjq @ N몢ԭ e4*tJk{|iE&ziPS,Eڨ%6}i,o]jSr ,&%%[.K| e^0j:f8[ȷM@5 ʞk")%݇X_dTJ,S^kR%+QNjkl!.$߁aqT[ugeKA#JJQǬ K40w96S^{{!/BeM~Z+Y1/sRj41mpQ'/SmHȍ#OO΃lG2e{ODKs{fnI G4dCX8`w_(庒T"GVit.PX!67R.1J}lst8.m#8-7?5esG-N޻m_WDsmfu}sx"HAΰG QJG IhcO x !s!Ȋj%Ÿp`_غ|l (35YC@E^ >48@eǽ'_3Ѿ_ ;2HnŠt uahV]Z5 'b9ˮvEI11`}+^ZFE|Hi{{~sȲv~>Kl2bkz؎9{$"ٰn/B"'SMF1KRy$MI3'ym^\Zwk;ok:|(qz"޾[q@%ywn5YpgM;B'ͽ{J?e]dȶCDB~+yQDG m^evgxQث;v5C/Һj X)y}t3 T(v1Q~veWK23I|p&9/eW麢!#6[<tt?G) ң, ,lp?9&KVa7NZ#T4(EN]F=>\P=4tIoyfjw {-qR7\QVU|{xA8b }"K/9CZtʢ1"( GlŞOoNb# jKbz4dSh5uL2֊`j"i.ʊh[uPK-2$(ʲʏFwS3[7${.-FNP4d(n1̋a[TAћνQbcԲ1Nzg]ƦUGy*j((җU]H$S Fu&{S3Aſ V:Z4B#ߪf_zeɡVoC)&9iJTk'=龯я`Vc9j8tXw_i %fSTyT+en5.Z_[ :u/"Vɤ:("9>Ptpo낌fʃI$<絙^\T{ﭩ7{a&a(4<6߲XT՜: , 쌚Rקjzv:fX2t@P3_X#DHfm԰-J \ʯ"O,}\M.`oW<&s㒚OV ]=VUbT*cjrf(#-0|H ^0D=+G :dnFsҞ JYrFP)'TLK@5N{R` -JB(v܍l 5c콯>&jNCzSQl9iL\bଥ4Ke;+O1"T';ja"҈.Zļ/ef]O f aQ:cZP.b 41_F"X_3tgiojP|q`)աFB|,wFLPS 1x7$UeeW  #9.J=&]V*Lgل+r){&ELl:yjʂ>ե c&⟟?G]K?*wbI?;nk`Ojs Ko.=/D"ǁte93M ِxS(=h`^Zs$)Ky:i ذ^xܚIa[Yu町1Rm1fzڼ ͛^/=_T6ċ6d^KqTYI0:9o!y#2G!Ӥecd_c^x"@P5T"E-4'1Oԥϕ%K+XNih,eWo`uX,rh3tyԬ5',%1k26ī+-Ax˹6Ua `Sݪv9c,g-0>&ȷ r4p1bȇFAr[g_֐n=3mk:2SSF*X'<<4W%lRH ޠ֚N,{5%~[M\*Fʥ Cc15=֧篕3njA:ȖЩvZ*M 'OWn,ԓRA@WƇ RUQC _ eO${>/**hCNh^*y#jD!{gC?.ߊUUI:5$ b)k%uA)OYZ[]ݵ heO#j=&7_b1vO3JmD@M!:,=qOfԝ[vK)`R^Ӆ^K +:)[.yqg`׸"q>0-0,=wyǸТ1(ϸNPYWF2X7$Ζ[ }5ۣ]}wĿr4OBqL:_ɭeZ*^Lͮ}ՀCkXѨ2).t\|dA0CHQ5 4wt5(UU(RڇcheH% 5'޿Yd]6U6|znރ:{iާ(o\95QCN<lj2 ieT\ ޱ VI-Kdڒ׼dn&u9bTwmIt> ؾƙn/Ikz@ 뎻*OՇ3AMQ߫v{ezPM}*>YYYByiM7<̀Cc ó/A}kMP1jrO]W:0;oqθ/pD`M4x렠4S+2EӋAqFF@``:X"T{/LǸ^3w}- b $RI i>e~v[:gG ]?V"%y,Y~$ -/Ҕn ~7Y'+_D uSB*r֟# ܷq )1#A FQIKc"ʲr} B5tDs=hu0brjIߊއB`:aЖ^i&V/=ZpACsJ4Q;Q҈e\"t8TnM/sztRļI_"$T4E}bTL귶8 mOkPw(s1Iv*jqD/k#\ɱm;|FXf3lx΁,B{ځ(qx-o3k5M֗aP>}zub Kmfq-m9XOUp')$x?~%%MԊQ_1&Uئ_D6.hⱨHA4c`' #{}0+cyẻ?|^*aEv6t d+K?mv;|M·ă[.thm !xI6v_ I'Dv.E`5Cȣ_-cO?]æ',e1<./Yu Ĺ Ȅn@:M[]w7KXla|kүN:ՑO}RNhY pTC#ըn&]? ڒjݥT,j;\M3Q!gEN:,+ %|P]F%seO(Ub\ʂ|ѫ_w(L9ܬ8Nʎ.J{[=F'igsH€{>޺~k{ zϦ 9 %4o rؤ|#/pdZwEor) _gLt]BP*F;(R, q{fxzXXodETbN_ijO?TIR[҅@P?G=Zʐ.*,kSƝtՇ=U^wUĪ]MSbo%/5G/) WiiadI]-pfgG˴7VQtGުԢk, 2a-U4;|vRP}Ϣ;l J;BTcP3M-<=bեbgܴ/WUSS] w:kŅ%/O6'*1=E 4V#JNOUIG\ N򦓷>UQ sdO#xi/_L57<ᶛ5K eNV83ia"i wgZ~!^4WɤZ5-*}]tٵU8Xj 3Ɲʈʹn*)kK+W-afJt\fOxYt'"22hݦ]Zr˜.M\^ 9%6;3ZaԮ)?>a:~`VұH !&7K/v=x'305F23D. `J`\s*'Tq^ gQVuSxj,^3 t.,f;*^AGBlÖ?Nnjl1hh: "sȻ&!?G^Ȇ#@XF!h(/8)FH"#t1>%޾Zd9!i]Bzc|U>FGp::_v|@Em$ttfJdu<~"Cc9](C:GGj@nxCU׈Բ9_[CT[BNM7S쏷rv*襥2ySf̩ EDxk(e0>ǒB7X.̪$t&iKsD0WåJ9縏Dã;T1xV9G¶H| ry5->HhX"{nr<~m6? z;ӣM]VEx^b'kM%ZkHM:uFeFA@G#t4yץ׋N#z#qoOJgʺbָ3=Q6x.*{>S*$s5+B&ŗ`6 C(rxh,RV1gnbƐCZfͩ ԡL$f,SPwW8XGCu7GJ.$$SE/ˡ\Iё?ӣO({,,]UF Jz-5⪸'iCkwRJ@A- TpGmAe<0 ':gk:{Y+e.)cLpL# +KRC@CI,K(Ϊћݴ+B˲mQ7@Uw9XJj+(rh{x%PilV'K*5!yVNo掻/qTZY\Qj !)T6EU^cnf֯kbl*jZ57ƎוipUus /OʺHBa!C}d߳b]YJ5F=,ԕIzUZ\2֎xp8]m+hg 85Tn.sW?&Ԇhl 9Ua:䦙rR}G^R+%oYEyX^b雪^?1A4/Ρ(XҚ-QwCRmX_NgY]q=wzq{nN˸?a VXi,'t D]\0JJ)ÛI٤!U/:IZw_s;FvxqFHo". f&O]pљF vZ|,X*0ırL6уu {|bЖ44G[ jc1ZbƖg"o=Tk0횮6.n%nVj(v1iU>czT_#ohB߳꼔O`@OQЭ ]9iP ]Q3]{^] 4.Z=+QTI]TG4:EheHGz#DS MEJ]2 1#"Lֈ^#Ȇ PE%́Hlxn (,%ȒWlO8gKo=_#Ʉ8|\0:9L&fWߊb!3D\TV!cQ+|:nrk W]| h1BWU鴁Q}O1d@_Js?vU2KNXEuUU%JāA&⺼%!;m L3lYJ#bc">Is}@dРڜq @ZgGvh{dURJn`A.DREI[TJ Ǥ(,w,;ߧێua/a> ,6#EL Kܛ"vHr|[ZT VO}1jEQh_CjB=YQSu| /L!{nu){'i25iZFU$'(͘w{S)-.vMݪO]^ AE |ipn[<]}lk\=+[b02 \Ԫ!ACV-?C~xΨtJ+ foѬw=%;.M SߺE T,k65:gDhP]y݉O@|g=BQ/$Y#w,e /VW\ˑo-ʲiR &;׉zǭ.I56l6L:ui˺ 3ݗQkaeKL *i(ZжMR^%q}CRG:Ǻ!D0S Z=Y$PB8*r A\`T%sXŽ+Z>X=6x|=)GMU%P[zetb$ 슈OwEEDe(402U[i 1|ˆU0an%Y`6YR+cQD׼d4NM_EW4(==&8 DHG qЮ(>4+cGDhK#| 0OT8ghA\mMN#dEQKuY )꿰HqBs@;]A|[lnIc}_|{WI,jo 5/U4Ž @Wv:xj: 'o_\"5_,Ǝt'hɴx˹*45v"1ͨ5yG|٠uNo;j/,CŶΚTGYf}ggͳ+c)EٶXL\6YNhi%]KRJ׊tXUӁ{ۦԯlzdc'OT/J:#F!!=d{hÿDI_Ya{oTIwh:!STu=wx/)؃F@6r p(!QgV> 8W5cM ,pLdifyTB=H"խ~·gNյݱu}#'fAG[ql4h"1z \y m\QsCtlBca?6ѩ)Ӭ9Uӥ*EeRZ\r%)>w-KFR%9NZO&U^ { 6G7M0*viݛ&I}_罏2%9nb1ۮ7E>%co"+ MOV&K:ouiYF̧2-7;o:Kj2r[1'!|M2Hlq歷6>;fwH|~vxTu1 Wk`ܔIR%)j $ VP8 +Z~K׻K"z 5GۤmB㈈j~MX2(Ím䆪Mǖ: _.CQ™z_қScBDl* 0MdwIIsե'o#KSp}']ÅP"Gkw/1}ɮN@kSnsg?Aj4%b"ɣ7B ɵQcmi{RP5LbmϠA0a ZRCx *ذ @_wŦlʺR?1-4<>:4[ueTS.҉Ry>cvm}`&q.rO]U1eG(q8 kȕ5-Tz˼Zwp'vv*\'婤ljT<w\W^bފ. w|:*>vIMHӜ<lj]ϋ#\HUCmE~IfYx}s>Y+sNϯ1YE}~NMw&ѓKQl4;8° w4TH+ZU9_69SQmZ~5'\IJ=(L$ UEO4$?QOyL"x r_.W5jLH4C) eK"/ 蟮IdT*YN@ K{иngi+)M=k3C^J,q @r ZED=Ly^ iS*4蕀T5Ҟ54?[{W&)/Mm8sY*ǹUx50W!T$39Uf|˓/aEvSQGJy24_"5,/YZ㱸b%. 6y 歵WT! MhZB jM}ZȖq'.;!a׋ǂ7)j&hU''a,JC  lEGХ|,Zʤɸy-o/wCCksvzVmu!F7ћ&ZVttRu%Dt6θ\g‹ZSz7Po"t:7ϞbrXl+B@,Jֲ_3}v weM~7vY% ; v1!0O\BUCGܺgf kC=o)ukgfCzH);M @s8(|J*|Jr( 85XF+ Gؕ@-Z®HO*%n!ݴ:|S݁5ueGĥَ|*;HK_Jcc#'Y&]Q!$OQfj!#䜞Ig 2GS5M*Qd@^:) ģ#ѷϢ}|rr*4>=NWBD* & "[t ScyipKaxHz96+oޓY\35mV7`yv峋Zd:ji+M iz˷WaH;G7)D1/YT_r誻IF. B(uA\gW%x^_]Z)~Ra*:ȤPY^ExBx$v"dwrOϠߒ(!;W!*% #JG@ap3EsH^-v@}4KcALDC6Ǵea\k]XTF݁O0Bm=QBdӴ,k,|SkK[h13nup_4i^P0f>1W.h *Q^tFR@ME3EX3 ];W-(~R!Ss~SJ) ]]39|hI k1-syO9q$>Μ3DBj贮'msnWKX׭ VαE/Y2gEA?v w9֩]/PX_/E@L]Il*4YG"^VB)dsQfG( y\jAh^DBl'oCIŏ;- )CΫO-s^Jb$9OW&]d]š2/Ay|xu5^zo"*+>VfOpRfpsL3c^R:P xs\Vĕoib?t]sB@]{XJ/Ha"c@ Jd dG C#tzF&4~f.%%W9q{}z'k+R֖Gh==)kP 6U01B^*.BpyLrϸtϡ- DMkIz/i~=\ӭΑ?~]3O/&{t;XkJJ/k}.y L=9PnT+E9a%UypEf>u~! i"}*l ty&c:ڒSuVۿ5ac~ӧ̳]#THn.KQyQ@Cb2Ԓ*oR+Oq&þuM0J=DRSRJ6um_K06kcIR5٧0 -)η)3藾X 1%VHQPEkoɽ[_*k&I YExu@F-G줯cY$OHWAKĺ;L_Y,J9*a IެidT)7|9b#&~ 3䃠XxID5; *k&#E}u[#SQrɭ)72,xЯl׍ @5B<]9EJ7IJ+3Q3gxSP8)O*|Mԧ+xSIemH+X5{e),nKw pMmr)SX-3 ;aOb~l߲`*sZ[N%!>|+$9\Yoĥw_JLn=ckN!y`a 4c'UJL]o X- .O;U:m) 7huNvgn6leinWϭSf*VW糮~Ωg;~j3ɵTGJNe xEL6n\)]BA(y ^-h궙%_)m;*%և>Ce'D'ݨ zCHI)~E""uGsm?\c$JIFMm=8O z3vRXQJHM`?%G ^S⸝nKRu#TpJ2@-BD8Av2{k._04D2Ƒg0ܮ SSSh?2E+<%۩%pLf5I9z V>gZV5Q|2ʺ>ʋh5SI1K1Ъk֍@U$k2HK5e@xuoU|;]3ɻUVb IE%'U]щy*2NuFSiƇdOQD(ga\hb{}ZN͜3}^f/YNڞ ;T],U'A:v|xն˛-|S\YƉE)*-|~HdeBZV)$јU)@\[N 21PA|(0\sLʌ(ջIcd)U x✾}շ 3.z>BfSt?毺n)O[pZy`wf +"Ƭ= c1(PpZ*@@Zs0.&w^)^^fkZȷ1Ԡ G Tcxy9JǠGClAfTl˜Lڄ̵m3 jC̫ =~-h|P9;jh|$ƤJωti~VD=>:Q]fzVڼB.S,$,ViTL:qs\Y,vYCRZv,;kdc8GotS:K{ N_SQdmfQ4Īݳ"1AF"6K:>4bl: bUI|4P6҇!T~)(R٥-%WZקn鋪ZB7 X)axdLP!B-D!/[ %`_be7DAb1 [I9Ar©VTI `{ۭ:$/sBu֑Zth8tQ=R`#a>%ʘ1mf\z4%Ia(B^,jLRRq2+!&O±Wz,YG- (x:h[U4Ɓ- zD RdešY3\-BsJ.1uGa45Rz],mKiqCh~Y;rThx#!j1mOW>ⵜ1j56^RDQfrˊp в'8iWEѮ'ʕfҥh2l[)k]NZq\]cWϭZߕhf֒?kdu2.Q^R@1 wR\&am~P9`"RUvDʵ,*rKbd}=@li%3P37֔_j]x`WotZvB^/{ꈐFƮ0HЗ|X.)fڭ&IP ݋Vܸ†x/'eZ+xtNS\|P+T|fS6VFh^{.\[fZ,h(?&&hrn*ގImM >EQm)o]~ x-$jV=I0cROgH< S%&E.+LzDljWs3lɻ14!گW"[HgtT!dѪ(C{;;J*ޤUI/=%n0 P"z$*~HJ)BPA!$T$ջ ñ/3ĹʼnbE:>mU8TxR!LԼT֥}iH4H\U(: >q KABXmI&!Pe])|PAá: dN!ujBJiq+Y$t汉"/)Hf_:^ ToJLY-;jBeLzYEbCUFcU=%iK^\jdT𐗢6PtCLG6RG""eouR2(BZRB!EP&!e`)RR3} R!MLR6tp9YpQuHx+넄"_~-ST3 1+pBi^o᫩Fu )\)jFnXacl~ ]mTCxXEe\f N\D=Y'{=rE.G!'[E)+ qAN^S"Bc9'LPKmUwd, j%o!4̽S.D6VIpӞz|<œLCYF]E*pT^BgW3*9^`RC|D4f@Bс9#i&I&1Q6$6[i#--'cY>|2U/Q=A1i(Pu=%vQYe^o"S(ahPˣAI*5g*͕8ϘCWyO\Pыo/ѧB9)2ƿA3-rznQY1oGaUIF}UȤ˘ϩi)X &{UkE :qͱF4fha?!4du_0B1ȧo' :By3|mRtzlE)FV]Gh֍UWdD0i1bI1:f6JsgXޫh}A]NR8UBn!Q]G9Qs á?6l6Dʢ$JdCDW5Ϊ*lTEU_dd=苵qA@v! \9+zuES-*# 9])ouR>ZYdJIn@)*žϧ)TJ%DGV nFOT[cNW??Ҡ_XG^ļvI̮<);tSe);~oCv׬Ud";I&U"[yM8 66H!fsfXP3rW$*]o`0%+kzQ2<͇M䞻פȽķC+ $t9+|טg=E9Taq~{7J1NRĝJ!'U, {e+2$[eM&VJjCsMU}ֿ>\0uO,E( y}eFi8ێ8RvE&hh"=6&Ⱦ= s]&OV.P-W:6 ᫇B0P8?ΒE1i';s(X4H_P5aMQk b>m-o;ǵ[6 WjeLje~HjT^Fc(I=QO1<͓1,b uN]^"8O>#yt'R+)ɡcV9k *5 ((IeQjhNUE)TEmx׶i.n^:D'kfWi-;NTjvἧ?}cP-OrL #XE%)ݪ4 H. Uڶ |?QƟX%r\5ail4®uȨWz -M RM{D:!9OM1N;_%8z 3SL թWm 93tD ]ۛM} LİQlW;79 Q$8cX+%%U};X4 :V |>9޼Qp.K"~' O."˩uX)&Gk2z>|Am؞"=fOT׶RgĜ2OE{2۹/!KA*=b4IT}ExjQk{˼<5j IaʫY,o]*0{+φ=AWTUęRF<ύR#{lz$ UGZ!&ĬMna'ʡ:Mj?&-I`IY_`*^C\ЕY3(lMߖL$XE 䳙u0ߧߪuu!KLآѝEM,)C70{t1 "ĢTuKn1vwq^TmkBzJ֨+\AQ't%Ws:V?eXxPv12ꂌɿUN iTRS*L"DC%O1Z< (MI)ht5"?B"&'F3Ann7W&!HE:3Jc4øl}8vnuy*SM/%#v:&|0'Zdb nwF=f0yAKMVUc7{Yca_Nŭ@qBSDz\+=' Qa6E*F%) pƹji/wfVQF$| CmGɩ\v#bJJB{ iKvFsbV_ůz(i76PQPS)*^:/ňY${lCehT'XVu&_V,"Y#w}tCo:BQNҒIlGT c*%y7Q@pk\ӤQnm&45״t2wmur-n_nsİkINZ^a:͡9""y}>yW{ov 8831wX1F%(`z'#x<pj#Snrv9~$$78]fB"PSK1( Ѯ0Ƭ\~7Lez#Va^)Gn!٤M[z*iq #g} q^X.e{@qW4fH)Dn̊=tHpEqU(Q])%`2qJ\]* 1w \£߻2=pWxjsfVwiЉևV\ *=C[gaH`8;"$2HsԙUc\l'<ٚG0JuwD4/45,+ɢ%!C ”0HroyE1dD» BU SXZN%~` qKQЮ+ɫ뗧s(2Etbo[8TPCF"(g6=sa#wki#(wRdzQQw:i°۬5ߨքtaItԧF"MOkCSQVQI þ}7:ʇ*CJK-y~>eAznjdQ+G3XIMZA`QŠEҨ)7Ї/*s+k+=>X$,埲0Zx`XH .It2<-b(R" ]<\}J^}>7wcԳ(0-@ /m3e.FO,Klqc@τsDa0}WE^YVB *ӫeg,NA=tb>a\|M%kCtd{_RU,>7"a=2B˲ Ww<ԦQ+8 &q]]ٴZ V!"}f%!  ,ʤ͢q"uӴ& }* =U%D m}F Y-ĽAA9m:s4".ޥI{}6q!opQ;mME5^0H:`\R:bkш1fӡ, PW+rdUW-0CJg6T?_ ;m[M:ocl%Ix6%K6 ;V>:ot~10(8m;!~j4i9lqr֮}.ݺy>ߍ0t4E7;$ՅS_5>tPbB s^[2[BoZ5[[Z9*pޒFaSq9.0%@Q[Lx!4hrc"mF͕c]W$߻ݲUgm|]CX}xn[QFdSx$ӶXWֽʣv7ĽWW&$5*2i©[[uve|eU_oM9[w9y?wk!'Ԃ>EwoYVu!#lQW\_wrkr+OmqJ2%JnGu) "c$SQpEz l㽳y&<е8j-d ?"P;f2<."|ꔋ]Iٻ$ˡ\#йvf]i!Qa(XF[ jyp-> D>c!zt/vu8Mv~?u)$@OQ!Aͤ&_TTU 1aLg!umKJx2,Mq,r$,~T]X{uCHeQJ %Tjh_D ԭaԡaݽ.u>h0?ʦܒwfH/&N+F좔!%3]#TF*TFzpdFDd9IAۉ)]ŃobDݡv>v֮-b:^q^ x|l{}5ZWn4E(-J^f ER݇YkZՅ]n|Od@C-itR*}G=7ԃK䘮,!DؙR)[`-#HPa~n{!.y|ɍ얣\3B"|\Fw+Zd!1>lilO5%Q!ߎA\<1DOs~N)1:[W34}Z&5^(qM#="XClkhX׭C; GY},d`]q( Ϻ ^;q?-9> ^L /p2t7¹D>Odtw_1Ԟj=yl_^S(lnuEM> ̯6>܂̱*آ='>M*߽"QLBpmoݤ5vpi|hU'ƾ׾-+zdFͳ_i?vۤs NSHKi<)`j$ݺn"ҿ)pBҹ}ieT}K5zjֹjܾˠkaaP"|5J="ǢGf!`SԷiI-`WťLQ}2jP|2M^Y#}*4&zqʡ:Qa6N4f__L5O漢ҫ:TӆYFQEs0ԧ'-rؠ 3ns, 00g(œyGO Iǘ{=ŧ.`kBf`|Nb8kԂDn?Ȓgm d~U %Z)h~=>J>f!JZI&:cRs3Jo+j:TXhomT]M*5Z6pRHm$Y]]gIE"פ9g{F=,ޡ2FRn͛3*[n]Q(5I k|v%$) I I+f'<3|~Vc8&/L{n!;k[yM@r芎 o5B`ZihCvTfeJIYt}>fyOlcLJRwG2_'&Uz5J1CjJqȼ-jΦ4d6%Pr#3Kqs[B89v%OxNTnwX^7g"?G-_]a\x0Y9U2T_Y*.0w2ǩonfۡ(8A15nBh1P_S*Dw?1Ogt\QqG8wMHCD=e7ފl\ۭ2bO ɚp CG)++1ڏlm:JY@Y\np_^ Ѯ4ˠA=GmBz tYp/ȗ1j5RRK}c2)'LT Hw˙.GĊe~]kmdr,J[;c4^jN1XX22NtmkFGÅ>?3>jk;ͭg؝g?mx^~Yqˋ]k_sy-UT'f”yI('Mu(ƭ*RDYiy{ U~uZ;zȶ=f[CEyd=%$ț1)Kb4Rx|Ep^6\G%#U7̭$,b]۵Ke93cIXN7|,۟4?rR+hᲯr-{LU28'/$REWrBQ״L>OI&CmٙTNo16IP!Ѭs ֨D p" :,ޯ^)y^iӐ8,}W.&CnO~{pLu }~P nsU$` x-~Z 魞/.tgdnڽ _ ;_4.B'*i?k?^E:5qP79U>bq^q&mU]ۭ7X1SD5-d;|sȋ tɜc=o(5Kl _tHNq:4ܪ^fc0mFE5\"96pPM:n }A>78m`6nA(H+(2B>>Oe3'm( jǣץ-M0n:%ʷ#HJp?EvH8_2}u9Yg Zt׶oR*);jׅO+jIK7>T5 :{ 7تfiR^* 7P늦|-l_;g4~ ;HF؅ҞLl/L ^I*.13!*;SeuKŊaZW|zWWükm.X`6N%si)ӼFU)?ji^)\eߥهnb喜bRK-! )m ueks:Uv3jB|EƦaXeVJUy3"| $kK&ϽW_Q8rL\yRtΟ񪦮lEkHvJZ]Ӟ\9JI4՟ =,4ޱE/QLk} z ȧ1̢Ʒo-`μ栖egkK]Cu$H!7HUe>r9^% NmCB?& ~ | 0'l="ˇHApa?oeҺ{VMd5Mn+iexwʴۗM'OY*m > 6b&K)ZB>$q>#PÓ{m*+'L{,}",swT:Mشmh;]cuC/\-k4>F%t;!/9TD7E)"&ԽbxK:_Y; Q<6豺xXWVci;*԰h~]T,]B !MUeM Z[NiV}eb s"D  @]ET\r.][9UγpF1vi1jחXoFy[4:=Κ~ (.׿t'=iҸw/8zvw,mMо:3 R)aI t[;"h :6EVҗe֜K*&!ͳiɭvӻ-޴oȇE4`[3=u-߻H  LT\DKܰ.6,'La-='JwKͩZk(\|^SJ)o`)K2^kYO>QWՖ9)ı6QݺF"i/kŵNY,L2Ʈ7'&yMaрJ!%1XZ MWAEQ%!WY%V&\: T%-UkuZ~(Jdr~gUQ dz%zvoe*A¿Kf۝y2iگewU.rȡdQ.Q4*Vz7^woʯ0ZCdK5EwLnmKU]M*ªϖ:?JsU9wUhɺ٘^]W0nÊgMzἲJ! )kUn/]_Euߜ^AgB OĬJ UjΪ!-81h7d굶X3k$o]߯~i7eԡ%O6)|RFO3qز m;VZ#Z}^>$Vu+e/.2Vv u8Q--U]]>ʓL 'Ĵt*i ~4$BH/~tQ1sHVmWԧWI~3 +P:k/(Ԋ8%4e%&׫cX3*d \8Ax@ HX˦j)'A!BƟ*J6֚6U+ikɥM4"?\zEԥrUZպ֋{FªuOgm^[NXdVثhajI`ש3Q0KT*Gx9a[o`]#U=X7$ ʥA«KZlZKvgL}kf D,fHĸ(ִ0S@Q^ԡ& eXT)KubUKel-,;Wb૜"P4Tc_;dfC6 $r]׮e2SϞ7TXa~, _wM%897^kfʮB`%(tG'j_=|B>lӪR.XP|=Qe YQnOa9 ͞.ĆGҐBhcȿ.Cۑ PK2x %Ljfŕ椄%_K#EI/90q.G2LL&Ī>H(:uղzퟭ>ܒ>w[wDטi/}sڕh[UGC_ط~/_FY[r%Sa38x)bЗD|ɉvf gJcQ"wo aʱaI*84 ͚B.zSь*J'i<h[ösop~+h^ykw^ZUvrV%$_#"OuM1y[\ǘܭnK*)EmR)v+=F)ui}qS0hKk ?aϱLc}KSikﴶSOո}~TfSaw:5CiѴ;C: %s|};R>|2ԺΓG9mOmLz +hVŖ}]WD0 Tr]Jd3eӿ%6'},NIGdxlKCe 㴭c`2_|YE'~.U1;%=ͬFEds[tB޾VG$iU1x~%*&IUu9 o^+*(O2.4q*]yApU-)L0dR_2 @Ü[;u?ZVlJY,u^eE 0(7z,$^W}5w' j>mZK(udM)t oIDOSE^J!+lԧ psEUm+cE,ʧU\Hd2IGYgMN b|@n[G(Od5wE5+JVmN}bNHLhZe**j |xꋿ_Ud5'KZ{ =x妛6[+`y4"5)kgIQQ]nٺ;YwQw^+}.Df0~aodk,Xe/?>wXDꚶi6yCf+:&7IHN ]5iq.}tw,mmצ!_UVu<}gͲ(׬mrl!PZf/Z,9)c&&ґ]0ۺF>4sRQO&ˎ<=|},JG_pH•FNmQKc 0ynS7PUYٔ#ry)JW®UxS$hjc^p[uf+OמvTN``V{eTeis]7;V:NL,V7c!YȖKn$|`ġ='"n)u0?[Ql)Zt1 ^ Ub%?yx{E{ ¤oԝ(;i%.q)*!6Oxx^9"|?\ QHF02_Fȼb76}2{ـ#zE$!T흤f_FY[P9X{3 Vپ+0 )Txi\Zڰԟ,'I‚I(Dv\Ro *J^Ksnlji*۪fK؆ٳw&;b~pZ]mRkYGXVw)@mU˻%akMpWUFoT{+:1w c42UUrjv6mçbx]N y1d9dY=lϹ !ʡh{t%9/ۈM;@rL(j=Z4)p뷑iᡥUFIAK%g]Q tS#VdCEYX^*|SeSKw6mཇL5 _RyߢN_W_9K_כ_1 XeaE"Z8O9 0uѬ#r]BP?/L}H0+i\kJJE]j%BhWDaDeFHGZ6/4Wf*/}wua +(T9ϺHB ] c+9AP`̒́Q3UKOgrVV* = vmP&'ٴ&MPo!X ܣ҉!AJR$̉1TFzMyFQu;:֥}ejW0<+ T#Syum }ܵեbp#R S *L"䰳/epHn AhHw')QMB^XHݠlr>(^x, m1zQ _vNxҎvQI &OaGaLpHLmnh@Bq?>梓KP1 [E% 4M2K$Zfgn6{AvĬl(oEd"LbDea6]CG!۵8 e砃i _Uy&2ptG|6*csK X/%g,Prd*)cgx7Ef3K,W4 C0J.ab&ܷW"eRUddm-*l R!XF) 5(̴cfիl&.,=? aqp ϩg74M<3ݨkj hݶqdLLkP,0']xRe|uUI)/]B;w.,!ק`G`oʟiŧzrnc~w䗛z)x,~d<²,Kr Mi/0m?6TE'ڊ*7(Ue#qk"V1u7NqqVv5خ=/d\D2^R!p@h4{-"9J[q\hh21̊*bٔBcUe_qZSxCWk)b#m͵76A6.5ʿ 2[$|N8/ػeK,7H7\d®㵰toB|$F좔%zM!W{2nly 筬R=um\UuzЧTW1뭮c07vu2~Mv=HMs- 8G9んbhԗ'>^,D#dlbUޛ$GOcz䞼x O1(u%+$]qNvd3BvHM8l 3$($[ #n`R%z+QHDTFKTPRHdȹH mpU34,P(8s^"*_Ž)km5qžmD!%ڒ2{#§;0@.zgJll*nDkGB+o_\:rt]#31:iƼ=Jb{^St.k$)Yz{Kz XLK]7~(.)S]tQ8{@+I xCnJIu=E^lpdt1w=$g+5m%GNPiN+ڠT J[䣄:Zʻni$ٜui<`W8~7)*i5kvׅEgUg'$!.j9QtI- y_1gWK"-l}L+-%gmz&ZX-m;Ȼқ$2 !b:KDTQgz체=Q؇)ZN_R-Jm*[]6E8[Q%~f;Se&}BPljbR rX@O3.Skb&MM3)uQ]b^3C~N ,5@SigߩC kK"/LY&5M:r X͂ ✝QTI:iZ%emVmY~fš\XG}ͻ¦7)EAX  r9V5FV6# mh2~ su@IӵOuL7ㄍDrXcP0lz M2v6QҾ\QFR;㟸RT"X )6`lbYzɧUWIT]T CTXVIvLʐdMElM0!KIOyJmC խF'U#}j:z cuGУȬchףPVGUdI8˳gE^^ɽy ѩyĚ; !wۨ #CRD+* N~HԲ̩ tBaFz,UR[ GN2f%ZY}>Fzf]Y~oq2jCif_np{jTtޮ[sD}[JAT3R̿d++PrJЕD݅RӗX)kQR.} Q+L?EW媪 Bp c.w~GnKO78]HT9Ob>U\6f)򤱫b4Ă0/~Ujtv]_]8M#. =t@F8 QMӔKԇnEr]beщ6F6!_iFUN;|/&]qќDD^AQ[_h*w?غq jyKՓ $՚ez,%w׉)9UOKL}ro- _f_YdFr1#c [D-a'Zi㳀PGS:Z{xi hdU?A ONׇ# XN4N[|\r Q,ҺwͰLaehԺЬoPead~Us ry a$ccc܌2+kl9Ɨ #P<7aOܦ-;X׷{ÒfF XdMI(9Rkf(Ӆ+T.QY{nߪ|8 ġ9 \F`5S{ Dua !Exh a)V֍=bRnUZSCO`|Nu.=r} 6}lK % ;&A#ٷ}Ie{*f呠:LR5FĔ)4RYwg+rKQCt Q~pq>$}i^qWGq[+XgyMA}U&I~5UQ8f쒨 ѼjS.q$qQQ^>ݎ#:Qu&&妺zS~JYT*z :]#5 e90(! UpRؽM,Қoؗ8Y-U:ԡHc 44D8/zRĦ oVR_  ϜUꦖ CGX>u=^k-ɫM!O) /\RUT?o~NpYy^![SpҧZSVU*a?GqYfкn %8g"F9-QNvƯTzߝ@mC5ń!%/X"Zu4Xw"в+$ _COf:c}ke/ISnS7-ߤAOOtK DՍOUÒG+ 8K8Œ̎'Cw3.-N^Fwr=w*ޡo_>1h9PQA=h92FL,K;W :mO DL)a;\p3xe/N*H2'1g"̬^͕ܹ)jUB`[&F՝)lSn.I9ˮ ƼJUzʵuS8ةcHUO1kͩ6-uq.RekG:mv{Rc#g[^gQ<-yK|p%զ7mkJan BD5z&7/U/![E~hP2HxyI`y Sr K<@Vf י$9Y5ruɲ2A^}JKCqnϚ$"Hs?ѩi.E$dgK(ߐhȥ#wp)Gp|\q !{cvE% xpJ-*`}T^S"ze3fٔgDV]Z,E$DFM'tѽWK_Zq/O_QʽԶϗ9:zbfh*^?Qd_I!ue--è67ɠagFvCĜ}3ȑBO1k}$㝽oeٳbv9ƬrS&*5.$$ z ü934F׀o9H(gˤ++y8%Z +\@]`=pq eTϋ;2^w|xC#Э3N1ه¦+ZcDHY& I&~K+=ExcquMn"ĖQzY\=|5$QR!=dE,D7 "Z٣QJy kj[ /c~FҲ gUxR^R/=E)" EE@s̚Ũ;)!TJTB)*F< -.2J\4Ӏ )9/2-ⴒn@~e~a5e>.&Q zԻ<+^z0 b@%q[(Q>)Nn-JP ;pK3ܗ(A"޴NLSK5A>D*@(߬/VjɺLD+mv9')XQ5\n_;ֵ@uca"rC:hA~KY_i".mW=8Yv@RkcEԿK 9vAk>WOqUNC @!ҏ5gjO,2X8,m fk`y)!PN跍C)8݂¢ƒ4F#uo-&p.P@>Bq:=yi@h/Gu{$$)퉉}@i؊.g"'km<r 9j"Pn#(*e I :Y6Vm6s˱XՕ,Lb1ksx1I6 O`¦*(IwZ&!\Sʺ!5L jxP\rUYSwu* Y]UY]lQ&WH5৶4z UH\TڅcThKRz%f%.(IRs~ ?PHٿPm544|IZ3\q?F`?e-m[yˡWsSQ>vP.J/!oWS)|WM>fM kaI)WXt4l-bUv,Mה?5D='4֩kfOTG%Ny8u AO>5}F1QDtJTNVYֱ=Uevؑop_7إ潷EXq;~X9AenNEbY]d0ቬ|O0/={z?:'vldǩ p^G%g0?etK9g63m5(= }" yH3FK <r ƺ,S}}6<dןCOѺ\qf0цp?Ak0QRo+'u`G+5V3LǤ9ρm}::s[ tFFscX7UX.kM*00+*\砖UnuQMR؃ !]yzo,ƸPW+dUQ}P K*QltMO^%9=8BV0(fܰn|+{W!>q*rt.촫 Mg~/jfU%R[GꙸI?8[ȡzΙ h!"a=,-{mX7 0_n! tK~}QEm?f'scx֌\Z#c=:LR2>K F*#2L d} WWMx@?~Z .!7mYQ=[9mF]%~VĒrՓ߉(e^I,@f %:G*nx3](O|F3N9۾kߵlv+ӻZp sQ;y Se؍5it%) &%꒬Zא>+]RF(|3'x|FbDKVSf_;;LJ{+jz(HuqvHiNL|ª)o~^z5@J;7áxͳ'R_=q_RId5?\\"ܭ{(> ɥvbQvjŭ'JW] ~w3V[RM|Esf`4e|OVP-ՔCzSuRc֔cn>a&q)7U}_#[5)OJ=4I5N9wj﶐w|WbݜYdSL<;F [zHmH!36amSG`;N"QlXHK9eLi&cS zOKw}G:4bXX<7J "81S_.B[4pynImzR7Aw 7tsc '&9&&x]r inLL+1q ێx fϣ*aܵ-y^"Uvy=rhTljcX+i?9{QW#mY[mU'C"5_Rcb]l$ W]7-w(JFE5p/T}BT'geZ:͢t z"겜.KĪ?E@ ^+.) 1+O)&3v1UFHBV.*&LkJn[D6U"-B`'# %Q I^+g+nZ2}d, :JI[;=KmIyʪ6qNMn8 !Jc.+tqzAซbx)1M'-ڸ2N+iz!\6;4w;|ljO^r^XUL y|uG,1FdJB`W7'LV )B50 Wb4"ߋ=;}NDJ># rlvHb["c~R6׻\Jɝ &_0{کVCHh"N0g] 0@]l u~|Lq!\ la.gπ|1Z'--!;ڌM<kPw=+\|X?q˅R;Fe@ꉁ _aDtP_csy^ PD>5 ͈Wt;Ƴ8jɇIQ 4֥䓌xf'Lc G_#HeUceU!L9M/CPL:EpMqTҍ9:O7b0=ˡ!cs%% +K@I3qs+^J H DX_)kJݧgB?|J5p%% eor_Fn!G,)šA%y54^#o.ISuMa/a]ފyr~5'ĒmІZ==ٳ(ER &íXVſ4wGl#%sa?\,wd%6ZT?~.L ?qj ,i ,6z* 3ƖzoNr| s>~ϭHZܗ\X%AXcQ_Khj6&#VPYJ닞'EzQ@t}&$e X CV[&o&NJ$<ʧH/'ţѤkҧIjò]UNyrW;NW azɗ}Ja]5Hsܸx!ݴ ZTg <+e0(hRMRpU棭I`K]#9\E;byeaCV`-L klHta\L͟lRj&*Ҋʖ=EQSGZ:-gu!) [ֲ_cF#ufzFW2/)dр54-(eeTzMVaZQS,|D#2eOViGR'@EmKH+K]EDRPT 2YkC*MҵEGw7UydJTV9 g|(,HKk3HUVS+q[grӴ^nkpsUsy^{O]PV"U$ 5HL膡=r-nUa"g#=o&RKy>gz5aW_uQ_n-șû`u?Rc(a\ (>s7gǵS#v,s7%1;#O+=M*m+-C.40g >CZB&\yO#8G^08"ueAz;bV&u2n),q+: c Œyws# Z7#}[V7>R3Cퟚ*xߙQ,3S^mbY>1༳}kȉՙ|Cr_RL`#i(>HctoBG|WzW;0%vMZ9 [rW\˚)RU*ZZޫpMazdlN+LPsML>K>/ K`e&rkq *Oha7Ҋھyx(|sL疣)LKt`HËuhuRF;YJhÛh @%ͫڥafTq A8&NwŪQKD_h0kiFHѴ7Dh z8#*/=غ>HuA,t`V2TuХ@f}V1~Ϟh0ruЁ{_[[ ye$YH">3ڒI-3jBa|svYv/"z;=73zzȹ2?4F}&8 ʅu/Z=o1YQP#nu&5(KVd\9l{])X6 ^[MxoSA}A{CYߪk"l.lv>_&(\exv fHױ3zWc2йM½2lאj!=Q%!q<4-7S cߙ&Sm Vr,r Y?7ޙ[e4>xU.+Xէ2"t9O;TAeuw|mG5mg TϜ]cE*ź>}l1R)H@hcP⊙3+rkF"]9;L.8<b^T)XФߛu7u w;K|jM2-wgj*͙5Lun I"w."bGD6YVSQvS%(Ǿҷ @US[l>ijЙ2n4%pЙ: aP[BIcqx_b>KE]uȿ[搻)n cS%ۧqhHVFaAJh\=U QTٝzp!v͂ƈnJ)/_A<pllbv, wř\,*.ZӲR(Ō~/D,mz >F}Bal"l8 iE^3ҷ ISmw5j0' Pj|}-1_rmS%f8 z\~E0XNh ̦ 9j#i#2Mqr<Ď%/4!1a,#2:~dB)<QZB_f#HߌѝR(5vE?(a!-|̫_(9ϵhq6Z{fږ$r"8&0j!FIc;U۞y Ԓ99HB1?i(RݱAan\Ԝ}u]v%Ѯȑwݛ9wWC* ޝ_喰)?TM8nTĊzCz8]5*.껇RK6t(as)ӭ:T!n6:H!z/(Ct԰FŒ>^4\S XO W muA[luWWrٰd6hs jME|!|}ʙP76*N +K¬98S]e*IFFCB jI^Sȴd!MVģp{*CCG) *ԍB^՛o ET[{,-IM7ReI~^S#q<'Xe fJdR\@gJp37$(%R Q-q?{4`\PuG<@a xZUeARZ-n kUzr?^󘔻XЯznǚO)ED$Q. -mmcv~@&T Yp 9.ܽ$SQDj8nU$ZЎXV=uf%*5 V>€yf`R(*pQ8H*4&ݳ-]Ƕ**o[KB8-NSSאuVeopW{ .HGr yLj߹M14" gdեe=sZ5JSxeNU֠ =4nr?iFM&qvg@?żXԥeFr Z~E=IDq© 7UgM6 94f4ABsY4R ,Z$UXfKv%<2#qkx2!4>ruNٯ˩5l|Z'kpYz7aQP56k:=V/+g9}('~ YLS% M87zH.1)z"}CXY(ݹfWEEb5zNcFae_fG1$\Gns3<N\u6ŁUg GmcGCaOaii: #N+u[͖(iG83]=us=lÚgf$QVL2ǀ,ϒ##`bL\[֝qAP R(/ڏFDf#kJBN+CS H+#L6uY@Aβ~96+B ^xFc -šMROE1ubע;,WY{GsvW~-!HFgꚳ3cElA6Kpzy3*I\jj&&P%Age%Ob_>i]VYSWݬ*Rѣad-\7;!7RJrMi?p9fAWeqyQB'iVm%ZymzڽW吞d"Yy2ᖹ u=2 O"2ƢXK+pOcnN>0-?Jj2),Dh}#KXi9Ou-LMh$A/n1/y)0C[ -iݮ!xΏ,zF7 g^/VqӼHbE77r\'nj<[,X_!q7,D\rMV̦3q΀;HXi .50QUy""e$}?&>F-BLشCarԌ4#9\];)vWr+yiW*L)nF_Mհvx,eA.t]eNg}I#?jm]`QY+bfEx%V7:<u:ڡhc;$wuqT$0ƃҐY(u.=,,8R( cNe}"]}> 24 hF#bOr >v# }Ko6ezm_?Fjg[o^i<|E%ʒM(GiV'Q  XKԧ.dFՙ{(A{2.  5x+ |,#jC #RE;"SŬr/u { se+ޣeŴɳзQF!0Wvċ\&TToW񱏡_n{κK굸ȷ5||3~e\=:Hpg,WUz֑9JؾޞbQw!sԩ(?5b>{7+e }dkjϣNRR n59a_9wvw]QsjGVJ+|A+Ӥ7Jrd=!}IwDҰo3Ƈuߺ/O)WǸɕ2 V 5oa)U%uwBJW'ȸIſ`Ut'WT;γO pWa [oeVݧZVfA\[։JVsVqK- D?ls(c$5Jyi/yÉD,N n$6ĪNP`g6Y,WmGhae Re $[0-2m 9GGDucwP<ђl͒[c^ζ R^%XOU^֔紫#뭭ZK( 09SI5[&Wyc-Gl;x$tmo|L&00*H tX8xy P{t'3O%WNyBAOr0c̟Q/d|蟂R'=XS`?@ɖv՝5s;/#;C῰K}. j6z :+Бae_B;8LGlPvO8yGb ?ڮcp*(GOqOԦNJLȊ\51 5'xjwz!cLexGr=mOS=\19ƫ9Q4[aQHGy5.X?;r[EN*mORل An6.;žssш;ƋV|'SzQ39/< BH7`1R͹=Q1T[_+MM0D-SN=p{pb!Ї A2sQP!IEK`&rRnFJ7 @F]ZcyDgesgcpF l} g?ȓ@"SwXBg]1h;;)]kcb|GBE#2/aVgkI+> ~'`B9u bݧ~b NbL呲߫& wbݜN*﫵V!0:6OR)7Kce,r8⁧ArrH|wЉס,ڌl3 Q뾋6z_,Om?6Yrɱ5x2eiizoJ-}ɵԺLpL=UVŞx:sNgҽZGK-j{ r_Up,uik+BA8=? =mKh>GYuCdCZĿ) XU #2OwC;s1()7| fۅ-j=Xa,*&(~ik1qo1u|a^zܩG^(*t a!$J,byu_5A9T?߭)&I^U=ciQ.-C*%]NZE-P#dL1,a<lSl~ae߇C;c#ݗk㾃EpY͚S n{rh0{r?`7,pE7뎘)rM} qYE8-DAH-P)ȉ> {b[UYU/7}Rh]K62 oݫpnL42n2!T^<%W?sxI myz6:ƃCv=(76M7M*gIVtƬimub ғCVқ2ComiWp}WpIlpҴ5z3ޫvh "EԿ3?~U=v]*7}uZ5l,Յ V^v HjQʆZ8floj:67hJ^!N&ZTא':dT*=JYd9cXcv>YNQdc^Atܻ[X YM-lb[-Tu Zuq|L+Fk )/%V5叡b~teFa_s Y9,2n1ٶt9pP5R)&-婿E@@X/D6 #C]47-c8*;7_]'ϔYK}'9a4Wf_ b:x.͖5 p`wMc[QIu= _{Mtgmߛz1C;hidچ&dў">ki,MkZjKy?4|c-"rJhXۨ;Gr T??؝ܲ9vjŨcAɖ0 RN jn`WS)@3]Q\ \QUnӚm~!1 -Y~[8p@u6E-h? 5ᇼ {Ȇ- kcR:Ѥ>I;?T{Hs`IF Qw.V$,I4ma{)*쌜$ջ;r|XONS@Wfp. Dnec[ᔩ%#]Ǥ0|r>V#@0ڻEX;q$ۺ+( #1iyޕ&гN3"{J\񫗙y}t8b/$WYP; A4~+tLw,桖@0"2I$u-p~{D,".հL.%t/Hᩕ3`,m!yJHAh/yk^oyNO@ܖh2$ODV?xj"«Pl} *97٢6 nQ8~2ljK5}K"~E0%l;Ja J]jXa+es髰 R2 =egHݮhbT9d9ţWmu孾'/onp3iDӶA{s$E&nP+jo]xGj ˶_)>׾ڼFڛ^v7q\[\Z7?vhi :?upYjak6beMgϰy$:..pW{z2iuXO9M`ш̺Ow eJ SE>_jwRzդ2bjx\QBSHA9$JcY4 3TXrmj|4_0ȬE(WPҔ*:M\w ]$DB!* }~spkLEyRM|2Q“ՔỶ]_ gbTNN[xǀ B5DeI6oOXn3I '7vO!2 7S5E_@U7-ALR[1W GĬSl-Z%SFw Π/G Y{]s~:F B(!!M`e0)øxopKP:}V/І[]rʥc&z,Fv$IPSnk-:64'3j4hP#FCTKiUTCWFte'P7%1T*ۿ1Ŵ]-.n[#PPB!%ӷSFb/V !$ d斴7X4G-SKtP" uꫝ{wXכy2-|N/y(v%]4b=>P0)c92Sz)/} '!}rN[-6+0? >uH۠$ߵӦUf > P{>0JOU1B{Ӯ>tPdbw=3khY4G[^R^ Yw¨Y߳ ]*=4:^EG'Ҍ{rauU.´G^Zr2Ũ Z^Sr? 5Cl`L0e S+g(dD( ‰xv_[4%7N̤=mkN + QR ġ(#kB4| #Hn 7Su%&  Oձ"^!,7Gh.'+zh/<*c!/X0=hyB#a]7-0Ɉ>{>mWWCsʗFXĽ~LOco!4cDyJTF @9tk?+m$;!n%<覰^e::r=$,vL۳//,)y ~lt]0[S4y`,3uF]$иs L_`%yIN@uD0t Æ19QRX&O1wXH:ddqӡdG $: \DTf\k@s[u^ΗMiT]єd󏪛v~؝`\smA`XO-bı 7cvCqDcYVhxOVŔPj-鑥AL'r(SGc1HKZ]׵vd]Д 'JL У{OiZsQ2%f#V|V67&Fx-Ҹ YW$(%:HowN.4oQO8-NhP=p Y NFIЏIv$+z&8gڞ^D`)H#q#LJR tiL`>_]p]cu.z菗dzⲠ=f\~mƐ>yH0|QIӗDljΙ7nݮnO_sgsj8ͧ+m'5X_Ɗvɾsvy)[]mF %p\#-jJ5#L]UMS&nyYsZvnb]M56̺QXY?<[ S̻uINS!C' X"ީʈ-2ҭ~i}j+<šO!5a}4"i"ݤa*:1i|q\n9͌}4iPT퇦"ߍY|QTzpTB.ifA)opbTkY0Vu(-J Z8Rk6e @|ROy" 7;K}2$ s+U M ɕРK6Rp̅QWX?pXnEcK^cPF{x$9&Xj詎\d߲3HO1P߾]ZQҌ6-qͲP]%u$ZҵJ^<]UT s]j" [EPdct*ג$w0 H+mO-+Vj;8yKySRD^TZvԗXO]:lk\l;k؆m5|K :_[wTn[׻a5rFLB֡#^:t@ mW_%YF7ͤF+Қo,h|-@S/a1֕Ba쎾\]2=ĝSi[pWU4g?Ei RPޣ 8ln'HP9 fIJν}c58ߔ0Vj*7s# g)tکl>`7H 7Jߟ>=co\}y|z9~FR|f\S@-#3?Qodj$ƈeS})8sv력ƍ NO`^9[q=-GE荞Qr.:i7SH>l6QT/2GB *jd3u3)ibyΒ:lm4Le.89\%웉Z?^;Tՙ'S|'QZz2X$F6q)K"i,9y;&w5eڤ(O/<Ŝf5%9o.m'*Q 1'<q8M|J<X!+"j6I0:q1LВ |İBcq0I];|20Z+` G"L8Ew8 #0 fF$O)#v Qipz}~Rdk܆yeQp2 G]3,,Ϣ.RrzJRrU|j8ja}9H ȵ+-^_H?Cn1Ը-F *L{@e])뭊`e0*m v(S,]) bF-r|EBU;Pʻڀoʒ*դUf%{I 0O" 0/Lf1fU_ROTYOM\F?uVUƽ^BV|TSrJ(mz'‰YzN~rK|Vyih;oZ4Kxj=3zIQlBhzWTγTԊRE򪲞R}0e XU#ARid}m`1qu _s4R?oUլwESf`RפSHf/=ezX7o陚Vpjl&sNCb )- 3\P&n1-8&IEAFpVnAZW}ׄrajyAfU7OC*V)[Xu 'wcϜg˰*⸎;T̤,4;R<ڐ_"U满&8\E:b:G;k( | dzm,!(ʯ*9N|%5ZpV5=XTӆYV[۶NgUvu?R1ES_cBbhQ IA5% wL;fܵMqF=$ZBAVKsiGߥHpFF-Fܾ?s:&"%U?L}BV,iAqE' 4 Gsn,.0-v5c*D3CLI?m_~ dOM6VWa8df3nM#;xO }OPskˢ&:=d}b+h7ְ"bl>|WD>̭nP:&MP^1ӨYcJX/%P)s06a]Ml* et}RPeQAF>[BlJ-3VYCTsx`Uְʾ(:|vM.|zE+ IJ^v, ib f6PzȎMG ^4c Y w-O<4>/Fdgr+H8G6>yֲ6{Z3ZEourBkN'?.1` akqAG)U(tۢ5$Qi_\i9D9_u..^\+C 7!kr^dF.k8#KՌ}~}e;YMȄX9(Nʥ;"zVYsx#$NP8f}$-]YkbK&GZBjY&Ż]{bAdV7ͻU}xW{KBԈHm4`}L)㚲j:ռ"Bg;pB;gheؑb Ӑ걪Vv׹E{6׋cel5D`\[}4 ɕ!BB9Đ1,w8Er/Gym; cfo-< iQtp*K&|%/IL%70Hj2:Iv:D3 *IkaśD<Ήo]W*MVSVXstguE:hb׹%H+צL$cIWyv,gꩻ.ISǪBSN(ȚZUg#;Ǿ޾Y7ַ~4ܿ#XE=U[_wj؝<$r5/I*zQS c;{%UgK#Vӹ>*ڸf.gҪ6aYtNSmBF>^&IOUרW_*|-r*Sf,Y+**nF(m#F;˸Lf$N~<.!#Cc@;)TC$5mۦ #%Uxj:JEgMJ.SJF>w+C2Vސdkl4A!?]5@I+ $Ī[['o*^QjSM JM3!k[DPuU @zJQbe=6q2\ΨJJzQ\{] 3k߻7Ho:ЯL7xW%aE=$ OqQ*jȄChټ >5 ɭ-Xk/u]߸iO!X]P&MpO*S]5gz2 :-*/j6~@Rn6'_k@z-oX_{@%!ky*WS( ?yuZV%ȭS-_fz v8Z_@&t*_ME v1ϵ{kHA/]fwoIl:h fUgZ|dg}7 k^ϵ&;U4 :YiW%{flSe6餾ٽ>2B)ur(S%߅qc՟]&4Eūwh*Tkq#$LWQJDE9X%ɡj 9spTY''fxIMUǤ8E;Lp[5Du;.I%  G"t: ZIQCZCDSimBNh%\b@%u'}16=qEP3p%vCo\0 Tmsr3tp( 8iVBOiaԧ ˗mQBp|bK2ˑ,E0_F!@3o9}aPз&jT8ɆJUMCq!B&7 G%Mӭ**u 6^ A6* 1?q Z o~N'"@3;33 :;Dfٹ#${bi9i]kk崋 :J2Oﴏ/t "VxY( zc6q޼Bߏ*˂+%h8NvM{;l/b:P1.EzZ3K=Ld 'Gw4k~}]">3KJ @puONCmKy'xU9/8a^RA~u^[D#V]7jw80}{u= 9e URtRӸs$%࠽D\WI *.5TCtJ&Z9gx{.VҮSE+^VQi QOjR%USlݗrKY5(/=OD-jbp@+RfA$1i`Mb/8)sn K c UT84M נgCzWJ 7k-McpբƳ1tV.̠VSAjlbUA@٭*.Cpߵθ?q+ ERVL"rԫ^G;3QDE%l*5R 9sV! ԫzdN9oWU]Em-!,*R*Kex;oylC.r%OV^eNNiwfhmvAK BM_Q?W-P(<A'>[QV$:{ sPcfӟO) !;VSYCi4٦7ED|QZЗR$UGķXaO|^+|C/ m -{kޞ9SXeJikQXAc F;D*k{p]}eF j4m?[!(K'ֆ4e9k|WNi88sڊ*ѭS0;‘~bzؠP,#n 4:lj_TTNuѸt.Ń.u"R~s7j:XW_@p juY߳Vބ5i_2b:0k'O{gBjv糆oQn{Xt%R N0m°/0fԳ簛.Gԙ2cgK i!r|$iJtK3)W65BgAqP7Kf9^qosh>6 rܳ945@nQrpօəGN'v=F _z|Øߋʒ](tFlK9C?)-3ڿvXԳ&3V/gO陿dYPCΜeL^2i;ׂ4_X> pr}"v$4.+ц οT,tAX-LbUDmX 5Ɵ {7e 5jT%GgQ, PFL:|=}\Afb$8 n|M c>X<\k!9v֙tIzta kkq!\ܖL r%A8/kȩfIBH? *,crI؎g- <1O/nup,Vqީנ>O͕lre9"\kw k/#4ul0eQ0coJ:l_7 cĽsgF2"دrXK( s0A!raq$?wA!m%LK\8%QRM B[ #JK'YNX+HJ E ̨*UsJ2F1:=UG<2RƟK"`$c/Ag+MLd[FST@)e1hlzOX\\TM縛Wmy,z^zMb8?@ wJIrht2~(pLf (ZbЏ7e鬍5 V.rZ%˫H@HP 30nXY؟X&;hG4!9:t<6 ^z2f'1Ǿ9J79q;SvD!޲&~BM8IոnӼ,‰!BӣF82%G4ВI27q$IyA|-cMjχ)2~c37D|v|Shh>q:E|G”ǜEdVg(5kˆbZdmmVf92ʔ:o,Hfo97U%sUG6Z}(ݟ3t]{-AI]nʶƱ}5Rc|xMrʡUٵu9/.cVšR/Y藱2{)46@3=G̦)΅Ā/jTa7k;e#fPm-Pt\WZ CPxz⟡ <&YM~BWUyz`KyWyjJ9t*7, -o x.q+и̃"/kٶKjʞ젖T=bA%5 |_yL2j%̤Μ@鬊?}8^k~~ T,e4 :l~%m@ LZ4QWS*[.IX1S6Rޠ="^WiXv8UD˜VlLIbYcυEyj̾{;cU8kJÄges^[Ie˶Kcx`T& ;Ek`ULce=q,ٶZteTSl@bP% B BIB˪*cR)Rk>ǚ7|k: kVL0I>"5~uU.S\iI&CH%rKUp~/_t5iAnTG ʉ{ȔD'N%oCWwCW=2 R֬Ҕ)NenNSbfoڏy- .X+Ltޭ?*yINب)F`X!ij"a YN-)H5k=&PR4BI6iQQ*tNJ| *qqxϱbrI`V#<1NB?oE2ɐoQ=m;ŸnXtDu2~ kOP.+:ֶT@2 ~'z:{y*_]÷6m`XF$e`ڷ1i~w[rj#Mo8 %M8/!آdM!cQjUu=,?EMHL3 ~ؿ~_s4ۓ+Y{z&0MǏ'(VY#5 %݉qlw!53/\O3?uճ~-A ~,>x9LRs-a3 |\OqSx4f~ m+^ Jm LF)|y?nQ%yьcMŰfō *G9UuG&Q H+MDBe-abqBV2P[늉/$`W/P~㷣ezjx<˕tFH+U R`GGYlvs%W_Vb%At7*av@f À4Z+*1R*AQWKO{mlY{u@禌 ]I,qf*{\Í6Wp (P#f> N7e^8,utcYVV&ɦ]i>T*ٟ~(Pʖlww |ϡLkWWWf/Yf/`@ku< Pϯq[?W(Pj7q70: BlX 3Wr4i;"Yޡ=e: 'p]wP ͎|ҏ5Ya<"%'썌FѝFO<z5[6$`9ȳ>m"h+:?aOo{@r[lгP}R71:ZE|L)kf֠>kN_[PE;NWءO%{臍ɱ_:ػ>ʓ\մ+eA_Ҏ:d«VMlIWWx=6Bc)ƭ~ހ+h^;0澀n%]s:Qlו5{23_DK%K/]nqL# yv՝}L:4A`,QDͲ , ;SY}>:{us=9UL0E(|2`v98gP"0! hڻxFcШsT9 jT˻\ 3nio y'Yjk{rz/GOl7Q'&&4A˙9:v競Og߈lt˭(?S|sz^Wkk. jޝۇ,aLCoa&E#M~MEǂu ֙#1nJ _G,w(- z -o6HtG!C7_ 3.ˍفؓKPs[1&Zg`xNIP@蹤QS[ϝ)U?Ϳ5ENτ$ˆ:WB!z&CxYD: ED!5"TXzm&;bf>$) óPp`—Z#@ij"Xgٰ>!-~$(EZipe7{ҖS PK&Vpz&50LOIu"ȍ,z_SFj{%dhp^438ʊI֚k|be 0kF طM RT /TTsl:PN"4^B-CQ#eڦZ*#ҌYM C.]vXޘKJEԭ ' B8;q9>+AeUڳֽVA2W_1 2NDޜʼVhjKlR_ O \x)A"lA4@wJWzS2<'/k<9ZVYia/1as#FEj3`ܨ(")H)B3S%5cl9IWU()d7aҞE/ SL^f%:c#ބqr K+l^4_g@{$Ggg֫*l=^(c4"C6Z_b3,0 G‘@8`0&tsFğ\ фj"gp;5ɞ$6Pz/lzUVKպ:UIcPzh|0JG@S+*JG;o"Vr`S\m&C%,p7fEEd(EX%^THzI7DC2g0Aé ѿvqH(Sɧ^Q\qxJ >MJ6+ֳͅ'o N:jg6ԁi-(k`][fbT c6Ux$1<Xݽ>ӎ -z#B33rf%ݶ4maAvHq:sġƶŎҜ\Yi@*6P1*"P@xN 3^Z%aQq!M̲"ܣgJvT*O=9UҨѮ:#v3ȜD2ÅajkY ekQn3$BW3E}uf Ir:Wuwxi nU4(cqWPu]Kek?mUp  C{q|޲Tk5%F%Ч/:7%a&gJBҮ{NL؞quOQZvPLݵV˺hPRtSzP/1mAAe*jӥ{m9keRδ9Uq# 5tw=efVZyS[LYej+SiMl70z KH5R4Cm;;w.E5Aa3# ㏑6ud)i=rd㗅5vc[-/QQ;UWm-ڂEFu"r=߳3#]ewiNPaRJBk}qδ ΆTNGFV+*ֲYv^Syi]mV\q5ܮC E-VOl{]AҾ.PkuDy =(Ԯƴn,9H6i@밲J);I]n&βp=n~Ӆ5/;nsQFfsPeaD[M|mZ ntXYoҲ̑&u֕5՜ V/OqԉK]2vu;sR Wk`aיYo.NS|~j &R꺲j |^{ eg*j%n60ȼUW0ST Tq=- _ºeɝUZzh*{Ob *K+i-.iqUĬWf0No:)BM<ؙ[{kI֛*;ÅAؾ¢JnpaHΉGԫ5Ջ'Ή==Fر2òWHOoy=yv4/Uʍm--ag;iU}VeYU]EΜ 7S{q$J1,;+l,No-mTp+7tÒ$ R;Y_vm&!A]r27[dJAΨoˬcTRM%fNz%i՛=Wzc}y&{0&|пN!_,)Nyk~k $h8f5hjE"k5JPJR% UT(IdRJYŶ-RE[L) JUB!ZX6ۯ3wp6\T\Us;).TgS|*KV?yؙo2Eϋ򒿚{g,[nLbΆg?}ѨiDREnYw٬P Qs@kuMve"_e_}MgܪqJ%:)ə)15i[V歯,TsP\{mRxVU匿Y&?uMCB P\m<1i" Djhwm$bZ44/η!/2 b&UJPLT XJI(VM[eXR,M* [8V}=LIʉ5]ƑG u͘-? >J nliq*0L3asXUZNf9Բ-Et_v[h[ -װux שzٙ#@YBί]9]Arl6.kfk) caw[liXT"JZNf#RF/e_BJQj\Q@@ae56GD <4[mѩ%Նv;ڕ%V&6"Ս]c.ue=xSn4NzyP)94$k1Mr6R;Ik*#ִQUmF-K%(} Ss ZnG9œ5v0nn$N^9b=5Ҩ}Yf'VKq~ +ԖߩtYr"T) +Lem,5(.)2mj5Jn1*I;&k {iU‹ aIAqaMߩҼ䵉yjI `̠YVM~-e7;͆wuT݆'RSC[!dacsvRԉeq#2(-26T%uӛ|"'ıӉAnM[ ʐ6m]J2*]k7Q;pONp+/; s01r}Lvs<* fBE LS vheksEYlx4J B!Bե_dgJYMf@of3qf6ՆnԪnW6 eस]Or(Mθ.~uH^_ӞvЊ9(RRb  6m$}%xy=k-lF|i^FuS[ggKbH?MChWL{nMeUByjfJվ;v[ XS Fܽm)Tm=95Rڻ#&,}dkSM垓jM坕EQHWV83(:f"2Fd:RSSHcXK*$ SR[_IFұ&m 4S+6 KsD+wvҥo~6f 6?G+"R:2Q=ij n5*OԊ}_])yEOUtwS`SkWx3^ +NWdrRiMyW񽚬+\q;TW.s&bQ\OZKji+a6E)Q6n4Sga{WwssWYc+y6I3ԗUt+9TnaMCYzvX^%ENEmamAjK&HSq58[ C.Ի NwmT\mdvړ٤bs2jIq= qLQa`WUbx<7v7M]=kFeq;{VvZY񲫷oe+eiJӍO tWsq.DKA= GI(ն۫p(Xg[2$ r;*Us+(ʠ4>,W^EKLztֺ ʭT< xpO_d{j-V]Nr>Tտ!u  MYڄ| "c/ҽ}ә$LsSRUv|jgЛ?YVgcx8? ]lH e]5\+m?WPnb?e]zvize(vshqo)jsn/R¶5M]a&z ^[*jm *پlv`ꍯeĜOiƑ}m1)ݝVn; h*/21vЦ'u(@ƚ9q%Ӌ^ͮ nkU1 CoU*X|djfmpM%{БL}0UL[ZF}u} &ەN|-ueyl* mӘ۩ϕSĨyjҤbT{8k3dNST]Qx)u&Ok*3M{޾S澡:sc3:=K o}&u<R|y7)B{3kΧePTӅȬZT%Jm36X\/ δĵK̯78D.2YW!r/cUBMvvVl8\0&׮YI95^$ّs]aIE.Z*mibR.UgywwZ[K1Qamy]8 JUR6#Ob_Ft>$YٵsKgL^f}wol.  jso/R]}ċJS K2 NZl"\]] 88;Tjux>JgRo*FGB ӧi7z(<>VYV+Zʹ/U5M'v:RWSdRw8Y:j')w\S^QJ㬅%Usn)6`}g$vꍶH.OHgD~DTe6 ЅY? L6)Pn48IX:9Žu5C% ucҶ!tۚj ~ yc_X~ob$Gu2]V9WSWv]yk4TH{j)4bY@c}qc[s+}abE>ki kqvme~* ge7ʜ.-ԶrMڂyШcՕ¼yi9 xw,k}62PVzEj)f߱5e:ʰ+@m/Qމ[Zrl~OYV64<([R]fKvvY3kUqc/SA6/V5' f}1yI>aߺjD.2$^i&]9HWĶ%ȁ¢5(K©9 zY~?zE>v_}eW[wS1Mcan=UU"w*+(-Yv5wm -x%% MIScxqFlTUzZI5̑9:%&Ӊe *PкңJqל.vuE5u5A9‘skljBK3qsgC:u&D 6{f=K;im*NʟҜ-*ebm4WkNF,w 7»m'07_r{+ (YKHU~ʓVf {W+0U]}UNU^OD‚o 60,R@Ap#Ul)):[Ӑ$AՇ| '"y/;KJ53vڛleOzy`RU@9 ~Ӊ5=U}q+CƉq*8yz4$n75wU`xٞ'nm=mY+45۫в|]5v sbgĝ5T԰km6dɼmrsoRցm XRԵ}EA*]֤F?qrWEiUP&q=;c]r68FX_m< hB!w_jch\E5IuTtr Mr=VUզJ[ ͯ+?a3XZYn=/oʠRnwWYMk-eF$إy}Vp''+7 %Pe];uzFf(EF6YUsYqImRmr9+: P=yby8Y}gT=֞#. c7VTl&u`TXIJAQY ̕fO&S!Ԝ+Tw܍MV=[|ה4[trFTdSeIJZ|uXڍ;_y:aɿA #<:mj,GeC}anSf B́avq:_ (\fڪs}Ϟb5۫+%&E[TwPp-+!bXD\fiˑœСȾc@77#*\J\S$=mˡ BZ[M͇RZ6`Vym,/KL!pZ.=9wtUS=E nƩz y\^ W>'Jqyy }h&goJeirF#+Esr^| SgU+5[n+N^4أZ]t[kK_Qn36TZY2ZFK Lk Vm+nr5^3OPUK}in(&m?1e}ZŖeVeͅ44JŬq(2pk&2ܹ>v3.+7TpdjĪxj-.6-ŴSiBޅȸMAMm48S* +EWZ }=FfIM.f3+o3Yi]ZYm.e)BXXļ>4'/eAF!,E2Z>?ci__ckN2dnӺm; mKt[98'O](g{H{?֚s}vSG:Jnj,I.c~sn$vX.ٸ.Kݚy7񠧙}?^ϑM_*sY 7IYUPwƅik7T)3,"UCUz)Ѹrf{Хi9~ϨMru&(]mym[.&'Db/+a|\lUR)b{耳2y, Tv|l?8ӢgW}k$/1~jT5&)LN9ٶ_CHnk}`ROy"6 ^ӝ;p8{ND(}+ 1/'^ZyeoQFs^ӾԇYXBU‚㥂"_m˭I#Uԅ&U+H_k+*r7ҩmy7 '*«۰cz΅ۛZFhUZV_Oȥ3#]5ȉA=C:I E+J\rU/#`M0$JV}6 -}8U;jwVmхi]ϧg$vzzꪚ%v,+3mƶ*vv|;irhJN9d`DEʸÝ5ʽF$ Ee%k]v<Ԫ(L@2e3nޱAwZ L}Qi9zD6vuvg=}+s9uU q8,eD]-G" HR[ξVTTUT\ :*r! fUv$mt /\U\^ki0ZpjUӶejkh*'e694ʬL 6nV9ى}Ņ=Iʻ jq{|m5tZ((ZTetؠ܅=nežNDΤԢwוּ8(_Yƕ~lZDIV`m[ 3Geʴk?l.ug?dNBw4\vj44;j *IcDnfҭKv u+Oa;elK;S0*Uۭ=;)]&IKYZfxtPq+*\+TN_O/.-:)jH鬩aik2D!Rn'!ѩe]^F¼Υ7Zb-Y'"JlH$_gVXX7?Ҽ-dzUfAC7}/{kea#Ge榍G6,nZoڲgem]WoSKR4]Wy۪ۜ)Ԝ+)7k*j&a[e-NS߰-;3VMNcЬōyA rkS&p1cj[Pm0iGV;ʖSZ[Lj* 6ԜqSȨ\O=yfj)T^UPn SZ)uai2ms0][ O29ïl ({D0Ӕk2qJ.pʚFM]2+UPEYPw*LY7xG7,ujUɋfFs"b"DL|Whl4@lЈ\ 5p++uVGis&˧<5̧g8Q1$96-ܹϡfRQ @PRjRngQ(}+^qD5x$1AZjI<z56v览ϙǂ50 ʙ$8ݨpfJnN U:>{3? XQ(8WB7%tc(]Aqε4b8V&P)Qr-g/p]5jO&V pK3jT- v,_@K n@=HaEh'r0nγf ho|F[b D.Grz'EB,v<ξqWU7MsL?yhf*iJ%rMR 15!OLǦR_&r)F/ '>AP OtYTc #tM~l6 ݀D@K%H?YI9cdRYM˭0+Q96Rd!_$xҨ t-G``BbRAd-'ڳR'\h~xn< Hđnd^HK!7`!``@,LjR] anD8#!, +C2nQٱr Í&JB$Rch4PJv'_VUF8NRP<(,5EBOk ˳*'C1 y{YhEb}voq 9١6I}47>_ i1T0 n.2@t)SSV&pE714Qtfdr Ba蜝R4Wձ~/:CUv&ux GOz4?}>s5(?fJOAS]%k1j*) bhc,d`?} <'=t6gAa߰6ۏG7Ss7 a8zgFt4#!(c)!7"ϪWR'7S(?YdّVoŀRd]^suFbZHPkDA\3r_DfV&Y?"7eBݤH't$JKqB#G0t#Ҡ3*Qk]IcS=Ģ|UҠ9z^:T 뜄M6e!1 0A ~ק?zo\MJ2wo1 ,6|T艼mަ-/]!pCX"D eBhmEҋkEe7I&4!\V<++k}wvH7_mnwcj%H ژ_UU }SgZwgeFEK_zWsF}\/f,_k U0.j-K( ]ɥLÉK/#lnntA7?߸Ht)vPNeLQ[*ТcB'aufĕ&oV?ԜąQv]w6eWxթՑijaal16QS].G+Jݚ{hUwib} K]*p(,d ^_4֜`Mei&Zp(ѧE=S]T.4ڱ;")Ұ]n CWNܸ:]ܨfpΞFDk~%{Q;p.Wt;L;'u3F"/iKP鰲UL4~k-젺ڷ-4$.4ݗwJҺq7vgATu]`QjAxyTYN΁ :.1.FTOk,Od5US/;J5'6RNƪ®ʺ;q6S7kN}-Jtϣw~o}IB{DȧՕ _'!lZQ짽 UfC["}ŲąPv$Mn_u8ՅW].5E'LrsO{Zh^}E ZVTj͵ [[i̮{aW_4F,Q1zTkw.'ᚗڒkk*ͯӁam1E[]]9˥6XTVC㲶)M]o0WX9ajs:Q*7B{SrǙVsqM#aSYak4+|y5XpθF\Iiݶ/ƬҬTL< 6~+9uu/\̂b WM'%nfmCWJۙԚœERM6QB:g*8JTQ;el iaQ"}57^ٜڞةNԊNa$kezˬ.&K u5^XGƻ /PU6deawywKN9j&)bu[5hϝkpkvr.//K>Fm,i_!l}`2EvL(]~Os+:S]uScZ1u󏓥JaeXQQ9JETaKs~Iچ ;vx<-.h) N\B29W$ :hQ?k'Um&=uA J/B6gH Lli+is<,T갳Tg[]k%զhOEKRjW-seeަ=fe7 z7i\s/)o\Z{>N>k+^˸cWq1{n"Lާև+u'A2hSM @[j)T d&iDZ[r*MrInԫ}>.+~&ϴY~GM>ӹNΪW^ 5&[˩ϝ԰dyYUԛ-\+mYM[XXqGeOz j]^G#k36 yථ4ƥ4+d-~[o/谤*>q3D͟mfGK6GZoSkul`1a> Xǽ9 b :|WĽ^vGbPgT]b5>9uM.E99'"v+4,*; 7+ S^ir[ĉW6 F$u,[]OpsnyWKiQfNFZ/m?M@nWS؅s=UqJx9Wz˝n^*Kzv K(YM"SAbt=+*FUizEi+)0._fcϲXmI?6Z9\B]vیNTUNWkZ_RWgTZ]ZOjQ{J YZVq)=t=+(TbPOD&Qyy5*D$-veFX0MU^۸SߍĤZF'ImTO|-JU̗6R-=J0ʽ&E= Ll[fEɨ)kn**ŦG"jsr-uɫ')25ZN+N͘2']n&^.aQʪƺ9uiǕU*~LڞiOPGĢQ(9[U n9[iTV3?w Utۊw $s$fzminS ҳmS; aV:v{y%8 U2;u_RE&պ勤_bB OlÝIZ#XZ$zG#-*˪I4_ JU g:˝3k.Ff4sFIB]xhZy"/іzmatDP;1sęOa\Åfnvw{rb_N-ѾPoYd:,;]}QTʛaQ 6ĶPJЁУ3,g[*+-0B(!Qkձ^=]t/L9MæfD$ 1iW2FzYim$km[ '^lӫ'?u]a+&7SNn*$XQo6\;nQ훭feȢw9'WMwXꓗ')?s)^ʫ:9[Tr'rքPl@VձtmJ7ʋI[LMKI VϵЧ Ok5m[SΟee wWYAuAAjJ{Zg#̷5-7_uYy1(U#EN)}&uUXeT_PD6v-YӒ6TS ɪU&Ҕ[Չ]DδQuRڍ^dw2xefjD|JLYq-$ K %='GwfҺE=#;Kgk3C]Y]Y|k <)[hrr;UnX,E+^UZŅ' xrcwY>6}UUO M[C{u=-K**?֞é'UFw#vYQ͉;i9}oŝ^eAA҆sfM=IY= Ӱԍ u[kmVvTO#^a%Fy@YS̤aO6V^VzT5ԃLX3yj+y𲨮-_{.C}SeeNYF3㑲z[IY&LJ X*m\=u~i눑X{ Ugc2a%$3ŧ.v{WL.?ruK莊 "k0-./pzҺk&ͬ얜jK?`r{Ep7&ZW=K0ANYUSv4at9u$̎Cie kyF37YIn :.inH֯M:mЉqeV}yR]қ9yk5E6-j\WAlZ?#++2((E7q@҉eT^B`ĤMTr$ _eezmu2<.nV`MNȁF Z}%y:TP'8uºAyx\ۯT䢲ҕVvl%]:讹kV]QVJ%Ay̍ܡ#eZzLL [UTEF .o}T; ζ7‰ ҁ  ɨ&Ǭ+uέaB%- \M--lU-27-8K(,;%eEKզVhkTDll-KaYmR~MļX~YjMiRҦkm]DYiuiY}"mPUuV+BHҹ^U9 kn67eA ZH5sfQl,)9gC[[Ji^N nC=¾\GAT5(- iL(-}lJAJSidK_4‚5R5.7"DEEԬ@oBFĪ_q"Ӻ8Tʔ6*VyP>5U%r&Db\-z?Ɗ); -L /Brq~mT}y*&o*KksPA*6wjhu7Gp1-k-s#u`_b^k&:sup*Lcoҭ6F4/eiEݴ,/evgQ^:3Y=:]\jUUؖ{mUf%riAv״4 X) i_GjE)Vݶˁu9y\YáXA!" C 9\>ld_砾/qi\ZoӣXhϵ/ ٮÁ֑ rw4WQv4SwZn%7Kҕ,ZlY[Mo"yKˉw~: zrV ц}ZޏGc*CV̝(4Ĭ>յv *rXth\Uk絴.B&l -:"qܞ’mGXdefJ[J/GiA]}{W6!iͥ*Z/^kUVUd<>)c:ә%Ytˏε2T:ʄ2t s­ n,qRw^Vϙ;ն^ZFTYu+`yԝO*;wه,+.,Փ`zn3F/щDΦSmB鉞pVQ_[Rm0? E%ۋ-̍=ŘE{BGNM bvq0@up.,68 ZRUVxgTY.kBjsI)7k>nhpqlqXeх<f+:5&{KaEԽ_St&Q"ݽY)!^,"e`oaWEm}YQQ9?"^t-d>t[/۫uyǁq]{]*7yR4(P؅E).6ymF歠@qUyy?% ,Wn')/J>t{W"Ii%a9=Zds7.p /V:n}xR9xecMYok2~g׻:mfm-;)F㰵ef:ekcOƗ$.Sf{ ڜ"*Nw&T^jxh;nW -d6"pWf$8 $,-fDW+E;}z]<[(Pm9~* U53Xvun)#SRƞd"ei#i-ElTmK,_jK5$W㺅E( T-2(ĚΨ26YmxΓKooϛ+Hl=&Ye(Ĵ7uYSMuF^r{Z˴^&ÉJ)LW(Rnp&Hrx+2K7L䠬k8U4cJSѱ [FQ/ߧAg, .ti͖u),Gi{)( ak,'gJcY~$]ΝQ!bZнt9Wq,<7_¹15mp+k..;ԔfS_OxV2uMve]lĺQPÚ#-mdN5N#ia,L]b-z٦paƿШ}];f=dl;l;~E&&˭*g#{~eUfetmW_c5tF4Jn&r=V)A^}g(eDZձ2[ x vY:>i\r7jJˋ_K9`hibl.8K+W;]+Sr*KmBWEt-(y|3SP(뭕556p'gu6ynʩ{I:;g;FsJK-Cne5Pq:ñ/),Nr} 58ZBX;嫊BJPp #lk뚣S`ֵ ʾ{ ǝ;,ՠ*l-sy47ȿzl+JWl6X]QM0yYh 9ֹ.!a<٫4QI#-)1,e=VaKV,)*m_j4x"aQ2.&iU=3BܥAt"Onb|OYqrFkp7og]s0ȭ8**]nTM)KȖ#kgC$P?IzD~t$(U \^Yk:K)=WʆuȂ$W\i\<>UsƧYK2um%?{W]HЧw+;2k-OkY= mTue_TOm&[Y OQۙmR] ijm܎6M}7b٣̕e֮iӲ/CSaiEi8N VqȬo 6= oaU#e4OXH֣JE٫/V\䚰ʕRs8fM+u'iiΫmk\O=?>c]?"}ĕm.ܙE ƪ_H#ebڕ\)Qc^l%Z݉cԢPkT\TY]RJNfv-6f^IkY]#UwّQr]UBe.Һ}*k.eE:s!@Esԉ{n|q¾mS7ej0AqПmʜ^YcgXЬҚaQ"ʃ,J6^tK ʨXeJS ^]+ &28/ř:򓋥ȕ1kZV7y{aG<N5(,5s/<6+*To)_*D֢]MOyUUwa^x'7mNJmOхer1jiYd^HM"RL2A]+Ij4z7uk+m*q?Eͺ[akcm4/'V$+()TP6̌2' H)7*)";Q(ԤCmF YĤo,YVXs$qs%tI 9+17u P{ᲟqR$Cr8[-,"u\9mI뻫3>W' a [@@eg nV`EWhp3],PNPCc! jVFY$\4a5_5M&#aA¼uIyI¦G5EN/#V:Im6؜U[+oi96t併FeRe u4WQ.<-;vjɏ^}MņKTCVlp)1EldhUq6V;{/*j%=4OZr9z[+LmpA"lS7^v5N 4* (U5NZkTMPQT^@.Vl6Tg.-^/0-\b櫝(f=rrRNc@C@BS@?DDꝚ~>hĤ *TXڵh鮣Broj[˫~J1:(ߣ]5ususNc@Քj:%+ҫt^ZSmI+TeZ[?U%: elGyuJꃉIY{&^‚WMk~yZf\ҧmEKr2aiopUibu^*C>W`rRzQ(9K:-L@!9g$ 9m\i27k<}ֹo&9#ȍ?iiFqgGt 54-iAjEHq?w㼓 eG][mH⻙ ʥvuۍQ6XS*CʩԿ}|ߗ]qZ-ZQؚѶMUB8 ѽ] ņe2W z%į5euqj$MņqmAAAQe*$n)>OYUIXS'*z#{V̨߬9xV}5}#mkBwU޲MeYHc0pr"^hMTHЁ"R*rrGINo'فfj lMu'U|oFv.##6[ )l¬FzFKh/+WMg!x*U΢: KIQH:b2GwLljОYJo#Ue5].BZ=5K}a ִ#gU mզ"JSMSTSu.g̉yYB&T% :k~j[+f ՒbJ;5Цqib uJV"ntSOrlY_UseU5IP`zj5&Y\XY3(C=54''3'MM ՔIꊮK )<(UIc~fln7^UХ}*x6,+!p'+8[+ w/#]ym6ҭ2!BNbA3M>+5=̠VyT;snkZ0.xS'-yk>; &H̼Cl(_B> nEe[5u-DƻcEOՃȕU]MTgjN}EQA} JEUVa jƲ&$;j͕g:mf*R{,q0Eŕibl0ZQJx_JՆ{+5=f*8/"q0 yRANTH7[ʺiyi p@j9(L(46>jǮ>Gi:]jlP, gʊ{T˟y-Y `iq2̡>GI^h]NڥK_ٕ͵vk|uzf.N3DI?;g}7,]6O2sb: IUB…E#YYd%{+HXb T&WQfp>~eB)e-,lgcUyRj:ӑQ~4&'=ּ*2]^j4.5nJ'ObԨܗ$ݦ/ʰ쩺3(6XjyZSw؜eab n4S7Y\i^ LHR6Y+03JN]iAy۶AƦ9UݷRWm^\12X\y܏箼r}[L+,\7Y/l%pn6nPm(3[(QnѕHE3\v4NG,+9\*b&&A]ކeuG#u}7n㩒f4+zLʫ*s($S@Җ"|8rMD]5R?Qaa+WT 94tҕmЂYW[Fl1p1"^h]aF趫ȶ(2*ˋif-t3TU/8Lv-*znhԆ kh԰°R#Se 5!T1 &+ux~ҏzRgpɆc%F) T&ϥ> TiO>rlV $L NYyqbUt''//5P[ے-* %xgřIu*^[WU /\F̠b4Yٮ/1yu5k^VMoMWk^﬩;W*,P6x43 Sw۷Wnz-*vsЁ\UbfK}%U̬gסOeŹfaE yp4S'xlkާ6~ϵ֟Yv3( 4" 8Im6fkl .NoC]{C;{Wn:`;L4#@a*TӓY|ľ'#yYma=Lqv1< )l'bkeQԸ^PjOms;l:֞G}5“i;aCkxJϗqR?H9`D+f] ˬt"߰RNc[J}q=3)q;P#DĜ_oOv veVCi7v2Eh;I)S՝/SL g寬ͰšYkv\S[JHI;mT?y=xT%SQSeN3^VGaߙ cgNЬ'}ԉWOƶEc$=lSPYJt x]hZN>DkUso59Lid`aqEEAM.y9Yݫ=kKL )Jg7XӷXq8/K݌Jg}9AWxut*z]_\qyi'i{? }֠39GSN2s;yR`ʞTTFPNnmSW&NhxUShW=M+M"SB2_E)זyF߿TXR{ak#?Bm+3찛aĮGס$kLi,;MQ:ҵ9Wnh4:s=,g}q2eUevmFWqWTn;JL7hgNefr¸Ņffl?UVҤ2VVӝ:-v9r"V# Ul OyaA YwZ*O]qu7>6|)^Fi!HYjy;h :46>kj-յU26li*ܘi])/{eD e +S]̨N~e5=gCBqۉ͆qa]sNHk7.nxU&Gbx1< i>k2Do:~n3[Xf"k$n*E2jv'Sq+ F۹;iz+|SliktZnvD󍼰԰7Y0L v>FԉⶱʍIA&lj,\7_DXiH:XC9AmYnepPWv25i`ii}ձ|6ے6^.5.⎳a$h^҉]Ucř7*vU 3o,UxOfƴ _IY1&O}F7ewVr*_v+1V+i-251tVq{-h+Ղ+,-7ZOMp'%K"ϲʍГCݚQr(MF]ĝ m9,[J&0ޭ+Vr:VᛩJEe.MWH)# *.-3UDs-y9 ?}/ȾQ^GдÞӇm_vCu V\ҸR3E; 7#; _C*z|6J [,j+8[vmkZnpT/Z:jJhK|Vq$:m1z]24搱'}q Зfh8\M˖t֗( o=|x-Yp,2gȴ7v#J=#t߯2ںz-V9T$ܦ%MYnX٢TͿGIz+?ieqԉO]6a|]&U%5扎Y{e9,:W3ٙmŰ&2Uj‰ Jsfl(8*rj| t 4訣Oo{LK[ Oem^P; @ +R 9|s 8j>390O+Yw%^yl)'@߻8RtRSXqT7^56n-ԭ*덧{ l h])z n&0n޺7ֶ'KhMIh'a^ ^r28_zp-ǴUIi5u ^mNRwrnMT_2tIvAmkK 8ޅtv֕/  :fn0:wcd`$U.,(V-+i&T#Yoߞ箚C5 N}vڪ^kXߑY%t֩f ǁY<)p|:QGzg 3 -nmxZ65Wv"x+i+O:Qb_xn;/]mqw*[sk"Zs-++[i5F^B<$seKLtiCSÏB'eaG5U$>DgyS~ \R*Ϲ.wqS ֥u%} 太c.:n$.J um*glVhX<4n#ZjɩlrSƴN%)-7m19\kZp,^O^CgR|VfSCC5̈́ܽմɩ4$RĠ/o%̉Y, αMwԍ8L R),RCƅ)4,0è;ϑ-g+e5- KvN%nA2 )$MDzVL I258Νm"rWl?mubE/aUMw$k+8 ?2Nglu*+72'TQШRYaʨ/5aB~{LSEGKIAv>w&vYh{.W`FJ"O@1!YN\I_R^)T#znkhE֧7u=DQ6UkOoN Н(ǟ3Z3CPPKB[*GԇRDOq%7w2= v9z9&mN5+Y5 Z 0Ae=9$_[RO@O 5 ݖZ ./Q̴7xn27ޅ;))?U%tIjV6й]WUi]Oq/MYi*=n )ðûZp=Ueɺ^t5*F|>8w D>wau; 魸BIz/pV|N~qb]yAN +KyE#3vuzYv[:ç=yy] @=W(?RL&˜YR azTY&Œ[ެ k f$/zK m;qN"UnU:;a,I\+Ypz5ݥWkJrA=~q+fw}ZaYTbq-^"'{ nYJfj6¢WYqqQ.UWަO}lv%-&7_6&5ua<;z>msh^[p8m m/}^7n6XI)Bܲ;00*&mߩ|?wԙy}eb~<6'#[|TِPY ˩P:_yͳ[(.y2980KUdMUȃ=wq5Yar~jhX'ڼ--HniҶ[M{\i_d+j\Ưfg U&&D;R⺳C;•ooQer{OޥH3(m9'fw&Q%f'"6G~fe o+p> +qZc`[EJ@p&agHZA}UƶZ}ǙmO1>%3U*VdaF)oCliʩS]Bs'8>?aEiIoxKĭwҊf~4W j#}?mhi"e wy'[d=I]}IQaN^{QZ~/v=JM;Gmagp=Dn#C3*4܏sWߢ e*KN4iX|W=dkH{[y*Ȕk5`Fsѱ WԫAT'^HQ\!@\_߷j0l"oqPu׮s*/vj;X~=imhW?R6լQO &TCcaJ&=wW>e婓-ilV{ƲjFDeBᅪw~yda_S`/IZQKœ+_6ګڢf\WeԞUEE.+4ӆ;VuSSQ?o'5TvZQ&FZjhk:JNkdX~_M\V?@h~_ŕJq |1U! 0rWaۤMeNGqe~ڒ m9UZϙQwkY^X,3}ok?#\Q;]ƙׅܹThm~Vf%7N2T^&og9n_|kF UX"akB¹=y3eWQ{,|WWl 'yV/8q6TfqJ<鯂mMUaE#8ĝЉ"ն :4jmI*0Sv>Phڱ6"-mW\~(+Vk\dF!:j4ĂdϪARtWgu nTQ,_pN;*jThռ ңqwuYbUU$5hS[+ ֲTmN:A+I'-6#SQYPgrUF?6nMt KZnu(&jʃbHH۩UJ*LТ6n'vwj) R>fD7-)\mƺQT+ҩBk^cz8t1)PSRiT_;r+}5u^e !a^}{Y]64tujWbq2; ;j*/ 2Oy<;= ee:q%&4M[58[8< l I5כ(Mv?Z-Ris0*VR[r(0lGvR'aSSXOhX\Y٨feԊ%&-VKGɅ]ζno.L]'iszJ5X˹suVs7\ԮugUwehΈ,%^as"F¦[`n(TYkyןV' 5uR:)&&٘q"Qi]yVQ^m; W}) (kq+ ۨohFI.UV&E)9/"{zveyjXlYJAuqe5cy &ɕMӃuIW/akLyd)ʮVfv$Յ-̳`k>uNTٞ{+*FN]YOq±0V,X׳NuYWKPq%Xzv jrOAyCii̬Ew#5ޅLTlIͥ~W:Uȷ۩+L+6xSug]td܋<ڐ,<[B4j8`y]6<} ]JqԸp^[b]ncF{כ5*6uf/q+ltH\ʣYIQuI4 -=MYb_0&n*HUa-7HV0J?k纙e3yV+knoWұ9,XiCi~{fCdȅ9+S>%6_IYC-0BF a[QeT ۯ=$7a]8fBIf^Å4PS.}Ku&9i'jE-g;3NL5weTw=~,S/G FKNxZj4_uStbdM<,3MPE-EE$愯Q'.B/sً*RqRjRо9}8S0qh C̼gmw\Y't'=z{N4(VIxХڸ*_}^๢e='i؅iWuMTjGk0gOj+46l-giCqff7J/0/!ǻ†a&ufe^mUaem[JÊuMQyʲlFe-CieuI*`lݾjq;Uy$ϰΚ֎Dmg'?&#CiB,xZݠjʛh5,gYZ\Un(cBeCAk%JŪSYYs[+-,K L7(Zy?SD+,Mkm 5wi{=JʍwJqq5U7 HTU`gcHꞕΪ^ԤOt|Z2٢۫5( M"oOY@m6_Ɖ.dn֨9q*_B+uEi]qcWs-+Q]:mTHzfV5_v65, 2ժg"ƓKSRڤݧ r*~syB ggÃm]V\:{0mxכ,jmjNXx2JNﻝN)X2[bŲG \]q.BamZօ.#Wi-ɗ6XcuRr`\q#Z~o ϻw6.-:-(2j='wt:ڻ2&e-3ڏj~2*u'yJ5846斅tkؓUP]NЮ_=6Ln[KL m2Uiy'XhSn-?MQXv1Ѽƒ5qvVkfkVwM(WXp+J[TMr ܠr "eFD.,B">?jNǯVڛNرv'qO9|Mz JZQD٬4 PX;Mu'헁Gm[{xXh.Rx#/e&\u5xUV rX@ǮWk4(|^4s0*27& ,OYQĺqEub*{m+ҭ\P5a]{8^Ņo>Ki~OS[nF4 G ~{IQ'F3VaIV_mA#G7 JJ J oՑO" 3~K3*$-sDoht& ZgƯimQXQaMMN~oam*ηQ쬺?/aB_.eGq4l{} {{ N^ 4ՑQҫvp27[ӗʼH&IR.I-7I d}L ]3*U ]Qk+9(FҶs;񱰮neeuT6Bj:U46UV{KaO{6nu]Ȝ,S=uia[\2~mI~D.!E3my mBMgUУMF`SWTZʮ}m `]uAmn60gL,蓭52!]dN U7~Ywn͇}fŽoUpԅ]ua¥+!TcBReڤGYʦ;i:FҙU1B""B"" !Vp2 bYFjL˲|mo ]X+,X=5 Ƶ7CBjCR{)(6"F2E6Hyێ5NvwFnq.c@ޙss6)%iCZm=5e9jJhT9ݜ޷Ed%-Gzk&Te4-bJ].湶aW:;Q5]+e,ueS5͵e=6us&?U9Mt,+>T¥Ҿ\qXh:[t.Rӕ}__37SqMq U#_땕gԙGF[CQz#d[kt$gh/нdS-mȤz\N4ksnr{YM{uU5(TכﴧAY|3{933߹"V(Ӡ~Һ3*r2'¢N£qLJAuzg5meǙk_mH뽤NWKk͘DlQʉ M]k(%[/eScESRM*KcS\Y༼I/溵u*_uqcjof.+=Ǹʯ]S:œB4l -YZ/Ī{E5-ȦJW{Vu9~FİU/Vb43E""ybPD6V36aƿȧBS)&xͿQj#^\KܳoN;joVbB޹*n-5.&@~'b=jV cnK™22Cah"'+iB)K+>j ,*OevWOMM@wܢ?·| IXww]l̪3.ESGaqs C3:2{=q]x;3>tL UqI*&入 -E"}RWr%_Bn[K[tkVo&zr8^tՖ5dk*7%JT*r"zOY܈tk:[[ YY"f}\:1UQi ʯ;}s#;qBl&\s23M@ mjhۮy#2UZ%+V첸VdJ}GZ!Ejsք9ښZy6t gRg]u7rj]t015*s554ObkWrT(Zv:kJ}FVXYUp~oSK*̉;TY81bUm՘qq"s0V G+8K55% U; ggtE4.?M*M[q)ʺVi]^Wи)7-kčJ턮|dZK.NAy']ZSXIJUiMVFܝykL`UhՐPiRYBN{(R ;]WV ZW\9přיrF¯ eU56rzznzaKM,VVmV[@m76\H>;jSj2Kuqspjbf{Y wge{V{T[4՜^²-ʝJMF}fͭf7];Ыv=\7Uc@R"$I[-fm iMnkNjv[G*06ݴTR ~w/rD~VsNT\̃yjg"d h i6ùB\֭"R$RbMIh-^/26U5eeiQu,~:mLS=wФبryܬ%Xfт+:e,P*%*KBm2$K6{q6q-fgР5*Ggı|p;\ᲁkY}]M6NmHt*mgu"ssW-:&Efתr䉑iώ\ZPRXDDNKH-zKOf?޷ wVK𦭱9GeKXjؔJOPmNfn,yˣQX4cf#c%[ MڠŸ% /Z~#SYBlC3LfQUzכ6õM ׸ڂV},FGvk:ә2^ fpBUambDpOy[O FT*Q[J3•ռ%_\U ~=>*6{뫱ik˺PE *κ:}td~eֲs®1`[vR3)X]Iyl>WѕkS,VN'žOmƁBDJ5uUORRk+XUFþDb O#UK9{($Php;k -del;o(`OaDFFEkw^bm4H[ZFƚYgRP" *LJ([aQa#kn3[NZithi{Ƭ`zNԧmWK]RTAIr9(ؑ&D}Uݶ#Kf\l'-_Fjf.i[E\*5UT N1#f̉?QNdʃf`鎝/+\E3: -"~*)|@6:*z:U>j&D\m6/+b_c*H%wiZ±}fv[KY_gޯZFW]J*索Ԝ(+1*қ'{tKH!gL1k HC wx2VcB k3$ I}f₪sBȅI$ȳDY=YqqʁF)š4U ]ʉ8kk#^i&a}Ωa$&KnmKSlh&\J*.aam@߆w͑5́īКOlZ"[XE>$wWS\s֥YayyV+]>ȅ\ blVŚu1ɃېQ8hrp2Q6J,1OP<|8sL ZɼqCX˹0y+& tW8&C2e6w2JvЁP *;>`.2/Izwx\|+xz's2J/Ⱥ9v6tYDRm9 7!Auу3q]Yf,y-0*e9gV3AqJG˾LfUD|*djB(f ,'Ngdw#-ABEa`~zWG9l}GP7#Gd]᫐%J+g8a.?`|p(G[@ks$Q9Øvq!/CoDyAlrGNٯT!4>]܂t2FJ'j3 kЍb8z_׷8wW ..&a|YK]*ׁFAWx/>OPrsjv%\_J3nOs0jMK|^o]K]G]b>,(Q_n͛P֝WYXR'ϭ3 M~.z *krpE'p]ٞNvXg Sc +OuydʛR]qp$.50=o6rSTzKbfVԖfR;xw‰VW4j;D&5h|i*HF=Nv5jUN~R`[}IEx9x˴,%(1>4Sv=0-͈g_9tKSz& 趩<5G~*rݱ څ=Shz֗Z5Q]|b9KR\Ui\T2/1N?EOvt7*-MuӅνőJ)㎭2jȄ۟"ܹlsܱ;K5d-Nԉ#jG|3Q?_iTk?J=WUӕ(D\g2a3|cb5[ܺ/|*_X:Z !#C6M;o[š яf؍ַ=KCA;/, hy㞜KŸyWW*,KHGBA>yHSG?ɖx^d;qX4i8tVQvm]`{ԙ<^^A5Vvocݨ7OB)X_2g  0xiSrcކ+nm1gJm& nDQXQp/-eo;fa.ҰĬʼ5nW4sx):h~(ͨ{@b KIf?9  1r WT):k 蛥)j#/^Jڑ{/Ѹz*h!츮r,̴jy^bgȶI7 ;Իw)?eRwҞЊR_P9hKV9* ?rSSl৫ZoS/I5[ZjLc7wY{j5zLؽKw]y џ.lhuR__n|)Fef>Iœ8*CF= vH7%">mm4j_q*sr&;YsOp%_ 1.ua}){T 崦6Kg4 ;B+, zCo1#@DC0卮tb0EEF6c}c"|ĦӧOtRQ $VEh R@$آ`1jF u"Y"e qƞҰAfyotdi SqC'\\gs߮UQCkq]D&/<̎*Fx~rZ7G7v8Gh%%nZ@.%jUH f "H{|_j"Jniiu0MgEz=ăγ .دrw %J3NKz 1fGK'jC}L7~o;Zq=wa)ooWNaY3;J,Ԍ'R/`KLngtv]/*5پ!9̖=z,AC)I'@h1F,Hg#u3{'ÎPp7 - xd+g`ZSx|FAxf -g:Ʌ8F$uUbRGjPOՙjK|6ꭼulRˈbQQ; Aaw\6XœU,ez<=5埰Z[.55&%󒗂 q`)|mַ}R4:G1W:EeՂ=7VDپG8<X3aN]V䤏fJRqS(DOs $]Ph*Vޖ(<=,o)E\]CsA g$Ǡ?!:#x 4$Z`YY29zC!.NG5~(sg_mq`;B]ȠYN(ްfpL;:eF7n0{K\ڰA9՚fqkOd2_( oyv;l#Ӈo]2Ԍ%Mk!HB"K% ҍRu/:xd( %5R¹t31RCc`I(!9H?V ~pC#t]V'EAKrs6aP"*-y5¸S?A3_k$?7&! C4qn:ђ9xk:?]{ /m㱏K@D; x[7WUIYvRtt~ˢ%}(:&)Kh? e]][ ׋5SjX]M;YE $\\*-bIyG+>%|~KH"K**t(RzƧjz#Msz~q-ݩ0Jj2Ho) FB ?Rg BR<"-B lҳh7Eȵ6?hq|I\ƁDATRp~ȵ(X[vB1~dny+d7_-iЫŮE,?*0(;`}S,ϜD,5cyqZִWXt +x/!*E`>1>vtwlya2clmiTTP4$Flѽ% Jb.Ú7!h)1F<ǒ1!շH ? B`w8n9GƶW ϋDb;'(0ٖ]] LؔgyQ ).qGh $~_H`|(q ѢZaeFzpL;݇ r/Hv9j ΛUTpyh/;[wi?i_9 "Jq޵5z ,"!4tӎ!jz9%۪SXocqC[إ{B͍înLX¦m:9,F>d /w)JYWb,0n)= $pNX.J8*!,) 7@mI޻}`N]Pzx:Pj@+ͣ.rW12N!"U>)Qgq_qwP INȹnHj%![ql.׈?OIgs ɟ`w0#S37='"lp_dEϖ%ZbNhD%_= "~-?ѭ4  I'¾+0M1hNX)AE۠PKފ%jhlhy`be-GPmO~6խ8G*'Ĩ haOkkA -S/hfSs/0eދVuQcى{ L $iPS'џr_d;TeNmrhhp[g>!8 R^aAE #vMrs ds':$*PTq:}kk`~OHxF§µg 0yZ!/kBW'Nd`{-(P 2Lxt}НqF?mL.(;N=(GsWT-:Jo\9ϑc-SkZIO28 L®LA[3BcRHTTgYn.6 꼊~U0/tq|l{8!-n:&wZ.Ey2p} J7*Q.rEiL!g9*-g#RϝNfZM;OU$;Kk;i=je&EFlj!ebX%W[2لnQ54&17M.".͝@X^Z9|`Y}.jUX9꿷1+cXeka;\}~/u\7 7)n Nڵi|D|,TK8<*>0(ŏ,ζ4 tș-y?cf3~g{B_f Z޵˹SDnX/GdD)Q=|oʉG;%?{⮣ZAqusf%:ue+JhVtD(ѨQFc06zRsuU#!`:V\WJZ%evtD}l~w&4nl0%-ύX4n&‚EJ6jt>2*"4z |5.r5~7 afc5_PY/96P_ _tIWRvIƛM@ZjrvV$D-2wYHg{^a3{ y-q_ jUN_ EWBR$2>w Vhlt,S2xr3#6#>Ԩ/`t]T@wD SǸ0EApPt3B&&%.$yn}!Σ%KUf1BL\8EY`l "9 Ǜ% DHJJ~05FihG‰u2Ǔ>=PG9:ikP/"Z%/Q x~VZ"538B=na U8qx䘉Gf=Aqk\+FJ'?W!v=X$ViģSl^,_rc]p)[~>Y<۶5yx'CXj"-fxiGf/ .A~+Kou,3 %&EGNv#vY{>m'B#+dB$')ƜpT (kΎejÚOqކTFd1=/qE$x^`zAw/IeS^;[I}PiHKEH[ -,K_)P=XkThs. Z΄>֒v4']ͮTxv7_OL Ty+.Dj4]t>y$'t't B$W0}*J''D] |94T M3jCđBd=/06s~/Y~ S|J7C. 3#AzkB!ֶuGѐ5qrPf!YcdBeJv=I.Fþ)C[ҘϦom)^h.wvp%Jឪ&t&+o;!wdU$Z=Ae %OUvth->4u.o@ RARM/'hiqR :!9+% sS0'kRם?)gj-N]仵Ɠ֜\p+mM1 ̂]+kJqM?v;(}Y",A%b ئ3~'e-lBOTH=<{:i4DpDWa0uh@5 9ȘZhb-ci{<--UYssah%AOàL$YRa=kfWԒr}cp*6\T,7`zXX3. kDmX(zaH<#E.i@xS>g$#,[ tJO`r,ۄR#>ҠُU$Ւui*Hڛ<8c~Wd{$tOE̵a!&}:3.@>=g{qMǰKhiϷyӚh'L%:M Y=rӚ?]h&M:g*iĻD(i eo_3$!H(K=xkZ%3I1 a$|]ȷ*Dz&/Kr[-EU:tJ6wVSds\T9hYqj R*Z;:*KY:.׵W`>^mݱi\h%]UO%Ep*̷:ZQg/BRRZyk˜VŖfYg>JBUa;EUgr^v穏Ҷ++.İnwgW%p_S/qSSj'm0}e8cOMИE}A[ZՓHY6s_S*ok.ywu3 AA_'o}ZdΫb@_饵YkL6 ;iՖ&+]ZKclsKQ/"c74GCp<[ba"nhךN`OLJLU oW+A$_G9`CKԂ6Nl΋+ 1֓ܯND̼>SOW . ETUwe3 ~.s$N)#gv duà ]+2_.eK&(]]jGB䜽—Xt#q0GX""-*GOw{E")/i[w˼sf+g6 rZIu3KWO<#)z29D}3H=Xq(sBbS(kŎŜ<_ZoC\.)L)Ԇ( ,9ZT jљyґW“~< H2}ILYg@&EbV’#5/AiQS1⿳⯌PQ=/Hɱ]8s^F:)k/lJjKE> !-o+^i" YwgxnQurzN9:>f5/.!styuw3ix |j)vϻYWЄ޶Z ! N}Bzru~ծ~lZHH*Qv&&w/eKi]-FR֨iZR~l u]S,F1mڬc ^d+3T\ǑiD,zFsɢ)=_׈峉S ^qިmSD'Ns׆-L8x/.**[XviH[;k楉x3 :ƻ:QR ¢V\ۢ{SmKh^]Gm ,C3b4fo+A48 A-aު5?wE6&=orpr?{u'0{ߘ0AL_ɀM7}<P&N LHKcH JGrbtczˑSV_zkTħ wnw;}D_ItUq a5Ol6>14ד'\QztF~S3A/ AJ}vȿR"UF1x|hxJVVDY{447ltSi|%2ҋoO7ӊ`SO nʉ}BXmk}J ':53k[ޤ@Fã]Ihjm,N< u{UBP 9Dy/X6Mgs]&r>l*7A!1M7+o ݹzle~HI.ѫ+JC[ !]\Ik0w< y!SKȁӴ+:[&y|aTA5|0"%_[rt:جؖ޵dI5z\F'9-sM-U#&GMx9BuD4|?Iq!%t~@̈́MW1ٱ;gw4QGFPa:? <=ՃN(q)&XyKyB_RWE$=/Y5I*lsXPKx'ϝ缈}#gqy ЉR(]Tڸ%uɈiAz6qQUf vX]̘Vc1*߼ V3{E933_эo֭l{NLlϩ/*z ͕~/%W⨌`Z$ߡ3U!ػJ#O45 OJpixYfV-&Ӎvs M"37+s2P&I?!)ۀ(EE2ɴ-&Ltz(H$PVE$Ӧ@~BӾ Z9R۩TݵL;6=aՙRZdc24hBj̵/l.%.%kRo[:y^[vFH=eBzkH0Tr?E2٩WS*Z1uP6À`gTC%5I&}>]˙yN#ض;V誸gs*Q|@J5G7]kn5i!hlÚtXl(-&E-)_ mTN.fiC3D%Cv6Rlr$,:"h>pЏGTi+Q-Y}t#/o=فU ~E/* x޼d"jK x:sM%Žϸh8iZޓS__vKȱ0*ONڀ) VqPoHJQd<:Q*rĒzRF=#R? .ם5^ךԪ(@M:-ݽQ-AF! VujYS.,%"ܓ/R`4-O*iXD"%^UVvm}/u.< ssmZZ `>ےY(;Ν:ʢ!+*Ϊ,%~Tg0!0H<={7-.J S?#O' d\D =Tq64$ϫ! $Ų>H"fA辘 ]V꽢r>w \w)QiPʯ^c3”ܐTefi&[j/N7!*)Phf=# c+:=ŭg:N< 8q׶>դ;D)$^ ,=ąKR-'5Q4lQ@+-dR&? tnlHF4*?h?_>8k=Щ(6ߩ#s.وPuRʛk1&RGR׺"}?ÎbVMERLm#!x )}W[˪beo&-V%bOOmGuשQ'F9/'tF!UCB⴯=}߿ʵBGxp$TX:DD ԋ:aPc}" 3h>w:MT+ _-]MQ ~(MK?dWjjqՌ$ 4=y@;<BQ1ޜ`"AK㢦w3Xᚘ^=>a7(9cN,)NZ|64=Lڕ!䃞<eBExl>#8Ξ7e541?C=4ɑr܆6|}YY=-S#M m|'ܮ>?هcZ]O Ҙ n&Ǯ̼dyUJ{;y\߇qmvD)e Sİ;$HB1"HR6`lqzqix5i`Fs,]tظ3;OBy*V6),Np0ɯ lQH,]-2Q.[M$`Vgkc݂J4ufs K!HFDԒ/ POBȰ)bKg'_bɨC'iҵu-SKX9d%da9S ̺_0~v4$.=# ;:YR9VW9DWdlqÅ62Sbt}{|Z_%.ec!>:s.V&Bs^/ϐjCDl(d5Q|ej3+Xg7sRa6QC zyXOA{+*u9/ٸQ 0k{F%B~#)dE2ja|b#7>ݝ>Ť%+J sm &(y4mQ+ѩ3T# ",zPE~L*(HͧgP$e4{Cd1L+:wrLB-;' 2%6Vx\(rM#"#-#@s b<<~13RUr6I$cOZ;bDyIsԊΩ=3%:x)Gjqq,ND k YfհoObbdaPԨ /7{)!5y8ſSn-BL'L'Ī/ B]q!nP[Ndp覎`H/D:nmMZ:SUU5qD!%ꪀ}( X'jҌFREuckʠ;ֶ$KTLب9P6ou*,vՖY(Sok qpbV׭Q+ɛo]:IcH6诩 V2+NWn讫Q2أT.roaQ,-ZX^p$Mz:ڪ;ؠ49 Sj|lNvZT澯L>~kMYyn,`fw]=\Hs2H=x5a۞+z YN:JL]?2FN XFB;AN= +5X- .tp6{ #г ̔UŒ֭c|YpTD Ojñ7i}5H0FY{%6ʠ`4[gn7aN5Zоp?Q_%[ D<;69D9lOm+4Zi-JriNꮏG;ωZbF3x̳ΣDWs#mtVՕmԊP ,v=:VV(FUKB],+]V.=4,a\,͡B7T1h,f9] rM>ᢡzk-j]g ]x˯ߵ,7ա,V>~k 3M)/B"&܊M4Fұ˯%jc{m~$.9GKKB->wm?cC]m/ &~ c0GYIMeH>屌dV^k[.cr!FnzR IcBe =pxXBL"UUú[꿥qaaCzF5M&)8KդM=b[ ,72+vO1W7!5e5Dz͠ŨiUk느A^7Cm[LÔsu4p:pLKM\u}c~fU7Cyt- S8#eY ݋&chqP=$/Rk-K^SR$y_i`1p`HQ+ܮ^כ 5'd&jB5h-4-gz< ~tM;z'M% [2կ_J ށW׿)לɚmdOA(F[j<1eZ}E^DZ|v$لLr3U°e&\,->|sLPG5'a-or-v/nFOd|#ϥGCr^qܛG6K SRJOb5m$١¯fMdV,'^:[=]`SeqEIFU` &H]v/!s ʙkfXp׮Xu. )^8lgKQ:Ql¯璍?^[-5 |4ݞwHX%VL׽HY0O퉔G1@&Wprnf\u[wYȦ9jI7%tAs`Zr{㉓W*WJaC+l+{  W—W$7uff9E\ !ul- e֞ ,#eS{Y ;_/?v2,hbZjuɓdrio|ak'y`CM61L5%ƖU |[O kdej,Xj,]~\Bħ\8柉|Z4Ex*8Ν^媭xˆMU-ft^k Գhsq?/҆@6^."+J=^@5OQ~W!/-4̤\i%N)XRl2׷>hJ#ݚIV)ʺM歔_-jÛSt]qh7q2IƸRv~4ĪJZp)E'NJPҔ5ږ43WW]bNO&?)_-jZ8WBlʭeץCCNs XwӞM$WkyG"M?ܹ5CI!8K<ȭ㱗cEqiLk+W(:yS!Z*]'LC>2*4 ýNp]\>b{*+ wiDVlJ~.SzriE,E4*9H:N7K^ͥVaVxҤFmU߂" 1[@:*ǶQJeK1 `Pƽ6^tZ_ _ߪX#VᚊK^MdRh4RSDb7huw9#c;3BojSĤU:I#PRjq \xݸGK8BF咘0gn!ƺz yb?r캽xsYSNhm8lii8կHp.& /Y`Ysw=njW,Ϊ1>}JqU3ip݅5W]foDpnlkGkf'PjvS )kRN7-7 +__:\ˇ"MVOmh7߃f{װw69w]& A1 (!Δظ%ˍնۣs5bRjRKj7K>t8O5WPO]Bش2M?ky*H[KXJZX˴ mџ+hhJ5ܪۗGF|Z4%y NUS?b&J]EQbS!ƭ[X4)1G$٦ƭTU]bb~&t`P`)oMIk`q՜`-þGR!GJ>7B0 !B!rUADzoꖭB)LmcZ T5IoqUK gn%\kvZ]\ZmqLmi|&l`!"c9 9\Oh*\Lqx?WC*Kkm recOA R޴+yh+ SaF{J;-G<Spԧȷ뢒4ݞcT(/1bWg(4 ʉgۣvc:]xvH i?7!sABl}U4`LP.d!3<4CAul3#h2,W4l-j:CTGz eJ_fWeZYE/$VYנwvy3[q9exIiК"RMVxːFEjW4ƐuC]ٶ#/'G-VX{ȧ\E;9e3Ia3GNsDYujb*p?npuKkoa47kGWiqG|z.2FA)K%[kޭ;nLxFH`Z(/ˊXb(A9 EK 2OG>z$L3cj6]Kild;Ž2/|և&<[i alaM1 |KqoF]ϜRW'_T_cNׅ2 {':J4HSK˭q ww2ٿS/%MV"5J?>)u&mHԉ?GQ}\{ 5jˣsk;"Ky,oB-Ei]8`Ը&([N"L O9jcD;ww^7׌ɭj_p dg5!"qt)..^v~/yWg]ީ(S;vnRwU<&ϧbJ)"lUNBq~VإAY}ZX E{qH$;NV7I6Wx&Mlq 0#3.""DlJOz'-hcz1('du{Qd合`k]k!Ӵ{t?.B^/*\1W/riO"XX8}mϲ{8i_c~FZ x'z?$mzAF/r,qNx/҅0{X,9T Ǎ9]\Ib>9GòQQ4RV?s@,C{9up9e_iU|"4v.{|*G@nv'P4F@oFW]WlTVү0ehuVIݍ4'N}^{BL©ud+ר+ibt#aߵ- +tnBs./mCay׍!J}'5OGIM;mnoNgXްuѨ*kk^A PZo/TڏD70FՋU݅dBƞ }_:R93dKzI&7] bcbdYI{+ҞgN \K CGX@Ѯ@}DP@eǡ+zgN܍7RoFz'*.E{5~$z?nLiNK;p8^%"ˤ>tZ*td.X: ^! `ne۱߾* r]h(iTVK+|:ϵ(+$jL9Je ?rIR#mz-f4'C4NzJhpKk `BaNN-LY^UYxVݷ%n@ʀ48n} Ҹ(zׅԺײY^S;bxS *$N2$=ү]}+Dc+,+Fs-aT4_棐5hXU(lBLplJ+B (%gy/)wBJUU⭫X ,ե+57[^&=6#Z)цS2a>w`$eׁ{UHtEXo>2Yتuqb_~W ®{.RYfg} ibmqc!I^42{YںI[򦾃wKW{ĭXZc-Ǥe!0b*^'߂ p: !"cI2h0vQ"Uh.}mDbBPMDV_rO|HhKxݺMwp|oE™A8׫}̯h,iߗkhuNrDV`ÏnLaCtZNvU}s%g٧ED:m,!/ ,5k,VC8kͻKti{H7tLUf $2M!Gpe!aiyjܒuT-5NkB} tΧ5ҞS4 ,QJ͗Yk5D`;{ܥSfEOAiyv9Ij_SW ,*hk~wMʀ>պIJ#яQWO]!l#qH״O`Cg"܎gȐ0ƨR|_S!+J?u6Q$tVR/eZѩ+TȆمhWnGE+JC׏/JM(̊$ j;hfQU6i'%l#g(NS]LZl?&V$}PU玙4É>;bG*F Gt_Dq+ ɣOaf(;~B3`XodUm[6hb Ύn r;jz#LU,TU*E4F3o=w&{g,ވ(5jLb61UFD.կ%S+Qg5,!oGcq҂qN]:WG|~2ܿ޳!ʆ1{P`>jٿ?*;iڶ7{&V>5ױ2/Aaރ;epؘ>EM@}=b,80gtâT{mw}{Tu_pY[ S/e"߀w/z`MNGMA<8%Z"n~2Kv=%S?,:9:"qFmJSP -)R?}1U耹st 8| A=6,~gGoᨲ52\Z f n;lb\LOj8Fxʟ~,;%$Rړ頡2u_jRV|UȚ3KcY/Ȼ֨7V_IX щ'Ƨ2KvtQ3##o^~L wq1NJʖ*{gu?{+! )-dpV2{2lh0Z-^85wbsU]R?1Gk愮XSڊFq4:zWBP}Dʢڏˮ m=<}1BBNUX2aE:p.1:eD3JnTHVkT~ qljlpń0Clfcyg$8wʯ.*^Xiipi mM1h׋oW0* %OGXe> ߴy@d4"LqeN"]W4Pg׾1\4gs n"{fSQIlYyKÅ[~h?|+@=婏O*p$6لP~wtqE~_k;.izW/%Lq7Ow7K_~<.CAJ[dєkĴ7u L&b9o&MU46_XEj3PF:B S/Nd-RBx`wF%ߔ\8 G4ݟ9assId`*EhYz .DŤ:E֌%P K%xXt6ѝ#dxl]qv 疭\:jD'@CV?(%Bw9)H1߭q߹L)ϽV!Ir g,hRyS08 ƚyg=ڀģ>3afN;`QhlK fg1iUyҡOySY6dyē?>]ȇ)=߮^U zAH<ѯKl~m .zC{D[h3-j9Վ'UcF=aY!q5|~GɻLOܣcc=W,E4{<Mn,'es 5X-_y!plUP1~V EV\), pKfQ92*gAOiNm]~wX N%a3I8BRP"6I7vvm%] E|-M/|4_qԸ( x)7>Ek}As47}TD$Qg^,ꚳSG]5|:֙MUbUtXaQUՒY' kSc4҅^ȶ-;A5-5 R)[L*ZIMiKX mCCx7^>KiZo_8LE)>^v ꭎ!0dʾάuyRVjuvjYUۥ!˦U+Q43dь3_w62Yߒo"bޫC"$U.I:DJj!Xq[5l`YtLqgt٤H&llReY[T)/9CTǴYNcL E?~6yw~\>\z.&ٖ=#Tڰw^{-v[el eA(ST||)f4?:qPkhRItY7npQh#R(8g`1"Zt&\l1$7L^}|ĎA1 'njdHGAS%5sEfG+ 0zTYc7ijaHUjܗk[5&-zS㣖5v^SM [{O=e SKe"TА{geZNwԛ>q`R*{ ^qk;'ra`M?LR.v4 H$ޡCy2̪9By.l V}醅D=4oJλhRݢ_OX$QZ2oIM՞"X!UETNI]!dc~&`ܶ*U멫@?_k" p2[,:\jfmZǁWqT1u)MʴdT):Roh:9ъs\w9%.SO4  "QIℜUE岉fUtQ, -3`T% nU$:3K Pg*`;H7)AKVcYMUn]EP5e{kl^ЃCY*:گaCuR'I|2_-a&mVD{ c~$sooDb/AjAL. RU{Jƍo2* {7k>7.q*y/.7!]Jcmy1btan1,ڝ7o]:&\ MɈ).s}OR36{L"#@ ՓEb4o7gɁ,+y^`y/d۪$&W>s؟8ӆ#-ݳL+wz"fQ`7Mq8ն3brq!-.OҝO^0s<0megDanTVvgw'H7A/yT@>unǠᘺXfx;'#K9!;Y ΅n5i")eY~l7_|^x"'HJE>X6s>LeSh2SRqA$iN{wx/T `hyHOϼ{ښ\¸ʼX/ KGe(VѓI YIR#7(6:2JJZ ~bR`gJJ tMƲՏ];Ơ*Lh4XZ5kso 1 /FC累ak-i|dAn|ks[T)eW>)a)3/'UId0qD*.҉FԐ5EW!3ۢ]iDB``ӞkvbENXgۤȞeT08sjK6PnSlDNS̺AݲcX8q;$ ,JPE~m sK$R%foNP!,bcsIΫ:D__ _K,^쵙d7 [ބF3Kqxop;㽄vSeW7-ۊ62~0`:/:&Jǣ,FQ /(WOkt#]:Ϟڑ^,fKBB fѽɱ@JOPy|,O`U6ߌAlfB%l[-4)hq|owuT ^MY*ѯrHo3yFe'B*)E˶tb(Q,V> 2AQG|4A8V$j|%.jzFJn ̊Dۄ 6; pSԞ~$xeqH%TE& PJ#žPwc~l?)KKӸp^{9+r7>pLh|Gڠ:8"R|wM49jE@M0-m%eI|tH(Io@ZbK֊'NUSk$mIeݳ=g~AM.[1('Pd47{`(<=.2Z6,toZpl+m%?HI o`C|q\𳂲b!&ܨA}F79! z7YṙXv?|43ȱ|Ie?qR̅%k>ngv{s 8Cω(q=%э:(!IfMw5廢ad#[2ƃ4/'_kvGP[)f(%f fbӽV8~NkG|?XHSpߢʣܽTqm@jiO ƕ%v0zʸ7} F=gNWH?R].I:%P[ (Uu\C2RO yrQ=(V68*3zGqU8?b L(:#L%mF/}i_Bjh=FOOjtlƍt ; y>e-25aUTyY֩DZ#OlN0.Bo%Ӻ/V]VRn^'UyjU4Ej+$PFx:Up1H+>wdzYjԆRH$`Ju9 #1a5%CƵIG!٧9* ˊȱ > S祰9[+ahO$k eSOPzjcǽ6/l9$sԢ}y}z1غs9/PePmT2Q>9vd?11`O 0'Ԕ%2oicb˃e(B\w;rr*;ٱmv My-ƨ[+c:㡣68F֭<]"!Xl>Da$s0 z+ O@;R&ծfqj~J8|rMu7vƍ$ܥFYd>wYD\fxD?Pz57$!o֗aLZ6Dr~#! C1 w&}9D* ؕNUٳ2'β&Z!$F"I%jSuY[#W$#mB4O4)i<9- 5,=AD@*KSGLz3 &*ҕ}zIWulDG!Rmeݚy#nqdNn'T}]o۟;~f?-kU~t ʡ!A?nP} rԄ|ܘ\q:Ndk5"ئYU_lbBV\sNl&^9N_ JrkҖ5fSvQKA"2+_73Nƽ$7FUy*50=OP&1ErN|d՝xP}~0/ML>-(I*~YN87ynE8(n:S WqE1k!whwʆhdFQgMCv:u[LZ3^Yz\Axo-<½<`kO0mHATtZ^kIJ"=SDғa(J>ZlO㰙Tٴ_D%d{հΑ1 mkҗJWF!o*3,r9(truW%7wr:踻:\Ν{0Z! ~*d"OYJ Cr\P+(u *HP|L Uz@_6;Q6uF&^]e(MKO 2bq[QQw?MNtE7ǦS.-.khb)%UVE6U;]EU`ޚѲ̻&?2ãJ+viU'vjYYyv3 p$_ar2=0Kh/JH IKϪhf0=)˷}רi6~ZVg`譞H 簉 !nioTWfHglCI;;wUԗN'9ry^tTwߨ{\l@DG-Vq=P<ϣd뉅Ufe[-CoOӸۄ)MEc${_dh}PؓCMP-m_^dѵ1Rfku8tDI >h5-fǰZ(iщhvw_o|&Wd86^ | tϚir x=jՔQ9`sVqu7789nÚvQ(){'T4>2y|b >d};g IR9Q-`/޸*ˡ٩`݉3+OJ#79s7v]ӣ]A\%6 {)IT"s6!ocΞ6-Q Dh~Y%AsL2 oVE"^L#D~ϱduynQ?EI/΢F(BO_na`%A7Z5:!T&МȡR1.QRS|p/ZA@,~'dR}xO`Z3Ekwe Tu xc#c$( Í !CaI!4oyrlG[lXkBFUL[fĭ. 7Kƥ~/=Aqwvv{1j=JWy՛ދʚÿ'>.iвgԄEW}I0>ě>bjC8(HI`qՄ$!bRkӔilKQ4_)US\%i*K:x+J-XV][` Bn'mZ?Z%9Sα9k2/~;KJ}m#V5N1Z-h~ljvyGQ`I y:4;F?P)V1)ҰO)CXBzڢ[{$ۜ)BM!Zj`Ub@FKj g}ϹOlx6oK7 * S3ʿO8uQ϶d3gc94f Pq .$`<4腑\Mn/[FMHˁ9X~4E lc I, % d}i=g@)l:O ,2$7$vZ XO9\Hw2¹-',]vH}biY6/ҩS_nv?Z{g7_dGْuGOxP,wjVDcR@֥%; 9E3JWԱN}RN焭yyֶw=!JI:)E_pz|5w/Ḣ߿#YZ^<U 8 0Қ5#@;sƼԝaDa6\'5!P~CsOdb+츉PJ?s jb*S|+?.AUPx[Id:É%is12ɞO'x_eRg8Z#hIN1.A}GJCuYɤƺw57Vh"ٝfs] PG |:+t{Ϋe t3V:qN>1޼)Q{[ \V׭OHqԨu^N]qf;/iL>A =B}#KXO 1n XKPڠXcy}9$M)_DЏP^?'NWWTZSz(3 =}-zz :QrV/(s_AKY+"B5."D^;}&`xSn1nYyxve"VxP F(J_8؎J2]Gq2LiΪH9&S ;9:? _1I0E G:q#*79%U.a8m.`X MWH}緣L8)ȟ~lq!:S5NlP#R*T{"c#IǦrAزTe1RT3<}M爿wQ׋ +S\iR-Vfd)}q*a"O-Rt9mj39rZ:o+I*0Dc8W G ΊBj\ [㺔ӌQdX=AriG;3Zbe~J-Ht<߳9{킣fڀ'8tC*ʯp?SՂ\Qut1pX氃h_Wz>ۚn͙^ tbD E+XgJq}L,T~,Cع`_:b@n'-˵7߬51I)B-zUɥD"RuA;N<5Ւ|&-x}QFnq]/x1i5p,Zߩsgm޷As婼/k|Τ.>vaaNcOcNZ&φԱɿ< ~L* z̮)P3^ǫ^yo1%(i2d&Ķ 4/g{x>)E^>٦W=, R|L#`&EVn4d&C䝢Jl/0| a8?s6=Whb)_{6myjKJԴMoPxsHuUo jESXN3~3'Q%QgygN{>lQ:{m.?e]>4{j$g&('CwL'a6/ɭ5jJa6GI%C [$Ҭ':nIl.dPp"Y ({`\[|x1cT4I9dG* 3KjiXD6M-}w(:V&%BzmcjM"Ryj{JΦ¨TX4% >vGBV <9M먽 ̸_rl'e<ʫP)=}#9,c0L# 9)}':P置"̳]=n}uyV .6yf'k[-W!s'E}aBna y߭>;&ҏѸŇ(-&PK7/MHK$pp}\7*.)Qzzƥzdzd75C)jZ/e;;dzxb.63Bt_90X|;-52m.7M0)3͔( Fb_#?HzӠMw˨ty% #фmEj)*~ 3zY!Cyv3|A]fr£% e-Ǜ|*4s-_Awr>a^'J}O dbnVQ%э5^XiGs= sb~g9Z)$D괴 q<,Pc˴&K:Eb+$d%Tks8@-+ +v47Ow"%뢗Ix`n+ xݽeu?2髬רyA0nV{0,Szi0-P &*tzu1M̹J2_j &w/DzVk)=cߢ=_ OIޏ+`/bk>B{|zA+n9~<(4fs?@=,3ZRݵrġ?M,3-Q*C(%@;\!0ಖ"g)I>{'ef>g'-Y2뿷UeZʷ.%.DYVNWz;F89E͔oWV^6]'[_B"UD^aL,(I 8}޿"猴QhիyJ_:= 4TCQڴ^5AXϡ4HS'"qkqPŪD!¯SBIktZҗJTu!Z%oRA ;*5D4VÐk,6eK؊{Qq֯ k !OA jjRiz}+YV)|_gy4^"v(Uexl㫚fv~%6NWZ~kpUJ*Hp[T:qsZN_FZR_ UCtX=ԝRԵKVB|S O܅_V<45Qc!:Z)KRx>dSICtIj(0VaW;&vo_k3;ՅtםLV6u8+Ո)ع[[\n q*3KW 2o X֙4 e|l;.XS^[#X=}r0[:psE6NBma+F[11p-ڢ19Ǚ4=S"E5jY7Rxn`r{gZE:Oo֮?m _խW>M\KcT>&5}c'2_{f;,:7ܸN?QT09l=S߹\?#r%4%y V,ܤtm1YPڻ~mq"4&I$(,'\|3ּ{yyMwYDW[?{v#k=1W)*ey[g63u77buOM3ja"o>&F"M<':C׮n׺0}ߙ+U%aƹF B2 yVޚ?o W}ւ}Qō T9~9;HGҳquYܪ2KN7gZ+1%AUP]Mi+D{Ì9|zv\2ZɷfLOr FBdРFMwauuR-}zka1ip7"LhA&H9o$DG(~Qvn08D/[?`,c_7sԓrr>iPcٿ*Ԝmk~ѳv{9(L1 U{MYVȻ=xu8#E͝*cW]06EטZ~0'xW %i/};#4CcA!B ƅ9HHwT.vウ>!mcZ Ba& ޜ}`?.>!EfGQiZ6oY"=*Su4WCܽ@Gk8 [)a`LQҪTR9&wLBۄS]qi4W1T6YhG9^/(&m U0< ^o>"눎f#>~R.%cikoÂkj=.vd,ha9  3DXQ긞{~ScH;fn;}ZRUaKW[ʳ!V&?6+D)5"+n갳nqNq̼B >2QᜥwI{Gލ뷤qm=Vt0>Š > )$ ynCB|Y^]=KUs+u/*E{pJ찔O:6q[)@1l [X/{B1_z9CB\vNZP`oTj"r!ի;b0A~̐efFEH$/\ks>׬QEiu%Jd9HpܕE evOz( ,?ۇ043wqc|' 7 !r똃NnB=aem^hueHWtCϾT ԟ4G/jYd;䠲d0\3XPJǪ#nrN1A?lgM*6F4?…SG|7ǎ$\]|-lewgҽ7ydd5Vs:xBd􊚃'!,e:9/+WCs.UnmiUp]IVdUvhQ]d&e'UxyY>-I2Юƴۡ~tLF')(ߗ(, d.㐞29>M_J/K5lxOyEo:QߘZt_"`_v3 GO=jl$a~I+=+9Hc<أWRSC/t7ץƓ!빬Eߺ1X.64[ɾO7߀?ܴlΙכ-[tJA}+5 ئ쬏p9Ty4$Mb%M'3ߐbwȚ~]f/A<"5rB ;xo9vcB{ث26.2Y?[5gϿf,ʙ ,~c"ͮ5x b;!瞋&~S0OQ2`UߕU|oer\ЗUN_|J&*7F`ҷ!M%6EgOT^sCڴMQ&ub^NحkkZ`YeW3o.Evz`X$0IO~_ws5#fq*nL¯ޱMA_ql.89V?޿6W뭕}qXVWrnYof3ݚ [e"k=}"z+bS}S}U!ռiKQ|ՂB)+HqKq= ]~w[㽤shR{ cVM=UэCGϟRy(*$JȯV4anYQ:!-uMnPmu֚ Rn`$oO3L~\b?:riBʺu@XL) cfLIZ`l{j *J_iT)n,hJ[OU?)zKH^ITa=sʖc0(9r-/dYd8Q\9)EAB)=T*+{ǫn$a̡b*pϩ*tRP#gXu1C[`O E"9$(V[^! Nsm!b!a44$Qz%Kq 4M)a hm aGڄN1ۤM- &dJ%M!ICXzofϥ =Jb[NpNVҚ:]R|zB* V pʵ(g4ORB⪡eXCRvAZSD˼zlpX?#J~$oJv%e?eQHɋhV _AЮHٓ\d-㲯Ϫ-ϱo7 [LK52/j?av9J?ʉZ?ӫMI2`莊XgJ;I ^J=EYU*wxwMsᵵjUt-X#`Xɬao? : tX?׵<G%% xk|,Mt nYMt#<љlL`qK-!9?IDBw}ަ']ͫ5 7BߏO]"gۋr[S= A@ ya}o++z6NRId>q_-J[^P9i~d#?7Us]Lck}'icC`vA&bRrFQ]1rУ| p*C(#%#p}CZcѰWm_ qAQ 5ͅ,M |y8f4CK_"דo)Gs+_Y VuT8`b&x )A<;kbQׅ6iUwڐ{s04I{|)*m]LYS"Lsj!u+1h }Bو3{,׫-{ nڡ<׻rz.` #:)GbYH^ y0x:qXO9ƈ2ocL-k^U]9:`W󇳽d -`Fvڲ1 #v?.fe*,3}O#̱4Ο!,6JS#u7cm3ViץF IVc  8AX{_-1%Wyzhk /5 [1x8 >g1>"xc ؃y[\^IKo8Oy8j]ht#p.#Buϙ%.6L y1ƙӭZ, c 0$42bfm28LӗgWdrǘWZҰ͡ͼ60C ͇A{a+NJWZ\Er'oC|ڇ҉He, 7ZzV>Wy}wEtܚ5lId+/j  p:-Jc 8u1b,CPpg "'Cw0E\EqlC=45QED,tsf@+0>cR(xW_(Ω9B#zhfh c˹ i&pa4ejt7/ UsSuu0N#%DO^>v xxM}Xadnr늁[&1'Lx.WI}an4qxR\w%oSCNǏnYaZ; Bis[+o4WG@]GIzph B|?䆟 "q WAeHD$COL`%́zK{ez2TLVzRDM'P;?<#%wTlTscq>TKt[{r e(R3gCH"Z/rΨx+~tu!cc7|޵R|Uml貒$Psλhh )#D&lfwu, GySsf9f*k"in 0Z+І5Vxp틧iXg^L+G z)f#/݉ẒuMTH &m!)_2<S$@9>9؍,ձkiŌ ?$F[NQnp@SL a8iqJ ++G8iAHs1xXXnr tI1Z@lͷ鎚jAEdQIhbhy i&9as55HYQG3wG9Gs \flE۶#7D̝ pGCXk={5HtrG㙹P m!Po?+'#wh0%*.Gx;'! vMgO<6__带.uyUV8o?.)ōc-гDkd\{P1/hBRf?`QIrM^8S# zsǓɉb,9eUnwfX` Ӯ{/?A[t1fh"UWCM.?a0}sznl[r+P:)VuKlkןIAh .u4cGf|+JY iJh:&Qs=Ǿ5ZJktm7H~>ϦK$<ylsd2M=͡? -ӊr=V} 1~šI-FU "UὗRWcLIFΫغY B]zω?S%X)gM)(B(ºKڵk `,B"mK2SZI#M6|AaWΟa碔A C\jzIyt46U4Uwo`,cZ%M%uݩX)<42Ġ5,E"C"+dդKyCKܮEָ И[i>y54/5ѻE4/>&b!1}ڴKQH(br ʮ)D4(+J)ai2ȥ-AMT(6XdQl[Q!ME$GZ]jS4KD)kȮΒM. PRuŔ_eҪPM\%}Pa.ۂkU'p1|+E-wAkIeIx~=>g[esW5͑ziGγi5a]P9_Rk_UfAq62slN+r%eEOJbQy6!]4xǠ $U}kEdDBeJ.c@u[v]0%hh^nEC7'9\.0>? 8x6T F/BXܽv{1&Gh*Y/ЉhIuD" 3_߷?hy0)fj-y4b#-5Ν9KLg42]/}7)\>{#ehV,(z~}}ۣPޚ/~"OҳE|1*ҩ8wG]OL{gס7w2ar3ʔES-DAwk1w93\X<^?f"݁*8|^ R->HSjOC U"rbL"p,ɱwտA^1vZ&ݐc͎1&bP!3aV!8P3[ ]W-uH9O'K' xnqZ:LUNߩqƎ$BsJS#zb:YhD_I0bcK\1WO4uJ03UP8/,†Ť/xޱu-&̧] ̱ZNs!0DYRH6}alwP3#‘~F05ƨt1q 1Q0;T/#JXy(J* ~ I+F*ZbuϬ=t<2m_̤J<@6@P13i*,ob%ٻ\|]JR8csx8sws(Jy*,ɗtdeʜr`<`XX1H-Gět&=v= ՠs?w/5^Y d= w, Y8E:a.`X ,ArȸLuKi'dP^fw8WأD23q@ibW^pB<{!YJ޷aĻK-} \a 㲨}<sା4IHEyF{> crf0oPy>pD{V &3> !`Ì1)^$g$h\]EؖqGa qs|V,L&{NOڍ H̋Y<?U!k6C?ɧޔ -U,x%1 ;zMDDePk&Ι1 yAP}z!tip1E7w whunxgg f*l v!ld82>[L.TפJ۶:z:1ws^yNv,R'_0^#c0ٶ#W ݉'r$6t*,u/-˪Fn{b?@u?U5}ƺ']`&308cb V2ٞN3+DA.XbS3nAauD"U \deeRj$ .=V41Sr^X S.7/0\[:ċ bnb^w5OޝJV%k5>S:p/ Bd V+33)THJtr9u:!Ktf-JUu{1Jn(jy{u¶"@r$>8zjOQkK&)<|Oi;]~$-#gXw*/oQ*80ch-2BOwWY[@>"[t}R""yh/fptVp( o~e$Mn-xM}&h[Sjd[ݍɎ)/E8&4 uq i-$IɦuqN*v$ N$8~(rG?}E3\܄aӨ㨄Q7'V,++A*d؏DeFNp^uO |+|}.j;eE,xQ)+;~䜛e80>f!EW74Kn{=5^#4*A}sg;{M<;ғJ'YD#s967H+\oGcQi8~[~ܪ6 |[9p_ήPE9-+1y;7Llmڞe{.q 1qrMܷG% r= KG!Ōr[$黗Pu{qS: nU/e{ZΆS\YߛS}r+fiXV/DgP!}8&yHך)TRUjZERvIwub`C@ ~;9,JHM9yl"orxI#|TS@"A$rtҟ34bWg둂,A7dt589\R%,lM]]5ehC1GF"ReO!|bo"P#2;f]7BFl7*>ej_V0\FV+Y}@DLAq}=뉶Q[oYza]hVzZ]& Q{ h[pVWWĖ$_*9}QaVx6dt0|ȻPY޺jЏ}*?e)94z^B-k*"{lzkddJ)`zmd54<Fo(Ozp- bۨ>eJni]uؔ0qĬh(Dz)`+km]Q2?ٴOTڭhmeQ)Qc*Ҁɢux=*uܡVX69UtPMs!ElPU7aQ"'JZM!&c:))YJ,fUђ1f!pY 0v{L#F(+T^KPRm|gWt~ysLZp63A}N1Ŀf>^*:c`Y6~YeAƧ.㠬GQCa2&w{==s΋bhOQ VfI|).TskkRk$;Nn Źt9 n ?H%סMf~ͦ[TTIB= 12l2"׫-}:Cx1ƘsaJ@\&/H:b̘iL'9F/Π{R c-bd3 ǽ]4vx]lib-%!y7&(":&ʿ9 ;VPCI#Rc}n6|_sFp U+[Ÿƞ5/ǃvD]Ƃe:,s%L ۛI- ~,y#Se>I]PK#.]XT/׼mu}d8Ժ207}/sHU4bHi.x;^dt;(`r@z$57g9)h HO7b-\q^^# =a^N`,:~G>Z#& $Őxd9lm[ɝMPcH;4!Ds9Na[GKj^he:wwG/dR$ouXb3E!HK fXb][ Fs* #` փXO Dgtg]?JB=1Ȯ:뤰h&_`ܮ;@Ӈ#cږs|mmtq)Y 9=b?T94n}W)RkA n!\Q; ZavQ˜#B_(ֵb : .?=()nSR' ]DgF RI}=nvj)1fMl2kՉBiq1Q gGJ%Ŋdh{6\ 0g?AxA1O]kϫ9{S"r=9?l>}E>l\F׆֭rB<(/oF=}f`4yo6bz+6خi6[>Fi= 'pYzӲY}YZ&I3%!Ngm0{RݙrTbcA׌9qhB [y!.GA3+deS.X>AΙ9}+=T>|ʤX}Af ?.!{>=,8*[*QKR5g)~RajnYG5o18P.H%0_ Z*pGPiK}tqهbA1\,0C8 m^Iuná_K#q*ALxhCݯ _y{7uP r9ִh]%MP`F؟? &H1h+L:D3c||Kύ&<^ q)58mw9>;y<${ __ǭ2c6X+Ք{k:D➭ ^I-ZA Zg0\4e19:pZ_?TZYaQ2<4`y8ޤUuŻ~obWJ8 w.y9XZ^e}Sݾښɖ.ѕ6VyhLys314eW3vY wd]dv.Ε ^E4Y~"EyɁkzA`xJdnw̲qD^w[cRBg a.{}W̦|1־Hs)g4,jZa?D˕'wnƮ;s,:Nk͙?u~o 션?Y5_ALY1?ȼHP8]N?>vyD>O]ÍK-ݻd_mm,^BFpl[ו׳b ׫M̰*CglK1h4k S@IZi EQ5w4/x<η'TV{Yz-ǠbpzJL~Br`KȠF3):~` @r hM^not4 jvUqkQ㒄'z6^u{c5 9Ee|7Nl3;L_YZW_v 3x_NE'QpbάoDz]ؑ9?2d i1^ m5DA6P@YFQz7 ,pBJgAow%.wZPymj^N;[ly"2P;a1J}79ŒRoFkK8Ըۦc>'1s.k:O&]mo//8&R]_ɝ,Z鵂߂,ƂӺ; ;I.ުkYX? E nyޥk>}Y=dJ}c($ 3ax>f0 EU>e97L^3VM;sq iowVb'o~}da,ef pHQV#wdP]fmYgS"ЋsHrpʕA8֢*GHFG= DwZ.=J\ĬjuOn3`i?Z\:ic8DeV7䡽Hjsgh ǰ$5ٴp +l 8 EWb\Wq:l7A$w;vç?TiR7ttQlBdyRɐPsA eZ~B)"Bx8bh'bT,dc ZmR!6~˹r8^ɏޟzVxcq7Vlr3o*W<5I)sy>t^kl@.V8>  7!G2lw 5ju-vK)-Fr;tN>'Kŝr=TF.z"f;rAIq[y#0SO uYo?W}=B+#s.Rx'hw>BQֵ])ρe sB9(j5#^ڬN[!e%_ӦZ@Xz.tOFC\e 8~Ix'Pk^:1}v ծLQxt-vUX%(_s|΂"H&̺Kl9Iʫ-/ʜ8P!X]aNR-4*pXd7#*ICH+BL,k/}ě0%fNf&LFг>>F2sPERwLqOj1X[l#6K!chbI*ld*^KF7kp 0j3>yCT;0;sao:{a- `4>pmupQ)f+~|IEɍsoQ'#re-_xgۓD<̦ת}{QrivF;8*կSɗ'p#">UM,>)jO޺pi\n1pR to+ӹ;:ʔ0¿SN^89m0\֢MI\kZ}&8us[QwތOq] Ϩ># ͌{JyW"K׏gs?.Sf쟒x3rQkmw~|73}>s7>tKCk+Ŕo#1'Otx,.ק3rhcu'PBTsR2 (e adcG ÆYL+@2)gMR }-t f4 '#\Fũ,&8' Rڗ ?Hinjf,Wi!&n$l3xàQ.8Wn*5'YV2YB>fY r[_|5v\ưZ'(o(XʓD P@01a!,^خF9f/:5=vVwqI;c/oDrlD!,u`+iGp#ǔIHm5^eλ̭լUW-̴'ՠu?%kW)P0b{ cN'I. [prPW@VCAB&4\(n*yw-d^_О?-O q-mﷻ\w=XUJ^J̴/j5)j-lD Ŭӽj:6wd}i# n\I֤Hyq؉]7ҺlAqW{݅,ȹXr4LƲe![ݿU>Y\٨̫Rrۢ0CH:$bL\44_=RzŞ#=nF9<{Sz騷]fiU(Rϗҗh0K|k/6GqG)uʥ@E;_Jn&Uzoak Oo*~´DzĈzD' |!SpG|KFwFt kBq>ȄZRJ`ƮЧls88țQ:cHNil})|'Ohc3w)gbu8QKZeA{x7U:³//EIt* {Y5;-'sp%Y3ltiԧ=V-hS(ʍCO^ ?޷ꩼG~y; X PH3 >^o.{bprΡ^>qT稹>ǟW^rɢ۰Jr7,: KZW.t9Hh"JJ 3@hӀАpg,xqfnvLeWUYL5ITXvы7#A! Rd򅏜AKih OPƣA2k2f hRÛ3O$qq7إ0qs]T\Ĵ/+,Ȭ :#uVvҮ6͢Qx'^#S$X6^o&šy{ZqsUmg8+aNKѻ=E[txhQs3(zF)lfdvI P1ϩ^Kt%_;&9^/9EJm*ggj{Cެܮ˿#b iZ}?l=7cʧj!__; 91*%aLNbN@11O?jX0BE]"KQGAQTKt| B|ʶ׵m_mөXa(2w{UF`?6$pl[YUfI7VÓqߋf)wIKAϜˌC@xkܵLͩW祮;Ra8jR2)"Y:ӢOP94E]c\d:=J/o`"g꾠+*;QZ y8$:nOI}DC. ȓ`زK)NI-6o="e*i%yhwy4&פJ]RL߉9R4L+oEJJښ:R|]Zn"3ðIdWmU.*ǶlaMZ5ITZC63JOOLd'2pv+M<%S]."$275×abbAC&,H|57a?Ziu86Vw3Kvs6aVKBQk! Ӝy-xZ0üuXvXyp<.zWG( k!4$nDdTWhі;FqE} E*ֵFp.ˍ vYV^4kVn] Ӵ4Tr-E0"4!/)q" 6dWZ[u@msZPZwJ-4_%y< P xu'Il_Ja98͡ cPX_B=zjU,5NEb*c)&5&eύ3ϱMY]ayYwًvUIka@^U>|5Hm:#_vWeüc|db::ge}dhKxKooY+ML|ϾR-sS "uw¤]_eXH^snn@o ~_xU;9ᆭW_ S2'y$ZPW;t] ܆Xž1 i(e1Dǝ.5A`aQ7Tn\KP*BQVʁszRu_jx6 γ*? VW ysz⹋o%{-TX^C.׬.S_6dƪ-L^#֗ܪ67`)/faV#y$3^?g 4|m>zLk=jzgW m~}Էj*B;-*O]{41>.UyfP6Q%]LN/3+KmcJley*_˅bÈ޸[V ZU_b-A+po}^UŤy RgmDV.dSsaoub9PYOb.ؤm3giLL55PU{rJ?η7l: Re_٧=cUu[d! ģhT١]o94'j׾מo_Gؒ$U%¿hoy~"CC]\:/@SUi:VTIirKb ".J #7-X|)VxꓠV39~< 3쳇X߃$N‹Uni ;mvIh7|y^z` ^q$9'ݵ6zlӟ0FSV6ne^<(SGcIvm0Se%"6y `?V9ˢi}6m. O!dz0HywGQ/ AaIi/kyEueX>#\WZr|:.eTfm[&'[.Vz,vKZҸ{m_]e40>ZX :l2_"7~S}1|/qm\],+gY H7$'wg>" 8Dft)Ռ#XIÂk !,iT~ʄz[tԚĦ)"c+J=/ Qe&>վcTרZZܤlGǜN~+Z碭t,R! "R)Uyy?U֕+&Upkn*YGɅ*%d"b(jS0ʪeRPt^ꆟ- k㥕tF7=tO󿠑YcrWlߝĶRZýؽԪ)m\+˳Hq؈M*?g=jZpFBZҳϽ&}DjR Fv˽v͸~Mg|(w3*#U)a5JkrR_Xò?a~&!?E̥'ȉT)(nW d}NAlL;@PG*L!r;M`Q\|NnĚ~#Jd7cnY"::u-([umE)~J\{~ IIau9oAgDel- [QXoM:5u e[b֤ʾfSY3%P0D9*BUWV%R$CLdNz,uiEuH!?7粏CAzr: 7˔KT-KcT%0,̗JLHsr<`Mh1C,1(P#RDUQrpw4#|Oq.uUg`@z4fŢi'΃5߿DR4<cko~ΞduI>=Ads[`ԋ=Uk ,J 6![,k^M]2kI5J2%%TM{ViՖҀ:,M-'2ڸkx6U57ʢ]y*S޻*J#X}kaZ90Ȩ\E?'+ 1 /±JXN8~!Sz訊4;t\-skԴ)O\j Bv,κWա_I.y&!co1A5ULKb'-\3o2uIlEw<«HTluMT%RH?[Q׼5p8M׆]@cX%su25S9pXZ8TF|ۧ%!BF^I }[4WfԦ]Zk }R]oY[1-eS c޺-a/Œ80mOv}L'Ȩ  RFN5CjvAh}dA2T%)*LDIdm]s5,[)몉~STQTX҉30O]X/Á\V٥-@iU-Z࣓ܓ()Kz*Ks궩->IeT3Ƨ/.(j\1)uILK ѳM*rBr5J$)G}):ykE+EpNksoTZycD7Դv~Miq,}m +VՏcOux_jҌVTm(v $PR<${zvi/]iWʪ V_y]{ $VwuTUdnɻm3TU51+{%X-e;<ߋ{ tUU7t/-ZJsTs,Sbni|қ->Ƭsf)b-ʧә3±g'VOӇ)yg_+eY5eQˋS9e]WuQx BӖʪ>Msc@vt5CeREi$]GP̐+.uhCq IŽ/˚l4_:g⳯ݺwdzu|v.ߓ9D03&jB h( 8u8i$6l?~\_~o9X B G;Sc;wk;.4֥f3od5vn~_uӶLR1Ӌ@>ֺC_nVɽ-fqɁH0e#!e&Ω#lխL2ݯ-v-%Q.,+{JB|G/ {_2,¹Ƞ>A,J"˘yNA5~ [Fu2c}/(dYV9OEx~7հ ϹK(oHXؤ7Ŷݽ\$?X_ӖW9q^2ɢ:k]zؘ%BۑM?jWnl˾Wϗ2xj:s1PٞmW(ϙ3b"f!YCA]apK??gvQߺ礠Et[KrDzn#Y?xs^% '9XSÒ~104zVj?WUgeVw*JJxlp VLdS;*?˽|˚˿']emݜů)el[uDfc7ٳ m']jy6R&0*F6:Ñ9eI.Ƙd<0,φ(5Q(D<(ē5szb_|7qM) 3!0ִ.+RIhWޟYNsRQ&ʢ' z-(BE_V\w1KApE:op,;}/}7an-cY8]K$n:){J"e!<,3, z4T4NUځrvkgWא񟪬ظa͒'ݥQzh1CS$v_fN݊6osuAlY'wbei^DoNqz>gg޼q{s]Ynf|¾VzLWZ{rEfl| AlwZM D3W:\fG\ɞ#DǏVuIgRF3yKQMiv2&Z}MqRw%}?Ł4+}zlR==swaTSL{TƚG(8cn{*GI8kMQ+/`C񓏥z9~;N촯>sޮb>-`XXʆ *(ɀfF|n<sOM\bR{1{c^j$'1[Wϛυ[|>u\[]鲭s-JC5oC_!hw;\WݤT0 Ct6=GAيUjs7Vmi\6 nL51=VXwʥ!//ľ}xɇX_#CW5MR&RTZ]nz˃MuA}} 6UnU>NU]}BtD]%hbf<;^#1% 1}EB5qAsRz|/FՁPry.J𤽊gW$tgyY@47X2'fS%H#&dƳ^L2mzQ -\ tgMҋ2qh;*2wtWdH[П߻C [3l.Cv}q,cpZǥ7@AIk֍y-*{(u^ӊ(n,uKV!)Jzo+NgN"e욓[TϴgQrԦj)ڕmOUn-އˡj6V;df3=cEⓕf_cBH/cjp,+XkO%bm" u] ppVўB1 hy+;UZ㔩2RA\cκ55וRE8Rh\UIfD5ɭōH7 Er ɶFiˬ.s6ibw]ŭ&vrr8̢ڽFljakk4sRm"]?og.;uXX R]_SeASgLŵݝ*$1x+î"E\dZ r}o,E( F7oV0וUji'J>"oXac`qAXVz؇R_/?,~*BvT+"5ZI>B5M/ʹkMZg`zJk\=2 F/|ֶ2g+vٷD{LIF~y2Sْ/d!,B8?| AaU~3Y]tEVSf>UG>]^YTV+fD8ll7+=THtkh {@Zr7Kʤ%Ks?ZW-,vmu˩{Nu{h_CźԂRT]BetviMvQ_ND=G Dcy.W6`h {Y&UnU]d,jnLnֿjkd'q:_3rEAz~tO lcP^#M5q\pW9b`Ex5LcYfB4]FV2E?&g,m_Mۏu,i m7uߓ[]ʲu)/ ߥXvzOPںkúf, s۽]qkB}*̫٠Z7'sKG!hVYvt'0:8ɻ4bOmאt<S>-R^S|ju\e]zI_tVƍ}n|x2ֵ>|r,1b$MA}ќ-d;Z%1 qZܳX뢼4zIṋ{6 !8[k\SlMo~ ;V9X9v%br~/7D%n,EQw/B64V3]U|ߟK~E/WǜLwE5Λz=2͓dԇ,ؼ6hJ;?JxKtYDiΠ4ˉgY- a#45 EۄY87sx(m:vn=羵ҜIc)[fɢ9 Z=pv]Di J ?t{F+304Ä󒨦 F}sMe޾nr$?̊lL˫t!y ̪'z:&@I p[&yiGq- 55nD-P7Ǡ3*&p*]ŏĬ\3nߴ|aK GXhd6~RԪJk}we5 jk>+\c6uz_b[8U2V064& #%6[TT}EZ`)ۺJ:IJB>E}c?oU-kRT4L)T!  5-ZyS@rm)ą,!>];_Miu Ed4$c8N!C8^4TSFZ8ZPWvU|x $ ݤ-3jx$e)qlK`XB0hKKUƺ zHjP2Cy}Xl.k"M4ޝKWےZOXa0USBmwRٺ-)+b~H)*TsK>s)xeq}v69Ic EA_-jG)U:Jf.-M@Cx5--{b=:l|,Dz(G`xiq+Wxqv;egIvz ʢdBEϾҬR1ΣK򒈟O jȂQ*+.QfY,;EsyCӝtsP!/uo*.Cq˔ZY!"8 5l`qhhǫP'Y$[vM\mRO2SbϸjRd%b[-*u3xBd{JK:ݾ?dIhMO}w%ךe౩–imWwE#U'Pد|a2 .u3V^r]&CWwXqugB`$FH Db;'`-+[KQ\{?F2P&壷 wzӥ .8BG{W>w [2gQ8s=^#  opa )~ʦ<' SO%6f* B^G[*j1;i>)br'}YŁ"F<2 S_kzдXw'K,&/0ȝ|jZzyh# j0roLJ츦Ĝ[WAQDjMA_S. Hϔ8{@*oQ6d gܱg긙콽O$e:n1ʄg;Z5bqȏ^YN(KE]V=3ZVDL;A餒],PiI| c(tO r2*z?'3 IYri:~I Ei+ d%,FǚG!|B<Ϋ!G8x%! {a68Tu  ͧia?ȲEaؚk8Ϲd[,wO0Q`58όy!-$cn n(0L78 #*O)UǎWWUɚboVj0 #m31h5i*;s9k5OptfrU&C)jJɼxw0 ei./MKTӪxʚ:_]ieڵ-_($߲eh\%_r0I2G"n]Kjݵ2BZWE[5L#G^J{~;J|͌v4~TY_o _Jepϭ:F ,0+Ҍ4eJo=oi|t]{MCPjtIזW*ZY*+(彇%akd'U(1LKOe=tZMUe!b7q s]X}MKa}׶;+oJ8rҏkfeI\\>D̿G I(D4nˮr={I6񙧘vժP4KB_W/"uwȯ4S_bu_hlWF=yX_VFKJ&_6 rڼӳ kZ9]{m}tBntm/]dRRG=[awOwIwg,ef`[i䟳Z wvlZAP Ļ"l L05l"J"Ga*G z]7\:'ү-PYfؕxt'ϳQTPvy!;$SUMPbjC d<^I[Y;1c3\ĎQcP7Tśӱ9UϩD7 đ1.#yyOy34bg$\7 Qv!:loVdwqxM:9"T>fWE3?j3(՞U[kc(D\v0RJ$rP $I3\ЌRTưĐe+{eFe"dM@/qCPf?Z<*M1$>v[[5KPYGay2vo3ռ$ fG%Է.":ƥʬMSVjOy j)^ϱy)jCXP)ܗD6~o0צk[7I19ċ.5ClUɨϽ(nrʃk\ھ4n]cY^ ҏy !y~>L\fA]Xpq\(knNՍ;/5 GTRצ?uzf/IuSdzjzB5A`̒A;q-Ef@;\ʒ/ZUnm}|dLHdF JoP/%aTm +i/:7>G]SYeyWp˚.v {cg#p%tDI1&֡T4?~mTjZ$enYOOz[54K4Xs/ cZc?bwhpԆ3=8|3*5iC%s]VJ7/}xcIIyK#Vt{ȅu?qUMauAk[=lx)6 3$ମPWf/OݺkՅOPAᥣ8fsՒ$ʣx˱ w8[ eNXY`9#>FeؒJ~E1`d3ո[-sqY_")X4m - ªjRO\~H_= +x(h&^E]5,+qދR)>jT֋O9]=X]6uS!ʲ )S*~(;?޶?œCz2Ot j̨oI)VUzfTj. A+*%_odؤA2V{v:bnν"-L^@}Y{2)1oi3/PʪI7*]UmBժM6UԂyr^Mߓ8^lTB@kҫY=as[z1=^Ꜫu2Q+B: ͊mRKtPSL;Ԕ$T)6 :PW&M"T(/c#@xDYahZ'5gl@d$8пG&®yLrlSg.+g;WǺvYf{lZ)muEZIDA}ӐPhQt_ ׺ll&oUʆTTQYQʣX^7RwzI%E|6ݝcךݠ(谰j1ɻba>YSk{ ̫9!=1i&(b]k'f, NV'࠳IQqHxΫȵƭ_UsEzF)}+**ƨRFOӥf$VXԴw=r\G;i)܇OAnV^3jT[?gzEnx^E(Z*Y~L1-إ뷉ͳI;ސ4shݙqܛ&ٳu4yթufNܪvEeӸKeСeMNRΛgfmc4QU>Z^Rp@Nh/É\VaY?ͥl(8YDeZ\/K ^(!ј4"Α}{Gĩ/e0}ɁpiDfX?,Af4-,|_n=? TS vG`D^_"雳$rNy E㉗l#I>h8ݝT_181uIBjq>q+j2ڞ9+9g7د sJ-muEmbCsh(֧-eqGQRRMWh uhaD!54R!.KXj{~br#4ZKSQWMauGi%lՇg| )iv1ie9.+Sv[*}^>xZ>D&'oJ. z:h46/W] A]a_[(+FX=wΡ޷&ViUyCqnc{XzݶIbzkN(̍WfQyY¼*eV\JkVVxZQ( \|xO(byy.>ICN :VgLh˺MmDiL#]e9 tK юʭn_"v|ɕMYgmfᾤ$qYVw=ĨC]!l=d>VZ bI.(%*ITi|[U'ӥa\WvR(/-Z=°DpuR& Np4Ȅ[R%k R:~\W&i}KjqYNjZ-H|J`JwğPkx尨dϚI6$)[2wj 웬n[b:Ҁu!< F>M"k΃MRwV}~a659XVɳ~|}]tKW0QSy*/MeqS[-K<- \V4Pڠ?ƾ$O%[^XyA/T-1=$p.%4*IL1T,UsmT>34HǷT Ji?Z7*C[ue*z[Z:O6%Aa*V7m 70 zli:P%^gΪ]*Ytk.7ZǬk+7ɲ1촎B $d.bEI߅QPP&ƻQ_شK_jJU;ӹkN;b &EC2nxI}4CPu_ޝ'!,Ұ-Lͣ+%Ey \֖ MJIMMOQ.ӄZ!#[KG'ZzV(ԧ*g̪9]_P_Bx&z2"i)",~@>%ZCeJz0ɆZ{r4k~MqF/JBYr^Șu.L\g.]|ōbioIN)|mmX!m#LtWHKK0UVh7;~L]\VDw~O, 25zLRe׌AlMX]7&Ȝڳ6w4x*3TWV$tF.͍Y )ꍫR!,?Cd-\<+èqr^_^\4u#}ܾheYy0, : J.3U_6=+D3ʷ̉Z_AzLݭ}۠' ]-3YNujCSbT4<逦U5m#ή唴'Z=cAմFm \UZTXM Rt$jM !E]CFe~k5-H*(6J;<˶Jk\]< nt%YTzI.ȓ@d.Ȗ,Qq\?( ZaSl"FiI>A`RUOj1ByNꚊ{ LJj'Kh-f|-<&]2!sZPxbxٸ`سMJn%YG!~/)!gm8rNJr#[Z'nMҭ l$ghK xE^ Hv 4=  !y\ZF 7Ȕ!!/HT+wtCqg]b5.Gx`':E`1}S{Lű+.+t>]BZ- $;xL:UTq ^ !1E8r[)>,ץ®x)cb] mbWhܤ3.q&)TFWh4x^BͬKr穬ۚX zhj%ġhagM8F^LϧV?O\%5uo;ֽ _QL*{俭boQO]A2>ciJ=Ev0f#>)bc-<CZWgRYEju ~~++,YT[=PV=_檌3u%~O@[lKF<6VÁc?ɸM0?$~Wx/QBs~eO%4) _DҳZ;OG--%Ie渋k+’ũY:2h~-fl9vK:1 ^R<9!N*Qif=jiVu7R!D־F=~sTm etC׏.H6nAJ}7P\P1ʮ>Gy)\Tv Wz>vLk3fgnA|eǛ >ڮa=>̔Wߞi;fUiET %"I'}43:CE4-E{8rd)2#1~kIA@ud|B+Xa﨏] X2Leô,"^ [^#\MReTiRN5G7:iNRk-o=>0T}lJzܾdUCng珣sSZڲhIz3.k)lbioeZpU_d%p-j-CyO_uZLR†\uJaQ*l }LYuv!]&=`y} B]²&XFjUKXT/9N} A]dN6ȔAi#)&zk<[`4,s*,z͠Hn:ܬnR!LkKsg*?GqR=S)?M0X~f`#kBl dLp$Kw>SRIbIx>37(K[Ky5V:SZ:CzѼ_ LTU~m`,œ08¼}Hvm:Nw=i]RUi[IDQo r۩ #9Z"5U>HڤuNuYtsܲs#'i$YG]R*R)ŦTU}:MI&Io#ἧ 9C!J'j5UGeԩ5{[x}jpTAҨg/.? O3aFOavQ%yI!"TE&`YKNӧˆM۪f `ت;)HꟷYGu#T^2VQ0D5ydf6!Qv2#(vW (k!SԣRM[DbNTdG-g:P[§1U\VjOyN3}]T%!D,2ʵ=tuV6um))6@R0ps[Yx2e!~hQ,2{|WV$(SmږzYx-^< hٷU"-QbzΩ-[|CmB-|/2#VǑ+ו\c{ |k.gl|v X7KssS 3}iF9|GQy}hsWj zRVTgײ./Vr򖡩%xjc.sHz~ w9gwG4*}l&yQ\%6MSh Ѩ B8+Bȿe+#&qtIڌ>:*kka+4=+9:bωh(eZu#+ᩒuE ǿW>4 #AI|몶,LN&5Aj;˅/S/¤WU:ZQ5P]%HK4|'MT>|[D ~u)nO?YJ,C+ϫȖɑt5V]DtEp)[ᵒ3[d .:/m(>4 <ֵcYVQ*^*l$غ ܹi%ZַTr8Pmq]`뒾¦GoURI/:ne5.zNG}Èֿ>)pǖYQ'\?~33b*"[+Z-1{N+4~yE;I"~5OZ$coI0M>F0xfEPKkYUsXҚR.ʈ.Δ'{{sNlcH+RPiWۄZ3LH/ ^-4+ΐ[']5S5ZU%vP!LzӫҰ"ZS3hf @k|"ͼ`\mLZ-ρ=uwܵ"Ԅq-sjʢo]`o էmFZgIv~ bb3e,exʳe?R9Hrl,a NZ4E~Mjľ#e+[g_4cT-+VuQ06Y%T#̲1yP0i\n;"k{]]met [ BPa|eZ NX%'Vƪ{JY&H±k߁hO8{Ej0nSuT=Te*}4?\U:Pyv)=$s蓶Т,bUXH4,J=ym=ndݽr] lO حyT矁zN`n -bʽcpIHy Áh'׈n=1j~o=RAO%q7]>v6,Rͤ,,j9&$S%хZdN^'4)ҏpBL<⠀<jzYlח-i/+Iq¹J~C@R (z.~H)T3hMa$؂1_+B%`myR[l?g? jfEQ=>iiP\vV{iAH:SC 4Nꏹ<޽݃zv\v~uQ qY7[i]DS}LSk P9xz, -;uRQAgԇʢMҊܭM 8wCx[$:|ieyzy̾u)89ުUE;գ}GEC9cg94ۛڼ:kZ&OeyӦ>f柾&W6if|M+CW);ebLYeƩ|ʊr+T] { Yv]@Lu{ҎVeϯ\`Kdy Qu|=+b(ԶhXǨ[%bo߃Rjޒ1XO}%FmWq|yJd46&UQLO%ВfV*W@)C:ĥ66⨺*qMT1)C-VY+^JWN YI7:fQK)(7V֕VRau%_Ѧ"^Q0).m]I;)S KyeRsDg`RVBp%4A//.@RXĩcgM<5&[>9]G,U("!ебBԴ-Ϫee2*EFsKkKVaO)u|ʭ19昵ktjQ39!vj R9JuzoMUzqZ#CTCnC\)>!k!ГbeChfL"C:Ek4e;aZ[;[F܇["/a R*$~4{ 4\ޒ▰mLMl.SwӌASFfKJZ5~lźf#aOpkxX/4ѺjI}Q5֢\\j)t TnsZ;jt c,X_^!G''@p5ھطb Tk"Yas$ucXǧqlu_sF0̾mX^* Vf¤+S p*L G[㕪}]1,y_ w&EٝjPJ.MQVCџ~+9NŠ5)Sy_o‚jv"|"o->7/iN ׫S_ѐ`!`ӛZJ^K5(QTqsKRڕ{o2Yw>YI];"mbgྦ>SWU8qMcX~( 3d8) )S~jġ /MſDCK3Z=IWJ ފ}|'+@^j*԰/d{QF.x|gU"Dӊ'\;Vq?D=Ǽ2,n9D.t.Y9pFb2TjސzkQzyNkx󶬨:(Mzk3t޼$nV H~eٴ qXV:Ȧm)Wd噊wviz]rBO_6Z/9UUUaTk}0 0 ^2w ICQMnGB<"tϚ#|NRuÙt:"\LmGt1̪̓:*ȅhm|l^5D| x2RJ^-iP$-*#dɄHKEѓQŗ$ʵ"͵2%'Kmu]d{gsXuYt_\ך| $kEilOzԷYjjKB+iEY꣐կӺ+o0Kfddm/ٕx])WOR碗y(<!ܖn-FckVawmW׆/F]Peuu`qT [64&,Ǩ~ qktrǫ--Dyoi%I)V="йwZ~*AOdkQb ʺͺ)ugGuE_j竊BY"A$Un[ <3/U%R/EowaM 3 q$+J_H-AALmmZSqd#.Umugٮaԫn[~4Lc!/ϛNN'G(,ʘŁqZZwZeN-pv65yUtu~ %WVEw[cGI=g;}«פPZtSE]]RkJX )!4ڶ~M)]ڮ*N= Zwg[ɵqfThTe=(<xUa5$ĚgI|\7"#\^ӫKU(87Vݘ&*5 5=WMmBPS$[ؔyee<]al%Oj=fP`EvupŽ `k0U+KP JSUj-UHB-ZIE) B(C[ꪆ(xN?J(oO*ph9G4*_%4"BO4!E6&(ޖ L*ʣM%\s>~J"IO$j衅@Х!x!8tj=g/\.Mu*С.j^6!'~Գ YU+KanI*)Đy r׌ށTS5h1 )q~Bi1SEcPB)զ}'UBbmJ) oIihR! D!H_?˯ ;TC TQ~$L%{H閛|F4b0 ,ߕ 7ǴJK3q=R_fSW[6́Xʼn_ԇ-CL0+usmð.*/kf^;.Ƭ ܝHmePS.KpӾ/{uEDzOJ>e5G52&b(g~cD1e/㾥m8OL+u&7ѼP]C0QOjm9NUv]XyH6,4ez9ԻL S^r[dUuNҋek`Z ft)ʠ;14n?nQ*̯\lEQ+cnr4,Rr1]9@\ԦU@`'\ƅ`ڢ?j46:= hٯj6i۷Y~5zlULI?fI9cX'⨏ahŒ7:-GWJ뀝)Nj 3&VV< e|B2hJWn:6ܪ(1i~ryHE)ߡ[+*ؐ}kT`a* {M9T%Z](eP?[J)!q0ң>*y--B%B_R^aMiD!drih*!HRTPHk >G:떯QD4t%V^ B<&!eRSQM0KAU1&t2^ $JZ{Kzo+x #V%F$$^%(! X)RjN׎eo=$s_akR&{eJ-)"0).2M ҍT`IT)PX|؞f53\OFx$En 덉:YٶEY1lnAVBn0OIRR)&XnN`4Q#tǿ8Qr(r X$:J4Ǹ1-!\1<愩3ˋŵ/ڏlߦVZ=T rQjU6PIx*TW/I>x8rΤ`?D=~]z ֻ>^BĒ=M3\kx=ԃnՈodMѺR.$:M]椪?WGcWֽ)but9Dc$1dI&I3.ߣeJ}VSw&U7oQܨKL# ,[~lЄǧoW%HQ:ef8OIWiOI0OJRwƸ#UmR^z* #SV.C:(H\ȾSpWm,3|TVkLK̪$נ/e*ϱ44OO5NK4pGǺ[ە ω)6Un`[p=Ma^EVkPCY,ҪilH^*+ũK{6+d͸+\ʳVMcG_iҶǪL0pWR^h)H+r%e50HT\G꭪Z9[V!yuZ"< t> 2YS!++J4œ3-:^cNn!=uvNG=U󜗊*K>B/KIs[FնNVg kf,ػ2ȇWvԶ5(*=P杣dr58wJA檬UFk_͜c۽;g9T cj[]"4ݪd(5U@d%ŦUz yhq;qߊds. #~;B0 NgY]uD[$oTPmgBeԆ(xi縯eFb$J1h Z#gF#DV=t;*5Ga.䒊ȧ3LT>& R0O[h=E(7AK;N5%Rm VŕyJR6XUxk=į:LE[@xj>ŪL[0,_5fY1ai*I[bܮ!K<]dGSI&1\#6,F_ˬG柹9g~6bk4ͿpUty(tNfvo"w=^u4gͩ! Cs^l޻i)VIF,$^ěF]$ɴf87_uuq c #mݪϾڹmw,h `Lp6hWBI^vgq O^kGyU#aTJ2UT8 Yy,m_k=>O?ǕΕd2 UCd8FƦMZEV1olrŷwSk>)1! fyMt WhXډF{V5'<]ּ/.$)𑩹~w.2Zm#w"C͋|%Q0F&KYl) SiE? /H:h6'?b!X=+8DZAgvƃZ_z_1'ʬp۷%\d:oXT7)>W!uPf),Þ@fTeyP3:U(BPЫLJT)!WvG$4:|oi\lREX^SˬUF3=6U{m)pT9OO#8ousgVE%A_SXT鯍;F-J5kzqJSrBҙ9iXƗqƗ V+ufzyG4ӂE5T^;}oy|-+fI WpK2׍ɌהKjS׵ m-Pc2@Kd .:$L{~]iӪv$E*u5NSvP[{jMW%ZkH`"gZ_8U濜"]+Zp)w> RM(U?v.ֵY4${i.eFGNS~ްt%5k^%_)Ȟ-Æ:iuj~~t&VZO~-;iu$yh4fx&N.%\mJE"mb=B:[5L[5vIsD&!wSMW}|mܔ`U*-Uuza{b)&1o-$|T:}UAJ1\-sψ+GE$JBطsD#ڕ#Ԗ[PMȤcuk]&jbl죞2Rk"2AwN7x&!<;a3I?䋆DR:b>ڌJI@\YU&;r5[\zr]ZxBMA^VgmJjQ]]k2W[fxW򱬋QEE%4|ghu[n3|ɠN)fe.Cz ߥԵU$;<4zIy}WD%oA]UVE`֧ YNw}Qy;<>aT`ʼ<9 ڥ%c\MFhi҆)6,_*}?(ꨉzk&ύly#qX1ުҲ-sF*^zp̣UZ#QNۘ$P쩨c~", Ҕ5ܬSzWR8ou1aD"߂7 ܩH*3XOoqkX]i<_T :js4L( KcpFKarEca yCFO,.RQ p(DғXRF#*/ڤZSB}/"^ kIAE! io2#eXYn+Gl:|ZI5ߗ<0JTJaDr )eAn I$Q tFV~yFyPZ҇i<$<|8oy +Y%z%a'76#Y8⓮< dȥ,6YvMJN["&ӦUta^PO1f9]Q)ay_Ql+J;]6A|M*i8z *0ϝYD7E+mnc|Pe&βxYli|eW'qBbT =֩zzKؼǠ+$Uvb-2ۤVz<ɇ( gK~'aXdwS}`J[m-:F/)8 u]\eXg6X!S5W57bٗ٤ \#f*.T)Ux^|I KK\(64զF=RN5k/\.(etR.e(ѪS~GM3 1xP}cV$Z~U>T|kӲL"l]ڔCprQkawJӹ[y$A?WQ/"d ι%m`H,#_+jc*E͝d5vҼG_|({ԫr .ExQ~TuJSW:%xֲSKK7i&P[iz0?u\}-j4&uJ?bx1OIlRs$WRIg-[^SvXҾE~{>O8 ꤦ5xPuv!_MqJ?Vް-y [pҾS@N˷,b lS&wVOaeAzTm4w7'^6`x1]zg'|(?ucAbU 8- `meY{:=^m7RFpG>%rH1KF. :$>m1/SEOUi= )ǢgjJxQdSs@uo_YTϋZ*hYjPYUmҏ.' <ϓAF%(?ʗCZ࿺$Q^q\ ޞ*N[@?aK;ŐVf47,xE賩KS {>"YQF%Hؠ֗ZsqڎB`FhCG g ;@Z8,"_Eߦ@ +ioJsdΓGx7Gn_ se+H0x)8dn7DŻ\ ~! 8{.suP=eMcLhI]?)-0td&^qG<'xǯ3unveOo~=A㨏=@~Lb\煡TU=Ri?U1krJ-޹_عb.eZdXcU6HWz!;{O]uG nrI.6ɻ_+ B{{4V9y n5?dyUCV_q=do2a(ec^+@oZ?F|)9r֣ \r]ErC=E^?K2ǴU͓lՁ=J˲(kJ#idu 欫3zֲ-6HEH9}hiĀS1A\N₪QU*9"{%r_16qeU7yppMʖܧo5/VǿF?9>[fO? oP^NN$'3`\/ PvL1"Wit\6%!Pva7$5\4r쫏QHWݷ·<+'$`d<^kbסzEA<hNi^ӿZv8waį*ums"%#tmYF)!WpOec1m5dv&tӱʊ!2k|SYܟkz{,"`)7vU)cG +C D̤}Q:gIU=AePUVFFWՅG`଼hTVQAWr;59KFxEH( PP+E,mb?UqGL2w\oTgμ/% 宔$;H{=FDD :~ ?tl[%N=4^̹1fo[*R F} ctҘ*r,mK(Λ*[K>eRu7q\$M-iPY$8IH=D?JikbIXE)\؅g_AhxBʖ|JWrfI0*Bʟ6ea^ @r7ًc&>=}^SvSn_#b6,/nڗbNT7Nv9'85=wm%+e| eʶZ'TQkO!s8p˰/฾Sul(^ hNHQS1#]>d%]YG[HO F_(u R8\ƺ?yO6&ρI?L%7 ֎ٲ ^/ȵ=vaVVjJ!L\z94ķSl]]Î'h3؂N^>+h)>qI@O銓W+^[tj<)(3u="(F+SRt'I|ʣ554oyZ=C4 I!7Ejuwwփ*ퟶuW'@ۤeq[^"e+1pS?Eps +^}ҍi;&%Ѹ+4XUo7W~{oK&l1WJlm V=X{ĝq\#+*3-j-|m@jP!ǶQ4\ќyq P !+J} *wFQޭdY, Sxܟ.e 'v7%Ծ6=/Z,Z!OCQ.¬A.BX)W<]ZSa#\E2׵=UBS[xr_շyx&b e,\GƹipJ3uRߚإ2rzY8Iw֮9tlXQT}O?hy6g`lxx0,zd}itkJZUuΉUT&mk}EH׸Ptʯʤ)4S攦'=2l+4vOA"eLZ K"p4Gpk0&x!FՎKhK|YfAY|+B}IU8E\.:#!Jz͜t:Y(!"^%Ƕ0ҏ"!!ׄ}~[_Cʬt#rXR84W4sA)R:ZTtKM!=65]K[gWU%N 4{^-n֧,s,(x˟H @*[N2j2 #^1,?Fj"iB)sSl4G V?5/L&|zs4YK-d!9$ .xNOjjL%r/}Iux"YJCs7Bf$m!$Jma5Bywz$(#'g<k^z>։eťBO/wj&uyiIR[[1A?Dc5s9,#*&G_^Z CkZ5uavQu[Yiyl}a󱌆[+JG:O+w}2,EYG121vO"`sȕW ]^J&=I__5$W''/Ai5J%8pB.J2zS)۴)2w'2]Q Ä; <ܫ ɤҏ/C!8dKjEDqW0,[$?s_k{]=ewSO٧2コvi7Irv,Rc\p sʶ'-]9hi KY(SmNZQWjX CEUlUdWjח*r7_IK nBuZ"t1 \îo0,!K.ޫ>>:Ȭ PlEah }9|l NBO/-anjRބNuU?>wdӖ6Y)!@e\uEj_SY1),sAnYf \Iw-8p4gVQR52a_'OJ’ -ʤfGoIfAGraJnzYeQFiomL/h;j nV Тg̘)U:2{}X SaL(-rؾyU[jjVVYxR)ґNeyT3pc*rz6/u0)5BRG˲Wfsz߻YZ|j'( 6)i,+l"=oe{J6 KP>ytM’CnͧvKH#^)++!tDOIezaGM :1،z׷_`W`X ?RYSpvҦӰݓ C,3!{t"S| {JMy=ڋ5=CpΈߎ.ܪ7:%7{Yܽ^w~|9f\Jj;4і1ghF4! С33D~hЗ?=wAP.fy/q%.B~]T|oe6?:]mJt%v=MfWѩI{Qo-"Y_JN_bb-؟>T׏QHj|Rp&OZdӥv)ƿ'y~ɉ%'B>efϨ=PHw|GѬڅQhlޜn>&IvRVOl%)Qkq/Ki8I0Jf9<Ձ=|^h%ujE׌_0nbsŵv 5`F{MXU8Ei~&؎A0&RRU>m*28Eo""ac|_k?%??us뵏Dp3Mݹez ynp;ګhj oʝ>;+g<*B<]/uu^YTZ[ <))aQDH0k$57ce*ҳ6RGȋT֦yNwHh #JځIL*u*RڜK 6/՗rQ˓(O=Ƌ4,I]OW)%Tџ-.;˳ŒX^+ȥ$k<-2d3O]#o͙fFee7P ^O=}PPj,/[X⮩+lݶN礑gi 3礎~!Q!'l9;6"&^  aLTnp efSlNoZVIOJ:b!y^'Ѹrtr/d0/mq=5[+]E#}V&?ئp`5j-X1\F` %P`ѫB(:؍HYFQ(f/A-r!7t$!9ٙ_1$8'>;7,*7=d'Lz$+Ik,MU)eXe958G65Ԗ}2|v}rey(,W\b쨯="(YKsH)`JIwUO*V Yh$[R+#yz *k,X=t Ce'tyJʪ>5Uj ZNeGmfp5 I^C[(e+*-8RM\UFrz\d[{FŶ=~OGl0b)\68n/\QZ֍k4BMgKZzjQw~PHY_Ѿ-T(˽K}kOڦw ZZ7m֞2ۓbw}][T{ױm+b1nƉE@O]y{lKKX}ϑeӇ?"YuUwcKGcbi_=QnJ^˲6tFEAO:af%0hs(E%P_CTf_8aHE)ۯKM*pپ؟mm4CgdhtϯX:_!.#>Z_v6b-fҵ6%bh51$KDYh qRM3YO)jUV6os)m6Lc^gugpdR䳺o[u8M4i97soE/uȪfu;xxQ/N*!ʰݿNstPMZXfk->HLwX4S?mi{NֶV7$a vk zTx=4.6`+0'wCnO̰M!`\sU,rV"9'ܩ0tSdX6kj_ǏƹŔjΐ $_Ӊj]K"`z{PMmV~Lb,q1VI[HiHf3M' #&L4`秋]c) -J*\d0t"U65F2h{*1h:[b)>ܾ)b] 44$%M/4Nm&tɪ,4R65J&9{0ĸX#Q^~WPS/O&I}-uTY.:j}ͺu^in2,Ӹ}+Q1t峙\u*RS %Og}hI_ ^,OWT)| {Z֪2H%NUu =眲ҟT1 msLIb!?Xhͮ)0<&j T[#ZZo+8E>6SI 6Mac~~UmlliQO-vOѲϢ)q/UD#`:Y-&xW):Eѣ-SjReeJZ_8pLkULswMZC"xa.:G@SG}HHa7$ m꾎⮆Rq!BsN1+6ռb,"<'E7.,]slK7f%UoBPl[UKR69eIBKA#ICT'dB<~bjlBf{rYFe*|aIq/crGy9ZlOitw&QM婧-p#6ujJ4 kUUM-YD:&6Ȯ*]!q 6dvx%ŴF'LKt#;@gÄӥz73}ʢ2XS.2i(C}BQv(rzRWLK4Vȴf;i&]ļnՈMEt'N2ӭWw %VR>Falc2‰!q!#=uit $w~PNJռF+,/:jw\MG_v *nDzt9*"Hα{>]V(9#dXN!OwpJGy)H!肗.fx6݀|#{ ]7xMo=%-hRLU Q$gye'n쫧AߢAy-]U ꢴG" K9NWq+y|Ecb( euzIMI'ΣUWF/M{lo(,j߱W5+ҚPS"AgOYiABcяM5VԢ4Ә< }FY9[)O\'1OdŦUD\V)J!=?}x>E(/`ԧ.R&[I#,KUE0/QR]G//s~KCe?Gⷊ-{׹P7;ߕC<4°"YQ-O-INNZΛ8z%TUhL!c|=u[ *dԛ_pUqa\؋V!F%Sꐋ<1dlJ~:ouphܵV[yGdR>ZʳA/OnE Nv_Z;Sgw8.-w! +"]ڗeWg ?pȆ9oEj4^=!Rh+k8jz44>"ieQ.m\2=1ya] !/U7l~K;Fman:IvbZ36峧v] ^:"8DTw% k=Y4,IY9T!Cq_"IC樾(ϝ:#߳?K^ xgITe^O=e Xn|F3_8ԝOyJc8.omc\dSs$}sn!A1"9F"f=|D%ʎ$^2]B,%99zk Q)@qgM18I&1=3n|;H5s\~ 5{c^xr,v5F]s#nEs\ttCO[_l+g<(CH;Y-FTjw\"No APmui#/x/yWX]EիYBq֗eoܹ.bJԝw3-21ۘD' 򪡶Itݦ:MkhHĚftiCM$KĿ8lO[%GN%z+:ٳE, Hi2SIQ>5]X h1IFy?eZqzYhsلh;`uG7N֧0뢜@E2Kƨ9IkeON Uʸum\W=T\e,=ef(l;Zl\>]7J?ae)k%Vt䨷ȥ=֝) /OuatF"odNאb3Q,0_-kԴWŵ]`Z[ؗn1˜x(.SY_.X,;0S_ W?YdP-Z4کhKrQ6oTВS;", bwJ4I݈_-"]}}RF43 FyI꺤5:\iUZw/(II+V4 i̽i̾QvP68YK-5 aر^t]DW-\l[B gp籾Eg9m#9)5T)H5ǝs+NZW,єu+xRԴBUHuM6S]c's|(F*r9oׯe~~)ƙ4Į B;TR0T՝j_ smni{^֩q^QIBYC02| 5ECERKX0wruN7PR$Y7aZmR=XkՅfEF]4vEo7u,{&]2-m/3-kqŕUxGj%\DKw<[I_k~]xt Kk !Pv4HѩrvԣuQvRX\]2eiFECYE@0 5 %CkBb~V@Xr) +;&wMs_R8׎Ә흆Gi,^1FY.aU4[K &5_^՞6NۥьtZ/ɣgXQ泇rMG5Uqp!ܡY@p"I!ckTJ.=2-%V_F*1ic:/RiLPFq l#EO%lnI4,*ր;zGʂJkAkR*zTTzƨź!mYn*K"UnhqPW%xzO~Fa⥻_sW4^wE6ȃC}`v&áٕ!pdzro^ fj|-z)BW '.tԄ5O^ӲaR/A1cm+[Uc{vv\pUpiԗʮ9n̪aB^y* ɣ1?\O3JV֡DkPY6\ۋ)WS-mbΕTiU+ R%]S>6\$L44sJٔ[-򤥩WZ)EFaұݢۖK,E[FXxl;W1ڳY_g^KڐKв. /.̼z!df\ILz Ң9V%s޲5!杚X稗Sg>`ZJA-)8T$j-&JLn'idcd[E y 3hH z! шxm}i]eY1*colY j?g-oD=E]G̻Og45` ˫=uĂd:ֽVPsm]} \[1x2ݽTT7-q[Jr<Ɠ|{Fc0nV%|I3y٭{+zlʸ̨O1>gȢ0F, ‰?5uBts2VYK 3#{%dkmA䢛U>Е@G,jKgN;YY^)vYMV]'orr~JlrꑓeY{+mʩ ҹB5De6 k-ۥ 0_BĎ'^1=JMG6eDsD. )dRq0j2I"XF%ijcVGէ͂vsbFMݿŦmRM\S̀U-۞^Uiq{&^48VjIL['i[r kp"s;"ү JrأC@tk ޴9(~*MS<9~ӺFyz9gjWʳNG!p 7 W(-ĉ~Uk^5)!%_؆ Rͱwk9(>Un#qȾt iOX% p[̞cSaƫr㿧C m\}>6Rj".L"Je;iڥqE.ꖩMZY=4OpU&A;Uc'!s<9nbn]Y{k_M%IYD.%S{_5Ox뀺?fl+yǼ$l#_X*7\hY9T4n=OTeO\3Ub7/lSOR(Gi{FȶvWV=ś^ *7uNJ)NzIS&uRTuAõ{<(.K7tZw&Jw.}~eZPum쨌{*xe)CWyI ߮΍a^nU^{{h{CO$)sW=J̾2}.3*zJZDŊ#ҭU_ ?s[ER}J@J2,]MYϙ[A=B1'CwPə9}&ramJb^B=]D&'_GwKIX]M5fX*hUQ,+2a6TE%}D#pH"xNE!Ěp4B Tj4LCHͣCEO ú(7r|`Nv񕇉%B= QZ3Bj v3HY ɑz3yO' [udR('5ç@y\HU= 2VU*%aFMH,U{]pwV3%q/Mz͉[xEyO*an}iDcUER)^s[cϩ]FSF#VO N!=籯ջ-~)iM*+>jR?jΑf?i#"M!+pxQhDh9HiBZE$E>Xt"̉aɣcǥe!"hOBXt̍zȐF9J(QLsHw@<;$vAXW9,@PiK $]9!EH7 6&"('-4'hDI5xN+a_JG4AR{'kdmsgb46yюH*~,#߿ujjZ7EG# )#pԛyNCP^ B%kv=7y-Rɭ V#̔,ɉ2>gZMXL"?0|ywe"%7~rW,G>6 Y{5QWk+6Q=X%Q;{7¦iII2VLdYJ&B^gתu^NW7ɗcYIRd2LL%OE"u'X&%k]XU>mW.)W;OeDfW\[)~1ҠoޕҦYQ5YL|+"lNqaDDRhApOSM2_ sWvŽQ{GeNSDEϮڔeebGWw?e:%4 Q$ĨˣB"Tͫ0Za\ky%jDCOR.=!K-k6'lyA,rƩ܊VY-)MRێk #J+\çQJp$}眝ԱȄB9H.-2\e C5(zQ\~FtaTYfƹ3袑R1#nz娪~(t{a)QiTx]6UFnUEBLS56{4M'.oXqR֊I16̸쐶Ԇ9 k~fⲊHV\X*$`kSCΗ)SB\ n1*qCY&bUS)qb]:)E-4[XXRT3*̥IfXdR.a]USXk)E4#ܪ* ,BU)B*}IzGަ!,(w BNݠ*ܥf X_1yf4?RA:Vk+ P>]cmZϞy\fQyu8YB>UBbajit%U] aK2[}rHgBh]H~IدҮ)L)#@]Pқ:J%wl|uԣב34šn6u :%h Ȫ}OY^4t,jVm3&˔iVFP2o1K10K3DpEE ;UaD"M"DUL]aJ re_/#߶Эt8V}֖qiN,~Wߥ;UCB!UU}(Z+TZ,&F'^hYU[oƘw=[\nrG>`iU!d?!5^6ryٵ_;U ~iNEW•|I\9T/[k"׾_Cr+ZU:&_{IR7ojK!v-a=fVvh=)?,) (-G3IITbdr@oP9l{FuR'TаQIw(on*Ph!]dzTш B@@bv}={O#j}.ky7Iqꭶ%Hf}u=u)o%δ m;$gHS6t{0K5_etockis.:-ITO۪ӓKQZ޻bT-B*TI, icULb!k:Q YccSS~Kj؂T XY; `ΙeL4UxB%/X%#XE)Atе r^aSjyRYkO 3vk$̯2CzHJzbEZ%.% }K 3TQ 4CKUOކ"9TXͰ+̬uM6)f S_$RlOS|BRUTMKFCC\Asd~2>O+Wl-=4QkƤNQB ;)ڽ(4[R8TJB(hH~rh 5,qnPA{VEghՙXR(* LTm$B>1R["eM_:X4|zQOP)fѤ9Wè+K N| tyJ)Gnk(mRjKK"IY(B6T%8yu ҘDŽ=SF$n"Z󼷭8uYjaݐyqB vpL!*ِ!zV,!0T7M:)/=m+sIp1$nJ{@3,Ы"~e!OSwV֕ai_"5!q]jڤU?X|Cz ~qilblEmGY7J5Tu*Ko!~TY>vIKG'i/Hw3P2 I"q&nRiEs!4mfU{.3}l/2Vj?^RYb2F!oJD+ٷ>#Ft+)ߦ*_!y7c}ɡר6M&uJ̫z%nGuxvȎ#K\nM! ؅/-7_8w} 'Cv:USHD;&Qa)Xg:0?1VX sM)I8=Xb,!3 1'غՕ~x@%Qn~j{FGEYaSmþg!pXSO欎KL z4쫺c]E9 :C*T4syKKh% |G&^¤'*|9`,gu{0hBgx7i\:x@x`1:ImJx4q7WMl+ U 0, 򘚳6)GbqdT(MG,'Rᧄ)/D/Z#`wR@;kJdR?KŜ?exC2PYGvȂS\@68l.\XDŚ$=L%P-'C,*؇C?s/ HuQI!j|O;)t5g"yρ%yb gYJN[οY^ʪI4CN)I~aeA_-u\5]7/E~30𬾇u0۽̙ҞsP+{Z7ɷX-;׷޳\ڿvh/ҚʤKs3r5(ȧwW `Rf]npsTrῑcj .%߂wԝWi.ާym5fT L>WuB- )P Jxj35eAWzO)q,] = $9i8N;CQ܇*`3 fqzВrQbſ'U#̛Y̡ ?YڊGfv?[QY7f9%2ʗcG"(+=pQ7?ک?9.m%hE3mL=URwa/AV]<ϖqw^L~ .4{KJ˨DžQx#|FA.T^DbT:NtyC4J⺱.iDo!ET5}))˸ r^V5U_EL+i5KG)Ml=V&QXZqk~z\+(׎%e{Nh7OK}޳kD 2 В ݕW[mp*W aZͣݯk&4U%rלlT$lǨ黮E^*群t<( a2!;dҍgGNfCY.]{v6u>vzɳd>FF@_n(Js]D9wjFk>CW6`isx_tIҿ'0$oizh.8C<ƀ1/)?3)+z4|繑fH1YAiYeoMv>m-ԠDCV>^ꃅ(Q1ėAtГ(يH ! !$ ͼcj= ɭw3@rB!YO]DDKj9{i΄=,CŸD ,1{)a67B$o! : #E骢)7!B>]Q4ZM&txnF#Ge ϶+rd /-ûCZTe]b5jhչYOgUNO5k۵YqNƅ.i7-+말ɜssٺw.#ݩ?迼~/]7-{RY7_T+*$QWy;XIO!NFj|E=biFaM.''BmEa>5Z>(s簥I&;FOzkf3 ِ_sPUI%tҔL; j< r]yi] al]f,.k:F5k_]MlbGID>POtUhPi晠t՟S+N] ޸)+D/:˷jr;I8NW!}6/Ty[K|rէmgP~2-(1(sD*˚eVq6Lq;&@;dƒE"<~iYNb[vrx39oE (ө-J}*g(&H+͑OFJ|͒'67Y}' ش,iSH.|=}Q$S-G`ž)=EΙQI :Ge1gy{SӺBU֤MY'*nF+bUԧԭkM&YщXm*l?!fXT 21K4?J&kJ4o= ucũRZ86 k_K fJk>֕Z,( FnU>fňzjD%X2 MF~[Q:h46t,ՊV(VTm/tSx{#[w@=mnNkΔe<|$A5;JI'jlkǣ*:7oZE!&퉹ߙĢr<ս߯W=eW-#̓1 $e.'”Fn.g⇆@C)sܦ!pt*Zu~3kJa\H#'IAU+jI5wK\XO'I^PT0V!oKڇ -`)wltDZq=Yªno,l]$bڕroTlC7مFPĒr +jM1XJ@\ޒWhT7Ae -TZo]PUZZ;rad$#W;TJ'e6NV&4KCdQ! Zn7ˆ}*➲ow>Q۰ߣrj:U(O.E1Y4Յe]HM! ԳD+:>E%{9e:#+1I6PBqdR舽Za,H.tzEH5 4}]ЦX C}LB0+a z Vڧu)2fa!w֦MY M@e$Rxc90re!Rj >)cMkbXv%}zD$V!_w4!Nֶy@p\5wHDR (3BOTwA\[uaGN:V9OSmZQwإh" ;JP7N)A0ӄIW⟶: fk'ޱ> D_PYϽfeDD zCFh>C~Kgƹt':ªQPq2 y^dnٽSIznO),g I9" 5|bdR1YUY#A8V/NPeGRקWtm/=&3'W`y9༝#LLS<2hyj_-N?؁!|~=~X_~bԎoMD#+&dn%hD)6$F]  帾 BT $RM!m5|r@H\ o`i;r^}jЭJ:Bஞ(.)j\-jE*}ȣ%^#?4佰b=w*a_SC \yg4p19XCrhךY1S)gF)%)}}J{r^PSkJ#էb5e=d19aߒ'merAwMuD(z:EXѝBH'MIw*)0#ag{;NQ^S/K#Σ;6gv q*F!'O._I Ac[5yjJQ z+6/E.-?-M[6 l+̣ŁzY:FHV>FQ5A|u~B'aw-Hq%yNcVeȾ*LŒ&=VGYPU[nEoơҊr9 XU=8ǰohlQlUԦIH Mê]vd ]HKOAaf=rxC{u=fg骈-Ҫb4~ !TtTi]#V5eCB)ӨXs/:oa$-`e:I\z#V^!;q7rTab*ثDgD@^yqw ] ~V$7rjmy"(]xPap^Tԍd֙|]zkQSBO[MFo{'$%TɬO1N Ū*ry1 9JHL].Ȍ_bmt73|/@x훳UVg&wd[%qG5mA Sܻ K&m*(&ւ{O¯곎tr ;6'/BxzYyat(%1kG.H]Kʰ,[SttxU TsYzN5=ܬݣU$z6UT:d%e9TdS,zK9vR~$kn OKE&MVu71C(FAܕ* "i"A`,m qxl3'$ &RU+:ICbG$PLBr+TR{ni[[s,n9?4,Ib hF*U:UxU},--QUe=AF M&:D. D+Je SԅK{(h]YA@WӢ3E^},cwNJ)MF2.;긏-]sDr G):ϵZ_Qv؃O}>~5A'3l+YEbx8mL<H(JSa;tlWV%qo)S˕]AYVay =xX_pl}ctr92կ VO)J܊N5M5ԕ(թG^_âĽ#f;Sd#~H0 訴OW_U'nش{'E;RU0T:j\!*XޜmlT*/)>WTաaI3,u,gv~S0Ϳ)a|~f+趨ޯN3c_MbR`-$= KnQm~t7vEN؄/~1%aQY.o~̯j4n$.okRྔSOբʁq=wǝy0R.]xwfR^m&_~"S,uPT^"%t& ˖I㋸#G$T ܫ]8%|0{4P>hJTHۊKsj,xaV< Bo ijQmQ-sk;aVgޗٸj%Ҧ ډLLn`;BhWW1m᳘0 0V<~O Ke~SE/9rVyB#ִ6wc 9xO7{XLqRJdtoOIMnTg19'Dx#hЧ7xzaYa? 5#]{+#6۬St}Yܮ uNeS̿P5F$o)7iM %ogZpt+=Z9 **⪯\1W䚐<ESҭ£MCR_Jy&YKm&{r住maoG9(eM, %5Ź9XT%|5 E'ޜ\")Ҹ(' [OmMX%F4%'1iM562ĪdRÌȆ|_ِ]cn)x/[S7ڦBm65[o*.y3S\y*MֱcssEk=xÿ,gѓwigw+ݴ>g"nk.K`~u cWRޟc xf-U@zCVSs\J1;[}kN-],0<3dU>xz Y"jBaB.&%MϾJY$)'Q[,YE Vm6~N@AC"V3Y#h~iY5ɶ `}QB,)֡7,7zV[V~(<ׁ,it͚Pؖ<7o5rqd\{E>i趈7iNTAX%1ՠuiY믎6C/KeGEG$)EhB ¼tJAqw 5U4HΣlP뒽,vXNhis\>ͱVCɦe{kue`@pu/͝L,M&f8.t~ ʀ/bU=CdU=R4_Ԅzʊ^Z2by+^Ey9Ch<]QgjqkYAg-(iϾ5fJⓤQ ,ؑ{+K.DZUhuZy[3vA?s(j6ȧvU^ԽTZCZa5irA=hңjh,עMdרW$SQ{'֤TR&6w ;Mu@&51K&)f>$y07L|8+ jlWlc$m(iHU8xɲߥ_s/^8YIzOܧ Xu:򡽤t#Utƿ&ssTC!l_cfկ!rtHn-2\"4j~$(*FU01%|U rǿxX)KHjª'ktĩjCxb˦Ie!Kkd?(:3W5%MlKcPc2VB#]9U\t TXG納Qšr҂bE4%.ۚ%JQ!5 ',TZ?37D<<(y7ތ^HDR^? !7\L<1^*.&")߭]Ļ=gpo}a`C‹' i{ٚ_)ޭ)t_!5yY ,[Gkô},ɿ}ུr!Tyhm3Nv%QN>c]ZaYh{ʒU,, 3>jطiǒ%7t-Z0جԣʞ7TC;襑kk:ӟa%ູ\)DotI_RMK0'mU5lUGu%'zDqH>:$kXRlP[]Jkvh1jj!VɪDv)_YJ[ե\X)KF cM+tM*Z$zϩ^N3ednWXs{ί¬!>w.JVtʹN~yE~Q21g™RFcqy.gf&Pj-fYMCwj}`ww>Mk}Zλ58슮2Yޜgzqҵ=SN&δDWV fH=>ܬj[e9e9 2@Av%{F%UWv]&K=Sc7eM-J;pMI sRő?Qm[ 9 ? %=Ddes| 'aꔊ=^pQ eSrexsrY1Hyg['t[K$;B¼5cOQ~)=W6=-qiAV|'qFX̠O=Z{)wQWu!4Ⱥ*Ϋ{P!t{%*e=íޖ0ljz4O:pou:;p}#]>oR'%yhxDJv!oR@?ty!&/HU-Kp}:\ρGz*?)].(I}Mҷ7 ] 61|Nū:ͳ{hUW]Vdpa'q[|k򐤬OrīmUXL՝9 Z=F@G9/YbLcGRkk s2aW1M@]sKmFZ/- Z!iѯkfMzsp`[;qLsXɖ~I#6V95G]B)C*C@Ps+ԽIFJ[ټ 8g2)8ZXz2j*0I d$:ZBD+sLs0AE{`D](N;2 _=y-U{ц(ٳ&R%|&bþEOP^3OhߣF씒Z5R-v#_#d7Jެ!t$a8(C.QK,{Kݘ|yqa*k0Uo'm]%&ֻ:v9]MV*\6dzRISST~(wabw]aQsvi/5o9Kf^Øja9 l:$%B[X=Q\EξU_cQMӷ :J+qd9j Dj&X'Щ)slI}I[.dHNUd}jk\΢St۾&T>S^2y5'%m}KS]*F"VƬ N|˻ O'Yq^E6@nC WNֲ)GN DE&˽QRLYӠ΋\'TF9m_t粅}HA{ jw;ZՆN쬏Fv+|)BSpy#N jJkq[veNP]A\*v0r±H-;"s^U"!iWp]`Ny_onɲtѢ;kX 5mixWǜB[Y4Jn#qQ #m+kq;Ou:#cC$z fx#Qj%ɏJ6*bz ԥ^Ȱ5M2*]]zl~& N_Ә 6]ԛ85PO{&^՜L`6mX&UF>\Vo^-@rPSVtu^a#~״dؿBV>g^f]/5m(mhr ZAF7~jӸ)Ċ!PVJ Hnp*چA-?[B0C@I7mQOGg9pDZìjel֕  鲞[_*MUIն/DxE)JCIzfK{4b1/Ƣ$s'08SKBB BH CŹA@|i0|FKڗ@FISl6ƣ쎺 wq* >S|wW]5G76DJ+)gRm4H[itgkj\ 5?;8}d ć)i5dot,Zen)`hUUu&x$k $e oYLQǫM_?Ykc.-oOe[Jeui-Eӿ쿆 hMg+9FU=Jcb.~5 &nLsaЅk!fĕ"~1^mm%uCZ>uvp(IV[l(#.%Je~J`$ݙ^܊< #O=5HM5;$T72 w}b嚶$N;5JRnԳlMM#:]S}fZ˴6^QJE۴([`onkpcnDz:1 1]mE')ޫ8~z6P wwm@k..R?:BnJ뮆Ga+(£GbQ(H >=uIpAF7 y*3J~SVCi j76N=woGgCKB7x 8$h:T aa󓦀`TS^ftr9]6en++9o%Z]SJ;HgI= k.~iD"X!Ԛ}'h;!G@~f(tR5AJd粊j)I#;x]*)xyΚ7JƢqJ0:1IBjR7$W7OU_P,SgOxqHVs'jh#,*9'1{DYHT]%X21LK1p €&D)֛(?ܡ G/Myr#ǺW$ V )v-v^ z`ʰ)zIWNdڊ"F*W_Ŀ5kx% ګPndUdy] llkorqDSaeeY}lل^]D\6AM|AJ/LYC9E5W*dz yf v.ֻ)޲r95$>>eoѴ)~޲'EyꅘCI&Qv][bi5iU6ujU?cP-Ϊ>~^WmB|,>ep8MpGMڣɳ>K)$4kRq cX)AJ"vyb%w^\Xlۨ&]}rv͙7 SVN(wVTyx8O1HXx )LsЯ7z Ꜧ#YTܥY#[ DնyH(;`)߼0K+q@RӆiP}By(y4 EoɁoSM;{'|OIG}k۳Эn.xG{tzޯ̃DyXLK2e5mh~2aSL˘j\zOehUֹU^Y29C@lu%bX7Q_̈́[KzԱ1nՕmmKsLˁiA~ [TWDߡmSe:C>揓 e1,61"_ªÚ&WEXWģ.*Oo+le^i^"UƿEY~NfgʝJB4Ub*Bt.{Ӫr,r]մVUuM1# xrz|e5b-f80N|veYvXcXVZ%}oI^3~@VVbw_>]TˌCeExN~{DNl^(ZA^4)^% f`z:Nsq_2H|+”b܅)QZKUHsSTzHoNj"p˕F̸gD6L2],=(/pٳy9jΛĆ1*;J*#O}x^cf{͏7 /gifxt\K^@ͺ,Mn WsWMA<~kpFҪϩ >7RSE& =-ZmuѠGUEmAy2RYwɀ;* yzyOZl#|MNP4o픩N$1rhBO콩:F3jȈxgF]HH z*䠠T+ nPn{[ߡ Bz&X־G8Kc濆!Ate£lzV.2e\}!H|CmVI"VgKa$&Z sqwy!d4W[%}qijZª?4?\vaV4CV)uu]Uzeziuݕg{uo60žTZ)5>qx<ǫGL$vCn]`ӵONjVq1,Z/mWiq~ Oh8m Ǝ|*#D3-Z5eFevIٺ$3Kf.kN;e/* A?WY:jRg^rQ_s&%ne/ ;y-%^&PY}#v %׍MtfŪKǤ闫ܡuOIl_KئCDYuPPr&\4W(CYWqKUmX"K'VukwB=eG*e0Z4KG_^~)v-u^4^:^!zX]{a/ܿ=lnz\w"Q, Rbtl1\5ֈ`%tg)]q&ą0lmYษ6E4fUB!ख़VB;vI%KhpoZRtXGpیfm\ygrڵҴ`Y.SA!ʿ"tW4xPslZ[v4/ECX`Gu.Myxe J}2N)>\Ty L3ԟ u<ŵB!+䩫!X2k ^"DQHA(nh3~UQq ?"͝Z][U&;hRUJPB!ZKhek>[8$Ғ IR!(EĺĤZiV,׮ *hPCKl k,^:M*bu[ B^C ;ZB %{ `R AuΟ2TrKaE*,!UoTҺhJ-mq. \BD_%cXRg uRBed>&V8G*U0֘0ho\C%IC@5D)*g 35+i) !8D%I fKV54Z5hˡvP—x, *XA4bǺB)x)xBUFb)dP8YERTU%SU|F*5"e-QJJIA"U 3RӐ֓IVHE!MSFuqU:'ϐ CG,P*TU$"*2-!AE!˫D$PR6M慥 *Җq+JxhIJ0 S$ZT6TUZ"MݒW,$RhHcrԸI0Hx_"EqRU$aU;Զ)j 4e4dBRPpZL1M)'");T&()4E +B0 LMYT0*d~hTԒQ XEfƪ A&Yk R2Rlˉ E4tJ'-b B()&D"A 1M ShKX88DRijB)XRWYIޅT0 ]a-V.Q^)*!$ӄU()"Y4V1hj˴D=t4RͱzЍ rSJSD#9Gݶ=qaRa[:ՔjkVd7VaQgn VW|!Nyꬆ:lO|q~]4l;86([Nߏ%8е 5U# =$uA@{H+H pyYr0NkX-:|ebSЈ)emebzI6.+J%Cհ}e ^'2ԈXcG}üE2ɳ,-}dY' `gZi܁MNp%%7[Sy[ڦ]2MhׅmUǠY鰈/}U;$kUIo;|ϩz8O1Cu/1PkiIԐ ]j8q1h^l/I_/`;bơ$%#%B8okM%*}}z:Fl+7dЈH NNadGٳrQM4PRjPc%eF1{y8I:[FMTFj{JBUzbJm[6:mj !wL( > >YW?<4/ [r5{{џZ]ij6Oyn"p-˳wA-oy؜ڛGl"U4"N{l>e?|Z6ˣ\tr**;Ov^2SLߔQ kͲQ{_呃DЕgʽԟ^G~۠D |Pұ @&S'b|ϱMh-O7UJ=-|Mpqiy%pY̳&iZrs Ͻdsm)7ZTkbaoE:=3(jԨUMل]e#T} ; YZg^x MQ˩/vuL"|jtVTuGf+D=Ӡϸ,✰lgry^b-E;5( s0,GUef Jf%X[,ʉfO#.^8m2K^'$|N$>?nQSLEk#HUg?\d7IpSQJ{Ƒ:xьy9r#8@̗yx}_ΫQK5eq-bzJEWr2*;Z&V=C*u>9'd*µͯKDFKFr%Ruo?`Ke:uSYՊB5phW[wmBHs|5Wvˋ AVlV sCxU(_ CEx:&JPDu5&쇧cM|x #Mi\sr;f_n-'ZweǓmvD+ςi>_w|O}n\uoebܗ1CУ"HɊ )yI|_o PO$U% $v`tqI :/iK@"u~ ([frqUEg ktO/¼g})NKZm|m'NƸlN')okoVH_jO:fy2.1sdU6bpS% z;CK- s[!|jfpQv!ͲDsajXd'nb\.`԰ܮ RƐn4f̫ܺ~L~ wg+;m:x'oL?Kƣ>|b/P;yM7.ug{y&_NqS^Eg-ӲoKz rvW֑wp?fAxbӵfP .L1Dz }+-~YtvI`ضQGrZ7;Ƚ|jZhvfK4ŸJX'7%E| gϧj{;zSUD(Qg=6IVV!'68/i!-쪩*HeT%P~U66 TWY\Q-Gu)+l ?'ァ'=!/Q:k UyxZ_-o%Sy{i"AgήJ>DM,N_["nU.AL'cJ9kHavT|L aTER{2fxjז8/͢X^hE{R,-Z18K+%fQNr3U%b/Ĩ= ^h;OS |$V=MKQu jE#3 'c9#8lE Lo$#`MC}5`fXa{nN\V+sF]$y!qːġ.D,- ~q5)8&>3<`iU|G4Y ֋qV̞b4^_<̡$JTrZ:_ij*MI ڡG'L/E/H:&Hb5f5s'5|[IWCP`4orTN$mp(x2JrƖg"( (ESGrM=(9iYPZTF"gMWZ*ܰP^Su֒ASR7 B9AvJc`)T>~ IBMvG.h$R+N']4~L}, 'E0©Wz-R¶,q'8Y(8Hμ, 1fUWԧq!%"| =W '̣1k.hZHtWU)7֘O]t*)- wh/yME4qQ-REtS&hN)ʈ%/9~w:&F|ms^nW$T2tkr|sǣUE+J~ɟH.ki~e~7Vƿh2mNF->sfmL{npWcAqrVkVw9W5G-׎/q=r;+`MCR4ge(eAriqLu q @75wޝ==e {]t"8+i$zMZWp}i/zV|Vr9][ccs"gzFzb2LRߟ^Dc?Ǒn5NRkR/C.+6@9BD!R %=B=[PJuG4) (ß/qگ{!7>Ե pMVocϐ͊'hl<]þzhl|rݵbw;)CaϵơKFVA`qV5!w|AND(KbQY+rKʡ(WyAgYҔK!D a:O,kG hl2I hV)E(éN;|p:ECjE1|cPӢ2r+KO5D-E_T,4O- 8!]K\FڊLo6hi^4\?WpLomЦǰ-#P(rQmM'QDj-+ wdV)TU0+f ^^`R괆 g~U/?-uֹI!.3|ޡݵJ\ސ P)+Q}.lп"Y-Kp_D`W3}k0/U(Mݚ*߅Nm˴M}M?;=j#{zVZ7yarW5sLKB!L| [۔ QcBi'{ɇ݆y( M~;RQ:ڝmA9̆WHzѤ%Gȵ3ȗB( z!Қ䚡^3zYKZlqfv}WS?wIt_2~ l?%x+ZIW WN]V Χ6y=g=\iX"YUh0JU^0ՒV1Ů*z%sV5Pf yV; #DP1#wifc74I^T~ϽNS[jݘj0Ͳelxj'Ӛ_ry-Ƣ Vj'c ?4,Ui|lJE~>Q;=_"W۾MVEfP8~: [XOkI.:fOQ"51SgSva(єAu8r2 >-!_^Qt@&WM3.Me'"v=A}&ʁnJT(0tLwa:|;Cܹ6c~<ʞfhQcXsݶu`ޫrqJU^kޏ0E)fz=gF4QLŨ;(?bJ(`V>2FQ`ļ@{$ '1DVp}tAwg'lzP5uDAf~<+Z.Gz%}LV[t%ݧe|i^OIY*b7vxY]e&-&˪澡?'>V~ttεF}hyknY% V5nU:J,q"Ԩ 6Y&[U x't2;YK_JzJbM} 6IB-3X+ʌ6ρڦ*)RNi"Z|EPgTu-YT:żřbS,qocmf;XSSLSgi41y[KRMq1QQ,jURbWf]Q;Q{Nm̞hx N4[Tp5Mߺe^;Ed`jYcObvշb6IKbE%(ЙY;BZ=g!sPoEx+%5Um727:aKcmő) bZV^!ݷG~T76l =UmM5*-$b"*겊)YM6h^㢲3&6JxNtb5¹Kۥ&]^+2Y]mm(/@RiFKȯΥ3/ZMXbE#֣[^.u/J)$nЌHxl{z* Mhy?֣<|KWG}, *mV !idnLScfQ驑K;5̣Tn}S$zوa5˦Ⱓ!<21"p QQpu^*z'h]kt>TL?$-%Օ!AbPEYv&;3E%ź`a#:|IGԜ/I  U8-j"ThJZS6s_ gJ,5B'mzc2Gh P\;iqc^˗mZ^p)b!cʥ={{rUz[7Sd'_*PͦSsj(bfUZe%H@B)(V5YdQ4hIz6al5i#Cr큄]UϿU/i-!WyUL0IaZIz볢:тmnUW֙6tkХ[hx4Ki64G 1hm;^(fvص ]V +ֹƸǴ&JU zӝBr mSgĮmJ?a\u+BڢDQ k_U<PduuիSeCϟi!d=+9M.ԧW D!"s^hKr"}Y4]IT>dRƀ#=D$õ?YL\k؍qc5LbzE <% q?MvC`Bb4$fʓvHtFεM8?(IGV}Ljg bK0AV͍fsѕI\m0M4|m9aGn=&զ'nc}@ Y&,8#`BH&U70vҊzh³ϣ8ϗ ߞwkN^}2ij+sVI$ju0 {Ǵm]Cp֛SedݢFV1USԓiCҨ+D.UPT5Q[j_kjRTCzUAxU= $U "+ "OmC*մK正ژc+4FE>ɡWo-U1MɢޙTYw=D5QL(dF >(falX$r' G C{fjC*Pʻ3Xٜ jJCEeeW9G2{ˬR*oǭYwUa"S!CAso-qizۢU3Hmyb-乷t%Īڟ*R`.aPTlےzp.4'8wFe7=ޱ30á!d:6406x}ǷC'"S4du=ONjZdb=QR(:-\"*)PQR35IlHuh!WU;_;Un)Aqaס,"T{l8[b2)L>Lj.-W7^RY``,q>VޱaU !z+cnRJZT}i9y)Oc.h`$*ZwYr+{u9g9uj< {ln9sI M)CFB!Ҫna\<+_ :堪W\JiN)u -Y,qHkX}%ag]Kw^k=*[oz {qXG=GHƖ1ʍ=I70+-]*h\r [{c+KHE4r̮ʮ%/jZcUV!oMX0(b؄O\G?W&҆xbFSPVStuCtH]ZwƮ$ŷy|k䳢2f*^NYr%E6#p֬nJJWsDZ[bY~Ȧ:lk&'5{ Z![̾13h]0)}ZQVI:l//Ed=)=ZeէHZNcNW%׹튊95zc홸D>)x{~ģZi9Ǹ*CJ6/CGt' 'MTc9ȽVA|;x̒XfOx*7|ϙP<{es eAB;65 t!+q"4}vqdE3z\@ A|9&;J ~ H5ve6il!cלρ"nlfE- P g UQv=-=!n{̒ )ǨBȉ/?XI$'TIܝϿ&SRML%hYh}G/6dtv-ځ?quȈ؄W<>X] '= o2+6)_O=]6 .]VQ6Or0rd*#(g`o6yceQH)dPMfta*$A_~U`O&9p\f]W~ETsb?J+G.ÿ7滝=Sk7ҿ9|Re[6]೾{6)fe6lU$9lmUbQuQN1&, z2?-?4wҐ]{NGTˮzu-PHg|m_NaFL bѿT6Q`ui/4琔RGrQy' #LR0܎ '3 E]Om{t鐫<9>o $vcKmĊ.gokuV+I"XnG١'}}]9"drH}X'ؾXl HgX}(ߢ1e-]Jw#f=hT99h׮i[&i)mVi*z_wɽ7~ dWlWw0ig_n=k{HԂƻӸCruiỎs8\e_*Mt+h+u`)!TYŽQU#)ws|TNUUsbdtκe]zSZ9Sf42eYy3!8}H3.UUEuqS=;* >$&,ʛ0%_B%PaXG)7۵Ui3KޛK~ @|-jVМ)۰:DUdݪ{̧JSړCߓ75I$hHJa^QλPIT_Vfz QTDt>L5adm9cRO9; 8[O=7fuphκ/6o:3'ȭ0԰u5[TJdF(O@Batvm"2vg<~$s:]Φm2nHR}I0*T%moW[:$#K~ġ{ t.eNi`F\fq`c+[zX-y_k^|6m5CWt-B$IcM$v,|ؗRM_'k.`E=e>znCMީ֣E+ af|2EPW-+n,eՊV[E$$[C'/O7~)9rwG~+?^±b{6OS2l[S%ר-}O1I%[ uKP y{랴$[qqG5\UA4}L=4z7V!@rMmo9_(lSxWL}2Ic^6d*mZQ~QksצOO\e Ɔ$hyuE- |yFS^:~1uBNXw޲;-,ːYtVzۥКjC0 XYWUҺՓE]qTt.|_Zw^7k +*r M!|M#K[%M23nlb]ã.i9|yKfꢐqIS: e@Q EbtHo$Mjڌ~lJ{8XO\va.()0ǽE\7_umΣl+#:J[h'+xTeV^YԵ.U3O{&V@3xZwpPx+z+ vkC<7%L˻4'yLUSօSNm:1'%xO=kr9*]l]=9K:wg\Wjc9|&z]e;⻯LX|t rʦUWH)e xD-onǦS!\'b\j9^5@bgaUgYYM`Ea^]#*;kXV՝lv)KO>]tQ+AD9Ή†ֽ(<!1aI9{;I {~ ?pezo(Wtl}D$%zDy93MC"PSeZH6"dr%mm$*  |OqP ?#v0yGLNJX$9=![XGb| {INiȧK*BqLtԶ`"tSSN=ų!ΊE*0oX Sid)PqEkTΐW%-_`' ~oXbQk|^E=ߔCG\e StDZI"Wg%{HF-rNFzA[g]4Q-FpѶ+K@2v,'QIIT}ne2L:%8`u c(~Tt]ъA' Cמ)tT^qm&Xn$U֑$]$C@taP%"><suJxgtjFMV;*#.$rl\xgm)pΊJZF*۾V-=*I=Ҹ}$C8 [Kֽ?]3t;2IY?osڕ5c] /f!uװ˛t^7bV >ʹOkYtʶk=L8I$$7or9)qU/&in=\Z 鲈NIEnRa",mtܩm{K$Ue%r}VgL€BH4ʪ E(F@RSGNhX]Ƴׇ>D$ͩe=eY!?K*ƮrKXj d)\[~28̚AU%TZBL+T,۵IR]DvڬG~dImS):d dm‚6I,^kSc/5Nyb5wVKyĠ9BUfYadMGʷ^ECb4l#VVܿ^MM;uxGhNYp}jTYYA+O@?5Қ `ԯ}ḴU/q?6#ZTIB F_12ƶ`6i^نoNE2=r٢dZQ%[pYFY77)6c:r4GI? Y4"ž QRBڔ*+XXn úxz#T*o*@ ¾Ț 5蔤OhѺMORIQ0R%s2Lbz`)<=fI 6CkLͿ$VWGH w7n->AZm̑PT=) W&Q(tj˽%9k,=Qk$1o?˿dnMzʩ#m^&dz+Ic jgִ!_ѥcJss^(3՘e)iIlm|N<~Pes"ϗauwˎzm>rO,\Կ"aIԑ3˞|*5KՐV&[B`iuw U4\E)NY-~YTe[Сk1qdT *ܿH.ųKlVi"OGYJ2z}"j;^9%&VS~E9[ecN(z*%RT\2lȣ:ì#ka ,^{ng[MO ~\AKLK;By (XWa-m=8$=e:=ޮĿzV|S _ ¿g殌B{o:ȴw=EXҴ_!.,qﹴ*YF e2%W'δ>vѴlZwց~4K4%=WteV+KVJ8)B-Vюr+S[;Dhv[ rD`<ő?d_ 3~sp٦ l5SQmέ}-ZQB>|r-.h[Cj^:OW1()hBU_|O9hX׶,"pK}Kz>p#CUZUc9=?9`di8*!H4f$Î'h-'|i" 6ARٖo2AY| ϩe]DJ$SҬM"_P0)͕hιORcjMd6qvT)$8i bugj^!\)y:脨Dc!;ZE54Ay,+ q9ɍ+#o|Ը,23@09)v 'fjY;E}K?b'bcQ%1^k㤽ۗLsR?cn!ğ%ԍe =2HM|{ʗmdH@F`_SEWPG)ĭ$ uCgvBOBu/:JYPN8Dpv҅9[e0˶slY{OpWnZ|mMG:;P7zdPѮҙŧ]Ld0K6_D' {J~.Jfa[p5MN߂˲+n9\T--;d7'M[q" :Aan>3$y@|-s^ Bo'x]KtYeNVnH#x&)\8U7"9J-fF5*0)8D5[m nMQZsRPڦ31і?%9u.k St9WT Sȗ y ym:e`'lUm+*rOElxiV`s {;jumoqG}IvbEDtPUH"| 1u@hH7&ET()[ Ql5b>p p²ie!Xw PuD o,iƽ2)0ap_7g!ӥS˓JY?NZ%mY4Q8@@'f]笏VO^ʂ?^Udέ5uE w]T XLUw,3 s+uY9|o^_j-]Sf#}7U-'LzֆP=$b=hepRpۚ-IȲ E7'I`}o !8>O)_,nfь#:ulN6T?.Ͱn16I{zlݡ>:v?i^8%Haڥ!6h|4j>ݹH1'UQ2)*.,21ľI^Ux)S$!/nYoiks\Ҩ CyK)*kJ?@[Twq~͠OvQ X<0 #Y%|rx$[KOYTs2JZUiR+R zܿE Ipgh^FI@NYY3MkR˲IbBnR9$#ڧ1{ 3m*;EV=Biu3ѼjMuJ-My)v-zjo cNuu@{GIIv z:* _}.f`_ԇUkjCBVbyLVL%UTLUӔ-{emW0J[uO93(M&HȆ! am.)&"< ۯZW%qq@\SU?D7KL.6v+Uǐoz{S[uʠ"CЪ~@o)9}Y)/aPޫ"m'! ˽ lAQzJj7ĢR:|, mZVX+SП'#[ 9 [$v>56EmWJԞUÝLPٴJ!r96e\.t(tbb%^f71\Wd=vHc=jk\|*'YFBFUNNW-a -]W e c&T%bdA*6uEI6LNZӝ%g2YqR'b4riS_H9OGY=/lsTd8]}߸ľ75suI{?Y'uv㱊tHclN,IѾ mʌ!:ڠ RH#)k}Q,O1*8& 9Du[l-:MKmI)N#(Dž Wz;Rd'0a6o4zt'5-C]jƥ jT"<Ʃn Qa漶}5-ϼM7.2zIvz` 6A 5kaR"{ZO[IPt м=}Xff:UW{e`-U|~îB#gy7>Qv,wyC>ɶ%)y1rz &M\Kᬶ0 AzI;i>=}m7(&`D5TBXv 7vV7aqzy_,]T_R@  )es WMq\VM^LJ7G5ށ~+ˆ@Jv!;0' {SeDǃЋެ߉(`.טdwxE~f;ǵw+|Bbtryˑnu9Os:欒vѯ(崭rIW36("{viXL?w$txdFE*~kW6jj2d t1 ! ȌG>H'<8PRh󇸌 3+;*/+ DZMdX۬?QHHk#"vʈ"yojƼFN8K6$DEmH)(L$YY% Z`-{O*a ,LZ)[%~Ăn3dU1I$,ﴋrWGEh"%IXT%Tp t5$Fe>P$/۬>z 2sD?9=pci?cUĕȇXt,mHҧK5o2FxiJj;K*OTY;]p&٩NVrϞod7 nivG,7'JaaN3(|/)E|ID/Q0SP6^,4\{ﲉږ~թ5[GG98TEFa% DL/IW\% 's&?RR̮5țhw@J|ƢDMJ9-g ZT(pe%҆z_WV%:L~Ɲ~-w^ӴA}𲒊D9K_CU7@CyDҗ2)OꅐibR>^{6eV3`Eޥ!B*v %XUM9&jia !%N9JءA<]zz6{.㪳d3go!U,^s衜"j̋bثh\sbJ_^9EQjWZkCv/AqOJ̦ɣiv&1SCϑ^u9DLΑKP%UzO;d5Ng/r( ̛UEnx txk Boi ȊžklErؾAǡ(m޺jRZrCnz6/=mR[S޶r<֢SG1{u~+qDjj8%tUW2LġN 'q:׿9BJJTи]@X\j#oMp;Ai[7.V+sؖ/fm[2?X^85"v*VrMJ&sIYU5dZ5kBB}zR0KSn?ۭqȧm?cjorKXD! Cd}: cϭ++Lh{ǰ?/\I`aX.̰JWxj.I@Iƪx>F#ǩr؉rhKWƦ2ȓpzn!h}l2Tױ恧:ߌ^/'`Y ÷K76Ic֡Yfnn%{?J)C_M2m-BCL#҇ЅMU")F79OuwiBBZ ۚžh嚪Nm'?m\ZYAUWh s cAztNbНX5e eG*&–PUBi!$)GSD"UcUZT(\bRp%TR.4Ѕxo=5;{JtM쮫Jyl26&%lJ\zHGRX)LfM)hpRVb)Gŭ>VtB44iY'\ͥ^%H84%R#;C@kkljm]Bp ukOU<U֪[0LE*ɤJ< h~Ĵ4%]oQM-uBz7SKY1MЄ~V"psR!Cjʪ)=TQTd7$)3bʒR$%Ҙ]c2u\y ~(RӨDJ pU|- i+&;vd"SL4wՈg|lX;dICTӤ?]j vضVE NSK! !"!3ԹӔpy\ۆ8FK_\RPo2ՕJno!b0U*ܚNuvPQM rjBYka q,=RBi /YKY| <PcUҮM, nFV9 {C bBCNn-0\O)"'ժsVMD PxU)ejZ~*6}XSeAsBP4kʛ4 tRR5WȥDZޚFA89CR- !״ُQ)Xc;a܎B"X&!&0۵ O~&DQcA["Sg),M@yF2@#ڰ( uMTFL򜵾iȡ 4*ɼ[=DSUx oBdiiª˲ԃؘiߛՐ- ko]Rv-ԓqdF,9H `HrzjGŌBuj8I)AEO^F ~isزUz, ~ؚMX,ӓ؊8haYMZ*XJ[%_CY(P~=ҕt\$-BūLM/Hgn@G#X) 7 uDxǰ_e^$٤|z&fGKZ%⹬ھ"%yo:e)xE@D_=2L;|̾J*2VZ =}~ͻda8'}u٤SVdOUdUI5 2v6l~-QRfsHʣ:d%z-1bR"E[TOCj)O%gU);( P( ~KR1Unaʖ=E 7d-ҒQ ?e_a״NML[Ly٧$ЙAζ_X;Gr!{RǪl)/ɿx)Nx+n)bu=H[U 0Ϣ)!&_3kpJẹMr@. ڐ5>9jV欅sS%}TkVWn[ @±"8eI\Sԕ$4:&Q4(Ptg=Z{(Xwk"r8IؓNP{h' L [+I{-r">q[顢d8fxşڗx7ؚ#ϬI2y[−?G|*0=&̺bRl.0֘>J }Gc6/@D)<|O^fq=G(lN7Z1Ai~1=d vZ% 6L rJSp8 zoh5J\e]U-k^Gt'2qymqCj^~꺠6%V݄ݫ[w'j9(I>F_Z:)ɧ]ҭ -ēH6b]Z[CSЦ]|D:{h_s1 54* #_)V Z'~Q&]$ ÚUA{\fDI~#6hkɖMJ0iJ]fI_jn*-O$:VcdZG®HԻ<2^RVg-J+HpZwkAЅ=6}+A,1Z7#mxc@zΤ6u E+kb৺OWCKJYIrBb%a[Q,1A7~ ʺz:}_T˲*Ϧ-wRWb+ |ٍF*8Sa =Ȼs_^ 2yߝVgvS^YׯԔVf!ɨ()z\5$ţ3*:&Pe|┫<YZnjk(CuAOc{xͣL0Bby*9wxU[T7~Jc^td:T[vǧN;CAyɆt)GW*!сyv$t/)%1{ ra7lXW_}?F/X(&>6Ly X ƤvvwREE!'=3[[b, !97yy ֡#)𤋮]⎞?tJ$ %E|Dph2 l(/,K=fn6JRZ ;(WѯbE/Uf=\5Rf!Y}D{H׀8<89HrBo&xYaws&AoPtMrB,nЊCG׌xu$L&koǸrW#c|K}?cZ&0u~e녧`]1md [G_S~=HlӷWV%N$ ;gpИ3:𸝚#Ȏ寪Beg\rQx~/ֲ7y  # /]ߛ9 "'(׵OSQ~jdcԛF;GβʎUi};n',7}[}d%uL~-ۤ4KY]Rt.,ӒH {lcY%Ct4>{Mϑ2J¶%q ZvV݋vQh3#R>F`)')ʶ׭ Kά0u][lQ,g%2*i2mΑ=ǝj5ysckĶ +㔤{TyύuXv.nj=k03!ݕeSzl1z 9*` 89R|J~ŒYơ"7*+mЎd|,4n &VMpeǬmoM}gFħI|3".s;v41ahwls`GẎM:t=5IeURۤzrP}ugY%,jhJ+/athңVYvq6bS̓WYd{bANy,/)?6|֞e%BRw?F2ABو`3*Jʿ3uVxc]e;Yث#)wZVU |ʼn5oQ;75CH}{VD;(~]g7H!.EP$Qل4˨xtb2."G/: )tJP-#VTF}Ziv ɕ&Tn*2,{m i|,Qm+YLUnBYNR"J۪YbF|oK)ʴUᴕ۔ª-ntIѮ&A*˶_掝59+n+ta?ׄ&F6]M[^3Eٻ1u=``eXuUE d˜GJ: ZjRJ9*f#?2۪Xv'.4l̻V++fSMf:lsKnmݑxr͙ym>vԱ/Rg74XM-úY? k-9It?__"nX~9 |/)+Դ%=Y-wiC|KZųWkmn#@W:m\y "< .I^d]fVuq0),p-$]U$.ô4`V,ԅ&!HR) BC!U 2Bag3?'IT|1(me!^1§LHEl,*l\ȷ~и^Sev-,?W{Xvm*ޒT_,֒f.y? m`)+/UКĒe>gb-B^Uɠ覛NTR<~PP\=M>JE*lJ%NMNd'gII_n^Gib6)G-~mZ]?Qmml ktXFlkM{6漧\:H!le"}{Pjk~.VƱrϝ>ٳi?{>(Ӑ77.+g|94ú݊zܤYbѹ j԰ -^ǜBý >,,*1i"m ,<+R"1[F`nՇƍH;i69YgO,Vrɽԥ;S&h+uWF-waWȴ:+.sV&Z' Эʪ鴏ky9]6SBOQ.ơ&*ۨR؛ nݦŵ 'nOb:ڌjP5]OU$B%x&9Ip2]L瘪)  L`"$$jkD6E}sٷַ6o:p'aRI5+4`C5,qKYb7O\~4#Px|/U'(ds)ʲK{6g}~R5.ӯK?ag\^*YIzNӒOwz3LR 4\d;TU#Qd(Jj*Ȥ:w41AkRʱӗ5 %(:)xUPG=6A9Mu@_Msp@SO[f^B F7[0j ̟:oE+>:x$gTYBd)5RF'+ 1@ri˵,RțaNiˍpncx\~%]h6{lyo-_xBe9:+A-mQkI|-^UYW2F5샀 <3 ޷Jbx(,CS!?Ni~=5:a"Т&w hRB"y{sxūpb4E0-B$u wL-%P@,p*'xIf!lĠTՅlc/wP7xɵq[y/wkWU,z 9q+"]_~Q:BY %BQM,Aꭍɹ٩TաE"!yhTU_)HM5 =;}Rv+s2SUs4J7Ƭ җFejPu]-#^v >Z_15]A>Yc&RD}qf t/On־9Kje{Mzr!|4NFyo7_-yK 9mod [IQum." :wքT7Hd_Cvm^Tdjˡdb"(j| -2ȕ+<>U\Td[!ANrRi4 %qQ^StSVX"!dO":̀Uۯ&vWfVU5z`SkڈTI*"7Xp?z4I6Bȴ<=yn]Pi쪰!\c9NYg z!BR5zh\nneϯI*뺚-{Rv[ QtF)zJ"?JKT5UUt€-5H]FPVt\5F8bo @}l97rcI\NY(Zܰ|dY K'+bUgV {xʢzJZ@N_ڐZ])gq>$s˙X{ȝU@y#'RcqJ]l3e0f(f]}iӆs=E P߻5=vee=$ץԶS^R]q_i9(owyo1&[XYǨ!:/+纒iط!,>]{==~Amٶ JU:^:X tTqnum~[վ " aX =$ݔ]k@P }r?y0> oi3\Ck;!TYHmDM ɘm\{opr>g|kluS +H1V={⢉qR)՛gFmW67f zcJG-idkl=ZLG^Y ;PoqQeWZť*˨c3\פwz=G;Br*{H3M)?]=X^|=5#c<)ܸ)+j#jɅu?F. B/Aiai^kY$gH7#=iw+L57Ows*۴=.isWEeNٚ.ѴT'M+܄>$%G_}lzWU8F 6F&gֵ>{m_ 1XQY+ZmS&UD1Io]2DAM(1iR볬GwTnYUA_- Η>DH֙౸K?F ԌE6/UK/*2ƕd+ S6*IA$'oPܢl2Y?SGr01 y)%Fض}bK0L<7gca 6+njRv?epΥrl&)ރ4lz%5{=f`ߥzsº)RЭ) ] :ѐx#[E #B}LXʢu%Mb*TF Ex'"1 V->/PUZ2iѼڸ\;vsj[=ԦwKJiDE)?Kc۪+Ts xO܃Xà_ho:¼lVMFUO!Ǿ $&Z qHC^gspĖ0 SBgoXFN4%tAq .:6W_&OҎacJE.7v4NWTɡ:ۧ;hgVSՔmfA_hմ;Bog(qlw7D_\c&\qq1QNBFLըpN+[:*}'Zжx įZWΖ rĿ3 BJǬB8/LDرw[F}a(SBQGv[-ǀ vѸ}Z߼5X rtܗ7o}< kmn4W2 (Z\L}[3=i( P{ʌ$jܥpRWM]Y]A6\hmfvoj?W[Ji kZBsmiKf5[,JUW& TgM $+XS;rU յ Kt }1%5t\0(%vA cW_ =&4핽eQ^eZ6W>zi0˪Xѵ;7̩vLSva.6E K&]ĵ؛?&b2ZrhPO85 lUkeNaJF T:Rϕ/=&]V* b¥M /_UN)v3$øGBBz5&3\\P) 89Sicؗc:% V!0gUyY*бz]WN_M^2% ] J͑)^%eږ3+2$.C4@r+ީ?J NAk8n18F!6? ϐl(.ĶDbƳkCmS<$Lz%%/}餛&/nKk 2:餘̜0 Nqa e羣J'.0ԿFlԤ~4iOX^MWOpXֆ9D[eŏpzef  غ^3&3sHctdEJg3wbj U9kvF36cդ_2ڲ  ^3 WWlꑌ;i Y,YLܖY%(XyRa 5Oa<+#e`w ئ5h}&چ<-%fF/m۟XOE.J4cedX&eCAOݒuQi)@ MsSd5etD-'lHě|Qi"AdUXOnwdB̟(*֗YZz.(*֯f_}Fl")Pi#ȟz 5ܡvzA Kz.F^V c\%e`G(*z2;ɆEqUkkqGP7F۬%Rƽ=FroE#^Vi/ rN&ӍS~M2;ppk)?#MB[1n*Z1{!B&iW2@Z*`pT?qP jB.hM.8Y]X5.ƒaOvQ#@jݫY2Ѯ2{"UDQD5NuGQřT^چ WisFc~-2<Gde!Jpva! m,cKѵyB;JB~o(Q5#LDX-fF4BW4pop7lxUɨBJ"L $%.&LiYTbz #,NL5*R2aV}˕X 6YVvT+W7"H)NN6Z Og ~\s yU˷r1OdAY z}y3% #JF tŝh2%RZ%T:.R1_r[֗(ڈ. fQhXSrOfYQ{O&:p6+;bزSTCOQvHR3u'#3lϭAT!7}HwH_dEھ+z(qZe^^Kתǭ{pKڎ2s|iCGN*hl徖> TfI\-WOi YPø[QR*{i 9ҟ* yit=Z}"iР:H l]jbOJ6}5(FQYՇĦV=EI7+J˺̳kTCE锪\CՄߡۗ?b4#6-jU6Qg(,ϙW+Nj+r}bJe_ک_Em!{ *d.HA~eM YD͆ >ܿ8%e"nn:n%tgnZBECV)46hN[T0 Qy_o1ḡ]ѬX|MdGfp 踏G@HsF#4)*,fqu Bıy@ pHӓ͊ T3!LaXǰuDăt窇 x}nȔD^ .쳌Х.s +GߢW\Nio'3ԅ..*&3#>"ڨN.KO ?{ˇ :aٹyP;Q1hq0G \ w I![. XDZzǶ^Pw#f}DgDyC8Dڿff^%ACĤQ*Zv;~#>Q Zj-2Ҹ *=Ri0IWԛTե9#Sl\.'ўGE) nXbهC;["|E<\pjX¨[B ǣU`ȷmm6#oį[Yc\hj** C< z% :xM0iQPnF]&1kkF)Ժ} ?2dIlz# $t@k8f=hX)R/_rVH5,jSZ0 QYknk5]Xi^4} ;NW}Ǽ~CDۋ &N~9+c8QN[_ Q A9QGDGdhq,N\ScLxiv ZWrԹ,(a}o5BƂVW4Js,(FUWY e-J^ %&k*jN)p{@iS J! Tٺ~|EmoCZY_n)Ty~1rC)'(ڪIErvmx_k8OQ \F/f~QᡐE$Ͽ4{M0oo/E@2\[4 Recb5/7AlդOA?֧ˌ1j%9q$k}M6JldzmJT}y{sF6IR>uuKoRTθ >MOgi6T]VI[#m2z5d{E$.yH/𧩋Nޓ*Cuyr XZ/!QdJuߩZtqk$Gb;Zbih66m{~bOޛӥDmM$qԂLYdOlFP2̍نɑ?GT98֦'8(1Duy,QAvBP:Ús}}w2ilYeETPNK~;~ȣ7FN2?CBwJ;ʕ$]sv2_[Û;ˊJuuKAcQb|=O#-*.h?֮4XEZ/%(fj)g׾VC}䝩]SRV||1qZu@la?h pCzSLE*)UM:>uF%=c"օrg.S[}O#(-kNBҽe:RS|'؛3L ŏK=Q Pxjt)W(sAoU;+r0lRRa]|XTt+ʸW'jG2izxZϸmZ!|˲-jc)Jԍӳd}J"{\]W)hT1jҞbwٽ`2%-IhVutz CDL6< zսVaelUkfVTP點YU9Rv+U4mjMXgG==k]/7~9m~uqr~ͺF7ª4tu~Wf_,.Ų\ێ zr(ȓziWy1|EmYd۷?FEJb3=,*[+rW5\Ĩ `# 3 XgQi~\5h缰 [N5f5@*l҈K%=q`THj%QU_iG%1*[N.K$Q(* ̴q.i {͓jſ'#ԃly]̾>^y00-겯=@]߻ pa޻F=F"E.%ԕYZq<[2k:Qpc2܌Kɫ7OeSĪ=VWU捸uWʍ`)OyL@$PZ7`_}-G&FȶiviMͪEzW-aL8k黊::nC郩KH4o q'Z@&liĹhǗ[[T%7JZA(mۦ+I҂*jZ P[_o:sYW}DEU py'[>7k[eG O=KFJD:zW'J-8~ ؎TG9 @Z3:L p %bEGEHC'*@uҏ)nN[!=~Kj& <[~*CL(v3D{ f(Ck{WtD7< ]] ^O-YxkwʗJ(l-^eql>1~ypM=EC;hۦZ\c\wfiK>lRdZ4o]GM TkJV-lPR }bԳH) 5{^{Oú~tG-/y JМݏ6thV-TtI)&y2ŧM ݞ(ɤjJxO򪒌ۤYT.+KыSzސQV7ALmu~kdfmMl]%=D>'~^e{kZ5 ƨ/MYK)+4~0:ٔ:3K -Sxi'M&&# E'Tаc1]f"\zRd'ju%X>HjQIט5 ]du]N*b٩I˜,5k,c #&EjpA>zl-oʭ{cRm7JtJD/jZ[j=W= dWD$#$^g1j䜰Id{LnPG1짞Hu=FPk$OI:i [nުEw˪x{)qCXj]%=H)WBcTCz?:okS5?mqf-yVݙyyDi&Z 2J,EBYEY&QVWTWG5 ! +_%] <CL&/W?ִ TooNEԔt5Nǀs,+F}X~o-W+"q,ݻNDҝ-ÃEol({Zuޥ\BbPR+YU8ǜEY~܌:ݣJ!/'wuoq5,ȯY̬f8/ݫ4ճ~\iW~{{V˷#;.liS ֤4~m voUWX6=WfƦS6Y y3L[v׽vWxo9I\ø%^! w?񙛋rBm_v jOY3}n+ΟfުU@~0!?v}D]sHjߑnMmZxO}n_X.l۸*y+ïqP|_tBJ|m3xa!ʮ-Rtߢb~GW)-{]aWm666n3xozC ť񿄬Gt͕; n_%b).*~wv*\lΪPCU/9{'qVUuxgvnux CU?*`њtؤ|74Ll%WUnLRM ƲբBtj?;}KW𵡡,cX8U =)~ůSNvU"!^%UfTᡙq. 8nY+)W}[F*іv !02\Ҹ5a]P8Ri0ʷ+PCYb5H kGiXBL%H?2ZXìR)4dRBĩK2&6UXCRE).BCUZ8V1,hM(尿uG^EUlLiL^ 4kUz]CipT"UC#yKUj/֦RN>%TȦKpzjuxj)T2aQGpiZ5z,U쥈;VB!z(B)O5)kw!R\.z k#O) MpVU,pЪ禔}Ȣ464BP9]23u(I({Oc)t%j)EZ ɤؑE4SrBZоaT%~Y-Z)WV!TM}~ݰ.|64 5ڪJ*ѐ+ Zt񏵶-r JZꜚrK%`D")UZRLS rj1)EB)%ت2WRNU%-ŹRW6 !=hQ4dK:ԡLl r)NkATS?p )-nZD S,jQB)x%Rh M 9MUbeZ M%qH6+K2M֚? ZgK)(#vIQG?S]]l 5R'bY)1HE!H?c8e~ k ?$չp^yvNj·gȲK,F_\s`YVY@I;̲&߽O^bn:h:eIxprDA53A6=_S@zKFēt ʢ%ӅnXzY a`Y}RUnkݙT(<:~V{j# S ${\5U7԰i:T5YR9Wĺ֟YVg7Nݽ׎Q紽2O)H ܭ*Yk8(!f~y[-1 +&,py*27M{~g(ƝmV&8kl둦|AOH:\`8B>svq$W1XV7.Lc?:9Xf[, OEQ7னm+bI7^Zqo\7x SX8oWSXpᔡSjWSITE3uhmTw> Vͪխ\W_9n܃YLo\PPDΛi dQ ҟϡyM5H#0>njNX$PF0$RJ!"2/()zh׮^Sc"wyr型d6W-"[d"]"\cu1 sagu~yƁ]vٍ`yC r]ED,\FXSL=5nc5ACweljsj⢤!5OJ& 6'Dʞ9PO(kzݭjcP[:#5,>W*XF cD:w'khBиcܢ=*DnYIg)6Fa_;Y\ucBRh4e?[ПQM/A?S6}IҲ%JQݦTxhseBf5qGa@w(*9h6b}Jp`K2aT$]xn0i#]؈X )/$3cT!h"w&uJ1"ߥ >Cvaz8mǒ0,껷C݁fyiZ8J@21a,K8-E-oj^\Y{75aGu'hؼWRB~ "*?Un.OeY1={Vhk}OECD q~9p⮮znN ƪA)/sH-Co9dWGt6 KZe=fWx1ѓhtߓT'WTĸmv tY qD7 ʸ׵Rj, 5E=]b2I>wٲIRb : ڷΫD3e;jCLoۄ&J,ʰvuV}SӇ7+9 CHkI@K/ILeiNvO3 ,^T|OlثNc0hsKZ,1 +DZ!APVME ":=p߅BG k *#,(=^G" G&E38#e#47d›+y*>=O^X%-CCX'YyKJ#umG@SUWmrJ_^§0Ѫkkb>*) 6ʨqKUe_}XNԥ|&%]ީee .ݚŕҦRHĦ1g7o)YhL%XgįbY24N͡LS']@bWŝ'JgʈmE9_?58G}9:T*FYvVr@J,,RL)ַA%ӆ߱󀐘UQHA;u)=9WqxT".ǹlU /dViD֩󲯝rdJBT-1N2+ZQ+Rk"[Cȁ - "1BHv6\E$oXjJB d)VZ ޠ^zJ@4 JȴLm` k`3h-t%BSYI O͆@7Uwԯ~UY$s ~NҠ…TҡAM,.0J!`du<sʽ^j<[gJ6tT9իV*UBĩT A&pjF#{9vg`;y/Nw_>lJSKFGiKw,6e]"=h6RN̥2Z9P& ll~ȥTи>f`DXgcrWcOE'L[b5 cG"#Ǝ2Q-OܷQ 2-^dF?zL/5>o@N߻[<,5 |"Bh؄]'*/cZx_x'ugJټRUG2L7ºCNQ}sȓRQZkRn)Tčn6]B'?i6/I3TEF`ӆŢXd[e[GEJYNRY=`kZJ!Y_WhfZz1(1h+CR?*Qo~J&04湸[޺>|fCRX YP[*oltΠ0 4uG9UPCw{xu=I|3h<&%mҩ) S#ѯip*$uĻz7d']b, 7iqkjYWޱZ[-`Oe laȅQAdj\4C2iB2 H)OiXD6k] bFACf/(Bj0mkOʼ8#q&Nl ۄEOz[3\m!TBi<̜=ս#_##O:@wڕЦ-)7f[K]1zquiUY]$@µF6Ei7Ej,򌣤^wPyo!bSU@eWҮ/j1Y#Q-pI ?dR)/p 볰6lڲQH*3IXenUmP,Y]Yf;yEK|VKN+7 lz+tM JálnFNɛl[Ot֭E}ſ{J{`5wZM5дRg8s3Ý2V{_ˣdu!{Wq,rCڹݫ+6M {N Ff86QwIn֛/K[̮si^3zmnſ;|;$|֟rK=Y-ei( IyKH EPHߢ/<66I'q=ۺ8Iۢ:/ ^Eef2ii0ԶU~%pNהs=5ZDk7#iHd>FjiYNU6gr(»kō1f!rR밟;TT( i^ԒREߨH:?' v\qoYW;!G^;N-Gב *G7?In:%k|Kq2,:xI'|oQ%(~|5Nߓ&$U&Jw5MNMWg(JOKŠv)."?/H"|FKK]JR#pM=a5J%>5#>==D R,Ÿcږy4>D@VO cF&UZ#cLb9=篞{.¬_ᢢ>0-,QBTciY;*9 `a&O7*Z;F'aljCՋg&a\Qw0St KR|cU+m?I#\MT߿bE6?/.ų˒T6Yg=ugO9FN!81&2n[T^R":n/ O~ʻu4ݩzL&VY^^ڱeHQu?j @oc'Z|* ~\5e^o5]ŪJ:N؇`vLp @$[}!>bԖ6nq#[G]1/eHFվ @д&joqLtP3>Moy5K/W >`S\|H0k¶MLJ[=4XYxh1 !8#B 1nS)" D蕒IG9ÙSxؔ9 M1% 2`̑“U/J^)2d#ސbF-")#'[p~=[,ɹ6\#淇ճޮmSBh+7ha)T~!oGcrxf 0C1 M\a-!6GZ7k.,c)^^POqقFưi)kyi( 35UlR'E=c!:Xo\O!  +d\]7lR9-Jح5Eq^*A^U=~OI^`wu禗I9kβJE'Ivꮤ?KЦr]F}OD/&V=&k&pV)vSWkRE zljSYG6nENJ{H?5es:ܼۘ!E-U8-[vcXEu#|1}MƙtY_Mf=_CՖI]_CZKM29jrR]u#uvĬ电ΞlSY:8'm<+DgSDں*kb:ӶNr-LX^%/RM5Kn_zTĵ8nxi% zqgM*Mr;SoYI@1+ih{/OsPŒcvepЪ8 n7-…eGhـE7i`ӂzYS[J1UEtR3gyl!z 5e,@ޡ' *̰m]+r[o 5{kN{ɽ8& ,>Ph;=a# ͸Gf?l!T)N)^T1]oWr%)BP}ͬ)=3DEà,RuO ^ϼ8LW]ZT)%vkzcoа+<Pf kE}M)j^g&W61"6kyꛖԶ*аiJ/5&SBcёX)䴮k~K-k%⎭ a 2j;@|t!cNܧFIJ-'F-Q.*k"Yte1^{ڍL!Rܻ-dg9J3?:x쳪_6NhKN+/vyr>|c.J'쳠=9o0ɽWGt~⽦-% d8bÙ|1ȋ)z8U]v{;$ꄿ \xj[.U"4 km'%COQ-]-lоTcj-+s^H'^52|ahY|שwQLGܷ/AWz$rT6>-YtmB|vTZ✥LC`gPYkF'!B4 ynCU [ڶܣ^ML0V ??Dꂿ`]`gdߎx0txnaT=(T^\sxiVX7q }+ v@sS&rsuqW*]LY)9ڔ|9zԚޒQz\vyD+#ԬulT&Mcޯܞ)ʡfV9XRKzj~m-\qPd;(<^A5|M#ѫ{T]vrfI'^%DNrῆzO+RU?Wlg +n[ydQ\W^l?]vUε15rK xz<}3\b&G`fSwYXNl_ͽT:35HeJ;ɲObYS* Ӗ$8v\_YL1.CXQH2iNօiJ!S,2o,ygojɟZ7@kލMշNݱC ?=U]q$ak*BK"hP\_Jb8لb$Ԣ}&4F(aH2O M;D!ӛYa9[lAfqam* 9)n '.J\&B/ac/nD֚|Jޱ9S~eUC9#PS2Qa:z??N͝~Z9\|^↠5npkJ,SoUi6.;иemRg>A+O"!RmH,ʍY_kY^˷3hivh\TV]VcA0. +v1'u'ȣ_^UM#Ɛoߓ:)uC;b0\U}KF0?!E~ E*iU,ħUeoW^cp1SNS1BҾGr.61X[{ЯsIcx 8p+wRtW* l1 ~,Rɦߞ*Oa+ MIo{ht]/Y*bGK &9oS'*}%9adr9nq^uQd/i_K,=TUMnfԆm}2Tu}wbwE]?M/2Hi*+KJ Qv)l+U|\րRzR!zWy\e1^8S[EZiEhQvܽ8B؉Xh 2v2b)tMYwVgm-J!myhwvѶ!q[*SLrJE¬ q۫(knѵ=&44=U4m;\V)­UW*C[㫝yj[oKiqi .4-۷f<~%ک4Eox员C3Ӄ5٫Μz{ycqW!1>F-3o-!m6f|j\.7y[oV^%qi~/`Fٮx]BSoVh=#`uVWgo"t~؋{(ul/9%5vHcfiW 0QH_RJH) @LL/R꼰1ff6(A1}Ne~oy;N^†\I:\0a"Q^}k:+6Ɩ|kf\ZzOY,Y^ޟcbnsi=tVvu$ei>^Ț3g*JywJwU.XR>0Z#/ aa|0ҭH[Zx\ z]k[ nk)y+2sY/Q/]G)mS>\i댤bQxVV9CiuέeYftm5X}[ıdUx"2?:.Qcn*f*U9]QU`]<ѥ"@Iq|*/⪡'BAV-ѦrԶnzָ5-ghKSlq]<4qfգlg*,uh`_#K;Zʹ*FN;9/i^ A݂!e2!5= yb2vcVnKJ7BRE):Zn:X8욗wgnv%*ʴt7ZvDۥ7L+\R*Ai0XLc:K)&/S|$3+,2ؚ&lv~Ců*_*q+VZӐM0K%BʤV_gmm4UT M!Kj kDDhSl r.q,3/L29'|26,(*x NNhM9D.Bt4u?z1j;9,7XM03 ʩvˮ,[ch!u0 BCBYv-v~ଦi>uV1vN g.3ms]RulTx:KgCrh(}hP!ø}㗼]BĻ(6DbҜ>i+{r!^U-&@n(WRO!%Z׍I=c]\{kHXA e ^䩣:Ը*AFA7X5E[?,*p*BCb_xUe94 7ueY"4U.ÞE6 ̃d`a j(`5Ժ唓oXl­lVW.&AQUS5ncg+ 34+gT;J P*.S#{"~?fXܗ:)4:;y0*U4ҥLSpê!n¾m\!lF]VD?Ǫ7o NZUnUaOyOԶMi=$ ̽gat:Jy`Rbe?db֍K('pYd*fW[PT۔lк>a*1)*r Ts*we[ORESeoo?A$V=~;_¼wھ6.Ё7L*hA@kc~ SUy:^Xp:b%׈ g[&! ˙cD u+W)Siʘ(<߷Bښ 632!5 L1ı3hB`,ľ(99']4 Z )x@ȳ^+2Us fBp!Bil%*ܻ; gCH ʱ2rP rlٝ>6!RO9CUݥIHqL).22C'F(JcRBs T|}ޭv3 FH\_EaJ8uv0:bՃ~//)S?gm*o[[xndDD8\Qj?PS,R^S[5!UV&=D?%ݘQ'ID4 b!N>pwȖmEJ&(-Qr(\cOA*M3^u_ˡn L1Nu|CGNOT4TZVe.#4vb9Tt;lR鋇y( rM-N K53y;+ PB4lڐlBFh7Y%GJ,ȵ][6wX譑 BnXw9 İhӳ5i$jҷ,ohZIe)X 8H>=V3c$ko%VCy=513ʨ[nԪЇG{cZv.c&zAtOZW}A+ JNQ̪UX\Sťd%# ++\ɮX$S/. !k3e58E&뫚tQjP'+y8z֭iOyqPdˢ==O6/M"oYV dC@SyrΏk#iv?YmOtW,P'oQEŵㆹ7γֺ`bs卐b~Qvrate77T[_Yyu!/)qu zujeZ6'dkI?Ei.ܼ+h12wwRI,|FJ0I#BYN 0.4te=N)q;@XC c,4&w{Ĩ§TJmk|)D5ɖu)D(~;5:\-rXxc bKU).zT@vƳx/]1.A!Iޚq!hav/%FL?5|'KٿŒ?yđ*Gz:`0(l2= OSu޳{KvlH( c>, :ijE[S2ն*6YEVU)Wڴ!]s61t@Df9o:\ھOlSV~Am?t}~BxϿRS2Vҡ%7F/vM;h1*r+/Aӄ.sZd SHAݭ Ђ& W[1\5QPrYhUtq)WF)&SuE{7[Dgagz xn) dR4곬օ/섂$=k`GlԻ[ pISۮqoKlQ'Qs5Kmͯ]).^_+:'N^xfRoyeߥBzC}i;OP|,FArSIt!ǯQ͡Y¹?=ܴ TJ#" ~1If,:$5lܧblbFA󥘪;@!Q(/к? A>7xx\4[:j7ILJj->UDNL 0+Y@dڶCח'i)cUij|he2SbSvqOIcTu^n]V4+B-IMwd%0r]?| %Rk"Zҗa%J8GֱJ;H[cXJ$=yJO,A7!DU$:ȵZ_quU#"h^['lrP'A?P(*T<a 0ܓ:Rbor"!ຎO?>;dز#r\M$Q:3!G13/`Fz@rA!$Ըc"8ԑ-J#S^TT1(1n݌Nh?D9C4;Rjw{,Ӑ'[;/1& +NTe!)BPV+de?bݪ(3j*f jx2NEk`ҍ+$ƢuW۔Kz*):dOR[3'*N;RsWee vfўtYU.~0j{WN+Si5榐yF%)I*n2Ɨ,3I6^@/fX#^jFQvkdg_DhƩWa*h= 6oD]q:'`iˌҰܹ[/4~OI(鵝5?GOl͇.ؓdbPµrRK 6IMߣ.e\Ş[ 7ղ!,TuUf.¦spb /#$(%JAbY>(MkD&.8wO%Pe׆?j=!U94'Qj)Ȗt I"(` őp}DMJقrNc"-a-~oϺW0]lZhȃtN`qv~{p_ULgVіPcLTXFw8z &W3 ARZzvzKd9^S:`V#}htMqv6;>s+ۘ>\e; c"j^ a5Sd@grfKvs O`V}XPF>փk)j"M`VE.޽eYU-^!.kii+&W=fx**:E_-s^Eoo[knnUcffvW˭XSVtc-^a^NҼm霔3E(M$+`ŧ gEhpjQP-kFսTlIv]Zb>2,nAD WzCߧVJ~nSoK& 9{&_(]BAY;]F[?;,n\_ϕMKbm>Wz}. կƿZ[ ޡdtU5ٱZ?i),]PјU:ϜVOU߹rBZk|k@e4~يg o&-SU9wضO6էbnk %Ʊn/v[Se2goy,.K)5%S)\PS_SJ"eު14I/K9i}0E5BNsw=x&fij1l1K)紦?a kP =lzXV,gDMz(FRvoC\[:xphJ~2Qwd_ Դ5A?6Gsׄ!eڑnmEKygviF<2}.6I崏I FȬ4s ĘV&@ג.Ej'*H㎙t;ȗĞMɨ SS:Ku qWȀQXhn\ݶߊ`~'-**gV`X8װ7bauIDLϔ }Llԉ-moĹ3:7"Iwb*BSxħL˿@JԄt|w9lYU N[i61~j32ֿAHzI>h_SE߰>Vw]wջC꼅`R7a,(/=%oB1@MZHA:ѕr3~f]cr;ju(YEBK'0K%#P\)$G% jk{!¶%Hn^J1zm CKDYSmTKٵ}$S>_ $T,juN˃xE"KbgIXw:83vI"'QbѰυ=QDkvEi!0nL<;#Sqr7u]}o7|r-Cl-ԛ0/pOV)z\V_j.f:AnjǮ)$8J0 7P_ }%AI/Wr$ B] %חD]uz+S^fYa'4c~pފz1-cNˤTX%oA8w9Om c+?kTf󫪛]6PV*4K{)vcjP71m=!re^Stz ڗURD _(t)x釴uedxQ$-oycr3 xbűj[DJscjuFP$dt&u9F{VӄҰ'̺OaPWGN'g.cM8K,lf{&_s2=6) xT]]6 yγS_[J7t=9Y[8n?J@/E6B"Yӫ ‘Um:A8L=~5ܻx#R}NU3omTQe}: Cp cX4 "Dv8O)N#[.\G+/9dSo&Q{Iw He&{u}IK zy8 Y/0"u/sDx=y/RԪ1j{ﺖֹ:N!3ec.Z+b7$:6 ?N%iP'=MiD6݂q==HO!?O+{QƱMr8( SbԆ!NKlO/]vB|.#Q F.xx(F%TV^T \# yXEx(LܫhԅU`#ǶOvW&x[&YK~?* Y[' "`ڷ7w(d(!MtO?Ĭ&>kޒ2ӊLb 9+s+U뵐ںυ8ef9*L'lWy9[)˂YOAZ쪄ȫ˽Ȟ4|&s*JJp- i7*}\(MOUqSe3kUlY4TX.IDz%@`]M$ﹽ4[_W=$#яJ0Be7YrN`uY m06W`R J谕Xom\uI}Ocv^cy2) {jj"u.oۍsV Pߧ(XC@Sm9$=4tw'樍J2NHv/K0ȯpiک4!i֩J~Ij'xyD6&ʛ\.W B$;%%1=#r8S|{$F>P'Yj2!=u ^x4hO=luSlpj{:SB1dG)AWq'-NI(.jH5ʖߢXK A=n `kץkf<ڕoָm͹];_0QseVpU<=twV޼_:mk,qUƺgї|Lν{Z]=onի]]Ce y,x?3l^{yE.EY47)jrK$ʪiswlӴ-}4U%.&Sz 3|ݘF5<-~QN-1N̽߉/EAYG*Җ)D"CZrSGZVX9JU7ϚbmE 2BdƅE)F9J)wɥK]T*bK)&T1eU 22ȪzRr+Gj)FiBFcOJZšx )}0ĶtR/i JF*fƿ4%* Nnv, ߦծ^:V =fV0̂ZWT 9MMI~R-XBBhqƺ_{8|\_h/dyE6(ie&U0"|U- q)A(B4Lf[j iqʫEA!΢,BZHRpԖ9{I![ _Nך*'j`3']\5y㰌39sb^Z9*.۬ҀBJw$o)&}L:B!8SIJ3iW@?Ʃ0Z9i3*)ɟTv)O'?eMNr \c7p:(f}gAL4}Rd3 B]\4br-{t,v {[$BA.1M-Sx_TgjkNͭsy h<.Hw6lۋ)ZSo j 0[*S- и()Ԇo҃ (T HY(IFH,HK99xڽlz*Tx5ܖ+„ݖ,>X PYtv0CS] FjԬwu6#GBRojuUCIB=j4=̰sEw"62P#! fQܤ(Lf )M0HoI#y>'`c\vpY< g˺S9FNw!s{+yjeMRv}VnbkN‚t 1$=pD9DnJ)h>rCC*'m** NH$$LT5Ȣ,v"*)Mwyϥn%TiU5=FqS5z%lZF-ϩv!hfActzPҌmA !T&S:d1sQ$t+D"2 a1@DVȴJBޱMRάoe]tVUPSKL]xzk.U=EPcVc Slc^ߺᷩeQ61zX%쟾E]DUE͠Ul=TW }O<$1,x0|vI!š>ވXKʛԧ/}քk-N-Ģw5 bxQH!*["I15"]QIHM>3%2M-3t{S>Ԇ]`°4M{f甡Mump\f£Vĩ7~i˜~2&35 ߗiڈ4JMC_Xf]DNZn2NņSfr>ыC4Q8MfG7ωNbF\WS,'.FaE! bI>M^NYoa0/iY޺M#WUb)ݚ$td4骊pcH=b~ͻ5毦Ȣ^r뢪KӨ|[)h{:4lC{C.N=3O"܇[6(R]S&Ȗ2 g6%_ޟ3EO[_!?gA#S0e9D0k F|`;vkDo/iV4{pⶌTܞ*2-2Ⲻ i!o(yZQ)b-[! C/{K(} K c!$_6]SPXs@^9$J(~)_d}&OatXUC,Auq 2X׷v1wi5!>\ ka|*:FtS±6ʊl|c( K- [ QcM'7N_ GylӘXYU|ݳ# :1(SiKFvY49FPc,.|bU%+mָZQdƳ|z0Ut>,!1_oUJޝ**x^1.ML?: ǵ9>O((v@xv9CqK9XQ <Bp^[l&ҤlkNIe0ޔ=hC 43ٌKR9X)-qJLbG~ɻT$ڧ Ƚ")AR^!CɠM'~W4oHr E::+H +N^)akatS>5c^:P6c>J%Ij٥Z*:*B%LHQDpu =U+KMO=ry $mk: B:z'Pӕ+rjaoyuJLAO,IoZV;qFknoQf5}@,PJR+BSq_UVyTR%q%1aQeY|R u@ȱIJ饖sP%olJ$ I>4&5}.o*z 朓9X#yMdHL &KI$zJ,M?уGŴ (|_)2 wF8HlC|7q`"b?T_oNeb4({RpP*uG5([0DOEeX!JK߲6 (ki[{Z]-G4ldjxrǢ5\]W_JΤ{gkJ +e[i[*s7c&Ի-z_kz)gTTr|)i˨!={XVC2oa<>&;*,.X5KRԬ*Ҹ=G׬ڵ #2UU%_9}K~YQYEcI(sJZ-:0\[C q($}2܁5oqJ`{}_dE)*Z;[9k4i@#T#R-`S~ R \M+Ghl*DE>"tQR 38]Sѫkw\UinS]vo_kPSzwy-iP!9_|%()lk #S͓C4)Ч;-R6 Rn=OP ^ƻuW֤I`VmKӒ.ϽK'{lo*7j㟑\Ij97>7*ˀQbqv\wèVlzM&-.ZĦل:'QQDk$CO 8El"M%MWA?dPiXf~ґWAUƧK*tnFla27| 9r.&]܍,2H ;zu&rؾUq*&_sMCP55#9Tb9 umf} u0rʦCf0Dw--h7֟j{*R7"6n?ZY[rYӤS5XSwR湅L{%hݘȱatvV3rm/ļq9T_{vjrQ`[Љ.5HQ^b?e#W @#:)CoU1ʱz=;8"FHVMEqYR 'E.۪ ZwN4(Z ʮKUXECT}n҉A7@s<$[jzI,W[hEQ&&ᜓZpmwK[*7wJpdgMLԈWetly-/:mMCtJ񖘫[n:7 ^ӵ4 te 8bDuݽzxxaS/_Cn?21kIN@5TI~JYE+Ep<)?4)@gr=%MQy^z-oF*/{(ϽBX#V[,Eh XV ^QOdv(/=lHJ)Db|k8^~U_I{ *m0r-j_* M(w)~:"eQ!STCw@QO 7Ss--c[_W\r\$2,KXʯSm+O2zv]lnE_763dh7{4m4GMdm"|# ^rC$R27~ɾ&AoZ\5E*%I_1.V 1BD;eB:BL.Mڶ)lފwD*+YcYL"~s#s+.kX:BTmPJ+ꋝ7t^ѨMdw]wbv@~o`[$KA5ZR/BJ#gp{qb nU61iի4ҟlX >m@4&K9:#\sSDـ4$$N:Wǡ|6nC͇]b{Ed,96o]e[_i@.nd~$ a2޳]; vmōϡ3\63 rP_BR1KrI$7"bXV8 n`1x?d$N&{I|Mbm^*ʕ*/ ߞ#pm"iN:H9KՃv*55sRtJЮ} b1Q)N%)vA{mSuUgDUumotlڐ&T\mG,'gXg[/8&t)YOSн?OickH[MכWb5VrSeAsfwH"dndz [Y8ҰC:tbW)4Q>Q<&I)ȨqwVj۷ꪒo|j%P**MXM+\ZGjx ; 4ݬd%vEaDc/k`⒬Tn=ɧBCOLr;}:NĪd1-\]MM,.Ц'Mgr1\eC41fS%҆Nj)SPCԤASW;={̹*q8*C:tI_՞eҔ4R1ntxɫe|ymBQ3PVFETTFɺȤwZ&- E5|6 2lY jC,]++CXoaxf٩<q'ĉuKmršQϊVz5tDwi̳v!Et/Ajds<)JRpge}۪*6&Y7SBf2o*;<Ԝb]ҏ)+:YgdqQP?6McwZ*0oH $RB7aw[2<ԩu)ʲ5bΞ"8&%[yC yNo߶B|70^Q9VܧĊnuCk]VS +?H%k.Ec&zZR:\Q,Bg($>}U9G}or1Ve9G{g;+[/9zyYVP7#`0S^;y`+ a00@RkGuG#iKk8oԟul2OS7o_]hk./)颚#>6c]IY'FLÚڟyjvw=,/j[*7!Pzv#bj*ϰ6QFw?'4W )3Fhyq!ԝ ǽR[f)ɧ j)/ V.mieu~lL5!=jC )Շ9e^S(dz]UPm*O-xuRG#lo4 j)$ BۨxTR&B? =-iNl%1u 2QgLKDn((&uJ3\uޒ6w2 Sti?D/}s&TYyM|NPe4HȔ9l\ã:&*iY05nCMSu[d"SYgФ5R}dto'w|΂=/0 be~qR(twiOYZزJB?XְNnS LB p }z%({#{-pwQ|T5ڣ ¯Wē MU9uխ a^ZV儡 3wT]_CC<U#vӦB1`XA,KE,![U- rĚ\s@jMa]W5eM.kŶt0"h^"ĥ %,R!G\&]y'O5m ZGChIC8ӛaëZL;vY!TfB)K|/Q-S5/V}%J Nj'YGC]w-E?#y+x,z|bGVWh3j.!bƗbŏmY!9I M;͜yM)a_`XG"7-V-կM,9᭺U+խw_/8ӐmѢ>n8ԡҫs({lġ6&~җ!<r q"KƆ)3?|Oڝ8XOW4bAh^nZt xu#mGJe!4 3&]BKdoKx!^{LS_~nUu]m1rX1X[xMk]Zm&IDZ[gp==56w DF5m2żu9X y3 lUvu^QWo}ShJ[GoM>֨iyeүFv<%}_B[u1%B^UK)N;h*Hi.,7e3\lZ;|clZ)ZS{-N߀}M!n-? +{IiA\} ժ҉E=%Nځþv^)b4ؒVfMo`KIC d_iY}b%ס1X[ģ U Rۊڱ"QT:d}{UKPB?? 9h"m*΁ 7&kzjn5E7eWK %E,oNW(A$EUB:6եBMi Pil")J!\rnYEi/!cf4uynq4,ihoPymHya4^fb-lHAR[|k @`+ϕ~5+;f9'K~PB)iSoQaƭ<~F 3 ^0(ō*9Ѷm!⡲:俚ZonY4(S~^>z5Mu#o[՚sg+5 ^3bBmgsOWRv_=(qSbPZ2o[I;yG~W|29] 쳼d5U"Rt*ouSfUq 4۴^b]Fm=!,fu^PXFA79_ϟv-%'PR$6Kt__kYMg*ڕ<)K{K:TP\8¤/MV c`#K4HP95s$*2HM;7lmخR2Z#b:=n"kR_9SV|K=z.%z{y w. 'C jY& ,@sLQ-,1Oe NfvII3(jN^Ȧ~ QVcZy/>fQzEQ@{0-u-sD_i7tUK2fTUSќ;G8K"}QXWW|kz¸' DP8 8oAroO]9Y\&$4aojעziRℱ# kz4JAEOt.m=u0ˊ]t3->^t'l+JP{YRR)2/i`'׹Mgz0u]q{L"p$KԄnҼLRypUEx]߲MFiP%Mp#H!+K9@*򮦦a;@RDeR󜴂]*|cqX6Gҫ-:%4&yh=*iRz9oq'yjS!YYy =ElnS9֍N~2soR%} |o2a5%%.l 9(ܥ),O5IeGN՗."=4ڔU9DhTGkF+bA(qwxۮn8Cna 1`dދ OQRԶ01s0qX!A!ci\[ ;/$MFb7XI4s$ sb2Q\_Ϋ( v>X9֩+THkmu6Eï銥^Zo?=5>IUҌ̶BaU@8$(Y>O@ی$@q~(Bfy9jiߖg)t9;Qc]jСüWY5LRTBՕ$E [r?\BaUh=?e{3bۤzKȲ9El;w5n[IHq^svu | ~UX]h|aGdRjKʸuiMR5G"aV%cBV8h缉$Hg0 B# [FPD 3jp"- '#xW场kxMgc_ɱM@o2' /J4La7yhRt7F$(Uv?`2 Dn4DQFoN嬩!Ŋ<WLj8|"cBM!YG*/ !럅;A&ArY_˲;o#8K%T[m1'E~%j1#I= :'#5lYf!.bրҪ E8.5 *NfE8lbw)KB}W/BH*zEVCrNnWlBb#t[j3 >E1ҎqgB)O)SߝM4RvΞ-/ PMZGvk@$n5:N,)G=yE)8f>/15Wc.Ŵ0$'2R!ӴtGתIb֮JeK(wNRM杽&yhw=TrZ&HR78}}\)W1HG7(t=M$TxJSZd$O]}.,-*m?f  .o XUAU&_Z_lxdiU~%q!g8U^F܌>dZ^Sy7?JumUp_= [-7M3Uܦtǭ{-Auj%Mj/;ddPMuuҴH9qNL9k= P+-ZHٚ |ؿcvM݁on5+/̽w=͹%xiLdџġz ѡE2aW=JB]@iߚi|k ִʱ|~qe+&q Caœ|Ea1vc)E!<򚄢Qgo =$SZ #V(;Dy=A0O] ܣZaqpz!/XKC\%M!r[brä^ĝ מu~7a{/)sWJ^RaVSI`vJhY'i ^5xL<#kv]kD]CϗYSiq "a9aD3Le]E-DBG etbzyHY܇٬j԰+.rEa*^5([~^|f*";l|J>黰ՆI,-YUPu)d:ZQMq[Л!Ghe5kH, -alE0">)yOTeS&xeYe篲R2zOeRt,%LA\H~-1璪ʘ*-*f?U R'O)]Ά$K2DY:82:# |wz"2WdC7g]BdRf$pieL,&ܒ䆏`[pZFk7t..HО+aU 82#ؗ=ȆJs.F=e^B.IħiWj︪ci`iޢ: l>ijW4{MUު)V?k$h"|yMv|JdtSQePd]@dsRAV%؇1o:f_k~]4*>Tx2NI*)l[w4~Jr)I|ʦ&p1S 1x\n={ H/SW+Y+G2Yi2 ħP;TIɲI}g)-K:+ ¸\W%TdfʂE#)+w+3 Zʰ-TE}uUDN9NIE%LC(-CX# PRBMu={ 3Wm ]"Qn (rA? Z垧8]EK(K-=t#^*»6楕P_4§W| rܴMNWP2 ޷3*AUbP$]-5zdsz&I40}K+JФ[ey".&OyE =K㡣},Rj['')WqI%$tGBWYR'6͌d] Sw_|knUXb0 zltq;//]TgyÎ袓Ԓ1yTg]WvV (,ʁ5°2H㷜C|1SeS,z ]MRO&E譱EGT#/-EsN?Uc,ʽa"W=}p8 bH~Ʌ%kR*@ɷ}]^Foނ£*1&Cp zxޡ"U{:,~ԬɄ6b5sQ܇?E9M.%e> 4à <ϑld͉$I2aFVkƢA3@E߰Mw?aO"49*wDj>a $X+1kέ Ñ$cɡ0,Z/=^_]Lү'G"tq9_dKBъg.R4Q ^P k -|"!CJK YRz\x O2I^QVVI/F n:]4.3.6{ao#ezBIۜ$`0 }ryв]Gdc2*fgnQ{AJVos%ndj FWݫ{QO<'z+8i^4B`y Mo9P)JS`Z݆QZX+BWZ^ŋI;L`[K>砞I>akaR5D" #R[8FyI | [̟g*򉌭/UDF3eۺ#-4-P 4ڏDߛ=-(0J}FZiq&^z+[޲c=IUVSM 0IH'YM[TMG:=;ZdRMH-q7PT LzJJXA;DIZZڽ*駌%Q>hi|.M1@UPօN+H>=:ME]n2ҤmJaS!81C1xJ$?q? kvjJK#4hj%Sֹt8XnePXgU&R){e_q5F 13+If-9ѽ"T l* x<\gjݥetY紲Jr>W}fӔZX{jKn+ѮF#T4XLe>vW}E mF഻ؾsu QŴ =t*/WV`}xյr(bKa :ibQ7*d5N.%UϯuWL$$;hyګ?8&XR9ϪWkVBBl|-C`B}_.\OQLP%]ڪJrT Z8/u%zI"/Z8NQ'.1{u(V$ۈPuDR7ٖP3NFe P5!I~T)ͧ%Nei&hRQP!pdjv^/*6IlׂHYDɥ#i^v^4Of{8u^ )"}Pg𦚇{ȱkS,Ǵ6'3 tXO@p>ȣiQ=A{(;ME=!|$6哸 wM?c]lwMfɁzbR+."DW!~OU,=*i'_^J]+JV؈)?z:P~ ~i{xPHT16k]OPUS㍉n6Ӷ\HpbBך?~&Tj1`F5wabp߂ j^^{ift-q1tƭ beKD(v}MQN~+s<%4vl$ro9(=)_6%Ik/2) nMsFFƮ=ʚ?E9n3߮ԁq9 \*N>bb~493eܶWν+m yОD) @ʑ=pd+ R'Т{?#HMճ~쨸[} +2}ڗ}7͗[G|0AE>zi𦫠`sE9n*J_ k} 9 'W=uFcUtgSUlNIQ{]6 **{k;7 ĢD /}q=9^_uR&o7<_Ruڣ$bTNE5}q!+}>4h,ρH9* WSB jɫhXDFDӍIo{;S.:VϊeeseNn"W{-+4bY|3'F)YYUVTUUvL~,2L )E4a"KK4%߯GX_;'KKXL}VcNljd@@IsK}ƞ%SWʁl>Dn۳fTxPwQsۉ=bz˯uTf94#V"~c-?5ƤeWu`n6{ ? se< SQa`YhKp D)i!]7S݇Vk <љW/z{V[..^͕ؤ-$~Ut Iugצª {[PckqlNrC2$ʠ:VY}iiɥ1vKoBJy+/gg\ɭӷ]y_;[ .{ZwC# UdOyƕġ99%5r{PX~t /ߴqABR]\g*rhi+\"Ԗ+ L.s/86k֙PB6} t *)$6F[H^[A!=d_M@}LI.k g22o6 YJ\F0#r;6t+͇T (T`J}+hR@?cԍ* 038"V s7x[{$]qjROFMNF_x]Vz'/3*q )}UcJnH؟Q5֤"Z<'r/9Zv,B5#T{M6ʍw f{75;ce="TM:\,<'NC^2{m:T+-tF2k%dׇ2Ytf_W;0"iZi}=[V123P|(PJ d+Fs2>˭Fal9W|AؔV^ѯѬXԙtH i2QUgJt ѶF‚8 ĬKwԢ|]5biŬҚsqRsuz"Yf칒%{6!kQ4e d{H^D oA.WWZn4D7=U+йP!q YdlRWxP+*"M{v X^wİRGSqgx꯶6($]BD$!OUh3`RUfxֻuzn_57ãAO"#{Л}iykT _kiPPvcwP:ofdt8TSL"6RaVj7SX0M b|k(K0bU`DtںkcOjBjBt[6fw'WyUihxUgM)"ٷesa`ʬQ%ukFFc:-"HL^%ʞ9|nJu ę#6XѨW~izĞ"zo9'=Δ/~qfq:Q$\o-{u"%+I'3B;A=E=rLjY1,Uߞ(iRr$aZo%-:AQ|o_=YPYHzƶ}׍Qn"jJYmgv'iak#fDu.˜(Z^Vn4rswVՐ lҊIg{ikN7ʑXAn*&9j j[ [oai ٌjme*`6b~vBj~E*gDxrf*'02.wr޶q%${,͝{Kl믱5-Aj!k2n(n"CqE%8mZP;Wj `i*{̭EH!{Gz+7Smۯ]9O"GqzSbz}EeJgE<n)1}K&<1E--O}}hZ{-Le#c0q᰷{D_4ZِϰTuxS̭;^z8֖;2F$ySB)$qHSѶ\Myiݠ ]Je5׬cuV{* Uv_j"~]j(uG|+1WZWJMM~5e%/Wav&v$klޅfxSW_Bd%J͑v(P5QL=I4-zR%@יm҈JE?^gB,[x[V_q IT0h嗌 aZ{*{yji)fFn)KB6dj T7̳U{r+Y@a:ǪWȣUcqbeWj60U9ICi5dn mYxkJ+a㚊Yf}%FR'tM`j;E`N&øUû$ġ"粃qVu(=fvZڋe$.sˡ|-ŧ #@>/ޕ]@*GRk l SrtKm /⩤{˯E $˫̎xզx`NRd+m5^LIOLV"] ﲂj}?MN#@-&j (ԾfV%A]eS=Ua{] &xuUutr K_@Fs$6L@uԞDMA2OPD iŕI?I]WjTJc4 ]bt66+ faUVYjB+CYCRljQ"GnhH kFb@6mYz,!P'1$k5?99i/UF%BWӲ) M!POD B/o]y$fl!cMΦyE5%EU$H4,JTI׫ C"cYc:rWjTNyv[M:zs5Hk:$ VoӓB]PM$Soc[y"{ojB@5r&W ۠Hia/2inERPErhP,%n0X*/f-l)*J~'Κ<>u"Y{nv7SzݾK59AU-R}ۢETu&;óF*>)Pr(CS5dH1BPlS`!!)?3),. 3+OZMM 6_i}ky6iiFBܻXKA5'1_N3Ш9; }nouIU?@EqfuV¼IK?JVBxnW{- a -2}ol$RO} YI2~eA¦;)]~ڍs[bVHyDHk*,;K)[_J2hąMv5*^kmumEyLz(X1o8ۋCX!;O_RXՔ*RJO4Ӻ\35浣MoXxX֎;n{ s̎ e Cm]<_āJ8п3~/Bi)#M}WY"tOYISO V^hXn _(2BUc!z[lew=:CC0𪨕Y*}"e|;A` ,rRu9`N4C5̫&E"Q*䒕H,vJY\Ȓ+}?-߁>ԕN@QGcb@i 64\K6E?SO˩E 2Fe$QeVuEuU@Qo"1#u(XΕe' _u=SYu"T[L^L&|I-ʦuҍ]9I5N.]9'uo(4O3A۬^{s9x4jbXb6m/;WOa*yM=U+,, U0P{T|PFyoQ9 IF{Ҩ>=׫5o$E቞Ҵ㶢7'#yK fW3]}cjCreZQj\]7b- TX0Gp ^+°ϙy,b~>Thvݣ/ν LrLփ!nU []M9.~̴F.4;_m]Nڃ5ڿdqNb7UYu]0s)9N :hr2( 4B~ߑV_Φi;S7΁*W7J/n{7p)l|?rl NZP¡Ba^=V7ܺ:S!9zr5ޗȜ+萠3u̲պVT~}JOHO~$+h:8)]Uo,;zH?BzE ?}_׃&TIG/( ou-薗h)X_l5H3'%% $HןF+.TIDΉzbF"B|_kɧ 2G#;Uzɨ?I*ӻkZRV^@I qIρN[~%* ʐ Ҳ$uWXBsӦ~kdxɦS 2;Uf8YH-J(#QMvM;\^FU]["{Oes+#:Gwv7m uRBt Z[ >E߹ǵU[6ߝ|>^!I7jP?um )}ge}|Hv^~Mv9BM4ȁ[D_I>Y@9_c~Ԣ۾)NXTSPi!~o^ٵi%-wm9`l.&HBৎkU>,ʌM̍Y$)LFdN@6&I2YߊI /ej^c(Z5ƤW%X{:ΠROz;t:ڙ,J5%et?XaA3I[&R0Vu&}(,$srmo(Egkꤸ˓f  eDږioԨ-S7cy_} J~e&ゖ&Ggs(n++!V,5#BNF944·  f)@ǩ񪿮]).*+zdTk(yҢaBfϼWkUgfgU '>'nrs2'HCؑ槨7,h`CU(gK-#iXڂgGB2zC݃{TStO .B+ yEmp,#]Vqƅz,JkX'+P*:,_RFW7V-鲽eG%mhѽO$7tym6*Ko?]*zyIGM#@m:1d(׉m?A"Kܴ_vg#lqvUDžDaYQ蠉BgK'~e*izuA!̞˔sk)ʹHP

&vf} /Ή6KJ=fJʉ('yߢޞȶ]iz46,5/N{JP"ּ%9 ͥqMuMUurGC:5Fs͡*_;M7·J+MR^m9GΜMEBڲvT}dP4vͲtVAr2ҭE/G\@s,.cWFxY^'cK VJwXt+XEuApI}+moȬa]aAxf]j,/0>hTk~W/ N+iG--r+!v2<+V CAk|eOl 43|އ10 Ͽ|Me{Y%z.O:~⚪zs.>uƟ9z8T>0Ֆq'$91U >\P*yi( er ()T:׽ vѵ܋Uv\mFPGaM/lvt'x7΅5%ts3b3ԥM+T4;M&._?2;>\-jLB7%ěi4^#n"FTmDJޥo}ևevj<-lOMV~5v|6S_"]m"j4Kp':"~)°N^f*bpoK_DK`;(қXUVUqcu=O҆fI]=QqaEsﺕ)>-ξ'o/LľcU|,*KWWSvY!a@@ChDF(~?4,ގJ)iIfhdћM4^GF̢wmxd*8[LuGG<Vo[OЕy:[WuYk-ב/2mj[2gʴμ56s: TllFyEȎdRэQNaS. 2kZBP۝l!iwl,) q$ N/2(Q){NV6/eз9+Er" 4Khб'⚉*D] +컴Kԓ^ji9 +ΩR#,X@xRgBi(LRB@6b'R/qve2q'}#*s;S]c˫uMnʵuUd4,,"3[UG΢ sNQJDݤ>U7ږ;葩 VL;ΙfyX)dvVyA2m3,$΍5gpα99vԕ^}\=%ՕՊp+YJA(5IP"3JBK?3VdnE]e9aGhQ5Ѫ++j5=f!@";shpwqRa\Vm0r F;ȟ`ZJQEJ%h'$U#YzPOXW3r;o$ՙ~v6[L+ܚ^IUĊ+ oc"6U)1Bi>qL2"xNE*F|}Ι/lRUJȑ dTk|] "My)2ydJƧ몳jՔ ֞=9ARnJĆwe5}*ҨԺpoȢǂqv{о=aAac@=jMD[MdMQgTwip S9=˞E''zȎх"đ̄Ɖ)\KZG2zY]Jԁ#^[닡+~67ߖu CB5w3MJwB؅Y%$"u]9':slH%C RS#iij7u'6sպ@o$}+)]4s̢wf'g2I|i,,sM]cSat6jzô)M J%89 jF{=ouO _S}ևR3&7WwiͮaL΅ЅБU"5G%CR5$HPU^ㅸfb}n5ľQm!`6Gu T )R c B6WƢ5fw9I}:i :T~kouSWgߺT fWSA>E*Im4&(^2cH1jpDHXW ;"ozn'd{tW+Zj|2F۴OzYS OФ-wY^[OЗQbd @ʠ,p&<$8PBȨ~(-w%lԪ3 A9Ƒz5)g/{Y#o%㳅U1ӽ@i-t)'h&! Ba:)(B;ϗߕ++cF,o]9}}pvg"erXqS@9/z밴k)~hUS}KUCe2%%(e=D7'$/Ʀ{(5Oi Uvu{ttU%msՑC=UEО@ȅUR5bTeMǦ ~z=Gؕ|O i@f]x$OJ.L$xPg|(181ozI͏J -ujdP($"Nh$u)v}}YϰayaiGJN ηmȨ}/SחUP|қkXF'+y fPn$N#u@q/9SOWB%4< (멿FG{V}r\:(͟ߥJ9ӤBبF|QҒ?qPj3P'[L{95өH iKgS@nTxoWWI2Dǵ`oq5WYqY}}Am뻬ZQ9YQDE6dU1X\Af)J K;T ^}lTK]"4)SнGS+̚ >wDޝ!/qmuASȌȨEIBJn0;ʯ+&eD\&dlݗUT#oSgPN÷ݮrYOb'+ml6Z΄0m;iaUA%ѷ[S)[ bޝ&,@ea=C̛N7y,^4J22_l|nErDg=7+*/v'? = }K ?}4R[NnZ!5ZfU0 _TӎuY'`5E&dyqȲWء7SDUt3%vpQ=WT{ ScMQ!_"eNۥ*񨼎(Ygz ;*X&>gz$-[UJ4Щx>if(m( T!}nΖu#a֞6ؑ$@}+#l~Em (ƩQ<u9Ʌs݅aܑVr5)!SFu${/yyƻ΢jQWNUuE&yV4} ӿReJ0BBAqR $%oQ='ҚW~5TO?\F 26,s;:[/ƕ]lBK_NNvW:wI'n)<ۍbZNx?Ofu7Bh@};'T;qQOVPk6=sB "_ՠPlO2={PKkn^?S IXI5+Wgj_^ܜL?r$OuW)|޳/*׽w3:nABɹhOOq}2D/<s(EaqvVG~/ftOؒ^4,ZRr k ;ȟf/e;/AiUY_JOSW5 u1?xh?Io G5uVDLo q{ΕcQy5ԞG2$S;UJwHR&W@G:9JtBd$*DeEnU/z YV4m}hu5ҦeTss8SX[ M&v$2fb\)M+#m=TQE3v$Nn0o?*iAm&# ]uШ@Po;L^#dR_2ku&qKOX]Ҽ<+-).иRHƸ/׸zMqS<(iI}S7С߁@ (tDVNp~Шȁ29 mv):*il'!P@O#vl&[^$u{U*?Ww(_WWDq]睊'0(I)ulOyV*ª%d ]WMgBz)#v&"jğSdXF@AED*XtbHhBo>;F{:ϙUhqm}gD:u~μ= H4Τ*iW!t?i$,JʏYa7ԅkF<7'=fOs~+=fFeb*yְQk+AMEYEЕe$i l#<0D%G#pB 2%&vD)UXxSR(Q=/ۇWgtck\+0;m5tKX_J Wew“YAC FtvX* MCz`ao(%œE4Q{RNd9YxQ%{);$j I[IPC2*3aIv둝9=tDȬD0)-RkdDNgf?.U&VһS\cYViv*M$,h8=V6&~R;kNO"56ӑQ7BiIIT'uZ}cVzS-f:P;Nr4p!G=VX `5][xV(58]s&:kUc6Η0 Z`Q!VϚZń ]sn 6?3uΚEnI3]=dmHP^oSz;+nV쯶=72Enox" [G$IVQw[%k;WaٵK]~l*kTlNCWl_^ayUjL9%/ZT)qOw!Q|R( R~_m߉v]eҡ9ӶQETlU)!W$x(G1Qx|pCWǑƾRȈĩSNWJC4Zw T`R+hM;^ϡԏL( aZ/*{庛"msXA1<,orl>s#QP(,= ~<;ovUvAi ̈́OB}>(yyp~ K[.z U@FGV6جzv ioɁ H2G@m)o~E…~BS*!a*Tr5t;f_C9J{/zj5U EzW'yAЪ$k!B_~WiYSENf6*"Xs[eG=Weas_mywNO/8)mU=%`cNJPl1}zyiVNO}Q&oMA]9IMIϾXm(̎ IJ"r6O9Qq(8q!y6o]X,رv܉/lp0Qk:#U}jjs- _cQ^q:opZ*,|I-1;+oimJzzs">bc25e/w *lb Zص X.RLl[pc-gïVFJGM+׮̚,M$h8D3V-7ѿ}sidZ5W;mq=|siDΤRYER}T"D3%FUi~Ю:\d)sm/+Jּ%Ρ$Pbhad]J'5/䬕y eArq4lD]kO}Y-g+J;w%74iJ|X99]%y3ln+7׫{(LP=?J%ޚYO,QZ4s(!s>9=1gJj'R}aq?W~qYo6;+Ld@/%ZQzȟmmR_Uj@4((%_U,0뫋\B1ִ|P?6ne+㬍5r+Uh=L}υHU <լ uQ?SCWDo!|5-U~zU'7jY Ol̶Zoeuy\,搽Z0{R뵚RZzR"d"/S_h]c[Qi(#QBG jtoM ,?5ǂ TWgaΝ2*@HP|fBeOFM7;pw_#aȮyjŒ̈W;H5'1| ];%Y~]TH[ w)6=yϥUUmϘgrONq5=Y;&*jVTjTOm/_+_ޠ8w.m;ʶs,*&w\3_.1˦v-,vLB/bGw(ѳ(R/24(˥P4LȁOB[o$Ё+ѩ"~m4jM+aQ K_FuemH[Wy4G9Q1 DO2V\Z4/ameM9jZd8|XT%A5AᲉZIiրu6W콈`* T6Q==OG>}KkgDf@؛cksloaiAV S o@cY]@;<KMn>{/2ׯGPblڤB\aU6"dO^yMfUƉ╬F /m+esl)Uz[(8su"h?8%( 4Z̸';F0(yO+Wj[vӮ[I+$K4ɫFGbDQq4ԟCD7^8?zs;1cerSLcCm^ 沯|sok+%M\Mڂ7"Pmt m#=b΂]Sr3uקsRX4;q"u^L'h>1 rZ }m]v:>RQEQ, άLjm#jшx cR3bwoIwQl;>Pץvœ^[^,^$7|ªe0e FP#|k|.6/5 ,lZǛaf NJ),*I5>QԚ% !tEך=ɩ4gH?lH 9 0"1AG죮BIJT=#k`v| [LWМA|^ BUf,yc4ޚn36F;9,DZqJKU3c~>YW]4~{v=ZƑVy EAZ:*wB7"(~Ͼ֩/ P90l5jhPX%T׾))gVϴ))JG<nīݧ of=wo%SoܘMNT$QHG~HʄS34K# d$|1d%<@GonzWSa7 ))4'PtFGq=[a߃^_mV<*C׌LPWG8b U*0ӂS5J+,~ҭ k=ڛf FI(iQ RzŽ]0tV 8#JqIG5´'TUzL:~8 }WyETg+!2 zU򐵡=ajϺhUUB>SFh )lb߳۾/μ)K) a(,!5:9z˒Җ@^ZHQGlGtnׅ}+EB[9X 5Kzާ,kBTdEVQX7ԑ[3tݎ C@t:o8va~ bo޺}>!6MaF1eeJS7o=[bQKKuݔJOfqAHf-ANXe(ZR=qV"i|f7wC1)'59TX&9s$P&2ygٝ~z?NgOdC406Pm YXDeHkcityU_>ByDzXj^5Q1WUg!U |m A@w"M$;D)rXb>ÛhBƽp4F5odLJSMCCpځ.iVzos@/ {lCT3qj~ \#.qiL\t3N(|Дp-,'QݦڣjjNo ?0~]5Aޕ/O;{j }Y؇[Yk7AR +M֦T"-bYI;j1xbԞvUwIp[5Lq$9+՗a޲{ 'ew^}W7lvR;7aQ;|e_]F*N|/5Su{Slp7[ |OMwZIvITť5x; ufvB1lWs1NwQ1|Vg-\h4觬i|{;-߰'̴ %ťOԆUjLrjѕZ7P7CV]7ԥϿئ5*WC\ny{ͭISJȮ/zP:A ~,>dAqխ,b!{!em|"SK[4ҨQr=w_lrTlM3It)uuSrLoŁUVUqWf/ZxoU'k'm2׷/kL{zTbT͖Ri)NpW9uI7kɭ)yrqMɩ:D;^" E?[=(DSu1}TWťk]]O(OQw}s\Q䭪w- U8Ot#d쓶FU:1QT}CnYH 0.]*KI`"l'Mкsε~3bU1:L( mۓMxGk56'(KBkkx[?rs{k[,W^!++}ڎάڛ=yl12=aηJyJr m?BPf$.Z ;F$|Gͱi{N4)E.>m ؚkQ"*h"~X}euGB|Y$J/*ЊS-4$IL,cfbP5„3'ѳl딗_f_hVvвH'"=5'=V7ʻJ!|D>iXFѯ霃t'tm3Bb CMV&YxȘ# (†I>.kYS]Y]mv5 T5u{j%1IuI40)_s=vXf vSMtd[h52zry%*bR]T+-*eN[j՞"XurQVi8Z_p4PS JmX|W߮OA_c,,MKq/JgH>U2-nB[Iu3tG+btWXW SU_I%!(~Z9qҪ*OS s^ns¼ k]oUFiǤX7n])=yNDظ[BxʳF$$_D'/-Zbغߓֵ*_zb6X}naXX gm*WR|īNf&_[եUYu+p-ݖvӭve/>%uG]qϲodPiQ(mcf_R51/%2MӰ'h4ylVgQZ|:7Nj&IYlfq9lU'G#N[/9`K~سS,rz%G_߲"T -/e^jQjNz bgЇ]%@Dl)tBkJ?tjAtXD,Hg~rOD_f|th%B\苃zϡbjExWصq9h:c(.=F+jJ0hB1{PG:SC hSZWBU=Xō,[DHJcj'V+ Pk!p %7.=$_]BRe_JDS*F0*/1? 7jxגɹO׼GZ蟅\[F9Ym5hg7^dX #7M58'_;o'}PvІNI %f,:1IFfo/q%SA^{?t%GׯҥSIG|m>-Z=!,Rf6cAeZ+Gh$ rE]huX+[JT({ B0^ a6ƅ.|p|e4j}xo9})'aK+ZTpheBX%# < movk^{{=PVlsF"; F;dHkbf$N*"9\\m_NGpX'b2=ouՒ7kSbPgP";AG%2#X]$SP'mZ]lZ&/(u4f_;mM29D0/hlZY0mWWCX纹clC~gL{VeXtBKgY[{V啃`ZͲ 4=Xq .Ry*f]x8LSLn9D_I%"ʡVK*QPUȸ*WK eAL{疖 ׇWb|-%ɀG>Ttgee*5"B2J Als4DU'ߤ)'bynV4n,G,yAkb.@kZVr2I&?(\,M~rh_1m­zm8%3_Y&-mݩ:ٜME<µBg=V]x [0ӡN$SuȪ CĞ2bDt*X[œ]W(^[hoYrmmkqUE@)HpJOxD9*ѮEG[\;\͡Wv ﳆ03K=$7f[yqӸaU!8m3K^c:2%{o2ˈCi5 >]Fa `ڮ"ޢ3/"#$Mm;9CVz"Uǔ$(1 UM$TSɚ1hE%V*Tk2"!/-Uco }jƷVx3axBC[ДFrzUs{! $1[2 퉚~*{IV* m@dO#X7šPϑt犧[y靻q8vhk8Y??vu6Zs<KZ~>WdsLޢuacXrԼDȴ;EHMI E0nȧ:R`i}&^Ŧh޳[V2w%I[+1v`Fm쵔%N(+[hs$56~n:i&גڵ9tbܺRy|Sp1r;y9\?}mS[m=΋{%6tt~ g=UL{q [sY־jjQiEK6(QIط~˜ِn#}/t{iJ iP$" d7X-nUBCb t#+ܤwɉ@3AߍbPߋPҿeLed(* 8#e{oc1M +G9 9>T]L:jN"F>4~6g{`=vP_&#X}ϕ'z*{oڷeܥu?uאcwO$KșwE@UL4(-|$mSN_=؞@(>3;PORaL(l˾2ɻ>=dc +Hװj%f&81g4X}kxvrO]=QfsZz̀վ_wćdQj#uqbS}қz;(*UgSG mSZjiR{MGu?i- 2t]G^ۄӖCXh[)PRc]]T=f9UT^ĞZ sL ]W }c du}7e^% M5S)rCH3h*̽EcjD˨)4"4ʖG9mx~7^1V]&V)稏!c/Vm'5%堩6oTc` ,K*Υ&?57p,eeg91:lz6(_B&tMl>mA6c]QR_O$c湿yjw@or=WGMK}9i&UtZ!ޚjmַݿ%{NW0.t3ö}Dnn3M N?܍ų7>; m!ΞKz>(ıoݵkp __PlZ1't7$B,G˙L9lO(N]OX@4##˾g|o9G S5) ]MWߪ l0iӜ7K^-\(a!aUȿ)*yV=U@ϱ{.tV[qouTcRG0끬EQĢPk B6řuWd%}yxБ!cs#s,IiM]Mbt) H 1`^9(AE)/? T7_r.MĨ6 //qUQVzO][ѱ]Ǐx`=i^{L?t1ݵ/eb~^`ˮ Ykh64[w֝C~NOPr+J# NxbvU|HMJVVUJ3e\U^lk$4ٕ犞hWҿv5k!OSJ]%Z=snT/!"Ys9t ;-Kn7iVH*S.*֥C^K6~ZUArSŽdW?e*W4VY@U^ZW%t?MĩpG>#Nq.w Qx"++ SIJ|u%yIuE{6*} /K^iw-wGv*M۶P+ŽG]E>sNM|Ƭ5 ^b% NO>Jkl{fIvvTe{IK\)ӻ;{ѨsҳV^C5dOT)7)8M#&𧯧nXc2dlk'־xsfjZdQAYA) £)y)iNwHNV'G1]t;f5=6\Up4:u Jkڲk)Vlj uMĬ:1 ̸Ad45 ^ZVQ@z/p8kۮNH].sR_B- FGrXt.3hHd4K-ZdnJ,h ԫjڭ0U">Z++Pw!Ӈ˻禮ƹQk2M+B]l:Y'k؍UD) P9d9EP #QОa!H]y߼љŠRL)ZR˰Mk(P ,׏(|ъ<Bւ1([ouod0IFM d)!(,$q-ʏ=Eg ԨS=w)"UK1o;D8F71R #jc$Hf?ik,?Luc|sϕۮŝk&"v_cyʝq캟B6I=1v_j;I6iˇr)/xuVt;KiXVm} zs7Q7WfZ(n "}1>sHY9gEWtf">ynK&K?fOx_D`nVA()Ve".K7Ԣ]+_mSօF.eAjXk=q"&]|jYPΪƬ* jxED7$u*jZ9M]3}:=WYC Wŕ ,w"x] M iׇT1>2^YR\Ct n2(Hʈau>yfT4d6K(zMSNS_*_M*[pt`IYIʪe ~S2w&Ӌb5D_tj;N9wHj{S!Kjx͝j[3.isUeJՌ YJ)}->֢Ž񮵫oYc2zj)"4K+g})8* EW$^S8%%PCHl"JzUlהBέ1\lJ͸nIb _4hoi+vd)U] P?qH]šqɥ5ZJ ƣފfߥn}\įۋkv WwVL36>ܙu_z)KΞ;ZoB)T)[ ӍrU,~g,h,[wTbq+hS9gqˆQ|U?~ɲPZ"k9Kx%\`Tݺ_b{>e{,vz %8zd{`oD"(НZzq QWlzlz=P`9S>`͖dl"p0I[֢#7~ͧ6Wӝb+ҾkڢAK}yu6f[r =un*<$9pl4 {K%;dv*Y 9Wf-Sђ˴}/aV]_Q|U)SG'`Wu^~E^|^MnMeĮ)ÒG`qoYSA!xۓ+檩ZnKܵj,h2ŬJ*xC*d]r ^64~u Z5ecBp^U֫N,[,=Ǎ#M)-m)6MZe"ܲ~R |9Ѿa{H1!!Ĺ6-a!iM+Y5÷4њA9DQE<Vsl4n xUܬp27eU],oV~ߗG%PX`"7uW)$кT>dґMiUz*URfjh}u/,eX8W\ѯj)UAĦNiQA,qVŭ bUl|ЩʒBK̭;4heThpWLPu:NԔ~YYRlajK~]^r.ONG="KJBQM= 1jƭR):T.uUlش1.-U-pnYū"f7VR-j W`g YkR ,mJh"%ooemn Zƭo<Eci VD3,3 lfiCC;]JnGgpKbܓh8i\hKR EMy'g[9XD<6\5:a/%%AEG]]dPt(eZE!nu8Y8ZJhs%2ݹN#lK K^ZaT9C[`V*E;)Mf =*!T-U-Z ],Ibj+Fjcfua=FvnM stQ/l: )z!dI7O=P9wf;踬&*"ոYSTuU;z2+$dz/(*Z[7Q{5,cդ`+Nʃ]DG&a6W1ͨ,6e,a"92;Hc+ XДv)r_3TOu/*W+ ,)EFѕYUw~T<}])cNPG;8BYwAS= +WXs&칈n"$aC=unKG?8e@~+ZYm#x'vi u4[hgܣ %\4Y1IdG(U\RP, W-bʚ;B8}&;>a$@8ˢ|Y,=9'5py8[qf!!ܿ4i^l'T؞LZ x69B$Ɯnl=cN*4Q8# cn*=|ZV"JRa,V CNh k&-OLA#"t_#ERCRRڳH; -̼n|W!8Eq^s\VǰtCg岠Ȟ}r)E 45aR_ySKޝURlo(rud1-[Z%,i'䫟W0,kN*> tGQf~)gԻifӢ\cL%OyE}ПqR0pضizo$q9MĬvIW}h|kz;ڂӄsss85lSVHұiVhz@O\61sQl*)wTTd%Ec{Oq歪Пj;6w͟#~gVˡМ\hc^^줻Kk;qLOdRs! luhm5{[vUI*wԿAɝ.y,kuwno#~n7)ل%rzNTV&( Қ^ZrtZxǎ=uMO%Q= \]^$xyc6#iKc1")ɜI'H!HB )+\t wn, GziE|+r)kfȸQP=Zm?σ )򴪹%]^74/Y\Zҫ̓45LTt|!Xc[sk:3g~>l-h G=,;v}K## A2e9#;Lܔ Li BeP !! 9PgWÕ\VeH)6aoY&::6"SO[=ɦz9S}J)J3 +BE͢':Q27{N[7QOTceܷ?I5źEEhdAzUپyM)l\HXRm UxSmbzڊ 7zP/hLG} s/ QW'xZ:)oiX|kRMfcE,cK7Y.EfUDjȪ2LƸ*{P _1S) -\_|Ϭ4k,;pT~sN4JS뉟 HiU.v6%ޒ,+>Fk/ń`HېdWQ٭3$ǚOjZݡ{O8pfM>v^c6(έK*;j)YQ3F%Y׌;S긎ϰTk,(*-$_|(('$%>/,(?E^~Y"IMTT[Igڱ`U) 7.7h_^BUS<1E@qw/*H!^ qe11dSt<}1:(lk4\ijr;I!d%(M+jG{(cCpAXsqHP5UV2D;"gf 1 GPnoR~L;.>6w66ݎ`_ `c{zob[%KMPތؘC^lj爈?bz1K&M<8haD8{YldV:'!fz:q `&hq%;eYmvWZl;g^gXr=EnxLfryNz1øsT_Wu)QI|z_P(|keǰ;R) *ۉtc1Ug9IԊLH%Ev7N˶3 >Rt5%mͰ6_j8cJbk(HKJ)bVxY'^&1inSt9!E#zeU=uge~aeS%}G9EUtKru]PԦٵ|iu')ɱYAc_&n&ޫ,zPWXeoHeq+-ӧ:6WCyc5i/DqW'I0),d-itY0k(IS{reU}1CVtPoDEUS.Ln9U'˪;Ԅ8C<)J ^YPt'RPǪ; jRNښYk**ue 9&ԁ8dUXVt., n:ΧI4ϕ^-,^()5IVO)t2Y ZWR\;jϥ}O.ĖoЭUc ~|ѬcDG%E7Nj̎+̀`+mhuPͷ̦ǡ8*keI]:W3/ 94/!x՘ ~ʽvdzo# +drenaVIzzD)>PŨ ןc*ɴ7rR3iE},kBn_:p8N+^ql 0,̫ldvd托J!ˬ7 lsHZkP]#v ܶ}m!*)W_!gs]Ճym̏w~M6`];1=-8E֌Y])pT]Bc[T?~ zz)Y'wEBmY讯#Rl7cZ~hPs`$X(ƳerV̒3@|Lj\`@Zkgb P{fxag-l1j)Vm3. U˯(T|EOAXYSxMkQQ,ḿDL1IU.փ}HF`ěamıfd0cɂ/LsCڔƥ4Ҳe,MCJTZ{[4 ϻMqݖqaiWx)d_;0Fzk^aEce^kFL Awיw"CFU)vyZ{ RV9l-(HݕJo5F\5^f=禓|jKF慭7ǵc܄pUIu$tZ\mCz! t$QpVzuUw-bxPoP#U*l}]Rr=At^SV.Kd rQAN}ƦT8Ml[$j[tcn 2J76?BWv.*zlY0l.Z+dyBM]egv&< 7Ҙa7&=JR lA޲v !LHBB(ST^S 9ß|%ϻڍ^w6t'e.*+ȀWb$٭FUNEK%)I)b)oC*,}yc4"R>N*+UpmZ 4{dco-@'nytun`hJ= 0psɅ9P߹k 2,f]wիMei)Is1ȽZ)bi6oXwT®*ݝ2SSm J.I0~瘴YҼdRǙq>T{k୭cbͨoM]<]UڷJʴ([_ Mxp¬skOARUDkX-"*_ƾsU$Z̩;T' NI. ϖ6Ys*d|uYB} >fG)ɲؕx'%b݃VmkP_T5ELjYrq/VEi{Cl<īFPšK'DgL"5$B'u3w~BI9_TFA~٠S7v~lFV=_*mz9/ fz!]//AdH%F<,9Q cr|ݔiV-&/_`lu 凌VtF5Ý`ICfuBIsa|b@֕eQ307SN&T]i^ŁYO\[Ǟ)赥#~P`ºf,ѣ}Ȣ:Cv+tI_K5 OP تEf.Y1/-w'Cc3뤃onZ:ށĞ95[5еV&u0rEӉʗj!r$ė0scc+y]O}7}M2N+RTйjFוFxYNKPNSP}tNX eTm2K㴊$Z Ү‡F>dpQq; ({;09bu1B;^ 8H<<ΜQ,&<ap^Kn*~=a{AejԥvE%Sb/ixVD6ŧoYMhM:u!,?t0M!#N5/"^z1,%5BT+oUڪTXߍw!G"]5u?kX11l⚈U)YJ;ST%YHjT#RwϽ4:ijQQe S+Ly ݦWTصvs],=UoJ_Ⳃbt ܛ19*-/l1nSeiPR/=jCi""D"B?ӫj/2ϡ:ࣼrȝ=m"oVPS/}g dn2yU[. {zp%|J6!:zg=q&=$D:M;`RY Oя˄2+ve.DzMQE-W}K7 lKL)˥XR "3uG>U%yFZb/n?aYpyn*WYS6Z-j,Ȅ[Vq42ȥb),bz&?:-Jq_pmO`H*e),9llC~d_9HGMM=oU3jX+H⫫ߑ~J'qӅdXSO!!%bI|t'PD;Lf0d6aScghB XNRʼO!.PԗfIy9t ²T6j/ARRK!ElVqPf|Ēh\eϥ3 ;{x=Y3j-6Y5%AYu't5sj2˪rW -Ή9z3U*-mfuq֥VB$'ɮmpV_^3Y]'MT]CI]dũyNRгt]Muj2guEM낸$ߛ6|s:P@PTѫ)=]'ܦA-3 Lk":C& 7[o1d-+6=J;k J+G9*䷣WuF J2MKcQzV{}=vRQdld\]YZ-MBׄ{R-fSP$ĦUy"z/ﭟACD%5#1jT6.P4rH Ͳ%kl{fwQDC|ob OI2 E!׶"qT;Zlj< ۅo=6WD%/xk%YzQ_z1SUo#c2e۟ڈ02>qT\er\1 U{<;S4ɲȳ+yz*¼PײOMJ㟫9ArhƒJ*7G(+S̬<(א|*tnee;,CYf%6ȁsVRx:γg0)UCJ/ %Š8yի wv\?݊x"U 4Zbd5!6l-D/#8#jY" 6Qܪc~JȖ T}"nKi csOէc2,ĠBǴʒ~?)X%ERUXp1K… Hdw>%YP"O*E) 811ThP4`qRè;F~{Y>F4g3B Ҏ~oY,>rgQߗe[6q:ٳ_^reTs3Pnsr[^;sh U!аT)[OHh_ IMυs^)0)7>$',ɵ#ӏegwyroEe~ O =i*r6+|I拓3Z5ϩGMlUE`q,M U$6A6, ][`wB#h~Caە=*gM~)Z-mtrݐz-R ;D5zjƨB׆ik! q8D#?z CYrVF)܁ڠbWzժ_%,M,)5gPPSE5QעiI ^YEZiXJCK[dSq>|.qy _%j)JP6KmW88'̾5g* ژ7ۜERbS=E;$wU6Es)@Wm ڧB\!NU ([竳f άU>2 Eq.4nax*PXR ^D.)xK}iJRSZ*i`r:E)&1ɩ3Ǝ5D%߉Z4fgmE9%xŴU 3nv5YFwa^O?3k:e: OV)R_9tX)=?j"ʥ󠺂7 J|v5<˞sֳkC Ygi*u唧j]oCx)6$) J(7[z?y/{&k.לȢ{u߁bUoU}",lx{MASKt_5,Úth65e\` =.Ze߭5_ظaPo_Bjޒ7m,ׅM{Ϳ;Jz[w[/oٴ sW[C) eZ2=VI/:EIFqogDu} 6G4Ģ0:!WWiqe)rY"lS;]Yz((U{Xr8vt[\E0I}o=f3W?wެw G̤ ;|l2Ƕ#YşT)LjZɃ&='Զ*m' ˼%JX3_!BRvCo-!8d0<nȥ8RaT!L#yI jyޖeH]ATҙXUC ^5DXgRƅ5Pk/UWzͺKhR*!8~cKclSU)7jR^vilvܺq CNl {Co1!,B .k"M UȠyI}j~=E}XT)HC4%%"ٔڬ&!ie+pE% ~\pqݯ(ͲXtKYTdJkBkAqJ}*d/=rh=RQDD0Y,E!leZRh2(M.~qџ%,RiM,EZCfU$<~Gsq.GPxE,CKCA"V "SPsGS\` JHAؿ*AR@$ZQI3ӘGL 5i5DO$3 =zI.HVB/JY /o{sxf_ a7܆a$0UϤѹ\xjRs]KRXJKE@iijqvoлDQsKw* BI"z_UM. .aHCAuұe*5SUt)W"UJ)jgK" h, oec.,B\t:n4T55KJkMA$8D9M-Zj)BRWSE)Oʤ\=5UhidI4Yt"iT!B̮-g6UY)Fʤ~S9qiXlCًbA bJbQ")kLN^)OSK@}8e[ *+iuq/K*m*mQ~ P(D QM5aY.ИSCrvVZRo Lhpꦔ6ϤDZ_ 4Z؜=jlSTؼuZckTnkmG_U]%VtQgZƀTSǥڣ(;ĸNY`WD[*ZmZKRBѐ`1,ipmUڱM4tSOUaC6RTP߫O1>Uum))ղa׉-LL֜*8qtce_2Ȫhi/}/))F%^ZНչbSˎqmL  Ҏ8\SpF=&!Խ;0SV0lҖ-v0 6OA|Yuv]Z܋z #>1JP*+5u; fh >CXgUg8C#W=xm [?S@PuTj܉NOe0'K!a-Ĥj "PI46Z[bNc!ubSoָi꾬TT0viv0Ht&u۵dy/=2W=ҳ=_l}M3:5_?e[eQc>:ѱj3D|IuzpI&q9.m93HN(]P-15`OQk70"[H*W%?S6Xj$rT\MثjsoQdNGAS'z7&COB3PRU@KFJ+3ٶW.'_ 9]3{~kľ z)pe%,r+q 藝M+koA # AYUƑ1AK ٚ[[ =i,W!5#Ug-y9e*G7;sҞ< zKjTd3 mEȬ|SxBRT7 Bb[2'?0jsoA<UjtitRԅ![ &./X+nHI=f^wuPrYͬ7k5$?A-BDЗB6x vٕJ&YOY]]5Q0q<A$Kom:8-]!A2&|gazf,d EP7H=MƄ̗RV *|fISs5Cy e=kޒ|̧2JPK(#kXt"Cj߷Jx=%aIIv' aj+_U#ԅ."r^(殈W'?{kE;Mڸ{O2^)+27<g| i7wi6LړN;>Me2Jp걉D-dβ3yo%W'yRFL&] }Mk(頨SyE$M9zAyOe4-R bSw; ȲuU¼4-z%<NYJ*;{֎P,VLSL2 P73RT5L.D,b. IF3-:{Ӹ1%OtpKY)f~#32ThcUG%$(Q$1PMG O[?rf{v+Bw)=CJZ\vOzT&!УFX9P YӒxG'X(K)9d1#B=c$ݎhmY;c^izOQ9ɹ&~ҰϱRfӵU(722\gѣ^b9]ޟ3SM!uǬ>#eKn7Y?D#;47q=R 7wzgE^C|H-X^ܪҭFa^;aE6ȕ_P*~nTe3w$|M`} ,}͢lV]F cШ5FE![^CTT%GJBVX^%Y4*4i4gWuȹkX)Aol&/bY^R6JAO11+y(N2}lq"5UYX5=yZvN|ʒ NՕEոc?pb5nW%RAWbTC3?l>wB}"Xחl: %w!nj1UCQROb& R*te-Id%k/~[N2"CS :rl"tgNcwYѦ +vcԖ1D"xL3z|ܹ&lM҄5D;lTEGjsj/-*~BZqD[Wq9ɮ*4$<k}˸Jݽ7[}Xǥc k[ci +*b-%a-U`+0r kXi3*x=XYFY.B!qX2,lM89~}N]FU|[b2K:uKAq.JBeQ$H,zaުu-Z|k94kׯ ,ZP.m6D֧ z[O|:l><$\{DCI`尭*kx ] C]s^׵e UWS nGjĊԿT&:|?vokq?LFYh/:"ggOڿS&OxiHx {H-g-!-AQ`R:|{+QZ:n27컋TUIкrօQIL zhRT#y~PrGArwɹi^G?Z Ϡ+Ug[1; W3*{k(O(yOyn?"q"@8nl~)5n" t'N$ͳ}H2h7ind(IDQxKRCͫJ1{J!,t[_K>wrgUۣK:1v߉.(]}<7=P>װl*hL$C&uI{ WªԕdXn:!*[8gĖ: ƞ)Ӓz"`~GU/sK`ӍCB ܥS=)@! >DCI?(׊'O*Dp7LȒu%s0~LQ^"Cc Ñ tߨ_lzo̰_ NM3o wEgxR%1Ԧ_ BVJDCJ ,YdF~E;A7q -@vzIN||zik[Bnai_-Pk*J!s߂]<*RE =NG.V!O{WgܡJ .PU^E\Z}1)~㟁?&fPQb ޱ"կA=Bf!xM(mKbW%WpE5ȷ;ᢴhyqϠ*y,p܅ *h/%`Nָt4xUO;\Ga*=:7؎|iWI~H1;IG'.+-|GVcřVr_&CsWU4El`е=^knp4'{_JXL }˔:n퓎horrelV} xoeT&eL"uMb Pn&sXg~?͙Ѣ6Y#O)`Tˆ$byWP HUl6,I>6oY7Ϧ\)0qH+ wMYJ̑ӨɦyiIeYR 0`"*sE27%HY_E^SG,H !k/OQ+NꨍU+Nr'0lT^QSr{)e,bȒGXqw~mPXUOg}ih(Tko6iZZnDk+)zɇK)T&}M^Ĝ)yRM{_iz6+"~o,l+#υ1v9QUHRĤR*)9&`-r7E+ ]tuA+*܆##%Y&J z;/DVEsG*O]ipQ*߃.z{tﳈc`]3( >5C|ωo9lds,*jr)nUMSaAY}/1oqalYsZUhS׹IWY /)`#ߛW7i{Jt=|~K"i6>D{x$uM2שPוQ!]+ ,1HK)8jTuv!ФHE8BKBX ;$%U{3Js#B^uiqerk_s67M{hg( uk֥̊*\qBbZEEcoah\}[ D%9eT6WtWXڦCsӵp]}6b7SyJ-dyH=` ˣE.;OiUyGQ Td86}_72*jjZR@I/jyhOSz `CNߚ-?jz\QI%Ŕr5TUfth>XMBvQDQzߧRv袑 #S"P5J,*C/c!,iWQ5O֧kp~QH~ʈk(iE~s&EH$_?ѩ7m9bmMTb;`(O]TepGqEh.JE kxS} 19stZJ1 PŪQ*JЩ2)$CyvvUTԹ+9ItwYp=-]]3RbK*9>~UqA}N1ϕn_L/>slX9?藂^R򖢐[LʳTKQv\ڼDfm,_ rMFE,׭Y|zϒr#`o+X 1|qeu2)Sw3UqNx[[WR3u#靌 9X^( 0*➇^c2SZ;:UܕgK]G܃g?rbC!XgQ,7rӖl4ފXz.;j4^ߩa=e>+eݣ"ܝo\mCr?!uD+*GR>:R6cENT3O} J)E9]Rƥ_gFE/[sei(8,[}BX(4fʾhGX+O hl rOmʽrvzeX8UW6FAnaqAg cpɁL 8ڟ&! iBWOU?|2COruﭢ,c؉S#`i~v3tpV A|  /*FʃATPfV.:+M;QIOKŴԐWR~'b=|BSd_.-kHfZeQj-+Jbh]QMT&w읔BĶ{oiSGtI^M0,N_:n;)C!K 76ƍz:1{Sªb::T;Jn.3gcUy)!3V0Υ4~'=ǻ\*+wkL95%гO4lÚu,5lngc2-&%g,%Sp]I U-h)mH-^7e%K]k /)tҫmm6is-̸EHB!Mt і1--oppG^6b)uJHɢ;]uQ9n$0;Rq/V-N|h-A]^@pïJZjq)ƶU h IF2 {|uˏ[Տ\J솨\ &(â3G\gMaAj(B/6xs Wi1WZ,ȪWxU/K?ew(s@c 5\PMi~B!zqYCޓq<-(whh-dKX}P&Q1zf+Dg aׯ*=ؚRx͏g-G'w\,naov!n,#J2ƾOqvysv 894,aL;˱&_N+ZU'O38"MbM"'ba>rP=E:)}G %ܠZzVW"UȰ8*!!^ګoAN}-Ύ~B 'Γ7d{4QRՍ(c*R"1Ku4)g?bc/q3b H[D}s@D04 q(/t[HF9>my fG_FAlԸI_N("GP[\P}ULZސVz E5 4h-Xp#_t~)&Y~m Qe'*Ϧ1 kz.;oŴ{\X٦ITBzU0)̶s՗`51 84GcS&j$:s8'dJ5KOa%ҙLʬ*G߫UPkTȂ'#K<|ue@ $y 9sPu7)v[77ҷjyfK]fIeF#4yVZ-YJ獊VU;R'+i)Dcj5P.b7I[ t|nљӅCw|?5:,׶&ro5ɵ7rӵYЩ_ 8^NmCNīF̤&n{e'P^36!wuPP+Hsu9H/qRLu}?[iQҷu|`A:{ S>ʺaѴ3#ZS_BxR},hChr)7~Mɯ`חI7Fn&S>?oP(U4Gq9ug](ibWf6q\h)^{Q o$V5$'_VפY'pP0+lm4̿E;3(x=dRK"ٟm{2 q2=tڙFM 3n#FT Zg(Js2NBb;Lcvq0P&y{ dקo{V{3SmT۷KMח mxo\8&)7$Rܺ h\.w#؄ϲ"ŧa 1i8}*JvsKBV^5zApR|Eu-XGuⱊCRVpWr* 8ҊD ؼT%e༧rK:Ҝ) t?^vm {"Y4ITt#@9-ꀋ^QGwHNWWVE!|~$6]?}:Bqkbػ Zޢb}q[_MS|*;ѧ.-kazg9\sX+/) <'Ty}oNګs WPg/E(=N/%MwN-OR(*^_x7wWB"4<Yz+B 0)F!\r7JM.AcmLeP| ɹ| ,vo9VP'Ơ+ǧwEj)i! SOS/ 3"Q)0T羖+OZbl;똶k3{2q(igZd^Ĵ4m9UՓ8.ob侼ƝR&_Qww;tˆW66=j*IT&ѕ~f_:LJl<]AO}sΓӈW6wqH~lOlVP(; a})`R(hL%qO]OSTtPSt?qgTôԣcP9&¬XvM) Yx|ʛb>F&r%d zlS6'Lt1dPIc:X_M$ZUT7Z%T_ҁĩr;z:TiU\ տK;RQ}X_3MSk#VުJ C 㘵)YT$}hIiqdc*+Ls85l%vuP(}ۆod{qKgPR#~* D`aD#7bW[hfl]r$yN@${^E.Ee:=}ɊgF,^8EDy3bUKѧv쭦^C3ժu< 'tZΌI0>q} -<|rڶ:ZA"yh߻A`[)UqTTDkѼvLXӟBź/>𭫩A?KR*Yr(ʋV_ʴLKj)ʻ q|׬wkJ½z9a)U *=TZ+)t;V[RK!b+O`Wz__'?!,2gkK-pJZ)n_v,M `־Y~k kVhWLWv%e6[* 1$l)x'j_]Z62I{Q@~ *E34rX_s}UAz_Fk{G8;'ɻbGৼԛ/oE8VzpoZqsY.>DL`%s_m-:M2 QS(ELekeX|j ڣF3 k F ax'wÆoz;5ה$)K0,:}Ӿ*j%VUEw B,*ke1#mWRa剚+j-{ݤ( ">Q 3M<2n/m`}55(-94X%26́Zvӷeeml2юkyw'ge"Xrn^{X!nT͂"6#/p k;0AEiޱ.F_'/9tS^մ: 9b-+`wvi2QW"I >߆E=kCmnL ]x7c6%Z&ٕ_|*/=E=F:尮_wI%?M婺@݊h*s>̷[6W)CoAΠt_>7Ꚉd49z6mkxdSpz T3^h\x Yr ӷWX86؛狿|xWqϭ)%8y W25^w++Ǵ-njr."[ksD}I3jmKf7Q"k˪o l"%5&=6gsxqΟzړV4%,brf=kbcC,?Bha36PVz↍0rAf›ӎ|f9ј_X hZBu긝(8s?}]%"̶Owωu3ԼgmZ{19Du0'V8hmkBR^{.⛇pm<(B94tR$ ŵi4[Z-nY",}EZRO=4%YUUzI)ެ9vS/wS^KT-ȱie>ͣUg2㜖5IkJMԄMejYݻ;"=€JUY啩`z˦V)C* S @4iX&UaRdDHkŶ֧Q4K"j.ꤹ_3z\R=R58jyoknS)↫"SQ"נ_]/85- htGb^D!wU]GP*f [˹O9_[( dV( <r`]!$}hB=Hj*Z`8D,*av`gO5iI $x:-L BOz \͝ [zQ*@wCQ;cR?$ !aÿx͡)jRmI >F$[>\(*~|VS}/)HWau ͜9r) ;=Med>&Zi5u깯3Wi=b1*+rNK ?3 ^+[p7~$U=-;d;k'5OT:oh$~OyLtʫz"ו](tǻOPѩkMm1~t1T/s8NJmkHQSP^' 7͡/J[67UM۞N}M/JeQ:o6jQjDvy>S[xԢ"2.3Q墭-\*mnpǶ Ш jp#5Dmn)qhL 6{b2xy|oP;mnn +;aD jz뒘;Ҿ_[FTSAv'MۼWZUTr5hhy, v\sjRZt| ~ t о猥!_ӕ]WmCR4\)òECpP]僤'(\e5}v=ORb4*WFtn.m+"E=s+(^nq 5p@IpʉYd%gKtm~Jg8xCzLqWeGQ!A`Po%""" 6mUe|[l,+,kՁ#3Wl .j8eRiAzȕgw.ƝNOrD*uax?:* sY(xON &7 ->U%}TF!Ӊl,DvF?ו=zIŦ:B7f1k5 sF z Z o\(B=ב椓M4vK?Qd$:#su9w2 #pLÃq:!*KSD<6gG]'}rtbx!f_Ep5J[EMC%HQ/Z~.(,ӗ鱮צ?ʨ{e .7C6nFT,m*Zaw-l~RV%E+ı;aO:*:Zzy∴NMUoxS5J{Um)Ra4VUa)5G-jSG^CXf]fej`dSLQ%ba"rJT$ЙZ~ϯf <޺ XoG0!OEPIG*Hǰ˖0'tߛ2i bߚ d;|( N0۶IGYw1j]SZS2o*M;j/O7ǼF[ۊ#bOilMzE  jC9|iycTYHۀdvMP cfު \wc}Tv|NlcNlrK͍W'`3B#H?Oj.OIWaՄGH ,& ob~ zG nmX4sIc7~+¿_e)BljTǓ@IPBn50f%q50I] NF"8G>r'˪6peݍgdBEv] 6(}<鳨ZɿC{5XnsPv 4\W4n??oƻ/𦔍5);m"ÅGn⬞KU F) L²RuAcM_x)(ؕB 5rk?L'Mn]fsr//;xY}3XüױN}jisOɡER!I!ҩ)>g(?;I-,ZhϢZ ]29, ,3P?U;QVR)U%~i\=in#YMA/U%SB *(I(b!+$šk)'k SH 6&h+;pҍB !SKQ\XU8b$(+QF%Z]Aѡr)O!-R V S|YZ5$-S=K,~~f\Ţ"0Q b[r@I+DDQ-X/])>Uau D65DQRHߖfVt1NZM?DJ+乥TEfR Kv+ 1 KIb)B=զk7xڄ~;8y[}LR>r1W.V%Ƈ3 SuKkm(U e㑖oN}ji{OEٶ1Ms0 KYzST"K.ԘJq ¨A -]9B"9+=PaB\\W=&vr_A$20c=AOjմH"/4>\MS M."CCLFv[9~T{U~HEb  " :B ,![K^8;ԤpJFВe5±(TR5 R!ƅ4 җaUnVih % D3UMPHbPW4~7mFmYIE*% 9 z j)+}kIn(sPcTˢ *>Ŭ,+RH"jTe)M3u<3-*3&U*7)icbK'\IB %P?Hnصk9B괔!HE0S [B]uLO-$ҟ U?Ǿ,f/4%I#vC)1F)ʕB6Zz Kvd_ՂZl%NYuoj)BB<8s&pRH!ĥ٥ PQJXT0m6eOнL!""R%%S^4*/VbB5 Tf=96ifX7V>8uJ@"z>Ƈ,)沩b(?z)~*ƞ| {|gjE?c6Ν;|vlVE>4-<ӱwp[jZ2kvʄWDj޿9i%u;&\ Q4;5够 WAZ&yiӆ}yk.%eX6}HN ie%7ӑS? uKgCdu kD82R㞓>lc>=?n/&1f~Pԅ]LZvleW\U'b Ϡ+@#=Z !g/װbfLVz2t<4wɮCiU!By,r g-kQ[tb]ҞPS$+;~+F[XE{(P(.jZl)"־t-KTR1^+SS1;Ӆ d:yJKq4x'*eAC7(JgO55 .Xg//ݛYI) l: "..I5o;>U|oTl+SY<+=dQTm(BE/|\T׊ԶSљ1kyH Sf‚Fҏx$ a/Y=Ya$MwUiȇP^MqYV8Kt¨KYͫk%姓ēx%9)/0 4=yIJl4f`~r]2ϵ !?tqeuzʂ&U0!"͊1ճȧC^{ fc<~fSѣO,!1 i>}ܣŤh`݆amgQ!AX{@#5A*ˬ&-'啾5mi(RXV3]~~ʪ 9Oag5zlgDW6 ^qVǴk._S6y;vV7M}bE{/F Z*3 7hE{/bf#]S]6c)9J=vѧ&MGMvaд n%YSXzʃ?TSZPUeVR򩁴6)hN('aaP% _Vw^¬Z~*V ?*u]V7Ҏ=TlgEH(H8+YO-Fnt1|i]怛)3-ˈt!!Y%TFO|/! $I"O.%IC2THG  Ma }edBg%YN#3Ȟ7oW@~*!p`~_Fb&#=̔ JEgIa̜vI)%IiW"Td K#V;pUKXw*հ} %e.xĒHk͛XQBV`I F-kM==wL_eCs$1PR8gLYYxv}z~THlk??QvOu7CfyN}Ο>+>{kk6pPچ:GJ;ԖA:wQjŷ$z칐T)8lIyvX'4ʊF#קDùN: 0 ;|J\Rw& 5tU9EL')aT4&Ļɶ/E:Gi";nL5}H4:5)tPeEO{%x1t ؞QD2&kڋ! k EZ(>hMgl,ҔFBk$C Q۴r1OL,R ң7mJ0ۣ`*a=4Ti蠰 eykþ1*B跺ݗꡑ~{0*!R1 ;L(ly0AgO;b1ŻM< 4s)hQbΩI0k^-O-hw9f==QѪY'Ty\^:]GrGQUa$ Uyi:mp뽤@mΎd-og£]ʌ^̨3j▭ Xا-rڿRr0[N qOvR{"=6L!/ 5\~5sS%jfZmQX[Ɖ0*l{LCro} L\S){Į VM).͆^6*Pa8e6G[#R4o"޶.]_'i-R&S[qm LjGuZA>{V6}}h깪rl+ g[ad~jWeJʕ1! ~9䴆=#5 X2pZ=q׃iS¶C]ڲ՞l{cY'vi{n6:ɶw\1o[G45kiyF6N];Wn\Bd;&tSJV2V8Œ%PomNm 0s_WKJhQLhYޭIARdwN׌䰭/3ˈiE!9-W^vD|͊Ydn߻wiʶIPZ%YOѧF<R| Xlmm%06u3YGM-'"bҵΤ]R 2Hii_QǞ"#K춨 3|HWPHe{ ?5K[tb=eg,uE he <6%~ ꔦ** P׮ɩuMge3ܩ-3ҾC+PQ\B(ҲαQslKz =CF(-M40wBIz& t"#үԇRP҉9>[`,ްu5/@Q111$D&NCJ:t" $’LCVI&75l>+Vʛ^%|_ʎF\UvFvQ6 M=BZe>ƇsBjM9H/ ֊J8Rnz(#fJ\ZYDҀx)zm^Rړer9ptzpUe -PSt(tB섣vRt#Mblvm몽k jxWa}kxeY4!y븖T:J$D/{j"60.ReyVklrwYɿӞbzpNmia3JrMIVY(ؤ0$n,^*8ƥ'OGQАj۷);TԂ"EbW5DM4q vͦjFCW"cmK}.1P(kRQ|Et`'{:}p0vx*Y^DYT¥WxKuY c:!8{E8TeàzQMFj-yOfOX-c+ LEU˿V#1M:U!71~[׋Y5*#ԩ-JIb!|7V,Z֒iZUB_?CV7tT*9w~?/R=߾d(~m#Zʟcu>XܖGgm%Wqx:jb a!?_D柳އF'*H_@uނˍt-xQl B .ѿgWX_.0uعE [`~Bؾi;O8wAb&`m:iA`e %\O]a#PHgXdrzondՔsG,'[YpQM5|~_鳢-VWwAO{ ?p)+Cr󈧦2KT:-aߑ}VUwBM9íhsW#-cjiqK)?ZW"f5ڙqm=lU4{ebf͓g-!2 >jmTPQUz4`w 9PqQV JRT沪kwmP^,)BX"?Ou{}Q)G˨7wS~ TH{Mh9hIfSeH)sFiHy9*v 9^E {:#9z4?j=,g~eBҩ>IGQ%{: z|m,k35=G87E];bM#J]9r5' Òy A8PR>S?F#2BPK]MBuW{/2:Fum< #ak!3?SskoQ0!fc&M~u2+l?@.Q5~~LM6~.5sB9.F:!Wׅՠ:~0&YEQd+;/eWk%"lw>b%1oU ]?wENZR鶨Lz<[_){]ENr~ B( At̹\WZFs}Hr[7%E#U l_=OU)k˴+-n/L\ *֞ad[.-EhE*.K׻D{Y4G1Ѹ-RZԊ۸Iء;Q#Mފ~'W&UtPgƞVݦu)qʬd,zO^e圠lL4+#'-M-IFإ Y&QtL'UgyLQoA8(_:F5`U_J|P5e}_IUX[VM.ֵH1b)SW1N&W&7֬j> o `.zګ2I2ɷ0>DU"ؗciuS&OUVmk(}QwvG6uM_E3( ܄h/ƔG6+5|tJ9^FB>~ /yÀA>eIOfErUP }l,DC.E@$Ya9j XyKvDº+SBZLKjLqSOU"kr_"ENg|i5]}Y6RxB詳;ЦSI[p{h1 uIv9[lR(M1GsҩX᧎2ܖF&C*.eQK+UUym{R=/.Pޕ2 "ykzgM4/0;("TOԯ+&{wwa~YVE{)uZw)*YP9ɣ{uVхe =x46D:IkzM6gjlЬZjndձTG 5mM&]iBHQ^rp+2(7ؽQ\feVg$*OL!u7dmeZ?06&yd3|B1Ѝr4yI0t=ܼcwNREO^b9Lhu_*Y\ۥjx';B/)t3-v̜Mb7 緟v Dbա$/_>5PWENw(D"&6qd_L[f5]%#L#֮zۦ;DJ9>氿m`NcOP2!?ڴ ]STubVbnNT4M`Qk@A]മQgՋjm a4)%R ODeI6.AD$kxJvPRW1\eEX,r_KeQ욆:"֗O猤/ Q{sxk%r=K[C ؕxFrȤz3 B'QCҲxԜ;a\>WL /u*{͙ }A`' /V=WyO!Vee"׫H3[Zȷ8'r"ՕIo}GnV?ƾ&1xwQU%)¨yNg|ϳrHrYN}U: t+)$cbٕyIQQӪl6-̧X}=t5]BM Z736rƠUư7)&ݦR]OWһx E5X)D׸Cts Ȅߢq 4ʳYG )Qz +?G\DKʗ/ЖeZ8T Wk+k&mJ)l[8D")Cn3O~Dv~ond&\%n`]gy5mIeF: JTggmfًR_+O]yZӷȷI#Z%]HzK&rM׼tП4 H҈b.g$lpF@%Mĺ$,U?a, =Qݥ}Bq{Iij/_?* c:Y0j UCMiU|WʠפGeW8:oO9ad\X\U!k[aCCIz2q+ K@#pSwnC_)6@N[yCxևbQV tyj%L.R)d,jf]*_ZZpRR]#Sw-)uN I6.ߡjB4*FЊS̥bUƖ¹TLcCVӞKEE6v<͞.xsM5qkUmfR<èjͨFe=%xǹ5mkW RTO ؞Z^4,jo9箷ķLyCAb!wM1Vg)<(:}ͅ7 լHI?.W1*إö96 ,|,[6/"ktw~ʸ5|ߩ\Zth>?ճ^p.\b! ;$1*:EM6s@ COW4<zhSV`@:V:\wýZ.ZJ eG8 SD>V}Z%CnLghsr+ spy䭶{*Q4dW yO*baIj <@IٴӶZjE1Lo@O)~Q{={M 32mm-/Ujt*,wXWx^."P*i4$S- Še]zhJݑI>ZT 0eZ(Z> nZSbU\=#rq BoKrRfSjky-"h[:-0%۹!aD%ꪖ=-xtMvM&z&Ǻ?$ۋumYk CKM%CTBY&J+\~_]ykiԵb/ʊ&R_U˾6aIn Y`XѠ;:tך%ܲ)"";Na#|m?)3RrZ2*٠=vDE)IEydz2RԳS_*CENU!:%E4GOn;\_֜1ܭmJfQVt))8QT|o =&Y h!&rKyUsvPT[bYHH lT5Zܪo*z!Jnͅo zv67׿/\ےORaN!XSq!qե-q+."2FmVzjMՠ4-_^\xOmzЕM)BQEz,jTʵ\Nѕv8*TlPm@>ۉ,zt9#@ưHa7O ց,n S_ )Dr%㶬r]}`q[9|ԥu&9 WP"rb,;]E> -jg6it 7})~QzlVesjžE!C=͞Xaz2+ⵆEnz j;k-htp_]wz溶jj^EӬ.v 2 mXE,T[Z,7AАP1-t RVҊxÙ@<2%}g˔+Mߝ.1 ʉYh3.=aA2DVע>E4ZKV5'!֬~'(^{r.Y7da9tPY$Al A+sElQ."LA$qb,$'ܹƳ}ECXX) ~=b,`澝aCpa6}H"S; z */2赋l۵1ogH#.g5]ld!KYeSEY_ʞd+zTԴ)6EASo!7jJ~ȱS}˴nK< BǼQT{m]U4I 9iGAaVw+nT#DRCNJjf-Ns9GK;OZm {TM&3tiؗUK9NZ՜|vQ/sH23=iJ&-g0NRؓI4ZvT e1 )+r]ĺl>4 %]ե6 j1HN?.~*rRSKM!%4Lr1LzM. NdҔ5YItQ/霙 ^CGDl4pg;rrSpQ(>p[sk RfIyGվ+{s*\W!0HA`d3G}eןM~h-ͳrִkex]GCFVvVI4Oȹu`ΣHCD0JH*Z].-j"i o_ G2xYTs8j|+_^3H/φ|B~]% "8Nbo}~zƉa:僁L/ 9a곥z孊a}\X  Ylٹ@R_5lA^yQ^GEp2x5)!i_ ؼmzsPx(T)m#J^;TNaԸ[nO!LMn-EANt|- ghqY-u=Xb$(^DkȅUtkV/ʰ.+b<~ݘvtvUsLI9iߑBCHfQFo۵.2)U-ӸONXa$f4fﰄ[ A!*O|7a쬤F鼯 "GMv>bCˆbAo[kJ :3C$ާ k1 }O6a* xkP V&Fp>02$5[J}DM@D"Hx+n)j\ƅe]Ԗ|>[uF6 tܧ ]+XaV`<)/;~,.Jk2եkפâ3M[Qj~fU@T:7BN1=bE dF4*b|6B~H,*3"6A`UV]n2C|ZyuD#xJ-Q†^z}勓T 5mdqåwlKsM{}|j5.ʻV*eD)!7ZV"^6_u'm@yi5q2P>sn$aDE;0˵ۯ\ˮ@(n2M u7ⰡB|BJP;c@,L ȑrMZ:fa0εȻÕh U5M]2^iq=%P6&"捔xxsd{ߧ|%j4$ӜyupivyBW)ık=s~WG\qV 4-Wʄ(*ćs=Ƒ!Ӽ=\I'dW˞elNJ޴cW@fڠOU1@D: T}wry ڨ HN]X dւi6q%ъcjqvEqFBdt eZ/0sпBV{"ܕ{ɠicU-"$ HOkLpsʞg36:SFfxθEF1 f [t*6-~Oeר#ۍM?N <_wZ͝kkPoDX &IP.2=om(9$ޣI~BA u}p%"H/rGa xG #{YzGCt/^_"Ԕj俢T?BJ8ʕH%'QOZ܉hPgܐo֕M(N㼣hHsc+Ǡum;erpVVAݡK"L'%%]`PZ3N=bֵ?0m)cFUq/Ouظt=YUiM1怵xJ^pMZqU}MESO`[KrbC ֿ "$0m>u/ξ_2(.+=WAXݨZTF1HF&_mI>D :)cDNdPK8woKKJko=[G^fޕh_'m%fI6»ȥFґLB}KB#6wJM 0Y }Vsu9X .$VE-5__^;)#_Տ/m?yq/nWsWo<1;V]ˍ`f s_ʖYq-Y7䴕xi y q@w'K Gs(UFw9}cM{xɖnAaJ<*kB .7Q.3aUI UW55!')ִ̚W}kKm RSMfssm2_ٗA=COX8E&8d6& so,c4e bYҲ;Us"2>] fwZv:*9z,q>\odI(AlVBD\)y:"}^ͮ $* . wD$n\#tMѮ;n&^h7.g=11 NQF|WEs$@$‰Mx}hQIH7(س餗6)dkUS@9PE_O&3l2SN&}#,ply:T>:5 LG۷ά]IdJQ߳MJ7\T֩"LkfV({'"%UHk`WqwӋzkɉ}ɩ&=D5H)(nwIYzN vLZڊC﵅c9Եr"\o鲬{],-vlפxg3L4S-wTMt6D\@R^л TcY57vO}L/ ɸO+Y":bE%k3K֒QT[09MeׯqK2 :t˻}>zބ)5*_(XyeTgx;sw<()ƭJiZG}wtkRkC$BMsqx \4ku)P>8v\kؽs$$cӁ)ꐈc'I@i6Icu *?5 ȧ[J0\i{&W@ A>_fpFqy?BX_/&(z1PUg4'|u5Px9YQ~=.a(/žCHMh- |#LF? 5x ZrVXDkgδ91 JL[uӭz [FVE]}Yޫ{.kVIMt뱋U@@d/r%}WS܏^b,6X;Tl-ؚ(a e'_2 z h"چwAUpu6q)BڌUn3=qo[i4^Pe2kb$^*et"gM"r BvoemOUxg%u|?i^qV5ϥ2ky-±lTV͕}-)wLѧ 5r bVGz^aX[WC-h]e8 U5isP &%u.zkϓV}?RZ!?zs(b +ZJ$X"ri`= $iL{htjQW?}Q#4.K$޺0g/#UTcEenUTgX),^sAGv|V3K;2Q+6UbWDr9({hO{Omqmu3#/~8ൃm6TI Q+*1+sdJR,H*c'-HBZUh$T˦@bmY&޲njU5ݿy=Hilt-(\|I%]JS#W\ZG䊈\$F۫'$&kl|-* +mz]*uOUQPuY=wp 1nkHqt:Ԥܗcxޕ^?R%ERu֗S.2Y"9oilwkk>Z7 Db\t-y3I' o=aNNSRr9 Ul$ ԟ͉#S?$.ŋI$\! =uExriRSikFPֿ6 ughil%XVT4BQ,e`MŌ=ۭ sρ6Jd]v{ēMJшڏ&_=bYbcLݩI eضknXL{og6!{uO͸gj'B;<>s8F &ׅ(=c}sLzp1zV)7euZ;U0'8QU&ϴa4cswºhg"HG}5eImmz14R|u("~ Zuf1Tݘ]]引%MϭNBp]RZzPR1MK~ {D0ǿ)v jSԷ H2uhEZ(=uʮHO=CV>izؖahBys[F饶hJ0FPCϩgLgȼ *DV 03~V!P˾ߥTx۳I5 co"jZvpK;+"*/[\{o}Z XRIE?]'nj\'ߙ.ջOq>jROPjޙU.xZ}}ZF+ZؾIT{OmPSãLy2/9Y(m߇^7.Br!9jS= q).|ޤ(EWI~RVv'ʲܨ Ȼ\tUe]L8mzN>`̢ 2dZkW1@#R.0? eD SekU?H)^ⱌm)s.A{浪٪x%u!o DWI6Y% 4 .VrMkse-EKmH0iȭ<V.нMRTӂ-.3'R\r 6½5vI 1I46_G^SGO~!Dr*COeBshn(:d94-RؓJr*ZOcZu*i֒g eX'E#dsJB֢YEwW=jUBSNTM)fRBhߝ̶eT)E2iKس*\K{I-%3Ue*C9'2V*T!eUX+M*PkGX!!IRmm:i%!Aڝb4gɬְ Gwyɣ.[=)֫W kRR4#7jwB(SXsf:6nPg>>d=RmGN҇l8$OBirOjN5ҷ[_~S/M%]BE<|K ?U7e铥>ȡD?m<KIwO/cR]z+!Lvvqݴ(B}vȈDϵwx%^_c%L'2x,y(ر/ QTC)`?xh RsHyvGD8瘿,ծx{&Scy-<9׫J|cvd[^]>ֽ5.ϪeK'̿ jCC(ҹ~Wf}şȺо`ޟ|Ve)~ƐL˩㍘oZ/)oJuM^xWͅ|f1+_Y5tCNϹYnkmF富 suh NdZk~"^7Vj-} =@oty7eUu=sw6{f1,U+ fpwЍuկ}kL\p nhLKJ$3@fYp*TV, ܤ2-6,oegL˴^ЫE)0WV6֤įwSByW"&WfJZKrC4.#RX1U5c3<\w:^U fUqMW"Hb1,C9rXT%24 SߣQ !hriu4! kM)x0бBiGDBJE0m-z[Xܝ]5 )UQH`IwKSze6uuסKab%`n-3&Z")uױK C;4w56RQqYNUtJbz%Na4eG*UORFДѫ!./ JqiNVSE)Qq|ʯ%4! E ׾]NTwkJm ynpkZ|{Jg7z9>fabn0V(գXlJT:V <%Cef-&"f)! N- k(I&JPU١Q$Kg7RfL!E'p^*̺VbjM-sҟ %jU/Z-ej)9D:Ty`PgL[IcWiՅ!LޖYq&j P]E lHl (+JJ?%QM /P5_D,A%,cjl s|0vۧVpy5zgFbiN%_`Mi"l/Aj 5 8 )~.U[bvwQ !FS- m q'Ě^?LK-m캥PJT՚l~_5R~)HھUzj bX:%%LB)ꯡuNΥWDR_iM gT| D]oK'MT$vO&Ę4V&oop*z*wl iObQohꦈnEV .ʧi%{С+CSAG܍-Y!ե,BDaVUq~1E)e!M)Iy y P~@yyeVөBt-c.3/Zb|p.6XEMMD6rACC~IdgQ*ws&,U7k:jN1S٦ tgoAyo_J(emb*Ԝ4%\NNry6NzQgEr^*j䭰I%Uŝ\Wu\aF9V~ WV4OsȵqeP>|?|g˳[NbIzF1NK^{(][𿆪f}NU:5څ2qH֞,U4B)jU;+įZll,қ半~b',koXNuX܊W΋RQ{gB8d'9jk͑<} AH4t"QyOiXt! =0<-\%ÊFLb\jΪ[q/ c:P/YtTF6mVZHj+`2*S}g3tǨ 3yYm|*~ZMN]:N/5IJ7F! }E|Nʻ\)61Kt]Q7qƩy-f[5npN, r-oJOUTd !V+i/.[JƲNkG2-˼d4{xRMST*~ K%gJmAZ- wxeu[2 eg P{ s)XP_#񰎣vtTk,6QP 'RG O"5ٺXQz1̧^#[KνU(6h𪫫^e<٧I*!NWĊNۥ{N4궏OX)ԟ24ԘOJʢVb"V4zyOUgͪ [b칕|J#͠/&+B_Nr§$tC,{ gTǐ;͂ISW̊Z_ wuz8/Uk'-(ouH9Z߼]^Waߣ<"dYb8b?M;c}HQF9ayM&j7H7 Wyu_],%~ ʇOzgd$/Jp#[vQ[WPkٿ;{+FO8hk, PtP iiPU-ľmMs.2M6RYSbyL=t_ l=\r~Igadk- iכށgU-HWǸ)xksp .L5C~ :efW @T% @OJ@`ܛT {$׻ L{-5]PUKlwD-u h c x;t;f+黃 \1PŧTW;*c|S4ɯDiG:-Mm' S4F"®RP]L5b] Z.r4MK 4)T=E씦A#E^;gA^ siDT+F1vOXXP&?h&Ob[u zmj}#6U-M-gtjCм|>"N5IյQwAICy%oq}5`]QL'IY=VV%~kB&abmz+S2Ntl^r ̲I|<46qeL Czң_%69ꢮ0{t_QvWҜ( &D:z+46þ,nw^rTiʺڙl(]ؤcV䏲aq}4ސB@m5՗Z"ݥ}Wv"K>8醩i'^wW{7 %b/A]j9;wdߩB؝M"K[8|?1KEse1wD>7}q$۸,21ڬ ~sӖ=7\ 5/cШ[2 BsRuOj)RH(Ak&܏a#kV,3·UR P,/iI?UU\nUaYeO ʰå^aT?ePVЯhRA;QaZ7J=b^2-^:j5/vURs=!19/+l$T2oTJxn=R~Iu[} k3Ƹ'um:b_{lVJjlݗ`d%.sNcZS:)vAlEU}AICsNY<ku'Z?QP-QS} S@Y:#C;_b*`N! C'"REt y{ 'K6 FyyנH# APi,¼'m%mi4?H 'D74'nMH?pf߯<&P7C`50]O ~jKN=F1&iE(?ތL"Ͱ["_;/VNMkFMQY5ny^]@(11rST&ti?y7cb:8cxrTMJ*JwbaQQI{ϡUY9g!mpN@VAwP |ɧ%eHne@˥˼ӟAuwÊn߻Z*Lۖ嗒v6n3t$˙&'/Gf=6Dɳ <) ru }IThWĞ-Eih"iL]?J JOuq%ԥ!7vJ>,k.GД@ev'H3>C+lL:C+E$Lr)}vZN_DziKXunQ'Il=/jOhc*_qc5 -N CPen״R;rWk$'"xL)wf0&RFDq-WptvwVa-Z]p;ytSKO 5e9t|/}=7rƙSyODȤZލzk#Ĵ+1>IXxʒތYlJj+Y;V\1Pu8zOv(ȥ݊DPZDB:T>ꮢQKuSlȣV,zbWLabpYz) FŁHdjJS* (3|#KC|lr-JU=n"e`-V;l9!>lYV~wU,$SAE`K;CDۈɕIF*EzC߭l#c!(e-tzwܼ"썂 -k(h ?ނ:cE/ =&[2ŬNp]XI|@?־(A)!}R7YP` 'a#cR8yV)6($plmr1pSgE'Y ,uTK\z;Տ̆ilv:1q2էn ?VFЏAvʴ&5UfxeE вze_9zX:^tmhM>׼M)u3 Ii2R䨤%"82Ef^轄b5Kt25/XL&WyxiJNQ Z&@wT&Eҗ_F0ɻ_ɈoUL`>Ʈ>e,AF<$+,:Y_zmj./  kkWA:S׬gIK]XpZbñhCޕYqH6G!.\ZD#21$!,Z:O_ΡD|:V(YkXTbDS8;X:{}_hd"R\Ӑ_Px:V^ SF96-sXi]w}mfBR f̳=Tz.L0HқjZ+Z'sAAϾrUCG"W4ΊcmE &þumJj[6+hFV;|} s 4jnuqWieR$b~צAIe^CˊpO)N>-cbԴznU”J *s QH]ѕ_epwM 0NŪܐ_V#*a 9kCR>Nd$V7ʿKVi/acM/h2pt7ס|kswY`8H,{GŲk7_$C} izK‚%{"ˣa'h^(Ncӧ̃J<᧐u`m/Xm)RP0CvJx=]91NҦLZҩf8&E&~*BswwV zj>u=T- z6jR3 ې.)Ӑ3lz>WR4z 㳌< 淍˫!Gtu|y9 Ϲ//&]Q) ]r /m|BZBI6rZ ^:I/Uh3RP QB` 2v>W\֪?@C_[-{ jMV 6&^\ttM8(Po(xh~3;/UOm+v% Ng}l+: YF;4Hh&RF< IoPš"\J0EO!sS nZmUG}fjSאw$(,;k~wG䰬IW_* &M"pP>|>&6 DdWYj+YݥavϭCMزa\lP[«ZvIXy~ K3 #_))wCPu T~δ6MS'Jt`QVyIOdE@RQC`@ʰX[|8m2b `kcj>$hc7]]iӝ66T hDJdk|ވ&a Qp8c7R I<.;¬$Q;k.2G9\K;)(KQ툇/  ^DK FzLX1J{dG\#iyzDHup{(`y G4m<@7)F-OלT)57zv+VNwv_X ˾N%sv=#od1e ed`kE9ʲi?8Ebtێoޛm8/]TWdӴ_x<ŭuuA͢q{cMO=XT-mU>Yz0bgׯE#(bV$G#ҌcXǨOED UHyn<.IWXyyl9tV+~X+vTWIPUF ]QEdս]"9N)ha!'ϩӟ yWO4Sb謾M>e0h*zT^Y6NlVN+rz)|Mݪi[k'kOwعb0 q~h%OzruYD/i5ID&>°\(N5eh^~}.ieW%FL?~U]5uS-7-%YӺRomy|j PE_:\E4'vLW?ØvU<[Qv|%ӶEl"R-q0QQQ?>*ZF]veXI}Ji;qTN#vľ9[^ٗi;zKrH(JVX&+̌rt^_:{VUU zˮL%.2GDQi0 fٗyoխVhJiK#6O}nږ˰a^r+{.lc0+&uo:GK#'܂kZ#ӵ]$Q%QyҞ^a^-uXGUTXayiGeߴGR\k)GghGڱ_a˃ y׌7HIgcėKBpR5[UyIJ¥UUviGPaSg%xFU =%!7(4IGά*j\ 雽Q̕^a[KƼAy!qp7Be+QMFH5%?DF*I)/Пl,iM3k?@:+S*r/mԡIMa; K<{ }paCKP;v>6 1uWѡwio(j~H!n?[pmP"?,.Ybkxq ;$hȑXv֬aOQ :LnVC}=6 nB3d%U[ʨy5Ւ{ }NqSle&$m.3ќ:"7Nq_:,U-\%o"V w Az/IhSm`M y+X.- 5ItQ @;hhj'$=VGkC,pQׅ+oa@CPpޥUkOvbSqj^Ӥe↷lbrϾEPFYxӬ{*w+U 8bԕMZgEqe[ {eӆ7'(Y'!ޚ~/]SwJկoyE.+!uHV)QnW@E?(,bQb[\[pUyXRG恗_t;rjQL(?)-ן&UeqS 7Z} mQGj+:8,WmEb/ɘOx"ʣ h lD՟r  P9Q#G2)&uyU M[ՄaGI2S:/1i[eY֟/4:74' gʌBV߂z"{d3 } mρ8%œ#/j,e!BoZV5$߸;ƒb1y,&aK`hNBgu p}Ҧ¤:tA#Ah/D!mOVՌq싉q˔-Eʬ^UT!%m+CCOlkM9iTSKlZ,g VQe&4ufҐ.0=if53/3znUZE\S^jOy-SލeW qV\dbAPDʆ}%EdfTɯSv->>~EZO]2arɡ?;rhkڢL3yjqdR5B*`FclѢ n|5sf*ƭZY0qw3 >Nq:Р!cוj fAI; AsOLֺX戄lkkyKNJt١f={濬d&ee/^l/*»'>Gyu"$ábYگ>gFrf'İΡLs=+]a}Q|KƧgB#qm'0Bt^J%MqИvIi)ґjƲ!ΐ$=^БmJ`B}v-r.7+ |I~Yv|Z5q bpS)HD P\}5I%{\|&!״S0)SES8{9mmqcCC^lZx UM-St~ dR/C={M-";D13h1Jg#лRUws~-RguaK;'$a} ď!2!UogJԗ$-p׶IZU$zeݐQCBSBi WVn~8r*!bUXU/YW< gm=?ķI*8~%СdN"%rh!7[՚*)5neUҐ}ohrS]JKɴg ߭KTx瀉!$TPISN&UVΊ*QK?*j2yixk/9/pܼ<ç񥧫CzGZu2?Ɨ#7T 1ڄ(SkPЫ_'J^*i ?z(p^9eT(W_:^jJg]b/qZŤ~t(W1ΙcE>}rouvK!Z1K>#$WX[V KuenYi1iZKB%b-~tRcV[rKE))|-_s ɹ}] 1#=66Ag⓾Y۹ƈ⮝oCYO%Vi!qO vS寥f%2,\3qkzI\Q{e_jW6Rgqn^6]^q{.YeE=eÛ^xu E_=z5Rw 5*4%T<43Se\ziW'LִŸkX\xM,"2Uס7e2f,* % eqHd6ֹxk%DhkX"4 :]Gb؇LЎ9|e`wOw}k)72/!DkSYY~af,z/9C&cCdP( ua*u=q" ;vV5rk¶ئ>󴿺{NK:5r4Hu?0tP mEYPMIJjp۽L@ҼdPeiyAtUgYzRwz~q֓[t)ն(B㝾-cy4Nr@U(<g::9@%f@ʻi@a"wv%PYõ $uyTr6d-p!+>ݲ {TBۇ@I(UVGT7%=Cvb+nx>AK:N|L$%# Y?T!TСZ(.S 8*RVVWWch&أ"9K([J3vK2?MFFnuHe?}._I@06PLւaЅ&̶=7A>{/x&+n2z%ZC~q2t?#^\h{I,*k5jzi\ќg?_꬚E P7qW#Z)llj3eSAX[ǠSM&x)gYS0}>F?&a9 l YK8l(/=B,+ !}+bNV9o)<g5*) ;&ȅm,g_kUObdZ!|5hjRqSߣNYO]J* %fay|-n=OJ) (@b qm>䘷:{ LA{'- N vXtDS"_u+ j6:|6ޛ‹,ATUUl "Ȧ%a]hQdDZj?G\t2+E ٤{:BPA^* e$ ^v0_ WZ-]RUW\baVa人̰NUVfŰ뢍5SqK'1^e}+)zrS!f7 Ou:KEѾT`[j}R w^MDz+J1kZW&Q9R4S*SF߳);/'ӺK0 ]W^-zMDWALq[Kj KRԑm_ʻ, ^󜷢 N6l_3'NfإWf ñQTnOpW=1tP18H+.˨= į,6]qWpΩv"נ?jͨxF?Ry6G[QJb5O\g %TSf+ȅUYD']STINe-ܸKX.}B( zڷ9|ܬ-;@ʿ@j򴧢4p:/絓~tij] Ia26Fy+ZH7Oxc߲ۨ5SԕrA~W :-{a6'Oog}GY[=y9rPf ݗq.k5*؜/pgz4okE?K,FW`cǓݾmq t" 9.Qa2Qei `9eD`u>l@ۭ+[Q ? SYLT_^\Z1#0uY\^8D,wa,' nRT3.`Dǚ3~Rϖj,sAoj%d:4%1_V(vO99uo.h^4PݦUM>_\mmYRv,GVц"АG 15BdE|]cݎZj5Y0bkP1"ۭȡg<-ѕ] 7 #ٶͻrI}x;w*+;Eo&_b^{훼eԢ!Ơ,RA`ཫieicgzٰJ(OI{$zNjkl=mɽ}>Z;όk.6ra$LE(pkb{M$R5)i\ܒnT@"=w pIý[1%IU9J|Rp$ǖRZ⎓X1;y,)M2|#8ʡ>i(N~o>T56')X' JKw67yDS7vɴiծYD{ P|Bi3m8F 3&ײ%Amޕ|o{}EO*-S4z:%]]Ե%)JmY?_.X*xi?y.ey*" `6T+f)2:2s_oԛxS* >)ʦ!nh]u9NebU nQ$|mJtijtr):"R-%{#PU$\ҒL'iVLVݤgBp"z%E7"[4<n0[3, MBq Tta9V&SRnFYW:eu1ydaǺ^>`r( ُSd=•88]{B٤#|u ֊Ἐx3lc{lֈøb~9qw趆J7]gQӖBlaCIO_m֋$|uu>yx\p(j.%MF_-.D69}K`Vݏٱ$us˗wbH ▴TuiECm0[ϋ?橯ht'{a#CCWQh(薤|(!p^#p$L@O3v'Jy ̺ mP^Ģ_;6ER9UiCCE]ke^bq!WF]R  }>0g>9VОo퍿]gk2uI^ZpzWf:lEPFo/ŒHRU\VOS s5qi6Vyg0v51vdE(۩.69ug/eո{{ۚr߫]jPi[ݳem 4 W0 u.: (vYU \3[\X1U+%=5m,]SvNdyiruEoC`'hgbSZQG_0O^1G[\2->|r88 0ܯ-KvԤBW/uc^(h%5Qj`PCn7U<&-^]a+EmzK|Myʪ*z;nOY%M4/* )e.)~Q4WwW~] YϽQQN͎PTٹGZPmf^Y"K\X[b'XT9GzsTmqwlzJҨ4/|y洱u\G,4>MIL^w[YQoԦE]KsBx|`Z 1X>f]\E$į+s*>j' ,w-9Bi@?%9[@;U`CˋeEu9te՟Z[O o8L~cP dǘ܋|{+h|ɈC\KJsƂtn LN/2yYg74v:^msN̤zGј#H &pS\qdRlDBh','#|IPA΁x ԡM_}YRIH?qآSPrR%YTѩ SɃMVX =dYp ~t$ɌnJ4in~9 2NS"YK$B) 4Qј90',ω{AUF!ُYd?Ah~27T!JCZJq$ӓ(<&T"N b[ iw[uٳ eν=reIJx IbhRx+Nu&Q"f<ǩ l\l=jiz[GߧIʤ&SB̼EP-*ʜ7 D`jn#N"zR\Z^Š.)G;MK#%[CkL=,tҫ"Z6{feQɪruv۪|̫η,.2Wh qIWrCe\%y(r]yFEʵ/!vwtkLߋ @VtZΪȷV-|-! ~6V1wpl[Rj3v"1O~wl08uiaԗ6+-WFL^|izੲ4;eSt/Ʃ|^2J2/[7̥#.XX'lvX$ѷ$cHDɖ "S}G`V_Ү\Ͽ9I)~{BOtGZF6mZMe %]ԓ~+-~^]R:NR^ ;.UTs;-HwCzJQ=ª0IlXԕhbnwZ._nL_48L(oϠh^ZAo-U#N9eaL#ZGN0_NhPYXj;BX>WF EjMPߵzZ j'g)XQ'|4Ŝ:sp\e/yNږ2_?SP-Y\h_t(z? cFEOPZ{^nyE/YXO!@@SUšۘK5)%Rlі8,qdzO ( $"Ϗ6T f-5湫u*q@ЮHJ6)iSOGӇ(4/i6|ˏ^ԷjkzŦbSrN YT&ieT )OQ)뚹v9]EVnX$vM"!&}qE݇ck]Z!Th޻Q DV"ΓjKOME!c:Y(i5M4nkR.#h=ޚn]B)u0*j.AH>7 /*RPNPߚMfU#V$l$5*gv:+{/YvV_z#XQ -g 2G)}Ǟi)'A#`b&`*:[X]T5}rʖ/ϵ>u)o{fyg1HJ*$GժsHJׯJ#s,1YQOm=bصTXg-Ufw*o lUPib>U`[iI.4 ת/ .>vq{&:WK)xm*=Ɨ" u) ҏ.uT멥* wOϵ-mD6*3Bsy;drCAQ"Lb3%0O % ȓ?ǾcFh0M|~f-M~_NYܾa.t/p:!}jO812I,]$x*'3 ۤnj5f:+;ӸxM9[ V;)PAd u/6&A\h'ƜsȮ0a*ԈԪ,p4~`ΐhN~cdD`9 O^R5+ F2"|G_„?k 9!,b6h\e_;mti |-ADR!oL4yvԬmaigf%h=P-y+Ƈ˭{\-qjvrڻ <>waBʼyU&ٍ%y..!O`C~/{ܧ稘d} VV!4:z%({D0}uO&!4&GVkgQ3K?~چ8SL|td.OJiPg0)Tr-+C롡=@a?yMb)%z۷[.?Fpl*u>'RwEs^8.Y|H=E)9l6˜S&̮ ⠢WơuY7?wܮvizxNe> ieaMyUg zGqsx7=^Dx4j..noNRtCdRYOy]zåfV I$gPYzCv>ɠKM('SP уѸJ.NZVA{˄f5}cyDECPэ=uKB {Y1JkZ5S֚&fYr=,v/ڼUQԅ]V%wҘPNT7>d<"^;4ϕ}6KGd%8pso/@z3bK'תc ,d$ArVԲ_rmvVIHeQؽ%΢]7VQ.:&MP}qbF5j57D7E=zS*q shb_ 2!ORS:m-TJ[ڶYqXҿ]BA0 Xs2/data/s2_data_tbl_cities.rda0000644000176200001440000001572014530411473015724 0ustar liggesusersBZh91AY&SYgjnq@z=;n;aDRuQN%qT& =2z )馆Li~%?OU6bOOI2jc@ ##LLimxL&C ziC)44LFP &CI)iziЧ0)j~)鴞S4dɦmI4lQzL$$M0žjHiT@4MOBSmS)#h&ԘS=CIрКc='F4h&OP4&M x'=O M~OThOOTTFꇩLCS=Qze=G=Ldd=G=GidmCzM4P444EOCD4OM44a4 M)M FC&jhi@ 4ɴ@@Ѡh 4z %L)h4zm&M 44A5=MHѠ @zL4= &4i4MI$ДSRىBJ}$ $96O§HN.666cm hiq6D&Pm6&Цi 6(D46&mJ!6  p' !&!Bm l@ؖ j!CGII2UTӢ)R6.zٴ!QA4F{+ b,1qk-I *hz,XG``/Ec-7rB 1}@Fnlb%9VڏrOEuhe{}ukN1TT7FF<1m`*YXhYSKY E_e̊]Lma' 6wSi!%_e8yr`&H^.lPe$ 3DFTNb+L8a=Qe>0+~70^}h5Pd)]VN` \!,Sh+;53b^ LOZ4+C |A2~ P̼ FF XC'AI))I I\|SV>Pe뮑\OOaü%Dw,~O ̣i$<a-5-[:}t/^Ҧh ռAu:.dIJ*I <'u,J2jSPMC*f$,iZݰSyl5`.γ f*&.6,.1БAj~B]S]Yvr0 ˅vtӬB(HmheB8jd!v=e(jRv FpgdBʽ &6&./lV1_X8\]]r_>V*<,u#a8m %лI}f{Ez0LP"$aBR] a6%5۹ѫ?+D̊2i1'۔*>oz =Q)呥^RhZOٴ,~TK)x Qګr&Yb掮eCMg+;fjD)x8!P\l#ʲEsaL[ApTIt" !5Ys>,8Ң,BϽZ~*Z3NAVFb^Am`{<77oa(Mjf/8-=\ʦV'1ew 8ɮz-wԉQϾlϕW\V_c[+B6x1ݘs-);Y~箲ƳPIMSv*E/Fpi[&%`EFMRک?+;Ro7|qK]]`pmzO)Ʒ4'^k|!hɘwSAzƟ*USq=$Sa\u2) yJC,dﰻ:Uا]*8"w *pU?Ԑ;0W^ӛT,"6fQwW"P[4J4eد/e=Wb /k68q3r\Ғ#ёy1s*[)Y]k(Ql(b9)bFm9׭#L }@A͢XyhQvCI9F HU߂i^/mfQe>~bT^Yi];vɸ >zȨ3a%hSTl͎[ç )C8sPD7zpsID>?}&]GWY>\~y|;NoPiIHu6w̺ҸNǵ1g誸{߱-Gsݝ {{G[YQ9E܇Khtv%Z8 ^iOT1,.rB`RI4g3 W櫒ApHq_4n1l kJ[%r˖J;\b.7 Q[6FK`tDw-]hzK*qأE :ܡP&%;qlH“lDQB &b:b96f90]յ4k B"8 ̳NʺnȊXή>o|│lc^ =YۄBa0-۾wЬ6ykچ5{؆6F<ٮZ3_ G[wfaxW$a>@Ỳ-R*dzKۚ;<8c=GE Iwwk ?LU0N=v|>Tz+( gN;}3?OAxY$/ISmՎxH6!Aӧ)o7~GqV[^h1uZS8QfCۘ +ޢ!oi]vIJ tGLY[aUĕ9Ws#26pҰC1a t DeW*m֯ͭ l-SO 6#Kl:S\yz=|ЖcŊ"{9@Gw,9͒m1'>b<{MJ:LU³g҈y5E^l.)9:GGab@2~#z-ylשׁ+bȚ3H"B Qx琒$uB%FP2~>g31]_;']ൿZQK}W8Hf_'_ ( 2<Yp YiXo\f@gB &a@REv5~v3zYy5d#X4L[bǕVy(oKщu|ozx3~$T٨Vr!' $b -`g"fRM+IN:CA_U5]n~4T JVմ#0I\ƙqH}Nv:oW=ZR=hI7`# ) i?q^2gY#W Os m9Naz;MZeѤ֌EV =ΫؐTlcLҁ W ^G}\Wcrs(\,qiѭkl9+ڮh0Il8`j<K20:h! @œD_:ihklp)+T*墻ho,ۥA0#X1r׏͘ Vlj1D:Ok}̻nWiſ ~wnhb"LRS:zԫRvڢ꣹#3jsM}"y+ZhYŒv:%0\\P3e eGjEAZ82ksֺ"fF+8U3xti"Cc:-^ȍ-DF2!hGcb~ @TR]P/@\i;@^4c7 ~ҳh݉6M>om8`l[˰2e29GJ:!k6b DضTd48Q6' CMnRa(,\S' p;1|>1 "~4#$8ARe03q<ēbd}%~νch~GS֮ ']00(؁kbW8"ORYJAjI t%뇩 ʼn|,+a3VJ0BU FNZ \V& ^kj"$FZLV^>| }e4<5¿ mŤC硂^bꮡZs8{a@rp (rə@;uTǮێ7᳣[Q;GMZk90S:˘W8Ulzze^ #ffןh$6u!Ƣ)c$EI8gS_L18USoanQvDJDlWj>IOkȦD|κ o\;VI2EO ~.?r-}|8Ț>Y Fzu,6i"0F09݂hqLOb&FM>ф ٤Qr&wOF ^]I:|*l/Bi LFnw#f߱}Gä 'jx_ A V}pSD=M=YRh4?Balq8՚a$\*&p\Q2jrZNQ/%#A9ZkJv /M-U8y`W|h,4orArU#bJ \Xbvcg-ڿ>:+4mr.s<7}߻ lU3 /K[-Ga>"/]T32oҲȦݎ&;aZ ӳ GA'tr[oKj(3\byzN2.B|1ͿUAE o+9=NmK3H~]6`r 70Dfg* is e1_iZ@pP(;GNu8slu(tvhHց|Le\'(.ږ_[^uєQɋɦ"5{ϵQ(^}I޺fn. ێs7wv\t` )PPnOUl׋5ݝcp)nwODp =2_wݤ}ڕXM֛f㕅I/]c3Zq̔Xa;`Rp x|*1g_b{hWR<+BVmPDh2+׮GjJQW̲H2f9A囋jfdǫSY^bE [![nZNe"xKMM`IT5wZ)eYS֐|j"-eCR<,F;ASfs)tV43FHcn#grӧ$vrۤH΋v{pC*ǽri{V!4NeY Sc /nunr1P*L! |Vwv/ 4Lc:g #W- Wn`5q|8dL:33jܸ۳eU;Z^RL>Jsh9&{54jAoLw>1pMe<`B6Nj=)䰃> y|k(b˵Gaf2 mdY#aos<Ab ѰptX|uQsNdkf/Qun#6FH Ch2.ł i5MA^s GX5nk2 ˀ31Ff0ku5V{_[:TB  Fi6RMY[b4lڱ(+2VձX›FՍF+fPbLYfe2@Յf[Vjf`m[4ea[ lՊmYe1ZյPj+VZef̭[5FK)bV[j›2jaU56+շͺ̾2>,+8}^NxUjYC3UQ)eצQnĥ7YyHir Wӊ80kiYøkVBmei3[qĭUd*WB3K4ieQuaÎl p)e6#KY,iiͮuR/Ls\ľG]euZQ1Yqu)v֦؋:uYJX\+FKfUY.嚋/Vq6"\X͸ӅJ)CQs**ŗn3YiW:vR+釪ǒEݣQleXA*8 5PjN:Eq}WYԨQ]b4!H[UTJm$NϿï,u,]iTJD-(MSYIi0ӻiUB!:Í=X"+yk5]jϚq!߻v$%U],"ZmcN+Zfv7yET<̹Ybr盗^D-&"VU<+-ˎ!:O8mEJDe#3]keIHvvN5]7=D8 ೋ"RT".ϙuuYtZ6ˬ2y8a8VVq"JBS,=Y)c.TUFiש{#wWVxhӵvܭ<(D53W4yw""JcU8\-7Y`3]$K"K&HVKe-&E˟_咬euL8b=N\:pA"!. UZۋV.UY8iu68ejiڄJ\qZ0Y=U'+5wCb%Dב^ymVR,ۦRXzX0%RI*2vgǣufDDuz+auz"n3J*R)^RXqy[Wl:FZ.ˬufM ue"a"J"D, T6ezaw ;\`q5K]VUyf)n04;kY&,TTk$uvXuaʳ:J%iTDri!1e.YQ 2qg뎺Qǝ,eגA (ʰfYfeyuZqDB%i dZG|8O_6su䈆]ʹ^a7Vn)pK4 nO 4mz ZD!im&J'vĞKxx}ەP˙h`jE2JF=@zO1(Ȗ%kK1 Pe)K(R$Ai%60%D,-)DQ )e \b1J8A1DˆhTDB8s:8Tsi;smۖ-bt:7WSv.=:Z7 Vrmt iHIUXHDEQJ},*WZmW3N4XrZD*ID$$}?4I%5 ي窻,6JH-*1f[l.FOEV][VZb*"7,dU*g+8ˏ8ɶa]Js(#NzxxYRiiekdmQe-×[YBDʔիJJ"jF+㪜Jsy HS̺.;b"Z6)UaUDAֳɪxum0RUr]36BDd[FHFXil8ӭyaYr]ڪ$]I,MbIZ$E֚{tgNjʲ [ Xm))oZu%۫z١mqiȔJEZ/r-dm.6 ̸l.84gnmsNʑW[:G{_Ғ%K[ImZTmuܱt 1O>4؟ *:jؒ(#:.4=Fc]WZ䨕%Dΰ1He)`l^y3H%s'i-&GEخyyg ꭪¢+-c,+'[duV""! )Q٬⺳ TLjԳmWWO['׋U[*IPuqYhTeavO?;OQx~>gY4;i$K[!U%tܪ[0%q^֬m.V$DJiyuDkev˳+P"t2r"˼iEz"%Eb6.̖cFז{oUYjͬh%'yiyGz󨫴ۧ7Z#"$G+ 1 ucY\eXB/VjVqf8ˮ$Ty]oey=PFy[.7^<٨^N6ѧze. ƷY]̢&%")lׇn]7,Z-TWugav2/ZqzD#O3hȉQH:%{^2jʔ֙$ַ{N>O04yCvq bUnE7Zuܴ2*,ݝ#3]T[(-4KZq=8vw|+M6ѮUGQʴÆҬmηZۭ}qDD3WZ68zN< A2DDQ 1$Ɋhh0bM4 P(1MhSh!JhQ `,Z$1LbWkzn봋CL~a~LwעO*f\ER~sqqHR_]Ynv`3}+_y`ע2z43Լ[ްI2i Rg@GD̘vS`r$7K@Ky{Ve)HʪڕUYq֩g,Gzi_PS}|eʮ HprԢ\g pߜrM1s(B+o#De˧ I..`t-!,A^jyVHեk5iZZVYTU*ot8Yfiř%X{"%DyVFSMҭ̷[tIRDHi+:cO2}}{IkǛRP$sԙNF1Z1]rʸ8kd+K=P͎hrO7dFXTe|qM/^DTRmŞG^hՖ*ezu:r%JD,iI,:M!I#gknQ̒<ˬ4ôߥCz3U,:|.#<̶בҽ%Dqwۗ/_"M㻳k.:YbJFXtk*{ wRI$_HZ|Q{*,zhȚII&ǯi8~w约M\"ՕL6*R+.x2˕em6M4ʔdqbi^yںĈ:kS0YWD:yoSG8o$dQty6"UDDJieQye֘.>H,#3շ5:-/6/eUoUg*em]W=N"UDl;uwZeg"*Jegz2V#e0"ymRMSylY.u׈䈲2uu5Z,,.yFokdLΫEͺLugDvë=aFbά6v3yn3ǣSiHf,egZp=^ӗY4KdMi*ԩ$)YaeՊRIOq먈^*!"+q[aƝV˦eg+u*%R$-^ǫ.]n3J %ie-XD<=b]H%D^nj]q0qZr/2,w)ii#HZYleժDua*2DIQ:l.{:.v̬($J˺كinu.j4PY.ST,1YrquԈ%"J5rէ>_)N*8nWiV'mQ*DDBm:M2xn6 "E! zӫ0M:2m7xWyaʉR5ΰZmRav0y&S"V61O.Yv-nmm4\j6[Ç_KI%j%yʰ%ZqʫmvHM $[㝜Dh9zyǘW 0ݭ%6\un;["JN:LMr¸rOTTDǙ4p.!f] ḽިJ7qa,e-U뭛<D[[믎;*5Æeg2y㔎V+5ں㮼uQ# :vaxvQRwKuYE|eF]|r|,..aV_wGʫ:zDSXU *tbFiu5Enr__DuΥH2n÷7ҵ"̤"RjB/NPa2e"0\sb"R"66}V6:W,.YJIJDkKIqZQiKSYdCy[qxL]jj󊔈2i2b*xˊmuj.ykjr(Esŗ^z, +3]Vx[< xMӼ-42ymWhYtDmli^uy0iyn{+5ڲY][ѣ+aQ:n߷[XF,$eWvu9\zif.Uvq~ "YB%ex "%$JE͑qsuwif`]5vly*%IHQjjn {%AWt=TpWp9 [$SVfP›P6Vֶ)l+(e1k5H4mL]mێu,r>a_2:8aIUYizu{eqk:Ii]nk8Y%B%TBT,n&o'YkԉQZyhYʼnM<[A0`uez&ܦe",SYGo,4IbDyuÈ2^nPˮv(UOs5k˼ dTHÊY-LDr;TDv\a4H;ٕn\y*ޮiu˭y:-%pۍ %{O;X{:p2XWgI$:k26Y-bijydGujoP6 4Z:eHmq)cM7^V<TBQ!v92㎪^Jqe"*:х""56R,=FT^YǫnVnzr~w-%4"Jiyʹnq-B DHY*8Uθe׌3S.|De{*Vu3Ś0i<*M; SVZuJIQjNN#l82q!ڕsѕag:I*DJe%44Oǯv"-!dIܹsDI*U1]TG\7ȨQ$Um qd>5Y.+9]zqX,+UJ:꼨eb蕢iM"DaRmzMqDJ JFkZi-^2UyZ.ˤj0x^aƙvmъˌj8g")wo2dR$RҒ'^+m/2T%pGz㌘h*"J$m "9x7\a \ci$}dI[|pgDM-"R#U6L;khwk'[_H 9\3jۏ+Kטx.]|vEjũ{~ȪhI"JDDYc9!wlǛxդe}pHo# ]]ElC=/*ueJDJ0Ǜ6akBD^}yKD\}G",ÇM!ܚ] t4"hDmŦ-Z$Zߞ:}x|zEgU=iۯ#+12.,! ."PJ"J+NYӺ+[JM:e]Z<2j9]W2XadDEDT6ԾJD#`;8Vi"U^H3M8ejay畅̶uHaVff!# qaΜPd ̉hЉ(OCh DX6 FI-VHeQ9pc_'v`yjTE <[y#ǗG*ZHIk=w_eeY^G+Jk֍P,3^B:f㯚I -4[ÍRXHqu8q*θҙJUmq+<{VC XYfW՝ude\|Q'sEZɢYZ;|pDDTQPuv74KME%(ODvK:,2knJ<M<ۯt(^ƓήHuje8Yj-=GT睵u>h;|r8;MJ$Idti~'|pd0_vy- _.Q#2SsM37- iNahHJGyW/Ykquydmg/V6TtѶVy縮,jlG""ik>dEMz󧯅i8m0iݫ,rwq Wq]s2YEB"Hg-^65K75Yr0ҼUy+=Qn1QWqng+uu$D񵗐EZjSn'r~:jSk.ӕۮ4ӎV]ūO"ki6r>2id׎gNKtx{*86vĨWuc*\4.2.˩DDAçZ.eqvIP8-*J("yŭ+,WT"Vq\uwuf6V]2k4^ɗ]q)Ry3sIZ]V3rZXujvO]>9E$Zi$Kh$IIԲ֪)c68oˮvifYUI;[-6s*KunU\!BF]tr]j˖O]>w-KEYUq۲74m+d.-VkWUj Q˪ӆ0xzi5EHpZ0ҬϕBk+-__TY7UĕںaVJEn_R:MS-ǗqӋ0x㬜e*1]6琈ˎeݢhEhFĈ"%"RfgnX^βNVe8mev1h(vFmzUDTb$Rl(bcJPĦ DPA Q&08`ƔB(hb L`(Q bŎ`!A@0װk{#7TZ~6` -׆F,]U[YQAzf]i8#L#TzDdá7`oY}"ѨYE#x/x?GN") li֝tZS[*RŠE<,[ugόTp4՚a8U׳-O/QӵjӬW,ڝehb֙s$manwM"'{vmT-,g]%!"*26o8n J8g]^GqlQEIL񳫴nawU,)ZjVEزDIVR$_ɛbq8gdUUez>4)YHI4%/;m۾HWimDvgmi^Uy(*"8nZ:Ja.-vk{-K,cegN0]ͽ*Dݮ%UthjWQ.0<㨽r2N:۔DZPDGʪuY$[MeڦLY4lBDet˵Z;^T,lm?~>:x{TU4ȵÓYw<*q-$B6{ireZeI $t󓖒5DI$[H:%jR2gyHD͞$EJm+.ׇʉN:e-6kJ8z;ƶX"Rq*[-rdHmj])*UDB,̭09LLp0emW4,yeѪ"TI *˛,jiM$Id T%ۧ%THJDv]ճNMwzgEHG1jaõNό+5vI%(D[BD㍸huMbm+m*$X.۫MTqRUF,33\7)uN{y*T%TL]cs , mmuj"jj$Y*%˽ۇݼWQڬj+4,[VYeJDRM˔z;\)÷EP5כ[wy4U]\#m f ,5G{!m:JLR"ی,N+δ%jB4l,vh"DEYDT" HmۧWe;dJZƞ]Dњ~Sucw]XIDD"\>:񍞬2e]%C+Sf.9^ XUcl ux$}jzdIdHIevtfmbS&*ʈD$Y%m"Ieq2 zYa!,Y\E٫.G HDVn:u[.w^qxdDe+:u\ۮ=Yنư$<כt~""Kd$BVT8 #e]UJ #.yq#K6yJ"%K]?<&ȕ 0 YiԳnq)+;<ѲWk"!*0㚺< Vmjq'դRTiӟ_44[-iU)*:9XiafK#k2jޭQ"V_ #2ujZum0DHIHVbqG0n,aP^Yjӝ]tsˢDKS.zن"! ԱrZDAN^$i"BZeT b[Tk4m;^+ VJ`Sκy.Su7hK"IqgO4Ou*.ӯx]y$"5iM9fF%._ S5MJ xw0)@D"0D0 3{7S5U5'A{r< o{Ȑ$`ha@ a#vtS̩Zz DץRM5BQQQEXLa}#&-3Vhw)Uo?:F_KKE'uvk QȾЇq]1D AD V"2F.tM8j+;8 !0 |9srVֽ6A -okckNj!#1(Q&ffj㛲pFP\ AhH b^s2/man/0000755000176200001440000000000014534575653011372 5ustar liggesuserss2/man/s2_interpolate.Rd0000644000176200001440000000332114530411473014573 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-accessors.R, R/s2-transformers.R \name{s2_project} \alias{s2_project} \alias{s2_project_normalized} \alias{s2_interpolate} \alias{s2_interpolate_normalized} \title{Linear referencing} \usage{ s2_project(x, y, radius = s2_earth_radius_meters()) s2_project_normalized(x, y) s2_interpolate(x, distance, radius = s2_earth_radius_meters()) s2_interpolate_normalized(x, distance_normalized) } \arguments{ \item{x}{A simple polyline geography vector} \item{y}{A simple point geography vector. The point will be snapped to the nearest point on \code{x} for the purposes of interpolation.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} \item{distance}{A distance along \code{x} in \code{radius} units.} \item{distance_normalized}{A \code{distance} normalized to \code{\link[=s2_length]{s2_length()}} of \code{x}.} } \value{ \itemize{ \item \code{s2_interpolate()} returns the point on \code{x}, \code{distance} meters along the line. \item \code{s2_interpolate_normalized()} returns the point on \code{x} interpolated to a fraction along the line. \item \code{s2_project()} returns the \code{distance} that \code{point} occurs along \code{x}. \item \code{s2_project_normalized()} returns the \code{distance_normalized} along \code{x} where \code{point} occurs. } } \description{ Linear referencing } \examples{ s2_project_normalized("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") s2_project("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") s2_interpolate_normalized("LINESTRING (0 0, 0 90)", 0.25) s2_interpolate("LINESTRING (0 0, 0 90)", 2501890) } s2/man/s2_earth_radius_meters.Rd0000644000176200001440000000142314530411473016277 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-earth.R \name{s2_earth_radius_meters} \alias{s2_earth_radius_meters} \title{Earth Constants} \usage{ s2_earth_radius_meters() } \description{ According to Yoder (1995), the radius of the earth is 6371.01 km. These functions are used to set the default radis for functions that return a distance or accept a distance as input (e.g., \code{\link[=s2_distance]{s2_distance()}} and \code{\link[=s2_dwithin]{s2_dwithin()}}). } \examples{ s2_earth_radius_meters() } \references{ Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the Solar System" in Global Earth Physics, A Handbook of Physical Constants, AGU Reference Shelf 1, American Geophysical Union, Table 2. \doi{10.1029/RF001p0001} } s2/man/s2_data_example_wkt.Rd0000644000176200001440000000063714530411473015565 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{s2_data_example_wkt} \alias{s2_data_example_wkt} \title{Example Geometries} \format{ An object of class \code{list} of length 29. } \usage{ s2_data_example_wkt } \description{ These geometries are toy examples useful for testing various coordinate shuffling operations in the s2 package. } \keyword{datasets} s2/man/wk_handle.s2_geography.Rd0000644000176200001440000000512014530411473016164 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wk-utils.R \name{wk_handle.s2_geography} \alias{wk_handle.s2_geography} \alias{s2_geography_writer} \alias{wk_writer.s2_geography} \alias{s2_trans_point} \alias{s2_trans_lnglat} \alias{s2_projection_plate_carree} \alias{s2_projection_mercator} \alias{s2_hemisphere} \alias{s2_world_plate_carree} \alias{s2_projection_orthographic} \title{Low-level wk filters and handlers} \usage{ \method{wk_handle}{s2_geography}( handleable, handler, ..., s2_projection = s2_projection_plate_carree(), s2_tessellate_tol = Inf ) s2_geography_writer( oriented = FALSE, check = TRUE, projection = s2_projection_plate_carree(), tessellate_tol = Inf ) \method{wk_writer}{s2_geography}(handleable, ...) s2_trans_point() s2_trans_lnglat() s2_projection_plate_carree(x_scale = 180) s2_projection_mercator(x_scale = 20037508.3427892) s2_hemisphere(centre) s2_world_plate_carree(epsilon_east_west = 0, epsilon_north_south = 0) s2_projection_orthographic(centre = s2_lnglat(0, 0)) } \arguments{ \item{handleable}{A geometry vector (e.g., \code{\link[wk:wkb]{wkb()}}, \code{\link[wk:wkt]{wkt()}}, \code{\link[wk:xy]{xy()}}, \code{\link[wk:rct]{rct()}}, or \code{\link[sf:sfc]{sf::st_sfc()}}) for which \code{\link[wk:wk_handle]{wk_handle()}} is defined.} \item{handler}{A \link[wk:wk_handle]{wk_handler} object.} \item{...}{Passed to the \code{\link[wk:wk_handle]{wk_handle()}} method.} \item{oriented}{TRUE if polygon ring directions are known to be correct (i.e., exterior rings are defined counter clockwise and interior rings are defined clockwise).} \item{check}{Use \code{check = FALSE} to skip error on invalid geometries} \item{projection, s2_projection}{One of \code{\link[=s2_projection_plate_carree]{s2_projection_plate_carree()}} or \code{\link[=s2_projection_mercator]{s2_projection_mercator()}}} \item{tessellate_tol, s2_tessellate_tol}{An angle in radians. Points will not be added if a line segment is within this distance of a point.} \item{x_scale}{The maximum x value of the projection} \item{centre}{The center point of the orthographic projection} \item{epsilon_east_west, epsilon_north_south}{Use a positive number to define the edges of a Cartesian world slightly inward from -180, -90, 180, 90. This may be used to define a world outline for a projection where projecting at the extreme edges of the earth results in a non-finite value.} } \value{ \itemize{ \item \code{s2_projection_plate_carree()}, \code{s2_projection_mercator()}: An external pointer to an S2 projection. } } \description{ Low-level wk filters and handlers } s2/man/s2_lnglat.Rd0000644000176200001440000000174214530411473013533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-lnglat.R \name{s2_lnglat} \alias{s2_lnglat} \alias{as_s2_lnglat} \alias{as_s2_lnglat.default} \alias{as_s2_lnglat.wk_xy} \alias{as_s2_lnglat.wk_xyz} \title{Create an S2 LngLat Vector} \usage{ s2_lnglat(lng, lat) as_s2_lnglat(x, ...) \method{as_s2_lnglat}{default}(x, ...) \method{as_s2_lnglat}{wk_xy}(x, ...) \method{as_s2_lnglat}{wk_xyz}(x, ...) } \arguments{ \item{lat, lng}{Vectors of latitude and longitude values in degrees.} \item{x}{A \code{\link[=s2_lnglat]{s2_lnglat()}} vector or an object that can be coerced to one.} \item{...}{Unused} } \value{ An object with class s2_lnglat } \description{ This class represents a latitude and longitude on the Earth's surface. Most calculations in S2 convert this to a \code{\link[=as_s2_point]{as_s2_point()}}, which is a unit vector representation of this value. } \examples{ s2_lnglat(45, -64) # Halifax, Nova Scotia! as.data.frame(s2_lnglat(45, -64)) } s2/man/s2_cell_union.Rd0000644000176200001440000000144614530411473014402 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-cell-union.R \name{s2_cell_union} \alias{s2_cell_union} \alias{as_s2_geography.s2_cell_union} \alias{as_s2_cell_union} \alias{as_s2_cell_union.s2_cell_union} \alias{as_s2_cell_union.s2_cell} \alias{as_s2_cell_union.character} \title{Create S2 Cell Union vectors} \usage{ s2_cell_union(x = list()) \method{as_s2_geography}{s2_cell_union}(x, ...) as_s2_cell_union(x, ...) \method{as_s2_cell_union}{s2_cell_union}(x, ...) \method{as_s2_cell_union}{s2_cell}(x, ...) \method{as_s2_cell_union}{character}(x, ...) } \arguments{ \item{x}{A \code{list()} of \code{\link[=s2_cell]{s2_cell()}} vectors.} \item{...}{Passed to S3 methods} } \value{ An object of class "s2_cell_union". } \description{ Create S2 Cell Union vectors } s2/man/s2_bounds_cap.Rd0000644000176200001440000000265314530411473014371 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-bounds.R \name{s2_bounds_cap} \alias{s2_bounds_cap} \alias{s2_bounds_rect} \title{Compute feature-wise and aggregate bounds} \usage{ s2_bounds_cap(x) s2_bounds_rect(x) } \arguments{ \item{x}{An \code{\link[=s2_geography]{s2_geography()}} vector.} } \value{ Both functions return a \code{data.frame}: \itemize{ \item \code{\link[=s2_bounds_rect]{s2_bounds_rect()}}: Columns \code{minlng}, \code{minlat}, \code{maxlng}, \code{maxlat} (degrees) \item \code{\link[=s2_bounds_cap]{s2_bounds_cap()}}: Columns \code{lng}, \code{lat}, \code{angle} (degrees) } } \description{ \code{\link[=s2_bounds_rect]{s2_bounds_rect()}} returns a bounding latitude-longitude rectangle that contains the region; \code{\link[=s2_bounds_cap]{s2_bounds_cap()}} returns a bounding circle represented by a centre point (lat, lng) and an angle. The bound may not be tight for points, polylines and geometry collections. The rectangle returned may depend on the order of points or polylines. \code{lng_lo} values larger than \code{lng_hi} indicate regions that span the antimeridian, see the Fiji example. } \examples{ s2_bounds_cap(s2_data_countries("Antarctica")) s2_bounds_cap(s2_data_countries("Netherlands")) s2_bounds_cap(s2_data_countries("Fiji")) s2_bounds_rect(s2_data_countries("Antarctica")) s2_bounds_rect(s2_data_countries("Netherlands")) s2_bounds_rect(s2_data_countries("Fiji")) } s2/man/s2_is_collection.Rd0000644000176200001440000000742514530411473015104 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-accessors.R \name{s2_is_collection} \alias{s2_is_collection} \alias{s2_is_valid} \alias{s2_is_valid_detail} \alias{s2_dimension} \alias{s2_num_points} \alias{s2_is_empty} \alias{s2_area} \alias{s2_length} \alias{s2_perimeter} \alias{s2_x} \alias{s2_y} \alias{s2_distance} \alias{s2_max_distance} \title{S2 Geography Accessors} \usage{ s2_is_collection(x) s2_is_valid(x) s2_is_valid_detail(x) s2_dimension(x) s2_num_points(x) s2_is_empty(x) s2_area(x, radius = s2_earth_radius_meters()) s2_length(x, radius = s2_earth_radius_meters()) s2_perimeter(x, radius = s2_earth_radius_meters()) s2_x(x) s2_y(x) s2_distance(x, y, radius = s2_earth_radius_meters()) s2_max_distance(x, y, radius = s2_earth_radius_meters()) } \arguments{ \item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} } \description{ Accessors extract information about \link[=as_s2_geography]{geography vectors}. } \examples{ # s2_is_collection() tests for multiple geometries in one feature s2_is_collection(c("POINT (-64 45)", "MULTIPOINT ((-64 45), (8 72))")) # s2_dimension() returns 0 for point, 1 for line, 2 for polygon s2_dimension( c( "GEOMETRYCOLLECTION EMPTY", "POINT (-64 45)", "LINESTRING (-64 45, 8 72)", "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", "GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 8 72))" ) ) # s2_num_points() counts points s2_num_points(c("POINT (-64 45)", "LINESTRING (-64 45, 8 72)")) # s2_is_empty tests for emptiness s2_is_empty(c("POINT (-64 45)", "POINT EMPTY")) # calculate area, length, and perimeter s2_area("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") s2_perimeter("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") s2_length(s2_boundary("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")) # extract x and y coordinates from points s2_x(c("POINT (-64 45)", "POINT EMPTY")) s2_y(c("POINT (-64 45)", "POINT EMPTY")) # calculate minimum and maximum distance between two geometries s2_distance( "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", "POINT (-64 45)" ) s2_max_distance( "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", "POINT (-64 45)" ) } \seealso{ BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_iscollection}{ST_ISCOLLECTION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dimension}{ST_DIMENSION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_numpoints}{ST_NUMPOINTS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_isempty}{ST_ISEMPTY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_area}{ST_AREA} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_length}{ST_LENGTH} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_perimeter}{ST_PERIMETER} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_x}{ST_X} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_y}{ST_Y} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_distance}{ST_DISTANCE} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_maxdistance}{ST_MAXDISTANCE} } } s2/man/s2_cell_is_valid.Rd0000644000176200001440000000330514530411473015040 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-cell.R \name{s2_cell_is_valid} \alias{s2_cell_is_valid} \alias{s2_cell_debug_string} \alias{s2_cell_to_lnglat} \alias{s2_cell_center} \alias{s2_cell_boundary} \alias{s2_cell_polygon} \alias{s2_cell_vertex} \alias{s2_cell_level} \alias{s2_cell_is_leaf} \alias{s2_cell_is_face} \alias{s2_cell_area} \alias{s2_cell_area_approx} \alias{s2_cell_parent} \alias{s2_cell_child} \alias{s2_cell_edge_neighbour} \alias{s2_cell_contains} \alias{s2_cell_distance} \alias{s2_cell_max_distance} \alias{s2_cell_may_intersect} \alias{s2_cell_common_ancestor_level} \alias{s2_cell_common_ancestor_level_agg} \title{S2 cell operators} \usage{ s2_cell_is_valid(x) s2_cell_debug_string(x) s2_cell_to_lnglat(x) s2_cell_center(x) s2_cell_boundary(x) s2_cell_polygon(x) s2_cell_vertex(x, k) s2_cell_level(x) s2_cell_is_leaf(x) s2_cell_is_face(x) s2_cell_area(x, radius = s2_earth_radius_meters()) s2_cell_area_approx(x, radius = s2_earth_radius_meters()) s2_cell_parent(x, level = -1L) s2_cell_child(x, k) s2_cell_edge_neighbour(x, k) s2_cell_contains(x, y) s2_cell_distance(x, y, radius = s2_earth_radius_meters()) s2_cell_max_distance(x, y, radius = s2_earth_radius_meters()) s2_cell_may_intersect(x, y) s2_cell_common_ancestor_level(x, y) s2_cell_common_ancestor_level_agg(x, na.rm = FALSE) } \arguments{ \item{x, y}{An \code{\link[=s2_cell]{s2_cell()}} vector} \item{k}{An integer between 0 and 3} \item{radius}{The radius to use (e.g., \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}})} \item{level}{An integer between 0 and 30, inclusive.} \item{na.rm}{Remove NAs prior to computing aggregate?} } \description{ S2 cell operators } s2/man/s2_point.Rd0000644000176200001440000000163214530411473013401 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-point.R \name{s2_point} \alias{s2_point} \alias{s2_point_crs} \alias{as_s2_point} \alias{as_s2_point.default} \alias{as_s2_point.wk_xy} \alias{as_s2_point.wk_xyz} \title{Create an S2 Point Vector} \usage{ s2_point(x, y, z) s2_point_crs() as_s2_point(x, ...) \method{as_s2_point}{default}(x, ...) \method{as_s2_point}{wk_xy}(x, ...) \method{as_s2_point}{wk_xyz}(x, ...) } \arguments{ \item{x, y, z}{Vectors of latitude and longitude values in degrees.} \item{...}{Unused} } \value{ An object with class s2_point } \description{ In S2 terminology, a "point" is a 3-dimensional unit vector representation of an \code{\link[=s2_point]{s2_point()}}. Internally, all s2 objects are stored as 3-dimensional unit vectors. } \examples{ point <- s2_lnglat(-64, 45) # Halifax, Nova Scotia! as_s2_point(point) as.data.frame(as_s2_point(point)) } s2/man/figures/0000755000176200001440000000000014530411473013017 5ustar liggesuserss2/man/figures/rc300.png0000644000176200001440000003652614530411473014370 0ustar liggesusersPNG  IHDR,D;sRGB pHYs.#.#x?viTXtXML:com.adobe.xmp Adobe ImageReady 1 ).=;$IDATx E;k& d!B!NC yB5O}$, "Ld\6$3dL2۽]wzٲ`fvuթSN:}jm{YΊى.~_ys\'Z Usy9PoߐA RVg,}[R7Y*eEiJV 3^g0Z1Oʯ~U.rȁ{W'뻮^$-u+XR9N1!m;n3o9ZJ͑r8Nj`0W /rȁ{/3RV˗?XNܗŃBAǯ 2]JR\+pQ٣P:&>.㤙;*hmsEW ʺ|ٳ nx7]3uܭt~Z;}A{Mdd 2Wrҝ'79Ac5E#(0S0ʾ6R9sPV9PAā= Z]%}æ' G8E7T^x/rȁ4hT )X}Rẫ\,{5u]Y^@JTۇ1Qȁ"0e~O ee碵e9Q9pppU"`MIΗX?p/dMwYel4>Hn2P!]o9|WZ_J)A"j%sǾԭ"eU=c[|7WZUI | jdRvTpX\u^MpêFhok)Nζm/|^pSڰ+rݶfVqY8Ӟ4 e#m&Na?:jTYZ{12ܯTU-Ͱ'irWE-˙Z__ߐ6*wpr*Dmm6F2ӦM+4iұGuFŅS7kh'u,:.F.e^MFte:ڻ&X:p7 vT%FTRC@AWeQSWW%\h]imIx vرUr؇Iill֑dNᅜZ%@sPsTC/suJz֕R RScOL +,VЀ) @!fNxXu5'IKX:-uE52EyEئNKp]tE K}ܸqDbd*;!^ٰaßB+,[SS3J1?%.8Nu`% KkeK$;#*˱>%A(/c,6?cٿb;TWѧe 4jngZ?GR).sMR::v"(WTT޽ctAk]{Ɓ:MA|7^6.;2UO'|K|OvhH-Jw|XHpIsGnݺ-ՓgɳByʉkljr]E>$n!JJlxqyN><3i'M=(a2ac~Iϫ7σY/{kӺN7@XzVc O`,e$7egm6^9qHUfRXm#ל/_qGYr.:L~^Њ9ݕ-el2qij@ہI+ayLCo(U]mĚ~ @`Fz'; AXo|/P'N|wBT~ ?h!OՈ+ƊG"LˉSDq?$?盔-?ޜxPk %Û^HKkj E (*x3Z<$ĿCÝFECOd<1 K*F.+?sYplyVHt& s9 RIy_3E8J%_# aUNxsĈ mjM$eUEk CnϦ6zM<9ưKC|RFxm8ʃпQgB_Fnl~Ws 3)gҹ\x}`<\P,yCs< GPQ//.ux՗(W}T|ΪoƣF,4 q6[6P"kjj.@(a`ueEEe34jZ*!MF %␐ʢ|>>)+[Y𥠜#UAc !tq܀ͭtRR]\ì̕ÖAّ(O/t_YeS=M&h%џL^Y_kϪ,ZE3qwҸϲJ |/^@k@|{.~Y.#:UN V=۹ūػΟT)N.NJKe:ΎxGpU^C'wei.QmQ:ɂҵ뙺wmzQ-zQpFQV)g)5ʺ}+ 3{Zp Τ!̤YPI}:1""HjjT+(4xؾwן9w=틡w6'*8ϯm%Mc)Tm]4V2h1N߼_'a2~ zD}%h.Ѥ |/O}CЬW-لY{E;aQ஧RdA%Lae]xltoAB<:RI]^B#!nS|I IƆX , &O/ey#Dr7cÐH$C< xC،<*彑gݥ3ׯ *VUv|5 %püy'4@Z;,3bFyif; 6 wylì%e 'eU*IGIEG^R۔K#Wb *9'ȃ>TJy@iifNN7\pzjWz#%:Hce3>$ ꣺3p>8 jh6<}ןΘ#1bi y g<' RN:+ai%\Y^ȉ7$5 1_5FKݲb]0DeG)3ϔ9ބN}EH6ԽSȩ\;S%3Ŵ>3NDaFcE}c„ zp_y\[V>l^Uȯg趽֎MR]C<]\MLsS)+uvf$JG_&JOf4É3܍ BhrNBZ;N3}ٙ\~BQIzیcԴn$#@9UptȾs}MuP' \r;Q~S0| Z6FN Rd@Q^,5PX)htN~A\ \عmlllrB%?m;ֲRy34?}x˵*%%>R|^a+ U׷[6]" $ߦzXM]f $,E\7WZ"$“/p_D*~)UZ#14o-~Ag5AEEsh .a%w1(x8Xq s~u4~͚Irj,RVHzIǜ[Zqhԍ*nntBƙB>gr]ዔdㆍK#^rF虅:F>IY%@F^ qXSSCB GR*N~/n ^-|F,5=;{>Li _TWmݛYnB ;0o~O) 3xx^%2׋d,Mes竊¹IU=ٳO?t%콣4ɘ{Oz9K&x_vГgN[gDѝF05l9ALxiw4#@ΈڡQ$VYQai6uΡUmW7mlTErbܚef-L~(4NklEEQ(X- РwcP7IiY(MC9t2ZaIv7FH,K@?IJĚLQ8ZQBpn&BQСEY`alnV+70$LlfD7qJh2NuV]M@[ދ9["eOJhr*րW<(mLWrघ  f5cmݺ5Ki'~ |~-H 2}~\*++1cnx*/&K޶c䊹bn97kb{uuRJp,|)CMMsXU`}3*c1ػw H8#eBc:8MG’ש0Qi"+0RF2V@􌔕cv7| >=ڔ2TXac {hxK"ˣbKU8QRmn|ZPZds*)_wRGKZ͈ ܬ"vrY!i(kko{`w6@xr: CCV㧸zѳ\į4Y!M+}Rc0ao3cR խ,lawǢhTR:4pRBª\4|ڀ{ule!L~H:H/-Țf۸_[T>ofZRRdΗ}?0˚5WS?awynv]{_xa8YaJMxEk؃nKt "_ҏR,)^$VDcf29#yeQ G016UTqTT&xY qee pzɯ ß^EHr#c aJ*Jw&JP\8I5\ WtZ)C]G& WKcv'Z5tNDpEܫ&kPߡ5~5ȸQ0I 4*EeXGlh }LςY os)t0^,!ڂf 1*}`< 7 ̱%+cQb.`֖W9缲2QY1t]g>Wm*T8x+Ӌϴ3w'۵+ñi`iAF>*03Ac12 xyXJp%}eއsQ\$tpvHPކ(>ec{qa-Je444`MJkqW/A NY%,T\FeƁ˅ r;%NJh-<+c%xNio볶B1DtzծsԱ;wnNGGݻw\#+-k?kH IY$q@cԭd8Wg~ᎋ7N9nUt\qQs{렰 -1akjIƭb0nӌ%߽伆gµ<V(,tPFط-O=3gvG H*Au)am:YT{Xv -4V_\H~vCD/17Kpt7ˬb|ŪJطV7jDJd-jOfx~ſcJk{ ]AB9$k L"FG-l᱉Ԡͺ%?f#o1@q ږUCn$g_;kb`XMWϠj7iS a+PDFfԜJ>œd62Ic 󜱺gy8 D/w(P' }0IL=ccϠW8}񛅣QjFoԨ緟hDJH!O%fA1NlM*$i2fPAJu&a̪~0NdMbV[ ӘnSN5- uI|jw xrgu5+f"ik Ѡ&5wCUa@F4M LZ!bVCwu@O횝#j:H,My<& Crʚ(p'M(cLK( #j˃x>C"5VCz-_HGxUsfN~ex#Qb.VFF(u^{gΠ-u\4QC؛j$`/)&}iӼ?oM":R`f6 pKHq8Uz ֭d4`~tZk}5N7s;+56iR|RV⩔%ea(E(뼓Hf)Яn8/F<OEn%kY|1Z9_F)<ה2Pm-^ (ecC*Zrjjj+{в[Iz;< !d-y?ZEx+/ªµH3 /O f>+Rox6X)gd4, GHmFy8 0!vO% 2݈ơ %ũ1N kY9 s(+}9ę߅%tqķFpItCt'M u&pAd? MP>Du;葆3Ն X4:(s9jWQ "%j&(ʡgX: oI)po{ ObwYtzڀ e~eįʗ+R7#?GidCQ޶AHȝ_͹fF4|G*'FuX;ke RrxJqK>BXkeb3M"A Clx _$=,m:=FX[0nc,}+n=c^呜Z"1J(<6;X0*[WƅMܦs>χWƙU6ሮO3o9t oGz574>|gwevYoƅ8.sѢEGki`Fk ⳌUqx7hFho#|SGù/Il*x`=Ğ B5xiow UFTiRz|!'"$8BD*x4!teA%X1rDw}*JZH%bM~S(g#D">)<dz e>`P*z hx*mSX'|v]8_ Xީ^ <@>Zc䃛4Z#NK-Ӊɋ2ކB^: w-p mq_cڴŗz3 y :+J@J[+DK*,wنv ƪǼ; ?9xTVGAtNsݔ .8]WS c}Q߹P٤6/g^т6ܕ\ĜjsKB>C J'۾&uӌaqYD[0ƈNNEIuJQ^3x|(1hV"\JZ]8z d{16,e ngYh4l"ϣPHKFPn\? ĝ8n֦/%op2PQ#p6G>@0)Zxw!҄}7`&!C9Eצ M'T](0n{eidpk Ƅ^g= oєG<!{f*8l)/m6.FRhLe67[(*)y.fYI9Gў:n'\.N+y7ϓ8'8$!mCNT1"z R[Y7/_M !::㛉VYՕ)$ݍ6],K]9g TB..f@k fxJUP8~P|jTd,l wi{>1QF@_ɥ {+-_Ќ࢟?U|DՐ)#R[ ^Ga/=N}92ZT<*DDX\Ƚ]J)5g׽ npW|DO~`(+՗+=ϻ|JCv;keUDk'EyK[ڝe ?a_)@m(PaVxp9P@}Z4XKl)E9pqpґ ~nߗvTB46jYkd|V1UOt$M+z%FtŷbxEB)d di,- ˇ)>9PAˁ֠ThLИ@֦ut&֐0THtGμijIw.i";ph)3]&Op &?i^m'w<&X[򴛪!*Sl~-F9PߝTJ -"Q6Ǭ8aOd W)kmn9pK }8mV;(a-9'Q2NSjhñ4tp B0鎹V /9ps`,,,;|m;_J{42qĦ/cZjs=EʪceƎG/P_ PVfay|3 PtE9@A/y WF)c3ԘPE2?we'z w;،=і( }N5 F+jjߖȁ":€z9|YPv+aU+Zf kjϳrcyY{7b 7<|\xBc_£ :_-r w=/]E9g +b8~ pI*(ͱV}̌#}EyIݢ/>갔Ύ6:tiNP,^XVφ:cV%l͉ґ.q)NKynҊE*r{@ĉ(kgɶm2_aYFę˷q(Ϲofjk='{wkgE#ۻsf\CV^JO=۱Vh¢{6۾TN OEl1,)M+f?ӻ%h>rl:O]w=$@RSEb&?QVW ,rȁ{faٵVX=VY+s,kfP~ڒRs9TGVaeLT3u*hrW{YnNEY$ʪNJaE.8G ܬ)dDvP3Ja# sm{՞"HKN++rȁ{ga(Y:Ұg_vYSfv}BھmK?sfUW]է#P o4ŧ"89QhdIENDB`s2/man/s2-package.Rd0000644000176200001440000000256614534575653013607 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-package.R \docType{package} \name{s2-package} \alias{s2} \alias{s2-package} \title{s2: Spherical Geometry Operators Using the S2 Geometry Library} \description{ Provides R bindings for Google's s2 library for geometric calculations on the sphere. High-performance constructors and exporters provide high compatibility with existing spatial packages, transformers construct new geometries from existing geometries, predicates provide a means to select geometries based on spatial relationships, and accessors extract information about geometries. } \seealso{ Useful links: \itemize{ \item \url{https://r-spatial.github.io/s2/} \item \url{https://github.com/r-spatial/s2} \item \url{http://s2geometry.io/} \item Report bugs at \url{https://github.com/r-spatial/s2/issues} } } \author{ \strong{Maintainer}: Edzer Pebesma \email{edzer.pebesma@uni-muenster.de} (\href{https://orcid.org/0000-0001-8049-7069}{ORCID}) Authors: \itemize{ \item Dewey Dunnington \email{dewey@fishandwhistle.net} (\href{https://orcid.org/0000-0002-9415-4582}{ORCID}) \item Ege Rubak \email{rubak@math.aau.dk} } Other contributors: \itemize{ \item Jeroen Ooms \email{jeroen.ooms@stat.ucla.edu} (configure script) [contributor] \item Google, Inc. (Original s2geometry.io source code) [copyright holder] } } \keyword{internal} s2/man/s2_cell_union_normalize.Rd0000644000176200001440000000302014530411473016450 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-cell-union.R \name{s2_cell_union_normalize} \alias{s2_cell_union_normalize} \alias{s2_cell_union_contains} \alias{s2_cell_union_intersects} \alias{s2_cell_union_intersection} \alias{s2_cell_union_union} \alias{s2_cell_union_difference} \alias{s2_covering_cell_ids} \alias{s2_covering_cell_ids_agg} \title{S2 cell union operators} \usage{ s2_cell_union_normalize(x) s2_cell_union_contains(x, y) s2_cell_union_intersects(x, y) s2_cell_union_intersection(x, y) s2_cell_union_union(x, y) s2_cell_union_difference(x, y) s2_covering_cell_ids( x, min_level = 0, max_level = 30, max_cells = 8, buffer = 0, interior = FALSE, radius = s2_earth_radius_meters() ) s2_covering_cell_ids_agg( x, min_level = 0, max_level = 30, max_cells = 8, buffer = 0, interior = FALSE, radius = s2_earth_radius_meters(), na.rm = FALSE ) } \arguments{ \item{x, y}{An \link[=as_s2_geography]{s2_geography} or \code{\link[=s2_cell_union]{s2_cell_union()}}.} \item{min_level, max_level}{The minimum and maximum levels to constrain the covering.} \item{max_cells}{The maximum number of cells in the covering. Defaults to 8.} \item{buffer}{A distance to buffer outside the geography} \item{interior}{Use \code{TRUE} to force the covering inside the geography.} \item{radius}{The radius to use (e.g., \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}})} \item{na.rm}{Remove NAs prior to computing aggregate?} } \description{ S2 cell union operators } s2/man/s2_cell.Rd0000644000176200001440000000452314530411473013171 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-cell.R \name{s2_cell} \alias{s2_cell} \alias{s2_cell_sentinel} \alias{s2_cell_invalid} \alias{as_s2_cell} \alias{as_s2_cell.s2_cell} \alias{as_s2_cell.character} \alias{as_s2_cell.s2_geography} \alias{as_s2_cell.wk_xy} \alias{as_s2_cell.integer64} \alias{new_s2_cell} \title{Create S2 Cell vectors} \usage{ s2_cell(x = character()) s2_cell_sentinel() s2_cell_invalid() as_s2_cell(x, ...) \method{as_s2_cell}{s2_cell}(x, ...) \method{as_s2_cell}{character}(x, ...) \method{as_s2_cell}{s2_geography}(x, ...) \method{as_s2_cell}{wk_xy}(x, ...) \method{as_s2_cell}{integer64}(x, ...) new_s2_cell(x) } \arguments{ \item{x}{The canonical S2 cell identifier as a character vector.} \item{...}{Passed to methods} } \value{ An object of class s2_cell } \description{ The S2 cell indexing system forms the basis for spatial indexing in the S2 library. On their own, S2 cells can represent points or areas. As a union, a vector of S2 cells can approximate a line or polygon. These functions allow direct access to the S2 cell indexing system and are designed to have minimal overhead such that looping and recursion have acceptable performance when used within R code. } \details{ Under the hood, S2 cell vectors are represented in R as vectors of type \code{\link[=double]{double()}}. This works because S2 cell identifiers are 64 bits wide, as are \code{double}s on all systems where R runs (The same trick is used by the bit64 package to represent signed 64-bit integers). As a happy accident, \code{NA_real_} is not a valid or meaningful cell identifier, so missing value support in the way R users might expect is preserved. It is worth noting that the underlying value of \code{s2_cell_sentinel()} would normally be considered \code{NA}; however, as it is meaningful and useful when programming with S2 cells, custom \code{is.na()} and comparison methods are implemented such that \code{s2_cell_sentinel()} is greater than all valid S2 cells and not considered missing. Users can and should implement compiled code that uses the underlying bytes of the vector, ensuring that the class of any returned object that should be interpreted in this way is constructed with \code{new_s2_cell()}. } \examples{ s2_cell("4b59a0cd83b5de49") as_s2_cell(s2_lnglat(-64, 45)) as_s2_cell(s2_data_cities("Ottawa")) } s2/man/as_s2_geography.Rd0000644000176200001440000000500014530411473014711 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-geography.R \name{as_s2_geography} \alias{as_s2_geography} \alias{s2_geography} \alias{as_s2_geography.s2_geography} \alias{as_s2_geography.wk_xy} \alias{as_s2_geography.wk_wkb} \alias{as_s2_geography.WKB} \alias{as_s2_geography.blob} \alias{as_s2_geography.wk_wkt} \alias{as_s2_geography.character} \alias{as_s2_geography.logical} \alias{as_wkb.s2_geography} \alias{as_wkt.s2_geography} \title{Create an S2 Geography Vector} \usage{ as_s2_geography(x, ...) s2_geography() \method{as_s2_geography}{s2_geography}(x, ...) \method{as_s2_geography}{wk_xy}(x, ...) \method{as_s2_geography}{wk_wkb}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{WKB}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{blob}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{wk_wkt}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{character}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{logical}(x, ...) \method{as_wkb}{s2_geography}(x, ...) \method{as_wkt}{s2_geography}(x, ...) } \arguments{ \item{x}{An object that can be converted to an s2_geography vector} \item{...}{Unused} \item{oriented}{TRUE if polygon ring directions are known to be correct (i.e., exterior rings are defined counter clockwise and interior rings are defined clockwise).} \item{check}{Use \code{check = FALSE} to skip error on invalid geometries} } \value{ An object with class s2_geography } \description{ Geography vectors are arrays of points, lines, polygons, and/or collections of these. Geography vectors assume coordinates are longitude and latitude on a perfect sphere. } \details{ The coercion function \code{\link[=as_s2_geography]{as_s2_geography()}} is used to wrap the input of most functions in the s2 package so that you can use other objects with an unambiguious interpretation as a geography vector. Geography vectors have a minimal \link[vctrs:vctrs-package]{vctrs} implementation, so you can use these objects in tibble, dplyr, and other packages that use the vctrs framework. } \seealso{ \code{\link[=s2_geog_from_wkb]{s2_geog_from_wkb()}}, \code{\link[=s2_geog_from_text]{s2_geog_from_text()}}, \code{\link[=s2_geog_point]{s2_geog_point()}}, \code{\link[=s2_make_line]{s2_make_line()}}, \code{\link[=s2_make_polygon]{s2_make_polygon()}} for other ways to create geography vectors, and \code{\link[=s2_as_binary]{s2_as_binary()}} and \code{\link[=s2_as_text]{s2_as_text()}} for other ways to export them. } s2/man/s2_options.Rd0000644000176200001440000000776114530411473013754 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-options.R \name{s2_options} \alias{s2_options} \alias{s2_snap_identity} \alias{s2_snap_level} \alias{s2_snap_precision} \alias{s2_snap_distance} \title{Geography Operation Options} \usage{ s2_options( model = NULL, snap = s2_snap_identity(), snap_radius = -1, duplicate_edges = FALSE, edge_type = "directed", validate = FALSE, polyline_type = "path", polyline_sibling_pairs = "keep", simplify_edge_chains = FALSE, split_crossing_edges = FALSE, idempotent = FALSE, dimensions = c("point", "polyline", "polygon") ) s2_snap_identity() s2_snap_level(level) s2_snap_precision(precision) s2_snap_distance(distance) } \arguments{ \item{model}{One of 'open', 'semi-open' (default for polygons), or 'closed' (default for polylines). See section 'Model'} \item{snap}{Use \code{s2_snap_identity()}, \code{s2_snap_distance()}, \code{s2_snap_level()}, or \code{s2_snap_precision()} to specify how or if coordinate rounding should occur.} \item{snap_radius}{As opposed to the snap function, which specifies the maximum distance a vertex should move, the snap radius (in radians) sets the minimum distance between vertices of the output that don't cause vertices to move more than the distance specified by the snap function. This can be used to simplify the result of a boolean operation. Use -1 to specify that any minimum distance is acceptable.} \item{duplicate_edges}{Use \code{TRUE} to keep duplicate edges (e.g., duplicate points).} \item{edge_type}{One of 'directed' (default) or 'undirected'.} \item{validate}{Use \code{TRUE} to validate the result from the builder.} \item{polyline_type}{One of 'path' (default) or 'walk'. If 'walk', polylines that backtrack are preserved.} \item{polyline_sibling_pairs}{One of 'discard' (default) or 'keep'.} \item{simplify_edge_chains}{Use \code{TRUE} to remove vertices that are within \code{snap_radius} of the original vertex.} \item{split_crossing_edges}{Use \code{TRUE} to split crossing polyline edges when creating geometries.} \item{idempotent}{Use \code{FALSE} to apply snap even if snapping is not necessary to satisfy vertex constraints.} \item{dimensions}{A combination of 'point', 'polyline', and/or 'polygon' that can used to constrain the output of \code{\link[=s2_rebuild]{s2_rebuild()}} or a boolean operation.} \item{level}{A value from 0 to 30 corresponding to the cell level at which snapping should occur.} \item{precision}{A number by which coordinates should be multiplied before being rounded. Rounded to the nearest exponent of 10.} \item{distance}{A distance (in radians) denoting the maximum distance a vertex should move in the snapping process.} } \description{ These functions specify defaults for options used to perform operations and construct geometries. These are used in predicates (e.g., \code{\link[=s2_intersects]{s2_intersects()}}), and boolean operations (e.g., \code{\link[=s2_intersection]{s2_intersection()}}) to specify the model for containment and how new geometries should be constructed. } \section{Model}{ The geometry model indicates whether or not a geometry includes its boundaries. Boundaries of line geometries are its end points. OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries (\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons do not overlap or two lines do not cross, no point exist that belong to more than one of the geometries. (This latter form, half-closed, is not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) and covers/covered_by (closed) correspond to the SFA standard specification of these operators. } \examples{ # use s2_options() to specify containment models, snap level # layer creation options, and builder options s2_options(model = "closed", snap = s2_snap_level(30)) } s2/man/s2_data_tbl_countries.Rd0000644000176200001440000000261314530411473016115 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{s2_data_tbl_countries} \alias{s2_data_tbl_countries} \alias{s2_data_tbl_timezones} \alias{s2_data_tbl_cities} \alias{s2_data_countries} \alias{s2_data_timezones} \alias{s2_data_cities} \title{Low-resolution world boundaries, timezones, and cities} \format{ A data.frame with columns \code{name} (character), and \code{geometry} (wk_wkb) An object of class \code{data.frame} with 120 rows and 2 columns. An object of class \code{data.frame} with 243 rows and 3 columns. } \source{ \href{https://www.naturalearthdata.com/}{Natural Earth Data} } \usage{ s2_data_tbl_countries s2_data_tbl_timezones s2_data_tbl_cities s2_data_countries(name = NULL) s2_data_timezones(utc_offset_min = NULL, utc_offset_max = utc_offset_min) s2_data_cities(name = NULL) } \arguments{ \item{name}{The name of a country, continent, city, or \code{NULL} for all features.} \item{utc_offset_min, utc_offset_max}{Minimum and/or maximum timezone offsets.} } \description{ Well-known binary versions of the \href{https://www.naturalearthdata.com/}{Natural Earth} low-resolution world boundaries and timezone boundaries. } \examples{ head(s2_data_countries()) s2_data_countries("Germany") s2_data_countries("Europe") head(s2_data_timezones()) s2_data_timezones(-4) head(s2_data_cities()) s2_data_cities("Cairo") } \keyword{datasets} s2/man/s2_closest_feature.Rd0000644000176200001440000001067114530411473015442 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-matrix.R \name{s2_closest_feature} \alias{s2_closest_feature} \alias{s2_closest_edges} \alias{s2_farthest_feature} \alias{s2_distance_matrix} \alias{s2_max_distance_matrix} \alias{s2_contains_matrix} \alias{s2_within_matrix} \alias{s2_covers_matrix} \alias{s2_covered_by_matrix} \alias{s2_intersects_matrix} \alias{s2_disjoint_matrix} \alias{s2_equals_matrix} \alias{s2_touches_matrix} \alias{s2_dwithin_matrix} \alias{s2_may_intersect_matrix} \title{Matrix Functions} \usage{ s2_closest_feature(x, y) s2_closest_edges( x, y, k, min_distance = -1, max_distance = Inf, radius = s2_earth_radius_meters() ) s2_farthest_feature(x, y) s2_distance_matrix(x, y, radius = s2_earth_radius_meters()) s2_max_distance_matrix(x, y, radius = s2_earth_radius_meters()) s2_contains_matrix(x, y, options = s2_options(model = "open")) s2_within_matrix(x, y, options = s2_options(model = "open")) s2_covers_matrix(x, y, options = s2_options(model = "closed")) s2_covered_by_matrix(x, y, options = s2_options(model = "closed")) s2_intersects_matrix(x, y, options = s2_options()) s2_disjoint_matrix(x, y, options = s2_options()) s2_equals_matrix(x, y, options = s2_options()) s2_touches_matrix(x, y, options = s2_options()) s2_dwithin_matrix(x, y, distance, radius = s2_earth_radius_meters()) s2_may_intersect_matrix(x, y, max_edges_per_cell = 50, max_feature_cells = 4) } \arguments{ \item{x, y}{Geography vectors, coerced using \code{\link[=as_s2_geography]{as_s2_geography()}}. \code{x} is considered the source, where as \code{y} is considered the target.} \item{k}{The number of closest edges to consider when searching. Note that in S2 a point is also considered an edge.} \item{min_distance}{The minimum distance to consider when searching for edges. This filter is applied after the search is complete (i.e., may cause fewer than \code{k} values to be returned).} \item{max_distance}{The maximum distance to consider when searching for edges. This filter is applied before the search.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} \item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline model to use and the snap level.} \item{distance}{A distance on the surface of the earth in the same units as \code{radius}.} \item{max_edges_per_cell}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}}, this values controls the nature of the index on \code{y}, with higher values leading to coarser index. Values should be between 10 and 50; the default of 50 is adequate for most use cases, but for specialized operations users may wish to use a lower value to increase performance.} \item{max_feature_cells}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}}, this value controls the approximation of \code{x} used to identify potential intersections on \code{y}. The default value of 4 gives the best performance for most operations, but for specialized operations users may wish to use a higher value to increase performance.} } \value{ A vector of length \code{x}. } \description{ These functions are similar to accessors and predicates, but instead of recycling \code{x} and \code{y} to a common length and returning a vector of that length, these functions return a vector of length \code{x} with each element \code{i} containing information about how the entire vector \code{y} relates to the feature at \code{x[i]}. } \examples{ city_names <- c("Vatican City", "San Marino", "Luxembourg") cities <- s2_data_cities(city_names) country_names <- s2_data_tbl_countries$name countries <- s2_data_countries() # closest feature returns y indices of the closest feature # for each feature in x country_names[s2_closest_feature(cities, countries)] # farthest feature returns y indices of the farthest feature # for each feature in x country_names[s2_farthest_feature(cities, countries)] # use s2_closest_edges() to find the k-nearest neighbours nearest <- s2_closest_edges(cities, cities, k = 2, min_distance = 0) city_names city_names[unlist(nearest)] # predicate matrices country_names[s2_intersects_matrix(cities, countries)[[1]]] # distance matrices s2_distance_matrix(cities, cities) s2_max_distance_matrix(cities, countries[1:4]) } \seealso{ See pairwise predicate functions (e.g., \code{\link[=s2_intersects]{s2_intersects()}}). } s2/man/s2_boundary.Rd0000644000176200001440000001623714530411473014102 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-transformers.R \name{s2_boundary} \alias{s2_boundary} \alias{s2_centroid} \alias{s2_closest_point} \alias{s2_minimum_clearance_line_between} \alias{s2_difference} \alias{s2_sym_difference} \alias{s2_intersection} \alias{s2_union} \alias{s2_snap_to_grid} \alias{s2_simplify} \alias{s2_rebuild} \alias{s2_buffer_cells} \alias{s2_convex_hull} \alias{s2_centroid_agg} \alias{s2_coverage_union_agg} \alias{s2_rebuild_agg} \alias{s2_union_agg} \alias{s2_convex_hull_agg} \alias{s2_point_on_surface} \title{S2 Geography Transformations} \usage{ s2_boundary(x) s2_centroid(x) s2_closest_point(x, y) s2_minimum_clearance_line_between(x, y) s2_difference(x, y, options = s2_options()) s2_sym_difference(x, y, options = s2_options()) s2_intersection(x, y, options = s2_options()) s2_union(x, y = NULL, options = s2_options()) s2_snap_to_grid(x, grid_size) s2_simplify(x, tolerance, radius = s2_earth_radius_meters()) s2_rebuild(x, options = s2_options()) s2_buffer_cells( x, distance, max_cells = 1000, min_level = -1, radius = s2_earth_radius_meters() ) s2_convex_hull(x) s2_centroid_agg(x, na.rm = FALSE) s2_coverage_union_agg(x, options = s2_options(), na.rm = FALSE) s2_rebuild_agg(x, options = s2_options(), na.rm = FALSE) s2_union_agg(x, options = s2_options(), na.rm = FALSE) s2_convex_hull_agg(x, na.rm = FALSE) s2_point_on_surface(x, na.rm = FALSE) } \arguments{ \item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline model to use and the snap level.} \item{grid_size}{The grid size to which coordinates should be snapped; will be rounded to the nearest power of 10.} \item{tolerance}{The minimum distance between vertexes to use when simplifying a geography.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} \item{distance}{The distance to buffer, in units of \code{radius}.} \item{max_cells}{The maximum number of cells to approximate a buffer.} \item{min_level}{The minimum cell level used to approximate a buffer (1 - 30). Setting this value too high will result in unnecessarily large geographies, but may help improve buffers along long, narrow regions.} \item{na.rm}{For aggregate calculations use \code{na.rm = TRUE} to drop missing values.} } \description{ These functions operate on one or more geography vectors and return a geography vector. } \section{Model}{ The geometry model indicates whether or not a geometry includes its boundaries. Boundaries of line geometries are its end points. OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries (\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons do not overlap or two lines do not cross, no point exist that belong to more than one of the geometries. (This latter form, half-closed, is not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) and covers/covered_by (closed) correspond to the SFA standard specification of these operators. } \examples{ # returns the boundary: # empty for point, endpoints of a linestring, # perimeter of a polygon s2_boundary("POINT (-64 45)") s2_boundary("LINESTRING (0 0, 10 0)") s2_boundary("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") # returns the area-weighted centroid, element-wise s2_centroid("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") s2_centroid("LINESTRING (0 0, 10 0)") # s2_point_on_surface guarantees a point on surface # Note: this is not the same as st_point_on_surface s2_centroid("POLYGON ((0 0, 10 0, 1 1, 0 10, 0 0))") s2_point_on_surface("POLYGON ((0 0, 10 0, 1 1, 0 10, 0 0))") # returns the unweighted centroid of the entire input s2_centroid_agg(c("POINT (0 0)", "POINT (10 0)")) # returns the closest point on x to y s2_closest_point( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT (0 90)" # north pole! ) # returns the shortest possible line between x and y s2_minimum_clearance_line_between( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT (0 90)" # north pole! ) # binary operations: difference, symmetric difference, intersection and union s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) # s2_convex_hull_agg builds the convex hull of a list of geometries s2_convex_hull_agg( c( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ) ) # use s2_union_agg() to aggregate geographies in a vector s2_coverage_union_agg( c( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ), # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) # snap to grid rounds coordinates to a specified grid size s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2) } \seealso{ BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_boundary}{ST_BOUNDARY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_centroid}{ST_CENTROID} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_closestpoint}{ST_CLOSESTPOINT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_difference}{ST_DIFFERENCE} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersection}{ST_INTERSECTION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union}{ST_UNION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_snaptogrid}{ST_SNAPTOGRID} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_simplify}{ST_SIMPLIFY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union_agg}{ST_UNION_AGG} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#s2_centroid_agg}{ST_CENTROID_AGG} } } s2/man/s2_plot.Rd0000644000176200001440000000256314530411473013232 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/plot.R \name{s2_plot} \alias{s2_plot} \title{Plot S2 Geographies} \usage{ s2_plot( x, ..., asp = 1, xlab = "", ylab = "", rule = "evenodd", add = FALSE, plot_hemisphere = FALSE, simplify = TRUE, centre = NULL ) } \arguments{ \item{x}{A \code{\link[wk:wkb]{wkb()}} or \code{\link[wk:wkt]{wkt()}}} \item{...}{Passed to plotting functions for features: \code{\link[graphics:points]{graphics::points()}} for point and multipoint geometries, \code{\link[graphics:lines]{graphics::lines()}} for linestring and multilinestring geometries, and \code{\link[graphics:polypath]{graphics::polypath()}} for polygon and multipolygon geometries.} \item{asp, xlab, ylab}{Passed to \code{\link[graphics:plot.default]{graphics::plot()}}} \item{rule}{The rule to use for filling polygons (see \code{\link[graphics:polypath]{graphics::polypath()}})} \item{add}{Should a new plot be created, or should \code{handleable} be added to the existing plot?} \item{plot_hemisphere}{Plot the outline of the earth} \item{simplify}{Use \code{FALSE} to skip the simplification step} \item{centre}{The longitude/latitude point of the centre of the orthographic projection} } \value{ The input, invisibly } \description{ Plot S2 Geographies } \examples{ s2_plot(s2_data_countries()) s2_plot(s2_data_cities(), add = TRUE) } s2/man/s2_contains.Rd0000644000176200001440000001243214530411473014066 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-predicates.R \name{s2_contains} \alias{s2_contains} \alias{s2_within} \alias{s2_covered_by} \alias{s2_covers} \alias{s2_disjoint} \alias{s2_intersects} \alias{s2_equals} \alias{s2_intersects_box} \alias{s2_touches} \alias{s2_dwithin} \alias{s2_prepared_dwithin} \title{S2 Geography Predicates} \usage{ s2_contains(x, y, options = s2_options(model = "open")) s2_within(x, y, options = s2_options(model = "open")) s2_covered_by(x, y, options = s2_options(model = "closed")) s2_covers(x, y, options = s2_options(model = "closed")) s2_disjoint(x, y, options = s2_options()) s2_intersects(x, y, options = s2_options()) s2_equals(x, y, options = s2_options()) s2_intersects_box( x, lng1, lat1, lng2, lat2, detail = 1000, options = s2_options() ) s2_touches(x, y, options = s2_options()) s2_dwithin(x, y, distance, radius = s2_earth_radius_meters()) s2_prepared_dwithin(x, y, distance, radius = s2_earth_radius_meters()) } \arguments{ \item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline model to use and the snap level.} \item{lng1, lat1, lng2, lat2}{A latitude/longitude range} \item{detail}{The number of points with which to approximate non-geodesic edges.} \item{distance}{A distance on the surface of the earth in the same units as \code{radius}.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} } \description{ These functions operate two geography vectors (pairwise), and return a logical vector. } \section{Model}{ The geometry model indicates whether or not a geometry includes its boundaries. Boundaries of line geometries are its end points. OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries (\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons do not overlap or two lines do not cross, no point exist that belong to more than one of the geometries. (This latter form, half-closed, is not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) and covers/covered_by (closed) correspond to the SFA standard specification of these operators. } \examples{ s2_contains( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_within( c("POINT (5 5)", "POINT (-1 1)"), "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))" ) s2_covered_by( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_covers( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_disjoint( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_intersects( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_equals( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))", "POLYGON ((-1 -1, 10 0, 10 10, 0 10, -1 -1))" ) ) s2_intersects( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_intersects_box( c("POINT (5 5)", "POINT (-1 1)"), 0, 0, 10, 10 ) s2_touches( "POLYGON ((0 0, 0 1, 1 1, 0 0))", c("POINT (0 0)", "POINT (0.5 0.75)", "POINT (0 0.5)") ) s2_dwithin( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)"), 0 # distance in meters ) s2_dwithin( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)"), 1e6 # distance in meters ) } \seealso{ Matrix versions of these predicates (e.g., \code{\link[=s2_intersects_matrix]{s2_intersects_matrix()}}). BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_contains}{ST_CONTAINS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_coveredby}{ST_COVEREDBY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_covers}{ST_COVERS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_disjoint}{ST_DISJOINT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_equals}{ST_EQUALS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersects}{ST_INTERSECTS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersectsbox}{ST_INTERSECTSBOX} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_touches}{ST_TOUCHES} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_within}{ST_WITHIN} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dwithin}{ST_DWITHIN} } } s2/man/s2_geog_point.Rd0000644000176200001440000001176414530411473014411 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-constructors-formatters.R \name{s2_geog_point} \alias{s2_geog_point} \alias{s2_make_line} \alias{s2_make_polygon} \alias{s2_geog_from_text} \alias{s2_geog_from_wkb} \alias{s2_as_text} \alias{s2_as_binary} \alias{s2_tessellate_tol_default} \title{Create and Format Geography Vectors} \usage{ s2_geog_point(longitude, latitude) s2_make_line(longitude, latitude, feature_id = 1L) s2_make_polygon( longitude, latitude, feature_id = 1L, ring_id = 1L, oriented = FALSE, check = TRUE ) s2_geog_from_text( wkt_string, oriented = FALSE, check = TRUE, planar = FALSE, tessellate_tol_m = s2_tessellate_tol_default() ) s2_geog_from_wkb( wkb_bytes, oriented = FALSE, check = TRUE, planar = FALSE, tessellate_tol_m = s2_tessellate_tol_default() ) s2_as_text( x, precision = 16, trim = TRUE, planar = FALSE, tessellate_tol_m = s2_tessellate_tol_default() ) s2_as_binary( x, endian = wk::wk_platform_endian(), planar = FALSE, tessellate_tol_m = s2_tessellate_tol_default() ) s2_tessellate_tol_default() } \arguments{ \item{longitude, latitude}{Vectors of latitude and longitude} \item{feature_id, ring_id}{Vectors for which a change in sequential values indicates a new feature or ring. Use \code{\link[=factor]{factor()}} to convert from a character vector.} \item{oriented}{TRUE if polygon ring directions are known to be correct (i.e., exterior rings are defined counter clockwise and interior rings are defined clockwise).} \item{check}{Use \code{check = FALSE} to skip error on invalid geometries} \item{wkt_string}{Well-known text} \item{planar}{Use \code{TRUE} to force planar edges in import or export.} \item{tessellate_tol_m}{The maximum number of meters to that a point must be moved to satisfy the planar edge constraint.} \item{wkb_bytes}{A \code{list()} of \code{raw()}} \item{x}{An object that can be converted to an s2_geography vector} \item{precision}{The number of significant digits to export when writing well-known text. If \code{trim = FALSE}, the number of digits after the decimal place.} \item{trim}{Should trailing zeroes be included after the decimal place?} \item{endian}{The endian-ness of the well-known binary. See \code{\link[wk:deprecated]{wk::wkb_translate_wkb()}}.} } \description{ These functions create and export \link[=as_s2_geography]{geography vectors}. Unlike the BigQuery geography constructors, these functions do not sanitize invalid or redundant input using \code{\link[=s2_union]{s2_union()}}. Note that when creating polygons using \code{\link[=s2_make_polygon]{s2_make_polygon()}}, rings can be open or closed. } \examples{ # create point geographies using coordinate values: s2_geog_point(-64, 45) # create line geographies using coordinate values: s2_make_line(c(-64, 8), c(45, 71)) # optionally, separate features using feature_id: s2_make_line( c(-64, 8, -27, -27), c(45, 71, 0, 45), feature_id = c(1, 1, 2, 2) ) # create polygon geographies using coordinate values: # (rings can be open or closed) s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)) # optionally, separate rings and/or features using # ring_id and/or feature_id s2_make_polygon( c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45), c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30), feature_id = c(rep(1, 8), rep(2, 3)), ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1) ) # import and export well-known text (geog <- s2_geog_from_text("POINT (-64 45)")) s2_as_text(geog) # import and export well-known binary (geog <- s2_geog_from_wkb(wk::as_wkb("POINT (-64 45)"))) s2_as_binary(geog) # import geometry from planar space s2_geog_from_text( "POLYGON ((0 0, 1 0, 0 1, 0 0))", planar = TRUE, tessellate_tol_m = 1 ) # export geographies into planar space geog <- s2_make_polygon(c(179, -179, 179), c(10, 10, 11)) s2_as_text(geog, planar = TRUE) # polygons containing a pole need an extra step geog <- s2_data_countries("Antarctica") geom <- s2_as_text( s2_intersection(geog, s2_world_plate_carree()), planar = TRUE ) } \seealso{ See \code{\link[=as_s2_geography]{as_s2_geography()}} for other ways to construct geography vectors. BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogpoint}{ST_GEOGPOINT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makeline}{ST_MAKELINE} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makepolygon}{ST_MAKEPOLYGON} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromtext}{ST_GEOGFROMTEXT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromwkb}{ST_GEOGFROMWKB} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_astext}{ST_ASTEXT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_asbinary}{ST_ASBINARY} } } s2/DESCRIPTION0000644000176200001440000000401514540350305012303 0ustar liggesusersPackage: s2 Title: Spherical Geometry Operators Using the S2 Geometry Library Version: 1.1.6 Authors@R: c( person(given = "Dewey", family = "Dunnington", role = c("aut"), email = "dewey@fishandwhistle.net", comment = c(ORCID = "0000-0002-9415-4582")), person(given = "Edzer", family = "Pebesma", role = c("aut", "cre"), email = "edzer.pebesma@uni-muenster.de", comment = c(ORCID = "0000-0001-8049-7069")), person("Ege", "Rubak", email="rubak@math.aau.dk", role = c("aut")), person("Jeroen", "Ooms", , "jeroen.ooms@stat.ucla.edu", role = "ctb", comment = "configure script"), person(family = "Google, Inc.", role = "cph", comment = "Original s2geometry.io source code") ) Description: Provides R bindings for Google's s2 library for geometric calculations on the sphere. High-performance constructors and exporters provide high compatibility with existing spatial packages, transformers construct new geometries from existing geometries, predicates provide a means to select geometries based on spatial relationships, and accessors extract information about geometries. License: Apache License (== 2.0) Encoding: UTF-8 LazyData: true RoxygenNote: 7.2.3 SystemRequirements: OpenSSL >= 1.0.1 LinkingTo: Rcpp, wk Imports: Rcpp, wk (>= 0.6.0) Suggests: bit64, testthat (>= 3.0.0), vctrs URL: https://r-spatial.github.io/s2/, https://github.com/r-spatial/s2, http://s2geometry.io/ BugReports: https://github.com/r-spatial/s2/issues Depends: R (>= 3.0.0) Config/testthat/edition: 3 NeedsCompilation: yes Packaged: 2023-12-19 14:38:19 UTC; edzer Author: Dewey Dunnington [aut] (), Edzer Pebesma [aut, cre] (), Ege Rubak [aut], Jeroen Ooms [ctb] (configure script), Google, Inc. [cph] (Original s2geometry.io source code) Maintainer: Edzer Pebesma Repository: CRAN Date/Publication: 2023-12-19 17:20:05 UTC s2/build/0000755000176200001440000000000014540325333011677 5ustar liggesuserss2/build/partial.rdb0000644000176200001440000000007514540325333014026 0ustar liggesusersb```b`afb`b1 H020piּb C"%!7s2/tests/0000755000176200001440000000000014530411513011735 5ustar liggesuserss2/tests/area.Rout.save0000644000176200001440000000325714530412230014461 0ustar liggesusers R version 4.3.2 (2023-10-31) -- "Eye Holes" Copyright (C) 2023 The R Foundation for Statistical Computing Platform: x86_64-pc-linux-gnu (64-bit) R is free software and comes with ABSOLUTELY NO WARRANTY. You are welcome to redistribute it under certain conditions. Type 'license()' or 'licence()' for distribution details. R is a collaborative project with many contributors. Type 'contributors()' for more information and 'citation()' on how to cite R or R packages in publications. Type 'demo()' for some demos, 'help()' for on-line help, or 'help.start()' for an HTML browser interface to help. Type 'q()' to quit R. > library(s2) > > u = s2_union( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", + s2_options(snap = s2_snap_level(30)) + ) > s2_area(u, radius = 1) [1] 0.05284581 > s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) + + s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) - + s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) [1] 0.04910511 > s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) [1] 0.03038216 > s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) [1] 0.03002974 > s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) [1] 0.01130679 > > df = s2_difference( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", + s2_options(snap = s2_snap_level(30)) + ) > s2_area(df, radius = 1) - + (s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - + s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)) [1] 0.003740703 > > proc.time() user system elapsed 0.436 1.276 0.215 s2/tests/testthat/0000755000176200001440000000000014540350305013577 5ustar liggesuserss2/tests/testthat/test-s2-bounds.R0000644000176200001440000000337614530411473016527 0ustar liggesusers test_that("s2_bounds_cap works", { cap_ant <- s2_data_countries("Antarctica") expect_s3_class(s2_bounds_cap(cap_ant), "data.frame") expect_identical(nrow(s2_bounds_cap(cap_ant)), 1L) expect_true(s2_bounds_cap(cap_ant)$lat < -80) expect_true(s2_bounds_cap(cap_ant)$angle > 20) expect_identical(nrow(s2_bounds_cap(s2_data_countries(c("Antarctica", "Netherlands")))), 2L) expect_true(s2_bounds_cap(s2_data_countries("Netherlands"))$angle < 2) expect_true(s2_bounds_cap(s2_data_countries("Fiji"))$angle < 2) }) test_that("s2_bounds_rect works", { rect_ant <- s2_bounds_rect(s2_data_countries("Antarctica")) expect_s3_class(rect_ant, "data.frame") expect_named(rect_ant, c("lng_lo", "lat_lo", "lng_hi", "lat_hi")) expect_identical(nrow(s2_bounds_rect(s2_data_countries(c("Antarctica", "Netherlands")))), 2L) expect_equal(rect_ant$lng_lo, -180) expect_equal(rect_ant$lng_hi, 180) expect_equal(rect_ant$lat_lo, -90) expect_true(rect_ant$lat_hi < -60) expect_identical(nrow(s2_bounds_rect(s2_data_countries(c("Antarctica", "Netherlands")))), 2L) rect_nl <- s2_bounds_rect(s2_data_countries("Netherlands")) expect_true((rect_nl$lng_hi - rect_nl$lng_lo) < 4) rect_fiji <- s2_bounds_rect(s2_data_countries("Fiji")) expect_true(rect_fiji$lng_hi < rect_fiji$lng_lo) rect_multipoint <- s2_bounds_rect("MULTIPOINT(-179 0,179 1,-180 10)") expect_equal(rect_multipoint$lat_lo, 0) expect_equal(rect_multipoint$lat_hi, 10) expect_equal(rect_multipoint$lng_lo, 179) expect_equal(rect_multipoint$lng_hi, -179) rect_linestring <- s2_bounds_rect("LINESTRING(-179 0,179 1)") expect_equal(rect_linestring$lat_lo, 0) expect_equal(rect_linestring$lat_hi, 1) expect_equal(rect_linestring$lng_lo, 179) expect_equal(rect_linestring$lng_hi, -179) }) s2/tests/testthat/test-vctrs.R0000644000176200001440000000145514530411473016050 0ustar liggesusers test_that("s2_geography is a vctr", { x <- new_s2_geography(list(NULL)) expect_true(vctrs::vec_is(x)) expect_identical(vctrs::vec_data(x), list(NULL)) expect_identical(vctrs::vec_restore(list(NULL), x), x) expect_identical(vctrs::vec_ptype_abbr(x), class(x)[1]) }) test_that("s2_cell is a vctr", { x <- new_s2_cell(NA_real_) expect_true(vctrs::vec_is(x)) expect_identical(vctrs::vec_data(x), NA_real_) expect_identical(vctrs::vec_restore(NA_real_, x), x) expect_identical(vctrs::vec_ptype_abbr(x), "s2cell") }) test_that("s2_cell_union is a vctr", { x <- new_s2_cell_union(list(NULL)) expect_true(vctrs::vec_is(x)) expect_identical(vctrs::vec_data(x), list(NULL)) expect_identical(vctrs::vec_restore(list(NULL), x), x) expect_identical(vctrs::vec_ptype_abbr(x), "s2cellunion") }) s2/tests/testthat/test-s2-accessors.R0000644000176200001440000001712114530411473017213 0ustar liggesusers test_that("s2_is_collection works", { expect_identical(s2_is_collection(NA_character_), NA) expect_false(s2_is_collection("POINT (-64 45)")) expect_false(s2_is_collection("POINT EMPTY")) expect_true(s2_is_collection("MULTIPOINT ((0 0), (1 1))")) expect_false(s2_is_collection("LINESTRING (0 0, 1 1)")) expect_true(s2_is_collection("MULTILINESTRING ((0 0, 1 1), (2 2, 3 3))")) expect_false(s2_is_collection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")) expect_true( s2_is_collection("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)) )") ) }) test_that("s2_is_valid() works", { expect_identical( s2_is_valid( c( # valid "POINT (0 1)", "LINESTRING (0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "GEOMETRYCOLLECTION (POINT (0 1))", # (for the purposes of S2, linestrings that cross aren't considered invalid # in the sense that they won't cause errors when you try to pass them to # a boolean operation or the builder) # invalid "LINESTRING (0 0, 0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0))", "GEOMETRYCOLLECTION (POINT (0 1), POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", NA ) ), c(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, NA) ) }) test_that("s2_is_valid_detail() works", { expect_identical( s2_is_valid_detail( c( # valid "POINT (0 1)", "LINESTRING (0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "GEOMETRYCOLLECTION (POINT (0 1))", # (for the purposes of S2, linestrings that cross aren't considered invalid # in the sense that they won't cause errors when you try to pass them to # a boolean operation or the builder) # invalid "LINESTRING (0 0, 0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0))", "GEOMETRYCOLLECTION (POINT (0 1), POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", NA ) ), data.frame( is_valid = c(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, NA), reason = c( NA, NA, NA, NA, "Vertices 0 and 1 are identical", "Loop 0: Edge 3 is degenerate (duplicate vertex)", "Loop 0: Edge 3 is degenerate (duplicate vertex)", NA ), stringsAsFactors = FALSE ) ) }) test_that("s2_dimension works", { expect_identical(s2_dimension(NA_character_), NA_integer_) expect_identical(s2_dimension("POINT EMPTY"), 0L) expect_identical(s2_dimension("LINESTRING EMPTY"), 1L) expect_identical(s2_dimension("POLYGON EMPTY"), 2L) }) test_that("s2_num_points works", { expect_identical(s2_num_points(NA_character_), NA_integer_) expect_identical(s2_num_points("POINT (-64 45)"), 1L) expect_identical(s2_num_points("POINT EMPTY"), 0L) expect_identical(s2_num_points("LINESTRING (0 0, 1 1)"), 2L) expect_identical(s2_num_points("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"), 4L) expect_identical( s2_num_points("GEOMETRYCOLLECTION (POINT (0 1), LINESTRING (0 0, 1 1))"), 3L ) }) test_that("s2_is_empty works", { expect_identical(s2_is_empty(NA_character_), NA) expect_false(s2_is_empty("POINT (-64 45)")) expect_true(s2_is_empty("POINT EMPTY")) expect_false(s2_is_empty("LINESTRING (0 0, 1 1)")) expect_true(s2_is_empty("LINESTRING EMPTY")) expect_false(s2_is_empty("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")) expect_true(s2_is_empty("POLYGON EMPTY")) }) test_that("s2_area works", { expect_identical(s2_area(NA_character_), NA_real_) expect_identical(s2_area("POINT (-64 45)"), 0) expect_identical(s2_area("POINT EMPTY"), 0) expect_identical(s2_area("LINESTRING (0 0, 1 1)"), 0) expect_identical(s2_area("POLYGON EMPTY"), 0) expect_identical(s2_area("POLYGON ((0 0, 90 0, 0 90, 0 0))", radius = 1), 4 * pi / 8) # make sure the radius is squared! expect_true( abs(s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 180 / pi) - 100) < 0.27 ) expect_identical( s2_area("POLYGON ((0 0, 90 0, 0 90, 0 0))"), s2_area("GEOMETRYCOLLECTION(POLYGON ((0 0, 90 0, 0 90, 0 0)))") ) }) test_that("s2_length works", { expect_identical(s2_length(NA_character_), NA_real_) expect_identical(s2_length("POINT (-64 45)"), 0) expect_identical(s2_length("POINT EMPTY"), 0) expect_identical(s2_length("LINESTRING EMPTY"), 0) expect_identical(s2_length("LINESTRING (0 0, 0 1)", radius = 180 / pi), 1) expect_identical(s2_length("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"), 0) }) test_that("s2_perimeter works", { expect_identical(s2_perimeter(NA_character_), NA_real_) expect_identical(s2_perimeter("POINT (-64 45)"), 0) expect_identical(s2_perimeter("POINT EMPTY"), 0) expect_identical(s2_perimeter("LINESTRING EMPTY"), 0) expect_identical(s2_perimeter("LINESTRING (0 0, 1 1)"), 0) # there is some error here because of the way this is calculated involves # some round-tripping through lat/lon expect_true( abs(s2_perimeter("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 180 / pi) - 40) < 0.155 ) }) test_that("s2_x and s2_y works", { expect_identical(s2_x(NA_character_), NA_real_) expect_identical(s2_y(NA_character_), NA_real_) expect_equal(s2_x("POINT (-64 45)"), -64) expect_equal(s2_y("POINT (-64 45)"), 45) expect_identical(s2_x("POINT EMPTY"), NaN) expect_identical(s2_y("POINT EMPTY"), NaN) expect_error(s2_x("LINESTRING EMPTY"), "Can't compute") expect_error(s2_y("LINESTRING EMPTY"), "Can't compute") expect_error(s2_x("POLYGON EMPTY"), "Can't compute") expect_error(s2_y("POLYGON EMPTY"), "Can't compute") }) test_that("s2_project() and s2_project_normalized() work", { expect_equal( s2_project( "LINESTRING (0 0, 0 90)", c("POINT (0 0)", "POINT (0 22.5)", "POINT (0 67.5)", "POINT (0 90)", NA), radius = 1 ), c(0, 0.25, 0.75, 1, NA_real_) * pi / 2 ) expect_equal( s2_project_normalized( "LINESTRING (0 0, 0 90)", c("POINT (0 0)", "POINT (0 22.5)", "POINT (0 67.5)", "POINT (0 90)", "POINT EMPTY", NA) ), c(0, 0.25, 0.75, 1, NaN, NA_real_) ) expect_identical( s2_project_normalized("POINT (0 1)", "POINT (0 1)"), NaN ) expect_identical( s2_project_normalized("LINESTRING (0 1, 1 1)", "LINESTRING (0 1, 1 1)"), NaN ) expect_identical( s2_project_normalized("LINESTRING (0 1, 1 1)", "MULTIPOINT (0 1, 1 1)"), NaN ) }) test_that("s2_distance works", { expect_equal( s2_distance("POINT (0 0)", "POINT (90 0)", radius = 180 / pi), 90 ) expect_equal( s2_distance("POINT (0 0)", "LINESTRING (90 0, 91 0)", radius = 180 / pi), 90 ) expect_equal( s2_distance("POINT (0 0)", "POLYGON ((90 0, 91 0, 92 1, 90 0))", radius = 180 / pi), 90 ) expect_identical(s2_distance("POINT (0 0)", NA_character_), NA_real_) expect_identical(s2_distance(NA_character_, "POINT (0 0)"), NA_real_) expect_identical(s2_distance("POINT (0 0)", "POINT EMPTY"), NA_real_) expect_identical(s2_distance("POINT EMPTY", "POINT (0 0)"), NA_real_) }) test_that("s2_max_distance works", { expect_equal( s2_max_distance("POINT (0 0)", "POINT (90 0)", radius = 180 / pi), 90 ) expect_equal( s2_max_distance("POINT (0 0)", "LINESTRING (90 0, 91 0)", radius = 180 / pi), 91 ) expect_equal( s2_max_distance("POINT (0 0)", "POLYGON ((90 0, 91 0, 89 1, 90 0))", radius = 180 / pi), 91 ) expect_identical(s2_max_distance("POINT (0 0)", NA_character_), NA_real_) expect_identical(s2_max_distance(NA_character_, "POINT (0 0)"), NA_real_) expect_identical(s2_max_distance("POINT (0 0)", "POINT EMPTY"), NA_real_) expect_identical(s2_max_distance("POINT EMPTY", "POINT (0 0)"), NA_real_) }) s2/tests/testthat/test-s2-point.R0000644000176200001440000000131214530411473016352 0ustar liggesusers test_that("s2_point objects can be created from and converted back to R objects", { # in expect_s3_class(s2_point(1, 2, 3), "wk_xyz") expect_length(s2_point(1, 2, 3), 1) expect_s3_class(as_s2_point(matrix(c(1, 2, 3), ncol = 3)), "wk_xyz") point <- s2_point(1, 2, 3) expect_identical(as_s2_point(point), point) expect_identical( as_s2_point(s2_lnglat(0, 0)), s2_point(1, 0, 0) ) }) test_that("s2_point can be imported from s2_geography", { expect_equal( as_s2_point(as_s2_geography("POINT (-64 45)")), as_s2_point(as_s2_lnglat(as_s2_geography("POINT (-64 45)"))) ) }) test_that("s2_point objects can be printed", { expect_output(print(s2_point(1, 2, 3)), "s2_point_crs") }) s2/tests/testthat/test-s2-geography.R0000644000176200001440000002053014530411473017211 0ustar liggesusers test_that("s2_geography class works", { expect_s3_class(s2_geography(), "s2_geography") geog <- new_s2_geography(list(NULL)) expect_output(print(geog), "s2_geography") expect_output(str(geog), "s2_geography") expect_identical(as_s2_geography(geog), geog) expect_identical( is.na(as_s2_geography(c("POINT (0 1)", NA_character_))), c(FALSE, TRUE) ) # subset assignment geog2 <- geog geog2[1] <- geog expect_identical(geog2, geog) geog2 <- geog geog2[[1]] <- geog expect_identical(geog2, geog) }) test_that("s2_geography vectors can be put in a data frame", { expect_identical( data.frame(geog = s2_geography()), new_data_frame(list(geog = s2_geography())) ) }) test_that("s2_geography vectors can't have other types of objects concatenated or asssigned", { geog <- new_s2_geography(list(NULL)) expect_s3_class(c(geog, geog), "s2_geography") expect_error(c(geog, wk::wkt()), "Can't combine 'wk_vctr' objects") expect_error(geog[1] <- factor(1), "no applicable method") expect_error(geog[[1]] <- factor(1), "no applicable method") }) test_that("s2_geography vectors can be created from s2_lnglat and s2_point", { expect_wkt_equal(as_s2_geography(s2_lnglat(-64, 45)), "POINT (-64 45)") expect_wkt_equal(as_s2_geography(as_s2_point(s2_lnglat(-64, 45))), "POINT (-64 45)") }) test_that("s2_geography vectors can be created from WKB and WKT", { wkb_point <- wk::as_wkb(wk::wkt("POINT (-64 45)", geodesic = TRUE)) expect_output(print(as_s2_geography(wkb_point)), "POINT \\(-64 45\\)") expect_error( as_s2_geography(wk::as_wkb("LINESTRING (0 0, 1 1)")), "Cartesian wkb\\(\\)" ) # empty, null, and point features are OK expect_identical(as_s2_geography(wk::wkb()), as_s2_geography(character())) expect_identical(as_s2_geography(wk::wkb(list(NULL))), as_s2_geography(NA_character_)) expect_silent(as_s2_geography(wk::as_wkb("POINT (0 1)"))) expect_silent(as_s2_geography(wk::as_wkb("MULTIPOINT (0 1)"))) wkt_point <- wk::as_wkt(wk::wkt("POINT (-64 45)", geodesic = TRUE)) expect_output(print(as_s2_geography(wkt_point)), "POINT \\(-64 45\\)") expect_error( as_s2_geography(wk::wkt("LINESTRING (0 0, 1 1)")), "Cartesian wkt\\(\\)" ) # empty, null, and point features are OK expect_identical(as_s2_geography(wk::wkt()), as_s2_geography(character())) expect_identical(as_s2_geography(wk::wkt(NA_character_)), as_s2_geography(NA_character_)) expect_silent(as_s2_geography(wk::wkt("POINT (0 1)"))) expect_silent(as_s2_geography(wk::wkt("MULTIPOINT (0 1)"))) # also test other classes commonly used to signify WKB or WKT expect_output(print(as_s2_geography(structure(wkb_point, class = "WKB")), "POINT \\(-64 45\\)")) expect_output(print(as_s2_geography(structure(wkb_point, class = "blob")), "POINT \\(-64 45\\)")) }) test_that("s2_geography can be exported to WKB/WKT", { expect_wkt_equal( wk::as_wkb(as_s2_geography("POINT (-64 45)")), wk::as_wkb(wk::wkt("POINT (-64 45)", geodesic = TRUE)), precision = 10 ) expect_wkt_equal( wk::as_wkt(as_s2_geography("POINT (-64 45)")), wk::as_wkt(wk::wkt("POINT (-64 45)", geodesic = TRUE)), precision = 10 ) }) test_that("s2_geography vectors can be created from wkt", { expect_output(print(as_s2_geography("POINT (-64 45)")), "POINT \\(-64 45\\)") expect_output(print(as_s2_geography("POINT EMPTY")), "POINT EMPTY") expect_output( print(as_s2_geography("MULTIPOINT ((-64 45), (30 10))")), "MULTIPOINT \\(\\(-64 45\\), \\(30 10\\)\\)" ) expect_output( print(as_s2_geography("LINESTRING (-64 45, 0 0)")), "LINESTRING \\(-64 45, 0 0\\)" ) expect_output( print(as_s2_geography("LINESTRING EMPTY")), "LINESTRING EMPTY" ) expect_output( print(as_s2_geography("MULTILINESTRING ((-64 45, 0 0), (0 1, 2 3))")), "MULTILINESTRING \\(\\(-64 45, 0 0), \\(0 1, 2 3\\)\\)" ) expect_output(print(as_s2_geography("POLYGON EMPTY"), "POLYGON EMPTY")) expect_output( print(as_s2_geography("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")), "POLYGON \\(\\(0 0, 10 0, 10 10" ) expect_output( print(as_s2_geography("MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))")), "POLYGON \\(\\(0 0, 10 0, 10 10" ) expect_output( print( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)) )") ), "MULTIPOLYGON" ) expect_output( print(as_s2_geography("GEOMETRYCOLLECTION (POINT (-64 45))")), "GEOMETRYCOLLECTION \\(POINT \\(-64 45\\)\\)" ) expect_match( s2_as_text( as_s2_geography( "GEOMETRYCOLLECTION ( POINT (30 10), MULTIPOINT (11 12, 12 13), LINESTRING (40 40, 40 41), MULTILINESTRING ((-10 -12, -12 -13), (-15 -15, -16 -16)), POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0)), MULTIPOLYGON (((0 0, -10 0, -10 -10, 0 -10, 0 0))), GEOMETRYCOLLECTION (POINT (-100 0), MULTIPOINT(-101 0, -102 0)) )" ) ), paste0( "GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT.*?LINESTRING.*?MULTILINESTRING.*?", "POLYGON.*?POLYGON.*?GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT" ) ) expect_output(print(as_s2_geography("GEOMETRYCOLLECTION EMPTY")), "GEOMETRYCOLLECTION EMPTY") }) test_that("empty points are empty when imported from WKB", { wkb_empty <- wk::as_wkb("POINT EMPTY") expect_true(s2_is_empty(s2_geog_from_wkb(wkb_empty))) }) test_that("nested ring depths are correctly exported", { # polygon with hole expect_match( s2_as_text( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20) ) )"), precision = 15 ), "\\(20 35, 10 30, 10 10, 30 5, 45 20, 20 35\\), \\(30 20, 20 15, 20 25" ) # polygon with a hole in a hole! expect_match( s2_as_text( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20) ), ((27 21, 21 21, 21 16, 27 21)) )"), precision = 15 ), "\\(\\(27 21, 21 21, 21 16, 27 21\\)\\)\\)" ) }) test_that("polygons with holes are interpreted as such by S2", { expect_true( s2_intersects( "MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20), (27 21, 21 21, 21 16, 27 21) ) )", "POINT (23 19.5)" ) ) expect_false( s2_intersects( "MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20) ) )", "POINT (23 19.5)" ) ) }) test_that("polygon construction works with oriented = TRUE and oriented = FALSE", { polygon_with_bad_hole_nested <- as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) ) )", oriented = FALSE) expect_false(s2_intersects(polygon_with_bad_hole_nested, "POINT (23 19.5)")) expect_error( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) ) )", oriented = TRUE, check = TRUE), "Inconsistent loop orientations" ) expect_silent( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) ) )", oriented = TRUE, check = FALSE) ) }) test_that("Full polygons work", { expect_true(s2_intersects(as_s2_geography(TRUE), "POINT(0 1)")) expect_wkt_equal(s2_difference(as_s2_geography(TRUE), "POINT(0 1)"), "POLYGON ((0 -90, 0 -90))") }) test_that("wk crs and geodesic methods are defined", { geog <- as_s2_geography("POINT (0 0)") expect_identical(wk::wk_crs(geog), wk::wk_crs_longlat()) expect_true(wk::wk_is_geodesic(geog)) expect_identical(wk::wk_set_crs(geog, wk::wk_crs_longlat()), geog) expect_identical(wk::wk_set_geodesic(geog, TRUE), geog) expect_warning( wk::wk_set_crs(geog, "EPSG:32620"), "is not supported" ) expect_error(wk::wk_set_geodesic(geog, FALSE), "Can't set geodesic") }) s2/tests/testthat/test-s2-lnglat.R0000644000176200001440000000165414530411473016513 0ustar liggesusers test_that("s2_lnglat objects can be created from and converted back to R objects", { # in expect_s3_class(s2_lnglat(45, 64), "wk_xy") expect_length(s2_lnglat(45, 64), 1) expect_s3_class(as_s2_lnglat(matrix(c(45, 64), ncol = 2)), "wk_xy") lnglat <- s2_lnglat(45, 64) expect_identical(as_s2_lnglat(lnglat), lnglat) expect_identical( as_s2_lnglat(s2_point(1, 0, 0)), s2_lnglat(0, 0) ) expect_identical( as_s2_lnglat(s2_point(NaN, NaN, NaN)), s2_lnglat(NaN, NaN) ) }) test_that("s2_lnglat can be imported from s2_geography", { expect_equal( as_s2_lnglat(as_s2_geography("POINT (-64 45)")), s2_lnglat(-64, 45) ) }) test_that("s2_lnglat can be imported from wkb", { wkb_point <- wk::as_wkb("POINT (-64 45)") expect_equal( as_s2_lnglat(wkb_point), s2_lnglat(-64, 45) ) }) test_that("s2_lnglat objects can be printed", { expect_output(print(s2_lnglat(-64, 45)), "OGC:CRS84") }) s2/tests/testthat/test-wk-utils.R0000644000176200001440000001666414530411473016476 0ustar liggesusers test_that("wk_handle() for s2_geography works", { for (name in names(s2_data_example_wkt)) { geog <- wk::wk_handle( s2_data_example_wkt[[name]], s2_geography_writer() ) geog2 <- wk::wk_handle( geog, s2_geography_writer(check = TRUE, oriented = TRUE) ) expect_equal(wk::wk_coords(geog), wk::wk_coords(geog2)) } }) test_that("wk_handle() for s2_geography works for s2_point projection", { for (name in names(s2_data_example_wkt)) { geog <- wk::wk_handle( s2_data_example_wkt[[name]], s2_geography_writer() ) geog2 <- wk::wk_handle( geog, s2_geography_writer( check = TRUE, oriented = TRUE, projection = NULL ), s2_projection = NULL ) expect_identical(wk::wk_coords(geog), wk::wk_coords(geog2)) } }) test_that("wk_writer() works for s2_geography()", { expect_s3_class(wk::wk_writer(s2_geography()), "s2_geography_writer") }) test_that("the s2_geography_writer() works for example WKT", { # nc has some rings that get reordered by this operation for (name in setdiff(names(s2_data_example_wkt), "nc")) { geog <- wk::wk_handle( s2_data_example_wkt[[name]], s2_geography_writer() ) expect_equal( wk::wk_coords(as_wkt(geog))[c("x", "y")], wk::wk_coords(s2_data_example_wkt[[name]])[c("x", "y")] ) } }) test_that("wk_handle() works for example WKT", { for (name in names(s2_data_example_wkt)) { geog <- wk::wk_handle( s2_data_example_wkt[[name]], s2_geography_writer() ) expect_wkt_equal( wk_handle(geog, s2_geography_writer()), geog, precision = 14 ) } }) test_that("wk_handle() works for example WKT with tessellation", { for (name in names(s2_data_example_wkt)) { geog <- wk::wk_handle( s2_data_example_wkt[[name]], s2_geography_writer() ) expect_wkt_equal( # use a big but non-infinite number to trigger the tessellator wk_handle(geog, s2_geography_writer(), s2_tessellate_tol = 1e10), geog, precision = 14 ) } }) test_that("the s2_trans_point() and s2_trans_lnglat() work", { lng_lats <- s2_lnglat(-179:179, 45) points <- as_s2_point(lng_lats) expect_identical(as_s2_lnglat(lng_lats), lng_lats) expect_equal( wk::wk_transform(lng_lats, s2_trans_point()), wk::wk_set_crs(points, NULL) ) expect_equal( wk::wk_transform(points, s2_trans_lnglat()), wk::wk_set_crs(lng_lats, NULL) ) }) test_that("s2_geography_writer() with tesselate_tol works", { # using big examples here, so use a tolerance of 100 km (forces # adding at least one point) tol <- 100000 / s2_earth_radius_meters() expect_equal( wk::as_xy( wk::wk_handle( wk::xy(0, 0), s2_geography_writer(tessellate_tol = tol) ) ), wk::xy(0, 0, crs = wk::wk_crs_longlat()) ) expect_identical( wk::wk_handle( wk::wkt("LINESTRING (0 0, 0 45, -60 45)"), s2_geography_writer(tessellate_tol = tol) ) %>% s2_num_points(), 6L ) expect_identical( wk::wk_handle( wk::wkt("POLYGON ((0 0, 0 45, -60 45, 0 0))"), s2_geography_writer(tessellate_tol = tol) ) %>% s2_num_points(), 8L ) }) test_that("s2_geography_writer() with tesselate_tol works with real data", { tol <- 1000 / s2_earth_radius_meters() countries_tes <- wk::wk_handle( s2::s2_data_tbl_countries$geometry, s2_geography_writer(tessellate_tol = tol) ) expect_true( sum(s2_num_points(countries_tes)) > sum(s2_num_points(s2_data_countries())) ) }) test_that("wk_handle + tessellate_tol works", { tol <- 100000 / s2_earth_radius_meters() expect_equal( wk::wk_handle( as_s2_geography(s2_lnglat(0, 0)), wk::xy_writer(), s2_tessellate_tol = tol ), wk::xy(0, 0) ) expect_identical( wk::wk_handle( as_s2_geography("LINESTRING (0 0, 0 45, -60 45)"), s2_geography_writer(), s2_tessellate_tol = tol ) %>% s2_num_points(), 6L ) expect_identical( wk::wk_handle( as_s2_geography("POLYGON ((0 0, 0 45, -60 45, 0 0))"), s2_geography_writer(), s2_tessellate_tol = tol ) %>% s2_num_points(), 8L ) }) test_that("s2_geography_writer() with tesselate_tol works with real data", { tol <- 1000 / s2_earth_radius_meters() countries <- s2_data_countries() countries_tes <- wk::wk_handle( countries, s2_geography_writer(check = FALSE), s2_tessellate_tol = tol ) expect_true( sum(s2_num_points(countries_tes)) > sum(s2_num_points(s2_data_countries())) ) }) test_that("wk_handle() for s2_geography works with s2_projection_mercator()", { # sf::sf_project("EPSG:4326", "EPSG:3857", wk::xy(30, 10)) %>% dput() geog <- wk::wk_handle( wk::xy(3339584.72379821, 1118889.97485796), s2_geography_writer(projection = s2_projection_mercator()) ) expect_equal( wk::wk_handle( geog, wk::xy_writer(), s2_projection = s2_projection_mercator() ), wk::xy(3339584.72379821, 1118889.97485796) ) expect_equal( wk::wk_handle( geog, wk::xy_writer(), s2_projection = s2_projection_mercator(), s2_tessellate_tol = 1e10 ), wk::xy(3339584.72379821, 1118889.97485796) ) }) test_that("s2_geography_writer() works with s2_projection_mercator()", { # sf::sf_project("EPSG:4326", "EPSG:3857", wk::xy(30, 10)) %>% dput() expect_equal( wk::as_xy( wk::wk_handle( wk::xy(3339584.72379821, 1118889.97485796), s2_geography_writer(projection = s2_projection_mercator()) ) ), wk::xy(30, 10, crs = wk::wk_crs_longlat()) ) }) test_that("wk_handle() for s2_geography works with s2_projection_orthographic()", { geog <- as_s2_geography(c("POINT (0 0)", "POINT (0 45)", "POINT (45 0)")) result <- wk::wk_handle( geog, wk::xy_writer(), s2_projection = s2_projection_orthographic() ) expect_equal( result, wk::xy( c(0, 0, sqrt(2) / 2), c(0, sqrt(2) / 2, 0) ) ) }) test_that("orthographic projection maintains 'north up' orientation", { result_coords <- wk::wk_coords( as_s2_geography(s2_lnglat(-64, c(45, 50))), s2_projection = s2_projection_orthographic(s2_lnglat(-64, 45)) ) expect_equal(result_coords$x[1], 0) expect_equal(result_coords$y[1], 0) expect_equal(result_coords$x[1], result_coords$x[2]) # proj_result <- sf::sf_project( # "EPSG:4326", # "+proj=ortho +lon_0=-64 +lat_0=45 +ellips=sphere", # s2::s2_lnglat(-64, c(45, 50)) # ) / 6370997 # result_coords <- wk::wk_coords(wk::as_xy(proj_result)) }) test_that("s2_geography_writer() works with s2_projection_mercator()", { # sf::sf_project("EPSG:4326", "EPSG:3857", wk::xy(30, 10)) %>% dput() xy <- wk::xy( c(0, 0, sqrt(2) / 2), c(0, sqrt(2) / 2, 0) ) geog <- wk::wk_handle( xy, s2_geography_writer(projection = s2_projection_orthographic()) ) expect_identical( s2_as_text(geog, precision = 5), c("POINT (0 0)", "POINT (0 45)", "POINT (45 0)") ) }) test_that("s2_hemisphere() works", { expect_equal( s2_area(s2_hemisphere(s2_lnglat(0, 0)), radius = 1), 2 * pi ) }) test_that("s2_world_plate_carree() works", { world0 <- s2_world_plate_carree(0, 0) expect_identical( wk::wk_bbox(wk::wkt(s2_as_text(world0))), wk::rct(-180, -90, 180, 90) ) world_eps <- s2_world_plate_carree(1, 2) expect_identical( wk::wk_bbox(wk::wkt(s2_as_text(world_eps))), wk::rct(-179, -88, 179, 88) ) }) s2/tests/testthat/test-s2-transformers.R0000644000176200001440000005245214530411473017761 0ustar liggesusers test_that("s2_centroid() works", { expect_wkt_equal(s2_centroid("POINT (30 10)"), "POINT (30 10)") expect_true(s2_is_empty(s2_centroid("POINT EMPTY"))) expect_wkt_equal(s2_centroid("MULTIPOINT ((0 0), (0 10))"), "POINT (0 5)") expect_wkt_equal(s2_centroid("LINESTRING (0 0, 0 10)"), "POINT (0 5)", precision = 15) expect_near( s2_distance( s2_centroid("POLYGON ((-5 -5, 5 -5, 5 5, -5 5, -5 -5))"), "POINT (0 0)" ), 0, epsilon = 1e-6 ) }) test_that("s2_centroid() and s2_centroid_agg() normalize points", { expect_equal( s2_distance( s2_centroid("MULTIPOINT (1 1, 1 1)"), "POINT (1 1)" ), 0 ) expect_equal( s2_distance( s2_centroid_agg(c("POINT (1 1)", "POINT (1 1)")), "POINT (1 1)" ), 0 ) }) test_that("s2_point_on_surface() works", { expect_wkt_equal(s2_point_on_surface("POINT (30 10)"), "POINT (30 10)") expect_true(s2_is_empty(s2_point_on_surface("POINT EMPTY"))) expect_wkt_equal( s2_point_on_surface("POLYGON ((0 0, 10 0, 1 1, 0 10, 0 0))"), "POINT (0.4502368024893488 0.4502229020796313)", precision = 15 ) expect_wkt_equal( s2_point_on_surface("MULTIPOINT ((0 0), (0 5), (0 10))"), "POINT (0 5)" ) }) test_that("s2_boundary() works", { expect_true(s2_is_empty(s2_boundary("POINT (30 10)"))) expect_true(s2_is_empty(s2_boundary("POINT EMPTY"))) expect_true(s2_is_empty(s2_boundary("POLYGON EMPTY"))) expect_wkt_equal(s2_boundary("LINESTRING (0 0, 0 10)"), "MULTIPOINT ((0 0), (0 10))") expect_wkt_equal( s2_boundary("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)) )"), "MULTILINESTRING ( (40 40, 20 45, 45 30, 40 40), (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20) )", precision = 15 ) }) test_that("s2_closest_point() works", { expect_wkt_equal(s2_closest_point("POINT (0 1)", "POINT (30 10)"), "POINT (0 1)") expect_wkt_equal(s2_closest_point("LINESTRING (0 1, -12 -12)", "POINT (30 10)"), "POINT (0 1)") }) test_that("s2_minimum_clearance_line_between() works", { expect_wkt_equal( s2_minimum_clearance_line_between("POINT (0 1)", "POINT (30 10)"), "LINESTRING (0 1, 30 10)" ) expect_true(s2_is_empty(s2_minimum_clearance_line_between("POINT (30 10)", "POINT EMPTY"))) expect_wkt_equal( s2_minimum_clearance_line_between("LINESTRING (0 1, -12 -12)", "POINT (30 10)"), "LINESTRING (0 1, 30 10)" ) expect_wkt_equal( s2_minimum_clearance_line_between("LINESTRING (0 0, 1 1)", "LINESTRING (1 0, 0 1)"), "MULTIPOINT ((0.5 0.500057), (0.5 0.500057))", precision = 6 ) }) test_that("s2_difference() works", { expect_wkt_equal(s2_difference("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)") expect_true(s2_is_empty(s2_difference("POINT (30 10)", "POINT (30 10)"))) expect_true(s2_is_empty(s2_difference("LINESTRING (0 0, 45 0)", "LINESTRING (0 0, 45 0)"))) }) test_that("s2_difference() works for polygons", { # on Windows i386, these fail without snap rounding df <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) expect_near( s2_area(df, radius = 1), s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1), epsilon = 0.004 ) df0 <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open", snap = s2_snap_level(30)) ) df1 <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open", snap = s2_snap_level(30)) ) df2 <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "closed", snap = s2_snap_level(30)) ) expect_equal(s2_area(df0) - s2_area(df2), 0.0) expect_equal(s2_area(df0) - s2_area(df1), 0.0) }) test_that("s2_sym_difference() works", { expect_wkt_equal(s2_sym_difference("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)") expect_true(s2_is_empty(s2_sym_difference("POINT (30 10)", "POINT (30 10)"))) expect_wkt_equal(s2_sym_difference("POINT (30 10)", "POINT (30 20)"), "MULTIPOINT ((30 20), (30 10))") expect_true(s2_is_empty(s2_sym_difference("LINESTRING (0 0, 45 0)", "LINESTRING (0 0, 45 0)"))) }) test_that("s2_sym_difference() works for polygons", { # on Windows i386, these fail without snap rounding df <- s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) expect_near( s2_area(df, radius = 1), 2 * s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1), epsilon = 0.0042 ) df0 <- s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open", snap = s2_snap_level(30)) ) df1 <- s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open", snap = s2_snap_level(30)) ) df2 = s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(model = "closed", snap = s2_snap_level(30)) ) expect_equal(s2_area(df0) - s2_area(df2), 0.0) expect_equal(s2_area(df0) - s2_area(df1), 0.0) }) test_that("s2_intersection() works", { expect_wkt_equal(s2_intersection("POINT (30 10)", "POINT (30 10)"), "POINT (30 10)") expect_true(s2_is_empty(s2_intersection("POINT (30 10)", "POINT (30 11)"))) expect_wkt_equal( s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "LINESTRING (0 5, 10 5)" ), "LINESTRING (0 5, 10 5)" ) expect_equal( s2_distance( s2_intersection("LINESTRING (-45 0, 45 0)", "LINESTRING (0 -10, 0 10)"), "POINT (0 0)" ), 0 ) }) test_that("s2_intersction() works for polygons", { expect_wkt_equal( s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ), "POLYGON ((5 5, 10 5, 10 10, 5 10, 5 5))", precision = 2 ) expect_true(s2_is_empty(s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)"))) expect_true( s2_is_empty( s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "open") ) ) ) expect_true( s2_is_empty( s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "semi-open")) ) ) expect_wkt_equal( s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "closed")), "POINT(0 0)" ) df0 <- s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open") ) df1 <- s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open") ) df2 <- s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "closed") ) expect_equal(s2_area(df0) - s2_area(df2), 0.0) expect_equal(s2_area(df0) - s2_area(df1), 0.0) }) test_that("s2_union(x) works", { expect_wkt_equal(s2_union("POINT (30 10)"), "POINT (30 10)") expect_wkt_equal(s2_union("POINT EMPTY"), "GEOMETRYCOLLECTION EMPTY") expect_wkt_equal( s2_union("MULTILINESTRING ((-45 0, 0 0), (0 0, 0 10))"), "LINESTRING (-45 0, 0 0, 0 10)" ) expect_wkt_equal(s2_union("GEOMETRYCOLLECTION (POINT (30 10))"), "POINT (30 10)") expect_wkt_equal( s2_union("GEOMETRYCOLLECTION (POINT (30 10), LINESTRING (0 0, 0 1))"), "GEOMETRYCOLLECTION (POINT (30 10), LINESTRING (0 0, 0 1))" ) }) test_that("s2_union(x) works with empty input", { expect_identical( s2_as_text(s2_union("MULTIPOLYGON EMPTY")), "GEOMETRYCOLLECTION EMPTY" ) }) test_that("s2_union(x) works with polygons that have overlapping input regions", { # two outer loops txt <- "MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)), ((0.1 0.9, 0.1 1.9, 1.1 1.9, 1.1 0.9, 0.1 0.9)))" # geos::geos_unary_union(txt) %>% as_wkb() %>% s2_area(radius = 1) unioned <- s2_union(as_s2_geography(txt, check = F)) expect_equal(round(s2_area(unioned, radius = 1), 12), 0.00058172748) # two outer loops, one valid inner loop # geos::geos_unary_union(txt2) %>% as_wkb() %>% s2_area(radius = 1) txt2 <- "MULTIPOLYGON ( ((0 0, 0 1, 1 1, 1 0, 0 0), (0.1 0.1, 0.5 0.1, 0.5 0.5, 0.1 0.5, 0.1 0.1)), ((0.1 0.9, 0.1 1.9, 1.1 1.9, 1.1 0.9, 0.1 0.9)) )" unioned <- s2_union(as_s2_geography(txt2, check = F)) expect_equal(round(s2_area(unioned, radius = 1), 12), 0.000532989259) }) test_that("s2_union(x) errors for the case of mixed dimension collections", { expect_error( s2_union( c("GEOMETRYCOLLECTION(POLYGON ((-10 -10, -10 10, 10 10, 10 -10, -10 -10)), LINESTRING (0 -20, 0 20))") ), "for multidimensional collections not implemented" ) }) test_that("s2_union(x, y) works", { expect_wkt_equal(s2_union("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)") expect_wkt_equal(s2_union("POINT EMPTY", "POINT EMPTY"), "GEOMETRYCOLLECTION EMPTY") # LINESTRING (-45 0, 0 0, 0 10) expect_wkt_equal( s2_union("LINESTRING (-45 0, 0 0)", "LINESTRING (0 0, 0 10)"), "LINESTRING (-45 0, 0 0, 0 10)" ) }) test_that("s2_union() works for polygons", { # on Windows i386, these fail without snap rounding u <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) u0 <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open", snap = s2_snap_level(30)) ) u1 <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open", snap = s2_snap_level(30)) ) u2 <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "closed", snap = s2_snap_level(30)) ) expect_equal(s2_area(u0) - s2_area(u2), 0.0) expect_equal(s2_area(u0) - s2_area(u1), 0.0) expect_near( s2_area(u, radius = 1), s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) + s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1), epsilon = 0.004 ) }) test_that("binary operations use layer creation options", { expect_wkt_equal( s2_union( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "path", polyline_sibling_pairs = "discard") ), "LINESTRING (0 0, 0 1, 0 2, 0 3)" ) expect_true( s2_is_collection( s2_union( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "walk") ) ) ) expect_wkt_equal( s2_coverage_union_agg( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "path", polyline_sibling_pairs = "discard") ), "LINESTRING (0 0, 0 1, 0 2, 0 3)" ) expect_true( s2_is_collection( s2_coverage_union_agg( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "walk") ) ) ) }) test_that("s2_coverage_union_agg() works", { expect_wkt_equal(s2_coverage_union_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_coverage_union_agg(c("POINT EMPTY", "POINT EMPTY")), "GEOMETRYCOLLECTION EMPTY") # NULL handling expect_identical( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_coverage_union_agg(character()), as_s2_geography("GEOMETRYCOLLECTION EMPTY") ) expect_wkt_equal( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) }) test_that("s2_union_agg() works", { expect_wkt_equal(s2_union_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_union_agg(c("POINT EMPTY", "POINT EMPTY")), "GEOMETRYCOLLECTION EMPTY") # NULL handling expect_identical( s2_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_union_agg(character()), as_s2_geography("GEOMETRYCOLLECTION EMPTY") ) expect_wkt_equal( s2_union_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) # make sure this works on polygons since they are handled differently than # points and linestrings expect_equal( s2_area(s2_union_agg(s2_data_countries())), sum(s2_area(s2_union_agg(s2_data_countries()))) ) # check non-polygons and polygons together points_and_poly <- s2_union_agg( c( s2_data_countries(), s2_data_cities() ) ) points <- s2_rebuild(points_and_poly, options = s2_options(dimensions = "point")) poly <- s2_rebuild(points_and_poly, options = s2_options(dimensions = "polygon")) expect_false(any(s2_intersects(points, poly))) }) test_that("s2_rebuild_agg() works", { expect_wkt_equal(s2_rebuild_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_rebuild_agg(c("POINT EMPTY", "POINT EMPTY")), "GEOMETRYCOLLECTION EMPTY") # NULL handling expect_identical( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_rebuild_agg(character()), as_s2_geography("GEOMETRYCOLLECTION EMPTY") ) expect_wkt_equal( s2_rebuild_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) }) test_that("s2_centroid_agg() works", { expect_wkt_equal(s2_centroid_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_centroid_agg(c("POINT EMPTY", "POINT EMPTY")), "POINT EMPTY") expect_wkt_equal(s2_centroid_agg(c("POINT (0 0)", "POINT (0 10)")), "POINT (0 5)", precision = 15) # NULL handling expect_identical( s2_centroid_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_centroid_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) }) test_that("s2_snap_to_grid() works", { expect_wkt_equal( s2_as_text(s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2)), "POINT (0.33 0.67)", precision = 6 ) }) test_that("s2_buffer() works", { # create a hemisphere! ply <- s2_buffer_cells("POINT (0 0)", distance = pi / 2, radius = 1) expect_near(s2_area(ply, radius = 1), 4 * pi / 2, epsilon = 0.1) }) test_that("s2_simplify() works", { expect_wkt_equal( s2_simplify("LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)", tolerance = 100), "LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)" ) expect_wkt_equal( s2_simplify("LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)", tolerance = 1000), "LINESTRING (0 0, 0 3)" ) }) test_that("s2_rebuild() works", { s2_rebuild("POINT (-64 45)") s2_rebuild("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") s2_rebuild("GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 0 0))") # duplicated edges expect_wkt_equal( s2_rebuild("MULTIPOINT (-64 45, -64 45)", options = s2_options(duplicate_edges = FALSE)), "POINT (-64 45)" ) expect_wkt_equal( s2_rebuild("MULTIPOINT (-64 45, -64 45)", options = s2_options(duplicate_edges = TRUE)), "MULTIPOINT (-64 45, -64 45)" ) # crossing edges expect_true( s2_is_collection( s2_rebuild( "LINESTRING (0 -5, 0 5, -5 0, 5 0)", options = s2_options(split_crossing_edges = TRUE) ) ) ) # snap expect_wkt_equal( s2_rebuild( "MULTIPOINT (0.01 0.01, -0.01 -0.01)", options = s2_options( snap = s2_snap_precision(1e1), duplicate_edges = TRUE ) ), "MULTIPOINT ((0 0), (0 0))" ) # snap radius expect_wkt_equal( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 3)", options = s2_options( snap_radius = 1.5 * pi / 180 ) ), "LINESTRING (0 0, 0 2)" ) # simplify edge chains expect_wkt_equal( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 3)", options = s2_options( snap_radius = 0.01, simplify_edge_chains = TRUE ) ), "LINESTRING (0 0, 0 3)" ) # validate bad_poly <- s2_geog_from_text( "POLYGON ((0 0, 1.0 0, 1.0 1.0, -0.1 1.0, 1.1 0, 0 0))", check = FALSE ) expect_wkt_equal( s2_rebuild(bad_poly, options = s2_options(validate = FALSE)), bad_poly ) expect_error( s2_rebuild(bad_poly, options = s2_options(validate = TRUE)), "Edge 1 crosses edge 3" ) # polyline type expect_wkt_equal( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "walk") ), "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)" ) expect_true( s2_is_collection( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "path") ) ) ) # sibling edge pairs expect_true( s2_is_collection( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "path", polyline_sibling_pairs = "keep") ) ) ) expect_false( s2_is_collection( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "path", polyline_sibling_pairs = "discard") ) ) ) # dimension expect_true( s2_is_empty( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(dimensions = c("point", "polygon")) ) ) ) }) test_that("real data survives the S2BooleanOperation", { # the 32-bit Solaris build results in some of the roundtripped # edges becoming degenerate. Rather than pass check = FALSE to # as_s2_geography(), just skip this on Solaris skip_on_os("solaris") for (continent in unique(s2::s2_data_tbl_countries$continent)) { # this is primarily a test of the S2BooleanOperation -> Geography constructor unioned <- expect_s3_class(s2_coverage_union_agg(s2_data_countries(continent)), "s2_geography") # this is a test of RGeography::Export() on potentially complex polygons exported <- expect_length(s2_as_binary(unioned), 1) # the output WKB should load as a polygon with oriented = TRUE and result in the # same number of points and similar area reloaded <- s2_geog_from_wkb(exported, oriented = TRUE) expect_equal(s2_num_points(reloaded), s2_num_points(unioned)) expect_equal(s2_area(reloaded, radius = 1), s2_area(unioned, radius = 1)) # also check with oriented = FALSE (may catch quirky nesting) reloaded <- s2_geog_from_wkb(exported, oriented = FALSE) expect_equal(s2_num_points(reloaded), s2_num_points(unioned)) expect_equal(s2_area(reloaded, radius = 1), s2_area(unioned, radius = 1)) } }) test_that("s2_interpolate() and s2_interpolate_normalized() work", { expect_identical( s2_as_text( s2_interpolate_normalized("LINESTRING (0 0, 0 60)", c(0, 0.25, 0.75, 1, NA)), precision = 5 ), c("POINT (0 0)", "POINT (0 15)", "POINT (0 45)", "POINT (0 60)", NA) ) expect_identical( s2_as_text( s2_interpolate("LINESTRING (0 0, 0 60)", c(0, 0.25, 0.75, 1, NA) * pi / 3, radius = 1), precision = 5 ), c("POINT (0 0)", "POINT (0 15)", "POINT (0 45)", "POINT (0 60)", NA) ) expect_error( s2_interpolate_normalized("POINT (0 1)", 1), "must be a polyline" ) expect_error( s2_interpolate_normalized("MULTILINESTRING ((0 1, 1 1), (1 1, 1 2))", 1), "must be a simple geography" ) }) test_that("s2_convex_hull() works", { expect_equal( s2_area(s2_convex_hull( c("GEOMETRYCOLLECTION(POINT(3.6 43.2), POINT (0 0), POINT(3.61 43.21))", NA) )), s2_area(c("POLYGON ((0 0, 3.61 43.21, 3.6 43.2, 0 0))", NA)) ) }) test_that("s2_convex_hull_agg() works", { expect_equal( s2_area(s2_convex_hull_agg(c("POINT(3.6 43.2)", "POINT (0 0)", "POINT(3.61 43.21)"))), s2_area("POLYGON ((0 0, 3.61 43.21, 3.6 43.2, 0 0))") ) expect_equal( s2_area(s2_convex_hull_agg(c( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"))), s2_area("POLYGON ((0 0, 10 0, 15 5, 15 15, 5 15, 0 10, 0 0))") ) expect_equal( s2_area(s2_convex_hull_agg(c( "POINT (3.6 43.2)", "LINESTRING (3.49 43.05, 3.52 43.1, 3.38 43.2, 3.1 43.1)", "POLYGON ((3.01 43.2, 3.4 44.01, 3.5 43.5, 3.1 43.2, 3.01 43.2))", "GEOMETRYCOLLECTION EMPTY" ))), s2_area( "POLYGON ((3.49 43.05, 3.6 43.2, 3.4 44.01, 3.01 43.2, 3.1 43.1, 3.49 43.05))" ) ) expect_equal( s2_area(s2_convex_hull_agg( "GEOMETRYCOLLECTION(POLYGON ((3.01 43.2, 3.4 44.01, 3.5 43.5, 3.1 43.2, 3.01 43.2)), POINT (3.6 43.2))" )), s2_area(s2_convex_hull_agg( c( "POLYGON ((3.01 43.2, 3.4 44.01, 3.5 43.5, 3.1 43.2, 3.01 43.2))", "POINT (3.6 43.2)" ) )) ) expect_identical( s2_convex_hull_agg(c("POINT (0 0)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_equal( s2_area(s2_convex_hull_agg(c("POINT (0 0)", NA), na.rm = TRUE)), 0 ) }) s2/tests/testthat/test-s2-constructors-formatters.R0000644000176200001440000001244214530411473022163 0ustar liggesusers test_that("s2_geog_point() works", { expect_wkt_equal(s2_geog_point(-64, 45), "POINT (-64 45)") }) test_that("s2_make_line() works", { expect_wkt_equal( s2_make_line(c(-64, 8), c(45, 71)), "LINESTRING (-64 45, 8 71)" ) # check separation using feature_id expect_wkt_equal( s2_make_line( c(-64, 8, -27, -27), c(45, 71, 0, 45), feature_id = c(1, 1, 2, 2) ), c("LINESTRING (-64 45, 8 71)", "LINESTRING (-27 0, -27 45)") ) }) test_that("s2_make_polygon() works", { expect_wkt_equal( s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)), "POLYGON ((-45 64, 8 71, 0 90, -45 64))" ) # check that loops can be open or closed expect_wkt_equal( s2_make_polygon(c(-45, 8, 0, -45), c(64, 71, 90, 64)), "POLYGON ((-45 64, 8 71, 0 90, -45 64))" ) # check feature/ring separation expect_wkt_equal( s2_make_polygon( c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45), c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30), feature_id = c(rep(1, 8), rep(2, 3)), ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1) ), c( "POLYGON ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))", "POLYGON ((40 40, 20 45, 45 30, 40 40))" ) ) }) test_that("s2_geog_from_wkt() works", { expect_wkt_equal(s2_geog_from_text("POINT (-64 45)"), "POINT (-64 45)") }) test_that("s2_geog_from_wkb() works", { expect_wkt_equal(s2_geog_from_wkb(as_wkb("POINT (-64 45)")), "POINT (-64 45)") }) test_that("s2_as_text() works", { expect_identical( s2_as_text("POINT (0.1234567890123456 0.1234567890123456)"), "POINT (0.1234567890123456 0.1234567890123456)" ) }) test_that("s2_as_binary() works", { expect_identical( s2_as_binary("POINT (0 0)", endian = 1), structure( list( as.raw( c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) ) ), class = "blob" ) ) }) test_that("s2_as_binary works on (multi)polygons", { geog <- s2_data_countries() wkb <- s2_as_binary(geog) expect_identical( sum(vapply(wkb, length, integer(1))), 173318L ) expect_identical(length(wkb), length(geog)) }) test_that("polygon constructors respect oriented and check arguments", { polygon_with_bad_hole_wkt <- "POLYGON ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) )" polygon_with_bad_hole_wkb <- wk::as_wkb(polygon_with_bad_hole_wkt) polygon_with_bad_hole_df <- data.frame( x = c(20, 10, 10, 30, 45, 30, 20, 20), y = c(35, 30, 10, 5, 20, 20, 25, 15), ring_id = c(1, 1, 1, 1, 1, 2, 2, 2) ) expect_false( s2_intersects( s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = FALSE), "POINT (23 19.5)" ) ) expect_false( s2_intersects( s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = FALSE), "POINT (23 19.5)" ) ) expect_false( s2_intersects( with( polygon_with_bad_hole_df, s2_make_polygon(x, y, ring_id = ring_id, oriented = FALSE) ), "POINT (23 19.5)" ) ) expect_error( s2_intersects( s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = TRUE, check = TRUE), "POINT (23 19.5)" ), "Inconsistent loop orientations" ) expect_error( s2_intersects( s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = TRUE, check = TRUE), "POINT (23 19.5)" ), "Inconsistent loop orientations" ) expect_error( s2_intersects( with( polygon_with_bad_hole_df, s2_make_polygon(x, y, ring_id = ring_id, oriented = TRUE, check = TRUE) ), "POINT (23 19.5)" ), "Inconsistent loop orientations" ) expect_silent( s2_intersects( s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = TRUE, check = FALSE), "POINT (23 19.5)" ) ) expect_silent( s2_intersects( s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = TRUE, check = FALSE), "POINT (23 19.5)" ) ) expect_silent( s2_intersects( with( polygon_with_bad_hole_df, s2_make_polygon(x, y, ring_id = ring_id, oriented = TRUE, check = FALSE) ), "POINT (23 19.5)" ) ) }) test_that("planar = TRUE works for s2_geog_from_text()", { geog_wkt <- "LINESTRING (-64 45, 0 45)" geog <- s2_geog_from_text(geog_wkt, planar = TRUE) expect_true(s2_distance(geog, "POINT (-30 45)") < s2_tessellate_tol_default()) }) test_that("planar = TRUE works for s2_geog_from_wkb()", { geog_wkb <- wk::as_wkb("LINESTRING (-64 45, 0 45)") geog <- s2_geog_from_wkb(geog_wkb, planar = TRUE) expect_true(s2_distance(geog, "POINT (-30 45)") < s2_tessellate_tol_default()) }) test_that("planar = TRUE works for s2_as_text()", { # cells very specifically have geodesic edges geog <- s2_cell_polygon(s2_cell_parent(as_s2_cell(s2_lnglat(-64, 45)), 4)) expect_identical(s2_num_points(geog), 4L) out <- s2_as_text(geog, planar = TRUE) expect_true(s2_num_points(out) > s2_num_points(geog)) }) test_that("planar = TRUE works for s2_geog_from_text()", { geog <- s2_cell_polygon(s2_cell_parent(as_s2_cell(s2_lnglat(-64, 45)), 4)) expect_identical(s2_num_points(geog), 4L) out <- s2_as_binary(geog, planar = TRUE) expect_true(s2_num_points(out) > s2_num_points(geog)) }) s2/tests/testthat/test-s2-cell-union.R0000644000176200001440000001256514530411473017302 0ustar liggesusers test_that("s2_cell_union() class works", { expect_s3_class(s2_cell_union(), "s2_cell_union") expect_s3_class(s2_cell_union(), "wk_vctr") x <- s2_cell_union() expect_identical(as_s2_cell_union(x), x) expect_output(expect_identical(str(x), x), "s2_cell_union") expect_output(expect_identical(print(x), x), "s2_cell_union") expect_identical(unlist(x), s2_cell()) expect_identical(is.na(new_s2_cell_union(list(NULL))), TRUE) }) test_that("s2_cell_union can be roundtripped through character", { expect_identical( as_s2_cell_union(c("3442c;345d5", NA)), s2_cell_union(list(s2_cell(c("3442c", "345d5")), NULL)) ) expect_identical( as.character(s2_cell_union(list(c("3442c", "345d5"), NULL))), c("3442c;345d5", NA) ) }) test_that("as_s2_cell_union() for s2_cell() works", { expect_identical( as_s2_cell_union(s2_cell(c("4b59a0cd83b5de49", NA))), s2_cell_union(list(s2_cell("4b59a0cd83b5de49"), NULL)) ) }) test_that("as_s2_geography() for s2_cell_union works", { union <- as_s2_cell_union(s2_cell(c("4b59a0cd83b5de49", NA))) geog <- as_s2_geography(union) expect_identical( s2_intersects(geog, s2_lnglat(c(-64, NA), c(45, NA))), c(TRUE, NA) ) expect_identical(s2_dimension(geog), c(2L, NA)) }) test_that("s2_cell_union_normalize() works", { cell <- s2_cell_parent(as_s2_cell("4b59a0cd83b5de49"), 10) children <- s2_cell_union(list(s2_cell_child(cell, 0:3))) expect_identical( s2_cell_union_normalize(children), as_s2_cell_union(cell) ) expect_identical( s2_cell_union_normalize(new_s2_cell_union(list(NULL))), new_s2_cell_union(list(NULL)) ) }) test_that("s2_cell_union_contains() works", { cell_na <- s2_cell_union(list(NULL)) cell <- s2_cell_parent(as_s2_cell("4b59a0cd83b5de49"), 10) children <- as_s2_cell_union(s2_cell_child(cell, 0:3)) expect_identical( s2_cell_union_contains(cell, c(children, cell_na)), c(rep(TRUE, 4), NA) ) expect_identical( s2_cell_union_contains(cell_na, children), rep(NA, 4) ) expect_identical( s2_cell_union_contains(c(children, cell_na), cell), c(rep(FALSE, 4), NA) ) expect_identical( s2_cell_union_contains(children, cell_na), rep(NA, 4) ) expect_error( s2_cell_union_contains(children, c(s2_cell_union(cell), cell_na)), "Can't recycle vectors" ) }) test_that("s2_cell_union_contains() works for cell y", { cell_na <- s2_cell(NA) cell <- s2_cell_parent(as_s2_cell("4b59a0cd83b5de49"), 10) children <- s2_cell_child(cell, 0:3) expect_identical( s2_cell_union_contains(cell, c(children, cell_na)), c(rep(TRUE, 4), NA) ) expect_identical( s2_cell_union_contains(cell_na, children), rep(NA, 4) ) expect_identical( s2_cell_union_contains(c(children, cell_na), cell), c(rep(FALSE, 4), NA) ) expect_identical( s2_cell_union_contains(children, cell_na), rep(NA, 4) ) expect_error( s2_cell_union_contains(children, c(children, cell_na)), "Incompatible lengths" ) }) test_that("s2_cell_union_intersects() works", { cell_na <- s2_cell_union(list(NULL)) cell <- s2_cell_parent(as_s2_cell("4b59a0cd83b5de49"), 10) children <- as_s2_cell_union(s2_cell_child(cell, 0:3)) expect_identical( s2_cell_union_intersects(cell, c(children, cell_na)), c(rep(TRUE, 4), NA) ) expect_identical( s2_cell_union_intersects(cell_na, children), rep(NA, 4) ) expect_identical( s2_cell_union_intersects(c(children, cell_na), cell), c(rep(TRUE, 4), NA) ) expect_identical( s2_cell_union_intersects(children, cell_na), rep(NA, 4) ) expect_error( s2_cell_union_intersects(children, c(s2_cell_union(cell), cell_na)), "Can't recycle vectors" ) }) test_that("s2_cell_union_intersection|difference|union() works", { cell <- s2_cell_parent(as_s2_cell("4b59a0cd83b5de49"), 10) children <- as_s2_cell_union(s2_cell_child(cell, 0:3)) expect_identical( s2_cell_union_intersection(cell, children[1]), children[1] ) expect_identical( s2_cell_union_difference(cell, children[1]), s2_cell_union(list(unlist(children[2:4]))) ) expect_identical( s2_cell_union_union( s2_cell_union(list(unlist(children[1:2]))), s2_cell_union(list(unlist(children[3:4]))) ), s2_cell_union(cell) ) }) test_that("s2_covering_cell_ids() works", { expect_length(unlist(s2_covering_cell_ids(s2_data_countries("France"))), 8) expect_length( unlist(s2_covering_cell_ids(s2_data_countries("France"), max_cells = 4)), 4 ) expect_length( unlist(s2_covering_cell_ids(s2_data_countries("France"), interior = TRUE)), 8 ) expect_identical(s2_covering_cell_ids(NA_character_), new_s2_cell_union(list(NULL))) }) test_that("s2_covering_cell_ids_agg() works", { geog <- s2_data_countries(c("France", "Germany")) coverings <- s2_covering_cell_ids(geog) coverings_agg <- s2_covering_cell_ids_agg(geog) expect_length(unlist(coverings_agg), 8) coverings_interior_agg <- s2_covering_cell_ids_agg(geog, interior = TRUE) expect_length(unlist(coverings_interior_agg), 8) expect_identical( s2_covering_cell_ids_agg(NA_character_, na.rm = FALSE), new_s2_cell_union(list(NULL)) ) expect_identical( s2_covering_cell_ids_agg(character(), radius = NA_real_), new_s2_cell_union(list(NULL)) ) expect_identical( s2_covering_cell_ids_agg(NA_character_, na.rm = TRUE), new_s2_cell_union(list(s2_cell())) ) }) s2/tests/testthat/test-s2-predicates.R0000644000176200001440000002153014530411473017350 0ustar liggesusers test_that("s2_contains() works", { expect_identical(s2_contains("POINT (0 0)", NA_character_), NA) expect_true(s2_contains("POINT (0 0)", "POINT (0 0)")) expect_false(s2_contains("POINT (0 0)", "POINT (1 1)")) expect_false(s2_contains("POINT (0 0)", "POINT EMPTY")) # make sure model is passed on to at least one binary predicate # in the open model, lines do not contain endpoints (but do contain other points) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "open"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "open"))) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "open"))) # semi-open and closed: endpoints are contained expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "semi-open"))) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "semi-open"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "closed"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "closed"))) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "closed"))) }) test_that("s2_covers() and s2_covered_by() work", { expect_true(s2_covers("POINT (0 0)", "POINT (0 0)")) expect_false(s2_covers("POINT (0 0)", "POINT (1 1)")) expect_false(s2_covers("POINT (0 0)", "POINT EMPTY")) expect_true(s2_covers("LINESTRING (0 0, 1 1)", "POINT (0 0)")) expect_false(s2_covers("LINESTRING (0 0, 1 1)", "POINT (-1 -1)")) # make sure line is along a geodesic edge # if the line doesn't contain the endpoints, floating # point math won't always keep it strictly inside or outside # the polygon (hard to predict which way it will go) # (when the model is set such that the boundary of the polygon # is considered 'contained' by it) polygon <- "POLYGON ((0 0, 1 1, 0 1, 0 0))" line <- "LINESTRING (0 0, 1 1)" point <- "POINT (0.5 0.7)" expect_true(s2_covers(polygon, polygon)) expect_false(s2_covers(polygon, line, s2_options(model = "open"))) expect_false(s2_covers(polygon, line, s2_options(model = "semi-open"))) expect_true(s2_covers(polygon, line, s2_options(model = "closed"))) expect_true(s2_covers(polygon, point, s2_options(model = "open"))) expect_true(s2_covers(polygon, point, s2_options(model = "semi-open"))) expect_true(s2_covers(polygon, point, s2_options(model = "closed"))) expect_false(s2_covered_by(polygon, line, s2_options(model = "open"))) expect_false(s2_covered_by(polygon, line, s2_options(model = "semi-open"))) expect_false(s2_covered_by(polygon, line, s2_options(model = "closed"))) expect_true(s2_covered_by(point, polygon, s2_options(model = "open"))) expect_true(s2_covered_by(point, polygon, s2_options(model = "semi-open"))) expect_true(s2_covered_by(point, polygon, s2_options(model = "closed"))) }) test_that("s2_disjoint() works", { expect_identical(s2_disjoint("POINT (0 0)", NA_character_), NA) expect_false(s2_disjoint("POINT (0 0)", "POINT (0 0)")) expect_true(s2_disjoint("POINT (0 0)", "POINT (1 1)")) expect_true(s2_disjoint("POINT (0 0)", "POINT EMPTY")) expect_false(s2_disjoint("LINESTRING (0 0, 1 1)", "POINT (0 0)")) expect_true(s2_disjoint("LINESTRING (0 0, 1 1)", "POINT (-1 -1)")) }) test_that("s2_equals() works", { expect_identical(s2_equals("POINT (0 0)", NA_character_), NA) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)")) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "open"))) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "closed"))) expect_false(s2_equals("POINT (0 0)", "POINT (1 1)")) expect_false(s2_equals("POINT (0 0)", "POINT EMPTY")) expect_true(s2_equals("POINT EMPTY", "POINT EMPTY")) expect_true(s2_equals("LINESTRING (0 0, 1 1)", "LINESTRING (1 1, 0 0)")) expect_false(s2_equals("LINESTRING (0 1, 1 1)", "LINESTRING (1 1, 0 0)")) }) test_that("s2_intersects() works", { expect_identical(s2_intersects("POINT (0 0)", NA_character_), NA) expect_true(s2_intersects("POINT (0 0)", "POINT (0 0)")) expect_false(s2_intersects("POINT (0 0)", "POINT (1 1)")) expect_false(s2_intersects("POINT (0 0)", "POINT EMPTY")) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "LINESTRING (1 0, 0 1)")) expect_false(s2_intersects("LINESTRING (0 0, 1 1)", "LINESTRING (-2 -2, -1 -1)")) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)")) expect_false(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "open"))) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "closed"))) polygon = "POLYGON((0 0,1 0,1 1,0 1,0 0))" expect_false(s2_intersects(polygon, "POINT (0 0)")) expect_false(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "open"))) expect_false(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "closed"))) }) test_that("s2_intersects_box() works", { expect_error( s2_intersects_box("POINT (-1 -1)", -2, -2, 2, 2, detail = 0), "Can't create polygon" ) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1)) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1)) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "open"))) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "semi-open"))) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "closed"))) expect_false(s2_intersects_box("POINT (1 1)", 0, 0, 1, 1)) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1)) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1)) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "open"))) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "semi-open"))) expect_true(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "closed"))) expect_true(s2_intersects_box("POINT (-1 -1)", -2, -2, 2, 2)) expect_false(s2_intersects_box("POINT (-1 -1)", 0, 0, 2, 2)) expect_false(s2_intersects_box("POINT (0 0)", 1, 1, 2, 2)) }) test_that("s2_within() works", { expect_identical(s2_within("POINT (0 0)", NA_character_), NA) expect_true(s2_within("POINT (0 0)", "POINT (0 0)")) expect_false(s2_within("POINT (0 0)", "POINT (1 1)")) expect_false(s2_within("POINT (0 0)", "POINT EMPTY")) expect_false(s2_within("POINT EMPTY", "POINT (0 0)")) expect_true(s2_within("POINT (0 0)", "LINESTRING (0 0, 1 1)", s2_options(model = "semi-open"))) expect_false(s2_within("POINT (0 0)", "LINESTRING (0 0, 1 1)", s2_options(model = "open"))) expect_false(s2_within("POINT (0 0)", "LINESTRING (1 1, 2 2)")) }) test_that("s2_touches() works", { # is inside expect_false(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (0.5 0.75)")) # is outside expect_false(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (-0.5 0.75)")) # is vertex expect_true(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (0 0)")) }) test_that("s2_dwithin() works", { expect_identical(s2_dwithin("POINT (0 0)", NA_character_, 0), NA) expect_true(s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + 0.01, radius = 1)) expect_false(s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 - 0.01, radius = 1)) expect_false(s2_dwithin("POINT (0 0)", "POINT EMPTY", 0)) expect_true(s2_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 21, radius = 180 / pi)) expect_false(s2_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 19, radius = 180 / pi)) # check vectorization expect_identical( s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + c(0.01, -0.01), radius = 1), c(TRUE, FALSE) ) }) test_that("s2_prepared_dwithin() works", { expect_identical(s2_prepared_dwithin("POINT (0 0)", NA_character_, 0), NA) expect_true(s2_prepared_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + 0.01, radius = 1)) expect_false(s2_prepared_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 - 0.01, radius = 1)) expect_false(s2_prepared_dwithin("POINT (0 0)", "POINT EMPTY", 0)) expect_true(s2_prepared_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 21, radius = 180 / pi)) expect_false(s2_prepared_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 19, radius = 180 / pi)) # check vectorization expect_identical( s2_prepared_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + c(0.01, -0.01), radius = 1), c(TRUE, FALSE) ) }) s2/tests/testthat/test-plot.R0000644000176200001440000000173614530411473015667 0ustar liggesusers test_that("s2_plot works", { x <- s2_data_countries() expect_identical(s2_plot(x), x) s2_plot(s2_data_cities(), add = TRUE) expect_identical(s2_plot(x, simplify = FALSE), x) expect_identical(s2_plot(x, plot_hemisphere = TRUE), x) expect_identical(s2_plot(x, centre = s2_lnglat(0, 0)), x) }) test_that("s2_plot works for all examples", { for (name in names(s2_data_example_wkt)) { geog <- as_s2_geography(s2_data_example_wkt[[name]]) # need non-nulls for now: # https://github.com/paleolimbot/wk/issues/143 geog <- geog[!is.na(geog)] expect_identical(s2_plot(geog), geog) } }) test_that("plot() works for vector classes", { x <- as_s2_geography("POINT (0 1)") expect_identical(plot(x), x) x <- s2_covering_cell_ids(as_s2_geography("POLYGON ((0 0, 1 0, 0 1, 0 0))")) expect_identical(plot(x), x) x <- as_s2_cell(as_s2_geography("POINT (0 1)")) expect_identical(plot(x), x) x <- s2_cell_parent(x, 7) expect_identical(plot(x), x) }) s2/tests/testthat/test-utils.R0000644000176200001440000000252314530411473016044 0ustar liggesusers test_that("recycle_common works", { expect_identical(recycle_common(1, 2), list(1, 2)) expect_identical(recycle_common(1, b = 2), list(1, b = 2)) expect_identical(recycle_common(1, 2:4), list(c(1, 1, 1), c(2L, 3L, 4L))) expect_identical(recycle_common(numeric(0), 2), list(numeric(0), numeric(0))) expect_error(recycle_common(numeric(0), 2:4), "Incompatible lengths") }) test_that("problems stopper formats problems correctly", { expect_error( stop_problems_create(1, "Houston! We have a correctly formatted problem!"), "Found 1 feature with invalid" ) expect_error( stop_problems_create(1:11, paste0("problem ", 1:11)), "\\.\\.\\.and 1 more" ) }) test_that("processing problems stopper formats problems correctly", { expect_error( stop_problems_process(1, "Houston! We have a correctly formatted problem!"), "Encountered 1 processing error" ) expect_error( stop_problems_process(1:11, paste0("problem ", 1:11)), "\\.\\.\\.and 1 more" ) }) test_that("wkt tester works", { expect_wkt_equal("POINT (0.123456 0)", "POINT (0.1234561 0)", precision = 6) expect_failure(expect_wkt_equal("POINT (0.123456 0)", "POINT (0.1234561 0)", precision = 7)) }) test_that("almost equal expectation works", { expect_near(0.001, 0, epsilon = 0.0011) expect_failure(expect_near(0.001, 0, epsilon = 0.0009)) }) s2/tests/testthat/test-s2-matrix.R0000644000176200001440000001606514530411473016540 0ustar liggesusers test_that("s2_closest|farthest_feature() works", { cities <- s2_data_cities("London") countries <- s2_data_countries() # with zero length y, results will all be empty expect_identical(s2_closest_feature(cities, character(0)), rep_len(NA_integer_, length(cities))) # should correctly identify that London is closest to United Kingdom country_match <- s2_closest_feature(cities, countries) expect_identical(s2_data_tbl_countries$name[country_match], "United Kingdom") country_match_farthest <- s2_farthest_feature(cities, countries) expect_identical(s2_data_tbl_countries$name[country_match_farthest], "New Zealand") }) test_that("s2_closest_edges() works", { expect_identical( s2_closest_edges( "POINT (0 0)", c("POINT (0 0)", "POINT (0 1)", "POINT (0 2)", "POINT (0 3)"), k = 1 ), list(1L) ) expect_identical( s2_closest_edges( "POINT (0 0)", c("POINT (0 0)", "POINT (0 1)", "POINT (0 2)", "POINT (0 3)"), k = 2, min_distance = 0 ), list(2L) ) expect_identical( s2_closest_edges( "POINT (0 0)", c("POINT (0 0)", "POINT (0 1)", "POINT (0 2)", "POINT (0 3)"), k = 5 ) %>% lapply(sort), list(1:4) ) expect_identical( s2_closest_edges( "POINT (0 0)", c("POINT (0 0)", "POINT (0 1)", "POINT (0 2)", "POINT (0 3)"), k = 5, max_distance = 2.5 * pi / 180, radius = 1 ) %>% lapply(sort), list(1:3) ) }) test_that("matrix predicates work", { expect_identical( s2_contains_matrix( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), ), list(2L) ) expect_identical( s2_within_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_covers_matrix( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), ), list(2L) ) expect_identical( s2_covered_by_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_intersects_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_disjoint_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(1L, integer(0), 1L) ) expect_identical( s2_equals_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)", "POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), integer(0), integer(0), 1L) ) expect_identical( s2_touches_matrix( c("POINT (0.5 0.5)", "POINT (0 0)", "POINT (-0.5 -0.5)"), "POLYGON ((0 0, 0 1, 1 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_dwithin_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)", "POINT (10 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", 0 ), list(integer(0), 1L, integer(0), integer(0)) ) expect_identical( s2_dwithin_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", s2_earth_radius_meters() ), list(1L, 1L, 1L) ) }) test_that("s2_(max_)?distance_matrix() works", { x <- c("POINT (0 0)", "POINT (0 90)") y <- c("POINT (180 0)", "POINT (0 -90)", "POINT (0 0)") expect_equal( s2_distance_matrix(x, x, radius = 180 / pi), matrix(c(0, 90, 90, 0), ncol = 2) ) expect_equal( s2_distance_matrix(x, y, radius = 180 / pi), matrix(c(180, 90, 90, 180, 0, 90), ncol = 3) ) # max distance is the same for points expect_equal( s2_max_distance_matrix(x, x, radius = 180 / pi), matrix(c(0, 90, 90, 0), ncol = 2) ) expect_equal( s2_max_distance_matrix(x, y, radius = 180 / pi), matrix(c(180, 90, 90, 180, 0, 90), ncol = 3) ) # NA handling for both rows and cols y[2] <- NA expect_true(all(is.na(s2_distance_matrix(x, y)[, 2]))) expect_true(all(is.na(s2_max_distance_matrix(x, y)[, 2]))) x[2] <- NA expect_true(all(is.na(s2_distance_matrix(x, y)[2, ]))) expect_true(all(is.na(s2_max_distance_matrix(x, y)[2, ]))) }) test_that("s2_may_intersect_matrix() works", { countries <- s2_data_countries() timezones <- s2_data_timezones() maybe_intersects <- s2_may_intersect_matrix(countries, timezones) intersects <- s2_intersects_matrix_brute_force(countries, timezones) for (i in seq_along(countries)) { expect_identical(setdiff(intersects[[!!i]], maybe_intersects[[!!i]]), integer(0)) } }) test_that("indexed matrix predicates return the same thing as brute-force comparisons", { countries <- s2_data_countries() timezones <- s2_data_timezones() # contains expect_identical( s2_contains_matrix(countries, countries), s2_contains_matrix_brute_force(countries, countries) ) expect_identical( s2_contains_matrix(timezones, countries), s2_contains_matrix_brute_force(timezones, countries) ) # within expect_identical( s2_within_matrix(countries, countries), s2_within_matrix_brute_force(countries, countries) ) expect_identical( s2_within_matrix(timezones, countries), s2_within_matrix_brute_force(timezones, countries) ) # covers expect_identical( s2_covers_matrix(countries, countries), s2_covers_matrix_brute_force(countries, countries) ) expect_identical( s2_covers_matrix(timezones, countries), s2_covers_matrix_brute_force(timezones, countries) ) # covered by expect_identical( s2_covered_by_matrix(countries, countries), s2_covered_by_matrix_brute_force(countries, countries) ) expect_identical( s2_covered_by_matrix(timezones, countries), s2_covered_by_matrix_brute_force(timezones, countries) ) # intersects expect_identical( s2_intersects_matrix(countries, countries), s2_intersects_matrix_brute_force(countries, countries) ) expect_identical( s2_intersects_matrix(timezones, countries), s2_intersects_matrix_brute_force(timezones, countries) ) # disjoint expect_identical( s2_disjoint_matrix(countries, countries), s2_disjoint_matrix_brute_force(countries, countries) ) expect_identical( s2_disjoint_matrix(timezones, countries), s2_disjoint_matrix_brute_force(timezones, countries) ) # equals expect_identical( s2_equals_matrix(countries, countries), s2_equals_matrix_brute_force(countries, countries) ) expect_identical( s2_equals_matrix(timezones, countries), s2_equals_matrix_brute_force(timezones, countries) ) # dwithin expect_identical( s2_dwithin_matrix(countries, countries, 1e6), s2_dwithin_matrix_brute_force(countries, countries, 1e6) ) expect_identical( s2_dwithin_matrix(timezones, countries, 1e6), s2_dwithin_matrix_brute_force(timezones, countries, 1e6) ) }) s2/tests/testthat/test-s2-cell.R0000644000176200001440000002651714530411473016156 0ustar liggesusers test_that("s2_cell class works", { expect_s3_class(new_s2_cell(double()), "s2_cell") expect_s3_class(new_s2_cell(NA_real_), "s2_cell") expect_true(is.na(new_s2_cell(NA_real_))) expect_identical(as_s2_cell(s2_cell()), s2_cell()) expect_identical( as.list(new_s2_cell(NA_real_)), list(new_s2_cell(NA_real_)) ) }) test_that("s2_cell bit64::integer64 support works", { cells <- c(as_s2_cell(NA_character_), s2_cell_sentinel()) int64s <- bit64::as.integer64(cells) expect_identical(int64s, bit64::as.integer64(c(NA, -1))) expect_identical(as_s2_cell(int64s), cells) }) test_that("invalid and sentinel values work as expected", { expect_false(s2_cell_is_valid(s2_cell_sentinel())) expect_false(s2_cell_is_valid(s2_cell_invalid())) expect_false(is.na(s2_cell_sentinel())) expect_false(is.na(s2_cell_invalid())) expect_true(s2_cell_sentinel() > s2_cell_invalid()) }) test_that("sort() and unique() work", { # temporary workaround for broken c() in wk expect_identical( unique(new_s2_cell(c(unclass(s2_cell_sentinel()), NA, 0, 0, unclass(s2_cell("5"))))), new_s2_cell(c(0, unclass(s2_cell("5")), NA, unclass(s2_cell_sentinel()))) ) expect_identical( sort(new_s2_cell(c(unclass(s2_cell_sentinel()), NA, 0, 0, unclass(s2_cell("5"))))), new_s2_cell(c(0, 0, unclass(s2_cell("5")), NA, unclass(s2_cell_sentinel()))) ) expect_identical( sort( new_s2_cell(c(unclass(s2_cell_sentinel()), NA, 0, 0, unclass(s2_cell("5")))), decreasing = TRUE ), rev(new_s2_cell(c(0, 0, unclass(s2_cell("5")), NA, unclass(s2_cell_sentinel())))) ) }) test_that("geography exporters work", { expect_identical( s2_as_text(s2_cell_center(as_s2_cell(s2_lnglat(c(-64, NA), c(45, NA)))), precision = 5), c("POINT (-64 45)", NA) ) expect_identical( s2_as_text(s2_cell_polygon(as_s2_cell(s2_lnglat(c(-64, NA), c(45, NA)))), precision = 5), c("POLYGON ((-64 45, -64 45, -64 45, -64 45, -64 45))", NA) ) expect_identical( s2_as_text(s2_cell_boundary(as_s2_cell(s2_lnglat(c(-64, NA), c(45, NA)))), precision = 5), c("LINESTRING (-64 45, -64 45, -64 45, -64 45, -64 45)", NA) ) expect_equal( s2_x(s2_cell_vertex(s2_cell(c("5", "5", "5", NA)), k = c(0, 1, NA, 1))), c(45, 135, NA, NA) ) }) test_that("s2_cell_is_valid() works", { expect_identical( s2_cell_is_valid(new_s2_cell(double())), logical() ) expect_identical( s2_cell_is_valid(new_s2_cell(NA_real_)), FALSE ) }) test_that("Ops, Math, and Summary errors for non-meaningful s2_cell() ops", { expect_error(s2_cell("X") + 1, "not meaningful") expect_error(abs(s2_cell("X")), "not meaningful") expect_error(all(s2_cell("X")), "not meaningful") }) test_that("Ops, Math, and Summary ops work", { expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) == s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(TRUE, FALSE, FALSE, TRUE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) != s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(FALSE, TRUE, TRUE, FALSE, NA, NA, NA) ) # 'invalid' is 0 expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) > s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(FALSE, TRUE, FALSE, FALSE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) >= s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(TRUE, TRUE, FALSE, TRUE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) < s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(FALSE, FALSE, TRUE, FALSE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) <= s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(TRUE, FALSE, TRUE, TRUE, NA, NA, NA) ) expect_identical( cummax(s2_cell(c("5", "x", "5", "x", NA, "x", NA))), s2_cell(c("5", "5", "5", "5", NA, NA, NA)) ) expect_identical( cummin(s2_cell(c("5", "x", "5", "x", NA, "x", NA))), s2_cell(c("5", "x", "x", "x", NA, NA, NA)) ) expect_identical( range(s2_cell(c("5", "x", "5", "x", NA, "x", NA)), na.rm = TRUE), s2_cell(c("X", "5")) ) expect_identical( max(s2_cell(c("5", "x", "5", "x", NA, "x", NA)), na.rm = TRUE), s2_cell("5") ) expect_identical( min(s2_cell(c("5", "x", "5", "x", NA, "x", NA)), na.rm = TRUE), s2_cell("X") ) expect_identical( range(s2_cell(c("5", "x", "5", "x", NA, "x", NA))), s2_cell(c(NA_character_, NA_character_)) ) expect_identical(range(s2_cell()), s2_cell(c(NA_character_, NA_character_))) expect_identical( range(s2_cell(NA_character_), na.rm = TRUE), s2_cell(c(NA_character_, NA_character_)) ) }) test_that("Binary ops are recycled at the C++ level", { expect_identical(s2_cell(rep("5", 2)) == s2_cell(rep("5", 2)), c(TRUE, TRUE)) expect_identical(s2_cell(rep("5", 1)) == s2_cell(rep("5", 2)), c(TRUE, TRUE)) expect_identical(s2_cell(rep("5", 2)) == s2_cell(rep("5", 1)), c(TRUE, TRUE)) expect_error(s2_cell(rep("5", 2)) == s2_cell(rep("5", 0)), "Can't recycle") }) test_that("s2_cell is not numeric", { expect_false(is.numeric(s2_cell())) }) test_that("s2_cell subsetting and concatenation work", { cells <- new_s2_cell(c(NA_real_, NA_real_)) expect_identical(cells[1], new_s2_cell(NA_real_)) expect_identical(cells[[1]], cells[1]) expect_identical( c(cells, cells), new_s2_cell(c(NA_real_, NA_real_, NA_real_, NA_real_)) ) expect_identical( rep(cells, 2), new_s2_cell(c(NA_real_, NA_real_, NA_real_, NA_real_)) ) expect_identical( rep_len(cells, 4), new_s2_cell(c(NA_real_, NA_real_, NA_real_, NA_real_)) ) expect_error( c(new_s2_cell(c(NA_real_, NA_real_)), 1:5), "Can't combine" ) }) test_that("s2_cell() can be created from s2_geography()", { expect_identical( as_s2_cell(as_s2_geography("POINT (-64 45)")), s2_cell("4b59a0cd83b5de49") ) }) test_that("s2_cell() can be created from s2_lnglat()", { expect_identical( as_s2_cell(s2_lnglat(-64, 45)), s2_cell("4b59a0cd83b5de49") ) }) test_that("s2_cell() can be created from s2_point()", { expect_identical( as_s2_cell(as_s2_point(s2_lnglat(-64, 45))), s2_cell("4b59a0cd83b5de49") ) }) test_that("s2_cell() can be created from character", { expect_identical( as_s2_cell("4b59a0cd83b5de49"), as_s2_cell(s2_lnglat(-64, 45)) ) }) test_that("subset-assignment works", { x <- as_s2_cell(c(NA, "4b59a0cd83b5de49", "4b5f6a7856889a33")) expect_identical(as.character(x[3]), "4b5f6a7856889a33") x[3] <- "4b59a0cd83b5de49" expect_identical(as.character(x[3]), "4b59a0cd83b5de49") x[[3]] <- "4b5f6a7856889a33" expect_identical(as.character(x[3]), "4b5f6a7856889a33") }) test_that("s2_cell can be put into a data.frame", { expect_identical( data.frame(geom = new_s2_cell(NA_real_)), new_data_frame(list(geom = new_s2_cell(NA_real_))) ) expect_error(as.data.frame(new_s2_cell(NA_real_)), "cannot coerce") }) test_that("s2_cell default format/print/str methods work", { expect_identical( format(s2_cell()), as.character(s2_cell()) ) expect_output(print(new_s2_cell(double())), "s2_cell") expect_output(print(new_s2_cell(NA_real_)), "s2_cell") expect_output(str(as_s2_cell(character())), "s2_cell\\[0\\]") expect_output(str(as_s2_cell(NA_character_)), "NA") }) test_that("s2 cell exporters work", { expect_equal( as.data.frame(s2_cell_to_lnglat(s2_cell(c("4b59a0cd83b5de49", "x", NA)))), as.data.frame(c(s2_lnglat(-64, 45), s2_lnglat(NA, NA), s2_lnglat(NA, NA))) ) expect_identical( s2_cell_debug_string(s2_cell(c("4b5f6a7856889a33", NA))), c("2/112233231103300223101010310121", NA) ) expect_match(s2_cell_debug_string(s2_cell("X")), "Invalid") }) test_that("s2_cell() accessors work", { expect_identical( s2_cell_is_valid(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(TRUE, TRUE, FALSE, FALSE) ) expect_identical( s2_cell_level(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(30L, 0L, NA, NA) ) expect_identical( s2_cell_is_leaf(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(TRUE, FALSE, NA, NA) ) expect_identical( s2_cell_is_face(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(FALSE, TRUE, NA, NA) ) expect_equal( s2_cell_area(s2_cell(c("5", "x", NA)), radius = 1), c(2 * pi / 3, NA, NA) ) expect_equal( s2_cell_area_approx(s2_cell(c("5", "x", NA)), radius = 1), c(2 * pi / 3, NA, NA) ) }) test_that("s2_cell() transversers work", { expect_identical( s2_cell_parent(s2_cell(c("5", "4b5f6a7856889a33", "x", NA)), 0), s2_cell(c("5", "5", NA, NA)) ) leaf_manual <- function(x, children) { if (length(children) == 0) { x } else { leaf_manual( s2_cell_child(x, children[1]), children[-1] ) } } # see output of s2_cell_debug_string() expect_identical( leaf_manual( s2_cell("5"), as.numeric(strsplit("112233231103300223101010310121", "")[[1]]) ), s2_cell("4b5f6a7856889a33") ) expect_identical( s2_cell_child(s2_cell(c("5", "5", "5", "x", NA)), c(-1, 4, NA, 0, 0)), s2_cell(as.character(c(NA, NA, NA, NA, NA))) ) expect_identical( s2_cell_debug_string(s2_cell_edge_neighbour(s2_cell("5"), 0:3)), c("1/", "3/", "4/", "0/") ) expect_identical( s2_cell_edge_neighbour(s2_cell(c("5", "5", "5", "x", NA)), c(-1, 4, NA, 0, 0)), s2_cell(as.character(c(NA, NA, NA, NA, NA))) ) }) test_that("s2_cell() binary operators work", { cell <- s2_cell("4b5f6a7856889a33") expect_true(s2_cell_contains(s2_cell_parent(cell), cell)) expect_false(s2_cell_contains(cell, s2_cell_parent(cell))) expect_identical(s2_cell_contains(s2_cell_sentinel(), s2_cell_sentinel()), NA) expect_equal( s2_cell_distance( as_s2_cell(s2_lnglat(0, 90)), as_s2_cell(s2_lnglat(0, 0)), radius = 1 ), pi / 2 ) expect_equal( s2_cell_max_distance( as_s2_cell(s2_lnglat(0, 90)), as_s2_cell(s2_lnglat(0, 0)), radius = 1 ), pi / 2 ) f1 <- s2_cell_parent(as_s2_cell(s2_lnglat(0, 0)), 0) f2 <- s2_cell_parent(as_s2_cell(s2_lnglat(0, 90)), 0) expect_equal(s2_cell_distance(f1, f2), 0) expect_equal(s2_cell_max_distance(f1, f2, radius = 1), pi) expect_identical(s2_cell_max_distance(s2_cell_sentinel(), s2_cell_sentinel()), NA_real_) expect_identical(s2_cell_distance(s2_cell_sentinel(), s2_cell_sentinel()), NA_real_) expect_false( s2_cell_may_intersect( as_s2_cell(s2_lnglat(0, 90)), as_s2_cell(s2_lnglat(0, 0)) ) ) expect_true(s2_cell_may_intersect(s2_cell_parent(cell), cell)) expect_true(s2_cell_may_intersect(cell, s2_cell_parent(cell))) expect_identical(s2_cell_may_intersect(s2_cell_sentinel(), s2_cell_sentinel()), NA) }) test_that("s2_cell_common_ancestor_level() works", { cell <- s2_cell_parent(s2_cell("4b5f6a7856889a33"), 10) children <- s2_cell_child(cell, 0:4) expect_identical( s2_cell_common_ancestor_level(children, cell), c(rep(10L, 4), NA), ) expect_identical(s2_cell_common_ancestor_level_agg(s2_cell()), NA_integer_) expect_identical( s2_cell_common_ancestor_level_agg( as_s2_cell(as_s2_geography(c("POINT (0 0)", "POINT (180 0)"))) ), -1L ) expect_identical( s2_cell_common_ancestor_level_agg(children, na.rm = TRUE), 10L ) expect_identical( s2_cell_common_ancestor_level_agg(children, na.rm = FALSE), NA_integer_ ) }) s2/tests/testthat/test-s2-options.R0000644000176200001440000000065414530411473016724 0ustar liggesusers test_that("s2_options() works", { expect_s3_class(s2_options(), "s2_options") }) test_that("s2_options() errors are readable", { expect_error(s2_intersects("POINT EMPTY", "POINT EMPTY", options = NULL), "must be created using") expect_error(s2_options(model = "not a model"), "must be one of") expect_error(s2_options(snap_radius = 100), "radius is too large") expect_error(s2_snap_level(31), "between 1 and 30") }) s2/tests/testthat/test-s2-earth.R0000644000176200001440000000021714530411473016327 0ustar liggesusers test_that("s2_earth_radius_meters works", { expect_type(s2_earth_radius_meters(), "double") expect_length(s2_earth_radius_meters(), 1) }) s2/tests/testthat/test-data.R0000644000176200001440000000167514530411473015624 0ustar liggesusers test_that("s2_data_country() works", { expect_s3_class(s2_data_countries("Germany"), "s2_geography") expect_length(s2_data_countries("Germany"), 1) expect_s3_class(s2_data_countries("Europe"), "s2_geography") expect_length(s2_data_countries("Europe"), 39) expect_s3_class(s2_data_countries(), "s2_geography") expect_length(s2_data_countries(), 177) }) test_that("s2_data_timezone() works", { expect_s3_class(s2_data_timezones(), "s2_geography") expect_length(s2_data_timezones(), 120) expect_s3_class(s2_data_timezones(-4), "s2_geography") expect_length(s2_data_timezones(-4), 3) expect_s3_class(s2_data_timezones(-15, 15), "s2_geography") expect_length(s2_data_timezones(-15, 15), 120) }) test_that("s2_data_cities() works", { expect_s3_class(s2_data_cities(), "s2_geography") expect_length(s2_data_cities(), 243) expect_s3_class(s2_data_cities("Cairo"), "s2_geography") expect_length(s2_data_cities("Cairo"), 1) }) s2/tests/testthat.R0000644000176200001440000000055414530411473013731 0ustar liggesusers# This file is part of the standard setup for testthat. # It is recommended that you do not modify it. # # Where should you do additional test configuration? # Learn more about the roles of various files in: # * https://r-pkgs.org/tests.html # * https://testthat.r-lib.org/reference/test_package.html#special-files library(testthat) library(s2) test_check("s2") s2/tests/area.R0000644000176200001440000000156314530411473013002 0ustar liggesuserslibrary(s2) u = s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) s2_area(u, radius = 1) s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) + s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) df = s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) s2_area(df, radius = 1) - (s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)) s2/src/0000755000176200001440000000000014540325333011367 5ustar liggesuserss2/src/s2-bounds.cpp0000644000176200001440000000320714530411473013711 0ustar liggesusers #include "s2/s2latlng_rect.h" #include "s2/s2cap.h" #include "s2-options.h" #include "geography-operator.h" #include using namespace Rcpp; // [[Rcpp::export]] DataFrame cpp_s2_bounds_cap(List geog) { SEXP item; NumericVector lng(geog.size()), lat(geog.size()), angle(geog.size()); for (R_xlen_t i = 0; i < geog.size(); i++) { Rcpp::checkUserInterrupt(); item = geog[i]; if (item == R_NilValue) { lat[i] = lng[i] = angle[i] = NA_REAL; } else { Rcpp::XPtr feature(item); S2Cap cap = feature->Geog().Region()->GetCapBound(); S2LatLng center(cap.center()); lng[i] = center.lng().degrees(); lat[i] = center.lat().degrees(); angle[i] = cap.GetRadius().degrees(); } } return DataFrame::create( _["lng"] = lng, _["lat"] = lat, _["angle"] = angle ); } // [[Rcpp::export]] DataFrame cpp_s2_bounds_rect(List geog) { SEXP item; NumericVector lng_lo(geog.size()), lat_lo(geog.size()), lng_hi(geog.size()), lat_hi(geog.size()); for (R_xlen_t i = 0; i < geog.size(); i++) { Rcpp::checkUserInterrupt(); item = geog[i]; if (item == R_NilValue) { lng_lo[i] = lat_lo[i] = lng_hi[i] = lat_hi[i] = NA_REAL; } else { Rcpp::XPtr feature(item); S2LatLngRect rect = feature->Geog().Region()->GetRectBound(); lng_lo[i] = rect.lng_lo().degrees(); lat_lo[i] = rect.lat_lo().degrees(); lng_hi[i] = rect.lng_hi().degrees(); lat_hi[i] = rect.lat_hi().degrees(); } } return DataFrame::create( _["lng_lo"] = lng_lo, _["lat_lo"] = lat_lo, _["lng_hi"] = lng_hi, _["lat_hi"] = lat_hi ); } s2/src/s2-lnglat.cpp0000644000176200001440000000426714530411473013707 0ustar liggesusers #include "s2/s2latlng.h" #include "s2/s2point.h" #include using namespace Rcpp; #include "wk-v1.h" // [[Rcpp::export]] List s2_lnglat_from_s2_point(List s2_point) { NumericVector x = s2_point[0]; NumericVector y = s2_point[1]; NumericVector z = s2_point[2]; R_len_t n = x.size(); NumericVector lng(n); NumericVector lat(n); S2LatLng item; for (R_xlen_t i = 0; i < n; i++) { item = S2LatLng(S2Point(x[i], y[i], z[i])); lng[i] = item.lng().degrees(); lat[i] = item.lat().degrees(); } return List::create(_["x"] = lng, _["y"] = lat); } // [[Rcpp::export]] List s2_point_from_s2_lnglat(List s2_lnglat) { NumericVector lng = s2_lnglat[0]; NumericVector lat = s2_lnglat[1]; R_len_t n = lng.size(); NumericVector x(n); NumericVector y(n); NumericVector z(n); S2Point item; for (R_xlen_t i = 0; i < n; i++) { item = S2LatLng::FromDegrees(lat[i], lng[i]).Normalized().ToPoint(); x[i] = item.x(); y[i] = item.y(); z[i] = item.z(); } return List::create(_["x"] = x, _["y"] = y, _["z"] = z); } int s2_point_trans(R_xlen_t feature_id, const double* xyzm_in, double* xyzm_out, void* trans_data) { S2Point pt = S2LatLng::FromDegrees(xyzm_in[1], xyzm_in[0]).Normalized().ToPoint(); xyzm_out[0] = pt.x(); xyzm_out[1] = pt.y(); xyzm_out[2] = pt.z(); return WK_CONTINUE; } void trans_s2_point_lnglat_finalize(void* trans_data) { } extern "C" SEXP c_s2_trans_s2_point_new() { wk_trans_t* trans = wk_trans_create(); trans->use_z = 1; trans->trans = &s2_point_trans; trans->finalizer = &trans_s2_point_lnglat_finalize; return wk_trans_create_xptr(trans, R_NilValue, R_NilValue); } int s2_lnglat_trans(R_xlen_t feature_id, const double* xyzm_in, double* xyzm_out, void* trans_data) { S2LatLng pt(S2Point(xyzm_in[0], xyzm_in[1], xyzm_in[2])); xyzm_out[0] = pt.lng().degrees(); xyzm_out[1] = pt.lat().degrees(); return WK_CONTINUE; } extern "C" SEXP c_s2_trans_s2_lnglat_new() { wk_trans_t* trans = wk_trans_create(); trans->use_z = 0; trans->trans = &s2_lnglat_trans; trans->finalizer = &trans_s2_point_lnglat_finalize; return wk_trans_create_xptr(trans, R_NilValue, R_NilValue); } s2/src/s2-cell-union.cpp0000644000176200001440000002774214530411473014476 0ustar liggesusers #include "s2/s2cell_id.h" #include "s2/s2cell.h" #include "s2/s2latlng.h" #include "s2/s2cell_union.h" #include "s2/s2region_coverer.h" #include "s2/s2shape_index_buffered_region.h" #include "s2/s2region_union.h" #include "geography-operator.h" #include using namespace Rcpp; static inline double reinterpret_double(uint64_t id) { double doppelganger; memcpy(&doppelganger, &id, sizeof(double)); return doppelganger; } S2CellUnion cell_union_from_cell_id_vector(const NumericVector& cellIdNumeric) { uint64* cellIds = (uint64*) &(cellIdNumeric[0]); std::vector cellIdsVector(cellIds, cellIds + cellIdNumeric.size()); return S2CellUnion(std::move(cellIdsVector)); } NumericVector cell_id_vector_from_cell_union(const S2CellUnion& cellUnion) { NumericVector cellIdNumeric(cellUnion.size()); for (R_xlen_t i = 0; i < cellIdNumeric.size(); i++) { cellIdNumeric[i] = reinterpret_double(cellUnion.cell_id(i).id()); } cellIdNumeric.attr("class") = CharacterVector::create("s2_cell", "wk_vctr"); return cellIdNumeric; } class S2CellUnionOperatorException: public std::runtime_error { public: S2CellUnionOperatorException(std::string msg): std::runtime_error(msg.c_str()) {} }; template class UnaryS2CellUnionOperator { public: VectorType processVector(Rcpp::List cellUnionVector) { VectorType output(cellUnionVector.size()); SEXP item; for (R_xlen_t i = 0; i < cellUnionVector.size(); i++) { if ((i % 1000) == 0) { Rcpp::checkUserInterrupt(); } item = cellUnionVector[i]; if (item == R_NilValue) { output[i] = VectorType::get_na(); } else { S2CellUnion cellUnion = cell_union_from_cell_id_vector(item); output[i] = this->processCell(cellUnion, i); } } return output; } virtual ScalarType processCell(S2CellUnion& cellUnion, R_xlen_t i) = 0; }; // For speed, take care of recycling here (only works if there is no // additional parameter). Most binary ops don't have a parameter and some // (like Ops, and Math) make recycling harder to incorporate at the R level template class BinaryS2CellUnionOperator { public: VectorType processVector(Rcpp::List cellUnionVector1, Rcpp::List cellUnionVector2) { SEXP item1 = R_NilValue; SEXP item2 = R_NilValue; if (cellUnionVector1.size() == cellUnionVector2.size()) { VectorType output(cellUnionVector1.size()); for (R_xlen_t i = 0; i < cellUnionVector1.size(); i++) { if ((i % 1000) == 0) { Rcpp::checkUserInterrupt(); } item1 = cellUnionVector1[i]; item2 = cellUnionVector2[i]; if (item1 == R_NilValue || item2 == R_NilValue) { output[i] = VectorType::get_na(); } else { S2CellUnion cellUnion1 = cell_union_from_cell_id_vector(item1); S2CellUnion cellUnion2 = cell_union_from_cell_id_vector(item2); output[i] = this->processCell(cellUnion1, cellUnion2, i); } } return output; } else if (cellUnionVector1.size() == 1) { VectorType output(cellUnionVector2.size()); item1 = cellUnionVector1[0]; if (item1 == R_NilValue) { for (R_xlen_t i = 0; i < cellUnionVector2.size(); i++) { if ((i % 1000) == 0) { Rcpp::checkUserInterrupt(); } output[i] = VectorType::get_na(); } return output; } S2CellUnion cellUnion1 = cell_union_from_cell_id_vector(item1); for (R_xlen_t i = 0; i < cellUnionVector2.size(); i++) { if ((i % 1000) == 0) { Rcpp::checkUserInterrupt(); } item2 = cellUnionVector2[i]; if (item2 == R_NilValue) { output[i] = VectorType::get_na(); } else { S2CellUnion cellUnion2 = cell_union_from_cell_id_vector(item2); output[i] = this->processCell(cellUnion1, cellUnion2, i); } } return output; } else if (cellUnionVector2.size() == 1) { VectorType output(cellUnionVector1.size()); item2 = cellUnionVector2[0]; if (item2 == R_NilValue) { for (R_xlen_t i = 0; i < cellUnionVector1.size(); i++) { if ((i % 1000) == 0) { Rcpp::checkUserInterrupt(); } output[i] = VectorType::get_na(); } return output; } S2CellUnion cellUnion2 = cell_union_from_cell_id_vector(item2); for (R_xlen_t i = 0; i < cellUnionVector1.size(); i++) { if ((i % 1000) == 0) { Rcpp::checkUserInterrupt(); } item1 = cellUnionVector1[i]; if (item1 == R_NilValue) { output[i] = VectorType::get_na(); } else { S2CellUnion cellUnion1 = cell_union_from_cell_id_vector(item1); output[i] = this->processCell(cellUnion1, cellUnion2, i); } } return output; } else { std::stringstream err; err << "Can't recycle vectors of size " << cellUnionVector1.size() << " and " << cellUnionVector2.size() << " to a common length."; stop(err.str()); } } virtual ScalarType processCell(const S2CellUnion& cellUnion1, const S2CellUnion& cellUnion2, R_xlen_t i) = 0; }; // [[Rcpp::export]] List cpp_s2_cell_union_normalize(List cellUnionVector) { class Op: public UnaryS2CellUnionOperator { SEXP processCell(S2CellUnion& cellUnion, R_xlen_t i) { cellUnion.Normalize(); return cell_id_vector_from_cell_union(cellUnion); } }; Op op; List out = op.processVector(cellUnionVector); out.attr("class") = CharacterVector::create("s2_cell_union", "wk_vctr"); return out; } // [[Rcpp::export]] LogicalVector cpp_s2_cell_union_is_na(List cellUnionVector) { LogicalVector out(cellUnionVector.size()); for (R_xlen_t i = 0; i < cellUnionVector.size(); i++) { SEXP item = cellUnionVector[i]; out[i] = item == R_NilValue; } return out; } // [[Rcpp::export]] LogicalVector cpp_s2_cell_union_contains(List cellUnionVector1, List cellUnionVector2) { class Op: public BinaryS2CellUnionOperator { int processCell(const S2CellUnion& cellUnion1, const S2CellUnion& cellUnion2, R_xlen_t i) { return cellUnion1.Contains(cellUnion2); } }; Op op; return op.processVector(cellUnionVector1, cellUnionVector2); } // optimized because it's a common case // [[Rcpp::export]] LogicalVector cpp_s2_cell_union_contains_cell(List cellUnionVector, NumericVector cellIdVector) { class Op: public UnaryS2CellUnionOperator { double* cellIdDouble; R_xlen_t cellIdVectorSize; public: Op(NumericVector cellIdVector) { cellIdDouble = REAL(cellIdVector); cellIdVectorSize = cellIdVector.size(); } int processCell(S2CellUnion& cellUnion, R_xlen_t i) { if (R_IsNA(cellIdDouble[i % cellIdVectorSize])) { return NA_LOGICAL; } else { S2CellId cellId(((uint64_t*) cellIdDouble)[i % cellIdVectorSize]); return cellUnion.Contains(cellId); } } }; Op op(cellIdVector); return op.processVector(cellUnionVector); } // [[Rcpp::export]] LogicalVector cpp_s2_cell_union_intersects(List cellUnionVector1, List cellUnionVector2) { class Op: public BinaryS2CellUnionOperator { int processCell(const S2CellUnion& cellUnion1, const S2CellUnion& cellUnion2, R_xlen_t i) { return cellUnion1.Intersects(cellUnion2); } }; Op op; return op.processVector(cellUnionVector1, cellUnionVector2); } // [[Rcpp::export]] List cpp_s2_cell_union_intersection(List cellUnionVector1, List cellUnionVector2) { class Op: public BinaryS2CellUnionOperator { SEXP processCell(const S2CellUnion& cellUnion1, const S2CellUnion& cellUnion2, R_xlen_t i) { return cell_id_vector_from_cell_union(cellUnion1.Intersection(cellUnion2)); } }; Op op; List out = op.processVector(cellUnionVector1, cellUnionVector2); out.attr("class") = CharacterVector::create("s2_cell_union", "wk_vctr"); return out; } // [[Rcpp::export]] List cpp_s2_cell_union_union(List cellUnionVector1, List cellUnionVector2) { class Op: public BinaryS2CellUnionOperator { SEXP processCell(const S2CellUnion& cellUnion1, const S2CellUnion& cellUnion2, R_xlen_t i) { return cell_id_vector_from_cell_union(cellUnion1.Union(cellUnion2)); } }; Op op; List out = op.processVector(cellUnionVector1, cellUnionVector2); out.attr("class") = CharacterVector::create("s2_cell_union", "wk_vctr"); return out; } // [[Rcpp::export]] List cpp_s2_cell_union_difference(List cellUnionVector1, List cellUnionVector2) { class Op: public BinaryS2CellUnionOperator { SEXP processCell(const S2CellUnion& cellUnion1, const S2CellUnion& cellUnion2, R_xlen_t i) { return cell_id_vector_from_cell_union(cellUnion1.Difference(cellUnion2)); } }; Op op; List out = op.processVector(cellUnionVector1, cellUnionVector2); out.attr("class") = CharacterVector::create("s2_cell_union", "wk_vctr"); return out; } // [[Rcpp::export]] List cpp_s2_geography_from_cell_union(List cellUnionVector) { class Op: public UnaryS2CellUnionOperator { SEXP processCell(S2CellUnion& cellUnion, R_xlen_t i) { std::unique_ptr polygon = absl::make_unique(); polygon->InitToCellUnionBorder(cellUnion); return RGeography::MakeXPtr(RGeography::MakePolygon(std::move(polygon))); } }; Op op; return op.processVector(cellUnionVector); } // [[Rcpp::export]] List cpp_s2_covering_cell_ids(List geog, int min_level, int max_level, int max_cells, NumericVector buffer, bool interior) { class Op: public UnaryGeographyOperator { public: NumericVector distance; S2RegionCoverer& coverer; bool interior; Op(NumericVector distance, S2RegionCoverer& coverer, bool interior): distance(distance), coverer(coverer), interior(interior) {} SEXP processFeature(XPtr feature, R_xlen_t i) { S2ShapeIndexBufferedRegion region; region.Init(&feature->Index().ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); S2CellUnion cellUnion; if (interior) { cellUnion = coverer.GetInteriorCovering(region); } else { cellUnion = coverer.GetCovering(region); } return cell_id_vector_from_cell_union(cellUnion); } }; S2RegionCoverer coverer; coverer.mutable_options()->set_min_level(min_level); coverer.mutable_options()->set_max_level(max_level); coverer.mutable_options()->set_max_cells(max_cells); Op op(buffer, coverer, interior); List out = op.processVector(geog); out.attr("class") = CharacterVector::create("s2_cell_union", "wk_vctr"); return out; } // [[Rcpp::export]] List cpp_s2_covering_cell_ids_agg(List geog, int min_level, int max_level, int max_cells, double buffer, bool interior, bool naRm) { S2RegionCoverer coverer; coverer.mutable_options()->set_min_level(min_level); coverer.mutable_options()->set_max_level(max_level); coverer.mutable_options()->set_max_cells(max_cells); S1ChordAngle bufferAngle = S1ChordAngle::Radians(buffer); S2RegionUnion regionUnion; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { List out = List::create(R_NilValue); out.attr("class") = CharacterVector::create("s2_cell_union", "wk_vctr"); return out; } if (item != R_NilValue) { Rcpp::XPtr feature(item); auto region = absl::make_unique(); region->Init(&feature->Index().ShapeIndex(), bufferAngle); regionUnion.Add(std::move(region)); } } S2CellUnion covering; if (interior) { covering = coverer.GetInteriorCovering(regionUnion); } else { covering = coverer.GetCovering(regionUnion); } List out = List::create(cell_id_vector_from_cell_union(covering)); out.attr("class") = CharacterVector::create("s2_cell_union", "wk_vctr"); return out; } s2/src/s2-matrix.cpp0000644000176200001440000005055314530411473013731 0ustar liggesusers #include #include #include #include "s2/s2boolean_operation.h" #include "s2/s2closest_edge_query.h" #include "s2/s2furthest_edge_query.h" #include "s2/s2shape_index_region.h" #include "s2/s2shape_index_buffered_region.h" #include "geography-operator.h" #include "s2-options.h" #include using namespace Rcpp; template class IndexedBinaryGeographyOperator: public UnaryGeographyOperator { public: std::unique_ptr geog2_index; std::unique_ptr iterator; // max_edges_per_cell should be between 10 and 50, with lower numbers // leading to more memory usage (but potentially faster query times). Benchmarking // with binary prediates seems to indicate that values on the high end // of the spectrum do a reasonable job of efficient preselection, and that // decreasing this value does little to increase performance. IndexedBinaryGeographyOperator(int maxEdgesPerCell = 50) { MutableS2ShapeIndex::Options index_options; index_options.set_max_edges_per_cell(maxEdgesPerCell); geog2_index = absl::make_unique(index_options); } virtual void buildIndex(List geog2) { for (R_xlen_t j = 0; j < geog2.size(); j++) { checkUserInterrupt(); SEXP item2 = geog2[j]; // build index and store index IDs so that shapeIds can be // mapped back to the geog index if (item2 == R_NilValue) { Rcpp::stop("Missing `y` not allowed in binary indexed operators()"); } else { Rcpp::XPtr feature2(item2); geog2_index->Add(feature2->Geog(), j); } } iterator = absl::make_unique(geog2_index.get()); } }; // -------- closest/farthest feature ---------- // [[Rcpp::export]] IntegerVector cpp_s2_closest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2ClosestEdgeQuery query(&geog2_index->ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(&feature->Index().ShapeIndex()); const auto& result = query.FindClosestEdge(&target); if (result.is_empty()) { return NA_INTEGER; } else { // convert to R index (+1) return geog2_index->value(result.shape_id()) + 1; } } }; Op op; op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] IntegerVector cpp_s2_farthest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2FurthestEdgeQuery query(&geog2_index->ShapeIndex()); S2FurthestEdgeQuery::ShapeIndexTarget target(&feature->Index().ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); if (result.is_empty()) { return NA_INTEGER; } else { // convert to R index (+1) return geog2_index->value(result.shape_id()) + 1; } } }; Op op; op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_closest_edges(List geog1, List geog2, int n, double min_distance, double max_distance) { class Op: public IndexedBinaryGeographyOperator { public: IntegerVector processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2ClosestEdgeQuery query(&geog2_index->ShapeIndex()); query.mutable_options()->set_max_results(n); query.mutable_options()->set_max_distance(S1ChordAngle::Radians(max_distance)); S2ClosestEdgeQuery::ShapeIndexTarget target(&feature->Index().ShapeIndex()); const auto& result = query.FindClosestEdges(&target); // this code searches edges, which may come from the same feature std::unordered_set features; for (S2ClosestEdgeQuery::Result res : result) { if (res.distance().radians() > this->min_distance) { features.insert(geog2_index->value(res.shape_id()) + 1); } } return IntegerVector(features.begin(), features.end()); } int n; double min_distance; double max_distance; }; Op op; op.n = n; op.min_distance = min_distance; op.max_distance = max_distance; op.buildIndex(geog2); return op.processVector(geog1); } // ----------- indexed binary predicate operators ----------- class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator { public: // a max_cells value of 8 was suggested in the S2RegionCoverer docs as a // reasonable approximation of a geometry, although benchmarking seems to indicate that // increasing this number above 4 actually decreasses performance (using a value // of 1 dramatically decreases performance) IndexedMatrixPredicateOperator(List s2options, int maxFeatureCells = 4, int maxEdgesPerCell = 50): IndexedBinaryGeographyOperator(maxEdgesPerCell), maxFeatureCells(maxFeatureCells) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); this->coverer.mutable_options()->set_max_cells(maxFeatureCells); } void buildIndex(List geog2) { this->geog2 = geog2; IndexedBinaryGeographyOperator::buildIndex(geog2); } IntegerVector processFeature(Rcpp::XPtr feature, R_xlen_t i) { coverer.GetCovering(*feature->Geog().Region(), &cell_ids); indices_unsorted.clear(); iterator->Query(cell_ids, &indices_unsorted); // loop through features from geog2 that might intersect feature // and build a list of indices that actually intersect (based on // this->actuallyIntersects(), which might perform alternative // comparisons) indices.clear(); for (int j: indices_unsorted) { SEXP item = this->geog2[j]; XPtr feature2(item); if (this->actuallyIntersects(feature->Index(), feature2->Index(), i, j)) { // convert to R index here + 1 indices.push_back(j + 1); } } // return sorted integer vector std::sort(indices.begin(), indices.end()); return Rcpp::IntegerVector(indices.begin(), indices.end()); }; virtual bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) = 0; protected: List geog2; S2BooleanOperation::Options options; int maxFeatureCells; S2RegionCoverer coverer; std::vector cell_ids; std::unordered_set indices_unsorted; std::vector indices; }; // [[Rcpp::export]] List cpp_s2_may_intersect_matrix(List geog1, List geog2, int maxEdgesPerCell, int maxFeatureCells, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options, int maxFeatureCells, int maxEdgesPerCell): IndexedMatrixPredicateOperator(s2options, maxFeatureCells, maxEdgesPerCell) {} bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return true; }; }; Op op(s2options, maxFeatureCells, maxEdgesPerCell); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_contains_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_contains(index1, index2, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_within_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 return s2geography::s2_contains(index2, index1, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_intersects_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_intersects(index1, index2, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_equals_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_equals(index1, index2, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_touches_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) { this->closedOptions = this->options; this->closedOptions.set_polygon_model(S2BooleanOperation::PolygonModel::CLOSED); this->closedOptions.set_polyline_model(S2BooleanOperation::PolylineModel::CLOSED); this->openOptions = this->options; this->openOptions.set_polygon_model(S2BooleanOperation::PolygonModel::OPEN); this->openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN); } bool actuallyIntersects(const s2geography::ShapeIndexGeography& index1, const s2geography::ShapeIndexGeography& index2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_intersects(index1, index2, this->closedOptions) && !s2geography::s2_intersects(index1, index2, this->openOptions); }; private: S2BooleanOperation::Options closedOptions; S2BooleanOperation::Options openOptions; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // ----------- brute force binary predicate operators ------------------ class BruteForceMatrixPredicateOperator { public: std::vector geog2Indices; S2BooleanOperation::Options options; BruteForceMatrixPredicateOperator() {} BruteForceMatrixPredicateOperator(Rcpp::List s2options) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); } List processVector(Rcpp::List geog1, Rcpp::List geog2) { List output(geog1.size()); // using instead of IntegerVector because // std::vector is much faster with repeated calls to .push_back() std::vector trueIndices; for (R_xlen_t i = 0; i < geog1.size(); i++) { trueIndices.clear(); SEXP item1 = geog1[i]; if (item1 == R_NilValue) { output[i] = R_NilValue; } else { Rcpp::XPtr feature1(item1); for (size_t j = 0; j < geog2.size(); j++) { checkUserInterrupt(); SEXP item2 = geog2[j]; if (item2 == R_NilValue) { stop("Missing `y` not allowed in binary index operations"); } XPtr feature2(item2); bool result = this->processFeature(feature1, feature2, i, j); if (result) { // convert to R index here (+1) trueIndices.push_back(j + 1); } } IntegerVector itemOut(trueIndices.size()); for (size_t k = 0; k < trueIndices.size(); k++) { itemOut[k] = trueIndices[k]; } output[i] = itemOut; } } return output; } virtual bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) = 0; }; // [[Rcpp::export]] List cpp_s2_dwithin_matrix(List geog1, List geog2, double distance) { class Op: public IndexedBinaryGeographyOperator { public: List geog2; S2RegionCoverer coverer; std::vector cell_ids; std::unordered_set indices_unsorted; std::vector indices; S1ChordAngle distance; IntegerVector processFeature(Rcpp::XPtr feature1, R_xlen_t i) { S2ShapeIndexBufferedRegion buffered( &feature1->Index().ShapeIndex(), this->distance ); coverer.GetCovering(buffered, &cell_ids); indices_unsorted.clear(); iterator->Query(cell_ids, &indices_unsorted); S2ClosestEdgeQuery query(&feature1->Index().ShapeIndex()); indices.clear(); for (int j: indices_unsorted) { SEXP item = this->geog2[j]; XPtr feature2(item); S2ClosestEdgeQuery::ShapeIndexTarget target(&feature2->Index().ShapeIndex()); if (query.IsDistanceLessOrEqual(&target, this->distance)) { indices.push_back(j + 1); } } // return sorted integer vector std::sort(indices.begin(), indices.end()); return Rcpp::IntegerVector(indices.begin(), indices.end()); } }; Op op; op.geog2 = geog2; op.distance = S1ChordAngle::Radians(distance); op.buildIndex(geog2); return op.processVector(geog1); } // ----------- distance matrix operators ------------------- template class MatrixGeographyOperator { public: MatrixType processVector(Rcpp::List geog1, Rcpp::List geog2) { MatrixType output(geog1.size(), geog2.size()); SEXP item1; SEXP item2; for (R_xlen_t i = 0; i < geog1.size(); i++) { item1 = geog1[i]; if (item1 == R_NilValue) { for (R_xlen_t j = 0; j < geog2.size(); j++) { output(i, j) = MatrixType::get_na(); } } else { Rcpp::XPtr feature1(item1); for (R_xlen_t j = 0; j < geog2.size(); j++) { checkUserInterrupt(); item2 = geog2[j]; if (item2 == R_NilValue) { output(i, j) = MatrixType::get_na(); } else { Rcpp::XPtr feature2(item2); output(i, j) = this->processFeature(feature1, feature2, i, j); } } } } return output; } virtual ScalarType processFeature(Rcpp::XPtr feature1, Rcpp::XPtr feature2, R_xlen_t i, R_xlen_t j) = 0; }; // [[Rcpp::export]] NumericMatrix cpp_s2_distance_matrix(List geog1, List geog2) { class Op: public MatrixGeographyOperator { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { S2ClosestEdgeQuery query(&feature1->Index().ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(&feature2->Index().ShapeIndex()); const auto& result = query.FindClosestEdge(&target); S1ChordAngle angle = result.distance(); double distance = angle.ToAngle().radians(); if (distance == R_PosInf) { return NA_REAL; } else { return distance; } } }; Op op; return op.processVector(geog1, geog2); } // [[Rcpp::export]] NumericMatrix cpp_s2_max_distance_matrix(List geog1, List geog2) { class Op: public MatrixGeographyOperator { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { S2FurthestEdgeQuery query(&feature1->Index().ShapeIndex()); S2FurthestEdgeQuery::ShapeIndexTarget target(&feature2->Index().ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); S1ChordAngle angle = result.distance(); double distance = angle.ToAngle().radians(); // returns -1 if one of the indexes is empty // NA is more consistent with the BigQuery // function, and makes way more sense if (distance < 0) { return NA_REAL; } else { return distance; } } }; Op op; return op.processVector(geog1, geog2); } // ----------- brute force binary predicate operators (for testing) ------------------ // [[Rcpp::export]] List cpp_s2_contains_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_contains(feature1->Index(), feature2->Index(), options); }; }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_within_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 return s2geography::s2_contains(feature2->Index(), feature1->Index(), options); }; }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_intersects_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_intersects(feature1->Index(), feature2->Index(), options); } }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_disjoint_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { return !s2geography::s2_intersects(feature1->Index(), feature2->Index(), options); } }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_equals_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { return s2geography::s2_equals(feature1->Index(), feature2->Index(), options); } }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_dwithin_matrix_brute_force(List geog1, List geog2, double distance) { class Op: public BruteForceMatrixPredicateOperator { public: double distance; Op(double distance): distance(distance) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { S2ClosestEdgeQuery query(&feature2->Index().ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(&feature1->Index().ShapeIndex()); return query.IsDistanceLessOrEqual(&target, S1ChordAngle::Radians(this->distance)); }; }; Op op(distance); return op.processVector(geog1, geog2); } s2/src/s2geography.h0000644000176200001440000000056514530411473014000 0ustar liggesusers #pragma once #include "s2geography/accessors-geog.h" #include "s2geography/accessors.h" #include "s2geography/build.h" #include "s2geography/constructor.h" #include "s2geography/coverings.h" #include "s2geography/distance.h" #include "s2geography/geography.h" #include "s2geography/index.h" #include "s2geography/linear-referencing.h" #include "s2geography/predicates.h" s2/src/wk-impl.c0000644000176200001440000000003114530411473013105 0ustar liggesusers #include "wk-v1-impl.c" s2/src/init.cpp0000644000176200001440000000101714530411473013035 0ustar liggesusers #include "s2/s2debug.h" #include using namespace Rcpp; // [[Rcpp::export]] void cpp_s2_init() { // It's important to set this flag, as users might have "debug" flags // for their build environment, and there are some checks that will terminate // R instead of throw an exception if this value is set to true. // When possible, we also disable debug checks on a per-operation basis // if there is another way to do so (e.g., constructing S2Loop and S2Polygon objects). FLAGS_s2debug = false; // # nocov } s2/src/s2-constructors-formatters.cpp0000644000176200001440000010020514530411473017347 0ustar liggesusers #define R_NO_REMAP #include #include #include "s2/s2pointutil.h" #include "wk-v1.h" #include "geography.h" #define CPP_START \ char cpp_exception_error[8096]; \ memset(cpp_exception_error, 0, 8096); \ try { #define CPP_END \ } catch (std::exception& e) { \ strncpy(cpp_exception_error, e.what(), 8096 - 1); \ } \ Rf_error("%s", cpp_exception_error); \ return R_NilValue; // The other versions of CPP_START and CPP_END stack-allocate the // error message buffer, which takes a non-trivial amount of time // when done at this scale (at worst 4 times per coordinate). By // keeping the buffer in the handler_data struct, we can call C++ // from every handler method without measurable overhead. #define WK_METHOD_CPP_START \ try { #define WK_METHOD_CPP_END \ } catch (std::exception& e) { \ strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ } \ Rf_error("%s", data->cpp_exception_error); \ return R_NilValue; #define WK_METHOD_CPP_END_INT \ } catch (std::exception& e) { \ strncpy(data->cpp_exception_error, e.what(), 8096 - 1); \ } \ Rf_error("%s", data->cpp_exception_error); \ return WK_ABORT; typedef struct { s2geography::util::FeatureConstructor* builder; SEXP result; R_xlen_t feat_id; int coord_size; char cpp_exception_error[8096]; } builder_handler_t; // TODO: Both of these allocate in a way that could longjmp and possibly leak memory static inline void builder_result_append(builder_handler_t* data, SEXP value) { R_xlen_t current_size = Rf_xlength(data->result); if (data->feat_id >= current_size) { SEXP new_result = PROTECT(Rf_allocVector(VECSXP, current_size * 2 + 1)); for (R_xlen_t i = 0; i < current_size; i++) { SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); } R_ReleaseObject(data->result); data->result = new_result; R_PreserveObject(data->result); UNPROTECT(1); } SET_VECTOR_ELT(data->result, data->feat_id, value); data->feat_id++; } static inline void builder_result_finalize(builder_handler_t* data) { R_xlen_t current_size = Rf_xlength(data->result); if (data->feat_id != current_size) { SEXP new_result = PROTECT(Rf_allocVector(VECSXP, data->feat_id)); for (R_xlen_t i = 0; i < data->feat_id; i++) { SET_VECTOR_ELT(new_result, i, VECTOR_ELT(data->result, i)); } R_ReleaseObject(data->result); data->result = new_result; R_PreserveObject(data->result); UNPROTECT(1); } } int builder_vector_start(const wk_vector_meta_t* meta, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; if (data->result != R_NilValue) { Rf_error("Destination vector was already allocated"); // # nocov } if (meta->size == WK_VECTOR_SIZE_UNKNOWN) { data->result = PROTECT(Rf_allocVector(VECSXP, 1024)); } else { data->result = PROTECT(Rf_allocVector(VECSXP, meta->size)); } R_PreserveObject(data->result); UNPROTECT(1); data->feat_id = 0; return WK_CONTINUE; } SEXP builder_vector_end(const wk_vector_meta_t* meta, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; builder_result_finalize(data); SEXP cls = PROTECT(Rf_allocVector(STRSXP, 2)); SET_STRING_ELT(cls, 0, Rf_mkChar("s2_geography")); SET_STRING_ELT(cls, 1, Rf_mkChar("wk_vctr")); Rf_setAttrib(data->result, R_ClassSymbol, cls); UNPROTECT(1); return data->result; } int builder_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START data->builder->feat_start(); return WK_CONTINUE; WK_METHOD_CPP_END_INT } int builder_feature_null(void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; builder_result_append(data, R_NilValue); return WK_ABORT_FEATURE; } int builder_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START std::unique_ptr feat = data->builder->finish_feature(); SEXP feature_xptr = PROTECT(RGeography::MakeXPtr(std::move(feat))); builder_result_append(data, feature_xptr); UNPROTECT(1); return WK_CONTINUE; WK_METHOD_CPP_END_INT } int builder_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START auto geometry_type = static_cast(meta->geometry_type); int32_t size; if (meta->size == WK_SIZE_UNKNOWN) { size = -1; } else { size = meta->size; } if (meta->flags & WK_FLAG_HAS_Z && meta->flags & WK_FLAG_HAS_M) { data->coord_size = 4; } else if (meta->flags & WK_FLAG_HAS_Z) { data->coord_size = 3; } else if (meta->flags & WK_FLAG_HAS_M) { data->coord_size = 3; } else { data->coord_size = 2; } data->builder->geom_start(geometry_type, size); return WK_CONTINUE; WK_METHOD_CPP_END_INT } int builder_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START data->builder->geom_end(); return WK_CONTINUE; WK_METHOD_CPP_END_INT } int builder_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START if (size == WK_SIZE_UNKNOWN) { data->builder->ring_start(-1); } else { data->builder->ring_start(size); } return WK_CONTINUE; WK_METHOD_CPP_END_INT } int builder_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START data->builder->ring_end(); return WK_CONTINUE; WK_METHOD_CPP_END_INT } int builder_coord(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; WK_METHOD_CPP_START data->builder->coords(coord, 1, data->coord_size); return WK_CONTINUE; WK_METHOD_CPP_END_INT } int builder_error(const char* message, void* handler_data) { Rf_error("%s", message); return WK_ABORT; } void builder_deinitialize(void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; if (data->result != R_NilValue) { R_ReleaseObject(data->result); data->result = R_NilValue; } } void builder_finalize(void* handler_data) { builder_handler_t* data = (builder_handler_t*) handler_data; if (data != nullptr) { free(data); } } void delete_vector_constructor(SEXP xptr) { auto ptr = reinterpret_cast(R_ExternalPtrAddr(xptr)); if (ptr != nullptr) { delete ptr; } } extern "C" SEXP c_s2_geography_writer_new(SEXP oriented_sexp, SEXP check_sexp, SEXP projection_xptr, SEXP tessellate_tolerance_sexp) { CPP_START int oriented = LOGICAL(oriented_sexp)[0]; int check = LOGICAL(check_sexp)[0]; S2::Projection* projection = NULL; if (projection_xptr != R_NilValue) { projection = reinterpret_cast(R_ExternalPtrAddr(projection_xptr)); } double tessellate_tolerance = REAL(tessellate_tolerance_sexp)[0]; s2geography::util::Constructor::Options options; options.set_oriented(oriented); options.set_check(check); options.set_projection(projection); if (tessellate_tolerance != R_PosInf) { options.set_tessellate_tolerance(S1Angle::Radians(tessellate_tolerance)); } auto builder = new s2geography::util::FeatureConstructor(options); SEXP builder_xptr = PROTECT(R_MakeExternalPtr(builder, R_NilValue, R_NilValue)); R_RegisterCFinalizer(builder_xptr, &delete_vector_constructor); wk_handler_t* handler = wk_handler_create(); handler->vector_start = &builder_vector_start; handler->vector_end = &builder_vector_end; handler->feature_start = &builder_feature_start; handler->null_feature = &builder_feature_null; handler->feature_end = &builder_feature_end; handler->geometry_start = &builder_geometry_start; handler->geometry_end = &builder_geometry_end; handler->ring_start = &builder_ring_start; handler->ring_end = &builder_ring_end; handler->coord = &builder_coord; handler->error = &builder_error; handler->deinitialize = &builder_deinitialize; handler->finalizer = &builder_finalize; builder_handler_t* data = (builder_handler_t*) malloc(sizeof(builder_handler_t)); if (data == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } data->coord_size = 2; data->builder = builder; data->result = R_NilValue; memset(data->cpp_exception_error, 0, 8096); handler->handler_data = data; // include the builder pointer as a tag for this external pointer // which guarnatees that it will not be garbage collected until // this object is garbage collected SEXP handler_xptr = wk_handler_create_xptr(handler, builder_xptr, projection_xptr); UNPROTECT(1); return handler_xptr; CPP_END } // The following defines exporting...it will hopefully be subsumed by a more general // approach supported by geoarrow and/or s2geography #define HANDLE_OR_RETURN(expr) \ result = expr; \ if (result != WK_CONTINUE) return result #define HANDLE_CONTINUE_OR_BREAK(expr) \ result = expr; \ if (result == WK_ABORT_FEATURE) continue; else if (result == WK_ABORT) break class S2Exporter { public: void set_vector_meta_flags(wk_vector_meta_t* vector_meta) { vector_meta->flags |= WK_FLAG_HAS_Z; } void set_meta_flags(wk_meta_t* vector_meta) { vector_meta->flags |= WK_FLAG_HAS_Z; } int coord_point(const wk_meta_t* meta, const S2Point& point, uint32_t coord_id, wk_handler_t* handler) { int result; coord_[0] = point.x(); coord_[1] = point.y(); coord_[2] = point.z(); HANDLE_OR_RETURN(handler->coord(meta, coord_, coord_id, handler->handler_data)); return WK_CONTINUE; } void reset() { coord_id_ = -1; } int coord_in_series(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { coord_id_++; return coord_point(meta, point, coord_id_, handler); } int last_coord_in_series(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { coord_id_++; return coord_point(meta, point, coord_id_, handler); } int last_coord_in_loop(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { coord_id_++; return coord_point(meta, point, coord_id_, handler); } protected: int32_t coord_id_; double coord_[4]; }; class SimpleExporter: public S2Exporter { public: SimpleExporter(const s2geography::util::Constructor::Options& options): options_(options) {} void set_vector_meta_flags(wk_vector_meta_t* vector_meta) {} void set_meta_flags(wk_meta_t* vector_meta) {} int coord_point(const wk_meta_t* meta, const S2Point& point, uint32_t coord_id, wk_handler_t* handler) { int result; R2Point out = options_.projection()->Project(point); coord_[0] = out.x(); coord_[1] = out.y(); HANDLE_OR_RETURN(handler->coord(meta, coord_, coord_id, handler->handler_data)); return WK_CONTINUE; } void reset() { coord_id_ = -1; } int coord_in_series(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { coord_id_++; return coord_point(meta, point, coord_id_, handler); } int last_coord_in_series(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { coord_id_++; return coord_point(meta, point, coord_id_, handler); } int last_coord_in_loop(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { coord_id_++; return coord_point(meta, point, coord_id_, handler); } private: s2geography::util::Constructor::Options options_; }; class TessellatingExporter { public: TessellatingExporter(const s2geography::util::Constructor::Options& options): options_(options), tessellator_(new S2EdgeTessellator(options.projection(), options.tessellate_tolerance())) {} void set_vector_meta_flags(wk_vector_meta_t* vector_meta) {} void set_meta_flags(wk_meta_t* vector_meta) {} int coord_point(const wk_meta_t* meta, const S2Point& point, uint32_t coord_id, wk_handler_t* handler) { int result; R2Point out = options_.projection()->Project(point); coord_[0] = out.x(); coord_[1] = out.y(); HANDLE_OR_RETURN(handler->coord(meta, coord_, coord_id, handler->handler_data)); return WK_CONTINUE; } void reset() { points_out_.clear(); is_first_point_ = true; } int coord_in_series(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { if (is_first_point_) { is_first_point_ = false; most_recent_ = point; first_in_loop_ = point; return WK_CONTINUE; } tessellator_->AppendProjected(most_recent_, point, &points_out_); most_recent_ = point; return WK_CONTINUE; } int last_coord_in_series(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { int result; HANDLE_OR_RETURN(coord_in_series(meta, point, handler)); for (int i = 0; i < points_out_.size(); i++) { coord_[0] = points_out_[i].x(); coord_[1] = points_out_[i].y(); HANDLE_OR_RETURN(handler->coord(meta, coord_, i, handler->handler_data)); } return WK_CONTINUE; } int last_coord_in_loop(const wk_meta_t* meta, const S2Point& point, wk_handler_t* handler) { int result; HANDLE_OR_RETURN(coord_in_series(meta, point, handler)); for (int i = 0; i < (points_out_.size() - 1); i++) { coord_[0] = points_out_[i].x(); coord_[1] = points_out_[i].y(); HANDLE_OR_RETURN(handler->coord(meta, coord_, i, handler->handler_data)); } if (!is_first_point_) { HANDLE_OR_RETURN(coord_point(meta, point, points_out_.size() - 1, handler)); } return WK_CONTINUE; } private: s2geography::util::Constructor::Options options_; std::unique_ptr tessellator_; bool is_first_point_; S2Point first_in_loop_; S2Point most_recent_; std::vector points_out_; double coord_[4]; }; template int handle_points(const s2geography::PointGeography& geog, EdgeExporterT* exporter, wk_handler_t* handler, uint32_t part_id = WK_PART_ID_NONE) { int result; wk_meta_t meta; WK_META_RESET(meta, WK_MULTIPOINT); meta.size = geog.Points().size(); exporter->set_meta_flags(&meta); wk_meta_t meta_child; WK_META_RESET(meta_child, WK_POINT); meta_child.size = 1; exporter->set_meta_flags(&meta_child); if (meta.size == 0) { meta_child.size = 0; HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); } else if (meta.size == 1) { HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); HANDLE_OR_RETURN(exporter->coord_point(&meta_child, geog.Points()[0], 0, handler)); HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); } else { HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); for (size_t i = 0; i < geog.Points().size(); i++) { HANDLE_OR_RETURN(handler->geometry_start(&meta_child, i, handler->handler_data)); HANDLE_OR_RETURN(exporter->coord_point(&meta_child, geog.Points()[i], 0, handler)); HANDLE_OR_RETURN(handler->geometry_end(&meta_child, i, handler->handler_data)); } HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); } return WK_CONTINUE; } template int handle_polylines(const s2geography::PolylineGeography& geog, EdgeExporterT* exporter, wk_handler_t* handler, uint32_t part_id = WK_PART_ID_NONE) { int result; wk_meta_t meta; WK_META_RESET(meta, WK_MULTILINESTRING); meta.size = geog.Polylines().size(); exporter->set_meta_flags(&meta); wk_meta_t meta_child; WK_META_RESET(meta_child, WK_LINESTRING); exporter->set_meta_flags(&meta_child); if (meta.size == 0) { meta_child.size = 0; HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); } else if (meta.size == 1) { const S2Polyline& poly = *geog.Polylines()[0]; meta_child.size = poly.num_vertices(); HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); exporter->reset(); for (int j = 0; j < poly.num_vertices(); j++) { if (j < (poly.num_vertices() - 1)) { HANDLE_OR_RETURN(exporter->coord_in_series(&meta_child, poly.vertex(j), handler)); } else { HANDLE_OR_RETURN(exporter->last_coord_in_series(&meta_child, poly.vertex(j), handler)); } } HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); } else { HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); for (size_t i = 0; i < geog.Polylines().size(); i++) { const S2Polyline& poly = *geog.Polylines()[i]; meta_child.size = poly.num_vertices(); HANDLE_OR_RETURN(handler->geometry_start(&meta_child, i, handler->handler_data)); exporter->reset(); for (int j = 0; j < poly.num_vertices(); j++) { if (j < (poly.num_vertices() - 1)) { HANDLE_OR_RETURN(exporter->coord_in_series(&meta_child, poly.vertex(j), handler)); } else { HANDLE_OR_RETURN(exporter->last_coord_in_series(&meta_child, poly.vertex(j), handler)); } } HANDLE_OR_RETURN(handler->geometry_end(&meta_child, i, handler->handler_data)); } HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); } return WK_CONTINUE; } template int handle_loop_shell(const S2Loop* loop, EdgeExporterT* exporter, const wk_meta_t* meta, uint32_t loop_id, wk_handler_t* handler) { int result; if (loop->num_vertices() == 0) { return handler->error("Unexpected S2Loop with 0 vertices", handler->handler_data); } HANDLE_OR_RETURN(handler->ring_start(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); exporter->reset(); for (int i = 0; i < loop->num_vertices(); i++) { HANDLE_OR_RETURN(exporter->coord_in_series(meta, loop->vertex(i), handler)); } HANDLE_OR_RETURN(exporter->last_coord_in_loop(meta, loop->vertex(loop->num_vertices()), handler)); HANDLE_OR_RETURN(handler->ring_end(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); return WK_CONTINUE; } template int handle_loop_hole(const S2Loop* loop, EdgeExporterT* exporter, const wk_meta_t* meta, uint32_t loop_id, wk_handler_t* handler) { int result; if (loop->num_vertices() == 0) { return handler->error("Unexpected S2Loop with 0 vertices", handler->handler_data); } HANDLE_OR_RETURN(handler->ring_start(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); exporter->reset(); for (int i = loop->num_vertices() - 1; i >= 0; i--) { HANDLE_OR_RETURN(exporter->coord_in_series(meta, loop->vertex(i), handler)); } HANDLE_OR_RETURN(exporter->last_coord_in_loop(meta, loop->vertex(loop->num_vertices() - 1), handler)); HANDLE_OR_RETURN(handler->ring_end(meta, loop->num_vertices() + 1, loop_id, handler->handler_data)); return WK_CONTINUE; } template int handle_shell(const S2Polygon& poly, EdgeExporterT* exporter, const wk_meta_t* meta, int loop_start, wk_handler_t* handler) { int result; const S2Loop* loop0 = poly.loop(loop_start); HANDLE_OR_RETURN(handle_loop_shell(loop0, exporter, meta, 0, handler)); uint32_t loop_id = 1; for (int j = loop_start + 1; j <= poly.GetLastDescendant(loop_start); j++) { const S2Loop* loop = poly.loop(j); if (loop->depth() == (loop0->depth() + 1)) { HANDLE_OR_RETURN(handle_loop_hole(loop, exporter, meta, loop_id, handler)); loop_id++; } } return WK_CONTINUE; } template int handle_polygon(const s2geography::PolygonGeography& geog, EdgeExporterT* exporter, wk_handler_t* handler, uint32_t part_id = WK_PART_ID_NONE) { const S2Polygon& poly = *geog.Polygon(); // find the outer shells (loop depth = 0, 2, 4, etc.) std::vector outer_shell_loop_ids; std::vector outer_shell_loop_sizes; outer_shell_loop_ids.reserve(poly.num_loops()); for (int i = 0; i < poly.num_loops(); i++) { if ((poly.loop(i)->depth() % 2) == 0) { outer_shell_loop_ids.push_back(i); } } // count the number of rings in each outer_shell_loop_sizes.reserve(outer_shell_loop_ids.size()); for (const auto loop_start : outer_shell_loop_ids) { const S2Loop* loop0 = poly.loop(loop_start); int num_loops = 1; for (int j = loop_start + 1; j <= poly.GetLastDescendant(loop_start); j++) { const S2Loop* loop = poly.loop(j); num_loops += loop->depth() == (loop0->depth() + 1); } outer_shell_loop_sizes.push_back(num_loops); } int result; wk_meta_t meta; WK_META_RESET(meta, WK_MULTIPOLYGON); meta.size = outer_shell_loop_ids.size(); exporter->set_meta_flags(&meta); wk_meta_t meta_child; WK_META_RESET(meta_child, WK_POLYGON); exporter->set_meta_flags(&meta_child); if (meta.size == 0) { meta_child.size = 0; HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); } else if (meta.size == 1) { meta_child.size = outer_shell_loop_sizes[0]; HANDLE_OR_RETURN(handler->geometry_start(&meta_child, part_id, handler->handler_data)); HANDLE_OR_RETURN(handle_shell(poly, exporter, &meta_child, outer_shell_loop_ids[0], handler)); HANDLE_OR_RETURN(handler->geometry_end(&meta_child, part_id, handler->handler_data)); } else { HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); for (size_t i = 0; i < outer_shell_loop_sizes.size(); i++) { meta_child.size = outer_shell_loop_sizes[i]; HANDLE_OR_RETURN(handler->geometry_start(&meta_child, i, handler->handler_data)); HANDLE_OR_RETURN(handle_shell(poly, exporter, &meta_child, outer_shell_loop_ids[i], handler)); HANDLE_OR_RETURN(handler->geometry_end(&meta_child, i, handler->handler_data)); } HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); } return WK_CONTINUE; } template int handle_collection(const s2geography::GeographyCollection& geog, EdgeExporterT* exporter, wk_handler_t* handler, uint32_t part_id = WK_PART_ID_NONE) { int result; wk_meta_t meta; WK_META_RESET(meta, WK_GEOMETRYCOLLECTION); meta.size = geog.Features().size(); exporter->set_meta_flags(&meta); HANDLE_OR_RETURN(handler->geometry_start(&meta, part_id, handler->handler_data)); for (size_t i = 0; i < geog.Features().size(); i++) { const s2geography::Geography* child_ptr = geog.Features()[i].get(); auto child_point = dynamic_cast(child_ptr); if (child_point != nullptr) { HANDLE_OR_RETURN(handle_points(*child_point, exporter, handler, i)); continue; } auto child_polyline = dynamic_cast(child_ptr); if (child_polyline != nullptr) { HANDLE_OR_RETURN(handle_polylines(*child_polyline, exporter, handler, i)); continue; } auto child_polygon = dynamic_cast(child_ptr); if (child_polygon != nullptr) { HANDLE_OR_RETURN(handle_polygon(*child_polygon, exporter, handler, i)); continue; } auto child_collection = dynamic_cast(child_ptr); if (child_collection != nullptr) { HANDLE_OR_RETURN(handle_collection(*child_collection, exporter, handler, i)); continue; } return handler->error("Unsupported S2Geography subclass", handler->handler_data); } HANDLE_OR_RETURN(handler->geometry_end(&meta, part_id, handler->handler_data)); return WK_CONTINUE; } template SEXP handle_geography_templ(SEXP data, EdgeExporterT* exporter, wk_handler_t* handler) { R_xlen_t n_features = Rf_xlength(data); wk_vector_meta_t vector_meta; WK_VECTOR_META_RESET(vector_meta, WK_GEOMETRY); vector_meta.size = n_features; vector_meta.flags |= WK_FLAG_DIMS_UNKNOWN; exporter->set_vector_meta_flags(&vector_meta); if (handler->vector_start(&vector_meta, handler->handler_data) == WK_CONTINUE) { int result; SEXP item; for (R_xlen_t i = 0; i < n_features; i++) { item = VECTOR_ELT(data, i); HANDLE_CONTINUE_OR_BREAK(handler->feature_start(&vector_meta, i, handler->handler_data)); if (item == R_NilValue) { HANDLE_CONTINUE_OR_BREAK(handler->null_feature(handler->handler_data)); } else { auto item_ptr = reinterpret_cast(R_ExternalPtrAddr(item)); const s2geography::Geography* geog_ptr = &item_ptr->Geog(); auto child_point = dynamic_cast(geog_ptr); if (child_point != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_points(*child_point, exporter, handler)); } else { auto child_polyline = dynamic_cast(geog_ptr); if (child_polyline != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_polylines(*child_polyline, exporter, handler)); } else { auto child_polygon = dynamic_cast(geog_ptr); if (child_polygon != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_polygon(*child_polygon, exporter, handler)); } else { auto child_collection = dynamic_cast(geog_ptr); if (child_collection != nullptr) { HANDLE_CONTINUE_OR_BREAK(handle_collection(*child_collection, exporter, handler)); } else { HANDLE_CONTINUE_OR_BREAK( handler->error("Unsupported S2Geography subclass", handler->handler_data)); } } } } } if (handler->feature_end(&vector_meta, i, handler->handler_data) == WK_ABORT) { break; } } } SEXP result = PROTECT(handler->vector_end(&vector_meta, handler->handler_data)); UNPROTECT(1); return result; } template void finalize_cpp_xptr(SEXP xptr) { auto ptr = reinterpret_cast(R_ExternalPtrAddr(xptr)); if (ptr != nullptr) { delete ptr; } } SEXP handle_geography(SEXP data, wk_handler_t* handler) { SEXP projection_xptr = Rf_getAttrib(data, Rf_install("s2_projection")); SEXP result; if (projection_xptr != R_NilValue) { auto projection = reinterpret_cast(R_ExternalPtrAddr(projection_xptr)); s2geography::util::Constructor::Options options; options.set_projection(projection); auto exporter = new SimpleExporter(options); SEXP exporter_shelter = PROTECT(R_MakeExternalPtr(exporter, R_NilValue, R_NilValue)); R_RegisterCFinalizer(exporter_shelter, &finalize_cpp_xptr); result = PROTECT(handle_geography_templ(data, exporter, handler)); UNPROTECT(2); } else { auto exporter = new S2Exporter(); SEXP exporter_shelter = PROTECT(R_MakeExternalPtr(exporter, R_NilValue, R_NilValue)); R_RegisterCFinalizer(exporter_shelter, &finalize_cpp_xptr); result = PROTECT(handle_geography_templ(data, exporter, handler)); UNPROTECT(2); } return result; } extern "C" SEXP c_s2_handle_geography(SEXP data, SEXP handler_xptr) { return wk_handler_run_xptr(&handle_geography, data, handler_xptr); } SEXP handle_geography_tessellated(SEXP data, wk_handler_t* handler) { SEXP projection_xptr = Rf_getAttrib(data, Rf_install("s2_projection")); auto projection = reinterpret_cast(R_ExternalPtrAddr(projection_xptr)); SEXP tessellate_tolerance_sexp = Rf_getAttrib(data, Rf_install("s2_tessellate_tol")); double tessellate_tol = REAL(tessellate_tolerance_sexp)[0]; s2geography::util::Constructor::Options options; options.set_projection(projection); options.set_tessellate_tolerance(S1Angle::Radians(tessellate_tol)); auto exporter = new TessellatingExporter(options); SEXP exporter_shelter = PROTECT(R_MakeExternalPtr(exporter, R_NilValue, R_NilValue)); R_RegisterCFinalizer(exporter_shelter, &finalize_cpp_xptr); SEXP result = PROTECT(handle_geography_templ(data, exporter, handler)); UNPROTECT(2); return result; } class OrthographicProjection: public S2::Projection { public: OrthographicProjection(const S2LatLng& centre): centre_(centre) { z_axis_ = S2Point(0, 0, 1); y_axis_ = S2Point(0, 1, 0); } // Converts a point on the sphere to a projected 2D point. R2Point Project(const S2Point& p) const { S2Point out = S2::Rotate(p, z_axis_, -centre_.lng()); out = S2::Rotate(out, y_axis_, centre_.lat()); return R2Point(out.y(), out.z()); } // Converts a projected 2D point to a point on the sphere. S2Point Unproject(const R2Point& p) const { double y = p.x(); double z = p.y(); double x = sqrt(1.0 - y * y - z * z); S2Point pp(x, y, z); S2Point out = S2::Rotate(pp, y_axis_, -centre_.lat()); out = S2::Rotate(out, z_axis_, centre_.lng()); return out; } R2Point FromLatLng(const S2LatLng& ll) const { return Project(ll.ToPoint()); } S2LatLng ToLatLng(const R2Point& p) const { return S2LatLng(Unproject(p)); } R2Point wrap_distance() const {return R2Point(0, 0); } private: S2LatLng centre_; S2Point z_axis_; S2Point y_axis_; }; extern "C" SEXP c_s2_handle_geography_tessellated(SEXP data, SEXP handler_xptr) { return wk_handler_run_xptr(&handle_geography_tessellated, data, handler_xptr); } extern "C" SEXP c_s2_projection_plate_carree(SEXP x_scale_sexp) { double x_scale = REAL(x_scale_sexp)[0]; auto projection = new S2::PlateCarreeProjection(x_scale); SEXP xptr = PROTECT(R_MakeExternalPtr(projection, R_NilValue, R_NilValue)); R_RegisterCFinalizer(xptr, &finalize_cpp_xptr); UNPROTECT(1); return xptr; } extern "C" SEXP c_s2_projection_mercator(SEXP x_scale_sexp) { double x_scale = REAL(x_scale_sexp)[0]; auto projection = new S2::MercatorProjection(x_scale); SEXP xptr = PROTECT(R_MakeExternalPtr(projection, R_NilValue, R_NilValue)); R_RegisterCFinalizer(xptr, &finalize_cpp_xptr); UNPROTECT(1); return xptr; } extern "C" SEXP c_s2_projection_orthographic(SEXP centre_sexp) { S2LatLng centre = S2LatLng::FromDegrees(REAL(centre_sexp)[1], REAL(centre_sexp)[0]); auto projection = new OrthographicProjection(centre); SEXP xptr = PROTECT(R_MakeExternalPtr(projection, R_NilValue, R_NilValue)); R_RegisterCFinalizer(xptr, &finalize_cpp_xptr); UNPROTECT(1); return xptr; } s2/src/s2-transformers.cpp0000644000176200001440000002721314530411473015147 0ustar liggesusers #include "s2/s2shape_index_buffered_region.h" #include "s2/s2region_coverer.h" #include "s2-options.h" #include "geography-operator.h" #include using namespace Rcpp; class BooleanOperationOp: public BinaryGeographyOperator { public: BooleanOperationOp(S2BooleanOperation::OpType opType, List s2options): opType(opType) { GeographyOperationOptions options(s2options); this->geography_options = options.geographyOptions(); } SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { std::unique_ptr geog_out = s2geography::s2_boolean_operation( feature1->Index(), feature2->Index(), this->opType, this->geography_options); return RGeography::MakeXPtr(std::move(geog_out)); } private: S2BooleanOperation::OpType opType; s2geography::GlobalOptions geography_options; }; // [[Rcpp::export]] List cpp_s2_intersection(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::INTERSECTION, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_union(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::UNION, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_difference(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::DIFFERENCE, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_sym_difference(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::SYMMETRIC_DIFFERENCE, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); s2geography::S2CoverageUnionAggregator agg(options.geographyOptions()); SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); agg.Add(feature->Geog()); } } std::unique_ptr geog_out = agg.Finalize(); return List::create(RGeography::MakeXPtr(std::move(geog_out))); } // [[Rcpp::export]] List cpp_s2_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); s2geography::S2UnionAggregator agg(options.geographyOptions()); SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); agg.Add(feature->Geog()); } } std::unique_ptr geog_out = agg.Finalize(); return List::create(RGeography::MakeXPtr(std::move(geog_out))); } // [[Rcpp::export]] List cpp_s2_centroid_agg(List geog, bool naRm) { s2geography::CentroidAggregator agg; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); agg.Add(feature->Geog()); } } S2Point centroid = agg.Finalize(); List output(1); if (centroid.Norm2() == 0) { output[0] = RGeography::MakeXPtr(RGeography::MakePoint()); } else { output[0] = RGeography::MakeXPtr(RGeography::MakePoint(centroid)); } return output; } // [[Rcpp::export]] List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); s2geography::RebuildAggregator agg(options.geographyOptions()); std::vector> geographies; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); agg.Add(feature->Geog()); } } auto geog_out = agg.Finalize(); return List::create(RGeography::MakeXPtr(std::move(geog_out))); } // [[Rcpp::export]] List cpp_s2_closest_point(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { S2Point pt = s2geography::s2_closest_point(feature1->Index(), feature2->Index()); if (pt.Norm2() == 0) { return RGeography::MakeXPtr(RGeography::MakePoint()); } else { return RGeography::MakeXPtr(RGeography::MakePoint(pt)); } } }; Op op; return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { std::pair pts = s2geography::s2_minimum_clearance_line_between( feature1->Index(), feature2->Index() ); if (pts.first.Norm2() == 0) { return RGeography::MakeXPtr(RGeography::MakePoint()); } std::vector vertices(2); vertices[0] = pts.first; vertices[1] = pts.second; if (pts.first == pts.second) { return RGeography::MakeXPtr(RGeography::MakePoint(std::move(vertices))); } else { std::vector vertices(2); vertices[0] = pts.first; vertices[1] = pts.second; std::unique_ptr polyline = absl::make_unique(); polyline->Init(vertices); return RGeography::MakeXPtr(RGeography::MakePolyline(std::move(polyline))); } } }; Op op; return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_centroid(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { S2Point centroid = s2geography::s2_centroid(feature->Geog()); if (centroid.Norm2() == 0) { return RGeography::MakeXPtr(RGeography::MakePoint()); } else { return RGeography::MakeXPtr(RGeography::MakePoint(centroid.Normalize())); } } }; Op op; return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_point_on_surface(List geog) { class Op: public UnaryGeographyOperator { public: S2RegionCoverer coverer; SEXP processFeature(XPtr feature, R_xlen_t i) { S2Point result = s2geography::s2_point_on_surface(feature->Geog(), coverer); if (result.Norm2() == 0) { return RGeography::MakeXPtr(RGeography::MakePoint()); } else { return RGeography::MakeXPtr(RGeography::MakePoint(result)); } } }; Op op; return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_boundary(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { std::unique_ptr result = s2geography::s2_boundary(feature->Geog()); return RGeography::MakeXPtr(std::move(result)); } }; Op op; return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_rebuild(List geog, List s2options) { class Op: public UnaryGeographyOperator { public: Op(List s2options) { GeographyOperationOptions options(s2options); this->options = options.geographyOptions(); } SEXP processFeature(XPtr feature, R_xlen_t i) { std::unique_ptr ptr = s2geography::s2_rebuild( feature->Geog(), this->options ); return RGeography::MakeXPtr(std::move(ptr)); } private: s2geography::GlobalOptions options; }; Op op(s2options); return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_unary_union(List geog, List s2options) { class Op: public UnaryGeographyOperator { public: Op(List s2options) { GeographyOperationOptions options(s2options); this->geographyOptions = options.geographyOptions(); } SEXP processFeature(XPtr feature, R_xlen_t i) { std::unique_ptr geog_out = s2geography::s2_unary_union(feature->Index(), this->geographyOptions); return RGeography::MakeXPtr(std::move(geog_out)); } private: S2BooleanOperation::Options options; GeographyOperationOptions::LayerOptions layerOptions; s2geography::GlobalOptions geographyOptions; }; Op op(s2options); return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_interpolate_normalized(List geog, NumericVector distanceNormalized) { class Op: public UnaryGeographyOperator { public: NumericVector distanceNormalized; Op(NumericVector distanceNormalized): distanceNormalized(distanceNormalized) {} SEXP processFeature(XPtr feature, R_xlen_t i) { if (NumericVector::is_na(this->distanceNormalized[i])) { return R_NilValue; } if (s2geography::s2_is_empty(feature->Geog())) { return RGeography::MakeXPtr(RGeography::MakePoint()); } if (s2geography::s2_is_collection(feature->Geog())) { throw GeographyOperatorException("`x` must be a simple geography"); } else if (feature->Geog().dimension() != 1) { throw GeographyOperatorException("`x` must be a polyline"); } S2Point point = s2geography::s2_interpolate_normalized(feature->Geog(), this->distanceNormalized[i]); if (point.Norm2() == 0) { return RGeography::MakeXPtr(RGeography::MakePoint()); } else { return RGeography::MakeXPtr(RGeography::MakePoint(point)); } } }; Op op(distanceNormalized); return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int minLevel) { class Op: public UnaryGeographyOperator { public: NumericVector distance; S2RegionCoverer coverer; Op(NumericVector distance, int maxCells, int minLevel): distance(distance) { this->coverer.mutable_options()->set_max_cells(maxCells); if (minLevel > 0) { this->coverer.mutable_options()->set_min_level(minLevel); } } SEXP processFeature(XPtr feature, R_xlen_t i) { S2ShapeIndexBufferedRegion region; region.Init(&feature->Index().ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); S2CellUnion cellUnion; cellUnion = coverer.GetCovering(region); std::unique_ptr polygon = absl::make_unique(); polygon->InitToCellUnionBorder(cellUnion); return RGeography::MakeXPtr(RGeography::MakePolygon(std::move(polygon))); } }; Op op(distance, maxCells, minLevel); return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_convex_hull(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { std::unique_ptr geog_out = s2geography::s2_convex_hull(feature->Geog()); return RGeography::MakeXPtr(std::move(geog_out)); } }; Op op; return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_convex_hull_agg(List geog, bool naRm) { s2geography::S2ConvexHullAggregator agg; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { XPtr feature(item); agg.Add(feature->Geog()); } } return List::create(RGeography::MakeXPtr(agg.Finalize())); } s2/src/tests/0000755000176200001440000000000014530411473012531 5ustar liggesuserss2/src/tests/main.c0000644000176200001440000000047214530411473013624 0ustar liggesusers#include #include #include #include #include int main() { OpenSSL_add_all_digests(); OpenSSL_add_all_algorithms(); OpenSSL_add_all_ciphers(); ERR_load_crypto_strings(); SSL_load_error_strings(); SSL_library_init(); } s2/src/tests/soname.h0000644000176200001440000000026214530411473014164 0ustar liggesusers#include "openssl/opensslv.h" #define XSTR(x) STR(x) #define STR(x) #x #ifdef SHLIB_VERSION_NUMBER echo XSTR(SHLIB_VERSION_NUMBER) #else echo XSTR(OPENSSL_SHLIB_VERSION) #endif s2/src/s2-geography.cpp0000644000176200001440000000127114530411473014403 0ustar liggesusers #include "s2/s2latlng.h" #include "s2/s2polyline.h" #include "s2/s2polygon.h" #include "geography.h" #include using namespace Rcpp; // [[Rcpp::export]] List s2_geography_full(LogicalVector x) { // create single geography with full polygon std::unique_ptr l = absl::make_unique(S2Loop::kFull()); std::unique_ptr p = absl::make_unique(std::move(l)); return List::create(RGeography::MakeXPtr(RGeography::MakePolygon(std::move(p)))); } // [[Rcpp::export]] LogicalVector cpp_s2_geography_is_na(List geog) { LogicalVector out(geog.size()); for (R_xlen_t i = 0; i < geog.size(); i++) { out[i] = geog[i] == R_NilValue; } return out; } s2/src/s2geography/0000755000176200001440000000000014540350305013616 5ustar liggesuserss2/src/s2geography/linear-referencing.cc0000644000176200001440000000430414530411473017670 0ustar liggesusers #include "linear-referencing.h" #include "accessors.h" #include "build.h" #include "geography.h" namespace s2geography { double s2_project_normalized(const PolylineGeography& geog1, const S2Point& point) { if (geog1.Polylines().size() != 1 || point.Norm2() == 0) { return NAN; } int next_vertex; S2Point point_on_line = geog1.Polylines()[0]->Project(point, &next_vertex); return geog1.Polylines()[0]->UnInterpolate(point_on_line, next_vertex); } double s2_project_normalized(const Geography& geog1, const Geography& geog2) { if (geog1.dimension() != 1 || geog2.dimension() != 0) { return NAN; } S2Point point; for (int i = 0; i < geog2.num_shapes(); i++) { auto shape = geog2.Shape(i); for (int j = 0; j < shape->num_edges(); j++) { if (point.Norm2() != 0) { return NAN; } else { point = shape->edge(j).v0; } } } auto geog1_poly_ptr = dynamic_cast(&geog1); if (geog1_poly_ptr != nullptr) { return s2_project_normalized(*geog1_poly_ptr, point); } std::unique_ptr geog_poly = s2_rebuild(geog1, GlobalOptions()); return s2_project_normalized(*geog_poly, geog2); } S2Point s2_interpolate_normalized(const PolylineGeography& geog, double distance_norm) { if (s2_is_empty(geog)) { return S2Point(); } else if (geog.Polylines().size() == 1) { return geog.Polylines()[0]->Interpolate(distance_norm); } else { throw Exception("`geog` must contain 0 or 1 polyines"); } } S2Point s2_interpolate_normalized(const Geography& geog, double distance_norm) { if (s2_is_empty(geog)) { return S2Point(); } if (geog.dimension() != 1 || geog.num_shapes() > 1) { throw Exception("`geog` must be a single polyline"); } auto geog_poly_ptr = dynamic_cast(&geog); if (geog_poly_ptr != nullptr) { return s2_interpolate_normalized(*geog_poly_ptr, distance_norm); } std::unique_ptr geog_poly = s2_rebuild(geog, GlobalOptions()); return s2_interpolate_normalized(*geog_poly, distance_norm); } } // namespace s2geography s2/src/s2geography/geoarrow-imports.h0000644000176200001440000000327614530411473017322 0ustar liggesusers #pragma once #include // These definitions are part of an in-development geometry library // based on the Arrow C Data interface. Eventually that library // could be used here (or if it isn't, these concepts are at least // useful and allowed me to re-use the WKT and WKB readers and // writers that I refactored to suit that library). namespace s2geography { namespace util { enum Dimensions { DIMENSIONS_UNKNOWN = 10000, XY = 0, XYZ = 1000, XYM = 2000, XYZM = 3000 }; enum GeometryType { GEOMETRY_TYPE_UNKNOWN = 0, POINT = 1, LINESTRING = 2, POLYGON = 3, MULTIPOINT = 4, MULTILINESTRING = 5, MULTIPOLYGON = 6, GEOMETRYCOLLECTION = 7 }; } // namespace util class Handler { public: enum Result { CONTINUE = 0, ABORT = 1, ABORT_FEATURE = 2 }; virtual void new_geometry_type(util::GeometryType geometry_type) {} virtual void new_dimensions(util::Dimensions geometry_type) {} virtual Result array_start(const struct ArrowArray* array_data) { return Result::CONTINUE; } virtual Result feat_start() { return Result::CONTINUE; } virtual Result null_feat() { return Result::CONTINUE; } virtual Result geom_start(util::GeometryType geometry_type, int64_t size) { return Result::CONTINUE; } virtual Result ring_start(int64_t size) { return Result::CONTINUE; } virtual Result coords(const double* coord, int64_t n, int32_t coord_size) { return Result::CONTINUE; } virtual Result ring_end() { return Result::CONTINUE; } virtual Result geom_end() { return Result::CONTINUE; } virtual Result feat_end() { return Result::CONTINUE; } virtual Result array_end() { return Result::CONTINUE; } virtual ~Handler() {} }; } // namespace s2geography s2/src/s2geography/constructor.h0000644000176200001440000002612214530411473016362 0ustar liggesusers #pragma once #include #include #include "geoarrow-imports.h" #include "geography.h" namespace s2geography { namespace util { class Constructor : public Handler { public: class Options { public: Options() : oriented_(false), check_(true), tessellate_tolerance_(S1Angle::Infinity()) {} bool oriented() const { return oriented_; } void set_oriented(bool oriented) { oriented_ = oriented; } bool check() const { return check_; } void set_check(bool check) { check_ = check; } S2::Projection* projection() const { return projection_; } void set_projection(S2::Projection* projection) { projection_ = projection; } S1Angle tessellate_tolerance() const { return tessellate_tolerance_; } void set_tessellate_tolerance(S1Angle tessellate_tolerance) { tessellate_tolerance_ = tessellate_tolerance; } private: bool oriented_; bool check_; S2::Projection* projection_; S1Angle tessellate_tolerance_; }; Constructor(const Options& options) : options_(options) { if (options.projection() != nullptr) { this->tessellator_ = absl::make_unique(options.projection(), options.tessellate_tolerance()); } } virtual ~Constructor() {} virtual Result coords(const double* coord, int64_t n, int32_t coord_size) { if (coord_size == 3) { for (int64_t i = 0; i < n; i++) { input_points_.push_back( S2Point( coord[i * coord_size], coord[i * coord_size + 1], coord[i * coord_size + 2] ) ); } } else { for (int64_t i = 0; i < n; i++) { input_points_.push_back(S2Point(coord[i * coord_size], coord[i * coord_size + 1], 0)); } } return Result::CONTINUE; } virtual std::unique_ptr finish() = 0; protected: std::vector input_points_; std::vector points_; Options options_; std::unique_ptr tessellator_; void finish_points() { points_.clear(); points_.reserve(input_points_.size()); if (options_.projection() == nullptr) { for (const auto& pt: input_points_) { points_.push_back(pt); } } else if (options_.tessellate_tolerance() != S1Angle::Infinity()) { for (size_t i = 1; i < input_points_.size(); i++) { const S2Point& pt0(input_points_[i - 1]); const S2Point& pt1(input_points_[i]); tessellator_->AppendUnprojected(R2Point(pt0.x(), pt0.y()), R2Point(pt1.x(), pt1.y()), &points_); } } else { for (const auto& pt: input_points_) { points_.push_back(options_.projection()->Unproject(R2Point(pt.x(), pt.y()))); } } input_points_.clear(); } }; class PointConstructor : public Constructor { public: PointConstructor(const Options& options) : Constructor(options) {} Result geom_start(util::GeometryType geometry_type, int64_t size) { if (size != 0 && geometry_type != util::GeometryType::POINT && geometry_type != util::GeometryType::MULTIPOINT && geometry_type != util::GeometryType::GEOMETRYCOLLECTION) { throw Exception( "PointConstructor input must be empty, point, multipoint, or " "collection"); } if (size > 0) { points_.reserve(points_.size() + size); } return Result::CONTINUE; } Result coords(const double* coord, int64_t n, int32_t coord_size) { for (int64_t i = 0; i < n; i++) { if (coord_empty(coord + (i * coord_size), coord_size)) { continue; } if (options_.projection() == nullptr) { S2Point pt(coord[i * coord_size], coord[i * coord_size + 1], coord[i * coord_size + 2]); points_.push_back(pt); } else { R2Point pt(coord[i * coord_size], coord[i * coord_size + 1]); points_.push_back(options_.projection()->Unproject(pt)); } } return Result::CONTINUE; } std::unique_ptr finish() { auto result = absl::make_unique(std::move(points_)); points_.clear(); return std::unique_ptr(result.release()); } private: bool coord_empty(const double* coord, int32_t coord_size) { for (int32_t i = 0; i < coord_size; i++) { if (!std::isnan(coord[i])) { return false; } } return true; } }; class PolylineConstructor : public Constructor { public: PolylineConstructor(const Options& options) : Constructor(options) {} Result geom_start(util::GeometryType geometry_type, int64_t size) { if (size != 0 && geometry_type != util::GeometryType::LINESTRING && geometry_type != util::GeometryType::MULTILINESTRING && geometry_type != util::GeometryType::GEOMETRYCOLLECTION) { throw Exception( "PolylineConstructor input must be empty, linestring, " "multilinestring, or collection"); } if (size > 0 && geometry_type == util::GeometryType::LINESTRING) { input_points_.reserve(size); } return Result::CONTINUE; } Result geom_end() { finish_points(); if (!points_.empty()) { auto polyline = absl::make_unique(); polyline->Init(std::move(points_)); // Previous version of s2 didn't check for this, so in // this check is temporarily disabled to avoid mayhem in // reverse dependency checks. // if (options_.check() && !polyline->IsValid()) { // polyline->FindValidationError(&error_); // throw Exception(error_.text()); // } polylines_.push_back(std::move(polyline)); } return Result::CONTINUE; } std::unique_ptr finish() { std::unique_ptr result; if (polylines_.empty()) { result = absl::make_unique(); } else { result = absl::make_unique(std::move(polylines_)); polylines_.clear(); } return std::unique_ptr(result.release()); } private: std::vector> polylines_; S2Error error_; }; class PolygonConstructor : public Constructor { public: PolygonConstructor(const Options& options) : Constructor(options) {} Result ring_start(int64_t size) { input_points_.clear(); if (size > 0) { input_points_.reserve(size); } return Result::CONTINUE; } Result ring_end() { finish_points(); if (points_.empty()) { return Result::CONTINUE; } points_.pop_back(); auto loop = absl::make_unique(); loop->set_s2debug_override(S2Debug::DISABLE); loop->Init(std::move(points_)); if (!options_.oriented()) { loop->Normalize(); } if (options_.check() && !loop->IsValid()) { std::stringstream err; err << "Loop " << (loops_.size()) << " is not valid: "; loop->FindValidationError(&error_); err << error_.text(); throw Exception(err.str()); } loops_.push_back(std::move(loop)); points_.clear(); return Result::CONTINUE; } std::unique_ptr finish() { auto polygon = absl::make_unique(); polygon->set_s2debug_override(S2Debug::DISABLE); if (options_.oriented()) { polygon->InitOriented(std::move(loops_)); } else { polygon->InitNested(std::move(loops_)); } loops_.clear(); if (options_.check() && !polygon->IsValid()) { polygon->FindValidationError(&error_); throw Exception(error_.text()); } auto result = absl::make_unique(std::move(polygon)); return std::unique_ptr(result.release()); } private: std::vector> loops_; S2Error error_; }; class CollectionConstructor : public Constructor { public: CollectionConstructor(const Options& options) : Constructor(options), point_constructor_(options), polyline_constructor_(options), polygon_constructor_(options), collection_constructor_(nullptr), level_(0) {} Result geom_start(util::GeometryType geometry_type, int64_t size) { level_++; if (level_ == 1 && geometry_type == util::GeometryType::GEOMETRYCOLLECTION) { active_constructor_ = nullptr; return Result::CONTINUE; } if (active_constructor_ != nullptr) { active_constructor_->geom_start(geometry_type, size); return Result::CONTINUE; } switch (geometry_type) { case util::GeometryType::POINT: case util::GeometryType::MULTIPOINT: active_constructor_ = &point_constructor_; break; case util::GeometryType::LINESTRING: case util::GeometryType::MULTILINESTRING: active_constructor_ = &polyline_constructor_; break; case util::GeometryType::POLYGON: case util::GeometryType::MULTIPOLYGON: active_constructor_ = &polygon_constructor_; break; case util::GeometryType::GEOMETRYCOLLECTION: this->collection_constructor_ = absl::make_unique(options_); this->active_constructor_ = this->collection_constructor_.get(); break; default: throw Exception("CollectionConstructor: unsupported geometry type"); } active_constructor_->geom_start(geometry_type, size); return Result::CONTINUE; } Result ring_start(int64_t size) { active_constructor_->ring_start(size); return Result::CONTINUE; } Result coords(const double* coord, int64_t n, int32_t coord_size) { active_constructor_->coords(coord, n, coord_size); return Result::CONTINUE; } Result ring_end() { active_constructor_->ring_end(); return Result::CONTINUE; } Result geom_end() { level_--; if (level_ >= 1) { active_constructor_->geom_end(); } if (level_ == 1) { auto feature = active_constructor_->finish(); features_.push_back(std::move(feature)); active_constructor_ = nullptr; } return Result::CONTINUE; } std::unique_ptr finish() { auto result = absl::make_unique(std::move(features_)); features_.clear(); return std::unique_ptr(result.release()); } private: PointConstructor point_constructor_; PolylineConstructor polyline_constructor_; PolygonConstructor polygon_constructor_; std::unique_ptr collection_constructor_; protected: Constructor* active_constructor_; int level_; std::vector> features_; }; class FeatureConstructor : public CollectionConstructor { public: FeatureConstructor(const Options& options) : CollectionConstructor(options) {} Result feat_start() { active_constructor_ = nullptr; level_ = 0; features_.clear(); geom_start(util::GeometryType::GEOMETRYCOLLECTION, 1); return Result::CONTINUE; } std::unique_ptr finish_feature() { geom_end(); if (features_.empty()) { return absl::make_unique(); } else { std::unique_ptr feature = std::move(features_.back()); if (feature == nullptr) { throw Exception("finish_feature() generated nullptr"); } features_.pop_back(); return feature; } } }; } // namespace util } // namespace s2geography s2/src/s2geography/geography.cc0000644000176200001440000001422114530411473016115 0ustar liggesusers #include "geography.h" #include #include #include #include #include #include #include #include #include #include using namespace s2geography; // This class is a shim to allow a class to return a std::unique_ptr(), // which is required by MutableS2ShapeIndex::Add(), without copying the // underlying data. S2Shape instances do not typically own their data (e.g., // S2Polygon::Shape), so this does not change the general relationship (that // anything returned by Geography::Shape() is only valid within the scope of // the Geography). Note that this class is also available (but not exposed) in // s2/s2shapeutil_coding.cc. class S2ShapeWrapper : public S2Shape { public: S2ShapeWrapper(S2Shape* shape) : shape_(shape) {} int num_edges() const { return shape_->num_edges(); } Edge edge(int edge_id) const { return shape_->edge(edge_id); } int dimension() const { return shape_->dimension(); } ReferencePoint GetReferencePoint() const { return shape_->GetReferencePoint(); } int num_chains() const { return shape_->num_chains(); } Chain chain(int chain_id) const { return shape_->chain(chain_id); } Edge chain_edge(int chain_id, int offset) const { return shape_->chain_edge(chain_id, offset); } ChainPosition chain_position(int edge_id) const { return shape_->chain_position(edge_id); } private: S2Shape* shape_; }; // Just like the S2ShapeWrapper, the S2RegionWrapper helps reconcile the // differences in lifecycle expectation between S2 and Geography. We often // need access to a S2Region to generalize algorithms; however, there are some // operations that need ownership of the region (e.g., the S2RegionUnion). In // Geography the assumption is that anything returned by a Geography is only // valid for the lifetime of the underlying Geography. A different design of // the algorithms implemented here might well make this unnecessary. class S2RegionWrapper : public S2Region { public: S2RegionWrapper(S2Region* region) : region_(region) {} S2Region* Clone() const { return region_->Clone(); } S2Cap GetCapBound() const { return region_->GetCapBound(); } S2LatLngRect GetRectBound() const { return region_->GetRectBound(); } void GetCellUnionBound(std::vector* cell_ids) const { return region_->GetCellUnionBound(cell_ids); } bool Contains(const S2Cell& cell) const { return region_->Contains(cell); } bool MayIntersect(const S2Cell& cell) const { return region_->MayIntersect(cell); } bool Contains(const S2Point& p) const { return region_->Contains(p); } private: S2Region* region_; }; void Geography::GetCellUnionBound(std::vector* cell_ids) const { MutableS2ShapeIndex index; for (int i = 0; i < num_shapes(); i++) { index.Add(Shape(i)); } MakeS2ShapeIndexRegion(&index).GetCellUnionBound( cell_ids); } std::unique_ptr PointGeography::Shape(int id) const { return absl::make_unique(points_); } std::unique_ptr PointGeography::Region() const { auto region = absl::make_unique(); for (const S2Point& point : points_) { region->Add(absl::make_unique(point)); } // because Rtools for R 3.6 on Windows complains about a direct // return region return std::unique_ptr(region.release()); } void PointGeography::GetCellUnionBound(std::vector* cell_ids) const { if (points_.size() < 10) { for (const S2Point& point : points_) { cell_ids->push_back(S2CellId(point)); } } else { Geography::GetCellUnionBound(cell_ids); } } int PolylineGeography::num_shapes() const { return polylines_.size(); } std::unique_ptr PolylineGeography::Shape(int id) const { return absl::make_unique(polylines_[id].get()); } std::unique_ptr PolylineGeography::Region() const { auto region = absl::make_unique(); for (const auto& polyline : polylines_) { region->Add(absl::make_unique(polyline.get())); } // because Rtools for R 3.6 on Windows complains about a direct // return region return std::unique_ptr(region.release()); } void PolylineGeography::GetCellUnionBound( std::vector* cell_ids) const { for (const auto& polyline : polylines_) { polyline->GetCellUnionBound(cell_ids); } } std::unique_ptr PolygonGeography::Shape(int id) const { return absl::make_unique(polygon_.get()); } std::unique_ptr PolygonGeography::Region() const { return absl::make_unique(polygon_.get()); } void PolygonGeography::GetCellUnionBound( std::vector* cell_ids) const { polygon_->GetCellUnionBound(cell_ids); } int GeographyCollection::num_shapes() const { return total_shapes_; } std::unique_ptr GeographyCollection::Shape(int id) const { int sum_shapes_ = 0; for (int i = 0; i < features_.size(); i++) { sum_shapes_ += num_shapes_[i]; if (id < sum_shapes_) { return features_[i]->Shape(id - sum_shapes_ + num_shapes_[i]); } } throw Exception("shape id out of bounds"); } std::unique_ptr GeographyCollection::Region() const { auto region = absl::make_unique(); for (const auto& feature : features_) { region->Add(feature->Region()); } // because Rtools for R 3.6 on Windows complains about a direct // return region return std::unique_ptr(region.release()); } int ShapeIndexGeography::num_shapes() const { return shape_index_.num_shape_ids(); } std::unique_ptr ShapeIndexGeography::Shape(int id) const { S2Shape* shape = shape_index_.shape(id); return std::unique_ptr(new S2ShapeWrapper(shape)); } std::unique_ptr ShapeIndexGeography::Region() const { auto region = absl::make_unique>(&shape_index_); // because Rtools for R 3.6 on Windows complains about a direct // return region return std::unique_ptr(region.release()); } s2/src/s2geography/predicates.cc0000644000176200001440000000625114530411473016257 0ustar liggesusers #include "predicates.h" #include #include #include #include "accessors.h" namespace s2geography { bool s2_intersects(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options) { return S2BooleanOperation::Intersects(geog1.ShapeIndex(), geog2.ShapeIndex(), options); } bool s2_equals(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options) { return S2BooleanOperation::Equals(geog1.ShapeIndex(), geog2.ShapeIndex(), options); } bool s2_contains(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options) { if (s2_is_empty(geog2)) { return false; } else { return S2BooleanOperation::Contains(geog1.ShapeIndex(), geog2.ShapeIndex(), options); } } // Note that 'touches' can be implemeted using: // // S2BooleanOperation::Options closedOptions = options; // closedOptions.set_polygon_model(S2BooleanOperation::PolygonModel::CLOSED); // closedOptions.set_polyline_model(S2BooleanOperation::PolylineModel::CLOSED); // S2BooleanOperation::Options openOptions = options; // openOptions.set_polygon_model(S2BooleanOperation::PolygonModel::OPEN); // openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN); // s2_intersects(geog1, geog2, closed_options) && // !s2_intersects(geog1, geog2, open_options); // // ...it isn't implemented here because the options creation should be done // outside of any loop. bool s2_intersects_box(const ShapeIndexGeography& geog1, const S2LatLngRect& rect, const S2BooleanOperation::Options& options, double tolerance) { // 99% of this is making a S2Loop out of a S2LatLngRect // This should probably be implemented elsewhere S2::PlateCarreeProjection projection(180); S2EdgeTessellator tessellator(&projection, S1Angle::Degrees(tolerance)); std::vector vertices; tessellator.AppendUnprojected( R2Point(rect.lng_lo().degrees(), rect.lat_lo().degrees()), R2Point(rect.lng_hi().degrees(), rect.lat_lo().degrees()), &vertices); tessellator.AppendUnprojected( R2Point(rect.lng_hi().degrees(), rect.lat_lo().degrees()), R2Point(rect.lng_hi().degrees(), rect.lat_hi().degrees()), &vertices); tessellator.AppendUnprojected( R2Point(rect.lng_hi().degrees(), rect.lat_hi().degrees()), R2Point(rect.lng_lo().degrees(), rect.lat_hi().degrees()), &vertices); tessellator.AppendUnprojected( R2Point(rect.lng_lo().degrees(), rect.lat_hi().degrees()), R2Point(rect.lng_lo().degrees(), rect.lat_lo().degrees()), &vertices); vertices.pop_back(); auto loop = absl::make_unique(std::move(vertices)); MutableS2ShapeIndex index; index.Add(std::move(loop)); return S2BooleanOperation::Intersects(geog1.ShapeIndex(), index, options); } } // namespace s2geography s2/src/s2geography/build.h0000644000176200001440000000541714530411473015100 0ustar liggesusers #pragma once #include #include #include #include "aggregator.h" #include "geography.h" namespace s2geography { class GlobalOptions { public: enum OutputAction { OUTPUT_ACTION_INCLUDE, OUTPUT_ACTION_IGNORE, OUTPUT_ACTION_ERROR }; GlobalOptions() : point_layer_action(OUTPUT_ACTION_INCLUDE), polyline_layer_action(OUTPUT_ACTION_INCLUDE), polygon_layer_action(OUTPUT_ACTION_INCLUDE) {} S2BooleanOperation::Options boolean_operation; S2Builder::Options builder; s2builderutil::S2PointVectorLayer::Options point_layer; s2builderutil::S2PolylineVectorLayer::Options polyline_layer; s2builderutil::S2PolygonLayer::Options polygon_layer; OutputAction point_layer_action; OutputAction polyline_layer_action; OutputAction polygon_layer_action; }; std::unique_ptr s2_boolean_operation( const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, S2BooleanOperation::OpType op_type, const GlobalOptions& options); std::unique_ptr s2_unary_union(const ShapeIndexGeography& geog, const GlobalOptions& options); std::unique_ptr s2_rebuild(const Geography& geog, const GlobalOptions& options); std::unique_ptr s2_build_point(const Geography& geog); std::unique_ptr s2_build_polyline(const Geography& geog); std::unique_ptr s2_build_polygon(const Geography& geog); class RebuildAggregator : public Aggregator> { public: RebuildAggregator(const GlobalOptions& options) : options_(options) {} void Add(const Geography& geog); std::unique_ptr Finalize(); private: GlobalOptions options_; ShapeIndexGeography index_; }; class S2CoverageUnionAggregator : public Aggregator> { public: S2CoverageUnionAggregator(const GlobalOptions& options) : options_(options) {} void Add(const Geography& geog); std::unique_ptr Finalize(); private: GlobalOptions options_; ShapeIndexGeography index_; }; class S2UnionAggregator : public Aggregator> { public: S2UnionAggregator(const GlobalOptions& options) : options_(options) {} void Add(const Geography& geog); std::unique_ptr Finalize(); private: class Node { public: ShapeIndexGeography index1; ShapeIndexGeography index2; std::vector> data; std::unique_ptr Merge(const GlobalOptions& options); }; GlobalOptions options_; Node root_; std::vector> other_; }; } // namespace s2geography s2/src/s2geography/distance.h0000644000176200001440000000107214530411473015564 0ustar liggesusers #pragma once #include "geography.h" namespace s2geography { double s2_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); double s2_max_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); S2Point s2_closest_point(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); std::pair s2_minimum_clearance_line_between( const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2); } // namespace s2geography s2/src/s2geography/accessors.cc0000644000176200001440000001512414530411473016120 0ustar liggesusers #include "accessors.h" #include "build.h" #include "geography.h" namespace s2geography { bool s2_is_collection(const PolygonGeography& geog) { int num_outer_loops = 0; for (int i = 0; i < geog.Polygon()->num_loops(); i++) { S2Loop* loop = geog.Polygon()->loop(i); num_outer_loops += loop->depth() == 0; if (num_outer_loops > 1) { return true; } } return false; } bool s2_is_collection(const Geography& geog) { int dimension = s2_dimension(geog); if (dimension == -1) { return false; } if (dimension == 0) { return s2_num_points(geog) > 1; } if (dimension == 1) { int num_chains = 0; for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); num_chains += shape->num_chains(); if (num_chains > 1) { return true; } } return false; } auto polygon_geog_ptr = dynamic_cast(&geog); if (polygon_geog_ptr != nullptr) { return s2_is_collection(*polygon_geog_ptr); } else { std::unique_ptr built = s2_build_polygon(geog); return s2_is_collection(*built); } } int s2_dimension(const Geography& geog) { int dimension = geog.dimension(); if (dimension != -1) { return dimension; } for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); if (shape->dimension() > dimension) { dimension = shape->dimension(); } } return dimension; } int s2_num_points(const Geography& geog) { int num_points = 0; for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); switch (shape->dimension()) { case 0: case 2: num_points += shape->num_edges(); break; case 1: num_points += shape->num_edges() + shape->num_chains(); break; } } return num_points; } bool s2_is_empty(const Geography& geog) { for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); if (!shape->is_empty()) { return false; } } return true; } double s2_area(const PolygonGeography& geog) { return geog.Polygon()->GetArea(); } double s2_area(const GeographyCollection& geog) { double area = 0; for (auto& feature : geog.Features()) { area += s2_area(*feature); } return area; } double s2_area(const Geography& geog) { if (s2_dimension(geog) != 2) { return 0; } auto polygon_geog_ptr = dynamic_cast(&geog); if (polygon_geog_ptr != nullptr) { return s2_area(*polygon_geog_ptr); } auto collection_geog_ptr = dynamic_cast(&geog); if (collection_geog_ptr != nullptr) { return s2_area(*collection_geog_ptr); } std::unique_ptr built = s2_build_polygon(geog); return s2_area(*built); } double s2_length(const Geography& geog) { double length = 0; if (s2_dimension(geog) == 1) { for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); for (int j = 0; j < shape->num_edges(); j++) { S2Shape::Edge e = shape->edge(j); S1ChordAngle angle(e.v0, e.v1); length += angle.radians(); } } } return length; } double s2_perimeter(const Geography& geog) { double length = 0; if (s2_dimension(geog) == 2) { for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); for (int j = 0; j < shape->num_edges(); j++) { S2Shape::Edge e = shape->edge(j); S1ChordAngle angle(e.v0, e.v1); length += angle.radians(); } } } return length; } double s2_x(const Geography& geog) { double out = NAN; for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); if (shape->dimension() == 0 && shape->num_edges() == 1 && std::isnan(out)) { S2LatLng pt(shape->edge(0).v0); out = pt.lng().degrees(); } else if (shape->dimension() == 0 && shape->num_edges() == 1) { return NAN; } } return out; } double s2_y(const Geography& geog) { double out = NAN; for (int i = 0; i < geog.num_shapes(); i++) { std::unique_ptr shape = geog.Shape(i); if (shape->dimension() == 0 && shape->num_edges() == 1 && std::isnan(out)) { S2LatLng pt(shape->edge(0).v0); out = pt.lat().degrees(); } else if (shape->dimension() == 0 && shape->num_edges() == 1) { return NAN; } } return out; } bool s2_find_validation_error(const PolylineGeography& geog, S2Error* error) { for (const auto& polyline : geog.Polylines()) { if (polyline->FindValidationError(error)) { return true; } } return false; } bool s2_find_validation_error(const PolygonGeography& geog, S2Error* error) { return geog.Polygon()->FindValidationError(error); } bool s2_find_validation_error(const GeographyCollection& geog, S2Error* error) { for (const auto& feature : geog.Features()) { if (s2_find_validation_error(*feature, error)) { return true; } } return false; } bool s2_find_validation_error(const Geography& geog, S2Error* error) { if (geog.dimension() == 0) { error->Clear(); return false; } if (geog.dimension() == 1) { auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { return s2_find_validation_error(*poly_ptr, error); } else { try { auto poly = s2_build_polyline(geog); return s2_find_validation_error(*poly, error); } catch (Exception& e) { error->Init(S2Error::INTERNAL, "%s", e.what()); return true; } } } if (geog.dimension() == 2) { auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { return s2_find_validation_error(*poly_ptr, error); } else { try { auto poly = s2_build_polygon(geog); return s2_find_validation_error(*poly, error); } catch (Exception& e) { error->Init(S2Error::INTERNAL, "%s", e.what()); return true; } } } auto collection_ptr = dynamic_cast(&geog); if (collection_ptr != nullptr) { return s2_find_validation_error(*collection_ptr, error); } else { try { auto collection = s2_build_polygon(geog); return s2_find_validation_error(*collection, error); } catch (Exception& e) { error->Init(S2Error::INTERNAL, "%s", e.what()); return true; } } throw Exception( "s2_find_validation() error not implemented for this geography type"); } } // namespace s2geography s2/src/s2geography/accessors-geog.h0000644000176200001440000000145414530411473016702 0ustar liggesusers #pragma once #include #include "aggregator.h" #include "geography.h" namespace s2geography { S2Point s2_centroid(const Geography& geog); std::unique_ptr s2_boundary(const Geography& geog); std::unique_ptr s2_convex_hull(const Geography& geog); class CentroidAggregator : public Aggregator { public: void Add(const Geography& geog); void Merge(const CentroidAggregator& other); S2Point Finalize(); private: S2Point centroid_; }; class S2ConvexHullAggregator : public Aggregator> { public: void Add(const Geography& geog); std::unique_ptr Finalize(); private: S2ConvexHullQuery query_; std::vector> keep_alive_; }; } // namespace s2geography s2/src/s2geography/index.h0000644000176200001440000000627414530411473015112 0ustar liggesusers #pragma once #include #include "geography.h" namespace s2geography { // Unlike the ShapeIndexGeography, whose function is to index a single // Geography or index multiple Geography objects as if they were a single // Geography, the GeographyIndex exists to index a vector of Geography // objects (like a GEOSSTRTree index), providing (hopefully) rapid access to // possibly intersecting features. class GeographyIndex { public: GeographyIndex( MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) : index_(options) {} void Add(const Geography& geog, int value) { values_.reserve(values_.size() + geog.num_shapes()); for (int i = 0; i < geog.num_shapes(); i++) { int new_shape_id = index_.Add(geog.Shape(i)); values_.resize(new_shape_id + 1); values_[new_shape_id] = value; } } int value(int shape_id) const { return values_[shape_id]; } const MutableS2ShapeIndex& ShapeIndex() const { return index_; } MutableS2ShapeIndex& MutableShapeIndex() { return index_; } class Iterator { public: Iterator(const GeographyIndex* index) : index_(index), iterator_(&index_->ShapeIndex()) {} void Query(const std::vector& covering, std::unordered_set* indices) { for (const S2CellId& query_cell : covering) { Query(query_cell, indices); } } void Query(const S2CellId& cell_id, std::unordered_set* indices) { S2ShapeIndex::CellRelation relation = iterator_.Locate(cell_id); if (relation == S2ShapeIndex::CellRelation::INDEXED) { // We're in luck! these indexes have this cell in common // add all the shapes it contains as possible intersectors const S2ShapeIndexCell& index_cell = iterator_.cell(); for (int k = 0; k < index_cell.num_clipped(); k++) { int shape_id = index_cell.clipped(k).shape_id(); indices->insert(index_->value(shape_id)); } } else if (relation == S2ShapeIndex::CellRelation::SUBDIVIDED) { // Promising! the index has a child cell of iterator_.id() // (at which iterator_ is now positioned). Keep iterating until the // iterator is done OR we're no longer at a child cell of // iterator_.id(). The ordering of the iterator isn't guaranteed // anywhere in the documentation; however, this ordering would be // consistent with that of a Normalized S2CellUnion. while (!iterator_.done() && cell_id.contains(iterator_.id())) { // add all the shapes the child cell contains as possible intersectors const S2ShapeIndexCell& index_cell = iterator_.cell(); for (int k = 0; k < index_cell.num_clipped(); k++) { int shape_id = index_cell.clipped(k).shape_id(); indices->insert(index_->value(shape_id)); } // go to the next cell in the index iterator_.Next(); } } // else: relation == S2ShapeIndex::CellRelation::DISJOINT (do nothing) } private: const GeographyIndex* index_; MutableS2ShapeIndex::Iterator iterator_; }; private: MutableS2ShapeIndex index_; std::vector values_; }; } // namespace s2geography s2/src/s2geography/accessors-geog.cc0000644000176200001440000001250514530411473017037 0ustar liggesusers #include "accessors-geog.h" #include #include "accessors.h" #include "build.h" #include "geography.h" namespace s2geography { S2Point s2_centroid(const Geography& geog) { S2Point centroid(0, 0, 0); if (geog.dimension() == 0) { for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); for (int j = 0; j < shape->num_edges(); j++) { centroid += shape->edge(j).v0; } } return centroid.Normalize(); } if (geog.dimension() == 1) { for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); for (int j = 0; j < shape->num_edges(); j++) { S2Shape::Edge e = shape->edge(j); centroid += S2::TrueCentroid(e.v0, e.v1); } } return centroid.Normalize(); } if (geog.dimension() == 2) { auto polygon_ptr = dynamic_cast(&geog); if (polygon_ptr != nullptr) { centroid = polygon_ptr->Polygon()->GetCentroid(); } else { std::unique_ptr built = s2_build_polygon(geog); centroid = built->Polygon()->GetCentroid(); } return centroid.Normalize(); } auto collection_ptr = dynamic_cast(&geog); if (collection_ptr == nullptr) { throw Exception( "Can't compute s2_centroid() on custom collection geography"); } for (auto& feat : collection_ptr->Features()) { centroid += s2_centroid(*feat); } return centroid.Normalize(); } std::unique_ptr s2_boundary(const Geography& geog) { int dimension = s2_dimension(geog); if (dimension == 1) { std::vector endpoints; for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); if (shape->dimension() < 1) { continue; } endpoints.reserve(endpoints.size() + shape->num_chains() * 2); for (int j = 0; j < shape->num_chains(); j++) { S2Shape::Chain chain = shape->chain(j); if (chain.length > 0) { endpoints.push_back(shape->edge(chain.start).v0); endpoints.push_back(shape->edge(chain.start + chain.length - 1).v1); } } } return absl::make_unique(std::move(endpoints)); } if (dimension == 2) { std::vector> polylines; polylines.reserve(geog.num_shapes()); for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); if (shape->dimension() != 2) { throw Exception("Can't extract boundary from heterogeneous collection"); } for (int j = 0; j < shape->num_chains(); j++) { S2Shape::Chain chain = shape->chain(j); if (chain.length > 0) { std::vector points(chain.length + 1); points[0] = shape->edge(chain.start).v0; for (int k = 0; k < chain.length; k++) { points[k + 1] = shape->edge(chain.start + k).v1; } auto polyline = absl::make_unique(std::move(points)); polylines.push_back(std::move(polyline)); } } } return absl::make_unique(std::move(polylines)); } return absl::make_unique(); } std::unique_ptr s2_convex_hull(const Geography& geog) { S2ConvexHullAggregator agg; agg.Add(geog); return agg.Finalize(); } void CentroidAggregator::Add(const Geography& geog) { S2Point centroid = s2_centroid(geog); if (centroid.Norm2() > 0) { centroid_ += centroid.Normalize(); } } void CentroidAggregator::Merge(const CentroidAggregator& other) { centroid_ += other.centroid_; } S2Point CentroidAggregator::Finalize() { if (centroid_.Norm2() > 0) { return centroid_.Normalize(); } else { return centroid_; } } void S2ConvexHullAggregator::Add(const Geography& geog) { if (geog.dimension() == 0) { auto point_ptr = dynamic_cast(&geog); if (point_ptr != nullptr) { for (const auto& point : point_ptr->Points()) { query_.AddPoint(point); } } else { keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } return; } if (geog.dimension() == 1) { auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { for (const auto& polyline : poly_ptr->Polylines()) { query_.AddPolyline(*polyline); } } else { keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } return; } if (geog.dimension() == 2) { auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { query_.AddPolygon(*poly_ptr->Polygon()); } else { keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } return; } auto collection_ptr = dynamic_cast(&geog); if (collection_ptr != nullptr) { for (const auto& feature : collection_ptr->Features()) { Add(*feature); } } else { keep_alive_.push_back(s2_rebuild(geog, GlobalOptions())); Add(*keep_alive_.back()); } } std::unique_ptr S2ConvexHullAggregator::Finalize() { auto polygon = absl::make_unique(); polygon->Init(query_.GetConvexHull()); return absl::make_unique(std::move(polygon)); } } // namespace s2geography s2/src/s2geography/coverings.cc0000644000176200001440000000436314530411473016135 0ustar liggesusers #include "coverings.h" #include #include #include "accessors-geog.h" #include "accessors.h" #include "geography.h" namespace s2geography { S2Point s2_point_on_surface(const Geography& geog, S2RegionCoverer& coverer) { if (s2_is_empty(geog)) { return S2Point(); } int dimension = s2_dimension(geog); if (dimension == 2) { std::unique_ptr region = geog.Region(); S2CellUnion covering = coverer.GetInteriorCovering(*region); // Take center of cell with smallest level (biggest) int min_level = 31; S2Point pt; for (const S2CellId& id : covering) { if (id.level() < min_level) { // Already normalized pt = id.ToPoint(); min_level = id.level(); } } return pt; } if (dimension == 0) { // For point, return point closest to centroid S2Point centroid = s2_centroid(geog); S1Angle nearest_dist = S1Angle::Infinity(); S1Angle dist; S2Point closest_pt; for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); for (int j = 0; j < shape->num_edges(); j++) { S2Shape::Edge e = shape->edge(j); dist = S1Angle(e.v0, centroid); if (dist < nearest_dist) { nearest_dist = dist; closest_pt = e.v0; } } } return closest_pt; } throw Exception("s2_point_on_surface() not implemented for polyline"); } void s2_covering(const Geography& geog, std::vector* covering, S2RegionCoverer& coverer) { coverer.GetCovering(*geog.Region(), covering); } void s2_interior_covering(const Geography& geog, std::vector* covering, S2RegionCoverer& coverer) { coverer.GetInteriorCovering(*geog.Region(), covering); } void s2_covering_buffered(const ShapeIndexGeography& geog, double distance_radians, std::vector* covering, S2RegionCoverer& coverer) { S2ShapeIndexBufferedRegion region(&geog.ShapeIndex(), S1ChordAngle::Radians(distance_radians)); coverer.GetCovering(region, covering); } } // namespace s2geography s2/src/s2geography/coverings.h0000644000176200001440000000130114530411473015764 0ustar liggesusers #pragma once #include #include "geography.h" namespace s2geography { S2Point s2_point_on_surface(const Geography& geog, S2RegionCoverer& coverer); void s2_covering(const Geography& geog, std::vector* covering, S2RegionCoverer& coverer); void s2_interior_covering(const Geography& geog, std::vector* covering, S2RegionCoverer& coverer); void s2_covering_buffered(const ShapeIndexGeography& geog, double distance_radians, std::vector* covering, S2RegionCoverer& coverer); } // namespace s2geography s2/src/s2geography/build.cc0000644000176200001440000003467014530411473015241 0ustar liggesusers #include "build.h" #include #include #include #include #include #include #include "accessors.h" #include "geography.h" namespace s2geography { std::unique_ptr s2_geography_from_layers( std::vector points, std::vector> polylines, std::unique_ptr polygon, GlobalOptions::OutputAction point_layer_action, GlobalOptions::OutputAction polyline_layer_action, GlobalOptions::OutputAction polygon_layer_action) { // count non-empty dimensions bool has_polygon = !polygon->is_empty(); bool has_polylines = !polylines.empty(); bool has_points = !points.empty(); // use the requstested dimensions to produce the right kind of EMTPY bool include_polygon = polygon_layer_action == GlobalOptions::OUTPUT_ACTION_INCLUDE; bool include_polylines = polyline_layer_action == GlobalOptions::OUTPUT_ACTION_INCLUDE; bool include_points = point_layer_action == GlobalOptions::OUTPUT_ACTION_INCLUDE; if (has_polygon && polygon_layer_action == GlobalOptions::OUTPUT_ACTION_ERROR) { throw Exception("Output contained unexpected polygon"); } else if (has_polygon && polygon_layer_action == GlobalOptions::OUTPUT_ACTION_IGNORE) { has_polygon = false; } if (has_polylines && polyline_layer_action == GlobalOptions::OUTPUT_ACTION_ERROR) { throw Exception("Output contained unexpected polylines"); } else if (has_polylines && polyline_layer_action == GlobalOptions::OUTPUT_ACTION_IGNORE) { has_polylines = false; } if (has_points && point_layer_action == GlobalOptions::OUTPUT_ACTION_ERROR) { throw Exception("Output contained unexpected points"); } else if (has_points && point_layer_action == GlobalOptions::OUTPUT_ACTION_IGNORE) { has_points = false; } int non_empty_dimensions = has_polygon + has_polylines + has_points; int included_dimensions = include_polygon + include_polylines + include_points; // return mixed dimension output if (non_empty_dimensions > 1) { std::vector> features; if (has_points) { features.push_back(absl::make_unique(std::move(points))); } if (has_polylines) { features.push_back( absl::make_unique(std::move(polylines))); } if (has_polygon) { features.push_back( absl::make_unique(std::move(polygon))); } return absl::make_unique(std::move(features)); } // return single dimension output if (has_polygon || (included_dimensions == 1 && include_polygon)) { return absl::make_unique(std::move(polygon)); } else if (has_polylines || (included_dimensions == 1 && include_polylines)) { return absl::make_unique(std::move(polylines)); } else if (has_points || (included_dimensions == 1 && include_points)) { return absl::make_unique(std::move(points)); } else { return absl::make_unique(); } } std::unique_ptr s2_boolean_operation( const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, S2BooleanOperation::OpType op_type, const GlobalOptions& options) { // Create the data structures that will contain the output. std::vector points; std::vector> polylines; std::unique_ptr polygon = absl::make_unique(); s2builderutil::LayerVector layers(3); layers[0] = absl::make_unique( &points, options.point_layer); layers[1] = absl::make_unique( &polylines, options.polyline_layer); layers[2] = absl::make_unique( polygon.get(), options.polygon_layer); // specify the boolean operation S2BooleanOperation op(op_type, // Normalizing the closed set here is required for line // intersections to work in the same way as GEOS s2builderutil::NormalizeClosedSet(std::move(layers)), options.boolean_operation); // do the boolean operation, build layers, and check for errors S2Error error; if (!op.Build(geog1.ShapeIndex(), geog2.ShapeIndex(), &error)) { throw Exception(error.text()); } // construct output return s2_geography_from_layers( std::move(points), std::move(polylines), std::move(polygon), options.point_layer_action, options.polyline_layer_action, options.polygon_layer_action); } std::unique_ptr s2_unary_union(const PolygonGeography& geog, const GlobalOptions& options) { // A geography with invalid loops won't work with the S2BooleanOperation // we will use to accumulate (i.e., union) valid polygons, // so we need to rebuild each loop as its own polygon, // splitting crossed edges along the way. // Not exposing these options as an argument (except snap function) // because a particular combiation of them is required for this to work S2Builder::Options builder_options; builder_options.set_split_crossing_edges(true); builder_options.set_snap_function(options.boolean_operation.snap_function()); s2builderutil::S2PolygonLayer::Options layer_options; layer_options.set_edge_type(S2Builder::EdgeType::UNDIRECTED); layer_options.set_validate(false); // Rebuild all loops as polygons using the S2Builder() std::vector> loops; for (int i = 0; i < geog.Polygon()->num_loops(); i++) { std::unique_ptr loop = absl::make_unique(); S2Builder builder(builder_options); builder.StartLayer(absl::make_unique( loop.get(), layer_options)); builder.AddShape(S2Loop::Shape(geog.Polygon()->loop(i))); S2Error error; if (!builder.Build(&error)) { throw Exception(error.text()); } // Check if the builder created a polygon whose boundary contained more than // half the earth (and invert it if so) if (loop->GetArea() > (2 * M_PI)) { loop->Invert(); } loops.push_back(std::move(loop)); } // Accumulate the union of outer loops (but difference of inner loops) std::unique_ptr accumulated_polygon = absl::make_unique(); for (int i = 0; i < geog.Polygon()->num_loops(); i++) { std::unique_ptr polygon_result = absl::make_unique(); // Use original nesting to suggest if this loop should be unioned or diffed. // For valid polygons loops are arranged such that the biggest loop is on // the outside followed by holes such that the below strategy should work // (since we are just iterating along the original loop structure) if ((geog.Polygon()->loop(i)->depth() % 2) == 0) { polygon_result->InitToUnion(accumulated_polygon.get(), loops[i].get()); } else { polygon_result->InitToDifference(accumulated_polygon.get(), loops[i].get()); } accumulated_polygon.swap(polygon_result); } return absl::make_unique(std::move(accumulated_polygon)); } std::unique_ptr s2_unary_union(const ShapeIndexGeography& geog, const GlobalOptions& options) { // complex union only needed when a polygon is involved bool simple_union_ok = s2_is_empty(geog) || s2_dimension(geog) < 2; // valid polygons that are not part of a collection can also use a // simple union (common) if (geog.dimension() == 2) { S2Error validation_error; if (!s2_find_validation_error(geog, &validation_error)) { simple_union_ok = true; } } if (simple_union_ok) { ShapeIndexGeography empty; return s2_boolean_operation(geog, empty, S2BooleanOperation::OpType::UNION, options); } if (geog.dimension() == 2) { // If we've made it here we have an invalid polygon on our hands. auto poly_ptr = dynamic_cast(&geog); if (poly_ptr != nullptr) { return s2_unary_union(*poly_ptr, options); } else { auto poly = s2_build_polygon(geog); return s2_unary_union(*poly, options); } } throw Exception( "s2_unary_union() for multidimensional collections not implemented"); } std::unique_ptr s2_rebuild( const Geography& geog, const GlobalOptions& options, GlobalOptions::OutputAction point_layer_action, GlobalOptions::OutputAction polyline_layer_action, GlobalOptions::OutputAction polygon_layer_action) { // create the builder S2Builder builder(options.builder); // create the data structures that will contain the output std::vector points; std::vector> polylines; std::unique_ptr polygon = absl::make_unique(); // add shapes to the layer with the appropriate dimension builder.StartLayer(absl::make_unique( &points, options.point_layer)); for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); if (shape->dimension() == 0) { builder.AddShape(*shape); } } builder.StartLayer(absl::make_unique( &polylines, options.polyline_layer)); for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); if (shape->dimension() == 1) { builder.AddShape(*shape); } } builder.StartLayer(absl::make_unique( polygon.get(), options.polygon_layer)); for (int i = 0; i < geog.num_shapes(); i++) { auto shape = geog.Shape(i); if (shape->dimension() == 2) { builder.AddShape(*shape); } } // build the output S2Error error; if (!builder.Build(&error)) { throw Exception(error.text()); } // construct output return s2_geography_from_layers( std::move(points), std::move(polylines), std::move(polygon), options.point_layer_action, options.polyline_layer_action, options.polygon_layer_action); } std::unique_ptr s2_rebuild(const Geography& geog, const GlobalOptions& options) { return s2_rebuild(geog, options, options.point_layer_action, options.polyline_layer_action, options.polygon_layer_action); } std::unique_ptr s2_build_point(const Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, GlobalOptions(), GlobalOptions::OutputAction::OUTPUT_ACTION_INCLUDE, GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR); if (s2_is_empty(*geog_out)) { return absl::make_unique(); } else { return std::unique_ptr( dynamic_cast(geog_out.release())); } } std::unique_ptr s2_build_polyline(const Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, GlobalOptions(), GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, GlobalOptions::OutputAction::OUTPUT_ACTION_INCLUDE, GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR); if (s2_is_empty(*geog_out)) { return absl::make_unique(); } else { return std::unique_ptr( dynamic_cast(geog_out.release())); } } std::unique_ptr s2_build_polygon(const Geography& geog) { std::unique_ptr geog_out = s2_rebuild( geog, GlobalOptions(), GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, GlobalOptions::OutputAction::OUTPUT_ACTION_ERROR, GlobalOptions::OutputAction::OUTPUT_ACTION_INCLUDE); if (s2_is_empty(*geog_out)) { return absl::make_unique(); } else { return std::unique_ptr( dynamic_cast(geog_out.release())); } } void RebuildAggregator::Add(const Geography& geog) { index_.Add(geog); } std::unique_ptr RebuildAggregator::Finalize() { return s2_rebuild(index_, options_); } void S2CoverageUnionAggregator::Add(const Geography& geog) { index_.Add(geog); } std::unique_ptr S2CoverageUnionAggregator::Finalize() { ShapeIndexGeography empty_index_; return s2_boolean_operation(index_, empty_index_, S2BooleanOperation::OpType::UNION, options_); } void S2UnionAggregator::Add(const Geography& geog) { if (geog.dimension() == 0 || geog.dimension() == 1) { root_.index1.Add(geog); return; } if (other_.empty()) { other_.push_back(absl::make_unique()); other_.back()->index1.Add(geog); return; } Node* last = other_.back().get(); if (last->index1.num_shapes() == 0) { last->index1.Add(geog); } else if (last->index2.num_shapes() == 0) { last->index2.Add(geog); } else { other_.push_back(absl::make_unique()); other_.back()->index1.Add(geog); } } std::unique_ptr S2UnionAggregator::Node::Merge( const GlobalOptions& options) { return s2_boolean_operation(index1, index2, S2BooleanOperation::OpType::UNION, options); } std::unique_ptr S2UnionAggregator::Finalize() { for (int j = 0; j < 100; j++) { if (other_.size() <= 1) { break; } for (int64_t i = static_cast(other_.size()) - 1; i >= 1; i = i - 2) { // merge other_[i] with other_[i - 1] std::unique_ptr merged = other_[i]->Merge(options_); std::unique_ptr merged_prev = other_[i - 1]->Merge(options_); // erase the last two nodes other_.erase(other_.begin() + i - 1, other_.begin() + i + 1); // ..and replace it with a single node other_.push_back(absl::make_unique()); other_.back()->index1.Add(*merged); other_.back()->index2.Add(*merged_prev); // making sure to keep the underlying data alive other_.back()->data.push_back(std::move(merged)); other_.back()->data.push_back(std::move(merged_prev)); } } if (other_.empty()) { return root_.Merge(options_); } else { std::unique_ptr merged = other_[0]->Merge(options_); root_.index2.Add(*merged); return root_.Merge(options_); } } } // namespace s2geography s2/src/s2geography/aggregator.h0000644000176200001440000000043114530411473016112 0ustar liggesusers #pragma once #include "geography.h" namespace s2geography { template class Aggregator { public: virtual void Add(const Geography& geog, Params... parameters) = 0; virtual ReturnType Finalize() = 0; }; } // namespace s2geography s2/src/s2geography/distance.cc0000644000176200001440000000441014530411473015721 0ustar liggesusers #include "distance.h" #include #include #include "geography.h" namespace s2geography { double s2_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { S2ClosestEdgeQuery query(&geog1.ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); const auto& result = query.FindClosestEdge(&target); S1ChordAngle angle = result.distance(); return angle.ToAngle().radians(); } double s2_max_distance(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { S2FurthestEdgeQuery query(&geog1.ShapeIndex()); S2FurthestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); S1ChordAngle angle = result.distance(); return angle.ToAngle().radians(); } S2Point s2_closest_point(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { return s2_minimum_clearance_line_between(geog1, geog2).first; } std::pair s2_minimum_clearance_line_between( const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2) { S2ClosestEdgeQuery query1(&geog1.ShapeIndex()); query1.mutable_options()->set_include_interiors(false); S2ClosestEdgeQuery::ShapeIndexTarget target(&geog2.ShapeIndex()); const auto& result1 = query1.FindClosestEdge(&target); if (result1.edge_id() == -1) { return std::pair(S2Point(0, 0, 0), S2Point(0, 0, 0)); } // Get the edge from index1 (edge1) that is closest to index2. S2Shape::Edge edge1 = query1.GetEdge(result1); // Now find the edge from index2 (edge2) that is closest to edge1. S2ClosestEdgeQuery query2(&geog2.ShapeIndex()); query2.mutable_options()->set_include_interiors(false); S2ClosestEdgeQuery::EdgeTarget target2(edge1.v0, edge1.v1); auto result2 = query2.FindClosestEdge(&target2); // what if result2 has no edges? if (result2.is_interior()) { throw Exception("S2ClosestEdgeQuery result is interior!"); } S2Shape::Edge edge2 = query2.GetEdge(result2); // Find the closest point pair on edge1 and edge2. return S2::GetEdgePairClosestPoints(edge1.v0, edge1.v1, edge2.v0, edge2.v1); } } // namespace s2geography s2/src/s2geography/predicates.h0000644000176200001440000000175214530411473016122 0ustar liggesusers #pragma once #include #include "geography.h" namespace s2geography { bool s2_intersects(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); bool s2_equals(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); bool s2_contains(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); bool s2_touches(const ShapeIndexGeography& geog1, const ShapeIndexGeography& geog2, const S2BooleanOperation::Options& options); bool s2_intersects_box(const ShapeIndexGeography& geog1, const S2LatLngRect& rect, const S2BooleanOperation::Options& options, double tolerance); } // namespace s2geography s2/src/s2geography/linear-referencing.h0000644000176200001440000000047214530411473017534 0ustar liggesusers #pragma once #include "geography.h" namespace s2geography { double s2_project_normalized(const Geography& geog1, const Geography& geog2); S2Point s2_interpolate_normalized(const Geography& geog, double distance_norm); } // namespace s2geography s2/src/s2geography/accessors.h0000644000176200001440000000102114530411473015751 0ustar liggesusers #pragma once #include "geography.h" namespace s2geography { bool s2_is_collection(const Geography& geog); int s2_dimension(const Geography& geog); int s2_num_points(const Geography& geog); bool s2_is_empty(const Geography& geog); double s2_area(const Geography& geog); double s2_length(const Geography& geog); double s2_perimeter(const Geography& geog); double s2_x(const Geography& geog); double s2_y(const Geography& geog); bool s2_find_validation_error(const Geography& geog, S2Error* error); } // namespace s2geography s2/src/s2geography/geography.h0000644000176200001440000001510214530411473015756 0ustar liggesusers #pragma once #include #include #include #include #include namespace s2geography { class Exception : public std::runtime_error { public: Exception(std::string what) : std::runtime_error(what.c_str()) {} }; // An Geography is an abstraction of S2 types that is designed to closely // match the scope of a GEOS Geometry. Its methods are limited to those needed // to implement C API functions. From an S2 perspective, an Geography is an // S2Region that can be represented by zero or more S2Shape objects. Current // implementations of Geography own their data (i.e., the coordinate vectors // and underlying S2 objects), however, the interface is designed to allow // future abstractions where this is not the case. class Geography { public: virtual ~Geography() {} // Returns 0, 1, or 2 if all Shape()s that are returned will have // the same dimension (i.e., they are all points, all lines, or // all polygons). virtual int dimension() const { if (num_shapes() == 0) { return -1; } int dim = Shape(0)->dimension(); for (int i = 1; i < num_shapes(); i++) { if (dim != Shape(i)->dimension()) { return -1; } } return dim; } // The number of S2Shape objects needed to represent this Geography virtual int num_shapes() const = 0; // Returns the given S2Shape (where 0 <= id < num_shapes()). The // caller retains ownership of the S2Shape but the data pointed to // by the object requires that the underlying Geography outlives // the returned object. virtual std::unique_ptr Shape(int id) const = 0; // Returns an S2Region that represents the object. The caller retains // ownership of the S2Region but the data pointed to by the object // requires that the underlying Geography outlives the returned // object. virtual std::unique_ptr Region() const = 0; // Adds an unnormalized set of S2CellIDs to `cell_ids`. This is intended // to be faster than using Region().GetCovering() directly and to // return a small number of cells that can be used to compute a possible // intersection quickly. virtual void GetCellUnionBound(std::vector* cell_ids) const; }; // An Geography representing zero or more points using a std::vector // as the underlying representation. class PointGeography : public Geography { public: PointGeography() {} PointGeography(S2Point point) { points_.push_back(point); } PointGeography(std::vector points) : points_(std::move(points)) {} int dimension() const { return 0; } int num_shapes() const { return 1; } std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; void GetCellUnionBound(std::vector* cell_ids) const; const std::vector& Points() const { return points_; } private: std::vector points_; }; // An Geography representing zero or more polylines using the S2Polyline class // as the underlying representation. class PolylineGeography : public Geography { public: PolylineGeography() {} PolylineGeography(std::unique_ptr polyline) { polylines_.push_back(std::move(polyline)); } PolylineGeography(std::vector> polylines) : polylines_(std::move(polylines)) {} int dimension() const { return 1; } int num_shapes() const; std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; void GetCellUnionBound(std::vector* cell_ids) const; const std::vector>& Polylines() const { return polylines_; } private: std::vector> polylines_; }; // An Geography representing zero or more polygons using the S2Polygon class // as the underlying representation. Note that a single S2Polygon (from the S2 // perspective) can represent zero or more polygons (from the simple features // perspective). class PolygonGeography : public Geography { public: PolygonGeography(): polygon_(new S2Polygon()) {} PolygonGeography(std::unique_ptr polygon) : polygon_(std::move(polygon)) {} int dimension() const { return 2; } int num_shapes() const { return 1; } std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; void GetCellUnionBound(std::vector* cell_ids) const; const std::unique_ptr& Polygon() const { return polygon_; } private: std::unique_ptr polygon_; }; // An Geography wrapping zero or more Geography objects. These objects // can be used to represent a simple features GEOMETRYCOLLECTION. class GeographyCollection : public Geography { public: GeographyCollection() : total_shapes_(0) {} GeographyCollection(std::vector> features) : features_(std::move(features)), total_shapes_(0) { for (const auto& feature : features_) { num_shapes_.push_back(feature->num_shapes()); total_shapes_ += feature->num_shapes(); } } int num_shapes() const; std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; const std::vector>& Features() const { return features_; } private: std::vector> features_; std::vector num_shapes_; int total_shapes_; }; // An Geography with a MutableS2ShapeIndex as the underlying data. // These are used as inputs for operations that are implemented in S2 // using the S2ShapeIndex (e.g., boolean operations). If an Geography // instance will be used repeatedly, it will be faster to construct // one ShapeIndexGeography and use it repeatedly. This class does not // own any Geography objects that are added do it and thus is only // valid for the scope of those objects. class ShapeIndexGeography : public Geography { public: ShapeIndexGeography( MutableS2ShapeIndex::Options options = MutableS2ShapeIndex::Options()) : shape_index_(options) {} explicit ShapeIndexGeography(const Geography& geog) { Add(geog); } // Add a Geography to the index, returning the last shape_id // that was added to the index or -1 if no shapes were added // to the index. int Add(const Geography& geog) { int id = -1; for (int i = 0; i < geog.num_shapes(); i++) { id = shape_index_.Add(geog.Shape(i)); } return id; } int num_shapes() const; std::unique_ptr Shape(int id) const; std::unique_ptr Region() const; const MutableS2ShapeIndex& ShapeIndex() const { return shape_index_; } private: MutableS2ShapeIndex shape_index_; }; } // namespace s2geography s2/src/cpp-compat.cpp0000644000176200001440000000176214530411473014144 0ustar liggesusers #include "cpp-compat.h" #include #include using namespace Rcpp; #include // va_ stuff void cpp_compat_printf(const char* fmt, ...) { va_list args; va_start(args, fmt); Rprintf(fmt, args); va_end(args); } void cpp_compat_abort() { throw std::runtime_error("abort() called"); } void cpp_compat_exit(int code) { throw std::runtime_error("exit() called"); } int cpp_compat_random() { // trying to match what random() would return // www.gnu.org/software/libc/manual/html_node/BSD-Random.html#BSD-Random // the RNG state is correctly managed for functions that use // Rcpp::export...other functions will require management of the RNGScope return unif_rand() * INT_MAX; } void cpp_compat_srandom(int seed) { // pretty sure this should not have any effect // it gets called on load here with the initiation // of the Random class in s2testing, so it can't // error out } std::ostream& cpp_compat_cerr = Rcerr; std::ostream& cpp_compat_cout = Rcout; s2/src/s2/0000755000176200001440000000000014540350305011710 5ustar liggesuserss2/src/s2/s2coords_internal.h0000644000176200001440000000521414530411473015520 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_S2COORDS_INTERNAL_H_ #define S2_S2COORDS_INTERNAL_H_ // Author: ericv@google.com (Eric Veach) namespace S2 { namespace internal { // The canonical Hilbert traversal order looks like an inverted 'U': // the subcells are visited in the order (0,0), (0,1), (1,1), (1,0). // The following tables encode the traversal order for various // orientations of the Hilbert curve (axes swapped and/or directions // of the axes reversed). // Together these flags define a cell orientation. If 'kSwapMask' // is true, then canonical traversal order is flipped around the // diagonal (i.e. i and j are swapped with each other). If // 'kInvertMask' is true, then the traversal order is rotated by 180 // degrees (i.e. the bits of i and j are inverted, or equivalently, // the axis directions are reversed). int constexpr kSwapMask = 0x01; int constexpr kInvertMask = 0x02; // kIJtoPos[orientation][ij] -> pos // // Given a cell orientation and the (i,j)-index of a subcell (0=(0,0), // 1=(0,1), 2=(1,0), 3=(1,1)), return the order in which this subcell is // visited by the Hilbert curve (a position in the range [0..3]). extern const int kIJtoPos[4][4]; // kPosToIJ[orientation][pos] -> ij // // Return the (i,j) index of the subcell at the given position 'pos' in the // Hilbert curve traversal order with the given orientation. This is the // inverse of the previous table: // // kPosToIJ[r][kIJtoPos[r][ij]] == ij extern const int kPosToIJ[4][4]; // kPosToOrientation[pos] -> orientation_modifier // // Return a modifier indicating how the orientation of the child subcell // with the given traversal position [0..3] is related to the orientation // of the parent cell. The modifier should be XOR-ed with the parent // orientation to obtain the curve orientation in the child. extern const int kPosToOrientation[4]; // The U,V,W axes for each face. extern const double kFaceUVWAxes[6][3][3]; // The precomputed neighbors of each face (see GetUVWFace). extern const int kFaceUVWFaces[6][3][2]; } // namespace internal } // namespace S2 #endif // S2_S2COORDS_INTERNAL_H_ s2/src/s2/s2shapeutil_shape_edge_id.h0000644000176200001440000000570514530411473017156 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_ #define S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_ #include #include "s2/base/integral_types.h" namespace s2shapeutil { // ShapeEdgeId is a unique identifier for an edge within an S2ShapeIndex, // consisting of a (shape_id, edge_id) pair. It is similar to // std::pair except that it has named fields. // It should be passed and returned by value. struct ShapeEdgeId { public: int32 shape_id, edge_id; ShapeEdgeId() : shape_id(-1), edge_id(-1) {} ShapeEdgeId(int32 _shape_id, int32 _edge_id); bool operator==(ShapeEdgeId other) const; bool operator!=(ShapeEdgeId other) const; bool operator<(ShapeEdgeId other) const; bool operator>(ShapeEdgeId other) const; bool operator<=(ShapeEdgeId other) const; bool operator>=(ShapeEdgeId other) const; }; std::ostream& operator<<(std::ostream& os, ShapeEdgeId id); // Hasher for ShapeEdgeId. // Example use: std::unordered_set. struct ShapeEdgeIdHash; ////////////////// Implementation details follow //////////////////// inline ShapeEdgeId::ShapeEdgeId(int32 _shape_id, int32 _edge_id) : shape_id(_shape_id), edge_id(_edge_id) { } inline bool ShapeEdgeId::operator==(ShapeEdgeId other) const { return shape_id == other.shape_id && edge_id == other.edge_id; } inline bool ShapeEdgeId::operator!=(ShapeEdgeId other) const { return !(*this == other); } inline bool ShapeEdgeId::operator<(ShapeEdgeId other) const { if (shape_id < other.shape_id) return true; if (other.shape_id < shape_id) return false; return edge_id < other.edge_id; } inline bool ShapeEdgeId::operator>(ShapeEdgeId other) const { return other < *this; } inline bool ShapeEdgeId::operator<=(ShapeEdgeId other) const { return !(other < *this); } inline bool ShapeEdgeId::operator>=(ShapeEdgeId other) const { return !(*this < other); } inline std::ostream& operator<<(std::ostream& os, ShapeEdgeId id) { return os << id.shape_id << ":" << id.edge_id; } struct ShapeEdgeIdHash { size_t operator()(ShapeEdgeId id) const { // The following preserves all bits even when edge_id < 0. return std::hash()((static_cast(id.shape_id) << 32) | static_cast(id.edge_id)); } }; } // namespace s2shapeutil #endif // S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_ s2/src/s2/s2loop_measures.h0000644000176200001440000003122314530411473015207 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // Defines various angle and area measures for loops on the sphere. These are // low-level methods that work directly with arrays of S2Points. They are // used to implement the methods in s2shapeindex_measures.h, // s2shape_measures.h, s2loop.h, and s2polygon.h. // // See s2polyline_measures.h, s2edge_distances.h, and s2measures.h for // additional low-level methods. #ifndef S2_S2LOOP_MEASURES_H_ #define S2_S2LOOP_MEASURES_H_ #include #include #include #include "s2/s1angle.h" #include "s2/s2point.h" #include "s2/s2point_span.h" #include "s2/s2pointutil.h" namespace S2 { // Returns the perimeter of the loop. S1Angle GetPerimeter(S2PointLoopSpan loop); // Returns the area of the loop interior, i.e. the region on the left side of // the loop. The result is between 0 and 4*Pi steradians. The implementation // ensures that nearly-degenerate clockwise loops have areas close to zero, // while nearly-degenerate counter-clockwise loops have areas close to 4*Pi. double GetArea(S2PointLoopSpan loop); // Like GetArea(), except that this method is faster and has more error. The // result is between 0 and 4*Pi steradians. The maximum error is 2.22e-15 // steradians per loop vertex, which works out to about 0.09 square meters per // vertex on the Earth's surface. For example, a loop with 100 vertices has a // maximum error of about 9 square meters. (The actual error is typically // much smaller than this.) The error bound can be computed using // GetCurvatureMaxError(), which returns the maximum error in steradians. double GetApproxArea(S2PointLoopSpan loop); // Returns either the positive area of the region on the left side of the // loop, or the negative area of the region on the right side of the loop, // whichever is smaller in magnitude. The result is between -2*Pi and 2*Pi // steradians. This method is used to accurately compute the area of polygons // consisting of multiple loops. // // The following cases are handled specially: // // - Counter-clockwise loops are guaranteed to have positive area, and // clockwise loops are guaranteed to have negative area. // // - Degenerate loops (consisting of an isolated vertex or composed entirely // of sibling edge pairs) have an area of exactly zero. // // - The full loop (containing all points, and represented as a loop with no // vertices) has a negative area with the minimum possible magnitude. // (This is the "signed equivalent" of having an area of 4*Pi.) double GetSignedArea(S2PointLoopSpan loop); // Returns the geodesic curvature of the loop, defined as the sum of the turn // angles at each vertex (see S2::TurnAngle). The result is positive if the // loop is counter-clockwise, negative if the loop is clockwise, and zero if // the loop is a great circle. The geodesic curvature is equal to 2*Pi minus // the area of the loop. // // The following cases are handled specially: // // - Degenerate loops (consisting of an isolated vertex or composed entirely // of sibling edge pairs) have a curvature of 2*Pi exactly. // // - The full loop (containing all points, and represented as a loop with no // vertices) has a curvature of -2*Pi exactly. // // - All other loops have a non-zero curvature in the range (-2*Pi, 2*Pi). // For any such loop, reversing the order of the vertices is guaranteed to // negate the curvature. This property can be used to define a unique // normalized orientation for every loop. double GetCurvature(S2PointLoopSpan loop); // Returns the maximum error in GetCurvature() for the given loop. This value // is also an upper bound on the error in GetArea(), GetSignedArea(), and // GetApproxArea(). double GetCurvatureMaxError(S2PointLoopSpan loop); // Returns the true centroid of the loop multiplied by the area of the loop // (see s2centroids.h for details on centroids). The result is not unit // length, so you may want to normalize it. Also note that in general, the // centroid may not be contained by the loop. // // The result is scaled by the loop area for two reasons: (1) it is cheaper to // compute this way, and (2) it makes it easier to compute the centroid of // more complicated shapes (by splitting them into disjoint regions and adding // their centroids). S2Point GetCentroid(S2PointLoopSpan loop); // Returns true if the loop area is at most 2*Pi. (A small amount of error is // allowed in order to ensure that loops representing an entire hemisphere are // always considered normalized.) // // Degenerate loops are handled consistently with s2pred::Sign(), i.e., if a // loop can be expressed as the union of degenerate or nearly-degenerate // counter-clockwise triangles then this method will return true. bool IsNormalized(S2PointLoopSpan loop); // LoopOrder represents a cyclic ordering of the loop vertices, starting at // the index "first" and proceeding in direction "dir" (either +1 or -1). // "first" and "dir" must be chosen such that (first, ..., first + n * dir) // are all in the range [0, 2*n-1] as required by S2PointLoopSpan::operator[]. struct LoopOrder { LoopOrder(int _first, int _dir) : first(_first), dir(_dir) {} int first; int dir; }; bool operator==(LoopOrder x, LoopOrder y); std::ostream& operator<<(std::ostream& os, LoopOrder order); // Returns an index "first" and a direction "dir" such that the vertex // sequence (first, first + dir, ..., first + (n - 1) * dir) does not change // when the loop vertex order is rotated or reversed. This allows the loop // vertices to be traversed in a canonical order. LoopOrder GetCanonicalLoopOrder(S2PointLoopSpan loop); // Returns the oriented surface integral of some quantity f(x) over the loop // interior, given a function f_tri(A,B,C) that returns the corresponding // integral over the spherical triangle ABC. Here "oriented surface integral" // means: // // (1) f_tri(A,B,C) must be the integral of f if ABC is counterclockwise, // and the integral of -f if ABC is clockwise. // // (2) The result of this function is *either* the integral of f over the // loop interior, or the integral of (-f) over the loop exterior. // // Note that there are at least two common situations where property (2) above // is not a limitation: // // - If the integral of f over the entire sphere is zero, then it doesn't // matter which case is returned because they are always equal. // // - If f is non-negative, then it is easy to detect when the integral over // the loop exterior has been returned, and the integral over the loop // interior can be obtained by adding the integral of f over the entire // unit sphere (a constant) to the result. // // REQUIRES: The default constructor for T must initialize the value to zero. // (This is true for built-in types such as "double".) template T GetSurfaceIntegral(S2PointLoopSpan loop, T f_tri(const S2Point&, const S2Point&, const S2Point&)); // Returns a new loop obtained by removing all degeneracies from "loop". In // particular, the result will not contain any adjacent duplicate vertices or // sibling edge pairs, i.e. vertex sequences of the form (A, A) or (A, B, A). // // "new_vertices" represents storage where new loop vertices may be written. // Note that the S2PointLoopSpan result may be a subsequence of either "loop" // or "new_vertices", and therefore "new_vertices" must persist until the // result of this method is no longer needed. S2PointLoopSpan PruneDegeneracies(S2PointLoopSpan loop, std::vector* new_vertices); //////////////////// Implementation details follow //////////////////////// inline bool operator==(LoopOrder x, LoopOrder y) { return x.first == y.first && x.dir == y.dir; } template T GetSurfaceIntegral(S2PointLoopSpan loop, T f_tri(const S2Point&, const S2Point&, const S2Point&)) { // We sum "f_tri" over a collection T of oriented triangles, possibly // overlapping. Let the sign of a triangle be +1 if it is CCW and -1 // otherwise, and let the sign of a point "x" be the sum of the signs of the // triangles containing "x". Then the collection of triangles T is chosen // such that either: // // (1) Each point in the loop interior has sign +1, and sign 0 otherwise; or // (2) Each point in the loop exterior has sign -1, and sign 0 otherwise. // // The triangles basically consist of a "fan" from vertex 0 to every loop // edge that does not include vertex 0. These triangles will always satisfy // either (1) or (2). However, what makes this a bit tricky is that // spherical edges become numerically unstable as their length approaches // 180 degrees. Of course there is not much we can do if the loop itself // contains such edges, but we would like to make sure that all the triangle // edges under our control (i.e., the non-loop edges) are stable. For // example, consider a loop around the equator consisting of four equally // spaced points. This is a well-defined loop, but we cannot just split it // into two triangles by connecting vertex 0 to vertex 2. // // We handle this type of situation by moving the origin of the triangle fan // whenever we are about to create an unstable edge. We choose a new // location for the origin such that all relevant edges are stable. We also // create extra triangles with the appropriate orientation so that the sum // of the triangle signs is still correct at every point. // The maximum length of an edge for it to be considered numerically stable. // The exact value is fairly arbitrary since it depends on the stability of // the "f_tri" function. The value below is quite conservative but could be // reduced further if desired. static const double kMaxLength = M_PI - 1e-5; // The default constructor for T must initialize the value to zero. // (This is true for built-in types such as "double".) T sum = T(); if (loop.size() < 3) return sum; S2Point origin = loop[0]; for (int i = 1; i + 1 < loop.size(); ++i) { // Let V_i be loop[i], let O be the current origin, and let length(A,B) // be the length of edge (A,B). At the start of each loop iteration, the // "leading edge" of the triangle fan is (O,V_i), and we want to extend // the triangle fan so that the leading edge is (O,V_i+1). // // Invariants: // 1. length(O,V_i) < kMaxLength for all (i > 1). // 2. Either O == V_0, or O is approximately perpendicular to V_0. // 3. "sum" is the oriented integral of f over the area defined by // (O, V_0, V_1, ..., V_i). S2_DCHECK(i == 1 || origin.Angle(loop[i]) < kMaxLength); S2_DCHECK(origin == loop[0] || std::fabs(origin.DotProd(loop[0])) < 1e-15); if (loop[i + 1].Angle(origin) > kMaxLength) { // We are about to create an unstable edge, so choose a new origin O' // for the triangle fan. S2Point old_origin = origin; if (origin == loop[0]) { // The following point is well-separated from V_i and V_0 (and // therefore V_i+1 as well). origin = S2::RobustCrossProd(loop[0], loop[i]).Normalize(); } else if (loop[i].Angle(loop[0]) < kMaxLength) { // All edges of the triangle (O, V_0, V_i) are stable, so we can // revert to using V_0 as the origin. origin = loop[0]; } else { // (O, V_i+1) and (V_0, V_i) are antipodal pairs, and O and V_0 are // perpendicular. Therefore V_0.CrossProd(O) is approximately // perpendicular to all of {O, V_0, V_i, V_i+1}, and we can choose // this point O' as the new origin. origin = loop[0].CrossProd(old_origin); // Advance the edge (V_0,O) to (V_0,O'). sum += f_tri(loop[0], old_origin, origin); } // Advance the edge (O,V_i) to (O',V_i). sum += f_tri(old_origin, loop[i], origin); } // Advance the edge (O,V_i) to (O,V_i+1). sum += f_tri(origin, loop[i], loop[i+1]); } // If the origin is not V_0, we need to sum one more triangle. if (origin != loop[0]) { // Advance the edge (O,V_n-1) to (O,V_0). sum += f_tri(origin, loop[loop.size() - 1], loop[0]); } return sum; } } // namespace S2 #endif // S2_S2LOOP_MEASURES_H_ s2/src/s2/s1interval.cc0000644000176200001440000002400514530411473014313 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s1interval.h" #include #include #include #include "s2/base/logging.h" using std::fabs; using std::max; S1Interval S1Interval::FromPoint(double p) { if (p == -M_PI) p = M_PI; return S1Interval(p, p, ARGS_CHECKED); } double S1Interval::GetCenter() const { double center = 0.5 * (lo() + hi()); if (!is_inverted()) return center; // Return the center in the range (-Pi, Pi]. return (center <= 0) ? (center + M_PI) : (center - M_PI); } double S1Interval::GetLength() const { double length = hi() - lo(); if (length >= 0) return length; length += 2 * M_PI; // Empty intervals have a negative length. return (length > 0) ? length : -1; } S1Interval S1Interval::Complement() const { if (lo() == hi()) return Full(); // Singleton. return S1Interval(hi(), lo(), ARGS_CHECKED); // Handles empty and full. } double S1Interval::GetComplementCenter() const { if (lo() != hi()) { return Complement().GetCenter(); } else { // Singleton. return (hi() <= 0) ? (hi() + M_PI) : (hi() - M_PI); } } bool S1Interval::FastContains(double p) const { if (is_inverted()) { return (p >= lo() || p <= hi()) && !is_empty(); } else { return p >= lo() && p <= hi(); } } bool S1Interval::Contains(double p) const { // Works for empty, full, and singleton intervals. S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; return FastContains(p); } bool S1Interval::InteriorContains(double p) const { // Works for empty, full, and singleton intervals. S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; if (is_inverted()) { return p > lo() || p < hi(); } else { return (p > lo() && p < hi()) || is_full(); } } bool S1Interval::Contains(const S1Interval& y) const { // It might be helpful to compare the structure of these tests to // the simpler Contains(double) method above. if (is_inverted()) { if (y.is_inverted()) return y.lo() >= lo() && y.hi() <= hi(); return (y.lo() >= lo() || y.hi() <= hi()) && !is_empty(); } else { if (y.is_inverted()) return is_full() || y.is_empty(); return y.lo() >= lo() && y.hi() <= hi(); } } bool S1Interval::InteriorContains(const S1Interval& y) const { if (is_inverted()) { if (!y.is_inverted()) return y.lo() > lo() || y.hi() < hi(); return (y.lo() > lo() && y.hi() < hi()) || y.is_empty(); } else { if (y.is_inverted()) return is_full() || y.is_empty(); return (y.lo() > lo() && y.hi() < hi()) || is_full(); } } bool S1Interval::Intersects(const S1Interval& y) const { if (is_empty() || y.is_empty()) return false; if (is_inverted()) { // Every non-empty inverted interval contains Pi. return y.is_inverted() || y.lo() <= hi() || y.hi() >= lo(); } else { if (y.is_inverted()) return y.lo() <= hi() || y.hi() >= lo(); return y.lo() <= hi() && y.hi() >= lo(); } } bool S1Interval::InteriorIntersects(const S1Interval& y) const { if (is_empty() || y.is_empty() || lo() == hi()) return false; if (is_inverted()) { return y.is_inverted() || y.lo() < hi() || y.hi() > lo(); } else { if (y.is_inverted()) return y.lo() < hi() || y.hi() > lo(); return (y.lo() < hi() && y.hi() > lo()) || is_full(); } } inline static double PositiveDistance(double a, double b) { // Compute the distance from "a" to "b" in the range [0, 2*Pi). // This is equivalent to (remainder(b - a - M_PI, 2 * M_PI) + M_PI), // except that it is more numerically stable (it does not lose // precision for very small positive distances). double d = b - a; if (d >= 0) return d; // We want to ensure that if b == Pi and a == (-Pi + eps), // the return result is approximately 2*Pi and not zero. return (b + M_PI) - (a - M_PI); } double S1Interval::GetDirectedHausdorffDistance(const S1Interval& y) const { if (y.Contains(*this)) return 0.0; // this includes the case *this is empty if (y.is_empty()) return M_PI; // maximum possible distance on S1 double y_complement_center = y.GetComplementCenter(); if (Contains(y_complement_center)) { return PositiveDistance(y.hi(), y_complement_center); } else { // The Hausdorff distance is realized by either two hi() endpoints or two // lo() endpoints, whichever is farther apart. double hi_hi = S1Interval(y.hi(), y_complement_center).Contains(hi()) ? PositiveDistance(y.hi(), hi()) : 0; double lo_lo = S1Interval(y_complement_center, y.lo()).Contains(lo()) ? PositiveDistance(lo(), y.lo()) : 0; S2_DCHECK(hi_hi > 0 || lo_lo > 0); return max(hi_hi, lo_lo); } } void S1Interval::AddPoint(double p) { S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; if (FastContains(p)) return; if (is_empty()) { set_hi(p); set_lo(p); } else { // Compute distance from p to each endpoint. double dlo = PositiveDistance(p, lo()); double dhi = PositiveDistance(hi(), p); if (dlo < dhi) { set_lo(p); } else { set_hi(p); } // Adding a point can never turn a non-full interval into a full one. } } double S1Interval::Project(double p) const { S2_DCHECK(!is_empty()); S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; if (FastContains(p)) return p; // Compute distance from p to each endpoint. double dlo = PositiveDistance(p, lo()); double dhi = PositiveDistance(hi(), p); return (dlo < dhi) ? lo() : hi(); } S1Interval S1Interval::FromPointPair(double p1, double p2) { S2_DCHECK_LE(fabs(p1), M_PI); S2_DCHECK_LE(fabs(p2), M_PI); if (p1 == -M_PI) p1 = M_PI; if (p2 == -M_PI) p2 = M_PI; if (PositiveDistance(p1, p2) <= M_PI) { return S1Interval(p1, p2, ARGS_CHECKED); } else { return S1Interval(p2, p1, ARGS_CHECKED); } } S1Interval S1Interval::Expanded(double margin) const { if (margin >= 0) { if (is_empty()) return *this; // Check whether this interval will be full after expansion, allowing // for a 1-bit rounding error when computing each endpoint. if (GetLength() + 2 * margin + 2 * DBL_EPSILON >= 2 * M_PI) return Full(); } else { if (is_full()) return *this; // Check whether this interval will be empty after expansion, allowing // for a 1-bit rounding error when computing each endpoint. if (GetLength() + 2 * margin - 2 * DBL_EPSILON <= 0) return Empty(); } S1Interval result(remainder(lo() - margin, 2*M_PI), remainder(hi() + margin, 2*M_PI)); if (result.lo() <= -M_PI) result.set_lo(M_PI); return result; } S1Interval S1Interval::Union(const S1Interval& y) const { // The y.is_full() case is handled correctly in all cases by the code // below, but can follow three separate code paths depending on whether // this interval is inverted, is non-inverted but contains Pi, or neither. if (y.is_empty()) return *this; if (FastContains(y.lo())) { if (FastContains(y.hi())) { // Either this interval contains y, or the union of the two // intervals is the Full() interval. if (Contains(y)) return *this; // is_full() code path return Full(); } return S1Interval(lo(), y.hi(), ARGS_CHECKED); } if (FastContains(y.hi())) return S1Interval(y.lo(), hi(), ARGS_CHECKED); // This interval contains neither endpoint of y. This means that either y // contains all of this interval, or the two intervals are disjoint. if (is_empty() || y.FastContains(lo())) return y; // Check which pair of endpoints are closer together. double dlo = PositiveDistance(y.hi(), lo()); double dhi = PositiveDistance(hi(), y.lo()); if (dlo < dhi) { return S1Interval(y.lo(), hi(), ARGS_CHECKED); } else { return S1Interval(lo(), y.hi(), ARGS_CHECKED); } } S1Interval S1Interval::Intersection(const S1Interval& y) const { // The y.is_full() case is handled correctly in all cases by the code // below, but can follow three separate code paths depending on whether // this interval is inverted, is non-inverted but contains Pi, or neither. if (y.is_empty()) return Empty(); if (FastContains(y.lo())) { if (FastContains(y.hi())) { // Either this interval contains y, or the region of intersection // consists of two disjoint subintervals. In either case, we want // to return the shorter of the two original intervals. if (y.GetLength() < GetLength()) return y; // is_full() code path return *this; } return S1Interval(y.lo(), hi(), ARGS_CHECKED); } if (FastContains(y.hi())) return S1Interval(lo(), y.hi(), ARGS_CHECKED); // This interval contains neither endpoint of y. This means that either y // contains all of this interval, or the two intervals are disjoint. if (y.FastContains(lo())) return *this; // is_empty() okay here S2_DCHECK(!Intersects(y)); return Empty(); } bool S1Interval::ApproxEquals(const S1Interval& y, double max_error) const { // Full and empty intervals require special cases because the "endpoints" // are considered to be positioned arbitrarily. if (is_empty()) return y.GetLength() <= 2 * max_error; if (y.is_empty()) return GetLength() <= 2 * max_error; if (is_full()) return y.GetLength() >= 2 * (M_PI - max_error); if (y.is_full()) return GetLength() >= 2 * (M_PI - max_error); // The purpose of the last test below is to verify that moving the endpoints // does not invert the interval, e.g. [-1e20, 1e20] vs. [1e20, -1e20]. return (fabs(remainder(y.lo() - lo(), 2 * M_PI)) <= max_error && fabs(remainder(y.hi() - hi(), 2 * M_PI)) <= max_error && fabs(GetLength() - y.GetLength()) <= 2 * max_error); } s2/src/s2/s2metrics.h0000644000176200001440000002101214530411473013773 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // The following are various constants that describe the shapes and sizes of // S2Cells (see s2coords.h and s2cell_id.h). They are useful for deciding // which cell level to use in order to satisfy a given condition (e.g. that // cell vertices must be no further than "x" apart). All of the raw constants // are differential quantities; you can use the GetValue(level) method to // compute the corresponding length or area on the unit sphere for cells at a // given level. The minimum and maximum bounds are valid for cells at all // levels, but they may be somewhat conservative for very large cells // (e.g. face cells). #ifndef S2_S2METRICS_H_ #define S2_S2METRICS_H_ #include #include #include "s2/s2coords.h" #include "s2/util/math/mathutil.h" namespace S2 { // Defines a cell metric of the given dimension (1 == length, 2 == area). template class Metric { public: explicit constexpr Metric(double deriv) : deriv_(deriv) {} // The "deriv" value of a metric is a derivative, and must be multiplied by // a length or area in (s,t)-space to get a useful value. double deriv() const { return deriv_; } // Return the value of a metric for cells at the given level. The value is // either a length or an area on the unit sphere, depending on the // particular metric. double GetValue(int level) const { return ldexp(deriv_, - dim * level); } // Return the level at which the metric has approximately the given // value. For example, S2::kAvgEdge.GetClosestLevel(0.1) returns the // level at which the average cell edge length is approximately 0.1. // The return value is always a valid level. int GetClosestLevel(double value) const; // Return the minimum level such that the metric is at most the given // value, or S2CellId::kMaxLevel if there is no such level. For example, // S2::kMaxDiag.GetLevelForMaxValue(0.1) returns the minimum level such // that all cell diagonal lengths are 0.1 or smaller. The return value // is always a valid level. int GetLevelForMaxValue(double value) const; // Return the maximum level such that the metric is at least the given // value, or zero if there is no such level. For example, // S2::kMinWidth.GetLevelForMinValue(0.1) returns the maximum level such // that all cells have a minimum width of 0.1 or larger. The return value // is always a valid level. int GetLevelForMinValue(double value) const; private: const double deriv_; Metric(const Metric&) = delete; void operator=(const Metric&) = delete; }; using LengthMetric = Metric<1>; using AreaMetric = Metric<2>; // Each cell is bounded by four planes passing through its four edges and // the center of the sphere. These metrics relate to the angle between each // pair of opposite bounding planes, or equivalently, between the planes // corresponding to two different s-values or two different t-values. For // example, the maximum angle between opposite bounding planes for a cell at // level k is kMaxAngleSpan.GetValue(k), and the average angle span for all // cells at level k is approximately kAvgAngleSpan.GetValue(k). extern const LengthMetric kMinAngleSpan; extern const LengthMetric kMaxAngleSpan; extern const LengthMetric kAvgAngleSpan; // The width of geometric figure is defined as the distance between two // parallel bounding lines in a given direction. For cells, the minimum // width is always attained between two opposite edges, and the maximum // width is attained between two opposite vertices. However, for our // purposes we redefine the width of a cell as the perpendicular distance // between a pair of opposite edges. A cell therefore has two widths, one // in each direction. The minimum width according to this definition agrees // with the classic geometric one, but the maximum width is different. (The // maximum geometric width corresponds to kMaxDiag defined below.) // // For a cell at level k, the distance between opposite edges is at least // kMinWidth.GetValue(k) and at most kMaxWidth.GetValue(k). The average // width in both directions for all cells at level k is approximately // kAvgWidth.GetValue(k). // // The width is useful for bounding the minimum or maximum distance from a // point on one edge of a cell to the closest point on the opposite edge. // For example, this is useful when "growing" regions by a fixed distance. // // Note that because S2Cells are not usually rectangles, the minimum width of // a cell is generally smaller than its minimum edge length. (The interior // angles of an S2Cell range from 60 to 120 degrees.) extern const LengthMetric kMinWidth; extern const LengthMetric kMaxWidth; extern const LengthMetric kAvgWidth; // The minimum edge length of any cell at level k is at least // kMinEdge.GetValue(k), and the maximum is at most kMaxEdge.GetValue(k). // The average edge length is approximately kAvgEdge.GetValue(k). // // The edge length metrics can also be used to bound the minimum, maximum, // or average distance from the center of one cell to the center of one of // its edge neighbors. In particular, it can be used to bound the distance // between adjacent cell centers along the space-filling Hilbert curve for // cells at any given level. extern const LengthMetric kMinEdge; extern const LengthMetric kMaxEdge; extern const LengthMetric kAvgEdge; // The minimum diagonal length of any cell at level k is at least // kMinDiag.GetValue(k), and the maximum is at most kMaxDiag.GetValue(k). // The average diagonal length is approximately kAvgDiag.GetValue(k). // // The maximum diagonal also happens to be the maximum diameter of any cell, // and also the maximum geometric width (see the discussion above). So for // example, the distance from an arbitrary point to the closest cell center // at a given level is at most half the maximum diagonal length. extern const LengthMetric kMinDiag; extern const LengthMetric kMaxDiag; extern const LengthMetric kAvgDiag; // The minimum area of any cell at level k is at least kMinArea.GetValue(k), // and the maximum is at most kMaxArea.GetValue(k). The average area of all // cells at level k is exactly kAvgArea.GetValue(k). extern const AreaMetric kMinArea; extern const AreaMetric kMaxArea; extern const AreaMetric kAvgArea; // This is the maximum edge aspect ratio over all cells at any level, where // the edge aspect ratio of a cell is defined as the ratio of its longest // edge length to its shortest edge length. extern const double kMaxEdgeAspect; // This is the maximum diagonal aspect ratio over all cells at any level, // where the diagonal aspect ratio of a cell is defined as the ratio of its // longest diagonal length to its shortest diagonal length. extern const double kMaxDiagAspect; ////////////////// Implementation details follow //////////////////// template int S2::Metric::GetLevelForMaxValue(double value) const { if (value <= 0) return S2::kMaxCellLevel; // This code is equivalent to computing a floating-point "level" // value and rounding up. ilogb() returns the exponent corresponding to a // fraction in the range [1,2). int level = ilogb(value / deriv_); level = std::max(0, std::min(S2::kMaxCellLevel, -(level >> (dim - 1)))); S2_DCHECK(level == S2::kMaxCellLevel || GetValue(level) <= value); S2_DCHECK(level == 0 || GetValue(level - 1) > value); return level; } template int S2::Metric::GetLevelForMinValue(double value) const { if (value <= 0) return S2::kMaxCellLevel; // This code is equivalent to computing a floating-point "level" // value and rounding down. int level = ilogb(deriv_ / value); level = std::max(0, std::min(S2::kMaxCellLevel, level >> (dim - 1))); S2_DCHECK(level == 0 || GetValue(level) >= value); S2_DCHECK(level == kMaxCellLevel || GetValue(level + 1) < value); return level; } template int Metric::GetClosestLevel(double value) const { return GetLevelForMaxValue((dim == 1 ? M_SQRT2 : 2) * value); } } // namespace S2 #endif // S2_S2METRICS_H_ s2/src/s2/s2region_coverer.cc0000644000176200001440000004506614530411473015512 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2region_coverer.h" #include #include #include #include #include #include #include #include #include "s2/base/logging.h" #include "s2/s1angle.h" #include "s2/s2cap.h" #include "s2/s2cell_union.h" #include "s2/s2metrics.h" #include "s2/s2region.h" #include "absl/base/casts.h" using std::is_sorted; using std::max; using std::min; using std::unordered_set; using std::vector; // Define storage for header file constants (the values are not needed here). constexpr int S2RegionCoverer::Options::kDefaultMaxCells; S2RegionCoverer::S2RegionCoverer(const S2RegionCoverer::Options& options) : options_(options) { S2_DCHECK_LE(options.min_level(), options.max_level()); } // Defaulted in the implementation to prevent inline bloat. S2RegionCoverer::S2RegionCoverer() = default; S2RegionCoverer::~S2RegionCoverer() = default; S2RegionCoverer::S2RegionCoverer(S2RegionCoverer&&) = default; S2RegionCoverer& S2RegionCoverer::operator=(S2RegionCoverer&&) = default; void S2RegionCoverer::Options::set_max_cells(int max_cells) { max_cells_ = max_cells; } void S2RegionCoverer::Options::set_min_level(int min_level) { S2_DCHECK_GE(min_level, 0); S2_DCHECK_LE(min_level, S2CellId::kMaxLevel); // min_level() <= max_level() is checked by S2RegionCoverer. min_level_ = max(0, min(S2CellId::kMaxLevel, min_level)); } void S2RegionCoverer::Options::set_max_level(int max_level) { S2_DCHECK_GE(max_level, 0); S2_DCHECK_LE(max_level, S2CellId::kMaxLevel); // min_level() <= max_level() is checked by S2RegionCoverer. max_level_ = max(0, min(S2CellId::kMaxLevel, max_level)); } void S2RegionCoverer::Options::set_fixed_level(int level) { set_min_level(level); set_max_level(level); } void S2RegionCoverer::Options::set_level_mod(int level_mod) { S2_DCHECK_GE(level_mod, 1); S2_DCHECK_LE(level_mod, 3); level_mod_ = max(1, min(3, level_mod)); } int S2RegionCoverer::Options::true_max_level() const { if (level_mod_ == 1) return max_level_; return max_level_ - (max_level_ - min_level_) % level_mod_; } S2RegionCoverer::Candidate* S2RegionCoverer::NewCandidate(const S2Cell& cell) { if (!region_->MayIntersect(cell)) return nullptr; bool is_terminal = false; if (cell.level() >= options_.min_level()) { if (interior_covering_) { if (region_->Contains(cell)) { is_terminal = true; } else if (cell.level() + options_.level_mod() > options_.max_level()) { return nullptr; } } else { if (cell.level() + options_.level_mod() > options_.max_level() || region_->Contains(cell)) { is_terminal = true; } } } ++candidates_created_counter_; const std::size_t max_children = is_terminal ? 0 : 1 << max_children_shift(); return new (max_children) Candidate(cell, max_children); } void S2RegionCoverer::DeleteCandidate(Candidate* candidate, bool delete_children) { if (delete_children) { for (int i = 0; i < candidate->num_children; ++i) DeleteCandidate(candidate->children[i], true); } delete candidate; } int S2RegionCoverer::ExpandChildren(Candidate* candidate, const S2Cell& cell, int num_levels) { num_levels--; S2Cell child_cells[4]; cell.Subdivide(child_cells); int num_terminals = 0; for (int i = 0; i < 4; ++i) { if (num_levels > 0) { if (region_->MayIntersect(child_cells[i])) { num_terminals += ExpandChildren(candidate, child_cells[i], num_levels); } continue; } Candidate* child = NewCandidate(child_cells[i]); if (child) { candidate->children[candidate->num_children++] = child; if (child->is_terminal) ++num_terminals; } } return num_terminals; } void S2RegionCoverer::AddCandidate(Candidate* candidate) { if (candidate == nullptr) return; if (candidate->is_terminal) { result_.push_back(candidate->cell.id()); DeleteCandidate(candidate, true); return; } S2_DCHECK_EQ(0, candidate->num_children); // Expand one level at a time until we hit min_level() to ensure that we // don't skip over it. int num_levels = ((candidate->cell.level() < options_.min_level()) ? 1 : options_.level_mod()); int num_terminals = ExpandChildren(candidate, candidate->cell, num_levels); if (candidate->num_children == 0) { DeleteCandidate(candidate, false); } else if (!interior_covering_ && num_terminals == 1 << max_children_shift() && candidate->cell.level() >= options_.min_level()) { // Optimization: add the parent cell rather than all of its children. // We can't do this for interior coverings, since the children just // intersect the region, but may not be contained by it - we need to // subdivide them further. candidate->is_terminal = true; AddCandidate(candidate); } else { // We negate the priority so that smaller absolute priorities are returned // first. The heuristic is designed to refine the largest cells first, // since those are where we have the largest potential gain. Among cells // of the same size, we prefer the cells with the fewest children. // Finally, among cells with equal numbers of children we prefer those // with the smallest number of children that cannot be refined further. int priority = -((((candidate->cell.level() << max_children_shift()) + candidate->num_children) << max_children_shift()) + num_terminals); pq_.push(std::make_pair(priority, candidate)); S2_VLOG(2) << "Push: " << candidate->cell.id() << " (" << priority << ") "; } } inline int S2RegionCoverer::AdjustLevel(int level) const { if (options_.level_mod() > 1 && level > options_.min_level()) { level -= (level - options_.min_level()) % options_.level_mod(); } return level; } void S2RegionCoverer::AdjustCellLevels(vector* cells) const { S2_DCHECK(is_sorted(cells->begin(), cells->end())); if (options_.level_mod() == 1) return; int out = 0; for (S2CellId id : *cells) { int level = id.level(); int new_level = AdjustLevel(level); if (new_level != level) id = id.parent(new_level); if (out > 0 && (*cells)[out-1].contains(id)) continue; while (out > 0 && id.contains((*cells)[out-1])) --out; (*cells)[out++] = id; } cells->resize(out); } void S2RegionCoverer::GetInitialCandidates() { // Optimization: start with a small (usually 4 cell) covering of the // region's bounding cap. S2RegionCoverer tmp_coverer; tmp_coverer.mutable_options()->set_max_cells(min(4, options_.max_cells())); tmp_coverer.mutable_options()->set_max_level(options_.max_level()); vector cells; tmp_coverer.GetFastCovering(*region_, &cells); AdjustCellLevels(&cells); for (S2CellId cell_id : cells) { AddCandidate(NewCandidate(S2Cell(cell_id))); } } void S2RegionCoverer::GetCoveringInternal(const S2Region& region) { // We check this on each call because of mutable_options(). S2_DCHECK_LE(options_.min_level(), options_.max_level()); // Strategy: Start with the 6 faces of the cube. Discard any // that do not intersect the shape. Then repeatedly choose the // largest cell that intersects the shape and subdivide it. // // result_ contains the cells that will be part of the output, while pq_ // contains cells that we may still subdivide further. Cells that are // entirely contained within the region are immediately added to the output, // while cells that do not intersect the region are immediately discarded. // Therefore pq_ only contains cells that partially intersect the region. // Candidates are prioritized first according to cell size (larger cells // first), then by the number of intersecting children they have (fewest // children first), and then by the number of fully contained children // (fewest children first). S2_DCHECK(pq_.empty()); S2_DCHECK(result_.empty()); region_ = ®ion; candidates_created_counter_ = 0; GetInitialCandidates(); while (!pq_.empty() && (!interior_covering_ || result_.size() < options_.max_cells())) { Candidate* candidate = pq_.top().second; pq_.pop(); S2_VLOG(2) << "Pop: " << candidate->cell.id(); // For interior coverings we keep subdividing no matter how many children // the candidate has. If we reach max_cells() before expanding all // children, we will just use some of them. For exterior coverings we // cannot do this, because the result has to cover the whole region, so // all children have to be used. The (candidate->num_children == 1) case // takes care of the situation when we already have more than max_cells() // in results (min_level is too high). Subdividing the candidate with one // child does no harm in this case. if (interior_covering_ || candidate->cell.level() < options_.min_level() || candidate->num_children == 1 || (result_.size() + pq_.size() + candidate->num_children <= options_.max_cells())) { // Expand this candidate into its children. for (int i = 0; i < candidate->num_children; ++i) { if (interior_covering_ && result_.size() >= options_.max_cells()) { DeleteCandidate(candidate->children[i], true); } else { AddCandidate(candidate->children[i]); } } DeleteCandidate(candidate, false); } else { candidate->is_terminal = true; AddCandidate(candidate); } } S2_VLOG(2) << "Created " << result_.size() << " cells, " << candidates_created_counter_ << " candidates created, " << pq_.size() << " left"; while (!pq_.empty()) { DeleteCandidate(pq_.top().second, true); pq_.pop(); } region_ = nullptr; // Rather than just returning the raw list of cell ids, we construct a cell // union and then denormalize it. This has the effect of replacing four // child cells with their parent whenever this does not violate the covering // parameters specified (min_level, level_mod, etc). This significantly // reduces the number of cells returned in many cases, and it is cheap // compared to computing the covering in the first place. S2CellUnion::Normalize(&result_); if (options_.min_level() > 0 || options_.level_mod() > 1) { auto result_copy = result_; S2CellUnion::Denormalize(result_copy, options_.min_level(), options_.level_mod(), &result_); } S2_DCHECK(IsCanonical(result_)); } void S2RegionCoverer::GetCovering(const S2Region& region, vector* covering) { interior_covering_ = false; GetCoveringInternal(region); *covering = std::move(result_); } void S2RegionCoverer::GetInteriorCovering(const S2Region& region, vector* interior) { interior_covering_ = true; GetCoveringInternal(region); *interior = std::move(result_); } S2CellUnion S2RegionCoverer::GetCovering(const S2Region& region) { interior_covering_ = false; GetCoveringInternal(region); return S2CellUnion::FromVerbatim(std::move(result_)); } S2CellUnion S2RegionCoverer::GetInteriorCovering(const S2Region& region) { interior_covering_ = true; GetCoveringInternal(region); return S2CellUnion::FromVerbatim(std::move(result_)); } void S2RegionCoverer::GetFastCovering(const S2Region& region, vector* covering) { region.GetCellUnionBound(covering); CanonicalizeCovering(covering); } bool S2RegionCoverer::IsCanonical(const S2CellUnion& covering) const { return IsCanonical(covering.cell_ids()); } bool S2RegionCoverer::IsCanonical(const vector& covering) const { // We check this on each call because of mutable_options(). S2_DCHECK_LE(options_.min_level(), options_.max_level()); const int min_level = options_.min_level(); const int max_level = options_.true_max_level(); const int level_mod = options_.level_mod(); const bool too_many_cells = covering.size() > options_.max_cells(); int same_parent_count = 1; S2CellId prev_id = S2CellId::None(); for (const S2CellId id : covering) { if (!id.is_valid()) return false; // Check that the S2CellId level is acceptable. const int level = id.level(); if (level < min_level || level > max_level) return false; if (level_mod > 1 && (level - min_level) % level_mod != 0) return false; if (prev_id != S2CellId::None()) { // Check that cells are sorted and non-overlapping. if (prev_id.range_max() >= id.range_min()) return false; // If there are too many cells, check that no pair of adjacent cells // could be replaced by an ancestor. if (too_many_cells && id.GetCommonAncestorLevel(prev_id) >= min_level) { return false; } // Check that there are no sequences of (4 ** level_mod) cells that all // have the same parent (considering only multiples of "level_mod"). int plevel = level - level_mod; if (plevel < min_level || level != prev_id.level() || id.parent(plevel) != prev_id.parent(plevel)) { same_parent_count = 1; } else if (++same_parent_count == (1 << (2 * level_mod))) { return false; } } prev_id = id; } return true; } bool S2RegionCoverer::ContainsAllChildren(const vector& covering, S2CellId id) const { auto it = std::lower_bound(covering.begin(), covering.end(), id.range_min()); int level = id.level() + options_.level_mod(); for (S2CellId child = id.child_begin(level); child != id.child_end(level); ++it, child = child.next()) { if (it == covering.end() || *it != child) return false; } return true; } // Replaces all descendants of "id" in "covering" with "id". // REQUIRES: "covering" contains at least one descendant of "id". void S2RegionCoverer::ReplaceCellsWithAncestor(vector* covering, S2CellId id) const { auto begin = std::lower_bound(covering->begin(), covering->end(), id.range_min()); auto end = std::upper_bound(covering->begin(), covering->end(), id.range_max()); S2_DCHECK(begin != end); covering->erase(begin + 1, end); *begin = id; } S2CellUnion S2RegionCoverer::CanonicalizeCovering(const S2CellUnion& covering) { vector ids = covering.cell_ids(); CanonicalizeCovering(&ids); return S2CellUnion(std::move(ids)); } void S2RegionCoverer::CanonicalizeCovering(vector* covering) { // We check this on each call because of mutable_options(). S2_DCHECK_LE(options_.min_level(), options_.max_level()); // Note that when the covering parameters have their default values, almost // all of the code in this function is skipped. // If any cells are too small, or don't satisfy level_mod(), then replace // them with ancestors. if (options_.max_level() < S2CellId::kMaxLevel || options_.level_mod() > 1) { for (S2CellId& id : *covering) { int level = id.level(); int new_level = AdjustLevel(min(level, options_.max_level())); if (new_level != level) { id = id.parent(new_level); } } } // Sort the cells and simplify them. S2CellUnion::Normalize(covering); // Make sure that the covering satisfies min_level() and level_mod(), // possibly at the expense of satisfying max_cells(). if (options_.min_level() > 0 || options_.level_mod() > 1) { S2CellUnion::Denormalize(*covering, options_.min_level(), options_.level_mod(), &result_); *covering = std::move(result_); } // If there are too many cells and the covering is very large, use the // S2RegionCoverer to compute a new covering. (This avoids possible O(n^2) // behavior of the simpler algorithm below.) int64 excess = covering->size() - options_.max_cells(); if (excess <= 0 || IsCanonical(*covering)) { return; } if (excess * covering->size() > 10000) { GetCovering(S2CellUnion(std::move(*covering)), covering); } else { // Repeatedly replace two adjacent cells in S2CellId order by their lowest // common ancestor until the number of cells is acceptable. while (covering->size() > options_.max_cells()) { int best_index = -1, best_level = -1; for (int i = 0; i + 1 < covering->size(); ++i) { int level = (*covering)[i].GetCommonAncestorLevel((*covering)[i+1]); level = AdjustLevel(level); if (level > best_level) { best_level = level; best_index = i; } } if (best_level < options_.min_level()) break; // Replace all cells contained by the new ancestor cell. S2CellId id = (*covering)[best_index].parent(best_level); ReplaceCellsWithAncestor(covering, id); // Now repeatedly check whether all children of the parent cell are // present, in which case we can replace those cells with their parent. while (best_level > options_.min_level()) { best_level -= options_.level_mod(); id = id.parent(best_level); if (!ContainsAllChildren(*covering, id)) break; ReplaceCellsWithAncestor(covering, id); } } } S2_DCHECK(IsCanonical(*covering)); } void S2RegionCoverer::FloodFill(const S2Region& region, S2CellId start, vector* output) { unordered_set all; vector frontier; output->clear(); all.insert(start); frontier.push_back(start); while (!frontier.empty()) { S2CellId id = frontier.back(); frontier.pop_back(); if (!region.MayIntersect(S2Cell(id))) continue; output->push_back(id); S2CellId neighbors[4]; id.GetEdgeNeighbors(neighbors); for (int edge = 0; edge < 4; ++edge) { S2CellId nbr = neighbors[edge]; if (all.insert(nbr).second) { frontier.push_back(nbr); } } } } void S2RegionCoverer::GetSimpleCovering( const S2Region& region, const S2Point& start, int level, vector* output) { return FloodFill(region, S2CellId(start).parent(level), output); } s2/src/s2/s1angle.h0000644000176200001440000002424714530411473013427 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S1ANGLE_H_ #define S2_S1ANGLE_H_ #include #include #include #include #include "s2/base/integral_types.h" #include "s2/_fp_contract_off.h" #include "s2/s2point.h" #include "s2/util/math/mathutil.h" #include "s2/util/math/vector.h" class S2LatLng; #ifndef SWIG #define IFNDEF_SWIG(x) x #else #define IFNDEF_SWIG(x) #endif // This class represents a one-dimensional angle (as opposed to a // two-dimensional solid angle). It has methods for converting angles to // or from radians, degrees, and the E5/E6/E7 representations (i.e. degrees // multiplied by 1e5/1e6/1e7 and rounded to the nearest integer). // // The internal representation is a double-precision value in radians, so // conversion to and from radians is exact. Conversions between E5, E6, E7, // and Degrees are not always exact; for example, Degrees(3.1) is different // from E6(3100000) or E7(310000000). However, the following properties are // guaranteed for any integer "n", provided that "n" is in the input range of // both functions: // // Degrees(n) == E6(1000000 * n) // Degrees(n) == E7(10000000 * n) // E6(n) == E7(10 * n) // // The corresponding properties are *not* true for E5, so if you use E5 then // don't test for exact equality when comparing to other formats such as // Degrees or E7. // // The following conversions between degrees and radians are exact: // // Degrees(180) == Radians(M_PI) // Degrees(45 * k) == Radians(k * M_PI / 4) for k == 0..8 // // These identities also hold when the arguments are scaled up or down by any // power of 2. Some similar identities are also true, for example, // Degrees(60) == Radians(M_PI / 3), but be aware that this type of identity // does not hold in general. For example, Degrees(3) != Radians(M_PI / 60). // // Similarly, the conversion to radians means that Angle::Degrees(x).degrees() // does not always equal "x". For example, // // S1Angle::Degrees(45 * k).degrees() == 45 * k for k == 0..8 // but S1Angle::Degrees(60).degrees() != 60. // // This means that when testing for equality, you should allow for numerical // errors (EXPECT_DOUBLE_EQ) or convert to discrete E5/E6/E7 values first. // // CAVEAT: All of the above properties depend on "double" being the usual // 64-bit IEEE 754 type (which is true on almost all modern platforms). // // This class is intended to be copied by value as desired. It uses // the default copy constructor and assignment operator. class S1Angle { public: // These methods construct S1Angle objects from their measure in radians // or degrees. static constexpr S1Angle Radians(double radians); static constexpr S1Angle Degrees(double degrees); static constexpr S1Angle E5(int32 e5); static constexpr S1Angle E6(int32 e6); static constexpr S1Angle E7(int32 e7); // Convenience functions -- to use when args have been fixed32s in protos. // // The arguments are static_cast into int32, so very large unsigned values // are treated as negative numbers. static constexpr S1Angle UnsignedE6(uint32 e6); static constexpr S1Angle UnsignedE7(uint32 e7); // The default constructor yields a zero angle. This is useful for STL // containers and class methods with output arguments. IFNDEF_SWIG(constexpr) S1Angle() : radians_(0) {} // Return an angle larger than any finite angle. static constexpr S1Angle Infinity(); // A explicit shorthand for the default constructor. static constexpr S1Angle Zero(); // Return the angle between two points, which is also equal to the distance // between these points on the unit sphere. The points do not need to be // normalized. This function has a maximum error of 3.25 * DBL_EPSILON (or // 2.5 * DBL_EPSILON for angles up to 1 radian). If either point is // zero-length (e.g. an uninitialized S2Point), or almost zero-length, the // resulting angle will be zero. S1Angle(const S2Point& x, const S2Point& y); // Like the constructor above, but return the angle (i.e., distance) between // two S2LatLng points. This function has about 15 digits of accuracy for // small distances but only about 8 digits of accuracy as the distance // approaches 180 degrees (i.e., nearly-antipodal points). S1Angle(const S2LatLng& x, const S2LatLng& y); constexpr double radians() const; constexpr double degrees() const; int32 e5() const; int32 e6() const; int32 e7() const; // Return the absolute value of an angle. S1Angle abs() const; // Comparison operators. friend bool operator==(S1Angle x, S1Angle y); friend bool operator!=(S1Angle x, S1Angle y); friend bool operator<(S1Angle x, S1Angle y); friend bool operator>(S1Angle x, S1Angle y); friend bool operator<=(S1Angle x, S1Angle y); friend bool operator>=(S1Angle x, S1Angle y); // Simple arithmetic operators for manipulating S1Angles. friend S1Angle operator-(S1Angle a); friend S1Angle operator+(S1Angle a, S1Angle b); friend S1Angle operator-(S1Angle a, S1Angle b); friend S1Angle operator*(double m, S1Angle a); friend S1Angle operator*(S1Angle a, double m); friend S1Angle operator/(S1Angle a, double m); friend double operator/(S1Angle a, S1Angle b); S1Angle& operator+=(S1Angle a); S1Angle& operator-=(S1Angle a); S1Angle& operator*=(double m); S1Angle& operator/=(double m); // Trigonmetric functions (not necessary but slightly more convenient). friend double sin(S1Angle a); friend double cos(S1Angle a); friend double tan(S1Angle a); // Return the angle normalized to the range (-180, 180] degrees. S1Angle Normalized() const; // Normalize this angle to the range (-180, 180] degrees. void Normalize(); // When S1Angle is used as a key in one of the btree container types // (util/btree), indicate that linear rather than binary search should be // used. This is much faster when the comparison function is cheap. typedef std::true_type absl_btree_prefer_linear_node_search; private: explicit IFNDEF_SWIG(constexpr) S1Angle(double radians) : radians_(radians) {} double radians_; }; ////////////////// Implementation details follow //////////////////// inline constexpr S1Angle S1Angle::Infinity() { return S1Angle(std::numeric_limits::infinity()); } inline constexpr S1Angle S1Angle::Zero() { return S1Angle(0); } inline constexpr double S1Angle::radians() const { return radians_; } inline constexpr double S1Angle::degrees() const { return (180 / M_PI) * radians_; } // Note that the E5, E6, and E7 conversion involve two multiplications rather // than one. This is mainly for backwards compatibility (changing this would // break many tests), but it does have the nice side effect that conversions // between Degrees, E6, and E7 are exact when the arguments are integers. inline int32 S1Angle::e5() const { return MathUtil::FastIntRound(1e5 * degrees()); } inline int32 S1Angle::e6() const { return MathUtil::FastIntRound(1e6 * degrees()); } inline int32 S1Angle::e7() const { return MathUtil::FastIntRound(1e7 * degrees()); } inline S1Angle S1Angle::abs() const { return S1Angle(std::fabs(radians_)); } inline bool operator==(S1Angle x, S1Angle y) { return x.radians() == y.radians(); } inline bool operator!=(S1Angle x, S1Angle y) { return x.radians() != y.radians(); } inline bool operator<(S1Angle x, S1Angle y) { return x.radians() < y.radians(); } inline bool operator>(S1Angle x, S1Angle y) { return x.radians() > y.radians(); } inline bool operator<=(S1Angle x, S1Angle y) { return x.radians() <= y.radians(); } inline bool operator>=(S1Angle x, S1Angle y) { return x.radians() >= y.radians(); } inline S1Angle operator-(S1Angle a) { return S1Angle::Radians(-a.radians()); } inline S1Angle operator+(S1Angle a, S1Angle b) { return S1Angle::Radians(a.radians() + b.radians()); } inline S1Angle operator-(S1Angle a, S1Angle b) { return S1Angle::Radians(a.radians() - b.radians()); } inline S1Angle operator*(double m, S1Angle a) { return S1Angle::Radians(m * a.radians()); } inline S1Angle operator*(S1Angle a, double m) { return S1Angle::Radians(m * a.radians()); } inline S1Angle operator/(S1Angle a, double m) { return S1Angle::Radians(a.radians() / m); } inline double operator/(S1Angle a, S1Angle b) { return a.radians() / b.radians(); } inline S1Angle& S1Angle::operator+=(S1Angle a) { radians_ += a.radians(); return *this; } inline S1Angle& S1Angle::operator-=(S1Angle a) { radians_ -= a.radians(); return *this; } inline S1Angle& S1Angle::operator*=(double m) { radians_ *= m; return *this; } inline S1Angle& S1Angle::operator/=(double m) { radians_ /= m; return *this; } inline double sin(S1Angle a) { return sin(a.radians()); } inline double cos(S1Angle a) { return cos(a.radians()); } inline double tan(S1Angle a) { return tan(a.radians()); } inline constexpr S1Angle S1Angle::Radians(double radians) { return S1Angle(radians); } inline constexpr S1Angle S1Angle::Degrees(double degrees) { return S1Angle((M_PI / 180) * degrees); } inline constexpr S1Angle S1Angle::E5(int32 e5) { return Degrees(1e-5 * e5); } inline constexpr S1Angle S1Angle::E6(int32 e6) { return Degrees(1e-6 * e6); } inline constexpr S1Angle S1Angle::E7(int32 e7) { return Degrees(1e-7 * e7); } inline constexpr S1Angle S1Angle::UnsignedE6(uint32 e6) { return E6(static_cast(e6)); } inline constexpr S1Angle S1Angle::UnsignedE7(uint32 e7) { return E7(static_cast(e7)); } // Writes the angle in degrees with 7 digits of precision after the // decimal point, e.g. "17.3745904". std::ostream& operator<<(std::ostream& os, S1Angle a); #undef IFNDEF_SWIG #endif // S2_S1ANGLE_H_ s2/src/s2/s2edge_tessellator.cc0000644000176200001440000003250414530411473016020 0ustar liggesusers// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2edge_tessellator.h" #include #include "s2/s2edge_distances.h" #include "s2/s2latlng.h" #include "s2/s2pointutil.h" using std::vector; // Tessellation is implemented by subdividing the edge until the estimated // maximum error is below the given tolerance. Estimating error is a hard // problem, especially when the only methods available are point evaluation of // the projection and its inverse. (These are the only methods that // S2::Projection provides, which makes it easier and less error-prone to // implement new projections.) // // One technique that significantly increases robustness is to treat the // geodesic and projected edges as parametric curves rather than geometric ones. // Given a spherical edge AB and a projection p:S2->R2, let f(t) be the // normalized arc length parametrization of AB and let g(t) be the normalized // arc length parameterization of the projected edge p(A)p(B). (In other words, // f(0)=A, f(1)=B, g(0)=p(A), g(1)=p(B).) We now define the geometric error as // the maximum distance from the point p^-1(g(t)) to the geodesic edge AB for // any t in [0,1], where p^-1 denotes the inverse projection. In other words, // the geometric error is the maximum distance from any point on the projected // edge (mapped back onto the sphere) to the geodesic edge AB. On the other // hand we define the parametric error as the maximum distance between the // points f(t) and p^-1(g(t)) for any t in [0,1], i.e. the maximum distance // (measured on the sphere) between the geodesic and projected points at the // same interpolation fraction t. // // The easiest way to estimate the parametric error is to simply evaluate both // edges at their midpoints and measure the distance between them (the "midpoint // method"). This is very fast and works quite well for most edges, however it // has one major drawback: it doesn't handle points of inflection (i.e., points // where the curvature changes sign). For example, edges in the Mercator and // Plate Carree projections always curve towards the equator relative to the // corresponding geodesic edge, so in these projections there is a point of // inflection whenever the projected edge crosses the equator. The worst case // occurs when the edge endpoints have different longitudes but the same // absolute latitude, since in that case the error is non-zero but the edges // have exactly the same midpoint (on the equator). // // One solution to this problem is to split the input edges at all inflection // points (i.e., along the equator in the case of the Mercator and Plate Carree // projections). However for general projections these inflection points can // occur anywhere on the sphere (e.g., consider the Transverse Mercator // projection). This could be addressed by adding methods to the S2Projection // interface to split edges at inflection points but this would make it harder // and more error-prone to implement new projections. // // Another problem with this approach is that the midpoint method sometimes // underestimates the true error even when edges do not cross the equator. For // the Plate Carree and Mercator projections, the midpoint method can // underestimate the error by up to 3%. // // Both of these problems can be solved as follows. We assume that the error // can be modeled as a convex combination of two worst-case functions, one // where the error is maximized at the edge midpoint and another where the // error is *minimized* (i.e., zero) at the edge midpoint. For example, we // could choose these functions as: // // E1(x) = 1 - x^2 // E2(x) = x * (1 - x^2) // // where for convenience we use an interpolation parameter "x" in the range // [-1, 1] rather than the original "t" in the range [0, 1]. Note that both // error functions must have roots at x = {-1, 1} since the error must be zero // at the edge endpoints. E1 is simply a parabola whose maximum value is 1 // attained at x = 0, while E2 is a cubic with an additional root at x = 0, // and whose maximum value is 2 * sqrt(3) / 9 attained at x = 1 / sqrt(3). // // Next, it is convenient to scale these functions so that the both have a // maximum value of 1. E1 already satisfies this requirement, and we simply // redefine E2 as // // E2(x) = x * (1 - x^2) / (2 * sqrt(3) / 9) // // Now define x0 to be the point where these two functions intersect, i.e. the // point in the range (-1, 1) where E1(x0) = E2(x0). This value has the very // convenient property that if we evaluate the actual error E(x0), then the // maximum error on the entire interval [-1, 1] is bounded by // // E(x) <= E(x0) / E1(x0) // // since whether the error is modeled using E1 or E2, the resulting function // has the same maximum value (namely E(x0) / E1(x0)). If it is modeled as // some other convex combination of E1 and E2, the maximum value can only // decrease. // // Finally, since E2 is not symmetric about the y-axis, we must also allow for // the possibility that the error is a convex combination of E1 and -E2. This // can be handled by evaluating the error at E(-x0) as well, and then // computing the final error bound as // // E(x) <= max(E(x0), E(-x0)) / E1(x0) . // // Effectively, this method is simply evaluating the error at two points about // 1/3 and 2/3 of the way along the edges, and then scaling the maximum of // these two errors by a constant factor. Intuitively, the reason this works // is that if the two edges cross somewhere in the interior, then at least one // of these points will be far from the crossing. // // The actual algorithm implemented below has some additional refinements. // First, edges longer than 90 degrees are always subdivided; this avoids // various unusual situations that can happen with very long edges, and there // is really no reason to avoid adding vertices to edges that are so long. // // Second, the error function E1 above needs to be modified to take into // account spherical distortions. (It turns out that spherical distortions are // beneficial in the case of E2, i.e. they only make its error estimates // slightly more conservative.) To do this, we model E1 as the maximum error // in a Plate Carree edge of length 90 degrees or less. This turns out to be // an edge from 45:-90 to 45:90 (in lat:lng format). The corresponding error // as a function of "x" in the range [-1, 1] can be computed as the distance // between the the the Plate Caree edge point (45, 90 * x) and the geodesic // edge point (90 - 45 * abs(x), 90 * sgn(x)). Using the Haversine formula, // the corresponding function E1 (normalized to have a maximum value of 1) is: // // E1(x) = // asin(sqrt(sin(Pi / 8 * (1 - x)) ^ 2 + // sin(Pi / 4 * (1 - x)) ^ 2 * cos(Pi / 4) * sin(Pi / 4 * x))) / // asin(sqrt((1 - 1 / sqrt(2)) / 2)) // // Note that this function does not need to be evaluated at runtime, it // simply affects the calculation of the value x0 where E1(x0) = E2(x0) // and the corresponding scaling factor C = 1 / E1(x0). // // ------------------------------------------------------------------ // // In the case of the Mercator and Plate Carree projections this strategy // produces a conservative upper bound (verified using 10 million random // edges). Furthermore the bound is nearly tight; the scaling constant is // C = 1.19289, whereas the maximum observed value was 1.19254. // // Compared to the simpler midpoint evaluation method, this strategy requires // more function evaluations (currently twice as many, but with a smarter // tessellation algorithm it will only be 50% more). It also results in a // small amount of additional tessellation (about 1.5%) compared to the // midpoint method, but this is due almost entirely to the fact that the // midpoint method does not yield conservative error estimates. // // For random edges with a tolerance of 1 meter, the expected amount of // overtessellation is as follows: // // Midpoint Method Cubic Method // Plate Carree 1.8% 3.0% // Mercator 15.8% 17.4% // The interpolation fraction at which the two edges are evaluated in order to // measure the error between them. (Edges are evaluated at two points measured // this fraction from either end.) With respect to the algorithm description // above, this value is t0 = (1 - x0) / 2 in the range [0, 1] that corresponds // to x0 in the range [-1, 1] chosen such that E1(x0) == E2(x0). static constexpr double kInterpolationFraction = 0.31215691082248312; // The following is the value of E1(x0) == E2(x0). static constexpr double kScaleFactor = 0.83829992569888509; S1Angle S2EdgeTessellator::kMinTolerance() { // This distance is less than 1 micrometer on the Earth's surface, but is // still much larger than the expected projection and interpolation errors. return S1Angle::Radians(1e-13); } S2EdgeTessellator::S2EdgeTessellator(const S2::Projection* projection, S1Angle tolerance) : proj_(*projection) { if (tolerance < kMinTolerance()) S2_LOG(DFATAL) << "Tolerance too small"; // Rather than scaling the error estimate as described above, instead we scale // the tolerance. See algorithm description at the top of this file. scaled_tolerance_ = S1ChordAngle(kScaleFactor * std::max(tolerance, kMinTolerance())); } S1ChordAngle S2EdgeTessellator::EstimateMaxError( const R2Point& pa, const S2Point& a, const R2Point& pb, const S2Point& b) const { // See the algorithm description at the top of this file. // We always tessellate edges longer than 90 degrees on the sphere, since the // approximation below is not robust enough to handle such edges. if (a.DotProd(b) < -1e-14) return S1ChordAngle::Infinity(); constexpr double t1 = kInterpolationFraction; constexpr double t2 = 1 - kInterpolationFraction; S2Point mid1 = S2::Interpolate(t1, a, b); S2Point mid2 = S2::Interpolate(t2, a, b); S2Point pmid1 = proj_.Unproject(proj_.Interpolate(t1, pa, pb)); S2Point pmid2 = proj_.Unproject(proj_.Interpolate(t2, pa, pb)); return std::max(S1ChordAngle(mid1, pmid1), S1ChordAngle(mid2, pmid2)); } void S2EdgeTessellator::AppendProjected( const S2Point& a, const S2Point& b, vector* vertices) const { R2Point pa = proj_.Project(a); if (vertices->empty()) { vertices->push_back(pa); } else { pa = proj_.WrapDestination(vertices->back(), pa); S2_DCHECK_EQ(vertices->back(), pa) << "Appended edges must form a chain"; } R2Point pb = proj_.Project(b); AppendProjected(pa, a, pb, b, vertices); } // Given a geodesic edge AB, split the edge as necessary and append all // projected vertices except the first to "vertices". // // The maximum recursion depth is (M_PI / kMinTolerance()) < 45, and the // frame size is small so stack overflow should not be an issue. void S2EdgeTessellator::AppendProjected(const R2Point& pa, const S2Point& a, const R2Point& pb_in, const S2Point& b, vector* vertices) const { R2Point pb = proj_.WrapDestination(pa, pb_in); if (EstimateMaxError(pa, a, pb, b) <= scaled_tolerance_) { vertices->push_back(pb); } else { S2Point mid = (a + b).Normalize(); R2Point pmid = proj_.WrapDestination(pa, proj_.Project(mid)); AppendProjected(pa, a, pmid, mid, vertices); AppendProjected(pmid, mid, pb, b, vertices); } } void S2EdgeTessellator::AppendUnprojected( const R2Point& pa, const R2Point& pb, vector* vertices) const { S2Point a = proj_.Unproject(pa); S2Point b = proj_.Unproject(pb); if (vertices->empty()) { vertices->push_back(a); } else { // Note that coordinate wrapping can create a small amount of error. For // example in the edge chain "0:-175, 0:179, 0:-177", the first edge is // transformed into "0:-175, 0:-181" while the second is transformed into // "0:179, 0:183". The two coordinate pairs for the middle vertex // ("0:-181" and "0:179") may not yield exactly the same S2Point. S2_DCHECK(S2::ApproxEquals(vertices->back(), a)) << "Appended edges must form a chain"; } AppendUnprojected(pa, a, pb, b, vertices); } // Like AppendProjected, but interpolates a projected edge and appends the // corresponding points on the sphere. void S2EdgeTessellator::AppendUnprojected( const R2Point& pa, const S2Point& a, const R2Point& pb_in, const S2Point& b, vector* vertices) const { // See notes above regarding measuring the interpolation error. R2Point pb = proj_.WrapDestination(pa, pb_in); if (EstimateMaxError(pa, a, pb, b) <= scaled_tolerance_) { vertices->push_back(b); } else { R2Point pmid = proj_.Interpolate(0.5, pa, pb); S2Point mid = proj_.Unproject(pmid); AppendUnprojected(pa, a, pmid, mid, vertices); AppendUnprojected(pmid, mid, pb, b, vertices); } } s2/src/s2/s2latlng_rect_bounder.cc0000644000176200001440000004161614530411473016513 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2latlng_rect_bounder.h" #include #include #include "s2/base/logging.h" #include "s2/r1interval.h" #include "s2/s1chord_angle.h" #include "s2/s1interval.h" #include "s2/s2pointutil.h" using std::fabs; using std::max; using std::min; void S2LatLngRectBounder::AddPoint(const S2Point& b) { S2_DCHECK(S2::IsUnitLength(b)); AddInternal(b, S2LatLng(b)); } void S2LatLngRectBounder::AddLatLng(const S2LatLng& b_latlng) { AddInternal(b_latlng.ToPoint(), b_latlng); } void S2LatLngRectBounder::AddInternal(const S2Point& b, const S2LatLng& b_latlng) { // Simple consistency check to verify that b and b_latlng are alternate // representations of the same vertex. S2_DCHECK(S2::ApproxEquals(b, b_latlng.ToPoint())); if (bound_.is_empty()) { bound_.AddPoint(b_latlng); } else { // First compute the cross product N = A x B robustly. This is the normal // to the great circle through A and B. We don't use S2::RobustCrossProd() // since that method returns an arbitrary vector orthogonal to A if the two // vectors are proportional, and we want the zero vector in that case. Vector3_d n = (a_ - b).CrossProd(a_ + b); // N = 2 * (A x B) // The relative error in N gets large as its norm gets very small (i.e., // when the two points are nearly identical or antipodal). We handle this // by choosing a maximum allowable error, and if the error is greater than // this we fall back to a different technique. Since it turns out that // the other sources of error in converting the normal to a maximum // latitude add up to at most 1.16 * DBL_EPSILON (see below), and it is // desirable to have the total error be a multiple of DBL_EPSILON, we have // chosen to limit the maximum error in the normal to 3.84 * DBL_EPSILON. // It is possible to show that the error is less than this when // // n.Norm() >= 8 * sqrt(3) / (3.84 - 0.5 - sqrt(3)) * DBL_EPSILON // = 1.91346e-15 (about 8.618 * DBL_EPSILON) double n_norm = n.Norm(); if (n_norm < 1.91346e-15) { // A and B are either nearly identical or nearly antipodal (to within // 4.309 * DBL_EPSILON, or about 6 nanometers on the earth's surface). if (a_.DotProd(b) < 0) { // The two points are nearly antipodal. The easiest solution is to // assume that the edge between A and B could go in any direction // around the sphere. bound_ = S2LatLngRect::Full(); } else { // The two points are nearly identical (to within 4.309 * DBL_EPSILON). // In this case we can just use the bounding rectangle of the points, // since after the expansion done by GetBound() this rectangle is // guaranteed to include the (lat,lng) values of all points along AB. bound_ = bound_.Union(S2LatLngRect::FromPointPair(a_latlng_, b_latlng)); } } else { // Compute the longitude range spanned by AB. S1Interval lng_ab = S1Interval::FromPointPair(a_latlng_.lng().radians(), b_latlng.lng().radians()); if (lng_ab.GetLength() >= M_PI - 2 * DBL_EPSILON) { // The points lie on nearly opposite lines of longitude to within the // maximum error of the calculation. (Note that this test relies on // the fact that M_PI is slightly less than the true value of Pi, and // that representable values near M_PI are 2 * DBL_EPSILON apart.) // The easiest solution is to assume that AB could go on either side // of the pole. lng_ab = S1Interval::Full(); } // Next we compute the latitude range spanned by the edge AB. We start // with the range spanning the two endpoints of the edge: R1Interval lat_ab = R1Interval::FromPointPair(a_latlng_.lat().radians(), b_latlng.lat().radians()); // This is the desired range unless the edge AB crosses the plane // through N and the Z-axis (which is where the great circle through A // and B attains its minimum and maximum latitudes). To test whether AB // crosses this plane, we compute a vector M perpendicular to this // plane and then project A and B onto it. Vector3_d m = n.CrossProd(S2Point(0, 0, 1)); double m_a = m.DotProd(a_); double m_b = m.DotProd(b); // We want to test the signs of "m_a" and "m_b", so we need to bound // the error in these calculations. It is possible to show that the // total error is bounded by // // (1 + sqrt(3)) * DBL_EPSILON * n_norm + 8 * sqrt(3) * (DBL_EPSILON**2) // = 6.06638e-16 * n_norm + 6.83174e-31 double m_error = 6.06638e-16 * n_norm + 6.83174e-31; if (m_a * m_b < 0 || fabs(m_a) <= m_error || fabs(m_b) <= m_error) { // Minimum/maximum latitude *may* occur in the edge interior. // // The maximum latitude is 90 degrees minus the latitude of N. We // compute this directly using atan2 in order to get maximum accuracy // near the poles. // // Our goal is compute a bound that contains the computed latitudes of // all S2Points P that pass the point-in-polygon containment test. // There are three sources of error we need to consider: // - the directional error in N (at most 3.84 * DBL_EPSILON) // - converting N to a maximum latitude // - computing the latitude of the test point P // The latter two sources of error are at most 0.955 * DBL_EPSILON // individually, but it is possible to show by a more complex analysis // that together they can add up to at most 1.16 * DBL_EPSILON, for a // total error of 5 * DBL_EPSILON. // // We add 3 * DBL_EPSILON to the bound here, and GetBound() will pad // the bound by another 2 * DBL_EPSILON. double max_lat = min( atan2(sqrt(n[0]*n[0] + n[1]*n[1]), fabs(n[2])) + 3 * DBL_EPSILON, M_PI_2); // In order to get tight bounds when the two points are close together, // we also bound the min/max latitude relative to the latitudes of the // endpoints A and B. First we compute the distance between A and B, // and then we compute the maximum change in latitude between any two // points along the great circle that are separated by this distance. // This gives us a latitude change "budget". Some of this budget must // be spent getting from A to B; the remainder bounds the round-trip // distance (in latitude) from A or B to the min or max latitude // attained along the edge AB. double lat_budget = 2 * asin(0.5 * (a_ - b).Norm() * sin(max_lat)); double max_delta = 0.5*(lat_budget - lat_ab.GetLength()) + DBL_EPSILON; // Test whether AB passes through the point of maximum latitude or // minimum latitude. If the dot product(s) are small enough then the // result may be ambiguous. if (m_a <= m_error && m_b >= -m_error) { lat_ab.set_hi(min(max_lat, lat_ab.hi() + max_delta)); } if (m_b <= m_error && m_a >= -m_error) { lat_ab.set_lo(max(-max_lat, lat_ab.lo() - max_delta)); } } bound_ = bound_.Union(S2LatLngRect(lat_ab, lng_ab)); } } a_ = b; a_latlng_ = b_latlng; } S2LatLngRect S2LatLngRectBounder::GetBound() const { // To save time, we ignore numerical errors in the computed S2LatLngs while // accumulating the bounds and then account for them here. // // S2LatLng(S2Point) has a maximum error of 0.955 * DBL_EPSILON in latitude. // In the worst case, we might have rounded "inwards" when computing the // bound and "outwards" when computing the latitude of a contained point P, // therefore we expand the latitude bounds by 2 * DBL_EPSILON in each // direction. (A more complex analysis shows that 1.5 * DBL_EPSILON is // enough, but the expansion amount should be a multiple of DBL_EPSILON in // order to avoid rounding errors during the expansion itself.) // // S2LatLng(S2Point) has a maximum error of DBL_EPSILON in longitude, which // is simply the maximum rounding error for results in the range [-Pi, Pi]. // This is true because the Gnu implementation of atan2() comes from the IBM // Accurate Mathematical Library, which implements correct rounding for this // instrinsic (i.e., it returns the infinite precision result rounded to the // nearest representable value, with ties rounded to even values). This // implies that we don't need to expand the longitude bounds at all, since // we only guarantee that the bound contains the *rounded* latitudes of // contained points. The *true* latitudes of contained points may lie up to // DBL_EPSILON outside of the returned bound. const S2LatLng kExpansion = S2LatLng::FromRadians(2 * DBL_EPSILON, 0); return bound_.Expanded(kExpansion).PolarClosure(); } S2LatLngRect S2LatLngRectBounder::ExpandForSubregions( const S2LatLngRect& bound) { // Empty bounds don't need expansion. if (bound.is_empty()) return bound; // First we need to check whether the bound B contains any nearly-antipodal // points (to within 4.309 * DBL_EPSILON). If so then we need to return // S2LatLngRect::Full(), since the subregion might have an edge between two // such points, and AddPoint() returns Full() for such edges. Note that // this can happen even if B is not Full(); for example, consider a loop // that defines a 10km strip straddling the equator extending from // longitudes -100 to +100 degrees. // // It is easy to check whether B contains any antipodal points, but checking // for nearly-antipodal points is trickier. Essentially we consider the // original bound B and its reflection through the origin B', and then test // whether the minimum distance between B and B' is less than 4.309 * // DBL_EPSILON. // "lng_gap" is a lower bound on the longitudinal distance between B and its // reflection B'. (2.5 * DBL_EPSILON is the maximum combined error of the // endpoint longitude calculations and the GetLength() call.) double lng_gap = max(0.0, M_PI - bound.lng().GetLength() - 2.5 * DBL_EPSILON); // "min_abs_lat" is the minimum distance from B to the equator (if zero or // negative, then B straddles the equator). double min_abs_lat = max(bound.lat().lo(), -bound.lat().hi()); // "lat_gap1" and "lat_gap2" measure the minimum distance from B to the // south and north poles respectively. double lat_gap1 = M_PI_2 + bound.lat().lo(); double lat_gap2 = M_PI_2 - bound.lat().hi(); if (min_abs_lat >= 0) { // The bound B does not straddle the equator. In this case the minimum // distance is between one endpoint of the latitude edge in B closest to // the equator and the other endpoint of that edge in B'. The latitude // distance between these two points is 2*min_abs_lat, and the longitude // distance is lng_gap. We could compute the distance exactly using the // Haversine formula, but then we would need to bound the errors in that // calculation. Since we only need accuracy when the distance is very // small (close to 4.309 * DBL_EPSILON), we substitute the Euclidean // distance instead. This gives us a right triangle XYZ with two edges of // length x = 2*min_abs_lat and y ~= lng_gap. The desired distance is the // length of the third edge "z", and we have // // z ~= sqrt(x^2 + y^2) >= (x + y) / sqrt(2) // // Therefore the region may contain nearly antipodal points only if // // 2*min_abs_lat + lng_gap < sqrt(2) * 4.309 * DBL_EPSILON // ~= 1.354e-15 // // Note that because the given bound B is conservative, "min_abs_lat" and // "lng_gap" are both lower bounds on their true values so we do not need // to make any adjustments for their errors. if (2 * min_abs_lat + lng_gap < 1.354e-15) { return S2LatLngRect::Full(); } } else if (lng_gap >= M_PI_2) { // B spans at most Pi/2 in longitude. The minimum distance is always // between one corner of B and the diagonally opposite corner of B'. We // use the same distance approximation that we used above; in this case // we have an obtuse triangle XYZ with two edges of length x = lat_gap1 // and y = lat_gap2, and angle Z >= Pi/2 between them. We then have // // z >= sqrt(x^2 + y^2) >= (x + y) / sqrt(2) // // Unlike the case above, "lat_gap1" and "lat_gap2" are not lower bounds // (because of the extra addition operation, and because M_PI_2 is not // exactly equal to Pi/2); they can exceed their true values by up to // 0.75 * DBL_EPSILON. Putting this all together, the region may // contain nearly antipodal points only if // // lat_gap1 + lat_gap2 < (sqrt(2) * 4.309 + 1.5) * DBL_EPSILON // ~= 1.687e-15 if (lat_gap1 + lat_gap2 < 1.687e-15) { return S2LatLngRect::Full(); } } else { // Otherwise we know that (1) the bound straddles the equator and (2) its // width in longitude is at least Pi/2. In this case the minimum // distance can occur either between a corner of B and the diagonally // opposite corner of B' (as in the case above), or between a corner of B // and the opposite longitudinal edge reflected in B'. It is sufficient // to only consider the corner-edge case, since this distance is also a // lower bound on the corner-corner distance when that case applies. // Consider the spherical triangle XYZ where X is a corner of B with // minimum absolute latitude, Y is the closest pole to X, and Z is the // point closest to X on the opposite longitudinal edge of B'. This is a // right triangle (Z = Pi/2), and from the spherical law of sines we have // // sin(z) / sin(Z) = sin(y) / sin(Y) // sin(max_lat_gap) / 1 = sin(d_min) / sin(lng_gap) // sin(d_min) = sin(max_lat_gap) * sin(lng_gap) // // where "max_lat_gap" = max(lat_gap1, lat_gap2) and "d_min" is the // desired minimum distance. Now using the facts that sin(t) >= (2/Pi)*t // for 0 <= t <= Pi/2, that we only need an accurate approximation when // at least one of "max_lat_gap" or "lng_gap" is extremely small (in // which case sin(t) ~= t), and recalling that "max_lat_gap" has an error // of up to 0.75 * DBL_EPSILON, we want to test whether // // max_lat_gap * lng_gap < (4.309 + 0.75) * (Pi/2) * DBL_EPSILON // ~= 1.765e-15 if (max(lat_gap1, lat_gap2) * lng_gap < 1.765e-15) { return S2LatLngRect::Full(); } } // Next we need to check whether the subregion might contain any edges that // span (M_PI - 2 * DBL_EPSILON) radians or more in longitude, since AddPoint // sets the longitude bound to Full() in that case. This corresponds to // testing whether (lng_gap <= 0) in "lng_expansion" below. // Otherwise, the maximum latitude error in AddPoint is 4.8 * DBL_EPSILON. // In the worst case, the errors when computing the latitude bound for a // subregion could go in the opposite direction as the errors when computing // the bound for the original region, so we need to double this value. // (More analysis shows that it's okay to round down to a multiple of // DBL_EPSILON.) // // For longitude, we rely on the fact that atan2 is correctly rounded and // therefore no additional bounds expansion is necessary. double lat_expansion = 9 * DBL_EPSILON; double lng_expansion = (lng_gap <= 0) ? M_PI : 0; return bound.Expanded(S2LatLng::FromRadians(lat_expansion, lng_expansion)).PolarClosure(); } S2LatLng S2LatLngRectBounder::MaxErrorForTests() { // The maximum error in the latitude calculation is // 3.84 * DBL_EPSILON for the RobustCrossProd calculation // 0.96 * DBL_EPSILON for the Latitude() calculation // 5 * DBL_EPSILON added by AddPoint/GetBound to compensate for error // ------------------ // 9.80 * DBL_EPSILON maximum error in result // // The maximum error in the longitude calculation is DBL_EPSILON. GetBound // does not do any expansion because this isn't necessary in order to // bound the *rounded* longitudes of contained points. return S2LatLng::FromRadians(10 * DBL_EPSILON, 1 * DBL_EPSILON); } s2/src/s2/s2shape_measures.cc0000644000176200001440000001105314530411473015473 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2shape_measures.h" #include #include #include "s2/s2loop_measures.h" #include "s2/s2polyline_measures.h" using std::fabs; using std::vector; namespace S2 { S1Angle GetLength(const S2Shape& shape) { if (shape.dimension() != 1) return S1Angle::Zero(); S1Angle length; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); length += S2::GetLength(vertices); } return length; } S1Angle GetPerimeter(const S2Shape& shape) { if (shape.dimension() != 2) return S1Angle::Zero(); S1Angle perimeter; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); perimeter += S2::GetPerimeter(S2PointLoopSpan(vertices)); } return perimeter; } double GetArea(const S2Shape& shape) { if (shape.dimension() != 2) return 0.0; // Since S2Shape uses the convention that the interior of the shape is to // the left of all edges, in theory we could compute the area of the polygon // by simply adding up all the loop areas modulo 4*Pi. The problem with // this approach is that polygons holes typically have areas near 4*Pi, // which can create large cancellation errors when computing the area of // small polygons with holes. For example, a shell with an area of 4 square // meters (1e-13 steradians) surrounding a hole with an area of 3 square // meters (7.5e-14 sterians) would lose almost all of its accuracy if the // area of the hole was computed as 12.566370614359098. // // So instead we use S2::GetSignedArea() to ensure that all loops have areas // in the range [-2*Pi, 2*Pi]. double area = 0; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); area += S2::GetSignedArea(S2PointLoopSpan(vertices)); } // Note that S2::GetSignedArea() guarantees that the full loop (containing // all points on the sphere) has a very small negative area. S2_DCHECK_LE(fabs(area), 4 * M_PI); if (area < 0.0) area += 4 * M_PI; return area; } double GetApproxArea(const S2Shape& shape) { if (shape.dimension() != 2) return 0.0; double area = 0; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); area += S2::GetApproxArea(S2PointLoopSpan(vertices)); } // Special case to ensure that full polygons are handled correctly. if (area <= 4 * M_PI) return area; return fmod(area, 4 * M_PI); } S2Point GetCentroid(const S2Shape& shape) { S2Point centroid; vector vertices; int dimension = shape.dimension(); int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { switch (dimension) { case 0: centroid += shape.edge(chain_id).v0; break; case 1: GetChainVertices(shape, chain_id, &vertices); centroid += S2::GetCentroid(S2PointSpan(vertices)); break; default: GetChainVertices(shape, chain_id, &vertices); centroid += S2::GetCentroid(S2PointLoopSpan(vertices)); break; } } return centroid; } void GetChainVertices(const S2Shape& shape, int chain_id, std::vector* vertices) { S2Shape::Chain chain = shape.chain(chain_id); int num_vertices = chain.length + (shape.dimension() == 1); vertices->clear(); vertices->reserve(num_vertices); int e = 0; if (num_vertices & 1) { vertices->push_back(shape.chain_edge(chain_id, e++).v0); } for (; e < num_vertices; e += 2) { auto edge = shape.chain_edge(chain_id, e); vertices->push_back(edge.v0); vertices->push_back(edge.v1); } } } // namespace S2 s2/src/s2/s2error.h0000644000176200001440000001244014530411473013463 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // S2Error is a simple class consisting of an error code and a human-readable // error message. #ifndef S2_S2ERROR_H_ #define S2_S2ERROR_H_ #include #include #include #include "s2/base/port.h" // This class is intended to be copied by value as desired. It uses // the default copy constructor and assignment operator. class S2Error { public: enum Code { OK = 0, // No error. //////////////////////////////////////////////////////////////////// // Generic errors, not specific to geometric objects: UNKNOWN = 1000, // Unknown error. UNIMPLEMENTED = 1001, // Operation is not implemented. OUT_OF_RANGE = 1002, // Argument is out of range. INVALID_ARGUMENT = 1003, // Invalid argument (other than a range error). FAILED_PRECONDITION = 1004, // Object is not in the required state. INTERNAL = 1005, // An internal invariant has failed. DATA_LOSS = 1006, // Data loss or corruption. RESOURCE_EXHAUSTED = 1007, // A resource has been exhausted. //////////////////////////////////////////////////////////////////// // Error codes in the following range can be defined by clients: USER_DEFINED_START = 1000000, USER_DEFINED_END = 9999999, //////////////////////////////////////////////////////////////////// // Errors that apply to more than one type of geometry: NOT_UNIT_LENGTH = 1, // Vertex is not unit length. DUPLICATE_VERTICES = 2, // There are two identical vertices. ANTIPODAL_VERTICES = 3, // There are two antipodal vertices. //////////////////////////////////////////////////////////////////// // S2Loop errors: LOOP_NOT_ENOUGH_VERTICES = 100, // Loop with fewer than 3 vertices. LOOP_SELF_INTERSECTION = 101, // Loop has a self-intersection. //////////////////////////////////////////////////////////////////// // S2Polygon errors: POLYGON_LOOPS_SHARE_EDGE = 200, // Two polygon loops share an edge. POLYGON_LOOPS_CROSS = 201, // Two polygon loops cross. POLYGON_EMPTY_LOOP = 202, // Polygon has an empty loop. POLYGON_EXCESS_FULL_LOOP = 203, // Non-full polygon has a full loop. // InitOriented() was called and detected inconsistent loop orientations. POLYGON_INCONSISTENT_LOOP_ORIENTATIONS = 204, // Loop depths don't correspond to any valid nesting hierarchy. POLYGON_INVALID_LOOP_DEPTH = 205, // Actual polygon nesting does not correspond to the nesting hierarchy // encoded by the loop depths. POLYGON_INVALID_LOOP_NESTING = 206, //////////////////////////////////////////////////////////////////// // S2Builder errors: // The S2Builder snap function moved a vertex by more than the specified // snap radius. BUILDER_SNAP_RADIUS_TOO_SMALL = 300, // S2Builder expected all edges to have siblings (as specified by // S2Builder::GraphOptions::SiblingPairs::REQUIRE), but some were missing. BUILDER_MISSING_EXPECTED_SIBLING_EDGES = 301, // S2Builder found an unexpected degenerate edge. For example, // Graph::GetLeftTurnMap() does not support degenerate edges. BUILDER_UNEXPECTED_DEGENERATE_EDGE = 302, // S2Builder found a vertex with (indegree != outdegree), which means // that the given edges cannot be assembled into loops. BUILDER_EDGES_DO_NOT_FORM_LOOPS = 303, // The edges provided to S2Builder cannot be assembled into a polyline. BUILDER_EDGES_DO_NOT_FORM_POLYLINE = 304, // There was an attempt to assemble a polygon from degenerate geometry // without having specified a predicate to decide whether the output is // the empty polygon (containing no points) or the full polygon // (containing all points). BUILDER_IS_FULL_PREDICATE_NOT_SPECIFIED = 305, }; S2Error() : code_(OK), text_() {} // Set the error to the given code and printf-style message. Note that you // can prepend text to an existing error by calling Init() more than once: // // error->Init(error->code(), "Loop %d: %s", j, error->text().c_str()); void Init(Code code, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(3, 4); bool ok() const { return code_ == OK; } Code code() const { return code_; } std::string text() const { return text_; } // Clear the error to contain the OK code and no error message. void Clear(); private: Code code_; std::string text_; }; ////////////////// Implementation details follow //////////////////// inline std::ostream& operator<<(std::ostream& os, const S2Error& error) { return os << error.text(); } inline void S2Error::Clear() { code_ = OK; text_.clear(); } #endif // S2_S2ERROR_H_ s2/src/s2/s2shapeutil_get_reference_point.h0000644000176200001440000000407114530411473020417 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_ #define S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_ #include "s2/s2shape_index.h" namespace s2shapeutil { // This is a helper function for implementing S2Shape::GetReferencePoint(). // // Given a shape consisting of closed polygonal loops, the interior of the // shape is defined as the region to the left of all edges (which must be // oriented consistently). This function then chooses an arbitrary point and // returns true if that point is contained by the shape. // // Unlike S2Loop and S2Polygon, this method allows duplicate vertices and // edges, which requires some extra care with definitions. The rule that we // apply is that an edge and its reverse edge "cancel" each other: the result // is the same as if that edge pair were not present. Therefore shapes that // consist only of degenerate loop(s) are either empty or full; by convention, // the shape is considered full if and only if it contains an empty loop (see // S2LaxPolygonShape for details). // // Determining whether a loop on the sphere contains a point is harder than // the corresponding problem in 2D plane geometry. It cannot be implemented // just by counting edge crossings because there is no such thing as a "point // at infinity" that is guaranteed to be outside the loop. S2Shape::ReferencePoint GetReferencePoint(const S2Shape& shape); } // namespace s2shapeutil #endif // S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_ s2/src/s2/s2edge_crosser.cc0000644000176200001440000000716114530411473015140 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2edge_crosser.h" #include "s2/base/logging.h" #include "s2/s2pointutil.h" #include "s2/s2predicates.h" int S2EdgeCrosser::CrossingSignInternal(const S2Point* d) { // Compute the actual result, and then save the current vertex D as the next // vertex C, and save the orientation of the next triangle ACB (which is // opposite to the current triangle BDA). int result = CrossingSignInternal2(*d); c_ = d; acb_ = -bda_; return result; } inline int S2EdgeCrosser::CrossingSignInternal2(const S2Point& d) { // At this point, a very common situation is that A,B,C,D are four points on // a line such that AB does not overlap CD. (For example, this happens when // a line or curve is sampled finely, or when geometry is constructed by // computing the union of S2CellIds.) Most of the time, we can determine // that AB and CD do not intersect by computing the two outward-facing // tangents at A and B (parallel to AB) and testing whether AB and CD are on // opposite sides of the plane perpendicular to one of these tangents. This // is moderately expensive but still much cheaper than s2pred::ExpensiveSign. if (!have_tangents_) { S2Point norm = S2::RobustCrossProd(*a_, *b_).Normalize(); a_tangent_ = a_->CrossProd(norm); b_tangent_ = norm.CrossProd(*b_); have_tangents_ = true; } // The error in RobustCrossProd() is insignificant. The maximum error in // the call to CrossProd() (i.e., the maximum norm of the error vector) is // (0.5 + 1/sqrt(3)) * DBL_EPSILON. The maximum error in each call to // DotProd() below is DBL_EPSILON. (There is also a small relative error // term that is insignificant because we are comparing the result against a // constant that is very close to zero.) static const double kError = (1.5 + 1/sqrt(3.0)) * DBL_EPSILON; if ((c_->DotProd(a_tangent_) > kError && d.DotProd(a_tangent_) > kError) || (c_->DotProd(b_tangent_) > kError && d.DotProd(b_tangent_) > kError)) { return -1; } // Otherwise, eliminate the cases where two vertices from different edges // are equal. (These cases could be handled in the code below, but we would // rather avoid calling ExpensiveSign whenever possible.) if (*a_ == *c_ || *a_ == d || *b_ == *c_ || *b_ == d) return 0; // Eliminate cases where an input edge is degenerate. (Note that in most // cases, if CD is degenerate then this method is not even called because // acb_ and bda have different signs.) if (*a_ == *b_ || *c_ == d) return -1; // Otherwise it's time to break out the big guns. if (acb_ == 0) acb_ = -s2pred::ExpensiveSign(*a_, *b_, *c_); S2_DCHECK_NE(acb_, 0); if (bda_ == 0) bda_ = s2pred::ExpensiveSign(*a_, *b_, d); S2_DCHECK_NE(bda_, 0); if (bda_ != acb_) return -1; Vector3_d c_cross_d = c_->CrossProd(d); int cbd = -s2pred::Sign(*c_, d, *b_, c_cross_d); S2_DCHECK_NE(cbd, 0); if (cbd != acb_) return -1; int dac = s2pred::Sign(*c_, d, *a_, c_cross_d); S2_DCHECK_NE(dac, 0); return (dac != acb_) ? -1 : 1; } s2/src/s2/s2polyline_alignment.h0000644000176200001440000002735114530411473016232 0ustar liggesusers// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_S2POLYLINE_ALIGNMENT_H_ #define S2_S2POLYLINE_ALIGNMENT_H_ #include #include #include "s2/s2polyline.h" // This library provides code to compute vertex alignments between S2Polylines. // // A vertex "alignment" or "warp" between two polylines is a matching between // pairs of their vertices. Users can imagine pairing each vertex from // S2Polyline `a` with at least one other vertex in S2Polyline `b`. The "cost" // of an arbitrary alignment is defined as the summed value of the squared // chordal distance between each pair of points in the warp path. An "optimal // alignment" for a pair of polylines is defined as the alignment with least // cost. Note: optimal alignments are not necessarily unique. The standard way // of computing an optimal alignment between two sequences is the use of the // `Dynamic Timewarp` algorithm. // // We provide three methods for computing (via Dynamic Timewarp) the optimal // alignment between two S2Polylines. These methods are performance-sensitive, // and have been reasonably optimized for space- and time- usage. On modern // hardware, it is possible to compute exact alignments between 4096x4096 // element polylines in ~70ms, and approximate alignments much more quickly. // // The results of an alignment operation are captured in a VertexAlignment // object. In particular, a VertexAlignment keeps track of the total cost of // alignment, as well as the warp path (a sequence of pairs of indices into each // polyline whose vertices are linked together in the optimal alignment) // // For a worked example, consider the polylines // // a = [(1, 0), (5, 0), (6, 0), (9, 0)] and // b = [(2, 0), (7, 0), (8, 0)]. // // The "cost matrix" between these two polylines (using squared chordal // distance, .Norm2(), as our distance function) looks like this: // // (2, 0) (7, 0) (8, 0) // (1, 0) 1 36 49 // (5, 0) 9 4 9 // (6, 0) 16 1 4 // (9, 0) 49 4 1 // // The Dynamic Timewarp DP table for this cost matrix has cells defined by // // table[i][j] = cost(i,j) + min(table[i-1][j-1], table[i][j-1], table[i-1, j]) // // (2, 0) (7, 0) (8, 0) // (1, 0) 1 37 86 // (5, 0) 10 5 14 // (6, 0) 26 6 9 // (9, 0) 75 10 7 // // Starting at the bottom right corner of the DP table, we can work our way // backwards to the upper left corner to recover the reverse of the warp path: // (3, 2) -> (2, 1) -> (1, 1) -> (0, 0). The VertexAlignment produced containing // this has alignment_cost = 7 and warp_path = {(0, 0), (1, 1), (2, 1), (3, 2)}. // // We also provide methods for performing alignment of multiple sequences. These // methods return a single, representative polyline from a non-empty collection // of polylines, for various definitions of "representative." // // GetMedoidPolyline() returns a new polyline (point-for-point-equal to some // existing polyline from the collection) that minimizes the summed vertex // alignment cost to all other polylines in the collection. // // GetConsensusPolyline() returns a new polyline (unlikely to be present in the // input collection) that represents a "weighted consensus" polyline. This // polyline is constructed iteratively using the Dynamic Timewarp Barycenter // Averaging algorithm of F. Petitjean, A. Ketterlin, and P. Gancarski, which // can be found here: // https://pdfs.semanticscholar.org/a596/8ca9488199291ffe5473643142862293d69d.pdf namespace s2polyline_alignment { typedef std::vector> WarpPath; struct VertexAlignment { // `alignment_cost` represents the sum of the squared chordal distances // between each pair of vertices in the warp path. Specifically, // cost = sum_{(i, j) \in path} (a.vertex(i) - b.vertex(j)).Norm2(); // This means that the units of alignment_cost are "squared distance". This is // an optimization to avoid the (expensive) atan computation of the true // spherical angular distance between the points, as well as an unnecessary // square root. All we need to compute vertex alignment is a metric that // satisifies the triangle inequality, and squared chordal distance works as // well as spherical S1Angle distance for this purpose. double alignment_cost; // Each entry (i, j) of `warp_path` represents a pairing between vertex // a.vertex(i) and vertex b.vertex(j) in the optimal alignment. // The warp_path is defined in forward order, such that the result of // aligning polylines `a` and `b` is always a warp_path with warp_path.front() // = {0,0} and warp_path.back() = {a.num_vertices() - 1, b.num_vertices() - 1} // Note that this DOES NOT define an alignment from a point sequence to an // edge sequence. That functionality may come at a later date. WarpPath warp_path; VertexAlignment(const double cost, const WarpPath& path) : alignment_cost(cost), warp_path(path) {} }; // GetExactVertexAlignment takes two non-empty polylines as input, and returns // the VertexAlignment corresponding to the optimal alignment between them. This // method is quadratic O(A*B) in both space and time complexity. VertexAlignment GetExactVertexAlignment(const S2Polyline& a, const S2Polyline& b); // GetExactVertexAlignmentCost takes two non-empty polylines as input, and // returns the *cost* of their optimal alignment. A standard, traditional // dynamic timewarp algorithm can output both a warp path and a cost, but // requires quadratic space to reconstruct the path by walking back through the // Dynamic Programming cost table. If all you really need is the warp cost (i.e. // you're inducing a similarity metric between S2Polylines, or something // equivalent), you can overwrite the DP table and use constant space - // O(max(A,B)). This method provides that space-efficiency optimization. double GetExactVertexAlignmentCost(const S2Polyline& a, const S2Polyline& b); // GetApproxVertexAlignment takes two non-empty polylines `a` and `b` as input, // and a `radius` paramater GetApproxVertexAlignment (quickly) computes an // approximately optimal vertex alignment of points between polylines `a` and // `b` by implementing the algorithm described in `FastDTW: Toward Accurate // Dynamic Time Warping in Linear Time and Space` by Stan Salvador and Philip // Chan. Details can be found below: // // https://pdfs.semanticscholar.org/05a2/0cde15e172fc82f32774dd0cf4fe5827cad2.pdf // // The `radius` parameter controls the distance we search outside of the // projected warp path during the refining step. Smaller values of `radius` // correspond to a smaller search window, and therefore distance computation on // fewer cells, which leads to a faster (but worse) approximation. // This method is O(max(A, B)) in both space and time complexity. VertexAlignment GetApproxVertexAlignment(const S2Polyline& a, const S2Polyline& b, const int radius); // A convience overload for GetApproxVertexAlignment which computes and uses // suggested default parameter of radius = max(a.size(), b.size())^0.25 VertexAlignment GetApproxVertexAlignment(const S2Polyline& a, const S2Polyline& b); // GetMedoidPolyline returns the index `p` of a "medoid" polyline from a // non-empty collection of `polylines` such that // // sum_{all j in `polylines`} VertexAlignmentCost(p, j) is minimized. // // In the case of a tie for minimal summed alignment cost, we return the lowest // index - this tie is guaranteed to happen in the two-polyline-input case. // // ASYMPTOTIC BEHAVIOR: // Computation may require up to (N^2 - N) / 2 alignment cost function // evaluations, for N input polylines. For polylines of length U, V, the // alignment cost function evaluation is O(U+V) if options.approx = true and // O(U*V) if options.approx = false. class MedoidOptions { public: // If options.approx = false, we compute vertex alignment costs exactly. // If options.approx = true, we use approximate vertex alignment // computation, called with the default radius parameter. bool approx() const { return approx_; } void set_approx(bool approx) { approx_ = approx; } private: bool approx_ = true; }; int GetMedoidPolyline(const std::vector>& polylines, const MedoidOptions options); // GetConsensusPolyline allocates and returns a new "consensus" polyline from a // non-empty collection of polylines. We iteratively apply Dynamic Timewarp // Barycenter Averaging to an initial `seed` polyline, which improves the // consensus alignment quality each iteration. For implementation details, see // // https://pdfs.semanticscholar.org/a596/8ca9488199291ffe5473643142862293d69d.pdf // // The returned polyline from this method is unlikely to be point-for-point // equal to an input polyline, whereas a polyline returned from // GetMedoidPolyline() is guaranteed to match an input polyline point-for-point. // NOTE: the number of points in our returned consensus polyline is always equal // to the number of points in the initial seed, which is implementation-defined. // If the collection of polylines has a large resolution distribution, it might // be a good idea to reinterpolate them to have about the same number of points. // In practice, this doesn't seem to matter, but is probably worth noting. // // ASYMPTOTIC BEHAVIOR: // Seeding this algorithm requires O(1) vertex alignments if seed_medoid = // false, and O(N^2) vertex alignments if seed_medoid = true. Once the seed // polyline is chosen, computing the consensus polyline requires at most // (iteration_cap)*N vertex alignments. For polylines of length U, V, the // alignment cost function evaluation is O(U+V) if options.approx = true, and // O(U*V) if options.approx = false. class ConsensusOptions { public: // If options.approx = false, vertex alignments are computed with // GetExactVertexAlignment. If options.approx = true, vertex alignments are // computed with GetApproxVertexAlignment, called with default radius // parameter. bool approx() const { return approx_; } void set_approx(bool approx) { approx_ = approx; } // If options.seed_medoid = true, we seed the consensus polyline with the // medoid of the collection. This is a more expensive approach, but may result // in higher quality consensus sequences by avoiding bad arbitrary initial // seeds. Seeding with the medoid will incur up to (N^2 - N) / 2 evaluations // of the vertex alignment function. If options.seed_medoid = false, we seed // the consensus polyline by taking an arbitrary element from the collection. bool seed_medoid() const { return seed_medoid_; } void set_seed_medoid(bool seed_medoid) { seed_medoid_ = seed_medoid; } // options.iteration_cap controls the maximum number of DBA refining steps we // apply to the initial seed. int iteration_cap() const { return iteration_cap_; } void set_iteration_cap(int iteration_cap) { iteration_cap_ = iteration_cap; } private: bool approx_ = true; bool seed_medoid_ = false; int iteration_cap_ = 5; }; std::unique_ptr GetConsensusPolyline( const std::vector>& polylines, const ConsensusOptions options); } // namespace s2polyline_alignment #endif // S2_S2POLYLINE_ALIGNMENT_H_ s2/src/s2/encoded_s2point_vector.h0000644000176200001440000001204314530411473016525 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_ENCODED_S2POINT_VECTOR_H_ #define S2_ENCODED_S2POINT_VECTOR_H_ #include #include "absl/types/span.h" #include "s2/encoded_string_vector.h" #include "s2/encoded_uint_vector.h" #include "s2/s2point.h" namespace s2coding { // Controls whether to optimize for speed or size when encoding points. (Note // that encoding is always lossless, and that currently compact encodings are // only possible when points have been snapped to S2CellId centers.) enum class CodingHint : uint8 { FAST, COMPACT }; // Encodes a vector of S2Points in a format that can later be decoded as an // EncodedS2PointVector. // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). void EncodeS2PointVector(absl::Span points, CodingHint hint, Encoder* encoder); // This class represents an encoded vector of S2Points. Values are decoded // only when they are accessed. This allows for very fast initialization and // no additional memory use beyond the encoded data. The encoded data is not // owned by this class; typically it points into a large contiguous buffer // that contains other encoded data as well. // // This is one of several helper classes that allow complex data structures to // be initialized from an encoded format in constant time and then decoded on // demand. This can be a big performance advantage when only a small part of // the data structure is actually used. class EncodedS2PointVector { public: // Constructs an uninitialized object; requires Init() to be called. EncodedS2PointVector() {} // Initializes the EncodedS2PointVector. // // REQUIRES: The Decoder data buffer must outlive this object. bool Init(Decoder* decoder); // Returns the size of the original vector. size_t size() const; // Returns the element at the given index. S2Point operator[](int i) const; // Decodes and returns the entire original vector. std::vector Decode() const; // TODO(ericv): Consider adding a method that returns an adjacent pair of // points. This would save some decoding overhead. private: friend void EncodeS2PointVector(absl::Span, CodingHint, Encoder*); friend void EncodeS2PointVectorFast(absl::Span, Encoder*); friend void EncodeS2PointVectorCompact(absl::Span, Encoder*); bool InitUncompressedFormat(Decoder* decoder); bool InitCellIdsFormat(Decoder* decoder); S2Point DecodeCellIdsFormat(int i) const; // We use a tagged union to represent multiple formats, as opposed to an // abstract base class or templating. This represents the best compromise // between performance, space, and convenience. Note that the overhead of // checking the tag is trivial and will typically be branch-predicted // perfectly. // // TODO(ericv): Once additional formats have been implemented, consider // using std::variant<> instead. It's unclear whether this would have // better or worse performance than the current approach. // dd: These structs are anonymous in the upstream S2 code; however, // this generates CMD-check failure due to the [-Wnested-anon-types] // (anonymous types declared in an anonymous union are an extension) // The approach here just names the types. struct CellIDStruct { EncodedStringVector blocks; uint64 base; uint8 level; bool have_exceptions; // TODO(ericv): Use std::atomic_flag to cache the last point decoded in // a thread-safe way. This reduces benchmark times for actual polygon // operations (e.g. S2ClosestEdgeQuery) by about 15%. }; struct UncompressedStruct { const S2Point* points; }; enum Format : uint8 { UNCOMPRESSED = 0, CELL_IDS = 1, }; Format format_; uint32 size_; union { struct UncompressedStruct uncompressed_; struct CellIDStruct cell_ids_; }; }; ////////////////// Implementation details follow //////////////////// inline size_t EncodedS2PointVector::size() const { return size_; } inline S2Point EncodedS2PointVector::operator[](int i) const { switch (format_) { case Format::UNCOMPRESSED: return uncompressed_.points[i]; case Format::CELL_IDS: return DecodeCellIdsFormat(i); default: S2_LOG(DFATAL) << "Unrecognized format"; return S2Point(); } } } // namespace s2coding #endif // S2_ENCODED_S2POINT_VECTOR_H_ s2/src/s2/s2polyline_simplifier.h0000644000176200001440000001110114530411473016401 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // This is a helper class for simplifying polylines. It allows you to compute // a maximal edge that intersects a sequence of discs, and that optionally // avoids a different sequence of discs. The results are conservative in that // the edge is guaranteed to intersect or avoid the specified discs using // exact arithmetic (see s2predicates.h). // // Note that S2Builder can also simplify polylines and supports more features // (e.g., snapping to S2CellId centers), so it is only recommended to use this // class if S2Builder does not meet your needs. // // Here is a simple example showing how to simplify a polyline into a sequence // of edges that stay within "max_error" of the original edges: // // vector v = { ... }; // S2PolylineSimplifier simplifier; // simplifier.Init(v[0]); // for (int i = 1; i < v.size(); ++i) { // if (!simplifier.Extend(v[i])) { // OutputEdge(simplifier.src(), v[i-1]); // simplifier.Init(v[i-1]); // } // simplifier.TargetDisc(v[i], max_error); // } // OutputEdge(simplifer.src(), v.back()); // // Note that the points targeted by TargetDisc do not need to be the same as // the candidate endpoints passed to Extend. So for example, you could target // the original vertices of a polyline, but only consider endpoints that are // snapped to E7 coordinates or S2CellId centers. // // Please be aware that this class works by maintaining a range of acceptable // angles (bearings) from the start vertex to the hypothetical destination // vertex. It does not keep track of distances to any of the discs to be // targeted or avoided. Therefore to use this class correctly, constraints // should be added in increasing order of distance. (The actual requirement // is slightly weaker than this, which is why it is not enforced, but // basically you should only call TargetDisc() and AvoidDisc() with arguments // that you want to constrain the immediately following call to Extend().) #ifndef S2_S2POLYLINE_SIMPLIFIER_H_ #define S2_S2POLYLINE_SIMPLIFIER_H_ #include "s2/_fp_contract_off.h" #include "s2/s1chord_angle.h" #include "s2/s1interval.h" class S2PolylineSimplifier { public: S2PolylineSimplifier() {} // Starts a new simplified edge at "src". void Init(const S2Point& src); // Returns the source vertex of the output edge. S2Point src() const; // Returns true if the edge (src, dst) satisfies all of the targeting // requirements so far. Returns false if the edge would be longer than // 90 degrees (such edges are not supported). bool Extend(const S2Point& dst) const; // Requires that the output edge must pass through the given disc. bool TargetDisc(const S2Point& point, S1ChordAngle radius); // Requires that the output edge must avoid the given disc. "disc_on_left" // specifies whether the disc must be to the left or right of the edge. // (This feature allows the simplified edge to preserve the topology of the // original polyline with respect to other nearby points.) // // If your input is a polyline, you can compute "disc_on_left" as follows. // Let the polyline be ABCDE and assume that it already avoids a set of // points X_i. Suppose that you have aleady added ABC to the simplifer, and // now want to extend the edge chain to D. First find the X_i that are near // the edge CD, then discard the ones such that AX_i <= AC or AX_i >= AD // (since these points have either already been considered or aren't // relevant yet). Now X_i is to the left of the polyline if and only if // s2pred::OrderedCCW(A, D, X, C) (in other words, if X_i is to the left of // the angle wedge ACD). bool AvoidDisc(const S2Point& point, S1ChordAngle radius, bool disc_on_left); private: double GetAngle(const S2Point& p) const; double GetSemiwidth(const S2Point& p, S1ChordAngle r, int round_direction) const; S2Point src_; S2Point x_dir_, y_dir_; S1Interval window_; }; #endif // S2_S2POLYLINE_SIMPLIFIER_H_ s2/src/s2/s2max_distance_targets.h0000644000176200001440000002062314530411473016524 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // This file defines a collection of classes that are useful for computing // maximum distances on the sphere. Their purpose is to allow code to be // shared among the various query classes that find remote geometry, such as // S2FurthestPointQuery and S2FurthestEdgeQuery. #ifndef S2_S2MAX_DISTANCE_TARGETS_H_ #define S2_S2MAX_DISTANCE_TARGETS_H_ #include #include "s2/_fp_contract_off.h" #include "s2/s1angle.h" #include "s2/s1chord_angle.h" #include "s2/s2cell.h" #include "s2/s2distance_target.h" #include "s2/s2edge_distances.h" #include "s2/s2shape_index.h" class S2FurthestEdgeQuery; // S2MaxDistance is a class that allows maximum distances to be computed using // a minimum distance algorithm. Specifically, S2MaxDistance(x) represents the // supplementary distance (Pi - x). This has the effect of inverting the sort // order, i.e. // // (S2MaxDistance(x) < S2MaxDistance(y)) <=> (Pi - x < Pi - y) <=> (x > y) // // All other operations are implemented similarly (using the supplementary // distance Pi - x). For example, S2MaxDistance(x) - S2MaxDistance(y) == // S2MaxDistance(x + y). class S2MaxDistance { public: using Delta = S1ChordAngle; S2MaxDistance() : distance_() {} explicit S2MaxDistance(S1ChordAngle x) : distance_(x) {} explicit operator S1ChordAngle() const { return distance_; } static S2MaxDistance Zero(); static S2MaxDistance Infinity(); static S2MaxDistance Negative(); friend bool operator==(S2MaxDistance x, S2MaxDistance y); friend bool operator<(S2MaxDistance x, S2MaxDistance y); friend S2MaxDistance operator-(S2MaxDistance x, S1ChordAngle delta); S1ChordAngle GetChordAngleBound() const; // If (dist < *this), updates *this and returns true (used internally). bool UpdateMin(const S2MaxDistance& dist); private: S1ChordAngle distance_; }; // S2MaxDistanceTarget represents a geometric object to which maximum distances // on the sphere are measured. // // Subtypes are defined below for measuring the distance to a point, an edge, // an S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry). using S2MaxDistanceTarget = S2DistanceTarget; // An S2DistanceTarget subtype for computing the maximum distance to a point. class S2MaxDistancePointTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistancePointTarget(const S2Point& point); S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& index, const ShapeVisitor& visitor) final; private: S2Point point_; }; // An S2DistanceTarget subtype for computing the maximum distance to an edge. class S2MaxDistanceEdgeTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistanceEdgeTarget(const S2Point& a, const S2Point& b); S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& index, const ShapeVisitor& visitor) final; private: S2Point a_, b_; }; // An S2DistanceTarget subtype for computing the maximum distance to an S2Cell // (including the interior of the cell). class S2MaxDistanceCellTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistanceCellTarget(const S2Cell& cell); S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& index, const ShapeVisitor& visitor) final; private: S2Cell cell_; }; // An S2DistanceTarget subtype for computing the maximum distance to an // S2ShapeIndex (a collection of points, polylines, and/or polygons). // // Note that ShapeIndexTarget has its own options: // // include_interiors() // - specifies that distances are measured to the boundary and interior // of polygons in the S2ShapeIndex. (If set to false, distance is // measured to the polygon boundary only.) // DEFAULT: true. // // brute_force() // - specifies that the distances should be computed by examining every // edge in the S2ShapeIndex (for testing and debugging purposes). // DEFAULT: false. // // These options are specified independently of the corresponding // S2FurthestEdgeQuery options. For example, if include_interiors is true for // a ShapeIndexTarget but false for the S2FurthestEdgeQuery where the target // is used, then distances will be measured from the boundary of one // S2ShapeIndex to the boundary and interior of the other. // class S2MaxDistanceShapeIndexTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistanceShapeIndexTarget(const S2ShapeIndex* index); ~S2MaxDistanceShapeIndexTarget() override; // Specifies that distance will be measured to the boundary and interior // of polygons in the S2ShapeIndex rather than to polygon boundaries only. // // DEFAULT: true bool include_interiors() const; void set_include_interiors(bool include_interiors); // Specifies that the distances should be computed by examining every edge // in the S2ShapeIndex (for testing and debugging purposes). // // DEFAULT: false bool use_brute_force() const; void set_use_brute_force(bool use_brute_force); bool set_max_error(const S1ChordAngle& max_error) override; S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& query_index, const ShapeVisitor& visitor) final; private: const S2ShapeIndex* index_; std::unique_ptr query_; }; ////////////////// Implementation details follow //////////////////// inline S2MaxDistance S2MaxDistance::Zero() { return S2MaxDistance(S1ChordAngle::Straight()); } inline S2MaxDistance S2MaxDistance::Infinity() { return S2MaxDistance(S1ChordAngle::Negative()); } inline S2MaxDistance S2MaxDistance::Negative() { return S2MaxDistance(S1ChordAngle::Infinity()); } inline bool operator==(S2MaxDistance x, S2MaxDistance y) { return x.distance_ == y.distance_; } inline bool operator<(S2MaxDistance x, S2MaxDistance y) { return x.distance_ > y.distance_; } inline S2MaxDistance operator-(S2MaxDistance x, S1ChordAngle delta) { return S2MaxDistance(x.distance_ + delta); } inline S1ChordAngle S2MaxDistance::GetChordAngleBound() const { return S1ChordAngle::Straight() - distance_; } inline bool S2MaxDistance::UpdateMin(const S2MaxDistance& dist) { if (dist < *this) { *this = dist; return true; } return false; } inline S2MaxDistancePointTarget::S2MaxDistancePointTarget(const S2Point& point) : point_(point) { } inline S2MaxDistanceEdgeTarget::S2MaxDistanceEdgeTarget(const S2Point& a, const S2Point& b) : a_(a), b_(b) { a_.Normalize(); b_.Normalize(); } inline S2MaxDistanceCellTarget::S2MaxDistanceCellTarget(const S2Cell& cell) : cell_(cell) { } #endif // S2_S2MAX_DISTANCE_TARGETS_H_ s2/src/s2/s2error.cc0000644000176200001440000000160014530411473013615 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2error.h" #include "s2/base/stringprintf.h" void S2Error::Init(Code code, const char* format, ...) { code_ = code; text_.clear(); va_list ap; va_start(ap, format); StringAppendV(&text_, format, ap); va_end(ap); } s2/src/s2/s2builderutil_s2point_vector_layer.h0000644000176200001440000000744114530411473021117 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_ #define S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_ #include #include #include "s2/base/logging.h" #include "absl/memory/memory.h" #include "s2/id_set_lexicon.h" #include "s2/mutable_s2shape_index.h" #include "s2/s2builder.h" #include "s2/s2builder_graph.h" #include "s2/s2builder_layer.h" #include "s2/s2error.h" #include "s2/s2point_vector_shape.h" namespace s2builderutil { // A layer type that collects degenerate edges as points. // This layer expects all edges to be degenerate. In case of finding // non-degenerate edges it sets S2Error but it still generates the // output with degenerate edges. class S2PointVectorLayer : public S2Builder::Layer { public: class Options { public: using DuplicateEdges = GraphOptions::DuplicateEdges; Options(); explicit Options(DuplicateEdges duplicate_edges); // DEFAULT: DuplicateEdges::MERGE DuplicateEdges duplicate_edges() const; void set_duplicate_edges(DuplicateEdges duplicate_edges); private: DuplicateEdges duplicate_edges_; }; explicit S2PointVectorLayer(std::vector* points, const Options& options = Options()); using LabelSetIds = std::vector; S2PointVectorLayer(std::vector* points, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options = Options()); // Layer interface: GraphOptions graph_options() const override; void Build(const Graph& g, S2Error* error) override; private: std::vector* points_; LabelSetIds* label_set_ids_; IdSetLexicon* label_set_lexicon_; Options options_; }; // Like S2PointVectorLayer, but adds the points to a MutableS2ShapeIndex (if // the point vector is non-empty). class IndexedS2PointVectorLayer : public S2Builder::Layer { public: using Options = S2PointVectorLayer::Options; explicit IndexedS2PointVectorLayer(MutableS2ShapeIndex* index, const Options& options = Options()) : index_(index), layer_(&points_, options) {} GraphOptions graph_options() const override { return layer_.graph_options(); } void Build(const Graph& g, S2Error* error) override { layer_.Build(g, error); if (error->ok() && !points_.empty()) { index_->Add(absl::make_unique(std::move(points_))); } } private: MutableS2ShapeIndex* index_; std::vector points_; S2PointVectorLayer layer_; }; ////////////////// Implementation details follow //////////////////// inline S2PointVectorLayer::Options::Options() : duplicate_edges_(DuplicateEdges::MERGE) {} inline S2PointVectorLayer::Options::Options(DuplicateEdges duplicate_edges) : duplicate_edges_(duplicate_edges) {} inline S2Builder::GraphOptions::DuplicateEdges S2PointVectorLayer::Options::duplicate_edges() const { return duplicate_edges_; } inline void S2PointVectorLayer::Options::set_duplicate_edges( S2Builder::GraphOptions::DuplicateEdges duplicate_edges) { duplicate_edges_ = duplicate_edges; } } // namespace s2builderutil #endif // S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_ s2/src/s2/s2min_distance_targets.cc0000644000176200001440000002432214530411473016660 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2min_distance_targets.h" #include #include "absl/memory/memory.h" #include "s2/s1angle.h" #include "s2/s2cap.h" #include "s2/s2cell.h" #include "s2/s2closest_cell_query.h" #include "s2/s2closest_edge_query.h" #include "s2/s2edge_distances.h" #include "s2/s2shape_index_region.h" S2Cap S2MinDistancePointTarget::GetCapBound() { return S2Cap(point_, S1ChordAngle::Zero()); } bool S2MinDistancePointTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(S1ChordAngle(p, point_))); } bool S2MinDistancePointTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { return S2::UpdateMinDistance(point_, v0, v1, min_dist); } bool S2MinDistancePointTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell.GetDistance(point_))); } bool S2MinDistancePointTarget::VisitContainingShapes( const S2ShapeIndex& index, const ShapeVisitor& visitor) { return MakeS2ContainsPointQuery(&index).VisitContainingShapes( point_, [this, &visitor](S2Shape* shape) { return visitor(shape, point_); }); } S2Cap S2MinDistanceEdgeTarget::GetCapBound() { // The following computes a radius equal to half the edge length in an // efficient and numerically stable way. double d2 = S1ChordAngle(a_, b_).length2(); double r2 = (0.5 * d2) / (1 + sqrt(1 - 0.25 * d2)); return S2Cap((a_ + b_).Normalize(), S1ChordAngle::FromLength2(r2)); } bool S2MinDistanceEdgeTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { return S2::UpdateMinDistance(p, a_, b_, min_dist); } bool S2MinDistanceEdgeTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { return S2::UpdateEdgePairMinDistance(a_, b_, v0, v1, min_dist); } bool S2MinDistanceEdgeTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell.GetDistance(a_, b_))); } bool S2MinDistanceEdgeTarget::VisitContainingShapes( const S2ShapeIndex& index, const ShapeVisitor& visitor) { // We test the center of the edge in order to ensure that edge targets AB // and BA yield identical results (which is not guaranteed by the API but // users might expect). Other options would be to test both endpoints, or // return different results for AB and BA in some cases. S2MinDistancePointTarget target((a_ + b_).Normalize()); return target.VisitContainingShapes(index, visitor); } S2MinDistanceCellTarget::S2MinDistanceCellTarget(const S2Cell& cell) : cell_(cell) { } S2Cap S2MinDistanceCellTarget::GetCapBound() { return cell_.GetCapBound(); } bool S2MinDistanceCellTarget::UpdateMinDistance(const S2Point& p, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(p))); } bool S2MinDistanceCellTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(v0, v1))); } bool S2MinDistanceCellTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(cell))); } bool S2MinDistanceCellTarget::VisitContainingShapes( const S2ShapeIndex& index, const ShapeVisitor& visitor) { // The simplest approach is simply to return the polygons that contain the // cell center. Alternatively, if the index cell is smaller than the target // cell then we could return all polygons that are present in the // S2ShapeIndexCell, but since the index is built conservatively this may // include some polygons that don't quite intersect the cell. So we would // either need to recheck for intersection more accurately, or weaken the // VisitContainingShapes contract so that it only guarantees approximate // intersection, neither of which seems like a good tradeoff. S2MinDistancePointTarget target(cell_.GetCenter()); return target.VisitContainingShapes(index, visitor); } S2MinDistanceCellUnionTarget::S2MinDistanceCellUnionTarget( S2CellUnion cell_union) : cell_union_(std::move(cell_union)), query_(absl::make_unique(&index_)) { for (S2CellId cell_id : cell_union_) { index_.Add(cell_id, 0); } index_.Build(); } S2MinDistanceCellUnionTarget::~S2MinDistanceCellUnionTarget() { } bool S2MinDistanceCellUnionTarget::use_brute_force() const { return query_->options().use_brute_force(); } void S2MinDistanceCellUnionTarget::set_use_brute_force( bool use_brute_force) { query_->mutable_options()->set_use_brute_force(use_brute_force); } bool S2MinDistanceCellUnionTarget::set_max_error( const S1ChordAngle& max_error) { query_->mutable_options()->set_max_error(max_error); return true; // Indicates that we may return suboptimal results. } S2Cap S2MinDistanceCellUnionTarget::GetCapBound() { return cell_union_.GetCapBound(); } inline bool S2MinDistanceCellUnionTarget::UpdateMinDistance( S2MinDistanceTarget* target, S2MinDistance* min_dist) { query_->mutable_options()->set_max_distance(*min_dist); S2ClosestCellQuery::Result r = query_->FindClosestCell(target); if (r.is_empty()) return false; *min_dist = r.distance(); return true; } bool S2MinDistanceCellUnionTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { S2ClosestCellQuery::PointTarget target(p); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceCellUnionTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { S2ClosestCellQuery::EdgeTarget target(v0, v1); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceCellUnionTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { S2ClosestCellQuery::CellTarget target(cell); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceCellUnionTarget::VisitContainingShapes( const S2ShapeIndex& query_index, const ShapeVisitor& visitor) { for (S2CellId cell_id : cell_union_) { S2MinDistancePointTarget target(cell_id.ToPoint()); if (!target.VisitContainingShapes(query_index, visitor)) { return false; } } return true; } S2MinDistanceShapeIndexTarget::S2MinDistanceShapeIndexTarget( const S2ShapeIndex* index) : index_(index), query_(absl::make_unique(index)) { } S2MinDistanceShapeIndexTarget::~S2MinDistanceShapeIndexTarget() { } bool S2MinDistanceShapeIndexTarget::include_interiors() const { return query_->options().include_interiors(); } void S2MinDistanceShapeIndexTarget::set_include_interiors( bool include_interiors) { query_->mutable_options()->set_include_interiors(include_interiors); } bool S2MinDistanceShapeIndexTarget::use_brute_force() const { return query_->options().use_brute_force(); } void S2MinDistanceShapeIndexTarget::set_use_brute_force( bool use_brute_force) { query_->mutable_options()->set_use_brute_force(use_brute_force); } bool S2MinDistanceShapeIndexTarget::set_max_error( const S1ChordAngle& max_error) { query_->mutable_options()->set_max_error(max_error); return true; // Indicates that we may return suboptimal results. } S2Cap S2MinDistanceShapeIndexTarget::GetCapBound() { return MakeS2ShapeIndexRegion(index_).GetCapBound(); } inline bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( S2MinDistanceTarget* target, S2MinDistance* min_dist) { query_->mutable_options()->set_max_distance(*min_dist); S2ClosestEdgeQuery::Result r = query_->FindClosestEdge(target); if (r.is_empty()) return false; *min_dist = r.distance(); return true; } bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { S2ClosestEdgeQuery::PointTarget target(p); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { S2ClosestEdgeQuery::EdgeTarget target(v0, v1); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { S2ClosestEdgeQuery::CellTarget target(cell); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceShapeIndexTarget::VisitContainingShapes( const S2ShapeIndex& query_index, const ShapeVisitor& visitor) { // It is sufficient to find the set of chain starts in the target index // (i.e., one vertex per connected component of edges) that are contained by // the query index, except for one special case to handle full polygons. // // TODO(ericv): Do this by merge-joining the two S2ShapeIndexes, and share // the code with S2BooleanOperation. for (S2Shape* shape : *index_) { if (shape == nullptr) continue; int num_chains = shape->num_chains(); // Shapes that don't have any edges require a special case (below). bool tested_point = false; for (int c = 0; c < num_chains; ++c) { S2Shape::Chain chain = shape->chain(c); if (chain.length == 0) continue; tested_point = true; S2Point v0 = shape->chain_edge(c, 0).v0; S2MinDistancePointTarget target(v0); if (!target.VisitContainingShapes(query_index, visitor)) { return false; } } if (!tested_point) { // Special case to handle full polygons. S2Shape::ReferencePoint ref = shape->GetReferencePoint(); if (!ref.contained) continue; S2MinDistancePointTarget target(ref.point); if (!target.VisitContainingShapes(query_index, visitor)) { return false; } } } return true; } s2/src/s2/s2closest_cell_query.cc0000644000176200001440000001155014530411473016371 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2closest_cell_query.h" #include #include "absl/memory/memory.h" #include "s2/s1angle.h" #include "s2/s2cap.h" #include "s2/s2cell.h" #include "s2/s2cell_id.h" #include "s2/s2cell_union.h" #include "s2/s2edge_distances.h" #include "s2/s2region_coverer.h" #include "s2/s2shape_index_region.h" void S2ClosestCellQuery::Options::set_conservative_max_distance( S1ChordAngle max_distance) { set_max_distance(Distance(max_distance.PlusError( S2::GetUpdateMinDistanceMaxError(max_distance)).Successor())); } void S2ClosestCellQuery::Options::set_conservative_max_distance( S1Angle max_distance) { set_conservative_max_distance(S1ChordAngle(max_distance)); } // The thresholds for using the brute force algorithm are generally tuned to // optimize IsDistanceLess (which compares the distance against a threshold) // rather than FindClosest (which actually computes the minimum distance). // This is because the former operation is (1) more common, (2) inherently // faster, and (3) closely related to finding all cells within a given // distance, which is also very common. int S2ClosestCellQuery::PointTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosest 18 16 // BM_IsDistanceLess 8 9 return 9; } int S2ClosestCellQuery::EdgeTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToLongEdge 14 16 // BM_IsDistanceLessToLongEdge 5 5 return 5; } int S2ClosestCellQuery::CellTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToSmallCell 12 13 // BM_IsDistanceLessToSmallCell 6 6 // // Note that the primary use of CellTarget is to implement CellUnionTarget, // and therefore it is very important to optimize for the case where a // distance limit has been specified. return 6; } int S2ClosestCellQuery::CellUnionTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToSmallCoarseCellUnion 12 10 // BM_IsDistanceLessToSmallCoarseCellUnion 7 6 return 8; } int S2ClosestCellQuery::ShapeIndexTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToSmallCoarseShapeIndex 10 8 // BM_IsDistanceLessToSmallCoarseShapeIndex 7 6 return 7; } S2ClosestCellQuery::S2ClosestCellQuery() { // Prevent inline constructor bloat by defining here. } S2ClosestCellQuery::~S2ClosestCellQuery() { // Prevent inline destructor bloat by defining here. } bool S2ClosestCellQuery::IsDistanceLess(Target* target, S1ChordAngle limit) { static_assert(sizeof(Options) <= 32, "Consider not copying Options here"); Options tmp_options = options_; tmp_options.set_max_results(1); tmp_options.set_max_distance(limit); tmp_options.set_max_error(S1ChordAngle::Straight()); return !base_.FindClosestCell(target, tmp_options).is_empty(); } bool S2ClosestCellQuery::IsDistanceLessOrEqual(Target* target, S1ChordAngle limit) { static_assert(sizeof(Options) <= 32, "Consider not copying Options here"); Options tmp_options = options_; tmp_options.set_max_results(1); tmp_options.set_inclusive_max_distance(limit); tmp_options.set_max_error(S1ChordAngle::Straight()); return !base_.FindClosestCell(target, tmp_options).is_empty(); } bool S2ClosestCellQuery::IsConservativeDistanceLessOrEqual( Target* target, S1ChordAngle limit) { static_assert(sizeof(Options) <= 32, "Consider not copying Options here"); Options tmp_options = options_; tmp_options.set_max_results(1); tmp_options.set_conservative_max_distance(limit); tmp_options.set_max_error(S1ChordAngle::Straight()); return !base_.FindClosestCell(target, tmp_options).is_empty(); } s2/src/s2/s2padded_cell.cc0000644000176200001440000001435014530411473014712 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2padded_cell.h" #include #include #include "s2/util/bits/bits.h" #include "s2/r1interval.h" #include "s2/s2coords.h" using std::max; using std::min; using S2::internal::kSwapMask; using S2::internal::kInvertMask; using S2::internal::kIJtoPos; using S2::internal::kPosToOrientation; S2PaddedCell::S2PaddedCell(S2CellId id, double padding) : id_(id), padding_(padding) { if (id_.is_face()) { // Fast path for constructing a top-level face (the most common case). double limit = 1 + padding; bound_ = R2Rect(R1Interval(-limit, limit), R1Interval(-limit, limit)); middle_ = R2Rect(R1Interval(-padding, padding), R1Interval(-padding, padding)); ij_lo_[0] = ij_lo_[1] = 0; orientation_ = id_.face() & 1; level_ = 0; } else { int ij[2]; id.ToFaceIJOrientation(&ij[0], &ij[1], &orientation_); level_ = id.level(); bound_ = S2CellId::IJLevelToBoundUV(ij, level_).Expanded(padding); int ij_size = S2CellId::GetSizeIJ(level_); ij_lo_[0] = ij[0] & -ij_size; ij_lo_[1] = ij[1] & -ij_size; } } S2PaddedCell::S2PaddedCell(const S2PaddedCell& parent, int i, int j) : padding_(parent.padding_), bound_(parent.bound_), level_(parent.level_ + 1) { // Compute the position and orientation of the child incrementally from the // orientation of the parent. int pos = kIJtoPos[parent.orientation_][2*i+j]; id_ = parent.id_.child(pos); int ij_size = S2CellId::GetSizeIJ(level_); ij_lo_[0] = parent.ij_lo_[0] + i * ij_size; ij_lo_[1] = parent.ij_lo_[1] + j * ij_size; orientation_ = parent.orientation_ ^ kPosToOrientation[pos]; // For each child, one corner of the bound is taken directly from the parent // while the diagonally opposite corner is taken from middle(). const R2Rect& middle = parent.middle(); bound_[0][1-i] = middle[0][1-i]; bound_[1][1-j] = middle[1][1-j]; } const R2Rect& S2PaddedCell::middle() const { // We compute this field lazily because it is not needed the majority of the // time (i.e., for cells where the recursion terminates). if (middle_.is_empty()) { int ij_size = S2CellId::GetSizeIJ(level_); double u = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size)); double v = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)); middle_ = R2Rect(R1Interval(u - padding_, u + padding_), R1Interval(v - padding_, v + padding_)); } return middle_; } S2Point S2PaddedCell::GetCenter() const { int ij_size = S2CellId::GetSizeIJ(level_); unsigned int si = 2 * ij_lo_[0] + ij_size; unsigned int ti = 2 * ij_lo_[1] + ij_size; return S2::FaceSiTitoXYZ(id_.face(), si, ti).Normalize(); } S2Point S2PaddedCell::GetEntryVertex() const { // The curve enters at the (0,0) vertex unless the axis directions are // reversed, in which case it enters at the (1,1) vertex. unsigned int i = ij_lo_[0]; unsigned int j = ij_lo_[1]; if (orientation_ & kInvertMask) { int ij_size = S2CellId::GetSizeIJ(level_); i += ij_size; j += ij_size; } return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize(); } S2Point S2PaddedCell::GetExitVertex() const { // The curve exits at the (1,0) vertex unless the axes are swapped or // inverted but not both, in which case it exits at the (0,1) vertex. unsigned int i = ij_lo_[0]; unsigned int j = ij_lo_[1]; int ij_size = S2CellId::GetSizeIJ(level_); if (orientation_ == 0 || orientation_ == kSwapMask + kInvertMask) { i += ij_size; } else { j += ij_size; } return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize(); } S2CellId S2PaddedCell::ShrinkToFit(const R2Rect& rect) const { S2_DCHECK(bound().Intersects(rect)); // Quick rejection test: if "rect" contains the center of this cell along // either axis, then no further shrinking is possible. int ij_size = S2CellId::GetSizeIJ(level_); if (level_ == 0) { // Fast path (most calls to this function start with a face cell). if (rect[0].Contains(0) || rect[1].Contains(0)) return id(); } else { if (rect[0].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size))) || rect[1].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)))) { return id(); } } // Otherwise we expand "rect" by the given padding() on all sides and find // the range of coordinates that it spans along the i- and j-axes. We then // compute the highest bit position at which the min and max coordinates // differ. This corresponds to the first cell level at which at least two // children intersect "rect". // Increase the padding to compensate for the error in S2::UVtoST(). // (The constant below is a provable upper bound on the additional error.) R2Rect padded = rect.Expanded(padding() + 1.5 * DBL_EPSILON); int ij_min[2]; // Min i- or j- coordinate spanned by "padded" int ij_xor[2]; // XOR of the min and max i- or j-coordinates for (int d = 0; d < 2; ++d) { ij_min[d] = max(ij_lo_[d], S2::STtoIJ(S2::UVtoST(padded[d][0]))); int ij_max = min(ij_lo_[d] + ij_size - 1, S2::STtoIJ(S2::UVtoST(padded[d][1]))); ij_xor[d] = ij_min[d] ^ ij_max; } // Compute the highest bit position where the two i- or j-endpoints differ, // and then choose the cell level that includes both of these endpoints. So // if both pairs of endpoints are equal we choose kMaxLevel; if they differ // only at bit 0, we choose (kMaxLevel - 1), and so on. int level_msb = ((ij_xor[0] | ij_xor[1]) << 1) + 1; int level = S2CellId::kMaxLevel - Bits::FindMSBSetNonZero(level_msb); if (level <= level_) return id(); return S2CellId::FromFaceIJ(id().face(), ij_min[0], ij_min[1]).parent(level); } s2/src/s2/s2shapeutil_build_polygon_boundaries.cc0000644000176200001440000001077014530411473021633 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2shapeutil_build_polygon_boundaries.h" #include "absl/container/btree_map.h" #include "absl/memory/memory.h" #include "s2/mutable_s2shape_index.h" #include "s2/s2contains_point_query.h" #include "s2/s2shape_index.h" #include "s2/s2shapeutil_contains_brute_force.h" using absl::WrapUnique; using std::vector; namespace s2shapeutil { void BuildPolygonBoundaries(const vector>& components, vector>* polygons) { polygons->clear(); if (components.empty()) return; // Since the loop boundaries do not cross, a loop nesting hierarchy can be // defined by choosing any point on the sphere as the "point at infinity". // Loop A then contains loop B if (1) A contains the boundary of B and (2) // loop A does not contain the point at infinity. // // We choose S2::Origin() for this purpose. The loop nesting hierarchy then // determines the face structure. Here are the details: // // 1. Build an S2ShapeIndex of all loops that do not contain S2::Origin(). // This leaves at most one unindexed loop per connected component // (the "outer loop"). // // 2. For each component, choose a representative vertex and determine // which indexed loops contain it. The "depth" of this component is // defined as the number of such loops. // // 3. Assign the outer loop of each component to the containing loop whose // depth is one less. This generates a set of multi-loop polygons. // // 4. The outer loops of all components at depth 0 become a single face. MutableS2ShapeIndex index; // A map from shape.id() to the corresponding component number. vector component_ids; vector outer_loops; for (int i = 0; i < components.size(); ++i) { const auto& component = components[i]; for (S2Shape* loop : component) { if (component.size() > 1 && !s2shapeutil::ContainsBruteForce(*loop, S2::Origin())) { // Ownership is transferred back at the end of this function. index.Add(WrapUnique(loop)); component_ids.push_back(i); } else { outer_loops.push_back(loop); } } // Check that there is exactly one outer loop in each component. S2_DCHECK_EQ(i + 1, outer_loops.size()) << "Component is not a subdivision"; } // Find the loops containing each component. vector> ancestors(components.size()); auto contains_query = MakeS2ContainsPointQuery(&index); for (int i = 0; i < outer_loops.size(); ++i) { auto loop = outer_loops[i]; S2_DCHECK_GT(loop->num_edges(), 0); ancestors[i] = contains_query.GetContainingShapes(loop->edge(0).v0); } // Assign each outer loop to the component whose depth is one less. // Components at depth 0 become a single face. absl::btree_map> children; for (int i = 0; i < outer_loops.size(); ++i) { S2Shape* ancestor = nullptr; int depth = ancestors[i].size(); if (depth > 0) { for (auto candidate : ancestors[i]) { if (ancestors[component_ids[candidate->id()]].size() == depth - 1) { S2_DCHECK(ancestor == nullptr); ancestor = candidate; } } S2_DCHECK(ancestor != nullptr); } children[ancestor].push_back(outer_loops[i]); } // There is one face per loop that is not an outer loop, plus one for the // outer loops of components at depth 0. polygons->resize(index.num_shape_ids() + 1); for (int i = 0; i < index.num_shape_ids(); ++i) { auto polygon = &(*polygons)[i]; auto loop = index.shape(i); auto itr = children.find(loop); if (itr != children.end()) { *polygon = itr->second; } polygon->push_back(loop); } polygons->back() = children[nullptr]; // Explicitly release the shapes from the index so they are not deleted. for (auto& ptr : index.ReleaseAll()) ptr.release(); } } // namespace s2shapeutil s2/src/s2/s2builderutil_s2polyline_layer.cc0000644000176200001440000000710214530411473020367 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2builderutil_s2polyline_layer.h" #include "s2/s2debug.h" using std::vector; using EdgeType = S2Builder::EdgeType; using Graph = S2Builder::Graph; using GraphOptions = S2Builder::GraphOptions; using Label = S2Builder::Label; using DegenerateEdges = GraphOptions::DegenerateEdges; using DuplicateEdges = GraphOptions::DuplicateEdges; using SiblingPairs = GraphOptions::SiblingPairs; using EdgeId = Graph::EdgeId; using PolylineType = Graph::PolylineType; namespace s2builderutil { S2PolylineLayer::S2PolylineLayer(S2Polyline* polyline, const S2PolylineLayer::Options& options) { Init(polyline, nullptr, nullptr, options); } S2PolylineLayer::S2PolylineLayer( S2Polyline* polyline, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { Init(polyline, label_set_ids, label_set_lexicon, options); } void S2PolylineLayer::Init(S2Polyline* polyline, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr); polyline_ = polyline; label_set_ids_ = label_set_ids; label_set_lexicon_ = label_set_lexicon; options_ = options; if (options_.validate()) { polyline_->set_s2debug_override(S2Debug::DISABLE); } } GraphOptions S2PolylineLayer::graph_options() const { // Remove edges that collapse to a single vertex, but keep duplicate and // sibling edges, since merging duplicates or discarding siblings can make // it impossible to assemble the edges into a single polyline. return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD, DuplicateEdges::KEEP, SiblingPairs::KEEP); } void S2PolylineLayer::Build(const Graph& g, S2Error* error) { if (g.num_edges() == 0) { polyline_->Init(vector{}); return; } vector edge_polylines = g.GetPolylines(PolylineType::WALK); if (edge_polylines.size() != 1) { error->Init(S2Error::BUILDER_EDGES_DO_NOT_FORM_POLYLINE, "Input edges cannot be assembled into polyline"); return; } const Graph::EdgePolyline& edge_polyline = edge_polylines[0]; vector vertices; // Temporary storage for vertices. vertices.reserve(edge_polyline.size()); vertices.push_back(g.vertex(g.edge(edge_polyline[0]).first)); for (EdgeId e : edge_polyline) { vertices.push_back(g.vertex(g.edge(e).second)); } if (label_set_ids_) { Graph::LabelFetcher fetcher(g, options_.edge_type()); vector(3); // // However, the C++ language defines the set of possible values for an // enumeration to be essentially the range of a bitfield that can represent // all the enumerators, i.e. those within the nearest containing power // of two. In the example above, the nearest positive power of two is 64, // and so the upper bound is 63. The nearest negative power of two is // -32 and so the lower bound is -32 (two's complement), which is upgraded // to match the upper bound, becoming -64. The values within this range // of -64 to 63 are valid, according to the C++ standard. You can cast // values within this range as follows. // // A var = loose_enum_cast(45); // // These casts will log a message if the value does not reside within the // specified range, and will be fatal when in debug mode. // // For those times when an assert too strong, there are test functions. // // bool var = tight_enum_test(3); // bool var = loose_enum_test(45); // // For code that needs to use the enumeration value if and only if // it is good, there is a function that both tests and casts. // // int i = ....; // A var; // if (tight_enum_test_cast(i, &var)) // .... // use valid var with value as indicated by i // else // .... // handle invalid enum cast // // The enum test/cast facility is currently limited to enumerations that // fit within an int. It is also limited to two's complement ints. // ** Implementation Description // // The enum_limits template class captures the minimum and maximum // enumerator. All uses of this template are intended to be of // specializations, so the generic has a field to identify itself as // not specialized. The test/cast templates assert specialization. template class enum_limits { public: static const Enum min_enumerator = 0; static const Enum max_enumerator = 0; static const bool is_specialized = false; }; // Now we define the macro to define the specialization for enum_limits. // The specialization checks that the enumerators fit within an int. // This checking relies on integral promotion. #define MAKE_ENUM_LIMITS(ENUM_TYPE, ENUM_MIN, ENUM_MAX) \ template <> \ class enum_limits { \ public: \ static const ENUM_TYPE min_enumerator = ENUM_MIN; \ static const ENUM_TYPE max_enumerator = ENUM_MAX; \ static const bool is_specialized = true; \ static_assert(ENUM_MIN >= INT_MIN, "enumerator too negative for int"); \ static_assert(ENUM_MAX <= INT_MAX, "enumerator too positive for int"); \ }; // The loose enum test/cast is actually the more complicated one, // because of the problem of finding the bounds. // // The unary upper bound, ub, on a positive number is its positive // saturation, i.e. for a value v within pow(2,k-1) <= v < pow(2,k), // the upper bound is pow(2,k)-1. // // The unary lower bound, lb, on a negative number is its negative // saturation, i.e. for a value v within -pow(2,k) <= v < -pow(2,k-1), // the lower bound is -pow(2,k). // // The actual bounds are (1) the binary upper bound over the maximum // enumerator and the one's complement of a negative minimum enumerator // and (2) the binary lower bound over the minimum enumerator and the // one's complement of the positive maximum enumerator, except that if no // enumerators are negative, the lower bound is zero. // // The algorithm relies heavily on the observation that // // a,b>0 then ub(a,b) == ub(a) | ub(b) == ub(a|b) // a,b<0 then lb(a,b) == lb(a) & lb(b) == lb(a&b) // // Note that the compiler will boil most of this code away // because of value propagation on the constant enumerator bounds. template inline bool loose_enum_test(int e_val) { static_assert(enum_limits::is_specialized, "missing MAKE_ENUM_LIMITS"); const Enum e_min = enum_limits::min_enumerator; const Enum e_max = enum_limits::max_enumerator; static_assert(sizeof(e_val) == 4 || sizeof(e_val) == 8, "unexpected int size"); // Find the unary bounding negative number of e_min and e_max. // Find the unary bounding negative number of e_max. // This would be b_min = e_max < 0 ? e_max : ~e_max, // but we want to avoid branches to help the compiler. int e_max_sign = e_max >> (sizeof(e_val)*8 - 1); int b_min = ~e_max_sign ^ e_max; // Find the binary bounding negative of both e_min and e_max. b_min &= e_min; // However, if e_min is positive, the result will be positive. // Now clear all bits right of the most significant clear bit, // which is a negative saturation for negative numbers. // In the case of positive numbers, this is flush to zero. b_min &= b_min >> 1; b_min &= b_min >> 2; b_min &= b_min >> 4; b_min &= b_min >> 8; b_min &= b_min >> 16; #if INT_MAX > 2147483647 b_min &= b_min >> 32; #endif // Find the unary bounding positive number of e_max. int b_max = e_max_sign ^ e_max; // Find the binary bounding positive number of that // and the unary bounding positive number of e_min. int e_min_sign = e_min >> (sizeof(e_val)*8 - 1); b_max |= e_min_sign ^ e_min; // Now set all bits right of the most significant set bit, // which is a positive saturation for positive numbers. b_max |= b_max >> 1; b_max |= b_max >> 2; b_max |= b_max >> 4; b_max |= b_max >> 8; b_max |= b_max >> 16; #if INT_MAX > 2147483647 b_max |= b_max >> 32; #endif // Finally test the bounds. return b_min <= e_val && e_val <= b_max; } template inline bool tight_enum_test(int e_val) { static_assert(enum_limits::is_specialized, "missing MAKE_ENUM_LIMITS"); const Enum e_min = enum_limits::min_enumerator; const Enum e_max = enum_limits::max_enumerator; return e_min <= e_val && e_val <= e_max; } template inline bool loose_enum_test_cast(int e_val, Enum* e_var) { if (loose_enum_test(e_val)) { *e_var = static_cast(e_val); return true; } else { return false; } } template inline bool tight_enum_test_cast(int e_val, Enum* e_var) { if (tight_enum_test(e_val)) { *e_var = static_cast(e_val); return true; } else { return false; } } // The plain casts require logging, and we get header recursion if // it is done directly. So, we do it indirectly. // The following function is defined in logging.cc. namespace base { namespace internal { void WarnEnumCastError(int value_of_int); } // namespace internal } // namespace base template inline Enum loose_enum_cast(int e_val) { if (!loose_enum_test(e_val)) { base::internal::WarnEnumCastError(e_val); } return static_cast(e_val); } template inline Enum tight_enum_cast(int e_val) { if (!tight_enum_test(e_val)) { base::internal::WarnEnumCastError(e_val); } return static_cast(e_val); } #endif // S2_BASE_CASTS_H_ s2/src/s2/base/spinlock.h0000644000176200001440000000277214530411473014630 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_SPINLOCK_H_ #define S2_BASE_SPINLOCK_H_ #include class SpinLock { public: SpinLock() = default; ~SpinLock() = default; SpinLock(SpinLock const&) = delete; SpinLock& operator=(SpinLock const&) = delete; inline void Lock() { while (locked_.exchange(true, std::memory_order_acquire)) { // Spin. continue; } } inline void Unlock() { locked_.store(false, std::memory_order_release); } inline bool IsHeld() const { return locked_.load(std::memory_order_relaxed); } private: std::atomic_bool locked_{false}; }; class SpinLockHolder { public: inline explicit SpinLockHolder(SpinLock* l) : lock_(l) { lock_->Lock(); } inline ~SpinLockHolder() { lock_->Unlock(); } SpinLockHolder(const SpinLockHolder&) = delete; SpinLockHolder& operator=(const SpinLockHolder&) = delete; private: SpinLock* lock_; }; #endif // S2_BASE_SPINLOCK_H_ s2/src/s2/base/timer.h0000644000176200001440000000227714530411473014126 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_TIMER_H_ #define S2_BASE_TIMER_H_ #include #include "s2/base/integral_types.h" class CycleTimer { public: CycleTimer() = default; void Start() { start_ = Now(); } int64 GetInMs() const { using msec = std::chrono::milliseconds; return std::chrono::duration_cast(GetDuration()).count(); } private: using Clock = std::chrono::high_resolution_clock; static Clock::time_point Now() { return Clock::now(); } Clock::duration GetDuration() const { return Now() - start_; } Clock::time_point start_; }; #endif // S2_BASE_TIMER_H_ s2/src/s2/base/stringprintf.h0000644000176200001440000000366214530411473015536 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // NOTE: See third_party/absl/strings for more options. // // As of 2017q4, most use of these routines is considered legacy: use // of absl::StrCat, absl::Substitute, or absl::StrFormat is preferred for // performance and safety reasons. #ifndef S2_BASE_STRINGPRINTF_H_ #define S2_BASE_STRINGPRINTF_H_ #include #include #include #include "s2/base/port.h" // Return a C++ string extern std::string StringPrintf(const char* format, ...) // Tell the compiler to do printf format string checking. ABSL_PRINTF_ATTRIBUTE(1, 2); // Store result into a supplied string and return it extern const std::string& SStringPrintf(std::string* dst, const char* format, ...) // Tell the compiler to do printf format string checking. ABSL_PRINTF_ATTRIBUTE(2, 3); // Append result to a supplied string extern void StringAppendF(std::string* dst, const char* format, ...) // Tell the compiler to do printf format string checking. ABSL_PRINTF_ATTRIBUTE(2, 3); // Lower-level routine that takes a va_list and appends to a specified // string. All other routines are just convenience wrappers around it. // // Implementation note: the va_list is never modified, this implementation // always operates on copies. extern void StringAppendV(std::string* dst, const char* format, va_list ap); #endif // S2_BASE_STRINGPRINTF_H_ s2/src/s2/base/logging.h0000644000176200001440000001266314530411473014434 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_LOGGING_H_ #define S2_BASE_LOGGING_H_ #include "cpp-compat.h" #ifdef S2_USE_GLOG #include // The names CHECK, etc. are too common and may conflict with other // packages. We use S2_CHECK to make it easier to switch to // something other than GLOG for logging. #define S2_LOG LOG #define S2_LOG_IF LOG_IF #define S2_DLOG_IF DLOG_IF #define S2_CHECK CHECK #define S2_CHECK_EQ CHECK_EQ #define S2_CHECK_NE CHECK_NE #define S2_CHECK_LT CHECK_LT #define S2_CHECK_LE CHECK_LE #define S2_CHECK_GT CHECK_GT #define S2_CHECK_GE CHECK_GE #define S2_DCHECK DCHECK #define S2_DCHECK_EQ DCHECK_EQ #define S2_DCHECK_NE DCHECK_NE #define S2_DCHECK_LT DCHECK_LT #define S2_DCHECK_LE DCHECK_LE #define S2_DCHECK_GT DCHECK_GT #define S2_DCHECK_GE DCHECK_GE #define S2_VLOG VLOG #define S2_VLOG_IS_ON VLOG_IS_ON #else // !defined(S2_USE_GLOG) #include #include "s2/base/log_severity.h" #include "absl/base/attributes.h" #include "absl/base/log_severity.h" class S2LogMessage { public: S2LogMessage(const char* file, int line, absl::LogSeverity severity, std::ostream& stream) : severity_(severity), stream_(stream) { if (enabled()) { stream_ << file << ":" << line << " " << absl::LogSeverityName(severity) << " "; } } ~S2LogMessage() { if (enabled()) stream_ << std::endl; } std::ostream& stream() { return stream_; } // silences an 'unused member' compiler warning absl::LogSeverity severity() { return severity_; } private: bool enabled() const { #ifdef ABSL_MIN_LOG_LEVEL return (static_cast(severity_) >= ABSL_MIN_LOG_LEVEL || severity_ >= absl::LogSeverity::kFatal); #else return true; #endif } absl::LogSeverity severity_; std::ostream& stream_; }; // Same as S2LogMessage, but destructor is marked no-return to avoid // "no return value warnings" in functions that return non-void. class S2FatalLogMessage : public S2LogMessage { public: S2FatalLogMessage(const char* file, int line, absl::LogSeverity severity, std::ostream& stream) ABSL_ATTRIBUTE_COLD : S2LogMessage(file, line, severity, stream) {} ABSL_ATTRIBUTE_NORETURN ~S2FatalLogMessage() { cpp_compat_abort(); } }; // Logging stream that does nothing. struct S2NullStream { template S2NullStream& operator<<(const T& v) { return *this; } }; // Used to suppress "unused value" warnings. struct S2LogMessageVoidify { // Must have precedence lower than << but higher than ?:. void operator&(std::ostream&) {} }; #define S2_LOG_MESSAGE_(LogMessageClass, log_severity) \ LogMessageClass(__FILE__, __LINE__, log_severity, cpp_compat_cerr) #define S2_LOG_INFO \ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kInfo) #define S2_LOG_WARNING \ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kWarning) #define S2_LOG_ERROR \ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kError) #define S2_LOG_FATAL \ S2_LOG_MESSAGE_(S2FatalLogMessage, absl::LogSeverity::kFatal) #ifndef NDEBUG #define S2_LOG_DFATAL S2_LOG_FATAL #else #define S2_LOG_DFATAL S2_LOG_ERROR #endif #define S2_LOG(severity) S2_LOG_##severity.stream() // Implementing this as if (...) {} else S2_LOG(...) will cause dangling else // warnings when someone does if (...) S2_LOG_IF(...), so do this tricky // thing instead. #define S2_LOG_IF(severity, condition) \ !(condition) ? (void)0 : S2LogMessageVoidify() & S2_LOG(severity) #define S2_CHECK(condition) \ S2_LOG_IF(FATAL, ABSL_PREDICT_FALSE(!(condition))) \ << ("Check failed: " #condition " ") #ifndef NDEBUG #define S2_DLOG_IF S2_LOG_IF #define S2_DCHECK S2_CHECK #else // defined(NDEBUG) #define S2_DLOG_IF(severity, condition) \ while (false && (condition)) S2NullStream() #define S2_DCHECK(condition) \ while (false && (condition)) S2NullStream() #endif // defined(NDEBUG) #define S2_CHECK_OP(op, val1, val2) S2_CHECK((val1) op (val2)) #define S2_CHECK_EQ(val1, val2) S2_CHECK_OP(==, val1, val2) #define S2_CHECK_NE(val1, val2) S2_CHECK_OP(!=, val1, val2) #define S2_CHECK_LT(val1, val2) S2_CHECK_OP(<, val1, val2) #define S2_CHECK_LE(val1, val2) S2_CHECK_OP(<=, val1, val2) #define S2_CHECK_GT(val1, val2) S2_CHECK_OP(>, val1, val2) #define S2_CHECK_GE(val1, val2) S2_CHECK_OP(>=, val1, val2) #define S2_DCHECK_OP(op, val1, val2) S2_DCHECK((val1) op (val2)) #define S2_DCHECK_EQ(val1, val2) S2_DCHECK_OP(==, val1, val2) #define S2_DCHECK_NE(val1, val2) S2_DCHECK_OP(!=, val1, val2) #define S2_DCHECK_LT(val1, val2) S2_DCHECK_OP(<, val1, val2) #define S2_DCHECK_LE(val1, val2) S2_DCHECK_OP(<=, val1, val2) #define S2_DCHECK_GT(val1, val2) S2_DCHECK_OP(>, val1, val2) #define S2_DCHECK_GE(val1, val2) S2_DCHECK_OP(>=, val1, val2) // We don't support VLOG. #define S2_VLOG(verbose_level) S2NullStream() #define S2_VLOG_IS_ON(verbose_level) (false) #endif // !defined(S2_USE_GLOG) #endif // S2_BASE_LOGGING_H_ s2/src/s2/base/port.h0000644000176200001440000010134014530411473013761 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_PORT_H_ #define S2_BASE_PORT_H_ // This file contains things that are not used in third_party/absl but needed by // - Platform specific requirement // - MSVC // - Utility macros // - Endianness // - Hash // - Global variables // - Type alias // - Predefined system/language macros // - Predefined system/language functions // - Performance optimization (alignment) // - Obsolete #include #include #include #include #include "s2/base/integral_types.h" #include "absl/base/config.h" #include "absl/base/port.h" #ifdef SWIG %include "third_party/absl/base/port.h" #endif // ----------------------------------------------------------------------------- // MSVC Specific Requirements // ----------------------------------------------------------------------------- #ifdef _MSC_VER /* if Visual C++ */ #include // Must come before #include #include // _getpid() #include #undef ERROR #undef DELETE #undef DIFFERENCE #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #define S_IRUSR 00400 #define S_IWUSR 00200 #define S_IXUSR 00100 #define S_IRGRP 00040 #define S_IWGRP 00020 #define S_IXGRP 00010 #define S_IROTH 00004 #define S_IWOTH 00002 #define S_IXOTH 00001 // This compiler flag can be easily overlooked on MSVC. // _CHAR_UNSIGNED gets set with the /J flag. #ifndef _CHAR_UNSIGNED #error chars must be unsigned! Use the /J flag on the compiler command line. // NOLINT #endif // Allow comparisons between signed and unsigned values. // // Lots of Google code uses this pattern: // for (int i = 0; i < container.size(); ++i) // Since size() returns an unsigned value, this warning would trigger // frequently. Very few of these instances are actually bugs since containers // rarely exceed MAX_INT items. Unfortunately, there are bugs related to // signed-unsigned comparisons that have been missed because we disable this // warning. For example: // const long stop_time = os::GetMilliseconds() + kWaitTimeoutMillis; // while (os::GetMilliseconds() <= stop_time) { ... } #pragma warning(disable : 4018) // level 3 #pragma warning(disable : 4267) // level 3 // Don't warn about unused local variables. // // extension to silence particular instances of this warning. There's no way // to define ABSL_ATTRIBUTE_UNUSED to quiet particular instances of this warning // in VC++, so we disable it globally. Currently, there aren't many false // positives, so perhaps we can address those in the future and re-enable these // warnings, which sometimes catch real bugs. #pragma warning(disable : 4101) // level 3 // Allow initialization and assignment to a smaller type without warnings about // possible loss of data. // // There is a distinct warning, 4267, that warns about size_t conversions to // smaller types, but we don't currently disable that warning. // // Correct code can be written in such a way as to avoid false positives // by making the conversion explicit, but Google code isn't usually that // verbose. There are too many false positives to address at this time. Note // that this warning triggers at levels 2, 3, and 4 depending on the specific // type of conversion. By disabling it, we not only silence minor narrowing // conversions but also serious ones. #pragma warning(disable : 4244) // level 2, 3, and 4 // Allow silent truncation of double to float. // // Silencing this warning has caused us to miss some subtle bugs. #pragma warning(disable : 4305) // level 1 // Allow a constant to be assigned to a type that is too small. // // I don't know why we allow this at all. I can't think of a case where this // wouldn't be a bug, but enabling the warning breaks many builds today. #pragma warning(disable : 4307) // level 2 // Allow passing the this pointer to an initializer even though it refers // to an uninitialized object. // // Some observer implementations rely on saving the this pointer. Those are // safe because the pointer is not dereferenced until after the object is fully // constructed. This could however, obscure other instances. In the future, we // should look into disabling this warning locally rather globally. #pragma warning(disable : 4355) // level 1 and 4 // Allow implicit coercion from an integral type to a bool. // // These could be avoided by making the code more explicit, but that's never // been the style here, so there would be many false positives. It's not // obvious if a true positive would ever help to find an actual bug. #pragma warning(disable : 4800) // level 3 #endif // _MSC_VER // ----------------------------------------------------------------------------- // Utility Macros // ----------------------------------------------------------------------------- // OS_IOS #if defined(__APPLE__) // Currently, blaze supports iOS yet doesn't define a flag. Mac users have // traditionally defined OS_IOS themselves via other build systems, since mac // hasn't been supported by blaze. // TODO(user): Remove this when all toolchains make the proper defines. #include #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #ifndef OS_IOS #define OS_IOS 1 #endif #endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #endif // defined(__APPLE__) // __GLIBC_PREREQ #if defined __linux__ // GLIBC-related macros. #include #ifndef __GLIBC_PREREQ #define __GLIBC_PREREQ(a, b) 0 // not a GLIBC system #endif #endif // __linux__ // STATIC_ANALYSIS // Klocwork static analysis tool's C/C++ complier kwcc #if defined(__KLOCWORK__) #define STATIC_ANALYSIS #endif // __KLOCWORK__ // SIZEOF_MEMBER, OFFSETOF_MEMBER #define SIZEOF_MEMBER(t, f) sizeof(reinterpret_cast(4096)->f) #define OFFSETOF_MEMBER(t, f) \ (reinterpret_cast(&(reinterpret_cast(16)->f)) - \ reinterpret_cast(16)) // LANG_CXX11 // GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least // gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1 // in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is // defined according to the language version in effect thereafter. // Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite // reasonably good C++11 support, so we set LANG_CXX for it and // newer versions (_MSC_VER >= 1900). #if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ (defined(_MSC_VER) && _MSC_VER >= 1900)) // DEPRECATED: Do not key off LANG_CXX11. Instead, write more accurate condition // that checks whether the C++ feature you need is available or missing, and // define a more specific feature macro (GOOGLE_HAVE_FEATURE_FOO). You can check // http://en.cppreference.com/w/cpp/compiler_support for compiler support on C++ // features. // Define this to 1 if the code is compiled in C++11 mode; leave it // undefined otherwise. Do NOT define it to 0 -- that causes // '#ifdef LANG_CXX11' to behave differently from '#if LANG_CXX11'. #define LANG_CXX11 1 #endif // This sanity check can be removed when all references to // LANG_CXX11 is removed from the code base. #if defined(__cplusplus) && !defined(LANG_CXX11) && !defined(SWIG) #error "LANG_CXX11 is required." #endif // GOOGLE_OBSCURE_SIGNAL #if defined(__APPLE__) // No SIGPWR on MacOSX. SIGINFO seems suitably obscure. #define GOOGLE_OBSCURE_SIGNAL SIGINFO #else /* We use SIGPWR since that seems unlikely to be used for other reasons. */ #define GOOGLE_OBSCURE_SIGNAL SIGPWR #endif // ABSL_FUNC_PTR_TO_CHAR_PTR // On some platforms, a "function pointer" points to a function descriptor // rather than directly to the function itself. // Use ABSL_FUNC_PTR_TO_CHAR_PTR(func) to get a char-pointer to the first // instruction of the function func. // TODO(b/30407660): Move this macro into Abseil when symbolizer is released in // Abseil. #if defined(__cplusplus) #if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64) // use opd section for function descriptors on these platforms, the function // address is the first word of the descriptor namespace absl { enum { kPlatformUsesOPDSections = 1 }; } // namespace absl #define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast(func)[0]) #else // not PPC or IA64 namespace absl { enum { kPlatformUsesOPDSections = 0 }; } // namespace absl #define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast(func)) #endif // PPC or IA64 #endif // __cplusplus // ----------------------------------------------------------------------------- // Utility Functions // ----------------------------------------------------------------------------- // sized_delete #ifdef __cplusplus namespace base { // We support C++14's sized deallocation for all C++ builds, // though for other toolchains, we fall back to using delete. inline void sized_delete(void *ptr, size_t size) { #ifdef GOOGLE_HAVE_SIZED_DELETE ::operator delete(ptr, size); #else (void)size; ::operator delete(ptr); #endif // GOOGLE_HAVE_SIZED_DELETE } inline void sized_delete_array(void *ptr, size_t size) { #ifdef GOOGLE_HAVE_SIZED_DELETEARRAY ::operator delete[](ptr, size); #else (void) size; ::operator delete[](ptr); #endif } } // namespace base #endif // __cplusplus // ----------------------------------------------------------------------------- // Endianness // ----------------------------------------------------------------------------- // IS_LITTLE_ENDIAN, IS_BIG_ENDIAN // Allow compiler -D defines to override detection here // which occasionally fails (e.g., on CRAN Solaris) #if defined(IS_LITTLE_ENDIAN) #undef IS_BIG_ENDIAN #elif defined(IS_BIG_ENDIAN) #undef IS_LITTLE_ENDIAN #else #if defined __linux__ || defined OS_ANDROID || defined(__ANDROID__) // TODO(user): http://b/21460321; use one of OS_ANDROID or __ANDROID__. // _BIG_ENDIAN #include #elif defined(__APPLE__) // BIG_ENDIAN #include // NOLINT(build/include) /* Let's try and follow the Linux convention */ #define __BYTE_ORDER BYTE_ORDER #define __LITTLE_ENDIAN LITTLE_ENDIAN #define __BIG_ENDIAN BIG_ENDIAN #endif // defines __BYTE_ORDER for MSVC #ifdef _MSC_VER #define __BYTE_ORDER __LITTLE_ENDIAN #define IS_LITTLE_ENDIAN #else // define the macros IS_LITTLE_ENDIAN or IS_BIG_ENDIAN // using the above endian definitions from endian.h if // endian.h was included #ifdef __BYTE_ORDER #if __BYTE_ORDER == __LITTLE_ENDIAN #define IS_LITTLE_ENDIAN #endif #if __BYTE_ORDER == __BIG_ENDIAN #define IS_BIG_ENDIAN #endif #else // __BYTE_ORDER #if defined(__LITTLE_ENDIAN__) #define IS_LITTLE_ENDIAN #elif defined(__BIG_ENDIAN__) #define IS_BIG_ENDIAN #endif #endif // __BYTE_ORDER #endif // _MSC_VER #endif // #if defined(IS_LITTLE_ENDIAN) ... #else // byte swap functions (bswap_16, bswap_32, bswap_64). // The following guarantees declaration of the byte swap functions #ifdef _MSC_VER #include // NOLINT(build/include) #define bswap_16(x) _byteswap_ushort(x) #define bswap_32(x) _byteswap_ulong(x) #define bswap_64(x) _byteswap_uint64(x) #elif defined(__APPLE__) // Mac OS X / Darwin features #include #define bswap_16(x) OSSwapInt16(x) #define bswap_32(x) OSSwapInt32(x) #define bswap_64(x) OSSwapInt64(x) #elif defined(__GLIBC__) || defined(__BIONIC__) || defined(__ASYLO__) #include // IWYU pragma: export #else static inline uint16 bswap_16(uint16 x) { #ifdef __cplusplus return static_cast(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); #else return (uint16)(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); // NOLINT #endif // __cplusplus } #define bswap_16(x) bswap_16(x) static inline uint32 bswap_32(uint32 x) { return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24)); } #define bswap_32(x) bswap_32(x) static inline uint64 bswap_64(uint64 x) { return (((x & 0xFFULL) << 56) | ((x & 0xFF00ULL) << 40) | ((x & 0xFF0000ULL) << 24) | ((x & 0xFF000000ULL) << 8) | ((x & 0xFF00000000ULL) >> 8) | ((x & 0xFF0000000000ULL) >> 24) | ((x & 0xFF000000000000ULL) >> 40) | ((x & 0xFF00000000000000ULL) >> 56)); } #define bswap_64(x) bswap_64(x) #endif // ----------------------------------------------------------------------------- // Hash // ----------------------------------------------------------------------------- #ifdef __cplusplus #ifdef STL_MSVC // not always the same as _MSC_VER #include "absl/base/internal/port_hash.inc" #else struct PortableHashBase {}; #endif // STL_MSVC #endif // __cplusplus // ----------------------------------------------------------------------------- // Global Variables // ----------------------------------------------------------------------------- // PATH_SEPARATOR // Define the OS's path separator // // NOTE: Assuming the path separator at compile time is discouraged. // Prefer instead to be tolerant of both possible separators whenever possible. #ifdef __cplusplus // C won't merge duplicate const variables at link time // Some headers provide a macro for this (GCC's system.h), remove it so that we // can use our own. #undef PATH_SEPARATOR #if defined(_WIN32) const char PATH_SEPARATOR = '\\'; #else const char PATH_SEPARATOR = '/'; #endif // _WIN32 #endif // __cplusplus // ----------------------------------------------------------------------------- // Type Alias // ----------------------------------------------------------------------------- // uint, ushort, ulong #if defined __linux__ // The uint mess: // mysql.h sets _GNU_SOURCE which sets __USE_MISC in // sys/types.h typedefs uint if __USE_MISC // mysql typedefs uint if HAVE_UINT not set // The following typedef is carefully considered, and should not cause // any clashes #if !defined(__USE_MISC) #if !defined(HAVE_UINT) #define HAVE_UINT 1 typedef unsigned int uint; #endif // !HAVE_UINT #if !defined(HAVE_USHORT) #define HAVE_USHORT 1 typedef unsigned short ushort; // NOLINT #endif // !HAVE_USHORT #if !defined(HAVE_ULONG) #define HAVE_ULONG 1 typedef unsigned long ulong; // NOLINT #endif // !HAVE_ULONG #endif // !__USE_MISC #endif // __linux__ #ifdef _MSC_VER /* if Visual C++ */ // VC++ doesn't understand "uint" #ifndef HAVE_UINT #define HAVE_UINT 1 typedef unsigned int uint; #endif // !HAVE_UINT #endif // _MSC_VER #ifdef _MSC_VER // uid_t // MSVC doesn't have uid_t typedef int uid_t; // pid_t // Defined all over the place. typedef int pid_t; #endif // _MSC_VER // mode_t #ifdef _MSC_VER // From stat.h typedef unsigned int mode_t; #endif // _MSC_VER // sig_t #ifdef _MSC_VER typedef void (*sig_t)(int); #endif // _MSC_VER // u_int16_t, int16_t #ifdef _MSC_VER // u_int16_t, int16_t don't exist in MSVC typedef unsigned short u_int16_t; // NOLINT typedef short int16_t; // NOLINT #endif // _MSC_VER // using std::hash #ifdef _MSC_VER #ifdef __cplusplus // Define a minimal set of things typically available in the global // namespace in Google code. ::string is handled elsewhere, and uniformly // for all targets. #include using std::hash; #endif // __cplusplus #endif // _MSC_VER // printf macros // __STDC_FORMAT_MACROS must be defined before inttypes.h inclusion */ #if defined(__APPLE__) /* From MacOSX's inttypes.h: * "C++ implementations should define these macros only when * __STDC_FORMAT_MACROS is defined before is included." */ #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif /* __STDC_FORMAT_MACROS */ #endif /* __APPLE__ */ // printf macros for size_t, in the style of inttypes.h #if defined(_LP64) || defined(__APPLE__) #define __PRIS_PREFIX "z" #else #define __PRIS_PREFIX #endif // Use these macros after a % in a printf format string // to get correct 32/64 bit behavior, like this: // size_t size = records.size(); // printf("%" PRIuS "\n", size); #define PRIdS __PRIS_PREFIX "d" #define PRIxS __PRIS_PREFIX "x" #define PRIuS __PRIS_PREFIX "u" #define PRIXS __PRIS_PREFIX "X" #define PRIoS __PRIS_PREFIX "o" #define GPRIuPTHREAD "lu" #define GPRIxPTHREAD "lx" #if defined(__APPLE__) #define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast(pthreadt) #else #define PRINTABLE_PTHREAD(pthreadt) pthreadt #endif #ifdef PTHREADS_REDHAT_WIN32 #include // NOLINT(build/include) #include // NOLINT(build/include) // pthread_t is not a simple integer or pointer on Win32 std::ostream &operator<<(std::ostream &out, const pthread_t &thread_id); #endif // ----------------------------------------------------------------------------- // Predefined System/Language Macros // ----------------------------------------------------------------------------- // EXFULL #if defined(__APPLE__) // Linux has this in #define EXFULL ENOMEM // not really that great a translation... #endif // __APPLE__ #ifdef _MSC_VER // This actually belongs in errno.h but there's a name conflict in errno // on WinNT. They (and a ton more) are also found in Winsock2.h, but // if'd out under NT. We need this subset at minimum. #define EXFULL ENOMEM // not really that great a translation... #endif // _MSC_VER // MSG_NOSIGNAL #if defined(__APPLE__) // Doesn't exist on OSX. #define MSG_NOSIGNAL 0 #endif // __APPLE__ // __ptr_t #if defined(__APPLE__) // Linux has this in #define __ptr_t void * #endif // __APPLE__ #ifdef _MSC_VER // From glob.h #define __ptr_t void * #endif // HUGE_VALF #ifdef _MSC_VER #include // for HUGE_VAL #ifndef HUGE_VALF #define HUGE_VALF (static_cast(HUGE_VAL)) #endif #endif // _MSC_VER // MAP_ANONYMOUS #if defined(__APPLE__) // For mmap, Linux defines both MAP_ANONYMOUS and MAP_ANON and says MAP_ANON is // deprecated. In Darwin, MAP_ANON is all there is. #if !defined MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif // !MAP_ANONYMOUS #endif // __APPLE__ // PATH_MAX // You say tomato, I say atotom #ifdef _MSC_VER #define PATH_MAX MAX_PATH #endif // ----------------------------------------------------------------------------- // Predefined System/Language Functions // ----------------------------------------------------------------------------- // strtoq, strtouq, atoll #ifdef _MSC_VER #define strtoq _strtoi64 #define strtouq _strtoui64 #define atoll _atoi64 #endif // _MSC_VER #ifdef _MSC_VER // You say tomato, I say _tomato #define strcasecmp _stricmp #define strncasecmp _strnicmp #define strdup _strdup #define tempnam _tempnam #define chdir _chdir #define getpid _getpid #define getcwd _getcwd #define putenv _putenv #define timezone _timezone #define tzname _tzname #endif // _MSC_VER // random, srandom #ifdef _MSC_VER // You say tomato, I say toma inline int random() { return rand(); } inline void srandom(unsigned int seed) { srand(seed); } #endif // _MSC_VER // bcopy, bzero #ifdef _MSC_VER // You say juxtapose, I say transpose #define bcopy(s, d, n) memcpy(d, s, n) // Really from inline void bzero(void *s, int n) { memset(s, 0, n); } #endif // _MSC_VER // gethostbyname #if defined(_WIN32) || defined(__APPLE__) // gethostbyname() *is* thread-safe for Windows native threads. It is also // safe on Mac OS X and iOS, where it uses thread-local storage, even though the // manpages claim otherwise. For details, see // http://lists.apple.com/archives/Darwin-dev/2006/May/msg00008.html #else // gethostbyname() is not thread-safe. So disallow its use. People // should either use the HostLookup::Lookup*() methods, or gethostbyname_r() #define gethostbyname gethostbyname_is_not_thread_safe_DO_NOT_USE #endif // ----------------------------------------------------------------------------- // Performance Optimization // ----------------------------------------------------------------------------- // Alignment // Unaligned APIs // Portable handling of unaligned loads, stores, and copies. // On some platforms, like ARM, the copy functions can be more efficient // then a load and a store. // // It is possible to implement all of these these using constant-length memcpy // calls, which is portable and will usually be inlined into simple loads and // stores if the architecture supports it. However, such inlining usually // happens in a pass that's quite late in compilation, which means the resulting // loads and stores cannot participate in many other optimizations, leading to // overall worse code. // TODO(user): These APIs are forked in Abseil, see // LLVM, we should reimplement these APIs with functions calling memcpy(), and // maybe publish them in Abseil. // The unaligned API is C++ only. The declarations use C++ features // (namespaces, inline) which are absent or incompatible in C. #if defined(__cplusplus) #if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \ defined(MEMORY_SANITIZER) // Consider we have an unaligned load/store of 4 bytes from address 0x...05. // AddressSanitizer will treat it as a 3-byte access to the range 05:07 and // will miss a bug if 08 is the first unaddressable byte. // ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will // miss a race between this access and some other accesses to 08. // MemorySanitizer will correctly propagate the shadow on unaligned stores // and correctly report bugs on unaligned loads, but it may not properly // update and report the origin of the uninitialized memory. // For all three tools, replacing an unaligned access with a tool-specific // callback solves the problem. // Make sure uint16_t/uint32_t/uint64_t are defined. #include extern "C" { uint16_t __sanitizer_unaligned_load16(const void *p); uint32_t __sanitizer_unaligned_load32(const void *p); uint64_t __sanitizer_unaligned_load64(const void *p); void __sanitizer_unaligned_store16(void *p, uint16_t v); void __sanitizer_unaligned_store32(void *p, uint32_t v); void __sanitizer_unaligned_store64(void *p, uint64_t v); } // extern "C" inline uint16 UNALIGNED_LOAD16(const void *p) { return __sanitizer_unaligned_load16(p); } inline uint32 UNALIGNED_LOAD32(const void *p) { return __sanitizer_unaligned_load32(p); } inline uint64 UNALIGNED_LOAD64(const void *p) { return __sanitizer_unaligned_load64(p); } inline void UNALIGNED_STORE16(void *p, uint16 v) { __sanitizer_unaligned_store16(p, v); } inline void UNALIGNED_STORE32(void *p, uint32 v) { __sanitizer_unaligned_store32(p, v); } inline void UNALIGNED_STORE64(void *p, uint64 v) { __sanitizer_unaligned_store64(p, v); } #elif defined(UNDEFINED_BEHAVIOR_SANITIZER) inline uint16 UNALIGNED_LOAD16(const void *p) { uint16 t; memcpy(&t, p, sizeof t); return t; } inline uint32 UNALIGNED_LOAD32(const void *p) { uint32 t; memcpy(&t, p, sizeof t); return t; } inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \ defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \ defined(__ppc64__) || defined(__PPC64__) // x86 and x86-64 can perform unaligned loads/stores directly; // modern PowerPC hardware can also do unaligned integer loads and stores; // but note: the FPU still sends unaligned loads and stores to a trap handler! #define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) #define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) #define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) #define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) #define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) #define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) #elif defined(__arm__) && !defined(__ARM_ARCH_5__) && \ !defined(__ARM_ARCH_5T__) && !defined(__ARM_ARCH_5TE__) && \ !defined(__ARM_ARCH_5TEJ__) && !defined(__ARM_ARCH_6__) && \ !defined(__ARM_ARCH_6J__) && !defined(__ARM_ARCH_6K__) && \ !defined(__ARM_ARCH_6Z__) && !defined(__ARM_ARCH_6ZK__) && \ !defined(__ARM_ARCH_6T2__) // ARMv7 and newer support native unaligned accesses, but only of 16-bit // and 32-bit values (not 64-bit); older versions either raise a fatal signal, // do an unaligned read and rotate the words around a bit, or do the reads very // slowly (trip through kernel mode). There's no simple #define that says just // “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 // sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define, // so in time, maybe we can move on to that. // // This is a mess, but there's not much we can do about it. // // To further complicate matters, only LDR instructions (single reads) are // allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we // explicitly tell the compiler that these accesses can be unaligned, it can and // will combine accesses. On armcc, the way to signal this is done by accessing // through the type (uint32 __packed *), but GCC has no such attribute // (it ignores __attribute__((packed)) on individual variables). However, // we can tell it that a _struct_ is unaligned, which has the same effect, // so we do that. namespace base { namespace internal { struct Unaligned16Struct { uint16 value; uint8 dummy; // To make the size non-power-of-two. } ABSL_ATTRIBUTE_PACKED; struct Unaligned32Struct { uint32 value; uint8 dummy; // To make the size non-power-of-two. } ABSL_ATTRIBUTE_PACKED; } // namespace internal } // namespace base #define UNALIGNED_LOAD16(_p) \ ((reinterpret_cast(_p))->value) #define UNALIGNED_LOAD32(_p) \ ((reinterpret_cast(_p))->value) #define UNALIGNED_STORE16(_p, _val) \ ((reinterpret_cast< ::base::internal::Unaligned16Struct *>(_p))->value = \ (_val)) #define UNALIGNED_STORE32(_p, _val) \ ((reinterpret_cast< ::base::internal::Unaligned32Struct *>(_p))->value = \ (_val)) // TODO(user): NEON supports unaligned 64-bit loads and stores. // See if that would be more efficient on platforms supporting it, // at least for copies. inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #else #define NEED_ALIGNED_LOADS // These functions are provided for architectures that don't support // unaligned loads and stores. inline uint16 UNALIGNED_LOAD16(const void *p) { uint16 t; memcpy(&t, p, sizeof t); return t; } inline uint32 UNALIGNED_LOAD32(const void *p) { uint32 t; memcpy(&t, p, sizeof t); return t; } inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #endif // The UNALIGNED_LOADW and UNALIGNED_STOREW macros load and store values // of type uword_t. #ifdef _LP64 #define UNALIGNED_LOADW(_p) UNALIGNED_LOAD64(_p) #define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE64(_p, _val) #else #define UNALIGNED_LOADW(_p) UNALIGNED_LOAD32(_p) #define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE32(_p, _val) #endif inline void UnalignedCopy16(const void *src, void *dst) { UNALIGNED_STORE16(dst, UNALIGNED_LOAD16(src)); } inline void UnalignedCopy32(const void *src, void *dst) { UNALIGNED_STORE32(dst, UNALIGNED_LOAD32(src)); } inline void UnalignedCopy64(const void *src, void *dst) { if (sizeof(void *) == 8) { UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src)); } else { const char *src_char = reinterpret_cast(src); char *dst_char = reinterpret_cast(dst); UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char)); UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4)); } } #endif // defined(__cplusplus), end of unaligned API // aligned_malloc, aligned_free #if defined(__ANDROID__) || defined(__ASYLO__) || defined(_WIN32) #include // for memalign() #endif // __ASYLO__ platform uses newlib without an underlying OS, which provides // memalign, but not posix_memalign. #if defined(__cplusplus) && \ (((defined(__GNUC__) || defined(__APPLE__) || \ defined(__NVCC__)) && \ !defined(SWIG)) || \ ((__GNUC__ >= 3 || defined(__clang__)) && defined(__ANDROID__)) || \ defined(__ASYLO__)) inline void *aligned_malloc(size_t size, size_t minimum_alignment) { #if defined(__ANDROID__) || defined(OS_ANDROID) || defined(__ASYLO__) || defined(_WIN32) || defined(__sun) || defined(sun) # if defined(_WIN32) return _aligned_malloc(size, minimum_alignment); # else return memalign(minimum_alignment, size); # endif #else // !__ANDROID__ && !OS_ANDROID && !__ASYLO__ // posix_memalign requires that the requested alignment be at least // sizeof(void*). In this case, fall back on malloc which should return memory // aligned to at least the size of a pointer. const size_t required_alignment = sizeof(void*); if (minimum_alignment < required_alignment) return malloc(size); void *ptr = nullptr; if (posix_memalign(&ptr, minimum_alignment, size) == 0) return ptr; return nullptr; #endif } inline void aligned_free(void *aligned_memory) { free(aligned_memory); } #elif defined(_MSC_VER) // MSVC inline void *aligned_malloc(size_t size, size_t minimum_alignment) { return _aligned_malloc(size, minimum_alignment); } inline void aligned_free(void *aligned_memory) { _aligned_free(aligned_memory); } #endif // aligned_malloc, aligned_free // ALIGNED_CHAR_ARRAY // // Provides a char array with the exact same alignment as another type. The // first parameter must be a complete type, the second parameter is how many // of that type to provide space for. // // ALIGNED_CHAR_ARRAY(struct stat, 16) storage_; // #if defined(__cplusplus) #undef ALIGNED_CHAR_ARRAY // Because MSVC and older GCCs require that the argument to their alignment // construct to be a literal constant integer, we use a template instantiated // at all the possible powers of two. #ifndef SWIG template struct AlignType { }; template struct AlignType<0, size> { typedef char result[size]; }; #if defined(_MSC_VER) #define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __declspec(align(X)) #define BASE_PORT_H_ALIGN_OF(T) __alignof(T) #elif defined(__GNUC__) || defined(__INTEL_COMPILER) #define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __attribute__((aligned(X))) #define BASE_PORT_H_ALIGN_OF(T) __alignof__(T) #endif #if defined(BASE_PORT_H_ALIGN_ATTRIBUTE) #define BASE_PORT_H_ALIGNTYPE_TEMPLATE(X) \ template struct AlignType { \ typedef BASE_PORT_H_ALIGN_ATTRIBUTE(X) char result[size]; \ } BASE_PORT_H_ALIGNTYPE_TEMPLATE(1); BASE_PORT_H_ALIGNTYPE_TEMPLATE(2); BASE_PORT_H_ALIGNTYPE_TEMPLATE(4); BASE_PORT_H_ALIGNTYPE_TEMPLATE(8); BASE_PORT_H_ALIGNTYPE_TEMPLATE(16); BASE_PORT_H_ALIGNTYPE_TEMPLATE(32); BASE_PORT_H_ALIGNTYPE_TEMPLATE(64); BASE_PORT_H_ALIGNTYPE_TEMPLATE(128); BASE_PORT_H_ALIGNTYPE_TEMPLATE(256); BASE_PORT_H_ALIGNTYPE_TEMPLATE(512); BASE_PORT_H_ALIGNTYPE_TEMPLATE(1024); BASE_PORT_H_ALIGNTYPE_TEMPLATE(2048); BASE_PORT_H_ALIGNTYPE_TEMPLATE(4096); BASE_PORT_H_ALIGNTYPE_TEMPLATE(8192); // Any larger and MSVC++ will complain. #define ALIGNED_CHAR_ARRAY(T, Size) \ typename AlignType::result #undef BASE_PORT_H_ALIGNTYPE_TEMPLATE #undef BASE_PORT_H_ALIGN_ATTRIBUTE #else // defined(BASE_PORT_H_ALIGN_ATTRIBUTE) #define ALIGNED_CHAR_ARRAY \ you_must_define_ALIGNED_CHAR_ARRAY_for_your_compiler_in_base_port_h #endif // defined(BASE_PORT_H_ALIGN_ATTRIBUTE) #else // !SWIG // SWIG can't represent alignment and doesn't care about alignment on data // members (it works fine without it). template struct AlignType { typedef char result[Size]; }; #define ALIGNED_CHAR_ARRAY(T, Size) AlignType::result // Enough to parse with SWIG, will never be used by running code. #define BASE_PORT_H_ALIGN_OF(Type) 16 #endif // !SWIG #else // __cplusplus #define ALIGNED_CHAR_ARRAY ALIGNED_CHAR_ARRAY_is_not_available_without_Cplusplus #endif // __cplusplus #endif // S2_BASE_PORT_H_ s2/src/s2/base/commandlineflags.h0000644000176200001440000000276314530411473016311 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_COMMANDLINEFLAGS_H_ #define S2_BASE_COMMANDLINEFLAGS_H_ #ifdef S2_USE_GFLAGS #include #else // !defined(S2_USE_GFLAGS) #include #include "s2/base/integral_types.h" #define DEFINE_bool(name, default_value, description) \ bool FLAGS_##name = default_value #define DECLARE_bool(name) \ extern bool FLAGS_##name #define DEFINE_double(name, default_value, description) \ double FLAGS_##name = default_value #define DECLARE_double(name) \ extern double FLAGS_##name #define DEFINE_int32(name, default_value, description) \ int32 FLAGS_##name = default_value #define DECLARE_int32(name) \ extern int32 FLAGS_##name #define DEFINE_string(name, default_value, description) \ std::string FLAGS_##name = default_value #define DECLARE_string(name) \ extern std::string FLAGS_##name #endif // !defined(S2_USE_GFLAGS) #endif // S2_BASE_COMMANDLINEFLAGS_H_ s2/src/s2/base/strtoint.cc0000644000176200001440000000440714530411473015027 0ustar liggesusers// Copyright 2008 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // Architecture-neutral plug compatible replacements for strtol() friends. // See strtoint.h for details on how to use this component. // #include #include #include #include "s2/base/integral_types.h" #include "s2/base/port.h" #include "s2/base/strtoint.h" // Replacement strto[u]l functions that have identical overflow and underflow // characteristics for both ILP-32 and LP-64 platforms, including errno // preservation for error-free calls. int32 strto32_adapter(const char *nptr, char **endptr, int base) { const int saved_errno = errno; errno = 0; const long result = strtol(nptr, endptr, base); if (errno == ERANGE && result == LONG_MIN) { return std::numeric_limits::min(); } else if (errno == ERANGE && result == LONG_MAX) { return std::numeric_limits::max(); } else if (errno == 0 && result < std::numeric_limits::min()) { errno = ERANGE; return std::numeric_limits::min(); } else if (errno == 0 && result > std::numeric_limits::max()) { errno = ERANGE; return std::numeric_limits::max(); } if (errno == 0) errno = saved_errno; return static_cast(result); } uint32 strtou32_adapter(const char *nptr, char **endptr, int base) { const int saved_errno = errno; errno = 0; const unsigned long result = strtoul(nptr, endptr, base); if (errno == ERANGE && result == ULONG_MAX) { return std::numeric_limits::max(); } else if (errno == 0 && result > std::numeric_limits::max()) { errno = ERANGE; return std::numeric_limits::max(); } if (errno == 0) errno = saved_errno; return static_cast(result); } s2/src/s2/s2padded_cell.h0000644000176200001440000001013614530411473014552 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2PADDED_CELL_H_ #define S2_S2PADDED_CELL_H_ #include "s2/_fp_contract_off.h" #include "s2/r2rect.h" #include "s2/s2cell_id.h" // S2PaddedCell represents an S2Cell whose (u,v)-range has been expanded on // all sides by a given amount of "padding". Unlike S2Cell, its methods and // representation are optimized for clipping edges against S2Cell boundaries // to determine which cells are intersected by a given set of edges. // // This class is intended to be copied by value as desired. class S2PaddedCell { public: // Construct an S2PaddedCell for the given cell id and padding. S2PaddedCell(S2CellId id, double padding); // Construct the child of "parent" with the given (i,j) index. The four // child cells have indices of (0,0), (0,1), (1,0), (1,1), where the i and j // indices correspond to increasing u- and v-values respectively. S2PaddedCell(const S2PaddedCell& parent, int i, int j); S2CellId id() const { return id_; } double padding() const { return padding_; } int level() const { return level_; } // Return the bound for this cell (including padding). const R2Rect& bound() const { return bound_; } // Return the "middle" of the padded cell, defined as the rectangle that // belongs to all four children. // // Note that this method is *not* thread-safe, because the return value is // computed on demand and cached. (It is expected that this class will be // mainly useful in the context of single-threaded recursive algorithms.) const R2Rect& middle() const; // Return the (i,j) coordinates for the child cell at the given traversal // position. The traversal position corresponds to the order in which child // cells are visited by the Hilbert curve. void GetChildIJ(int pos, int* i, int* j) const; // Return the smallest cell that contains all descendants of this cell whose // bounds intersect "rect". For algorithms that use recursive subdivision // to find the cells that intersect a particular object, this method can be // used to skip all the initial subdivision steps where only one child needs // to be expanded. // // Note that this method is not the same as returning the smallest cell that // contains the intersection of this cell with "rect". Because of the // padding, even if one child completely contains "rect" it is still // possible that a neighboring child also intersects "rect". // // REQUIRES: bound().Intersects(rect) S2CellId ShrinkToFit(const R2Rect& rect) const; // Return the center of this cell. S2Point GetCenter() const; // Return the vertex where the S2 space-filling curve enters this cell. S2Point GetEntryVertex() const; // Return the vertex where the S2 space-filling curve exits this cell. S2Point GetExitVertex() const; private: S2CellId id_; double padding_; R2Rect bound_; // Bound in (u,v)-space // The rectangle in (u,v)-space that belongs to all four padded children. // It is computed on demand by the middle() accessor method. mutable R2Rect middle_; int ij_lo_[2]; // Minimum (i,j)-coordinates of this cell, before padding int orientation_; // Hilbert curve orientation of this cell (see s2coords.h) int level_; // Level of this cell (see s2coords.h) }; ////////////////// Implementation details follow //////////////////// inline void S2PaddedCell::GetChildIJ(int pos, int* i, int* j) const { int ij = S2::internal::kPosToIJ[orientation_][pos]; *i = ij >> 1; *j = ij & 1; } #endif // S2_S2PADDED_CELL_H_ s2/src/s2/s2cell_union.h0000644000176200001440000004123614530411473014466 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2CELL_UNION_H_ #define S2_S2CELL_UNION_H_ #include #include "s2/base/commandlineflags.h" #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "s2/_fp_contract_off.h" #include "s2/s2cell_id.h" #include "s2/s2region.h" #include "absl/base/macros.h" class Decoder; class Encoder; class S1Angle; class S2Cap; class S2Cell; class S2LatLngRect; DECLARE_bool(s2debug); DECLARE_int32(s2cell_union_decode_max_num_cells); // An S2CellUnion is a region consisting of cells of various sizes. Typically // a cell union is used to approximate some other shape. There is a tradeoff // between the accuracy of the approximation and how many cells are used. // Unlike polygons, cells have a fixed hierarchical structure. This makes // them more suitable for optimizations based on preprocessing. // // An S2CellUnion is represented as a vector of sorted, non-overlapping // S2CellIds. By default the vector is also "normalized", meaning that groups // of 4 child cells have been replaced by their parent cell whenever possible. // S2CellUnions are not required to be normalized, but certain operations will // return different results if they are not (e.g., Contains(S2CellUnion).) // // S2CellUnion is movable and copyable. class S2CellUnion final : public S2Region { public: // Creates an empty cell union. S2CellUnion() {} // Constructs a cell union with the given S2CellIds, then calls Normalize() // to sort them, remove duplicates, and merge cells when possible. (See // FromNormalized if your vector is already normalized.) // // The argument is passed by value, so if you are passing a named variable // and have no further use for it, consider using std::move(). // // A cell union containing a single S2CellId may be constructed like this: // // S2CellUnion example({cell_id}); explicit S2CellUnion(std::vector cell_ids); // Convenience constructor that accepts a vector of uint64. Note that // unlike the constructor above, this one makes a copy of "cell_ids". explicit S2CellUnion(const std::vector& cell_ids); // Constructs a cell union for the whole sphere. static S2CellUnion WholeSphere(); // Constructs a cell union from S2CellIds that have already been normalized // (typically because they were extracted from another S2CellUnion). // // The argument is passed by value, so if you are passing a named variable // and have no further use for it, consider using std::move(). // // REQUIRES: "cell_ids" satisfies the requirements of IsNormalized(). static S2CellUnion FromNormalized(std::vector cell_ids); // Constructs a cell union from a vector of sorted, non-overlapping // S2CellIds. Unlike the other constructors, FromVerbatim does not require // that groups of 4 child cells have been replaced by their parent cell. In // other words, "cell_ids" must satisfy the requirements of IsValid() but // not necessarily IsNormalized(). // // Note that if the cell union is not normalized, certain operations may // return different results (e.g., Contains(S2CellUnion)). // // REQUIRES: "cell_ids" satisfies the requirements of IsValid(). static S2CellUnion FromVerbatim(std::vector cell_ids); // Constructs a cell union that corresponds to a continuous range of cell // ids. The output is a normalized collection of cell ids that covers the // leaf cells between "min_id" and "max_id" inclusive. // // REQUIRES: min_id.is_leaf(), max_id.is_leaf(), min_id <= max_id. static S2CellUnion FromMinMax(S2CellId min_id, S2CellId max_id); // Like FromMinMax() except that the union covers the range of leaf cells // from "begin" (inclusive) to "end" (exclusive), as with Python ranges or // STL iterator ranges. If (begin == end) the result is empty. // // REQUIRES: begin.is_leaf(), end.is_leaf(), begin <= end. static S2CellUnion FromBeginEnd(S2CellId begin, S2CellId end); // Init() methods corresponding to the constructors/factory methods above. // TODO(ericv): Consider deprecating these methods in favor of using the // constructors and move assignment operator. void Init(std::vector cell_ids); void Init(const std::vector& cell_ids); void InitFromMinMax(S2CellId min_id, S2CellId max_id); void InitFromBeginEnd(S2CellId begin, S2CellId end); // Clears the contents of the cell union and minimizes memory usage. void Clear(); // Gives ownership of the vector data to the client without copying, and // clears the content of the cell union. The original data in cell_ids // is lost if there was any. std::vector Release(); // Convenience methods for accessing the individual cell ids. int num_cells() const { return static_cast(cell_ids_.size()); } S2CellId cell_id(int i) const { return cell_ids_[i]; } // Vector-like methods for accessing the individual cell ids. size_t size() const { return cell_ids_.size(); } bool empty() const { return cell_ids_.empty(); } S2CellId operator[](int i) const { return cell_ids_[i]; } // Standard begin/end methods, to allow range-based for loops: // // for (S2CellId id : cell_union) { ... } std::vector::const_iterator begin() const; std::vector::const_iterator end() const; // Direct access to the underlying vector for STL algorithms. const std::vector& cell_ids() const { return cell_ids_; } // Returns true if the cell union is valid, meaning that the S2CellIds are // valid, non-overlapping, and sorted in increasing order. bool IsValid() const; // Returns true if the cell union is normalized, meaning that it is // satisfies IsValid() and that no four cells have a common parent. // Certain operations such as Contains(S2CellUnion) will return a different // result if the cell union is not normalized. bool IsNormalized() const; // Normalizes the cell union by discarding cells that are contained by other // cells, replacing groups of 4 child cells by their parent cell whenever // possible, and sorting all the cell ids in increasing order. // // Returns true if the number of cells was reduced. // TODO(ericv): Change this method to return void. bool Normalize(); // Replaces "output" with an expanded version of the cell union where any // cells whose level is less than "min_level" or where (level - min_level) // is not a multiple of "level_mod" are replaced by their children, until // either both of these conditions are satisfied or the maximum level is // reached. // // This method allows a covering generated by S2RegionCoverer using // min_level() or level_mod() constraints to be stored as a normalized cell // union (which allows various geometric computations to be done) and then // converted back to the original list of cell ids that satisfies the // desired constraints. void Denormalize(int min_level, int level_mod, std::vector* output) const; // If there are more than "excess" elements of the cell_ids() vector that // are allocated but unused, reallocates the array to eliminate the excess // space. This reduces memory usage when many cell unions need to be held // in memory at once. void Pack(int excess = 0); // Returns true if the cell union contains the given cell id. Containment // is defined with respect to regions, e.g. a cell contains its 4 children. // This is a fast operation (logarithmic in the size of the cell union). // // CAVEAT: If you have constructed a non-normalized S2CellUnion using // FromVerbatim, note that groups of 4 child cells are *not* considered to // contain their parent cell. To get this behavior you must use one of the // other constructors or call Normalize() explicitly. bool Contains(S2CellId id) const; // Returns true if the cell union intersects the given cell id. // This is a fast operation (logarithmic in the size of the cell union). bool Intersects(S2CellId id) const; // Returns true if this cell union contains the given other cell union. // // CAVEAT: If you have constructed a non-normalized S2CellUnion using // FromVerbatim, note that groups of 4 child cells are *not* considered to // contain their parent cell. To get this behavior you must use one of the // other constructors or call Normalize() explicitly. bool Contains(const S2CellUnion& y) const; // Returns true if this cell union intersects the given other cell union. bool Intersects(const S2CellUnion& y) const; // Returns the union of the two given cell unions. S2CellUnion Union(const S2CellUnion& y) const; // Returns the intersection of the two given cell unions. S2CellUnion Intersection(const S2CellUnion& y) const; // Specialized version of GetIntersection() that returns the intersection of // a cell union with an S2CellId. This can be useful for splitting a cell // union into pieces. S2CellUnion Intersection(S2CellId id) const; // Returns the difference of the two given cell unions. S2CellUnion Difference(const S2CellUnion& y) const; // Expands the cell union by adding a buffer of cells at "expand_level" // around the union boundary. // // For each cell "c" in the union, we add all neighboring cells at level // "expand_level" that are adjacent to "c". Note that there can be many // such cells if "c" is large compared to "expand_level". If "c" is smaller // than "expand_level", we first add the parent of "c" at "expand_level" and // then add all the neighbors of that cell. // // Note that the size of the output is exponential in "expand_level". For // example, if expand_level == 20 and the input has a cell at level 10, // there will be on the order of 4000 adjacent cells in the output. For // most applications the Expand(min_radius, max_level_diff) method below is // easier to use. void Expand(int expand_level); // Expands the cell union such that it contains all points whose distance to // the cell union is at most "min_radius", but do not use cells that are // more than "max_level_diff" levels higher than the largest cell in the // input. The second parameter controls the tradeoff between accuracy and // output size when a large region is being expanded by a small amount // (e.g. expanding Canada by 1km). For example, if max_level_diff == 4 the // region will always be expanded by approximately 1/16 the width of its // largest cell. Note that in the worst case, the number of cells in the // output can be up to 4 * (1 + 2 ** max_level_diff) times larger than the // number of cells in the input. void Expand(S1Angle min_radius, int max_level_diff); // The number of leaf cells covered by the union. // This will be no more than 6*2^60 for the whole sphere. uint64 LeafCellsCovered() const; // Approximates this cell union's area in steradians by summing the average // area of each contained cell's average area, using the AverageArea method // from the S2Cell class. This is equivalent to the number of leaves covered, // multiplied by the average area of a leaf. Note that AverageArea does not // take into account distortion of cell, and thus may be off by up to a // factor of up to 1.7. // // NOTE: Since this is proportional to LeafCellsCovered(), it is // always better to use that function if all you care about is // the relative average area between objects. double AverageBasedArea() const; // Calculates this cell union's area in steradians by summing the approximate // area for each contained cell, using the ApproxArea method from the S2Cell // class. double ApproxArea() const; // Calculates this cell union's area in steradians by summing the exact area // for each contained cell, using the Exact method from the S2Cell class. double ExactArea() const; // Return true if two cell unions are identical. friend bool operator==(const S2CellUnion& x, const S2CellUnion& y); // Return true if two cell unions are different. friend bool operator!=(const S2CellUnion& x, const S2CellUnion& y); //////////////////////////////////////////////////////////////////////// // S2Region interface (see s2region.h for details): S2CellUnion* Clone() const override; S2Cap GetCapBound() const override; S2LatLngRect GetRectBound() const override; // This is a fast operation (logarithmic in the size of the cell union). bool Contains(const S2Cell& cell) const override; // This is a fast operation (logarithmic in the size of the cell union). bool MayIntersect(const S2Cell& cell) const override; // The point 'p' does not need to be normalized. // This is a fast operation (logarithmic in the size of the cell union). bool Contains(const S2Point& p) const override; // Appends a serialized representation of the S2CellUnion to "encoder". // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). void Encode(Encoder* const encoder) const; // Decodes an S2CellUnion encoded with Encode(). Returns true on success. bool Decode(Decoder* const decoder); //////////////////////////////////////////////////////////////////////// // Static methods intended for high-performance clients that prefer to // manage their own storage. // Like Normalize(), but works with a vector of S2CellIds. // Equivalent to: // *cell_ids = S2CellUnion(std::move(*cell_ids)).Release(); static bool Normalize(std::vector* cell_ids); // Like Denormalize(), but works with a vector of S2CellIds. // REQUIRES: out != &in static void Denormalize(const std::vector& in, int min_level, int level_mod, std::vector* out); // Like GetIntersection(), but works directly with vectors of S2CellIds, // Equivalent to: // // *out = S2CellUnion(x).Intersection(S2CellUnion(y)).Release() // // except that this method has slightly more relaxed normalization // requirements: the input vectors may contain groups of 4 child cells that // all have the same parent. (In a normalized S2CellUnion, such groups are // always replaced by the parent cell.) static void GetIntersection(const std::vector& x, const std::vector& y, std::vector* out); private: friend class S2CellUnionTestPeer; // For creating invalid S2CellUnions. // Internal constructor that does not check "cell_ids" for validity. enum VerbatimFlag { VERBATIM }; S2CellUnion(std::vector cell_ids, VerbatimFlag verbatim) : cell_ids_(std::move(cell_ids)) {} // Converts a vector of uint64 to a vector of S2CellIds. static std::vector ToS2CellIds(const std::vector& ids); std::vector cell_ids_; }; ////////////////// Implementation details follow //////////////////// inline S2CellUnion::S2CellUnion(std::vector cell_ids) : cell_ids_(std::move(cell_ids)) { Normalize(); } inline S2CellUnion S2CellUnion::FromNormalized(std::vector cell_ids) { S2CellUnion result(std::move(cell_ids), VERBATIM); S2_DCHECK(result.IsNormalized()); return result; } inline S2CellUnion S2CellUnion::FromVerbatim(std::vector cell_ids) { S2CellUnion result(std::move(cell_ids), VERBATIM); S2_DCHECK(!FLAGS_s2debug || result.IsValid()); return result; } inline void S2CellUnion::Init(std::vector cell_ids) { cell_ids_ = std::move(cell_ids); Normalize(); } inline void S2CellUnion::Clear() { // swap() guarantees to reduce the RHS vector's size and capacity // to zero (as opposed to clear(), shrink_to_fit() sequence). std::vector().swap(cell_ids_); } inline std::vector S2CellUnion::Release() { // vector's rvalue reference constructor does not necessarily leave // moved-from value in empty state, so swap instead. std::vector cell_ids; cell_ids_.swap(cell_ids); return cell_ids; } inline std::vector::const_iterator S2CellUnion::begin() const { return cell_ids_.begin(); } inline std::vector::const_iterator S2CellUnion::end() const { return cell_ids_.end(); } #endif // S2_S2CELL_UNION_H_ s2/src/s2/s2latlng_rect.h0000644000176200001440000004573314530411473014643 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2LATLNG_RECT_H_ #define S2_S2LATLNG_RECT_H_ #include #include #include #include "s2/base/logging.h" #include "s2/_fp_contract_off.h" #include "s2/r1interval.h" #include "s2/s1angle.h" #include "s2/s1interval.h" #include "s2/s2latlng.h" #include "s2/s2region.h" class Decoder; class Encoder; class S2Cap; class S2Cell; // An S2LatLngRect represents a closed latitude-longitude rectangle. It is // capable of representing the empty and full rectangles as well as single // points. Note that the latitude-longitude space is considered to have a // *cylindrical* topology rather than a spherical one, i.e. the poles have // multiple lat/lng representations. An S2LatLngRect may be defined so that // includes some representations of a pole but not others. Use the // PolarClosure() method if you want to expand a rectangle so that it contains // all possible representations of any contained poles. // // Because S2LatLngRect uses S1Interval to store the longitude range, // longitudes of -180 degrees are treated specially. Except for empty // and full longitude spans, -180 degree longitudes will turn into +180 // degrees. This sign flip causes lng_lo() to be greater than lng_hi(), // indicating that the rectangle will wrap around through -180 instead of // through +179. Thus the math is consistent within the library, but the sign // flip can be surprising, especially when working with map projections where // -180 and +180 are at opposite ends of the flattened map. See the comments // on S1Interval for more details. // // This class is intended to be copied by value as desired. It uses // the default copy constructor and assignment operator, however it is // not a "plain old datatype" (POD) because it has virtual functions. class S2LatLngRect final : public S2Region { public: // Construct a rectangle from minimum and maximum latitudes and longitudes. // If lo.lng() > hi.lng(), the rectangle spans the 180 degree longitude // line. Both points must be normalized, with lo.lat() <= hi.lat(). // The rectangle contains all the points p such that 'lo' <= p <= 'hi', // where '<=' is defined in the obvious way. S2LatLngRect(const S2LatLng& lo, const S2LatLng& hi); // Construct a rectangle from latitude and longitude intervals. The two // intervals must either be both empty or both non-empty, and the latitude // interval must not extend outside [-90, +90] degrees. // Note that both intervals (and hence the rectangle) are closed. S2LatLngRect(const R1Interval& lat, const S1Interval& lng); // The default constructor creates an empty S2LatLngRect. S2LatLngRect(); // Construct a rectangle of the given size centered around the given point. // "center" needs to be normalized, but "size" does not. The latitude // interval of the result is clamped to [-90,90] degrees, and the longitude // interval of the result is Full() if and only if the longitude size is // 360 degrees or more. Examples of clamping (in degrees): // // center=(80,170), size=(40,60) -> lat=[60,90], lng=[140,-160] // center=(10,40), size=(210,400) -> lat=[-90,90], lng=[-180,180] // center=(-90,180), size=(20,50) -> lat=[-90,-80], lng=[155,-155] static S2LatLngRect FromCenterSize(const S2LatLng& center, const S2LatLng& size); // Construct a rectangle containing a single (normalized) point. static S2LatLngRect FromPoint(const S2LatLng& p); // Construct the minimal bounding rectangle containing the two given // normalized points. This is equivalent to starting with an empty // rectangle and calling AddPoint() twice. Note that it is different than // the S2LatLngRect(lo, hi) constructor, where the first point is always // used as the lower-left corner of the resulting rectangle. static S2LatLngRect FromPointPair(const S2LatLng& p1, const S2LatLng& p2); // Accessor methods. S1Angle lat_lo() const { return S1Angle::Radians(lat_.lo()); } S1Angle lat_hi() const { return S1Angle::Radians(lat_.hi()); } S1Angle lng_lo() const { return S1Angle::Radians(lng_.lo()); } S1Angle lng_hi() const { return S1Angle::Radians(lng_.hi()); } const R1Interval& lat() const { return lat_; } const S1Interval& lng() const { return lng_; } R1Interval *mutable_lat() { return &lat_; } S1Interval *mutable_lng() { return &lng_; } S2LatLng lo() const { return S2LatLng(lat_lo(), lng_lo()); } S2LatLng hi() const { return S2LatLng(lat_hi(), lng_hi()); } // The canonical empty and full rectangles, as derived from the Empty // and Full R1 and S1 Intervals. // Empty: lat_lo=1, lat_hi=0, lng_lo=Pi, lng_hi=-Pi (radians) static S2LatLngRect Empty(); // Full: lat_lo=-Pi/2, lat_hi=Pi/2, lng_lo=-Pi, lng_hi=Pi (radians) static S2LatLngRect Full(); // The full allowable range of latitudes and longitudes. static R1Interval FullLat() { return R1Interval(-M_PI_2, M_PI_2); } static S1Interval FullLng() { return S1Interval::Full(); } // Returns true if the rectangle is valid, which essentially just means // that the latitude bounds do not exceed Pi/2 in absolute value and // the longitude bounds do not exceed Pi in absolute value. Also, if // either the latitude or longitude bound is empty then both must be. bool is_valid() const; // Returns true if the rectangle is empty, i.e. it contains no points at all. bool is_empty() const; // Returns true if the rectangle is full, i.e. it contains all points. bool is_full() const; // Returns true if the rectangle is a point, i.e. lo() == hi() bool is_point() const; // Returns true if lng_.lo() > lng_.hi(), i.e. the rectangle crosses // the 180 degree longitude line. bool is_inverted() const { return lng_.is_inverted(); } // Returns the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order // (lower left, lower right, upper right, upper left). For convenience, the // argument is reduced modulo 4 to the range [0..3]. S2LatLng GetVertex(int k) const; // Returns the center of the rectangle in latitude-longitude space // (in general this is not the center of the region on the sphere). S2LatLng GetCenter() const; // Returns the width and height of this rectangle in latitude-longitude // space. Empty rectangles have a negative width and height. S2LatLng GetSize() const; // Returns the surface area of this rectangle on the unit sphere. double Area() const; // Returns the true centroid of the rectangle multiplied by its surface area // (see s2centroids.h for details on centroids). The result is not unit // length, so you may want to normalize it. Note that in general the // centroid is *not* at the center of the rectangle, and in fact it may not // even be contained by the rectangle. (It is the "center of mass" of the // rectangle viewed as subset of the unit sphere, i.e. it is the point in // space about which this curved shape would rotate.) // // The reason for multiplying the result by the rectangle area is to make it // easier to compute the centroid of more complicated shapes. The centroid // of a union of disjoint regions can be computed simply by adding their // GetCentroid() results. S2Point GetCentroid() const; // More efficient version of Contains() that accepts a S2LatLng rather than // an S2Point. The argument must be normalized. bool Contains(const S2LatLng& ll) const; // Returns true if and only if the given point is contained in the interior // of the region (i.e. the region excluding its boundary). The point 'p' // does not need to be normalized. bool InteriorContains(const S2Point& p) const; // More efficient version of InteriorContains() that accepts a S2LatLng // rather than an S2Point. The argument must be normalized. bool InteriorContains(const S2LatLng& ll) const; // Returns true if and only if the rectangle contains the given other // rectangle. bool Contains(const S2LatLngRect& other) const; // Returns true if and only if the interior of this rectangle contains all // points of the given other rectangle (including its boundary). bool InteriorContains(const S2LatLngRect& other) const; // Returns true if this rectangle and the given other rectangle have any // points in common. bool Intersects(const S2LatLngRect& other) const; // Returns true if this rectangle intersects the given cell. (This is an // exact test and may be fairly expensive, see also MayIntersect below.) bool Intersects(const S2Cell& cell) const; // Returns true if and only if the interior of this rectangle intersects // any point (including the boundary) of the given other rectangle. bool InteriorIntersects(const S2LatLngRect& other) const; // Returns true if the boundary of this rectangle intersects the given // geodesic edge (v0, v1). bool BoundaryIntersects(const S2Point& v0, const S2Point& v1) const; // Increase the size of the bounding rectangle to include the given point. // The rectangle is expanded by the minimum amount possible. The S2LatLng // argument must be normalized. void AddPoint(const S2Point& p); void AddPoint(const S2LatLng& ll); // Returns a rectangle that has been expanded by margin.lat() on each side in // the latitude direction, and by margin.lng() on each side in the longitude // direction. If either margin is negative, then shrinks the rectangle on // the corresponding sides instead. The resulting rectangle may be empty. // // As noted above, the latitude-longitude space has the topology of a // cylinder. Longitudes "wrap around" at +/-180 degrees, while latitudes // are clamped to range [-90, 90]. This means that any expansion (positive // or negative) of the full longitude range remains full (since the // "rectangle" is actually a continuous band around the cylinder), while // expansion of the full latitude range remains full only if the margin is // positive. // // If either the latitude or longitude interval becomes empty after // expansion by a negative margin, the result is empty. // // Note that if an expanded rectangle contains a pole, it may not contain // all possible lat/lng representations of that pole (see header above). // Use the PolarClosure() method if you do not want this behavior. // // If you are trying to grow a rectangle by a certain *distance* on the // sphere (e.g. 5km), use the ExpandedByDistance() method instead. S2LatLngRect Expanded(const S2LatLng& margin) const; // If the rectangle does not include either pole, returns it unmodified. // Otherwise expands the longitude range to Full() so that the rectangle // contains all possible representations of the contained pole(s). S2LatLngRect PolarClosure() const; // Returns the smallest rectangle containing the union of this rectangle and // the given rectangle. S2LatLngRect Union(const S2LatLngRect& other) const; // Returns the smallest rectangle containing the intersection of this // rectangle and the given rectangle. Note that the region of intersection // may consist of two disjoint rectangles, in which case a single rectangle // spanning both of them is returned. S2LatLngRect Intersection(const S2LatLngRect& other) const; // Expands this rectangle so that it contains all points within the given // distance of the boundary, and return the smallest such rectangle. If the // distance is negative, then instead shrinks this rectangle so that it // excludes all points within the given absolute distance of the boundary, // and returns the largest such rectangle. // // Unlike Expanded(), this method treats the rectangle as a set of points on // the sphere, and measures distances on the sphere. For example, you can // use this method to find a rectangle that contains all points within 5km // of a given rectangle. Because this method uses the topology of the // sphere, note the following: // // - The full and empty rectangles have no boundary on the sphere. Any // expansion (positive or negative) of these rectangles leaves them // unchanged. // // - Any rectangle that covers the full longitude range does not have an // east or west boundary, therefore no expansion (positive or negative) // will occur in that direction. // // - Any rectangle that covers the full longitude range and also includes // a pole will not be expanded or contracted at that pole, because it // does not have a boundary there. // // - If a rectangle is within the given distance of a pole, the result will // include the full longitude range (because all longitudes are present // at the poles). // // Expansion and contraction are defined such that they are inverses whenver // possible, i.e. // // rect.ExpandedByDistance(x).ExpandedByDistance(-x) == rect // // (approximately), so long as the first operation does not cause a // rectangle boundary to disappear (i.e., the longitude range newly becomes // full or empty, or the latitude range expands to include a pole). S2LatLngRect ExpandedByDistance(S1Angle distance) const; // Returns the minimum distance (measured along the surface of the sphere) to // the given S2LatLngRect. Both S2LatLngRects must be non-empty. S1Angle GetDistance(const S2LatLngRect& other) const; // Returns the minimum distance (measured along the surface of the sphere) // from a given point to the rectangle (both its boundary and its interior). // The latlng must be valid. S1Angle GetDistance(const S2LatLng& p) const; // Returns the (directed or undirected) Hausdorff distance (measured along the // surface of the sphere) to the given S2LatLngRect. The directed Hausdorff // distance from rectangle A to rectangle B is given by // h(A, B) = max_{p in A} min_{q in B} d(p, q). // The Hausdorff distance between rectangle A and rectangle B is given by // H(A, B) = max{h(A, B), h(B, A)}. S1Angle GetDirectedHausdorffDistance(const S2LatLngRect& other) const; S1Angle GetHausdorffDistance(const S2LatLngRect& other) const; // Returns true if two rectangles contains the same set of points. bool operator==(const S2LatLngRect& other) const; // Returns the opposite of what operator == returns. bool operator!=(const S2LatLngRect& other) const; // Returns true if the latitude and longitude intervals of the two rectangles // are the same up to the given tolerance (see r1interval.h and s1interval.h // for details). bool ApproxEquals(const S2LatLngRect& other, S1Angle max_error = S1Angle::Radians(1e-15)) const; // ApproxEquals() with separate tolerances for latitude and longitude. bool ApproxEquals(const S2LatLngRect& other, const S2LatLng& max_error) const; //////////////////////////////////////////////////////////////////////// // S2Region interface (see s2region.h for details): S2LatLngRect* Clone() const override; S2Cap GetCapBound() const override; S2LatLngRect GetRectBound() const override; bool Contains(const S2Cell& cell) const override; // This test is cheap but is NOT exact. Use Intersects() if you want a more // accurate and more expensive test. Note that when this method is used by // an S2RegionCoverer, the accuracy isn't all that important since if a cell // may intersect the region then it is subdivided, and the accuracy of this // method goes up as the cells get smaller. bool MayIntersect(const S2Cell& cell) const override; // The point 'p' does not need to be normalized. bool Contains(const S2Point& p) const override; // Appends a serialized representation of the S2LatLngRect to "encoder". // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). void Encode(Encoder* const encoder) const; // Decodes an S2LatLngRect encoded with Encode(). Returns true on success. bool Decode(Decoder* const decoder); // Returns true if the edge AB intersects the given edge of constant // longitude. static bool IntersectsLngEdge(const S2Point& a, const S2Point& b, const R1Interval& lat, double lng); // Returns true if the edge AB intersects the given edge of constant // latitude. Requires the vectors to have unit length. static bool IntersectsLatEdge(const S2Point& a, const S2Point& b, double lat, const S1Interval& lng); private: // Helper function. See .cc for description. static S1Angle GetDirectedHausdorffDistance(double lng_diff, const R1Interval& a_lat, const R1Interval& b_lat); // Helper function. See .cc for description. static S1Angle GetInteriorMaxDistance(const R1Interval& a_lat, const S2Point& b); // Helper function. See .cc for description. static S2Point GetBisectorIntersection(const R1Interval& lat, double lng); R1Interval lat_; S1Interval lng_; }; inline S2LatLngRect::S2LatLngRect(const S2LatLng& lo, const S2LatLng& hi) : lat_(lo.lat().radians(), hi.lat().radians()), lng_(lo.lng().radians(), hi.lng().radians()) { S2_DLOG_IF(ERROR, !is_valid()) << "Invalid rect: " << lo << ", " << hi; } inline S2LatLngRect::S2LatLngRect(const R1Interval& lat, const S1Interval& lng) : lat_(lat), lng_(lng) { S2_DLOG_IF(ERROR, !is_valid()) << "Invalid rect: " << lat << ", " << lng; } inline S2LatLngRect::S2LatLngRect() : lat_(R1Interval::Empty()), lng_(S1Interval::Empty()) { } inline S2LatLngRect S2LatLngRect::Empty() { return S2LatLngRect(); } inline S2LatLngRect S2LatLngRect::Full() { return S2LatLngRect(FullLat(), FullLng()); } inline bool S2LatLngRect::is_valid() const { // The lat/lng ranges must either be both empty or both non-empty. return (std::fabs(lat_.lo()) <= M_PI_2 && std::fabs(lat_.hi()) <= M_PI_2 && lng_.is_valid() && lat_.is_empty() == lng_.is_empty()); } inline bool S2LatLngRect::is_empty() const { return lat_.is_empty(); } inline bool S2LatLngRect::is_full() const { return lat_ == FullLat() && lng_.is_full(); } inline bool S2LatLngRect::is_point() const { return lat_.lo() == lat_.hi() && lng_.lo() == lng_.hi(); } inline bool S2LatLngRect::operator==(const S2LatLngRect& other) const { return lat() == other.lat() && lng() == other.lng(); } inline bool S2LatLngRect::operator!=(const S2LatLngRect& other) const { return !operator==(other); } std::ostream& operator<<(std::ostream& os, const S2LatLngRect& r); #endif // S2_S2LATLNG_RECT_H_ s2/src/s2/s2coords.h0000644000176200001440000004265614530411473013637 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // This file contains documentation of the various coordinate systems used // throughout the library. Most importantly, S2 defines a framework for // decomposing the unit sphere into a hierarchy of "cells". Each cell is a // quadrilateral bounded by four geodesics. The top level of the hierarchy is // obtained by projecting the six faces of a cube onto the unit sphere, and // lower levels are obtained by subdividing each cell into four children // recursively. Cells are numbered such that sequentially increasing cells // follow a continuous space-filling curve over the entire sphere. The // transformation is designed to make the cells at each level fairly uniform // in size. // // ////////////////////////// S2Cell Decomposition ///////////////////////// // // The following methods define the cube-to-sphere projection used by // the S2Cell decomposition. // // In the process of converting a latitude-longitude pair to a 64-bit cell // id, the following coordinate systems are used: // // (id) // An S2CellId is a 64-bit encoding of a face and a Hilbert curve position // on that face. The Hilbert curve position implicitly encodes both the // position of a cell and its subdivision level (see s2cell_id.h). // // (face, i, j) // Leaf-cell coordinates. "i" and "j" are integers in the range // [0,(2**30)-1] that identify a particular leaf cell on the given face. // The (i, j) coordinate system is right-handed on each face, and the // faces are oriented such that Hilbert curves connect continuously from // one face to the next. // // (face, s, t) // Cell-space coordinates. "s" and "t" are real numbers in the range // [0,1] that identify a point on the given face. For example, the point // (s, t) = (0.5, 0.5) corresponds to the center of the top-level face // cell. This point is also a vertex of exactly four cells at each // subdivision level greater than zero. // // (face, si, ti) // Discrete cell-space coordinates. These are obtained by multiplying // "s" and "t" by 2**31 and rounding to the nearest unsigned integer. // Discrete coordinates lie in the range [0,2**31]. This coordinate // system can represent the edge and center positions of all cells with // no loss of precision (including non-leaf cells). In binary, each // coordinate of a level-k cell center ends with a 1 followed by // (30 - k) 0s. The coordinates of its edges end with (at least) // (31 - k) 0s. // // (face, u, v) // Cube-space coordinates in the range [-1,1]. To make the cells at each // level more uniform in size after they are projected onto the sphere, // we apply a nonlinear transformation of the form u=f(s), v=f(t). // The (u, v) coordinates after this transformation give the actual // coordinates on the cube face (modulo some 90 degree rotations) before // it is projected onto the unit sphere. // // (face, u, v, w) // Per-face coordinate frame. This is an extension of the (face, u, v) // cube-space coordinates that adds a third axis "w" in the direction of // the face normal. It is always a right-handed 3D coordinate system. // Cube-space coordinates can be converted to this frame by setting w=1, // while (u,v,w) coordinates can be projected onto the cube face by // dividing by w, i.e. (face, u/w, v/w). // // (x, y, z) // Direction vector (S2Point). Direction vectors are not necessarily unit // length, and are often chosen to be points on the biunit cube // [-1,+1]x[-1,+1]x[-1,+1]. They can be be normalized to obtain the // corresponding point on the unit sphere. // // (lat, lng) // Latitude and longitude (S2LatLng). Latitudes must be between -90 and // 90 degrees inclusive, and longitudes must be between -180 and 180 // degrees inclusive. // // Note that the (i, j), (s, t), (si, ti), and (u, v) coordinate systems are // right-handed on all six faces. #ifndef S2_S2COORDS_H_ #define S2_S2COORDS_H_ #include #include #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "s2/r2.h" #include "s2/s2coords_internal.h" #include "s2/s2point.h" #include "s2/util/math/mathutil.h" // S2 is a namespace for constants and simple utility functions that are used // throughout the S2 library. The name "S2" is derived from the mathematical // symbol for the two-dimensional unit sphere (note that the "2" refers to the // dimension of the surface, not the space it is embedded in). namespace S2 { // This is the number of levels needed to specify a leaf cell. This // constant is defined here so that the S2::Metric class and the conversion // functions below can be implemented without including s2cell_id.h. Please // see s2cell_id.h for other useful constants and conversion functions. const int kMaxCellLevel = 30; // The maximum index of a valid leaf cell plus one. The range of valid leaf // cell indices is [0..kLimitIJ-1]. const int kLimitIJ = 1 << kMaxCellLevel; // == S2CellId::kMaxSize // The maximum value of an si- or ti-coordinate. The range of valid (si,ti) // values is [0..kMaxSiTi]. unsigned const int kMaxSiTi = 1U << (kMaxCellLevel + 1); // Convert an s- or t-value to the corresponding u- or v-value. This is // a non-linear transformation from [-1,1] to [-1,1] that attempts to // make the cell sizes more uniform. double STtoUV(double s); // The inverse of the STtoUV transformation. Note that it is not always // true that UVtoST(STtoUV(x)) == x due to numerical errors. double UVtoST(double u); // Convert the i- or j-index of a leaf cell to the minimum corresponding s- // or t-value contained by that cell. The argument must be in the range // [0..2**30], i.e. up to one position beyond the normal range of valid leaf // cell indices. double IJtoSTMin(int i); // Return the i- or j-index of the leaf cell containing the given // s- or t-value. If the argument is outside the range spanned by valid // leaf cell indices, return the index of the closest valid leaf cell (i.e., // return values are clamped to the range of valid leaf cell indices). int STtoIJ(double s); // Convert an si- or ti-value to the corresponding s- or t-value. double SiTitoST(unsigned int si); // Return the si- or ti-coordinate that is nearest to the given s- or // t-value. The result may be outside the range of valid (si,ti)-values. unsigned int STtoSiTi(double s); // Convert (face, u, v) coordinates to a direction vector (not // necessarily unit length). S2Point FaceUVtoXYZ(int face, double u, double v); S2Point FaceUVtoXYZ(int face, const R2Point& uv); // If the dot product of p with the given face normal is positive, // set the corresponding u and v values (which may lie outside the range // [-1,1]) and return true. Otherwise return false. bool FaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv); bool FaceXYZtoUV(int face, const S2Point& p, R2Point* puv); // Given a *valid* face for the given point p (meaning that dot product // of p with the face normal is positive), return the corresponding // u and v values (which may lie outside the range [-1,1]). void ValidFaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv); void ValidFaceXYZtoUV(int face, const S2Point& p, R2Point* puv); // Transform the given point P to the (u,v,w) coordinate frame of the given // face (where the w-axis represents the face normal). S2Point FaceXYZtoUVW(int face, const S2Point& p); // Return the face containing the given direction vector. (For points on // the boundary between faces, the result is arbitrary but repeatable.) int GetFace(const S2Point& p); // Convert a direction vector (not necessarily unit length) to // (face, u, v) coordinates. int XYZtoFaceUV(const S2Point& p, double* pu, double* pv); int XYZtoFaceUV(const S2Point& p, R2Point* puv); // Convert a direction vector (not necessarily unit length) to // (face, si, ti) coordinates and, if p is exactly equal to the center of a // cell, return the level of this cell (-1 otherwise). int XYZtoFaceSiTi(const S2Point& p, int* face, unsigned int* si, unsigned int* ti); // Convert (face, si, ti) coordinates to a direction vector (not necessarily // unit length). S2Point FaceSiTitoXYZ(int face, unsigned int si, unsigned int ti); // Return the right-handed normal (not necessarily unit length) for an // edge in the direction of the positive v-axis at the given u-value on // the given face. (This vector is perpendicular to the plane through // the sphere origin that contains the given edge.) S2Point GetUNorm(int face, double u); // Return the right-handed normal (not necessarily unit length) for an // edge in the direction of the positive u-axis at the given v-value on // the given face. S2Point GetVNorm(int face, double v); // Return the unit-length normal, u-axis, or v-axis for the given face. S2Point GetNorm(int face); S2Point GetUAxis(int face); S2Point GetVAxis(int face); // Return the given axis of the given face (u=0, v=1, w=2). S2Point GetUVWAxis(int face, int axis); // With respect to the (u,v,w) coordinate system of a given face, return the // face that lies in the given direction (negative=0, positive=1) of the // given axis (u=0, v=1, w=2). For example, GetUVWFace(4, 0, 1) returns the // face that is adjacent to face 4 in the positive u-axis direction. int GetUVWFace(int face, int axis, int direction); ////////////////// Implementation details follow //////////////////// // We have implemented three different projections from cell-space (s,t) to // cube-space (u,v): linear, quadratic, and tangent. They have the following // tradeoffs: // // Linear - This is the fastest transformation, but also produces the least // uniform cell sizes. Cell areas vary by a factor of about 5.2, with the // largest cells at the center of each face and the smallest cells in // the corners. // // Tangent - Transforming the coordinates via atan() makes the cell sizes // more uniform. The areas vary by a maximum ratio of 1.4 as opposed to a // maximum ratio of 5.2. However, each call to atan() is about as expensive // as all of the other calculations combined when converting from points to // cell ids, i.e. it reduces performance by a factor of 3. // // Quadratic - This is an approximation of the tangent projection that // is much faster and produces cells that are almost as uniform in size. // It is about 3 times faster than the tangent projection for converting // cell ids to points or vice versa. Cell areas vary by a maximum ratio of // about 2.1. // // Here is a table comparing the cell uniformity using each projection. "Area // ratio" is the maximum ratio over all subdivision levels of the largest cell // area to the smallest cell area at that level, "edge ratio" is the maximum // ratio of the longest edge of any cell to the shortest edge of any cell at // the same level, and "diag ratio" is the ratio of the longest diagonal of // any cell to the shortest diagonal of any cell at the same level. "ToPoint" // and "FromPoint" are the times in microseconds required to convert cell ids // to and from points (unit vectors) respectively. "ToPointRaw" is the time // to convert to a non-unit-length vector, which is all that is needed for // some purposes. // // Area Edge Diag ToPointRaw ToPoint FromPoint // Ratio Ratio Ratio (microseconds) // ------------------------------------------------------------------- // Linear: 5.200 2.117 2.959 0.020 0.087 0.085 // Tangent: 1.414 1.414 1.704 0.237 0.299 0.258 // Quadratic: 2.082 1.802 1.932 0.033 0.096 0.108 // // The worst-case cell aspect ratios are about the same with all three // projections. The maximum ratio of the longest edge to the shortest edge // within the same cell is about 1.4 and the maximum ratio of the diagonals // within the same cell is about 1.7. // // This data was produced using s2cell_test and s2cell_id_test. #define S2_LINEAR_PROJECTION 0 #define S2_TAN_PROJECTION 1 #define S2_QUADRATIC_PROJECTION 2 #define S2_PROJECTION S2_QUADRATIC_PROJECTION #if S2_PROJECTION == S2_LINEAR_PROJECTION inline double STtoUV(double s) { return 2 * s - 1; } inline double UVtoST(double u) { return 0.5 * (u + 1); } #elif S2_PROJECTION == S2_TAN_PROJECTION inline double STtoUV(double s) { // Unfortunately, tan(M_PI_4) is slightly less than 1.0. This isn't due to // a flaw in the implementation of tan(), it's because the derivative of // tan(x) at x=pi/4 is 2, and it happens that the two adjacent floating // point numbers on either side of the infinite-precision value of pi/4 have // tangents that are slightly below and slightly above 1.0 when rounded to // the nearest double-precision result. s = std::tan(M_PI_2 * s - M_PI_4); return s + (1.0 / (int64{1} << 53)) * s; } inline double UVtoST(double u) { volatile double a = std::atan(u); return (2 * M_1_PI) * (a + M_PI_4); } #elif S2_PROJECTION == S2_QUADRATIC_PROJECTION inline double STtoUV(double s) { if (s >= 0.5) return (1/3.) * (4*s*s - 1); else return (1/3.) * (1 - 4*(1-s)*(1-s)); } inline double UVtoST(double u) { if (u >= 0) return 0.5 * std::sqrt(1 + 3*u); else return 1 - 0.5 * std::sqrt(1 - 3*u); } #else #error Unknown value for S2_PROJECTION #endif inline double IJtoSTMin(int i) { S2_DCHECK(i >= 0 && i <= kLimitIJ); return (1.0 / kLimitIJ) * i; } inline int STtoIJ(double s) { return std::max(0, std::min(kLimitIJ - 1, MathUtil::FastIntRound(kLimitIJ * s - 0.5))); } inline double SiTitoST(unsigned int si) { S2_DCHECK_LE(si, kMaxSiTi); return (1.0 / kMaxSiTi) * si; } inline unsigned int STtoSiTi(double s) { // kMaxSiTi == 2^31, so the result doesn't fit in an int32 when s == 1. return static_cast(MathUtil::FastInt64Round(s * kMaxSiTi)); } inline S2Point FaceUVtoXYZ(int face, double u, double v) { switch (face) { case 0: return S2Point( 1, u, v); case 1: return S2Point(-u, 1, v); case 2: return S2Point(-u, -v, 1); case 3: return S2Point(-1, -v, -u); case 4: return S2Point( v, -1, -u); default: return S2Point( v, u, -1); } } inline S2Point FaceUVtoXYZ(int face, const R2Point& uv) { return FaceUVtoXYZ(face, uv[0], uv[1]); } inline void ValidFaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv) { S2_DCHECK_GT(p.DotProd(GetNorm(face)), 0); switch (face) { case 0: *pu = p[1] / p[0]; *pv = p[2] / p[0]; break; case 1: *pu = -p[0] / p[1]; *pv = p[2] / p[1]; break; case 2: *pu = -p[0] / p[2]; *pv = -p[1] / p[2]; break; case 3: *pu = p[2] / p[0]; *pv = p[1] / p[0]; break; case 4: *pu = p[2] / p[1]; *pv = -p[0] / p[1]; break; default: *pu = -p[1] / p[2]; *pv = -p[0] / p[2]; break; } } inline void ValidFaceXYZtoUV(int face, const S2Point& p, R2Point* puv) { ValidFaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]); } inline int GetFace(const S2Point& p) { int face = p.LargestAbsComponent(); if (p[face] < 0) face += 3; return face; } inline int XYZtoFaceUV(const S2Point& p, double* pu, double* pv) { int face = GetFace(p); ValidFaceXYZtoUV(face, p, pu, pv); return face; } inline int XYZtoFaceUV(const S2Point& p, R2Point* puv) { return XYZtoFaceUV(p, &(*puv)[0], &(*puv)[1]); } inline bool FaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv) { if (face < 3) { if (p[face] <= 0) return false; } else { if (p[face-3] >= 0) return false; } ValidFaceXYZtoUV(face, p, pu, pv); return true; } inline bool FaceXYZtoUV(int face, const S2Point& p, R2Point* puv) { return FaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]); } inline S2Point GetUNorm(int face, double u) { switch (face) { case 0: return S2Point( u, -1, 0); case 1: return S2Point( 1, u, 0); case 2: return S2Point( 1, 0, u); case 3: return S2Point(-u, 0, 1); case 4: return S2Point( 0, -u, 1); default: return S2Point( 0, -1, -u); } } inline S2Point GetVNorm(int face, double v) { switch (face) { case 0: return S2Point(-v, 0, 1); case 1: return S2Point( 0, -v, 1); case 2: return S2Point( 0, -1, -v); case 3: return S2Point( v, -1, 0); case 4: return S2Point( 1, v, 0); default: return S2Point( 1, 0, v); } } inline S2Point GetNorm(int face) { return GetUVWAxis(face, 2); } inline S2Point GetUAxis(int face) { return GetUVWAxis(face, 0); } inline S2Point GetVAxis(int face) { return GetUVWAxis(face, 1); } inline S2Point GetUVWAxis(int face, int axis) { const double* p = internal::kFaceUVWAxes[face][axis]; return S2Point(p[0], p[1], p[2]); } inline int GetUVWFace(int face, int axis, int direction) { S2_DCHECK(face >= 0 && face <= 5); S2_DCHECK(axis >= 0 && axis <= 2); S2_DCHECK(direction >= 0 && direction <= 1); return internal::kFaceUVWFaces[face][axis][direction]; } } // namespace S2 #endif // S2_S2COORDS_H_ s2/src/s2/s2region_intersection.h0000644000176200001440000000522314530411473016404 0ustar liggesusers// Copyright 2006 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_S2REGION_INTERSECTION_H_ #define S2_S2REGION_INTERSECTION_H_ #include #include #include "s2/base/logging.h" #include "s2/_fp_contract_off.h" #include "s2/s2region.h" #include "absl/base/macros.h" class Decoder; class Encoder; class S2Cap; class S2Cell; class S2LatLngRect; // An S2RegionIntersection represents the intersection of a set of regions. // It is convenient for computing a covering of the intersection of a set of // regions. class S2RegionIntersection final : public S2Region { public: // Creates an empty intersection that should be initialized by calling Init(). // Note: an intersection of no regions covers the entire sphere. S2RegionIntersection() = default; // Create a region representing the intersection of the given regions. explicit S2RegionIntersection(std::vector> regions); ~S2RegionIntersection() override = default; // Initialize region by taking ownership of the given regions. void Init(std::vector> regions); // Releases ownership of the regions of this intersection and returns them, // leaving this region empty. std::vector> Release(); // Accessor methods. int num_regions() const { return regions_.size(); } const S2Region* region(int i) const { return regions_[i].get(); } //////////////////////////////////////////////////////////////////////// // S2Region interface (see s2region.h for details): S2RegionIntersection* Clone() const override; S2Cap GetCapBound() const override; S2LatLngRect GetRectBound() const override; bool Contains(const S2Point& p) const override; bool Contains(const S2Cell& cell) const override; bool MayIntersect(const S2Cell& cell) const override; private: // Internal copy constructor used only by Clone() that makes a deep copy of // its argument. S2RegionIntersection(const S2RegionIntersection& src); std::vector> regions_; void operator=(const S2RegionIntersection&) = delete; }; #endif // S2_S2REGION_INTERSECTION_H_ s2/src/s2/s2region.h0000644000176200001440000001355514530411473013625 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2REGION_H_ #define S2_S2REGION_H_ #include #include "s2/_fp_contract_off.h" #include "s2/s1angle.h" class Decoder; class Encoder; class S2Cap; class S2Cell; class S2CellId; class S2LatLngRect; // An S2Region represents a two-dimensional region over the unit sphere. // It is an abstract interface with various concrete subtypes. // // The main purpose of this interface is to allow complex regions to be // approximated as simpler regions. So rather than having a wide variety // of virtual methods that are implemented by all subtypes, the interface // is restricted to methods that are useful for computing approximations. class S2Region { public: S2Region() = default; S2Region(const S2Region& other) = default; S2Region& operator=(const S2Region&) = default; virtual ~S2Region() {} // Returns a deep copy of the region. // // Note that each subtype of S2Region returns a pointer to an object of its // own type (e.g., S2Cap::Clone() returns an S2Cap*). virtual S2Region* Clone() const = 0; // Returns a bounding spherical cap that contains the region. The bound may // not be tight. virtual S2Cap GetCapBound() const = 0; // Returns a bounding latitude-longitude rectangle that contains the region. // The bound may not be tight. virtual S2LatLngRect GetRectBound() const = 0; // Returns a small collection of S2CellIds whose union covers the region. // The cells are not sorted, may have redundancies (such as cells that // contain other cells), and may cover much more area than necessary. // // This method is not intended for direct use by client code. Clients // should typically use S2RegionCoverer::GetCovering, which has options to // control the size and accuracy of the covering. Alternatively, if you // want a fast covering and don't care about accuracy, consider calling // S2RegionCoverer::GetFastCovering (which returns a cleaned-up version of // the covering computed by this method). // // GetCellUnionBound() implementations should attempt to return a small // covering (ideally 4 cells or fewer) that covers the region and can be // computed quickly. The result is used by S2RegionCoverer as a starting // point for further refinement. // // TODO(ericv): Remove the default implementation. virtual void GetCellUnionBound(std::vector *cell_ids) const; // Returns true if the region completely contains the given cell, otherwise // returns false. virtual bool Contains(const S2Cell& cell) const = 0; // If this method returns false, the region does not intersect the given // cell. Otherwise, either region intersects the cell, or the intersection // relationship could not be determined. // // Note that there is currently exactly one implementation of this method // (S2LatLngRect::MayIntersect) that takes advantage of the semantics above // to be more efficient. For all other S2Region subtypes, this method // returns true if the region intersect the cell and false otherwise. virtual bool MayIntersect(const S2Cell& cell) const = 0; // Returns true if and only if the given point is contained by the region. // The point 'p' is generally required to be unit length, although some // subtypes may relax this restriction. virtual bool Contains(const S2Point& p) const = 0; ////////////////////////////////////////////////////////////////////////// // Many S2Region subtypes also define the following non-virtual methods. ////////////////////////////////////////////////////////////////////////// // Appends a serialized representation of the region to "encoder". // // The representation chosen is left up to the sub-classes but it should // satisfy the following constraints: // - It should encode a version number. // - It should be deserializable using the corresponding Decode method. // - Performance, not space, should be the chief consideration. Encode() and // Decode() should be implemented such that the combination is equivalent // to calling Clone(). // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). // // void Encode(Encoder* const encoder) const; // Decodes an S2Region encoded with Encode(). Note that this method // requires that an S2Region object of the appropriate concrete type has // already been constructed. It is not possible to decode regions of // unknown type. // // Whenever the Decode method is changed to deal with new serialized // representations, it should be done so in a manner that allows for // older versions to be decoded i.e. the version number in the serialized // representation should be used to decide how to decode the data. // // Returns true on success. // // bool Decode(Decoder* const decoder); // Provides the same functionality as Decode, except that decoded regions // are allowed to point directly into the Decoder's memory buffer rather // than copying the data. This method can be much faster for regions that // have a lot of data (such as polygons), but the decoded region is only // valid within the scope (lifetime) of the Decoder's memory buffer. // // bool DecodeWithinScope(Decoder* const decoder); }; #endif // S2_S2REGION_H_ s2/src/s2/s2projections.h0000644000176200001440000001634614530411473014702 0ustar liggesusers// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // Defines a few simple map projections. (Clients that need more complex // projections should use a third-party library such as GeographicLib to // implement their own projection subtypes.) #ifndef S2_S2PROJECTIONS_H_ #define S2_S2PROJECTIONS_H_ #include "s2/r2.h" #include "s2/s2latlng.h" #include "s2/s2point.h" namespace S2 { // For the purposes of the S2 library, a projection is a function that maps // between S2Points and R2Points. It can also define the coordinate wrapping // behavior along each axis. class Projection { public: virtual ~Projection() {} // Converts a point on the sphere to a projected 2D point. virtual R2Point Project(const S2Point& p) const = 0; // Converts a projected 2D point to a point on the sphere. // // If wrapping is defined for a given axis (see below), then this method // should accept any real number for the corresponding coordinate. virtual S2Point Unproject(const R2Point& p) const = 0; // Convenience function equivalent to Project(ll.ToPoint()), but the // implementation may be more efficient. virtual R2Point FromLatLng(const S2LatLng& ll) const = 0; // Convenience function equivalent to S2LatLng(Unproject(p)), but the // implementation may be more efficient. virtual S2LatLng ToLatLng(const R2Point& p) const = 0; // Returns the point obtained by interpolating the given fraction of the // distance along the line from A to B. Almost all projections should // use the default implementation of this method, which simply interpolates // linearly in R2 space. Fractions < 0 or > 1 result in extrapolation // instead. // // The only reason to override this method is if you want edges to be // defined as something other than straight lines in the 2D projected // coordinate system. For example, using a third-party library such as // GeographicLib you could define edges as geodesics over an ellipsoid model // of the Earth. (Note that very few data sets define edges this way.) // // Also note that there is no reason to define a projection where edges are // geodesics over the sphere, because this is the native S2 interpretation. virtual R2Point Interpolate(double f, const R2Point& a, const R2Point& b) const; // Defines the coordinate wrapping distance along each axis. If this value // is non-zero for a given axis, the coordinates are assumed to "wrap" with // the given period. For example, if wrap_distance.y() == 360 then (x, y) // and (x, y + 360) should map to the same S2Point. // // This information is used to ensure that edges takes the shortest path // between two given points. For example, if coordinates represent // (latitude, longitude) pairs in degrees and wrap_distance().y() == 360, // then the edge (5:179, 5:-179) would be interpreted as spanning 2 degrees // of longitude rather than 358 degrees. // // If a given axis does not wrap, its wrap_distance should be set to zero. virtual R2Point wrap_distance() const = 0; // Helper function that wraps the coordinates of B if necessary in order to // obtain the shortest edge AB. For example, suppose that A = [170, 20], // B = [-170, 20], and the projection wraps so that [x, y] == [x + 360, y]. // Then this function would return [190, 20] for point B (reducing the edge // length in the "x" direction from 340 to 20). R2Point WrapDestination(const R2Point& a, const R2Point& b) const; }; // PlateCarreeProjection defines the "plate carree" (square plate) projection, // which converts points on the sphere to (longitude, latitude) pairs. // Coordinates can be scaled so that they represent radians, degrees, etc, but // the projection is always centered around (latitude=0, longitude=0). // // Note that (x, y) coordinates are backwards compared to the usual (latitude, // longitude) ordering, in order to match the usual convention for graphs in // which "x" is horizontal and "y" is vertical. class PlateCarreeProjection final : public Projection { public: // Constructs the plate carree projection where the x coordinates // (longitude) span [-x_scale, x_scale] and the y coordinates (latitude) // span [-x_scale/2, x_scale/2]. For example if x_scale==180 then the x // range is [-180, 180] and the y range is [-90, 90]. // // By default coordinates are expressed in radians, i.e. the x range is // [-Pi, Pi] and the y range is [-Pi/2, Pi/2]. explicit PlateCarreeProjection(double x_scale = M_PI); R2Point Project(const S2Point& p) const override; S2Point Unproject(const R2Point& p) const override; R2Point FromLatLng(const S2LatLng& ll) const override; S2LatLng ToLatLng(const R2Point& p) const override; R2Point wrap_distance() const override; private: double x_wrap_; double to_radians_; // Multiplier to convert coordinates to radians. double from_radians_; // Multiplier to convert coordinates from radians. }; // MercatorProjection defines the spherical Mercator projection. Google Maps // uses this projection together with WGS84 coordinates, in which case it is // known as the "Web Mercator" projection (see Wikipedia). This class makes // no assumptions regarding the coordinate system of its input points, but // simply applies the spherical Mercator projection to them. // // The Mercator projection is finite in width (x) but infinite in height (y). // "x" corresponds to longitude, and spans a finite range such as [-180, 180] // (with coordinate wrapping), while "y" is a function of latitude and spans // an infinite range. (As "y" coordinates get larger, points get closer to // the north pole but never quite reach it.) The north and south poles have // infinite "y" values. (Note that this will cause problems if you tessellate // a Mercator edge where one endpoint is a pole. If you need to do this, clip // the edge first so that the "y" coordinate is no more than about 5 * max_x.) class MercatorProjection final : public Projection { public: // Constructs a Mercator projection where "x" corresponds to longitude in // the range [-max_x, max_x] , and "y" corresponds to latitude and can be // any real number. The horizontal and vertical scales are equal locally. explicit MercatorProjection(double max_x); R2Point Project(const S2Point& p) const override; S2Point Unproject(const R2Point& p) const override; R2Point FromLatLng(const S2LatLng& ll) const override; S2LatLng ToLatLng(const R2Point& p) const override; R2Point wrap_distance() const override; private: double x_wrap_; double to_radians_; // Multiplier to convert coordinates to radians. double from_radians_; // Multiplier to convert coordinates from radians. }; } // namespace S2 #endif // S2_S2PROJECTIONS_H_ s2/src/s2/s2builderutil_s2polyline_vector_layer.cc0000644000176200001440000000676414530411473021766 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2builderutil_s2polyline_vector_layer.h" #include using std::unique_ptr; using std::vector; using EdgeType = S2Builder::EdgeType; using Graph = S2Builder::Graph; using GraphOptions = S2Builder::GraphOptions; using Label = S2Builder::Label; using DegenerateEdges = GraphOptions::DegenerateEdges; using DuplicateEdges = GraphOptions::DuplicateEdges; using SiblingPairs = GraphOptions::SiblingPairs; using EdgeId = Graph::EdgeId; namespace s2builderutil { S2PolylineVectorLayer::S2PolylineVectorLayer( vector>* polylines, const S2PolylineVectorLayer::Options& options) { Init(polylines, nullptr, nullptr, options); } S2PolylineVectorLayer::S2PolylineVectorLayer( vector>* polylines, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { Init(polylines, label_set_ids, label_set_lexicon, options); } void S2PolylineVectorLayer::Init(vector>* polylines, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr); polylines_ = polylines; label_set_ids_ = label_set_ids; label_set_lexicon_ = label_set_lexicon; options_ = options; } GraphOptions S2PolylineVectorLayer::graph_options() const { return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD, options_.duplicate_edges(), options_.sibling_pairs()); } void S2PolylineVectorLayer::Build(const Graph& g, S2Error* error) { vector edge_polylines = g.GetPolylines( options_.polyline_type()); polylines_->reserve(edge_polylines.size()); if (label_set_ids_) label_set_ids_->reserve(edge_polylines.size()); vector vertices; // Temporary storage for vertices. vector

::emplace_value(const size_type i, allocator_type *alloc, Args &&... args) { assert(i <= count()); // Shift old values to create space for new value and then construct it in // place. if (i < count()) { value_init(count(), alloc, slot(count() - 1)); for (size_type j = count() - 1; j > i; --j) slot_type::move(alloc, slot(j - 1), slot(j)); value_destroy(i, alloc); } value_init(i, alloc, std::forward(args)...); set_count(count() + 1); if (!leaf() && count() > i + 1) { for (int j = count(); j > i + 1; --j) { set_child(j, child(j - 1)); } clear_child(i + 1); } } template inline void btree_node

::remove_value(const int i, allocator_type *alloc) { if (!leaf() && count() > i + 1) { assert(child(i + 1)->count() == 0); for (size_type j = i + 1; j < count(); ++j) { set_child(j, child(j + 1)); } clear_child(count()); } slot_type::move(alloc, slot(i + 1), slot(count()), slot(i)); value_destroy(count() - 1, alloc); set_count(count() - 1); } template void btree_node

::rebalance_right_to_left(const int to_move, btree_node *right, allocator_type *alloc) { assert(parent() == right->parent()); assert(position() + 1 == right->position()); assert(right->count() >= count()); assert(to_move >= 1); assert(to_move <= right->count()); // 1) Move the delimiting value in the parent to the left node. value_init(count(), alloc, parent()->slot(position())); // 2) Move the (to_move - 1) values from the right node to the left node. right->uninitialized_move_n(to_move - 1, 0, count() + 1, this, alloc); // 3) Move the new delimiting value to the parent from the right node. slot_type::move(alloc, right->slot(to_move - 1), parent()->slot(position())); // 4) Shift the values in the right node to their correct position. slot_type::move(alloc, right->slot(to_move), right->slot(right->count()), right->slot(0)); // 5) Destroy the now-empty to_move entries in the right node. right->value_destroy_n(right->count() - to_move, to_move, alloc); if (!leaf()) { // Move the child pointers from the right to the left node. for (int i = 0; i < to_move; ++i) { init_child(count() + i + 1, right->child(i)); } for (int i = 0; i <= right->count() - to_move; ++i) { assert(i + to_move <= right->max_count()); right->init_child(i, right->child(i + to_move)); right->clear_child(i + to_move); } } // Fixup the counts on the left and right nodes. set_count(count() + to_move); right->set_count(right->count() - to_move); } template void btree_node

::rebalance_left_to_right(const int to_move, btree_node *right, allocator_type *alloc) { assert(parent() == right->parent()); assert(position() + 1 == right->position()); assert(count() >= right->count()); assert(to_move >= 1); assert(to_move <= count()); // Values in the right node are shifted to the right to make room for the // new to_move values. Then, the delimiting value in the parent and the // other (to_move - 1) values in the left node are moved into the right node. // Lastly, a new delimiting value is moved from the left node into the // parent, and the remaining empty left node entries are destroyed. if (right->count() >= to_move) { // The original location of the right->count() values are sufficient to hold // the new to_move entries from the parent and left node. // 1) Shift existing values in the right node to their correct positions. right->uninitialized_move_n(to_move, right->count() - to_move, right->count(), right, alloc); for (slot_type *src = right->slot(right->count() - to_move - 1), *dest = right->slot(right->count() - 1), *end = right->slot(0); src >= end; --src, --dest) { slot_type::move(alloc, src, dest); } // 2) Move the delimiting value in the parent to the right node. slot_type::move(alloc, parent()->slot(position()), right->slot(to_move - 1)); // 3) Move the (to_move - 1) values from the left node to the right node. slot_type::move(alloc, slot(count() - (to_move - 1)), slot(count()), right->slot(0)); } else { // The right node does not have enough initialized space to hold the new // to_move entries, so part of them will move to uninitialized space. // 1) Shift existing values in the right node to their correct positions. right->uninitialized_move_n(right->count(), 0, to_move, right, alloc); // 2) Move the delimiting value in the parent to the right node. right->value_init(to_move - 1, alloc, parent()->slot(position())); // 3) Move the (to_move - 1) values from the left node to the right node. const size_type uninitialized_remaining = to_move - right->count() - 1; uninitialized_move_n(uninitialized_remaining, count() - uninitialized_remaining, right->count(), right, alloc); slot_type::move(alloc, slot(count() - (to_move - 1)), slot(count() - uninitialized_remaining), right->slot(0)); } // 4) Move the new delimiting value to the parent from the left node. slot_type::move(alloc, slot(count() - to_move), parent()->slot(position())); // 5) Destroy the now-empty to_move entries in the left node. value_destroy_n(count() - to_move, to_move, alloc); if (!leaf()) { // Move the child pointers from the left to the right node. for (int i = right->count(); i >= 0; --i) { right->init_child(i + to_move, right->child(i)); right->clear_child(i); } for (int i = 1; i <= to_move; ++i) { right->init_child(i - 1, child(count() - to_move + i)); clear_child(count() - to_move + i); } } // Fixup the counts on the left and right nodes. set_count(count() - to_move); right->set_count(right->count() + to_move); } template void btree_node

::split(const int insert_position, btree_node *dest, allocator_type *alloc) { assert(dest->count() == 0); assert(max_count() == kNodeValues); // We bias the split based on the position being inserted. If we're // inserting at the beginning of the left node then bias the split to put // more values on the right node. If we're inserting at the end of the // right node then bias the split to put more values on the left node. if (insert_position == 0) { dest->set_count(count() - 1); } else if (insert_position == kNodeValues) { dest->set_count(0); } else { dest->set_count(count() / 2); } set_count(count() - dest->count()); assert(count() >= 1); // Move values from the left sibling to the right sibling. uninitialized_move_n(dest->count(), count(), 0, dest, alloc); // Destroy the now-empty entries in the left node. value_destroy_n(count(), dest->count(), alloc); // The split key is the largest value in the left sibling. set_count(count() - 1); parent()->emplace_value(position(), alloc, slot(count())); value_destroy(count(), alloc); parent()->init_child(position() + 1, dest); if (!leaf()) { for (int i = 0; i <= dest->count(); ++i) { assert(child(count() + i + 1) != nullptr); dest->init_child(i, child(count() + i + 1)); clear_child(count() + i + 1); } } } template void btree_node

::merge(btree_node *src, allocator_type *alloc) { assert(parent() == src->parent()); assert(position() + 1 == src->position()); // Move the delimiting value to the left node. value_init(count(), alloc, parent()->slot(position())); // Move the values from the right to the left node. src->uninitialized_move_n(src->count(), 0, count() + 1, this, alloc); // Destroy the now-empty entries in the right node. src->value_destroy_n(0, src->count(), alloc); if (!leaf()) { // Move the child pointers from the right to the left node. for (int i = 0; i <= src->count(); ++i) { init_child(count() + i + 1, src->child(i)); src->clear_child(i); } } // Fixup the counts on the src and dest nodes. set_count(1 + count() + src->count()); src->set_count(0); // Remove the value on the parent node. parent()->remove_value(position(), alloc); } template void btree_node

::swap(btree_node *x, allocator_type *alloc) { using std::swap; assert(leaf() == x->leaf()); // Determine which is the smaller/larger node. btree_node *smaller = this, *larger = x; if (smaller->count() > larger->count()) { swap(smaller, larger); } // Swap the values. for (slot_type *a = smaller->slot(0), *b = larger->slot(0), *end = a + smaller->count(); a != end; ++a, ++b) { slot_type::swap(alloc, a, b); } // Move values that can't be swapped. const size_type to_move = larger->count() - smaller->count(); larger->uninitialized_move_n(to_move, smaller->count(), smaller->count(), smaller, alloc); larger->value_destroy_n(smaller->count(), to_move, alloc); if (!leaf()) { // Swap the child pointers. std::swap_ranges(&smaller->mutable_child(0), &smaller->mutable_child(smaller->count() + 1), &larger->mutable_child(0)); // Update swapped children's parent pointers. int i = 0; for (; i <= smaller->count(); ++i) { smaller->child(i)->set_parent(smaller); larger->child(i)->set_parent(larger); } // Move the child pointers that couldn't be swapped. for (; i <= larger->count(); ++i) { smaller->init_child(i, larger->child(i)); larger->clear_child(i); } } // Swap the counts. swap(mutable_count(), x->mutable_count()); } //// // btree_iterator methods template void btree_iterator::increment_slow() { if (node->leaf()) { assert(position >= node->count()); btree_iterator save(*this); while (position == node->count() && !node->is_root()) { assert(node->parent()->child(node->position()) == node); position = node->position(); node = node->parent(); } if (position == node->count()) { *this = save; } } else { assert(position < node->count()); node = node->child(position + 1); while (!node->leaf()) { node = node->child(0); } position = 0; } } template void btree_iterator::decrement_slow() { if (node->leaf()) { assert(position <= -1); btree_iterator save(*this); while (position < 0 && !node->is_root()) { assert(node->parent()->child(node->position()) == node); position = node->position() - 1; node = node->parent(); } if (position < 0) { *this = save; } } else { assert(position >= 0); node = node->child(position); while (!node->leaf()) { node = node->child(node->count()); } position = node->count() - 1; } } //// // btree methods template void btree

::copy_values_in_order(const btree &x) { assert(empty()); // We can avoid key comparisons because we know the order of the // values is the same order we'll store them in. const_iterator iter = x.begin(); if (iter == x.end()) return; insert_multi(*iter); ++iter; for (; iter != x.end(); ++iter) { // If the btree is not empty, we can just insert the new value at the end // of the tree! internal_emplace(end(), *iter); } } template btree

::btree(const key_compare &comp, const allocator_type &alloc) : root_(comp, alloc, nullptr), rightmost_(nullptr), size_(0) {} template btree

::btree(const btree &x) : btree(x.key_comp(), x.allocator()) { copy_values_in_order(x); } template template auto btree

::insert_unique(const key_type &key, Args &&... args) -> std::pair { if (empty()) { mutable_root() = rightmost_ = new_leaf_root_node(1); } std::pair res = internal_locate(key, iterator(root(), 0)); iterator &iter = res.first; if (res.second == kExactMatch) { // The key already exists in the tree, do nothing. return std::make_pair(internal_last(iter), false); } else if (!res.second) { iterator last = internal_last(iter); if (last.node && !compare_keys(key, last.key())) { // The key already exists in the tree, do nothing. return std::make_pair(last, false); } } return std::make_pair(internal_emplace(iter, std::forward(args)...), true); } template template inline auto btree

::insert_hint_unique(iterator position, const key_type &key, Args &&... args) -> iterator { if (!empty()) { if (position == end() || compare_keys(key, position.key())) { iterator prev = position; if (position == begin() || compare_keys((--prev).key(), key)) { // prev.key() < key < position.key() return internal_emplace(position, std::forward(args)...); } } else if (compare_keys(position.key(), key)) { iterator next = position; ++next; if (next == end() || compare_keys(key, next.key())) { // position.key() < key < next.key() return internal_emplace(next, std::forward(args)...); } } else { // position.key() == key return position; } } return insert_unique(key, std::forward(args)...).first; } template template void btree

::insert_iterator_unique(InputIterator b, InputIterator e) { for (; b != e; ++b) { insert_hint_unique(end(), params_type::key(*b), *b); } } template template auto btree

::insert_multi(const key_type &key, ValueType &&v) -> iterator { if (empty()) { mutable_root() = rightmost_ = new_leaf_root_node(1); } iterator iter = internal_upper_bound(key, iterator(root(), 0)); if (!iter.node) { iter = end(); } return internal_emplace(iter, std::forward(v)); } template template auto btree

::insert_hint_multi(iterator position, ValueType &&v) -> iterator { if (!empty()) { const key_type &key = params_type::key(v); if (position == end() || !compare_keys(position.key(), key)) { iterator prev = position; if (position == begin() || !compare_keys(key, (--prev).key())) { // prev.key() <= key <= position.key() return internal_emplace(position, std::forward(v)); } } else { iterator next = position; ++next; if (next == end() || !compare_keys(next.key(), key)) { // position.key() < key <= next.key() return internal_emplace(next, std::forward(v)); } } } return insert_multi(std::forward(v)); } template template void btree

::insert_iterator_multi(InputIterator b, InputIterator e) { for (; b != e; ++b) { insert_hint_multi(end(), *b); } } template auto btree

::operator=(const btree &x) -> btree & { if (this != &x) { clear(); *mutable_key_comp() = x.key_comp(); *mutable_allocator() = x.allocator(); copy_values_in_order(x); } return *this; } template auto btree

::erase(iterator iter) -> iterator { bool internal_delete = false; if (!iter.node->leaf()) { // Deletion of a value on an internal node. First, move the largest value // from our left child here, then delete that position (in remove_value() // below). We can get to the largest value from our left child by // decrementing iter. iterator internal_iter(iter); --iter; assert(iter.node->leaf()); assert(!compare_keys(internal_iter.key(), iter.key())); slot_type::move(mutable_allocator(), iter.node->slot(iter.position), internal_iter.node->slot(internal_iter.position)); internal_delete = true; } // Delete the key from the leaf. iter.node->remove_value(iter.position, mutable_allocator()); --size_; // We want to return the next value after the one we just erased. If we // erased from an internal node (internal_delete == true), then the next // value is ++(++iter). If we erased from a leaf node (internal_delete == // false) then the next value is ++iter. Note that ++iter may point to an // internal node and the value in the internal node may move to a leaf node // (iter.node) when rebalancing is performed at the leaf level. // Merge/rebalance as we walk back up the tree. iterator res(iter); for (;;) { if (iter.node == root()) { try_shrink(); if (empty()) { return end(); } break; } if (iter.node->count() >= kMinNodeValues) { break; } bool merged = try_merge_or_rebalance(&iter); if (iter.node->leaf()) { res = iter; } if (!merged) { break; } iter.node = iter.node->parent(); } // Adjust our return value. If we're pointing at the end of a node, advance // the iterator. if (res.position == res.node->count()) { res.position = res.node->count() - 1; ++res; } // If we erased from an internal node, advance the iterator. if (internal_delete) { ++res; } return res; } template int btree

::erase(iterator begin, iterator end) { int count = std::distance(begin, end); for (int i = 0; i < count; i++) { begin = erase(begin); } return count; } template template int btree

::erase_unique(const K &key) { iterator iter = internal_find_unique(key, iterator(root(), 0)); if (!iter.node) { // The key doesn't exist in the tree, return nothing done. return 0; } erase(iter); return 1; } template template int btree

::erase_multi(const K &key) { iterator begin = internal_lower_bound(key, iterator(root(), 0)); if (!begin.node) { // The key doesn't exist in the tree, return nothing done. return 0; } // Delete all of the keys between begin and upper_bound(key). iterator end = internal_end( internal_upper_bound(key, iterator(root(), 0))); return erase(begin, end); } template void btree

::clear() { if (root() != nullptr) { internal_clear(root()); } mutable_root() = nullptr; rightmost_ = nullptr; size_ = 0; } template void btree

::swap(btree &x) { using std::swap; swap(root_, x.root_); swap(rightmost_, x.rightmost_); swap(size_, x.size_); } template void btree

::verify() const { if (root() != nullptr) { assert(size() == internal_verify(root(), nullptr, nullptr)); assert(leftmost() == (++const_iterator(root(), -1)).node); assert(rightmost_ == (--const_iterator(root(), root()->count())).node); assert(leftmost()->leaf()); assert(rightmost_->leaf()); } else { assert(empty()); assert(leftmost() == nullptr); assert(rightmost_ == nullptr); } } template void btree

::rebalance_or_split(iterator *iter) { node_type *&node = iter->node; int &insert_position = iter->position; assert(node->count() == node->max_count()); assert(kNodeValues == node->max_count()); // First try to make room on the node by rebalancing. node_type *parent = node->parent(); if (node != root()) { if (node->position() > 0) { // Try rebalancing with our left sibling. node_type *left = parent->child(node->position() - 1); assert(left->max_count() == kNodeValues); if (left->count() < kNodeValues) { // We bias rebalancing based on the position being inserted. If we're // inserting at the end of the right node then we bias rebalancing to // fill up the left node. int to_move = (kNodeValues - left->count()) / (1 + (insert_position < kNodeValues)); to_move = std::max(1, to_move); if (((insert_position - to_move) >= 0) || ((left->count() + to_move) < kNodeValues)) { left->rebalance_right_to_left(to_move, node, mutable_allocator()); assert(node->max_count() - node->count() == to_move); insert_position = insert_position - to_move; if (insert_position < 0) { insert_position = insert_position + left->count() + 1; node = left; } assert(node->count() < node->max_count()); return; } } } if (node->position() < parent->count()) { // Try rebalancing with our right sibling. node_type *right = parent->child(node->position() + 1); assert(right->max_count() == kNodeValues); if (right->count() < kNodeValues) { // We bias rebalancing based on the position being inserted. If we're // inserting at the beginning of the left node then we bias rebalancing // to fill up the right node. int to_move = (kNodeValues - right->count()) / (1 + (insert_position > 0)); to_move = std::max(1, to_move); if ((insert_position <= (node->count() - to_move)) || ((right->count() + to_move) < kNodeValues)) { node->rebalance_left_to_right(to_move, right, mutable_allocator()); if (insert_position > node->count()) { insert_position = insert_position - node->count() - 1; node = right; } assert(node->count() < node->max_count()); return; } } } // Rebalancing failed, make sure there is room on the parent node for a new // value. assert(parent->max_count() == kNodeValues); if (parent->count() == kNodeValues) { iterator parent_iter(node->parent(), node->position()); rebalance_or_split(&parent_iter); } } else { // Rebalancing not possible because this is the root node. // Create a new root node and set the current root node as the child of the // new root. parent = new_internal_node(parent); parent->init_child(0, root()); mutable_root() = parent; // If the former root was a leaf node, then it's now the rightmost node. assert(!parent->child(0)->leaf() || parent->child(0) == rightmost_); } // Split the node. node_type *split_node; if (node->leaf()) { split_node = new_leaf_node(parent); node->split(insert_position, split_node, mutable_allocator()); if (rightmost_ == node) rightmost_ = split_node; } else { split_node = new_internal_node(parent); node->split(insert_position, split_node, mutable_allocator()); } if (insert_position > node->count()) { insert_position = insert_position - node->count() - 1; node = split_node; } } template void btree

::merge_nodes(node_type *left, node_type *right) { left->merge(right, mutable_allocator()); if (right->leaf()) { if (rightmost_ == right) rightmost_ = left; delete_leaf_node(right); } else { delete_internal_node(right); } } template bool btree

::try_merge_or_rebalance(iterator *iter) { node_type *parent = iter->node->parent(); if (iter->node->position() > 0) { // Try merging with our left sibling. node_type *left = parent->child(iter->node->position() - 1); assert(left->max_count() == kNodeValues); if ((1 + left->count() + iter->node->count()) <= kNodeValues) { iter->position += 1 + left->count(); merge_nodes(left, iter->node); iter->node = left; return true; } } if (iter->node->position() < parent->count()) { // Try merging with our right sibling. node_type *right = parent->child(iter->node->position() + 1); assert(right->max_count() == kNodeValues); if ((1 + iter->node->count() + right->count()) <= kNodeValues) { merge_nodes(iter->node, right); return true; } // Try rebalancing with our right sibling. We don't perform rebalancing if // we deleted the first element from iter->node and the node is not // empty. This is a small optimization for the common pattern of deleting // from the front of the tree. if ((right->count() > kMinNodeValues) && ((iter->node->count() == 0) || (iter->position > 0))) { int to_move = (right->count() - iter->node->count()) / 2; to_move = std::min(to_move, right->count() - 1); iter->node->rebalance_right_to_left(to_move, right, mutable_allocator()); return false; } } if (iter->node->position() > 0) { // Try rebalancing with our left sibling. We don't perform rebalancing if // we deleted the last element from iter->node and the node is not // empty. This is a small optimization for the common pattern of deleting // from the back of the tree. node_type *left = parent->child(iter->node->position() - 1); if ((left->count() > kMinNodeValues) && ((iter->node->count() == 0) || (iter->position < iter->node->count()))) { int to_move = (left->count() - iter->node->count()) / 2; to_move = std::min(to_move, left->count() - 1); left->rebalance_left_to_right(to_move, iter->node, mutable_allocator()); iter->position += to_move; return false; } } return false; } template void btree

::try_shrink() { if (root()->count() > 0) { return; } // Deleted the last item on the root node, shrink the height of the tree. if (root()->leaf()) { assert(size() == 0); delete_leaf_node(root()); mutable_root() = nullptr; rightmost_ = nullptr; } else { node_type *child = root()->child(0); child->make_root(); delete_internal_node(root()); mutable_root() = child; } } template template inline IterType btree

::internal_last(IterType iter) { while (iter.node && iter.position == iter.node->count()) { iter.position = iter.node->position(); iter.node = iter.node->parent(); if (iter.node->leaf()) { iter.node = nullptr; } } return iter; } template template inline auto btree

::internal_emplace(iterator iter, Args &&... args) -> iterator { if (!iter.node->leaf()) { // We can't insert on an internal node. Instead, we'll insert after the // previous value which is guaranteed to be on a leaf node. --iter; ++iter.position; } const int max_count = iter.node->max_count(); if (iter.node->count() == max_count) { // Make room in the leaf for the new item. if (max_count < kNodeValues) { // Insertion into the root where the root is smaller than the full node // size. Simply grow the size of the root node. assert(iter.node == root()); iter.node = new_leaf_root_node(std::min(kNodeValues, 2 * max_count)); iter.node->swap(root(), mutable_allocator()); delete_leaf_node(root()); mutable_root() = iter.node; rightmost_ = iter.node; } else { rebalance_or_split(&iter); } } iter.node->emplace_value(iter.position, mutable_allocator(), std::forward(args)...); ++size_; return iter; } template template inline std::pair btree

::internal_locate( const K &key, IterType iter) const { return is_key_compare_to::value ? internal_locate_compare_to(key, iter) : internal_locate_plain_compare(key, iter); } template template inline std::pair btree

::internal_locate_plain_compare( const K &key, IterType iter) const { for (;;) { iter.position = iter.node->lower_bound(key, key_comp()); if (iter.node->leaf()) { break; } iter.node = iter.node->child(iter.position); } return std::make_pair(iter, 0); } template template inline std::pair btree

::internal_locate_compare_to( const K &key, IterType iter) const { for (;;) { int res = iter.node->lower_bound(key, key_comp()); iter.position = res & kMatchMask; if (res & kExactMatch) { return std::make_pair(iter, static_cast(kExactMatch)); } if (iter.node->leaf()) { break; } iter.node = iter.node->child(iter.position); } return std::make_pair(iter, -kExactMatch); } template template IterType btree

::internal_lower_bound( const K &key, IterType iter) const { const_lookup_key_reference lookup_key(key); if (iter.node) { for (;;) { iter.position = iter.node->lower_bound(lookup_key, key_comp()) & kMatchMask; if (iter.node->leaf()) { break; } iter.node = iter.node->child(iter.position); } iter = internal_last(iter); } return iter; } template template IterType btree

::internal_upper_bound( const K &key, IterType iter) const { const_lookup_key_reference lookup_key(key); if (iter.node) { for (;;) { iter.position = iter.node->upper_bound(lookup_key, key_comp()); if (iter.node->leaf()) { break; } iter.node = iter.node->child(iter.position); } iter = internal_last(iter); } return iter; } template template IterType btree

::internal_find_unique( const K &key, IterType iter) const { const_lookup_key_reference lookup_key(key); if (iter.node) { std::pair res = internal_locate(lookup_key, iter); if (res.second == kExactMatch) { return res.first; } if (!res.second) { iter = internal_last(res.first); if (iter.node && !compare_keys(lookup_key, iter.key())) { return iter; } } } return IterType(nullptr, 0); } template template IterType btree

::internal_find_multi( const K &key, IterType iter) const { const_lookup_key_reference lookup_key(key); if (iter.node) { iter = internal_lower_bound(lookup_key, iter); if (iter.node) { iter = internal_last(iter); if (iter.node && !compare_keys(lookup_key, iter.key())) { return iter; } } } return IterType(nullptr, 0); } template void btree

::internal_clear(node_type *node) { if (!node->leaf()) { for (int i = 0; i <= node->count(); ++i) { internal_clear(node->child(i)); } delete_internal_node(node); } else { delete_leaf_node(node); } } template int btree

::internal_verify( const node_type *node, const key_type *lo, const key_type *hi) const { assert(node->count() > 0); assert(node->count() <= node->max_count()); if (lo) { assert(!compare_keys(node->key(0), *lo)); } if (hi) { assert(!compare_keys(*hi, node->key(node->count() - 1))); } for (int i = 1; i < node->count(); ++i) { assert(!compare_keys(node->key(i), node->key(i - 1))); } int count = node->count(); if (!node->leaf()) { for (int i = 0; i <= node->count(); ++i) { assert(node->child(i) != nullptr); assert(node->child(i)->parent() == node); assert(node->child(i)->position() == i); count += internal_verify( node->child(i), (i == 0) ? lo : &node->key(i - 1), (i == node->count()) ? hi : &node->key(i)); } } return count; } } // namespace internal_btree } // namespace gtl #endif // S2_UTIL_GTL_BTREE_H_ s2/src/s2/util/gtl/btree_container.h0000644000176200001440000003413314530411473016776 0ustar liggesusers// Copyright 2007 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // #ifndef S2_UTIL_GTL_BTREE_CONTAINER_H_ #define S2_UTIL_GTL_BTREE_CONTAINER_H_ #include #include #include #include "absl/base/internal/throw_delegate.h" #include "s2/util/gtl/btree.h" // IWYU pragma: export namespace gtl { namespace internal_btree { // A common base class for btree_set, btree_map, btree_multiset, and // btree_multimap. template class btree_container { public: using key_type = typename Tree::key_type; using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; using difference_type = typename Tree::difference_type; using key_compare = typename Tree::key_compare; using value_compare = typename Tree::value_compare; using allocator_type = typename Tree::allocator_type; using reference = typename Tree::reference; using const_reference = typename Tree::const_reference; using pointer = typename Tree::pointer; using const_pointer = typename Tree::const_pointer; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; using reverse_iterator = typename Tree::reverse_iterator; using const_reverse_iterator = typename Tree::const_reverse_iterator; // Constructors/assignments. btree_container() : tree_(key_compare(), allocator_type()) {} explicit btree_container(const key_compare &comp, const allocator_type &alloc = allocator_type()) : tree_(comp, alloc) {} btree_container(const btree_container &x) = default; btree_container(btree_container &&x) noexcept = default; btree_container &operator=(const btree_container &x) = default; btree_container &operator=(btree_container &&x) noexcept( std::is_nothrow_move_assignable::value) = default; // Iterator routines. iterator begin() { return tree_.begin(); } const_iterator begin() const { return tree_.begin(); } const_iterator cbegin() const { return tree_.begin(); } iterator end() { return tree_.end(); } const_iterator end() const { return tree_.end(); } const_iterator cend() const { return tree_.end(); } reverse_iterator rbegin() { return tree_.rbegin(); } const_reverse_iterator rbegin() const { return tree_.rbegin(); } const_reverse_iterator crbegin() const { return tree_.rbegin(); } reverse_iterator rend() { return tree_.rend(); } const_reverse_iterator rend() const { return tree_.rend(); } const_reverse_iterator crend() const { return tree_.rend(); } // Lookup routines. template iterator lower_bound(const K &key) { return tree_.lower_bound(key); } template const_iterator lower_bound(const K &key) const { return tree_.lower_bound(key); } template iterator upper_bound(const K &key) { return tree_.upper_bound(key); } template const_iterator upper_bound(const K &key) const { return tree_.upper_bound(key); } template std::pair equal_range(const K &key) { return tree_.equal_range(key); } template std::pair equal_range(const K &key) const { return tree_.equal_range(key); } // Utility routines. void clear() { tree_.clear(); } void swap(btree_container &x) { tree_.swap(x.tree_); } void verify() const { tree_.verify(); } // Size routines. size_type size() const { return tree_.size(); } size_type max_size() const { return tree_.max_size(); } bool empty() const { return tree_.empty(); } size_type height() const { return tree_.height(); } size_type internal_nodes() const { return tree_.internal_nodes(); } size_type leaf_nodes() const { return tree_.leaf_nodes(); } size_type nodes() const { return tree_.nodes(); } size_type bytes_used() const { return tree_.bytes_used(); } static double average_bytes_per_value() { return Tree::average_bytes_per_value(); } double fullness() const { return tree_.fullness(); } double overhead() const { return tree_.overhead(); } friend bool operator==(const btree_container &x, const btree_container &y) { if (x.size() != y.size()) return false; return std::equal(x.begin(), x.end(), y.begin()); } friend bool operator!=(const btree_container &x, const btree_container &y) { return !(x == y); } friend bool operator<(const btree_container &x, const btree_container &y) { return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); } friend bool operator>(const btree_container &x, const btree_container &y) { return y < x; } friend bool operator<=(const btree_container &x, const btree_container &y) { return !(y < x); } friend bool operator>=(const btree_container &x, const btree_container &y) { return !(x < y); } // The allocator used by the btree. allocator_type get_allocator() const { return tree_.get_allocator(); } // The key comparator used by the btree. key_compare key_comp() const { return tree_.key_comp(); } value_compare value_comp() const { return tree_.value_comp(); } // Support absl::Hash. template friend State AbslHashValue(State h, const btree_container &b) { for (const auto &v : b) { h = State::combine(std::move(h), v); } return State::combine(std::move(h), b.size()); } // Exposed only for tests. static bool testonly_uses_linear_node_search() { return Tree::testonly_uses_linear_node_search(); } protected: Tree tree_; }; // A common base class for btree_set and btree_map. template class btree_set_container : public btree_container { using super_type = btree_container; using mutable_value_type = typename Tree::mutable_value_type; using params_type = typename Tree::params_type; friend class BtreeNodePeer; public: using value_type = typename Tree::value_type; using size_type = typename Tree::size_type; using key_compare = typename Tree::key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; // Inherit constructors. using super_type::super_type; btree_set_container() {} // Range constructor. template btree_set_container(InputIterator b, InputIterator e, const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()) : super_type(comp, alloc) { insert(b, e); } // Initializer list constructor. btree_set_container(std::initializer_list init, const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()) : btree_set_container(init.begin(), init.end(), comp, alloc) {} // Lookup routines. template iterator find(const K &key) { return this->tree_.find_unique(key); } template const_iterator find(const K &key) const { return this->tree_.find_unique(key); } template size_type count(const K &key) const { return this->tree_.count_unique(key); } // Insertion routines. std::pair insert(const value_type &x) { return this->tree_.insert_unique(params_type::key(x), x); } std::pair insert(value_type &&x) { return this->tree_.insert_unique(params_type::key(x), std::move(x)); } template std::pair emplace(Args &&... args) { mutable_value_type v(std::forward(args)...); return this->tree_.insert_unique(params_type::key(v), std::move(v)); } iterator insert(iterator position, const value_type &x) { return this->tree_.insert_hint_unique(position, params_type::key(x), x); } iterator insert(iterator position, value_type &&x) { return this->tree_.insert_hint_unique(position, params_type::key(x), std::move(x)); } template iterator emplace_hint(iterator position, Args &&... args) { mutable_value_type v(std::forward(args)...); return this->tree_.insert_hint_unique(position, params_type::key(v), std::move(v)); } template void insert(InputIterator b, InputIterator e) { this->tree_.insert_iterator_unique(b, e); } void insert(std::initializer_list init) { this->tree_.insert_iterator_unique(init.begin(), init.end()); } // Deletion routines. template int erase(const K &key) { return this->tree_.erase_unique(key); } // Erase the specified iterator from the btree. The iterator must be valid // (i.e. not equal to end()). Return an iterator pointing to the node after // the one that was erased (or end() if none exists). iterator erase(const iterator &iter) { return this->tree_.erase(iter); } void erase(const iterator &first, const iterator &last) { this->tree_.erase(first, last); } }; // Base class for btree_map. template class btree_map_container : public btree_set_container { using super_type = btree_set_container; public: using key_type = typename Tree::key_type; using mapped_type = typename Tree::mapped_type; using value_type = typename Tree::value_type; using key_compare = typename Tree::key_compare; using allocator_type = typename Tree::allocator_type; // Inherit constructors. using super_type::super_type; btree_map_container() {} // Insertion routines. mapped_type &operator[](const key_type &key) { return this->tree_ .insert_unique(key, std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple()) .first->second; } mapped_type &operator[](key_type &&key) { return this->tree_ .insert_unique(key, std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::forward_as_tuple()) .first->second; } mapped_type &at(const key_type &key) { auto it = this->find(key); if (it == this->end()) absl::base_internal::ThrowStdOutOfRange("btree_map::at"); return it->second; } const mapped_type &at(const key_type &key) const { auto it = this->find(key); if (it == this->end()) absl::base_internal::ThrowStdOutOfRange("btree_map::at"); return it->second; } }; // A common base class for btree_multiset and btree_multimap. template class btree_multiset_container : public btree_container { using super_type = btree_container; public: using key_type = typename Tree::key_type; using value_type = typename Tree::value_type; using mapped_type = typename Tree::mapped_type; using size_type = typename Tree::size_type; using key_compare = typename Tree::key_compare; using allocator_type = typename Tree::allocator_type; using iterator = typename Tree::iterator; using const_iterator = typename Tree::const_iterator; // Inherit constructors. using super_type::super_type; btree_multiset_container() {} // Range constructor. template btree_multiset_container(InputIterator b, InputIterator e, const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()) : super_type(comp, alloc) { insert(b, e); } // Initializer list constructor. btree_multiset_container(std::initializer_list init, const key_compare &comp = key_compare(), const allocator_type &alloc = allocator_type()) : btree_multiset_container(init.begin(), init.end(), comp, alloc) {} // Lookup routines. template iterator find(const K &key) { return this->tree_.find_multi(key); } template const_iterator find(const K &key) const { return this->tree_.find_multi(key); } template size_type count(const K &key) const { return this->tree_.count_multi(key); } // Insertion routines. iterator insert(const value_type &x) { return this->tree_.insert_multi(x); } iterator insert(value_type &&x) { return this->tree_.insert_multi(std::move(x)); } iterator insert(iterator position, const value_type &x) { return this->tree_.insert_hint_multi(position, x); } iterator insert(iterator position, value_type &&x) { return this->tree_.insert_hint_multi(position, std::move(x)); } template void insert(InputIterator b, InputIterator e) { this->tree_.insert_iterator_multi(b, e); } void insert(std::initializer_list init) { this->tree_.insert_iterator_multi(init.begin(), init.end()); } // Deletion routines. template int erase(const K &key) { return this->tree_.erase_multi(key); } // Erase the specified iterator from the btree. The iterator must be valid // (i.e. not equal to end()). Return an iterator pointing to the node after // the one that was erased (or end() if none exists). iterator erase(const iterator &iter) { return this->tree_.erase(iter); } void erase(const iterator &first, const iterator &last) { this->tree_.erase(first, last); } }; // A base class for btree_multimap. template class btree_multimap_container : public btree_multiset_container { using super_type = btree_multiset_container; public: using mapped_type = typename Tree::mapped_type; // Inherit constructors. using super_type::super_type; btree_multimap_container() {} }; } // namespace internal_btree } // namespace gtl #endif // S2_UTIL_GTL_BTREE_CONTAINER_H_ s2/src/s2/util/gtl/btree_map.h0000644000176200001440000000525414530411473015573 0ustar liggesusers// Copyright 2007 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // A btree_map<> implements the STL unique sorted associative container // interface and the pair associative container interface (a.k.a map<>) using a // btree. A btree_multimap<> implements the STL multiple sorted associative // container interface and the pair associative container interface (a.k.a // multimap<>) using a btree. See btree.h for details of the btree // implementation and caveats. // #ifndef S2_UTIL_GTL_BTREE_MAP_H_ #define S2_UTIL_GTL_BTREE_MAP_H_ #include #include #include #include #include #include "s2/util/gtl/btree.h" // IWYU pragma: export #include "s2/util/gtl/btree_container.h" // IWYU pragma: export namespace gtl { template , typename Alloc = std::allocator>, int TargetNodeSize = 256> class btree_map : public internal_btree::btree_map_container< internal_btree::btree>> { using Base = typename btree_map::btree_map_container; public: btree_map() {} using Base::Base; }; template void swap(btree_map &x, btree_map &y) { return x.swap(y); } template , typename Alloc = std::allocator>, int TargetNodeSize = 256> class btree_multimap : public internal_btree::btree_multimap_container< internal_btree::btree>> { using Base = typename btree_multimap::btree_multimap_container; public: btree_multimap() {} using Base::Base; }; template void swap(btree_multimap &x, btree_multimap &y) { return x.swap(y); } } // namespace gtl #endif // S2_UTIL_GTL_BTREE_MAP_H_ s2/src/s2/util/gtl/hashtable_common.h0000644000176200001440000002133014530411473017131 0ustar liggesusers// Copyright 2010 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // --- #ifndef S2_UTIL_GTL_HASHTABLE_COMMON_H_ #define S2_UTIL_GTL_HASHTABLE_COMMON_H_ #include #include #include #include // For length_error // Settings contains parameters for growing and shrinking the table. // It also packages zero-size functor (ie. hasher). One invariant // enforced in enlarge_size() is that we never allow all slots // occupied. (This is unlikely to matter to users, because using // a load near 1 is slow and not recommended. It allows other code // to assume there is at least one empty bucket.) // // It does some munging of the hash value in cases where we think // (fear) the original hash function might not be very good. In // particular, the default hash of pointers is the identity hash, // so probably all the low bits are 0. We identify when we think // we're hashing a pointer, and chop off the low bits. Note this // isn't perfect: even when the key is a pointer, we can't tell // for sure that the hash is the identity hash. If it's not, this // is needless work (and possibly, though not likely, harmful). template class sh_hashtable_settings : public HashFunc { public: typedef Key key_type; typedef HashFunc hasher; typedef SizeType size_type; public: sh_hashtable_settings(const hasher& hf, const float ht_occupancy_flt, const float ht_empty_flt) : hasher(hf), enlarge_threshold_(0), shrink_threshold_(0), consider_shrink_(false), use_empty_(false), use_deleted_(false), num_ht_copies_(0) { set_enlarge_factor(ht_occupancy_flt); set_shrink_factor(ht_empty_flt); } template size_type hash(const K& v) const { // We munge the hash value when we don't trust hasher::operator(). It is // very important that we use hash_munger instead of hash_munger. // Within a given hashtable, all hash values must be munged in the same way. return hash_munger::MungedHash(hasher::operator()(v)); } float enlarge_factor() const { return enlarge_factor_; } void set_enlarge_factor(float f) { enlarge_factor_ = f; } float shrink_factor() const { return shrink_factor_; } void set_shrink_factor(float f) { shrink_factor_ = f; } size_type enlarge_threshold() const { return enlarge_threshold_; } void set_enlarge_threshold(size_type t) { enlarge_threshold_ = t; } size_type shrink_threshold() const { return shrink_threshold_; } void set_shrink_threshold(size_type t) { shrink_threshold_ = t; } size_type enlarge_size(size_type x) const { return std::min(x - 1, x * enlarge_factor_); } size_type shrink_size(size_type x) const { return static_cast(x * shrink_factor_); } bool consider_shrink() const { return consider_shrink_; } void set_consider_shrink(bool t) { consider_shrink_ = t; } bool use_empty() const { return use_empty_; } void set_use_empty(bool t) { use_empty_ = t; } bool use_deleted() const { return use_deleted_; } void set_use_deleted(bool t) { use_deleted_ = t; } size_type num_ht_copies() const { return static_cast(num_ht_copies_); } void inc_num_ht_copies() { ++num_ht_copies_; } // Reset the enlarge and shrink thresholds void reset_thresholds(size_type num_buckets) { set_enlarge_threshold(enlarge_size(num_buckets)); set_shrink_threshold(shrink_size(num_buckets)); // whatever caused us to reset already considered set_consider_shrink(false); } // Caller is resposible for calling reset_threshold right after // set_resizing_parameters. void set_resizing_parameters(float shrink, float grow) { assert(shrink >= 0.0); assert(grow <= 1.0); if (shrink > grow/2.0f) shrink = grow / 2.0f; // otherwise we thrash hashtable size set_shrink_factor(shrink); set_enlarge_factor(grow); } // This is the smallest size a hashtable can be without being too crowded. // If you like, you can give a min #buckets as well as a min #elts. // This is guaranteed to return a power of two. size_type min_buckets(size_type num_elts, size_type min_buckets_wanted) { float enlarge = enlarge_factor(); size_type sz = HT_MIN_BUCKETS; // min buckets allowed while ( sz < min_buckets_wanted || num_elts >= static_cast(sz * enlarge) ) { // This just prevents overflowing size_type, since sz can exceed // max_size() here. if (static_cast(sz * 2) < sz) { throw std::length_error("resize overflow"); // protect against overflow } sz *= 2; } return sz; } private: template class hash_munger { public: static size_t MungedHash(size_t hash) { return hash; } }; // This matches when the hashtable key is a pointer. template class hash_munger { public: static size_t MungedHash(size_t hash) { // TODO(user): consider rotating instead: // static const int shift = (sizeof(void *) == 4) ? 2 : 3; // return (hash << (sizeof(hash) * 8) - shift)) | (hash >> shift); // This matters if we ever change sparse/dense_hash_* to compare // hashes before comparing actual values. It's speedy on x86. return hash / sizeof(void*); // get rid of known-0 bits } }; size_type enlarge_threshold_; // table.size() * enlarge_factor size_type shrink_threshold_; // table.size() * shrink_factor float enlarge_factor_; // how full before resize float shrink_factor_; // how empty before resize // consider_shrink=true if we should try to shrink before next insert bool consider_shrink_; bool use_empty_; // used only by densehashtable, not sparsehashtable bool use_deleted_; // false until delkey has been set // num_ht_copies is a counter incremented every Copy/Move unsigned int num_ht_copies_; }; // This traits class checks whether T::is_transparent exists and names a type. // // struct Foo { using is_transparent = void; }; // struct Bar {}; // static_assert(sh_is_transparent::value, "Foo is transparent."); // staitc_assert(!sh_is_transparent::value, "Bar is not transparent."); template struct sh_is_transparent { private: struct No { char x; }; struct Yes { No x[2]; }; template static Yes Test(typename U::is_transparent*); template static No Test(...); public: enum { value = sizeof(Test(nullptr)) == sizeof(Yes) }; }; #endif // S2_UTIL_GTL_HASHTABLE_COMMON_H_ s2/src/s2/util/gtl/container_logging.h0000644000176200001440000002247114530411473017325 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // All Rights Reserved. // // Utilities for container logging. // TODO(user): Broaden the scope and rename to "stream_util.h" // #ifndef S2_UTIL_GTL_CONTAINER_LOGGING_H_ #define S2_UTIL_GTL_CONTAINER_LOGGING_H_ #include #include #include #include #include "s2/base/integral_types.h" #include "s2/base/port.h" #include "s2/strings/ostringstream.h" namespace gtl { // Several policy classes below determine how LogRangeToStream will // format a range of items. A Policy class should have these methods: // // Called to print an individual container element. // void Log(ostream &out, const ElementT &element) const; // // Called before printing the set of elements: // void LogOpening(ostream &out) const; // // Called after printing the set of elements: // void LogClosing(ostream &out) const; // // Called before printing the first element: // void LogFirstSeparator(ostream &out) const; // // Called before printing the remaining elements: // void LogSeparator(ostream &out) const; // // Returns the maximum number of elements to print: // int64 MaxElements() const; // // Called to print an indication that MaximumElements() was reached: // void LogEllipsis(ostream &out) const; namespace internal { struct LogBase { template void Log(std::ostream &out, const ElementT &element) const { // NOLINT out << element; } void LogEllipsis(std::ostream &out) const { // NOLINT out << "..."; } }; struct LogShortBase : public LogBase { void LogOpening(std::ostream &out) const { out << "["; } // NOLINT void LogClosing(std::ostream &out) const { out << "]"; } // NOLINT void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT void LogSeparator(std::ostream &out) const { out << ", "; } // NOLINT }; struct LogMultilineBase : public LogBase { void LogOpening(std::ostream &out) const { out << "["; } // NOLINT void LogClosing(std::ostream &out) const { out << "\n]"; } // NOLINT void LogFirstSeparator(std::ostream &out) const { out << "\n"; } // NOLINT void LogSeparator(std::ostream &out) const { out << "\n"; } // NOLINT }; struct LogLegacyBase : public LogBase { void LogOpening(std::ostream &out) const { out << ""; } // NOLINT void LogClosing(std::ostream &out) const { out << ""; } // NOLINT void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT void LogSeparator(std::ostream &out) const { out << " "; } // NOLINT }; } // namespace internal // LogShort uses [] braces and separates items with comma-spaces. For // example "[1, 2, 3]". struct LogShort : public internal::LogShortBase { int64 MaxElements() const { return std::numeric_limits::max(); } }; // LogShortUpToN(max_elements) formats the same as LogShort but prints no more // than the max_elements elements. class LogShortUpToN : public internal::LogShortBase { public: explicit LogShortUpToN(int64 max_elements) : max_elements_(max_elements) {} int64 MaxElements() const { return max_elements_; } private: int64 max_elements_; }; // LogShortUpTo100 formats the same as LogShort but prints no more // than 100 elements. struct LogShortUpTo100 : public LogShortUpToN { LogShortUpTo100() : LogShortUpToN(100) {} }; // LogMultiline uses [] braces and separates items with // newlines. For example "[ // 1 // 2 // 3 // ]". struct LogMultiline : public internal::LogMultilineBase { int64 MaxElements() const { return std::numeric_limits::max(); } }; // LogMultilineUpToN(max_elements) formats the same as LogMultiline but // prints no more than max_elements elements. class LogMultilineUpToN : public internal::LogMultilineBase { public: explicit LogMultilineUpToN(int64 max_elements) : max_elements_(max_elements) {} int64 MaxElements() const { return max_elements_; } private: int64 max_elements_; }; // LogMultilineUpTo100 formats the same as LogMultiline but // prints no more than 100 elements. struct LogMultilineUpTo100 : public LogMultilineUpToN { LogMultilineUpTo100() : LogMultilineUpToN(100) {} }; // The legacy behavior of LogSequence() does not use braces and // separates items with spaces. For example "1 2 3". struct LogLegacyUpTo100 : public internal::LogLegacyBase { int64 MaxElements() const { return 100; } }; struct LogLegacy : public internal::LogLegacyBase { int64 MaxElements() const { return std::numeric_limits::max(); } }; // The default policy for new code. typedef LogShortUpTo100 LogDefault; // LogRangeToStream should be used to define operator<< for // STL and STL-like containers. For example, see stl_logging.h. template inline void LogRangeToStream(std::ostream &out, // NOLINT IteratorT begin, IteratorT end, const PolicyT &policy) { policy.LogOpening(out); for (size_t i = 0; begin != end && i < policy.MaxElements(); ++i, ++begin) { if (i == 0) { policy.LogFirstSeparator(out); } else { policy.LogSeparator(out); } policy.Log(out, *begin); } if (begin != end) { policy.LogSeparator(out); policy.LogEllipsis(out); } policy.LogClosing(out); } namespace detail { // RangeLogger is a helper class for gtl::LogRange and // gtl::LogContainer; do not use it directly. This object // captures iterators into the argument of the LogRange and // LogContainer functions, so its lifetime should be confined to a // single logging statement. Objects of this type should not be // assigned to local variables. template class RangeLogger { public: RangeLogger(const IteratorT &begin, const IteratorT &end, const PolicyT &policy) : begin_(begin), end_(end), policy_(policy) { } friend std::ostream &operator<<(std::ostream &out, const RangeLogger &range) { gtl::LogRangeToStream(out, range.begin_, range.end_, range.policy_); return out; } // operator<< above is generally recommended. However, some situations may // require a string, so a convenience str() method is provided as well. std::string str() const { std::string s; ::strings::OStringStream(&s) << *this; return s; } private: IteratorT begin_; IteratorT end_; PolicyT policy_; }; template class EnumLogger { public: explicit EnumLogger(E e) : e_(e) {} friend std::ostream &operator<<(std::ostream &out, const EnumLogger &v) { using I = typename std::underlying_type::type; return out << static_cast(v.e_); } private: E e_; }; } // namespace detail // Log a range using "policy". For example: // // S2_LOG(INFO) << gtl::LogRange(start_pos, end_pos, gtl::LogMultiline()); // // The above example will print the range using newlines between // elements, enclosed in [] braces. template detail::RangeLogger LogRange( const IteratorT &begin, const IteratorT &end, const PolicyT &policy) { return gtl::detail::RangeLogger(begin, end, policy); } // Log a range. For example: // // S2_LOG(INFO) << gtl::LogRange(start_pos, end_pos); // // By default, Range() uses the LogShortUpTo100 policy: comma-space // separation, no newlines, and with limit of 100 items. template detail::RangeLogger LogRange( const IteratorT &begin, const IteratorT &end) { return gtl::LogRange(begin, end, LogDefault()); } // Log a container using "policy". For example: // // S2_LOG(INFO) << gtl::LogContainer(container, gtl::LogMultiline()); // // The above example will print the container using newlines between // elements, enclosed in [] braces. template auto LogContainer(const ContainerT &container, const PolicyT &policy) -> decltype(gtl::LogRange(container.begin(), container.end(), policy)) { return gtl::LogRange(container.begin(), container.end(), policy); } // Log a container. For example: // // S2_LOG(INFO) << gtl::LogContainer(container); // // By default, Container() uses the LogShortUpTo100 policy: comma-space // separation, no newlines, and with limit of 100 items. template auto LogContainer(const ContainerT &container) -> decltype(gtl::LogContainer(container, LogDefault())) { return gtl::LogContainer(container, LogDefault()); } // Log a (possibly scoped) enum. For example: // // enum class Color { kRed, kGreen, kBlue }; // S2_LOG(INFO) << gtl::LogEnum(kRed); template detail::EnumLogger LogEnum(E e) { static_assert(std::is_enum::value, "must be an enum"); return detail::EnumLogger(e); } } // namespace gtl #endif // S2_UTIL_GTL_CONTAINER_LOGGING_H_ s2/src/s2/util/gtl/legacy_random_shuffle.h0000644000176200001440000000544314530411473020155 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // gtl::legacy_random_shuffle is similar in API and behavior to // std::random_shuffle, which was removed in C++17. // // When built for Linux production targets using crosstool 18, // these APIs produce the same results as std::random_shuffle. // // Otherwise, the specification for these functions reverts to that // of std::random_shuffle as specified in C++11. In particular, // these functions do not promise to produce the same shuffle // sequences forever. // // These are deprecated, and intended to be used only for legacy // code that must move off std::random_shuffle simply because the // function is not part of C++17. #ifndef S2_UTIL_GTL_LEGACY_RANDOM_SHUFFLE_H_ #define S2_UTIL_GTL_LEGACY_RANDOM_SHUFFLE_H_ #include #include #include #include "absl/base/macros.h" namespace gtl { // Reorders the elements in the range `[begin, last)` randomly. The // random number generator `rnd` must be a function object returning a // randomly chosen value of type convertible to and from // `std::iterator_traits::difference_type` in the interval // `[0,n)` if invoked as `r(n)`. // // This function is deprecated. See the file comment above for // additional details. template ABSL_DEPRECATED("Use std::shuffle instead; see go/nors-legacy-api") void legacy_random_shuffle(const RandomIt begin, const RandomIt end, RandomFunc&& rnd) { auto size = std::distance(begin, end); for (decltype(size) i = 1; i < size; ++i) { // Loop invariant: elements below i are uniformly shuffled. std::iter_swap(begin + i, begin + rnd(i + 1)); } } // Reorders the elements in the range `[begin, last)` randomly. The // random number generator is `std::rand()`. // // This function is deprecated. See the file comment above for // additional details. template ABSL_DEPRECATED("Use std::shuffle instead; see go/nors-legacy-api") void legacy_random_shuffle(RandomIt begin, RandomIt end) { legacy_random_shuffle( begin, end, [](typename std::iterator_traits::difference_type i) { return std::rand() % i; }); } } // namespace gtl #endif // S2_UTIL_GTL_LEGACY_RANDOM_SHUFFLE_H_ s2/src/s2/util/gtl/dense_hash_set.h0000644000176200001440000003431714530411473016613 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // --- // // This is just a very thin wrapper over densehashtable.h, just // like sgi stl's stl_hash_set is a very thin wrapper over // stl_hashtable. // // This is more different from dense_hash_map than you might think, // because all iterators for sets are const (you obviously can't // change the key, and for sets there is no value). // // NOTE: this is exactly like sparse_hash_set.h, with the word // "sparse" replaced by "dense", except for the addition of // set_empty_key(). // // YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. // // Otherwise your program will die in mysterious ways. (Note if you // use the constructor that takes an InputIterator range, you pass in // the empty key in the constructor, rather than after. As a result, // this constructor differs from the standard STL version.) // // In other respects, we adhere mostly to the STL semantics for // hash-map. One important exception is that insert() may invalidate // iterators entirely -- STL semantics are that insert() may reorder // iterators, but they all still refer to something valid in the // hashtable. Not so for us. Likewise, insert() may invalidate // pointers into the hashtable. (Whether insert invalidates iterators // and pointers depends on whether it results in a hashtable resize, // but that's an implementation detail that may change in the future.) // On the plus side, delete() doesn't invalidate iterators or pointers // at all, or even change the ordering of elements. // // Also please note: // // 1) set_deleted_key(): // If you want to use erase() you must call set_deleted_key(), // in addition to set_empty_key(), after construction. // The deleted and empty keys must differ. // // 2) Keys equal to the empty key or deleted key (if any) cannot be // used as keys for find(), count(), insert(), etc. // // 3) min_load_factor(): // Setting the minimum load factor controls how aggressively the // table is shrunk when keys are erased. Setting it to 0.0 // guarantees that the hash table will never shrink. // // 4) resize(0): // When an item is deleted, its memory isn't freed right // away. This allows you to iterate over a hashtable, // and call erase(), without invalidating the iterator. // To force the memory to be freed, call resize(0). // For tr1 compatibility, this can also be called as rehash(0). // Roughly speaking: // (1) dense_hash_set: fastest, uses the most memory unless entries are small // (2) sparse_hash_set: slowest, uses the least memory // (3) hash_set / unordered_set (STL): in the middle // // Typically I use sparse_hash_set when I care about space and/or when // I need to save the hashtable on disk. I use hash_set otherwise. I // don't personally use dense_hash_set ever; some people use it for // small sets with lots of lookups. // // - dense_hash_set has, typically, about 78% memory overhead (if your // data takes up X bytes, the hash_set uses .78X more bytes in overhead). // - sparse_hash_set has about 4 bits overhead per entry. // - sparse_hash_set can be 3-7 times slower than the others for lookup and, // especially, inserts. See time_hash_map.cc for details. // // See /usr/(local/)?doc/sparsehash-*/dense_hash_set.html // for information about how to use this class. #ifndef S2_UTIL_GTL_DENSE_HASH_SET_H_ #define S2_UTIL_GTL_DENSE_HASH_SET_H_ #include #include #include #include #include #include #include "s2/base/port.h" #include "absl/base/macros.h" #include "s2/util/gtl/densehashtable.h" // IWYU pragma: export // Some files test for this symbol. #define S2__DENSE_HASH_SET_H_ namespace gtl { template , class EqualKey = std::equal_to, class Alloc = std::allocator > class dense_hash_set { private: // Apparently identity is not stl-standard, so we define our own struct Identity { typedef const Value& result_type; const Value& operator()(const Value& v) const { return v; } }; struct SetKey { void operator()(Value* value, const Value& new_key) const { *value = new_key; } }; // The actual data typedef dense_hashtable ht; ht rep; public: typedef typename ht::key_type key_type; typedef typename ht::value_type value_type; typedef typename ht::hasher hasher; typedef typename ht::key_equal key_equal; typedef Alloc allocator_type; typedef typename ht::size_type size_type; typedef typename ht::difference_type difference_type; typedef typename ht::const_pointer pointer; typedef typename ht::const_pointer const_pointer; typedef typename ht::const_reference reference; typedef typename ht::const_reference const_reference; typedef typename ht::const_iterator iterator; typedef typename ht::const_iterator const_iterator; typedef typename ht::const_local_iterator local_iterator; typedef typename ht::const_local_iterator const_local_iterator; // Iterator functions -- recall all iterators are const iterator begin() const { return rep.begin(); } iterator end() const { return rep.end(); } // These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements. ABSL_DEPRECATED( "This method is slated for removal. Please migrate to " "absl::flat_hash_set.") local_iterator begin(size_type i) const { return rep.begin(i); } ABSL_DEPRECATED( "This method is slated for removal. Please migrate to " "absl::flat_hash_set.") local_iterator end(size_type i) const { return rep.end(i); } // Accessor functions allocator_type get_allocator() const { return rep.get_allocator(); } hasher hash_funct() const { return rep.hash_funct(); } hasher hash_function() const { return hash_funct(); } // tr1 name key_equal key_eq() const { return rep.key_eq(); } // Constructors dense_hash_set() {} explicit dense_hash_set(size_type expected_max_items_in_table, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type()) : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { } template dense_hash_set(InputIterator f, InputIterator l, const key_type& empty_key_val, size_type expected_max_items_in_table = 0, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type()) : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { set_empty_key(empty_key_val); rep.insert(f, l); } // We use the default copy constructor // We use the default operator=() // We use the default destructor void clear() { rep.clear(); } // This clears the hash set without resizing it down to the minimum // bucket count, but rather keeps the number of buckets constant void clear_no_resize() { rep.clear_no_resize(); } void swap(dense_hash_set& hs) { rep.swap(hs.rep); } // Functions concerning size size_type size() const { return rep.size(); } size_type max_size() const { return rep.max_size(); } bool empty() const { return rep.empty(); } size_type bucket_count() const { return rep.bucket_count(); } ABSL_DEPRECATED( "This method is slated for removal. Please migrate to " "absl::flat_hash_set.") size_type max_bucket_count() const { return rep.max_bucket_count(); } // These are tr1 methods. bucket() is the bucket the key is or would be in. ABSL_DEPRECATED( "This method is slated for removal. Please migrate to " "absl::flat_hash_set.") size_type bucket_size(size_type i) const { return rep.bucket_size(i); } ABSL_DEPRECATED( "This method is slated for removal. Please migrate to " "absl::flat_hash_set.") size_type bucket(const key_type& key) const { return rep.bucket(key); } float load_factor() const { return size() * 1.0f / bucket_count(); } float max_load_factor() const { float shrink, grow; rep.get_resizing_parameters(&shrink, &grow); return grow; } void max_load_factor(float new_grow) { float shrink, grow; rep.get_resizing_parameters(&shrink, &grow); rep.set_resizing_parameters(shrink, new_grow); } // These aren't tr1 methods but perhaps ought to be. ABSL_DEPRECATED( "This method is slated for removal. Please migrate to " "absl::flat_hash_set.") float min_load_factor() const { float shrink, grow; rep.get_resizing_parameters(&shrink, &grow); return shrink; } void min_load_factor(float new_shrink) { float shrink, grow; rep.get_resizing_parameters(&shrink, &grow); rep.set_resizing_parameters(new_shrink, grow); } // Deprecated; use min_load_factor() or max_load_factor() instead. void set_resizing_parameters(float shrink, float grow) { rep.set_resizing_parameters(shrink, grow); } void resize(size_type hint) { rep.resize(hint); } void rehash(size_type hint) { resize(hint); } // the tr1 name // Lookup routines iterator find(const key_type& key) const { return rep.find(key); } size_type count(const key_type& key) const { return rep.count(key); } std::pair equal_range(const key_type& key) const { return rep.equal_range(key); } // Insertion routines std::pair insert(const value_type& obj) { std::pair p = rep.insert(obj); return std::pair(p.first, p.second); // const to non-const } std::pair insert(value_type&& obj) { // NOLINT std::pair p = rep.insert(std::move(obj)); return std::pair(p.first, p.second); // const to non-const } template void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } // Required for std::insert_iterator; the passed-in iterator is ignored. iterator insert(iterator, const value_type& obj) { return insert(obj).first; } iterator insert(iterator, value_type&& obj) { // NOLINT return insert(std::move(obj)).first; } // Unlike std::set, we cannot construct an element in place, as we do not have // a layer of indirection like std::set nodes. Therefore, emplace* methods do // not provide a performance advantage over insert + move. template std::pair emplace(Args&&... args) { return rep.insert(value_type(std::forward(args)...)); } // The passed-in const_iterator is ignored. template iterator emplace_hint(const_iterator, Args&&... args) { return rep.insert(value_type(std::forward(args)...)).first; } // Deletion and empty routines // THESE ARE NON-STANDARD! I make you specify an "impossible" key // value to identify deleted and empty buckets. You can change the // deleted key as time goes on, or get rid of it entirely to be insert-only. void set_empty_key(const key_type& key) { rep.set_empty_key(key); } void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } // These are standard size_type erase(const key_type& key) { return rep.erase(key); } void erase(iterator it) { rep.erase(it); } void erase(iterator f, iterator l) { rep.erase(f, l); } // Comparison bool operator==(const dense_hash_set& hs) const { return rep == hs.rep; } bool operator!=(const dense_hash_set& hs) const { return rep != hs.rep; } }; template inline void swap(dense_hash_set& hs1, dense_hash_set& hs2) { hs1.swap(hs2); } } #endif // S2_UTIL_GTL_DENSE_HASH_SET_H_ s2/src/s2/util/gtl/compact_array.h0000644000176200001440000005302514530411473016460 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // compact_array is a more memory-efficient implementation of std::vector. // It uses a pointer with an integer that stores both size and capacity. // // Implementation details: // // compact_array is a small-overhead STL-like Collection, but can only be used // for types with trivial copy, assign, and destructor. It only takes 16 // bytes for 64-bit binary (instead of the typical 24-bytes for vector). // Its size can grow to 2^24 (16M) elements. compact_array is memory // efficient when it is small, and CPU-efficient for growing a large array. // It does this by keeping both the size and capacity. When the size is less // than 64, the capacity is exactly as reserved, and grows linearly. Once the // size grows bigger than 64, the capacity grows exponentially. // // IMPORTANT: compact_array_base does not support a constructor and destructor // because it is designed to be used in "union". The replacements are // Construct() and Destruct() which MUST be called explicitly. If you need // constructor and destructor, use compact_array instead. // #ifndef S2_UTIL_GTL_COMPACT_ARRAY_H_ #define S2_UTIL_GTL_COMPACT_ARRAY_H_ #include #include #include #include #include #include #include // NOLINT #include #include #include #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "absl/base/macros.h" #include "s2/base/port.h" #include "absl/meta/type_traits.h" #include "s2/util/bits/bits.h" #include "s2/util/gtl/container_logging.h" namespace gtl { template > class compact_array_base { private: // The number of bits for the variable size_ and capacity_ static const int kSizeNumBits = 24; static const int kCapacityNumBits = 6; // Where capacity_ becomes an exponent (of 2) instead of the exact value static const int kExponentStart = (1 << kCapacityNumBits); // kMaxSize is the maximum size this array can grow. static const int kMaxSize = (1 << kSizeNumBits) - 1; #ifdef IS_LITTLE_ENDIAN uint32 size_ : kSizeNumBits; // number of valid items in the array uint32 capacity_ : kCapacityNumBits; // allocated array size uint32 is_exponent_ : 1; // whether capacity_ is an exponent // This object might share memory representation (ie. union) with // other data structures. We reserved the DO_NOT_USE (32nd bit in // little endian format) to be used as a tag. uint32 DO_NOT_USE : 1; #else uint32 DO_NOT_USE : 1; uint32 is_exponent_ : 1; uint32 capacity_ : kCapacityNumBits; uint32 size_ : kSizeNumBits; #endif // Opportunistically consider allowing inlined elements. // dd: this has to be disabled to pass CRAN checks, since there is a // (potentially) zero-length array that is not the last element of the class (so // this can't be silenced using __extension__) #if defined(_LP64) && defined(__GNUC__) && false // With 64-bit pointers, our approach is to form a 16-byte struct: // [5 bytes for size, capacity, is_exponent and is_inlined] // [3 bytes of padding or inlined elements] // [8 bytes of more inlined elements or a pointer] // We require 0-length arrays to take 0 bytes, and no strict aliasing. There // should be no compiler-inserted padding between any of our members. enum { kMaxInlinedBytes = 11, kInlined = kMaxInlinedBytes / sizeof(T), kActualInlinedBytes = kInlined * sizeof(T), kUnusedPaddingBytes = (kMaxInlinedBytes - kActualInlinedBytes) > 3 ? 3 : (kMaxInlinedBytes - kActualInlinedBytes) }; T* Array() { return IsInlined() ? InlinedSpace() : pointer_; } void SetArray(T* p) { static_assert(sizeof(*this) == 16, "size assumption"); static_assert(sizeof(this) == 8, "pointer size assumption"); is_inlined_ = false; pointer_ = p; } void SetInlined() { S2_DCHECK_LE(capacity(), kInlined); is_inlined_ = true; } T* InlinedSpace() { return reinterpret_cast(inlined_elements_); } bool is_inlined_; // If false, the last 8 bytes of *this are a pointer. // After is_inlined_, the next field may not be sufficiently aligned to store // an object of type T. Pad it out with (unaligned) chars. char unused_padding_[kUnusedPaddingBytes]; // inlined_elements_ stores the first N elements, potentially as few as zero. __extension__ char inlined_elements_[3 - kUnusedPaddingBytes]; // compact_array_base itself is at least as aligned as a T* because of the // T* member inside this union. The only reason to split inlined_elements_ // into two pieces is to have a place to put this T* member. union { T* pointer_; char more_inlined_elements_[sizeof(T*)]; }; #else enum { kInlined = 0, is_inlined_ = false }; T* Array() { return first_; } void SetArray(T* p) { first_ = p; } void SetInlined() { S2_LOG(FATAL); } T* InlinedSpace() { return nullptr; } // The pointer to the actual data array. T* first_; #endif bool IsInlined() const { return is_inlined_; } const T* ConstArray() const { return const_cast*>(this)->Array(); } typedef typename A::template rebind::other value_allocator_type; public: typedef T value_type; typedef A allocator_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef uint32 size_type; typedef ptrdiff_t difference_type; typedef value_type* iterator; typedef const value_type* const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; // Init() replace the default constructors; so it can be used in "union". // This means Init() must be called for every new compact_array_base void Init() noexcept { memset(this, 0, sizeof(*this)); } // Construct an array of size n and initialize the values to v. // Any old contents, if heap-allocated, will be leaked. void Construct(size_type n, const value_type& v = value_type()) { Init(); value_init(n, v); } // See 23.1.1/9 in the C++ standard for an explanation. template void Copy(Iterator first, Iterator last) { Init(); typedef typename std::is_integral::type Int; initialize(first, last, Int()); } void CopyFrom(const compact_array_base& v) { Init(); initialize(v.begin(), v.end(), std::false_type()); } compact_array_base& AssignFrom(const compact_array_base& v) { // Safe for self-assignment, which is rare. // Optimized to use existing allocated space. // Also to use assignment instead of copying where possible. if (size() < v.size()) { // grow reserve(v.size()); std::copy(v.begin(), v.begin() + size(), begin()); insert(end(), v.begin() + size(), v.end()); } else { // maybe shrink erase(begin() + v.size(), end()); std::copy(v.begin(), v.end(), begin()); } return *this; } // Deallocate the whole array. void Destruct() { if (!MayBeInlined() || Array() != InlinedSpace()) { value_allocator_type allocator; allocator.deallocate(Array(), capacity()); } Init(); } // Safe against self-swapping. // copying/destruction of compact_array_base is fairly trivial as the type // was designed to be useable in a C++98 union. void swap(compact_array_base& v) noexcept { compact_array_base tmp = *this; *this = v; v = tmp; } // The number of active items in the array. size_type size() const { return size_; } bool empty() const { return size() == 0; } // Maximum size that this data structure can hold. static size_type max_size() { return kMaxSize; } static bool MayBeInlined() { return kInlined > 0; } public: // Container interface (tables 65,66). iterator begin() { return Array(); } iterator end() { return Array() + size(); } const_iterator begin() const { return ConstArray(); } const_iterator end() const { return ConstArray() + size(); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(Array()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(ConstArray()); } private: // This Insert() is private because it might return the end(). iterator Insert(const_iterator p, const value_type& v) { if (size() >= kMaxSize) { throw std::length_error("compact_array size exceeded"); } iterator r = make_hole(p, 1); *r = v; return r; } public: // Sequence operations, table 67. iterator insert(const_iterator p, const value_type& v) { return Insert(p, v); } void insert(const_iterator p, size_type n, const value_type& v) { if (n + size() > kMaxSize) { throw std::length_error("compact_array size exceeded"); } value_insert(p, n, v); } // See 23.1.1/9 in the C++ standard for an explanation. template void insert(const_iterator p, Iterator first, Iterator last) { typedef typename std::is_integral::type Int; insert(p, first, last, Int()); } iterator erase(const_iterator p) { size_type index = p - begin(); erase_aux(p, 1); return begin() + index; } iterator erase(const_iterator first, const_iterator last) { size_type index = first - begin(); erase_aux(first, last - first); return begin() + index; } // clear just resets the size to 0, without deallocating the storage. // To deallocate the array, use Destruct(). void clear() { set_size(0); } reference front() { return begin()[0]; } const_reference front() const { return begin()[0]; } reference back() { return end()[-1]; } const_reference back() const { return end()[-1]; } void push_back(const value_type& v) { iterator p = make_hole(end(), 1); *p = v; } void pop_back() { erase_aux(end()-1, 1); } reference operator[](size_type n) { S2_DCHECK_LT(n, size_); return Array()[n]; } const_reference operator[](size_type n) const { S2_DCHECK_LT(n, size_); return ConstArray()[n]; } reference at(size_type n) { if (n >= size_) { throw std::out_of_range("compact_array index out of range"); } return Array()[n]; } const_reference at(size_type n) const { if (n >= size_) { throw std::out_of_range("compact_array index out of range"); } return ConstArray()[n]; } // Preallocate the array of size n. Only changes the capacity, not size. void reserve(int n) { reallocate(n); } size_type capacity() const { return is_exponent_ ? (1 << capacity_) : capacity_; } void resize(size_type n) { if (n > capacity()) reserve(n); // resize(n) is the only place in the class that exposes uninitialized // memory as live elements, so call a constructor for each element if // needed. // Destroying elements on shrinking resize isn't a concern, since the // value_type must be trivially destructible. if (n > size() && !absl::is_trivially_default_constructible::value) { // Increasing size would expose unconstructed elements. value_type *new_end = Array() + n; for (value_type *p = Array() + size(); p != new_end; ++p) new (p) value_type(); } set_size(n); } private: // Low-level helper functions. void set_size(size_type n) { S2_DCHECK_LE(n, capacity()); size_ = n; } void set_capacity(size_type n) { S2_DCHECK_LE(size(), n); is_exponent_ = (n >= kExponentStart); capacity_ = is_exponent_ ? Bits::Log2Ceiling(n) : n; // A tiny optimization here would be to set capacity_ to kInlined if // it's currently less. We don't bother, because doing so would require // changing the existing comments and unittests that say that, for small n, // capacity() will be exactly n if one calls reserve(n). S2_DCHECK(n == capacity() || n > kInlined); } // Make capacity n or more. Reallocate and copy data as necessary. void reallocate(size_type n) { size_type old_capacity = capacity(); if (n <= old_capacity) return; set_capacity(n); if (MayBeInlined()) { if (!IsInlined() && n <= kInlined) { SetInlined(); return; } else if (IsInlined()) { if (n > kInlined) { value_allocator_type allocator; value_type* new_array = allocator.allocate(capacity()); memcpy(new_array, InlinedSpace(), size() * sizeof(T)); SetArray(new_array); } return; } } value_allocator_type allocator; T* new_ptr = allocator.allocate(capacity()); // dd: this modification fixes a ASAN/UBSAN error, because // when old_capacity is 0, Array() is nullptr, which is UB // for memcpy. if (old_capacity > 0) { memcpy(new_ptr, Array(), old_capacity * sizeof(T)); allocator.deallocate(Array(), old_capacity); } SetArray(new_ptr); } value_type* lastp() { return Array() + size(); } void move(const value_type* first, const value_type* last, value_type* out) { memmove(out, first, (last - first) * sizeof(value_type)); } iterator make_hole(const_iterator p, size_type n) { iterator q = const_cast(p); if (n != 0) { size_type new_size = size() + n; size_type index = q - Array(); reallocate(new_size); q = Array() + index; move(q, Array() + new_size - n, q + n); set_size(new_size); } return q; } void erase_aux(const_iterator p, size_type n) { iterator q = const_cast(p); size_type new_size = size() - n; move(q + n, lastp(), q); reallocate(new_size); set_size(new_size); } private: // Helper functions for range/value. void value_init(size_type n, const value_type& v) { reserve(n); set_size(n); std::fill(Array(), lastp(), v); } template void range_init(InputIter first, InputIter last, std::input_iterator_tag) { for ( ; first != last; ++first) push_back(*first); } template void range_init(ForwIter first, ForwIter last, std::forward_iterator_tag) { size_type n = std::distance(first, last); reserve(n); set_size(n); std::copy(first, last, Array()); } template void initialize(Integer n, Integer v, std::true_type) { value_init(n, v); } template void initialize(Iterator first, Iterator last, std::false_type) { typedef typename std::iterator_traits::iterator_category Cat; range_init(first, last, Cat()); } void value_insert(const_iterator p, size_type n, const value_type& v) { if (n + size() > kMaxSize) { throw std::length_error("compact_array size exceeded"); } iterator hole = make_hole(p, n); std::fill(hole, hole + n, v); } template void range_insert(const_iterator p, InputIter first, InputIter last, std::input_iterator_tag) { size_type pos = p - begin(); size_type old_size = size(); for (; first != last; ++first) push_back(*first); std::rotate(begin() + pos, begin() + old_size, end()); } template void range_insert(const_iterator p, ForwIter first, ForwIter last, std::forward_iterator_tag) { size_type n = std::distance(first, last); if (n + size() > kMaxSize) { throw std::length_error("compact_array size exceeded"); } std::copy(first, last, make_hole(p, n)); } template void insert(const_iterator p, Integer n, Integer v, std::true_type) { value_insert(p, n, v); } template void insert(const_iterator p, Iterator first, Iterator last, std::false_type) { typedef typename std::iterator_traits::iterator_category Cat; range_insert(p, first, last, Cat()); } static_assert(absl::is_trivially_copy_constructible::value && absl::is_trivially_copy_assignable::value && absl::is_trivially_destructible::value, "Requires trivial copy, assignment, and destructor."); }; // Allocates storage for constants in compact_array_base template const int compact_array_base::kSizeNumBits; template const int compact_array_base::kCapacityNumBits; template const int compact_array_base::kMaxSize; template const int compact_array_base::kExponentStart; // compact_array: Wrapper for compact_array_base that provides the // constructors and destructor. template > class compact_array : public compact_array_base { private: typedef compact_array_base Base; public: typedef typename Base::value_type value_type; typedef typename Base::allocator_type allocator_type; typedef typename Base::pointer pointer; typedef typename Base::const_pointer const_pointer; typedef typename Base::reference reference; typedef typename Base::const_reference const_reference; typedef typename Base::size_type size_type; typedef typename Base::iterator iterator; typedef typename Base::const_iterator const_iterator; typedef typename Base::reverse_iterator reverse_iterator; typedef typename Base::const_reverse_iterator const_reverse_iterator; compact_array() noexcept(noexcept(std::declval().Init())) { Base::Init(); } explicit compact_array(size_type n) { Base::Construct(n, value_type()); } compact_array(size_type n, const value_type& v) { Base::Construct(n, v); } // See 23.1.1/9 in the C++ standard for an explanation. template compact_array(Iterator first, Iterator last) { Base::Copy(first, last); } compact_array(const compact_array& v) { Base::CopyFrom(v); } compact_array(compact_array&& v) noexcept( noexcept(compact_array()) && noexcept(std::declval().swap(v))) : compact_array() { Base::swap(v); } compact_array& operator=(const compact_array& v) { Base::AssignFrom(v); return *this; } compact_array& operator=(compact_array&& v) { // swap is only right here because the objects are trivially destructible // and thus there are no side effects on their destructor. // Otherwise we must destroy the objects on `this`. Base::swap(v); return *this; } ~compact_array() { Base::Destruct(); } }; // Comparison operators template bool operator==(const compact_array& x, const compact_array& y) { return x.size() == y.size() && std::equal(x.begin(), x.end(), y.begin()); } template bool operator!=(const compact_array& x, const compact_array& y) { return !(x == y); } template bool operator<(const compact_array& x, const compact_array& y) { return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end()); } template bool operator>(const compact_array& x, const compact_array& y) { return y < x; } template bool operator<=(const compact_array& x, const compact_array& y) { return !(y < x); } template bool operator>=(const compact_array& x, const compact_array& y) { return !(x < y); } // Swap template inline void swap(compact_array& x, compact_array& y) { x.swap(y); } namespace compact_array_internal { struct LogArray : public gtl::LogLegacyUpTo100 { template void Log(std::ostream& out, const ElementT& element) const { // NOLINT out << element; } void Log(std::ostream& out, int8 c) const { // NOLINT out << static_cast(c); } void Log(std::ostream& out, uint8 c) const { // NOLINT out << static_cast(c); } void LogOpening(std::ostream& out) const { out << "["; } // NOLINT void LogClosing(std::ostream& out) const { out << "]"; } // NOLINT }; } // namespace compact_array_internal // Output operator for compact_array. Requires that T has an // operator<< for std::ostream. Note that // compact_array_internal::LogArray ensures that "signed char" and // "unsigned char" types print as integers. template std::ostream& operator<<(std::ostream& out, const compact_array& array) { gtl::LogRangeToStream(out, array.begin(), array.end(), compact_array_internal::LogArray()); return out; } } // namespace gtl #endif // S2_UTIL_GTL_COMPACT_ARRAY_H_ s2/src/s2/util/gtl/densehashtable.h0000644000176200001440000016244614530411473016616 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // --- // // A dense hashtable is a particular implementation of // a hashtable: one that is meant to minimize memory allocation. // It does this by using an array to store all the data. We // steal a value from the key space to indicate "empty" array // elements (ie indices where no item lives) and another to indicate // "deleted" elements. // // (Note it is possible to change the value of the delete key // on the fly; you can even remove it, though after that point // the hashtable is insert_only until you set it again. The empty // value however can't be changed.) // // To minimize allocation and pointer overhead, we use internal // probing, in which the hashtable is a single table, and collisions // are resolved by trying to insert again in another bucket. The // most cache-efficient internal probing schemes are linear probing // (which suffers, alas, from clumping) and quadratic probing, which // is what we implement by default. // // Type requirements: value_type is required to be Move Constructible // and Default Constructible. It is not required to be (and commonly // isn't) Assignable. // // You probably shouldn't use this code directly. Use dense_hash_map<> // or dense_hash_set<> instead. // You can change the following below: // HT_OCCUPANCY_PCT -- how full before we double size // HT_EMPTY_PCT -- how empty before we halve size // HT_MIN_BUCKETS -- default smallest bucket size // // You can also change enlarge_factor (which defaults to // HT_OCCUPANCY_PCT), and shrink_factor (which defaults to // HT_EMPTY_PCT) with set_resizing_parameters(). // // How to decide what values to use? // shrink_factor's default of .4 * OCCUPANCY_PCT, is probably good. // HT_MIN_BUCKETS is probably unnecessary since you can specify // (indirectly) the starting number of buckets at construct-time. // For enlarge_factor, you can use this chart to try to trade-off // expected lookup time to the space taken up. By default, this // code uses quadratic probing, though you can change it to linear // via JUMP_ below if you really want to. // // From // L = N / M, // where N is the number of data items in the table and M is the table size. // NUMBER OF PROBES / LOOKUP Successful Unsuccessful // Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) // Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)^2]/2 // // -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 // QUADRATIC COLLISION RES. // probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 // probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 // LINEAR COLLISION RES. // probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 // probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0 #ifndef S2_UTIL_GTL_DENSEHASHTABLE_H_ #define S2_UTIL_GTL_DENSEHASHTABLE_H_ #include #include #include // for FILE, fwrite, fread #include // For swap(), eg #include #include // For iterator tags #include // for numeric_limits #include // For uninitialized_fill #include #include #include #include #include #include "s2/util/gtl/hashtable_common.h" #include "s2/base/port.h" #include // For length_error namespace gtl { // Some files test for this symbol. #define S2__DENSEHASHTABLE_H_ // The probing method // Linear probing // #define JUMP_(key, num_probes) ( 1 ) // Quadratic probing #define JUMP_(key, num_probes) (num_probes) // The weird mod in the offset is entirely to quiet compiler warnings // as is the cast to int after doing the "x mod 256" #define PUT_(take_from, offset) do { \ if (putc(static_cast(offset >= sizeof(take_from)*8) \ ? 0 : ((take_from) >> (offset)) % 256, fp) \ == EOF) \ return false; \ } while (0) #define GET_(add_to, offset) do { \ if ((x=getc(fp)) == EOF) \ return false; \ else if (offset >= sizeof(add_to) * 8) \ assert(x == 0); /* otherwise it's too big for us to represent */ \ else \ add_to |= (static_cast(x) << ((offset) % (sizeof(add_to)*8))); \ } while (0) // Hashtable class, used to implement the hashed associative containers // hash_set and hash_map. // Value: what is stored in the table (each bucket is a Value). // Key: something in a 1-to-1 correspondence to a Value, that can be used // to search for a Value in the table (find() takes a Key). // HashFcn: Takes a Key and returns an integer, the more unique the better. // ExtractKey: given a Value, returns the unique Key associated with it. // Must have a result_type enum indicating the return type of // operator(). // SetKey: given a Value* and a Key, modifies the value such that // ExtractKey(value) == key. We guarantee this is only called // with key == deleted_key or key == empty_key. // EqualKey: Given two Keys, says whether they are the same (that is, // if they are both associated with the same Value). // Alloc: STL allocator to use to allocate memory. template class dense_hashtable; template struct dense_hashtable_const_iterator; // We're just an array, but we need to skip over empty and deleted elements template struct dense_hashtable_iterator { private: typedef typename A::template rebind::other value_alloc_type; public: typedef dense_hashtable_iterator iterator; typedef dense_hashtable_const_iterator const_iterator; typedef std::forward_iterator_tag iterator_category; // very little defined! typedef V value_type; typedef typename value_alloc_type::difference_type difference_type; typedef typename value_alloc_type::size_type size_type; typedef typename value_alloc_type::reference reference; typedef typename value_alloc_type::pointer pointer; // "Real" constructor and default constructor dense_hashtable_iterator( const dense_hashtable *h, pointer it, pointer it_end, bool advance) : ht(h), pos(it), end(it_end) { if (advance) advance_past_empty_and_deleted(); } dense_hashtable_iterator() { } // The default destructor is fine; we don't define one // The default operator= is fine; we don't define one // Happy dereferencer reference operator*() const { return *pos; } pointer operator->() const { return &(operator*()); } // Arithmetic. The only hard part is making sure that // we're not on an empty or marked-deleted array element void advance_past_empty_and_deleted() { while (pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) ) ++pos; } iterator& operator++() { assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; } iterator operator++(int /*unused*/) { auto tmp(*this); ++*this; return tmp; } // Comparison. bool operator==(const iterator& it) const { return pos == it.pos; } bool operator!=(const iterator& it) const { return pos != it.pos; } // The actual data const dense_hashtable *ht; pointer pos, end; }; // Now do it all again, but with const-ness! template struct dense_hashtable_const_iterator { private: typedef typename A::template rebind::other value_alloc_type; public: typedef dense_hashtable_iterator iterator; typedef dense_hashtable_const_iterator const_iterator; typedef std::forward_iterator_tag iterator_category; // very little defined! typedef V value_type; typedef typename value_alloc_type::difference_type difference_type; typedef typename value_alloc_type::size_type size_type; typedef typename value_alloc_type::const_reference reference; typedef typename value_alloc_type::const_pointer pointer; // "Real" constructor and default constructor dense_hashtable_const_iterator( const dense_hashtable *h, pointer it, pointer it_end, bool advance) : ht(h), pos(it), end(it_end) { if (advance) advance_past_empty_and_deleted(); } dense_hashtable_const_iterator() : ht(nullptr), pos(pointer()), end(pointer()) { } // This lets us convert regular iterators to const iterators dense_hashtable_const_iterator(const iterator &it) : ht(it.ht), pos(it.pos), end(it.end) { } // The default destructor is fine; we don't define one // The default operator= is fine; we don't define one // Happy dereferencer reference operator*() const { return *pos; } pointer operator->() const { return &(operator*()); } // Arithmetic. The only hard part is making sure that // we're not on an empty or marked-deleted array element void advance_past_empty_and_deleted() { while (pos != end && (ht->test_empty(*this) || ht->test_deleted(*this))) ++pos; } const_iterator& operator++() { assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; } const_iterator operator++(int /*unused*/) { auto tmp(*this); ++*this; return tmp; } // Comparison. bool operator==(const const_iterator& it) const { return pos == it.pos; } bool operator!=(const const_iterator& it) const { return pos != it.pos; } // The actual data const dense_hashtable *ht; pointer pos, end; }; template class dense_hashtable { private: typedef typename Alloc::template rebind::other value_alloc_type; public: typedef Key key_type; typedef Value value_type; typedef HashFcn hasher; typedef EqualKey key_equal; typedef Alloc allocator_type; typedef typename value_alloc_type::size_type size_type; typedef typename value_alloc_type::difference_type difference_type; typedef typename value_alloc_type::reference reference; typedef typename value_alloc_type::const_reference const_reference; typedef typename value_alloc_type::pointer pointer; typedef typename value_alloc_type::const_pointer const_pointer; typedef dense_hashtable_iterator iterator; typedef dense_hashtable_const_iterator const_iterator; // These come from tr1. For us they're the same as regular iterators. typedef iterator local_iterator; typedef const_iterator const_local_iterator; // How full we let the table get before we resize, by default. // Knuth says .8 is good -- higher causes us to probe too much, // though it saves memory. static const int HT_OCCUPANCY_PCT; // defined at the bottom of this file // How empty we let the table get before we resize lower, by default. // (0.0 means never resize lower.) // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing static const int HT_EMPTY_PCT; // defined at the bottom of this file // Minimum size we're willing to let hashtables be. // Must be a power of two, and at least 4. // Note, however, that for a given hashtable, the initial size is a // function of the first constructor arg, and may be >HT_MIN_BUCKETS. static const size_type HT_MIN_BUCKETS = 4; // By default, if you don't specify a hashtable size at // construction-time, we use this size. Must be a power of two, and // at least HT_MIN_BUCKETS. static const size_type HT_DEFAULT_STARTING_BUCKETS = 32; // ITERATOR FUNCTIONS iterator begin() { return iterator(this, table, table + num_buckets, true); } iterator end() { return iterator(this, table + num_buckets, table + num_buckets, true); } const_iterator begin() const { return const_iterator(this, table, table+num_buckets, true); } const_iterator end() const { return const_iterator(this, table + num_buckets, table+num_buckets, true); } // These come from tr1 unordered_map. They iterate over 'bucket' n. // We'll just consider bucket n to be the n-th element of the table. local_iterator begin(size_type i) { return local_iterator(this, table + i, table + i+1, false); } local_iterator end(size_type i) { local_iterator it = begin(i); if (!test_empty(i) && !test_deleted(i)) ++it; return it; } const_local_iterator begin(size_type i) const { return const_local_iterator(this, table + i, table + i+1, false); } const_local_iterator end(size_type i) const { const_local_iterator it = begin(i); if (!test_empty(i) && !test_deleted(i)) ++it; return it; } // ACCESSOR FUNCTIONS for the things we templatize on, basically hasher hash_funct() const { return settings; } key_equal key_eq() const { return key_info; } value_alloc_type get_allocator() const { return key_info; } // Accessor function for statistics gathering. int num_table_copies() const { return settings.num_ht_copies(); } private: // Annoyingly, we can't copy values around, because they might have // const components (they're probably std::pair). We use // explicit destructor invocation and placement new to get around // this. Arg. static void set_value(pointer dst, const_reference src) { dst->~value_type(); // delete the old value, if any new(dst) value_type(src); } static void set_value(pointer dst, value_type&& src) { // NOLINT dst->~value_type(); new(dst) value_type(std::move(src)); } void destroy_buckets(size_type first, size_type last) { for ( ; first != last; ++first) table[first].~value_type(); } // DELETE HELPER FUNCTIONS // This lets the user describe a key that will indicate deleted // table entries. This key should be an "impossible" entry -- // if you try to insert it for real, you won't be able to retrieve it! // (NB: while you pass in an entire value, only the key part is looked // at. This is just because I don't know how to assign just a key.) private: // Gets rid of any deleted entries we have. void squash_deleted() { if (num_deleted > 0) { rebucket(settings.min_buckets(size(), num_buckets)); } assert(num_deleted == 0); } // Test if the given key is the deleted indicator. Requires // num_deleted > 0, for correctness of read(), and because that // guarantees that key_info.delkey is valid. bool test_deleted_key(const key_type& key) const { assert(num_deleted > 0); return equals(key_info.delkey, key); } public: void set_deleted_key(const key_type &key) { // the empty indicator (if specified) and the deleted indicator // must be different assert((!settings.use_empty() || !equals(key, key_info.empty)) && "Passed the empty-key to set_deleted_key"); // It's only safe to change what "deleted" means if we purge deleted guys squash_deleted(); settings.set_use_deleted(true); key_info.delkey = key; } key_type deleted_key() const { assert(settings.use_deleted() && "Must set deleted key before calling deleted_key"); return key_info.delkey; } // These are public so the iterators can use them // True if the item at position bucknum is "deleted" marker bool test_deleted(size_type bucknum) const { // Invariant: !use_deleted() implies num_deleted is 0. assert(settings.use_deleted() || num_deleted == 0); return num_deleted > 0 && test_deleted_key(get_key(table[bucknum])); } bool test_deleted(const iterator &it) const { // Invariant: !use_deleted() implies num_deleted is 0. assert(settings.use_deleted() || num_deleted == 0); return num_deleted > 0 && test_deleted_key(get_key(*it)); } bool test_deleted(const const_iterator &it) const { // Invariant: !use_deleted() implies num_deleted is 0. assert(settings.use_deleted() || num_deleted == 0); return num_deleted > 0 && test_deleted_key(get_key(*it)); } private: void check_use_deleted(const char* caller) { (void)caller; // could log it if the assert failed assert(settings.use_deleted()); } // Write the deleted key to the position specified. // Requires: !test_deleted(it) void set_deleted(iterator &it) { check_use_deleted("set_deleted()"); assert(!test_deleted(it)); // &* converts from iterator to value-type. set_key(&(*it), key_info.delkey); } // We also allow to set/clear the deleted bit on a const iterator. // We allow a const_iterator for the same reason you can delete a // const pointer: it's convenient, and semantically you can't use // 'it' after it's been deleted anyway, so its const-ness doesn't // really matter. // Requires: !test_deleted(it) void set_deleted(const_iterator &it) { check_use_deleted("set_deleted()"); assert(!test_deleted(it)); set_key(const_cast(&(*it)), key_info.delkey); } // EMPTY HELPER FUNCTIONS // This lets the user describe a key that will indicate empty (unused) // table entries. This key should be an "impossible" entry -- // if you try to insert it for real, you won't be able to retrieve it! // (NB: while you pass in an entire value, only the key part is looked // at. This is just because I don't know how to assign just a key.) public: // These are public so the iterators can use them // True if the item at position bucknum is "empty" marker bool test_empty(size_type bucknum) const { assert(settings.use_empty()); // we always need to know what's empty! return equals(key_info.empty, get_key(table[bucknum])); } bool test_empty(const iterator &it) const { assert(settings.use_empty()); // we always need to know what's empty! return equals(key_info.empty, get_key(*it)); } bool test_empty(const const_iterator &it) const { assert(settings.use_empty()); // we always need to know what's empty! return equals(key_info.empty, get_key(*it)); } private: bool test_empty(size_type bucknum, const_pointer table) const { assert(settings.use_empty()); return equals(key_info.empty, get_key(table[bucknum])); } void fill_range_with_empty(pointer table_start, pointer table_end) { for (; table_start != table_end; ++table_start) { new (table_start) value_type(); set_key(table_start, key_info.empty); } } public: // TODO(user): change all callers of this to pass in a key instead, // and take a const key_type instead of const value_type. void set_empty_key(const_reference val) { // Once you set the empty key, you can't change it assert(!settings.use_empty() && "Calling set_empty_key multiple times"); // The deleted indicator (if specified) and the empty indicator // must be different. const key_type& key = get_key(val); assert((!settings.use_deleted() || !equals(key, key_info.delkey)) && "Setting the empty key the same as the deleted key"); settings.set_use_empty(true); key_info.empty.~key_type(); new (&key_info.empty) key_type(key); assert(!table); // must set before first use // num_buckets was set in constructor even though table was nullptr table = get_internal_allocator().allocate(num_buckets); fill_range_with_empty(table, table + num_buckets); } // TODO(user): this should return the key by const reference. value_type empty_key() const { assert(settings.use_empty()); value_type ret = value_type(); set_key(&ret, key_info.empty); return ret; } // FUNCTIONS CONCERNING SIZE public: size_type size() const { return num_elements - num_deleted; } size_type max_size() const { return get_allocator().max_size(); } bool empty() const { return size() == 0; } size_type bucket_count() const { return num_buckets; } size_type max_bucket_count() const { return max_size(); } size_type nonempty_bucket_count() const { return num_elements; } // These are tr1 methods. Their idea of 'bucket' doesn't map well to // what we do. We just say every bucket has 0 or 1 items in it. size_type bucket_size(size_type i) const { return begin(i) == end(i) ? 0 : 1; } private: // Because of the above, size_type(-1) is never legal; use it for errors static const size_type ILLEGAL_BUCKET = size_type(-1); // Used after a string of deletes. Returns true if we actually shrunk. // TODO(user): take a delta so we can take into account inserts // done after shrinking. Maybe make part of the Settings class? bool maybe_shrink() { assert(num_elements >= num_deleted); assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two assert(bucket_count() >= HT_MIN_BUCKETS); bool retval = false; // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, // we'll never shrink until you get relatively big, and we'll never // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something // like "dense_hash_set x; x.insert(4); x.erase(4);" will // shrink us down to HT_MIN_BUCKETS buckets, which is too small. const size_type num_remain = num_elements - num_deleted; const size_type shrink_threshold = settings.shrink_threshold(); if (shrink_threshold > 0 && num_remain < shrink_threshold && bucket_count() > HT_DEFAULT_STARTING_BUCKETS) { const float shrink_factor = settings.shrink_factor(); size_type sz = bucket_count() / 2; // find how much we should shrink while (sz > HT_DEFAULT_STARTING_BUCKETS && num_remain < sz * shrink_factor) { sz /= 2; // stay a power of 2 } rebucket(sz); retval = true; } settings.set_consider_shrink(false); // because we just considered it return retval; } // We'll let you resize a hashtable -- though this makes us copy all! // When you resize, you say, "make it big enough for this many more elements" // Returns true if we actually resized, false if size was already ok. bool resize_delta(size_type delta) { bool did_resize = false; if (settings.consider_shrink()) { // see if lots of deletes happened if (maybe_shrink()) did_resize = true; } if (num_elements >= std::numeric_limits::max() - delta) { throw std::length_error("resize overflow"); } assert(settings.enlarge_threshold() < bucket_count()); // Check if our work is done. if (bucket_count() >= HT_MIN_BUCKETS && num_elements + delta <= settings.enlarge_threshold()) { return did_resize; } // Sometimes, we need to resize just to get rid of all the // "deleted" buckets that are clogging up the hashtable. So when // deciding whether to resize, count the deleted buckets (which // are currently taking up room). But later, when we decide what // size to resize to, *don't* count deleted buckets, since they // get discarded during the resize. const size_type needed_size = settings.min_buckets(num_elements + delta, 0); if (needed_size <= bucket_count()) // we have enough buckets return did_resize; // We will rebucket. size_type resize_to = settings.min_buckets(num_elements - num_deleted + delta, bucket_count()); if (resize_to < needed_size) { // This situation means that we have enough deleted elements, // that once we purge them, we won't actually have needed to // grow. But we may want to grow anyway: if we just purge one // element, say, we'll have to grow anyway next time we // insert. Might as well grow now, since we're already going // through the trouble of rebucketing in order to purge the // deleted elements. (Safety note: Can resize_to * 2 overflow? No. // The output of min_buckets() is always a power of two, so resize_to // and needed_size are powers of two. That plus resize_to < needed_size // proves that overflow isn't a concern.) const size_type target = static_cast(settings.shrink_size(resize_to*2)); if (num_elements - num_deleted + delta >= target) { // Good, we won't be below the shrink threshhold even if we double. resize_to *= 2; } } rebucket(resize_to); return true; } // We require table be non-null and empty before calling this. void resize_table(size_type old_size, size_type new_size) { get_internal_allocator().deallocate(table, old_size); table = get_internal_allocator().allocate(new_size); } // Copy (or, if Iter is a move_iterator, move) the elements from // [src_first, src_last) into dest_table, which we assume has size // dest_bucket_count and has been initialized to the empty key. template void copy_elements(Iter src_first, Iter src_last, pointer dest_table, size_type dest_bucket_count) { assert((dest_bucket_count & (dest_bucket_count - 1)) == 0); // a power of 2 // We use a normal iterator to get non-deleted bcks from ht // We could use insert() here, but since we know there are // no duplicates and no deleted items, we can be more efficient for (; src_first != src_last; ++src_first) { size_type num_probes = 0; // how many times we've probed size_type bucknum; const size_type bucket_count_minus_one = dest_bucket_count - 1; for (bucknum = hash(get_key(*src_first)) & bucket_count_minus_one; !test_empty(bucknum, dest_table); // not empty bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) { ++num_probes; assert(num_probes < dest_bucket_count && "Hashtable is full: an error in key_equal<> or hash<>"); } // Copies or moves the value into dest_table. set_value(&dest_table[bucknum], *src_first); } } // Used to actually do the rehashing when we grow/shrink a hashtable void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted) { size_type size = ht.size(); // clear_to_size() sets ht.size() to 0. clear_to_size(settings.min_buckets(ht.size(), min_buckets_wanted)); copy_elements(ht.begin(), ht.end(), table, bucket_count()); num_elements = size; settings.inc_num_ht_copies(); } // Rebuckets and resizes the hashtable. Gets rid of any deleted entries. void rebucket(size_type new_num_buckets) { if (table == nullptr) { // When we eventually allocate the table, it will have this many buckets. num_buckets = new_num_buckets; return; } assert(settings.use_empty()); assert((new_num_buckets & (new_num_buckets - 1)) == 0); // a power of two // If settings.shrink_factor() is zero then we must not shrink. assert(settings.shrink_factor() > 0 || new_num_buckets >= num_buckets); pointer new_table = get_internal_allocator().allocate(new_num_buckets); fill_range_with_empty(new_table, new_table + new_num_buckets); copy_elements(std::make_move_iterator(begin()), std::make_move_iterator(end()), new_table, new_num_buckets); destroy_buckets(0, num_buckets); // Destroy table's elements. get_internal_allocator().deallocate(table, num_buckets); table = new_table; num_buckets = new_num_buckets; assert(num_elements >= num_deleted); num_elements -= num_deleted; num_deleted = 0; settings.reset_thresholds(bucket_count()); settings.inc_num_ht_copies(); } // Required by the spec for hashed associative container public: // Though the docs say this should be num_buckets, I think it's much // more useful as num_elements. As a special feature, calling with // req_elements==0 will cause us to shrink if we can, saving space. void resize(size_type req_elements) { // resize to this or larger if ( settings.consider_shrink() || req_elements == 0 ) maybe_shrink(); if ( req_elements > num_elements ) resize_delta(req_elements - num_elements); } // Get and change the value of shrink_factor and enlarge_factor. The // description at the beginning of this file explains how to choose // the values. Setting the shrink parameter to 0.0 ensures that the // table never shrinks. void get_resizing_parameters(float* shrink, float* grow) const { *shrink = settings.shrink_factor(); *grow = settings.enlarge_factor(); } void set_resizing_parameters(float shrink, float grow) { settings.set_resizing_parameters(shrink, grow); settings.reset_thresholds(bucket_count()); } // CONSTRUCTORS -- as required by the specs, we take a size, // but also let you specify a hashfunction, key comparator, // and key extractor. We also define a copy constructor and =. // DESTRUCTOR -- needs to free the table explicit dense_hashtable(size_type expected_max_items_in_table = 0, const HashFcn& hf = HashFcn(), const EqualKey& eql = EqualKey(), const ExtractKey& ext = ExtractKey(), const SetKey& set = SetKey(), const Alloc& alloc = Alloc()) : settings(hf), key_info(ext, set, eql, value_alloc_type(alloc)), num_deleted(0), num_elements(0), num_buckets(expected_max_items_in_table == 0 ? HT_DEFAULT_STARTING_BUCKETS : settings.min_buckets(expected_max_items_in_table, 0)), table(nullptr) { // table is nullptr until the empty key is set. However, we set num_buckets // here so we know how much space to allocate once the empty key is set. settings.reset_thresholds(bucket_count()); } // As a convenience for resize(), we allow an optional second argument // which lets you make this new hashtable a different size than ht dense_hashtable(const dense_hashtable& ht, size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) : settings(ht.settings), key_info(ht.key_info.as_extract_key(), ht.key_info.as_set_key(), ht.key_info.as_equal_key(), value_alloc_type( std::allocator_traits:: select_on_container_copy_construction( ht.key_info.as_value_alloc()))), num_deleted(0), num_elements(0), num_buckets(0), table(nullptr) { key_info.delkey = ht.key_info.delkey; key_info.empty = ht.key_info.empty; if (!ht.settings.use_empty()) { // If use_empty isn't set, copy_from will crash, so we do our own copying. assert(ht.empty()); num_buckets = settings.min_buckets(ht.size(), min_buckets_wanted); settings.reset_thresholds(bucket_count()); return; } settings.reset_thresholds(bucket_count()); copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries } dense_hashtable& operator=(const dense_hashtable& ht) { if (&ht == this) return *this; // don't copy onto ourselves settings = ht.settings; key_info.as_extract_key() = ht.key_info.as_extract_key(); key_info.as_set_key() = ht.key_info.as_set_key(); key_info.as_equal_key() = ht.key_info.as_equal_key(); if (std::allocator_traits< value_alloc_type>::propagate_on_container_copy_assignment::value) { // If we're about to overwrite our allocator, we need to free all // memory using our old allocator. if (key_info.as_value_alloc() != ht.key_info.as_value_alloc()) { destroy_table(); } static_cast(key_info) = static_cast(ht.key_info); } key_info.empty = ht.key_info.empty; key_info.delkey = ht.key_info.delkey; if (ht.settings.use_empty()) { // copy_from() calls clear and sets num_deleted to 0 too copy_from(ht, HT_MIN_BUCKETS); } else { assert(ht.empty()); destroy_table(); } // we purposefully don't copy the allocator, which may not be copyable return *this; } dense_hashtable(dense_hashtable&& ht) : settings(std::move(ht.settings)), key_info(std::move(ht.key_info)), num_deleted(ht.num_deleted), num_elements(ht.num_elements), num_buckets(ht.num_buckets), table(ht.table) { ht.num_deleted = 0; ht.num_elements = 0; ht.table = nullptr; ht.num_buckets = HT_DEFAULT_STARTING_BUCKETS; ht.settings.set_use_empty(false); ht.settings.set_use_deleted(false); } dense_hashtable& operator=(dense_hashtable&& ht) { if (&ht == this) return *this; // don't move onto ourselves const bool can_move_table = std::allocator_traits< Alloc>::propagate_on_container_move_assignment::value || key_info.as_value_alloc() == ht.key_info.as_value_alloc(); // First, deallocate with this's allocator. destroy_table(); if (std::allocator_traits< value_alloc_type>::propagate_on_container_move_assignment::value) { // This moves the allocator. key_info = std::move(ht.key_info); } else { // Move all other base classes of key_info from ht, but don't move the // allocator. key_info.as_extract_key() = std::move(ht.key_info.as_extract_key()); key_info.as_set_key() = std::move(ht.key_info.as_set_key()); key_info.as_equal_key() = std::move(ht.key_info.as_equal_key()); key_info.delkey = std::move(ht.key_info.delkey); key_info.empty = std::move(ht.key_info.empty); } settings = std::move(ht.settings); num_deleted = ht.num_deleted; ht.num_deleted = 0; num_elements = ht.num_elements; ht.num_elements = 0; num_buckets = ht.num_buckets; ht.num_buckets = HT_DEFAULT_STARTING_BUCKETS; ht.settings.set_use_empty(false); ht.settings.set_use_deleted(false); if (can_move_table) { // We can transfer ownership of the table from ht to this because either // we're propagating the allocator or ht's allocator is equal to this's. table = ht.table; ht.table = nullptr; } else if (ht.table) { // We can't transfer ownership of any memory from ht to this, so the // best we can do is move element-by-element. table = get_internal_allocator().allocate(num_buckets); for (size_type i = 0; i < num_buckets; ++i) { new(table + i) Value(std::move(ht.table[i])); } ht.destroy_table(); } return *this; } ~dense_hashtable() { destroy_table(); } // Many STL algorithms use swap instead of copy constructors void swap(dense_hashtable& ht) { if (this == &ht) return; // swap with self. using std::swap; swap(settings, ht.settings); // Swap everything in key_info but the allocator. swap(key_info.as_extract_key(), ht.key_info.as_extract_key()); swap(key_info.as_set_key(), ht.key_info.as_set_key()); swap(key_info.as_equal_key(), ht.key_info.as_equal_key()); if (std::allocator_traits< value_alloc_type>::propagate_on_container_swap::value) { swap(static_cast(key_info), static_cast(ht.key_info)); } else { // Swapping when allocators are unequal and // propagate_on_container_swap is false is undefined behavior. S2_CHECK(key_info.as_value_alloc() == ht.key_info.as_value_alloc()); } swap(key_info.empty, ht.key_info.empty); swap(key_info.delkey, ht.key_info.delkey); swap(num_deleted, ht.num_deleted); swap(num_elements, ht.num_elements); swap(num_buckets, ht.num_buckets); swap(table, ht.table); } private: void destroy_table() { if (table) { destroy_buckets(0, num_buckets); get_internal_allocator().deallocate(table, num_buckets); table = nullptr; } } void clear_to_size(size_type new_num_buckets) { if (!table) { table = get_internal_allocator().allocate(new_num_buckets); } else { destroy_buckets(0, num_buckets); if (new_num_buckets != num_buckets) { // resize, if necessary resize_table(num_buckets, new_num_buckets); } } assert(table); fill_range_with_empty(table, table + new_num_buckets); num_elements = 0; num_deleted = 0; num_buckets = new_num_buckets; // our new size settings.reset_thresholds(bucket_count()); } public: // It's always nice to be able to clear a table without deallocating it void clear() { // If the table is already empty, and the number of buckets is // already as we desire, there's nothing to do. const size_type new_num_buckets = settings.min_buckets(0, 0); if (num_elements == 0 && new_num_buckets == num_buckets) { return; } clear_to_size(new_num_buckets); } // Clear the table without resizing it. // Mimicks the stl_hashtable's behaviour when clear()-ing in that it // does not modify the bucket count void clear_no_resize() { if (num_elements > 0) { assert(table); destroy_buckets(0, num_buckets); fill_range_with_empty(table, table + num_buckets); } // don't consider to shrink before another erase() settings.reset_thresholds(bucket_count()); num_elements = 0; num_deleted = 0; } // LOOKUP ROUTINES private: template void assert_key_is_not_empty_or_deleted(const K& key) const { assert(settings.use_empty() && "set_empty_key() was not called"); assert(!equals(key, key_info.empty) && "Using the empty key as a regular key"); assert((!settings.use_deleted() || !equals(key, key_info.delkey)) && "Using the deleted key as a regular key"); } template std::pair find_position(const K& key) const { return find_position_using_hash(hash(key), key); } // Returns a pair of positions: 1st where the object is, 2nd where // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET // if object is not found; 2nd is ILLEGAL_BUCKET if it is. // Note: because of deletions where-to-insert is not trivial: it's the // first deleted bucket we see, as long as we don't find the key later template std::pair find_position_using_hash( const size_type key_hash, const K& key) const { assert_key_is_not_empty_or_deleted(key); size_type num_probes = 0; // how many times we've probed const size_type bucket_count_minus_one = bucket_count() - 1; size_type bucknum = key_hash & bucket_count_minus_one; size_type insert_pos = ILLEGAL_BUCKET; // where we would insert while (1) { // probe until something happens if (test_empty(bucknum)) { // bucket is empty if (insert_pos == ILLEGAL_BUCKET) // found no prior place to insert return std::pair(ILLEGAL_BUCKET, bucknum); else return std::pair(ILLEGAL_BUCKET, insert_pos); } else if (test_deleted(bucknum)) { // keep searching, but mark to insert if ( insert_pos == ILLEGAL_BUCKET ) insert_pos = bucknum; } else if (equals(key, get_key(table[bucknum]))) { return std::pair(bucknum, ILLEGAL_BUCKET); } ++num_probes; // we're doing another probe bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; assert(num_probes < bucket_count() && "Hashtable is full: an error in key_equal<> or hash<>"); } } template std::pair find_if_present(const K& key) const { return find_if_present_using_hash(hash(key), key); } // Return where the key is (if at all), and if it is present. If // the key isn't present then the first part of the return value is // undefined. The same information can be extracted from the result // of find_position(), but that tends to be slower in practice. template std::pair find_if_present_using_hash( const size_type key_hash, const K& key) const { assert_key_is_not_empty_or_deleted(key); size_type num_probes = 0; // how many times we've probed const size_type bucket_count_minus_one = bucket_count() - 1; size_type bucknum = key_hash & bucket_count_minus_one; while (1) { // probe until something happens if (equals(key, get_key(table[bucknum]))) { return std::pair(bucknum, true); } else if (test_empty(bucknum)) { return std::pair(0, false); } ++num_probes; // we're doing another probe bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; assert(num_probes < bucket_count() && "Hashtable is full: an error in key_equal<> or hash<>"); } } private: template iterator find_impl(const K& key) { std::pair pos = find_if_present(key); return pos.second ? iterator(this, table + pos.first, table + num_buckets, false) : end(); } template const_iterator find_impl(const K& key) const { std::pair pos = find_if_present(key); return pos.second ? const_iterator(this, table + pos.first, table + num_buckets, false) : end(); } template size_type bucket_impl(const K& key) const { std::pair pos = find_position(key); return pos.first == ILLEGAL_BUCKET ? pos.second : pos.first; } template size_type count_impl(const K& key) const { return find_if_present(key).second ? 1 : 0; } template std::pair equal_range_impl(const K& key) { iterator pos = find(key); if (pos == end()) { return std::pair(pos, pos); } else { const iterator startpos = pos++; return std::pair(startpos, pos); } } template std::pair equal_range_impl(const K& key) const { const_iterator pos = find(key); if (pos == end()) { return std::pair(pos, pos); } else { const const_iterator startpos = pos++; return std::pair(startpos, pos); } } public: iterator find(const key_type& key) { return find_impl(key); } const_iterator find(const key_type& key) const { return find_impl(key); } // This is a tr1 method: the bucket a given key is in, or what bucket // it would be put in, if it were to be inserted. Shrug. size_type bucket(const key_type& key) const { return bucket_impl(key); } // Counts how many elements have key key. For maps, it's either 0 or 1. size_type count(const key_type &key) const { return count_impl(key); } // Likewise, equal_range doesn't really make sense for us. Oh well. std::pair equal_range(const key_type& key) { return equal_range_impl(key); } std::pair equal_range(const key_type& key) const { return equal_range_impl(key); } // INSERTION ROUTINES private: // Private method used by insert_noresize and find_or_insert. // 'obj' is either value_type&& or const value_type&. template iterator insert_at(U&& obj, size_type pos) { if (size() >= max_size()) { throw std::length_error("insert overflow"); } if ( test_deleted(pos) ) { // just replace if it's been del. assert(num_deleted > 0); --num_deleted; // used to be, now it isn't } else { ++num_elements; // replacing an empty bucket } set_value(&table[pos], std::forward(obj)); return iterator(this, table + pos, table + num_buckets, false); } // If you know *this is big enough to hold obj, use this routine // 'obj' is value_type&& or const value_type&. template std::pair insert_noresize(U&& obj) { // NOLINT return insert_noresize_using_hash(hash(get_key(obj)), std::forward(obj)); } // If you know *this is big enough to hold obj, use this routine // 'obj' is value_type&& or const value_type&. template std::pair insert_noresize_using_hash(const size_type key_hash, U&& obj) { const std::pair pos = find_position_using_hash(key_hash, get_key(obj)); if (pos.first != ILLEGAL_BUCKET) { // object was already there return std::pair(iterator(this, table + pos.first, table + num_buckets, false), false); // false: we didn't insert } else { // pos.second says where to put it iterator i = insert_at(std::forward(obj), pos.second); return std::pair(i, true); } } // Specializations of insert(it, it) depending on the power of the iterator: // (1) Iterator supports operator-, resize before inserting template void insert(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag) { size_t dist = std::distance(f, l); if (dist >= std::numeric_limits::max()) { throw std::length_error("insert-range overflow"); } resize_delta(static_cast(dist)); for ( ; dist > 0; --dist, ++f) { insert_noresize(*f); } } // (2) Arbitrary iterator, can't tell how much to resize template void insert(InputIterator f, InputIterator l, std::input_iterator_tag) { for ( ; f != l; ++f) insert(*f); } public: // This is the normal insert routine, used by the outside world std::pair insert(const value_type& obj) { resize_delta(1); // adding an object, grow if need be return insert_noresize(obj); } std::pair insert(value_type&& obj) { // NOLINT resize_delta(1); // adding an object, grow if need be return insert_noresize(std::move(obj)); } // When inserting a lot at a time, we specialize on the type of iterator template void insert(InputIterator f, InputIterator l) { // specializes on iterator type insert(f, l, typename std::iterator_traits::iterator_category()); } template value_type& find_or_insert(const key_type& key) { return find_or_insert_using_hash(hash(key), key); } // DefaultValue is a functor that takes a key and returns a value_type // representing the default value to be inserted if none is found. template value_type& find_or_insert_using_hash(const size_type key_hash, const key_type& key) { const std::pair pos = find_position_using_hash(key_hash, key); DefaultValue default_value; if (pos.first != ILLEGAL_BUCKET) { // object was already there return table[pos.first]; } else if (resize_delta(1)) { // needed to rehash to make room // Since we resized, we can't use pos, so recalculate where to insert. return *insert_noresize(default_value(key)).first; } else { // no need to rehash, insert right here return *insert_at(default_value(key), pos.second); } } // DELETION ROUTINES private: template size_type erase_impl(const K& key) { iterator pos = find(key); if (pos != end()) { assert(!test_deleted(pos)); // or find() shouldn't have returned it set_deleted(pos); ++num_deleted; // will think about shrink after next insert settings.set_consider_shrink(true); return 1; // because we deleted one thing } else { return 0; // because we deleted nothing } } public: size_type erase(const key_type& key) { return erase_impl(key); } void erase(iterator pos) { if (pos == end()) return; // sanity check set_deleted(pos); ++num_deleted; // will think about shrink after next insert settings.set_consider_shrink(true); } void erase(iterator f, iterator l) { for (; f != l; ++f) { set_deleted(f); ++num_deleted; } // will think about shrink after next insert settings.set_consider_shrink(true); } // We allow you to erase a const_iterator just like we allow you to // erase an iterator. This is in parallel to 'delete': you can delete // a const pointer just like a non-const pointer. The logic is that // you can't use the object after it's erased anyway, so it doesn't matter // if it's const or not. void erase(const_iterator pos) { if (pos == end()) return; // sanity check set_deleted(pos); ++num_deleted; // will think about shrink after next insert settings.set_consider_shrink(true); } void erase(const_iterator f, const_iterator l) { for ( ; f != l; ++f) { set_deleted(f); ++num_deleted; } // will think about shrink after next insert settings.set_consider_shrink(true); } // COMPARISON bool operator==(const dense_hashtable& ht) const { if (size() != ht.size()) { return false; } else if (this == &ht) { return true; } else { // Iterate through the elements in "this" and see if the // corresponding element is in ht for ( const_iterator it = begin(); it != end(); ++it ) { const_iterator it2 = ht.find(get_key(*it)); if ((it2 == ht.end()) || (*it != *it2)) { return false; } } return true; } } bool operator!=(const dense_hashtable& ht) const { return !(*this == ht); } // I/O // We support reading and writing hashtables to disk. Alas, since // I don't know how to write a hasher or key_equal, you have to make // sure everything but the table is the same. We compact before writing. private: // Every time the disk format changes, this should probably change too typedef unsigned long MagicNumberType; static const MagicNumberType MAGIC_NUMBER = 0x13578642; // Package functors with another class to eliminate memory needed for // zero-size functors. Since ExtractKey and hasher's operator() might // have the same function signature, they must be packaged in // different classes. struct Settings : sh_hashtable_settings { explicit Settings(const hasher& hf) : sh_hashtable_settings( hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {} }; // Packages ExtractKey, SetKey, EqualKey functors, allocator and deleted and // empty key values. struct KeyInfo : public ExtractKey, public SetKey, public EqualKey, public value_alloc_type { KeyInfo(const ExtractKey& ek, const SetKey& sk, const EqualKey& eq, const value_alloc_type& a) : ExtractKey(ek), SetKey(sk), EqualKey(eq), value_alloc_type(a), delkey(), empty() {} // Accessors for convenient access to base classes. ExtractKey& as_extract_key() { return *this; } const ExtractKey& as_extract_key() const { return *this; } SetKey& as_set_key() { return *this; } const SetKey& as_set_key() const { return *this; } EqualKey& as_equal_key() { return *this; } const EqualKey& as_equal_key() const { return *this; } value_alloc_type& as_value_alloc() { return *this; } const value_alloc_type& as_value_alloc() const { return *this; } // We want to return the exact same type as ExtractKey: Key or const Key& typename ExtractKey::result_type get_key(const_reference v) const { return ExtractKey::operator()(v); } void set_key(pointer v, const key_type& k) const { SetKey::operator()(v, k); } // We only ever call EqualKey::operator()(key_type, K) -- we never use the // other order of args. This allows consumers to get away with implementing // only half of operator==. template bool equals(const key_type& a, const K& b) const { return EqualKey::operator()(a, b); } pointer allocate(size_type size) { pointer memory = value_alloc_type::allocate(size); assert(memory != nullptr); return memory; } // Which key marks deleted entries. // TODO(user): make a pointer, and get rid of use_deleted (benchmark!) typename std::remove_const::type delkey; // Key value used to mark unused entries. typename std::remove_const::type empty; }; // Returns the value_alloc_type used to allocate and deallocate // the table. This can be different from the one returned by get_allocator(). value_alloc_type& get_internal_allocator() { return key_info; } // Utility functions to access the templated operators size_type hash(const key_type& v) const { return settings.hash(v); } bool equals(const key_type& a, const key_type& b) const { return key_info.equals(a, b); } typename ExtractKey::result_type get_key(const_reference v) const { return key_info.get_key(v); } void set_key(pointer v, const key_type& k) const { key_info.set_key(v, k); } private: // Actual data Settings settings; KeyInfo key_info; size_type num_deleted; // how many occupied buckets are marked deleted size_type num_elements; size_type num_buckets; pointer table; }; // We need a global swap as well template inline void swap(dense_hashtable &x, dense_hashtable &y) { x.swap(y); } #undef JUMP_ #undef PUT_ #undef GET_ template const typename dense_hashtable::size_type dense_hashtable::ILLEGAL_BUCKET; // How full we let the table get before we resize. Knuth says .8 is // good -- higher causes us to probe too much, though saves memory. // However, we go with .5, getting better performance at the cost of // more space (a trade-off densehashtable explicitly chooses to make). // Feel free to play around with different values, though, via // max_load_factor() and/or set_resizing_parameters(). template const int dense_hashtable::HT_OCCUPANCY_PCT = 50; // How empty we let the table get before we resize lower. // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing. template const int dense_hashtable::HT_EMPTY_PCT = static_cast( 0.4 * dense_hashtable::HT_OCCUPANCY_PCT); } #endif // S2_UTIL_GTL_DENSEHASHTABLE_H_ s2/src/s2/util/math/0000755000176200001440000000000014540325333013621 5ustar liggesuserss2/src/s2/util/math/matrix3x3.h0000644000176200001440000004307314530411473015643 0ustar liggesusers// Copyright 2003 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // A simple class to handle 3x3 matrices // The aim of this class is to be able to manipulate 3x3 matrices // and 3D vectors as naturally as possible and make calculations // readable. // For that reason, the operators +, -, * are overloaded. // (Reading a = a + b*2 - c is much easier to read than // a = Sub(Add(a, Mul(b,2)),c) ) // // Please be careful about overflows when using those matrices wth integer types // The calculations are carried with VType. eg : if you are using uint8 as the // base type, all values will be modulo 256. // This feature is necessary to use the class in a more general framework with // VType != plain old data type. #ifndef S2_UTIL_MATH_MATRIX3X3_H_ #define S2_UTIL_MATH_MATRIX3X3_H_ #include #include #include #include "s2/base/logging.h" #include "s2/util/math/mathutil.h" #include "s2/util/math/vector.h" template class Matrix3x3 { private: VType m_[3][3]; public: typedef Matrix3x3 Self; typedef VType BaseType; typedef Vector3 MVector; // Initialize the matrix to 0 Matrix3x3() { m_[0][2] = m_[0][1] = m_[0][0] = VType(); m_[1][2] = m_[1][1] = m_[1][0] = VType(); m_[2][2] = m_[2][1] = m_[2][0] = VType(); } // Constructor explicitly setting the values of all the coefficient of // the matrix Matrix3x3(const VType &m00, const VType &m01, const VType &m02, const VType &m10, const VType &m11, const VType &m12, const VType &m20, const VType &m21, const VType &m22) { m_[0][0] = m00; m_[0][1] = m01; m_[0][2] = m02; m_[1][0] = m10; m_[1][1] = m11; m_[1][2] = m12; m_[2][0] = m20; m_[2][1] = m21; m_[2][2] = m22; } // Casting constructor template static Matrix3x3 Cast(const Matrix3x3 &mb) { return Matrix3x3(static_cast(mb(0, 0)), static_cast(mb(0, 1)), static_cast(mb(0, 2)), static_cast(mb(1, 0)), static_cast(mb(1, 1)), static_cast(mb(1, 2)), static_cast(mb(2, 0)), static_cast(mb(2, 1)), static_cast(mb(2, 2))); } // Change the value of all the coefficients of the matrix inline Matrix3x3 & Set(const VType &m00, const VType &m01, const VType &m02, const VType &m10, const VType &m11, const VType &m12, const VType &m20, const VType &m21, const VType &m22) { m_[0][0] = m00; m_[0][1] = m01; m_[0][2] = m02; m_[1][0] = m10; m_[1][1] = m11; m_[1][2] = m12; m_[2][0] = m20; m_[2][1] = m21; m_[2][2] = m22; return (*this); } // Matrix addition inline Matrix3x3& operator+=(const Matrix3x3 &mb) { m_[0][0] += mb.m_[0][0]; m_[0][1] += mb.m_[0][1]; m_[0][2] += mb.m_[0][2]; m_[1][0] += mb.m_[1][0]; m_[1][1] += mb.m_[1][1]; m_[1][2] += mb.m_[1][2]; m_[2][0] += mb.m_[2][0]; m_[2][1] += mb.m_[2][1]; m_[2][2] += mb.m_[2][2]; return (*this); } // Matrix subtration inline Matrix3x3& operator-=(const Matrix3x3 &mb) { m_[0][0] -= mb.m_[0][0]; m_[0][1] -= mb.m_[0][1]; m_[0][2] -= mb.m_[0][2]; m_[1][0] -= mb.m_[1][0]; m_[1][1] -= mb.m_[1][1]; m_[1][2] -= mb.m_[1][2]; m_[2][0] -= mb.m_[2][0]; m_[2][1] -= mb.m_[2][1]; m_[2][2] -= mb.m_[2][2]; return (*this); } // Matrix multiplication by a scalar inline Matrix3x3& operator*=(const VType &k) { m_[0][0] *= k; m_[0][1] *= k; m_[0][2] *= k; m_[1][0] *= k; m_[1][1] *= k; m_[1][2] *= k; m_[2][0] *= k; m_[2][1] *= k; m_[2][2] *= k; return (*this); } // Matrix addition inline Matrix3x3 operator+(const Matrix3x3 &mb) const { return Matrix3x3(*this) += mb; } // Matrix subtraction inline Matrix3x3 operator-(const Matrix3x3 &mb) const { return Matrix3x3(*this) -= mb; } // Change the sign of all the coefficients in the matrix friend inline Matrix3x3 operator-(const Matrix3x3 &vb) { return Matrix3x3(-vb.m_[0][0], -vb.m_[0][1], -vb.m_[0][2], -vb.m_[1][0], -vb.m_[1][1], -vb.m_[1][2], -vb.m_[2][0], -vb.m_[2][1], -vb.m_[2][2]); } // Matrix multiplication by a scalar inline Matrix3x3 operator*(const VType &k) const { return Matrix3x3(*this) *= k; } friend inline Matrix3x3 operator*(const VType &k, const Matrix3x3 &mb) { return Matrix3x3(mb)*k; } // Matrix multiplication inline Matrix3x3 operator*(const Matrix3x3 &mb) const { return Matrix3x3( m_[0][0] * mb.m_[0][0] + m_[0][1] * mb.m_[1][0] + m_[0][2] * mb.m_[2][0], m_[0][0] * mb.m_[0][1] + m_[0][1] * mb.m_[1][1] + m_[0][2] * mb.m_[2][1], m_[0][0] * mb.m_[0][2] + m_[0][1] * mb.m_[1][2] + m_[0][2] * mb.m_[2][2], m_[1][0] * mb.m_[0][0] + m_[1][1] * mb.m_[1][0] + m_[1][2] * mb.m_[2][0], m_[1][0] * mb.m_[0][1] + m_[1][1] * mb.m_[1][1] + m_[1][2] * mb.m_[2][1], m_[1][0] * mb.m_[0][2] + m_[1][1] * mb.m_[1][2] + m_[1][2] * mb.m_[2][2], m_[2][0] * mb.m_[0][0] + m_[2][1] * mb.m_[1][0] + m_[2][2] * mb.m_[2][0], m_[2][0] * mb.m_[0][1] + m_[2][1] * mb.m_[1][1] + m_[2][2] * mb.m_[2][1], m_[2][0] * mb.m_[0][2] + m_[2][1] * mb.m_[1][2] + m_[2][2] * mb.m_[2][2]); } // Multiplication of a matrix by a vector inline MVector operator*(const MVector &v) const { return MVector( m_[0][0] * v[0] + m_[0][1] * v[1] + m_[0][2] * v[2], m_[1][0] * v[0] + m_[1][1] * v[1] + m_[1][2] * v[2], m_[2][0] * v[0] + m_[2][1] * v[1] + m_[2][2] * v[2]); } // Return the determinant of the matrix inline VType Det(void) const { return m_[0][0] * m_[1][1] * m_[2][2] + m_[0][1] * m_[1][2] * m_[2][0] + m_[0][2] * m_[1][0] * m_[2][1] - m_[2][0] * m_[1][1] * m_[0][2] - m_[2][1] * m_[1][2] * m_[0][0] - m_[2][2] * m_[1][0] * m_[0][1]; } // Return the trace of the matrix inline VType Trace(void) const { return m_[0][0] + m_[1][1] + m_[2][2]; } // Return a pointer to the data array for interface with other libraries // like opencv VType* Data() { return reinterpret_cast(m_); } const VType* Data() const { return reinterpret_cast(m_); } // Return matrix element (i,j) with 0<=i<=2 0<=j<=2 inline VType &operator()(const int i, const int j) { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 3); S2_DCHECK_GE(j, 0); S2_DCHECK_LT(j, 3); return m_[i][j]; } inline VType operator()(const int i, const int j) const { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 3); S2_DCHECK_GE(j, 0); S2_DCHECK_LT(j, 3); return m_[i][j]; } // Return matrix element (i/3,i%3) with 0<=i<=8 (access concatenated rows). inline VType &operator[](const int i) { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 9); return reinterpret_cast(m_)[i]; } inline VType operator[](const int i) const { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 9); return reinterpret_cast(m_)[i]; } // Return the transposed matrix inline Matrix3x3 Transpose(void) const { return Matrix3x3(m_[0][0], m_[1][0], m_[2][0], m_[0][1], m_[1][1], m_[2][1], m_[0][2], m_[1][2], m_[2][2]); } // Return the transposed of the matrix of the cofactors // (Useful for inversion for example) inline Matrix3x3 ComatrixTransposed(void) const { return Matrix3x3( m_[1][1] * m_[2][2] - m_[2][1] * m_[1][2], m_[2][1] * m_[0][2] - m_[0][1] * m_[2][2], m_[0][1] * m_[1][2] - m_[1][1] * m_[0][2], m_[1][2] * m_[2][0] - m_[2][2] * m_[1][0], m_[2][2] * m_[0][0] - m_[0][2] * m_[2][0], m_[0][2] * m_[1][0] - m_[1][2] * m_[0][0], m_[1][0] * m_[2][1] - m_[2][0] * m_[1][1], m_[2][0] * m_[0][1] - m_[0][0] * m_[2][1], m_[0][0] * m_[1][1] - m_[1][0] * m_[0][1]); } // Matrix inversion inline Matrix3x3 Inverse(void) const { VType det = Det(); S2_CHECK_NE(det, VType(0)) << " Can't inverse. Determinant = 0."; return (VType(1) / det) * ComatrixTransposed(); } // Return the vector 3D at row i inline MVector Row(const int i) const { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 3); return MVector(m_[i][0], m_[i][1], m_[i][2]); } // Return the vector 3D at col i inline MVector Col(const int i) const { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 3); return MVector(m_[0][i], m_[1][i], m_[2][i]); } // Create a matrix from 3 row vectors static inline Matrix3x3 FromRows(const MVector &v1, const MVector &v2, const MVector &v3) { Matrix3x3 temp; temp.Set(v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], v3[0], v3[1], v3[2]); return temp; } // Create a matrix from 3 column vectors static inline Matrix3x3 FromCols(const MVector &v1, const MVector &v2, const MVector &v3) { Matrix3x3 temp; temp.Set(v1[0], v2[0], v3[0], v1[1], v2[1], v3[1], v1[2], v2[2], v3[2]); return temp; } // Set the vector in row i to be v1 void SetRow(int i, const MVector &v1) { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 3); m_[i][0] = v1[0]; m_[i][1] = v1[1]; m_[i][2] = v1[2]; } // Set the vector in column i to be v1 void SetCol(int i, const MVector &v1) { S2_DCHECK_GE(i, 0); S2_DCHECK_LT(i, 3); m_[0][i] = v1[0]; m_[1][i] = v1[1]; m_[2][i] = v1[2]; } // Return a matrix M close to the original but verifying MtM = I // (useful to compensate for errors in a rotation matrix) Matrix3x3 Orthogonalize() const { MVector r1, r2, r3; r1 = Row(0).Normalize(); r2 = (Row(2).CrossProd(r1)).Normalize(); r3 = (r1.CrossProd(r2)).Normalize(); return FromRows(r1, r2, r3); } // Return the identity matrix static inline Matrix3x3 Identity(void) { Matrix3x3 temp; temp.Set(VType(1), VType(0), VType(0), // VType(0), VType(1), VType(0), // VType(0), VType(0), VType(1)); return temp; } // Return a matrix full of zeros static inline Matrix3x3 Zero(void) { return Matrix3x3(); } // Return a diagonal matrix with the coefficients in v static inline Matrix3x3 Diagonal(const MVector &v) { return Matrix3x3(v[0], VType(), VType(), VType(), v[1], VType(), VType(), VType(), v[2]); } // Return the matrix vvT static Matrix3x3 Sym3(const MVector &v) { return Matrix3x3( v[0]*v[0], v[0]*v[1], v[0]*v[2], v[1]*v[0], v[1]*v[1], v[1]*v[2], v[2]*v[0], v[2]*v[1], v[2]*v[2]); } // Return a matrix M such that: // for each u, M * u = v.CrossProd(u) static Matrix3x3 AntiSym3(const MVector &v) { return Matrix3x3(VType(), -v[2], v[1], v[2], VType(), -v[0], -v[1], v[0], VType()); } // Returns matrix that rotates |rot| radians around axis rot. static Matrix3x3 Rodrigues(const MVector &rot) { Matrix3x3 R; VType theta = rot.Norm(); MVector w = rot.Normalize(); Matrix3x3 Wv = Matrix3x3::AntiSym3(w); Matrix3x3 I = Matrix3x3::Identity(); Matrix3x3 A = Matrix3x3::Sym3(w); R = (1 - cos(theta)) * A + sin(theta) * Wv + cos(theta) * I; return R; } // Returns v.Transpose() * (*this) * u VType MulBothSides(const MVector &v, const MVector &u) const { return ((*this) * u).DotProd(v); } // Use the 3x3 matrix as a projective transform for 2d points Vector2 Project(const Vector2 &v) const { MVector temp = (*this) * MVector(v[0], v[1], 1); return Vector2(temp[0] / temp[2], temp[1] / temp[2]); } // Return the Frobenius norm of the matrix: sqrt(sum(aij^2)) VType FrobeniusNorm() const { VType sum = VType(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { sum += m_[i][j] * m_[i][j]; } } return sqrt(sum); } // Finds the eigen values of the matrix. Return the number of real eigenvalues // found. // If the matrix is known to be symmetric due to your problem formulation, // then please use SymmetricEigenSolver, since this method does not guarantee // finding all 3 real eigenvalues in pathological cases. See CL 49170250. int EigenValues(MVector *eig_val) const { long double r1, r2, r3; // NOLINT // characteristic polynomial // x^3 + a*x^2 + b*x + c VType a = -Trace(); VType b = m_[0][0]*m_[1][1] + m_[1][1]*m_[2][2] + m_[2][2]*m_[0][0] - m_[1][0]*m_[0][1] - m_[2][1]*m_[1][2] - m_[0][2]*m_[2][0]; VType c = -Det(); bool res = MathUtil::RealRootsForCubic(a, b, c, &r1, &r2, &r3); (*eig_val)[0] = r1; if (res) { (*eig_val)[1] = r2; (*eig_val)[2] = r3; return 3; } return 1; } // Finds the eigen values and optional associated eigen vectors of a // symmetric 3x3 matrix (not necessarily positive definite). // eigen values are sorted in decreasing order; // eig_val corresponds to the columns of the eig_vec matrix. // Note: The routine will only use the lower diagonal part // of the matrix, i.e. // | a00, | // | a10, a11, | // | a20, a21, a22 | void SymmetricEigenSolver(MVector *eig_val, Matrix3x3 *eig_vec /*nullable*/) const { // Compute characteristic polynomial coefficients. double c2 = -Trace(); double c1 = -(m_[1][0] * m_[1][0] - m_[0][0] * m_[1][1] - m_[0][0] * m_[2][2] - m_[1][1] * m_[2][2] + m_[2][0] * m_[2][0] + m_[2][1] * m_[2][1]); double c0 = -(m_[0][0] * m_[1][1] * m_[2][2] // - m_[2][0] * m_[2][0] * m_[1][1] // - m_[1][0] * m_[1][0] * m_[2][2] // - m_[0][0] * m_[2][1] * m_[2][1] // + 2 * m_[1][0] * m_[2][0] * m_[2][1]); // Root finding x^3 + c2*x^2 + c1*x + c0 = 0. // NOTE: Cannot reuse general cubic solver MathUtil::RealRootsForCubic() // because it doesn't guarantee finding 3 real roots, e.g. it won't always // return roots {2, 2, 0} for the cubic x^3 - 4*x^2 + 4*x + epsilon = 0. double q = (c2*c2-3*c1)/9.0; double r = (2*c2*c2*c2-9*c2*c1+27*c0)/54.0; // Assume R^2 <= Q^3 so there are three real roots. // Avoid sqrt of negative q, which can only happen due to numerical error. if (q < 0) q = 0; double sqrt_q = -2.0 * sqrt(q); double q3_r2 = q * q * q - r * r; // Avoid sqrt of negative q3_r2, which can only happen due to numerical // error. double theta = atan2(q3_r2 <= 0 ? 0 : sqrt(q3_r2), r); double c2_3 = c2 / 3; (*eig_val)[0] = sqrt_q * cos(theta / 3.0) - c2_3; (*eig_val)[1] = sqrt_q * cos((theta + 2.0 * M_PI)/3.0) - c2_3; (*eig_val)[2] = sqrt_q * cos((theta - 2.0 * M_PI)/3.0) - c2_3; // Sort eigen value in decreasing order Vector3 d_order = eig_val->ComponentOrder(); (*eig_val) = MVector((*eig_val)[d_order[2]], (*eig_val)[d_order[1]], (*eig_val)[d_order[0]]); // Compute eigenvectors if (!eig_vec) return; for (int i = 0; i < 3; ++i) { MVector r1 , r2 , r3 , e1 , e2 , e3; r1[0] = m_[0][0] - (*eig_val)[i]; r2[0] = m_[1][0]; r3[0] = m_[2][0]; r1[1] = m_[1][0]; r2[1] = m_[1][1] - (*eig_val)[i]; r3[1] = m_[2][1]; r1[2] = m_[2][0]; r2[2] = m_[2][1]; r3[2] = m_[2][2] - (*eig_val)[i]; e1 = r1.CrossProd(r2); e2 = r2.CrossProd(r3); e3 = r3.CrossProd(r1); // Make e2 and e3 point in the same direction as e1 if (e2.DotProd(e1) < 0) e2 = -e2; if (e3.DotProd(e1) < 0) e3 = -e3; MVector e = (e1 + e2 + e3).Normalize(); eig_vec->SetCol(i, e); } } // Return true is one of the elements of the matrix is NaN bool IsNaN() const { for ( int i = 0; i < 3; ++i ) { for ( int j = 0; j < 3; ++j ) { if ( isnan(m_[i][j]) ) { return true; } } } return false; } friend bool operator==(const Matrix3x3 &a, const Matrix3x3 &b) { return a.m_[0][0] == b.m_[0][0] && a.m_[0][1] == b.m_[0][1] && a.m_[0][2] == b.m_[0][2] && a.m_[1][0] == b.m_[1][0] && a.m_[1][1] == b.m_[1][1] && a.m_[1][2] == b.m_[1][2] && a.m_[2][0] == b.m_[2][0] && a.m_[2][1] == b.m_[2][1] && a.m_[2][2] == b.m_[2][2]; } friend bool operator!=(const Matrix3x3 &a, const Matrix3x3 &b) { return !(a == b); } friend std::ostream &operator <<(std::ostream &out, const Matrix3x3 &mb) { int i, j; for (i = 0; i < 3; i++) { if (i ==0) { out << "["; } else { out << " "; } for (j = 0; j < 3; j++) { out << mb(i, j) << " "; } if (i == 2) { out << "]"; } else { out << std::endl; } } return out; } }; typedef Matrix3x3 Matrix3x3_i; typedef Matrix3x3 Matrix3x3_f; typedef Matrix3x3 Matrix3x3_d; #endif // S2_UTIL_MATH_MATRIX3X3_H_ s2/src/s2/util/math/mathutil.h0000644000176200001440000001644514530411473015633 0ustar liggesusers// Copyright 2001 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // This class is intended to contain a collection of useful (static) // mathematical functions, properly coded (by consulting numerical // recipes or another authoritative source first). #ifndef S2_UTIL_MATH_MATHUTIL_H_ #define S2_UTIL_MATH_MATHUTIL_H_ #include #include "s2/base/integral_types.h" class MathUtil { public: // Solves for the real roots of x^3+ax^2+bx+c=0, returns true iff // all three are real, in which case the roots are stored (in any // order) in r1, r2, r3; otherwise, exactly one real root exists and // it is stored in r1. static bool RealRootsForCubic(long double a, long double b, long double c, long double *r1, long double *r2, long double *r3); // -------------------------------------------------------------------- // Round // This function rounds a floating-point number to an integer. It // works for positive or negative numbers. // // Values that are halfway between two integers may be rounded up or // down, for example Round(0.5) == 0 and Round(1.5) == 2. // This allows the function to be implemented efficiently on Intel // processors (see the template specializations at the bottom of this // file). You should not use this function if you care about which // way such half-integers are rounded. // // Example usage: // double y, z; // int x = Round(y + 3.7); // int64 b = Round(0.3 * z); // // Note that the floating-point template parameter is typically inferred // from the argument type, i.e. there is no need to specify it explicitly. // -------------------------------------------------------------------- template static IntOut Round(FloatIn x) { static_assert(!std::is_integral::value, "FloatIn is integer"); static_assert(std::is_integral::value, "IntOut is not integer"); // We don't use sgn(x) below because there is no need to distinguish the // (x == 0) case. Also note that there are specialized faster versions // of this function for Intel processors at the bottom of this file. return static_cast(x < 0 ? (x - 0.5) : (x + 0.5)); } // -------------------------------------------------------------------- // FastIntRound, FastInt64Round // Fast routines for converting floating-point numbers to integers. // // These routines are approximately 6 times faster than the default // implementation of Round on Intel processors (12 times faster on // the Pentium 3). They are also more than 5 times faster than simply // casting a "double" to an "int" using static_cast. This is // because casts are defined to truncate towards zero, which on Intel // processors requires changing the rounding mode and flushing the // floating-point pipeline (unless programs are compiled specifically // for the Pentium 4, which has a new instruction to avoid this). // // Numbers that are halfway between two integers may be rounded up or // down. This is because the conversion is done using the default // rounding mode, which rounds towards the closest even number in case // of ties. So for example, FastIntRound(0.5) == 0, but // FastIntRound(1.5) == 2. These functions should only be used with // applications that don't care about which way such half-integers are // rounded. // // There are template specializations of Round() which call these // functions (for "int" and "int64" only), but it's safer to call them // directly. // // This functions are equivalent to lrint() and llrint() as defined in // the ISO C99 standard. Unfortunately this standard does not seem to // widely adopted yet and these functions are not available by default. // -------------------------------------------------------------------- static int32 FastIntRound(double x) { // This function is not templatized because gcc doesn't seem to be able // to deal with inline assembly code in templatized functions, and there // is no advantage to passing an argument type of "float" on Intel // architectures anyway. #if defined __GNUC__ && (defined __i386__ || defined __SSE2__) #if defined __SSE2__ // SSE2. int32 result; __asm__ __volatile__ ("cvtsd2si %1, %0" : "=r" (result) // Output operand is a register : "x" (x)); // Input operand is an xmm register return result; #elif defined __i386__ // FPU stack. Adapted from /usr/include/bits/mathinline.h. int32 result; __asm__ __volatile__ ("fistpl %0" : "=m" (result) // Output operand is a memory location : "t" (x) // Input operand is top of FP stack : "st"); // Clobbers (pops) top of FP stack return result; #endif // if defined __x86_64__ || ... #else return Round(x); #endif // if defined __GNUC__ && ... } static int64 FastInt64Round(double x) { #if defined __GNUC__ && (defined __i386__ || defined __x86_64__) #if defined __x86_64__ // SSE2. int64 result; __asm__ __volatile__ ("cvtsd2si %1, %0" : "=r" (result) // Output operand is a register : "x" (x)); // Input operand is an xmm register return result; #elif defined __i386__ // There is no CVTSD2SI in i386 to produce a 64 bit int, even with SSE2. // FPU stack. Adapted from /usr/include/bits/mathinline.h. int64 result; __asm__ __volatile__ ("fistpll %0" : "=m" (result) // Output operand is a memory location : "t" (x) // Input operand is top of FP stack : "st"); // Clobbers (pops) top of FP stack return result; #endif // if defined __i386__ #else return Round(x); #endif // if defined __GNUC__ && ... } }; // ========================================================================= // #if (defined __i386__ || defined __x86_64__) && defined __GNUC__ // We define template specializations of Round() to get the more efficient // Intel versions when possible. Note that gcc does not currently support // partial specialization of templatized functions. template<> inline int32 MathUtil::Round(double x) { return FastIntRound(x); } template<> inline int32 MathUtil::Round(float x) { return FastIntRound(x); } template<> inline int64 MathUtil::Round(double x) { return FastInt64Round(x); } template<> inline int64 MathUtil::Round(float x) { return FastInt64Round(x); } #endif #endif // S2_UTIL_MATH_MATHUTIL_H_ s2/src/s2/util/math/vector3_hash.h0000644000176200001440000000302014530411473016355 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_UTIL_MATH_VECTOR3_HASH_H_ #define S2_UTIL_MATH_VECTOR3_HASH_H_ #include #include #include #include "s2/util/hash/mix.h" #include "s2/util/math/vector.h" template struct GoodFastHash; template struct GoodFastHash> { std::size_t operator()(const Vector2& v) const { static_assert(std::is_pod::value, "POD expected"); // std::hash collapses +/-0. std::hash h; HashMix mix(h(v.x())); mix.Mix(h(v.y())); return mix.get(); } }; template struct GoodFastHash> { std::size_t operator()(const Vector3& v) const { static_assert(std::is_pod::value, "POD expected"); // std::hash collapses +/-0. std::hash h; HashMix mix(h(v.x())); mix.Mix(h(v.y())); mix.Mix(h(v.z())); return mix.get(); } }; #endif // S2_UTIL_MATH_VECTOR3_HASH_H_ s2/src/s2/util/math/mathutil.cc0000644000176200001440000000463414530411473015766 0ustar liggesusers// Copyright 2008 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "s2/util/math/mathutil.h" #include #include namespace { // Returns the sign of x: // -1 if x < 0, // +1 if x > 0, // 0 if x = 0. template inline T sgn(const T x) { return (x == 0 ? 0 : (x < 0 ? -1 : 1)); } } // namespace bool MathUtil::RealRootsForCubic(long double const a, long double const b, long double const c, long double *const r1, long double *const r2, long double *const r3) { // According to Numerical Recipes (pp. 184-5), what // follows is an arrangement of computations to // compute the roots of a cubic that minimizes // roundoff error (as pointed out by A.J. Glassman). long double const a_squared = a * a, a_third = a / 3.0, b_tripled = 3.0 * b; long double const Q = (a_squared - b_tripled) / 9.0; long double const R = (2.0 * a_squared * a - 3.0 * a * b_tripled + 27.0 * c) / 54.0; long double const R_squared = R * R; long double const Q_cubed = Q * Q * Q; if (R_squared < Q_cubed) { long double const root_Q = sqrt(Q); long double const two_pi_third = 2.0 * M_PI / 3.0; long double const theta_third = acos(R / sqrt(Q_cubed)) / 3.0; long double const minus_two_root_Q = -2.0 * root_Q; *r1 = minus_two_root_Q * cos(theta_third) - a_third; *r2 = minus_two_root_Q * cos(theta_third + two_pi_third) - a_third; *r3 = minus_two_root_Q * cos(theta_third - two_pi_third) - a_third; return true; } long double const A = -sgn(R) * pow(std::abs(R) + sqrt(R_squared - Q_cubed), 1.0 / 3.0L); if (A != 0.0) { // in which case, B from NR is zero *r1 = A + Q / A - a_third; return false; } *r1 = *r2 = *r3 = -a_third; return true; } s2/src/s2/util/math/exactfloat/0000755000176200001440000000000014540325333015753 5ustar liggesuserss2/src/s2/util/math/exactfloat/exactfloat.h0000644000176200001440000006521714530411473020271 0ustar liggesusers// Copyright 2009 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // ExactFloat is a multiple-precision floating point type based on the OpenSSL // Bignum library. It has the same interface as the built-in "float" and // "double" types, but only supports the subset of operators and intrinsics // where it is possible to compute the result exactly. So for example, // ExactFloat supports addition and multiplication but not division (since in // general, the quotient of two floating-point numbers cannot be represented // exactly). Exact arithmetic is useful for geometric algorithms, especially // for disambiguating cases where ordinary double-precision arithmetic yields // an uncertain result. // // ExactFloat is a subset of the faster and more capable MPFloat class (which // is based on the GNU MPFR library). The main reason to use this class // rather than MPFloat is that it is subject to a BSD-style license rather // than the much more restrictive LGPL license. // // It has the following features: // // - ExactFloat uses the same syntax as the built-in "float" and "double" // types, for example: x += 4 + fabs(2*y*y - z*z). There are a few // differences (see below), but the syntax is compatible enough so that // ExactFloat can be used as a template argument to templatized classes // such as Vector2, VectorN, Matrix3x3, etc. // // - Results are not rounded; instead, precision is increased so that the // result can be represented exactly. An inexact result is returned only // in the case of underflow or overflow (yielding signed zero or infinity // respectively), or if the maximum allowed precision is exceeded (yielding // NaN). ExactFloat uses IEEE 754-2008 rules for handling infinities, NaN, // rounding to integers, etc. // // - ExactFloat only supports calculations where the result can be // represented exactly. Therefore it supports intrinsics such as fabs() // but not transcendentals such as sin(), sqrt(), etc. // // Syntax Compatibility with "float" and "double" // ---------------------------------------------- // // ExactFloat supports a subset of the operators and intrinsics for the // built-in "double" type. (Thus it supports fabs() but not fabsf(), for // example.) The syntax is different only in the following cases: // // - Casts and implicit conversions to built-in types (including "bool") are // not supported. So for example, the following will not compile: // // ExactFloat x = 7.5; // double y = x; // ERROR: use x.ToDouble() instead // long z = x; // ERROR: use x.ToDouble() or lround(trunc(x)) // q = static_cast(x); // ERROR: use x.ToDouble() or lround(trunc(x)) // if (x) { ... } // ERROR: use (x != 0) instead // // - The glibc floating-point classification macros (fpclassify, isfinite, // isnormal, isnan, isinf) are not supported. Instead there are // zero-argument methods: // // ExactFloat x; // if (isnan(x)) { ... } // ERROR: use (x.is_nan()) instead // if (isinf(x)) { ... } // ERROR: use (x.is_inf()) instead // // Using ExactFloat with Vector3, etc. // ----------------------------------- // // ExactFloat can be used with templatized classes such as Vector2 and Vector3 // (see "util/math/vector.h"), with the following limitations: // // - Cast() can be used to convert other vector types to an ExactFloat vector // type, but not the other way around. This is because there are no // implicit conversions from ExactFloat to built-in types. You can work // around this by calling an explicit conversion method such as // ToDouble(). For example: // // typedef Vector3 Vector3_xf; // Vector3_xf x; // Vector3_d y; // x = Vector3_xf::Cast(y); // This works. // y = Vector3_d::Cast(x); // This doesn't. // y = Vector3_d(x[0].ToDouble(), x[1].ToDouble(), x[2].ToDouble()); // OK // // - IsNaN() is not supported because it calls isnan(), which is defined as a // macro in and therefore can't easily be overrided. // // Precision Semantics // ------------------- // // Unlike MPFloat, ExactFloat does not allow a maximum precision to be // specified (it is always unbounded). Therefore it does not have any of the // corresponding constructors. // // The current precision of an ExactFloat (i.e., the number of bits in its // mantissa) is returned by prec(). The precision is increased as necessary // so that the result of every operation can be represented exactly. #ifndef S2_UTIL_MATH_EXACTFLOAT_EXACTFLOAT_H_ #define S2_UTIL_MATH_EXACTFLOAT_EXACTFLOAT_H_ #include #include #include #include #include #include #include #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "s2/base/port.h" class ExactFloat { public: // The following limits are imposed by OpenSSL. // The maximum exponent supported. If a value has an exponent larger than // this, it is replaced by infinity (with the appropriate sign). static const int kMaxExp = 200*1000*1000; // About 10**(60 million) // The minimum exponent supported. If a value has an exponent less than // this, it is replaced by zero (with the appropriate sign). static const int kMinExp = -kMaxExp; // About 10**(-60 million) // The maximum number of mantissa bits supported. If a value has more // mantissa bits than this, it is replaced with NaN. (It is expected that // users of this class will never want this much precision.) static const int kMaxPrec = 64 << 20; // About 20 million digits // Rounding modes. kRoundTiesToEven and kRoundTiesAwayFromZero both round // to the nearest representable value unless two values are equally close. // In that case kRoundTiesToEven rounds to the nearest even value, while // kRoundTiesAwayFromZero always rounds away from zero. enum RoundingMode { kRoundTiesToEven, kRoundTiesAwayFromZero, kRoundTowardZero, kRoundAwayFromZero, kRoundTowardPositive, kRoundTowardNegative }; ///////////////////////////////////////////////////////////////////////////// // Constructors // The default constructor initializes the value to zero. (The initial // value must be zero rather than NaN for compatibility with the built-in // float types.) inline ExactFloat(); // Construct an ExactFloat from a "double". The constructor is implicit so // that this class can be used as a replacement for "float" or "double" in // templatized libraries. (With an explicit constructor, code such as // "ExactFloat f = 2.5;" would not compile.) All double-precision values are // supported, including denormalized numbers, infinities, and NaNs. ExactFloat(double v); // Construct an ExactFloat from an "int". Note that in general, ints are // automatically converted to doubles and so would be handled by the // constructor above. However, the particular argument (0) would be // ambiguous; the compiler wouldn't know whether to treat it as a "double" or // "const char*" (since 0 is a valid null pointer constant). Adding an "int" // constructor solves this problem. // // We do not provide constructors for "unsigned", "long", "unsigned long", // "long long", or "unsigned long long", since these types are not typically // used in floating-point calculations and it is safer to require them to be // explicitly cast. ExactFloat(int v); // Construct an ExactFloat from a string (such as "1.2e50"). Requires that // the value is exactly representable as a floating-point number (so for // example, "0.125" is allowed but "0.1" is not). explicit ExactFloat(const char* s) { Unimplemented(); } // Copy constructor. ExactFloat(const ExactFloat& b); // The destructor is not virtual for efficiency reasons. Therefore no // subclass should declare additional fields that require destruction. inline ~ExactFloat() = default; ///////////////////////////////////////////////////////////////////// // Constants // // As an alternative to the constants below, you can also just use the // constants defined in , for example: // // ExactFloat x = NAN, y = -INFINITY; // Return an ExactFloat equal to positive zero (if sign >= 0) or // negative zero (if sign < 0). static ExactFloat SignedZero(int sign); // Return an ExactFloat equal to positive infinity (if sign >= 0) or // negative infinity (if sign < 0). static ExactFloat Infinity(int sign); // Return an ExactFloat that is NaN (Not-a-Number). static ExactFloat NaN(); ///////////////////////////////////////////////////////////////////////////// // Accessor Methods // Return the maximum precision of the ExactFloat. This method exists only // for compatibility with MPFloat. int max_prec() const { return kMaxPrec; } // Return the actual precision of this ExactFloat (the current number of // bits in its mantissa). Returns 0 for non-normal numbers such as NaN. int prec() const; // Return the exponent of this ExactFloat given that the mantissa is in the // range [0.5, 1). It is an error to call this method if the value is zero, // infinity, or NaN. int exp() const; // Set the value of the ExactFloat to +0 (if sign >= 0) or -0 (if sign < 0). void set_zero(int sign); // Set the value of the ExactFloat to positive infinity (if sign >= 0) or // negative infinity (if sign < 0). void set_inf(int sign); // Set the value of the ExactFloat to NaN (Not-a-Number). void set_nan(); // Unfortunately, isinf(x), isnan(x), isnormal(x), and isfinite(x) are // defined as macros in . Therefore we can't easily extend them // here. Instead we provide methods with underscores in their names that do // the same thing: x.is_inf(), etc. // // These macros are not implemented: signbit(x), fpclassify(x). // Return true if this value is zero (including negative zero). inline bool is_zero() const; // Return true if this value is infinity (positive or negative). inline bool is_inf() const; // Return true if this value is NaN (Not-a-Number). inline bool is_nan() const; // Return true if this value is a normal floating-point number. Non-normal // values (zero, infinity, and NaN) often need to be handled separately // because they are represented using special exponent values and their // mantissa is not defined. inline bool is_normal() const; // Return true if this value is a normal floating-point number or zero, // i.e. it is not infinity or NaN. inline bool is_finite() const; // Return true if the sign bit is set (this includes negative zero). inline bool sign_bit() const; // Return +1 if this ExactFloat is positive, -1 if it is negative, and 0 // if it is zero or NaN. Note that unlike sign_bit(), sgn() returns 0 for // both positive and negative zero. inline int sgn() const; ///////////////////////////////////////////////////////////////////////////// // Conversion Methods // // Note that some conversions are defined as functions further below, // e.g. to convert to an integer you can use lround(), llrint(), etc. // Round to double precision. Note that since doubles have a much smaller // exponent range than ExactFloats, very small values may be rounded to // (positive or negative) zero, and very large values may be rounded to // infinity. // // It is very important to make this a named method rather than an implicit // conversion, because otherwise there would be a silent loss of precision // whenever some desired operator or function happens not to be implemented. // For example, if fabs() were not implemented and "x" and "y" were // ExactFloats, then x = fabs(y) would silently convert "y" to a "double", // take its absolute value, and convert it back to an ExactFloat. double ToDouble() const; // Return a human-readable string such that if two values with the same // precision are different, then their string representations are different. // The format is similar to printf("%g"), except that the number of // significant digits depends on the precision (with a minimum of 10). // Trailing zeros are stripped (just like "%g"). // // Note that if two values have different precisions, they may have the same // ToString() value even though their values are slightly different. If you // need to distinguish such values, use ToUniqueString() intead. std::string ToString() const; // Return a string formatted according to printf("%Ng") where N is the given // maximum number of significant digits. std::string ToStringWithMaxDigits(int max_digits) const; // Return a human-readable string such that if two ExactFloats have different // values, then their string representations are always different. This // method is useful for debugging. The string has the form "value", // where "prec" is the actual precision of the ExactFloat (e.g., "0.215<50>"). std::string ToUniqueString() const; // Return an upper bound on the number of significant digits required to // distinguish any two floating-point numbers with the given precision when // they are formatted as decimal strings in exponential format. static int NumSignificantDigitsForPrec(int prec); // Output the ExactFloat in human-readable format, e.g. for logging. friend std::ostream& operator<<(std::ostream& o, ExactFloat const& f) { return o << f.ToString(); } ///////////////////////////////////////////////////////////////////////////// // Other Methods // Round the ExactFloat so that its mantissa has at most "max_prec" bits // using the given rounding mode. Requires "max_prec" to be at least 2 // (since kRoundTiesToEven doesn't make sense with fewer bits than this). ExactFloat RoundToMaxPrec(int max_prec, RoundingMode mode) const; ///////////////////////////////////////////////////////////////////////////// // Operators // Assignment operator. ExactFloat& operator=(const ExactFloat& b); // Unary plus. ExactFloat operator+() const { return *this; } // Unary minus. ExactFloat operator-() const; // Addition. friend ExactFloat operator+(const ExactFloat& a, const ExactFloat& b); // Subtraction. friend ExactFloat operator-(const ExactFloat& a, const ExactFloat& b); // Multiplication. friend ExactFloat operator*(const ExactFloat& a, const ExactFloat& b); // Division is not implemented because the result cannot be represented // exactly in general. Doing this properly would require extending all the // operations to support rounding to a specified precision. // Arithmetic assignment operators (+=, -=, *=). ExactFloat& operator+=(const ExactFloat& b) { return (*this = *this + b); } ExactFloat& operator-=(const ExactFloat& b) { return (*this = *this - b); } ExactFloat& operator*=(const ExactFloat& b) { return (*this = *this * b); } // Comparison operators (==, !=, <, <=, >, >=). friend bool operator==(const ExactFloat& a, const ExactFloat& b); friend bool operator<(const ExactFloat& a, const ExactFloat& b); // These don't need to be friends but are declared here for completeness. inline friend bool operator!=(const ExactFloat& a, const ExactFloat& b); inline friend bool operator<=(const ExactFloat& a, const ExactFloat& b); inline friend bool operator>(const ExactFloat& a, const ExactFloat& b); inline friend bool operator>=(const ExactFloat& a, const ExactFloat& b); ///////////////////////////////////////////////////////////////////// // Math Intrinsics // // The math intrinsics currently supported by ExactFloat are listed below. // Except as noted, they behave identically to the usual glibc intrinsics // except that they have greater precision. See the man pages for more // information. //////// Miscellaneous simple arithmetic functions. // Absolute value. friend ExactFloat fabs(const ExactFloat& a); friend ExactFloat abs(const ExactFloat& a); // Maximum of two values. friend ExactFloat fmax(const ExactFloat& a, const ExactFloat& b); // Minimum of two values. friend ExactFloat fmin(const ExactFloat& a, const ExactFloat& b); // Positive difference: max(a - b, 0). friend ExactFloat fdim(const ExactFloat& a, const ExactFloat& b); //////// Integer rounding functions that return ExactFloat values. // Round up to the nearest integer. friend ExactFloat ceil(const ExactFloat& a); // Round down to the nearest integer. friend ExactFloat floor(const ExactFloat& a); // Round to the nearest integer not larger in absolute value. // For example: f(-1.9) = -1, f(2.9) = 2. friend ExactFloat trunc(const ExactFloat& a); // Round to the nearest integer, rounding halfway cases away from zero. // For example: f(-0.5) = -1, f(0.5) = 1, f(1.5) = 2, f(2.5) = 3. friend ExactFloat round(const ExactFloat& a); // Round to the nearest integer, rounding halfway cases to an even integer. // For example: f(-0.5) = 0, f(0.5) = 0, f(1.5) = 2, f(2.5) = 2. friend ExactFloat rint(const ExactFloat& a); // A synonym for rint(). friend ExactFloat nearbyint(const ExactFloat& a) { return rint(a); } //////// Integer rounding functions that return C++ integer types. // Like rint(), but rounds to the nearest "long" value. Returns the // minimum/maximum possible integer if the value is out of range. friend long lrint(const ExactFloat& a); // Like rint(), but rounds to the nearest "long long" value. Returns the // minimum/maximum possible integer if the value is out of range. friend long long llrint(const ExactFloat& a); // Like round(), but rounds to the nearest "long" value. Returns the // minimum/maximum possible integer if the value is out of range. friend long lround(const ExactFloat& a); // Like round(), but rounds to the nearest "long long" value. Returns the // minimum/maximum possible integer if the value is out of range. friend long long llround(const ExactFloat& a); //////// Remainder functions. // The remainder of dividing "a" by "b", where the quotient is rounded toward // zero to the nearest integer. Similar to (a - trunc(a / b) * b). friend ExactFloat fmod(const ExactFloat& a, const ExactFloat& b) { // Note that it is possible to implement this operation exactly, it just // hasn't been done. return Unimplemented(); } // The remainder of dividing "a" by "b", where the quotient is rounded to the // nearest integer, rounding halfway cases to an even integer. Similar to // (a - rint(a / b) * b). friend ExactFloat remainder(const ExactFloat& a, const ExactFloat& b) { // Note that it is possible to implement this operation exactly, it just // hasn't been done. return Unimplemented(); } // A synonym for remainder(). friend ExactFloat drem(const ExactFloat& a, const ExactFloat& b) { return remainder(a, b); } // Break the argument "a" into integer and fractional parts, each of which // has the same sign as "a". The fractional part is returned, and the // integer part is stored in the output parameter "i_ptr". Both output // values are set to have the same maximum precision as "a". friend ExactFloat modf(const ExactFloat& a, ExactFloat* i_ptr) { // Note that it is possible to implement this operation exactly, it just // hasn't been done. return Unimplemented(); } //////// Floating-point manipulation functions. // Return an ExactFloat with the magnitude of "a" and the sign bit of "b". // (Note that an IEEE zero can be either positive or negative.) friend ExactFloat copysign(const ExactFloat& a, const ExactFloat& b); // Convert "a" to a normalized fraction in the range [0.5, 1) times a power // of two. Return the fraction and set "exp" to the exponent. If "a" is // zero, infinity, or NaN then return "a" and set "exp" to zero. friend ExactFloat frexp(const ExactFloat& a, int* exp); // Return "a" multiplied by 2 raised to the power "exp". friend ExactFloat ldexp(const ExactFloat& a, int exp); // A synonym for ldexp(). friend ExactFloat scalbn(const ExactFloat& a, int exp) { return ldexp(a, exp); } // A version of ldexp() where "exp" is a long integer. friend ExactFloat scalbln(const ExactFloat& a, long exp); // Convert "a" to a normalized fraction in the range [1,2) times a power of // two, and return the exponent value as an integer. This is equivalent to // lrint(floor(log2(fabs(a)))) but it is computed more efficiently. Returns // the constants documented in the man page for zero, infinity, or NaN. friend int ilogb(const ExactFloat& a); // Convert "a" to a normalized fraction in the range [1,2) times a power of // two, and return the exponent value as an ExactFloat. This is equivalent to // floor(log2(fabs(a))) but it is computed more efficiently. friend ExactFloat logb(const ExactFloat& a); protected: // OpenSSL >= 1.1 does not have BN_init, and does not support stack- // allocated BIGNUMS. We use BN_init when possible, but BN_new otherwise. // If the performance penalty is too high, an object pool can be added // in the future. #if defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER < 0x10100000L // BoringSSL and OpenSSL < 1.1 support stack allocated BIGNUMs and BN_init. class BigNum { public: BigNum() { BN_init(&bn_); } // Prevent accidental, expensive, copying. BigNum(const BigNum&) = delete; BigNum& operator=(const BigNum&) = delete; ~BigNum() { BN_free(&bn_); } BIGNUM* get() { return &bn_; } const BIGNUM* get() const { return &bn_; } private: BIGNUM bn_; }; #else class BigNum { public: BigNum() : bn_(BN_new()) {} BigNum(const BigNum&) = delete; BigNum& operator=(const BigNum&) = delete; ~BigNum() { BN_free(bn_); } BIGNUM* get() { return bn_; } const BIGNUM* get() const { return bn_; } private: BIGNUM* bn_; }; #endif // Non-normal numbers are represented using special exponent values and a // mantissa of zero. Do not change these values; methods such as // is_normal() make assumptions about their ordering. Non-normal numbers // can have either a positive or negative sign (including zero and NaN). static const int32 kExpNaN = INT_MAX; static const int32 kExpInfinity = INT_MAX - 1; static const int32 kExpZero = INT_MAX - 2; // Normal numbers are represented as (sign_ * bn_ * (2 ** bn_exp_)), where: // - sign_ is either +1 or -1 // - bn_ is a BIGNUM with a positive value // - bn_exp_ is the base-2 exponent applied to bn_. int32 sign_; int32 bn_exp_; BigNum bn_; // A standard IEEE "double" has a 53-bit mantissa consisting of a 52-bit // fraction plus an implicit leading "1" bit. static const int kDoubleMantissaBits = 53; // Convert an ExactFloat with no more than 53 bits in its mantissa to a // "double". This method handles non-normal values (NaN, etc). double ToDoubleHelper() const; // Round an ExactFloat so that it is a multiple of (2 ** bit_exp), using the // given rounding mode. ExactFloat RoundToPowerOf2(int bit_exp, RoundingMode mode) const; // Convert the ExactFloat to a decimal value of the form 0.ddd * (10 ** x), // with at most "max_digits" significant digits (trailing zeros are removed). // Set (*digits) to the ASCII digits and return the decimal exponent "x". int GetDecimalDigits(int max_digits, std::string* digits) const; // Return a_sign * fabs(a) + b_sign * fabs(b). Used to implement addition // and subtraction. static ExactFloat SignedSum(int a_sign, const ExactFloat* a, int b_sign, const ExactFloat* b); // Convert an ExactFloat to its canonical form. Underflow results in signed // zero, overflow results in signed infinity, and precision overflow results // in NaN. A zero mantissa is converted to the canonical zero value with // the given sign; otherwise the mantissa is normalized so that its low bit // is 1. Non-normal numbers are left unchanged. void Canonicalize(); // Scale the mantissa of this ExactFloat so that it has the same bn_exp_ as // "b", then return -1, 0, or 1 according to whether the scaled mantissa is // less, equal, or greater than the mantissa of "b". Requires that both // values are normal. int ScaleAndCompare(const ExactFloat& b) const; // Return true if the magnitude of this ExactFloat is less than the // magnitude of "b". Requires that neither value is NaN. bool UnsignedLess(const ExactFloat& b) const; // Return an ExactFloat with the magnitude of this ExactFloat and the given // sign. (Similar to copysign, except that the sign is given explicitly // rather than being copied from another ExactFloat.) inline ExactFloat CopyWithSign(int sign) const; // Convert an ExactFloat to an integer of type "T" using the given rounding // mode. The type "T" must be signed. Returns the largest possible integer // for NaN, and clamps out of range values to the largest or smallest // possible values. template T ToInteger(RoundingMode mode) const; // Log a fatal error message (used for unimplemented methods). static ExactFloat Unimplemented(); }; ///////////////////////////////////////////////////////////////////////// // Implementation details follow: inline ExactFloat::ExactFloat() : sign_(1), bn_exp_(kExpZero) { } inline bool ExactFloat::is_zero() const { return bn_exp_ == kExpZero; } inline bool ExactFloat::is_inf() const { return bn_exp_ == kExpInfinity; } inline bool ExactFloat::is_nan() const { return bn_exp_ == kExpNaN; } inline bool ExactFloat::is_normal() const { return bn_exp_ < kExpZero; } inline bool ExactFloat::is_finite() const { return bn_exp_ <= kExpZero; } inline bool ExactFloat::sign_bit() const { return sign_ < 0; } inline int ExactFloat::sgn() const { return (is_nan() || is_zero()) ? 0 : sign_; } inline bool operator!=(const ExactFloat& a, const ExactFloat& b) { return !(a == b); } inline bool operator<=(const ExactFloat& a, const ExactFloat& b) { // NaN is unordered compared to everything, including itself. if (a.is_nan() || b.is_nan()) return false; return !(b < a); } inline bool operator>(const ExactFloat& a, const ExactFloat& b) { return b < a; } inline bool operator>=(const ExactFloat& a, const ExactFloat& b) { return b <= a; } inline ExactFloat ExactFloat::CopyWithSign(int sign) const { ExactFloat r = *this; r.sign_ = sign; return r; } #endif // S2_UTIL_MATH_EXACTFLOAT_EXACTFLOAT_H_ s2/src/s2/util/math/exactfloat/exactfloat.cc0000644000176200001440000006611414530411473020424 0ustar liggesusers// Copyright 2009 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/util/math/exactfloat/exactfloat.h" #include #include #include #include #include #include #include #include // for OPENSSL_free #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "absl/base/macros.h" #include "absl/container/fixed_array.h" using std::max; using std::min; // Define storage for constants. const int ExactFloat::kMinExp; const int ExactFloat::kMaxExp; const int ExactFloat::kMaxPrec; const int32 ExactFloat::kExpNaN; const int32 ExactFloat::kExpInfinity; const int32 ExactFloat::kExpZero; const int ExactFloat::kDoubleMantissaBits; // To simplify the overflow/underflow logic, we limit the exponent and // precision range so that (2 * bn_exp_) does not overflow an "int". We take // advantage of this, for example, by only checking for overflow/underflow // *after* multiplying two numbers. static_assert( ExactFloat::kMaxExp <= INT_MAX / 2 && ExactFloat::kMinExp - ExactFloat::kMaxPrec >= INT_MIN / 2, "exactfloat exponent might overflow"); // We define a few simple extensions to the OpenSSL's BIGNUM interface. // In some cases these depend on BIGNUM internal fields, so they might // require tweaking if the BIGNUM implementation changes significantly. // These are just thin wrappers for BoringSSL. #ifdef OPENSSL_IS_BORINGSSL inline static void BN_ext_set_uint64(BIGNUM* bn, uint64 v) { S2_CHECK(BN_set_u64(bn, v)); } // Return the absolute value of a BIGNUM as a 64-bit unsigned integer. // Requires that BIGNUM fits into 64 bits. inline static uint64 BN_ext_get_uint64(const BIGNUM* bn) { uint64_t u64; if (!BN_get_u64(bn, &u64)) { S2_DCHECK(false) << "BN has " << BN_num_bits(bn) << " bits"; return 0; } return u64; } static int BN_ext_count_low_zero_bits(const BIGNUM* bn) { return BN_count_low_zero_bits(bn); } #else // !defined(OPENSSL_IS_BORINGSSL) // Set a BIGNUM to the given unsigned 64-bit value. inline static void BN_ext_set_uint64(BIGNUM* bn, uint64 v) { #if BN_BITS2 == 64 S2_CHECK(BN_set_word(bn, v)); #else static_assert(BN_BITS2 == 32, "at least 32 bit openssl build needed"); S2_CHECK(BN_set_word(bn, static_cast(v >> 32))); S2_CHECK(BN_lshift(bn, bn, 32)); S2_CHECK(BN_add_word(bn, static_cast(v))); #endif } #if OPENSSL_VERSION_NUMBER < 0x10100000L // Return the absolute value of a BIGNUM as a 64-bit unsigned integer. // Requires that BIGNUM fits into 64 bits. inline static uint64 BN_ext_get_uint64(const BIGNUM* bn) { S2_DCHECK_LE(BN_num_bytes(bn), sizeof(uint64)); #if BN_BITS2 == 64 return BN_get_word(bn); #else static_assert(BN_BITS2 == 32, "at least 32 bit openssl build needed"); if (bn->top == 0) return 0; if (bn->top == 1) return BN_get_word(bn); S2_DCHECK_EQ(bn->top, 2); return (static_cast(bn->d[1]) << 32) + bn->d[0]; #endif } // Count the number of low-order zero bits in the given BIGNUM (ignoring its // sign). Returns 0 if the argument is zero. static int BN_ext_count_low_zero_bits(const BIGNUM* bn) { int count = 0; for (int i = 0; i < bn->top; ++i) { BN_ULONG w = bn->d[i]; if (w == 0) { count += 8 * sizeof(BN_ULONG); } else { for (; (w & 1) == 0; w >>= 1) { ++count; } break; } } return count; } #else // OPENSSL_VERSION_NUMBER >= 0x10100000L // Return the absolute value of a BIGNUM as a 64-bit unsigned integer. // Requires that BIGNUM fits into 64 bits. inline static uint64 BN_ext_get_uint64(const BIGNUM* bn) { uint64 r; #ifdef IS_LITTLE_ENDIAN S2_CHECK_EQ(BN_bn2lebinpad(bn, reinterpret_cast(&r), sizeof(r)), sizeof(r)); #elif IS_BIG_ENDIAN S2_CHECK_EQ(BN_bn2binpad(bn, reinterpret_cast(&r), sizeof(r)), sizeof(r)); #else #error one of IS_LITTLE_ENDIAN or IS_BIG_ENDIAN should be defined! #endif return r; } static int BN_ext_count_low_zero_bits(const BIGNUM* bn) { // In OpenSSL >= 1.1, BIGNUM is an opaque type, so d and top // cannot be accessed. The bytes must be copied out at a ~25% // performance penalty. absl::FixedArray bytes(BN_num_bytes(bn)); // "le" indicates little endian. S2_CHECK_EQ(BN_bn2lebinpad(bn, bytes.data(), bytes.size()), bytes.size()); int count = 0; for (unsigned char c : bytes) { if (c == 0) { count += 8; } else { for (; (c & 1) == 0; c >>= 1) { ++count; } break; } } return count; } #endif // OPENSSL_VERSION_NUMBER >= 0x10100000L #endif // !defined(OPENSSL_IS_BORINGSSL) ExactFloat::ExactFloat(double v) { sign_ = std::signbit(v) ? -1 : 1; if (std::isnan(v)) { set_nan(); } else if (std::isinf(v)) { set_inf(sign_); } else { // The following code is much simpler than messing about with bit masks, // has the advantage of handling denormalized numbers and zero correctly, // and is actually quite efficient (at least compared to the rest of this // code). "f" is a fraction in the range [0.5, 1), so if we shift it left // by the number of mantissa bits in a double (53, including the leading // "1") then the result is always an integer. int exp; double f = frexp(fabs(v), &exp); uint64 m = static_cast(ldexp(f, kDoubleMantissaBits)); BN_ext_set_uint64(bn_.get(), m); bn_exp_ = exp - kDoubleMantissaBits; Canonicalize(); } } ExactFloat::ExactFloat(int v) { sign_ = (v >= 0) ? 1 : -1; // Note that this works even for INT_MIN because the parameter type for // BN_set_word() is unsigned. S2_CHECK(BN_set_word(bn_.get(), abs(v))); bn_exp_ = 0; Canonicalize(); } ExactFloat::ExactFloat(const ExactFloat& b) : sign_(b.sign_), bn_exp_(b.bn_exp_) { BN_copy(bn_.get(), b.bn_.get()); } ExactFloat ExactFloat::SignedZero(int sign) { ExactFloat r; r.set_zero(sign); return r; } ExactFloat ExactFloat::Infinity(int sign) { ExactFloat r; r.set_inf(sign); return r; } ExactFloat ExactFloat::NaN() { ExactFloat r; r.set_nan(); return r; } int ExactFloat::prec() const { return BN_num_bits(bn_.get()); } int ExactFloat::exp() const { S2_DCHECK(is_normal()); return bn_exp_ + BN_num_bits(bn_.get()); } void ExactFloat::set_zero(int sign) { sign_ = sign; bn_exp_ = kExpZero; if (!BN_is_zero(bn_.get())) BN_zero(bn_.get()); } void ExactFloat::set_inf(int sign) { sign_ = sign; bn_exp_ = kExpInfinity; if (!BN_is_zero(bn_.get())) BN_zero(bn_.get()); } void ExactFloat::set_nan() { sign_ = 1; bn_exp_ = kExpNaN; if (!BN_is_zero(bn_.get())) BN_zero(bn_.get()); } double ExactFloat::ToDouble() const { // If the mantissa has too many bits, we need to round it. if (prec() <= kDoubleMantissaBits) { return ToDoubleHelper(); } else { ExactFloat r = RoundToMaxPrec(kDoubleMantissaBits, kRoundTiesToEven); return r.ToDoubleHelper(); } } double ExactFloat::ToDoubleHelper() const { S2_DCHECK_LE(BN_num_bits(bn_.get()), kDoubleMantissaBits); if (!is_normal()) { if (is_zero()) return copysign(0, sign_); if (is_inf()) { return std::copysign(std::numeric_limits::infinity(), sign_); } return std::copysign(std::numeric_limits::quiet_NaN(), sign_); } uint64 d_mantissa = BN_ext_get_uint64(bn_.get()); // We rely on ldexp() to handle overflow and underflow. (It will return a // signed zero or infinity if the result is too small or too large.) return sign_ * ldexp(static_cast(d_mantissa), bn_exp_); } ExactFloat ExactFloat::RoundToMaxPrec(int max_prec, RoundingMode mode) const { // The "kRoundTiesToEven" mode requires at least 2 bits of precision // (otherwise both adjacent representable values may be odd). S2_DCHECK_GE(max_prec, 2); S2_DCHECK_LE(max_prec, kMaxPrec); // The following test also catches zero, infinity, and NaN. int shift = prec() - max_prec; if (shift <= 0) return *this; // Round by removing the appropriate number of bits from the mantissa. Note // that if the value is rounded up to a power of 2, the high-order bit // position may increase, but in that case Canonicalize() will remove at // least one zero bit and so the output will still have prec() <= max_prec. return RoundToPowerOf2(bn_exp_ + shift, mode); } ExactFloat ExactFloat::RoundToPowerOf2(int bit_exp, RoundingMode mode) const { S2_DCHECK_GE(bit_exp, kMinExp - kMaxPrec); S2_DCHECK_LE(bit_exp, kMaxExp); // If the exponent is already large enough, or the value is zero, infinity, // or NaN, then there is nothing to do. int shift = bit_exp - bn_exp_; if (shift <= 0) return *this; S2_DCHECK(is_normal()); // Convert rounding up/down to toward/away from zero, so that we don't need // to consider the sign of the number from this point onward. if (mode == kRoundTowardPositive) { mode = (sign_ > 0) ? kRoundAwayFromZero : kRoundTowardZero; } else if (mode == kRoundTowardNegative) { mode = (sign_ > 0) ? kRoundTowardZero : kRoundAwayFromZero; } // Rounding consists of right-shifting the mantissa by "shift", and then // possibly incrementing the result (depending on the rounding mode, the // bits that were discarded, and sometimes the lowest kept bit). The // following code figures out whether we need to increment. ExactFloat r; bool increment = false; if (mode == kRoundTowardZero) { // Never increment. } else if (mode == kRoundTiesAwayFromZero) { // Increment if the highest discarded bit is 1. if (BN_is_bit_set(bn_.get(), shift - 1)) increment = true; } else if (mode == kRoundAwayFromZero) { // Increment unless all discarded bits are zero. if (BN_ext_count_low_zero_bits(bn_.get()) < shift) increment = true; } else { S2_DCHECK_EQ(mode, kRoundTiesToEven); // Let "w/xyz" denote a mantissa where "w" is the lowest kept bit and // "xyz" are the discarded bits. Then using regexp notation: // ./0.* -> Don't increment (fraction < 1/2) // 0/10* -> Don't increment (fraction = 1/2, kept part even) // 1/10* -> Increment (fraction = 1/2, kept part odd) // ./1.*1.* -> Increment (fraction > 1/2) if (BN_is_bit_set(bn_.get(), shift - 1) && ((BN_is_bit_set(bn_.get(), shift) || BN_ext_count_low_zero_bits(bn_.get()) < shift - 1))) { increment = true; } } r.bn_exp_ = bn_exp_ + shift; S2_CHECK(BN_rshift(r.bn_.get(), bn_.get(), shift)); if (increment) { S2_CHECK(BN_add_word(r.bn_.get(), 1)); } r.sign_ = sign_; r.Canonicalize(); return r; } int ExactFloat::NumSignificantDigitsForPrec(int prec) { // The simplest bound is // // d <= 1 + ceil(prec * log10(2)) // // The following bound is tighter by 0.5 digits on average, but requires // the exponent to be known as well: // // d <= ceil(exp * log10(2)) - floor((exp - prec) * log10(2)) // // Since either of these bounds can be too large by 0, 1, or 2 digits, we // stick with the simpler first bound. return static_cast(1 + ceil(prec * (M_LN2 / M_LN10))); } // Numbers are always formatted with at least this many significant digits. // This prevents small integers from being formatted in exponential notation // (e.g. 1024 formatted as 1e+03), and also avoids the confusion of having // supposedly "high precision" numbers formatted with just 1 or 2 digits // (e.g. 1/512 == 0.001953125 formatted as 0.002). static const int kMinSignificantDigits = 10; std::string ExactFloat::ToString() const { int max_digits = max(kMinSignificantDigits, NumSignificantDigitsForPrec(prec())); return ToStringWithMaxDigits(max_digits); } std::string ExactFloat::ToStringWithMaxDigits(int max_digits) const { S2_DCHECK_GT(max_digits, 0); if (!is_normal()) { if (is_nan()) return "nan"; if (is_zero()) return (sign_ < 0) ? "-0" : "0"; return (sign_ < 0) ? "-inf" : "inf"; } std::string digits; int exp10 = GetDecimalDigits(max_digits, &digits); std::string str; if (sign_ < 0) str.push_back('-'); // We use the standard '%g' formatting rules. If the exponent is less than // -4 or greater than or equal to the requested precision (i.e., max_digits) // then we use exponential notation. // // But since "exp10" is the base-10 exponent corresponding to a mantissa in // the range [0.1, 1), whereas the '%g' rules assume a mantissa in the range // [1.0, 10), we need to adjust these parameters by 1. if (exp10 <= -4 || exp10 > max_digits) { // Use exponential format. str.push_back(digits[0]); if (digits.size() > 1) { str.push_back('.'); str.append(digits.begin() + 1, digits.end()); } char exp_buf[20]; snprintf(exp_buf, sizeof(exp_buf), "e%+02d", exp10 - 1); str += exp_buf; } else { // Use fixed format. We split this into two cases depending on whether // the integer portion is non-zero or not. if (exp10 > 0) { if (exp10 >= digits.size()) { str += digits; for (int i = exp10 - digits.size(); i > 0; --i) { str.push_back('0'); } } else { str.append(digits.begin(), digits.begin() + exp10); str.push_back('.'); str.append(digits.begin() + exp10, digits.end()); } } else { str += "0."; for (int i = exp10; i < 0; ++i) { str.push_back('0'); } str += digits; } } return str; } // Increment an unsigned integer represented as a string of ASCII digits. static void IncrementDecimalDigits(std::string* digits) { std::string::iterator pos = digits->end(); while (--pos >= digits->begin()) { if (*pos < '9') { ++*pos; return; } *pos = '0'; } digits->insert(0, "1"); } int ExactFloat::GetDecimalDigits(int max_digits, std::string* digits) const { S2_DCHECK(is_normal()); // Convert the value to the form (bn * (10 ** bn_exp10)) where "bn" is a // positive integer (BIGNUM). BIGNUM* bn = BN_new(); int bn_exp10; if (bn_exp_ >= 0) { // The easy case: bn = bn_ * (2 ** bn_exp_)), bn_exp10 = 0. S2_CHECK(BN_lshift(bn, bn_.get(), bn_exp_)); bn_exp10 = 0; } else { // Set bn = bn_ * (5 ** -bn_exp_) and bn_exp10 = bn_exp_. This is // equivalent to the original value of (bn_ * (2 ** bn_exp_)). BIGNUM* power = BN_new(); S2_CHECK(BN_set_word(power, -bn_exp_)); S2_CHECK(BN_set_word(bn, 5)); BN_CTX* ctx = BN_CTX_new(); S2_CHECK(BN_exp(bn, bn, power, ctx)); S2_CHECK(BN_mul(bn, bn, bn_.get(), ctx)); BN_CTX_free(ctx); BN_free(power); bn_exp10 = bn_exp_; } // Now convert "bn" to a decimal string. char* all_digits = BN_bn2dec(bn); S2_DCHECK(all_digits != nullptr); BN_free(bn); // Check whether we have too many digits and round if necessary. int num_digits = strlen(all_digits); if (num_digits <= max_digits) { *digits = all_digits; } else { digits->assign(all_digits, max_digits); // Standard "printf" formatting rounds ties to an even number. This means // that we round up (away from zero) if highest discarded digit is '5' or // more, unless all other discarded digits are zero in which case we round // up only if the lowest kept digit is odd. if (all_digits[max_digits] >= '5' && ((all_digits[max_digits-1] & 1) == 1 || strpbrk(all_digits + max_digits + 1, "123456789") != nullptr)) { // This can increase the number of digits by 1, but in that case at // least one trailing zero will be stripped off below. IncrementDecimalDigits(digits); } // Adjust the base-10 exponent to reflect the digits we have removed. bn_exp10 += num_digits - max_digits; } OPENSSL_free(all_digits); // Now strip any trailing zeros. S2_DCHECK_NE((*digits)[0], '0'); std::string::iterator pos = digits->end(); while (pos[-1] == '0') --pos; if (pos < digits->end()) { bn_exp10 += digits->end() - pos; digits->erase(pos, digits->end()); } S2_DCHECK_LE(digits->size(), max_digits); // Finally, we adjust the base-10 exponent so that the mantissa is a // fraction in the range [0.1, 1) rather than an integer. return bn_exp10 + digits->size(); } std::string ExactFloat::ToUniqueString() const { char prec_buf[20]; snprintf(prec_buf, sizeof(prec_buf), "<%d>", prec()); return ToString() + prec_buf; } ExactFloat& ExactFloat::operator=(const ExactFloat& b) { if (this != &b) { sign_ = b.sign_; bn_exp_ = b.bn_exp_; BN_copy(bn_.get(), b.bn_.get()); } return *this; } ExactFloat ExactFloat::operator-() const { return CopyWithSign(-sign_); } ExactFloat operator+(const ExactFloat& a, const ExactFloat& b) { return ExactFloat::SignedSum(a.sign_, &a, b.sign_, &b); } ExactFloat operator-(const ExactFloat& a, const ExactFloat& b) { return ExactFloat::SignedSum(a.sign_, &a, -b.sign_, &b); } ExactFloat ExactFloat::SignedSum(int a_sign, const ExactFloat* a, int b_sign, const ExactFloat* b) { if (!a->is_normal() || !b->is_normal()) { // Handle zero, infinity, and NaN according to IEEE 754-2008. if (a->is_nan()) return *a; if (b->is_nan()) return *b; if (a->is_inf()) { // Adding two infinities with opposite sign yields NaN. if (b->is_inf() && a_sign != b_sign) return NaN(); return Infinity(a_sign); } if (b->is_inf()) return Infinity(b_sign); if (a->is_zero()) { if (!b->is_zero()) return b->CopyWithSign(b_sign); // Adding two zeros with the same sign preserves the sign. if (a_sign == b_sign) return SignedZero(a_sign); // Adding two zeros of opposite sign produces +0. return SignedZero(+1); } S2_DCHECK(b->is_zero()); return a->CopyWithSign(a_sign); } // Swap the numbers if necessary so that "a" has the larger bn_exp_. if (a->bn_exp_ < b->bn_exp_) { using std::swap; swap(a_sign, b_sign); swap(a, b); } // Shift "a" if necessary so that both values have the same bn_exp_. ExactFloat r; if (a->bn_exp_ > b->bn_exp_) { S2_CHECK(BN_lshift(r.bn_.get(), a->bn_.get(), a->bn_exp_ - b->bn_exp_)); a = &r; // The only field of "a" used below is bn_. } r.bn_exp_ = b->bn_exp_; if (a_sign == b_sign) { S2_CHECK(BN_add(r.bn_.get(), a->bn_.get(), b->bn_.get())); r.sign_ = a_sign; } else { // Note that the BIGNUM documentation is out of date -- all methods now // allow the result to be the same as any input argument, so it is okay if // (a == &r) due to the shift above. S2_CHECK(BN_sub(r.bn_.get(), a->bn_.get(), b->bn_.get())); if (BN_is_zero(r.bn_.get())) { r.sign_ = +1; } else if (BN_is_negative(r.bn_.get())) { // The magnitude of "b" was larger. r.sign_ = b_sign; BN_set_negative(r.bn_.get(), false); } else { // They were equal, or the magnitude of "a" was larger. r.sign_ = a_sign; } } r.Canonicalize(); return r; } void ExactFloat::Canonicalize() { if (!is_normal()) return; // Underflow/overflow occurs if exp() is not in [kMinExp, kMaxExp]. // We also convert a zero mantissa to signed zero. int my_exp = exp(); if (my_exp < kMinExp || BN_is_zero(bn_.get())) { set_zero(sign_); } else if (my_exp > kMaxExp) { set_inf(sign_); } else if (!BN_is_odd(bn_.get())) { // Remove any low-order zero bits from the mantissa. S2_DCHECK(!BN_is_zero(bn_.get())); int shift = BN_ext_count_low_zero_bits(bn_.get()); if (shift > 0) { S2_CHECK(BN_rshift(bn_.get(), bn_.get(), shift)); bn_exp_ += shift; } } // If the mantissa has too many bits, we replace it by NaN to indicate // that an inexact calculation has occurred. if (prec() > kMaxPrec) { set_nan(); } } ExactFloat operator*(const ExactFloat& a, const ExactFloat& b) { int result_sign = a.sign_ * b.sign_; if (!a.is_normal() || !b.is_normal()) { // Handle zero, infinity, and NaN according to IEEE 754-2008. if (a.is_nan()) return a; if (b.is_nan()) return b; if (a.is_inf()) { // Infinity times zero yields NaN. if (b.is_zero()) return ExactFloat::NaN(); return ExactFloat::Infinity(result_sign); } if (b.is_inf()) { if (a.is_zero()) return ExactFloat::NaN(); return ExactFloat::Infinity(result_sign); } S2_DCHECK(a.is_zero() || b.is_zero()); return ExactFloat::SignedZero(result_sign); } ExactFloat r; r.sign_ = result_sign; r.bn_exp_ = a.bn_exp_ + b.bn_exp_; BN_CTX* ctx = BN_CTX_new(); S2_CHECK(BN_mul(r.bn_.get(), a.bn_.get(), b.bn_.get(), ctx)); BN_CTX_free(ctx); r.Canonicalize(); return r; } bool operator==(const ExactFloat& a, const ExactFloat& b) { // NaN is not equal to anything, not even itself. if (a.is_nan() || b.is_nan()) return false; // Since Canonicalize() strips low-order zero bits, all other cases // (including non-normal values) require bn_exp_ to be equal. if (a.bn_exp_ != b.bn_exp_) return false; // Positive and negative zero are equal. if (a.is_zero() && b.is_zero()) return true; // Otherwise, the signs and mantissas must match. Note that non-normal // values such as infinity have a mantissa of zero. return a.sign_ == b.sign_ && BN_ucmp(a.bn_.get(), b.bn_.get()) == 0; } int ExactFloat::ScaleAndCompare(const ExactFloat& b) const { S2_DCHECK(is_normal() && b.is_normal() && bn_exp_ >= b.bn_exp_); ExactFloat tmp = *this; S2_CHECK(BN_lshift(tmp.bn_.get(), tmp.bn_.get(), bn_exp_ - b.bn_exp_)); return BN_ucmp(tmp.bn_.get(), b.bn_.get()); } bool ExactFloat::UnsignedLess(const ExactFloat& b) const { // Handle the zero/infinity cases (NaN has already been done). if (is_inf() || b.is_zero()) return false; if (is_zero() || b.is_inf()) return true; // If the high-order bit positions differ, we are done. int cmp = exp() - b.exp(); if (cmp != 0) return cmp < 0; // Otherwise shift one of the two values so that they both have the same // bn_exp_ and then compare the mantissas. return (bn_exp_ >= b.bn_exp_ ? ScaleAndCompare(b) < 0 : b.ScaleAndCompare(*this) > 0); } bool operator<(const ExactFloat& a, const ExactFloat& b) { // NaN is unordered compared to everything, including itself. if (a.is_nan() || b.is_nan()) return false; // Positive and negative zero are equal. if (a.is_zero() && b.is_zero()) return false; // Otherwise, anything negative is less than anything positive. if (a.sign_ != b.sign_) return a.sign_ < b.sign_; // Now we just compare absolute values. return (a.sign_ > 0) ? a.UnsignedLess(b) : b.UnsignedLess(a); } ExactFloat fabs(const ExactFloat& a) { return abs(a); } ExactFloat abs(const ExactFloat& a) { return a.CopyWithSign(+1); } ExactFloat fmax(const ExactFloat& a, const ExactFloat& b) { // If one argument is NaN, return the other argument. if (a.is_nan()) return b; if (b.is_nan()) return a; // Not required by IEEE 754, but we prefer +0 over -0. if (a.sign_ != b.sign_) { return (a.sign_ < b.sign_) ? b : a; } return (a < b) ? b : a; } ExactFloat fmin(const ExactFloat& a, const ExactFloat& b) { // If one argument is NaN, return the other argument. if (a.is_nan()) return b; if (b.is_nan()) return a; // Not required by IEEE 754, but we prefer -0 over +0. if (a.sign_ != b.sign_) { return (a.sign_ < b.sign_) ? a : b; } return (a < b) ? a : b; } ExactFloat fdim(const ExactFloat& a, const ExactFloat& b) { // This formulation has the correct behavior for NaNs. return (a <= b) ? 0 : (a - b); } ExactFloat ceil(const ExactFloat& a) { return a.RoundToPowerOf2(0, ExactFloat::kRoundTowardPositive); } ExactFloat floor(const ExactFloat& a) { return a.RoundToPowerOf2(0, ExactFloat::kRoundTowardNegative); } ExactFloat trunc(const ExactFloat& a) { return a.RoundToPowerOf2(0, ExactFloat::kRoundTowardZero); } ExactFloat round(const ExactFloat& a) { return a.RoundToPowerOf2(0, ExactFloat::kRoundTiesAwayFromZero); } ExactFloat rint(const ExactFloat& a) { return a.RoundToPowerOf2(0, ExactFloat::kRoundTiesToEven); } template T ExactFloat::ToInteger(RoundingMode mode) const { using std::numeric_limits; static_assert(sizeof(T) <= sizeof(uint64), "max 64 bits supported"); static_assert(numeric_limits::is_signed, "only signed types supported"); const int64 kMinValue = numeric_limits::min(); const int64 kMaxValue = numeric_limits::max(); ExactFloat r = RoundToPowerOf2(0, mode); if (r.is_nan()) return kMaxValue; if (r.is_zero()) return 0; if (!r.is_inf()) { // If the unsigned value has more than 63 bits it is always clamped. if (r.exp() < 64) { int64 value = BN_ext_get_uint64(r.bn_.get()) << r.bn_exp_; if (r.sign_ < 0) value = -value; return max(kMinValue, min(kMaxValue, value)); } } return (r.sign_ < 0) ? kMinValue : kMaxValue; } long lrint(const ExactFloat& a) { return a.ToInteger(ExactFloat::kRoundTiesToEven); } long long llrint(const ExactFloat& a) { return a.ToInteger(ExactFloat::kRoundTiesToEven); } long lround(const ExactFloat& a) { return a.ToInteger(ExactFloat::kRoundTiesAwayFromZero); } long long llround(const ExactFloat& a) { return a.ToInteger(ExactFloat::kRoundTiesAwayFromZero); } ExactFloat copysign(const ExactFloat& a, const ExactFloat& b) { return a.CopyWithSign(b.sign_); } ExactFloat frexp(const ExactFloat& a, int* exp) { if (!a.is_normal()) { // If a == 0, exp should be zero. If a.is_inf() or a.is_nan(), exp is not // defined but the glibc implementation returns zero. *exp = 0; return a; } *exp = a.exp(); return ldexp(a, -a.exp()); } ExactFloat ldexp(const ExactFloat& a, int exp) { if (!a.is_normal()) return a; // To prevent integer overflow, we first clamp "exp" so that // (kMinExp - 1) <= (a_exp + exp) <= (kMaxExp + 1). int a_exp = a.exp(); exp = min(ExactFloat::kMaxExp + 1 - a_exp, max(ExactFloat::kMinExp - 1 + a_exp, exp)); // Now modify the exponent and check for overflow/underflow. ExactFloat r = a; r.bn_exp_ += exp; r.Canonicalize(); return r; } ExactFloat scalbln(const ExactFloat& a, long exp) { // Clamp the exponent to the range of "int" in order to avoid truncation. exp = max(static_cast(INT_MIN), min(static_cast(INT_MAX), exp)); return ldexp(a, exp); } int ilogb(const ExactFloat& a) { if (a.is_zero()) return FP_ILOGB0; if (a.is_inf()) return INT_MAX; if (a.is_nan()) return FP_ILOGBNAN; // a.exp() assumes the significand is in the range [0.5, 1). return a.exp() - 1; } ExactFloat logb(const ExactFloat& a) { if (a.is_zero()) return ExactFloat::Infinity(-1); if (a.is_inf()) return ExactFloat::Infinity(+1); // Even if a < 0. if (a.is_nan()) return a; // exp() assumes the significand is in the range [0.5,1). return ExactFloat(a.exp() - 1); } ExactFloat ExactFloat::Unimplemented() { S2_LOG(FATAL) << "Unimplemented ExactFloat method called"; return NaN(); } s2/src/s2/util/math/vector.h0000644000176200001440000004131214530411473015275 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Simple classes to handle vectors in 2D, 3D, and 4D. // // Maintainers: Please be mindful of extreme degradations in unoptimized // performance here. #ifndef S2_UTIL_MATH_VECTOR_H_ #define S2_UTIL_MATH_VECTOR_H_ #include #include #include #include #include // NOLINT(readability/streams) #include #include #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "absl/base/macros.h" #include "absl/utility/utility.h" template class Vector2; template class Vector3; template class Vector4; namespace util { namespace math { namespace internal_vector { // CRTP base class for all Vector templates. template