Repository: gunnarmorling/1brc Branch: main Commit: db064194be37 Files: 486 Total size: 3.8 MB Directory structure: gitextract_ytom6d3p/ ├── .gitattributes ├── .github/ │ ├── pull_request_template.md │ └── workflows/ │ └── maven.yml ├── .gitignore ├── .mvn/ │ └── wrapper/ │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .sdkmanrc ├── ENVIRONMENT.md ├── LICENSE.txt ├── README.md ├── calculate_average_0xshivamagarwal.sh ├── calculate_average_3j5a.sh ├── calculate_average_AbstractKamen.sh ├── calculate_average_AlexanderYastrebov.sh ├── calculate_average_C5H12O5.sh ├── calculate_average_ChrisBellew.sh ├── calculate_average_EduardoSaverin.sh ├── calculate_average_JaimePolidura.sh ├── calculate_average_JamalMulla.sh ├── calculate_average_JesseVanRooy.sh ├── calculate_average_Judekeyser.sh ├── calculate_average_JurenIvan.sh ├── calculate_average_Kidlike.sh ├── calculate_average_MahmoudFawzyKhalil.sh ├── calculate_average_MeanderingProgrammer.sh ├── calculate_average_PanagiotisDrakatos.sh ├── calculate_average_PawelAdamski.sh ├── calculate_average_SamuelYvon.sh ├── calculate_average_Smoofie.sh ├── calculate_average_Ujjwalbharti.sh ├── calculate_average_YannMoisan.sh ├── calculate_average_abeobk.sh ├── calculate_average_abfrmblr.sh ├── calculate_average_adriacabeza.sh ├── calculate_average_agoncal.sh ├── calculate_average_ags313.sh ├── calculate_average_albertoventurini.sh ├── calculate_average_alesj.sh ├── calculate_average_algirdasrascius.sh ├── calculate_average_anandmattikopp.sh ├── calculate_average_anestoruk.sh ├── calculate_average_anitasv.sh ├── calculate_average_arjenvaneerde.sh ├── calculate_average_arjenw.sh ├── calculate_average_armandino.sh ├── calculate_average_artpar.sh ├── calculate_average_artsiomkorzun.sh ├── calculate_average_as-com.sh ├── calculate_average_baseline.sh ├── calculate_average_baseline_original_rounding.sh ├── calculate_average_berry120.sh ├── calculate_average_bjhara.sh ├── calculate_average_breejesh.sh ├── calculate_average_bytesfellow.sh ├── calculate_average_cb0s.sh ├── calculate_average_charlibot.sh ├── calculate_average_cliffclick.sh ├── calculate_average_coolmineman.sh ├── calculate_average_couragelee.sh ├── calculate_average_criccomini.sh ├── calculate_average_davecom.sh ├── calculate_average_davery22.sh ├── calculate_average_ddimtirov.sh ├── calculate_average_deemkeen.sh ├── calculate_average_dkarampi.sh ├── calculate_average_dmitry-midokura.sh ├── calculate_average_dpsoft.sh ├── calculate_average_dqhieuu.sh ├── calculate_average_ebarlas.sh ├── calculate_average_elh.sh ├── calculate_average_entangled90.sh ├── calculate_average_eriklumme.sh ├── calculate_average_faridtmammadov.sh ├── calculate_average_fatroom.sh ├── calculate_average_felix19350.sh ├── calculate_average_filiphr.sh ├── calculate_average_flippingbits.sh ├── calculate_average_fragmede.sh ├── calculate_average_gabrielfoo.sh ├── calculate_average_gabrielreid.sh ├── calculate_average_gamlerhart.sh ├── calculate_average_gauravdeshmukh.sh ├── calculate_average_gigiblender.sh ├── calculate_average_giovannicuccu.sh ├── calculate_average_gnabyl.sh ├── calculate_average_gnmathur.sh ├── calculate_average_godofwharf.sh ├── calculate_average_gonix.sh ├── calculate_average_gonixunsafe.sh ├── calculate_average_hallvard.sh ├── calculate_average_hchiorean.sh ├── calculate_average_hundredwatt.sh ├── calculate_average_ianopolous.sh ├── calculate_average_ianopolousfast.sh ├── calculate_average_imrafaelmerino.sh ├── calculate_average_isolgpus.sh ├── calculate_average_itaske.sh ├── calculate_average_ivanklaric.sh ├── calculate_average_iziamos.sh ├── calculate_average_japplis.sh ├── calculate_average_jatingala.sh ├── calculate_average_javamak.sh ├── calculate_average_jbachorik.sh ├── calculate_average_jeevjyot.sh ├── calculate_average_jerrinot.sh ├── calculate_average_jgrateron.sh ├── calculate_average_jincongho.sh ├── calculate_average_jonathan-aotearoa.sh ├── calculate_average_jotschi.sh ├── calculate_average_jparera.sh ├── calculate_average_justplainlaake.sh ├── calculate_average_karthikeyan97.sh ├── calculate_average_kevinmcmurtrie.sh ├── calculate_average_kgeri.sh ├── calculate_average_khmarbaise.sh ├── calculate_average_kuduwa-keshavram.sh ├── calculate_average_kumarsaurav123.sh ├── calculate_average_lawrey.sh ├── calculate_average_linl33.sh ├── calculate_average_maeda6uiui.sh ├── calculate_average_mahadev-k.sh ├── calculate_average_makohn.sh ├── calculate_average_manishgarg90.sh ├── calculate_average_martin2038.sh ├── calculate_average_mattiz.sh ├── calculate_average_maximz101.sh ├── calculate_average_melgenek.sh ├── calculate_average_merykitty.sh ├── calculate_average_merykittyunsafe.sh ├── calculate_average_moysesb.sh ├── calculate_average_mtopolnik.sh ├── calculate_average_mudit-saxena.sh ├── calculate_average_netrunnereve.sh ├── calculate_average_obourgain.sh ├── calculate_average_omarchenko4j.sh ├── calculate_average_padreati.sh ├── calculate_average_palmr.sh ├── calculate_average_parkertimmins.sh ├── calculate_average_pedestrianlove.sh ├── calculate_average_phd3.sh ├── calculate_average_plbpietrz.sh ├── calculate_average_plevart.sh ├── calculate_average_raipc.sh ├── calculate_average_rby.sh ├── calculate_average_rcasteltrione.sh ├── calculate_average_ricardopieper.sh ├── calculate_average_richardstartin.sh ├── calculate_average_roman-r-m.sh ├── calculate_average_royvanrijn.sh ├── calculate_average_rprabhu.sh ├── calculate_average_santanu.sh ├── calculate_average_seijikun.sh ├── calculate_average_semotpan.sh ├── calculate_average_serkan-ozal.sh ├── calculate_average_shipilev.sh ├── calculate_average_slovdahl.sh ├── calculate_average_spullara.sh ├── calculate_average_stephenvonworley.sh ├── calculate_average_sudhirtumati.sh ├── calculate_average_thanhtrinity.sh ├── calculate_average_thomaswue.sh ├── calculate_average_tivrfoa.sh ├── calculate_average_tkosachev.sh ├── calculate_average_tonivade.sh ├── calculate_average_truelive.sh ├── calculate_average_twobiers.sh ├── calculate_average_unbounded.sh ├── calculate_average_vaidhy.sh ├── calculate_average_vemana.sh ├── calculate_average_vemanaNonIdiomatic.sh ├── calculate_average_xpmatteo.sh ├── calculate_average_yavuztas.sh ├── calculate_average_yehwankim23.sh ├── calculate_average_yemreinci.sh ├── calculate_average_yonatang.sh ├── calculate_average_yourwass.sh ├── calculate_average_zerninv.sh ├── checkout.sh ├── cleanup.sh ├── create_fork.sh ├── create_measurements.sh ├── create_measurements2.sh ├── create_measurements3.sh ├── create_measurements_fast.sh ├── data/ │ └── weather_stations.csv ├── etc/ │ ├── eclipse-formatter-config.xml │ └── license.txt ├── evaluate.sh ├── evaluate_10K.sh ├── github_users.txt ├── mvnw ├── mvnw.cmd ├── pom.xml ├── prepare_3j5a.sh ├── prepare_AlexanderYastrebov.sh ├── prepare_C5H12O5.sh ├── prepare_EduardoSaverin.sh ├── prepare_JaimePolidura.sh ├── prepare_JamalMulla.sh ├── prepare_Judekeyser.sh ├── prepare_Kidlike.sh ├── prepare_MeanderingProgrammer.sh ├── prepare_PanagiotisDrakatos.sh ├── prepare_PawelAdamski.sh ├── prepare_SamuelYvon.sh ├── prepare_Smoofie.sh ├── prepare_YannMoisan.sh ├── prepare_abeobk.sh ├── prepare_adriacabeza.sh ├── prepare_agoncal.sh ├── prepare_ags313.sh ├── prepare_anitasv.sh ├── prepare_armandino.sh ├── prepare_artsiomkorzun.sh ├── prepare_baseline.sh ├── prepare_breejesh.sh ├── prepare_cb0s.sh ├── prepare_charlibot.sh ├── prepare_chrisbellew.sh ├── prepare_coolmineman.sh ├── prepare_davecom.sh ├── prepare_ddimtirov.sh ├── prepare_dpsoft.sh ├── prepare_dqhieuu.sh ├── prepare_ebarlas.sh ├── prepare_elh.sh ├── prepare_eriklumme.sh ├── prepare_filiphr.sh ├── prepare_flippingbits.sh ├── prepare_gabrielfoo.sh ├── prepare_gnabyl.sh ├── prepare_godofwharf.sh ├── prepare_hundredwatt.sh ├── prepare_imrafaelmerino.sh ├── prepare_iziamos.sh ├── prepare_jatingala.sh ├── prepare_jbachorik.sh ├── prepare_jerrinot.sh ├── prepare_jonathan-aotearoa.sh ├── prepare_justplainlaake.sh ├── prepare_kuduwa-keshavram.sh ├── prepare_linl33.sh ├── prepare_mahadev-k.sh ├── prepare_manishgarg90.sh ├── prepare_martin2038.sh ├── prepare_maximz101.sh ├── prepare_melgenek.sh ├── prepare_mtopolnik.sh ├── prepare_phd3.sh ├── prepare_plevart.sh ├── prepare_rcasteltrione.sh ├── prepare_ricardopieper.sh ├── prepare_roman-r-m.sh ├── prepare_royvanrijn.sh ├── prepare_seijikun.sh ├── prepare_serkan-ozal.sh ├── prepare_slovdahl.sh ├── prepare_spullara.sh ├── prepare_stephenvonworley.sh ├── prepare_sudhirtumati.sh ├── prepare_thanhtrinity.sh ├── prepare_thomaswue.sh ├── prepare_tivrfoa.sh ├── prepare_tonivade.sh ├── prepare_truelive.sh ├── prepare_twobiers.sh ├── prepare_vaidhy.sh ├── prepare_vemana.sh ├── prepare_vemanaNonIdiomatic.sh ├── prepare_yavuztas.sh ├── prepare_yonatang.sh ├── prepare_zerninv.sh ├── process.sh ├── process_output.java ├── src/ │ ├── main/ │ │ ├── go/ │ │ │ ├── AlexanderYastrebov/ │ │ │ │ ├── Dockerfile │ │ │ │ ├── README.md │ │ │ │ ├── calc.go │ │ │ │ ├── calc_test.go │ │ │ │ └── go.mod │ │ │ └── elh/ │ │ │ ├── Dockerfile │ │ │ ├── go.mod │ │ │ └── main.go │ │ ├── java/ │ │ │ ├── dev/ │ │ │ │ └── morling/ │ │ │ │ └── onebrc/ │ │ │ │ ├── CalculateAverage_0xshivamagarwal.java │ │ │ │ ├── CalculateAverage_3j5a.java │ │ │ │ ├── CalculateAverage_AbstractKamen.java │ │ │ │ ├── CalculateAverage_C5H12O5.java │ │ │ │ ├── CalculateAverage_EduardoSaverin.java │ │ │ │ ├── CalculateAverage_JaimePolidura.java │ │ │ │ ├── CalculateAverage_JamalMulla.java │ │ │ │ ├── CalculateAverage_JesseVanRooy.java │ │ │ │ ├── CalculateAverage_Judekeyser.java │ │ │ │ ├── CalculateAverage_JurenIvan.java │ │ │ │ ├── CalculateAverage_Kidlike.java │ │ │ │ ├── CalculateAverage_MahmoudFawzyKhalil.java │ │ │ │ ├── CalculateAverage_MeanderingProgrammer.java │ │ │ │ ├── CalculateAverage_PanagiotisDrakatos.java │ │ │ │ ├── CalculateAverage_PawelAdamski.java │ │ │ │ ├── CalculateAverage_SamuelYvon.java │ │ │ │ ├── CalculateAverage_Smoofie.java │ │ │ │ ├── CalculateAverage_Ujjwalbharti.java │ │ │ │ ├── CalculateAverage_YannMoisan.java │ │ │ │ ├── CalculateAverage_abeobk.java │ │ │ │ ├── CalculateAverage_abfrmblr.java │ │ │ │ ├── CalculateAverage_adriacabeza.java │ │ │ │ ├── CalculateAverage_agoncal.java │ │ │ │ ├── CalculateAverage_ags313.java │ │ │ │ ├── CalculateAverage_albertoventurini.java │ │ │ │ ├── CalculateAverage_alesj.java │ │ │ │ ├── CalculateAverage_algirdasrascius.java │ │ │ │ ├── CalculateAverage_anandmattikopp.java │ │ │ │ ├── CalculateAverage_anestoruk.java │ │ │ │ ├── CalculateAverage_anitasv.java │ │ │ │ ├── CalculateAverage_arjenvaneerde.java │ │ │ │ ├── CalculateAverage_arjenw.java │ │ │ │ ├── CalculateAverage_armandino.java │ │ │ │ ├── CalculateAverage_artpar.java │ │ │ │ ├── CalculateAverage_artsiomkorzun.java │ │ │ │ ├── CalculateAverage_as-com.java │ │ │ │ ├── CalculateAverage_baseline.java │ │ │ │ ├── CalculateAverage_baseline_original_rounding.java │ │ │ │ ├── CalculateAverage_berry120.java │ │ │ │ ├── CalculateAverage_bjhara.java │ │ │ │ ├── CalculateAverage_breejesh.java │ │ │ │ ├── CalculateAverage_bufistov.java │ │ │ │ ├── CalculateAverage_bytesfellow.java │ │ │ │ ├── CalculateAverage_cb0s.java │ │ │ │ ├── CalculateAverage_charlibot.java │ │ │ │ ├── CalculateAverage_chrisbellew.java │ │ │ │ ├── CalculateAverage_cliffclick.java │ │ │ │ ├── CalculateAverage_coolmineman.java │ │ │ │ ├── CalculateAverage_couragelee.java │ │ │ │ ├── CalculateAverage_criccomini.java │ │ │ │ ├── CalculateAverage_davecom.java │ │ │ │ ├── CalculateAverage_davery22.java │ │ │ │ ├── CalculateAverage_ddimtirov.java │ │ │ │ ├── CalculateAverage_deemkeen.java │ │ │ │ ├── CalculateAverage_dkarampi.java │ │ │ │ ├── CalculateAverage_dpsoft.java │ │ │ │ ├── CalculateAverage_dqhieuu.java │ │ │ │ ├── CalculateAverage_ebarlas.java │ │ │ │ ├── CalculateAverage_entangled90.java │ │ │ │ ├── CalculateAverage_eriklumme.java │ │ │ │ ├── CalculateAverage_faridtmammadov.java │ │ │ │ ├── CalculateAverage_fatroom.java │ │ │ │ ├── CalculateAverage_felix19350.java │ │ │ │ ├── CalculateAverage_filiphr.java │ │ │ │ ├── CalculateAverage_flippingbits.java │ │ │ │ ├── CalculateAverage_fragmede.java │ │ │ │ ├── CalculateAverage_gabrielfoo.java │ │ │ │ ├── CalculateAverage_gabrielreid.java │ │ │ │ ├── CalculateAverage_gamlerhart.java │ │ │ │ ├── CalculateAverage_gauravdeshmukh.java │ │ │ │ ├── CalculateAverage_gigiblender.java │ │ │ │ ├── CalculateAverage_giovannicuccu.java │ │ │ │ ├── CalculateAverage_gnabyl.java │ │ │ │ ├── CalculateAverage_gnmathur.java │ │ │ │ ├── CalculateAverage_godofwharf.java │ │ │ │ ├── CalculateAverage_gonix.java │ │ │ │ ├── CalculateAverage_gonixunsafe.java │ │ │ │ ├── CalculateAverage_hallvard.java │ │ │ │ ├── CalculateAverage_hchiorean.java │ │ │ │ ├── CalculateAverage_hundredwatt.java │ │ │ │ ├── CalculateAverage_ianopolous.java │ │ │ │ ├── CalculateAverage_ianopolousfast.java │ │ │ │ ├── CalculateAverage_imrafaelmerino.java │ │ │ │ ├── CalculateAverage_isolgpus.java │ │ │ │ ├── CalculateAverage_itaske.java │ │ │ │ ├── CalculateAverage_ivanklaric.java │ │ │ │ ├── CalculateAverage_iziamos.java │ │ │ │ ├── CalculateAverage_japplis.java │ │ │ │ ├── CalculateAverage_jatingala.java │ │ │ │ ├── CalculateAverage_javamak.java │ │ │ │ ├── CalculateAverage_jbachorik.java │ │ │ │ ├── CalculateAverage_jeevjyot.java │ │ │ │ ├── CalculateAverage_jerrinot.java │ │ │ │ ├── CalculateAverage_jgrateron.java │ │ │ │ ├── CalculateAverage_jincongho.java │ │ │ │ ├── CalculateAverage_jonathanaotearoa.java │ │ │ │ ├── CalculateAverage_jotschi.java │ │ │ │ ├── CalculateAverage_jparera.java │ │ │ │ ├── CalculateAverage_justplainlaake.java │ │ │ │ ├── CalculateAverage_karthikeyan97.java │ │ │ │ ├── CalculateAverage_kevinmcmurtrie.java │ │ │ │ ├── CalculateAverage_kgeri.java │ │ │ │ ├── CalculateAverage_khmarbaise.java │ │ │ │ ├── CalculateAverage_kuduwa_keshavram.java │ │ │ │ ├── CalculateAverage_kumarsaurav123.java │ │ │ │ ├── CalculateAverage_lawrey.java │ │ │ │ ├── CalculateAverage_maeda6uiui.java │ │ │ │ ├── CalculateAverage_mahadev_k.java │ │ │ │ ├── CalculateAverage_makohn.java │ │ │ │ ├── CalculateAverage_manishgarg90.java │ │ │ │ ├── CalculateAverage_martin2038.java │ │ │ │ ├── CalculateAverage_mattiz.java │ │ │ │ ├── CalculateAverage_maximz101.java │ │ │ │ ├── CalculateAverage_melgenek.java │ │ │ │ ├── CalculateAverage_merykitty.java │ │ │ │ ├── CalculateAverage_merykittyunsafe.java │ │ │ │ ├── CalculateAverage_moysesb.java │ │ │ │ ├── CalculateAverage_mtopolnik.java │ │ │ │ ├── CalculateAverage_muditsaxena.java │ │ │ │ ├── CalculateAverage_naive.java │ │ │ │ ├── CalculateAverage_netrunnereve.java │ │ │ │ ├── CalculateAverage_obourgain.java │ │ │ │ ├── CalculateAverage_omarchenko4j.java │ │ │ │ ├── CalculateAverage_padreati.java │ │ │ │ ├── CalculateAverage_palmr.java │ │ │ │ ├── CalculateAverage_parkertimmins.java │ │ │ │ ├── CalculateAverage_pedestrianlove.java │ │ │ │ ├── CalculateAverage_phd3.java │ │ │ │ ├── CalculateAverage_plbpietrz.java │ │ │ │ ├── CalculateAverage_plevart.java │ │ │ │ ├── CalculateAverage_raipc.java │ │ │ │ ├── CalculateAverage_rby.java │ │ │ │ ├── CalculateAverage_rcasteltrione.java │ │ │ │ ├── CalculateAverage_ricardopieper.java │ │ │ │ ├── CalculateAverage_richardstartin.java │ │ │ │ ├── CalculateAverage_roman_r_m.java │ │ │ │ ├── CalculateAverage_royvanrijn.java │ │ │ │ ├── CalculateAverage_rprabhu.java │ │ │ │ ├── CalculateAverage_santanu.java │ │ │ │ ├── CalculateAverage_seijikun.java │ │ │ │ ├── CalculateAverage_semotpan.java │ │ │ │ ├── CalculateAverage_serkan_ozal.java │ │ │ │ ├── CalculateAverage_shipilev.java │ │ │ │ ├── CalculateAverage_slovdahl.java │ │ │ │ ├── CalculateAverage_spullara.java │ │ │ │ ├── CalculateAverage_stephenvonworley.java │ │ │ │ ├── CalculateAverage_sudhirtumati.java │ │ │ │ ├── CalculateAverage_thanhtrinity.java │ │ │ │ ├── CalculateAverage_thomaswue.java │ │ │ │ ├── CalculateAverage_tivrfoa.java │ │ │ │ ├── CalculateAverage_tkosachev.java │ │ │ │ ├── CalculateAverage_tonivade.java │ │ │ │ ├── CalculateAverage_truelive.java │ │ │ │ ├── CalculateAverage_twobiers.java │ │ │ │ ├── CalculateAverage_unbounded.java │ │ │ │ ├── CalculateAverage_vaidhy.java │ │ │ │ ├── CalculateAverage_vemana.java │ │ │ │ ├── CalculateAverage_vemanaNonIdiomatic.java │ │ │ │ ├── CalculateAverage_xpmatteo.java │ │ │ │ ├── CalculateAverage_yavuztas.java │ │ │ │ ├── CalculateAverage_yehwankim23.java │ │ │ │ ├── CalculateAverage_yemreinci.java │ │ │ │ ├── CalculateAverage_yonatang.java │ │ │ │ ├── CalculateAverage_yourwass.java │ │ │ │ ├── CalculateAverage_zerninv.java │ │ │ │ ├── CreateMeasurements.java │ │ │ │ ├── CreateMeasurements2.java │ │ │ │ ├── CreateMeasurements3.java │ │ │ │ ├── CreateMeasurementsFast.java │ │ │ │ └── PerfectHashSearch_hundredwatt.java │ │ │ └── org/ │ │ │ └── rschwietzke/ │ │ │ ├── CheaperCharBuffer.java │ │ │ └── FastRandom.java │ │ ├── java-22/ │ │ │ └── dev/ │ │ │ └── morling/ │ │ │ └── onebrc/ │ │ │ └── CalculateAverage_linl33.java │ │ ├── python/ │ │ │ └── create_measurements.py │ │ └── resources/ │ │ └── .dontdelete │ └── test/ │ └── resources/ │ ├── .dontdelete │ └── samples/ │ ├── measurements-1.out │ ├── measurements-1.txt │ ├── measurements-10.out │ ├── measurements-10.txt │ ├── measurements-10000-unique-keys.out │ ├── measurements-10000-unique-keys.txt │ ├── measurements-2.out │ ├── measurements-2.txt │ ├── measurements-20.out │ ├── measurements-20.txt │ ├── measurements-3.out │ ├── measurements-3.txt │ ├── measurements-boundaries.out │ ├── measurements-boundaries.txt │ ├── measurements-complex-utf8.out │ ├── measurements-complex-utf8.txt │ ├── measurements-dot.out │ ├── measurements-dot.txt │ ├── measurements-rounding.out │ ├── measurements-rounding.txt │ ├── measurements-short.out │ ├── measurements-short.txt │ ├── measurements-shortest.out │ └── measurements-shortest.txt ├── test.sh ├── test_all.sh ├── test_ci.sh └── tocsv.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.java text *.md text *.yml text *.xml text *.gradle text *.properties text mvnw text eol=lf *.sh text eol=lf *.bat text eol=crlf *.cmd text eol=crlf *.jar binary src/test/resources/samples/*.txt text eol=lf src/test/resources/samples/*.out text eol=lf ================================================ FILE: .github/pull_request_template.md ================================================ **NOTE:** The challenge has been closed for new submissions. No new pull requests for adding submissions are accepted at this time. The final leaderboard has been published on Feb 4. #### Check List: - [ ] You have run `./mvnw verify` and the project builds successfully - [ ] Tests pass (`./test.sh ` shows no differences between expected and actual outputs) - [ ] All formatting changes by the build are committed - [ ] Your launch script is named `calculate_average_.sh` (make sure to match casing of your GH user name) and is executable - [ ] Output matches that of `calculate_average_baseline.sh` - [ ] For new entries, or after substantial changes: When implementing custom hash structures, please point to where you deal with hash collisions (line number) * Execution time: * Execution time of reference implementation: ================================================ FILE: .github/workflows/maven.yml ================================================ # # Copyright 2023 The original authors # # 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. # name: Build on: # Enable manual re-run workflow_dispatch: { } push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - name: 'Check out repository' uses: actions/checkout@v2 with: submodules: 'true' - name: Cache SDKMan id: cache-sdkman uses: actions/cache@v4 with: path: ~/.sdkman key: ${{ runner.os }}-sdkman - name: 'Cache Maven packages' uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - name: 'Setup SDKMAN' uses: sdkman/sdkman-action@b1f9b696c79148b66d3d3a06f7ea801820318d0f id: sdkman - name: 'Build project' shell: bash run: | source "$HOME/.sdkman/bin/sdkman-init.sh" if [ -f ${{ format('src/main/java-22/dev/morling/onebrc/CalculateAverage_{0}.java', github.event.pull_request.user.login || '') }} ]; then sdk install java 22.ea.32-open || true sdk use java 22.ea.32-open fi ./mvnw --version ./mvnw -B clean verify -Pci - name: 'Test submission' shell: bash run: | ./test_ci.sh ${{ github.event.pull_request.user.login }} if: github.event_name == 'pull_request' ================================================ FILE: .gitignore ================================================ #Maven target/ pom.xml.tag pom.xml.releaseBackup pom.xml.versionsBackup release.properties # Eclipse .project .classpath .settings/ bin/ # IntelliJ .idea *.ipr *.iml *.iws # NetBeans nb-configuration.xml nbactions.xml # Visual Studio Code .vscode .factorypath # OSX .DS_Store # Vim *.swp *.swo # patch *.orig *.rej # Local environment .env #JReleaser out/ # 1BRC /measurements*.txt /*.out out_expected.txt /*-timing.json ================================================ FILE: .mvn/wrapper/maven-wrapper.properties ================================================ # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you 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. distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar ================================================ FILE: .sdkmanrc ================================================ # Enable auto-env through the sdkman_auto_env config # Add key=value pairs of SDKs to use below java=21.0.1-open ================================================ FILE: ENVIRONMENT.md ================================================ # Environment This file just contains some intel about the environment in use and what has been done to get it into that state. ## Machine Type * Hetzner AX161, Dedicated Hosted Hardware * CPU: AMD EPYC 7502P 32 cores / 64 threads @ 2.5 GHz * Memory: 128 GB ECC DDR4 RAM * 2x SAMSUNG MZQL2960HCJR-00A07, 1 TB, Software RAID-1 * CentOS 9, Linux 5.14.0-378.el9.x86_64 ## Configuration * SMT off * Turbo Boost Off * Filesystem EXT4 ## Details ### CPU ``` $ cat /proc/cpuinfo processor : 0 vendor_id : AuthenticAMD cpu family : 23 model : 49 model name : AMD EPYC 7502P 32-Core Processor stepping : 0 microcode : 0x8301055 cpu MHz : 2500.000 cache size : 512 KB physical id : 0 siblings : 32 core id : 0 cpu cores : 32 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 16 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr rdpru wbnoinvd arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif v_spec_ctrl umip rdpid overflow_recov succor smca sme sev sev_es bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass retbleed smt_rsb bogomips : 4990.70 TLB size : 3072 4K pages clflush size : 64 cache_alignment : 64 address sizes : 43 bits physical, 48 bits virtual power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14] ... more for all other cores ``` ## Setup ### Turn SMT off Disable during boot via boot-param, able to switch it on later again, if needed. Add `nosmt` to grub boot config in `/etc/default/grub` ``` # Added nosmt to command line GRUB_CMDLINE_LINUX="biosdevname=0 crashkernel=auto rd.auto=1 consoleblank=0 nosmt" ``` Update boot config: ``` sudo grub2-mkconfig -o /boot/grub2/grub.cfg ``` ### Turbo Off Using the legacy `/etc/rc.local` concept to change things during boot: ``` # Turn SMT off via software as well, already got nosmt in grub echo off > /sys/devices/system/cpu/smt/control # Turn off turbo boost echo 0 |tee /sys/devices/system/cpu/cpufreq/boost ``` ### Reduce Swapping Reduce from default 60 to 10% memory pressure by adding `vm.swappiness = 10` to `/etc/sysctl.conf`. ## Verify Check after boot if all settings have been applied. Can also be used to control these during runtime. * SMT off: `cat /sys/devices/system/cpu/smt/active` must be 0 * SWAP: `cat /proc/sys/vm/swappiness` must be 10 * Turbo off: `cat /sys/devices/system/cpu/cpufreq/boost` must be 0 ================================================ FILE: LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: README.md ================================================ # 1️⃣🐝🏎️ The One Billion Row Challenge _Status Feb 4: The final leaderboards [have been published](https://www.morling.dev/blog/1brc-results-are-in/). Congrats to all the winners, and a big thank you to everyone participating in this challenge as well as to everyone helping to organize it!_ _Status Feb 3: All entries have been evaluated and I am in the process of finalizing the leaderboards._ _Status Feb 1: The challenge has been closed for new submissions. No new pull requests for adding submissions are accepted at this time. Pending PRs will be evaluated over the next few days._ _Status Jan 31: The challenge will close today at midnight UTC._ _Status Jan 12: As there has been such a large number of entries to this challenge so far (100+), and this is becoming hard to manage, please only create new submissions if you expect them to run in 10 seconds or less on the evaluation machine._ _Status Jan 1: This challenge is [open for submissions](https://www.morling.dev/blog/one-billion-row-challenge/)!_ > **Sponsorship** > > A big thank you to my employer [Decodable](https://www.decodable.co/) for funding the evaluation environment and supporting this challenge! The One Billion Row Challenge (1BRC) is a fun exploration of how far modern Java can be pushed for aggregating one billion rows from a text file. Grab all your (virtual) threads, reach out to SIMD, optimize your GC, or pull any other trick, and create the fastest implementation for solving this task! 1BRC The text file contains temperature values for a range of weather stations. Each row is one measurement in the format `;`, with the measurement value having exactly one fractional digit. The following shows ten rows as an example: ``` Hamburg;12.0 Bulawayo;8.9 Palembang;38.8 St. John's;15.2 Cracow;12.6 Bridgetown;26.9 Istanbul;6.2 Roseau;34.4 Conakry;31.2 Istanbul;23.0 ``` The task is to write a Java program which reads the file, calculates the min, mean, and max temperature value per weather station, and emits the results on stdout like this (i.e. sorted alphabetically by station name, and the result values per station in the format `//`, rounded to one fractional digit): ``` {Abha=-23.0/18.0/59.2, Abidjan=-16.2/26.0/67.3, Abéché=-10.0/29.4/69.0, Accra=-10.1/26.4/66.4, Addis Ababa=-23.7/16.0/67.0, Adelaide=-27.8/17.3/58.5, ...} ``` Submit your implementation by Jan 31 2024 and become part of the leaderboard! ## Results These are the results from running all entries into the challenge on eight cores of a [Hetzner AX161](https://www.hetzner.com/dedicated-rootserver/ax161) dedicated server (32 core AMD EPYC™ 7502P (Zen2), 128 GB RAM). | # | Result (m:s.ms) | Implementation | JDK | Submitter | Notes | Certificates | |---|-----------------|--------------------|-----|---------------|-----------|--------------| | 1 | 00:01.535 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_thomaswue.java)| 21.0.2-graal | [Thomas Wuerthinger](https://github.com/thomaswue), [Quan Anh Mai](https://github.com/merykitty), [Alfonso² Peterssen](https://github.com/mukel) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/thomaswue_merykitty_mukel.pdf) | | 2 | 00:01.587 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_artsiomkorzun.java)| 21.0.2-graal | [Artsiom Korzun](https://github.com/artsiomkorzun) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/artsiomkorzun.pdf) | | 3 | 00:01.608 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jerrinot.java)| 21.0.2-graal | [Jaromir Hamala](https://github.com/jerrinot) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jerrinot.pdf) | | | 00:01.880 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_serkan_ozal.java)| 21.0.1-open | [Serkan ÖZAL](https://github.com/serkan-ozal) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/serkan_ozal.pdf) | | | 00:01.921 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_abeobk.java)| 21.0.2-graal | [Van Phu DO](https://github.com/abeobk) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/abeobk.pdf) | | | 00:02.018 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_stephenvonworley.java)| 21.0.2-graal | [Stephen Von Worley](https://github.com/stephenvonworley) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/stephenvonworley.pdf) | | | 00:02.157 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_royvanrijn.java)| 21.0.2-graal | [Roy van Rijn](https://github.com/royvanrijn) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/royvanrijn.pdf) | | | 00:02.319 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yavuztas.java)| 21.0.2-graal | [Yavuz Tas](https://github.com/yavuztas) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/yavuztas.pdf) | | | 00:02.332 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_mtopolnik.java)| 21.0.2-graal | [Marko Topolnik](https://github.com/mtopolnik) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/mtopolnik.pdf) | | | 00:02.367 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_merykittyunsafe.java)| 21.0.1-open | [Quan Anh Mai](https://github.com/merykitty) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/merykittyunsafe.pdf) | | | 00:02.507 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gonixunsafe.java)| 21.0.1-open | [gonix](https://github.com/gonix) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gonixunsafe.pdf) | | | 00:02.557 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yourwass.java)| 21.0.1-open | [yourwass](https://github.com/yourwass) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/yourwass.pdf) | | | 00:02.820 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_linl33.java)| 22.ea.32-open | [Li Lin](https://github.com/linl33) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/linl33.pdf) | | | 00:02.995 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_tivrfoa.java)| 21.0.2-graal | [tivrfoa](https://github.com/tivrfoa) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/tivrfoa.pdf) | | | 00:02.997 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gonix.java)| 21.0.1-open | [gonix](https://github.com/gonix) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gonix.pdf) | | | 00:03.095 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JamalMulla.java)| 21.0.2-graal | [Jamal Mulla](https://github.com/JamalMulla) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/JamalMulla.pdf) | | | 00:03.210 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_merykitty.java)| 21.0.1-open | [Quan Anh Mai](https://github.com/merykitty) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/merykitty.pdf) | | | 00:03.298 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vemanaNonIdiomatic.java)| 21.0.1-graal | [Subrahmanyam](https://github.com/vemana) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/vemanaNonIdiomatic.pdf) | | | 00:03.431 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_roman_r_m.java)| 21.0.1-graal | [Roman Musin](https://github.com/roman-r-m) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/roman_r_m.pdf) | | | 00:03.469 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ebarlas.java)| 21.0.2-graal | [Elliot Barlas](https://github.com/ebarlas) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ebarlas.pdf) | | | 00:03.698 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_hundredwatt.java)| 21.0.1-graal | [Jason Nochlin](https://github.com/hundredwatt) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/hundredwatt.pdf) | | | 00:03.785 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_zerninv.java)| 21.0.2-graal | [zerninv](https://github.com/zerninv) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/zerninv.pdf) | | | 00:03.820 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java)| 21.0.2-graal | [John Ziamos](https://github.com/iziamos) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/iziamos.pdf) | | | 00:03.902 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jparera.java)| 21.0.1-open | [Juan Parera](https://github.com/jparera) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jparera.pdf) | | | 00:03.966 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jincongho.java)| 21.0.1-open | [Jin Cong Ho](https://github.com/jincongho) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jincongho.pdf) | | | 00:03.991 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vaidhy.java)| 21.0.1-graal | [Vaidhy Mayilrangam](https://github.com/vaidhy) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/vaidhy.pdf) | | | 00:04.066 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JesseVanRooy.java)| 21.0.1-open | [JesseVanRooy](https://github.com/JesseVanRooy) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/JesseVanRooy.pdf) | | | 00:04.101 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JaimePolidura.java)| 21.0.2-graal | [Jaime Polidura](https://github.com/JaimePolidura) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/JaimePolidura.pdf) | | | 00:04.209 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_giovannicuccu.java)| 21.0.1-open | [Giovanni Cuccu](https://github.com/giovannicuccu) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/giovannicuccu.pdf) | | | 00:04.474 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gamlerhart.java)| 21.0.1-open | [Roman Stoffel](https://github.com/gamlerhart) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gamlerhart.pdf) | | | 00:04.676 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_plevart.java)| 21.0.2-tem | [Peter Levart](https://github.com/plevart) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/plevart.pdf) | | | 00:04.684 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gigiblender.java)| 21.0.1-open | [Florin Blanaru](https://github.com/gigiblender) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gigiblender.pdf) | | | 00:04.701 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ianopolousfast.java)| 21.0.1-open | [Dr Ian Preston](https://github.com/ianopolousfast) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ianopolousfast.pdf) | | | 00:04.741 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_cliffclick.java)| 21.0.1-open | [Cliff Click](https://github.com/cliffclick) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/cliffclick.pdf) | | | 00:04.800 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_parkertimmins.java)| 21.0.1-open | [Parker Timmins](https://github.com/parkertimmins) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/parkertimmins.pdf) | | | 00:04.884 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_shipilev.java)| 21.0.1-open | [Aleksey Shipilëv](https://github.com/shipilev) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/shipilev.pdf) | | | 00:04.920 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vemana.java)| 21.0.1-graal | [Subrahmanyam](https://github.com/vemana) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/vemana.pdf) | | | 00:05.077 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jonathanaotearoa.java)| 21.0.2-graal | [Jonathan Wright](https://github.com/jonathan-aotearoa) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jonathanaotearoa.pdf) | | | 00:05.142 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_arjenw.java)| 21.0.1-open | [Arjen Wisse](https://github.com/arjenw) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/arjenw.pdf) | | | 00:05.167 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_melgenek.java)| 21.0.2-open | [Yevhenii Melnyk](https://github.com/melgenek) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/melgenek.pdf) | | | 00:05.235 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_unbounded.java)| 21.0.1-open | [unbounded](https://github.com/unbounded) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/unbounded.pdf) | | | 00:05.336 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_EduardoSaverin.java)| java | [Sumit Chaudhary](https://github.com/EduardoSaverin) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/EduardoSaverin.pdf) | | | 00:05.354 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_armandino.java)| 21.0.2-graal | [Arman Sharif](https://github.com/armandino) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/armandino.pdf) | | | 00:05.478 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_obourgain.java)| 21.0.1-open | [Olivier Bourgain](https://github.com/obourgain) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/obourgain.pdf) | | | 00:05.559 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_PanagiotisDrakatos.java)| 21.0.1-graal | [Panagiotis Drakatos](https://github.com/PanagiotisDrakatos) | GraalVM native binary | [Certificate](http://gunnarmorling.github.io/1brc-certificates/PanagiotisDrakatos.pdf) | | | 00:05.887 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_charlibot.java)| 21.0.1-graal | [Charlie Evans](https://github.com/charlibot) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/charlibot.pdf) | | | 00:05.979 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_spullara.java)| 21.0.1-graal | [Sam Pullara](https://github.com/spullara) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/spullara.pdf) | | | 00:06.166 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_isolgpus.java)| 21.0.1-open | [Jamie Stansfield](https://github.com/isolgpus) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/isolgpus.pdf) | | | 00:06.257 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_flippingbits.java)| 21.0.1-graal | [Stefan Sprenger](https://github.com/flippingbits) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/flippingbits.pdf) | | | 00:06.392 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_dpsoft.java)| 21.0.2-graal | [Diego Parra](https://github.com/dpsoft) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/dpsoft.pdf) | | | 00:06.576 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_as-com.java)| 21.0.1-open | [Andrew Sun](https://github.com/as-com) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/as-com.pdf) | | | 00:06.635 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_justplainlaake.java)| 21.0.1-graal | [Laake Scates-Gervasi](https://github.com/justplainlaake) | GraalVM native binary, uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/justplainlaake.pdf) | | | 00:06.654 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jbachorik.java)| 21.0.1-graal | [Jaroslav Bachorik](https://github.com/jbachorik) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jbachorik.pdf) | | | 00:06.715 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_algirdasrascius.java)| 21.0.1-open | [Algirdas Raščius](https://github.com/algirdasrascius) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/algirdasrascius.pdf) | | | 00:06.884 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_rcasteltrione.java)| 21.0.1-graal | [rcasteltrione](https://github.com/rcasteltrione) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/rcasteltrione.pdf) | | | 00:06.982 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ChrisBellew.java)| 21.0.1-open | [Chris Bellew](https://github.com/ChrisBellew) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ChrisBellew.pdf) | | | 00:07.563 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_3j5a.java)| 21.0.1-graal | [3j5a](https://github.com/3j5a) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/3j5a.pdf) | | | 00:07.680 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_C5H12O5.java)| 21.0.1-graal | [Xylitol](https://github.com/C5H12O5) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/C5H12O5.pdf) | | | 00:07.712 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_anitasv.java)| 21.0.1-graal | [Anita SV](https://github.com/anitasv) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/anitasv.pdf) | | | 00:07.730 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jotschi.java)| 21.0.1-open | [Johannes Schüth](https://github.com/jotschi) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jotschi.pdf) | | | 00:07.894 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_tonivade.java)| 21.0.2-tem | [Antonio Muñoz](https://github.com/tonivade) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/tonivade.pdf) | | | 00:07.925 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ricardopieper.java)| 21.0.1-graal | [Ricardo Pieper](https://github.com/ricardopieper) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ricardopieper.pdf) | | | 00:07.948 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_Smoofie.java)| java | [Smoofie](https://github.com/Smoofie) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/Smoofie.pdf) | | | 00:08.157 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JurenIvan.java)| 21.0.1-open | [JurenIvan](https://github.com/JurenIvan) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/JurenIvan.pdf) | | | 00:08.167 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ddimtirov.java)| 21.0.1-tem | [Dimitar Dimitrov](https://github.com/ddimtirov) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ddimtirov.pdf) | | | 00:08.214 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_deemkeen.java)| 21.0.1-open | [deemkeen](https://github.com/deemkeen) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/deemkeen.pdf) | | | 00:08.255 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_mattiz.java)| 21.0.1-open | [Mathias Bjerke](https://github.com/mattiz) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/mattiz.pdf) | | | 00:08.398 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_artpar.java)| 21.0.1-open | [Parth Mudgal](https://github.com/artpar) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/artpar.pdf) | | | 00:08.489 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gnabyl.java)| 21.0.1-graal | [Bang NGUYEN](https://github.com/gnabyl) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gnabyl.pdf) | | | 00:08.517 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ags313.java)| 21.0.1-graal | [ags](https://github.com/ags313) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ags313.pdf) | | | 00:08.557 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_adriacabeza.java)| 21.0.1-graal | [Adrià Cabeza](https://github.com/adriacabeza) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/adriacabeza.pdf) | | | 00:08.622 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_kuduwa_keshavram.java)| 21.0.1-graal | [Keshavram Kuduwa](https://github.com/kuduwa-keshavram) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/kuduwa_keshavram.pdf) | | | 00:08.892 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_fatroom.java)| 21.0.1-open | [Roman Romanchuk](https://github.com/fatroom) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/fatroom.pdf) | | | 00:08.896 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_anestoruk.java)| 21.0.1-open | [Andrzej Nestoruk](https://github.com/anestoruk) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/anestoruk.pdf) | | | 00:09.020 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yemreinci.java)| 21.0.1-open | [yemreinci](https://github.com/yemreinci) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/yemreinci.pdf) | | | 00:09.071 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gabrielreid.java)| 21.0.1-open | [Gabriel Reid](https://github.com/gabrielreid) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gabrielreid.pdf) | | | 00:09.352 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_filiphr.java)| 21.0.1-graal | [Filip Hrisafov](https://github.com/filiphr) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/filiphr.pdf) | | | 00:09.725 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_martin2038.java)| 21.0.2-graal | [Martin](https://github.com/martin2038) | GraalVM native binary | [Certificate](http://gunnarmorling.github.io/1brc-certificates/martin2038.pdf) | | | 00:09.867 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ricardopieper.java)| 21.0.1-graal | [Ricardo Pieper](https://github.com/ricardopieper) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ricardopieper.pdf) | | | 00:09.945 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_japplis.java)| 21.0.1-open | [Anthony Goubard](https://github.com/japplis) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/japplis.pdf) | | | 00:10.092 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_phd3.java)| 21.0.1-graal | [Pratham](https://github.com/phd3) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/phd3.pdf) | | | 00:10.127 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_artpar.java)| 21.0.1-open | [Parth Mudgal](https://github.com/artpar) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/artpar.pdf) | | | 00:11.577 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_netrunnereve.java)| 21.0.1-open | [Eve](https://github.com/netrunnereve) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/netrunnereve.pdf) | | | 00:10.473 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_raipc.java)| 21.0.1-open | [Anton Rybochkin](https://github.com/raipc) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/raipc.pdf) | | | 00:11.119 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_lawrey.java)| 21.0.1-open | [lawrey](https://github.com/lawrey) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/lawrey.pdf) | | | 00:11.156 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_YannMoisan.java)| java | [Yann Moisan](https://github.com/YannMoisan) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/YannMoisan.pdf) | | | 00:11.167 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_palmr.java)| 21.0.1-open | [Nick Palmer](https://github.com/palmr) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/palmr.pdf) | | | 00:11.352 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_karthikeyan97.java)| 21.0.1-open | [karthikeyan97](https://github.com/karthikeyan97) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/karthikeyan97.pdf) | | | 00:11.363 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_godofwharf.java)| 21.0.2-tem | [Guruprasad Sridharan](https://github.com/godofwharf) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/godofwharf.pdf) | | | 00:11.405 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_imrafaelmerino.java)| 21.0.1-graal | [Rafael Merino García](https://github.com/imrafaelmerino) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/imrafaelmerino.pdf) | | | 00:11.406 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gabrielfoo.java)| 21.0.1-graal | [gabrielfoo](https://github.com/gabrielfoo) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gabrielfoo.pdf) | | | 00:11.433 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jatingala.java)| 21.0.1-graal | [Jatin Gala](https://github.com/jatingala) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jatingala.pdf) | | | 00:11.505 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_bufistov.java)| 21.0.1-open | [Dmitry Bufistov](https://github.com/dmitry-midokura) | uses Unsafe | [Certificate](http://gunnarmorling.github.io/1brc-certificates/bufistov.pdf) | | | 00:11.744 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_slovdahl.java)| 21.0.2-tem | [Sebastian Lövdahl](https://github.com/slovdahl) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/slovdahl.pdf) | | | 00:11.805 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_coolmineman.java)| 21.0.1-graal | [Cool_Mineman](https://github.com/coolmineman) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/coolmineman.pdf) | | | 00:11.934 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_arjenvaneerde.java)| 21.0.1-open | [arjenvaneerde](https://github.com/arjenvaneerde) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/arjenvaneerde.pdf) | | | 00:12.220 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_richardstartin.java)| 21.0.1-open | [Richard Startin](https://github.com/richardstartin) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/richardstartin.pdf) | | | 00:12.495 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_SamuelYvon.java)| 21.0.1-graal | [Samuel Yvon](https://github.com/SamuelYvon) | GraalVM native binary | [Certificate](http://gunnarmorling.github.io/1brc-certificates/SamuelYvon.pdf) | | | 00:12.568 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_MeanderingProgrammer.java)| 21.0.1-graal | [Vlad](https://github.com/MeanderingProgrammer) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/MeanderingProgrammer.pdf) | | | 00:12.800 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yonatang.java)| java | [Yonatan Graber](https://github.com/yonatang) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/yonatang.pdf) | | | 00:13.013 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_thanhtrinity.java)| 21.0.1-graal | [Thanh Duong](https://github.com/thanhtrinity) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/thanhtrinity.pdf) | | | 00:13.071 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ianopolous.java)| 21.0.1-open | [Dr Ian Preston](https://github.com/ianopolous) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ianopolous.pdf) | | | 00:13.729 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_cb0s.java)| java | [Cedric Boes](https://github.com/cb0s) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/cb0s.pdf) | | | 00:13.817 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_entangled90.java)| 21.0.1-open | [Carlo](https://github.com/entangled90) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/entangled90.pdf) | | | 00:14.502 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_eriklumme.java)| 21.0.1-graal | [eriklumme](https://github.com/eriklumme) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/eriklumme.pdf) | | | 00:14.772 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_kevinmcmurtrie.java)| 21.0.1-open | [Kevin McMurtrie](https://github.com/kevinmcmurtrie) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/kevinmcmurtrie.pdf) | | | 00:14.867 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_berry120.java)| 21.0.1-open | [Michael Berry](https://github.com/berry120) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/berry120.pdf) | | | 00:14.900 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_Judekeyser.java)| java | [Judekeyser](https://github.com/Judekeyser) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/Judekeyser.pdf) | | | 00:15.006 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_PawelAdamski.java)| java | [Paweł Adamski](https://github.com/PawelAdamski) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/PawelAdamski.pdf) | | | 00:15.662 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_semotpan.java)| 21.0.1-open | [Serghei Motpan](https://github.com/semotpan) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/semotpan.pdf) | | | 00:16.063 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_makohn.java)| 21.0.1-open | [Marek Kohn](https://github.com/makohn) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/makohn.pdf) | | | 00:16.457 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_bytesfellow.java)| 21.0.1-open | [Aleksei](https://github.com/bytesfellow) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/bytesfellow.pdf) | | | 00:16.953 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gauravdeshmukh.java)| 21.0.1-open | [Gaurav Anantrao Deshmukh](https://github.com/gauravdeshmukh) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gauravdeshmukh.pdf) | | | 00:17.046 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_dkarampi.java)| 21.0.1-open | [Dimitris Karampinas](https://github.com/dkarampi) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/dkarampi.pdf) | | | 00:17.086 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_breejesh.java)| java | [Breejesh Rathod](https://github.com/breejesh) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/breejesh.pdf) | | | 00:17.490 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_kgeri.java)| 21.0.1-open | [Gergely Kiss](https://github.com/kgeri) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/kgeri.pdf) | | | 00:17.255 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_tkosachev.java)| 21.0.1-open | [tkosachev](https://github.com/tkosachev) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/tkosachev.pdf) | | | 00:17.520 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_faridtmammadov.java)| 21.0.1-open | [Farid](https://github.com/faridtmammadov) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/faridtmammadov.pdf) | | | 00:17.717 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_omarchenko4j.java)| 21.0.1-open | [Oleh Marchenko](https://github.com/omarchenko4j) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/omarchenko4j.pdf) | | | 00:17.815 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_hallvard.java)| 21.0.1-open | [Hallvard Trætteberg](https://github.com/hallvard) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/hallvard.pdf) | | | 00:17.932 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_plbpietrz.java)| 21.0.1-open | [Bartłomiej Pietrzyk](https://github.com/plbpietrz) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/plbpietrz.pdf) | | | 00:18.251 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_seijikun.java)| 21.0.1-graal | [Markus Ebner](https://github.com/seijikun) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/seijikun.pdf) | | | 00:18.448 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_moysesb.java)| 21.0.1-open | [Moysés Borges Furtado](https://github.com/moysesb) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/moysesb.pdf) | | | 00:18.771 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_davecom.java)| 21.0.1-graal | [David Kopec](https://github.com/davecom) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/davecom.pdf) | | | 00:18.902 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_maximz101.java)| 21.0.1-graal | [Maxime](https://github.com/maximz101) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/maximz101.pdf) | | | 00:19.357 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_truelive.java)| 21.0.1-graalce | [Roman Schweitzer](https://github.com/truelive) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/truelive.pdf) | | | 00:20.691 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_Kidlike.java)| 21.0.1-graal | [Kidlike](https://github.com/Kidlike) | GraalVM native binary | [Certificate](http://gunnarmorling.github.io/1brc-certificates/Kidlike.pdf) | | | 00:21.989 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_couragelee.java)| 21.0.1-open | [couragelee](https://github.com/couragelee) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/couragelee.pdf) | | | 00:22.188 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jgrateron.java)| 21.0.1-open | [Jairo Graterón](https://github.com/jgrateron) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jgrateron.pdf) | | | 00:22.334 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_albertoventurini.java)| 21.0.1-open | [Alberto Venturini](https://github.com/albertoventurini) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/albertoventurini.pdf) | | | 00:22.457 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_rby.java)| 21.0.1-open | [Ramzi Ben Yahya](https://github.com/rby) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/rby.pdf) | | | 00:22.471 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_0xshivamagarwal.java)| 21.0.1-open | [Shivam Agarwal](https://github.com/0xshivamagarwal) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/0xshivamagarwal.pdf) | | | 00:24.986 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_kumarsaurav123.java)| 21.0.1-open | [kumarsaurav123](https://github.com/kumarsaurav123) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/kumarsaurav123.pdf) | | | 00:25.064 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_sudhirtumati.java)| 21.0.2-open | [Sudhir Tumati](https://github.com/sudhirtumati) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/sudhirtumati.pdf) | | | 00:26.500 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_felix19350.java)| 21.0.1-open | [Bruno Félix](https://github.com/felix19350) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/felix19350.pdf) | | | 00:28.381 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_bjhara.java)| 21.0.1-open | [Hampus](https://github.com/bjhara) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/bjhara.pdf) | | | 00:29.741 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_xpmatteo.java)| 21.0.1-open | [Matteo Vaccari](https://github.com/xpmatteo) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/xpmatteo.pdf) | | | 00:32.018 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_padreati.java)| 21.0.1-open | [Aurelian Tutuianu](https://github.com/padreati) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/padreati.pdf) | | | 00:34.388 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_twobiers.java)| 21.0.1-tem | [Tobi](https://github.com/twobiers) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/twobiers.pdf) | | | 00:35.875 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_MahmoudFawzyKhalil.java)| 21.0.1-open | [MahmoudFawzyKhalil](https://github.com/MahmoudFawzyKhalil) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/MahmoudFawzyKhalil.pdf) | | | 00:36.180 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_hchiorean.java)| 21.0.1-open | [Horia Chiorean](https://github.com/hchiorean) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/hchiorean.pdf) | | | 00:36.424 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_manishgarg90.java)| java | [Manish Garg](https://github.com/manishgarg90) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/manishgarg90.pdf) | | | 00:38.340 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_AbstractKamen.java)| 21.0.1-open | [AbstractKamen](https://github.com/AbstractKamen) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/AbstractKamen.pdf) | | | 00:41.982 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_criccomini.java)| 21.0.1-open | [Chris Riccomini](https://github.com/criccomini) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/criccomini.pdf) | | | 00:42.893 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_javamak.java)| 21.0.1-open | [javamak](https://github.com/javamak) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/javamak.pdf) | | | 00:46.597 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_maeda6uiui.java)| 21.0.1-open | [Maeda-san](https://github.com/maeda6uiui) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/maeda6uiui.pdf) | | | 00:58.811 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_Ujjwalbharti.java)| 21.0.1-open | [Ujjwal Bharti](https://github.com/Ujjwalbharti) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/Ujjwalbharti.pdf) | | | 01:05.094 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_muditsaxena.java)| 21.0.1-open | [Mudit Saxena](https://github.com/mudit-saxena) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/muditsaxena.pdf) | | | 01:05.979 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_dqhieuu.java)| 21.0.1-graal | [Hieu Dao Quang](https://github.com/dqhieuu) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/dqhieuu.pdf) | | | 01:06.790 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_khmarbaise.java)| 21.0.1-open | [Karl Heinz Marbaise](https://github.com/khmarbaise) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/khmarbaise.pdf) | | | 01:06.944 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_santanu.java)| 21.0.1-open | [santanu](https://github.com/santanu) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/santanu.pdf) | | | 01:07.014 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_pedestrianlove.java)| 21.0.1-open | [pedestrianlove](https://github.com/pedestrianlove) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/pedestrianlove.pdf) | | | 01:07.101 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jeevjyot.java)| 21.0.1-open | [Jeevjyot Singh Chhabda](https://github.com/jeevjyot) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/jeevjyot.pdf) | | | 01:08.811 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_alesj.java)| 21.0.1-open | [Aleš Justin](https://github.com/alesj) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/alesj.pdf) | | | 01:08.908 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_itaske.java)| 21.0.1-open | [itaske](https://github.com/itaske) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/itaske.pdf) | | | 01:09.595 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_agoncal.java)| 21.0.1-tem | [Antonio Goncalves](https://github.com/agoncal) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/agoncal.pdf) | | | 01:09.882 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_rprabhu.java)| 21.0.1-open | [Prabhu R](https://github.com/rprabhu) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/rprabhu.pdf) | | | 01:14.815 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_anandmattikopp.java)| 21.0.1-open | [twohardthings](https://github.com/anandmattikopp) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/anandmattikopp.pdf) | | | 01:25.801 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ivanklaric.java)| 21.0.1-open | [ivanklaric](https://github.com/ivanklaric) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/ivanklaric.pdf) | | | 01:33.594 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gnmathur.java)| 21.0.1-open | [Gaurav Mathur](https://github.com/gnmathur) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/gnmathur.pdf) | | | 01:53.208 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_mahadev_k.java)| java | [Mahadev K](https://github.com/mahadev-k) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/mahadev_k.pdf) | | | 01:56.607 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_abfrmblr.java)| 21.0.1-open | [Abhilash](https://github.com/abfrmblr) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/abfrmblr.pdf) | | | 03:43.521 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yehwankim23.java)| 21.0.1-open | [김예환 Ye-Hwan Kim (Sam)](https://github.com/yehwankim23) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/yehwankim23.pdf) | | | 03:59.760 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_fragmede.java)| 21.0.1-open | [Samson](https://github.com/fragmede) | | [Certificate](http://gunnarmorling.github.io/1brc-certificates/fragmede.pdf) | | | --- | | | | | | | 04:49.679 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_baseline.java) (Baseline) | 21.0.1-open | [Gunnar Morling](https://github.com/gunnarmorling) | | Note that I am not super-scientific in the way I'm running the contenders (see [Evaluating Results](#evaluating-results) for the details). This is not a high-fidelity micro-benchmark and there can be variations of up to +-3% between runs. So don't be too hung up on the exact ordering of your entry compared to others in close proximity. The primary purpose of this challenge is to learn something new, have fun along the way, and inspire others to do the same. The leaderboard is only means to an end for achieving this goal. If you observe drastically different results though, please open an issue. See [Entering the Challenge](#entering-the-challenge) for instructions how to enter the challenge with your own implementation. The [Show & Tell](https://github.com/gunnarmorling/1brc/discussions/categories/show-and-tell) features a wide range of 1BRC entries built using other languages, databases, and tools. ### Bonus Results This section lists results from running the fastest N entries with different configurations. As entries have been optimized towards the specific conditions of the original challenge description and set-up (such as size of the key set), challenge entries may perform very differently across different configurations. These bonus results are provided here for informational purposes only. For the 1BRC challenge, only the results in the previous section are of importance. #### 32 Cores / 64 Threads For officially evaluating entries into the challenge, each contender is run on eight cores of the evaluation machine (AMD EPYC™ 7502P). Here are the results from running the top 50 entries (as of commit [e1fb378a](https://github.com/gunnarmorling/1brc/commit/e1fb378acce53d8c3035ee4813ae377aaf51aa3c), Feb 2) on all 32 cores / 64 threads (i.e. SMT is enabled) of the machine: | # | Result (m:s.ms) | Implementation | JDK | Submitter | Notes | |---|-----------------|--------------------|-----|---------------|-----------| | 1 | 00:00.323 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jerrinot.java)| 21.0.2-graal | [Jaromir Hamala](https://github.com/jerrinot) | GraalVM native binary, uses Unsafe | | 2 | 00:00.326 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_thomaswue.java)| 21.0.2-graal | [Thomas Wuerthinger](https://github.com/thomaswue), [Quan Anh Mai](https://github.com/merykitty), [Alfonso² Peterssen](https://github.com/mukel) | GraalVM native binary, uses Unsafe | | 3 | 00:00.349 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_artsiomkorzun.java)| 21.0.2-graal | [Artsiom Korzun](https://github.com/artsiomkorzun) | GraalVM native binary, uses Unsafe | | | 00:00.351 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_abeobk.java)| 21.0.2-graal | [Van Phu DO](https://github.com/abeobk) | GraalVM native binary, uses Unsafe | | | 00:00.389 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_stephenvonworley.java)| 21.0.2-graal | [Stephen Von Worley](https://github.com/stephenvonworley) | GraalVM native binary, uses Unsafe | | | 00:00.408 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yavuztas.java)| 21.0.2-graal | [Yavuz Tas](https://github.com/yavuztas) | GraalVM native binary, uses Unsafe | | | 00:00.415 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_royvanrijn.java)| 21.0.2-graal | [Roy van Rijn](https://github.com/royvanrijn) | GraalVM native binary, uses Unsafe | | | 00:00.499 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_mtopolnik.java)| 21.0.2-graal | [Marko Topolnik](https://github.com/mtopolnik) | GraalVM native binary, uses Unsafe | | | 00:00.602 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_roman_r_m.java)| 21.0.1-graal | [Roman Musin](https://github.com/roman-r-m) | GraalVM native binary, uses Unsafe | | | 00:00.623 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gonixunsafe.java)| 21.0.1-open | [gonix](https://github.com/gonixunsafe) | uses Unsafe | | | 00:00.710 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JamalMulla.java)| 21.0.2-graal | [Jamal Mulla](https://github.com/JamalMulla) | GraalVM native binary, uses Unsafe | | | 00:00.727 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_tivrfoa.java)| 21.0.2-graal | [tivrfoa](https://github.com/tivrfoa) | GraalVM native binary, uses Unsafe | | | 00:00.774 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_serkan_ozal.java)| 21.0.1-open | [Serkan ÖZAL](https://github.com/serkan-ozal) | uses Unsafe | | | 00:00.788 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ebarlas.java)| 21.0.2-graal | [Elliot Barlas](https://github.com/ebarlas) | GraalVM native binary, uses Unsafe | | | 00:00.832 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_zerninv.java)| 21.0.2-graal | [zerninv](https://github.com/zerninv) | GraalVM native binary, uses Unsafe | | | 00:00.840 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gonix.java)| 21.0.1-open | [gonix](https://github.com/gonix) | | | | 00:00.857 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JaimePolidura.java)| 21.0.2-graal | [Jaime Polidura](https://github.com/JaimePolidura) | GraalVM native binary, uses Unsafe | | | 00:00.880 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java)| 21.0.2-graal | [John Ziamos](https://github.com/iziamos) | GraalVM native binary, uses Unsafe | | | 00:00.939 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_shipilev.java)| 21.0.1-open | [Aleksey Shipilëv](https://github.com/shipilev) | | | | 00:01.026 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JesseVanRooy.java)| 21.0.1-open | [JesseVanRooy](https://github.com/JesseVanRooy) | uses Unsafe | | | 00:01.118 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jonathanaotearoa.java)| 21.0.2-graal | [Jonathan Wright](https://github.com/jonathan-aotearoa) | GraalVM native binary | | | 00:01.140 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_armandino.java)| 21.0.2-graal | [Arman Sharif](https://github.com/armandino) | GraalVM native binary, uses Unsafe | | | 00:01.143 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_cliffclick.java)| 21.0.1-open | [Cliff Click](https://github.com/cliffclick) | uses Unsafe | | | 00:01.169 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_melgenek.java)| 21.0.2-open | [Yevhenii Melnyk](https://github.com/melgenek) | | | | 00:01.188 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vemanaNonIdiomatic.java)| 21.0.1-graal | [Subrahmanyam](https://github.com/vemanaNonIdiomatic) | uses Unsafe | | | 00:01.193 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gigiblender.java)| 21.0.1-open | [Florin Blanaru](https://github.com/gigiblender) | uses Unsafe | | | 00:01.234 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_obourgain.java)| 21.0.1-open | [Olivier Bourgain](https://github.com/obourgain) | uses Unsafe | | | 00:01.242 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_merykittyunsafe.java)| 21.0.1-open | [Quan Anh Mai](https://github.com/merykittyunsafe) | uses Unsafe | | | 00:01.252 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jincongho.java)| 21.0.1-open | [Jin Cong Ho](https://github.com/jincongho) | uses Unsafe | | | 00:01.267 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_linl33.java)| 22.ea.32-open | [Li Lin](https://github.com/linl33) | uses Unsafe | | | 00:01.363 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_plevart.java)| 21.0.2-tem | [Peter Levart](https://github.com/plevart) | | | | 00:01.380 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_hundredwatt.java)| 21.0.1-graal | [Jason Nochlin](https://github.com/hundredwatt) | | | | 00:01.391 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_merykitty.java)| 21.0.1-open | [Quan Anh Mai](https://github.com/merykitty) | | | | 00:01.439 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_arjenw.java)| 21.0.1-open | [Arjen Wisse](https://github.com/arjenw) | | | | 00:01.446 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ianopolousfast.java)| 21.0.1-open | [Dr Ian Preston](https://github.com/ianopolousfast) | | | | 00:01.504 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_isolgpus.java)| 21.0.1-open | [Jamie Stansfield](https://github.com/isolgpus) | | | | 00:01.514 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vemana.java)| 21.0.1-graal | [Subrahmanyam](https://github.com/vemana) | | | | 00:01.516 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vaidhy.java)| 21.0.1-graal | [Vaidhy Mayilrangam](https://github.com/vaidhy) | uses Unsafe | | | 00:01.586 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yourwass.java)| 21.0.1-open | [yourwass](https://github.com/yourwass) | uses Unsafe | | | 00:01.647 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_dpsoft.java)| 21.0.2-graal | [Diego Parra](https://github.com/dpsoft) | | | | 00:01.694 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_parkertimmins.java)| 21.0.1-open | [Parker Timmins](https://github.com/parkertimmins) | | | | 00:01.694 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_charlibot.java)| 21.0.1-graal | [Charlie Evans](https://github.com/charlibot) | uses Unsafe | | | 00:01.702 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_spullara.java)| 21.0.1-graal | [Sam Pullara](https://github.com/spullara) | | | | 00:01.733 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_EduardoSaverin.java)| java | [Sumit Chaudhary](https://github.com/EduardoSaverin) | uses Unsafe | | | 00:01.742 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_unbounded.java)| 21.0.1-open | [unbounded](https://github.com/unbounded) | | | | 00:02.241 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_flippingbits.java)| 21.0.1-graal | [Stefan Sprenger](https://github.com/flippingbits) | uses Unsafe | | | 00:02.294 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_giovannicuccu.java)| 21.0.1-open | [Giovanni Cuccu](https://github.com/giovannicuccu) | | | | 00:02.990 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_PanagiotisDrakatos.java)| 21.0.1-graal | [Panagiotis Drakatos](https://github.com/PanagiotisDrakatos) | GraalVM native binary | | | 00:03.205 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jparera.java)| 21.0.1-open | [Juan Parera](https://github.com/jparera) | | | | 00:10.929 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gamlerhart.java)| 21.0.1-open | [Roman Stoffel](https://github.com/gamlerhart) | | #### 10K Key Set The 1BRC challenge data set contains 413 distinct weather stations, whereas the rules allow for 10,000 different station names to occur. Here are the results from running the top 40 entries (as of commit [e1fb378a](https://github.com/gunnarmorling/1brc/commit/e1fb378acce53d8c3035ee4813ae377aaf51aa3c), Feb 2) against 1,000,000,000 measurement values across 10K stations (created via _./create_measurements3.sh 1000000000_), using eight cores on the evaluation machine: | # | Result (m:s.ms) | Implementation | JDK | Submitter | Notes | |---|-----------------|--------------------|-----|---------------|-----------| | 1 | 00:02.957 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_artsiomkorzun.java)| 21.0.2-graal | [Artsiom Korzun](https://github.com/artsiomkorzun) | GraalVM native binary, uses Unsafe | | 2 | 00:03.058 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_mtopolnik.java)| 21.0.2-graal | [Marko Topolnik](https://github.com/mtopolnik) | GraalVM native binary, uses Unsafe | | 3 | 00:03.186 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_stephenvonworley.java)| 21.0.2-graal | [Stephen Von Worley](https://github.com/stephenvonworley) | GraalVM native binary, uses Unsafe | | | 00:03.998 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_royvanrijn.java)| 21.0.2-graal | [Roy van Rijn](https://github.com/royvanrijn) | GraalVM native binary, uses Unsafe | | | 00:04.042 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jerrinot.java)| 21.0.2-graal | [Jaromir Hamala](https://github.com/jerrinot) | GraalVM native binary, uses Unsafe | | | 00:04.289 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gonixunsafe.java)| 21.0.1-open | [gonix](https://github.com/gonixunsafe) | uses Unsafe | | | 00:04.522 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_tivrfoa.java)| 21.0.2-graal | [tivrfoa](https://github.com/tivrfoa) | GraalVM native binary, uses Unsafe | | | 00:04.653 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JamalMulla.java)| 21.0.2-graal | [Jamal Mulla](https://github.com/JamalMulla) | GraalVM native binary, uses Unsafe | | | 00:04.733 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gonix.java)| 21.0.1-open | [gonix](https://github.com/gonix) | | | | 00:04.836 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vemanaNonIdiomatic.java)| 21.0.1-graal | [Subrahmanyam](https://github.com/vemanaNonIdiomatic) | uses Unsafe | | | 00:04.870 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_thomaswue.java)| 21.0.2-graal | [Thomas Wuerthinger](https://github.com/thomaswue), [Quan Anh Mai](https://github.com/merykitty), [Alfonso² Peterssen](https://github.com/mukel) | GraalVM native binary, uses Unsafe | | | 00:05.240 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_zerninv.java)| 21.0.2-graal | [zerninv](https://github.com/zerninv) | GraalVM native binary, uses Unsafe | | | 00:05.394 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yavuztas.java)| 21.0.2-graal | [Yavuz Tas](https://github.com/yavuztas) | GraalVM native binary, uses Unsafe | | | 00:05.906 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ebarlas.java)| 21.0.2-graal | [Elliot Barlas](https://github.com/ebarlas) | GraalVM native binary, uses Unsafe | | | 00:06.086 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_abeobk.java)| 21.0.2-graal | [Van Phu DO](https://github.com/abeobk) | GraalVM native binary, uses Unsafe | | | 00:06.379 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java)| 21.0.2-graal | [John Ziamos](https://github.com/iziamos) | GraalVM native binary, uses Unsafe | | | 00:07.113 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_melgenek.java)| 21.0.2-open | [Yevhenii Melnyk](https://github.com/melgenek) | | | | 00:07.542 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jonathan-aotearoa.java)| 21.0.2-graal | [Jonathan Wright](https://github.com/jonathan-aotearoa) | GraalVM native binary | | | 00:07.889 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gigiblender.java)| 21.0.1-open | [Florin Blanaru](https://github.com/gigiblender) | uses Unsafe | | | 00:07.970 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_cliffclick.java)| 21.0.1-open | [Cliff Click](https://github.com/cliffclick) | uses Unsafe | | | 00:08.857 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_serkan-ozal.java)| 21.0.1-open | [Serkan ÖZAL](https://github.com/serkan-ozal) | | | | 00:09.333 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_yourwass.java)| 21.0.1-open | [yourwass](https://github.com/yourwass) | uses Unsafe | | | 00:09.722 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_shipilev.java)| 21.0.1-open | [Aleksey Shipilëv](https://github.com/shipilev) | | | | 00:09.777 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vaidhy.java)| 21.0.1-graal | [Vaidhy Mayilrangam](https://github.com/vaidhy) | uses Unsafe | | | 00:10.263 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_merykittyunsafe.java)| 21.0.1-open | [Quan Anh Mai](https://github.com/merykittyunsafe) | uses Unsafe | | | 00:11.154 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_parkertimmins.java)| 21.0.1-open | [Parker Timmins](https://github.com/parkertimmins) | | | | 00:13.175 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_merykitty.java)| 21.0.1-open | [Quan Anh Mai](https://github.com/merykitty) | | | | 00:13.245 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_ianopolousfast.java)| 21.0.1-open | [Dr Ian Preston](https://github.com/ianopolousfast) | | | | 00:13.377 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_giovannicuccu.java)| 21.0.1-open | [Giovanni Cuccu](https://github.com/giovannicuccu) | | | | 00:13.761 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jparera.java)| 21.0.1-open | [Juan Parera](https://github.com/jparera) | | | | 00:14.441 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_plevart.java)| 21.0.2-tem | [Peter Levart](https://github.com/plevart) | | | | 00:15.548 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_jincongho.java)| 21.0.1-open | [Jin Cong Ho](https://github.com/jincongho) | uses Unsafe | | | 00:17.906 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_hundredwatt.java)| 21.0.1-graal | [Jason Nochlin](https://github.com/hundredwatt) | | | | 00:18.770 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_linl33.java)| 22.ea.32-open | [Li Lin](https://github.com/linl33) | uses Unsafe | | | 00:19.106 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_gamlerhart.java)| 21.0.1-open | [Roman Stoffel](https://github.com/gamlerhart) | | | | 00:20.151 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_roman_r_m.java)| 21.0.1-graal | [Roman Musin](https://github.com/roman-r-m) | GraalVM native binary, uses Unsafe; seg-faults occassionally | | | 00:22.953 | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JaimePolidura.java)| 21.0.2-graal | [Jaime Polidura](https://github.com/JaimePolidura) | GraalVM native binary, uses Unsafe | | | --- | | | | | | | DNF | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_JesseVanRooy.java)| 21.0.1-open | [JesseVanRooy](https://github.com/JesseVanRooy) | Incorrect output | | | DNF | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_vemana.java)| 21.0.1-graal | [Subrahmanyam](https://github.com/vemana) | Doesn't complete in 60 sec | | | DNF | [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_arjenw.java)| 21.0.1-open | [Arjen Wisse](https://github.com/arjenw) | Incorrect output | ## Prerequisites [Java 21](https://openjdk.org/projects/jdk/21/) must be installed on your system. ## Running the Challenge This repository contains two programs: * `dev.morling.onebrc.CreateMeasurements` (invoked via _create\_measurements.sh_): Creates the file _measurements.txt_ in the root directory of this project with a configurable number of random measurement values * `dev.morling.onebrc.CalculateAverage` (invoked via _calculate\_average\_baseline.sh_): Calculates the average values for the file _measurements.txt_ Execute the following steps to run the challenge: 1. Build the project using Apache Maven: ``` ./mvnw clean verify ``` 2. Create the measurements file with 1B rows (just once): ``` ./create_measurements.sh 1000000000 ``` This will take a few minutes. **Attention:** the generated file has a size of approx. **12 GB**, so make sure to have enough diskspace. If you're running the challenge with a non-Java language, there's a non-authoritative Python script to generate the measurements file at `src/main/python/create_measurements.py`. The authoritative method for generating the measurements is the Java program `dev.morling.onebrc.CreateMeasurements`. 3. Calculate the average measurement values: ``` ./calculate_average_baseline.sh ``` The provided naive example implementation uses the Java streams API for processing the file and completes the task in ~2 min on environment used for [result evaluation](#evaluating-results). It serves as the base line for comparing your own implementation. 4. Optimize the heck out of it: Adjust the `CalculateAverage` program to speed it up, in any way you see fit (just sticking to a few rules described below). Options include parallelizing the computation, using the (incubating) Vector API, memory-mapping different sections of the file concurrently, using AppCDS, GraalVM, CRaC, etc. for speeding up the application start-up, choosing and tuning the garbage collector, and much more. ## Flamegraph/Profiling A tip is that if you have [jbang](https://jbang.dev) installed, you can get a flamegraph of your program by running [async-profiler](https://github.com/jvm-profiling-tools/async-profiler) via [ap-loader](https://github.com/jvm-profiling-tools/ap-loader): `jbang --javaagent=ap-loader@jvm-profiling-tools/ap-loader=start,event=cpu,file=profile.html -m dev.morling.onebrc.CalculateAverage_yourname target/average-1.0.0-SNAPSHOT.jar` or directly on the .java file: `jbang --javaagent=ap-loader@jvm-profiling-tools/ap-loader=start,event=cpu,file=profile.html src/main/java/dev/morling/onebrc/CalculateAverage_yourname` When you run this, it will generate a flamegraph in profile.html. You can then open this in a browser and see where your program is spending its time. ## Rules and limits * Any of these Java distributions may be used: * Any builds provided by [SDKMan](https://sdkman.io/jdks) * Early access builds available on openjdk.net may be used (including EA builds for OpenJDK projects like Valhalla) * Builds on [builds.shipilev.net](https://builds.shipilev.net/openjdk-jdk-lilliput/) If you want to use a build not available via these channels, reach out to discuss whether it can be considered. * No external library dependencies may be used * Implementations must be provided as a single source file * The computation must happen at application _runtime_, i.e. you cannot process the measurements file at _build time_ (for instance, when using GraalVM) and just bake the result into the binary * Input value ranges are as follows: * Station name: non null UTF-8 string of min length 1 character and max length 100 bytes, containing neither `;` nor `\n` characters. (i.e. this could be 100 one-byte characters, or 50 two-byte characters, etc.) * Temperature value: non null double between -99.9 (inclusive) and 99.9 (inclusive), always with one fractional digit * There is a maximum of 10,000 unique station names * Line endings in the file are `\n` characters on all platforms * Implementations must not rely on specifics of a given data set, e.g. any valid station name as per the constraints above and any data distribution (number of measurements per station) must be supported * The rounding of output values must be done using the semantics of IEEE 754 rounding-direction "roundTowardPositive" ## Entering the Challenge To submit your own implementation to 1BRC, follow these steps: * Create a fork of the [onebrc](https://github.com/gunnarmorling/onebrc/) GitHub repository. * Run `./create_fork.sh ` to copy the baseline implementation to your personal files, or do this manually: * Create a copy of _CalculateAverage\_baseline.java_, named _CalculateAverage\_.java_, e.g. _CalculateAverage\_doloreswilson.java_. * Create a copy of _calculate\_average\_baseline.sh_, named _calculate\_average\_.sh_, e.g. _calculate\_average\_doloreswilson.sh_. * Adjust that script so that it references your implementation class name. If needed, provide any JVM arguments via the `JAVA_OPTS` variable in that script. Make sure that script does not write anything to standard output other than calculation results. * (Optional) OpenJDK 21 is used by default. If a custom JDK build is required, create a copy of _prepare\_baseline.sh_, named _prepare\_.sh_, e.g. _prepare\_doloreswilson.sh_. Include the SDKMAN command `sdk use java [version]` in the your prepare script. * (Optional) If you'd like to use native binaries (GraalVM), add all the required build logic to your _prepare\_.sh_ script. * Make that implementation fast. Really fast. * Run the test suite by executing _/test.sh _; if any differences are reported, fix them before submitting your implementation. * Create a pull request against the upstream repository, clearly stating * The name of your implementation class. * The execution time of the program on your system and specs of the same (CPU, number of cores, RAM). This is for informative purposes only, the official runtime will be determined as described below. * I will run the program and determine its performance as described in the next section, and enter the result to the scoreboard. **Note:** I reserve the right to not evaluate specific submissions if I feel doubtful about the implementation (I.e. I won't run your Bitcoin miner ;). If you'd like to discuss any potential ideas for implementing 1BRC with the community, you can use the [GitHub Discussions](https://github.com/gunnarmorling/onebrc/discussions) of this repository. Please keep it friendly and civil. The challenge runs until Jan 31 2024. Any submissions (i.e. pull requests) created after Jan 31 2024 23:59 UTC will not be considered. ## Evaluating Results Results are determined by running the program on a [Hetzner AX161](https://www.hetzner.com/dedicated-rootserver/ax161) dedicated server (32 core AMD EPYC™ 7502P (Zen2), 128 GB RAM). Programs are run from a RAM disk (i.o. the IO overhead for loading the file from disk is not relevant), using 8 cores of the machine. Each contender must pass the 1BRC test suite (_/test.sh_). The `hyperfine` program is used for measuring execution times of the launch scripts of all entries, i.e. end-to-end times are measured. Each contender is run five times in a row. The slowest and the fastest runs are discarded. The mean value of the remaining three runs is the result for that contender and will be added to the results table above. The exact same _measurements.txt_ file is used for evaluating all contenders. See the script _evaluate.sh_ for the exact implementation of the evaluation steps. ## Prize If you enter this challenge, you may learn something new, get to inspire others, and take pride in seeing your name listed in the scoreboard above. Rumor has it that the winner may receive a unique 1️⃣🐝🏎️ t-shirt, too! ## FAQ _Q: Can I use Kotlin or other JVM languages other than Java?_\ A: No, this challenge is focussed on Java only. Feel free to inofficially share implementations significantly outperforming any listed results, though. _Q: Can I use non-JVM languages and/or tools?_\ A: No, this challenge is focussed on Java only. Feel free to inofficially share interesting implementations and results though. For instance it would be interesting to see how DuckDB fares with this task. _Q: I've got an implementation—but it's not in Java. Can I share it somewhere?_\ A: Whilst non-Java solutions cannot be formally submitted to the challenge, you are welcome to share them over in the [Show and tell](https://github.com/gunnarmorling/1brc/discussions/categories/show-and-tell) GitHub discussion area. _Q: Can I use JNI?_\ A: Submissions must be completely implemented in Java, i.e. you cannot write JNI glue code in C/C++. You could use AOT compilation of Java code via GraalVM though, either by AOT-compiling the entire application, or by creating a native library (see [here](https://www.graalvm.org/22.0/reference-manual/native-image/ImplementingNativeMethodsInJavaWithSVM/). _Q: What is the encoding of the measurements.txt file?_\ A: The file is encoded with UTF-8. _Q: Can I make assumptions on the names of the weather stations showing up in the data set?_\ A: No, while only a fixed set of station names is used by the data set generator, any solution should work with arbitrary UTF-8 station names (for the sake of simplicity, names are guaranteed to contain no `;` or `\n` characters). _Q: Can I copy code from other submissions?_\ A: Yes, you can. The primary focus of the challenge is about learning something new, rather than "winning". When you do so, please give credit to the relevant source submissions. Please don't re-submit other entries with no or only trivial improvements. _Q: Which operating system is used for evaluation?_\ A: Fedora 39. _Q: My solution runs in 2 sec on my machine. Am I the fastest 1BRC-er in the world?_\ A: Probably not :) 1BRC results are reported in wallclock time, thus results of different implementations are only comparable when obtained on the same machine. If for instance an implementation is faster on a 32 core workstation than on the 8 core evaluation instance, this doesn't allow for any conclusions. When sharing 1BRC results, you should also always share the result of running the baseline implementation on the same hardware. _Q: Why_ 1️⃣🐝🏎️ _?_\ A: It's the abbreviation of the project name: **One** **B**illion **R**ow **C**hallenge. ## 1BRC on the Web A list of external resources such as blog posts and videos, discussing 1BRC and specific implementations: * [The One Billion Row Challenge Shows That Java Can Process a One Billion Rows File in Two Seconds ](https://www.infoq.com/news/2024/01/1brc-fast-java-processing), by Olimpiu Pop (interview) * [Cliff Click discussing his 1BRC solution on the Coffee Compiler Club](https://www.youtube.com/watch?v=NJNIbgV6j-Y) (video) * [1️⃣🐝🏎️🦆 (1BRC in SQL with DuckDB)](https://rmoff.net/2024/01/03/1%EF%B8%8F%E2%83%A3%EF%B8%8F-1brc-in-sql-with-duckdb/), by Robin Moffatt (blog post) * [1 billion rows challenge in PostgreSQL and ClickHouse](https://ftisiot.net/posts/1brows/), by Francesco Tisiot (blog post) * [The One Billion Row Challenge with Snowflake](https://medium.com/snowflake/the-one-billion-row-challenge-with-snowflake-f612ae76dbd5), by Sean Falconer (blog post) * [One billion row challenge using base R](https://www.r-bloggers.com/2024/01/one-billion-row-challenge-using-base-r/), by David Schoch (blog post) * [1 Billion Row Challenge with Apache Pinot](https://hubertdulay.substack.com/p/1-billion-row-challenge-in-apache), by Hubert Dulay (blog post) * [One Billion Row Challenge In C](https://www.dannyvankooten.com/blog/2024/1brc/), by Danny Van Kooten (blog post) * [One Billion Row Challenge in Racket](https://defn.io/2024/01/10/one-billion-row-challenge-in-racket/), by Bogdan Popa (blog post) * [The One Billion Row Challenge - .NET Edition](https://dev.to/mergeconflict/392-the-one-billion-row-challenge-net-edition), by Frank A. Krueger (podcast) * [One Billion Row Challenge](https://curiouscoding.nl/posts/1brc/), by Ragnar Groot Koerkamp (blog post) * [ClickHouse and The One Billion Row Challenge](https://clickhouse.com/blog/clickhouse-one-billion-row-challenge), by Dale McDiarmid (blog post) * [One Billion Row Challenge & Azure Data Explorer](https://nielsberglund.com/post/2024-01-28-one-billion-row-challenge--azure-data-explorer/), by Niels Berglund (blog post) * [One Billion Row Challenge - view from sidelines](https://www.chashnikov.dev/post/one-billion-row-challenge-view-from-sidelines), by Leo Chashnikov (blog post) * [1 billion row challenge in SQL and Oracle Database](https://geraldonit.com/2024/01/31/1-billion-row-challenge-in-sql-and-oracle-database/), by Gerald Venzl (blog post) * [One Billion Row Challenge: Learned So Far](https://gamlor.info/posts-output/2024-01-12-one-billion-row-challenge/en/), by Roman Stoffel (blog post) * [One Billion Row Challenge in Racket](https://defn.io/2024/01/10/one-billion-row-challenge-in-racket/), by Bogdan Popa (blog post) * [The 1 Billion row challenge with Singlestore](https://medium.com/@testily/the-1-billion-row-challenge-with-singlestore-224ce97e451f), by Anna Semjen (blog post) * [1BRC in .NET among fastest on Linux: My Optimization Journey](https://hotforknowledge.com/2024/01/13/1brc-in-dotnet-among-fastest-on-linux-my-optimization-journey/), by Victor Baybekov (blog post) * [One Billion Rows – Gerald’s Challenge](https://connor-mcdonald.com/2024/02/03/one-billion-rows-geralds-challenge/), by Connor McDonald (blog post) * [Reading a file insanely fast in Java](https://rmannibucau.metawerx.net/reading-a-file-insanely-fast-in-java.html), by Romain Manni-Bucau (blog post) * [#1BRC Timeline](https://tivrfoa.github.io/java/benchmark/performance/2024/02/05/1BRC-Timeline.html), by tivrfoa (blog post) * [1BRC - What a Journey](https://www.esolutions.tech/1brc-what-a-journey), by Marius Staicu (blog post) * [One Billion Rows Challenge in Golang](https://www.bytesizego.com/blog/one-billion-row-challenge-go), by Shraddha Agrawal (blog post) * [The Billion Row Challenge (1BRC) - Step-by-step from 71s to 1.7s](https://questdb.io/blog/billion-row-challenge-step-by-step/) by Marko Topolnik (blog post) * [Entering The One Billion Row Challenge With GitHub Copilot](https://devblogs.microsoft.com/java/entering-the-one-billion-row-challenge-with-github-copilot/) by Antonio Goncalves (blog post) * [DataFrame and The One Billion Row Challenge--How to use a Java DataFrame to save developer time, produce readable code, and not win any prizes](https://medium.com/@zakhav/dataframe-and-one-billion-row-challenge-97b3d0255dd1) by Vladimir Zakharov (blog post) ## License This code base is available under the Apache License, version 2. ## Code of Conduct Be excellent to each other! More than winning, the purpose of this challenge is to have fun and learn something new. ================================================ FILE: calculate_average_0xshivamagarwal.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -dsa -XX:+UseNUMA" if [[ ! "$(uname -s)" = "Darwin" ]]; then JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" fi java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_0xshivamagarwal ================================================ FILE: calculate_average_3j5a.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--add-opens=java.base/jdk.internal.util=ALL-UNNAMED" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_3j5a ================================================ FILE: calculate_average_AbstractKamen.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_AbstractKamen ================================================ FILE: calculate_average_AlexanderYastrebov.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # INPUT=${1:-"measurements.txt"} target/AlexanderYastrebov/1brc "$INPUT" ================================================ FILE: calculate_average_C5H12O5.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_C5H12O5 ================================================ FILE: calculate_average_ChrisBellew.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--add-modules jdk.incubator.vector --enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_chrisbellew ================================================ FILE: calculate_average_EduardoSaverin.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_EduardoSaverin ================================================ FILE: calculate_average_JaimePolidura.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_JaimePolidura_image ]; then target/CalculateAverage_JaimePolidura_image else echo "Native image not found. Running in JVM mode" JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+UseTransparentHugePages -XX:+TrustFinalNonStaticFields" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_JaimePolidura fi ================================================ FILE: calculate_average_JamalMulla.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_JamalMulla_image ]; then target/CalculateAverage_JamalMulla_image else JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -XX:+UseTransparentHugePages -XX:-TieredCompilation" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_JamalMulla fi ================================================ FILE: calculate_average_JesseVanRooy.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview -XX:-TieredCompilation -Dsun.stdout.encoding=UTF-8" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_JesseVanRooy ================================================ FILE: calculate_average_Judekeyser.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_Judekeyser ================================================ FILE: calculate_average_JurenIvan.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_JurenIvan ================================================ FILE: calculate_average_Kidlike.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f ./target/image_calculateaverage_Kidlike ]; then ./target/image_calculateaverage_Kidlike else # -XX:+UseEpsilonGC JAVA_OPTS="--enable-preview -Xms18g -Xmx18g -XX:+UnlockExperimentalVMOptions" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_Kidlike fi ================================================ FILE: calculate_average_MahmoudFawzyKhalil.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_MahmoudFawzyKhalil ================================================ FILE: calculate_average_MeanderingProgrammer.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_MeanderingProgrammer ================================================ FILE: calculate_average_PanagiotisDrakatos.sh ================================================ # # Copyright 2023 The original authors # # 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. # # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 JAVA_OPTS="--enable-preview -Xms1536m -Xmx10536m -XX:NewSize=256m -XX:MaxNewSize=512m -XX:MaxMetaspaceSize=512m -XX:+DisableExplicitGC -XX:+UseSerialGC -XX:-TieredCompilation -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -dsa -XX:+UseNUMA" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_PanagiotisDrakatos ================================================ FILE: calculate_average_PawelAdamski.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xnoclassgc" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_PawelAdamski ================================================ FILE: calculate_average_SamuelYvon.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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 IS A DIRECT COPY OF royvanrijn's CALCULATE SCRIPT; I AM NOT FAMILIAR WITH AOT STUFF ON JAVA. # THANKS royvanrijn!! if [ -f target/CalculateAverage_SamuelYvon_image ]; then echo "Picking up existing native image 'target/CalculateAverage_SamuelYvon_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_SamuelYvon_image else JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -dsa -XX:+UseNUMA" if [[ ! "$(uname -s)" = "Darwin" ]]; then # On OS/X, my machine, this errors: JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" fi echo "Choosing to run the app in JVM mode as no native image was found, use additional_build_step_SamuelYvon.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_SamuelYvon fi ================================================ FILE: calculate_average_Smoofie.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_Smoofie ================================================ FILE: calculate_average_Ujjwalbharti.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_Ujjwalbharti ================================================ FILE: calculate_average_YannMoisan.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_YannMoisan ================================================ FILE: calculate_average_abeobk.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_abeobk_image ]; then echo "Picking up existing native image 'target/CalculateAverage_abeobk_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_abeobk_image else JAVA_OPTS="--enable-preview" echo "Chosing to run the app in JVM mode as no native image was found, use prepare_abeobk.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_abeobk fi ================================================ FILE: calculate_average_abfrmblr.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview -Xms2g -Xmx2g" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_abfrmblr ================================================ FILE: calculate_average_adriacabeza.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-XX:+UseStringDeduplication -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+AlwaysPreTouch" java --enable-preview -classpath target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_adriacabeza ================================================ FILE: calculate_average_agoncal.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # sdk use java 21.0.1-tem JAVA_OPTS="--enable-preview -XX:+UseShenandoahGC -XX:+UseStringDeduplication -da" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_agoncal ================================================ FILE: calculate_average_ags313.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-server -Xnoclassgc -Xmx1G" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ags313 ================================================ FILE: calculate_average_albertoventurini.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xnoclassgc" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_albertoventurini ================================================ FILE: calculate_average_alesj.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_alesj ================================================ FILE: calculate_average_algirdasrascius.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_algirdasrascius ================================================ FILE: calculate_average_anandmattikopp.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_anandmattikopp ================================================ FILE: calculate_average_anestoruk.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_anestoruk ================================================ FILE: calculate_average_anitasv.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_anitasv ================================================ FILE: calculate_average_arjenvaneerde.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_arjenvaneerde ================================================ FILE: calculate_average_arjenw.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xms500m -Xmx500m --enable-preview -dsa -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:-AlwaysPreTouch" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_arjenw $@ ================================================ FILE: calculate_average_armandino.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_armandino_image ]; then echo "Picking up existing native image 'target/CalculateAverage_armandino_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_armandino_image else echo "Chosing to run the app in JVM mode as no native image was found, use prepare_armandino.sh to generate." 1>&2 JAVA_OPTS="--enable-preview -da -dsa -Xms128m -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+AlwaysPreTouch" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_armandino fi ================================================ FILE: calculate_average_artpar.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_artpar ================================================ FILE: calculate_average_artsiomkorzun.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_artsiomkorzun_image ]; then echo "Picking up existing native image 'target/CalculateAverage_artsiomkorzun_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_artsiomkorzun_image else JAVA_OPTS="--enable-preview -Xmx128m -XX:+UseSerialGC -XX:-TieredCompilation" echo "Chosing to run the app in JVM mode as no native image was found, use prepare_artsiomkorzun.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_artsiomkorzun fi ================================================ FILE: calculate_average_as-com.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector -XX:+UnlockExperimentalVMOptions -XX:ActiveProcessorCount=8 -Xms500m -Xmx500m -XX:CompilationMode=high-only -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_asun ================================================ FILE: calculate_average_baseline.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_baseline ================================================ FILE: calculate_average_baseline_original_rounding.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_baseline_original_rounding ================================================ FILE: calculate_average_berry120.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # #sdk use java 21.0.1-amzn JAVA_OPTS="-Xlog:gc=error --enable-preview --add-modules=jdk.incubator.vector" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_berry120 ================================================ FILE: calculate_average_bjhara.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_bjhara ================================================ FILE: calculate_average_breejesh.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_breejesh ================================================ FILE: calculate_average_bytesfellow.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xms12g -Xmx12g -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:-OmitStackTraceInFastThrow " java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_bytesfellow ================================================ FILE: calculate_average_cb0s.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # Arguments JAVA_OPTS="--enable-preview -XX:MaxGCPauseMillis=1 -XX:-AlwaysPreTouch -XX:+UseParallelGC -XX:+TieredCompilation" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_cb0s ================================================ FILE: calculate_average_charlibot.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_charlibot ================================================ FILE: calculate_average_cliffclick.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_cliffclick ================================================ FILE: calculate_average_coolmineman.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_coolmineman ================================================ FILE: calculate_average_couragelee.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_couragelee ================================================ FILE: calculate_average_criccomini.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # #JAVA_OPTS="-XX:+UseZGC -server -Xms700m -Xlog:gc" JAVA_OPTS="-XX:+UseZGC -Xms700m" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_criccomini ================================================ FILE: calculate_average_davecom.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_davecom ================================================ FILE: calculate_average_davery22.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_davery22 ================================================ FILE: calculate_average_ddimtirov.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # --enable-preview to use the new memory mapped segments # We don't allocate much, so just give it 1G heap and turn off GC; the AlwaysPreTouch was suggested by the ergonomics # Experimenting on the target VM config with various memory tweaks showed that UseTransparentHugePages gives us 10% boost JAVA_OPTS="--enable-preview -da -dsa -Xms1g -Xmx1g -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+AlwaysPreTouch -XX:+UseTransparentHugePages" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ddimtirov ================================================ FILE: calculate_average_deemkeen.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_deemkeen ================================================ FILE: calculate_average_dkarampi.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-XX:+AlwaysCompileLoopMethods" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_dkarampi ================================================ FILE: calculate_average_dmitry-midokura.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # #JAVA_OPTS="-verbose:gc" JAVA_OPTS="--enable-preview -Xmx128m -XX:+UseSerialGC -XX:-TieredCompilation" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_bufistov $1 $2 ================================================ FILE: calculate_average_dpsoft.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:-EnableJVMCI -XX:+UseEpsilonGC -Xms128m -Xmx128m -XX:+AlwaysPreTouch -XX:+UseTransparentHugePages -XX:-TieredCompilation -XX:+TrustFinalNonStaticFields" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_dpsoft ================================================ FILE: calculate_average_dqhieuu.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_dqhieuu ================================================ FILE: calculate_average_ebarlas.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_ebarlas_image ]; then echo "Picking up existing native image 'target/CalculateAverage_ebarlas_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_ebarlas_image else echo "Choosing to run the app in JVM mode as no native image was found, use prepare_ebarlas.sh to generate." 1>&2 java --enable-preview --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ebarlas fi ================================================ FILE: calculate_average_elh.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # target/elh/1brc-go ================================================ FILE: calculate_average_entangled90.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_entangled90 ================================================ FILE: calculate_average_eriklumme.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xms6g -Xmx6g" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_eriklumme ================================================ FILE: calculate_average_faridtmammadov.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_faridtmammadov ================================================ FILE: calculate_average_fatroom.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-server -Xnoclassgc -Xms16G -Xmx16G" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_fatroom ================================================ FILE: calculate_average_felix19350.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # ParallelGC test - Time (measured by evaluate2.sh): 00:33.130 # JAVA_OPTS="--enable-preview -XX:+UseParallelGC -XX:+UseTransparentHugePages" # G1GC test - Time (measured by evaluate2.sh): 00:26.447 # JAVA_OPTS="--enable-preview -XX:+UseG1GC -XX:+UseTransparentHugePages" # ZGC test - Time (measured by evaluate2.sh): 00:22.813 JAVA_OPTS="--enable-preview -XX:+UseZGC -XX:+UseTransparentHugePages" # EpsilonGC test - for now doesnt work because heap space gets exhausted #JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+AlwaysPreTouch" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_felix19350 ================================================ FILE: calculate_average_filiphr.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_filiphr ================================================ FILE: calculate_average_flippingbits.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--add-modules=jdk.incubator.vector --enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_flippingbits ================================================ FILE: calculate_average_fragmede.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xmx16g" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_fragmede measurements.txt ================================================ FILE: calculate_average_gabrielfoo.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xmx64m" JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions" JAVA_OPTS="$JAVA_OPTS -XX:+AlwaysPreTouch" JAVA_OPTS="$JAVA_OPTS -XX:+TrustFinalNonStaticFields -XX:InlineSmallCode=10000" JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:CICompilerCount=2 -XX:CompileThreshold=1000" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gabrielfoo ================================================ FILE: calculate_average_gabrielreid.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gabrielreid ================================================ FILE: calculate_average_gamlerhart.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xmx512m -Xlog:all=error" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gamlerhart ================================================ FILE: calculate_average_gauravdeshmukh.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gauravdeshmukh ================================================ FILE: calculate_average_gigiblender.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gigiblender ================================================ FILE: calculate_average_giovannicuccu.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector -XX:-TieredCompilation" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_giovannicuccu ================================================ FILE: calculate_average_gnabyl.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 JAVA_OPTS="-XX:+UseStringDeduplication" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gnabyl ================================================ FILE: calculate_average_gnmathur.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gnmathur ================================================ FILE: calculate_average_godofwharf.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector -DpageSize=262144 -XX:+UseParallelGC -Xms2600m -XX:ParallelGCThreads=8 -XX:Tier4CompileThreshold=1000 -XX:Tier3CompileThreshold=500 -XX:Tier3CompileThreshold=250 -Dthreads=9 -Djava.util.concurrent.ForkJoinPool.common.parallelism=9" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_godofwharf 2>/dev/null ================================================ FILE: calculate_average_gonix.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" exec cat < <(exec java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gonix) ================================================ FILE: calculate_average_gonixunsafe.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" # Copied from @serkan-ozal # Unsure if it helps (maybe something within ~10ms), # but at least it doesn't seem to make anything worse. JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions" JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:MaxInlineSize=10000 -XX:InlineSmallCode=10000 -XX:FreqInlineSize=10000" JAVA_OPTS="$JAVA_OPTS -XX:-UseCountedLoopSafepoints -XX:GuaranteedSafepointInterval=0" JAVA_OPTS="$JAVA_OPTS -XX:+TrustFinalNonStaticFields -da -dsa -XX:+UseNUMA -XX:-EnableJVMCI" if [[ ! "$(uname -s)" = "Darwin" ]]; then JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" fi exec cat < <(exec java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_gonixunsafe) ================================================ FILE: calculate_average_hallvard.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_hallvard ================================================ FILE: calculate_average_hchiorean.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xms1000M -Xmx16G" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_hchiorean ================================================ FILE: calculate_average_hundredwatt.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_hundredwatt ================================================ FILE: calculate_average_ianopolous.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ianopolous ================================================ FILE: calculate_average_ianopolousfast.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0 -XX:-UseTransparentHugePages" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ianopolousfast ================================================ FILE: calculate_average_imrafaelmerino.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xms1024m -Xms1024m -XX:+UseParallelGC -XX:MaxHeapFreeRatio=10 -XX:ParallelGCThreads=2" CHUNK_SIZE=$((50 * 1024 * 1024)) java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar \ dev.morling.onebrc.CalculateAverage_imrafaelmerino $CHUNK_SIZE ================================================ FILE: calculate_average_isolgpus.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_isolgpus ================================================ FILE: calculate_average_itaske.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_itaske ================================================ FILE: calculate_average_ivanklaric.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ivanklaric ================================================ FILE: calculate_average_iziamos.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_iziamos_image ]; then echo "Using graal" 1>&2 target/CalculateAverage_iziamos_image else echo "Using openjdk" 1>&2 JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions \ -XX:+UseEpsilonGC -Xms16m -Xmx16m -XX:-AlwaysPreTouch \ -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:CompilationMode=high-only \ -XX:C1MaxTrivialSize=500 -XX:-UseCountedLoopSafepoints -XX:+UseCMoveUnconditionally -XX:+DisableAttachMechanism \ -XX:-PreserveFramePointer -Xnoclassgc -disablesystemassertions -XX:-UsePerfData \ -XX:-UseTransparentHugePages -XX:-UseCompressedOops" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_iziamos fi ================================================ FILE: calculate_average_japplis.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_japplis $* ================================================ FILE: calculate_average_jatingala.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jatingala ================================================ FILE: calculate_average_javamak.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xmx25G -Xms15G -XX:+UseParallelGC" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_javamak ================================================ FILE: calculate_average_jbachorik.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xmx512m -Xms512m" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jbachorik $@ ================================================ FILE: calculate_average_jeevjyot.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jeevjyot ================================================ FILE: calculate_average_jerrinot.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # -XX:+UnlockDiagnosticVMOptions -XX:PrintAssemblyOptions=intel -XX:CompileCommand=print,*.CalculateAverage_mtopolnik::recordMeasurementAndAdvanceCursor" # -XX:InlineSmallCode=10000 -XX:-TieredCompilation -XX:CICompilerCount=2 -XX:CompileThreshold=1000\ if [ -f target/CalculateAverage_jerrinot_image ]; then echo "Picking up existing native image 'target/CalculateAverage_jerrinot_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_jerrinot_image else JAVA_OPTS="--enable-preview" echo "Choosing to run the app in JVM mode as no native image was found, use prepare_jerrinot.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jerrinot fi ================================================ FILE: calculate_average_jgrateron.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jgrateron ================================================ FILE: calculate_average_jincongho.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector --enable-native-access=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:InlineSmallCode=10000 -XX:FreqInlineSize=10000" JAVA_OPTS="$JAVA_OPTS -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jincongho ================================================ FILE: calculate_average_jonathan-aotearoa.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_jonathan-aotearoa_image ]; then echo "Using native image 'target/CalculateAverage_jonathan-aotearoa_image'. Delete this file to select JVM mode." 1>&2 target/CalculateAverage_jonathan-aotearoa_image else JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -dsa -XX:+UseNUMA" JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" echo "Running in JVM mode as no native image was found. Run 'prepare_jonathan-aotearoa.sh' to generate a native image." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jonathanaotearoa fi ================================================ FILE: calculate_average_jotschi.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jotschi ================================================ FILE: calculate_average_jparera.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector -XX:-TieredCompilation" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_jparera ================================================ FILE: calculate_average_justplainlaake.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_justplainlaake_image ]; then #if there is a native image, then lets run it. Else fallback to standard java execution target/CalculateAverage_justplainlaake_image else java -XX:+UseG1GC --enable-preview -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -dsa -XX:+UseNUMA --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_justplainlaake fi ================================================ FILE: calculate_average_karthikeyan97.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xms10240m -Xmx40960m " if [ -f target/CalculateAverage_karthikeyan97_image ]; then #echo "Picking up existing native image 'target/CalculateAverage_karthikeyan97_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_karthikeyan97_image -Xms10240m -Xmx40960m else #echo "Chosing to run the app in JVM mode as no native image was found, use prepare_karthikeyan97.sh to generate." 1>&2 java -Xms10240m -Xmx40960m --enable-preview --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_karthikeyan97 fi ================================================ FILE: calculate_average_kevinmcmurtrie.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_kevinmcmurtrie $1 ================================================ FILE: calculate_average_kgeri.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS -Xmx99M --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_kgeri ================================================ FILE: calculate_average_khmarbaise.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Duser.language=en-US" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_khmarbaise ================================================ FILE: calculate_average_kuduwa-keshavram.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_kuduwa_keshavram ================================================ FILE: calculate_average_kumarsaurav123.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xms16G -Xmx32G --enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_kumarsaurav123 ================================================ FILE: calculate_average_lawrey.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS -Xmx99m --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_lawrey ================================================ FILE: calculate_average_linl33.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xrs --enable-preview --add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED" JAVA_OPTS="${JAVA_OPTS} -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions" JAVA_OPTS="${JAVA_OPTS} -Xms128m -XX:+AlwaysPreTouch -XX:+AlwaysPreTouchStacks -XX:-UseTransparentHugePages" JAVA_OPTS="${JAVA_OPTS} -XX:-UseCompressedClassPointers -XX:+ForceUnreachable -XX:-CompactStrings" JAVA_OPTS="${JAVA_OPTS} -XX:CodeEntryAlignment=64 -XX:OptoLoopAlignment=64 -XX:MaxLoopPad=16 -XX:ObjectAlignmentInBytes=64" JAVA_OPTS="${JAVA_OPTS} -XX:-UseLoopPredicate -XX:LoopStripMiningIter=0 -XX:LoopStripMiningIterShortLoop=0" JAVA_OPTS="${JAVA_OPTS} -XX:-UseCountedLoopSafepoints -XX:GuaranteedSafepointInterval=0 -XX:AllocatePrefetchStyle=0" JAVA_OPTS="${JAVA_OPTS} -XX:+TrustFinalNonStaticFields -XX:LockingMode=2 -XX:+UseSystemMemoryBarrier" JAVA_OPTS="${JAVA_OPTS} -XX:-UseDynamicNumberOfCompilerThreads -XX:-UseDynamicNumberOfGCThreads" JAVA_OPTS="${JAVA_OPTS} -XX:ArchiveRelocationMode=0 -XX:-UsePerfData -XX:-UseNotificationThread -XX:-CheckIntrinsics" #JAVA_OPTS="${JAVA_OPTS} -XX:+UseZGC -XX:-ZProactive -XX:+ZCollectionIntervalOnly -XX:ZCollectionInterval=0 -XX:-ZUncommit -XX:-ZBufferStoreBarriers -XX:ZIndexDistributorStrategy=1" JAVA_OPTS="${JAVA_OPTS} -XX:+UseEpsilonGC -XX:-UseCompressedOops" #JAVA_OPTS="${JAVA_OPTS} -XX:+UseParallelGC -XX:-UseCompressedOops" #JAVA_OPTS="${JAVA_OPTS} -XX:+UseG1GC -XX:-UseCompressedOops" JAVA_OPTS="${JAVA_OPTS} -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0 -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.MethodHandle.DONT_INLINE_THRESHOLD=-1" JAVA_OPTS="${JAVA_OPTS} -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8" JAVA_OPTS="${JAVA_OPTS} -Xlog:all=off -Xverify:none -XX:SharedArchiveFile=target/CalculateAverage_linl33_dynamic.jsa" MALLOC_ARENA_MAX=1 java ${JAVA_OPTS} --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_linl33 2>/dev/null ================================================ FILE: calculate_average_maeda6uiui.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_maeda6uiui ================================================ FILE: calculate_average_mahadev-k.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_mahadev_k ================================================ FILE: calculate_average_makohn.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_makohn ================================================ FILE: calculate_average_manishgarg90.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_manishgarg90 ================================================ FILE: calculate_average_martin2038.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_martin2038_image ]; then echo "Picking up existing native image 'target/CalculateAverage_martin2038_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_martin2038_image else #JAVA_OPTS="--enable-preview" echo "Chosing to run the app in JVM mode as no native image was found, use prepare_martin2038.sh to generate." 1>&2 # JAVA_OPTS="-XX:-EnableJVMCI -Xms16g -Xmx16g -XX:+AlwaysPreTouch -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC" JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_martin2038 fi ================================================ FILE: calculate_average_mattiz.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_mattiz ================================================ FILE: calculate_average_maximz101.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview -Xmx6g" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_maximz101 ================================================ FILE: calculate_average_melgenek.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0" JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+AlwaysPreTouch" # These flags are mostly copied from the shipilev's branch. They don't really give a predictable benefit, but they don't hurt either. JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:CompileThreshold=2048 -XX:-UseCountedLoopSafepoints -XX:+TrustFinalNonStaticFields" if [[ "$(uname -s)" == "Linux" ]]; then JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" fi # https://stackoverflow.com/a/23378780/7221823 logicalCpuCount=$([ $(uname) = 'Darwin' ] && sysctl -n hw.logicalcpu_max || lscpu -p | egrep -v '^#' | wc -l) # The required heap is proportional to the number of cores. # There's roughly 6MB heap per thread required for the 10k problem. requiredMemory=$(echo "(l(15 + 6 * $logicalCpuCount)/l(2))" | bc -l) heapSize=$(echo "scale=0; 2^(($requiredMemory+1)/1)" | bc) JAVA_OPTS="$JAVA_OPTS -Xms${heapSize}m -Xmx${heapSize}m" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_melgenek ================================================ FILE: calculate_average_merykitty.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector -XX:-TieredCompilation" # -XX:+UnlockDiagnosticVMOptions -XX:PrintAssemblyOptions=intel -XX:CompileCommand=print,*.CalculateAverage_merykitty::iterate" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_merykitty ================================================ FILE: calculate_average_merykittyunsafe.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector --enable-native-access=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:InlineSmallCode=10000 -XX:FreqInlineSize=10000" JAVA_OPTS="$JAVA_OPTS -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_merykittyunsafe ================================================ FILE: calculate_average_moysesb.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-XX:+UseZGC --enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_moysesb ================================================ FILE: calculate_average_mtopolnik.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_mtopolnik_image ]; then echo "Using native image 'target/CalculateAverage_mtopolnik_image'" 1>&2 target/CalculateAverage_mtopolnik_image else JAVA_OPTS="--enable-preview" echo "Native image not found, using JVM mode." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_mtopolnik fi ================================================ FILE: calculate_average_mudit-saxena.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --enable-preview --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_muditsaxena -Xmx16384m ================================================ FILE: calculate_average_netrunnereve.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_netrunnereve ================================================ FILE: calculate_average_obourgain.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # runs with -Xmx24m on my machine, playing it safe with a larger heap JAVA_OPTS="-Xmx64m --enable-preview" # to use some black magic options JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions" # no GC, not needed JAVA_OPTS="$JAVA_OPTS -XX:+UseEpsilonGC -XX:+AlwaysPreTouch" # my finals are really final JAVA_OPTS="$JAVA_OPTS -XX:+TrustFinalNonStaticFields" # to get CalculateAverage_obourgain$OpenAddressingMap::getOrCreate to inline. A compile command wasn't enough, it was still hitting 'already compiled into a big method' JAVA_OPTS="$JAVA_OPTS -XX:InlineSmallCode=10000" # seems to be a bit faster JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:CICompilerCount=2 -XX:CompileThreshold=1000" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_obourgain ================================================ FILE: calculate_average_omarchenko4j.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_omarchenko4j ================================================ FILE: calculate_average_padreati.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_padreati ================================================ FILE: calculate_average_palmr.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_palmr ================================================ FILE: calculate_average_parkertimmins.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_parkertimmins ================================================ FILE: calculate_average_pedestrianlove.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_pedestrianlove ================================================ FILE: calculate_average_phd3.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # -agentpath:/Users/phd3/tools/async-profiler-2.9-macos/build/libasyncProfiler.so=start,event=cpu,file=profile.html JAVA_OPTS="" java $JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_phd3 ================================================ FILE: calculate_average_plbpietrz.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS -Xmx99m --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_plbpietrz ================================================ FILE: calculate_average_plevart.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules=jdk.incubator.vector" JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation" JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields" JAVA_OPTS="$JAVA_OPTS -XX:InlineSmallCode=15000 -XX:FreqInlineSize=400 -XX:MaxInlineSize=400" #JAVA_OPTS="$JAVA_OPTS -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_plevart $* ================================================ FILE: calculate_average_raipc.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens java.base/jdk.internal.util=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms128M -Xmx128M -XX:-AlwaysPreTouch -XX:-TieredCompilation -XX:CICompilerCount=1" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_raipc ================================================ FILE: calculate_average_rby.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_rby ================================================ FILE: calculate_average_rcasteltrione.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" time java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_rcasteltrione ================================================ FILE: calculate_average_ricardopieper.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_ricardopieper ================================================ FILE: calculate_average_richardstartin.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_richardstartin ================================================ FILE: calculate_average_roman-r-m.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_roman_r_m_image ]; then echo "Running native image 'target/CalculateAverage_roman_r_m_image'." 1>&2 target/CalculateAverage_roman_r_m_image else JAVA_OPTS="--enable-preview -XX:+UseTransparentHugePages" JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -dsa -XX:+UseNUMA" # epsilon GC needs enough memory or it makes things worse # see https://stackoverflow.com/questions/58087596/why-are-repeated-memory-allocations-observed-to-be-slower-using-epsilon-vs-g1 JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:-EnableJVMCI -XX:+UseEpsilonGC -Xmx1G -Xms1G -XX:+AlwaysPreTouch" echo "Running on JVM" 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_roman_r_m fi ================================================ FILE: calculate_average_royvanrijn.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_royvanrijn_image ]; then echo "Picking up existing native image 'target/CalculateAverage_royvanrijn_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_royvanrijn_image else JAVA_OPTS="--enable-preview -XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields -dsa -XX:+UseNUMA" if [[ ! "$(uname -s)" = "Darwin" ]]; then # On OS/X, my machine, this errors: JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" fi echo "Choosing to run the app in JVM mode as no native image was found, use additional_build_step_royvanrijn.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_royvanrijn fi ================================================ FILE: calculate_average_rprabhu.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_rprabhu ================================================ FILE: calculate_average_santanu.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_santanu ================================================ FILE: calculate_average_seijikun.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-XX:+UseParallelGC --enable-preview --add-modules jdk.incubator.vector" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_seijikun ================================================ FILE: calculate_average_semotpan.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_semotpan ================================================ FILE: calculate_average_serkan-ozal.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --enable-native-access=ALL-UNNAMED --add-modules=jdk.incubator.vector " JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions" JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:MaxInlineSize=10000 -XX:InlineSmallCode=10000 -XX:FreqInlineSize=10000" JAVA_OPTS="$JAVA_OPTS -XX:-UseCountedLoopSafepoints -XX:LoopStripMiningIter=0 -XX:GuaranteedSafepointInterval=0" JAVA_OPTS="$JAVA_OPTS -XX:+TrustFinalNonStaticFields -da -dsa -XX:+UseNUMA -XX:-EnableJVMCI" JAVA_OPTS="$JAVA_OPTS -XX:SharedArchiveFile=target/CalculateAverage_serkan_ozal_cds.jsa" JAVA_OPTS="$JAVA_OPTS -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0" if [[ ! "$(uname -s)" = "Darwin" ]]; then JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" fi #echo "Process started at $(date +%s%N | cut -b1-13)" eval "exec 3< <({ java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_serkan_ozal; })" read <&3 result echo -e "$result" #echo "Process finished at $(date +%s%N | cut -b1-13)" ================================================ FILE: calculate_average_shipilev.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms1g -Xmx1g -XX:-AlwaysPreTouch -XX:+UseTransparentHugePages -XX:-TieredCompilation -XX:-UseCountedLoopSafepoints -XX:+TrustFinalNonStaticFields -XX:CompileThreshold=2048 --add-opens java.base/java.nio=ALL-UNNAMED --add-exports java.base/jdk.internal.ref=ALL-UNNAMED -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=quiet -XX:CompileCommand=dontinline,dev.morling.onebrc.CalculateAverage_shipilev\$ParsingTask::seqCompute -XX:CompileCommand=dontinline,dev.morling.onebrc.CalculateAverage_shipilev\$MeasurementsMap::updateSlow -XX:CompileCommand=inline,dev.morling.onebrc.CalculateAverage_shipilev\$Bucket::matches" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_shipilev ================================================ FILE: calculate_average_slovdahl.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="${JAVA_OPTS} --enable-preview -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions" JAVA_OPTS="${JAVA_OPTS} -Xmx8g -Xms8g" JAVA_OPTS="${JAVA_OPTS} -XX:+TrustFinalNonStaticFields -XX:-UseCompressedOops" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_slovdahl ================================================ FILE: calculate_average_spullara.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_spullara ================================================ FILE: calculate_average_stephenvonworley.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_stephenvonworley_image ]; then target/CalculateAverage_stephenvonworley_image else JAVA_OPTS="--enable-preview" echo "Chosing to run the app in JVM mode as no native image was found, use prepare_stephenvonworley.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_stephenvonworley fi ================================================ FILE: calculate_average_sudhirtumati.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview -Xmx128m -XX:+UseSerialGC -XX:-TieredCompilation" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_sudhirtumati ================================================ FILE: calculate_average_thanhtrinity.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_thanhtrinity ================================================ FILE: calculate_average_thomaswue.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_thomaswue_image ]; then echo "Picking up existing native image 'target/CalculateAverage_thomaswue_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_thomaswue_image else JAVA_OPTS="--enable-preview" echo "Chosing to run the app in JVM mode as no native image was found, use prepare_thomaswue.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_thomaswue fi ================================================ FILE: calculate_average_tivrfoa.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_tivrfoa_image ]; then target/CalculateAverage_tivrfoa_image else JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_tivrfoa fi ================================================ FILE: calculate_average_tkosachev.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_tkosachev ================================================ FILE: calculate_average_tonivade.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xmx1G -Xms1G -XX:+AlwaysPreTouch -XX:+UseParallelGC -XX:-UseCompressedOops --enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_tonivade ================================================ FILE: calculate_average_truelive.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-Xmx8G -Xms2G" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_truelive ================================================ FILE: calculate_average_twobiers.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="-XX:+UseShenandoahGC -XX:+UseStringDeduplication -XX:+UseTransparentHugePages -da" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_twobiers ================================================ FILE: calculate_average_unbounded.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview --add-modules jdk.incubator.vector -XX:-TieredCompilation -XX:InlineSmallCode=10000 -XX:FreqInlineSize=10000" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_unbounded ================================================ FILE: calculate_average_vaidhy.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_vaidhy ================================================ FILE: calculate_average_vemana.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Basics JAVA_OPTS="" JAVA_OPTS="$JAVA_OPTS --enable-preview" JAVA_OPTS="$JAVA_OPTS --add-exports java.base/jdk.internal.ref=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS --add-opens java.base/java.nio=ALL-UNNAMED" #JAVA_OPTS="$JAVA_OPTS --add-modules jdk.incubator.vector" #JAVA_OPTS="$JAVA_OPTS -XX:+UnlockDiagnosticVMOptions" # JIT parameters #JAVA_OPTS="$JAVA_OPTS -Xlog:class+load=info" #JAVA_OPTS="$JAVA_OPTS -XX:+LogCompilation" JAVA_OPTS="$JAVA_OPTS -XX:+AlwaysCompileLoopMethods" #JAVA_OPTS="$JAVA_OPTS -XX:TieredStopAtLevel=1" #JAVA_OPTS="$JAVA_OPTS -XX:TieredStopAtLevel=1" #JAVA_OPTS="$JAVA_OPTS -XX:CompileCommand=inline,*State.processLine()" #JAVA_OPTS="$JAVA_OPTS -XX:+PrintAssembly" #JAVA_OPTS="$JAVA_OPTS -XX:LogFile=../hotspot.log" #JAVA_OPTS="$JAVA_OPTS -XX:+DebugNonSafepoints" #JAVA_OPTS="$JAVA_OPTS -XX:C1MaxInlineSize=150" #JAVA_OPTS="$JAVA_OPTS -XX:C1InlineStackLimit=40" #JAVA_OPTS="$JAVA_OPTS -XX:FreqInlineSize=500" #JAVA_OPTS="$JAVA_OPTS -XX:+PrintCompilation" #JAVA_OPTS="$JAVA_OPTS -XX:+PrintInlining" #JAVA_OPTS="$JAVA_OPTS -XX:CompileThreshold=20 " #JAVA_OPTS="$JAVA_OPTS -Xlog:async" # GC parameters JAVA_OPTS="$JAVA_OPTS -XX:+UseParallelGC" #JAVA_OPTS="$JAVA_OPTS -Xlog:gc*=debug:file=/tmp/gc.log" #JAVA_OPTS="$JAVA_OPTS -XX:+UseEpsilonGC -Xlog:all=off" #JAVA_OPTS="$JAVA_OPTS -XX:+PrintGC -XX:+PrintGCDetails" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_vemana "$@" ================================================ FILE: calculate_average_vemanaNonIdiomatic.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Basics JAVA_OPTS="" JAVA_OPTS="$JAVA_OPTS --enable-preview" JAVA_OPTS="$JAVA_OPTS --add-exports java.base/jdk.internal.ref=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS --add-opens java.base/java.nio=ALL-UNNAMED" # JIT parameters JAVA_OPTS="$JAVA_OPTS -XX:+AlwaysCompileLoopMethods" # GC parameters JAVA_OPTS="$JAVA_OPTS -XX:+UseParallelGC" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_vemanaNonIdiomatic "$@" ================================================ FILE: calculate_average_xpmatteo.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="--enable-preview" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_xpmatteo ================================================ FILE: calculate_average_yavuztas.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_yavuztas_image ]; then echo "Picking up existing native image 'target/CalculateAverage_yavuztas_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_yavuztas_image else JAVA_OPTS="-XX:MaxGCPauseMillis=1 -XX:-AlwaysPreTouch -XX:+UseSerialGC -XX:+TieredCompilation --enable-preview" echo "Choosing to run the app in JVM mode as no native image was found, use prepare_yavuztas.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_yavuztas fi ================================================ FILE: calculate_average_yehwankim23.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_yehwankim23 ================================================ FILE: calculate_average_yemreinci.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # JAVA_OPTS="" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_yemreinci ================================================ FILE: calculate_average_yonatang.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # # GC is overrated JAVA_OPTS="-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+AlwaysPreTouch -Xms512m -Xmx512m" java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_yonatang ================================================ FILE: calculate_average_yourwass.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 JAVA_OPTS="-Xlog:all=off -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0 --enable-preview --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.vector" eval "exec 3< <({ java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_yourwass; })" read <&3 result echo -e "$result" ================================================ FILE: calculate_average_zerninv.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -f target/CalculateAverage_zerninv_image ]; then echo "Picking up existing native image 'target/CalculateAverage_zerninv_image', delete the file to select JVM mode." 1>&2 target/CalculateAverage_zerninv_image else JAVA_OPTS="--enable-preview -Xmx512m -XX:+UseSerialGC -XX:-TieredCompilation" echo "Chosing to run the app in JVM mode as no native image was found, use prepare_zerninv.sh to generate." 1>&2 java $JAVA_OPTS --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CalculateAverage_zerninv fi ================================================ FILE: checkout.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # set -x if [ -z "$1" ] then echo "Usage: checkout.sh :" exit 1 fi parts=(${1//:/ }) echo " User: ${parts[0]}" echo "Branch: ${parts[1]}" git branch -D ${parts[0]} &>/dev/null git checkout -b ${parts[0]} git fetch https://github.com/${parts[0]}/1brc.git ${parts[1]} # git fetch git@github.com:${parts[0]}/1brc.git ${parts[1]} git reset --hard FETCH_HEAD git rebase main ================================================ FILE: cleanup.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -z "$1" ] then echo "Usage: cleanup.sh " exit 1 fi git checkout . git checkout main git branch -D $1 git pull upstream main ================================================ FILE: create_fork.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # set -eo pipefail SOURCE_FORK="baseline" usage() { echo "Usage: create_fork.sh [-s ] " echo " -s The name of the fork to copy from (default: baseline)" echo " The name of the fork to create" exit 1 } # Parse while getopts ":s:" opt; do case ${opt} in s ) SOURCE_FORK=$OPTARG ;; \? ) usage exit 1 ;; : ) echo "Invalid option: $OPTARG requires an argument" 1>&2 exit 1 ;; esac done FORK=${@:$OPTIND:1} if [ -z "$FORK" ] then usage exit 1 fi # validate the fork name has only [a-zA-Z0-9_] and then error otherwise to let the user fix if [[ ! "$FORK" =~ ^[a-zA-Z0-9_]+$ ]]; then echo "Fork name must only contain characters result in a valid Java class name [a-zA-Z0-9_]" exit 1 fi # helper function function substitute_in_file { if [[ "$OSTYPE" == "darwin"* ]]; then sed -i '' "s/$1/$2/g" $3 else sed -i "s/$1/$2/g" $3 fi } set -x # create new fork cp -i prepare_$SOURCE_FORK.sh prepare_$FORK.sh cp -i calculate_average_$SOURCE_FORK.sh calculate_average_$FORK.sh substitute_in_file $SOURCE_FORK $FORK calculate_average_$FORK.sh if [ $SOURCE_FORK == "baseline" ]; then cp -i src/main/java/dev/morling/onebrc/CalculateAverage_baseline.java src/main/java/dev/morling/onebrc/CalculateAverage_$FORK.java substitute_in_file CalculateAverage_baseline CalculateAverage_$FORK src/main/java/dev/morling/onebrc/CalculateAverage_$FORK.java else cp -i src/main/java/dev/morling/onebrc/CalculateAverage_$SOURCE_FORK.java src/main/java/dev/morling/onebrc/CalculateAverage_$FORK.java substitute_in_file $SOURCE_FORK $FORK src/main/java/dev/morling/onebrc/CalculateAverage_$FORK.java fi ================================================ FILE: create_measurements.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # java --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CreateMeasurements $1 ================================================ FILE: create_measurements2.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # java --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CreateMeasurements2 $1 ================================================ FILE: create_measurements3.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # java --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CreateMeasurements3 $1 ================================================ FILE: create_measurements_fast.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # java --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.CreateMeasurementsFast $1 ================================================ FILE: data/weather_stations.csv ================================================ # Adapted from https://simplemaps.com/data/world-cities # Licensed under Creative Commons Attribution 4.0 (https://creativecommons.org/licenses/by/4.0/) Tokyo;35.6897 Jakarta;-6.1750 Delhi;28.6100 Guangzhou;23.1300 Mumbai;19.0761 Manila;14.5958 Shanghai;31.1667 São Paulo;-23.5500 Seoul;37.5600 Mexico City;19.4333 Cairo;30.0444 New York;40.6943 Dhaka;23.7639 Beijing;39.9040 Kolkāta;22.5675 Bangkok;13.7525 Shenzhen;22.5350 Moscow;55.7558 Buenos Aires;-34.5997 Lagos;6.4550 Istanbul;41.0136 Karachi;24.8600 Bangalore;12.9789 Ho Chi Minh City;10.7756 Ōsaka;34.6939 Chengdu;30.6600 Tehran;35.6892 Kinshasa;-4.3250 Rio de Janeiro;-22.9111 Chennai;13.0825 Xi’an;34.2667 Lahore;31.5497 Chongqing;29.5500 Los Angeles;34.1141 Baoding;38.8671 London;51.5072 Paris;48.8567 Linyi;35.1041 Dongguan;23.0475 Hyderābād;17.3850 Tianjin;39.1467 Lima;-12.0600 Wuhan;30.5872 Nanyang;32.9987 Hangzhou;30.2500 Foshan;23.0292 Nagoya;35.1833 Taipei;25.0375 Tongshan;34.2610 Luanda;-8.8383 Zhoukou;33.6250 Ganzhou;25.8292 Kuala Lumpur;3.1478 Heze;35.2333 Quanzhou;24.9139 Chicago;41.8375 Nanjing;32.0608 Jining;35.4000 Hanoi;21.0283 Pune;18.5203 Fuyang;32.8986 Ahmedabad;23.0300 Johannesburg;-26.2044 Bogotá;4.7111 Dar es Salaam;-6.8161 Shenyang;41.8025 Khartoum;15.5006 Shangqiu;34.4259 Cangzhou;38.3037 Hong Kong;22.3000 Shaoyang;27.2418 Zhanjiang;21.1967 Yancheng;33.3936 Hengyang;26.8968 Riyadh;24.6333 Zhumadian;32.9773 Santiago;-33.4372 Xingtai;37.0659 Chattogram;22.3350 Bijie;27.3019 Shangrao;28.4419 Zunyi;27.7050 Sūrat;21.1702 Surabaya;-7.2458 Huanggang;30.4500 Maoming;21.6618 Nanchong;30.7991 Xinyang;32.1264 Madrid;40.4169 Baghdad;33.3153 Qujing;25.5102 Jieyang;23.5533 Singapore;1.3000 Prayagraj;25.4358 Liaocheng;36.4500 Dalian;38.9000 Yulin;22.6293 Changde;29.0397 Qingdao;36.1167 Douala;4.0500 Miami;25.7840 Nangandao;35.2992 Pudong;31.2231 Xiangyang;32.0654 Dallas;32.7935 Houston;29.7860 Zhengzhou;34.7492 Lu’an;31.7542 Dezhou;37.4513 Jinan;36.6667 Giza;29.9870 Zhaotong;27.3328 Yichun;27.8041 Nairobi;-1.2864 Guadalajara;20.6767 Philadelphia;40.0077 Ankara;39.9300 Tai’an;36.2001 Dazhou;31.2152 Langfang;39.5196 Yongzhou;26.4515 Toronto;43.7417 Suihua;46.6384 Saint Petersburg;59.9500 Qiqihar;47.3398 Suzhou;33.6333 Monterrey;25.6667 Belo Horizonte;-19.9167 Weinan;34.4996 Rangoon;16.7950 Zhangzhou;24.5093 Yuncheng;35.0304 Xianyang;34.3500 Guilin;25.2819 Atlanta;33.7628 Taizhou;32.4831 Kāshān;33.9833 Bozhou;33.8626 Abidjan;5.3167 Suqian;33.9331 Huaihua;27.5494 Ji’an;27.1172 Xiaoganzhan;30.9273 Pingdingshan;33.7350 Jiujiang;29.7048 Alexandria;31.1975 Mianyang;31.4669 Sydney;-33.8678 Huanglongsi;34.7950 Washington;38.9047 Barcelona;41.3825 Changsha;28.1987 Chenzhou;25.7989 Anqing;30.5000 Jiangmen;22.5833 Xinpu;34.5906 Yibin;28.7596 Yangzhou;32.3912 Melbourne;-37.8142 Berlin;52.5200 Hengshui;37.7348 Timbío;2.3445 Kunming;25.0433 Yiyang;28.5833 Guigang;23.0961 Changchun;43.9000 Jiangguanchi;34.0244 Casablanca;33.5333 Meizhou;24.2998 Zhangjiakou;40.8108 Chifeng;42.2663 Ürümqi;43.8225 Suzhou;31.3000 İzmir;38.4200 Linfen;36.0812 Shantou;23.3735 Kabul;34.5253 Mogadishu;2.0392 Luzhou;28.8918 Hefei;31.8639 Boston;42.3188 Liuzhou;24.3264 Zhaoqing;23.0500 Xiaoxita;30.7083 Shijiazhuang;38.0422 Ningbo;29.8750 Fuzhou;27.9814 Phoenix;33.5722 Zhuzhou;27.8407 Amman;31.9497 Chuzhou;32.3062 Jeddah;21.5433 Qingyuan;23.6842 Loudi;27.7378 Binzhou;37.3806 Deyang;31.1289 Taiyuan;37.8733 Kano;12.0000 Wuhu;31.3340 Nanning;22.8192 Harbin;45.7500 Abuja;9.0667 Yokohama;35.4442 Baojishi;34.3609 Zaozhuang;34.8667 Xiamen;24.4797 Neijiang;29.5872 Fuzhou;26.0769 Baicheng;23.9010 Anshan;41.1066 Medan;3.5894 Yulinshi;38.2655 Wenzhou;27.9991 Changzhou;31.8122 Puyang;35.7639 Jiaozuo;35.2290 Nanchang;28.6842 Ibadan;7.3964 Hechi;24.6928 Detroit;42.3834 Montréal;45.5089 Busan;35.1800 Hohhot;40.8151 Seattle;47.6211 Algiers;36.7539 Hanzhong;33.0794 Tangshan;39.6292 Shiyan;32.6351 Lucknow;26.8500 Siping;43.1715 Mashhad;36.3000 Boankra;6.6944 Changzhi;36.1953 Dubai;25.2631 Qinzhou;21.9500 Guiyang;26.5794 Bengbu;32.9354 San Francisco;37.7558 Bazhou;31.8576 Qincheng;34.5809 Suining;30.5098 Wuxi;31.5667 Leshan;29.5854 Putian;25.4394 Zhenjiang;32.2109 Faisalabad;31.4167 Guang’an;30.4673 Tongren;27.7233 Santa Cruz;-17.7892 Qinhuangdao;39.9398 Tongliao;43.6172 Jinzhou;41.1144 Heyuan;23.7503 San Diego;32.8313 Jaipur;26.9000 Xinzhou;38.4178 Lanzhou;36.0617 Wuzhou;23.4833 Athens;37.9842 Addis Ababa;9.0300 Chaoyang;41.5757 Brasília;-15.7939 Taichung;24.1439 Kuwait City;29.3697 Budapest;47.4925 Shaoguan;24.8011 Shanwei;22.7664 Quezon City;14.6300 Rizhao;35.4164 Kyiv;50.4500 Sanaa;15.3483 Meishan;30.0575 Incheon;37.4833 Guatemala City;14.6099 Birmingham;52.4800 Zhongshan;22.5333 Ningde;26.6617 Weihai;37.5000 Daqing;46.5979 Bursa;40.1833 Salvador;-12.9747 Rome;41.8931 Haikou;20.0200 La Paz;-16.5000 Xiangtan;27.8431 Pyongyang;39.0194 Minneapolis;44.9635 Omdurman;15.6500 Malang;-7.9800 Mudanjiang;44.5861 Stuttgart;48.7775 Brooklyn;40.6501 Kaohsiung;22.6150 Guayaquil;-2.1900 Lisbon;38.7253 Longyan;25.0881 Tieling;42.2841 Manchester;53.4794 Baotou;40.6562 Handan;36.6116 Cawnpore;26.4499 Dingxi;35.5806 Nanping;26.6448 Tampa;27.9945 Zigong;29.3498 Maracaibo;10.6333 Chaozhou;23.6700 Mbuji-Mayi;-6.1500 Denver;39.7620 Gulou;26.0865 Weifang;36.7167 Huai’an;33.5058 Zibo;36.7831 Ankang;32.6877 Baoshan;25.1211 Antananarivo;-18.9333 Huludao;40.7094 Munich;48.1375 Yanjiang;30.1256 Santo Domingo;18.4764 Sanming;26.2658 Tashkent;41.3111 Longba;33.5350 Yangjiang;21.8556 Jiamusi;46.8081 Luohe;33.5830 Lincang;23.8864 Medellín;6.2308 Xuanzhou;30.9475 Yunfu;22.9242 Shaoxing;30.0000 Yantai;37.3997 Huizhou;23.1115 Lishui;28.4500 Mirzāpur;25.1460 Hamburg;53.5500 Guangyuan;32.4353 Cali;3.4206 Lusaka;-15.4167 Huangshi;30.2018 Xining;36.6239 Ouagadougou;12.3686 Daegu;35.8717 Fortaleza;-3.7275 Yaoundé;3.8667 Jilin;43.8519 Dandong;40.1167 Zhuhai;22.2769 Lianshan;40.7523 Yingkou;40.6653 Antalya;36.8874 Nāgpur;21.1497 Queens;40.7498 Accra;5.5500 Bekasi;-6.2349 Ghāziābād;28.6700 Yuxi;24.3495 Luoyang;34.6587 Brisbane;-27.4678 Anshun;26.2456 Depok;-6.3940 Shangzhou;33.8680 Huainan;32.4831 Colombo;6.9344 Kuaidamao;41.7302 Baku;40.3667 Fukuoka;33.5833 Yan’an;36.5952 Jincheng;35.4906 Vancouver;49.2500 Nantong;31.9829 Tangerang;-6.1783 Caracas;10.4806 Sanmenxia;34.7736 Laibin;23.7333 Konya;37.8667 Manaus;-3.1000 Eşfahān;32.6447 Qinbaling;35.7278 Baltimore;39.3051 Ma’anshan;31.6858 Shengli;37.4500 Gaoping;30.7824 Taizhou;28.6583 Harare;-17.8292 Kowloon;22.3167 Las Vegas;36.2333 Havana;23.1367 Perth;-31.9559 Phnom Penh;11.5694 Puning;23.2993 Huaibei;33.9562 Qingyang;24.8141 Haiphong;20.8651 Chongzuo;22.4167 Rawalpindi;33.6000 Yushan;31.3867 St. Louis;38.6359 Kumasi;6.6667 Vadodara;22.3000 Hezhou;24.4164 Pingliang;35.5412 Portland;45.5371 Vishākhapatnam;17.7042 Gujranwala;32.1567 Baicheng;45.6148 Gaziantep;37.0628 Fushun;41.8708 Riverside;33.9381 Bamako;12.6458 Quito;-0.2200 Minsk;53.9000 Tijuana;32.5250 Bamenda;5.9614 Boosaaso;11.2886 Indore;22.7167 Şanlıurfa;37.1583 Vienna;48.2083 Karaj;35.8272 Kananga;-5.8970 Peshawar;34.0144 Sapporo;43.0667 Ecatepec;19.6097 Pingxiang;27.6333 Orlando;28.4773 Aleppo;36.2000 Sacramento;38.5677 Almaty;43.2775 San Juan;18.3985 San Antonio;29.4632 Yinchuan;38.4795 Thāne;19.1972 Santos;-23.9369 Blantyre;-15.7861 Bucharest;44.4325 Curitiba;-25.4297 Multan;30.1978 Tainan;22.9833 Xiping;40.0820 Port Harcourt;4.8242 Warsaw;52.2300 Jixi;45.2937 Saidu Sharif;34.7500 Liaoyang;41.2643 Beihai;21.4667 Meru;0.0500 Brazzaville;-4.2694 Fuxin;42.0127 Wuwei;37.9278 Mersin;36.8000 Bhopāl;23.2500 Lubumbashi;-11.6647 Denpasar;-8.6500 Davao;7.0667 Shuyangzha;34.1299 Adana;37.0000 Damascus;33.5131 Brussels;50.8467 Hyderabad City;25.3792 Diyarbakır;37.9100 San Jose;37.3012 Chinchvad;18.6186 Montevideo;-34.8836 Pittsburgh;40.4397 Shuozhou;39.3408 Cincinnati;39.1413 Benxi;41.2920 Baiyin;36.5448 Mosul;36.3400 Manhattan;40.7834 Caloocan City;14.6500 Kampala;0.3136 Patna;25.6000 Tegucigalpa;14.1000 Cleveland;41.4764 Sanzhou;30.8200 Changshu;31.6500 Mecca;21.4225 Heihe;50.2458 Jingdezhen;29.2942 Conakry;9.5092 Recife;-8.0500 Indianapolis;39.7771 Austin;30.3005 Sangereng;-6.2889 Kansas City;39.1238 Zhongli;24.9650 Novosibirsk;55.0333 Bilāspur;22.0900 Semarang;-6.9667 Ludhiāna;30.9100 Nārāyanganj;23.6200 Stockholm;59.3294 Chengtangcun;35.0833 Āgra;27.1800 Balandougou;13.3558 Agwār;27.1800 León de los Aldama;21.1167 Yopougon;5.3167 Puebla;19.0333 Madurai;9.9252 Hebi;35.7497 Córdoba;-31.4167 Shīrāz;29.6100 Jamshedpur;22.7925 Tabrīz;38.0814 Huzhou;30.8925 Columbus;39.9862 Sofia;42.7000 Kawasaki;35.5167 San José;9.9325 Aba;5.1167 Palembang;-2.9861 Zhangjiajie;29.1255 Kōbe;34.6900 Jiaxing;30.7522 Charlotte;35.2083 Guiping;23.4000 Lianjiang;21.6146 Ximeicun;24.9633 Jianguang;28.1958 Yucheng;29.9888 Panama City;8.9833 Xushan;30.1697 Belém;-1.4558 Virginia Beach;36.7335 Leizhou;20.9147 Gwangju;35.1653 Nāsik;20.0000 Porto Alegre;-30.0331 Valencia;10.1667 Onitsha;6.1667 Abu Dhabi;24.4667 Daejeon;36.3510 Zapopan;20.7167 Bronx;40.8501 Yekaterinburg;56.8356 Huazhou;32.6832 Kyōto;35.0117 Jinhua;29.1046 Amsterdam;52.3728 Shuangyashan;46.6388 Pizhou;34.3422 El Kelaa des Srarhna;32.0481 Dakar;14.6928 Kharkiv;49.9925 Yangshe;31.8775 Guyuan;36.0080 Rui’an;27.7780 Khulna;22.8167 Muscat;23.6139 Wenling;28.3797 Gaozhou;21.9135 Farīdābād;28.4167 Chizhou;30.6583 Tel Aviv-Yafo;32.08 Ulaanbaatar;47.9203 Goiânia;-16.6667 Fuqing;25.7232 Kayseri;38.7225 Wuzhong;37.9874 Belgrade;44.8200 Pingdu;36.7833 Milan;45.4669 Aurangābād;19.8800 Copenhagen;55.6761 Yangquan;37.8576 Yutan;28.3147 Huangshan;29.7132 Auckland;-36.8406 Makassar;-5.1619 Santiago;19.4572 Milwaukee;43.0642 Rājkot;22.3000 Prague;50.0875 Samsun;41.2903 Liangshi;27.2578 Barranquilla;10.9833 Saitama;35.8614 Guarulhos;-23.4628 Al Başrah;30.5150 Mandalay;21.9831 Juárez;31.7386 Xintai;35.9100 Wusong;30.9333 Meerut;28.9800 Yushu;44.8249 Rongcheng;26.2312 Huazhou;21.6540 Adelaide;-34.9275 Baishan;41.9377 Dayan;26.8808 Haicheng;40.8523 Tripoli;32.8872 Jiangyin;31.9087 Yicheng;31.3697 Huaiyin;33.5819 Porto;41.1621 Cacuaco;-8.8053 Soweto;-26.2678 Rosario;-32.9575 Canagatan;18.0000 Helsinki;60.1708 Jabalpur;23.1667 Providence;41.8230 Rucheng;32.3852 Nizhniy Novgorod;56.3269 Ahvāz;31.3203 Jepara;-6.5333 Shaoyang;32.9387 Comayagüela;14.0981 Laiwu;36.1833 Sharjah;25.3575 Kalamboli;19.2333 Jingling;30.6667 Kazan;55.7964 Suwon;37.2667 Yongcheng;33.9317 Sumedang;-6.8400 Calgary;51.0500 Cần Thơ;10.0333 Yiwu;29.3081 Bagam;1.0833 Beidao;34.6020 Vasai;19.4700 Xiangshui;26.5964 Jacksonville;30.3322 Akçaabat;41.0167 Campinas;-22.9058 Dadukou;26.5849 Mombasa;-4.0500 Lingcheng;22.7000 Najafgarh;28.6092 Vila Velha;3.2167 Gāzipura;23.9889 Chelyabinsk;55.1500 Vārānasi;25.3189 Xinyu;27.7950 Qom;34.6400 Hargeysa;9.5631 Zhangye;38.9355 Hiroshima;34.3914 Maiduguri;11.8333 Chiang Mai;18.7953 Doha;25.2867 Maputo;-25.9667 Mbandaka;0.0478 Pikine;14.7500 Medina;24.4700 Srīnagar;34.0900 Omsk;54.9667 Dublin;53.3500 Liaoyuan;42.8976 Cilacap;-7.7167 Yingtan;28.2333 Bandar Lampung;-5.4500 Samara;53.2028 Guankou;28.1417 Ulsan;35.5500 Dhanbād;23.7998 Dingzhou;38.5158 Lianyuan;27.6961 Rongcheng;29.8239 Kaiyuan;36.0656 Nay Pyi Taw;19.7475 Zhuji;29.7169 Kigali;-1.9536 Bukavu;-2.5000 Leiyang;26.4179 Bafoussam;5.4667 Yichun;47.7235 Benin City;6.3333 Rostov;47.2333 Xiantao;30.3833 Amritsar;31.6400 Callao;-12.0333 Salt Lake City;40.7776 Alīgarh;27.8800 Shagamu;6.8333 Yingchuan;34.1511 Ciudad Nezahualcóyotl;19.4081 Tbilisi;41.7225 Guwāhāti;26.1722 Ufa;54.7261 Fès;34.0433 São Luís;-2.5283 Biên Hòa;10.9500 Sevilla;37.2400 N’Djamena;12.1100 Mexicali;32.6633 Nezahualcóyotl;19.4006 Ikare;7.5167 Nashville;36.1715 Tamale;9.4075 Xibeijie;39.7370 Yuyao;30.0372 Hāora;22.5800 Hanchuan;30.6520 Gongzhuling;43.5036 Krasnoyarsk;56.0089 Cologne;50.9364 Bujumbura;-3.3833 Bishkek;42.8747 Zhufeng;36.0000 São Gonçalo;-22.8269 Yerevan;40.1814 Ezhou;30.3972 Nur-Sultan;51.1472 Tongjin;37.2333 Nouakchott;18.0858 Xiashi;30.5333 Rānchi;23.3600 Taixing;32.1724 Vereeniging;-26.6736 Gwalior;26.2215 Zhongwei;37.5139 Goyang;37.6500 Oslo;59.9133 Vijayavāda;16.5193 Chandīgarh;30.7500 Edmonton;53.5344 Sendai;38.2682 Raleigh;35.8324 Mizhou;35.9900 Tunis;36.8064 Xishan;27.6609 Barquisimeto;10.0678 Hegang;47.3139 Voronezh;51.6717 Perm;58.0139 Changwon;35.2708 Fangchenggang;21.6000 Shouguang;36.8833 Bogor;-6.5966 Cartagena;10.4000 Matola;-25.9667 Jodhpur;26.2800 Memphis;35.1087 Ogbomoso;8.1333 Rangapukur;25.5600 Managua;12.1544 Sanya;18.2533 Shymkent;42.3167 Wutong;30.6326 Niamey;13.5150 Shubrā al Khaymah;30.1286 Linhai;28.8523 Denizli;37.7667 Maceió;-9.6658 Monrovia;6.3133 Wafangdian;39.6271 Zhongxiang;31.1690 Louisville;38.1663 Odesa;46.4775 Thủ Đức;10.8266 Volgograd;48.7086 Islamabad;33.6931 Port-au-Prince;18.5333 Xinyi;22.3559 Raipur;21.2500 Arequipa;-16.4000 Richmond;37.5295 Zaoyang;32.1287 Buffalo;42.9018 Shuizhai;33.4433 Xingyi;25.0910 Kota;25.1800 Quetta;30.1833 Kathmandu;27.7172 Ottawa;45.4247 Lilongwe;-13.9833 Asmara;15.3228 Freetown;8.4844 Vientiane;17.9667 Jerusalem;31.7784 Riga;56.9489 Bangui;4.3733 Dushanbe;38.5367 Lomé;6.1319 Ashgabat;37.9500 Zagreb;45.8167 Libreville;0.3903 Cotonou;6.3667 Pretoria;-25.7461 Vilnius;54.6872 Winnipeg;49.8844 Quebec City;46.8139 Chisinau;47.0228 Port Moresby;-9.4789 Skopje;41.9961 Djibouti;11.5883 Gaza;31.5069 Kingston;17.9714 Rabat;34.0209 San Salvador;13.6989 The Hague;52.0800 Asunción;-25.3000 Juba;4.8500 Maseru;-29.3100 Bissau;11.8500 Valletta;35.8983 Bratislava;48.1439 Kitchener;43.4186 Manama;26.2250 Tallinn;59.4372 Beirut;33.8869 Cape Town;-33.9253 Tirana;41.3289 Sarajevo;43.8564 Wellington;-41.2889 Banjul;13.4531 Halifax;44.6475 Canberra;-35.2931 Yamoussoukro;6.8161 Victoria;48.4283 Nicosia;35.1725 Windhoek;-22.5700 Saint-Denis;-20.8789 Porto-Novo;6.4972 Sucre;-19.0475 Ljubljana;46.0514 Nassau;25.0781 Bloemfontein;-29.1167 Fort-de-France;14.6000 New Delhi;28.6139 Gaborone;-24.6581 Paramaribo;5.8522 Dili;-8.5536 Dodoma;-6.1731 Georgetown;6.8058 Gibraltar;36.1333 Malabo;3.7500 Suva;-18.1416 Nouméa;-22.2758 Pristina;42.6633 Male;4.1753 Port Louis;-20.1644 Podgorica;42.4413 Willemstad;12.1080 Bern;46.9481 Gitega;-3.4283 Reykjavík;64.1467 Luxembourg;49.6117 Papeete;-17.5334 Praia;14.9180 Sri Jayewardenepura Kotte;6.9108 Bridgetown;13.0975 Moroni;-11.6990 Thimphu;27.4722 Mbabane;-26.3208 Honiara;-9.4319 Port of Spain;10.6667 Castries;14.0167 Putrajaya;2.9300 Cayenne;4.9330 São Tomé;0.3361 Port-Vila;-17.7333 Bandar Seri Begawan;4.8903 Monaco;43.7333 Apia;-13.8333 Tarawa;1.3382 Oranjestad;12.5186 Saint Helier;49.1858 Mamoudzou;-12.7794 Majuro;7.0833 Douglas;54.1450 George Town;19.2866 Victoria;-4.6167 Kingstown;13.1578 Andorra la Vella;42.5000 Saint John’s;17.1167 Nuku‘alofa;-21.1333 Nuuk;64.1814 Belmopan;17.2514 Roseau;15.3014 Basseterre;17.3000 Tórshavn;62.0000 Road Town;18.4167 Pago Pago;-14.2740 Grand Turk;21.4664 Marigot;18.0706 Palikir;6.9178 Funafuti;-8.5167 Vaduz;47.1410 Lobamba;-26.4465 Avarua;-21.2070 Saint George’s;12.0500 San Marino;43.9346 Tifariti;26.1580 Philipsburg;18.0237 Capitol Hill;15.2137 Stanley;-51.7000 Hamilton;32.2942 Vatican City;41.9040 Alofi;-19.0560 Basse-Terre;16.0104 Hagåtña;13.4745 Jamestown;-15.9251 Brades;16.7928 Yaren;-0.5477 Gustavia;17.8958 Ngerulmud;7.5006 Saint-Pierre;46.7811 The Valley;18.2167 Mata-Utu;-13.2825 Kingston;-29.0569 Longyearbyen;78.2167 Adamstown;-25.0667 Flying Fish Cove;-10.4167 King Edward Point;-54.2833 Bareilly;28.3640 Quảng Hà;15.9333 Domaa-Ahenkro;7.2833 Oklahoma City;35.4676 Xingcheng;24.1681 Dongtai;32.8534 Yingcheng;24.1878 Chiba;35.6073 Al Mijlad;11.0339 Pekanbaru;0.5092 Luocheng;22.7645 Dnipro;48.4675 Danyang;31.9948 Godē;5.9527 Natal;-6.9838 Nada;19.5000 Zamboanga City;6.9042 Kirkuk;35.4667 Bridgeport;41.1918 Naples;40.8333 Wuchuan;21.4283 Huilong;31.8131 Morelia;19.7683 Málaga;36.7194 Cebu City;10.3200 Al Manşūrah;31.0500 Coimbatore;11.0168 Santo Domingo Este;18.4855 Setagaya;35.6466 Sŏngnam;37.4333 Taishan;22.2486 Teresina;-5.0949 Solāpur;17.6800 Tangier;35.7767 Kermānshāh;34.3142 Krasnodar;45.0333 Baidoa;3.1167 Gaalkacyo;6.7697 Anqiu;36.3619 Feicheng;36.1860 Seberang Jaya;5.4000 El Alto;-16.5047 Kitakyūshū;33.8833 Meishan;34.1736 Khartoum North;15.6333 Kisangani;0.5153 Aguascalientes;21.8760 Marrakech;31.6300 Donetsk;48.0028 Trujillo;-8.1120 New Orleans;30.0687 Taihe;30.8706 Trichinopoly;10.7903 Xin’an;34.3662 Taihecun;45.7680 Kashgar;39.4681 Naucalpan de Juárez;19.4753 Çankaya;39.9244 Santiago de Cuba;20.0217 Owerri;5.4833 Padang;-0.9500 Qingzhou;36.6967 Lichuan;30.2965 Santiago del Estero;-27.7833 Daye;30.1003 Hengzhou;22.6896 Fort Worth;32.7817 Hartford;41.7661 Esenyurt;41.0470 Campo Grande;-20.4839 Zhuanghe;39.6896 Bobo-Dioulasso;11.1833 Ad Dammām;26.4333 Quzhou;28.9545 Lhasa;29.6534 Jiaozhou;36.2481 Bunia;1.5667 Taguig City;14.5200 Cancún;21.1606 Mérida;20.9700 Yangchun;22.1717 Dengtalu;36.1386 Morādābād;28.8389 Antipolo;14.5842 Abeokuta;7.1608 Bucheon;37.5000 Zhoushan;29.9887 Tiruppūr;11.1085 Natal;-5.7833 Chihuahua;28.6353 Klang;3.0333 As Sulaymānīyah;35.5572 Gurgaon;28.4560 Turin;45.0792 Tucson;32.1541 Hai’an;32.5320 Ar Ramādī;33.4258 Laiyang;36.9758 Sale;34.0333 Jalandhar;31.2569 Marseille;43.2964 Bucaramanga;7.1333 Kaifeng Chengguanzhen;34.8519 Ikeja;6.6186 Eskişehir;39.7767 Saltillo;25.4231 Gaomi;36.3833 Liverpool;53.4075 Ipoh;4.5972 Oran;35.6969 Portsmouth;50.8058 Southampton;50.9025 Hermosillo;29.0989 Cochabamba;-17.3833 Weichanglu;37.1792 Shache;38.4261 Wuxi;26.5895 Leping;28.9632 Hailun;47.4585 Macheng;31.1817 Akure;7.2500 Ilorin;8.5000 Yuci;37.6823 Saratov;51.5333 Erbil;36.1912 Iguaçu;-22.7400 Pasig City;14.5750 Dehui;44.5333 Kālkāji Devi;28.5485 Bhubaneshwar;20.2700 Honolulu;21.3294 Tongchuan;34.9057 Cheongju;36.6333 Chengxiang;31.4515 Thessaloníki;40.6403 Warri;5.5167 Sakai;34.5733 Pointe-Noire;-4.8000 Rongjiawan;29.1409 Mediouna;33.4500 Butterworth;5.3992 Renqiu;38.7094 Xindi;29.8182 Bhayandar;19.2900 Wu’an;36.6941 São Bernardo do Campo;-23.7000 Māndvi;19.3000 Barinas;8.6333 Zijinglu;34.7513 Gaoyou;32.7847 Culiacán;24.8069 Hejian;38.4451 Yiyang;26.4103 Puxi;35.2125 Androtsy;-24.1000 McAllen;26.2252 Qingping;34.5380 Omaha;41.2627 João Pessoa;-7.1200 Dongyang;29.2785 Querétaro;20.5875 Kraków;50.0614 George Town;5.4144 Abaeté;-19.1583 Palermo;2.8917 Valencia;39.4700 Xigazê;29.2500 El Paso;31.8476 Seyhan;36.9831 Niigata;37.9161 Hempstead;40.6629 Leeds;53.8003 Hamamatsu;34.7108 Bağcılar;41.0344 Xiangxiang;27.7389 Bilbao;43.2569 Chaohucun;31.6783 Fuyang;30.0553 Homs;34.7333 Küçükçekmece;41.0000 Lubango;-14.9167 Zouping;36.8625 Khŭjand;40.2833 San Luis Potosí;22.1511 Nottingham;52.9533 Cencheng;22.9297 Dali;25.6808 Hamhŭng;39.9167 Korla;41.7259 Rājshāhi;24.3667 Erzurum;39.9086 Frankfurt;50.1106 Al ‘Ayn;24.2075 Songzi;30.1772 Albuquerque;35.1054 Patiāla;30.3400 Laixi;36.8667 Bahawalpur;29.3956 Zhongba;31.7761 Kaduna;10.5167 Qingnian;36.8494 San Pedro Sula;15.5000 Qamdo;31.1375 Xinhualu;34.3962 Nerima;35.7355 Guangshui;31.6189 Pietermaritzburg;-29.6167 Baardheere;2.3333 Tlajomulco de Zúñiga;20.4736 Samarinda;-0.5000 Changhua;24.0667 Sizhan;39.0099 Ciudad Guayana;8.3667 Cúcuta;7.8942 Dhūlia;20.8997 Burco;9.5279 Licheng;31.4174 Ōta-ku;35.5613 Thiruvananthapuram;8.4875 Tyumen;57.1500 Nampula;-15.1167 Zaporizhzhia;47.8500 Kyaukse;21.6131 Chengguan;35.5256 Kumamoto;32.8031 Nehe;48.4800 Osogbo;7.7667 Cabinda;-5.5600 Kermān;30.2833 Zunhua;40.1881 Rochester;43.1680 Valenzuela;14.7000 Orūmīyeh;37.5486 Wugang;26.7345 Shuangqiao;35.0833 Tshikapa;-6.4167 São José dos Campos;-23.1789 Comodoro Rivadavia;-45.8647 Cagayan de Oro;8.4833 Tondo;14.6170 Langzhong;31.5504 Qian’an;40.0059 Lviv;49.8425 Sarasota;27.3387 Reynosa;26.0922 Santo André;-23.6572 An Najaf;32.0000 Sagamihara;35.5667 Guli;28.9008 Mississauga;43.6000 Concepción;-36.8282 Okayama;34.6500 Anlu;31.2575 Mwanza;-2.5167 Fresno;36.7830 Changsha;22.3762 Torreón;25.5394 Shihezi;44.3054 Enugu;6.4528 Jaboatão;-8.1803 Tulsa;36.1283 Yanggok;37.6333 Yatou;37.1653 Edogawa;35.7066 Allentown;40.5961 Ribeirão Prêto;-21.1783 Xichang;27.8983 Latakia;35.5167 Bhiwandi;19.2967 City of Parañaque;14.4700 Dasmariñas;14.3294 Dayton;39.7805 Sahāranpur;29.9640 Warangal;17.9689 Soledad;10.9167 Osasco;-23.5328 Dashiqiao;40.6328 Birmingham;33.5279 Nampo;38.7333 Shiliguri;26.7100 Banjarmasin;-3.3200 Sha Tin;22.3802 Salem;11.6500 General Santos;6.1167 Cocody;5.3500 Ḩamāh;35.1333 Pendik;40.8747 Bacoor;14.4624 Vellore;12.9165 Shishi;24.7355 Guadalupe;25.6775 Adachi;35.7749 Qianxi;27.0284 Tolyatti;53.5089 Macau;22.2006 Bauchi;10.3158 Hamilton;43.2567 Łódź;51.7769 Miluo Chengguanzhen;28.8049 Gaizhou;40.4019 Karbalā’;32.6167 Leling;37.7333 Jianshe;34.2189 Shizuoka;34.9756 Charleston;32.8168 Jingcheng;32.0058 Kochi;9.9700 Mar del Plata;-38.0000 Cape Coral;26.6443 Tasikmalaya;-7.3161 Rasht;37.2808 Xinmin;41.9952 Shanhu;29.5908 Zhongshu;27.8116 Gorakhpur;26.7637 Zaragoza;41.6500 Wrocław;51.1100 Acapulco de Juárez;16.8636 Bahār;34.9072 Kumul;42.8193 Murcia;37.9861 Pinghu;30.7005 Palermo;38.1157 Guankou;30.9933 Tlaquepaque;20.6167 Songyang;34.4603 Ch’ŏngjin;41.7833 Puyang Chengguanzhen;35.7004 Qionghu;28.8417 Zhaodong;46.0635 Narela;28.8527 Huambo;-12.7767 Wenchang;31.0540 Villavicencio;4.1500 Shulan;44.4079 Tlalnepantla;19.5367 Catia La Mar;10.6000 Sargodha;32.0836 Al Hufūf;25.3833 Durango;24.0250 Jalingo;8.9195 Bouaké;7.6833 Mazatlán;23.2167 San Jose del Monte;14.8139 Ansan;37.3167 Abomey-Calavi;6.4486 Lingbao Chengguanzhen;34.5221 Hwasu-dong;37.1997 Sialkot City;32.4925 Huế;16.4667 Bulawayo;-20.1700 Xiping;25.6005 Sanhe;39.9808 Ch’ŏnan;36.8167 Contagem;-19.9319 Jieshou;33.2605 Selçuklu;37.8814 Ērer Sātā;9.5667 Honchō;35.6946 Düsseldorf;51.2333 Izhevsk;56.8500 Guntūr;16.3008 Guixi;28.2861 Sorocaba;-23.5017 Yıldırım;40.1100 Petaling Jaya;3.0972 Tengyue;25.0208 Wuxue;29.8518 Rasūlpur;28.5700 Bhāvnagar;21.7600 Brahmapur;19.3150 Qufu;35.6000 Gaobeidian;39.3257 Ruiming;25.8833 Columbia;34.0378 Concord;37.9722 Noida;28.57 Gold Coast;-28.0167 Xinshi;31.0236 Ta‘izz;13.5789 Aracaju;-10.9167 Jeonju;35.8167 Bhāngar;22.5800 Jin’e;29.3395 Barnaul;53.3333 Makati City;14.5567 Rotterdam;51.9167 Changping;40.2248 Benghazi;32.1167 Kryvyi Rih;47.9086 Ḩalwān;29.8453 Colorado Springs;38.8674 Suohe;34.7879 Chimalhuacán;19.4167 Xinxing;39.8734 Bhilai;21.3667 Mangalore;12.9020 Port-Bouët;5.2667 Zhuangyuan;37.3056 Glasgow;55.8611 Namangan;41.0011 Ulyanovsk;54.3167 Tân An;10.9050 Irkutsk;52.2833 Bhilai;21.2100 Nāgercoil;8.1833 Jos;9.9167 Pontianak;-0.0206 Bazhou;39.1235 Turpan;42.9512 Villa Nueva;14.5314 Las Piñas City;14.4500 Springfield;42.1155 Knoxville;35.9692 Meihekou;42.5279 Jurong;31.9579 Feira de Santana;-12.2500 Khabarovsk;48.4833 Zhugang;28.1277 Leipzig;51.3400 Xinji;37.9423 Serang;-6.1200 Jambi;-1.5900 Kandahār;31.6200 San Miguel de Tucumán;-26.8167 Gothenburg;57.7075 Yaroslavl;57.6167 Baton Rouge;30.4420 Zhangshu;28.0667 Vladivostok;43.1333 Ogden;41.2279 Kuantan;3.8167 Raurkela;22.2492 Cuttack;20.5236 Ar Rayyān;25.2500 Haifa;32.8192 Malanje;-9.5333 Bacolod;10.6765 Zhuozhou;39.4887 Grand Rapids;42.9619 Uberlândia;-18.9231 Albany;42.6664 Tianchang;32.6853 Kawaguchi;35.8077 Itabashi;35.7512 Suginami-ku;35.6995 Cimahi;-6.8712 Tuxtla;16.7528 Garoua;9.3000 Ḩafr al Bāţin;28.4342 Tumkūr;13.3400 Balikpapan;-1.2768 Durban;-29.8833 Kagoshima;31.6000 Al Ḩillah;23.4895 Makhachkala;42.9833 Sihui;23.3448 Irapuato;20.6667 Brampton;43.6833 Luocheng;30.9793 Mbale;1.0806 Pereira;4.8143 Bakersfield;35.3529 Uvira;-3.4000 Dortmund;51.5167 Chuxiong;25.0461 Mission Viejo;33.6096 Zāhedān;29.4964 New Haven;41.3113 Shah Alam;3.0722 Wenlan;23.3989 Cuiabá;-15.5958 Shangzhi;45.2116 Essen;51.4508 Botou;38.0740 Mymensingh;24.7539 Anyang;37.3833 Genoa;44.4111 Durgāpur;23.5500 Port Sudan;19.6167 Al Mafraq;32.3399 Puente Alto;-33.6167 Dehra Dūn;30.3450 Aţ Ţā’if;21.2751 Wancheng;18.8000 Hachiōji;35.6664 Xiulin;29.7211 Fu’an;27.1000 Tomsk;56.4886 Tonalá;20.6167 Juiz de Fora;-21.7619 Worcester;42.2705 Kolwezi;-10.7167 Chāpra;25.7848 Bristol;51.4536 Luofeng;37.3590 Lingyuan;41.2407 Irbid;32.5500 Al Maḩallah al Kubrá;30.9686 Wencheng;19.6167 Bremen;53.0833 Ciudad Bolívar;8.1219 Orenburg;51.7667 Shenzhou;38.0005 Āsansol;23.6800 Kuiju;36.8528 Zhenzhou;32.2739 Surakarta;-7.5667 Dresden;51.0500 Kolhāpur;16.6917 Benguela;-12.5500 Ensenada;31.8578 Kemerovo;55.3667 Lanxi;29.2167 Dangyang;30.8258 Farīdpur;23.6020 Herāt;34.3419 Luanzhou;39.7396 Hamadān;34.8064 Novokuznetsk;53.7667 Nellore;14.4500 Palma;39.5667 Chiclayo;-6.7630 Nānded;19.1500 Ajmer;26.4499 Al Ḩudaydah;14.8022 Kimhae;35.2342 Provo;40.2457 Rustenburg;-25.6667 Amrāvati;20.9258 Poznań;52.4083 Huanghua;38.3710 Muntinlupa City;14.3800 Londrina;-23.3100 Nansana;0.3639 Tabūk;28.3972 Heroica Matamoros;25.8797 Akron;41.0798 Samarkand;39.6547 Xingcheng;40.6189 Kaiyuan;42.5380 Hannover;52.3667 Valledupar;10.4833 Fengcheng;40.4543 Ixtapaluca;19.3186 Ghulja;43.9000 City of Calamba;14.2167 Ananindeua;-1.3658 Xiangyang;34.2999 Fuding;27.2000 An Nāşirīyah;31.0439 Al Ḩillah;32.4833 Ibagué;4.4333 Ryazan;54.6300 Kassala;15.4500 Tripoli;34.4367 Chang’an;29.4761 Kōtō-ku;35.6728 Naberezhnyye Chelny;55.7000 Aksu;41.1850 Salta;-24.7833 Astrakhan;46.3500 Gulbarga;17.3290 Cadaado;6.1403 Lyon;45.7600 Wuhai;39.6844 Mingguang;32.7816 Antwerp;51.2178 Yazd;31.8822 Touba;14.8667 Ardabīl;38.2425 Bīkaner;28.0167 Agartala;23.8333 Ndola;-12.9667 Himeji;34.8167 Palm Bay;27.9631 Tultitlán de Mariano Escobedo;19.6450 Ailan Mubage;43.9080 Bandar ‘Abbās;27.1833 Bắc Ninh;21.1833 Ciudad Apodaca;25.7817 Santa Teresa del Tuy;10.2333 Maipú;-33.5167 Penza;53.2000 Soacha;4.5872 Al Qaţīf;26.5560 Port Said;31.2625 Loni;28.7500 Yucheng;36.9329 Sevastopol;44.6050 Meknès;33.8950 Mixco;14.6338 Arāk;34.0917 Pohang;36.0322 Longzhou;30.3200 Anda;46.4078 Jinghong;22.0057 Sheffield;53.3808 Utsunomiya;36.5551 Suez;29.9667 Nuremberg;49.4539 Surrey;49.1900 Heshan;22.7697 Ujjain;23.1700 Jiaji;19.2431 Santa Marta;11.2419 Joinvile;-26.3204 Beining;41.5961 Des Moines;41.5725 Hải Dương;20.9397 Carrefour;18.5344 Maturín;9.7423 Homyel’;52.4453 Leicester;52.6344 Yanji;42.9044 Macapá;0.0330 Yicheng;31.7117 Ulhāsnagar;19.2200 Al ‘Āshir min Ramaḑān;30.3065 Lipetsk;52.6167 Al ‘Amārah;31.8333 Bhīlwāra;25.3500 Encheng;22.1879 Murrieta;33.5719 Aden;12.8000 Kitwe;-12.8167 Matsuyama;33.8333 Jhānsi;25.4486 Pingtung;22.6761 Lapu-Lapu City;10.3127 Matsudo;35.7876 Montería;8.7500 Ichikawa;35.7219 Nagqu;31.4766 Imus;14.4297 Hsinchu;24.8167 Kimberley;-28.7383 Jammu;32.7300 Belas;-8.9983 Talatona;-8.9167 Ile-Ife;7.4667 Purnea;25.7780 Kirov;58.6000 Mykolaiv;46.9750 Meicheng;30.6412 Tinnevelly;8.7136 Niterói;-22.8833 Farāh;32.3436 Sukkur;27.7061 Oujda-Angad;34.6867 Taozhou;30.8956 Marka;1.6833 Higashi-ōsaka;34.6794 Johor Bahru;1.4556 Hongjiang;27.1167 Bello;6.3333 Chimbote;-9.0745 Duisburg;51.4347 Qaraghandy;49.8028 Mesa;33.4015 Toulouse;43.6045 Likasi;-10.9833 Dengtacun;41.4237 Lembok;-8.5650 Zhijiang;30.4271 Chengjiao;35.4043 Beipiao;41.8020 Suoluntun;45.2279 Staten Island;40.5834 Kota Bharu;6.1333 Ciudad López Mateos;19.5500 Celaya;20.5222 Vinh;18.6667 Duyun;26.2672 Los Mochis;25.7835 ‘Ajmān;25.4136 Nyala;12.0500 Larkana;27.5583 Wichita;37.6895 Nishinomiya-hama;34.7376 Cheboksary;56.1333 Yuanping;38.7299 Toledo;41.6638 Yueqing;28.1188 Edinburgh;55.9533 Belgaum;15.8500 Tula;54.2000 Gdańsk;54.3475 Shahe;36.8622 Serra;-20.1289 Brookhaven;40.8320 Gaoping;35.7911 Dunhua;43.3667 Az Zarqā’;32.0833 Sylhet;24.9000 Kaihua;23.3715 Caerdydd;51.4817 Jāmnagar;22.4700 Fuyuan;48.3614 Gaya;24.7500 Florianópolis;-27.6122 Chiniot;31.7194 Jiannan;31.3446 Ōita;33.2333 Berbera;10.4356 Kaliningrad;54.7003 Mbeya;-8.9000 Nangong;37.3606 Ambattūr;13.1143 Katlehong;-26.3333 Kurashiki;34.5833 Barura;23.3750 San Juan;-31.5342 Vila Velha;-20.3364 Mazār-e Sharīf;36.7000 Shekhupura;31.7111 Worthing;50.8147 Piura;-5.2000 Mandaluyong City;14.5800 Kartal;40.8872 Jiaojiangcun;28.6804 Harrisburg;40.2752 Laohekou;32.3849 Kâğıthane;41.0719 Agege;6.6219 Angeles City;15.1472 São José do Rio Prêto;-20.8081 Pasay City;14.5500 Beian;48.2395 Fujin;47.2489 Salt Lake City;22.6100 Balashikha;55.8167 Xiaoyi;37.1449 Port St. Lucie;27.2796 Lanús;-34.7000 Qingzhen;26.5555 Jiangshan;28.7412 Ba‘qūbah;33.7500 Katsushika-ku;35.7434 Buraydah;26.3333 Longjiang;27.5820 Tân Uyên;11.0508 Long Beach;33.7977 Marikina City;14.6500 Manado;1.4931 Campos;-21.7539 Kanazawa;36.5611 Iloilo;10.7167 Reno;39.5497 Calabar;4.9767 Kuqa;41.7156 Piraeus;37.9430 Madison;43.0822 Jalgaon;21.0040 Cranbourne;-38.0996 Kowloon City;22.3282 Mauá;-23.6678 Fukuyama;34.4833 Xicheng;23.3629 Kikwit;-5.0386 Amagasaki;34.7333 Pyeongtaek;36.9922 Kurnool;15.8300 Little Rock;34.7256 Melaka;2.1944 General Trias;14.3833 Jian’ou;27.0387 Esenler;41.0339 Huadian;42.9633 Ţanţā;30.7833 Kota Kinabalu;5.9750 Kursk;51.7167 Minzhu;43.7192 Stavropol;45.0500 Udaipur;24.5800 Mogi das Cruzes;-23.5228 Mariupol;47.0958 Eslāmshahr;35.5606 San Nicolás de los Garza;25.7500 Greenville;34.8354 Sham Shui Po;22.3307 Barcelona;10.1333 Constantine;36.3650 Tanbei;35.0907 Ado-Ekiti;7.6167 Batman;37.8870 Bandung;-6.9120 Baisha;29.4774 Avcılar;40.9792 Denton;33.2175 Rodriguez;14.7167 Taoyuan District;24.9913 Yongji;34.8670 Chauddagram;23.2283 Danjiangkou;32.5408 Kahramanmaraş;37.5833 Fujisawa;35.3500 Andijon;40.7833 Mathura;27.4925 Zürich;47.3744 Diadema;-23.6861 Machida;35.7106 Ulan-Ude;51.8272 Spokane;47.6671 Ning’an;44.3439 Zhangjiakou Shi Xuanhua Qu;40.5944 Sunch’ŏn;39.4167 Jinchang;38.5168 Kashiwa;35.8676 Guangming;45.3357 Sāngli;16.8667 Oakland;37.7904 Beira;-19.8333 Kupang;-10.1702 Jeju;33.5097 Davangere;14.4666 Caxias do Sul;-29.1667 Manizales;5.1000 Yogyakarta;-7.8014 Calicut;11.2500 Koumassi;5.3000 Veracruz;19.1903 Zanjān;36.6789 Welkom;-27.9831 Shinagawa-ku;35.6092 Kenitra;34.2500 Agadir;30.4333 Somolu;6.5408 Uyo;5.0500 Sokoto;13.0622 Lancaster;40.0420 Masan;35.1833 Huili Chengguanzhen;26.6614 Sidi Bouzid;35.0381 Cusco;-13.5250 Porto Velho;-8.7619 Sihŭng;37.3799 Xalapa;19.5400 Florencio Varela;-34.8167 Tver;56.8578 Trabzon;41.0050 Malatya;38.3486 Xunyang;32.8357 Uijeongbu;37.7486 Akola;20.7000 Aomori;40.8223 Yan’an Beilu;44.0144 Owo;7.1961 Aves;41.4400 Buenaventura;3.8772 Poughkeepsie;41.6950 Laval;45.5833 Boise;43.6005 Toyota;35.0824 Santa Rosa;14.3167 Daloa;6.8833 Elazığ;38.6744 Hpa-An;16.8906 Rahimyar Khan;28.4200 Bettiah;26.8014 Ugep;5.8000 Winston-Salem;36.1029 Kushtia;23.9101 Mishan;45.5420 Hailin;44.5735 Seremban;2.7222 Boa Vista;2.8194 Lecheng;25.1307 Zhengjiatun;43.5130 Luhansk;48.5667 Pencheng;29.6792 Magnitogorsk;53.3833 Thủ Dầu Một;10.9667 Takamatsu;34.3500 Syracuse;43.0407 El Obeid;13.1833 Da’an;45.5050 Xingren;25.4352 Biñan;14.3333 Arusha;-3.3667 Fenyang;37.2647 Ajdābiyā;30.7556 Paju;37.8667 Mataram;-8.5833 Carapicuíba;-23.5192 Jhang City;31.2694 Tepic;21.5083 Jayapura;-2.5330 Rio Branco;-9.9747 Toyama;36.6959 Fangting;31.1282 Sanandaj;35.3144 Delhi Cantonment;28.6000 Linghai;41.1676 Shorāpur;16.5200 Toyonaka;34.7833 Sochi;43.5853 Bhāgalpur;25.2500 Bellary;15.1000 Ivanovo;57.0000 Kisumu;-0.0833 Augusta;33.3645 Türkmenabat;39.0833 Zaria;11.0667 Bryansk;53.2500 Kumi;36.1195 Asyūţ;27.1833 Wanyuan;32.0691 Maracay;10.2469 Padiāla;30.5667 Yüreğir;36.9981 Jiexiu;37.0282 Stockton;37.9765 Bogra;24.8500 Santa Fe;-31.6333 Campina Grande;-7.2306 Nagasaki;32.7447 Szczecin;53.4325 Maringá;-23.4000 Palm Coast;29.5389 Chattanooga;35.0660 Qazvīn;36.2667 Quilon;8.8800 Jundiaí;-23.1858 Hirakata;34.8167 Gifu;35.4232 Khayelitsha;-34.0403 Kūkatpalli;17.4849 Jiangjiafan;31.0238 Maungdaw;20.8167 Kissimmee;28.3041 Durham;35.9792 Dera Ghazi Khan;30.0331 Miyazaki;31.9167 Bhātpāra;22.8700 Hazāribāgh;23.9800 Sandakan;5.8388 Taytay;14.5692 Hejin;35.5914 Olinda;-8.0000 Thanh Hóa;19.8075 Surgut;61.2500 Nnewi;6.0167 Minamisuita;34.7594 Thiès;14.7833 Betsiboka;-16.9500 Belgorod;50.6000 Tāngāil;24.2644 Yokosuka;35.2500 Nha Trang;12.2450 Malabon;14.6600 Al Jahrā’;29.3500 Yola;9.2300 Pasto;1.2078 Lobito;-12.3597 Al Jubayl;27.0000 Saurimo;-9.6500 Bologna;44.4939 Arlington;32.6998 Gujrat;32.5739 Ad Dīwānīyah;31.9892 Piracicaba;-22.7253 Hancheng;35.4768 Karamay;45.5799 Kākināda;16.9661 San-Pédro;4.7500 Tieli;46.9838 Cilegon;-6.0027 Darbhanga;26.1700 Victorville;34.5277 Gwoza;11.0861 Saki;8.6667 Tétouan;35.5667 Aqtöbe;50.2836 Bibā;28.9218 Plovdiv;42.1500 Oyo;7.8500 Ilesa;7.6167 Tarlac City;15.4869 Okazaki;34.9543 Sirūr;18.8300 Cainta;14.5667 Ḩā’il;27.5167 Garoowe;8.4000 Olomouc;49.5939 Yidu;30.3880 Lianzhou;24.7868 Ceel Baraf;3.2073 Florence;43.7714 Christchurch;-43.5310 Nuevo Laredo;27.4861 Brno;49.1925 Bawshar;23.5333 London;42.9836 Novi Sad;45.2542 Aurora;39.7083 Gusau;12.1500 Modesto;37.6375 Kaunas;54.8972 Sardārshahr;28.4401 Zêtang;29.2380 Las Palmas;28.1272 Ichinomiya;35.3039 Taunggyi;20.7836 Lancaster;34.6935 Fayetteville;36.0714 Cantonment;31.5167 Pānihāti;22.6900 Huancayo;-12.0667 Betim;-19.9678 Usulután;13.3500 Sintra;38.7992 Chitungwiza;-17.9939 Hatay;36.2025 Iquitos;-3.7500 Sivas;39.7500 Helixi;30.6267 Scranton;41.4044 Mamou;10.3833 Al Kharj;24.1483 Manukau City;-37.0000 Stoke-on-Trent;53.0000 Cumaná;10.4500 Rohtak;28.8909 Vinnytsia;49.2333 Māler Kotla;30.5167 Lipa City;13.9411 Mandaue City;10.3333 Bhawana;31.5661 Khorramābād;33.4878 Ambon;-3.7000 Takasaki;36.3219 Butuan;8.9534 Toyohashi;34.7692 Keelung;25.1333 Baguio City;16.4119 La Florida;-33.5333 Gebze;40.8000 Lengshuijiang;27.6858 Petare;10.4833 Anguo;38.4177 Kita-ku;35.7528 Madan;30.3392 Panshi;42.9392 Bharatpur;27.6833 Az Zubayr;30.3833 Caucaia;-3.7167 Vitsyebsk;55.1917 Shinjuku;35.6939 Nicolás Romero;19.5833 Huichang;34.9136 Nagano;36.6486 Bauru;-22.3150 Yanjiao;39.9432 Bochum;51.4819 Tecámac;19.7131 Anápolis;-16.3339 Coventry;52.4081 Oxnard;34.1964 Korba;22.3500 Cibinong;-6.4850 Dāsarhalli;13.0465 Qardho;9.5000 Wŏnsan;39.1475 Kocaeli;40.7625 Newcastle;-27.7464 Iligan;8.2300 Youngstown;41.0993 Cabuyao;14.2750 Kayapınar;37.9400 Nakano;35.7073 Utrecht;52.0833 Bengkulu;-3.7956 Orizaba;18.8500 Blumenau;-26.9333 Montes Claros;-16.7306 Indio;33.7346 Pétion-Ville;18.5128 Tungi;23.9000 Buurhakaba;2.7833 Shuanghe;30.3866 Konak;38.4189 Ash Shuhadā’;30.6039 Pensacola;30.4413 Umuahia;5.5333 Gedaref;14.0333 Palu;-0.8950 Mardan;34.2012 Pokhara;28.2083 Mahilyow;53.9167 Wudalianchi;48.6433 Hrodna;53.6667 Sungai Petani;5.6500 Linxia Chengguanzhen;35.6000 Sīkar;27.6200 Nam Định;20.4200 Vitória;-20.2889 Sasarām;24.9500 Karūr;10.9601 Franca;-20.5389 Vladimir;56.1286 Karnāl;29.6860 Kismaayo;-0.3581 São Vicente;-23.9633 Kawagoe;35.9251 Villahermosa;17.9892 Taraz;42.9000 Cubal;-13.1117 Luena;-11.7918 Ibb;13.9667 Yong’an;25.9733 Wuppertal;51.2667 Manisa;38.6306 San Fernando;15.0333 Chānda;19.9500 Minatitlán;17.9833 Malmö;55.6058 Nizhniy Tagil;57.9167 San Pedro;14.3583 Cuito;-12.3833 Hongzhai;34.9857 Guédiawaye;14.7833 Wakayama;34.2333 Tin Shui Wai;22.4608 Pavlodar;52.3000 Gimpo;37.7000 Itaquaquecetuba;-23.4864 Nara;34.6844 Van;38.4942 Corrientes;-27.4833 Neiva;2.9345 Arkhangelsk;64.5500 Batangas;13.8300 Licheng;24.4935 Koshigaya;35.8911 Sinŭiju;40.1000 Cabimas;10.4000 Yakeshi;49.2842 Oruro;-17.9667 Ahmadnagar;19.0800 Āvadi;13.1097 Sarai Alamgir;32.9000 Varna;43.2167 Gorgān;36.8386 Takatsuki;34.8500 Holguín;20.8869 Qo‘qon;40.5286 Semey;50.4333 Yingmen;39.9487 Cariacica;-20.2639 Palmira;3.5833 Tapachula;14.9000 Bydgoszcz;53.1219 Antofagasta;-23.6500 Anaheim;33.8390 Rājahmundry;16.9800 Chita;52.0333 Bonita Springs;26.3558 Caruaru;-8.2828 Nice;43.7034 Shāhjānpur;27.8800 António Enes;-16.2333 Central Coast;-33.3000 Pamukkale;37.9167 Wad Medani;14.4000 Öskemen;49.9833 Tanch’ŏn;40.4580 Tokorozawa;35.7996 Hosūr;12.7409 Serekunda;13.4333 Greensboro;36.0956 Kūstī;13.1667 Cuddapah;14.4700 Simferopol;44.9484 Sarıyer;41.1669 Sumqayıt;40.5917 Ōtsu;35.0167 Vitória da Conquista;-14.8658 Makiivka;48.0556 Vũng Tàu;10.3833 Brest;52.1347 Uruapan;19.4208 Gómez Palacio;25.5611 Yeosu;34.7333 Kaluga;54.5333 Meram;37.8364 Muzaffarpur;26.1225 Alwar;27.5498 Lublin;51.2500 Lianran;24.9211 Buôn Ma Thuột;12.6667 Cuernavaca;18.9186 Alicante;38.3453 East London;-33.0175 Tarsus;36.9165 Matadi;-5.8167 Islip;40.7385 Cimanggis;-6.3645 Kaesŏng;37.9667 Beni;0.5000 Sultanbeyli;40.9683 Huntsville;34.6981 Randburg;-26.0936 Jitpur;27.6666 Beylikdüzü;41.0011 Petrolina;-9.3928 Chinju;35.2000 Tangdong;25.9755 Reading;51.4542 Sambalpur;21.4700 Hangu;39.2320 Belfast;54.5964 Shah Latif Town;24.8806 Iwaki;37.0505 Salamanca;20.5703 Yingzhong;32.2381 Viña del Mar;-33.0244 Hirosaki;40.6031 Bielefeld;52.0211 Az Zaqāzīq;30.5667 Nazrēt;8.5414 Cuenca;-2.8974 Tampere;61.4981 Bonn;50.7333 Al Fayyūm;29.3084 Blida;36.4722 Ciudad Obregón;27.4939 Uberaba;-19.7478 Corpus Christi;27.7254 Asan;36.7833 Ribeirão das Neves;-19.7669 Soledad de Graciano Sánchez;22.1833 Maebashi;36.3895 Kāmārhāti;22.6686 Gəncə;40.6828 Kendari;-3.9907 Balıkesir;39.6333 Thái Nguyên;21.6000 Asahikawa;43.7706 Smolensk;54.7828 Dahūk;36.8667 Wŏnju;37.3417 Nakuru;-0.3000 Lafia;8.5000 Fort Wayne;41.0888 Güngören;41.0225 Koriyama;37.4004 Bamiantong;44.9164 Taourirt;34.4169 Rāmpur;28.8000 Debrecen;47.5300 Cabanatuan City;15.4908 Markham;43.8767 Fayetteville;35.0850 Pelotas;-31.7719 Roodepoort;-26.1625 Volzhskiy;48.8056 Ann Arbor;42.2759 Bijāpur;16.8300 Sukabumi;-6.9181 Tharād;24.3926 Ratnāgiri;16.9944 Ulanhot;46.0726 Yunzhong;39.8143 Jackson;32.3157 Cotabato;7.2200 Al Fallūjah;33.3500 Kōchi;33.5589 Minna;9.6139 Bielsko-Biała;49.8225 Canoas;-29.9200 Gwangmyeongni;37.4760 Cluj-Napoca;46.7667 Bari;41.1253 Pucallpa;-8.3833 Kuching;1.5575 Gonder;12.6075 Kikuyu;-1.2500 Mekele;13.4969 Gonaïves;19.4456 Guasave;25.5744 Naha;26.2122 Nantes;47.2181 Binxian;35.0364 Shimoga;13.9333 Hotan;37.1000 Kaiyuan;23.7147 Ciudad del Este;-25.5167 Temara;33.9267 Antioch;37.9787 Uíge;-7.6167 Osh;40.5333 Córdoba;37.8845 Camagüey;21.3839 Mobile;30.6782 San Salvador de Jujuy;-24.1833 Al Kūt;32.4907 Cherepovets;59.1167 Lexington;38.0423 San Miguelito;9.0330 Okene;7.5500 Timişoara;45.7597 Menongue;-14.6556 Jūnāgadh;21.5200 Katsina;12.9889 Poltava;49.5894 Maroua;10.5971 Xiaoli;22.6782 Kaech’ŏn;39.6986 Asan;36.3500 Tehuacán;18.4617 Aksaray;38.3742 Attiecoubé;5.3333 Coatzacoalcos;18.1500 Saransk;54.1833 Nukus;42.4667 Puerto Plata;19.8000 Hāpur;28.7309 Zalantun;48.0033 Münster;51.9625 Trichūr;10.5200 An Nhơn;13.9170 Taubaté;-23.0250 Binangonan;14.4514 Cirebon;-6.7071 Safi;32.2833 Mitchells Plain;-34.0506 Boaco;12.4719 Asheville;35.5707 Cà Mau;9.1833 Trenton;40.2237 Toshima;35.7261 Vologda;59.2167 Barddhamān;23.2333 Tanza;14.3944 Bor;6.2125 Kasur;31.1167 Yakou;33.2937 Santa Rosa;38.4458 Orël;52.9686 Shahr-e Qods;35.7214 Kingston upon Hull;53.7444 Mannheim;49.4878 Santa Ana;33.7367 Guarujá;-23.9936 Playa del Carmen;20.6281 Pasir Gudang;1.5028 Datang;22.9481 Panvel;18.9944 Lansing;42.7142 Zarzal;4.3983 La Victoria;18.5500 Port Elizabeth;-33.9581 Alanya;36.5500 Nizāmābād;18.6720 Al ‘Ajamī;31.0959 Quy Nhơn;13.7667 Yakutsk;62.0272 Yokkaichi;34.9650 Ponta Grossa;-25.0994 Catania;37.5000 Chalco;19.2647 Henderson;36.0133 Gwangju;37.3667 Longquan;25.9106 Karlsruhe;49.0092 Shahrīār;35.6597 Kurgan;55.4408 Kasugai;35.2475 St. Paul;44.9478 Sariwŏn;38.5064 St. Catharines;43.1833 Karawang;-6.3125 Lakeland;28.0557 Niagara Falls;43.0600 Oulgaret;11.9570 Taboão da Serra;-23.5328 Vladikavkaz;43.0400 Nghi Sơn;19.4170 Juliaca;-15.4833 Parbhani;19.2700 Hisar;29.1500 Puerto La Cruz;10.2000 Puerto Princesa;9.7500 Podolsk;55.4311 Meguro;35.6415 Ciudad Victoria;23.7389 Ciudad Santa Catarina;25.6833 Santarém;-2.4300 Newark;40.7245 Vaughan;43.8333 Āwasa;7.0500 Ōakashichō;34.6500 Yeşilyurt;38.2961 Pekalongan;-6.8833 Pondokaren;-6.2811 Adıyaman;37.7639 Kurume;33.3167 Vila Nova de Gaia;41.1333 Paulista;-7.9408 Oaxaca;17.0606 Armenia;4.5300 Akita;39.7200 Awka;6.2000 San Bernardo;-33.5833 Curepipe;-20.3188 Iksan;35.9439 Pachuca;20.1000 Al Maţarīyah;31.1833 Sóc Trăng;9.6028 Bahía Blanca;-38.7167 Coacalco;19.6333 San Juan del Río;20.3833 Fort Collins;40.5477 Limeira;-22.5650 Popayán;2.4542 Praia Grande;-24.0061 Qianzhou;28.3185 Newcastle;54.9800 Māldah;25.0119 Ciudad General Escobedo;25.7933 Oyster Bay;40.7846 Montpellier;43.6119 Phan Thiết;10.9333 Stockport;53.4083 Al Qunfudhah;19.1264 Bada Barabīl;22.1200 Tampico;22.2553 Białystok;53.1353 Coatepeque;14.7000 Ash Shāmīyah;31.9636 Mérida;8.4800 Chakwal;32.9303 Murmansk;68.9706 Ar Raqqah;35.9500 Afyonkarahisar;38.7578 Bihār;25.1970 Jember;-8.1727 Valladolid;41.6528 Reading;40.3400 Springfield;37.1943 İskenderun;36.5817 Al Mubarraz;25.4416 Petrópolis;-22.5050 Augsburg;48.3689 Navotas;14.6667 Chernihiv;51.4939 Yangsan;35.3333 Comilla;23.4500 Irvine;33.6772 Bradford;53.8000 Tagum;7.4478 Espoo;60.2056 Valparaíso;-33.0461 Silang;14.2306 Finglas;53.4597 Rangpur;25.7500 Vigo;42.2314 Sikandarābād;28.4512 Battalgazi;38.4228 Bāli;22.6500 Çorum;40.5455 Ismailia;30.5833 Vila Teixeira da Silva;-12.0072 Pānīpat;29.3875 Delmas;18.5500 Strasbourg;48.5833 Mabalacat;15.2167 Batna;35.5500 Āīzawl;23.7272 Tambov;52.7167 Dexing;28.9580 Iaşi;47.1622 Kunp’o;37.3500 San Lorenzo;-25.3400 Santa Maria;14.8183 Sonpur;25.7000 Groznyy;43.3125 Kherson;46.6425 Anchorage;61.1508 The Woodlands;30.1738 Hong’an;47.2100 Resistencia;-27.4514 Graz;47.0708 Karīmnagar;18.4386 Braşov;45.6667 Sumida;35.7107 Sekondi;4.9433 South Bend;41.6767 Morioka;39.7021 Sonīpat;28.9900 Sétif;36.1900 Atyraū;47.1167 Ipswich;52.0594 Dewās;22.9600 Hulin;45.7671 Rockford;42.2596 Savannah;32.0286 Itagüí;6.1667 Croix-des-Bouquets;18.5761 Farg‘ona;40.3864 Magway;20.1500 Huixquilucan;19.3611 Lincoln;40.8099 Ichalkaranji;16.7000 Punto Fijo;11.7167 Várzea Grande;-15.6500 Sincelejo;9.2950 Ibaraki;34.8164 Katowice;50.2667 Ar Ruşayfah;32.0178 Camaçari;-12.6978 Jersey City;40.7184 San Cristóbal;7.7682 Round Lake Beach;42.3791 Bolton;53.5780 San Pablo;14.0700 Suzano;-23.5428 Korhogo;9.4167 Bhatinda;30.2300 Cascavel;-24.9556 Flint;43.0236 Ostrava;49.8356 Tacna;-18.0147 Shreveport;32.4653 Al Qurnah;31.0158 Jālna;19.8410 Aarhus;56.1572 Constanţa;44.1667 San Juan Sacatepéquez;14.7189 Myeik;12.4333 Fukushima;37.7608 Coro;11.3950 Bago;17.3333 Pasarkemis;-6.1703 Fuquan;26.7039 Tongchuanshi;35.0800 Sannai;24.5984 Huozhou;36.5726 Temuco;-38.7333 Ihosy;-22.4036 Sterlitamak;53.6333 Tegal;-6.8667 Ica;-14.0667 Lucena;13.9333 Plano;33.0502 Jining;41.0300 Chuncheon;37.8667 Malārd;35.6658 Pematangsiantar;2.9600 Satna;24.6005 Long Xuyên;10.3736 Myrtle Beach;33.7094 Governador Valadares;-18.8500 Petrozavodsk;61.7833 Santa Ana;13.9950 Mau;25.9417 Companiganj;22.8750 Wiesbaden;50.0825 Mingaora;34.7717 Davenport;41.5565 Bukhara;39.7667 Sumbe;-11.2053 Việt Trì;21.3000 Sunderland;54.9060 Bārāsat;22.7200 Cherkasy;49.4444 Maastricht;50.8500 Çorlu;41.1500 Kostroma;57.7681 Gyeongsan;35.8167 Ciego de Ávila;21.8481 Mamuju;-2.6833 Los Alcarrizos;18.5167 Nizhnevartovsk;60.9500 San Mateo;14.6969 Shaowu;27.3417 Canton;40.8078 Sfax;34.7400 Dinājpur;25.6167 Dire Dawa;9.6000 Gagnoa;6.1333 Khmelnytskyi;49.4167 Farrukhābād;27.3900 Lashkar Gāh;31.5831 Posadas;-27.3667 Gatineau;45.4833 Windsor;42.2833 Santa Clarita;34.4175 Kunsan;35.9833 Minato;35.6580 Qarshi;38.8667 Chula Vista;32.6281 Saugor;23.8300 Kafr ad Dawwār;31.1311 Ratlām;23.3340 Yeosu;34.7607 Crato;-7.4639 Mỹ Tho;10.3500 Volta Redonda;-22.5228 Eugene;44.0564 Tsu;34.7184 Novorossiysk;44.7167 Bijiao;22.9311 Székesfehérvár;47.1956 Soubré;5.7833 Palmas;-10.1844 Majene;-3.5403 Sorong;-0.8667 Chandler;33.2825 Craiova;44.3333 Thái Bình;20.4461 Binjai;3.5986 Fuchū;35.6689 Gabela;-10.8500 Sakarya;40.7833 Dayr az Zawr;35.3333 Brāhmanbāria;23.9656 Mito;36.3658 Sartā;36.4491 Plymouth;50.3714 Santo Domingo de los Colorados;-0.2542 Arnavutköy;41.1856 Nguru;12.8792 Maradi;13.4833 Gombe;10.2904 Gijón;43.5333 Drug;21.1900 Floridablanca;7.2167 Handwāra;34.4000 Quilmes;-34.7167 Talisay;10.2500 Kunduz;36.7286 Yoshkar-Ola;56.6328 Nalchik;43.4833 Aswān;24.0889 Ichihara;35.4981 Salem;44.9233 Zhytomyr;50.2500 Imphāl;24.8074 İnegöl;40.0806 Chernivtsi;48.3000 Tarija;-21.5333 Siddhirganj;23.6833 Bimo;4.2558 Heroica Nogales;31.3186 Bergen;60.3894 Sumaré;-22.8219 Feni;23.0183 Sumy;50.9167 Anantapur;14.6800 Westminster;51.4947 Xiangkhoang;19.4167 Marabá;-5.3500 Kütahya;39.4167 Gent;51.0536 Yao;34.6269 Djelfa;34.6667 Barueri;-23.5111 Bordeaux;44.8400 Nagaoka;37.4462 Suncheon;34.9506 Jalālābād;34.4342 Engels;51.5017 Malolos;14.8433 Nogales;31.1833 Dezfūl;32.3825 Ed Daein;11.4608 Luque;-25.2700 Potosí;-19.5892 Concord;35.3933 São José dos Pinhais;-25.5350 Osmaniye;37.0750 Arrah;25.5514 Kyŏngju;35.8500 Dumai;1.6667 Tāluqān;36.7167 Damanhūr;31.0361 El Fasher;13.6306 Malkājgiri;17.4519 Nawabshah;26.2442 Tchibota;-4.1794 Annaba;36.9000 Gelsenkirchen;51.5167 Mönchengladbach;51.2000 Kakogawachō-honmachi;34.7500 Wollongong;-34.4331 Gilbert;33.3100 Nonthaburi;13.8667 Columbus;32.5100 Marilao;14.7581 Fukui;36.0641 Venice;45.4397 Boksburg;-26.2125 Quảng Ngãi;15.1167 Olongapo;14.8300 Ciudad Benito Juárez;25.6500 Hiratsuka;35.3167 Tiruvottiyūr;13.1600 Oumé;6.3833 Sōka;35.8254 Bunkyō-ku;35.7080 Aguadilla;18.4382 Al Ḩamzah;31.7339 Lubbock;33.5657 Mossoró;-5.1878 Kotri;25.3660 North Las Vegas;36.2883 Tuzla;40.8161 Singa;13.1500 Mişrātah;32.3775 Isparta;37.7647 Derby;52.9217 Verona;45.4386 Huayin;34.5664 Siverek;37.7500 St. Petersburg;27.7931 Ndulo;-11.4833 Rāniyah;36.2550 Germiston;-26.2178 Tallahassee;30.4551 Mount Lavinia;6.8731 Etāwah;26.7700 Horlivka;48.3336 Antsirabe;-19.8667 Ondo;7.0833 Indaiatuba;-23.0903 Puducherry;11.9167 Laredo;27.5625 Ấp Đa Lợi;11.9333 Foz do Iguaçu;-25.5400 Khimki;55.8892 Sab‘ al Būr;33.4644 Gravataí;-29.9333 Chiayi;23.4800 Kediri;-7.8111 Damaturu;11.7444 Tokushima;34.0703 Arua;3.0353 Peoria;40.7520 Turmero;10.2283 Pointe-à-Pitre;16.2411 Kuala Terengganu;5.3292 Campeche;19.8500 Byatarayanpur;13.0659 Mawlamyine;16.4847 Godomè;6.3667 San Pedro Carchá;15.4768 Moçâmedes;-15.1953 La Paz;24.1422 Central District;22.2867 São Carlos;-22.0000 Vitoria-Gasteiz;42.8500 Wolverhampton;52.5833 Avondale;33.3873 Lafayette;30.2082 Ash Shaţrah;31.4097 Irving;32.8583 Rishon LeẔiyyon;31.95 Montgomery;32.3482 Çekme;41.0369 Jhenida;23.5417 Bharatpur;27.2200 Gadda Madiral;18.7519 Shinozaki;33.9500 Goma;-1.6794 Tacloban;11.2444 Turku;60.4500 Rạch Giá;10.0167 Kanggye;40.9667 Taganrog;47.2167 Kızıltepe;37.1939 El Geneina;13.4500 Las Condes;-33.4117 Hakodate;41.7686 Uşak;38.6778 Jūnāgarh;19.8599 Begusarai;25.4200 São José;-27.6000 A Coruña;43.3667 Los Teques;10.3411 Jinshi;29.6334 Chōfugaoka;35.6506 Tsing Yi Town;22.3456 Higüey;18.6167 Juazeiro do Norte;-7.2000 Bata;1.8650 Al Minyā;28.1194 Aachen;50.7756 Bābol;36.5514 Komsomol’sk-na-Amure;50.5667 Iwo;7.6333 Rivne;50.6192 Galaţi;45.4233 Natogyi;21.4167 Braunschweig;52.2667 Kalār;34.6292 Al Bayḑā’;32.7664 Yanbu;24.0883 Kurmuk;10.5500 Shibuya-ku;35.6637 Gāndhīdhām;23.0800 Manzhouli;49.5881 Visalia;36.3276 Gdynia;54.5000 Rāmnagar;27.1700 Palangkaraya;-2.2100 Chemnitz;50.8333 Chigasaki;35.3333 Büyükçekmece;41.0200 Khomeynī Shahr;32.7003 Carmen;18.4167 McKinney;33.2016 Sahiwal;30.6611 Qā’em Shahr;36.4631 Banikoara;11.3000 Kiel;54.3233 Sibu;2.3000 Yato;35.4833 Paraná;-31.7331 Santa Clara;22.4069 Yamagata;38.2554 Katihār;25.5300 Beykoz;41.1342 Imperatriz;-5.5333 Oruro;-17.9799 Merlo;-34.6653 Tsukuba-kenkyūgakuen-toshi;36.0835 Wilmington;34.2099 Kesbewa;6.7953 Barnsley;53.5547 Chesapeake;36.6778 Syktyvkar;61.6667 Győr;47.6842 Birāṭnagar;26.4542 Hafizabad;32.0714 Saskatoon;52.1333 Krishnarājpur;13.0120 Abertawe;51.6167 Americana;-22.7386 Mahajanga;-15.7167 Long Khánh;10.9170 Nelamangala;13.1020 Jessore;23.1704 Beichengqu;40.4348 Glendale;33.5791 Tuy Hòa;13.0819 Garland;32.9100 Singrauliya;24.1100 Fuji;35.1613 Mokpo;34.7589 Kabinda;-6.1300 Tanga;-5.0667 Santa Maria;-29.6833 Sabzevār;36.2125 Bānchpār;23.2000 Rāmgundam;18.7639 Tarapoto;-6.4833 Bahir Dar;11.6000 Baruta;10.4322 Sapele;5.8261 Sasebo;33.1800 Myitkyina;25.3833 Pandharpur;17.6778 Sakākā;29.9697 Haeju;38.0333 Petaẖ Tiqwa;32.0889 González Catán;-34.7667 Masaurhi Buzurg;25.3500 Qyzylorda;44.8500 Diaobingshancun;42.4391 Man;7.4000 Zhangping;25.2938 Dagana;16.4833 Bạc Liêu;9.2833 Kennewick;46.1978 Lille;50.6278 Düzce;40.8417 Ipatinga;-19.5000 Anju;39.6200 Marília;-22.2139 Winter Haven;28.0118 Monclova;26.9103 Stavanger;58.9700 Abī al Khaşīb;30.4411 Atushi;39.7162 Chimoio;-19.1167 Limassol;34.6747 Ivano-Frankivsk;48.9228 Novo Hamburgo;-29.6778 Halle;51.4828 Arakawa;35.7361 Longueuil;45.5333 Szeged;46.2550 Viamão;-30.0808 Košice;48.7167 Matsumoto;36.2380 Tuticorin;8.7642 Banda Aceh;5.5500 Shenmu;38.8270 Springs;-26.2547 Taiping;4.8500 Zinder;13.8053 Gangānagar;29.9200 As Sīb;23.6703 P’yŏng-dong;39.2500 Scottsdale;33.6872 Āmol;36.4697 Norfolk;36.8945 Nyíregyháza;47.9531 Eindhoven;51.4333 Killeen;31.0753 Wuyishan;27.7562 Mirpur Khas;25.5269 Sandnes;58.8517 Beji;-6.3704 Pathein;16.7778 Nizhnekamsk;55.6333 Magdeburg;52.1317 Neya;34.7667 Rewa;24.5300 Chishui;28.5902 Centurion;-25.8603 Uluberiya;22.4700 Djougou;9.7000 Pākdasht;35.4817 Shakhty;47.7000 North Hempstead;40.7912 York;39.9651 Atlantic City;39.3797 Ibb;13.9759 Elche;38.2669 Abhā;18.2169 Bulandshahr;28.4069 Groningen;53.2167 Nashua;42.7491 Jacareí;-23.3053 Arlington;38.8786 Brownsville;25.9975 Bole;44.8539 Ganda;-13.0858 Cannanore;11.8689 Najafābād;32.6347 Bayamo;20.3817 Kamianske;48.5167 Borūjerd;33.8972 Singaraja;-8.1167 Macaé;-22.3708 Miri;4.3925 Machala;-3.2667 Kasulu;-4.5800 Longquan;28.0733 Granada;37.1781 Bo;7.9564 Messina;38.1936 Oral;51.2225 Badalona;41.4333 Formosa;-26.1833 San Cristóbal;18.4167 Arapiraca;-9.7519 Mambéré;5.5000 Damboa;11.1500 Okara;30.8092 Saidpur;25.8004 Freiburg im Breisgau;47.9950 Khénifra;32.9394 Saga;33.2667 Rāichūr;16.2000 Burnaby;49.2667 Meycauayan;14.7333 Sơn Tây;21.1333 Nazipur;25.0415 Rancagua;-34.1667 Kasukabe;35.9753 Dzerzhinsk;56.2333 Phú Yên;21.4156 Bratsk;56.1167 Barishal;22.7000 Jiayuguan;39.8112 Santa Ana;14.5800 Eloy Alfaro;-2.1733 Envigado;6.1667 Banfield;-34.7500 Guadalupe;22.7528 Ageoshimo;35.9774 Fremont;37.5265 Qarchak;35.4394 Gulfport;30.4274 Purwokerto;-7.4278 Puerto Vallarta;20.6458 Toluca;19.2925 Ormoc;11.0106 Neuquén;-38.9525 Fresnillo;23.1750 Singkawang;0.9000 Orsk;51.2000 Vizianagaram;18.1159 Manavgat;36.7833 Cotia;-23.6042 Pāli;25.7700 Songadh;21.1670 Noginsk;64.4833 Kropyvnytskyi;48.5000 Haridwār;29.9450 Ordu;40.9833 Taitō;35.7126 Ternopil;49.5667 Bojnūrd;37.4750 Rondonópolis;-16.4667 Cobán;15.4833 Guantánamo;20.1367 Krefeld;51.3333 Pāthardi;19.1700 Şabyā;17.1489 Kolpino;59.7333 Petlād;22.4700 Khanewal;30.3000 Magé;-22.6528 Ninh Hòa;12.4917 Khatīma;28.9200 Apapa;6.4500 Choloma;15.6136 Capiatá;-25.3500 Pallāvaram;12.9675 Nadiād;22.6900 Tarrasa;41.5611 Daşoguz;41.8333 Probolinggo;-7.7500 Cikupa;-6.4947 Evansville;37.9881 Araraquara;-21.7939 Quetzaltenango;14.8333 Angarsk;52.5500 Sousse;35.8333 Nyanza;-2.3500 Cox’s Bāzār;21.4272 Takarazuka;34.8114 Atsugichō;35.4333 Itapevi;-23.5489 Marcory;5.3000 Toamasina;-18.1500 Padangsidempuan;1.3786 Varāmīn;35.3242 Mutare;-18.9667 Mubi;10.2604 Chuādānga;23.6440 Korolëv;55.9167 Hunchun;42.8679 Blagoveshchensk;50.2578 Velikiy Novgorod;58.5210 Longjin;22.8711 Rennes;48.1147 Ashdod;31.8 Częstochowa;50.8000 Carlos Manuel de Céspedes;21.5767 Chapecó;-27.0958 Hialeah;25.8696 Ōta;36.2911 Sandton;-26.1070 Navarre;30.4174 Ijebu-Ode;6.8208 Tilburg;51.5500 Cork;51.8972 Tanjore;10.7870 Bandar-e Būshehr;28.9264 Hachinohe;40.5123 Staryy Oskol;51.2981 Kichha;28.9200 Secunderābād;17.4399 Tiptūr;13.2600 Ji’an Shi;41.1231 Mallawī;27.7333 José C. Paz;-34.5167 Bel Air South;39.5022 Rufisque;14.7167 Presidente Prudente;-22.1256 Barra do Dande;-8.4728 Hobart;-42.8806 Swindon;51.5600 Lutsk;50.7500 Katri;23.4800 Sambhal;28.5800 Guéckédou;8.5667 Sabadell;41.5483 Neyshābūr;36.2200 North Port;27.0576 Radom;51.4036 Santa Luzia;-19.7700 Polokwane;-23.9000 Chí Linh;21.1330 Tchaourou;8.8833 Duekoué;6.7333 San Felipe;10.3353 Sāveh;35.0214 San Bernardino;34.1416 Portland;43.6773 Santo Tomas;14.0833 Osisioma;5.1497 La Vega;19.2200 Nishitōkyō;35.7256 Damietta;31.4167 Pangkalpinang;-2.1333 Juazeiro;-9.4306 Kremenchuk;49.0631 Itabuna;-14.7858 Ānand;22.5560 Ciputat;-6.3111 Al Khubar;26.2833 Peñalolén;-33.4833 Islington;51.5440 Mainz;49.9833 Konibodom;40.2833 Zākhū;37.1500 Enterprise;36.0091 Bremerton;47.5436 Córdoba;18.8942 Green Bay;44.5148 Tuluá;4.0833 Pleiku;13.9833 Hortolândia;-22.8583 Bitung;1.4472 Naihāti;22.8900 Petrel;38.4789 Kafue;-15.7667 Jiutepec;18.8833 Manta;-0.9500 Osan;37.1498 San Miguel;13.4814 Butembo;0.1500 Netanya;32.3286 Oviedo;43.3600 Itaboraí;-22.7439 Appleton;44.2780 Zamora;19.9833 Alor Setar;6.1183 Luton;51.8783 Babylon;40.6925 Laâyoune;27.1536 Shashemenē;7.2000 Tân An;10.5333 Spring Valley;36.0952 Denov;38.2667 Cartagena;37.6000 Tacoma;47.2431 Yamunānagar;30.1330 Lübeck;53.8697 Zielona Góra;51.9333 Maţraḩ;23.6167 Qostanay;53.2000 Banjarbaru;-3.4425 Sa Đéc;10.3000 Wayaobu;37.1398 Valencia;7.9042 Norwich;52.6286 Gujiao;37.9069 Ayacucho;-13.1631 Petropavl;54.8833 Madiun;-7.6300 Dongxing;21.5833 Zhubei;24.8333 São Leopoldo;-29.7600 Hebron;31.5333 Spartanburg;34.9442 Marg‘ilon;40.4711 Ar Ruseris;11.8500 Trece Martires City;14.2833 Gangneung;37.7500 Roanoke;37.2785 Cúa;10.1667 Almere;52.3667 As Samāwah;31.3167 Monghyr;25.3810 Vantaa;60.2944 Kāpra;17.4859 Golmud;36.4072 Comalcalco;18.2801 Geneva;46.2017 Regina;50.4547 La Victoria;10.2278 Shimla;31.1033 Northampton;52.2304 Babruysk;53.1500 Mohammedia;33.6833 Erfurt;50.9833 Sete Lagoas;-19.4658 Sikasso;11.3167 Warnes;-17.5167 Móstoles;40.3333 Quilicura;-33.3667 Chilas;35.4194 Cao Lãnh;10.4672 El Tigre;8.8858 Gokāk;16.1667 Divinópolis;-20.1389 Gainesville;29.6804 Colombo;-25.2919 Jerez de la Frontera;36.6817 Puri;19.8106 Sidi Bel Abbès;35.2000 Nandyāl;15.4800 Limbe;4.0167 Kure;34.2500 Tumaco;1.8067 Padova;45.4064 Irákleio;35.3403 Deltona;28.9050 Arroyo Naranjo;23.0436 Bhadrāvati;13.8485 Colón;9.3572 Linz;48.3058 Oberhausen;51.4967 Criciúma;-28.6775 Kuje;8.8822 Mytishchi;55.9167 Lyubertsy;55.6783 Pamplona;42.8167 Panchkula;30.7400 Kalamazoo;42.2749 Pskov;57.8167 Bila Tserkva;49.7956 ‘Aqrah;36.7414 Ploieşti;44.9411 Isesaki;36.3114 Naga City;13.6244 Burgas;42.5030 Burhānpur;21.3000 Chungju;36.9667 Maracanaú;-3.8667 Thousand Oaks;34.1914 Hickory;35.7410 Pagadian;7.8272 Legazpi City;13.1333 Barrancabermeja;7.0667 Bukit Mertajam;5.3631 Kirātot;32.8376 Ashaiman;5.7000 Taisheng;23.2938 Sidon;33.5606 Rostock;54.0833 Guarenas;10.4739 Santa Cruz;28.4667 Panabo;7.3000 Dongning;44.0608 Borj Hammoud;33.8936 Kasangati;0.4378 Solihull;52.4130 Burutu;5.3500 Nasīm Shahr;35.5644 Moratuwa;6.7991 Yonkers;40.9466 Ich’ŏn;37.2667 Milton Keynes;52.0400 Marawi City;8.0000 Puerto Cabello;10.4667 Beersheba;31.2589 Huangyan;28.6500 Oulu;65.0142 Santo Agostinho;-8.2897 Lubuklinggau;-3.2967 Mary;37.6000 Sunyani;7.3333 Cascais;38.7000 Northcote;-36.8019 Moreno Valley;33.9244 Qinā;26.1667 Toledo;10.3833 Kharagpur;22.3302 Waitakere;-36.8500 Fontana;34.0968 Monywa;22.1083 Dindigul;10.3500 Robertsonpet;12.9563 Morogoro;-6.8242 Pingzhen;24.9439 Banī Suwayf;29.0667 College Station;30.5852 Bà Rịa;10.4992 Cidade de Nacala;-14.5428 Talca;-35.4269 Fargo;46.8651 Portoviejo;-1.0561 Parakou;9.3500 Hospet;15.2689 Trieste;45.6503 San Carlos City;15.9281 Kodaira;35.7285 Itami;34.7833 Lingampalli;17.4865 Kalemie;-5.9128 Kamirenjaku;35.6836 Loures;38.8333 Biskra;34.8500 Ingrāj Bāzār;25.0044 Phủ Từ Sơn;21.1189 La Ceiba;15.7792 Bené Beraq;32.0833 Nagareyama;35.8563 Ongole;15.5060 Zanzibar;-6.1650 Jiutai;44.1447 Bolu;40.7347 Ziguinchor;12.5833 Ternate;0.7800 Puqi;29.7204 Qal‘at Bīshah;20.0000 Ellore;16.7117 Betigeri;15.4430 Passo Fundo;-28.2500 Banjar;-7.3667 Kinh Môn;21.0330 Bingerville;5.3500 Bắc Giang;21.2667 Sejong;36.4870 Yachiyo;35.7224 Amarillo;35.1984 Mandi Burewala;30.1500 Vĩnh Long;10.2500 Mokameh;25.3898 Coquimbo;-29.9531 Tekirdağ;40.9778 Huntington;40.8521 Parnamirim;-5.9167 Holon;32.0167 Kukawa;12.9167 Biysk;52.5333 Charleroi;50.4000 Aberdeen;57.1500 Deoghar;24.4800 La Guaira;10.6000 Bīrjand;32.8667 Chhindwāra;22.0570 Timayy al Imdīd;30.9438 Mazabuka;-15.8467 Poza Rica de Hidalgo;20.5333 Toruń;53.0222 Tanjungpinang;0.9188 Kassel;51.3158 Haldia;22.0667 Luxor;25.6833 Lạng Sơn;21.8478 At Tājī;33.5092 Tarakan;3.3000 Las Tunas;20.9597 Al Khawr;25.6900 Matsue;35.4708 Arica;-18.4667 Hà Tĩnh;18.3333 Cosenza;39.3000 Al Khums;32.6497 Huacho;-11.1000 El Jadid;33.2333 Cajamarca;-7.1667 Norwich;41.5495 Ciudad Madero;22.2500 Mai’Adua;13.1906 Jacobabad;28.2769 Almería;36.8403 Tokat;40.3139 Sātkhira;22.7188 Néma;16.6167 Khandwa;21.8200 Pulimaddi;15.4800 Morena;26.5000 Soio;-6.1333 Guacara;10.2333 Olympia;47.0417 Kabankalan;9.9833 Isidro Casanova;-34.7000 Rio Claro;-22.4108 Santa Tecla;13.6731 Isanlu;8.1667 Az Zāwīyah;32.7522 Kasama;-10.2117 Maharagama;6.8494 Kouribga;32.8833 Amroha;28.9044 Lahad Datu;5.0300 Chilpancingo;17.5500 Drabar;33.4300 Waterbury;41.5582 Liège;50.6397 Banja Luka;44.7725 Salinas;36.6883 Huntington;38.4109 Alcalá de Henares;40.4818 Taranto;40.4711 Khowy;38.5519 Uripa;11.9167 Kankan;10.3833 Huntington Beach;33.6960 Clarksville;36.5692 Martapura;-3.4500 Ciudad del Carmen;18.6333 Bhind;26.5587 Chakradharpur;22.7000 Tocuyito;10.0889 Alvorada;-29.9897 Richmond;49.1667 Araçatuba;-21.2089 Santa Barbara;34.4285 Gemena;3.2500 Oradea;47.0722 Desē;11.1333 Helong;42.5404 Croydon;51.3727 Béni Mellal;32.3394 Madhyamgram;22.7000 Mwene-Ditu;-7.0000 Khammam;17.2473 Bhiwāni;28.7800 Higashi-Hiroshima;34.4167 N’Zérékoré;7.7500 Huánuco;-9.9295 Rzeszów;50.0333 Eldoret;0.5167 Mbanza-Ngungu;-5.2500 Shrīrāmpur;22.7500 Hino;35.6713 Zipaquirá;5.0333 Rio Grande;-32.0350 Prokopyevsk;53.8833 Rajin;42.3444 Fuenlabrada;40.2833 Ghāndīnagar;23.2230 Brescia;45.5389 Kusŏng;39.9667 Baharampur;24.1000 Sūhāj;26.5500 Acarigua;9.5597 Hugli;22.9089 San Pedro de Macorís;18.4500 Glendale;34.1819 Mbarara;-0.6133 Parma;44.8015 Sunrise Manor;36.1783 Kenema;7.8758 Brikama;13.2667 Suzuka;34.8820 Kajo Kaji;3.8492 Koronadal;6.5000 Cuautla;18.8167 Grand Prairie;32.6871 Viranşehir;37.2306 Iskandar;41.5581 Karaköprü;37.1847 Sosnowiec;50.2833 Tanauan;14.0833 Myawadi;16.6878 La Serena;-29.9000 Sodo;6.8550 Trondheim;63.4297 Morbi;22.8200 Ārba Minch’;6.0333 Prato;43.8808 Overland Park;38.8870 Tchitato;-7.3450 Malema;-14.9486 Anseong;37.0078 Yuzhno-Sakhalinsk;46.9500 Richmond Hill;43.8667 Narsingdi;23.9167 Gorontalo;0.5333 Guanajuato;21.0178 Hagerstown;39.6401 Peterborough;52.5662 Nampa;43.5844 Quelimane;-17.8764 Tébessa;35.4000 Kumagaya;36.1474 San Fernando;7.8940 Rijeka;45.3272 Gastonia;35.2494 Mahbūbnagar;16.7488 Karaman;37.1819 La Plata;-34.9211 Fatehpur;25.9300 Pāndhurnā;21.6000 Yamaguchi;34.1833 Lae;-6.7303 Muar;2.0500 Cap-Haïtien;19.7600 Oakville;43.4500 Godoy Cruz;-32.9167 Būkān;36.5172 Muş;38.7333 Hyesan;41.4000 Kielce;50.8742 Frisco;33.1560 Kırıkkale;39.8417 Jalapa;14.6379 Rājendranagar;17.3198 Castanhal;-1.2969 Guanare;9.0436 Cedar Rapids;41.9662 Basildon;51.5800 Fernando de la Mora;-25.3200 Leganés;40.3281 Pābna;24.0167 Rāe Bareli;26.2236 Florencia;1.6139 General Mariano Alvarez;14.3000 Fianarantsoa;-21.4536 San Cristóbal;16.7370 Bago;10.5388 Valera;9.3206 Bournemouth;50.7200 Pasuruan;-7.6406 Armavir;45.0000 Balakovo;52.0333 Jhelum;32.9425 Donostia;43.3200 Hemet;33.7341 Aydın;37.8481 Batu;-7.8720 Orai;25.9800 Manzanillo;19.0522 Anjōmachi;34.9587 Lam Tin;22.3094 Malaybalay;8.1564 Ferraz de Vasconcelos;-23.5411 Santa Bárbara d’Oeste;-22.7544 Chibia;-15.1833 Odawara;35.2500 Kishiwada;34.4667 Saddiqabad;28.3006 Shahr-e Kord;32.3311 Rybinsk;58.0500 Abū Ghurayb;33.2919 Chŏngju;39.6500 Cachoeiro de Itapemirim;-20.8489 Hagen;51.3667 Paradise;36.0872 Ngaoundéré;7.3167 Pak Kret;13.9125 Donghua;35.2175 Braga;41.5503 Sioux Falls;43.5396 Las Heras;-32.8500 Araure;9.5667 Digos;6.7500 Pinar del Río;22.4122 El Progreso;15.4000 Numazu;35.0956 Rāiganj;25.6200 Tachikawa;35.6942 Prabumulih;-3.4328 Angra dos Reis;-23.0067 Bhusāval;21.0500 San Francisco de Macorís;19.3000 Vancouver;45.6366 Sobral;-3.6739 Al Ḩasakah;36.5117 Klerksdorp;-26.8667 Kecskemét;46.9061 Guatire;10.4717 Iquique;-20.2167 El Bosque;-33.5667 Kōfu;35.6621 San Diego;10.2558 Gharyān;32.1697 Thingangyun;16.8281 Peoria;33.7843 Bārākpur;22.7642 Padalarang;-6.8435 Tottori;35.5000 Chaedŏk;40.7988 Jōetsu;37.1479 Bahraigh;27.5750 Ucu Seles;-11.4047 Calbayog City;12.0667 Waco;31.5599 Kairouan;35.6772 Tambaram;12.8310 Cabo Frio;-22.8789 Shendi;16.6833 Lorain;41.4409 Erie;42.1167 Shibīn al Kawm;30.5586 Ed Damazin;11.7667 Umreth;22.6986 Reims;49.2628 Dunhuang;40.1411 Mahesāna;23.6000 Nossa Senhora do Socorro;-10.8550 Breda;51.5889 Getafe;40.3047 Modena;44.6458 Guri;37.6000 Jamaame;0.0667 Basel;47.5547 Tân Châu;10.7739 Dinapore;25.6225 Semnān;35.5728 Temirtaū;50.0500 Toyokawa;34.8268 Newport News;37.1051 Yei;4.0904 Alasandigutta;15.6300 Luzhang;25.8519 Al Qāmishlī;37.0500 Fardīs;35.7167 Chūō-ku;35.6706 Geelong;-38.1500 Vĩnh Châu;9.3330 Siguiri;11.4189 Severodvinsk;64.5667 Batāla;31.8186 Potsdam;52.4000 Kottayam;9.5947 Itajaí;-26.9000 Trảng Bàng;11.0330 Cuango;-9.1444 Aqtaū;43.6525 Ongata Rongai;-1.4000 Niš;43.3192 Cuauhtémoc;28.4050 Aurora;41.7638 Narashino;35.6804 Abakan;53.7167 Burlington;43.3167 Cuautitlán;19.6833 Odense;55.3958 La Pintana;-33.5833 Pinrang;-3.7857 Isiro;2.7833 Toulon;43.1258 Sorsogon;12.9742 Lagos de Moreno;21.3567 Sirsa;29.5333 Dourados;-22.2208 Amadora;38.7333 Kāraikkudi;10.0735 Brăila;45.2692 Uji;34.8844 Nova Friburgo;-22.2819 Fort Lauderdale;26.1412 Reggio di Calabria;38.1144 Berazategui;-34.7167 Phan Rang-Tháp Chàm;11.5667 Vallejo;38.1125 Dosquebradas;4.8333 Thanhlyin;16.7333 York;53.9601 Petropavlovsk-Kamchatskiy;53.0167 Himatnagar;23.6000 Alleppey;9.4900 Epe;6.5573 Sittwe;20.1670 Dam Dam;22.6200 Jaunpur;25.7300 Murfreesboro;35.8490 Jīma;7.6667 Sucúa;-2.4600 Kindia;10.0497 Curug;-6.3711 Guna;24.6500 Lhokseumawe;5.1881 Roxas City;11.5894 Funtua;11.5204 Boma;-5.8500 Turbo;8.1000 Hinthada;17.6461 Çanakkale;40.1519 La Rioja;-29.4125 Madanapalle;13.5500 Palopo;-3.0000 Njeru;0.4311 Obuase;6.2000 Nijmegen;51.8475 Saarbrücken;49.2333 San Luis Río Colorado;32.4767 Edirne;41.6769 Tiaret;35.3667 Mahād;18.0830 Purwakarta;-6.5533 Longjing;42.7700 Split;43.5100 Ilhéus;-14.7889 Hosa’ina;7.5500 Barra Mansa;-22.5439 Shivpuri;25.4300 Bhālswa Jahangirpur;28.7354 Hamm;51.6833 Tongjiang;47.6502 Almada;38.6803 Norilsk;69.3333 Spring Hill;28.4798 Mongu;-15.2775 Al ‘Arīsh;31.1249 Divo;5.8333 Urayasu;35.6539 Bida;9.0804 Bade;24.9575 Paita;-5.0911 Torbalı;38.1619 Townsville;-19.2500 Langsa;4.4800 Dagupan City;16.0430 Teziutlan;19.8178 Tempe;33.3881 High Point;35.9910 Sultānpur Mazra;28.6981 Unnāo;26.5500 Şalālah;17.0197 Medford;42.3372 Salatiga;-7.3247 Matosinhos;41.1833 Hamilton;-37.7833 Lin’an;23.6236 Chicoloapan;19.4167 Kandi;11.1286 Danbury;41.4015 Meiktila;20.8833 Dundo;-7.3801 Bejaïa;36.7511 Titāgarh;22.7400 Loum;4.7180 Baranavichy;53.1333 Santa María Texmelucan;19.2833 Kamakurayama;35.3197 Yangmei;24.9167 Ilford;51.5575 Les Cayes;18.2000 Ciudad Valles;21.9833 Santander;43.4628 Kadugli;11.0167 Linhares;-19.3894 Letpandan;17.7866 Krasnogorsk;55.8217 Wārāseonī;21.7647 Mauli;30.6990 Saint-Louis;16.0333 Rio Verde;-17.7450 Plzeň;49.7475 Biu;10.6111 Ait Melloul;30.3342 Comitán;16.2511 Saint-Étienne;45.4347 Orekhovo-Borisovo Yuzhnoye;55.6031 Oeiras;38.6833 Habaswein;1.0100 Catumbela;-12.4167 Santa Cruz;14.5998 Ereğli;41.2792 Sầm Sơn;19.7333 Harar;9.3111 Shibirghān;36.6650 Burgos;42.3408 Chandannagar;22.8671 Cuddalore;11.7500 Olsztyn;53.7778 Elk Grove;38.4161 Kotdwāra;29.7500 Marāgheh;37.3906 Thành Phố Uông Bí;21.0356 Gliwice;50.2833 Bodrum;37.0378 Tebingtinggi;3.3283 Cianjur;-6.8200 Alcorcón;40.3500 Ontario;34.0393 Chetumal;18.5036 Khānaqīn;34.3333 Frederick;39.4337 Sīrjān;29.4370 Tyre;33.2708 Mexico;15.0667 Menemen;38.6000 Ludwigshafen;49.4811 Vero Beach South;27.6132 Syzran;53.1667 Carúpano;10.6722 Ocala;29.1780 Lubao;14.9333 Luziânia;-16.2528 Oceanside;33.2247 Chīrāla;15.8246 Ratnapura;6.6806 León;12.4344 San Andrés Tuxtla;18.4487 Tiantoujiao;23.0236 Castellón de la Plana;39.9831 Piedras Negras;28.7000 Banhā;30.4667 Linjiang;41.8471 Rancho Cucamonga;34.1247 Hạ Long;20.9500 Deo;24.6561 Silchar;24.8200 Tlemcen;34.8828 Hitachi;36.5991 Kindu;-2.9500 Shāhīn Shahr;32.8667 Le Havre;49.4900 Gadag;15.4167 Akhisar;38.9167 Polomolok;6.2167 Mang La;14.3833 Sirājganj;24.4500 Tiruvannāmalai;12.1300 Rantau Prapat;2.3333 Banyuwangi;-8.2186 Albacete;38.9956 Bīdar;17.9120 San Miguel;15.1458 Mülheim;51.4275 Sacaba;-17.4042 Garden Grove;33.7787 Reggio Emilia;44.7000 Barrie;44.3711 Kaolack;14.1389 Moca;19.3833 São Caetano do Sul;-23.7000 Cape Coast;5.1000 Izumo;35.3667 Baliuag;14.9540 Oldenburg;53.1439 Sawangan;-6.4086 Petite Rivière de l’Artibonite;19.1333 Niiza;35.7935 Cazanga;-9.3000 Surigao;9.7897 Batumi;41.6458 Francisco Morato;-23.2817 Cary;35.7819 Volgodonsk;47.5167 Bảo Lộc;11.5481 Ramat Gan;32.07 Petarukan;-6.8961 Kohat;33.5833 Makurdi;7.7306 Kamensk-Ural’skiy;56.4000 Loja;-3.9833 Valsād;20.6100 Escuintla;14.2978 Ussuriysk;43.8000 Sakura;35.7167 Concepcion;15.3249 Machilīpatnam;16.1700 Uppsala;59.8581 Tomakomai;42.6341 Portmore;17.9500 Bordj Bou Arreridj;36.0667 Aplahoué;6.9333 Pembroke Pines;26.0128 Malāyer;34.2969 Kluang;2.0336 As Suwayq;23.8494 Novocherkassk;47.4222 Métouia;33.9667 Magwe;4.1333 Nishio;34.8333 Bayamón;18.3794 San Luis;-33.3000 Puerto Montt;-41.4667 Medinīpur;22.4240 Fenglu;24.6728 Saḩāb;31.8667 Al Marj;32.5005 Caluquembe;-13.9208 Sŏsan;36.7817 Damoh;23.8331 Toliara;-23.3500 Warrington;53.3900 Gondomar;41.1500 Minbya;20.3667 Jamālpur;24.9004 Santa Rita;10.2003 Neyveli;11.6088 Bāramūla;34.2000 Kalalé;10.2953 Oyama;36.3146 Wāpi;20.3720 Ambato;-1.2417 Calabozo;8.9219 Hŭich’ŏn;40.1707 Mahābād;36.7678 Itapecerica da Serra;-23.7172 Villa Canales;14.4816 Basuo;19.0920 Zlatoust;55.1667 Malanville;11.8667 Jīnd;29.3167 Palmdale;34.5944 Haarlem;52.3833 Pátra;38.2500 Ríohacha;11.5442 Catape;-13.7667 Talas;38.6833 Katha;24.1822 Manchester;42.9848 Tuguegarao;17.6133 Guarapuava;-25.3833 Itu;-23.2642 Jutiapa;14.2917 Çerkezköy;41.2833 Mogok;22.9167 Mukono;0.3533 Al Kūfah;32.0300 Ādoni;15.6244 Grenoble;45.1715 Tabora;-5.0167 Sampit;-2.5333 Lafayette;40.3991 Tenāli;16.2390 Iwata;34.7179 Takaoka;36.7541 Tam Kỳ;15.5667 Skikda;36.8667 Siirt;37.9250 Osnabrück;52.2833 Kuytun;44.4264 Ipiales;0.8303 Fyzābād;26.7730 Körfez;40.7833 Perugia;43.1122 Udipi;13.3389 Oshawa;43.9000 Tanjungbalai;2.9700 Obihiro;42.9167 Santa Cruz;36.9789 Leverkusen;51.0333 Klaipėda;55.7500 Hadano;35.3667 Kushiro;42.9833 Midsayap;7.1917 Idlib;35.9333 Arnhem;51.9833 Kingswood;51.4600 Béchar;31.6333 Conjeeveram;12.8308 Kandy;7.2964 Proddatūr;14.7300 Cienfuegos;22.1456 Nador;35.1667 Saqqez;36.2464 Zhanlicun;23.2881 Ebo;-11.0000 Kökshetaū;53.2833 Nāngloi Jāt;28.6833 Fukang;44.1646 Türkistan;43.3019 Pikit;7.0500 Huddersfield;53.6450 Bocoio;-12.4667 Chillán;-36.6067 Marysville;48.0809 Abengourou;6.7333 Abū Ḩulayfah;29.1322 Muridke;31.8020 Tema;5.6667 Metro;-5.1167 Muskegon;43.2281 Ipswich;-27.6167 Myingyan;21.4600 Quibala;-10.7333 Teresópolis;-22.4119 Ciudad Acuña;29.3242 Tunja;5.5333 ‘Unayzah;26.0906 San Marcos;10.6204 Turgutlu;38.5000 Pocheon;37.8947 Ninh Bình;20.2539 São José de Ribamar;-2.5500 Wau;7.7000 Ube;33.9500 Gölcük;40.6667 Muzaffargarh;30.0694 Kebili;33.7050 Jizzax;40.1158 Kyaunggon;17.1000 Simao;22.7807 San Martin Texmelucan de Labastida;19.2833 Brahmanpara;23.6167 Naic;14.3167 Jinggang;23.2723 Bandar-e Māhshahr;30.5589 Godhra;22.7772 Anaco;9.4333 Sullana;-4.9000 Villeurbanne;45.7667 Zemun;44.8500 Sariaya;13.9667 Tuxtepec;18.1000 Springfield;39.7709 Musashino;35.7177 Bhisho;-32.8494 Benoni;-26.1883 Chitaldrug;14.2333 Andong;36.5592 Hayward;37.6328 Rājapālaiyam;9.4204 Guadalajara;40.6337 Multai;21.7700 Rafsanjān;30.4067 Dijon;47.3167 Al Ghardaqah;27.2578 Bontang;0.1333 Tulancingo;20.0833 Chittoor;13.2160 Salihli;38.4811 Ndalatando;-9.3000 Elektrostal;55.7833 Jequié;-13.8630 Apeldoorn;52.2167 Trà Vinh;9.9333 Đồng Hới;17.4831 Krishnanagar;23.4000 Sherbrooke;45.4000 Ash Shīḩānīyah;25.3722 Kidapawan;7.0083 San Miguel;-34.5333 Papantla de Olarte;20.4478 Dhamār;14.5500 Tân Phú;11.2720 José María Ezeiza;-34.8333 Southend;51.5500 Ibirité;-20.0219 Solingen;51.1667 Tacheng;46.7517 Zaranj;30.9600 General Roca;-39.0333 Bragança Paulista;-22.9531 Darmstadt;49.8722 Zhengding;38.1522 Pindamonhangaba;-22.9239 Enschede;52.2167 Girón;7.0731 Uttarpāra;22.6700 Heidelberg;49.4167 Khōst;33.3331 Ceyhan;37.0289 Koudougou;12.2500 Saint-Marc;19.1167 Newport;51.5886 Khanpur;28.6453 Ede;7.7389 Huaycan;-12.0181 Arad;46.1750 Moga;30.8220 Ferkessédougou;9.5833 Zabrze;50.3025 Bingöl;38.8861 San Nicolás de los Arroyos;-33.3333 Budaun;28.0500 Miyakonojō;31.7167 Pandi;14.8667 Kramatorsk;48.7392 Amersfoort;52.1500 Seixal;38.6500 Livorno;43.5519 Ekibastuz;51.6667 Hat Yai;7.0167 Herne;51.5500 Chirchiq;41.4667 La Laguna;28.4853 Catamarca;-28.4667 Angers;47.4736 Jijiga;9.3500 George;-33.9667 Ravenna;44.4161 Higashimurayama;35.7546 ’s-Hertogenbosch;51.6833 Bharūch;21.6948 Ōgaki;35.3594 Nan Zhuang;22.9839 Quipungo;-14.8167 Cadiz;10.9500 Molo;-0.2500 Châu Đốc;10.7000 Zaanstad;52.4697 Guimarães;41.4500 Tigaraksa;-6.2667 Matsuzaka;34.5779 Macuspana;17.7667 Aral;40.5480 Hoofddorp;52.3061 Sāmarrā’;34.1983 Ilagan;17.1489 New Bedford;41.6697 Rafael Castillo;-34.7167 Río Cuarto;-33.1333 Rafaḩ;31.2886 Bariadi;-2.7919 Alexandria;38.8185 Enfield;51.6522 Daiwanishi;34.8333 Gojra;31.1500 Teixeira de Freitas;-17.5403 Paterson;40.9147 Camarajibe;-8.0219 Corona;33.8616 Fethiye;36.6514 Mahlaing;21.0900 Ŭiwang;37.3448 Saharsa;25.8800 Cinere;-6.3333 Merced;37.3057 Danao;10.5333 Calama;-22.4667 Chaoshan;23.0701 Nāblus;32.2222 Erzincan;39.7464 Warner Robins;32.5961 Salzburg;47.8000 Mandi Bahauddin;32.5797 El Eulma;36.1528 Itapetininga;-23.5917 Xiaping;26.7646 Port Arthur;29.8554 Chlef;36.1647 Guaymas;28.1667 Pathānkot;32.2668 Nazilli;37.9125 Kamina;-8.7386 Lajes;-27.8158 Macon;32.8065 Piteşti;44.8606 Kétou;7.3581 Adzopé;6.1667 Poblacion;10.4167 Hanam;37.5167 Capas;15.3372 Ghorāhī;28.0333 Vidisha;23.5239 Cẩm Phả;21.0167 Esmeraldas;0.9667 Ar Ramthā;32.5589 Kariya;34.9893 Al Miqdādīyah;33.9786 Almetyevsk;54.9000 Danzao;23.0427 Cagliari;39.2278 Hitachi-Naka;36.3967 Vĩnh Yên;21.3100 Nalgonda;17.0500 Rudarpur;28.9708 Kansas City;39.1235 Kisi;9.0833 Thānesar;29.9667 El Minié;34.4470 Lakewood;39.6977 Borāzjān;29.2694 Gulu;2.7817 Sunnyvale;37.3836 Timon;-5.0939 Palo Negro;10.1600 Bytom;50.3483 Souk Ahras;36.2864 Middelburg;-25.7684 Horad Barysaw;54.2279 Regensburg;49.0167 Fredericksburg;38.2992 Jorhāt;26.7500 Oxford;51.7519 Neuss;51.2000 Hājīpur;25.6925 Caxias;-4.8589 Bartın;41.6344 San Juan;14.6000 Melitopol;46.8489 Zango;12.9333 Sasolburg;-26.8142 Kafr ash Shaykh;31.1117 Kitenkela;-1.5167 Bālurghāt;25.2200 Madīnat as Sādis min Uktūbar;29.9361 Dibrugarh;27.4800 Verāval;20.9000 Alagoinhas;-12.1358 Edremit;39.5922 Bandırma;40.3500 Salavat;53.3667 Gandajika;-6.7500 Lucapa;-8.4228 Leesburg;28.7672 Tama;35.6369 Ras Tanura;26.6500 Hòa Thành;11.2831 Harrow;51.5836 Seogwipo;33.2497 Tochigi;36.3813 Moanda;-5.9342 Colima;19.2433 Barreiras;-12.1483 Naogaon;24.8000 Tete;-16.1667 Villanueva;15.3167 Noda;35.9500 Kanasín;20.9344 Ixtlahuaca;19.5689 Hoeryŏng;42.4333 Iringa;-7.7700 San Jose;12.3528 Kırşehir;39.1456 Newcastle;-32.9167 Sievierodonetsk;48.9481 Sinpo;40.0347 Paderborn;51.7181 Nevşehir;38.6264 Dutse;11.8283 Minglanilla;10.2450 Champaign;40.1142 Kiambu;-1.1667 Ueda;36.4019 Dahuaishu;36.2601 Amatitlán;14.4741 Singida;-4.8167 Shāntipur;23.2500 Moriguchi;34.7375 Yilan;24.7500 Siem Reap;13.3622 Golmeh;33.7842 Fengyicun;23.6636 Hindupur;13.8300 Kawashiri;34.8301 Poços de Caldas;-21.7878 Peristéri;38.0167 Logroño;42.4650 Dīla;6.4083 San Andres;14.5739 Ciudad Choluteca;13.3011 Bohicon;7.2000 Baubau;-5.4667 Erode;11.3409 Ramu;3.9375 Hollywood;26.0293 Mudon;16.2578 Taza;34.2167 Nek’emtē;9.0833 Tauranga;-37.6833 Gwangyang;34.9333 Marbella;36.5167 Beāwar;26.1011 Abaetetuba;-1.7178 Kukichūō;36.0621 Consolacion;10.4000 Gonbad-e Kāvūs;37.2500 Al Manāqil;14.2500 Tororo;0.6928 Pasadena;29.6575 Miass;55.0000 Mzuzu;-11.4581 Delicias;28.1931 Nakhodka;42.8167 Quevedo;-1.0333 Asaka;35.7972 San Jose;15.7919 Foggia;41.4642 Pomona;34.0585 Sayama;35.8530 Aş Şuwayḩirah as Sāḩil;24.3620 Escondido;33.1348 Jaranwala;31.3342 Đức Phổ;14.8100 Miskolc;48.0833 Clermont-Ferrand;45.7831 Florencio Varela;-34.8167 Chichicastenango;14.9442 Manzanillo;20.3397 Vryheid;-27.7669 Talcahuano;-36.7167 Kerch;45.3619 M’Sila;35.7058 Patos de Minas;-18.5817 Kolār;13.1333 Mariveles;14.4333 Copiapó;-27.3664 Kragujevac;44.0142 Pôrto Seguro;-16.4333 Badajoz;38.8803 Soro;21.2900 Lalian;31.8253 Đồng Xoài;11.5169 Nîmes;43.8380 Sumbawanga;-7.9667 Araguaína;-7.1908 Chauk Azam;30.9648 Rimini;44.0594 Gubeng;-7.2729 Ocumare del Tuy;10.1136 Joliet;41.5188 Auchi;7.0667 Villa de Álvarez;19.2500 Urganch;41.5500 Komaki;35.2910 Valdivia;-39.8139 Valle de Santiago;20.3928 Toda;35.8176 Shāhrūd;36.4181 Kutaisi;42.2500 Kashikishi;-9.3000 Lake Charles;30.2010 Pénjamo;20.4311 Odivelas;38.7903 Mesquite;32.7602 Saumlaki;-7.9750 Gloucester;51.8644 Maīmanah;35.9333 Mauldin;34.7821 Potchefstroom;-26.7150 Imabari;34.0667 Abbottabad;34.1558 Concordia;-31.4000 West Bromwich;52.5190 Panama City;30.1995 Bellevue;47.5951 Centro Habana;23.1333 Sagay;10.9000 Ağrı;39.7186 Hưng Yên;20.6500 ‘Ajlūn;32.3325 Amasya;40.6500 Santiago;16.6833 Bandar-e Anzalī;37.4728 Iruma;35.8358 Kastamonu;41.3764 Naperville;41.7480 Dongsheng;22.8869 Sitārganj;28.9300 Marvdasht;29.8742 Chingola;-12.5333 Riobamba;-1.6731 Szombathely;47.2351 Jean-Rabel;19.8500 Hābra;22.8300 Mostaganem;35.9333 Harlingen;26.1916 Tuscaloosa;33.2348 Al ‘Aqabah;29.5319 Pemba;-12.9667 Nowgong;26.3492 Quilpué;-33.0500 Sibiu;45.7928 Yonago;35.4333 Dundee;56.4620 Disūq;31.1422 Kopeysk;55.1000 Lüleburgaz;41.4056 Al Ḩawīyah;21.4411 Salamanca;40.9650 Mbanza Kongo;-6.2667 Nchelenge;-9.3533 Zhangaözen;43.3378 Turbat;26.0031 Mati;6.9483 Mangghystaū;43.6905 Malakal;9.5500 Elkhart;41.6916 Osorno;-40.5725 Al Qurayyāt;31.3167 Vólos;39.3667 Bacău;46.5833 Mogi Guaçu;-22.3719 Pyatigorsk;44.0500 Moshi;-3.3349 Payatas;14.7099 Torrance;33.8346 Dar‘ā;32.6189 Ramapo;41.1404 Cairns;-16.9200 Bīr;18.9833 Poole;50.7167 Rubtsovsk;51.5167 Negombo;7.2111 Cam Ranh;11.9020 Maquela do Zombo;-6.0500 Wamba;2.1442 Binghamton;42.1014 Odintsovo;55.6667 Dadu;26.7319 Franco da Rocha;-23.3286 Houma;29.5800 Misato;35.8301 Le Mans;48.0077 Etah;27.6300 Keren;15.7778 Topeka;39.0346 Nārnaul;28.0444 Calapan;13.4140 Colina;-33.2017 Kolomna;55.0833 Lárisa;39.6417 Yima;34.7473 Bhāndāria;22.4883 Pauktaw;20.1500 Abohar;30.1334 Parnaíba;-2.9050 García;25.8167 Taungdwingyi;20.0017 Urdaneta;15.9761 Aalborg;57.0500 Barrechid;33.2667 Arayat;15.1493 Settat;33.0000 Khairpur Mir’s;27.5333 Aix-en-Provence;43.5263 Bocaue;14.8000 Baní;18.2900 Xintang;22.7824 Matanzas;23.0511 Roseville;38.7703 San Justo;-34.6833 Qūchān;37.1060 Yalova;40.6556 Berezniki;59.4167 Kaithal;29.8015 Shillong;25.5744 Thanatpin;17.2942 Ereğli;37.5167 Villa de Cura;10.0383 Kusatsu;35.0167 David;8.4333 Kakamigahara;35.3988 Khān Yūnis;31.3444 Fairfield;38.2583 Cuautitlán Izcalli;19.6500 Al Mukallā;14.5333 Santamesa;14.6000 Comayagua;14.4522 Okinawa;26.3342 Flores;16.9333 Barranca;-10.7541 Khasavyurt;43.2500 Saguenay;48.4167 Gisenyi;-1.7000 Kumba;4.6333 Mehtar Lām;34.6683 Chhatarpur;24.9177 Bizerte;37.2778 Ruse;43.8231 Malasiqui;15.9167 Debre Birhan;9.6833 Bandundu;-3.3167 Koiridih;24.1800 Tyler;32.3184 Qabr as Sitt;33.4472 Coimbra;40.2111 Calabayan;16.7667 Nawābganj;24.6000 Rewāri;28.1800 Birkenhead;53.3930 Altay;47.8666 Ashikaga;36.3402 Ghaznī;33.5492 Yopal;5.3500 Bilbays;30.4167 Jaú;-22.2958 Huelva;37.2500 Kumbakonam;10.9602 Lévis;46.8000 Bhīmavaram;16.5430 Jaraguá do Sul;-26.4833 Jinjiang;19.7386 Blackpool;53.8142 Cizre;37.3320 Paghmān;34.5833 Los Ángeles;-37.4667 Sannār;13.5500 Elbistan;38.2014 Boca Chica;18.4539 Telford;52.6766 Fullerton;33.8841 Kamālshahr;35.8653 Preston;53.7590 Lichinga;-13.3167 Brest;48.3900 Pueblo;38.2701 Teluknaga;-6.0989 Moundou;8.5667 Tetovo;42.0103 Nizip;37.0100 Lausanne;46.5198 Bahawalnagar;29.9928 Maykop;44.6000 Fukayachō;36.1975 Athens;33.9508 Santa Maria;34.9333 Dehri;24.9100 Gweru;-19.4614 Tabaco;13.3500 Kelowna;49.8881 Kisaran;2.9833 Namacunde;-17.3000 Mandsaur;24.0300 Boulogne-Billancourt;48.8352 Rybnik;50.0833 Surprise;33.6815 Nong’an;44.4190 Inezgane;30.3658 Rize;41.0247 Rio das Ostras;-22.5269 Vlorë;40.4667 Bondoukou;8.0333 Fujita;34.8674 Jahrom;28.5000 Charleston;38.3484 Jyväskylä;62.2417 Lleida;41.6167 Middlesbrough;54.5767 Khuzdar;27.8000 Tepatitlán de Morelos;20.8170 Las Cruces;32.3265 Abbotsford;49.0500 Batu Pahat;1.8500 Krugersdorp;-26.1000 Porac;15.0719 Ozamiz City;8.1500 Pécs;46.0708 Las Margaritas;16.3667 Tours;47.3936 Kovrov;56.3683 Shizhaobi;23.9210 Kokubunji;35.7109 Tottenham;51.5975 Mejicanos;13.7333 Ebina;35.4464 Paranaguá;-25.5208 Iguala de la Independencia;18.3450 Metairie;29.9977 Thornton;39.9197 Masaya;11.9764 Médéa;36.2675 Yaritagua;10.0753 Xiangcheng;25.4775 Fusagasugá;4.3452 Olathe;38.8833 Nāḩiyat Ghammās;31.7431 La Romana;18.4300 Giá Rai;9.2500 Tanay;14.4972 Medina Estates;5.6833 Ituzaingó;-34.6667 Torbat-e Ḩeydarīyeh;35.2739 Quảng Yên;20.9170 Momostenango;15.0444 Dawei;14.0833 Kombolcha;11.0867 Kuwana;35.0667 Subang;-6.5714 Atbara;17.6972 Norrköping;58.6000 Carolina;18.4054 Poblacion;14.3854 Pakpattan;30.3500 Chicacole;18.3000 Jicheon;36.1333 Botucatu;-22.8858 Gainesville;34.2902 Minō;34.8269 Shizuishan;39.2333 La Trinidad;16.4621 West Valley City;40.6886 Koga;36.1782 Sale;53.4240 Coquitlam;49.2839 Orange;33.8038 San José del Cabo;23.0614 Jacmel;18.2353 Ishizaki;38.4176 Al Khmissat;33.8167 Ingolstadt;48.7631 Çarşamba;41.1992 Renca;-33.4000 Warren;42.4934 Bat Yam;32.0167 Candelaria;13.9311 Tsuchiura;36.0667 Pīrānshahr;36.6956 Umtata;-31.5800 Pasadena;34.1597 Dipolog;8.5872 Sancti Spíritus;21.9339 Bukoba;-1.3333 Koganei;35.6995 Tultepec;19.6850 Dos Hermanas;37.2836 Jolo;6.0000 Montero;-17.3422 Boca del Rio;19.1056 Dabou;5.3167 Phủ Lý;20.5411 Zama;35.4833 Brusque;-27.0949 Shūnan;34.0500 Dumaguete City;9.3103 Mojokerto;-7.4722 Darwin;-12.4381 Silivri;41.0736 Mandya;12.5242 Ch’ungmu;34.8333 Tarragona;41.1187 Birgañj;27.0000 Iğdır;39.9208 Palhoça;-27.6444 Lira;2.2472 Carcar;10.1167 Negage;-7.7667 Gunungsitoli;1.2833 Beaumont;30.0849 Yunxian Chengguanzhen;32.8082 Cunduacán;18.0667 Atibaia;-23.1172 Rangkasbitung;-6.3667 Amiens;49.8920 Bānkura;23.2500 Kigoma;-4.8833 Vila Franca de Xira;38.9500 Quillacollo;-17.4000 Myebon;20.0500 Garanhuns;-8.8903 Minoo;34.8333 Bima;-8.4600 Kisarazu;35.3760 Burlington;36.0760 Livingstone;-17.8500 Curicó;-34.9833 Porto Amboim;-10.7183 Parla;40.2372 Nasugbu;14.0667 Siwān;26.2200 Long Bình;10.9458 Igboho;8.8333 Gingoog;8.8167 Maia;41.2333 Uppsala;59.8601 Yaizu;34.8669 Khargone;21.8229 Hampton;37.0551 Marīvān;35.5269 Columbia;38.9472 Huehuetenango;15.3147 Inazawa;35.2647 Stara Zagora;42.4333 Quimbele;-6.5167 Chech’ŏn;37.1333 Norzagaray;14.9167 Port-Gentil;-0.7167 Tiraspol;46.8403 Pageralam;-4.0217 Debre Mark’os;10.3333 Tizi Ouzou;36.7169 Tây Ninh;11.3678 Giresun;40.9153 Maijdi;22.8333 Ksar El Kebir;35.0090 Torrejón de Ardoz;40.4614 Termiz;37.2167 Zinacantepec;19.2833 Mabacun;24.6807 Floridablanca;14.9333 Salerno;40.6806 Rionegro;6.1535 Ağcabədi;40.0528 Blitar;-8.1000 Târgu-Mureş;46.5456 Yuma;32.5995 Brighton;50.8208 Toowoomba;-27.5667 Tobruk;32.0761 Srīpur;24.2011 Elizabeth;40.6658 Raigarh;21.8000 Sukrah;36.8833 Pyay;18.8167 Tabarre;18.5833 Ruda Śląska;50.2628 Shuangcheng;45.3503 Angono;14.5234 Cartago;4.7000 Yelahanka;13.1007 Paco;14.5830 May Pen;17.9650 Racine;42.7274 Zafarwal;32.3463 Greeley;40.4152 Andīmeshk;32.4600 Ipetumodu;7.5070 Conchalí;-33.3833 Agboville;5.9333 Djakotomé;6.9000 Santa Rita;-7.1139 Silopi;37.2486 Daraga;13.1619 Shahreẕā;32.0089 Milagro;-2.1347 Nakhon Ratchasima;14.9750 Zābol;31.0286 El Oued;33.3683 Kent;47.3887 Ashqelon;31.6667 Mataró;41.5333 Ba Đồn;17.7547 Stamford;41.1039 Odessa;31.8801 Teófilo Otoni;-17.8578 Hagonoy;14.8333 Laghouat;33.8000 Mörön;49.6356 Apatzingan de la Constitucion;19.0886 Aaley;33.8000 Famalicão;41.4167 Naga;10.2167 Trois-Rivières;46.3500 Bloomington;40.4757 Veszprém;47.0930 Zhangmu Touwei;22.9078 Miramar;25.9773 Londuimbali;-12.2186 Reẖovot;31.8981 Navoiy;40.0844 Grand Junction;39.0877 Sterling Heights;42.5809 Batu Gajah;4.4667 Tando Allahyar;25.4667 Luuq;3.6981 Idfū;24.9778 Ségou;13.4500 Isahaya;32.8500 Chikmagalūr;13.3167 Innsbruck;47.2683 Ōme;35.7880 Coral Springs;26.2702 Marituba;-10.2833 Matagalpa;12.9286 Jijel;36.8206 Jaramānā;33.4833 Jandira;-23.5278 Ferfer;5.0833 Gondiā;21.4500 Thandwe;18.4667 Hassan;13.0050 Pitalito;1.8989 Bibémi;9.3167 Abiko;35.8667 Talavera;15.5839 Khorramshahr;30.4397 Limoges;45.8353 Crato;-7.2339 Ouargla;31.9500 Ar Rass;25.8667 Sinop;-11.8481 San Carlos;10.4929 Kipushi;-11.7625 Lincoln;53.2283 Palwal;28.1430 Annecy;45.9160 Mogaung;25.3014 Ödemiş;38.2311 Kamëz;41.3833 Zagnanado;7.2667 Cametá;-2.2439 Cubatão;-23.8953 Ibarra;0.3627 San Luis;16.2000 Palenque;17.4333 Ferrara;44.8333 San Juan;18.8100 Āwarē;8.2667 Karabük;41.1986 Johnson City;36.3406 Saidpur;25.7781 Chicomba;-14.1333 Pālghāt;10.7792 Guelph;43.5500 Buea;4.1667 Los Guayos;10.1833 Nanqiaotou;22.7217 Ji-Paraná;-10.8853 Batang;-6.9081 Guagua;14.9667 Spanish Town;17.9959 Carrollton;32.9890 Chittandikavundanūr;10.6931 Surat Thani;9.1397 Narita;35.7767 Apopa;13.8000 Relizane;35.7372 Fürth;49.4667 Lào Cai;22.4806 Antsiranana;-12.3000 Lashio;22.9333 Silay;10.8000 Parepare;-4.0167 City of Isabela;6.7000 Thaton;16.9333 Zwolle;52.5167 Nueva Concepción;14.1997 Marand;38.4331 Kānchrāpāra;22.9456 Quibdó;5.6922 Girardot;4.3050 Pouso Alegre;-22.2281 Tychy;50.1236 Rustavi;41.5472 Bassila;9.0167 Midland;32.0243 Cholula de Rivadabia;19.0633 Trinidad;-14.8292 Onomichi;34.4167 Kislovodsk;43.9167 Kozan;37.4500 Udon Thani;17.4167 Jīroft;28.6781 Batticaloa;7.7167 Chās;23.6300 Yakima;46.5923 Bongaigaon;26.4769 Battambang;13.1000 Vitória de Santo Antão;-8.1264 Mukeriān;31.9500 Malambo;10.8500 Leiden;52.1600 Huejutla de Reyes;21.1333 Zhaozhou;37.7527 Lakewood;40.0763 Ergani;38.2692 Lucheng;30.0553 Hanumāngarh;29.5833 Çayırova;40.8265 Saïda;34.8303 Taldyqorghan;45.0167 Ñemby;-25.3935 Cambridge;43.3972 Fengcheng;37.4313 Ponnagyun;20.3333 Mothīhāri;26.6500 Serpukhov;54.9167 Mīt Ghamr;30.7167 Bellingham;48.7548 Yuba City;39.1357 Villa Alemana;-33.0422 Ciénaga;11.0069 Táriba;7.8167 Kibaha;-6.7667 Chinguar;-12.5500 Västerås;59.6161 Würzburg;49.7833 Mansa;-11.2000 Alwāl;17.5047 Songnim;38.7542 Santo Tomas;7.5333 Koidu;8.6439 Hoshangābād;22.7475 Opole;50.6667 Novocheboksarsk;56.1333 Araras;-22.3572 Khanna;30.7000 Puno;-15.8433 Koforidua;6.0833 Ahmadpur East;29.1439 Rosario;13.8460 Avrankou;6.5500 Salto;-31.3833 Mosquera;4.7078 Missérété;6.5625 Vihari;30.0419 Amherst;43.0117 Örebro;59.2739 Temperley;-34.7667 Baigou;39.1098 Tukuyu;-9.2500 Shiyan;23.1251 Zoetermeer;52.0667 Ban Bang Pu Mai;13.5441 Iwakuni;34.1664 Sinnūris;29.4072 Garut;-7.2167 Seto;35.2236 Bataysk;47.1667 Whitby;43.8833 Buôn Hồ;12.8544 Romford;51.5768 Pinsk;52.1153 Tumen;42.9627 Ramos Mejía;-34.6500 Rudnyy;52.9667 Guimba;15.6606 Miramar;22.3375 Nefteyugansk;61.0833 Nāḩiyat Khān Banī Sa‘d;33.5700 La Granja;-33.5333 South Lyon;42.4614 Al Ḩajar al Aswad;33.4640 Guasdualito;7.2467 Domodedovo;55.4400 Ulm;48.4000 Darnah;32.7667 Santa Lucía Cotzumalguapa;14.3333 Nagda;23.4564 Kadoma;34.7333 Salmās;38.2017 Santa Clara;37.3646 St. George;37.0758 Allada;6.6500 Jāzān;16.8892 Maricá;-22.9189 Dessalines;19.2833 Kaspiysk;42.8803 Heilbronn;49.1500 Anderlecht;50.8333 Fort Smith;35.3495 Neftekamsk;56.1417 Ōmiyachō;35.2221 San Ignacio;-5.1456 Bajos de Haina;18.4167 Bam;29.1061 High Wycombe;51.6287 Monza;45.5836 Pforzheim;48.8950 Ōsaki;38.5771 Jhārsugra;21.8500 Balneário de Camboriú;-26.9953 Santana de Parnaíba;-23.4439 Palni;10.4500 Rosetta;31.4044 Payakumbuh;-0.2333 Simi Valley;34.2663 Pakokku;21.3320 Angren;41.0167 Basīrhat;22.6612 Duitama;5.8333 Leiria;39.7500 Larache;35.1833 Navadwīp;23.4088 Latina;41.4672 Cambridge;52.2053 Ünye;41.1333 San Fernando;16.6200 Exeter;50.7256 Gurabo al Medio;19.4739 Santa Cruz;14.2833 Guntakal;15.1700 Pithampur;22.6197 Iizuka;33.6500 Sabará;-19.8858 Catabola;-12.1167 Suhum;6.0333 Bagong Silangan;14.7094 Barbacena;-21.2167 Bến Tre;10.2333 Hālīsahar;22.9500 Matamoros;25.5330 Western Bicutan;14.5094 Rishra;22.7100 Hoima;1.4319 Drohobych;49.3500 Panevėžys;55.7333 Leuwiliang;-6.5742 Magelang;-7.4667 Jizhou;37.5455 Kampong Cham;11.9870 Gashua;12.8681 Shchelkovo;55.9167 Dover;39.1610 Chinautla;14.7029 Pātan;23.8500 Novomoskovsk;54.0833 Norman;35.2335 Cam Ranh;11.9136 Kumbo;6.2000 Sai Wan Ho;22.2877 Giugliano in Campania;40.9319 Cagua;10.1875 Yuanlin;23.9611 Indramayu;-6.3528 Colchester;51.8917 Gateshead;54.9500 Pakxé;15.1167 Bonao;18.9500 Honchō;35.7580 Abilene;32.4543 Magalang;15.2167 Uruguaiana;-29.7550 Porlamar;10.9556 Resende;-22.4689 Daitōchō;34.7167 Móng Cái;21.5333 Sawrān;36.6606 León;42.6056 Sunām;30.1300 Central Signal Village;14.5106 Polatlı;39.5842 Jirjā;26.3333 Fugu;39.0259 Jilib;0.5000 Az Zulfī;26.2833 Billings;45.7891 Leeuwarden;53.2000 Ouahigouya;13.5833 Dūmā;33.5711 Juticalpa;14.6664 Zhaxi;27.8440 Gexianzhuang;37.0694 Wolfsburg;52.4231 Savannakhet;16.5500 Gorzów Wielkopolski;52.7333 Ponce;18.0127 Cai Lậy;10.4170 Gyumri;40.7894 Glazoué;7.9736 Pervouralsk;56.9167 New Mirpur;33.1490 Noksan;36.2039 San Pedro Garza García;25.6667 Montreuil;48.8611 Serik;36.9167 Algeciras;36.1275 Pāli;23.3500 Bimbo;4.3313 Hội An;15.8833 Setúbal;38.5243 Matsubara;34.5833 Baia Mare;47.6667 Kadirli;37.3697 Khrustalnyi;48.1214 Cherkessk;44.2222 Malindi;-3.2236 Pobé;6.9667 Bergamo;45.6950 Kirishima;31.7406 Gapan;15.3122 Magangué;9.2500 Jimaguayú;21.2667 Debre Tabor;11.8500 Maicao;11.3778 Delgado;13.7167 Uruma;26.3792 Lehigh Acres;26.6120 Derbent;42.0500 Melipilla;-33.6253 Pati;-6.7415 Cádiz;36.5350 Djidja;7.3333 Longtian;24.3512 St. Cloud;45.5340 Nāndgaon;20.3070 Arecibo;18.4491 Munūf;30.4658 Punta Arenas;-53.1667 Varginha;-21.5517 Munch’ŏn;39.2590 Kilifi;-3.6333 Bhadrakh;21.0545 Zacatecas;22.7736 Santana;-0.0350 Kaya;13.0833 Alberton;-26.2672 Kenosha;42.5865 Ise;34.4833 Lianhe;47.1414 Brits;-25.6344 Itatiba;-23.0058 Dimāpur;25.9200 Ed Damer;17.5900 Bragança;-1.0628 Patnos;39.2358 Bayawan;9.3667 Greenville;35.5943 Barretos;-20.5569 Arvada;39.8320 Kâhta;37.7803 Blackburn;53.7480 Machiques;10.0667 Ciudad Hidalgo;19.6923 Slough;51.5084 Pescara;42.4643 Guelma;36.4619 Behbahān;30.5958 Pearland;29.5581 Baidyabāti;22.7900 Kotamobagu;0.7333 Mufulira;-12.5356 Puerto Cortés;15.8833 Maina;13.4692 Dharmavaram;14.4300 Edéa;3.8000 Honmachi;32.5000 Ciudad Ojeda;10.2000 Bento Gonçalves;-29.1708 Independence;39.0871 Fier;40.7250 Kamalia;30.7258 Ghazīpur;25.5833 Orekhovo-Zuyevo;55.8000 Texas City;29.4154 Saint-Denis;48.9356 Chinandega;12.6297 Puruliya;23.3333 Kashiwara;34.5167 Fāqūs;30.7282 Urasoe;26.2458 Dorūd;33.4986 Guarapari;-20.6500 Hòa Bình;20.8133 Kuopio;62.8925 Ribeirão Pires;-23.7153 Ondjiva;-17.0667 Port-de-Paix;19.9500 Shuixi;22.5111 Wuling;39.4421 Samandağ;36.0850 Redding;40.5698 Tsuruoka;38.7272 Soreang;-7.0372 Ciudad Guzmán;19.7000 Gudiyāttam;12.9476 Upington;-28.4572 Chơn Thành;11.4292 Doğubayazıt;39.5472 Metz;49.1203 Płock;52.5500 Bagaha;27.0992 Gurdāspur;32.0333 Tabuk;17.4069 Apartadó;7.8833 Siracusa;37.0692 Apucarana;-23.5508 Perpignan;42.6986 Valinhos;-22.9706 Chilapa de Álvarez;17.5944 Cachoeirinha;-29.9508 Guelmim;28.9833 Navojoa;27.0813 Kot Addu;30.4700 Lynchburg;37.4003 Zonguldak;41.4564 Boulder;40.0248 Göttingen;51.5339 Unwana;5.8625 Vihiga;0.0500 Catchiungo;-12.7864 Barcelos;41.5167 Ilobu;7.8400 Kỳ Anh;18.0678 Sātāra;17.6833 Kallithéa;37.9500 Tizayuca;19.8333 Darjeeling;27.0375 Moḩammad Shahr;35.7483 Al Manşūrah;12.8531 Calumpit;14.9167 Orléans;47.9025 Bāgalkot;16.1833 Ebetsu;43.1037 Fujimino;35.8795 Đông Hòa;12.9931 Sertãozinho;-21.1381 Söke;37.7508 Dordrecht;51.7958 Poá;-23.5286 Lalo;6.9167 Ghardaïa;32.4833 Candaba;15.0933 Nokha;27.6000 San Carlos;9.6500 Besançon;47.2400 Baraka;-4.1041 Ar Rustāq;23.3908 Handa;34.8919 Iowa City;41.6559 Várzea Paulista;-23.2114 Ādīgrat;14.2667 Catanduva;-21.1378 Nouadhibou;20.9333 Chelmsford;51.7300 Gudivāda;16.4300 Yüksekova;37.5690 Naz̧arābād;35.9583 Pleven;43.4078 Rochester;44.0154 An;19.7833 Ajax;43.8583 Alcobendas;40.5333 Berkeley;37.8722 Xuqiaocun;30.4355 Dinalupihan;14.8833 Dongsheng;22.6199 Sopur;34.3000 Waldorf;38.6085 Toledo;-24.7139 Elbląg;54.1667 Nazran;43.2167 Banco Filipino International Village;14.4499 Araucária;-25.5928 Bruges;51.2089 Silifke;36.3761 Totonicapán;14.9108 The Villages;28.9034 Paulo Afonso;-9.4000 Guaratinguetá;-22.8167 Huaraz;-9.5333 Ţūz Khūrmātū;34.8772 Kasuga;33.5333 Aw Dheegle;1.9667 Apalit;14.9496 Bet Shemesh;31.7456 Gabès;33.8833 Bwana Mkubwa;-12.9833 Charallave;10.2431 Duluth;46.7756 East Los Angeles;34.0326 Cheltenham;51.9000 Nevinnomyssk;44.6333 Lahti;60.9833 Saginaw;43.4199 Dimitrovgrad;54.1833 Giyon;8.5333 Phagwāra;31.2200 Trento;46.0667 Ikoma;34.7000 Yongqing;39.2958 Pandacan;14.5940 Nowshera;34.0153 Butwāl;27.7000 Barcarena Nova;-1.5058 Martínez de la Torre;20.0667 Akishima;35.7057 Birigui;-21.2886 Ligao;13.2167 Pudukkottai;10.3833 Bottrop;51.5247 Santa Cruz do Sul;-29.7178 Kōnosu;36.0659 Clovis;36.8278 Karatepe;40.7333 Votorantim;-23.5469 Linköping;58.4158 Yulu;23.5193 Forlì;44.2333 Malita;6.4000 La Asunción;11.0333 Niğde;37.9667 Aizuwakamatsu;37.4948 Aïn Beïda;35.7964 Leominster;42.5209 Lomas de Zamora;-34.7667 Bama;11.5189 Codó;-4.4550 San Rafael;-34.6000 Chimaltenango;14.6622 Petržalka;48.1333 Plaridel;14.8869 Kabwe;-14.4333 Tahoua;14.8903 Nobeoka;32.5833 Gogounou;10.8386 Reutlingen;48.4833 Reutov;55.7622 Agadez;16.9959 Uzhhorod;48.6239 Round Rock;30.5270 Adilābād;19.6667 Luanshya;-13.1333 Kapaklı;41.3333 Obninsk;55.0931 Uribia;11.7139 Piedecuesta;7.0833 Salmān Bāk;33.1000 Tangjin;36.8931 Rouen;49.4428 Dąbrowa Górnicza;50.3214 Guiguinto;14.8333 Huanren;41.2671 Machakos;-1.5167 Fujimi;35.8566 Catacamas;14.8486 Argenteuil;48.9500 Los Baños;14.1667 San Martín;-33.0806 Xai-Xai;-25.0500 Rochdale;53.6136 Banfora;10.6308 Orsha;54.5092 Wałbrzych;50.7667 Langley;49.1044 Narasaraopet;16.2500 Temixco;18.8500 Heroica Guaymas;27.9183 Kyzyl;51.7167 Monroe;32.5185 Pollāchi;10.6590 Mendoza;-32.8833 Nkongsamba;4.9500 Samal;7.0500 Facatativá;4.8167 Guadalajara de Buga;3.9000 Cassongue;-11.8333 Barnāla;30.3700 Manpo;41.1570 Tandil;-37.3167 Yavatmāl;20.4000 Beppu;33.2795 Araguari;-18.6489 Tatuí;-23.3556 Cambridge;42.3759 Shibuya;35.6536 Sassari;40.7267 Conselheiro Lafaiete;-20.6600 Bagé;-31.3308 Chittaurgarh;24.8894 Berkane;34.9167 Buzău;45.1531 Santander de Quilichao;3.0167 Helsingborg;56.0500 Coronel;-37.0167 Kasese;0.1867 Clearwater;27.9790 Dharān;26.8167 Lugazi;0.3833 Tarogong;-7.2150 Boké;10.9400 Smithtown;40.8663 Himamaylan;10.1000 San Antonio Enchisi;19.7072 Joünié;33.9697 Tecomán;18.9089 Bongao;5.0292 Fatsa;41.0333 Phatthaya;12.9357 Swabi;34.1202 Qiaotou;36.9350 Khon Kaen;16.4333 Karatsu;33.4500 Ramenskoye;55.5667 Ra’s al Khaymah;25.7667 Kilis;36.7167 Seaside;36.6224 Sandvika;59.8833 Adjaouèrè;7.0000 Itapipoca;-3.4939 Guanabacoa;23.1252 West Jordan;40.6024 Sabhā;27.0389 Shacheng;40.4027 Sayhāt;26.4750 Kars;40.6078 Valle de La Pascua;9.2033 Parachinar;33.9000 Richardson;32.9716 San Ildefonso;15.0789 Kovilpatti;9.1744 West Palm Beach;26.7469 Temple;31.1068 Nasushiobara;36.9617 Tōkai;35.0231 Durrës;41.3111 Burlington;44.4876 Richmond;37.9477 Turbaco;10.3500 Pedro Juan Caballero;-22.5300 Rawasari;-7.5800 Três Lagoas;-20.7511 Berdiansk;46.7556 Westminster;39.8837 Kōenchō;43.8028 Mangaldan;16.0700 Niihama;33.9667 Pottstown;40.2508 Bānsbāria;22.9700 Puerto Madryn;-42.7667 Brandon;27.9367 Sliven;42.6833 Yessentuki;44.0333 Rock Hill;34.9415 Iriga City;13.4231 Jönköping;57.7828 Meridian;43.6116 Sano;36.3145 Ariana;36.8625 Sloviansk;48.8533 Koblenz;50.3597 Oktyabr’skiy;54.4667 Al Muḑaybī;22.5667 Sogamoso;5.7167 Salto;-23.2008 Zhijiang;27.4367 Hatsukaichi;34.3500 Sakiet ed Daier;34.8000 Coeur d'Alene;47.7040 Carlsbad;33.1246 Las Delicias;28.2000 Sach’on;35.0667 Bijeljina;44.7500 Lowell;42.6389 Heshan;23.8163 Erlangen;49.5833 Bremerhaven;53.5500 Ciudad de la Costa;-34.8167 Waterloo;42.4918 Kaikkudi;9.8930 Bernal;-34.7000 Kamagaya;35.7768 North Charleston;32.9067 Alphen aan den Rijn;52.1333 La Piedad;20.3333 Hammersmith;51.4928 Putatan;14.3984 Esteban Echeverría;-34.8167 Ambovombe;-25.1764 San Juan;13.8260 Sakété;6.7364 Espejo;-33.5167 Gilroy;37.0046 Bāgh-e Malek;31.5231 Rotherham;53.4300 Kakegawa;34.7687 Downey;33.9379 Ifanhim;6.6667 Gresham;45.5021 Bintulu;3.1700 Elgin;42.0383 Saanich;48.4840 Daet;14.1142 Āsela;7.9500 Matéri;10.6978 Kuşadası;37.8597 Kristiansand;58.1472 Pul-e Khumrī;35.9500 Recklinghausen;51.5850 Nagahama;35.3833 Tawau;4.2448 Walthamstow;51.5840 Quilengues;-14.0814 Mulhouse;47.7500 Mazhang;21.2757 Paarl;-33.7242 Hōfu;34.0500 Hikone;35.2667 Īrānshahr;27.2025 Maranguape;-3.8900 Rosario;14.4167 Ndali;9.8608 Ubá;-21.1200 Bukittinggi;-0.3097 Caraguatatuba;-23.6200 Manolo Fortich;8.3675 Midyat;37.4167 Mungo;-11.6667 Arafat;18.0464 Tecpán Guatemala;14.7667 Jendouba;36.5000 Alkmaar;52.6333 Sa-ch’on;35.2347 Hualien;23.9722 Ioánnina;39.6636 Akçakale;36.7108 Libmanan;13.6964 Bergisch Gladbach;50.9918 Euriápolis;-16.3778 Osmānābād;18.1667 Remscheid;51.1833 Novyy Urengoy;66.0833 Mostar;43.3436 Madrid;4.7344 Burdur;37.7194 La Gi;10.6600 Hosan;36.2000 Tayabas;14.0167 Mopti;14.4900 Sokodé;8.9833 Castelar;-34.6667 Sāhibganj;25.2500 Khenchela;35.4167 Vicenza;45.5500 Balāngīr;20.7200 Utica;43.0962 San Carlos de Bariloche;-41.1500 Otaru;43.1907 Goth Tando Sumro;25.4500 Rājpura;30.4840 Kaposvár;46.3638 Broken Arrow;36.0380 Higashiōmi;35.1167 Brovary;50.5111 Santa Lucía;10.2606 Mawanella;7.2534 Kuningan;-6.9764 Carora;10.1692 Madhavaram;13.1482 Laoag;18.1978 Tangjia;22.3566 Champdani;22.8000 Ōshū;39.1445 Zahlé;33.8333 Mahāsamund;21.1100 Habikino;34.5500 Kamyshin;50.0833 Campo Largo;-25.4589 Shujālpur;23.4000 Doncaster;53.5231 Dolgoprudnyy;55.9333 Shkodër;42.0681 Jena;50.9272 Trincomalee;8.5667 Subic;14.8769 Baleraja;-6.5167 Olmaliq;40.8500 Kawit;14.4333 Bou Saâda;35.2083 Pochuta;14.5500 Hezuo;34.9984 Estelí;13.0933 Jaén;37.7667 Araruama;-22.8728 Dassa-Zoumé;7.7500 Costa Mesa;33.6667 Chico;39.7578 Baybay;10.6833 Corumbá;-19.0089 Nancy;48.6936 Kouandé;10.3317 Zhukovskiy;55.5972 Nsukka;6.8567 Sioux City;42.4959 Ichinoseki;38.9347 Funchal;32.6500 Wythenshawe;53.3920 Colatina;-19.5389 Maidstone;51.2720 Sutton Coldfield;52.5630 League City;29.4874 Qalyūb;30.1997 Parral;26.9333 Terni;42.5619 Miami Gardens;25.9433 Tatalon;14.6242 Parintins;-2.6278 Trier;49.7567 Terrebonne;45.7000 Namur;50.4667 Changbang;30.4555 Pulilan;14.9020 Murom;55.5667 Kalmunai;7.4167 Sabara Bangou;15.1177 Manzini;-26.4833 Tondabayashichō;34.5000 Pingquan;40.9937 Kazo;36.1314 Pompano Beach;26.2428 Khardah;22.7200 Ōmuta;33.0333 Villa Mercedes;-33.6667 Jawhar;2.7833 Winterthur;47.4989 Tuzla;44.5381 Tarnów;50.0125 Gafsa;34.4167 Ash Shaykh ‘Uthmān;12.8866 Nkpor;6.1500 Ḩaraḑ;24.1456 Khushab;32.2986 Nawāda;24.8800 Muktsar;30.4743 Basingstoke;51.2667 Kintampo;8.0522 Catalão;-18.1700 Hounslow;51.4668 Ahuachapán;13.9167 Al Aḩad al Masāriḩah;16.7097 Puerto Barrios;15.7300 Fasā;28.9383 As Salamīyah;35.0118 Pelabuhanratu;-6.9878 Los Minas;18.5000 Shinyanga;-3.6619 Sonsonate;13.7167 Masindi;1.6836 Kiffa;16.6300 Betūl Bazār;21.9200 Nikopol;47.5667 Ferozepore;30.9166 Dali;34.7953 Yenangyaung;20.4597 Quíbor;9.9281 Ourinhos;-22.9744 Franceville;-1.6333 San Buenaventura;34.2741 Socopó;8.2322 Mascara;35.4000 Genhe;50.7783 Everett;47.9525 Montego Bay;18.4667 El Centro;32.7865 Ciamis;-7.3281 Caen;49.1800 Jeonghae;35.5667 Msaken;35.7333 Bāneh;35.9975 Shirayamamachi;36.5166 Itele;6.7667 Sugar Land;29.5935 Tinaquillo;9.9167 Xishancun;23.6014 Drammen;59.7378 El Monte;34.0739 Marugame;34.2833 Bangaon;23.0435 Qal‘at Sukkar;31.8589 Milton;43.5083 Yŏju;37.3000 Lewisville;33.0454 Retalhuleu;14.5333 Tacurong;6.6800 Navapolatsk;55.5333 Pisco;-13.7167 Dera Ismail Khan;31.8314 Labé;11.3167 Altamira;-3.2028 Cavite City;14.4833 Yevpatoriia;45.1939 Taitung;22.7583 Itabira;-19.6189 Malacatán;14.9106 Al Fqih Ben Çalah;32.5000 Naujan;13.3233 Quezon;7.7306 Sandachō;34.8833 Uitenhage;-33.7500 Aguachica;8.3167 Glan;5.8167 Carmona;14.3167 Bayugan;8.7100 Datia;25.6700 K’ebrī Dehar;6.7333 Sehore;23.2000 Medenine;33.3547 Kasserine;35.1667 Taoyang;35.3754 Gualeguaychú;-33.0167 Béja;36.7333 Talisay;10.7333 Lingayen;16.0167 Labo;14.1561 Yŏngju;36.8058 West Covina;34.0555 Paleng;-1.4000 Temecula;33.4928 Bagu Na Mohra;33.2200 Witbank;-25.8770 Maxixe;-23.8667 Ban Mangkon;13.6138 Bend;44.0563 Mineshita;35.1185 Túxpam de Rodríguez Cano;20.9500 Soma;39.1883 Novoshakhtinsk;47.7667 Acharnés;38.0833 Douliu;23.7075 São Mateus;-18.7158 Botoşani;47.7486 Žilina;49.2228 Balombo;-12.3500 Salisbury;38.3756 Crawley;51.1092 Nantang;22.4917 Ferizaj;42.3667 Ikeda;34.8167 Maţrūḩ;31.3500 St. John's;47.4817 Sītāmarhi;26.6000 Salford;53.4830 Mungeli;22.0700 Reus;41.1549 Moortebeek;50.8547 Taungoo;18.9333 Cawayan;9.9667 Gò Công;10.3667 Tādpatri;14.9200 Moncton;46.1328 Yāsūj;30.6683 Tipitapa;12.1964 Alto Hospicio;-20.2500 Jacksonville;34.7289 Islāmābād;33.7300 Maramag;7.7631 Jalpāiguri;26.5167 Birnin Kebbi;12.4504 Gharbara;28.4962 Shāmli;29.4500 Šiauliai;55.9333 Khemis Sahel;35.2500 Seversk;56.6000 Dagenham;51.5397 El Limón;10.3003 Malate;14.5642 Inglewood;33.9566 Villa Krause;-31.5833 Wembley;51.5528 Sarh;9.1500 An Nuhūd;12.7000 Kotmale;7.0167 Joyabaj;14.9950 Dearborn;42.3127 Centennial;39.5926 Koszalin;54.2000 Tajimi;35.3328 Thunder Bay;48.3822 Delft;52.0117 Baía Farta;-12.6128 Tagbilaran City;9.6500 Courbevoic;48.8978 Pavlohrad;48.5200 Emmen;52.7833 Queenstown;-31.9000 Chaman;30.9210 Pateros;14.5417 Umm Qaşr;30.0342 Musoma;-1.5000 Carmen;7.2000 Suriāpet;17.1415 Charsadda;34.1500 Kogon Shahri;39.7275 Shikohābād;27.1000 Kefar Sava;32.1714 Alchevsk;48.4778 Toufen;24.6832 Morales;15.4725 Burbank;34.1879 Manokwari;-0.8667 Bolzano;46.4981 Chorzów;50.3000 Erdenet;49.0278 Wa;10.0667 Launceston;-41.4419 Ejido;8.5466 Chongshan;18.7787 Idkū;31.3000 Kishanganj;26.0794 Ilebo;-4.3167 Namhkam;23.8333 Dieppe;46.0989 Luján;-34.5667 Arzamas;55.4000 Morón;-34.6500 Banté;8.4167 Longjiang;47.3404 Maipú;-32.9667 Edison;40.5360 Artëm;43.3667 Sparks;39.5736 Tiaong;13.9500 Erechim;-27.6339 Noyabrsk;63.2017 Monastir;35.7694 Catbalogan;11.7833 As Safīrah;36.0778 Chābahār;25.2919 Nakhon Si Thammarat;8.4364 Rānībennur;14.6167 Kŭlob;37.9092 Salihorsk;52.8000 Ad Dakhla;23.7081 Jincheng;39.5529 Sandy Springs;33.9366 Raba;-8.4614 Patos;-7.0244 Mmabatho;-25.8500 Lokossa;6.6333 La Banda;-27.7333 Kemalpaşa;38.4278 Jamālpur;25.3000 Sơn La;21.3270 Passos;-20.7189 Nautanwa;27.4300 Akhmīm;26.5667 Bloomington;39.1637 Logan;41.7399 Komatsu;36.4083 Aihua;24.4629 Roubaix;50.6901 Temoaya;19.4686 Achinsk;56.2817 Kailua;21.3920 Mingəçevir;40.7700 Contramaestre;20.3000 Ciudad Río Bravo;25.9861 El Cajon;32.8017 Vespasiano;-19.6919 Atebubu;7.7500 Khanty-Mansiysk;61.0000 Nusaybin;37.0750 Lo Barnechea;-33.3500 Azare;11.6742 Ariquemes;-9.9161 Paço do Lumiar;-2.5319 Gangāwati;15.4300 Hillsboro;45.5273 Koutiala;12.3833 Kiryū;36.4052 Pushkino;56.0167 Yelets;52.6167 Ourense;42.3364 Ballia;25.7604 Bình Long;11.6527 Tikrīt;34.6100 Bulan;12.6697 Miriālgūda;16.8667 Playas de Rosarito;32.3636 Lerma;19.2847 Tarīm;16.0500 Maluñgun;6.2667 Mityana;0.4006 Presidente Franco;-25.5333 South Fulton;33.6273 Mazyr;52.0500 Robāţ Karīm;35.4847 Kroonstad;-27.6456 Konotop;51.2369 Kandhkot;28.4000 Saint Helens;53.4500 Toride;35.9115 Ōnojō;33.5333 Makrāna;27.0500 Granada;11.9347 Assis;-22.6619 Renton;47.4784 Girona;41.9833 Kongolo;-5.4000 Sheopur;25.6700 Sultan Kudarat;7.2333 Moers;51.4592 Alaşehir;38.3500 Liberec;50.7667 Yozgat;39.8208 Texcoco;19.5200 Novara;45.4500 Tam Điệp;20.1556 Tourcoing;50.7239 Al Fāw;29.9758 Balanga;14.6800 Ndjamba;-14.7000 Mandeville;30.3751 San Mateo;37.5522 Columbia;39.2004 Masbate;12.2700 Salzgitter;52.1500 Tezpur;26.6300 Berdsk;54.7500 Kurichchi;10.9609 Ngong;-1.3667 Waterloo;43.4667 Worcester;52.1911 Daly City;37.6862 Tādepallegūdem;16.8150 Bălţi;47.7667 Włocławek;52.6592 Maumere;-8.6222 Davie;26.0789 Masaka;-0.3411 Nanterre;48.8988 Sergiyev Posad;56.3000 Jurupa Valley;34.0010 Francistown;-21.1736 Fugangcun;23.5899 Olanchito;15.4833 Trindade;-16.6581 Leme;-22.1861 Roquetas de Mar;36.7642 Inzai;35.8333 Khāk-e ‘Alī;36.1283 Maldonado;-34.9000 Fulgāzi;23.1333 Suruç;36.9764 Techiman;7.5772 Brājarājnagar;21.8167 Arapongas;-23.4189 Villa Luzuriaga;-34.6667 Aquin;18.2833 Chikushino;33.5000 Caseros;-34.6106 San Vicente de Baracaldo;43.2972 Gillingham;51.3850 Ginowan;26.2817 Tula de Allende;20.0500 Brockton;42.0821 Kalamariá;40.5833 Sindangan;8.2386 Lower Hutt;-41.2167 Coronel Fabriciano;-19.5189 Kwai Chung;22.3674 Kamianets-Podilskyi;48.6806 Açailandia;-4.9469 Mubende;0.5575 Natitingou;10.3000 Séguéla;7.9667 Longmont;40.1686 Elista;46.3167 Kalyani;22.9750 Kilinochchi;9.4004 Paniqui;15.6681 Saijō;33.9167 Isehara;35.3833 Dolisie;-4.2000 Negapatam;10.7667 Eastbourne;50.7700 Pazardzhik;42.2000 Wigan;53.5448 Rialto;34.1175 Libertad;-34.6833 San Rafael;14.9500 Thika;-1.0396 Telde;27.9985 Yunfu;28.6331 Buxar;25.5605 Dongguazhen;25.0790 San Germán;18.0827 Siegen;50.8833 Tantoyuca;21.3500 Vitry-sur-Seine;48.7875 Biak;-1.1800 Zomba;-15.3833 Yishi;35.1379 Songea;-10.6833 Hikkaduwa;6.1472 Eau Claire;44.8197 Bouskoura;33.4489 Lida;53.8833 Būndi;25.4383 Mazatenango;14.5333 Hove;50.8352 Nyeri;-0.4167 Amarāvati;16.5131 Messaad;34.1542 Bumba;2.1844 Araxá;-19.5928 Turlock;37.5053 Woodbridge;40.5611 Norwalk;33.9069 Almirante Tamandaré;-25.3250 Sakado;35.9573 São Lourenço da Mata;-8.0019 Yilong;23.7081 Itanhaém;-24.1806 Novokuybyshevsk;53.1000 Bergama;39.1167 Highlands Ranch;39.5419 Soasio;0.6833 Piacenza;45.0500 Miryang;35.5000 Rishīkesh;30.1083 Tanjungpandan;-2.7500 Houzhuang;35.6390 Mengdingjie;23.5517 Hildesheim;52.1500 Xırdalan;40.4486 Parang;7.3744 Satu Mare;47.7900 Nantou;23.9167 Coatepec;19.4522 Sangju;36.4400 Santa Rosa;-36.6167 Mumias;0.3333 Bhadreswar;22.8200 Guihulñgan;10.1167 Hinche;19.1500 Noginsk;55.8500 Leuven;50.8833 Bethal;-26.4500 Allen;33.1088 Hengnan;22.5337 San Felipe del Progreso;19.7125 Níkaia;37.9667 Chaguanas;10.5167 Bahrain;35.2078 Burzaco;-34.8167 Sundarnagar;31.5300 Seoni;22.0800 Wichita Falls;33.9072 Calasiao;16.0167 Paoy Paet;13.6500 Ngã Bảy;9.8164 Mayarí;20.6592 Kapūrthala;31.3800 Rio Rancho;35.2873 Mositai;45.5266 Sabanalarga;6.8500 Dhangaḍhi̇̄;28.7136 Igarassu;-7.8342 Kāshmar;35.2383 Aurangābād;24.7000 Vacaville;38.3587 Klagenfurt;46.6167 Delta;49.0847 Chilakalūrupet;16.0892 Vāsco Da Gāma;15.3981 Spokane Valley;47.6626 Charlottesville;38.0375 Chishtian;29.8000 Şabrātah;32.7922 Tota;6.8000 Gütersloh;51.9000 Al Jumayl;32.8528 Boryeong;36.3333 Dhamtari;20.7100 Jingping;39.5189 Deventer;52.2500 Shūshtar;32.0456 Tavşanlı;39.5333 Teluk Intan;4.0259 Santo Antônio de Jesus;-12.9692 Hồng Ngự;10.8330 Oued Zem;32.8667 Sūjāngarh;27.7000 Jeypore;18.8563 Chatham;42.4229 ‘Ibrī;23.2325 Bỉm Sơn;20.0781 Zheleznogorsk;52.3333 Sungai Penuh;-2.0589 Yên Bái;21.7000 Itauguá;-25.3833 Balsas;-7.5328 Santa Cruz;6.8333 San Luis de la Paz;21.3000 Aïn Oussera;35.4489 Créteil;48.7911 Liancheng;24.0515 Kawachinagano;34.4667 Idaho Falls;43.4871 Kousséri;12.0833 Mestre;45.4906 Radès;36.7667 Hāgere Hiywet;8.9833 Daoukro;7.0500 Weifen;38.4633 Kanoya;31.3831 Menifee;33.6909 Oum el Bouaghi;35.8706 Ancona;43.6169 Mporokoso;-9.3833 Sololá;14.7667 Port Blair;11.6683 Vimmerby;57.6667 Aboisso;5.4667 Chanwari;23.2000 Muriaé;-21.1306 Sungailiat;-1.8561 New Kru Town;6.3733 Kpalimé;6.9000 Leer;8.2979 Lee's Summit;38.9171 Umuarama;-23.7658 Nāḩiyat al Iskandarīyah;32.9000 Kaiserslautern;49.4447 Gangtok;27.3300 Diourbel;14.6550 Ahar;38.4847 Kōnan;35.3321 Chust;40.9978 Quincy;42.2506 Masjed Soleymān;31.9364 Geita;-2.8714 Sumber;-6.7544 Mairiporã;-23.3189 Torbat-e Jām;35.2439 Lamitan;6.6500 Rincón de Romos;22.2333 Red Deer;52.2681 Shahrisabz;39.0500 Aubervilliers;48.9131 Talipao;5.9760 San Angelo;31.4424 Santa Cruz del Quiché;15.0500 Lynn;42.4781 Holland;42.7677 Zelënodol’sk;55.8500 Formosa;-15.5369 Nanxicun;23.4976 Bacabal;-4.2250 Pyinmana;19.7500 Bāmyān;34.8250 Kamloops;50.6761 Rayleigh;51.5864 Dunedin;-45.8742 Nabire;-3.3682 Râmnicu Vâlcea;45.1047 Ocotlán;20.3514 La Libertad;-2.2333 Sisophon;13.5833 Jinotega;13.0908 Viseu;40.6667 Santa Fe;35.6619 Maribor;46.5575 Ashiya;34.7278 Dhuliān;24.6800 Bayeux;-7.1333 Kohīma;25.6700 Hemel Hempstead;51.7526 Sopron;47.6849 Galle;6.0536 Zhudong;24.7366 Ciudad General Belgrano;-34.7167 Vista;33.1896 Santiago de Compostela;42.8778 Chipata;-13.6453 San Ramón;-33.5333 Yashio;35.8225 Herẕliyya;32.1653 Catarman;12.4994 Bath;51.3800 Janakpur;26.7286 Tenancingo;18.9608 San Fernando;36.4667 Half Way Tree;18.0106 Inagi;35.6379 Vị Thanh;9.7833 San Joaquín;-33.4792 Isulan;6.6333 Colombes;48.9236 Petapa;14.4962 Pernik;42.6000 Sidi Slimane;34.2600 Wangqing;43.3126 Darlington;54.5270 Gävle;60.6747 Sibolga;1.7425 Fulham;51.4828 Tumbes;-3.5708 Arauca;7.0903 Levallois-Perret;48.8950 Chārīkār;35.0131 La Reina;-33.4500 Orem;40.2981 Sepatan;-6.1143 Komae;35.6348 La Marsa;36.8764 Yishui;35.7904 Marmagao;15.4020 San Francisco Solano;-34.7667 Ziftá;30.7142 Kakamega;0.2822 Schwerin;53.6333 Sunrise;26.1547 Labuan;5.3000 Guercif;34.2333 Compton;33.8930 Çankırı;40.5986 Errachidia;31.9319 Lingtang;23.6032 Yotsukaidō;35.6698 České Budějovice;48.9747 Arden-Arcade;38.6017 Río Gallegos;-51.6233 Fenggang;23.6283 Tataouine;32.9306 Tripunittura;9.9528 Ludwigsburg;48.8975 Wajir;1.7500 Ealing;51.5175 Léogâne;18.5108 Nisshin;35.1320 Xiancun;23.2374 Ōbu;35.0117 Al Badrashayn;29.8520 Naxçıvan;39.2089 Wimbledon;51.4220 Đông Hà;16.8303 Ílion;38.0333 Netrakona;24.8819 Ciudad Lázaro Cárdenas;17.9561 Mtwara;-10.2736 Ţalkhā;31.0547 Gjakovë;42.3833 Prizren;42.2128 Bình Hòa;10.9061 Ben Arous;36.7472 Watford;51.6550 Monte Chingolo;-34.7333 San Isidro;14.4685 Ben Guerir;32.2300 Vanadzor;40.8128 Cisauk;-6.3333 Hastings;50.8500 Drobeta-Turnu Severin;44.6333 Al Fujayrah;25.1223 Los Cerrillos;-33.5000 Tartu;58.3833 Makeni;8.8817 Suceava;47.6514 Amstelveen;52.3000 Néa Smýrni;37.9500 Hradec Králové;50.2092 Nuneaton;52.5230 Şırnak;37.5200 Miaoli;24.5700 South Gate;33.9447 Ciudad de Melilla;35.2825 Stevenage;51.9017 Ústí nad Labem;50.6583 Orpington;51.3741 Magadan;59.5667 Santa Monica;34.0235 Pardubice;50.0386 Bender;46.8333 Oulad Teïma;30.4000 Jaffna;9.6647 Queluz;38.7514 Aulnay-sous-Bois;48.9386 Umeå;63.8250 Masvingo;-20.0744 Brixton;51.4575 Edmonton;51.6154 Settsu;34.7772 Ouidah;6.3667 Hartlepool;54.6900 Wakō;35.7812 Upper Bicutan;14.4873 Cadereyta Jiménez;25.6000 Poitiers;46.5800 Jabālyā;31.5281 Westminster;33.7523 Fuengirola;36.5417 Chester;53.1900 Lobnya;56.0167 San Leandro;37.7074 Hemei;24.1167 Solwezi;-12.1433 Grand Bourg;-34.4833 Kalibo;11.7072 San Antonio;14.4656 Ama;35.2004 Abomey;7.1856 Zhunan;24.6833 Glyfáda;37.8667 Kitanagoya;35.2456 Remedios de Escalada;-34.7167 Babahoyo;-1.8167 Jangipur;24.4700 Acayucan;17.9422 Zhezqazghan;47.7833 Dobrich;43.5667 Xicotepec de Juárez;20.3000 Ţarţūs;34.8833 Higashiyamato;35.7454 Catanzaro;38.9000 East Ham;51.5323 Kennedy Town;22.2800 Wakiso;0.3981 Valjevo;44.2667 Bromley;51.4070 Hayes;51.5127 Germantown;39.1755 Jalal-Abad;40.9333 Bhola;22.6863 Tala;20.6525 Yoro;15.1333 Prešov;49.0017 Bismarck;46.8143 Warabi;35.8256 Balkanabat;39.5167 Ipil;7.7822 Takasagochō-takasemachi;34.7667 Perintalmanna;10.9765 Mechelen;51.0278 As Salţ;32.0333 Clifton;40.8630 Mukacheve;48.4414 Tangxing;35.7261 Chinnachauku;14.4732 Nabatîyé;33.3833 Daxincun;38.4427 Al Wakrah;25.1800 Bayanan;14.4078 Viana do Castelo;41.7000 Apizaco;19.4167 Aylesbury;51.8168 Sesto San Giovanni;45.5333 Podujevë;42.9167 State College;40.7909 Prosperidad;8.6057 Bertoua;4.5833 Alabel;6.1023 Versailles;48.8053 Antehiroka;-18.8500 Coronel Oviedo;-25.4167 Dārayyā;33.4500 Ayase;35.4333 Ciudad de Ceuta;35.8867 Lower Bicutan;14.5033 Ébolowa;2.9167 Sabaneta;6.1500 Clichy;48.9044 Torre del Greco;40.7853 Hawthorne;33.9147 Maasin;10.1300 San Juan de los Morros;9.9010 Lawrence;42.7002 Salina Cruz;16.1833 San Baudilio de Llobregat;41.3458 Citrus Heights;38.6948 Athurugiriya;6.8922 Edgware;51.6185 Burnley;53.7890 Whittier;33.9678 Mardin;37.3131 Xishancun;23.2589 Deurne;51.2247 Mpanda;-6.3500 Ezpeleta;-34.7517 Larnaca;34.9167 Mingxing;37.4264 Mādabā;31.7167 Nakhon Sawan;15.7133 Saint Albans;51.7550 Villa Celina;-34.7006 Issy-les-Moulineaux;48.8239 Loznica;44.5333 Tucupita;9.0575 Ath Thawrah;35.8367 Tacámbaro de Codallos;19.2356 Odienné;9.5000 Keratsíni;37.9667 Ciudad Lerdo;25.5500 Owariasahi;35.2165 Piatra Neamţ;46.9275 Deerfield Beach;26.3050 San Fernando;10.2833 Kouvola;60.8681 Kuznetsk;53.1167 Toledo;39.8567 Busto Arsizio;45.6120 El Bayadh;33.6831 Ponnāni;10.7700 Karakol;42.4903 Como;45.8103 Shumen;43.2833 Xuddur;4.1200 Ágios Dimítrios;37.9333 Pori;61.4833 Manfalūţ;27.3167 Mitrovicë;42.8833 Baj Baj;22.4828 Cozumel;20.5104 Louga;15.6167 Atakpamé;7.5269 Puerto Maldonado;-12.6000 Heṭauḍā;27.4167 Upper Darby;39.9490 Cicero;41.8445 Veliko Tarnovo;43.0778 Dar el Beïda;36.7142 Purmerend;52.5000 Nazareth;32.7019 Uman;48.7500 Nabunturan;7.6008 Nagaoka;34.9333 Osijek;45.5556 Chervonohrad;50.3822 Lucerne;47.0500 Būmahen;35.7297 Kunitachi;35.6839 Shek Tong Tsui;22.2871 Pine Hills;28.5818 Tecate;32.5728 Newmarket;44.0500 Ilioúpoli;37.9333 Barahona;18.2000 Ho;6.6119 Phuket;7.8881 Le Bardo;36.8092 Buena Park;33.8572 Kayes;14.4500 Champigny-sur-Marne;48.8172 Haskovo;41.9333 Chatham;51.3700 Batley;53.7167 Esteio;-29.8608 Reşiţa;45.3008 Rueil-Malmaison;48.8760 Shinkai;35.8367 Miami Beach;25.8171 Kirtipur;27.6667 Casoria;40.9000 Tallaght;53.2886 West Rembo;14.5667 Scunthorpe;53.5809 Schiedam;51.9167 Dudley;52.5080 Alhambra;34.0840 Ban Talat Rangsit;13.9833 Silao;20.9478 Târgu Jiu;45.0342 El Palomar;-34.6167 Al Khānkah;30.1601 Kiyose;35.7857 Owendo;0.2833 Tanuku;16.7500 Silver Spring;39.0028 Lakewood;33.8471 Mountain View;37.4001 Juchitán de Zaragoza;16.4333 White Rock;49.0250 Elbasan;41.1111 Campo Formoso;-10.5089 Évosmos;40.6689 Weston-super-Mare;51.3460 Néa Ionía;38.0333 Zaandam;52.4333 Paisley;55.8456 South Shields;54.9950 Saint-Maur-des-Fossés;48.7994 Bilecik;40.1431 Huolu;38.0874 Handeni;-5.4242 Bel-Air;14.5639 Banī Mazār;28.5000 Daugavpils;55.8750 Ivanteyevka;55.9833 Drancy;48.9300 Los Reyes de Salgado;19.5833 Cinisello Balsamo;45.5500 Kashiba;34.5333 Hakkari;37.5770 Rubí;41.4933 Bury;53.5930 Le Kram;36.8333 Arāria;26.1500 Dome;5.6550 Chalándri;38.0167 Târgovişte;44.9244 Galway;53.2719 Barreiro;38.6667 Paysandú;-32.3214 Lomas del Mirador;-34.6667 Cabadbaran;9.1228 New Rochelle;40.9304 Tejupilco;18.9058 Ciudadela;-34.6333 Aigáleo;37.9920 Viedma;-40.8000 Ragusa;36.9250 Lebanon;40.3412 Somerville;42.3908 Focşani;45.7000 Zuwārah;32.9333 Quatre Bornes;-20.2654 Yauco;18.0344 Shancheng;34.7904 Kuniyamuttūr;10.9638 Fnidq;35.8500 Sremska Mitrovica;44.9700 Puerto Ayacucho;5.6631 Chiquimula;14.7833 Sidi Qacem;34.2167 Kirdāsah;30.0320 La Rochelle;46.1600 Izumiōtsu;34.5000 Lelystad;52.5000 Tustin;33.7311 Alabang;14.4184 Mansfield;53.1444 Bracknell;51.4160 Balagtas;14.8145 Avilés;43.5561 Nakhon Pathom;13.8206 Mahdia;35.5000 Milpitas;37.4336 Ubon Ratchathani;15.2281 Bistriţa;47.1333 Weligama;5.9739 Carlisle;54.8910 Garissa;-0.4569 Lisala;2.1486 Nitra;48.3069 Zográfos;37.9783 East Kilbride;55.7644 Bellflower;33.8880 Frontera;26.9260 Banská Bystrica;48.7353 Katano;34.7833 Ra‘ananna;32.1833 Perote;19.5620 Kafr az Zayyāt;30.8247 Aveiro;40.6333 Tambacounda;13.7689 Burton upon Trent;52.8019 Pau;43.3000 Ungoofaaru;5.6681 Evanston;42.0464 Kensington;51.5000 Thornton Heath;51.4002 Faranah;10.0333 Bokhtar;37.8364 Moulay Abdallah;33.1978 Cannes;43.5513 Wang Tau Hom;22.3408 Alameda;37.7668 Al Muḩarraq;26.2500 Godāwari̇̄;28.9100 Maghāghah;28.6500 Kuacjok;8.3100 Madhubani;26.3700 Malappuram;11.0588 Sucat;14.4600 Crewe;53.0990 Cửa Lô;18.8167 Gladbeck;51.5667 San Miguel de Allende;20.9142 Sankt Gallen;47.4242 Gouda;52.0111 Kargilik;37.8850 Maroúsi;38.0500 Montrouge;48.8172 Limbé;19.7056 Newcastle under Lyme;53.0109 Chingford;51.6230 Korydallós;37.9833 Harrogate;53.9919 Caxito;-8.5800 Mao;19.5667 Okegawa;36.0057 Virac;13.5833 Las Piedras;-34.7167 Rugby;52.3700 Berbérati;4.2614 Tamazunchale;21.2667 Zrenjanin;45.3833 Fouchana;36.7000 Surt;31.2050 Pančevo;44.8706 Vlaardingen;51.9000 Nerkunram;13.0667 Jōyō;34.8531 Luodong;24.6767 Abéché;13.8331 Aïn Temouchent;35.3044 Palmerston North;-40.3550 Cheyenne;41.1350 Palaió Fáliro;37.9333 Tamworth;52.6330 Chiryū;35.0014 Watsonville;36.9206 Antibes;43.5808 Karlstad;59.3783 Nandi Hills;0.1003 Ramla;31.9275 Jinja;0.4233 Tamanrasset;22.7850 Zadar;44.1194 Ixtaczoquitlán;18.8500 Benalmádena;36.6000 Musashimurayama;35.7548 Bella Vista;-34.5333 Arta;11.5236 Vincennes;48.8478 Tafo;6.7358 Tissemsilt;35.6072 Joensuu;62.6000 Rioverde;21.9300 Matehuala;23.6528 Davis;38.5553 Darhan;49.4689 Teyateyaneng;-29.1511 Zacatecoluca;13.5000 Pawtucket;41.8744 Zlín;49.2331 Spijkenisse;51.8500 Avaniyāpuram;9.8818 Calais;50.9481 Jinsha;23.5287 Kiyosu;35.1998 Siuri;23.9100 Prey Veng;11.4833 Dazaifu;33.5167 Inowrocław;52.7931 Bitola;41.0319 San Andrés;12.5847 Poblacion;10.2500 Tangalla;6.0167 Lowestoft;52.4800 Southall;51.5121 Longkoucun;23.5726 Caguas;18.2319 Hekinan;34.8847 Yoshikawa;35.8939 Assab;13.0167 Bagumbayan;14.4744 Gosport;50.7948 Nabeul;36.4542 Cao Bằng;22.6667 Ivry-sur-Seine;48.8078 Uxbridge;51.5404 Birobidzhan;48.8000 Grays;51.4750 Tsurugashima;35.9345 Tulcea;45.1900 New Britain;41.6759 Lauderhill;26.1605 El Kef;36.1822 ‘Izbat al Burj;31.5031 Gerli;-34.6833 Salima;-13.7833 Saint-Louis du Nord;19.9333 Blagoevgrad;42.0119 Şa‘dah;16.9400 Maharlika Village;14.4989 Walton upon Thames;51.3868 Neuilly-sur-Seine;48.8881 Busia;0.4669 Noisy-le-Grand;48.8478 Galátsi;38.0167 Şirvan;39.9323 Kodungallūr;10.2338 Kamuli;0.9450 Fada Ngourma;12.0500 Yawata-shimizui;34.8667 San José de las Lajas;22.9678 Thakhèk;17.4000 Cuscatancingo;13.7333 San Dionisio;14.4839 Centreville;38.8390 Feltham;51.4496 San Antonio;-25.3797 Pavia;10.7750 Lappeenranta;61.0667 Gaura;25.4961 Mount Vernon;40.9136 Baldwin Park;34.0829 Chiyoda-ku;35.6940 Cabo San Lucas;22.8897 Kolonnawa;6.9283 Oroquieta;8.4833 Wandsworth;51.4550 Castelldefels;41.2800 Karakax;37.2714 Abancay;-13.6333 Camden;39.9361 Toyoake;35.0509 Wilde;-34.7000 Havířov;49.7831 Torremolinos;36.6218 Ajaccio;41.9267 Stara Pazova;44.9833 Borongan;11.6094 Matara;5.9500 Nizwá;22.9333 Najrān;17.4917 Víctor Larco Herrera;-8.1333 Čačak;43.8914 Walsall;52.5800 Cergy;49.0361 Sihanoukville;10.6333 Neyyāttinkara;8.4000 La Chorrera;8.8792 Al Qūşīyah;27.4143 Varisshiyakuni;11.6400 Kireka;0.3467 Pantin;48.8966 Halmstad;56.6739 Capelle aan den IJssel;51.9333 Vénissieux;45.6978 Tarīn Kōṯ;32.6267 Givatayim;32.0714 Youssoufia;32.2500 Mitcham;51.4009 Misantla;19.9333 Salamá;15.1052 Yambol;42.4833 Bitlis;38.4000 Martínez;-34.4833 Slatina;44.4297 Ramos Arizpe;25.5500 Phitsanulok;16.8158 Samraong;14.2500 Zinjibār;13.1283 Hatogaya-honchō;35.8267 Tamarac;26.2056 Điện Biên Phủ;21.3833 Borj el Qoblé;33.2631 Redondo Beach;33.8577 New Westminster;49.2069 Kumanovo;42.1322 Valle Hermoso;25.6736 Wilmington;39.7415 Novi Pazar;43.1500 Mindelo;16.8860 Gardēz;33.6000 Chiang Rai;19.9094 Tynemouth;55.0170 Abnūb;27.2667 Paignton;50.4353 Āksum;14.1208 Jinxing;37.9869 Huauchinango;20.1767 Huatusco;19.1489 Bayonne;40.6668 Guaynabo;18.3832 Fujiidera;34.5667 Aïn Harrouda;33.6372 Kashiwara;34.5833 Ţūkh;30.3539 Dimbokro;6.6500 Passaic;40.8574 Veenendaal;52.0167 Agía Paraskeví;38.0117 Sagaing;21.8822 Villanueva y Geltrú;41.2243 Szolnok;47.1747 Encarnación;-27.3333 Viladecáns;41.3158 Rochester;51.3750 Vushtrri;42.8222 Moquegua;-17.2000 Heroica Caborca;30.7167 Washington;54.9000 Rivera;-30.9025 Ashford;51.1465 Georgiyevsk;44.1500 Assen;53.0000 Castellammare di Stabia;40.6947 L’Aquila;42.3500 Cortazar;20.4830 Eskilstuna;59.3708 Antony;48.7539 Finchley;51.5990 Hornchurch;51.5565 Liepāja;56.5117 Acton;51.5135 Kati;12.7504 Bouira;36.3800 Portici;40.8197 Tamlūk;22.3000 East Orange;40.7651 San José del Guaviare;2.5667 Taman Johor Jaya;1.5333 Gaithersburg;39.1346 Ponta Delgada;37.7400 Hihyā;30.6687 Al Minshāh;26.4833 Villa Domínico;-34.6917 Sabinas;27.8489 Pruszków;52.1667 Merthyr Tudful;51.7430 Eastvale;33.9617 Al Buraymī;24.2592 Kitamoto;36.0269 Şəki;41.2000 Afragola;40.9167 Horad Zhodzina;54.1000 Arima;10.6333 Adrar;27.8742 Al Qurayn;30.6161 Waterlooville;50.8800 Valle de Bravo;19.1925 Karlskrona;56.1608 Kāyankulam;9.1720 Växjö;56.8769 Apac;1.9850 Tomigusuku;26.1611 Bayombong;16.4833 Bignay;14.7456 Výronas;37.9617 Buynaksk;42.8167 Bankra;22.6300 Ban Suan;13.3616 Areguá;-25.2953 Katwijk;52.2000 Boston;52.9740 Banda del Río Salí;-26.8500 Hämeenlinna;60.9944 Vaasa;63.1000 Bayt Lāhyā;31.5539 Scarborough;54.2773 Union City;40.7675 Chiapa de Corzo;16.7080 Cupang;14.4315 Tunasan;14.3725 Täby;59.4333 Streatham;51.4279 Farnborough;51.2900 Cabedelo;-6.9808 Barking;51.5400 Molepolole;-24.4067 Potenza;40.6333 El Prat de Llobregat;41.3246 Teresa;14.5586 Lynwood;33.9240 Skokie;42.0360 Patuakhāli;22.3542 Kozáni;40.3000 Arrecife;28.9625 Altamira;22.3375 Guamúchil;25.4639 Siemianowice Śląskie;50.2758 Stourbridge;52.4575 Twickenham;51.4490 Fryazino;55.9500 Acámbaro;20.0361 Maisons-Alfort;48.8058 Lechang;35.6415 Bolgatanga;10.7833 Martil;35.6167 Petroúpoli;38.0333 Laeken;50.8778 Schenectady;42.8025 Kraljevo;43.7234 Épinay-sur-Seine;48.9553 Hereford;52.0560 Khagaul;25.5790 Troyes;48.2997 Collado-Villalba;40.6333 Bayburt;40.2597 Granollers;41.6083 Nālūt;31.8685 Zacapa;14.9667 Los Polvorines;-34.5000 San Jose;10.7433 Gampaha;7.0917 South San Francisco;37.6538 Prilep;41.3464 Trnava;48.3775 Sarcelles;48.9956 Dewsbury;53.6910 Sundapālaiyam;11.0014 Bilwi;14.0297 Goz-Beida;12.2236 Loughborough;52.7700 Brentwood;40.7839 Wrecsam;53.0460 San Marcos;14.9653 Samut Sakhon;13.5486 Pervomaisk;48.0500 Don Bosco;14.4817 Tatabánya;47.5862 La Seyne-sur-Mer;43.1000 La Línea de la Concepción;36.1611 Daljā;27.6500 Kettering;52.3931 Forest;50.8131 Călăraşi;44.2000 Magong;23.5667 Malden;42.4305 Villejuif;48.7919 Ameca;20.5486 Rundu;-17.9167 Akurana;7.3650 Ambarawa;-7.2667 Hurlingham;-34.5883 Kitale;1.0167 Nagakute;35.1840 Scheveningen;52.1081 Paphos;34.7667 Guayama;17.9743 Karonga;-9.9333 Etterbeek;50.8333 Songkhla;7.2061 Kresek;-6.1314 Soroti;1.7150 Ouezzane;34.8000 Tagajō;38.2938 As Suwaydā’;32.7125 Euclides da Cunha;-10.5078 Wamena;-4.0975 Popondetta;-8.7656 Ełk;53.8214 Faro;37.0161 Faīẕābād;37.1166 Alba Iulia;46.0669 Cosamaloapan;18.3667 Sokhumi;43.0000 Ellesmere Port;53.2790 Cherbourg;49.6333 Le Blanc-Mesnil;48.9387 Bondy;48.9022 Yamatotakada;34.5167 Beccar;-34.4667 Manouba;36.8078 Nagari;13.3214 Bangor;54.6600 Inhambane;-23.8650 Qal‘ah-ye Now;34.9867 Runcorn;53.3419 Seinäjoki;62.7917 Taunton;51.0190 Littlehampton;50.8094 Florence-Graham;33.9682 Latacunga;-0.9319 Twin Rivers;40.2631 Vaslui;46.6383 Choi Hung;22.3350 Gorno-Altaysk;51.9600 Lalmanirhat;25.9167 Hasuda;35.9945 Marano di Napoli;40.9000 Dédougou;12.4667 Legnano;45.5781 Ruislip;51.5760 Ōsakasayama;34.5000 Taal;13.8833 Andoharanofotsy;-18.9750 Mukōchō;34.9500 Nkhotakota;-12.9167 Suresnes;48.8700 Bellevue;48.8710 Tsushima;35.1771 La Habra;33.9282 Anuradhapura;8.3350 Kratie;12.4800 Ban Rangsit;14.0167 Beledweyne;4.7358 Maduraivayal;13.0631 Wallasey;53.4158 Jette;50.8667 Tandag;9.0789 Dar Naim;18.0333 Khartsyzk;48.0333 Montebello;34.0155 Iganga;0.6150 Barrow in Furness;54.1108 North Bergen;40.7938 Saraburi;14.5286 Brookline;42.3243 Rayong;12.6742 Stryi;49.2500 Giurgiu;43.9008 Glew;-34.8833 Fort Portal;0.6544 Saint-Ouen;48.9123 Rovaniemi;66.5000 Bobigny;48.9106 Pico Rivera;33.9901 Kolda;12.8833 Santarém;39.2339 Fussa;35.7333 Napier;-39.4903 Linares;24.8597 Chornomorsk;46.3017 Madang;-5.2167 Aş Şaff;29.5772 Lytkarino;55.5833 Bebington;53.3500 Tirur;10.9000 Kırklareli;41.7347 Berriozábal;16.8003 Ruhengeri;-1.5000 Macclesfield;53.2500 Don Galo;14.5072 Vratsa;43.2000 Yala;6.5425 Puerto Limón;10.0022 Chambéry;45.5700 Braintree;51.8780 Gia Nghĩa;11.9833 Fontenay-sous-Bois;48.8517 Portugalete;43.3194 Suileng;47.2460 Poonamallee;13.0465 The Hammocks;25.6700 Koja;26.3344 Pinagkaisahan;14.5229 Nonoichi;36.5194 Santa Rosa de Copán;14.7667 Coyhaique;-45.5667 Jidd Ḩafş;26.2190 Monterey Park;34.0497 Limerick;52.6653 Leskovac;42.9981 Mislata;39.4750 Sidi Bennour;32.6500 Longxing;35.6091 Raharpur;24.8194 Hunedoara;45.7697 Rafael Calzada;-34.7833 La Mesa;32.7703 Royal Tunbridge Wells;51.1320 At Tawāhī;12.7833 Trang;7.5575 Gardena;33.8943 Ban Bang Kaeo;13.6371 Takaishi;34.5167 Puteaux;48.8850 Antsinanantsena;-18.8333 Kyustendil;42.2833 Revere;42.4189 Medford;42.4234 Oyem;1.6000 Cupertino;37.3168 Lokoja;7.8019 Juigalpa;12.1083 Trujillo;15.9186 Tczew;54.0875 Wellingborough;52.2939 Lorient;47.7500 North Miami;25.9008 Chakapara;22.6300 Temascalcingo;19.9147 Tezonapa;18.6058 Vejle;55.7167 Nausori;-18.0244 Taylorsville;40.6569 Zushi;35.2833 Świdnica;50.8500 Irvington;40.7243 West Allis;43.0068 Bungoma;0.5667 Pinotepa;16.3412 Sidi Yahya Zaer;33.7105 Alfortville;48.8050 Évry;48.6239 Midalt;32.6800 Irákleio;38.0500 Itānagar;27.1000 Užice;43.8500 Union;40.6953 Samut Prakan;13.5897 Antigua Guatemala;14.5667 Suharekë;42.3800 Azrou;33.4417 Klimovsk;55.3667 Samannūd;30.9622 Kasungu;-13.0333 Hod HaSharon;32.15 Ródos;36.4412 Stavroúpoli;40.6667 Tacuarembó;-31.7333 Kronjo;-6.0667 Folkestone;51.0810 Mérida;38.9000 Al Hoceïma;35.2472 Meaux;48.9603 Ocosingo;16.9072 White Plains;41.0220 Hamura;35.7672 Hoboken;40.7452 Pursat;12.5333 Zumpango;19.7969 Royal Leamington Spa;52.2920 Dzerzhinskiy;55.6333 Artemisa;22.8136 Crosby;53.4872 Karīmganj;24.8700 Yevlax;40.6172 Mūndka;28.6794 Cerro de Pasco;-10.6864 Liuhu;35.5449 Rijswijk;52.0456 Sartrouville;48.9372 Clamart;48.8014 Stratford;51.5423 Huamantla;19.3133 Sevran;48.9333 Kruševac;43.5833 South Whittier;33.9336 Mineiros;-17.5689 Qiryat Ata;32.8 Aversa;40.9667 Tenkodogo;11.7833 Dosso;13.0500 Naval;11.5833 Bāruipur;22.3654 Boudouaou;36.7300 Mandapeta;16.8700 Chelles;48.8833 Port Coquitlam;49.2625 Rosh Ha‘Ayin;32.0956 Gllogovc;42.6236 Kitgum;3.2889 Bârlad;46.2167 Dapaong;10.8667 Caridad;14.4828 Novohrad-Volynskyi;50.5833 Babīlā;33.4728 Margate;26.2466 Békéscsaba;46.6790 Chaniá;35.5167 Dayr al Balaḩ;31.4189 Shijōnawate;34.7400 Oak Lawn;41.7139 Legionowo;52.4000 Bhīmunipatnam;17.8846 Carson City;39.1511 Fountainebleau;25.7723 Slavonski Brod;45.1667 Hilden;51.1714 Ampelókipoi;40.6500 Nuevo Casas Grandes;30.4167 Acatzingo;18.9817 Umm el Faḥm;32.5194 Biel/Bienne;47.1333 Kidderminster;52.3885 Lipjan;42.5300 Coconut Creek;26.2803 Karuhatan;14.6883 Boac;13.4500 Mariano Acosta;-34.7167 Altrincham;53.3838 Nacaome;13.5333 Mafeteng;-29.8167 Pansol;14.6514 Elenga;24.3386 Chinhoyi;-17.3497 My Drarga;30.3800 Weymouth;50.6130 Gümüşhane;40.4597 Pithāpuram;17.1167 Caacupé;-25.3861 Melo;-32.3667 Barri;51.4050 Villa Hayes;-25.1000 Belize City;17.4986 Catemaco;18.4167 Miragoâne;18.4458 Fountain Valley;33.7105 Maun;-19.9833 Nikšić;42.7778 Ushuaia;-54.8019 Berwyn;41.8433 Pijijiapan;15.6917 Bagong Pag-Asa;14.6622 Leith;55.9800 San Pedro;25.7578 Esplugas de Llobregat;41.3767 National City;32.6654 Wote;-1.7833 Zalău;47.1911 Tirupparangunram;9.8815 Arcadia;34.1342 Sfântu-Gheorghe;45.8636 Abū Qīr;31.3167 Muğla;37.2167 Évora;38.5667 Villarrica;-25.7500 Madhipura;25.9200 Phra Nakhon Si Ayutthaya;14.3478 Moyobamba;-6.0333 Mocoa;1.1500 Greenford;51.5299 Zouerate;22.7333 Trenčín;48.8919 Rahovec;42.3994 Mendi;-6.1478 Jelgava;56.6483 Saint-Quentin;49.8486 Francisco I. Madero;25.7753 Castelo Branco;39.8167 Rio Tinto;41.1780 San Giorgio a Cremano;40.8333 Quiapo;14.6000 Vigan;17.5747 Mollet;41.5356 Sankt Pölten;48.2000 Massy;48.7309 Zalaegerszeg;46.8392 Fuchū;34.3926 Dunfermline;56.0719 Gallarate;45.6649 Hà Giang;22.8333 Iba;15.3333 Rowley Regis;52.4880 Ohrid;41.1169 Corbeil-Essonnes;48.6139 Florida;-34.5167 Guliston;40.4833 Madīnat Ḩamad;26.1128 New Brunswick;40.4870 Neath;51.6600 M.Ə. Rəsulzadə;40.4344 Bootle;53.4457 Skien;59.2081 Hamilton;55.7770 Marikina Heights;14.6534 Lautoka;-17.6242 Huntington Park;33.9800 Ercolano;40.8000 Cakung;-6.2507 Buta;2.8000 Vaulx-en-Velin;45.7768 Amalāpuram;16.5787 Lancaster;54.0489 Ibiza;38.9089 Lampang;18.3000 Vranje;42.5542 Tanjombato;-18.9500 Eltham;51.4510 Cagnes-sur-Mer;43.6644 Perth Amboy;40.5202 Bādurpalle;17.5468 Shiogama;38.3144 Al Karnak;25.7186 Cintalapa de Figueroa;16.6978 Kyaliwajjala;0.3800 Gabrovo;42.8667 Gutao;37.1989 Padangpanjang;-0.4500 Mikkeli;61.6833 Chake Chake;-5.2395 Morden;51.4015 Cumbernauld;55.9450 Choisy-le-Roi;48.7630 Brentwood;51.6200 Muban Saeng Bua Thong;13.9424 Tamiami;25.7556 Arwal;25.2500 Ruma;45.0000 Gjilan;42.4647 Bayonne;43.4900 Willenhall;52.5798 Westchester;25.7471 La Jagua de Ibirico;9.5667 Louangphabang;19.8833 Shiraoka;36.0191 Andover;51.2080 Plainfield;40.6154 Prachuap Khiri Khan;11.8167 Oak Park;41.8872 Gao;16.2667 Tulcán;0.8117 Korçë;40.6167 Paramount;33.8977 Muktāgācha;24.7662 Al Ma‘allā’;12.7897 Aspen Hill;39.0927 Sherpur;24.6650 Rosny-sous-Bois;48.8667 Ampitatafika;-18.9333 Sakon Nakhon;17.1564 Alajuela;10.1640 Qalqīlyah;32.1903 Kabale;-1.2500 Liberia;10.6333 Marondera;-18.1897 Yeovil;50.9452 Moskovskiy;55.6000 Nes Ẕiyyona;31.9333 San Vicente;13.6453 Cologno Monzese;45.5286 Kendale Lakes;25.7081 Sutton in Ashfield;53.1250 El Ghâzîyé;33.5186 Santo Cristo;14.6603 Noisy-le-Sec;48.8894 Scafati;40.7536 Rho;45.5333 Hrazdan;40.5000 Targovishte;43.2500 Buena Vista Tomatlán;19.2102 Maha Sarakham;16.1772 Valladolid;20.6894 Jiménez;27.1300 Boujad;32.7667 Chahār Dangeh;35.6031 Takeo;10.9833 Alytus;54.4014 Bodø;67.2827 Santurce-Antiguo;43.3303 Tam Hiệp;10.9497 Bang Bua Thong;13.9099 Aloha;45.4920 Gennevilliers;48.9333 Chong Nonsi;13.6965 Catford;51.4452 Myrnohrad;48.2911 Iwakura;35.2794 Levittown;40.7241 Elmshorn;53.7519 West New York;40.7857 Noveleta;14.4333 Takahama;34.9275 Bloomfield;40.8098 Collegno;45.0775 Gopālpur;24.5583 Jinotepe;11.8472 Ain El Aouda;33.8111 La Garenne-Colombes;48.9056 Suzukawa;35.3730 Lamía;38.9000 Mangochi;-14.4667 Qiryat Ono;32.0636 Lənkəran;38.7536 Vila Real;41.2958 Kardzhali;41.6500 Loreto;22.2667 Placentia;33.8807 Aliso Viejo;33.5792 Choma;-16.7711 Pen-y-Bont ar Ogwr;51.5072 Cojutepeque;13.7167 Livry-Gargan;48.9192 Wheaton;39.0492 Ksar Hellal;35.6429 Teplice;50.6444 Edenvale;-26.1411 Eger;47.8990 Ḩarastā;33.5667 Rosemead;34.0689 Kunnamkulam;10.6500 Jihlava;49.4003 Whangarei;-35.7250 Sombor;45.7833 Qasbat Tadla;32.6000 Florin;38.4832 Upplands Väsby;59.5167 Binondo;14.6000 Tizimín;21.1425 Kendrāparha;20.5000 Surbiton;51.3940 Tlapa de Comonfort;17.5461 Kasuya;33.6108 Guozhen;34.3668 Puttalam;8.0330 Stretford;53.4466 Cuetzalan;20.0333 Salekhard;66.5333 Sar-e Pul;36.2214 Evere;50.8667 Garges-lès-Gonesse;48.9728 Jūrmala;56.9681 Cheshunt;51.7020 Komotiní;41.1000 Country Club;25.9407 Rosso;16.5128 Covina;34.0903 Levakant;37.8667 La Courneuve;48.9322 Halle-Neustadt;51.4789 Bastia;42.7008 Changanācheri;9.4667 Bodupāl;17.4139 Longchamps;-34.8500 Plainview;14.5777 Barendrecht;51.8500 Skenderaj;42.7467 Jarash;32.2723 Bangued;17.5965 Lakewood;41.4822 Beni Yakhlef;33.6555 La Goulette;36.8181 Barnet;51.6444 Gori;41.9817 Kirkcaldy;56.1107 Abovyan;40.2739 Colcapirhua;-17.4167 Bagneux;48.7983 Clondalkin;53.3203 North Bethesda;39.0393 Azemmour;33.2878 Sutton;51.3656 Gbadolite;4.2750 West Bridgford;52.9320 Maḩmūd-e Rāqī;35.0206 Guadalupe Nuevo;14.5587 Meudon;48.8123 Lissone;45.6167 Colonia del Sol;22.9125 Villa Alsina;-34.6667 Beckenham;51.4080 Luwero;0.8331 Vidin;44.0000 Ma‘ān;30.1962 Gracias;14.5833 Östersund;63.1792 Selibe Phikwe;-21.9758 Al Balyanā;26.2333 Tlalmanalco;19.2044 Cypress;33.8171 Tubod;8.0500 Bagnolet;48.8692 Campobasso;41.5667 Merksem;51.2428 Al Khārjah;25.4400 Ashton;53.4897 Kotelniki;55.6617 Nichelino;44.9955 Semera;11.7922 Paderno Dugnano;45.5719 El‘ad;32.0522 Świętochłowice;50.2919 Pākaur;24.6300 Mairena del Aljarafe;37.3333 Yumbe;3.4650 Mpigi;0.2300 Kangar;6.4333 Manali;13.1667 Belfort;47.6400 Huancavelica;-12.7864 Dollard-des-Ormeaux;45.4833 Cerritos;33.8678 Jbaïl;34.1236 Strood;51.3930 Luleå;65.5844 Couva;10.4167 Inverness;57.4778 Waterford;52.2567 Figueras;42.2667 San Felíu de Llobregat;41.3833 Salisbury;51.0700 Ramat HaSharon;32.15 Kaédi;16.1506 Ayr;55.4580 Meoqui;28.2722 San Isidro;-34.4667 Cantel;14.8112 Isla;18.0292 Corroios;38.6147 Châtillon;48.8000 Xam Nua;20.4150 José Mármol;-34.7833 Wokingham;51.4100 Karlovy Vary;50.2306 Banbury;52.0610 Talence;44.8000 Bihāt;25.4253 Pejë;42.6603 Rye;41.0075 Santo Niño;14.5033 Schagen;52.7833 Mantes-la-Jolie;48.9908 Rovenky;48.0833 Asadābād;34.8742 Biləcəri;40.4314 Nellikkuppam;11.7667 Middelburg;51.5000 Tulum;20.2119 Gobārdānga;22.8700 Krasnoznamensk;55.6008 Llavallol;-34.7667 Nueva Loja;0.0847 Shertallai;9.6869 Seregno;45.6500 Kita;13.0504 Serowe;-22.3833 Mandeville;18.0333 Birendranagar;28.6000 Bakau;13.4833 Nong Khai;17.8681 Bindura;-17.3000 North Highlands;38.6713 Isiolo;0.3500 Antelope;38.7153 Mercedes;-33.2500 Everett;42.4064 University;28.0771 Sijua;23.7692 Borgerhout;51.2117 Malakoff;48.8169 Urmston;53.4487 Madīnat ‘Īsá;26.1736 Villa de Zaachila;16.9508 Inuma;36.0001 Álimos;37.9167 Faya;17.9169 Havant;50.8517 Kanye;-24.9833 Torre Annunziata;40.7569 Chalon-sur-Saône;46.7806 Leribe;-28.8734 Hinckley;52.5413 Njombe;-9.3333 La Mirada;33.9025 Ripollet;41.4969 Kanash;55.5069 Melito di Napoli;40.9167 Chalungalpādam;9.6896 Mamburao;13.2233 Ksar;18.1022 Worksop;53.3042 Morley;53.7492 Invercargill;-46.4131 Welling;51.4594 Al ‘Ayyāţ;29.6167 Kerkrade;50.8667 Ząbki;52.2928 Karmiel;32.9136 New Amsterdam;6.2500 Ḩajjah;15.6950 Charenton-le-Pont;48.8265 Kokkola;63.8367 Tepalcatepec;19.1833 Inđija;45.0500 Puerto Lempira;15.2653 Hammam-Lif;36.7333 Caluire-et-Cuire;45.7953 Bang Kruai;13.8042 Dock Sur;-34.6417 Le Cannet;43.5769 Chitré;7.9667 Badulla;6.9847 Nagykanizsa;46.4550 Naj‘ Ḩammādī;26.0500 Cuilapa;14.2783 San Adrián de Besós;41.4305 Santiago;8.1004 Lindi;-9.9969 Al Badārī;26.9925 Rāmachandrapuram;16.8500 Mrirt;33.1667 Fleet;51.2834 Middleton;53.5550 Aweil;8.7800 Bhārella;23.5506 Bois-Colombes;48.9175 Murshidābād;24.1800 Nelson;-41.2708 Dori;14.0300 Valenciennes;50.3581 Tōgō;35.0966 Yuchengcun;23.5633 Artigas;-30.4667 Coatbridge;55.8625 Šibenik;43.7350 Daijiazhuang;38.1345 Cobija;-11.0333 Bron;45.7394 Chorley;53.6530 Hammam Sousse;35.8589 Farim;12.4833 Ben Zakkay;31.8558 Vanves;48.8208 Slobozia;44.5639 Mafamude;41.1152 Kumatori;34.4014 Santa María Atzompa;17.0794 Vilvoorde;50.9281 Donggangli;39.9733 Arlington;42.4187 Rezé;47.1917 Kafr al Kurdī;31.1429 Fareham;50.8500 Melun;48.5406 Rozzano;45.3833 Igualada;41.5814 Gbarnga;6.9980 Jerada;34.3117 Alexandria;43.9686 Diamond Harbour;22.1910 Jáltipan de Morelos;17.9703 Palayan City;15.5422 Castleford;53.7160 Claypole;-34.8000 Thun;46.7667 Stains;48.9500 Baclaran;14.5319 Hackensack;40.8891 Yambio;4.5650 Kingston upon Thames;51.4103 Berchem;51.1833 Arendal;58.4608 Pinneberg;53.6333 Nueva Gerona;21.8847 Tindouf;27.6753 Sykiés;40.6500 Bluefields;12.0139 Tarbes;43.2300 Bellinzona;46.1954 Pattani;6.8664 Gagny;48.8833 Lecherías;10.1889 Boulogne-sur-Mer;50.7264 Trelleborg;55.3667 Actopan;19.5036 Dhāka;26.7200 Bắc Kạn;22.1333 Alcantarilla;37.9722 Heroica Ciudad de Tlaxiaco;17.2077 Arras;50.2920 Cleveland Heights;41.5113 Newbury;51.4010 Coyotepec;19.7756 Inongo;-1.9500 Bridgwater;51.1280 Hoboken;51.1667 Ādwa;14.1667 Teoloyucan;19.7442 Concepción;-23.4000 Whitney;36.1008 Pinyahan;14.6400 Varkkallai;8.7340 Port Loko;8.7667 Dunaújváros;46.9806 Wan Tau Tong;22.4423 Nogent-sur-Marne;48.8375 Razgrad;43.5333 Desio;45.6167 Salvatierra;20.2156 Zugdidi;42.5083 Mochudi;-24.4167 Qiryat Bialik;32.8333 El Aïoun;34.5852 Bakhtiyārpur;25.4667 North Lauderdale;26.2113 Boumerdes;36.7603 Tinnanūr;13.1145 Umm al Qaywayn;25.5533 Xonobod;40.8000 Ágioi Anárgyroi;38.0267 Salem;42.5129 Duncan;48.7787 Iten;0.6731 Laindon;51.5740 Rohnert Park;38.3479 Palladam;10.9900 Kapan;39.2011 Paracho de Verduzco;19.6500 Villaflores;16.2355 Empalme;27.9617 Freeport;40.6515 Katsuren-haebaru;26.1911 Caerphilly;51.5780 Llanelli;51.6840 Baler;15.7583 Trujillo Alto;18.3599 Ingeniero Pablo Nogués;-34.4667 Nueva Rosita;27.9390 La Paz;14.3169 Krus na Ligas;14.6442 Desamparados;9.8967 Oakland Park;26.1780 Kalmar;56.6614 Wilkes-Barre;41.2469 Sukuta;13.4167 Bushenyi;-0.5417 Caloundra;-26.7986 Guruvāyūr;10.5947 Ōizumi;36.2478 Temsia;30.3600 Kirkby;53.4800 Campbell;37.2802 Wattrelos;50.7000 Annemasse;46.1958 Playa Vicente;17.8333 Beverwijk;52.4833 Matale;7.4667 Ermezinde;41.2170 Buli;14.4430 Le Kremlin-Bicêtre;48.8100 Veles;41.7153 Or Yehuda;32.0333 Haedo;-34.6500 San Bruno;37.6256 Gambēla;8.2500 Štip;41.7358 Murzuq;25.9000 Drogheda;53.7150 Shefar‘am;32.8056 Cacém;38.7704 Concord;43.2305 Mińsk Mazowiecki;52.1833 Munro;-34.5333 Sagauli;26.7833 Greenacres;26.6270 Koboko;3.4100 Lai Châu;22.3992 Franconville;48.9889 Ramsgate;51.3360 Porur;13.0356 Hódmezővásárhely;46.4304 Small Heath;52.4629 Kampong Chhnang;12.2500 North Miami Beach;25.9302 Pallisa;1.1675 Uman;20.8833 Silistra;44.1172 Juan Rodríguez Clara;18.0000 Aziylal;31.9669 Kalutara;6.5869 Pomigliano d’Arco;40.9167 Svay Rieng;11.0833 Armavir;40.1500 Kwīhā;13.4769 Cleethorpes;53.5533 Kula;45.6000 Minas;-34.3667 Hicksville;40.7637 Jefferson City;38.5676 Woonsocket;42.0010 Bishops Stortford;51.8720 Garbahaarrey;3.3500 Balintawak;14.6506 Ban Sai Ma Tai;13.8444 Coalville;52.7240 Blyth;55.1260 Saronno;45.6255 Phulwāria;25.4697 Dubrovnik;42.6403 Blanes;41.6740 Ciudad Melchor Múzquiz;27.8775 Quinhámel;11.8833 Dayr Mawās;27.6333 Melmadai;9.9264 Tonalá;16.0914 Temascal;18.2394 Guarda;40.5333 Si Sa Ket;15.1069 Fribourg;46.8000 Saint-Martin-d’Hères;45.1672 Kenton;51.5878 Leighton Buzzard;51.9165 Kampot;10.6000 Arnold;53.0050 Miercurea-Ciuc;46.3594 Berat;40.7000 Ait Ourir;31.5644 Le Perreux-Sur-Marne;48.8422 Myrhorod;49.9667 Bambari;5.7653 Ardahan;41.1111 Zagora;30.3306 Le Pré-Saint-Gervais;48.8850 Qapshaghay;43.8844 Nebbi;2.4792 Ilkeston;52.9710 Poissy;48.9294 Żyrardów;52.0500 Nakama;33.8208 San José;-34.3333 Villafranca del Panadés;41.3447 Matoupu;38.3198 Aberdare;51.7130 Viborg;56.4333 Xico;19.4170 Lālganj;25.8700 Kahama;-3.8375 Cesano Maderno;45.6307 Cachan;48.7919 Madipakkam;12.9623 Savigny-sur-Orge;48.6797 Douai;50.3714 Herne Bay;51.3700 Santa Bárbara;14.9167 Annandale;38.8324 Placilla de Peñuelas;-33.1156 Amudālavalasa;18.4167 Embu;-0.5389 Puntarenas;9.9667 Biougra;30.2144 Narathiwat;6.4167 Mugnano di Napoli;40.9094 Échirolles;45.1436 Kenge;-4.8056 Tuvāgudi;10.7564 Kānkuria;24.6580 Mambajao;9.2500 Zghartā;34.4000 Ciampino;41.8000 Zacatelco;19.2167 Pujali;22.4679 Morshansk;53.4500 Villa Adelina;-34.5175 Nola;3.5333 Arzano;40.9167 Lusambo;-4.9729 Sayula de Alemán;17.8833 Villepinte;48.9550 Ngozi;-2.9083 Burjasot;39.5064 Teaneck;40.8900 Bicester;51.9000 Marcq-en-Baroeul;50.6711 Kisii;-0.6833 Chittaranjan;23.8700 Rivas;11.4386 Corsico;45.4333 Vredenburg;-32.9000 Neuilly-sur-Marne;48.8537 Wilrijk;51.1667 Oshakati;-17.7833 Panaji;15.4989 Malbork;54.0333 Calpulalpan;19.5869 Wood Green;51.5981 Famagusta;35.1250 Romblon;12.5500 Denton;53.4554 Puerto Francisco de Orellana;-0.4625 Hallandale Beach;25.9854 Zacatlán;19.9319 Binəqədi;40.4661 Nallūr;11.1003 Highbury;51.5520 Walkden;53.5239 Miyoshidai;35.8284 Shengli;37.9842 Cananea;30.9819 Al Bayḑā’;13.9790 Chartres;48.4560 Pärnu;58.3833 Şəmkir;40.8297 Whitley Bay;55.0456 Koekelberg;50.8667 Ez Zahra;36.7439 Bletchley;51.9940 Impfondo;1.6333 Paravūrkambolam;8.8168 Culver City;34.0058 Talas;42.5167 Ban Na Pa;13.3956 Dún Dealgan;54.0090 Annapolis;38.9706 Billingham;54.6100 Mariano Escobedo;18.9167 Lovech;43.1347 Montclair;40.8253 Airdrie;55.8600 Bjelovar;45.9000 Sensuntepeque;13.8667 Grugliasco;45.0680 Beeston;52.9270 Bouar;5.9500 Soccorro;14.6179 Long Eaton;52.8980 Montana;43.4075 Camberley;51.3350 Valley Stream;40.6647 Agualva;38.7700 L’Haÿ-les-Roses;48.7800 Sidi Yahia El Gharb;34.3058 Dunstable;51.8860 Houilles;48.9261 El Golea;30.6000 Luebo;-5.3500 Swords;53.4597 Port-Margot;19.7500 Villefranche-sur-Saône;45.9833 Kanie;35.1322 Chachoengsao;13.6903 Phônsavan;19.4600 Xaçmaz;41.4708 Chelsea;42.3959 North Shields;55.0097 Brčko;44.8783 Tumba;59.2000 Bugiri;0.5694 Waipahu;21.3859 Pierrefitte-sur-Seine;48.9656 Samrong;13.6421 Koumra;8.9100 Surin;14.8864 Sainte-Geneviève-des-Bois;48.6369 Jordan;10.6000 Bell Gardens;33.9663 Bentota;6.4200 Massawa;15.6097 Athis-Mons;48.7074 San Roque;14.4800 Nueva Italia de Ruiz;19.0194 Effia-Kuma;4.9167 Santa Elena;-2.2267 Lincoln Park;42.2432 Pioltello;45.5000 Aventura;25.9566 Fort Lee;40.8509 Ālamat’ā;12.4167 San Gabriel;34.0949 Vernier;46.2000 Les Lilas;48.8800 Sant’Antimo;40.9422 Wickford;51.6114 Āzezo;12.5586 San Juan Despí;41.3668 Touggourt;33.1000 Kālihāti;24.3833 Dáfni;37.9500 Miahuatlán de Porfirio Díaz;16.3327 Park Ridge;42.0125 Florida;-34.1000 Paravūr Tekkumbhāgam;8.8110 Celje;46.2358 Cabaret;18.7343 Sareh Mowndeh;13.4000 Châtenay-Malabry;48.7653 Redcar;54.6180 Kaffrine;14.1014 Pánuco;22.0500 Creil;49.2583 Lydenburg;-25.0960 Northolt;51.5467 Herohalli;12.9911 Bridlington;54.0819 Olteniţa;44.0867 Hanwell;51.5090 Istog;42.7833 Bezons;48.9261 Taibao;23.4500 Premiá de Mar;41.4920 Bollate;45.5500 Conflans-Sainte-Honorine;48.9992 Durazno;-33.3667 El Hajeb;33.6928 Ban Doi Suthep;18.7944 Varaždin;46.3081 Keizer;45.0028 Falkirk;56.0011 Bugembe;0.4675 Fāraskūr;31.3297 Tromsø;69.6828 Zinacantán;16.7601 Nyköping;58.7531 Abasolo;20.4511 Villeneuve-Saint-Georges;48.7325 Ayutuxtepeque;13.7356 Romainville;48.8840 Le Plessis-Robinson;48.7811 Zaïo;34.9333 Lambaréné;-0.6883 Mission Bend;29.6948 Esch-sur-Alzette;49.4969 Tuyên Quang;21.8167 Vrilíssia;38.0333 Cholargós;38.0000 Adjumani;3.3772 Bərdə;40.3744 Palaiseau;48.7145 Casalecchio di Reno;44.4833 Dover;51.1295 Riacho de Santana;-13.6089 Tunceli;39.1064 Klinë;42.6167 Bucha;50.5486 La Presa;32.7110 Agía Varvára;38.0000 Schiltigheim;48.6078 La Puente;34.0323 Wołomin;52.3500 Tomatlán;19.9333 Escuinapa;22.9822 Argyroúpoli;37.9000 Choybalsan;48.0783 Hitchin;51.9470 Shangzhuangcun;23.5226 Am-Timan;11.0333 Rainham;51.3600 Massamá;38.7568 Brugherio;45.5508 Trujillo;9.4170 Kranj;46.2333 Spalding;52.7858 Stanton;33.8003 Attingal;8.6800 Dighwāra;25.7443 Kikinda;45.8333 Zeghanghane;35.1500 East Meadow;40.7197 Brzeg;50.8667 Thonon-les-Bains;46.3627 La Huacana;18.9625 Ali Sabieh;11.1500 Limbiate;45.5972 Yihezhuang;39.1373 Villemomble;48.8833 Belleville;40.7950 Northglenn;39.9108 Hoogvliet;51.8667 Montclair;34.0715 Mannar;8.9772 Reforma;17.8658 Grantham;52.9180 Pandaul;26.2517 Falun;60.6072 Kajaani;64.2311 Tenosique;17.4756 Salto del Guairá;-24.0200 Richmond West;25.6105 Villagrán;20.5170 Soteapan;18.2333 Gabú;12.2833 San Carlos;11.1236 Hanover Park;41.9818 Ōharu;35.1751 Carshalton;51.3652 Rukungiri;-0.7900 Harima;34.7167 Schaffhausen;47.7000 Bửu Long;10.9600 Salt;41.9761 Pāppinisshēri;11.9500 Nanchital de Lázaro Cárdenas del Río;18.0667 Aizumi;34.1266 Foothill Farms;38.6867 Tozeur;33.9167 Bečej;45.6167 Madukkarai;10.9057 Ewell;51.3500 Ataq;14.5364 Fusō;35.3591 Colwyn Bay;53.2900 Mabole;7.0000 Banī Suhaylā;31.3428 Huyton;53.4111 Bossangoa;6.4833 Bibhutpur;25.6878 Calumpang;14.6249 Saint-Mandé;48.8422 Alacuás;39.4583 Putla Villa de Guerrero;17.0321 Senhora da Hora;41.1860 Prijepolje;43.5439 Pinner;51.5932 Al Qunayţirah;33.1256 Nishihara;26.2228 Chaiyaphum;15.8056 Riccione Marina;44.0000 Deal;51.2226 Kendall West;25.7065 Pāppākurichchi;10.8137 Motozintla;15.3632 Nzega;-4.2169 Pontoise;49.0516 Letchworth;51.9780 Cernusco sul Naviglio;45.5167 Richfield;44.8763 Marijampolė;54.5472 Maassluis;51.9333 Ometepec;16.6833 Ticul;20.3953 Kearns;40.6519 Rumonge;-3.9667 Pak Tin Pa;22.3364 Hyde;53.4474 Parras de la Fuente;25.4403 Abingdon;51.6670 Fresnes;48.7550 San Vicente;9.9636 Ratchaburi;13.5356 Elmont;40.7033 Paso del Macho;18.9667 Paso de Ovejas;19.2850 Pardigūda;17.3974 Borehamwood;51.6578 Tit Mellil;33.5533 Zapotiltic;19.6270 Trowbridge;51.3200 Wāris Alīganj;25.0100 Angri;40.7333 Ebebiyín;2.1500 Earley;51.4330 Temple City;34.1022 Rutherglen;55.8280 Wigston Magna;52.5812 Chillum;38.9666 Butajīra;8.1208 Ixtapan de la Sal;18.8333 Göyçay;40.6531 Póvoa de Santa Iria;38.8620 Bangaon;25.8673 Hillerød;55.9333 San Miguel;14.6000 Winchester;36.1365 Purral;9.9594 Buchanan;5.8808 Tancítaro;19.3384 Chur;46.8500 Clichy-sous-Bois;48.9102 Chatou;48.8897 Yehud;32.0333 San Juan Evangelista;17.8833 Amecameca de Juárez;19.1238 Chāvakkād;10.5890 South Miami Heights;25.5886 Lichfield;52.6820 Mangalam;10.8450 Prestwich;53.5333 Boende;-0.2810 Krong Kep;10.4875 Egypt Lake-Leto;28.0177 Minamishiro;36.0227 Kornwestheim;48.8598 Shanawān;30.5031 Rodolfo Sánchez Taboada;31.7958 Kamenicë;42.5839 Roanne;46.0367 Ermont;48.9922 Gostivar;41.8000 Byumba;-1.5761 Didcot;51.6060 Les Mureaux;48.9875 Viry-Châtillon;48.6713 Buenaventura Lakes;28.3349 Westmont;33.9417 Dāganbhuiya;22.9127 Lauderdale Lakes;26.1682 Ecclesfield;53.4429 Beja;38.0333 Vršac;45.1167 Cradock;-32.1833 Agen;44.2049 Sint-Joost-ten-Node;50.8500 Champotón;19.3500 Ambano;-19.8000 Turkauliyā;26.6079 Kaippakanchēri;10.9380 Villaricca;40.9167 Darwen;53.6980 Chuhuiv;49.8372 Aosta;45.7333 Fontenay-aux-Roses;48.7893 Saintard;18.8240 Kōryō;34.5500 West Hollywood;34.0883 Suong;11.9167 Paidha;2.4167 Project Six;14.6561 Ganshoren;50.8667 Norristown;40.1225 Manhattan Beach;33.8894 Barangka;14.6297 Timbuktu;16.7733 Villiers-sur-Marne;48.8275 Villanueva;22.3536 Pabellón de Arteaga;22.1500 Luboń;52.3333 Roi Et;16.0531 Mỹ Hòa;10.3655 Thiais;48.7650 Laḩij;13.0500 Savalou;7.9333 Gisborne;-38.6625 Sestao;43.3108 Metamórfosi;38.0500 Kaisarianí;37.9683 Bragança;41.8067 Phatthalung;7.5000 Sānkrāil;22.5700 Montigny-le-Bretonneux;48.7711 Neuchâtel;47.0000 Midvale;40.6148 Kartārpur;31.4427 Frattamaggiore;40.9333 Papendrecht;51.8333 Gentilly;48.8133 Ban Bang Krang;13.8422 Mechraa Bel Ksiri;34.5600 Pallijkarani;12.9333 San Salvador El Seco;19.1333 Qazax;41.0933 Bria;6.5369 San Donato Milanese;45.4167 Watertown Town;42.3700 Elesvaram;17.2833 Magdalena de Kino;30.6167 Bexleyheath;51.4590 Bussum;52.2833 Ban Ang Sila;13.3364 Maīdān Shahr;34.3972 Artvin;41.1833 Giv‘at Shemu’él;32.0781 Néa Filadélfeia;38.0367 Barguna;22.1500 Trappes;48.7775 Vigneux-sur-Seine;48.7001 San Pedro de Ycuamandiyú;-24.1000 Ibrā’;22.6833 Dikhil;11.1167 Awbārī;26.5833 Pinhal Novo;38.6310 Ocotal;13.6336 Beidaying;39.9686 Brighouse;53.7070 College Park;38.9960 Saint Neots;52.2280 Santa Maria Capua Vetere;41.0833 University City;38.6657 Motherwell;55.7839 Ciudad Sabinas Hidalgo;26.5000 San Rafael Abajo;9.8943 Dragash;42.0611 Naryn;41.4333 Fair Lawn;40.9359 Long Beach;40.5887 San Martin De Porres;14.4817 Qornet Chahouâne;33.9200 Escárcega;18.6067 Metepec;19.2511 Aldaya;39.4639 Pontypridd;51.6020 Busia;0.4633 Kendal;54.3260 Kalasin;16.4342 Fort Liberté;19.6678 Chamtha;25.5789 Huatabampo;26.8275 Chichester;50.8365 Smolyan;41.5833 Isingiro;-0.7950 Swakopmund;-22.6833 Montgomery Village;39.1788 Homa Bay;-0.5167 Fair Oaks;38.8653 Merauke;-8.4932 Nu‘ayjah;25.2525 Wishaw;55.7742 Azogues;-2.7333 Ostróda;53.7000 Perry Barr;52.5249 Lens;50.4322 La Unión;13.3369 West Ham;51.5340 Swadlincote;52.7740 Bulwell;53.0010 Šid;45.1167 Sliema;35.9122 Singia;25.8424 Le Chesnay;48.8203 Kararān;30.7720 Eastchester;40.9536 Bor;44.1303 Vandiyūr;9.9092 Fushë Kosovë;42.6300 Eastpointe;42.4657 Câmpulung;45.2678 Frome;51.2279 Uttaradit;17.6231 Barros Blancos;-34.7542 Lidingö;59.3667 Zumpango del Río;17.6500 Shariff Aguak;6.8647 Keşlə;40.3978 Grigny;48.6562 Zhaoyu;37.3512 Huntington Station;40.8446 Kampong Speu;11.4520 Kakata;6.5300 Moscháto;37.9500 Bresso;45.5333 Ventspils;57.3906 Saint-Cloud;48.8400 Tambo;14.5164 Sidi Smai’il;32.8167 Dandenong;-37.9810 Ciudad Sahagun;19.7714 Puyo;-1.4861 Lohāgāra;22.0181 North Providence;41.8616 Golden Glades;25.9129 Bell;33.9801 City of Orange;40.7681 Hendrik-Ido-Ambacht;51.8500 Somoto;13.4842 Balangkas;14.7381 Mount Lebanon;40.3752 Kovin;44.7500 Koh Kong;11.6167 Goussainville;49.0325 Nauāgarhi;25.3443 Nagtala;22.3693 Bègles;44.8086 San Giuseppe Vesuviano;40.8333 Chumphon;10.4939 Cabarroguis;16.5103 Sursand;26.6500 Wik’ro;13.7833 Kericho;-0.3692 Banlung;13.7468 Diemen;52.3333 Longton;52.9877 Mandera;3.9167 Foster City;37.5553 Englewood;39.6468 Curridabat;9.9160 Kaçanik;42.2467 Ziniaré;12.5833 Glendale Heights;41.9196 Sisak;45.4872 Bromsgrove;52.3353 Paiporta;39.4278 Birkirkara;35.8967 Gérakas;38.0333 Baldwin;40.6511 Pont-y-pŵl;51.7030 Dana Point;33.4733 Kyrenia;35.3403 Bent Jbaïl;33.1258 Mažeikiai;56.3167 West Little River;25.8571 Mukdahan;16.5431 Adrogue;-34.8000 Vandœuvre-lès-Nancy;48.6567 Ris-Orangis;48.6537 Dedza;-14.3667 Agblangandan;6.3667 Pothuhera;7.4199 Kurunegala;7.4833 San Juan;9.9609 Périgueux;45.1929 Timimoun;29.2500 Montfermeil;48.9000 Wahga;31.6047 Bang Phongphang;13.6791 Tarakeswar;22.8900 Sotteville-lès-Rouen;49.4092 Spring Valley;41.1151 Pilar;-26.8700 San Carlos;-34.8000 Dagestanskiye Ogni;42.1167 Welk’īt’ē;8.2833 Beverly Hills;34.0786 Aix-les-Bains;45.6885 Aci Catena;37.6000 Harper;4.3667 Vilāngudi;9.9458 Chester;39.8456 Rillieux-la-Pape;45.8214 Kamateró;38.0597 Uniondale;40.7176 Cramlington;55.0820 Barañáin;42.8000 Liévin;50.4228 Arfoud;31.4361 Krimpen aan den IJssel;51.9167 Menton;43.7750 Boscombe;50.7250 Narapalli;17.4875 Durango;43.1689 Kitui;-1.3667 Nethirimangalam;10.8000 Salgótarján;48.1040 Oullins;45.7150 Harpenden;51.8175 Sabalpur;25.6053 Hertford;51.7966 Kokhma;56.9311 I-n-Salah;27.1936 Soledad de Doblado;19.0447 Kiryas Joel;41.3411 Savigny-le-Temple;48.5841 Garfield;40.8791 Leticia;-4.2167 Rury;51.2386 Côte-Saint-Luc;45.4687 Demnat;31.7311 Yverdon-les-Bains;46.7785 Franklin Square;40.7002 Navan;53.6528 Bourg-la-Reine;48.7796 Bou Arfa;32.5309 Álamo;20.9167 Darlaston;52.5708 Marhaura;25.9700 Bangassou;4.7374 Anosipatrana;-18.9333 Yerres;48.7171 Villiers-le-Bel;49.0094 Juneau;58.4546 Chachapoyas;-6.2167 Cuitzeo del Porvenir;19.9686 Kulat;-8.8243 Rumbek;6.8060 Les Pavillons-sous-Bois;48.9000 El Salto;23.7823 Sannois;48.9722 Kiryandongo;1.9525 San Andrés de la Barca;41.4478 Helena;46.5965 Saint-Laurent-du-Var;43.6680 Maranga;25.7592 Lawndale;33.8884 Chon Buri;13.3611 Nibria;22.6100 Kampong Thom;12.7000 Molde;62.7375 Stepney;51.5152 Actopan;20.2681 San Pablo;37.9629 Teapa;17.5483 Ulundi;-28.3350 Pompeu;-19.2239 Zug;47.1681 Kaita;34.3722 Mirzāpur;24.1083 Benetúser;39.4250 Lambersart;50.6500 Limeil-Brévannes;48.7464 Navolato;24.7656 Ithaca;42.4442 Rumuruti;0.2600 Atlatlahucan;18.9350 Jérémie;18.6500 Daventry;52.2578 Babati;-4.2167 Shumerlya;55.5333 Guyancourt;48.7714 Long Branch;40.2965 Santa Ana;14.5277 Mehdya;34.2597 Mchinji;-13.8167 Cambuslang;55.8190 Giżycko;54.0400 Villeneuve-la-Garenne;48.9372 Ibanda;-0.1347 Contla;19.3333 Khorugh;37.4833 Vleuten;52.1081 Kuli;24.7366 Banqiao;25.0143 Chato;-2.6378 Ipu;-4.3257 Shek Wai Kok;22.3753 Voúla;37.8500 Bay Shore;40.7288 Beldānga;23.9300 Saranga;22.5400 Zaqatala;41.6336 Pando;-34.7167 Blackrock;53.3015 San Pablo;9.9918 İmişli;39.8697 Siliana;36.0819 Port Chester;41.0051 San Fernando;24.8504 Szekszárd;46.3560 Rhyl;53.3210 Bir Jdid;33.3737 Cheung Chau;22.2106 Castaños;26.7833 Wete;-5.0567 Péfki;38.0667 Sterling;39.0052 San Vicente dels Horts;41.3932 Nāravārikuppam;13.1913 Koprivnica;46.1500 Břevnov;50.0833 Oceanside;40.6328 Leyton;51.5700 Tuxpan;19.5661 Burlingame;37.5859 Arcueil;48.8075 Ystad;55.4167 Ashington;55.1810 Congleton;53.1620 Eaubonne;48.9922 Soissons;49.3817 Mirganj;26.3638 Aïn Taoujdat;33.9333 West Falls Church;38.8648 Diffa;13.3171 Lomme;50.6455 Ixhuatlancillo;18.9000 Dieppe;49.9200 Desnogorsk;54.1500 Udomlya;57.8833 Sabirabad;40.0128 Kayunga;0.7033 Martorell;41.4744 Ridley;39.8854 Mégrine;36.7667 Tung Tau Tsuen;22.3334 Ungheni;47.2167 Trípoli;37.5167 Catarroja;39.4028 Čakovec;46.3858 Vīrapāndi;11.0625 Longbridge;52.3950 Bregenz;47.5050 Bearsden;55.9192 Parkville;39.3832 Siraha;26.6528 San Carlos;37.4982 Melton Mowbray;52.7661 Avellaneda;-34.6625 Santa Paula;34.3545 Moyale;3.5270 Qiman al ‘Arūs;29.3005 Jamay;20.2944 Farnworth;53.5452 Tuxpan;21.8667 Simri Bakhriārpur;25.7216 Krabi;8.0592 Carouge;46.1833 Miami Lakes;25.9125 Puerto Escondido;15.8619 Lamu;-2.2694 Kefar Yona;32.3171 Ninomiya;35.2995 Āsosa;10.0667 Nāsriganj;25.0500 Salyan;39.5950 Shenley Brook End;52.0090 Tomares;37.3764 Tecax;20.2019 San Lorenzo;37.6733 Heemstede;52.3500 Point Fortin;10.1667 Boscoreale;40.7667 Camaligan;13.6208 Kantai;26.2142 Kętrzyn;54.0833 Jaynagar-Majilpur;22.1752 Mizumaki;33.8548 Consett;54.8500 Bilston;52.5660 Miacatlán;18.7722 Mount Hagen;-5.8600 Los Reyes de Juárez;18.9267 Daulatkhān;22.6000 Nogales;18.8167 Pakwach;2.4619 Golo-Djigbé;6.5403 Perunkalattu;12.9182 Kafr Baţnā;33.5000 Armilla;37.1500 Chatan;26.3200 Garbagnate Milanese;45.5771 As Sarw;31.2387 Mutsamudu;-12.1675 Cahul;45.9167 Cenon;44.8578 Bloxwich;52.6140 Geldrop;51.4222 Tahla;34.0500 Pānchla;22.5400 Vicente López;-34.5333 Bournville;52.4299 Billericay;51.6280 Middletown;41.4459 Otjiwarongo;-20.4642 Dübendorf;47.4167 Charentsavan;40.4097 Sai Mai;13.8882 Subotica;46.1003 Änew;37.8833 Treinta y Tres;-33.2333 Gorleston-on-Sea;52.5757 Nutley;40.8192 Stung Treng;13.5167 Muñiz;-34.5333 Aldridge;52.6060 East Palo Alto;37.4671 Newton Aycliffe;54.6200 Saint-Sébastien-sur-Loire;47.2081 Dietikon;47.4000 Miahuatlán;18.5667 Hakha;22.6455 Arapoti;-24.1578 Ambohijanaka;-18.9833 Douglas;51.8764 Villeparisis;48.9503 Camas;37.4020 Sucy-en-Brie;48.7697 Colonia del Sacramento;-34.4714 Upminster;51.5557 Dalsingh Sarai;25.6680 Kyegegwa;0.4803 Doba;8.6600 Ağdaş;40.6500 Oak Park;42.4649 Westhoughton;53.5490 La Madeleine;50.6558 Northfield;52.4080 Hindley;53.5355 Chiavari;44.3167 Cheadle Hulme;53.3761 Cardito;40.9500 Leiderdorp;52.1667 Mzimba;-11.9000 Nuwara Eliya;6.9667 Māmidālapādu;15.8310 Rahway;40.6077 Melrose;42.4556 Katima Mulilo;-17.5039 Saint-Gratien;48.9719 Mortsel;51.1667 Paracuaro;19.1464 Haverhill;52.0800 Fenoarivo;-18.9333 Kings Norton;52.4072 Bačka Palanka;45.2500 Chalatenango;14.0333 Sèvres;48.8239 Vallauris;43.5805 Balham;51.4434 Pūnch;33.7703 Laurel;39.0949 Taverny;49.0264 Suisun City;38.2473 Linden;6.0000 Estoril;38.7042 Rocha;-34.4833 Kamphaeng Phet;16.4811 Coulsdon;51.3211 Guadalupe;9.9494 Rafael Delgado;18.8167 Nokha;25.1015 Newton Mearns;55.7716 Burbank;41.7444 Golden Gate;26.1844 Hackney;51.5414 Vichy;46.1278 Siquijor;9.1800 Hunucmá;21.0153 Qualiano;40.9167 Brunoy;48.6979 Penonomé;8.5187 Ghatāro Chaturbhuj;25.8146 Qahā;30.2833 Mponela;-13.5167 Champs-Sur-Marne;48.8529 Englewood;40.8917 Samut Songkhram;13.4097 East Niles;35.3683 Ryde;50.7271 San Pedro;9.9332 Sogrāha;25.4798 Loyola Heights;14.6403 Koelwār;25.5805 Ginan;35.3896 Bishop Auckland;54.6630 Apatin;45.6667 Bāruni;25.4751 Louang Namtha;20.9500 Armentières;50.6881 Arriaga;16.2361 Pakxan;18.3964 Manga;11.6667 Wellington;52.7001 Altepexi;18.3676 Buri Ram;14.9942 Ölgiy;48.9683 Comalapa;15.6602 Clydebank;55.8997 Tixtla de Guerrero;17.5667 Mons-en-Baroeul;50.6369 Iheddadene;35.1500 Gosforth;55.0070 Élancourt;48.7847 Betio;1.3500 Jālhalli;13.0333 Missour;33.0500 Mendefera;14.8833 Barnstaple;51.0800 Drexel Hill;39.9495 Dunleary;53.3000 Salua;22.6100 Melíssia;38.0500 Princes Town;10.2667 Māvelikara;9.2670 Gedera;31.8139 Frankfort;38.1924 Wallington;51.3647 Koulikoro;12.8833 Le Bouscat;44.8651 Tapiales;-34.7058 Market Harborough;52.4775 Ocatlán;19.3167 Kočani;41.9167 Cartago;9.8667 Eṭ Ṭīra;32.2322 Temerin;45.4167 Cormeilles-en-Parisis;48.9739 Zawyat ech Cheïkh;32.6414 Shīshgarh;28.7200 Mitú;1.1983 Rēzekne;56.5127 Karunāgapalli;9.0544 Droylsden;53.4828 Buwenge;0.6503 Orly;48.7439 Le Grand-Quevilly;49.4072 Oegstgeest;52.1833 Alençon;48.4306 Bergenfield;40.9236 Thornaby on Tees;54.5585 Vevey;46.4667 Zaouiet Sousse;35.7833 Vilāchcheri;9.8937 Clifton;52.9040 Erdington;52.5236 Losino-Petrovskiy;55.8744 Joinville-le-Pont;48.8214 Dharmapuram;8.3839 Kathu;7.9112 Ouésso;1.6136 Alboraya;39.5000 Plymstock;50.3569 Sāndi;27.3000 Sitrah;26.1200 Phulpur;24.9550 Stratton Saint Margaret;51.5860 Don Bosco;-34.7000 Mytilíni;39.1000 Desamparados;10.0315 Ewa Gentry;21.3344 Belmont;37.5154 Swinton;53.5122 Deuil-la-Barre;48.9767 Hamar;60.7945 Droitwich;52.2670 Arcahaie;18.7718 Masrakh;26.1054 Biarritz;43.4800 Renens;46.5333 Simaria;25.4221 Sonzacate;13.7356 Sāhibpur Kamāl;25.4167 Ramotswa;-24.8667 Mililani Town;21.4465 Harborne;52.4600 Nørresundby;57.0667 Lobatse;-25.2167 Penarth;51.4300 Reisterstown;39.4550 Cherán;19.6833 Sombrerete;23.6333 Muggiò;45.6000 Batken;40.0667 Ban Bang Phun;13.9968 San Francisco;9.9083 Voorschoten;52.1333 Easton;40.6858 Béthune;50.5303 Bongor;10.2806 Saint-Nicolas;50.6333 Mongo;12.1837 Portishead;51.4840 Koper;45.5500 Xoxocotla;18.6850 Poranki;16.4743 Chapala;20.2961 San Juan de Aznalfarache;37.3667 Velenje;46.3625 Genet;9.0500 Pānapur;25.6729 Guastatoya;14.8539 Ciudad Altamirano;18.3583 Ashtown;53.3754 Hamtramck;42.3954 Lemon Grove;32.7331 Madingou;-4.1536 Caversham;51.4670 Mahārājgani;26.1075 Alwaye;10.1167 Arar;30.9833 Martín Coronado;-34.6000 Nsanje;-16.9167 Al Ghayz̧ah;16.2106 Cerro Azul;21.2000 Wednesfield;52.5998 Seriate;45.6847 La Huerta;14.4972 Shtime;42.4333 Itambé;-15.2450 Monsey;41.1181 Whitefield;53.5521 Le Petit-Quevilly;49.4311 Stalybridge;53.4834 Garhara;25.4407 Piastów;52.1833 Zalingei;12.9000 Camborne;50.2130 Kingswinford;52.4981 San Juan Zitlaltepec;19.7167 Weingarten;47.8092 Pompei;40.7500 Simria;25.9663 Kimbe;-5.5575 Oulad Tayeb;33.9598 Zabrat;40.4872 Jonava;55.0722 Lindenhurst;40.6858 San Buenaventura;27.0625 Ōji;34.5947 La Paz;-34.7617 Maisons-Laffitte;48.9469 Bailleston;55.8474 Ennis;52.8463 Belmont;42.3960 Maychew;12.7833 Chester-le-Street;54.8594 Catió;11.2833 Neuilly-Plaisance;48.8619 Hirrīyat Raznah;30.6028 Arbroath;56.5610 Sint-Amandsberg;51.0539 San Juan de Alicante;38.4014 Farnley;53.7876 Tamiahua;21.2788 Montigny-lès-Cormeilles;48.9944 Oadby;52.5987 Carlos A. Carrillo;18.3667 Frontera;18.5336 Sirkhandi Bhitha;26.6244 Texistepec;17.9000 South Pasadena;34.1103 Kościan;52.0833 Maracena;37.2000 Mairwa;26.2322 Litherland;53.4727 Sundararaopeta;16.8031 Stanmore;51.6180 Acomb;53.9550 Shark;40.5569 Saint-Ouen-l’Aumône;49.0447 Bougado;41.3370 Leisure City;25.4935 Rodez;44.3506 Bayanhongor;46.1917 Cazones de Herrera;20.7000 Fontaine;45.1939 Blenheim;-41.5167 Bridgeton;39.4286 Seveso;45.6434 Chaville;48.8086 Voinjama;8.4167 Palm Springs;26.6348 Sheldon;52.4500 Nakhon Phanom;17.4069 Meyrin;46.2167 Billinghurst;-34.5667 Ormskirk;53.5665 Failsworth;53.5102 Ankaraobato;-18.9000 Bedēsa;8.9000 Eysines;44.8853 Utena;55.5000 General Emilio Aguinaldo;14.1833 Oxkutzkab;20.3028 Malinalco;18.9483 Ukkāyapalle;14.4898 Suphan Buri;14.4675 Sélibaby;15.1592 Dongola;19.1698 Bishopbriggs;55.9046 Littleover;52.9060 Newquay;50.4120 Suitland;38.8492 Imperial Beach;32.5693 Belper;53.0290 Mahādebnagar;24.6896 San Juan de Vilasar;41.5053 Lormont;44.8792 Ulaangom;49.9833 Mīt Namā;30.1453 Karian;25.8594 Kraskovo;55.6586 Meda;45.6667 West Whittier-Los Nietos;33.9759 Clevedon;51.4380 Napindan;14.5398 Diego Martin;10.7167 Tysons;38.9215 Loos;50.6128 Chandlers Ford;50.9840 Newton in Makerfield;53.4500 Holborn;51.5204 Kemisē;10.7167 Bang Sao Thong;13.5812 Motul;21.1667 Atoyac de Álvarez;17.2000 Bajina Bašta;43.9731 Penwortham;53.7400 Torcy;48.8502 Sainte-Thérèse;45.6333 Hovd;48.0042 San Juan de Dios;9.8800 Krathum Baen;13.6631 Cəlilabad;39.2089 Lodi;40.8784 Castilleja de la Cuesta;37.3833 March;52.5510 Oak Ridge;28.4727 Farsley;53.8116 Amnat Charoen;15.8500 Sanga;34.6002 El Cerrito;37.9196 Montgeron;48.7039 El Tarf;36.7669 Friern Barnet;51.6126 Ottobrunn;48.0667 Kafr Shukr;30.5470 Coral Terrace;25.7464 Singhāra Buzurg;25.7964 Huntingdon;52.3364 Bispham;53.8520 Göygöl;40.5905 Sidi Lmokhtar;31.5700 Nar’yan-Mar;67.6333 Kidsgrove;53.0874 Bambang;14.5228 Croix;50.6781 Belgrave;52.6566 Northwood;51.6010 Kanchanaburi;14.0194 Montmorency;48.9906 Rockville Centre;40.6643 Santiago Ixcuintla;21.8110 Kibuku;1.0375 Onex;46.1833 Zwedru;6.0667 Tipasa;36.5942 Guiseley;53.8750 Ives Estates;25.9632 Vélizy-Villacoublay;48.7834 Longjumeau;48.6943 Fray Bentos;-33.1333 De Meern;52.0781 Cloverleaf;29.7882 Sceaux;48.7786 San Giovanni la Punta;37.5833 Visby;57.6290 Penistone;53.5250 Mill Creek East;47.8361 Cliffside Park;40.8222 Ossett;53.6800 Maywood;33.9886 Bāgalūr;13.1328 Santa Iria da Azóia;38.8464 Chocamán;18.9978 Sabunçu;40.4425 Kérkyra;39.6239 Charo;19.7500 Heredia;9.9985 Norton;54.5890 Sainte-Foy-lès-Lyon;45.7300 Maplewood;40.7330 Melrose Park;41.9030 Rumphi;-11.0167 Terrytown;29.9014 Jethuli;25.5378 Dip;26.2369 Vellalūr;10.9775 Chiconcuac;19.5500 Tlacolula de Matamoros;16.9542 Nuevo San Juan Parangaricutiro;19.4000 Fânzeres;41.1667 Peekskill;41.2884 Mahthi;25.7281 Maghull;53.5174 Ojinaga;29.5644 Hunasamaranhalli;13.1435 Emiliano Zapata;17.7446 Tassin-la-Demi-Lune;45.7640 Kitajima;34.1256 Atherton;53.5230 Juvisy-sur-Orge;48.6883 Hacı Zeynalabdin;40.6233 Vaikam;9.7667 Cacahoatán;14.9898 Mohale’s Hoek;-30.1500 Tiverton;50.9030 Kānke;23.4348 Morsang-sur-Orge;48.6618 Zapote;9.9203 Vukovar;45.3444 Wewak;-3.5500 Plumstead;51.4900 Mīzan Teferī;7.0000 Montigny-lès-Metz;49.1006 Horwich;53.5920 Tbeng Meanchey;13.8167 Soledad;36.4432 Maesteg;51.6100 Bathgate;55.9024 Carteret;40.5849 Szczytno;53.5667 Kajiado;-1.8500 Kaga Bandoro;7.0000 University Park;32.8506 Truro;50.2600 Hesarghatta;13.1391 Bartoszyce;54.2500 Rāja Pākar;25.7350 Ekeren;51.2833 Le Mée-sur-Seine;48.5333 Portalegre;39.3167 Kondalāmpatti;11.6262 South Bradenton;27.4612 Yate;51.5402 Degollado;20.4667 North Guwāhāti;26.1900 Valle Nacional;17.7667 Novo Mesto;45.8000 Cotija de la Paz;19.8100 Southbourne;50.7220 Cusano Milanino;45.5500 Stoke Gifford;51.5170 Fortín de las Flores;18.9000 Chinnasekkadu;13.1609 Leek;53.1080 Edegem;51.1500 Shārūnah;28.5667 Bāzārak;35.3128 Tequixquiac;19.9097 Hérouville-Saint-Clair;49.2044 Chevilly-Larue;48.7664 Sonāgāzi;22.8492 Riverbank;37.7260 Balasore;21.4942 Kirkstall;53.8160 Abū Şīr Banā;30.9000 Dongta;38.0824 Dronfield;53.3024 Buxton;53.2590 Sühbaatar;50.2364 Rawson;-43.3000 Royton;53.5660 Baalbek;34.0063 Rothwell;53.7485 Le Plessis-Trévise;48.8111 Abim;2.7019 Bundibugyo;0.7125 Hornsey;51.5870 Falmouth;50.1500 Kasamatsuchō;35.3672 Kanmaki;34.5627 Herndon;38.9699 Chausa;25.5283 North Lynnwood;47.8533 Falagueira;38.7589 Castelnau-le-Lez;43.6369 Lop Buri;14.8000 Panniyannūr;11.7538 Cibitoke;-2.8886 Allende;28.3333 Elmwood Park;41.9225 Landover;38.9241 Gan Yavne;31.7886 Bailey's Crossroads;38.8477 Xingangli;39.9101 Cormano;45.5500 Carlow;52.8306 Butaleja;0.9250 Chitipa;-9.7019 Saint-Pol-sur-Mer;51.0314 Woodlesford;53.7567 São João da Madeira;40.8972 Tralee;52.2675 Garches;48.8456 Fatick;14.3333 Tuktukan;14.5280 La Celle-Saint-Cloud;48.8411 Saint-Michel-sur-Orge;48.6325 Kafr Qāsim;32.1151 Rosemont;38.5478 Dammarie-lè-Lys;48.5177 Villa Sarmiento;-34.6333 Decatur;33.7711 Chanthaburi;12.6086 Baabda;33.8333 Gjirokastër;40.0667 Villeneuve-le-Roi;48.7333 Mantes-la-Ville;48.9750 Dollis Hill;51.5641 Frattaminore;40.9500 Valmiera;57.5500 Huskvarna;57.7919 Bakhri;25.5989 Vrbas;45.5667 Bontoc;17.0900 Atar;20.5167 Banī Murr;27.2272 Al Jawf;24.2167 Cottingham;53.7822 Saint-Maurice;48.8183 San Fernando;34.2886 Caazapá;-26.2000 Renfrew;55.8780 Tchibanga;-2.9333 Willowbrook;33.9209 Suchiapa;16.6220 Senago;45.5833 Qalansuwa;32.2822 Mecayapan;18.2167 Stamford;52.6560 Hellemmes-Lille;50.6283 Owando;-0.4833 Stellenbosch;-33.9367 Celbridge;53.3380 Kudrovo;59.9000 Guaranda;-1.6000 Hebburn;54.9720 Fengdeng;38.5515 Wasquehal;50.6694 Port Laoise;53.0308 Mapastepec;15.4411 Tabount;30.8800 Leigh-on-Sea;51.5425 Nkhata Bay;-11.6000 Achères;48.9602 Bariārpur;25.6860 Fada;17.1833 Le Bourget;48.9344 Kyenjojo;0.6100 Calne;51.4380 Sibiti;-3.6858 Behat;26.2435 Satun;6.6147 Saint-Cyr-l’École;48.8003 Phetchaburi;13.1119 Al Madāmūd;25.7333 Bāuria;22.4521 Tohoué;6.3967 Hythe;50.8690 Nakasi;-18.0667 Novate Milanese;45.5333 Peto;20.1256 Peterlee;54.7600 Watauga;32.8719 Ciudad Miguel Alemán;26.4003 Ashland;37.6942 Nerupperichchal;11.1610 Seaham;54.8400 Tanki Leendert;12.5418 Alfafar;39.4222 Al ‘Azīzīyah;32.5308 Shoreham-by-Sea;50.8340 Aţ Ţafīlah;30.8400 Nogent-sur-Oise;49.2756 Montereau-faut-Yonne;48.3853 Pakri;25.5876 Copiague;40.6707 Maywood;41.8798 Yonabaru;26.1995 Kiboga;0.9200 Goodmayes;51.5631 Allschwil;47.5500 Palmers Green;51.6178 La Lucila;-34.4833 Kėdainiai;55.2833 Mohiuddīnnagar;25.7428 Sukhodilsk;48.3500 Kagadi;0.9411 Biddulph;53.1200 Worcester Park;51.3752 Chilly-Mazarin;48.7025 Killingworth;55.0318 Opfikon;47.4333 Tena;-0.9890 Naas;53.2158 Jasmine Estates;28.2930 Sacavém;38.7944 Teopisca;16.5425 Sunbāţ;30.8057 Hidalgotitlán;17.7833 Lingolsheim;48.5575 Caterham;51.2803 Nyon;46.3833 Hatton;6.8897 Aarau;47.4000 Mosta;35.9097 Selby;53.7818 Coudekerque-Branche;51.0253 Buyende;1.1475 Fleury-les-Aubrais;47.9312 Shōwa;35.6279 Kayanza;-2.9167 Drapetsóna;37.9467 Millbrae;37.5994 Douar Toulal;33.8951 Gröbenzell;48.2000 Abbots Langley;51.7010 Bonga;7.2667 Peterhead;57.5091 Ronchin;50.6047 Pul-e ‘Alam;33.9808 Ogre;56.8186 Kilkenny;52.6477 Cudahy;33.9631 West Puente Valley;34.0513 Lami;-18.1167 Kotido;3.0061 Bellshill;55.8160 East San Gabriel;34.1157 Blue Island;41.6578 Zimatlán de Álvarez;16.8667 Ntcheu;-14.8333 Davyhulme;53.4559 Hialeah Gardens;25.8878 Tepatlaxco;19.0667 Moyo;3.6504 Biləsuvar;39.4583 Itaosy;-18.9167 Bedelē;8.4500 Llandudno;53.3250 Saint-Fons;45.7086 Monserrato;39.2500 Quedgeley;51.8250 Phra Pradaeng;13.6592 Trentola;40.9762 Southgate;51.6316 Oulad Fraj;32.9667 Sibut;5.7333 Al Qays;28.4833 Burnham-on-Sea;51.2376 Savanna-la-Mar;18.2167 Sancoale;15.4670 Prestatyn;53.3310 Soisy-sous-Montmorency;48.9878 Bry-sur-Marne;48.8411 Mulanje;-16.0258 José Cardel;19.3667 Ciudad Hidalgo;14.6792 North Plainfield;40.6209 Dumjor;22.6400 Dukinfield;53.4739 Jēkabpils;56.4994 Mödling;48.0856 Woodlawn;38.7332 Ischia;40.7500 Kannānkurichchi;11.6969 Antrim;54.7173 Jarājūs;25.8681 Bafatá;12.1719 Ecclesall;53.3620 ‘Abasān al Kabīrah;31.3237 Ahfir;34.9514 Roselle;40.6527 Požega;45.3331 Saint Paul’s Bay;35.9483 Phichit;16.4431 Khizrpur;30.5843 Mouila;-1.8639 Artashat;39.9539 Mīrpeta;17.3200 Krommenie;52.5000 Aldama;28.8386 Mahiari;22.5900 Frimley;51.3143 Loei;17.4853 Bhopatpur;26.4495 Bingley;53.8460 Svay Pak;11.6467 Al Bāḩah;20.0125 Palau;27.9167 Pahsara;25.5482 The Crossings;25.6708 Dover;40.0019 Santa Catarina Juquila;16.2379 Sant Just Desvern;41.3833 Vandalūr;12.8924 Molesey;51.4010 Mbaïki;3.8833 Hillside;40.6961 Tatahuicapan;18.2500 Connahs Quay;53.2180 Soroca;48.1667 Arese;45.5500 Telšiai;55.9833 Fgura;35.8725 Ayutla de los Libres;16.9000 Great Linford;52.0680 Cercola;40.8667 Massapequa;40.6676 Houghton Regis;51.9039 Zāwiyat Razīn;30.4122 Paraguarí;-25.6333 Wexford;52.3342 Sunny Isles Beach;25.9385 São Filipe;14.8950 Uliastay;47.7428 Vista Hermosa de Negrete;20.2717 Phetchabun;16.4169 Mullingar;53.5224 Liversedge;53.7067 Central Falls;41.8901 Sant’Antonio Abate;40.7333 Maltby;53.4260 Éragny;49.0172 Cran-Gévrier;45.9036 Keynsham;51.4135 Tapalpa;19.9445 Aḑ Ḑāli‘;13.6967 Alblasserdam;51.8702 Fâches-Thumesnil;50.5989 Acala;16.5533 Kew Green;51.5308 Sarso;26.2333 Bourne;52.7684 Bella Unión;-30.2500 Assebroek;51.2000 Ashtarak;40.2975 Broughty Ferry;56.4672 West Carson;33.8229 West Rancho Dominguez;33.9057 Ghāt;24.9644 Dumra;26.5671 Bedlington;55.1330 Al Karak;31.1833 Thalwil;47.2833 Hayesville;44.9793 Tonypandy;51.6223 Reinach;47.4833 Coalcomán de Vázquez Pallares;18.7775 Goroka;-6.0833 Saint-Lambert;45.5000 Chamalières;45.7736 Acatlán de Osorio;18.2086 Opuwo;-18.0556 Aldo Bonzi;-34.7083 Kitagata;35.4358 Carmelo;-34.0000 Melegnano;45.3588 Valinda;34.0400 Moulins;46.5647 Ijevan;40.8792 Mont-Saint-Aignan;49.4625 Salinas de Hidalgo;22.6280 Calella;41.6169 Dayr Abū Ḩinnis;27.7864 Morwa;25.8030 Four Square Mile;39.6808 Mayahaura;22.1834 Hadleigh;51.5535 Mānjha;26.4061 Nong Bua Lamphu;17.2042 East Barnet;51.6430 Columbia Heights;45.0484 Moston;53.5156 Schlieren;47.4000 Bozoum;6.3172 Hampton;51.4220 Chennevières-sur-Marne;48.7983 Viroflay;48.8000 Tauragė;55.2522 Highgate;51.5716 Hacıqabul;40.0433 North Bellmore;40.6904 Leicester;8.4500 Lennox;33.9380 Colne;53.8554 Ghosāi;25.5562 Le Pecq;48.8967 Ymittós;37.9500 Westbury;51.2600 Nilaiyūr;9.8572 Chemax;20.6550 Maralal;1.1000 Clitheroe;53.8711 Pully;46.5167 Ghanzi;-21.7000 Virovitica;45.8333 San Francisco;13.7000 Redhill;51.2393 Bonneuil-sur-Marne;48.7742 Vauréal;49.0300 Warminster;51.2050 Harihans;26.1457 Senguio;19.7539 Bischheim;48.6139 Canelones;-34.5167 Harpur;26.4527 Wellington;50.9755 Malgrat de Mar;41.6456 Ukmergė;55.2500 Técpan de Galeana;17.2500 Elmwood Park;40.9049 Santa Lucía;-34.4525 Utazu;34.3106 Obock;11.9667 Pajapan;18.2667 Lealman;27.8197 Le Raincy;48.8992 Mountlake Terrace;47.7921 Alajuelita;9.9035 Távros;37.9667 Devizes;51.3528 Écully;45.7744 Orhei;47.3833 Dumri;25.4014 Adliswil;47.3167 Sakri;26.2197 Wibsey;53.7672 Murugampālaiyam;11.0806 La Esperanza;14.3000 Dingle;53.3774 Boom;51.0833 Wolfratshausen;47.9133 Horsforth;53.8370 Ulao;25.4165 Al Hamalah;26.1497 Ad Dirāz;26.2186 El Astillero;43.4017 El Rosario;22.9922 McNair;38.9513 Lauālāgaon;25.4940 El Dorado;24.3228 Moreton;53.4010 Mahisi;25.8540 Öndörhaan;47.3233 Cowley;51.7330 Néa Erythraía;38.0833 Bonnyrigg;55.8747 Lomita;33.7933 Fern Down;50.8100 Amdjarass;16.0658 Gavarr;40.3589 Quba;41.3597 Neder-Over-Heembeek;50.9000 Mineola;40.7470 Staines-upon-Thames;51.4340 Dolores;-33.5333 Tadaoka-higashi;34.4871 Dawlish;50.5810 Hyattsville;38.9613 Langley Park;38.9897 Galhinna;7.4161 Basatpur;26.0011 Erumāpālaiyam;11.6324 Ostermundigen;46.9550 West Hempstead;40.6959 Rathfarnham;53.2988 Pachmīr;25.4077 İsmayıllı;40.7900 Ati;13.2133 Nan;18.7833 New Brighton;53.4320 Shirley;51.3813 Cosham;50.8424 Ćuprija;43.9231 Peraía;40.5000 High Blantyre;55.7930 Nailsea;51.4300 Yasothon;15.7972 Enfield Lock;51.6686 Cleckheaton;53.7250 Mānikpur;25.9100 Beinasco;45.0167 Gobabis;-22.4333 Paso de Carrasco;-34.8714 Kokopo;-4.3500 Zaghouan;36.4000 Basford;52.9780 Bhogpur;31.5500 Porthcawl;51.4800 Banī Ḩasan ash Shurūq;27.9314 Ban Rawai;7.7707 Bubanza;-3.0833 Milton;53.0500 Les Clayes-sous-Bois;48.8206 Mālancha;24.6660 Arājpur;25.5197 Senta;45.9314 Keota;25.6440 Harpur Pūsa;25.9667 Kimwanyi;0.4533 Szigethalom;47.3154 Hilsea;50.8300 Greystones;53.1440 Eldama Ravine;0.0500 Lynbrook;40.6579 Usmānpur;24.9218 Palisades Park;40.8472 Łomianki;52.3333 Floirac;44.8364 Zinvié;6.6167 Ağsu;40.5692 Ağstafa;41.1189 Dhobauli;25.4008 Kilwinning;55.6550 Heckmondwike;53.7080 Woodhouse;53.3580 Luganville;-15.5333 Baikatpur;25.4939 Raghudebbati;22.5300 Tyldesley;53.5166 Lakhnaur;26.2020 Comrat;46.3167 Pariharpur;26.7114 Tecuala;22.4004 San Josecito;10.0126 Kirkland;45.4500 La Crescenta-Montrose;34.2322 Maurepas;48.7628 Upton;53.3850 Qormi;35.8794 Haslingden;53.7050 Arvayheer;46.2661 Ciudad de Huitzuco;18.3000 Biyahmū;29.3675 Tepoztlán;18.9853 Tlaxcala;19.3151 Kidbrooke;51.4621 Santiago Tulantepec;20.0397 Dokolo;1.9186 Sherrelwood;39.8390 Mohan Eghu;25.4002 Mwanza;-15.5986 Cattolica;43.9667 Kotsyubyns’ke;50.4883 Nahāzāri;22.4347 San Rafael Arriba;9.8778 Maldon;51.7318 Fazakerley;53.4676 Morristown;40.7967 Ulcinj;41.9200 Petit-Goâve;18.4314 Seacroft;53.8222 Tidjikja;18.5500 Ciudad Guadalupe Victoria;24.4497 Albany;37.8897 Mahibadhoo;3.7575 Leagrave;51.9030 Abdullahnagar;25.7683 Rāmpur Jalālpur;25.6712 Westmount;45.4833 Attapu;14.8000 Fomboni;-12.2800 Visaginas;55.5980 Baguley;53.3990 Morges;46.5167 Saynshand;44.8917 Evergreen Park;41.7213 Kapsabet;0.3333 Kavieng;-2.5667 Clayton;53.7820 Kfar Aabîda;34.2264 South El Monte;34.0493 San Isidro;9.9737 Kalyānpur Bamaiya;25.7140 Wondelgem;51.0889 Khusropur;25.4817 Shipley;53.8330 West Wickham;51.3765 Giria;24.5167 Sligo;54.2667 Inírida;3.8653 North Amityville;40.7005 Pupri;26.4708 Majītha;31.7571 West Drayton;51.5043 Dhamaun;25.5999 Néo Psychikó;38.0000 Sironko;1.2306 Ellinikó;37.8667 Hermosa Beach;33.8653 Ḩawsh al Baḩdalīyah;33.4292 Glanerbrug;52.2150 Pećinci;44.9000 South San Jose Hills;34.0123 Karahia;26.4054 Cárdenas;22.0103 Izamal;20.9314 Larkhall;55.7370 Bayshore Gardens;27.4345 Lezhë;41.7819 Hatch End;51.6010 Shankar Saraiyā;26.5967 Kahhalé;33.8219 Ajā;30.9414 Hednesford;52.7115 Deysbrook;53.4290 Telavi;41.9167 La Crucecita;15.7753 Korem;12.5000 Whickham;54.9456 Noisiel;48.8547 Moroto;2.5300 Herceg Novi;42.4530 Baharu;22.2040 Lons-le-Saunier;46.6744 Seabrook;38.9802 Bredbury;53.4200 Baildon;53.8510 La Cruz;23.9214 Mau Dhaneshpur;25.5899 Brookfield;41.8245 Montmagny;48.9736 Hawthorne;40.9579 Mont-Royal;45.5161 Raiyam;26.2702 Ágios Ioánnis Réntis;37.9667 Solothurn;47.2167 Kamwenge;0.1861 Baro;25.4487 Sweetwater;25.7785 Cheam;51.3600 Ferndale;42.4592 Grajales;19.2500 Laï;9.4000 Netherton;52.4908 Saltash;50.4080 Budva;42.2881 Canovellas;41.6176 West Derby;53.4338 Arnouville-lès-Gonesse;48.9872 Macas;-2.3667 Partick;55.8699 Mālhīpur;25.4069 Tillabéri;14.2120 Akassato;6.5000 South Hayling;50.7876 Mukhtārpur Salkani;25.6643 Ban Houayxay;20.2631 Kisoro;-1.2850 Scarborough;11.1833 Brusciano;40.9167 Clifton;53.9721 Jagdispur;22.6500 Pemberton;53.5360 Johnstone;55.8346 Octeville;49.6269 Hargāwān;25.1428 Morriston;51.6647 Əhmədli;40.3839 Kulhudhuffushi;6.6225 Ramnagar;22.3245 Saint-Leu-la-Forêt;49.0169 Carrigaline;51.8166 Binningen;47.5333 Mariana;14.6194 Jouy-le-Moutier;49.0108 Sant’Arpino;40.9575 Pljevlja;43.3567 Royston;52.0471 Addlestone;51.3695 Buikwe;0.3442 Saint-Jean-de-la-Ruelle;47.9131 Kundal;25.7620 Le Vésinet;48.8939 Albal;39.3972 Bububu;-6.1658 Barharwa Kalān;26.5434 Kelandis;-8.5616 Hendaye;43.3586 Young;-32.7000 Brackley;52.0320 Winthrop;42.3761 Merrifield;38.8731 Kawai;34.5783 Tlaltetela;19.3167 Dhūmnagar;26.7560 Ribeira Grande;17.1830 Mao;14.1194 Braunstone;52.6160 Jaypul;22.7833 East Massapequa;40.6742 Cedar Mill;45.5355 Norwood;39.1605 Lagawe;16.7975 Carrières-sous-Poissy;48.9478 Meddappakkam;12.9166 Tengampudūr;8.1158 Hazel Grove;53.3750 Marly-le-Roi;48.8669 Loreto;26.0128 Hūn;29.1268 Kédougou;12.5500 Ambohidrapeto;-18.9000 Oteapan;18.0000 Bou Djeniba;32.9000 Wallisellen;47.4150 Gopālnagar;24.8063 Puréparo de Echaíz;19.9092 Masamagrell;39.5703 Point Pleasant;40.0772 Huixcolotla;18.9219 Greenfield;36.3242 Itāhri;25.3130 Mansa Konko;13.4667 Harrison;40.7431 Wombwell;53.5160 Härnösand;62.6361 Prestwick;55.4956 Lelydorp;5.6967 Jwaneng;-24.6019 Rutherford;40.8203 Bellwood;41.8829 Clonmel;52.3539 North Babylon;40.7314 Pedreiras;-4.5696 Buckley;53.1720 Yiewsley;51.5130 The Mumbles;51.5730 Mangrauni;26.3667 Vicente Guerrero;23.7500 Hostomel;50.5893 Pālda;22.6800 Cerritos;22.4275 Banstead;51.3220 Juchique de Ferrer;19.8333 Whittlesey;52.5580 Siyəzən;41.0761 Ōyamazaki;34.9028 Ptuj;46.4194 Bromborough;53.3360 Verwood;50.8815 Chertsey;51.3902 Leposaviq;43.1000 Country Walk;25.6330 Sudley;38.7878 Lognes;48.8361 Basse Santa Su;13.3167 West Chester;39.9601 North Massapequa;40.7031 Hythe;51.0716 Melksham;51.3710 Progreso;-34.6650 Tarrafal;15.2780 Tsetserleg;47.4769 Biały Kamień;50.7814 Casandrino;40.9333 Rwamagana;-1.9500 Goulmima;31.6944 Arenys de Mar;41.5819 North Bay Shore;40.7602 Lansdale;40.2417 Acasusso;-34.4667 Mésa Geitoniá;34.7024 Straşeni;47.1333 Kanhauli Manohar;25.9755 Singhwara;26.1842 Pendlebury;53.5075 Dover;40.8859 Hybla Valley;38.7484 Nantwich;53.0670 Santa Rosalía;27.3389 Garhpura;25.6638 Mayuge;0.4578 Kingstowne;38.7625 Péfka;40.6500 Beausoleil;43.7419 Palanga;55.9167 North Valley Stream;40.6840 Amblecote;52.4600 Montargis;47.9969 Ermúa;43.1875 Churriana de la Vega;37.1500 Bidston;53.4020 Żabbar;35.8772 Franconia;38.7682 Toyoyama;35.2505 Pūluvappatti;10.9630 Schofield Barracks;21.4936 Gornalwood;52.5230 Billapādu;16.6364 San Fernando;16.8717 Lymington;50.7500 Sangre Grande;10.5667 Xaignabouli;19.2500 Eilendorf;50.7794 Hessle;53.7239 Filadelfia;-22.3400 Carrières-sur-Seine;48.9108 Sidi Zouine;31.6706 Llantrisant;51.5420 Rosyth;56.0339 Le Hochet;-20.1350 Lyantonde;-0.4069 Tarxien;35.8658 Prachin Buri;14.0567 Hasanpur Juned;25.5915 Rock Ferry;53.3730 Erongarícuaro;19.5833 Salaspils;56.8667 Petite-Synthe;51.0231 Saffron Walden;52.0220 Dogāchi;24.6195 Bois-d’Arcy;48.8014 Suchindram;8.1544 Sing Buri;14.8911 Barton upon Irwell;53.4760 Burke Centre;38.7903 Ait Yaazem;33.7333 Bostonia;32.8189 Sainte-Marthe-sur-le-Lac;45.5300 Torit;4.4081 Vynnyky;49.8156 Thornbury;51.6094 Madera;29.1900 Tunapuna;10.6333 Eppelheim;49.4000 South Orange Village;40.7491 Porto Novo;17.0190 Prilly;46.5333 Gines;37.3875 Tanakkangulam;9.8877 Viljandi;58.3667 Roosevelt;40.6797 Orange Walk;18.0750 Stapleford;52.9290 Saatlı;39.9311 Lākho;25.4113 Redruth;50.2330 Lucé;48.4383 Mārupe;56.9069 Dumont;40.9452 San Pablo Atlazalpan;19.2172 Perivale;51.5383 Tabernes Blanques;39.5064 Puerto Ayora;-0.7500 Enghien-les-Bains;48.9697 Sa Kaeo;13.8206 Kidlington;51.8230 Gundūr;10.7339 Ngora;1.4575 Huitzilan;19.9667 Villa Unión;23.1883 Valparaíso;22.7667 Papágos;37.9900 Timperley;53.3870 Armthorpe;53.5352 Baynāla;22.5277 Al Mālikīyah;26.1008 Little Hulton;53.5300 Puttalam;8.0981 Lys-lès-Lannoy;50.6714 Sidlice;54.3471 Thônex;46.1833 Valbom;41.1333 Khaşab;26.1833 Anosy Avaratra;-18.8000 Kenley;51.3242 Rottingdean;50.8150 Marlow;51.5700 Brierley Hill;52.4795 Whitby;54.4858 Juan Aldama;24.2910 Brownsville;25.8216 Swinton;53.4877 Sāsan;25.6883 Kukës;42.0833 Hiệp Hòa;10.9289 Pathum Thani;14.0500 Harrow Weald;51.6040 Ezequiel Montes;20.6667 Idylwood;38.8896 Valenton;48.7450 Iselin;40.5697 Esquimalt;48.4306 Sha Kok Mei;22.3784 Wanstead;51.5778 La Llagosta;41.5156 Kaliro;0.8944 Guachochi;26.8194 Tak;16.8711 Balaungi;30.7306 Bryn Mawr-Skyway;47.4949 Rustington;50.8102 Kamrāwān;25.6956 Takoma Park;38.9810 Babhani Bholwa;26.0876 Rossington;53.4759 Sudbury;51.5537 Füzuli;39.6003 Phayao;19.1653 Fatehpur;25.5089 Ilkley;53.9250 Deux-Montagnes;45.5333 Saint-Brice-sous-Forêt;48.9986 Miahuatlán;18.2833 Haubourdin;50.6092 Manafwa;0.9564 Longwy;49.5197 Ovenden;53.7432 Breyten;-26.3000 Lowton;53.4710 Patcham;50.8640 Canet de Mar;41.5911 Guerrero Negro;27.9589 Childwall;53.3950 Wattignies;50.5850 Shenley Church End;52.0220 Busembatia;0.7750 Chāndi;25.7296 Glassmanor;38.8181 Plungė;55.9167 Belsandi Tāra;25.7722 Matam;15.6167 Rāje;26.2216 Newport Pagnell;52.0870 Tân Vạn;10.9119 Wahiawa;21.5012 San Ġwann;35.9094 Mugalivakkam;13.0205 Saint-André;50.6603 Ijra;26.3119 Birqāsh;30.1692 Shiremoor;55.0366 Boskoop;52.0667 Marsabit;2.3333 Udelnaya;55.6333 Djambala;-2.5500 Woodmere;40.6375 Bellaire;29.7040 San Nicolás de los Ranchos;19.0667 Tabhka Khās;25.6518 Illizi;26.5080 Kretinga;55.8900 Petersfield;51.0038 Trstenik;43.6186 Chaddesden;52.9301 Anta;41.0073 Calkiní;20.3667 Bhatranha;25.9846 Avon;48.4089 Tirumalaiyampālaiyam;10.8790 Marib;15.4606 Massapequa Park;40.6817 Rive-de-Gier;45.5294 Vicente Guerrero;30.7264 Egg Buckland;50.4006 Ermita;14.5830 East Riverdale;38.9600 Tempoal de Sánchez;21.5167 Primrose;-26.1833 Pinewood;25.8697 Nacozari de García;30.3833 Prescot;53.4286 El Parral;16.3662 Hillcrest Heights;38.8373 Manassas Park;38.7709 Makokou;0.5667 Mexicaltzingo;19.2092 Lodwar;3.1167 Montesson;48.9086 Neubiberg;48.0833 Shirebrook;53.2048 West Boldon;54.9450 Āndipālaiyam;11.0920 San Ignacio;17.1588 Anzin;50.3714 Adelphi;39.0017 Fraserburgh;57.6930 Arbon;47.5167 Ntungamo;-0.8819 Cachoeira Alta;-18.7628 Beur;25.5690 New Milford;40.9337 Clayton;38.6444 Sāhit;25.5888 Ramonville-Saint-Agne;43.5461 Walsall Wood;52.6277 Phrae;18.1453 Keetmanshoop;-26.5786 Bodmin;50.4660 Astara;38.4561 Bommayapālaiyam;11.9922 Mollerusa;41.6317 Hollinwood;53.5183 Zveçan;42.9000 Streetly;52.5770 Oulad Hamdane;32.3333 Spitalfields;51.5166 Wollaston;52.4619 Puerto Carreño;6.1833 Aylestone;52.6040 Perumbakkam;12.9055 L’Ancienne-Lorette;46.8000 Ogíjares;37.1167 Pueblo Nuevo;17.1576 Tukums;56.9667 Bull Run;38.7802 Conisbrough;53.4790 Struga;41.1775 Dayālpur Sāpha;25.7122 Ocean Pointe;21.3145 Greenville;5.0167 Fort Bonifacio;14.5311 Caerfyrddin;51.8560 Thorpe Saint Andrew;52.6354 Palmetto Estates;25.6211 Elesbão Veloso;-6.2019 Four Corners;44.9290 San Juan Bautista;-26.6333 Bellmore;40.6569 Manappakkam;13.0108 Nalambūr;13.0867 Courcouronnes;48.6239 Sendafa;9.1500 Qusar;41.4264 Santa Cruz Amilpas;17.0667 San Miguel Xoxtla;19.1833 Hawick;55.4220 Ojus;25.9563 Mongat;41.4667 Narhan;25.6941 Wolverton;52.0626 Jamira;25.5535 Linlithgow;55.9791 Walnut Park;33.9682 Burslem;53.0426 Bordj Mokhtar;21.3289 Broadwater;50.8282 Vengavasal;12.8991 Mariakerke;51.0728 Janāi;22.7157 Castellanza;45.6167 Persan;49.1533 Hall in Tirol;47.2833 Tamarakulam;8.1325 Vincent;34.0983 Ranong;9.9619 Artesia;33.8676 Milford Haven;51.7142 Anenecuilco;18.7781 Senglea;35.8878 Rakvere;59.3500 Moussoro;13.6333 Mahikeng;-25.8656 Srbobran;45.5333 Aldama;22.9194 South Houston;29.6611 Daharia;25.5307 Altamirano;16.7361 North Arlington;40.7874 Stanford;37.4252 Gopālpur;26.1317 Baranzate;45.5167 Mouvaux;50.7033 Nakhon Nayok;14.2069 Chojnów;51.2667 Balaxanı;40.4617 Sedgley;52.5400 Solaro;45.6150 Southborough;51.1598 Kotwāpatti Rāmpur;25.7153 Massakory;13.0000 Naama;33.2678 Morangis;48.7064 Kiratpur Rājārām;25.8560 Šilutė;55.3500 Kabugao;18.0239 Koulamoutou;-1.1333 La Grange;41.8072 Varedo;45.6000 Barwat Pasrāin;26.7925 Weehawken;40.7677 Minehead;51.2038 Bačka Topola;45.8167 Auray;47.6678 Monte di Procida;40.8000 Bāhāgalpur;24.5934 Floral Park;40.7227 Muragācha;22.6900 Bacalar;18.6769 Coseley;52.5500 Chard;50.8728 Buckingham;51.9950 Tullamore;53.2667 Kerugoya;-0.5000 Kinkala;-4.3614 Lomma;55.6667 Bela;25.8103 Sutton on Hull;53.7806 Parkway;38.4993 South River;40.4455 Ottaikkālmantapam;10.8827 Arpajon;48.5903 Wezembeek-Oppem;50.8333 Al Ḩazm;16.1642 Ālampur Gonpura;25.5404 Kraainem;50.8619 Midsomer Norton;51.2842 Simri;26.1639 Belwāra;25.7443 Milngavie;55.9421 Ascensión;31.0928 Fleury-Mérogis;48.6294 Ángel R. Cabada;18.5969 Montévrain;48.8753 Căuşeni;46.6333 Corsham;51.4340 Chhapra;26.4005 Fontenay-le-Fleury;48.8136 Alotau;-10.3167 Southwick;50.8360 Lye;52.4590 Villa Corzo;16.1848 Barentu;15.1167 Ayodhyāpattanam;11.6755 Bijelo Polje;43.0400 Anadyr;64.7333 Thatto Heath;53.4352 Parihāra;25.5378 Maliana;-8.9917 Ericeira;38.9620 Richmond;-41.3333 Tenango del Aire;19.1575 Aleg;17.0500 Parihārpur;26.4026 New Cassel;40.7602 Isla Mujeres;21.2084 Blunsdon Saint Andrew;51.6100 Neykkārappatti;11.6225 Sisai;26.1899 Asperg;48.9000 Altay;46.3728 Bartica;6.4000 Whakatane;-37.9600 Titel;45.2047 Rahui;25.2728 Glenmont;39.0698 Brownhills;52.6470 Stone Ridge;38.9294 Westbury;40.7599 Lwengo;-0.3911 Sukhothai;17.0000 Patālia;25.6600 Villa Juárez;27.1278 Isla Vista;34.4128 Mahespur;24.8616 Allestree;52.9519 Eckington;53.3080 White Center;47.5086 Épinay-sous-Sénart;48.6931 Desri;25.6691 Hoyland Nether;53.4985 Jaltenango;15.8725 Chepstow;51.6420 Maqsūda;26.2410 Giffnock;55.8051 Foammulah;-0.2932 Peshkopi;41.6833 Targuist;34.9500 La Palma;33.8504 Charābidya;22.3271 Wallingford;51.5990 Uthai Thani;15.3800 Seaford;40.6678 Castle Bromwich;52.5050 Vaires-sur-Marne;48.8742 Edineţ;48.1667 Billère;43.3025 Nuevo Ideal;24.8875 Sarmastpur;25.9490 Hévié;6.4167 Great Driffield;54.0050 Cherryland;37.6792 Chilwell;52.9160 Marungūr;8.1721 Saucillo;28.0333 El Carmen;13.7167 Seïada;35.6700 Tari;-5.8489 Sedaví;39.4271 Gombe;0.1811 Ormesby;54.5492 Khānpur;25.8572 Princeton Meadows;40.3347 Ghordaur;25.7089 Nohsa;25.5653 Voisins-le-Bretonneux;48.7583 Santa María Jalapa del Marqués;16.4401 Phra Samut Chedi;13.5976 La Magdalena Chichicaspa;19.4181 Schwyz;47.0167 Saint Budeaux;50.4033 Kürdəmir;40.3383 Blundellsands;53.4800 Mountain Ash;51.6814 Chandi;22.3503 Rayón;19.1481 Saint-Max;48.7011 Cuencamé de Ceniceros;24.8667 Kapchorwa;1.4000 Earlestown;53.4500 Snaresbrook;51.5870 La Tour-de-Peilz;46.4500 Dzuunmod;47.7069 Knutsford;53.3025 El Fuerte;26.4214 Kamnik;46.2257 Goh;24.9845 Cadereyta;20.7000 Kanungu;-0.8969 Zombo;2.5131 Santa Venera;35.8897 Santa María Huazolotitlán;16.3041 Santa Ana;9.9320 Ainsdale;53.6021 Jalpura;25.4862 Kottāram;8.1188 Asbury Park;40.2226 Namanga;-2.5500 Saktipur;23.8640 Radviliškis;55.8000 Norridge;41.9637 Harper Woods;42.4390 Parilla;17.9119 Karrānah;26.2306 Bou Fekrane;33.7667 Marauatpur;25.6165 Zubin Potok;42.9167 Santa Ana;30.5406 Haddon;39.9063 Berkley;42.4986 Chāndpura;25.4966 Holyhead;53.3090 Parkstone;50.7100 Phôngsali;21.6833 Calvizzano;40.9000 Chinsali;-10.5522 Hazel Park;42.4619 Campbelltown;-34.0733 Las Veredas;23.1500 Sukurhutu;23.4433 Kenmore;42.9646 Wordsley;52.4830 Liestal;47.4667 Trbovlje;46.1500 Escuintla;15.3193 Pacific Grove;36.6188 Chêne-Bougeries;46.1833 Paro;27.4333 Pānāpur Langa;25.7230 Sarpavaram;17.0102 Jasauli;26.4996 Culcheth;53.4517 Cromer;52.9310 North New Hyde Park;40.7460 Sākhmohan;25.6325 Ecublens;46.5276 Libertad;-34.6333 Lerma;19.8000 Quthing;-30.4001 Kidal;18.4389 Gwanda;-20.9389 West Park;25.9840 San Pedro Jicayán;16.4167 Masallı;39.0342 Masho Khel;33.9103 Fayrōz Kōh;34.5225 Nalerigu;10.5333 Gaurihar Khāliqnagar;25.9356 Leopold;-38.1892 South Farmingdale;40.7175 Woolton;53.3740 Katarmāla;25.5036 Escaldes-Engordany;42.5089 Leāma;26.2140 Baucau;-8.4667 Halawa;21.3753 Forécariah;9.4300 Killamarsh;53.3205 West University Place;29.7157 Huétor Vega;37.1500 Canegrate;45.5667 Sisauna;26.1320 Saint-André-les-Vergers;48.2797 Harpur Bochaha;25.5919 Highland Park;40.5006 Bhabānipur;24.7153 Bandlagūda;17.3543 Parsa;25.7721 Le Grand-Saconnex;46.2333 Cēsis;57.3167 Sandridge;51.7808 Fairview;40.8182 Port Antonio;18.1757 Begampur;22.3705 Jesenice;46.4366 Abergavenny;51.8240 El Menzel;33.8389 Warsop;53.2000 Kotia;26.2750 Marabella;10.3000 Gourock;55.9538 Chiautla de Tapia;18.3000 Olton;52.4377 San Josecito;9.8885 Tadjourah;11.7833 Tierra Colorada;17.1656 Boumia;32.7228 Ozurgeti;41.9406 Forres;57.6080 Whitefish Bay;43.1131 Eisenstadt;47.8500 Bandwār;25.5093 Hwlffordd;51.8000 Garh Sisai;25.6253 Oulad Ayyad;32.3333 Ahirauliyā;26.5091 Villa Unión;23.9667 Belobaka;-15.6833 Koog aan de Zaan;52.4667 Sandown;50.6551 Bottesford;53.5521 Rorschach;47.4667 Berriozar;42.8361 Lake Stickney;47.8733 La Pointe;19.9500 Debar;41.5250 Sonoita;31.8614 Bang Phlat;13.8247 Bargoed;51.6900 Lykóvrysi;38.0667 Salida;37.7083 Progreso;32.5842 Hambantota;6.1244 Parlier;36.6087 Djanet;24.5550 Biassono;45.6333 Oyam;2.2350 Maxcanú;20.5833 Coacoatzintla;19.6500 Deodha;25.7815 Muttanampālaiyam;11.0845 Māmidipalli;17.2516 Magugpo Poblacion;7.3821 Ewa Beach;21.3181 Escazú;9.9160 Hœnheim;48.6242 Sahtāh;25.8567 Trentham;52.9663 Naxxar;35.9150 Sigulda;57.1500 Malvinas Argentinas;-31.3697 Busumbala;13.3333 Soalandy;-18.9903 Dhanupra;25.6992 Pincourt;45.3833 Cowley;51.5280 Baruun-Urt;46.6814 Key Biscayne;25.6908 Ar Rommani;33.5333 Kemp Mill;39.0412 Falls Church;38.8847 Amtala;22.2200 Ninga;25.4616 Wilkinsburg;40.4442 Muna;20.4800 Stiring-Wendel;49.2000 Carnoustie;56.5010 Lemon Hill;38.5172 Tonyrefail;51.5840 East Rancho Dominguez;33.8949 Bariārpur Kāndh;25.9391 Clarkston;33.8117 Brislington;51.4316 Husepur;26.4609 Ucar;40.5183 Bharokhara;25.8655 Bilauri;25.7703 Candelaria;18.1835 Chicago Ridge;41.7034 Santiago Tangamandapio;19.9500 Aphaur;25.8828 Thames Ditton;51.3890 Cudworth;53.5784 Kavaratti;10.5626 Ardmore;40.0033 Wealdstone;51.5957 Birsfelden;47.5533 Marsaskala;35.8625 Newport;39.0856 North Fair Oaks;37.4754 Dasraha Bhogrājpur;25.5691 Vellānūr;13.1500 Mathila;25.4715 Neftçala;39.3586 Saidpur;33.7421 Forest Park;41.8683 Haydock;53.4678 Villars-sur-Glâne;46.7833 Jamsaut;25.6100 Ville-d’Avray;48.8261 Pākkam;13.1436 Bhainsahi;26.4404 Gillingham;51.0375 Lambeth;51.4900 Linslade;51.9243 Belwa;26.1056 Zefýri;38.0667 Sai Kung Tuk;22.3814 Nor Hachn;40.3019 Sarafand;33.4517 Hanzviur;34.1400 Edgewater;40.8237 Flower Hill;39.1676 East Cleveland;41.5317 Kaabong;3.5200 Street;51.1235 Oakham;52.6705 Ban Bang Sai;13.3847 Yuscarán;13.9417 Raynes Park;51.4033 Velivennu;16.8443 Negēlē;5.3167 Hawaiian Gardens;33.8304 Bukedea;1.3475 Willowick;41.6342 Canatlán;24.5200 Gladeview;25.8395 Khāngāon;25.5146 Kronshagen;54.3333 San Pedro Ixcatlán;18.1500 Rāmpur Kudarkatti;26.0672 Bharra;25.4398 Bhālpatti;26.1853 Pierre;44.3748 Astley;53.5008 Federal Heights;39.8651 Cetinje;42.3800 Ráth Tó;53.5060 Talant;47.3364 Olhanpur;25.8855 Mayilādi;8.1550 Collingswood;39.9160 Sjenica;43.2667 Karmauli;26.4502 Bishopstoke;50.9679 Begampur;22.7400 Cayey;18.1150 Akhaltsikhe;41.6389 Kippax;53.7669 Déville-lès-Rouen;49.4697 Bāgnān;22.4700 Bāwāli;22.4150 Ryhope;54.8679 Kalinagar;22.4383 Pelsall;52.6310 San Gregorio di Catania;37.5667 Natshal;22.1957 Huntington;38.7916 Birchington;51.3770 Roselle Park;40.6653 Bhansia;26.0992 Belauncha;26.2124 University Heights;41.4948 Chaital;22.5106 Nao Kothi;25.5196 Etchojoa;26.8667 Kuressaare;58.2500 Dugny;48.9536 Stocksbridge;53.4780 Risca;51.6080 Flixton;53.4470 Larbert;56.0229 González;22.8281 Dishāshah;28.9831 Villecresnes;48.7214 Chauki;25.4573 Plav;42.6000 Papraur;25.4413 Mīt Damsīs;30.8267 Bexley;39.9650 Waimalu;21.3913 Saint Ann’s Bay;18.4360 Pietà;35.8931 Tovuz;40.9922 Westmont;39.9082 Pantelhó;17.0067 Ermoúpoli;37.4333 Akil;20.2656 Sultānpur;25.6214 San Luis de La Loma;17.2714 Ascot;51.4084 Hemiksem;51.1500 Konand;25.1729 Kibiito;0.4775 Borsbeek;51.2000 Bar;42.1000 Shorewood;43.0913 Kumi;1.4608 Lemington;54.9720 Ndélé;8.4092 Bithān;25.6954 Épinay-sur-Orge;48.6739 Henley on Thames;51.5357 Olympia Heights;25.7240 Royston;53.6100 Ang Thong;14.5925 Shtërpcë;42.2333 Craponne;45.7453 Chichihualco;17.6550 Aberaman;51.7000 Holbeach;52.8037 Tavistock;50.5450 Liskeard;50.4536 New Silksworth;54.8710 Bradford-on-Avon;51.3470 Mesrā;23.4339 Little Falls;40.8762 Hirnyk;48.0500 Chatteris;52.4560 Wyandanch;40.7496 Männedorf;47.2553 Tres Ríos;9.9072 Horsell;51.3286 Shāhpur Baghauni;25.8831 New Carrollton;38.9656 Rahiār Kunchi;25.8627 Indiana;40.6220 Paura Madan Singh;25.7917 Imsida;35.8978 Nilavārappatti;11.6081 Marton;54.5372 Chanteloup-les-Vignes;48.9783 Shepton Mallet;51.1930 Wicklow;52.9779 Đà Nẵng;16.0748 Seonār;25.4142 Boudenib;31.9497 Whitchurch;52.9690 Panamarattuppatti;11.5620 Capodrise;41.0500 Ponteland;55.0480 La Grange Park;41.8308 Samayanallūr;9.9792 Castlebar;53.8608 Ayapango;19.1264 Buckhurst Hill;51.6320 Sinop;42.0267 Mānsinghpur Bijrauli;25.7552 Stanwell;51.4570 Ormesson-sur-Marne;48.7858 Newport;52.7691 Eccleston;53.4539 Dhāmua;22.2875 Beaumont;45.7517 Bothell East;47.8064 Koila Dewa;26.4079 Tetela del Volcán;18.8931 Cupar;56.3200 Dudhpura;25.7964 Bardīha Turki;25.8059 Platón Sánchez;21.2833 Arniya;25.7150 Pachāhi;26.1932 Kaith;25.4776 Maromme;49.4819 Domžale;46.1394 Stranraer;54.9020 Gżira;35.9050 Kumharsan;25.6421 Lathasepura;25.8131 Bāzid Chak Kasturi;25.6559 Somnāha;25.9518 Tixkokob;21.0022 Longbenton;55.0000 Corinda;-27.4833 Acanceh;20.8133 Sečanj;45.3667 Avocado Heights;34.0391 Swieqi;35.9208 Coatesville;39.9849 Kingston;41.2652 Mudichchur;12.9110 Carpinteria;34.3962 Mariental;-24.6333 Drochia;48.0333 Sahuli;26.1118 Żebbuġ;35.8731 Woodland Park;40.8904 Delémont;47.3667 Adalpur;26.1910 Amwa Majhār;26.7362 Nieuw Nickerie;5.9333 Maili;21.4133 Meliana;39.5272 Lydney;51.7286 Mannarai;11.1172 Barnoldswick;53.9147 Pajacuarán;20.1178 Montigny-en-Gohelle;50.4278 La Trinitaria;16.1191 Druskininkai;54.0167 Phulmālik;25.4132 Jarville-la-Malgrange;48.6694 Kibuye;-2.0617 Ridgefield Park;40.8543 Rangvāsa;22.6435 Handsworth;53.3700 Tubmanburg;6.8667 Novi Bečej;45.6000 Amolatar;1.6350 Kargahiā Purab;26.7887 Kampel;22.6167 Bocas del Toro;9.3403 Chettipālaiyam;10.9125 Pongalūr;10.9654 Evington;52.6210 Crosne;48.7147 Nārsingi;17.3876 San Ignacio Cohuirimpo;27.0500 Wilnecote;52.6081 Inverurie;57.2800 Cuatro Ciénegas de Carranza;26.9861 San Juanito;27.9700 Launceston;50.6370 Bhawānīpur;26.2385 Steinbach am Taunus;50.1667 Puck;54.7000 Serere;1.5000 Nova Gorica;45.9558 Żejtun;35.8556 Hellesdon;52.6485 Bishunpur;25.8029 Broadstone;50.7605 Rāmnagar Bankat;26.7595 Marquette-lès-Lille;50.6758 Abasingammedda;7.3167 Älta;59.2500 Steinkjer;64.0148 Na Sceirí;53.5828 Tlahualilo de Zaragoza;26.1083 Perūr;10.9752 Littleport;52.4568 Ghanīpur Bejha;25.9281 Qutubpur;25.6279 Pelham;40.9000 Croissy-sur-Seine;48.8778 Alagappapuram;8.1468 Nairn;57.5860 Sugarland Run;39.0309 Gaillard;46.1850 Psychikó;38.0108 Procida;40.7667 Ialoveni;46.9500 Home Gardens;33.8783 Mokarrampur;26.2176 Rāmpur Rajwa;25.7057 Dumri;25.7350 Valga;57.7833 Naranja;25.5164 Fords;40.5359 Cornelius;45.5188 Obo;5.4000 Daru;-9.0833 Tilehurst;51.4579 Srîfa;33.2814 Lamphun;18.5864 Caudebec-lès-Elbeuf;49.2808 Ifield;51.1234 Grover Beach;35.1204 Multi;22.2847 Chilmil;25.4563 Red Bank;40.3480 Sītalpur;25.7764 Pierre-Bénite;45.7036 Tottington;53.6130 Tinqueux;49.2500 Amesbury;51.1730 Cuapiaxtla de Madero;18.9167 Valadares;41.0937 Rokiškis;55.9667 Mahamda;25.8108 San Antonio;9.9781 Emeryville;37.8382 Shildon;54.6300 Aţ Ţūr;28.2417 Mauji;25.6585 Chai Nat;15.1872 Chāndpur;22.4368 Nayānagar;25.7465 Phaphot;25.6817 Helston;50.1000 Solindābād;25.8463 Morsand;25.9335 Sāhāpur;22.5200 Aberbargoed;51.6968 Kanoni;0.1728 Choppington;55.1450 Kotor;42.4300 Salisbury;40.7454 Ciudad Tula;23.0000 Ghoswāri;25.4687 Pipra Dewās;25.4636 Zaandijk;52.4667 Sihma;25.6992 San Ġiljan;35.9186 Hînceşti;46.8167 Attard;35.8928 Chapeltown;53.4620 Tərtər;40.3450 Dumri;25.4750 Krapina;46.1600 Nueva Palmira;-33.8833 Las Tablas;7.7667 Sîngerei;47.6333 Sohāna;30.6833 Culfa;38.9558 Avranches;48.6844 Sānrha;25.7979 Gorey;52.6770 Caldicot;51.5910 Qufādah;28.5812 Tlalixtac de Cabrera;17.0667 San Marzano sul Sarno;40.7697 Teotlaltzingo;19.2326 Igny;48.7422 Encamp;42.5361 Fosses;49.0981 Thorigny-sur-Marne;48.8788 Tramore;52.1588 Masar;25.5577 Simarwāra Durgāpur;25.8520 Somerville;40.5696 Bulgan;48.8119 Dunbar;56.0027 Koini;26.4226 Kaithinia;26.2317 Lālganj;25.7297 Meghaul;25.6400 San Gregorio Atzompa;19.0224 Ettimadai;10.8911 Taraclia;45.9000 Zamora;-4.0692 Warfield;51.4420 Ganvié;6.4667 Muthābana;24.9203 North Merrick;40.6871 Anáhuac;28.4800 Võru;57.8486 Beyləqan;39.7756 Mahisānrh;25.7358 Mandalgovĭ;45.7667 Patchogue;40.7621 Cesa;40.9667 Kondaparti;17.9219 Glenfield;52.6491 Hithadhoo;-0.6094 Māmobihāt;26.1466 Rāmkali;22.3351 Aurāhi;26.0355 Broughton Astley;52.5278 Khunays;35.7122 Mālīnagar;25.9967 Amgachia;22.4156 Bararam;26.1572 Dhangaraha;26.0204 Kilchberg;47.3247 Naruār;26.2388 Kanchanpur;25.6636 Amatlán de los Reyes;18.8457 Xicoténcatl;22.9958 Ga-Kgapane;-23.6490 Hakkila;60.2869 Chicalim;15.3994 Alum Rock;37.3694 Río Grande;18.3789 Qalāt;32.1061 Manatí;18.4283 Gangāpur Athar;25.8214 Eybens;45.1486 Tekit;20.5322 Sikandarpur;25.3256 Bamaiya Harlāl;25.6664 Assomada;15.0960 Ouédo;6.4833 Aioun;16.6667 Choix;26.7061 Rogerstone;51.5906 Sallaumines;50.4197 Newman;37.3156 Făleşti;47.5722 Neuville-lès-Dieppe;49.9267 Jagatpur;26.3738 Tzucacab;20.0708 Chuy;-33.6964 Ferney-Voltaire;46.2558 Hasbrouck Heights;40.8618 Caño Martin Peña;18.4309 Barwell;52.5682 Ikkarai Boluvāmpatti;10.9669 South Normanton;53.1070 Kilsyth;55.9800 Floreşti;47.8933 Izola;45.5344 Malaudh;30.6333 Gamharia;26.0561 Cimişlia;46.5167 Horbury;53.6595 Latifpur;22.3777 River Edge;40.9269 Robertsport;6.7500 Khānpur Khairanti;26.1456 South Miami;25.7079 Bougival;48.8650 Otumba;19.6989 Llantwit Major;51.4062 Semri;25.6246 Kaithwār;26.1678 Clayton le Moors;53.7750 Hathaura;26.1744 Nakasongola;1.3150 Yeadon;39.9325 Boxley;51.3024 Malaimāchchampatti;10.9058 Coahuitlán;20.2667 Rabat;35.8817 Jahāngīrpur Sālkhani;25.8335 Cam;51.7011 University of California-Santa Barbara;34.4151 Serravalle;43.9694 Telkathu;26.1124 Murska Sobota;46.6667 Hārua;24.5249 Odayārpatti;11.6663 Widnau;47.3997 Byureghavan;40.3147 Khundāwandpur;25.6689 Ukhāi Purbāri Patti;26.2471 Vanimo;-2.6667 Ban Son Loi;13.9122 Biltine;14.5275 Żurrieq;35.8292 Bhachhi;26.3404 Thakurainia;26.2098 Higuera de Zaragoza;25.9500 Bures-sur-Yvette;48.6967 Guttenberg;40.7928 Wallington;40.8536 Mamnūr;17.9092 Elektrėnai;54.7861 Mirzānagar;25.7726 Rossmoor;33.7887 Nawānagar;25.3861 North Wantagh;40.6983 Bradwell;52.0500 Bound Brook;40.5676 Berane;42.8400 Chalkāri;23.7582 Kundiawa;-6.0167 Karsaut;26.1298 An Cabhán;53.9910 Nāzira;22.2180 Signal Hill;33.8030 Balzan;35.8981 Škofja Loka;46.1672 La Riviera;38.5683 Bol;13.4600 Bhāsaula Dānāpur;25.5509 Oak Hills;45.5405 Epalinges;46.5500 La Junta;28.4778 Le Plessis-Bouchard;49.0028 Sidney;48.6506 Gurmia;25.8190 Pontardulais;51.7100 Sāgarpur;26.2306 Bhadwār;25.5576 Avanāshipālaiyam;10.9696 Pahārpur;25.7225 Chilón;17.1055 Mahadipur;24.8566 Beauchamp;49.0139 Sakaddi;25.5771 Casapulla;41.0667 Leven;56.1950 Halachó;20.4764 Wilton Manors;26.1593 Le Portel;50.7067 Vaucresson;48.8425 Radstock;51.2930 Naifaru;5.4444 Clawson;42.5367 Sanniquellie;7.3622 Iarpur;22.2998 Weißenthurm;50.4144 Sibkund;25.3041 Mariehamn;60.1000 Sandiacre;52.9230 Gaurdah;25.7097 Qax;41.4225 Pachrukhi;26.1593 Fatehpur Bāla;25.8338 Hillcrest;35.3790 Shanhūr;25.8604 Groslay;48.9867 Katrīdih;25.0829 Jasauli Patti;26.4912 Hazrat Shiura;25.5976 Ḩadībū;12.6500 Cherry Creek;39.6094 Ban Chang Phuak;18.8032 Suffern;41.1138 Glastonbury;51.1485 Piedmont;37.8225 Pérenchies;50.6681 Rāmpur;25.9256 Bonhill;55.9830 Sant’Agnello;40.6294 Bucksburn;57.1770 View Park-Windsor Hills;33.9955 Cullercoats;55.0330 Kahla;26.3747 Nālikkalpatti;11.6021 Saraunja;25.7690 Tekpanja;22.2101 Torre Boldone;45.7167 Lohna;26.2314 San Juan Ixcaquixtla;18.4500 Kandiyankovil;11.0147 Akjoujt;19.7500 Aucamville;43.6686 Évian-les-Bains;46.4006 Longford;53.7270 Madhuban;25.8838 Rye;50.9500 Gadaul;26.1287 Magas;43.1667 North Lindenhurst;40.7072 Amiāwār;25.0405 Cospicua;35.8822 Chānp;26.1951 Tataltepec de Valdés;16.3064 Dayr as Sanqūrīyah;28.4833 Summit;41.7877 Rezina;47.7333 Loikaw;19.6742 Ville-la-Grand;46.2022 Hucclecote;51.8500 Great Neck;40.8029 Chak Pahār;25.7986 Hetanpur;25.5821 East Bakersfield;35.3832 Bedwas;51.5926 Paola;35.8728 Chak Habib;25.7238 Lansdowne;39.9408 Northbrook;39.2467 Montmeló;41.5547 Waipio;21.4143 Anenii Noi;46.8833 Brevik;59.3500 Reinosa;43.0019 Little Ferry;40.8464 Strumica;41.4375 Karuzi;-3.1000 Bakhra;26.0333 Vinica;41.8828 Azīzpur Chānde;25.7932 Beni Abbès;30.0800 Brimington;53.2580 Chakla Waini;25.9081 Probištip;41.9936 Nijgaon Parānpur;25.1559 West Auckland;54.6318 Sānwas;25.1795 Amuru;2.8186 Călăraşi;47.2500 Jõhvi;59.3575 Kanyākulam;8.2012 Pokhraira;26.0711 Ahumada;30.6186 Birżebbuġa;35.8256 Pattanam;10.9808 Lázaro Cárdenas;28.3897 Atherstone;52.5787 Tokatippa;16.2836 Daşkəsən;40.5244 Salinas;-34.7833 Kralendijk;12.1444 Graçanicë;42.6000 Westwood Lakes;25.7237 Bela Crkva;44.8975 Roatán;16.3230 Timberlane;29.8781 Darby;39.9210 West Haverstraw;41.2063 Ayotoxco de Guerrero;20.1000 Citrus;34.1160 Del Aire;33.9167 Kirundo;-2.5900 Al Maḩwīt;15.4694 Ada;45.8000 Jurbarkas;55.0833 Charuānwān;25.1348 Siparia;10.1333 Trat;12.2417 Mellieħa;35.9564 Inwood;40.6219 Haapsalu;58.9394 Lascano;-33.6739 Potomac Park;35.3636 Sarnen;46.8961 Coamo;18.0765 Phangnga;8.4644 Santana;0.2583 Biržai;56.2000 Choyr;46.3606 Ordubad;38.9081 Bukomansimbi;-0.1667 Olaine;56.7833 Watervliet;42.7243 Butha-Buthe;-28.7833 Naftalan;40.5067 Bududa;1.0100 Woodlyn;39.8774 Altdorf;46.8667 Vilkaviškis;54.6500 Samux;40.7700 New Hyde Park;40.7323 Richmond Heights;25.6347 Kwale;-4.1744 La Massana;42.5444 Birao;10.2940 Muyinga;-2.8500 Neves;0.3592 Brookdale;40.8348 Nisporeni;47.0833 Kuldīga;56.9667 East Rockaway;40.6432 Vrnjačka Banja;43.6236 Nenagh;52.8632 Mojkovac;42.9600 West Perrine;25.6061 Ħamrun;35.8861 Blacklick Estates;39.9049 Maywood;40.9025 Capitola;36.9773 Roslyn;40.1311 Kanjiža;46.0667 Brentwood;40.3734 East Whittier;33.9244 Lake Hiawatha;40.8816 Nueva Ocotepeque;14.4333 Castillos;-34.1989 Bauska;56.4083 Wood-Ridge;40.8508 August;37.9797 Charter Oak;34.1025 Stonegate;39.5357 Katakwi;1.9150 Bulisa;2.1217 Keansburg;40.4469 Jacinto City;29.7663 Lincoln Village;39.9532 Pájaros;18.3610 Makamba;-4.1333 Basco;20.4500 Raseiniai;55.3667 Saldus;56.6667 Tshabong;-26.0200 Orange Cove;36.6211 Boulder Hill;41.7113 Auki;-8.7667 Tivat;42.4300 Postojna;45.7759 West Athens;33.9235 Thinadhoo;0.5302 Pedra Badejo;15.1370 Bladensburg;38.9424 Aiea;21.3865 River Road;44.0833 Bois-des-Filion;45.6667 Kirkwall;58.9810 Rožaje;42.8400 Hani i Elezit;42.1475 New Square;41.1410 Plainedge;40.7241 Samdrup Jongkhar;26.8000 Kolonia;6.9639 Ntchisi;-13.3667 Vrhnika;45.9624 Marina del Rey;33.9765 Logatec;45.9167 South Amboy;40.4852 Gizo;-8.1056 Rîşcani;47.9572 Sant Julià de Lòria;42.4650 Leisure World;39.1023 Leonia;40.8638 Victoria;36.0436 Kasane;-17.8167 Wangdue Phodrang;27.4833 Seven Corners;38.8658 Žabalj;45.3667 Evinayong;1.4500 Coral Hills;38.8706 Siġġiewi;35.8542 Grosuplje;45.9551 Stans;46.9500 Dangriga;16.9667 Amuria;2.0300 Thyolo;-16.0667 Conshohocken;40.0772 Ewo;-0.8667 Lerwick;60.1550 Manatuto;-8.5167 Odžaci;45.5167 Temple Hills;38.8106 Derby;39.8400 Westgate;26.6994 Harwood Heights;41.9663 Haledon;40.9363 Rio Claro;10.3042 Sremski Karlovci;45.2000 Prienai;54.6333 Gleno;-8.7239 Stony Brook University;40.9098 Collingdale;39.9151 Talsi;57.2444 Mokhotlong;-29.2885 Bogota;40.8751 Corozal;18.4000 Monaghan;54.2478 Caldwell;40.8389 Joniškis;56.2333 North Kensington;39.0393 Dobele;56.6167 Rakai;-0.7100 Glodeni;47.7667 Gədəbəy;40.5656 Kočevje;45.6430 La Cresta;35.3972 Sorø;55.4333 Pazin;45.2403 Audubon;39.8906 Cheviot;39.1577 Paide;58.8833 Hillcrest;41.1298 Anykščiai;55.5333 Swissvale;40.4206 Buba;11.5833 Malverne;40.6746 University of California-Davis;38.5378 Basarabeasca;46.3336 Zərdab;40.2183 Rubirizi;-0.2661 Broadview Park;26.0978 Varėna;54.2111 Krško;45.9500 Phalombe;-15.8033 Funadhoo;6.1482 Kaišiadorys;54.8667 Brokopondo;5.0667 Tevragh Zeina;18.0994 Balakən;41.7258 Slovenska Bistrica;46.3903 Naujoji Akmenė;56.3167 Koné;-21.0590 Beočin;45.2000 Alebtong;2.2500 Port Maria;18.3702 Nangan;26.1598 Sembabule;-0.0800 Mtskheta;41.8464 Sokobanja;43.6500 Greymouth;-42.4500 Montpelier;44.2659 Litija;46.0667 Yeghegnadzor;39.7611 Falmouth;18.4900 Guadalupe;0.3800 Bentiu;9.2600 Ajdovščina;45.8861 Debe;10.2000 Esperanza;27.5800 Dowa;-13.6667 Luba;3.4500 Aračinovo;42.0264 Yardımlı;38.9206 Borgo Maggiore;43.9450 Vittoriosa;35.8881 Diekirch;49.8681 Sen Monorom;12.4500 Ādaži;57.0667 Kelmė;55.6333 Leova;46.4833 Ludza;56.5500 Eydhafushi;5.1038 Ravne na Koroškem;46.5437 Goranboy;40.6103 Kičevo;41.5142 Ruyigi;-3.4833 Briceni;48.3611 Lerik;38.7753 Ocniţa;48.3853 Krāslava;55.8833 Slovenj Gradec;46.5094 Mobaye;4.3167 Mongomo;1.6287 Kəlbəcər;40.1067 Lapovo;44.1833 Hola;-1.5000 Teleneşti;47.5028 Radovljica;46.3425 Aizkraukle;56.6042 Novobërdë;42.6000 Donduşeni;48.2167 Ştefan Vodă;46.5153 Trindade;0.3000 Līvāni;56.3667 In Guezzam;19.5686 Brežice;45.9048 Novi Kneževac;46.0500 Gulbene;57.1667 Kiruhura;-0.2100 Obiliq;42.6900 Oğuz;41.0708 Limbaži;57.5167 Luqa;35.8597 Salavan;15.7167 Criuleni;47.2167 Šalčininkai;54.3167 Madona;56.8500 Kovačica;45.1117 Medvode;46.1382 Ros Comáin;53.6333 Mengeš;46.1626 Kerema;-7.9667 Alūksne;57.4167 Mae Hong Son;19.3011 Bogatić;44.8333 Pasvalys;56.0594 Siteki;-26.4500 Sal Rei;16.1770 Eenhana;-17.4658 Lorengau;-2.0306 Jõgeva;58.7469 Punakha;27.5833 Põlva;58.0536 Mitoma;-0.6150 Espargos;16.7560 Kavadarci;41.4328 Jakar;27.5500 Bački Petrovac;45.3606 Schaan;47.1667 Preiļi;56.3000 Kupiškis;55.8333 Viqueque;-8.8500 Junik;42.4761 Kudahuvadhoo;2.6711 Lucea;18.4500 Rutana;-3.9167 Puerto Baquerizo Moreno;-0.9025 Rapla;58.9944 Glarus;47.0333 Cəbrayıl;39.4000 Bač;45.3833 Appenzell;47.3333 Sežana;45.7034 Zarasai;55.7333 Zagorje;46.1342 Muramvya;-3.2500 Rogaška Slatina;46.2314 Prevalje;46.5438 Echternach;49.8117 Mamushë;42.3167 Marsa;35.8833 Trakai;54.6333 Şoldăneşti;47.8167 Mwatate;-3.5047 Outapi;-17.5167 Għaxaq;35.8483 Črnomelj;45.5711 Idrija;46.0025 Ranillug;42.4920 Lija;35.9014 Balvi;57.1333 Carrick on Shannon;53.9440 Hrastnik;46.1479 Molėtai;55.2333 Sisimiut;66.9389 Stratford;-39.3333 Halba;34.5506 Xocavənd;39.7953 Fulin;29.3489 Masunga;-20.6667 Gospić;44.5460 Wiltz;49.9667 Cacheu;12.2667 Babək;39.1519 Nieuw Amsterdam;5.8833 Kazlų Rūda;54.7500 Petnjica;42.9089 Mali Iđoš;45.7069 Šakiai;54.9500 Thaba-Tseka;-29.5333 Kanifing;13.4500 Triesen;47.1000 Xagħra;36.0503 Partesh;42.4019 Napak;2.1156 Skuodas;56.2667 Kalangala;-0.3214 Kirakira;-10.4500 Tarrafal;16.5660 Ķekava;56.8333 Albina;5.5000 Pembroke;35.9264 Slovenske Konjice;46.3383 Grevenmacher;49.6806 Danilovgrad;42.6100 Palé;-1.4069 Smiltene;57.4333 Liquiçá;-8.5935 Ilulissat;69.2167 Nadur;36.0381 Ignalina;55.3500 Vrapčište;41.8337 Žalec;46.2510 Punta Gorda;16.1005 Samtse;27.0333 Gevgelija;41.1392 Bu’aale;1.0833 Aībak;36.2653 Mauren;47.2167 Iklin;35.9042 Šentjur;46.2176 Bled;46.3688 Ordino;42.5550 Tuzi;42.3656 Bolama;11.5767 Eschen;47.2167 Ponta do Sol;17.2020 Irig;45.1000 Dispur;26.1500 Balzers;47.0667 Klaksvík;62.2375 Tolmin;46.1857 Viligili;0.7569 Kuala Belait;4.5828 Bururi;-3.9500 Čoka;45.9333 Šilalė;55.4833 Radoviš;41.6381 Kalkara;35.8892 Opovo;45.0519 Imqabba;35.8442 Miklavž na Dravskem Polju;46.5057 Bueng Kan;18.3672 Sevnica;46.0092 Remich;49.5444 Valka;57.7667 Pakruojis;55.9667 Fuerte Olimpo;-21.0375 Švenčionys;55.1333 Qıvraq;39.3997 Şuşa;39.7583 Black River;18.0257 Trzin;46.1353 Dravograd;46.5903 Floriana;35.8933 Tržič;46.3581 João Teves;15.0680 Nwoya;2.6350 Ilirska Bistrica;45.5679 Ta’ Xbiex;35.8992 Gudja;35.8483 Cerknica;45.7964 Marsaxlokk;35.8417 Laško;46.1563 Ruše;46.5386 Zreče;46.3750 Šempeter pri Gorici;45.9284 Dingli;35.8603 Bensonville;6.4456 La Palma;8.4100 Calheta de São Miguel;15.1860 Kirkop;35.8419 Wabag;-5.4919 Għargħur;35.9241 Canillo;42.5667 Žiri;46.0469 Gornja Radgona;46.6752 Butalangu;0.8228 Domagnano;43.9500 Kaberamaido;1.7667 Kalvarija;54.4167 Omuthiya;-18.3606 Xewkija;36.0331 Qobustan;40.5367 Ağdam;39.9833 Trebnje;45.9104 Deçan;42.5333 Kärdla;58.9981 Plandište;45.2269 Lazdijai;54.2333 Ribnica;45.7400 Piran;45.5283 Janjanbureh;13.5341 Cankuzo;-3.2194 Šoštanj;46.3798 Brezovica;46.0207 Massenya;11.4000 Ljutomer;46.5168 Imġarr;35.9197 Berovo;41.7078 São João dos Angolares;0.1333 Mongar;27.2750 Ankaran;45.5793 Kruševo;41.3700 Imtarfa;35.8908 Xai;20.6914 Colonia;9.5167 Sveti Nikole;41.8650 Tutin;42.9875 Cantemir;46.2667 Xgħajra;35.8864 Šenčur;46.2430 Alibunar;45.0806 Ivančna Gorica;45.9374 Malishevë;42.4828 Ainaro;-8.9833 Żebbuġ;36.0708 Fish Town;5.1964 Qrendi;35.8342 Škofljica;45.9836 Lenart v Slovenskih Goricah;46.5742 Ulbroka;56.9333 Buala;-8.1333 Mežica;46.5206 Železniki;46.2182 Demir Kapija;41.4114 Saulkrasti;57.2500 Golubovci;42.3344 Rietavas;55.7167 Groningen;5.8000 Metlika;45.6517 Qaqortoq;60.7222 Štore;46.2219 Għajnsielem;36.0269 Veymandoo;2.1881 Balaka;-14.9889 Şahbuz;39.4072 Birštonas;54.6028 Trashigang;27.3326 Borovnica;45.9197 Porto Inglês;15.1380 Delčevo;41.9661 Aasiaat;68.7097 Safi;35.8333 Rogašovci;46.8053 Rače;46.4529 Lethem;3.3833 Mabaruma;8.2000 Polzela;46.2809 Lendava;46.5631 Bogdanci;41.2031 Damān;20.4169 Vojnik;46.2933 Thulusdhoo;4.3742 Žitište;45.4833 Fiorentino;43.9106 Manadhoo;5.7628 Sowa Town;-20.5636 Bopolu;7.0667 Ta Khmau;11.4833 São Domingos;15.0280 Radlje ob Dravi;46.6152 Kerewan;13.5000 Radenci;46.6500 Fontana;36.0364 Aranđelovac;44.3042 Rasdhoo;4.2631 Dhihdhoo;6.8874 Mozirje;46.3381 Trashi Yangtse;27.5833 Barclayville;4.6797 Kolašin;42.8236 Triesenberg;47.1181 Muta;46.6167 Amudat;1.9522 Trongsa;27.4994 Negotino;41.4839 Cerklje na Gorenjskem;46.2488 Sannat;36.0244 Qala;36.0353 Šmarje;46.2281 Kllokot;42.3667 Cestos City;5.4667 Tofol;5.3258 Ruggell;47.2450 Acquaviva;43.9457 Mahdia;5.2667 Spodnje Hoče;46.4996 Ivanjica;43.5811 Vevčani;41.2403 Črna na Koroškem;46.4697 Beltinci;46.6060 Ig;45.9692 Kabarnet;0.4940 Afega;-13.8019 Prebold;46.2369 Nida;55.3033 Pivka;45.6833 Vianden;49.9336 Zhemgang;27.2134 Sariwŏn-si;38.5242 Laçın;39.6408 Kerċem;36.0406 Lovrenc na Pohorju;46.5381 Capellen;49.6444 Heydərabad;39.7203 Onverwacht;5.5931 Naklo;46.2749 Gradsko;41.5775 Straža;45.7864 Cidade Velha;14.9160 Priboj;43.5836 Semič;45.6546 Migori;-1.0634 Totness;5.8775 Spodnji Duplek;46.5053 Šentjernej;45.8389 Miren;45.8962 Felidhoo;3.4717 Chiradzulu;-15.7000 Same;-9.0000 Jagodina;43.9750 Sofifi;0.7244 Vipava;45.8476 Ambrolauri;42.5194 Tulagi;-9.1014 Zgornja Kungota;46.6388 Radeče;46.0658 Valandovo;41.3169 Damongo;9.0833 Djibloho;1.5889 Ormož;46.4086 Vuzenica;46.5992 Lifford;54.8340 Munxar;36.0303 Kriva Palanka;42.2017 Bohinjska Bistrica;46.2742 Divača;45.6820 Kratovo;42.0783 Žabljak;43.1550 Gamprin;47.2167 Šmartno;46.0441 Krivogaštani;41.3358 Nyamira;-0.5633 Zrnovci;41.8542 Mislinja;46.4431 Sopište;41.9500 Vodice;46.1833 Trim;53.5550 Rogatec;46.2243 Għarb;36.0611 Odranci;46.5856 Selnica ob Dravi;46.5514 Dragomer;46.0189 Nazarje;46.3202 Vreed-en-Hoop;6.8076 Oplotnica;46.3860 Šabac;44.7558 Machinga;-14.9667 Varakļāni;56.6000 Mirna;45.9476 Pehčevo;41.7592 Ropaži;56.9718 Gusinje;42.5619 Turnišče;46.6194 Plasnica;41.4667 Smederevo;44.6633 Pārūn;35.4167 Arilje;43.7531 Tearce;42.0775 Vladičin Han;42.7000 Cerkno;46.1283 Xızı;40.9078 Sotik Post;-0.7813 Bovec;46.3378 Nova Varoš;43.4667 Kanal;46.0880 Poljčane;46.3135 Clervaux;50.0500 Isangel;-19.5300 Murang’a;-0.7167 Tabor;46.2108 Nova Crnja;45.6667 Mogila;41.1083 Moravče;46.1356 Horjul;46.0230 Kuršumlija;43.1408 Dornava;46.4299 Kranjska Gora;46.4854 Aleksandrovac;43.4553 Xékong;15.3503 Šmartno;46.3297 Kidričevo;46.4060 Xocalı;39.9111 Leulumoega;-13.8167 Nilandhoo;3.0567 Taro;-6.7111 Schellenberg;47.2336 Santo António;1.6367 Plužine;43.1500 Gornji Milanovac;44.0212 Vitanje;46.3825 Faetano;43.9256 Gorenja Vas;46.1069 Safotu;-13.4528 Svrljig;43.4167 Novo Selo;41.4128 Ljubno;46.3333 Oranjestad;17.4833 Novaci;41.0419 Rosoman;41.5161 Lata;-10.7167 Komenda;46.2073 Črenšovci;46.5747 Montegiardino;43.9089 Benedikt;46.6065 ‘Amrān;15.6594 Raška;43.2856 Pirot;43.1519 Prokuplje;43.2339 Stari Trg;45.7142 Mirna Peč;45.8580 Asau;-13.5194 Brus;43.3836 Surdulica;42.6950 Bosilovo;41.4406 Daga;27.0667 Gorišnica;46.4122 Ub;44.4500 Lučani;43.8667 Muli;2.9217 Gornji Grad;46.2961 Pesnica;46.6099 Kobarid;46.2464 Andrijevica;42.7300 Središče ob Dravi;46.3933 Sveta Trojica v Slovenskih Goricah;46.5756 Veržej;46.5818 Lufilufi;-13.8500 Dobrova;46.0533 Videm pri Ptuju;46.3699 Požega;43.8459 Neno;-15.3981 Isale;-3.3444 Dobrovnik;46.6515 Jegunovce;42.0731 Siaya;0.0667 Velika Polana;46.5719 Mahonda;-5.9897 Konče;41.4958 Dolenjske Toplice;45.7545 Kostanjevica na Krki;45.8463 Smederevska Palanka;44.3655 Dol;46.0884 Chikwawa;-16.0350 Preddvor;46.3052 Zgornja Hajdina;46.4057 San Lawrenz;36.0550 Namutumba;0.8363 Fonadhoo;1.8342 Starše;46.4664 Sodražica;45.7616 Babušnica;43.0680 Vlasotince;42.9667 Ljubovija;44.1869 Lajkovac;44.3667 Rostuša;41.6100 Majšperk;46.3500 Kosjerić;44.0000 P’yŏngsŏng-si;39.2828 Mionica;44.2500 Velike Lašče;45.8363 Bela Palanka;43.2178 Asaba;6.1833 Moravske-Toplice;46.6927 Videm;45.8484 Dobrna;46.3381 Vailoa;-13.7558 Aïn Defla;36.2583 Bukwo;1.2928 Nkurenkuru;-17.6167 Obleševo;41.8833 Kapenguria;1.2333 Puconci;46.7039 Širvintos;55.0361 Mokronog;45.9426 Rankovce;42.1719 Petrovec;41.9389 Rečica;46.3247 Yenagoa;4.9267 Umm Şalāl ‘Alī;25.4697 Lakatoro;-16.1069 Kozje;46.0740 Komen;45.8159 Križevci;46.5631 Hvalba;61.6000 Qubadlı;39.3439 Aranguez;10.6472 Maracha;3.2883 Markovci;46.3956 Šmarješke Toplice;45.8622 Požarevac;44.6200 Krupanj;44.3667 Karbinci;41.8167 Vasilevo;41.4758 Bangar;4.7086 Knjaževac;43.5000 Picos;15.0830 Luče;46.3565 Grad;46.7977 Zaječar;43.9042 Podčetrtek;46.1573 Apače;46.6967 Paraćin;43.8667 Lebane;42.9167 Lukovica;46.1686 Đà Lạt;11.9359 Binyin;1.4175 Għasri;36.0583 The Bottom;17.6261 Rača;44.2333 Mila;36.4481 Tigoa;-11.5583 Cirkulane;46.3436 Dobrovo;45.9989 Zgornje Gorje;46.3801 Saleaula;-13.4501 Bulambuli;1.1600 Šentrupert;45.9769 Aileu;-8.7281 Makedonski Brod;41.5133 Aleksinac;43.5383 Kobilje;46.6837 Vwawa;-9.1081 Planken;47.1833 Staro Nagoričane;42.2000 Zgornje Jezersko;46.3951 Osečina;44.3667 Demir Hisar;41.2208 Weno;7.4500 Jincheng;24.4167 Sveti Jurij;46.5683 Kuzma;46.8333 Despotovac;44.0833 Čajetina;43.7500 Namayingo;0.2398 Cankova;46.7193 Lozovo;41.7817 Santa Cruz;14.1167 Centar Župa;41.4775 Ljig;44.2213 Butebo;1.1944 Juršinci;46.4853 Koani;-6.1333 Imdina;35.8858 Nova Vas;45.7726 Destrnik;46.4923 Varvarin;43.7167 Skopun;61.9125 Gornji Petrovci;46.8050 Ribnica;46.5372 Kon Tum;14.3544 Šavnik;42.9500 Podlehnik;46.3361 Busesa;0.6263 Dharamshāla;32.2186 Dhuusamarreeb;5.5350 Buabidi;8.4667 Brvenica;41.9672 Belčišta;41.3028 Makole;46.3174 Crna Trava;42.8101 Trnovska Vas;46.5231 Mali Zvornik;44.3992 Jurovski Dol;46.6087 Hvannasund;62.2833 Tišina;46.6558 Šalovci;46.8250 Vitomarci;46.5248 Dogbo;6.8167 Luuka Town;0.7642 Dolneni;41.4264 Abakaliki;6.3249 Braslovče;46.2884 Blace;43.2906 Sveta Ana;46.6497 Doljevac;43.1968 Cerkvenjak;46.5660 Pombas;17.1490 Az̧ Z̧a‘āyin;25.5669 Batočina;44.1500 Hrib-Loški Potok;45.7015 Podvelka;46.5864 Nsiika;-0.2958 Negotin;44.2167 Rubanda;-1.1864 Ntara;0.0047 Škocjan;45.9069 Bistrica ob Sotli;46.0589 Toftir;62.0978 Kalungu;-0.1681 Nelspruit;-25.4745 Bupoto;0.9061 Čučer-Sandevo;42.0975 Veliko Gradište;44.7500 Porkeri;61.4814 Santa Luċija;36.0431 Dimitrovgrad;43.0167 Tomaž pri Ormožu;46.4833 Al Jabīn;14.7040 Velika Plana;44.3333 Petrovac na Mlavi;44.3783 Svilajnac;44.2167 Boljevac;43.8247 Zelenikovo;41.8867 Hodoš;46.8285 Kole;2.4286 Narok;-1.0833 Ouled Djellal;34.4167 Kladovo;44.6039 Mparo;-1.1844 Ol Kalou;-0.2730 Kasanda;0.5467 Kasaali;-0.6167 Šentilj;46.6817 Bojnik;43.0142 Kakumiro;0.7811 Želino;41.9794 Bogovinje;41.9233 Kinoni;-0.6583 Sārī;36.5633 Koronadal;6.2541 Kibingo;-0.5700 Pailin;12.8489 Studeničani;41.9158 Kara;9.5511 Solčava;46.4201 Topola;44.2525 Suai;-9.3129 Pante Macassar;-9.2000 Razkrižje;46.5182 Waitangi;-43.9514 Kalaki;1.8160 Chiesanuova;43.9061 Saltangará;62.1156 Mulifanua;-13.8333 Lospalos;-8.5167 Fort Wellington;6.4000 Ilinden;41.9945 Rabak;13.1850 Tsirang;27.0219 Tutong;4.8067 Merošina;43.2833 Viti;42.3214 Sefwi Wiawso;6.2158 Dobje;46.1369 Hongseong;36.6009 Mersch;49.7500 Muan;34.9897 Bangolo;7.0123 Saratamata;-15.2875 Strendur;62.1096 Kurumul;-5.8550 Palenga;2.5764 Lamwo;3.5300 Osilnica;45.5289 Žitorađa;43.1833 Koceljeva;44.4708 Agago;2.9847 Morant Bay;17.8819 Star Dojran;41.1865 Žužemberk;45.8339 Madīnat ash Shamāl;26.1167 Gadžin Han;43.2203 Ćićevac;43.7167 Zavrč;46.3917 Bhararisain;30.0986 Īlām;33.6374 Lipkovo;42.1553 Bosilegrad;42.5005 Vladimirci;44.6167 Haa;27.3825 Ražanj;43.6667 Tvøroyri;61.5544 Kučevo;44.4833 Boorama;9.9361 San Jose;10.1800 Sharan;33.1757 Dambai;8.0689 Medveđa;42.8333 Goaso;6.8000 Şuḩār;24.3420 Şərur;39.5544 Redange-sur-Attert;49.7652 Gaoua;10.2992 Cocieri;47.3000 Fuglafjørður;62.2448 Fámjin;61.5264 Rekovac;43.8667 Knić;43.9167 Samamea;-13.9333 Nhlangano;-27.1167 Nīlī;33.7218 Tabuk;17.4084 Sarpang;26.8639 Nabilatuk;2.0525 Nova Sintra;14.8710 Sédhiou;12.7000 Pagėgiai;55.1333 Qəbələ;40.9825 Pemagatshel;27.0333 Igreja;15.0339 Vágur;61.4733 Safotulafai;-13.6817 Qacha’s Nek;-30.1167 Žabari;44.3562 Malo Crniće;44.5667 Tsimasham;27.0989 Pala;9.3646 Dalandzadgad;43.5708 Pader;3.0500 Otuke;2.4442 Dəvəçi;41.2012 El Meghaïer;33.9506 Norðragøta;62.1990 Leava;-14.2933 Buka;-5.4219 Anouvông;18.8989 Nakapiripirit;1.9167 Satupa‘itea;-13.7659 Makedonska Kamenica;42.0208 Golubac;44.6530 Majdanpek;44.4167 Şūr;22.5667 Ribeira Brava;16.6150 Žetale;46.2750 Rustavi;42.2897 Sørvágur;62.0717 Kostel;45.5088 Resen;41.0893 Cova Figueira;14.8900 Vransko;46.1720 Eiði;62.2995 Oyrarbakki;62.2079 Pili;13.7177 Vestmanna;62.1548 Gaigirgordub;9.5586 Kyankwanzi;1.2000 Kvívík;62.1186 Phôn-Hông;18.4953 Sumba;61.4055 Sandavágur;62.0537 Trgovište;42.3514 Sola;-13.8750 Hov;61.5068 Tanjung Selor;2.8500 Viðareiði;62.3600 Žagubica;44.1979 Sandur;61.8344 El Fula;11.7175 Lupane;-18.9315 Loango;-4.6307 Laascaanood;8.4774 Ntoroko;1.0500 Húsavík;61.8099 Georgetown;-7.9286 Lhuentse;27.6500 Ceerigaabo;10.6162 Kunoy;62.2917 Skálavík;61.8314 Wé;-20.9167 Kirkja;62.3263 Sieyik;9.3833 Edinburgh of the Seven Seas;-37.0675 Skúvoy;61.7710 Gasa;27.9167 Haymā’;19.9569 Idrī;27.4471 Unión Chocó;8.0778 Bardaï;21.3533 Preševo;42.3067 Bujanovac;42.4667 Kitamilo;0.2367 Sundsvall;62.4000 Xiongzhou;38.9786 Udine;46.0667 Kalisz;51.7575 Izumisano;34.4000 Legnica;51.2083 Wakefield;53.6800 Pouytenga;12.2500 Xiegang;22.9614 Kani;35.4261 Kadaiyanallūr;9.0743 Debre Zeyit;8.7500 Luckeesarai;25.1678 Dzolokpuita;6.7862 Chikusei;36.3071 Tellicherry;11.7489 Andria;41.2167 Ad Dujayl;33.8389 Tinsukia;27.5000 Gwelej;14.7500 Babīlē;9.2167 Ra’s Ghārib;28.3597 Alaminos;16.1553 Pili;13.5561 Fengning;41.2013 Urgut Shahri;39.4190 Dhār;22.5992 Sayaxché;16.5167 Federal Way;47.3091 Hanau;50.1328 Manteca;37.7927 Oton;10.6931 Hasilpur;29.6967 Silvassa;20.2700 Tsuyama;35.0692 Bislig;8.2133 Lafayette;39.9946 Itārsi;22.6200 Arezzo;43.4633 Ballarat;-37.5608 Kārwār;14.8136 Sakata;38.9145 Abhar;36.1511 Tobolsk;58.1953 Khamīs Mushayţ;18.3000 Cottbus;51.7606 Hesperia;34.3975 La Crosse;43.8241 Al Jammālīyah;31.1792 Ifakara;-8.1000 Chía;4.8500 Breves;-1.6819 Weldiya;11.8306 Riberalta;-11.0128 Sarapul;56.4667 Mīāneh;37.4239 Senahú;15.4164 Itoshima;33.5539 Riverview;27.8227 Es Senia;35.6478 Kontagora;10.4030 Ixmiquilpan;20.4861 Kingsport;36.5224 Edinburg;26.3196 Khambhāt;22.3000 Kallūru;15.8162 Zárate;-34.0833 Reyhanlı;36.2692 Bowling Green;36.9716 Saku;36.2488 M’lang;6.9500 Koytendag;37.5000 Itaituba;-4.2758 Dongducheon;37.9167 Worcester;-33.6450 Votkinsk;57.0500 Paulínia;-22.7611 Iseyin;7.9667 Adjarra;6.5333 Mariano Roque Alonso;-25.2122 Fanyang;31.0847 Rāmnagar;29.4000 Oodweyne;9.4000 Carmel;39.9650 Chitose;42.8210 Kawkareik;16.5556 Longview;32.5193 Attock Khurd;33.7667 Tracy;37.7269 Oldham;53.5444 Trelew;-43.2500 Lugo;43.0167 Prescott Valley;34.5980 Kambar;27.5868 Hammamet;36.4000 Witten;51.4333 Munakata;33.8000 Paragominas;-2.9958 Serov;59.6000 Caotun;23.9830 Aşağıçinik;41.2719 San Cugat del Vallés;41.4735 Quilenda;-10.6333 Hadera;32.45 Tubarão;-28.4667 Bafra;41.5722 Brantford;43.1667 Beaverton;45.4779 Portsmouth;43.0580 Portsmouth;36.8468 Ocozocoautla de Espinosa;16.8000 Valença;-13.3700 Las Rozas de Madrid;40.4917 Yacuiba;-22.0153 Bodināyakkanūr;10.0000 Lorca;37.6798 Ituiutaba;-18.9667 Villupuram;11.9401 Tucuruí;-3.7678 Lysychansk;48.9169 Jamundí;3.2667 Fishers;39.9588 Cesena;44.1389 Itacoatiara;-3.1428 Vīrappanchathiram;11.3553 Ukhta;63.5667 Tomohon;1.3244 Buin;-33.7333 Barra do Piraí;-22.4700 Colón;13.7167 Paraíso;18.3961 Tandwa;26.5500 Zerakpur;30.6500 Quvasoy;40.3000 Paletwa;21.3000 Tela;15.7833 Greece;43.2460 Harihar;14.5129 Balayan;13.9333 Iida;35.5150 Caieiras;-23.3644 Do Gonbadān;30.3586 Cambé;-23.2758 Kāzerūn;29.6194 Tiddim;23.3758 Escalante;10.8333 Japeri;-22.6431 Iguatu;-6.3589 Wujiaqu;44.1670 Chākdaha;23.0800 Arifwala;30.2981 Nova Lima;-19.9858 Vanderbijlpark;-26.6992 Amalner;21.0500 Ōmura;32.9000 Longquan;40.3703 Kimilili;0.7833 Leninsk-Kuznetskiy;54.6575 Kelo;9.3100 Mezhdurechensk;53.6864 Dovzhansk;48.0778 Sandy;40.5709 Tiruchengodu;11.3790 Bendigo;-36.7500 San Tan Valley;33.1786 Kamisu;35.8899 Longkeng;24.0341 Muzaffarabad;34.3583 Sivakāsi;9.4500 Paramagudi;9.5494 Mons;50.4500 Itaperuna;-21.2050 Jinbi;25.7356 Xiluodu;28.2360 Emmiganūr;15.7333 Nagīna;29.4430 Saint-Michel de l’Atalaye;19.3667 Pesaro;43.9167 Boli;45.7564 Vāniyambādi;12.6825 Shirē;14.1000 Tiruttani;13.1800 Aliağa;38.8008 Closepet;12.7230 Yi Xian;39.3444 Esslingen;48.7333 Hương Thủy;16.4000 Cáceres;39.4833 Shimada;34.8363 Allinagaram;10.0119 Lecce;40.3500 Hanford;36.3274 Guaíba;-30.1139 Bogo;10.7361 Erbaa;40.6667 Balamban;10.4667 Sarov;54.9333 Boca Raton;26.3752 Rubio;7.7000 Daltonganj;24.0333 Mianwali;32.5853 Yalamakūru;15.7680 Rawānduz;36.6119 Middletown;39.5033 Livonia;42.3972 San Martín Jilotepeque;14.7830 Birecik;37.0250 Jinshan;25.1496 Gerona;15.6069 Nāḩiyat al Karmah;33.3833 Solikamsk;59.6433 Siguatepeque;14.6000 Abreu e Lima;-7.9117 Barletta;41.3167 Carson;33.8374 Valongo;41.1833 Al Jīzah;31.7000 Toms River;39.9895 Dod Ballāpur;13.2920 Lagarto;-10.9169 Lawang;-7.8300 Lopez;13.8840 Bouna;9.2667 Jaworzno;50.2044 Behshahr;36.6922 Kanuma;36.5671 Gera;50.8806 Lawrence;38.9597 Grudziądz;53.4875 Alvand;36.1892 Gatchina;59.5684 Michurinsk;52.8833 Daanbantayan;11.2500 Slidell;30.2887 Bayan Hot;38.8556 Greenburgh;41.0330 Shibata;37.9479 Alessandria;44.9167 Santa Cruz Xoxocotlán;17.0296 Glazov;58.1333 Blacksburg;37.2300 Marmaris;36.8500 Helmond;51.4833 San Marcos;33.1350 Kwekwe;-18.9167 Tatakan;-6.1116 Cape Breton;46.1389 Azumino;36.3039 Manacapuru;-3.3000 Wangjia;30.6218 Monkayo;7.8239 Lemery;13.9167 Kabacan;7.1167 Yunnanyi;25.3916 Chiantla;15.3567 Itaúna;-20.0750 Menderes;38.2540 Sanjō;37.6368 Higashi-Matsuyama;36.0422 Simeulu;2.6300 Puerto Padre;21.1950 Voskresensk;55.3167 Vilhena;-12.7406 Indanan;6.0000 Mikhaylovsk;45.1333 Santa Barbara;16.0031 Goodyear;33.2614 Jaén;-5.7083 Piraquara;-25.4419 La Spezia;44.1000 Edmond;35.6689 Bedford;52.1350 Mayagüez;18.2003 Linquan;37.9513 Iserlohn;51.3833 Fall River;41.7136 Akşehir;38.3575 Contai;21.7800 Bafang;5.1500 Suffolk;36.6953 Terre Haute;39.4660 Hilversum;52.2333 Samālūţ;28.3097 Avignon;43.9500 Oss;51.7667 Phú Thọ;21.4003 Erciş;39.0311 Wukari;7.8704 Palimbang;6.2167 Akot;21.1000 Rafaela;-31.2667 Velikiye Luki;56.3333 Kilosa;-6.8300 Itumbiara;-18.4167 Hanamaki Onsen;39.3886 Düren;50.8000 Missoula;46.8751 Boundiali;9.5167 Azua;18.4600 Laiyuan;39.3515 Itapeva;-23.9819 Foumban;5.7167 Tatvan;38.5022 Rāyachoti;14.0583 Naju;35.0333 Flensburg;54.7819 Lethbridge;49.6942 Tübingen;48.5200 Roswell;34.0391 Sablayan;12.8428 Rongwo;35.5165 San Sebastián de los Reyes;40.5469 Grahamstown;-33.2996 Yumbo;3.5850 Kitakami;39.2867 Bauan;13.7917 Gießen;50.5833 Kot Kapūra;30.5833 Saint-Jean-sur-Richelieu;45.3167 Vineland;39.4653 Lavras;-21.2450 Acajutla;13.5900 Goalundo Ghāt;23.7333 Tonacatepeque;13.7833 Plantation;26.1259 Kendu Bay;-0.3596 Alafaya;28.5280 Clarington;43.9350 Satsumasendai;31.8167 Ubatuba;-23.4339 São João da Boa Vista;-21.9689 Çaycuma;41.4267 Ambohimangakely;-18.9167 Funza;4.7167 Modi‘in Makkabbim Re‘ut;31.9077 Pickering;43.8354 Southport;53.6475 Ţurayf;31.6775 Sinendé;10.3447 Mogi Mirim;-22.4319 Kirkland;47.6970 Weiyuan;23.5025 Julu;37.2200 Kāvali;14.9130 Hamilton;40.2046 Salaman;6.6333 Talara;-4.5799 Yao;12.8508 Peñaflor;-33.6167 Jauharabad;32.2919 Pāloncha;17.6018 Votuporanga;-20.4228 Jalalpur Jattan;32.7667 Caçapava;-23.1008 Hoover;33.3763 São Félix do Xingu;-6.6333 Avaré;-23.0989 Lachhmangarh Sīkar;27.8225 Cáceres;-16.0711 Lawton;34.6175 Maladzyechna;54.3208 Phusro;23.7700 Agua Prieta;31.3258 Auburn;32.6087 Chauk;20.8833 Imizuchō;36.7306 Itajubá;-22.4258 Pongotan;7.1269 Caimbambo;-12.9000 Norwalk;41.1144 O'Fallon;38.7850 Gwadar;25.1264 Cambambe;-9.7586 Victorias;10.9000 Pinamalayan;13.0364 Katiola;8.1333 Chililabombwe;-12.3667 Biga;40.2281 Eslāmābād-e Gharb;34.1094 Arsuz;36.4128 Pisa;43.7167 Fundación;10.5214 Sahagún;8.9500 São João del Rei;-21.1358 São Sebastião;-23.8040 Ma‘arrat an Nu‘mān;35.6386 Nanaimo;49.1642 Mancherāl;18.8679 Chalchuapa;13.9833 Kansk;56.2000 Kiselëvsk;54.0000 Zwickau;50.7167 Pistoia;43.9333 Chino;33.9836 Caucasia;7.9833 Compostela;7.6667 Ocaña;8.2333 Mihara;34.4000 Mijas;36.6000 Sankeshwar;16.2700 Kairāna;29.3953 Richard-Toll;16.4667 Uacu Cungo;-11.3583 San José Pinula;14.5446 Luau;-10.7044 Kadiri;14.1200 Necochea;-38.5500 Barwaaqo;3.4833 Möng Tun;20.3000 Afmadow;0.5156 El Puerto de Santa María;36.6000 Aalst;50.9383 Presidencia Roque Sáenz Peña;-26.7833 Dover;43.1887 Ciudad de Atlixco;18.9000 Polangui;13.2922 Waukegan;42.3698 Olavarría;-36.9000 Bogo;11.0167 Lucca;43.8417 Chosica;-11.9361 Buckeye;33.4314 Town 'n' Country;28.0106 Leping;37.6130 Serdar;38.9833 Cantaura;9.3005 Kamensk-Shakhtinskiy;48.3206 Banga;6.3000 Conda;-11.1667 Cheektowaga;42.9082 Surallah;6.3667 Gitarama;-2.0696 Hinigaran;10.2667 Calabanga;13.7089 Akyazı;40.6833 Bloomington;44.8306 Prijedor;44.9667 Caracase;3.7533 Toviklin;6.8333 Léré;9.7700 Ségbana;10.9278 Ban Laem Chabang;13.0833 Passi;11.1000 Shwebo;22.5667 Humpata;-15.0725 Saundatti;15.7833 Murcia;10.6000 Nirmal;19.1000 Qiantangcun;23.6742 Kūhdasht;33.5350 Candeias;-12.6678 San Francisco;30.9000 Santa Catarina Pinula;14.5644 Rāsipuram;11.4700 Calauan;14.1500 Aïn M’Lila;36.0367 Chimbas;-31.5000 Mount Pleasant;32.8537 Bhakkar;31.6278 Heerlen;50.8833 Dunkerque;51.0383 Solana;17.6522 Dongchuan;25.5086 Danlí;14.0328 Madgaon;15.2736 Halifax;53.7250 Montelíbano;7.9750 Kōka;34.9667 Florence;34.1780 San Luis;20.1881 Jamūī;24.9200 Baras;14.5167 Maravatío de Ocampo;19.8933 Paingkyon;17.0242 Ootacamund;11.4100 Newton;42.3316 Kimje;35.8017 Sakiet ez Zit;34.8000 Nirāla;19.5000 Aroroy;12.5125 Grimsby;53.5675 Echague;16.7056 La Grita;8.1333 Bayramaly;37.6167 San Fabian;16.1500 Sangrūr;30.2506 Jumri Tilaiyā;24.4289 Livermore;37.6868 Zhob;31.3417 Berisso;-34.8728 Norton;-17.8833 Sakai;36.1669 Mettupālaiyam;11.2341 Pingyuanjie;23.7472 Maiquetía;10.5958 Janzūr;32.8172 Ratingen;51.3000 Tacaná;15.2415 Makilala;6.9667 Ponta Porã;-22.5358 Changting;25.8670 Farīdkot;30.6700 São Pedro da Aldeia;-22.8389 Calaca;13.9300 Aruppukkottai;9.5139 Leshou;38.1902 Sinjār;36.3225 Jilotepec;19.9519 Rapid City;44.0716 Słupsk;54.4658 Sudbury;46.4900 São Gonçalo do Amarante;-5.7928 Jataí;-17.8808 Decatur;39.8557 Sassandra;4.9500 Dalton;34.7690 Camiling;15.6867 Conroe;30.3238 Chiclana de la Frontera;36.4167 Wislane;30.2167 Buzuluk;52.7833 Shuangshuicun;22.4356 El Ejido;36.7831 Jagüey Grande;22.5292 Dipalpur;30.6708 Baggao;17.9347 Santa Cruz do Capibaribe;-7.9569 Lünen;51.6167 Palangotu Adwār;32.7888 Anakāpalle;17.6913 Ceylanpınar;36.8461 Nahualá;14.8429 Pergamino;-33.8836 Fukuroi;34.7502 Binmaley;16.0323 Consolación del Sur;22.5083 Koidu-Bulma;8.4405 New Braunfels;29.6994 Cipolletti;-38.9333 Paredes;41.2000 Brindisi;40.6333 Ende;-8.8333 Nabua;13.4083 Denan;6.5000 Limpio;-25.1683 Tiflet;33.8931 Kumārapālaiyam;11.4416 Mobara;35.4285 Bantayan;11.2000 Chongoroi;-13.5667 Bi’r al ‘Abd;31.0181 Houndé;11.5000 Hānsi;29.1000 Jackson;42.2431 Itabaiana;-10.6850 Heyunkeng;23.9293 Wulan;36.5585 São Cristóvão;-11.0150 Menglang;22.5586 Qaraçuxur;40.3967 Ad Diwem;14.0000 Chiguayante;-36.9167 Muncie;40.1989 Villingen-Schwenningen;48.0603 Campana;-34.1667 Dingcheng;19.6803 Candelaria;3.4000 Kalpitiya;8.1667 Troy;42.5817 Shahdol;23.2800 Tuncheng;19.3633 Gubkin;51.2833 Cárdenas;23.0428 Yaofeng;35.1395 Kharian;32.8110 Widekum;5.8717 Longonjo;-12.9067 Ducheng;23.2445 São Roque;-23.5289 Kattagan;40.2000 Clarkstown;41.1319 Garulia;22.8200 Keffi;8.8464 Gotenba;35.3087 Novotroitsk;51.2039 Nīmbāhera;24.6200 Pariaman;-0.6261 Tagaytay;14.1000 Cabiao;15.2522 Lugang;24.0500 Santa Rosa Jauregui;20.7418 Homosassa Springs;28.8119 San Antonio;-33.5933 Kameoka;35.0167 Guasavito;25.5655 Treviso;45.6722 Maratturai;11.1000 Port Huron;42.9821 Yabēlo;4.8833 Sārni;22.1040 Shaoshanzhan;27.9100 Torrente;39.4365 Seoni Mālwa;22.4508 Konstanz;47.6667 Longchuan;25.1945 Kaizuka;34.4333 Hövsan;40.3744 Khowrāsgān;32.6539 Jaorā;23.6300 Bugulma;54.5364 Shchëkino;54.0000 Potiskum;11.7104 Guinobatan;13.1833 Napa;38.2975 Bāsoda;23.8515 Jastrzębie-Zdrój;49.9500 Dhārāpuram;10.7300 Iga;34.7667 Khemis Miliana;36.2600 San Pedro Sacatepéquez;14.9664 Longhua;41.3170 Sharūrah;17.4833 Kitakōriyamachō;34.6500 Ushiku;35.9794 North Vancouver;49.3641 Cacoal;-11.4386 Chiquinquirá;5.6333 Coari;-4.0850 Springdale;36.1901 Guanambi;-14.2228 Yeysk;46.7111 Sekimachi;35.4958 Newport Beach;33.6151 La Dorada;5.4538 Dharmapuri;12.1270 Woolwich;51.4880 Krishnagiri;12.5317 Al Hindīyah;32.5442 Cachoeira do Sul;-30.0394 Pilkhua;28.7120 Brossard;45.4667 Chita;35.0000 Anderson;40.0891 San Ramon;37.7624 General Rodríguez;-34.6167 Batarasa;8.6700 Kineshma;57.4333 Lake Forest;33.6605 Junín;-34.5833 Colonie;42.7396 Mhow;22.5500 Dapitan;8.6549 Warder;6.9667 Harda Khās;22.3441 Mission;26.2039 Caratinga;-19.7900 Auburn;47.3039 Brooklyn Park;45.1112 Takayama;36.1460 Derry;54.9917 Maga;10.8500 Luancheng;37.8846 Bryan;30.6650 Sumenep;-7.0049 Springfield;39.9300 Hattiesburg;31.3074 Walvisbaai;-22.9561 Moriyama;35.0586 Tshilenge;-6.2500 Bahlā’;22.9680 Korgas;44.2125 Repentigny;45.7333 Westland;42.3192 Albany;31.5776 Marl;51.6667 Dhorāji;21.7337 Sambava;-14.2667 Ciudad Mante;22.7333 Jacobina;-11.1808 Science City of Muñoz;15.7153 Arujá;-23.3967 Guider;9.9342 Chirundu;-16.0500 Senador Canedo;-16.7594 Paracatu;-17.2217 Torrevieja;37.9778 Fort Myers;26.6194 Yokotemachi;39.3113 Habiganj;24.3808 Sabanalarga;10.6300 Worms;49.6319 Concepción Tutuapa;15.2833 Bais;9.5907 Tire;38.0833 Villa Altagracia;18.6667 Dmitrov;56.3500 Vélez-Málaga;36.7833 Channapatna;12.6514 Bolinao;16.3881 Serra Talhada;-7.9858 Pará de Minas;-19.8600 Chigorodó;7.6675 Cukai;4.2332 El Milia;36.7500 Atascocita;29.9777 Tiruppattūr;12.5000 Lqoliaa;30.2908 Fredrikstad;59.2053 Cereté;8.8833 Ji’an;23.9500 Phúc Yên;21.2333 Bhawānipatna;19.9100 Zheleznogorsk;56.2500 Talavera de la Reina;39.9583 Arona;28.0996 Felipe Carrillo Puerto;19.5786 Redwood City;37.5025 Ijuí;-28.3878 Nantingcun;20.8040 Sirsilla;18.3800 Pontevedra;42.4333 Chilliwack;49.1577 Buuhoodle;8.2311 Farmington Hills;42.4860 Alton;38.9037 Lafey;3.1508 Tila;17.3667 San Antonio;15.3078 Chingleput;12.6918 Yurga;55.7231 Melbourne;28.1086 Maco;7.3619 Mian Channun;30.4397 Redditch;52.3000 Taytay;10.8167 Serrinha;-11.6639 Wutiancun;23.1852 Santana do Livramento;-30.8908 Rivadavia;-31.5303 Siaton;9.0667 Tual;-5.6368 Mānsa;29.9906 Boukoumbé;10.1833 Bhalwal;32.2656 Seropédica;-22.7439 Wuyi;37.7965 La Trinidad;15.9833 Marsala;37.7981 Kateríni;40.2667 Velbert;51.3333 Williamsburg;37.2693 Pozzuoli;40.8231 Al Ḩayy;32.1667 Mabinay;9.7333 Chelghoum el Aïd;36.1667 Tanjay;9.5167 Hukou;24.9000 Xiedian;35.4190 Nakatsu;33.5992 Ufeyn;10.6500 Bhaktapur;27.6722 Barra do Corda;-5.5058 Shīrvān;37.3967 Sorriso;-12.5450 Stockton-on-Tees;54.5700 Madera;36.9630 Kongjiazhuangcun;40.7536 Saunda;23.6600 Nowy Sącz;49.6239 Calatrava;10.6000 Karanganyar;-7.6033 Minden;52.2883 Warwick;41.7062 Cranston;41.7658 Cruzeiro do Sul;-7.6308 Rivadavia;-33.1833 Chulucanas;-5.0961 Kātoya;23.6500 Ōmihachiman;35.1283 Muroran;42.3152 Polatsk;55.4833 Baytown;29.7587 El Estor;15.5333 Shikokuchūō;33.9833 Carmen;7.3606 Largo;27.9088 Patrocínio;-18.9439 Campo Mourão;-24.0458 Chaykovskiy;56.7667 Atambua;-9.1061 Oleksandriia;48.6667 Hengelo;52.2656 Bulacan;14.7928 Bekobod;40.2167 Puerto Iguazú;-25.6000 Maple Ridge;49.2167 La Louvière;50.4667 Grosseto;42.7667 Pilar;-34.4588 Siasi;5.5462 Harlow;51.7790 Kankakee;41.1020 Peterborough;44.3000 Johns Creek;34.0333 Ubay;10.0560 Hengkou;32.7378 Varese;45.8167 Caldas;6.0900 Ust’-Ilimsk;58.0000 Ashoknagar;24.5800 Azov;47.1000 Aracruz;-19.8200 Campo Limpo;-23.2064 Kadi;23.3009 Timóteo;-19.5828 Watampone;-4.5386 Norderstedt;53.7064 Mannārgudi;10.6653 Xindian;25.3172 Imam Qasim;32.3014 Bargarh;21.3333 Flagstaff;35.1872 Planaltina;-15.4528 Paranavaí;-23.0728 Shuibian;24.1263 Buhi;13.4347 Anapa;44.8667 Dessau-Roßlau;51.8333 Kimitsu;35.3304 Matão;-21.6033 Kentaū;43.5167 Franklin;35.9200 Itá;-25.4833 Matalam;7.0833 Kāmāreddipet;18.3205 Afşin;38.2500 Gobernador Gálvez;-33.0256 Yanggao;21.3298 Lambunao;11.0500 Shiji;23.5607 Senhor do Bonfim;-10.4628 Athi River;-1.4500 Novouralsk;57.2500 Barili;10.1167 San Jose;13.8772 Sefrou;33.8300 Kashiwazaki;37.3719 Panzos;15.3986 Comitancillo;15.0906 Arni;12.6677 Taroudannt;30.4710 Kapalong;7.5854 Sint-Niklaas;51.1667 Aquiraz;-3.9008 San Cristóbal Verapaz;15.3650 Joplin;37.0757 Orihuela;38.0856 Tagoloan;8.5333 Ahenkro;7.1164 San Francisco;8.5050 Yonezawa;37.9222 Randfontein;-26.1797 Bolpur;23.6700 Parma;41.3843 Malapatan;5.9667 Layton;41.0770 Los Patios;7.8333 Meybod;32.2444 Ipojuca;-8.4000 Bafut;6.0833 Gamagōri;34.8431 Akiruno;35.7289 Anderson;34.5211 San Ramón;10.2182 Echizen;35.9035 Neumünster;54.0714 Sokcho;38.2069 Gumlā;23.0444 Samā’il;23.3000 Bulanık;39.0950 Rafḩā;29.6386 Peruvancha;17.3600 Ādīgala;10.4236 Calandala;-9.0667 Simdega;22.6200 Uvinza;-5.1036 Manmād;20.2510 Tākestān;36.0697 Suramāla;13.7500 Valdemoro;40.1908 Kottagūdem;17.5500 Lívingston;15.8300 Arsikere;13.3139 Mbalmayo;3.5167 Namsan;42.2275 Zarzis;33.5000 Andahuaylas;-13.6575 Jamshoro;25.4244 Tall ‘Afar;36.3742 Meïganga;6.5300 Jaen;15.3392 Balad;34.0164 Qo‘ng‘irot Shahri;43.0758 St. Joseph;39.7598 Poblacion;6.8000 Masallātah;32.5822 Vila do Conde;41.3528 Natori-shi;38.1715 Manresa;41.7264 San Carlos del Zulia;9.0000 Plymouth;45.0225 Koktokay;47.0004 Kathri;26.4583 Capenda Camulemba;-9.5647 Fiumicino;41.7667 Pátzcuaro;19.5164 Turhal;40.3900 Mabai;23.0188 Calarcá;4.5333 Jinhe;22.7815 Ben Gardane;33.1389 Idah;7.0833 Robles;10.3500 Yenakiieve;48.2311 Baracoa;20.3486 Patikul;6.0667 Zephyrhills;28.2409 Langarūd;37.1969 Yalta;44.4994 Jiangna;23.6128 Manhuaçu;-20.2581 Torres Vedras;39.0833 Tissamaharama;6.2833 Gyōda;36.1389 Florence;34.8303 Hannō;35.8557 Alfenas;-21.4289 Mandiraja Kulon;-7.4722 Mangatarem;15.7874 Pleasanton;37.6663 Kadoma;-18.3400 Pingyi;35.5104 Krasnyi Luch;48.1333 Eséka;3.6500 Iwamizawa;43.1962 Bauang;16.5333 Dobni Para;22.5200 Boynton Beach;26.5281 Afgooye;2.1413 Tīkamgarh;24.7472 Juventino Rosas;20.6500 Villa María;-32.4103 Sorgun;39.8144 Skarżysko-Kamienna;51.1167 Texarkana;33.4500 Eastleigh;50.9667 Klin;56.3333 Folsom;38.6668 Ozërsk;55.7500 Bahārestān;32.4797 Huaral;-11.5000 Pagbilao;13.9720 Pato Branco;-26.2289 Mooka;36.4404 Béziers;43.3476 Madīnat as Sādāt;30.3811 Pototan;10.9500 Sahuayo de Morelos;20.0575 Bamberg;49.8914 Mosigkau;51.8333 Francisco Beltrão;-26.0808 Jelenia Góra;50.9033 Telêmaco Borba;-24.3239 Limay;14.5619 Pèrèrè;9.7994 Cuamba;-14.8167 Pharr;26.1685 Koch Bihār;26.3242 Maizuru;35.4667 Kizugawa;34.7333 Arkonam;13.0778 Hengbei;23.8787 Macabebe;14.9081 Homestead;25.4665 Subulussalam;2.6422 Delmenhorst;53.0506 Toboali;-2.9997 Essaouira;31.5131 Tierralta;8.1728 Valdosta;30.8502 Dondo;-19.6167 Alīgūdarz;33.4006 Kortrijk;50.8333 Arlit;18.7333 Moju;-1.8839 Upland;34.1178 Argao;9.8833 Bamban;15.2742 Bandar Emām;30.4356 Kuvango;-14.4667 Manbij;36.5275 Pattoki;31.0214 Vyborg;60.7106 Dias d’Ávila;-12.6128 Ban Tha Khlong;14.0894 Usol’ye-Sibirskoye;52.7500 Newark;40.0706 Elizabethtown;37.7031 Hasselt;50.9305 San Pedro Pinula;14.6667 St. Augustine;29.8976 Viersen;51.2561 Rancho Cordova;38.5737 Kambam;9.7375 Bustos;14.9500 Qorveh;35.1664 Péhonko;10.2283 Kropotkin;45.4333 Chino Hills;33.9508 Pinheiro;-2.5208 Chengbin;19.9991 Chitembo;-13.5167 Perris;33.7898 Bor;56.3500 Bodhan;18.6700 Schaumburg;42.0308 Narra;9.2833 Kendall;25.6697 Balqash;46.8481 Villa Victoria;19.4333 Fray Bartolomé de Las Casas;15.8456 Linkou;45.2819 Hermosa;14.8333 Basavakalyān;17.8744 Roosendaal;51.5333 Palencia;42.0167 Camarillo;34.2230 Numan;9.4536 Puli;23.9667 Bebedouro;-20.9494 Khemis el Khechna;36.6500 Huebampo;26.6667 Guildford;51.2365 Anniston;33.6712 Três Rios;-22.1169 Santa Catalina;9.3331 Sumter;33.9392 Honjō;36.2436 Armant;25.6167 Unaí;-16.3639 Jonesboro;35.8212 Bakhmut;48.5947 Hammond;41.6168 Rheine;52.2833 Marburg;50.8100 Tecamachalco;18.8667 Santa Inês;-3.6669 Funing;39.8879 Manaoag;16.0439 Daying;37.3043 Umingan;15.9289 Carmichael;38.6318 Araripina;-7.5500 Aş Şuwayrah;32.9403 Vorkuta;67.5000 Harunabad;29.6130 Fukuchiyama;35.3000 Gelendzhik;44.5750 Arlington Heights;42.0955 Tsubame;37.6731 Balkh;36.7581 Gandía;38.9667 Nipāni;16.4800 Morgantown;39.6383 Siedlce;52.1650 Shūsh;32.1942 Bongabong;12.7469 Talakag;8.2319 Toyooka;35.5500 Nikkō;36.7198 Dongguan;39.0140 Nagua;19.3800 Al Musayyib;32.7786 Ryūgasaki;35.9116 Pyapon;16.2860 Kahror Pakka;29.6236 Caserta;41.0667 Mafra;38.9411 Montepuez;-13.1167 Aira;31.7283 Fāzilka;30.4030 Sarqan;45.4100 Ait Ali;30.1765 Chernogorsk;53.8167 Tuban;-6.9000 Itapetinga;-15.2489 El Viejo;12.6631 Jablah;35.3500 Dschang;5.4500 Balashov;51.5469 Shostka;51.8657 Gurupi;-11.7289 Dartford;51.4400 Asti;44.9000 Cotuí;19.0600 E’erguna;50.2411 So-Awa;6.4667 Wyoming;42.8908 Fancheng;39.1891 Gravatá;-8.2008 Keshod;21.3000 İdil;37.3410 Palma Soriano;20.2139 Shājāpur;23.4264 Stakhanov;48.5681 Palo;11.1583 Ciudad Sandino;12.1667 Nabari;34.6276 Ibiúna;-23.6564 Venado Tuerto;-33.7500 Sangolquí;-0.3344 Anzhero-Sudzhensk;56.0833 Troisdorf;50.8161 Chintāmani;13.4000 Infanta;14.7425 Pasco;46.2506 Houmt Souk;33.8667 Santo Ângelo;-28.2989 Kai;35.6608 Rānāghāt;23.1800 Toyomamachi-teraike;38.6918 Nahāvand;34.1886 Padre Hurtado;-33.5667 Daisen;39.4531 Berdychiv;49.8919 Lod;31.9519 Lins;-21.6786 Surendranagar;22.7000 Southfield;42.4765 Alcalá de Guadaira;37.3333 Espinal;4.2000 Santa Rosa;15.4239 Dayong;22.4707 Quixadá;-4.9708 Zhlobin;52.9000 Tocumen;9.0800 Pilar;12.9244 Jocotán;14.8167 San Ramón de la Nueva Orán;-23.1333 Wausau;44.9620 Gbawe;5.5767 Tailai;46.3909 Rochester Hills;42.6645 Villa Elisa;-25.5075 Toba Tek Singh;30.9711 Tindivanam;12.2267 Loveland;40.4166 Piotrków Trybunalski;51.4000 Jabuticabal;-21.2550 Iju;6.6107 Ovalle;-30.6000 Catacaos;-5.2653 Rio Largo;-9.4778 Wilhelmshaven;53.5286 Kengtung;21.2917 Xinglong;40.4146 Srīvilliputtūr;9.5120 Alexandria;31.2923 Pinamungahan;10.2667 Tiznit;29.7167 Goiana;-7.5608 Shadrinsk;56.1333 Zhongcheng;28.6014 Puqiancun;23.5723 Bayreuth;49.9481 Sentani;-2.5636 Arjona;10.2586 Tosu;33.3833 Kyōtanabe;34.8167 Pittsburg;38.0182 Lüneburg;53.2525 Hammond;30.5061 Dubna;56.7333 South Jordan;40.5570 Nakatsugawa;35.4876 Battle Creek;42.2985 Bethlehem;40.6266 Redenção;-8.0289 Fajardo;18.3331 Bonāb;37.3428 Sapiranga;-29.6378 Gangammapeta;18.3330 Tirumangalam;9.8216 Mangalagiri;16.4300 Kawartha Lakes;44.3500 Bombo;0.5778 Libon;13.3000 Sasagawa;37.2865 Itapira;-22.4361 Carpina;-7.8500 Ciudad Real;38.9833 Bugallon;15.9167 Ambājogāi;18.7300 Gūdūr;14.1473 Acacías;3.9878 Apple Valley;34.5352 Īṭahari̇̄;26.6631 Pozorrubio;16.1167 Ardakān;32.3061 Palencia;14.6676 Afak;32.0625 Dabra;25.8857 Tatebayashi;36.2448 Molina de Segura;38.0548 Dorsten;51.6600 Gela;37.0667 Tura;25.5200 Dĩ An;10.9039 Dalaguete;9.7612 Tongye;37.9679 Wheeling;40.0752 Flower Mound;33.0343 Estepona;36.4264 Wisil;5.4333 Hoorn;52.6500 Amakusa;32.4667 São Bento do Sul;-26.2500 Samundri;31.0639 Esperanza;6.7167 Harrisonburg;38.4362 Balingasag;8.7500 Toffo;6.8500 Castrop-Rauxel;51.5500 Wandiwāsh;12.5000 Kandori;35.6833 Péda-Houéyogbé;6.4500 Gravesend;51.4415 Cedar Park;30.5105 Darhan;49.6167 Oshkosh;44.0227 Piraçununga;-21.9961 Tanguiéta;10.6167 Miki;34.7936 Grand-Bassam;5.2000 Standerton;-26.9500 Copacabana;6.3333 San Juan Opico;13.8833 Nizhyn;51.0474 Aprilia;41.5833 Winchester;39.1735 Bozüyük;39.9078 Skellefteå;64.7500 Canindé;-4.3589 Dumangas;10.8333 Novoaltaysk;53.3833 Saquarema;-22.9200 Ellicott City;39.2774 Ouaké;9.6617 Detmold;51.9378 Palmeira dos Índios;-9.4069 Pocatello;42.8724 Alicia;16.7787 João Monlevade;-19.8100 Pedro Brand;18.5667 Majalengka;-6.8353 Uspantán;15.3458 Bongouanou;6.6500 Rāmhormoz;31.2800 Tatsunochō-tominaga;34.8508 Tarn Tāran;31.4519 Khomeyn;33.6422 Çınar;37.7242 Hilton Head Island;32.1896 Curvelo;-18.7558 Mineral’nyye Vody;44.2167 Cherry Hill;39.9034 Landshut;48.5397 Almelo;52.3500 Mankono;8.0500 Hāveri;14.7935 Meshgīn Shahr;38.3972 Kara-Balta;42.8333 Vriddhāchalam;11.5000 Al Qā’im;34.3688 Ouro Prêto;-20.3853 Yelabuga;55.7667 Kallakkurichchi;11.7380 Majadahonda;40.4728 Sharm ash Shaykh;27.9122 Brookes Point;8.7833 Santa Rosa;-27.8708 Konin;52.2167 Mansfield;40.7656 Woodbury;44.9057 Arnsberg;51.3833 Prince George;53.9169 Kostiantynivka;48.5333 Samadiāla;21.3422 Nova Serrana;-19.8667 Lala;7.9667 Lehi;40.4136 Yurihonjō;39.3859 Inuyama;35.3786 Türkoğlu;37.3914 Cunhinga;-12.2333 Bawku;11.0500 Yegoryevsk;55.3833 Kasama;36.3452 Bolingbrook;41.6901 Chaigoubu;40.6687 Beyşehir;37.6764 Gobindgarh;30.6709 Tupi;6.3333 Siuna;13.7347 Brick;40.0600 Dale City;38.6473 Pattukkottai;10.4300 Mérignac;44.8386 Missouri City;29.5630 Linares;-35.8500 Mysłowice;50.2333 Jingzhou;37.6911 Irecê;-11.3039 Shrewsbury;52.7080 Quillota;-32.8667 Troitsk;54.0833 Bula;13.4694 Picos;-7.0769 El Hamma;33.8864 Bandar-e Genāveh;29.5839 Lanxi;46.2664 Paterna;39.5028 Bagan Si Api-api;2.1667 Jose Abad Santos;5.9167 La Estrella;6.1667 Mackay;-21.1411 Brakpan;-26.2353 Lake Jackson;29.0516 Zhuolu;40.3753 Kirovo-Chepetsk;58.5500 Vinhedo;-23.0300 Södertälje;59.1958 Lüdenscheid;51.2167 Ostrowiec Świętokrzyski;50.9333 Altoona;40.5082 Chapadinha;-3.7419 Shibukawa;36.3894 Sault Ste. Marie;46.5333 Nepālgañj;28.0500 Belek;36.8500 Carles;11.5667 Reconquista;-29.1443 Ostend;51.2258 San Fernando;10.1667 Farroupilha;-29.2250 Wenping;27.1930 Sangāreddi;17.6294 Tokār;18.4253 Shakargarh;32.2628 Tan-Tan;28.4333 Sambrial;32.4750 Chapayevsk;52.9833 Santa Rosa de Cabal;4.8667 Moa;20.6397 Ōtawara;36.8711 Doral;25.8152 Owensboro;37.7575 Baghlān;36.1328 Naqadeh;36.9547 Luján de Cuyo;-32.9980 Esbjerg;55.4833 Bacacay;13.2925 Morong;14.5119 Sanwal;27.6061 Catanauan;13.5917 Alegrete;-29.7839 Çatalca;41.1417 Vsevolozhsk;60.0333 Türkmenbaşy;40.0167 Sotik;-0.6800 Palín;14.4039 Padre Las Casas;-38.7667 San Vicente del Caguán;2.1167 Belovo;54.4167 Xiangjiaba;28.6282 Virudunagar;9.5680 Saint-Nazaire;47.2736 Bakıxanov;40.4217 Hương Trà;16.4675 Três Corações;-21.6947 Tocoa;15.6833 Pacatuba;-3.9839 Siruguppa;15.6000 Amparo;-22.7031 Crateús;-5.1778 Pavia;45.1853 Ede;52.0500 Aracati;-4.5619 Brandenburg;52.4167 Broomfield;39.9542 Keshan;48.0263 Yafran;32.0629 Redlands;34.0512 Sipalay;9.7500 Penafiel;41.2060 Camalig;13.1333 Cleveland;35.1817 Pililla;14.4833 La Lima;15.4330 Concepción del Uruguay;-32.4833 Belo Jardim;-8.3358 Pigcawayan;7.2833 Aschaffenburg;49.9667 Dambulla;7.8578 Zogbodomé;7.0833 Melton;-37.6833 Long Mỹ;9.6814 Piła;53.1500 Ostrów Wielkopolski;51.6494 Dothan;31.2336 Patzún;14.6833 Cajamar;-23.3558 Nilanga;18.1161 Goya;-29.1333 Gumaca;13.9210 Warora;20.2300 Turbaná;10.2833 Lajeado;-29.4669 Ishioka;36.1908 Casa Nova;-9.1619 Redmond;47.6763 Saymayl;36.8583 Shahrixon;40.7167 Nanfengcun;23.7460 Colón;22.7225 Farshūţ;26.0549 Tibati;6.4667 Cremona;45.1333 Framingham;42.3085 Myaydo;19.3667 Santo Tirso;41.3333 Calauag;13.9575 Al Līth;20.1500 Belo Tsiribihina;-19.7000 Nadi;-17.8000 Merzifon;40.8750 Jackson;35.6538 Kawm Umbū;24.4667 Mun’gyŏng;36.5939 Yukuhashi;33.7333 Quixeramobim;-5.1989 Janesville;42.6854 Valença;-22.2458 Mandlā;22.6000 Bouaflé;6.9833 Rāyadrug;14.6997 Binalbagan;10.2000 Talibon;10.1167 Parang;5.9167 Juana Díaz;18.0532 Lubin;51.4000 Dondo;-9.6942 Florida;21.5294 Goa;13.6983 Alexandroúpoli;40.8500 Bocholt;51.8333 Carpi;44.7833 Katori;35.8977 Rāyagada;19.1700 Tenkāsi;8.9564 Lisburn;54.5167 Quartu Sant’Elena;39.2413 Bunbury;-33.3272 Sarnia;42.9994 Wood Buffalo;57.6042 Benidorm;38.5342 Bāpatla;15.9044 Izmail;45.3517 Mārkāpur;15.7300 Shujaabad;29.8803 Orani;14.8000 San Francisco del Rincón;21.0228 Ganthier;18.5333 Kaukhāli;22.6333 Villa Tunari;-16.9747 Mansfield;32.5690 Malaut;30.1900 Izalco;13.7333 Ratangarh;28.0787 Konongo;6.6167 Huwei;23.7200 Placetas;22.3158 Verkhnyaya Pyshma;56.9761 Waukesha;43.0087 Warzat;30.9167 Cabudare;10.0331 Melong;5.1211 Yinying;37.9410 Mauban;14.1911 Ayvalık;39.3167 Gibara;21.1072 Érd;47.3784 Entebbe;0.0500 Baiquan;47.6018 Fatehābād;29.5200 Aliaga;15.5036 An Nu‘mānīyah;32.5000 Minxiong;23.5504 Corozal;9.3333 Wenatchee;47.4360 Bundaberg;-24.8661 Thohoyandou;-22.9500 Djemmal;35.6400 Daytona Beach;29.1995 Castle Rock;39.3763 Pan’an;34.7575 Kopargo;9.8375 Sankaranayinār Kovil;9.1600 Moriya;35.9514 Rittō;35.0167 Hujra Shah Muqim;30.7408 Tumauini;17.2667 Ejura;7.3833 San Marcos;29.8734 Badvel;14.7500 Caçador;-26.7750 Esperanza;19.5800 Union City;37.6032 Kabirwala;30.4053 Châteauguay;45.3800 Novomoskovsk;48.6328 Rizal;15.7100 Zenica;44.2017 Loulé;37.1500 Stafford;52.8070 Vālpārai;10.3276 Kladno;50.1500 Altamura;40.8167 Sūratgarh;29.3177 Chekhov;55.1500 Eldersburg;39.4041 Xangongo;-16.7467 Togoch’alē;9.6014 Conway;35.0753 Būr Fu’ād;31.2500 São Sebastião do Paraíso;-20.9169 Caldas Novas;-17.7439 Dandeli;15.2667 Bopa;6.5833 Kovel;51.2167 Santa María La Pila;15.6056 Tinambac;13.8183 San Pascual;13.8000 Imerintsiatosika;-18.9833 Guzhou;25.9452 Eniwa;42.8826 Sesvete;45.8269 Rongcheng;39.0500 Zhanggu;30.8795 Rocklin;38.8075 Rohri;27.6831 Imola;44.3531 Limonade;19.6667 Sheboygan;43.7403 Casas Adobes;32.3423 St. Charles;38.7954 Rhondda;51.6159 Satyamangalam;11.5167 Kempten;47.7333 Villa del Rosario;7.8339 Sanlúcar de Barrameda;36.7667 Az Zubaydīyah;32.7588 Phú Quốc;10.2289 Raha;-4.8311 Al ‘Āmirāt;23.5242 Oshnavīyeh;37.0389 Nihtaur;29.3300 Fanzhuang;37.7771 Cahama;-16.2833 Cianorte;-23.6628 Nansang;20.8889 Shangchuankou;36.3283 Quilāndi;11.4390 Badhan;10.7139 Celle;52.6256 Nikki;9.9333 Victoria;15.5781 Qingquan;38.7823 Garzón;2.1819 Isnā;25.3000 Minami-Alps;35.6083 Saldanha;-32.9978 Maple Grove;45.1089 Cataguases;-21.3889 Tournai;50.6056 Russas;-4.9400 Walnut Creek;37.9024 Dublin;37.7161 North Richland Hills;32.8604 San Juan de los Lagos;21.2489 Solok;-0.7883 Uwajima;33.2167 Nowrangapur;19.2300 Massa;44.0333 Suwałki;54.0989 Gary;41.5905 Glen Burnie;39.1560 Colmar;48.0817 Bristol;36.5572 Decatur;34.5731 Velsen-Zuid;52.4667 Yitiaoshan;37.1889 Roxas;10.3197 West Bend;43.4173 San Pedro;-33.6794 Indang;14.2000 Macaíba;-5.8578 Saint-Jérôme;45.7833 Don Carlos;7.6808 Kollegāl;12.0628 Samch’ŏk;37.4500 La Paz;15.4431 Gniezno;52.5358 Fedosiia;45.0489 Bom Jesus da Lapa;-13.2550 Cawayan;11.9303 Fernandópolis;-20.2839 Anan;33.9167 Poptún;16.3222 Sabae;35.9565 Lodi;38.1218 Ongjang;37.9371 Lima;40.7410 Florida;18.3643 Mansehra;34.3339 Chisec;15.8125 Aqsū;52.0333 Planeta Rica;8.4089 Yanghe;38.2727 Korba;36.5667 Bafia;4.7500 Ko Samui;9.5000 Brumado;-14.2039 Pongnam;37.2200 Liuhe;42.2669 Kamsar;10.6500 Fulda;50.5508 Parādīp Garh;20.3160 Mamungan;8.1167 Huishi;35.6918 Ad Darb;17.7167 Necoclí;8.4167 Mocuba;-16.8500 Minusinsk;53.7000 Renk;11.8300 Aparri;18.3575 Smila;49.2167 Mococa;-21.4678 Tanabe;33.7333 Bagumbayan;6.5339 Oliveira de Azemeis;40.8400 Buenavista;8.9744 Purísima de Bustos;21.0333 Lakeville;44.6774 Mocuba;-16.8496 Blaine;45.1696 Arcoverde;-8.4189 San Leonardo;15.3611 Wagga Wagga;-35.1189 Stargard Szczeciński;53.3333 Tianguá;-3.7319 Say’ūn;15.9430 Pālghar;19.6900 Dinslaken;51.5667 Poinciana;28.1217 Sousa;-6.7608 Al Aḩmadī;29.0769 Noblesville;40.0355 Wum;6.3833 San Pedro Ayampuc;14.7785 Sōja;34.6728 Nghĩa Lộ;21.5758 Panna;24.7200 Aalen;48.8333 Lake Elsinore;33.6847 Głogów;51.6589 Tangub;8.0667 Ilkal;15.9592 Bay;14.1833 Alamada;7.3868 Concórdia;-27.2339 Cannock;52.6910 Beypore;11.1800 Jarabacoa;19.1167 Swedru;5.5306 Ōdate;40.2714 Zapotlanejo;20.6228 Palo Alto;37.3905 Doboj;44.7333 Castillejos;14.9333 Igbanke;6.3869 Lippstadt;51.6667 Drummondville;45.8833 Sagunto;39.6764 Mbaké;14.7917 Nābha;30.3700 Miagao;10.6442 Otukpo;7.1904 Tuburan;10.7333 Sipocot;13.7675 Sanza Pombo;-7.3333 Tulare;36.1995 Cuyapo;15.7778 Lala Musa;32.7003 Ash Shiḩr;14.7608 Rogers;36.3170 Ixtlahuacán de los Membrillos;20.3500 Yorba Linda;33.8890 Renukūt;24.2000 Tuymazy;54.6000 Chengjiao Chengguanzhen;34.4362 Bishnupur;23.0739 Lodja;-3.5242 Ceará-Mirim;-5.6339 Parkersburg;39.2623 Zamość;50.7167 Conceição do Coité;-11.5639 Xinhua;23.6272 Eagan;44.8170 Rājsamand;25.0700 Bergen op Zoom;51.5000 Gopālganj;26.4700 Weston;26.1006 Santa Barbara;10.8231 Urun-Islāmpur;17.0500 Modāsa;23.4700 La Barca;20.2833 Przemyśl;49.7833 Talipparamba;12.0368 Wanzhuang;39.5683 Malkāpur;20.8850 Zanhuang;37.6590 Dhenkānāl;20.6700 Kstovo;56.1667 Viterbo;42.4186 Longview;46.1461 Tzaneen;-23.8333 Trapani;38.0175 Antalaha;-14.8833 Bay City;43.5902 Dubuque;42.5002 Tepotzotlán;19.7161 Porterville;36.0643 Franklin;40.4759 Etāwa;24.1800 Longshan;26.4519 Xo‘jayli Shahri;42.4000 Mount Vernon;48.4203 Ullāl;12.8000 Palatine;42.1180 Fuefuki;35.6473 Embu-Guaçu;-23.8322 İslahiye;37.0250 Rosales;15.8944 Chicacao;14.5428 Southampton;40.8997 El Paso de Robles;35.6394 Sausar;21.6500 Yuquan;40.4202 West Des Moines;41.5521 Kashima;35.9656 Santa Catarina Otzolotepec;18.5667 Manaure;11.7792 Lushar;36.4971 Saint John;45.2806 Tôlan̈aro;-25.0325 Hashima;35.3199 Ayolas;-27.4000 Zihuatanejo;17.6444 Sahaswān;28.0680 Sa’ada;31.6258 Omīdīyeh;30.7458 Siddipet;18.1018 Herford;52.1333 Cuilco;15.4078 Kissidougou;9.1905 Koratla;18.8215 Guangping;36.4791 Ankazoabokely;-21.5036 Weiyuan;36.8413 Chegutu;-18.1400 Venustiano Carranza;16.3000 Pelileo;-1.3306 Yachimata;35.6667 Caicó;-6.4578 Tejen;37.3833 Agoo;16.3220 Itamaraju;-17.0389 Rüsselsheim;49.9950 Rio Negro;-26.1000 Hobyo;5.3514 Cosmópolis;-22.6458 Supaul;26.1260 Moita;38.6500 San Mateo;16.8833 Sherman;33.6273 Tabatinga;-4.2525 Dolores Hidalgo Cuna de la Independencia Nacional;21.1516 Ilidža;43.8167 Aflao;6.1468 Janiuay;10.9500 Kānhangād;12.3000 La Carlota;10.4167 Castro;-24.7908 Vidnoye;55.5500 Rockville;39.0834 Haverhill;42.7838 Bongabon;15.6321 Lupon;6.8969 Río Grande;-53.7833 Güines;22.8475 Hanover;39.8118 Shawnee;39.0158 Fukutsu;33.7667 Virginia;-28.1064 Middletown;40.3892 Cedeño;6.7100 Rome;34.2662 Godalming;51.1800 Nueva Guinea;11.6867 Coonoor;11.3450 Pessac;44.8067 Old Bridge;40.4004 Gohna;29.1300 El Seibo;18.7630 Pabianice;51.6500 Kerpen;50.8719 Arcot;12.9000 Capanema;-1.1958 Genk;50.9662 Janaúba;-15.8028 Bulancak;40.9333 Botolan;15.2896 Pulivendla;14.4167 Çumra;37.5750 Borås;57.7211 Arāmbāgh;22.8800 Hīt;33.6450 South Hill;47.1198 Opol;8.5167 Karimama;12.0667 Ocoyoacac;19.2739 Belogorsk;50.9167 Qarqan;38.1338 Pidugurālla;16.4793 Gwacheon;37.4333 DeKalb;41.9314 Petaluma;38.2423 Saiki;32.9667 Ishim;56.1167 Sammamish;47.6017 San Antonio Suchitepéquez;14.5333 Georgetown;30.6660 Valence;44.9333 Angat;14.9281 Caledon;43.8667 Gukovo;48.0500 Xieqiaocun;30.4973 Carbondale;37.7221 Urla;38.3222 Kavála;40.9333 Rechytsa;52.3639 San Remigio;11.0000 Delray Beach;26.4550 Dundalk;39.2704 Puliyankudi;9.1725 Huquan;39.7603 Kalush;49.0442 Kenner;30.0109 Shiojiri;36.1150 Albany;44.6272 Zeist;52.0906 Slavyansk-na-Kubani;45.2500 Ankeny;41.7288 Ilo;-17.6459 Itaberaba;-12.5278 Oas;13.2589 Malvar;14.0417 Castro Valley;37.7088 Bethesda;38.9866 Saratoga Springs;43.0674 Kampung Tengah;1.4897 Kazanlak;42.6167 Dangbo;6.5000 Kungur;57.4333 Lençóis Paulista;-22.5986 Roxas;17.1167 Oberá;-27.4833 Lantapan;8.0000 União dos Palmares;-9.1628 Chibok;10.8697 Singaparna;-7.3497 Sangamner;19.5700 San Gil;6.5592 Sindelfingen;48.7133 Don Torcuato;-34.5000 Corvallis;44.5698 Seraing;50.5833 El Carmen de Bolívar;9.7167 Stupino;54.8833 Most;50.5031 Asenovgrad;42.0167 Madirovalo;-16.4333 Venâncio Aires;-29.6058 Ames;42.0256 Tigbauan;10.6747 Glens Falls;43.3109 San Francisco El Alto;14.9500 Xibang;30.9412 Lahat;-3.7970 Kiamba;5.9833 Anamur;36.0243 Coron;12.0000 Michigan City;41.7092 Menzel Temime;36.7833 Weimar;50.9833 Villasis;15.9000 Santo Tomé;-31.6667 Neuwied;50.4286 Lutayan;6.6000 Gopichettipālaiyam;11.4549 Nieuwegein;52.0333 Xiantangcun;23.7940 Ina;35.8275 Sirsi;28.6400 Tupã;-21.9350 Solano;16.5183 Dhūri;30.3685 Jagraon;30.7800 Daule;-1.8667 Nagcarlan;14.1364 Zarechnyy;53.2000 Bordj Menaïel;36.7417 Istaravshan;39.9108 Victoria;28.8287 Sinnar;19.8500 Ladysmith;-28.5597 Villach;46.6167 Bourges;47.0844 Alīpur Duār;26.4890 Torquay;50.4700 Purulhá;15.2353 Gloucester;39.7924 St. Albert;53.6303 Roeselare;50.9447 Ferrol;43.4844 Bāqershahr;35.5325 Xarardheere;4.6544 Jocotitlán;19.7072 Rāth;25.5800 Vaijāpur;19.9200 Los Amates;15.2667 Icó;-6.4008 Beloretsk;53.9667 Jaisalmer;26.9167 Januária;-15.4878 Weirton;40.4060 Tāndūr;17.2300 Ishimbay;53.4500 Leszno;51.8458 Peruíbe;-24.3100 Ārān Bīdgol;34.0575 Korosten;50.9500 Jamjamāl;35.5333 Birnin Konni;13.7904 Novi;42.4786 Develi;38.3886 Quezon;9.2350 Idappādi;11.5835 Villa Curuguaty;-24.4800 Dormagen;51.1000 Plauen;50.4833 Sandefjord;59.1306 Pālitāna;21.5200 Odendaalsrus;-27.8667 Chunian;30.9639 Ḩalabjah;35.1833 Viana;-20.3900 Rosenheim;47.8500 Marinilla;6.1738 Alpharetta;34.0704 Formiga;-20.4639 Mecheria;33.5500 Wesley Chapel;28.2106 Esquipulas;14.6167 Caripito;10.1124 Maasin;5.8667 Pokrovsk;48.2828 Ban Lam Sam Kaeo;13.9728 Svyetlahorsk;52.6333 Marovoay;-16.1111 Soloma;15.7167 Bāzār-e Yakāwlang;34.7333 Ra’s al Khafjī;28.4167 Chiquimulilla;14.0858 Itō;34.9657 Tādepalle;16.4833 Bucak;37.4592 Donskoy;53.9658 Yanagawa;33.1597 Miyoshi;35.0894 Isabela;10.2000 Saravia;10.8833 Bertioga;-23.8539 Hilongos;10.3667 Neubrandenburg;53.5569 Gadwāl;16.2300 Yattir;31.4478 Sibulan;9.3500 Jatani;20.1700 Chipindo;-13.8244 Seydişehir;37.4183 Kodād;16.9978 Santo Domingo Tehuantepec;16.3184 Meulaboh;4.1333 Chokwé;-24.5253 Binga;2.4000 Waltham;42.3889 Sodegaura;35.4300 Cruz das Almas;-12.6700 Rahat;31.3925 San Felipe;-32.7500 Tamana;32.9281 Apaseo el Alto;20.4500 Laguna Niguel;33.5275 Hiriyūr;13.9446 San Clemente;33.4499 Qinggang;46.6900 Vittoria;36.9500 Pirané;-25.7328 Grevenbroich;51.0883 Dhuburi;26.0200 Atimonan;14.0036 Nānpāra;27.8700 Lingshou;38.3063 Santiago Tianguistenco;19.1797 Nawalgarh;27.8500 Estância;-11.2678 Tenri;34.5967 North Little Rock;34.7807 Irún;43.3378 Fraijanes;14.4622 Ouricuri;-7.8828 Pomezia;41.6693 Tohāna;29.7000 Ciénaga de Oro;8.8833 Samāstipur;25.8629 Salto de Agua;17.4167 Crotone;39.0833 Ghardimaou;36.4500 Bamei;24.2634 Tuao;17.7350 Sangmélima;2.9333 Sheikhpura;25.1403 Nawá;32.8889 At Tall;33.6000 Asbest;57.0000 Póvoa de Varzim;41.3800 Niksar;40.5833 Penedo;-10.2900 Fairbanks;64.8353 Sibalom;10.7883 Tiquisate;14.2833 Kangbao;41.8513 Māhdāsht;35.7194 Maidenhead;51.5217 Simav;39.0833 Eden Prairie;44.8488 Armūr;18.7900 West Hartford;41.7669 Quimper;47.9967 Lugano;46.0050 Chik Ballāpur;13.4300 Pflugerville;30.4515 Asahi;35.7167 Santa Cruz;15.7667 Yangqingcun;21.3594 Tsuruga;35.6452 Ponferrada;42.5461 Casper;42.8420 Jose Pañganiban;14.2922 Boadilla del Monte;40.4069 Vigevano;45.3167 Burnsville;44.7648 Athni;16.7300 Klintsy;52.7500 Midoun;33.8000 Sittingbourne;51.3400 Bognor Regis;50.7824 Zvornik;44.3833 Bhairāhawā;27.5000 Sherkot;29.3500 Nankana Sahib;31.4500 Dikirnis;31.0883 Woking;51.3050 Zarand;30.8128 Kurihara;38.7301 Grand Forks;47.9214 Randers;56.4570 Velika Gorica;45.7000 Escada;-8.3592 Chincha Alta;-13.4500 Visnagar;23.7000 Guiglo;6.5500 Brentwood;37.9356 Tajumulco;15.0839 Graaff-Reinet;-32.2522 Herten;51.6000 Moncada;15.7331 Wenxicun;28.1565 Aïn Sefra;32.7500 Galgamuwa;8.0000 Rouiba;36.7333 Bhabhua;25.0500 Budënnovsk;44.7833 Badr Ḩunayn;23.7800 Khulayş;22.1539 Asaka;40.6333 Greenwich;41.0665 Rolândia;-23.3100 Granby;45.4000 Millcreek;40.6892 Carrara;44.0792 Shiroi;35.7915 Elmira;42.0938 Balboa Heights;8.9500 Nago;26.5917 Mercedes;-34.6500 Santo Antônio do Descoberto;-15.9400 Wundanyi;-3.3983 Rānipet;12.9247 Sebring;27.4770 Chełm;51.1322 Abū Qurqāş;27.9333 Coon Rapids;45.1755 Medicine Hat;50.0417 Bannu;32.9864 Volsk;52.0500 Tartagal;-22.5000 Meshkīn Dasht;35.7469 Kaga;36.3028 Wāshīm;20.1000 Qurayyāt;23.3200 Bossier City;32.5224 Novaya Balakhna;56.4899 Grande Prairie;55.1708 Kōshizuka;32.8860 Oudtshoorn;-33.5833 Bansalan;6.7833 San Fernando;7.9178 Hamilton;39.3939 Chidambaram;11.4070 Itupeva;-23.1531 Łomża;53.1764 Calinog;11.1333 Santa Ana;15.0939 Taylor;42.2260 Puerto Asís;0.5167 Lower Merion;40.0282 Çaldıran;39.1419 Batatais;-20.8911 Kasongo;-4.4500 Sirsi;14.6195 São Borja;-28.6608 Pesqueira;-8.3617 Lakewood;47.1628 Palmela;38.5667 Fujioka;36.2587 Yurimaguas;-5.9000 Lāharpur;27.7200 Greenwood;39.6019 Tighenif;35.4167 Nkawkaw;6.5500 Bellevue;41.1485 Pavlovskiy Posad;55.7667 Mut;36.6333 Cruz Alta;-28.6386 Rossosh;50.2000 Widnes;53.3630 Kapatagan;7.9000 Sumbawa Besar;-8.5000 Bodītī;6.8667 Camaquã;-30.8528 Margate;51.3850 Oriximiná;-1.7658 Żory;50.0500 Kolomyia;48.5167 Qoryooley;1.7833 Revda;56.8053 Anse à Galets;18.8333 Borisoglebsk;51.3667 Kotlas;61.2500 Deva;45.8781 Zelenogorsk;56.1000 Bainet;18.1833 Camboriú;-27.0250 Moore;35.3293 Shangtangcun;21.5989 Council Bluffs;41.2369 Rowlett;32.9155 El Wak;2.8028 Mobārakeh;32.3464 Leninogorsk;54.6000 Ţūlkarm;32.3117 Berekum;7.4500 Spring;30.0613 Wels;48.1500 Narwāna;29.6167 Kayes;-4.1681 Port Charlotte;26.9918 Tendō;38.3623 Menzel Bourguiba;37.1500 Villa Carlos Paz;-31.4000 Bensalem;40.1086 Reston;38.9497 Bergheim;50.9667 Tefé;-3.3539 Kolding;55.4917 Friedrichshafen;47.6500 Riosucio;5.4208 Siddhapur;23.9167 Nanjangūd;12.1200 Tarnowskie Góry;50.4444 San Juan Chamelco;15.4257 Boryspil;50.3500 Caràzinho;-28.2839 Caltanissetta;37.4903 Meihua;37.8862 Katueté;-24.2481 Dearborn Heights;42.3164 Tuapse;44.1044 Puerto Peñasco;31.3167 Aurora;13.3500 Figueira da Foz;40.1500 Analavory;-18.9667 Chistopol;55.3647 Camotán;14.8167 Itoman;26.1236 Port Orange;29.1085 Ogōshi;32.8833 Penápolis;-21.4200 Itapecuru Mirim;-3.3928 Horsens;55.8583 Encinitas;33.0492 Springfield;44.0538 Mitoyo;34.1825 Ipirá;-12.1578 Great Falls;47.5022 Asker;59.8331 Cambanugoy;7.5386 Naro-Fominsk;55.3833 Grajaú;-5.8189 Morada Nova;-5.1069 Ostuncalco;14.8693 Pacajus;-4.1728 Viareggio;43.8672 Baao;13.4535 Jhārgrām;22.4500 Rāmanāthapuram;9.3639 Manicaragua;22.1500 Zaraza;9.3394 Candon;17.1958 San Francisco;-31.4356 Qiryat Gat;31.6061 Rocky Mount;35.9685 La Democracia;15.6333 Ar Rastan;34.9167 San Antonio del Táchira;7.8145 Diphu;25.8300 Santo Domingo;15.5900 Polevskoy;56.4500 Plato;9.7919 Yangiyŭl;41.1125 Sätbayev;47.9000 Piripiri;-4.2728 Sarandí;-34.6833 Derik;37.3658 Mielec;50.2833 Schwäbisch Gmünd;48.8000 Hita;33.3167 Aisai;35.1528 Santo Amaro;-12.5469 Montauban;44.0181 São Miguel dos Campos;-9.7808 Kapchagay;43.8833 Camabatela;-8.1833 Lysva;58.1003 Ilobasco;13.8400 Tríkala;39.5500 Coroatá;-4.1300 Xinmin;35.1180 Fairfield;41.1775 Sungo;-11.2333 Rubizhne;49.0336 St. Thomas;42.7750 Sayyid Şādiq;35.3536 Orion;14.6206 Sibay;52.7000 Sawahlunto;-0.6828 Cabatuan;10.8833 Itogon;16.3700 Offenburg;48.4667 Wanparti;16.3623 Hato Mayor;18.7667 Airdrie;51.2917 Santa Catarina Ixtahuacán;14.8000 Djibo;14.1011 Iztapa;13.9331 Cajazeiras;-6.8900 Uzunköprü;41.2669 Garbsen;52.4183 Commerce City;39.8641 Araranguá;-28.9350 Pontiac;42.6493 Kozluk;38.1944 Fano;43.8333 Udamalpet;10.5855 Wellington;26.6461 Slutsk;53.0333 Rio do Sul;-27.2139 Khagaria;25.5022 Vacaria;-28.5119 Hürth;50.8775 Tigaon;13.6319 Dumraon;25.5526 Nordre Fåle;59.7500 Abuyog;10.7458 Kindamba;-3.7275 Barberena;14.3167 Acaraú;-2.8858 Bocaranga;6.9844 Wesel;51.6586 Trollhättan;58.2828 Halton Hills;43.6300 Hamden;41.3961 San Rafael;37.9905 Tulunan;6.8333 Adjohon;6.7000 Nahariyya;33.0058 Obando;14.7083 Chāmrājnagar;11.9231 Idiofa;-4.9668 Gaibandha;25.3290 Skhirate;33.8500 Navegantes;-26.8989 Rosario;16.2333 Barabai;-2.5833 Panruti;11.7700 Nekā;36.6508 Campo Bom;-29.6789 Yongyang;27.1017 Jinoba-an;9.6018 Acerra;40.9500 Burhaniye;39.5000 Malay;11.8997 Plymouth;41.8783 Woodland;38.6712 Marietta;33.9533 Castilla;12.9553 Tamba;35.1833 Oda;5.9236 Al Qurayyā;35.0025 Chapel Hill;35.9271 Chalkída;38.4625 Velampālaiyam;11.1376 Nambuangongo;-8.0167 Sennan;34.3628 Villamaría;5.0000 Laoang;12.5667 Cubulco;15.1083 Tomaszów Mazowiecki;51.5167 Shihe;39.2708 Dongcun;38.2800 Matanao;6.7500 Meriden;41.5369 Koga;33.7333 Calulo;-10.0007 Gubat;12.9189 Bristol;41.6812 Stralsund;54.3092 Igarapé-Miri;-1.9750 Zamora;41.5033 Ibitinga;-21.7578 Palm Harbor;28.0847 Druzhkivka;48.6203 Langenfeld;51.1167 Autlán de Navarro;19.7667 Savona;44.3080 Jupiter;26.9200 Hendersonville;36.3063 Kangān;27.8389 Piscataway;40.5467 San Vicente del Raspeig;38.3964 Bełchatów;51.3667 Rāzampeta;14.1954 Atotonilco el Alto;20.5333 Matera;40.6667 Neu-Ulm;48.3833 Mirassol;-20.8189 Greifswald;54.0833 Hashimoto;34.3167 Kumertau;52.7667 Ponnūru;16.0667 Paracale;14.2797 Yongbei;26.6897 Rzhev;56.2500 Molfetta;41.2000 Çivril;38.3014 Clay;43.1808 Consuelito;18.5600 Belladère;18.8667 São Gabriel;-30.3358 Kakuma;3.7167 Mossel Bay;-34.1833 Medellin;11.1286 Olbia;40.9167 Jiguaní;20.3731 Des Plaines;42.0345 Arankhola;24.6610 Parkent;41.2944 Sayanogorsk;53.0500 Bor;37.8833 Lakhdaria;36.5622 Maimbung;5.9333 Kathua;32.3850 Esmeraldas;-19.7628 Barneveld;52.1333 Yame;33.1994 San Mateo Ixtatán;15.8333 North Lakhimpur;27.2414 Afula;32.6064 Manhiça;-25.4000 Benevento;41.1333 Belebey;54.1000 Bābolsar;36.7025 Labinsk;44.6333 Lianzhuang;37.1146 Sanford;28.7891 San Simon;14.9980 Camocim;-2.9019 Garín;-34.4167 Gunupur;19.0800 Shimotsuke;36.3872 Alfonso;14.1379 Chandralapādu;16.7150 Urus-Martan;43.1333 Ābyek;36.0400 Huntersville;35.4060 San Mariano;16.9833 Benevides;-1.3608 Towada;40.6127 Suifenhe;44.3998 Euless;32.8508 Aranjuez;40.0333 Sarāvān;27.3708 Vinukonda;16.0500 Kananya;11.1856 Ash Shaykh Zuwayd;31.2119 Sanyō-Onoda;34.0000 Rāpar;23.5700 Banane;0.5000 Ilog;10.0333 Niort;46.3258 Gohadi;26.4328 Sigaboy;6.6536 Horqueta;-23.3442 San Marcos;8.6611 Bayjī;34.9292 Lagoa Santa;-19.6269 Kribi;2.9350 Pāchora;20.6700 Buqda Caqable;4.0600 Maués;-3.3836 Shegaon;20.7944 Ragay;13.8217 Xinfeng;24.9167 Bougouni;11.4167 Hardenberg;52.5667 Huajiang;25.7491 Sardhana;29.1450 Heerhugowaard;52.6667 Shuya;56.8500 Aguacatán;15.3429 Uriangato;20.1333 Huehuetoca;19.8342 San Francisco;10.6500 Guerra;18.5500 Hamburg;42.7394 Lesosibirsk;58.2358 Zhoujiajing;31.1116 Chibuto;-24.6833 Richland;46.2824 Saint-Louis du Sud;18.2667 Palmares;-8.6828 Jōsō;36.0236 Pasrur;32.2681 Mouscron;50.7333 Santee;32.8554 Irosin;12.7050 Narasapur;16.4361 Saint-Hyacinthe;45.6167 Ogōri;33.4000 Goianésia;-15.3169 Alcoy;38.6983 San Luis;15.0400 Unna;51.5347 Agrigento;37.3167 Camajuaní;22.4678 Montenegro;-29.6889 Santa Isabel do Pará;-1.2989 Roxas;12.5833 Prokhladnyy;43.7500 Dhone;15.3960 Kesennuma;38.9081 Yara;20.2767 Esfarāyen;37.0764 Ouinhri;7.0000 Morón;22.1108 Los Andes;-32.8333 Lucas do Rio Verde;-13.0500 Manchester;41.7753 Hyūga;32.4167 Binnāguri;26.4600 Hua Hin;12.5686 Tiruvālūr;10.7730 Gannan;47.9117 Esperanza;8.6760 Rājgarh;28.6400 Aleksandrov;56.3936 Ibaan;13.8176 Sonabedha;18.7300 Motril;36.7500 El Salvador;8.5667 Tahara;34.6688 Zahirābād;17.6814 Kokomo;40.4640 Mansalay;12.5204 Chikuma;36.5339 Calatagan;13.8322 Shirakawa;37.1263 Halesowen;52.4502 Oued Lill;36.8333 Chichibu;35.9918 Tianningcun;30.8938 Santa María Chiquimula;15.0292 Zengcun;38.2451 Masantol;14.9000 Kattaqo’rg’on Shahri;39.8989 Euskirchen;50.6597 Ranchuelo;22.3764 Canlaon;10.3833 Pamplona;7.3781 Florida;3.3275 Jaguariúna;-22.6800 Nūzvīd;16.7800 Sandīla;27.0800 Constanza;18.9100 Upleta;21.7300 Dāmghān;36.1683 Wani;20.0556 Metapán;14.3314 Liantangcun;22.9517 Upi;7.0106 Taunton;41.9036 Faenza;44.2856 Bilhorod-Dnistrovskyi;46.1833 St. Clair Shores;42.4925 Xikeng;24.0505 Göppingen;48.7025 Towson;39.3944 Camacupa;-12.0256 Doetinchem;51.9667 Yaguajay;22.3303 Kélibia;36.8500 Barra do Garças;-15.8900 Rass el Djebel;37.2150 Gattaran;18.0611 Pānskura;22.4200 Myaungmya;16.5833 Pedro Leopoldo;-19.6178 Mpondwe;0.0400 Pipariā;22.7570 Lac-Brome;45.2167 Timargara;34.8278 Boufarik;36.5750 Caldwell;43.6453 Wuyang;27.0570 Una;20.8200 Jovellanos;22.8106 Barotac Nuevo;10.9000 Bezerros;-8.2333 Beauvais;49.4303 Mikhaylovka;50.0667 Baganga;7.5752 Palompon;11.0500 Surubim;-7.8319 Izberbash;42.5633 Sucun;31.0554 Asingan;16.0023 Lohārdagā;23.4300 Abaji;8.4756 Cerignola;41.2667 Yanguancun;30.4541 Kaş;36.2000 Orland Park;41.6075 Qaskeleng;43.2000 Aldershot;51.2483 Kitahiroshima;42.9855 Aketi;2.7333 Stonecrest;33.6842 Rāmpur Hat;24.1700 Cogan;10.5833 Sillod;20.3000 Felgueiras;41.3667 Ejmiatsin;40.1728 Tokoname;34.8865 Balancán;17.8000 Lambayeque;-6.7000 Sunshi;38.7631 Dumanjog;10.0500 Iskitim;54.6400 Guarabira;-6.8550 Palm Beach Gardens;26.8466 San Pedro;-24.2196 Date;37.8191 Mulbāgal;13.1635 Ma’erkang;31.9046 Moncalieri;45.0005 San Fernando;-34.5833 Kokawa;34.2664 Sérres;41.0833 Jiantang;27.8205 Royal Oak;42.5084 Ishikari;43.1667 Punta Alta;-38.8800 Biguaçu;-27.4939 La Gomera;14.0833 Kosai;34.7184 Pebane;-17.2667 Nyagan;62.1333 Şatrovka;39.9319 Stalowa Wola;50.5667 Melchor Ocampo;19.7083 Samaná;19.2000 Hunsūr;12.3036 Huaniu;34.5658 Antsalova;-18.6667 Matamey;13.4000 Blue Springs;39.0124 Fredericton;45.9636 Choshi;35.7346 Shoreline;47.7564 Tōgane;35.5599 Ávila;40.6500 Gengzhuangqiao;37.4453 Tønsberg;59.2981 Tanauan;11.1167 Chivilcoy;-34.9000 San Luis;18.5500 Pula;44.8703 Bakwa-Kalonji;-4.3500 Tikhvin;59.6500 Midwest City;35.4630 Benslimane;33.6167 Lonāvale;18.7481 New Corella;7.5866 Pradera;3.4167 Abington;40.1108 Gaspar;-26.9308 Roermond;51.2000 Bowie;38.9549 Jackson;40.0980 Mizuho;35.3918 Zempoala;19.9167 Xiezhou;34.9124 Hameln;52.1000 Apex;35.7237 Ambahikily;-21.6000 Kawthoung;9.9833 Meleuz;52.9500 Khlong Luang;14.0647 Allanmyo;19.3783 Sāmalkot;17.0531 Živinice;44.4500 Aleksin;54.5167 Berëzovskiy;56.9000 Ping’an;36.5020 Kālna;23.2200 Naugaon Sādāt;28.9833 Qianwu;22.1635 El Cerrito;3.6667 Santa Maria;6.5500 Mogoditshane;-24.6333 Kothāpet;19.3333 Keonjhargarh;21.6300 Ciudad Cuauhtémoc;22.1833 Będzin;50.3333 Tikhoretsk;45.8667 Grand-Popo;6.2833 Unjha;23.8000 Biała Podlaska;52.0333 Orito;0.6661 Jasaan;8.6500 Qŭnghirot;43.0704 Minokamo;35.4402 Sīra;13.7450 Tiruvallūr;13.1231 Mācherla;16.4800 Meerbusch;51.2667 Campo Alegre;-9.7819 Queen Creek;33.2454 Pavlovo;55.9667 Kandukūr;15.2167 Jerez de García Salinas;22.6481 Diriamba;11.8564 Floriano;-6.7669 Krasnotur’insk;59.7733 Leander;30.5728 Salsk;46.4833 Monte Plata;18.8100 Moknine;35.6333 Milagros;12.2192 Bartlett;35.2337 Beni Enzar;35.2667 St. Cloud;28.2363 Wiwilí;13.6197 Binalonan;16.0500 Alangalang;11.2061 Santiago Tuxtla;18.4654 Cosquín;-31.2436 Nova Odessa;-22.7797 Uson;12.2253 Ponte Nova;-20.4158 Kettering;39.6956 Içara;-28.7128 Buluan;6.7154 Yanam;16.7333 Foumbot;5.5000 Bail-Hongal;15.8137 La Plata;2.3900 Hagonoy;6.6833 Cuvelai;-15.6667 Görlitz;51.1528 Uki;32.6450 Frankfurt (Oder);52.3419 Richards Bay;-28.7694 Kędzierzyn-Koźle;50.3500 Sankt Augustin;50.7700 Barwāni;22.0300 Sendhwa;21.6847 Eldorado;-26.4000 M’diq;35.6858 Stolberg;50.7667 Coruripe;-10.1258 Riosucio;7.4406 Sattenapalle;16.3962 Bobbili;18.5667 Ipele;7.1333 Irpin;50.5167 Krymsk;44.9233 Parker;39.5084 Andradina;-20.8958 Kudamatsu;34.0167 St. Peters;38.7825 Huaixiangcun;22.4817 Mora;10.0088 Nova Esperança;-6.7333 Azzaba;36.7333 Nasīrabād;26.3000 Bulungu;-4.5500 Weymouth;42.1981 Foligno;42.9500 Gūdalur;11.5014 Shicun;38.5383 Sarapiquí;10.4853 Talagante;-33.6667 Vrindāvan;27.5800 Paços de Ferreira;41.2667 Eschweiler;50.8167 Borūjen;31.9678 Kan’onjichō;34.1272 Guáimaro;21.0589 Rîbniţa;47.7667 Lunglei;22.8800 Moramanga;-18.9469 Maricopa;33.0408 Raxaul;26.9833 Dehdasht;30.7950 Alvarado;18.7811 Manfredonia;41.6333 General Pico;-35.6667 Langenhagen;52.4394 Semara;26.7394 Lian;14.0333 Bura;3.1722 Neryungri;56.6833 Moreno;-8.1186 Chini;23.3916 Tiwi;13.4585 Tama;34.4886 Oosterhout;51.6431 Puerto Boyacá;6.0000 Cuemba;-12.1500 Ramon;16.7833 Gangārāmpur;25.4000 San Pedro Sacatepéquez;14.6862 Poblacion;10.4667 Den Helder;52.9333 Paombong;14.8311 Bagheria;38.0833 Talegaon Dābhāde;18.7200 Verviers;50.5833 Kharar;30.7400 Lagonoy;13.7353 Acambay;19.9539 Alcobaça;39.5528 Nedumangād;8.6033 Waiblingen;48.8303 Ixtahuacán;15.4167 Jacundá;-4.4508 Linares;38.0833 Basey;11.2817 Murakami;38.2240 Lenexa;38.9609 Cuneo;44.3833 Colomba;14.7167 Tilakpur;28.5278 Tivoli;41.9667 Athiémé;6.5833 Highland;34.1113 Kāndi;23.9595 Viseu;-1.1969 Anjangaon;21.1500 Ar Riqqah;29.1489 Pangantocan;7.8331 Taquaritinga;-21.4058 Mengla;21.4866 Salgueiro;-8.0669 Cui’erzhuang;38.2897 Placer;11.8689 Diglipur;13.2667 Nōgata;33.7417 Opava;49.9381 Libungan;7.2500 Khāsh;28.2211 Ajodhya;26.8036 Manicoré;-5.8089 Tāybād;34.7400 Trani;41.2667 Kengri;12.9122 Hohoe;7.1490 Bianyang;25.6194 Bartolomé Masó;20.1686 Tomé-Açu;-2.4189 Mount Prospect;42.0641 Lake Havasu City;34.5006 Hassa;36.7994 Magsaysay;6.7667 Kūt-e ‘Abdollāh;31.2414 Registro;-24.4878 Belampalli;19.0756 Amealco;20.1881 Amarante;41.2667 Shali;43.1500 Ropar;30.9664 Periya Semūr;11.3700 Winneba;5.3500 Araci;-11.3328 Mota;11.0833 Gulariyā;28.2056 Monte Alegre;-2.0078 Oke-Mesi;7.8167 Pithorāgarh;29.5800 Zgierz;51.8500 Pila;14.2333 Blainville;45.6700 Tursunzoda;38.5108 Takizawa;39.7347 Finote Selam;10.7000 Limoeiro do Norte;-5.1458 Xinqing;48.2363 Bihać;44.8167 Fréjus;43.4330 Sarikei;2.1256 San Andrés Villa Seca;14.5667 Diffun;16.5936 Irati;-25.4669 Urbiztondo;15.8227 Xánthi;41.1333 Zarrīn Shahr;32.3897 Antratsyt;48.1167 Khān Shaykhūn;35.4389 Longmen;35.6119 Acul du Nord;19.6833 Lozova;48.8892 Bisceglie;41.2431 Quezaltepeque;13.8333 General Tinio;15.3500 Nyenga;0.3800 Intibucá;14.3167 Rangewala;30.8222 Nawucun;22.0039 Yongqing;34.7522 Viramgām;23.1200 Frýdek-Místek;49.6856 Union;42.1258 Ambilobe;-13.2000 Siliancun;22.7155 Tinley Park;41.5670 Bambang;16.3872 Tsévié;6.4333 Boituva;-23.2833 Baden-Baden;48.7628 Portimão;37.1369 Lingen;52.5231 Wangguanzhuang;37.0183 Volzhsk;55.8703 Hoogeveen;52.7333 Changling;44.2700 Hidaka;35.9078 Toki;35.4192 Madhupur;24.2600 Catu;-12.3497 Parsippany-Troy Hills;40.8601 Xiva;41.3833 Narbonne;43.1836 Batac;18.0554 Hashtgerd;35.9619 Jaru;-10.4389 Gus’-Khrustal’nyy;55.6167 Asadābād;34.7825 Tucano;-10.9628 Xihu;23.9594 Atchampeta;15.5412 Umm Ruwaba;12.9046 Rengo;-34.4167 La Roche-sur-Yon;46.6705 Ovar;40.8667 Tūyserkān;34.5481 DeSoto;32.5992 Martin;49.0650 Apatity;67.5667 Azul;-36.7833 Tauá;-6.0028 Bhongīr;17.5100 Mulanay;13.5222 Zile;40.3000 Asturias;10.5679 Narutochō-mitsuishi;34.1667 San Agustín Acasaguastlán;14.9500 Sacapulas;15.2892 Mercedes;14.1093 Limoeiro;-7.8750 Monte Mor;-22.9467 Chicopee;42.1764 Canterbury;51.2800 Nāmakkal;11.2167 Corby;52.4877 Ghātāl;22.6700 El Banco;8.9983 Kuala Kapuas;-3.0013 Manapla;10.9580 Buthidaung;20.8667 Pulheim;51.0000 Belén de Escobar;-34.3333 Madison;34.7114 Harpanahalli;14.7877 Schweinfurt;50.0500 Horizonte;-4.0989 Khorramdarreh;36.2072 Villarrica;-39.2667 Porirua;-41.1333 West Haven;41.2739 Jaguaquara;-13.5308 San Remo;43.8175 Māngrol;21.1200 Porto Alexandre;-15.8000 Tonekābon;36.8164 Hyères;43.1199 Yamasá;18.7667 Smyrna;33.8633 Rolim de Moura;-11.7254 Aurora;44.0000 Bitonto;41.1167 Ottappālam;10.7700 Wylie;33.0362 Barbalha;-7.3050 Taliwang;-8.7336 Pallíni;38.0000 Challakere;14.3120 Vannes;47.6559 Polūr;12.5000 Puttūr;13.4500 Diamond Bar;33.9992 Songlindian;39.4100 San Isidro;15.3097 Chonthrhu;32.4865 Hattingen;51.3992 Bad Homburg;50.2167 Pombal;39.9161 Christchurch;50.7300 Hervey Bay;-25.2900 Islāmpur;26.2700 Modica;36.8672 Apple Valley;44.7457 Dompu;-8.5364 Agbangnizoun;7.0667 Chino;35.9955 Punganūru;13.3667 Bávaro;18.7167 Juruti;-2.1519 Eqbālīyeh;36.2314 Acıpayam;37.4250 Nāndod;21.8706 Konan;35.0000 Santa Cruz;10.2358 Carigara;11.3000 Funato;34.2500 Zhushan;23.6889 Ucuma;-12.8500 Santa Isabel;-23.3158 Dikwella South;5.9667 Oxchuc;16.7833 Ŏjŏk-tong;40.2167 Khurai;24.0437 Ensenada;-34.8644 Keighley;53.8670 Estancia;11.4500 Bilis Qooqaani;0.2822 Huaishu;38.0828 Cajicá;4.9167 Viçosa do Ceará;-3.5619 Pontevedra;10.3667 Pôrto Ferreira;-21.8539 Indaial;-26.8978 Alenquer;-1.9419 Sara;11.2500 Pinto;40.2500 Barreirinhas;-2.7469 Liushi;38.5445 Báguanos;20.7631 Bradenton;27.4901 Minamiuonuma;37.0655 Brookhaven;33.8743 Sakurai;34.5167 Santa Cruz;13.4833 Luquembo;-10.7333 Wayne;40.9481 Kāsaragod;12.5000 Sohna;28.2500 Mali;23.1277 Hacienda Heights;33.9970 Sarpsborg;59.2858 Barranqueras;-27.4833 Annaka;36.3263 Kampen;52.5500 Cholet;47.0600 Manhattan;39.1886 Xielu;37.0359 Tiruvalla;9.3856 Taquara;-29.6508 Terneuzen;51.3333 Dandarah;26.1681 Chota;-6.5614 Tigard;45.4237 Monte Santo;-10.4378 Saruhanlı;38.7342 Samāna;30.1500 Jinchang;38.4858 Masinloc;15.5333 Teramo;42.6589 San Manuel;16.0656 Ostrołęka;53.0667 Bad Salzuflen;52.0833 Sāgar;14.1667 Antanifotsy;-19.6800 Rāhuri;19.3800 Manjeri;11.1197 Kızılpınar;41.2667 Sidhi;24.4200 Chenab Nagar;31.7528 Dayin;38.9358 Nihonmatsu;37.5849 Trento;8.0459 Cava de’ Tirreni;40.7000 Jalapa;13.9222 Montesilvano;42.5142 Tapas;11.2622 Mbulungu;-6.0667 Umm Qurūn;25.2500 Nordhorn;52.4319 São José do Rio Pardo;-21.5958 Shidong;23.6193 Kotka;60.4667 Şabbāshahr;35.5833 Cornillon;18.6667 Qingan;46.8719 Javānrūd;34.8067 Palmaner;13.2007 Huixtla;15.1000 Chaozhou;22.5500 Pārvatipuram;18.7800 Minalabac;13.5700 Tlacotepec;18.6882 Popeşti-Leordeni;44.3800 Sado;38.0183 Siena;43.3183 El Rama;12.1617 Piekary Śląskie;50.3833 Jalor;25.3500 Kāliyāganj;25.6300 Hanyū;36.1726 Cachoeiras de Macacu;-22.4628 Bandar-e Torkaman;36.9017 Tarma;-11.4186 Yucaipa;34.0336 Bristol;40.1216 Zarafshon Shahri;41.6081 Xingcheng;40.1399 Valle del Guamuez;0.4253 Vikārābād;17.3300 Ruteng;-8.6127 Manglaur;29.8000 Hashtpar;37.8008 Ankadinondry-Sakay;-19.0000 Daudnagar;25.0300 Rotorua;-38.1378 Ixhuatlán de Madero;20.6833 Narva;59.3758 Peabody;42.5335 Barra;-11.0889 Tokmok;42.8333 Mansāla;15.9417 Bir Ali Ben Khalifa;34.7339 Ban Om Noi;13.7001 Zhigulevsk;53.3997 Umred;20.8500 Titay;7.8703 Southaven;34.9514 Apopka;28.7015 Tagkawayan;13.9667 Chèddra;13.4428 Acará;-1.9608 Badūria;22.7400 Juanjuí;-7.1802 Avellino;40.9167 Sultānganj;25.2460 Dholka;22.7200 Goālpāra;26.1700 Cuenca;40.0667 Puerto Libertador;7.9067 Horizon West;28.4417 Cabagan;17.4333 Millcreek;42.0859 Ghōriyān;34.3400 Chilibre;9.1500 New Plymouth;-39.0578 Patancheruvu;17.5333 Chystiakove;48.0219 Ashta;23.0175 Tuni;17.3500 Severn;39.1355 Liski;50.9667 Neustadt;49.3500 Markala;13.6739 Três Pontas;-21.3669 An Nimāş;19.1200 Jaggayyapeta;16.8920 Chenārān;36.6456 Svobodnyy;51.4000 Salo;60.3861 Impasugong;8.3028 A Yun Pa;13.3939 Bacaadweyn;7.1917 Santo Domingo;22.5833 Colton;34.0538 Bījār;35.8729 Kentwood;42.8852 Magpet;7.1167 Kitaotao;7.6406 Passau;48.5744 Zongo;4.3433 São Francisco do Sul;-26.2428 São Francisco;-15.9489 Hamada;34.9000 Zaragoza;15.4492 Sibonga;10.0333 Pasacao;13.5103 Minnetonka;44.9322 Kongoussi;13.3333 Ajuy;11.1725 Wheaton;41.8561 Huesca;42.1333 Cangola;-7.9667 Port Shepstone;-30.7500 Devakottai;9.9976 Elda;38.4789 Marco de Canavezes;41.1833 Dauis;9.6250 Tilhar;27.9800 Krasnokamsk;58.0833 Upata;8.0204 Colmenar Viejo;40.6589 Al Majāridah;19.1167 Wetzlar;50.5667 Guiuan;11.0300 Velletri;41.6667 Enerhodar;47.4989 Frechen;50.9167 Shāhpur;16.7000 Zhangjiazhuang;38.1753 Cambundi Catembo;-10.0756 Encarnación de Díaz;21.5167 West Sacramento;38.5557 Anzio;41.4472 Naka;36.4574 Acireale;37.6167 Dongshan;24.6319 Lakhminia;25.4126 Morondava;-20.2847 Lucban;14.1133 Porto Feliz;-23.2150 Frutal;-20.0250 Casa Grande;32.9069 Thérmi;40.5500 Gudermes;43.3500 Guernica;-34.9167 Pirapora;-17.3478 San Manuel;15.3333 Rumia;54.5667 Yangmei;22.8728 Dangila;11.2667 Mutsu;41.2928 Barbosa;6.4390 Xincheng;39.9883 Normal;40.5217 Olímpia;-20.7369 Santo Estêvão;-12.4300 Pryluky;50.6000 Puttūr;12.7648 Barobo;8.5292 Molave;8.0933 Vertientes;21.2569 Yaozhuangcun;30.9113 Capivari;-22.9950 São Bento do Una;-8.5228 Buenavista;10.7000 Howell;40.1819 San Jacinto;33.7970 Aurora;7.9484 Akbou;36.4667 Tame;6.4667 Canguçu;-31.3950 Irinjālakuda;10.3514 Açu;-5.5769 Karlovac;45.4833 Vyksa;55.3167 Woerden;52.0833 Kalamáta;37.0378 Asunción Mita;14.3333 San Bartolomé;27.9254 Novato;38.0920 Mladenovac;44.4333 Leopoldina;-21.5319 San Severo;41.6951 Acopiara;-6.0950 Sagua la Grande;22.8086 Hamtic;10.7000 Mandi Dabwāli;29.9477 Pasaje;-3.3269 Pinellas Park;27.8589 La Ceja;6.0306 Simojovel de Allende;17.1333 Padre Garcia;13.8833 Roskilde;55.6500 Galveston;29.2484 Edina;44.8914 Ahlen;51.7633 Arsenyev;44.1667 Panjakent;39.5000 Kleve;51.7900 San Estanislao;-24.6500 Venkatagiri;13.9667 Kelaa Kebira;35.8667 Neibu;22.6500 Hyosha;0.6975 ‘Alīābād-e Katūl;36.9083 Jiāganj;24.2300 Sihor;21.7000 Sison;16.1667 Mafra;-26.1108 Lorica;9.2419 Lower Paxton;40.3183 Chone;-0.6833 Tomiya;38.4000 Imarichō-kō;33.2667 Bani;16.1869 Jangaon;17.7227 Usa;33.5320 Kobryn;52.2167 Korāput;18.8120 Herriman;40.4899 Mongaguá;-24.0869 Clorinda;-25.2833 Elyria;41.3760 San Joaquin;10.6000 Wolfenbüttel;52.1622 Poprad;49.0594 Krasnokamensk;50.1000 Tutóia;-2.7619 Canoinhas;-26.1769 União da Vitória;-26.2300 San José Villa de Allende;19.3747 Malkara;40.8933 Capitão Poço;-1.7450 Fatwa;25.5096 Granadilla de Abona;28.1167 El Tumbador;14.8667 Grand Island;40.9218 Minami-Sōma;37.6422 Thongwa;16.7547 San Miguel Ixtahuacán;15.2500 Huaiyang;37.7558 Bargny;14.6939 Ålesund;62.4740 Burauen;10.9833 Piaseczno;52.0667 Ibajay;11.8211 Naguilian;16.5333 Torrelavega;43.3531 Jiyyammavalasa;18.7900 Sironj;24.1000 Granja;-3.1200 Kahnūj;27.9500 An Nabk;34.0167 Miyakojima;24.8056 Bato;13.3578 Horishni Plavni;49.0167 Bad Kreuznach;49.8500 Pacora;9.0800 Mandamāri;18.9822 Sertolovo;60.1500 President Roxas;7.1544 Başkale;38.0475 Lacey;47.0462 Claveria;8.6100 Qabqa;36.2667 Bentonville;36.3547 Caetité;-14.0689 Cihanbeyli;38.6581 Methuen Town;42.7340 Victoria;13.1719 Vyazma;55.2107 Alaminos;14.0635 Boa Viagem;-5.1278 Tamboril;19.4800 Diu;20.7150 Bikramganj;25.2107 Phaltan;17.9800 Pordenone;45.9500 Glendora;34.1449 Tekkeköy;41.2125 Marechal Deodoro;-9.7100 Rincón de la Victoria;36.7167 Smyrna;35.9687 Sapé;-7.0950 Girardota;6.3764 Florissant;38.7996 Ibbenbüren;52.2778 Ambohibary;-19.6167 Naviraí;-23.0650 Taxco de Alarcón;18.5564 Leon;10.7808 Stratford;41.2070 Delmiro Gouveia;-9.3858 Roman;46.9300 Plaisance;19.6000 Villareal;39.9378 Civitavecchia;42.1000 Welland;42.9833 Khurda;20.1833 Basankusu;1.2222 Novovolynsk;50.7333 Cangandala;-9.7833 Delano;35.7662 Thomazeau;18.6500 Abdul Hakim;30.5500 Cruz del Eje;-30.7333 Portel;-1.9358 Kannapolis;35.4764 Burla;21.5098 Belorechensk;44.7686 Hoffman Estates;42.0640 Buíque;-8.6233 Tūndla;27.2000 Chajul;15.4872 Belle-Anse;18.2333 Santa Eulalia;15.7333 Sagua de Tánamo;20.5858 Ampanihy;-24.6833 Yendi;9.4324 Beaumont;33.9076 Gādarwāra;22.9235 Ālbū Kamāl;34.4536 Wangjiazhai;26.6895 Izumi;32.0833 Bar Bigha;25.2186 San Onofre;9.7333 Trinidad;21.8042 Pola de Siero;43.3833 Khattan;33.3700 Zangāreddigūdem;17.1229 Dungu;3.6167 Kālpi;26.1200 Ad Dabbah;18.0502 Mabini;13.7167 Timashevsk;45.6167 Calviá;39.5656 Eilat;29.5569 Kandangan;-2.7833 Peranāmpattu;12.9387 Santa Ignacia;15.6167 Parobé;-29.6289 Metpalli;18.8492 Ntoum;0.3844 Racibórz;50.0833 Ānaiyūr;9.9615 Caldas da Rainha;39.4072 Bandō;36.0484 Caramoan;13.7707 El Bagre;7.5942 Battipaglia;40.6167 Cardona;14.4861 Covilhã;40.2833 Aguadulce;8.2400 Forbesganj;26.3000 Livingston;55.8834 Cathedral City;33.8362 Shimencun;30.6263 Itupiranga;-5.1350 Mulongo;-7.8333 Huazangsi;36.9836 Tsukubamirai;35.9631 Karviná;49.8542 İncirliova;37.8547 Böblingen;48.6856 Maubin;16.7300 Speyer;49.3194 Isfara;40.1167 Solnechnogorsk;56.1833 Baham;5.3333 Caleta Olivia;-46.4333 Mazara del Vallo;37.6500 Tulsīpur;28.1278 Jaspur;29.2833 Gummersbach;51.0333 El Talar de Pacheco;-34.4719 Bowringpet;12.9912 Burien;47.4762 Marigot;18.2333 Campo Belo;-20.8969 Cerca la Source;19.1667 Tachilek;20.4500 Guapimirim;-22.5369 Shahr-e Bābak;30.1164 Chieti;42.3500 Rovigo;45.0809 São Miguel do Guamá;-1.6269 Guarne;6.2800 Qabb Eliâs;33.7986 Bozeman;45.6833 Māndvi;22.8333 Bantacan;7.5483 North Bay;46.3000 Knysna;-34.0356 Pau d’Alho;-7.8969 Horsham;51.0620 Zhexiang;24.2591 Bhavāni;11.4455 Butare;-2.6000 Ocampo;13.5594 Tucurú;15.3000 Scandicci;43.7544 Sidlaghatta;13.3900 Kizhake Chālakudi;10.3000 Ribeira do Pombal;-10.8339 Uzlovaya;53.9833 Cumanayagua;22.1522 Ravensburg;47.7831 Trairi;-3.2778 Utrera;37.1830 Sūsangerd;31.5608 Arao;32.9833 Kalawana;6.5315 Satte;36.0781 Nentón;15.8012 Cutervo;-6.3778 Santa Cruz del Sur;20.7194 Montijo;38.6833 Kyzyl-Kyya;40.2611 Severomorsk;69.0692 Willich;51.2631 Tadmur;34.5600 Misterbianco;37.5183 Hassan Abdal;33.8195 Marpalli;17.5389 Humaitá;-7.5061 Rastatt;48.8500 Rongat;12.4667 Cheremkhovo;53.1500 Segovia;40.9481 Sibsāgar;26.9800 San Narciso;13.5677 Cuyotenango;14.5421 Laksar;29.7490 Twin Falls;42.5645 Douyu;37.9007 Repalle;16.0172 Chinnamanūr;10.0412 Sakubva;-18.9833 Snezhinsk;56.0833 Laval;48.0733 Kuchaiburi;22.2700 Hannan;34.3597 Zabīd;14.2000 Nanao;37.0431 Fangcun;37.9799 Hilsa;25.3200 Vyāra;21.1200 Timbaúba;-7.5050 Enid;36.4063 Sakaidechō;34.3167 Padrauna;26.9000 Dunwoody;33.9418 Levittown;40.1533 Arles;43.6767 Jesús Menéndez;21.1633 Palm Desert;33.7378 Houten;52.0333 Esher;51.3691 Bas Limbé;19.8000 Goslar;51.9072 Gombong;-7.6000 Irondequoit;43.2096 Pardwāl;32.3286 Campos do Jordão;-22.7394 Nazarovo;56.0000 Umán;20.8500 Guisa;20.2608 Pontal;-21.0225 Yūki;36.3055 Aringay;16.3982 Kalamansig;6.5667 East Hartford;41.7634 Naugachhia;25.4000 Sujānpur;32.3127 Kyōtango;35.6242 Mozhga;56.4500 Ye;15.2467 Kirishi;59.4500 Cuyahoga Falls;41.1641 Goba;7.0000 Louveira;-23.0864 Miyako;39.6414 Kizlyar;43.8500 Cataingan;12.0028 Goshogawara;40.8080 Peine;52.3203 Susono;35.1739 Çermik;38.1372 Moss;59.4592 Fafe;41.4500 Nicoya;10.1024 Nakagawa;33.5000 Banī Walīd;31.7619 Marana;32.4355 Higashiura;34.9771 Novovyatsk;58.5083 Mishawaka;41.6742 Pozi;23.4611 Tŭrtkŭl;41.5500 Longwan;38.9564 Beloeil;45.5667 El Jem;35.2967 Santa Lucía del Camino;17.0667 Mahalapye;-23.1000 Puławy;51.4167 Mweka;-4.8500 Columbus;39.2093 Haskah Mēnah;34.1000 Troy;42.7354 Belleville;44.1667 Beruniy;41.6833 Gloria;12.9722 Milford city;41.2255 Northwich;53.2590 Peddāpuram;17.0800 Yasu;35.0667 Collierville;35.0470 Yerba Buena;-26.8167 Chbar Mon;11.4500 Baranoa;10.8000 Santiago;-29.1919 Shakhtarsk;48.0333 Ghŭlakandoz;40.1617 La Calera;-32.7867 Herning;56.1333 Metema;12.9667 Nichinan;31.6022 Sittard;51.0000 Silkeborg;56.1833 Donsol;12.9083 Lörrach;47.6167 Tebourba;36.8333 Ikot Okoro;4.9000 Qinhe;36.5047 El Nido;11.1956 Clacton-on-Sea;51.7918 Grapevine;32.9343 Nettuno;41.4667 Amulung;17.8387 Artur Nogueira;-22.5731 Kabarore;-1.6211 Starogard Gdański;53.9667 Mirabel;45.6500 Nahuizalco;13.7833 San Ignacio;-26.8867 Weert;51.2500 Parāsia;22.1913 Wao;7.6404 Biaora;23.9163 Chinú;9.0833 Amora;38.6265 Āqchah;36.9114 Chatrā;24.2045 Tājūrā’;32.8818 Bhadrāchalam;17.6688 Emden;53.3669 Ardea;41.6167 Peñablanca;17.6258 Baicheng;41.7957 Tammampatti;11.4381 Wandan;22.5897 Summerville;33.0016 Casalnuovo di Napoli;40.9167 Pananaw;5.9833 Palpalá;-24.2500 Sihorā;23.4871 Huanghuajie;27.9950 Macaúbas;-13.0189 Rivoli;45.0697 Nanjian;25.0530 Xiazhai;27.6909 Kameyama;34.8558 São Joaquim da Barra;-20.5808 Erftstadt;50.8167 Downers Grove;41.7949 Alta Floresta;-9.8758 Cerquilho Velho;-23.1650 Mantova;45.1564 Murray;40.6498 Vargem Grande Paulista;-23.6028 Lindong;43.9673 Sojat;25.9200 Bhainsa;19.1000 Borovichi;58.4000 Santa Lucía;21.0281 Aarsâl;34.1794 Zargar;37.8000 Itararé;-24.1125 Chhāgalnāiya;23.0395 Perambalūr;11.2300 Déressia;9.7603 Roslavl;53.9500 Haripur;33.9942 Gogrial;8.5300 Shankou;24.5032 Haverford;39.9868 Miyoshi;34.8000 Pontevedra;11.4833 IJmuiden;52.4586 Albi;43.9289 Sesto Fiorentino;43.8333 Catalina Foothills;32.3046 Sardasht;36.1542 East Honolulu;21.2975 Upper Buchanan;5.9161 Shawinigan;46.5667 Tepeapulco;19.7856 Paine;-33.8167 Kahemba;-7.2829 Phalodi;27.1310 Ceel Dheere;5.3683 Godinlabe;5.8794 Sechura;-5.5576 Porvoo;60.3944 Nohar;29.1800 Al Wajh;26.2833 Abū Ḩamad;19.5433 Nenjiang;49.1745 Jiuzhou;39.5054 Harderwijk;52.3500 Heidenheim;48.6761 Draper;40.4957 Alcala;15.8468 Ejeda;-24.3500 Grasse;43.6667 Chikugo;33.2092 Leonberg;48.8014 Bigadiç;39.3925 Chomutov;50.4611 Vryburg;-26.9500 Mailiao;23.7500 Menggala;-4.4750 Buguruslan;53.6583 Kapadvanj;23.0200 Frankenthal;49.5333 Xankəndi;39.8153 Slonim;53.0833 Concepción;-27.3333 Gediz;39.0417 Santa Rita;14.9953 Mājalgaon;19.1500 Çine;37.6117 Bergkamen;51.6167 Prattipādu;16.1878 Mojo;8.6500 ‘Akko;32.9278 Karapınar;37.7147 Gualán;15.1333 Sosúa;19.7500 Sulleru;18.5333 Carmen de Viboral;6.0833 Sesimbra;38.4437 Noshiromachi;40.2121 Rosario;8.3814 Lucan;53.3544 Adeje;28.1167 Tomisato;35.7000 Suzaka;36.6511 Godda;24.8270 Dumarao;11.2631 Ridder;50.3500 Batajnica;44.9022 Chesterfield;38.6589 Zyryanovsk;49.7453 Zongshizhuang;37.8615 Gronau;52.2125 Mbouda;5.6333 Araklı;40.9333 Smethwick;52.4931 Cimitarra;6.3167 Guaxupé;-21.3050 Liujiaxia;35.9423 Bedford;32.8464 Berber;18.0306 Tōkamachi;37.1333 Bad Oeynhausen;52.2000 María la Baja;10.0000 Wofotang;38.6190 Odiongan;12.4000 Hikari;33.9619 St. Louis Park;44.9488 Viana;-3.2200 Waalwijk;51.6833 Kiblawan;6.6167 Jiashizhuang;37.8683 Özgön;40.7667 Donglizhuang;37.9351 Okhtyrka;50.3074 Muhanga;-2.0845 Chioggia;45.2196 Azusa;34.1386 Asakura;33.4167 Villaguay;-31.8500 Kīlvishāram;12.9143 East Brunswick;40.4281 Nomimachi;36.4470 Vargem Grande;-3.5428 Pio Duran;13.0333 Dulag;10.9525 Nowshahr;36.6489 Midori;36.3948 Welwyn Garden City;51.8062 Empoli;43.7167 Carmen;9.8167 Vinzons;14.1739 Ad Darwa;33.4167 Samaniego;1.3500 Minalin;14.9667 Euclid;41.5903 Cwmbran;51.6530 Mulukukú;13.1764 Changyŏn;38.2517 Yong’an;31.0206 Al Aaroui;35.0029 Beberibe;-4.1800 Malabang;7.5903 Óbidos;-1.9178 Panay;11.5553 McLean;38.9436 Libertador General San Martín;-23.8000 Sông Đốc;9.0333 Santa Rosa;-3.4522 Rheda-Wiedenbrück;51.8417 Coral Gables;25.7037 Monopoli;40.9500 Agustín Codazzi;10.0367 Martigues;43.4053 Lecco;45.8500 Bantvāl;12.8905 Farajok;3.8724 San Benedetto del Tronto;42.9500 Wejherowo;54.6000 Lincoln;38.8774 Nikaweratiya;7.7475 Lesnoy;58.6333 Saray;41.4411 Freising;48.4028 Alsdorf;50.8833 Verrettes;19.0500 Jeffersonville;38.3376 La Libertad;15.5000 Porto Nacional;-10.7078 Tunuyán;-33.5667 Guntakal Junction;15.1200 Ceres;37.5952 La Reja;-34.6394 Washington;39.7469 Kifisiá;38.0833 Dachau;48.2603 Sungurlu;40.1610 Vich;41.9304 Libona;8.3333 Jaggisettigūdem;17.1167 Starachowice;51.0500 Huaquillas;-3.4814 Ascoli Piceno;42.8500 Biloxi;30.4426 Jales;-20.2689 Lawrence;39.8674 Dumingag;8.1550 Iranduba;-3.2850 Caraballeda;10.6167 Santo Domingo Suchitepéquez;14.4667 Bornheim;50.7592 Gooty;15.1210 Al Mayādīn;35.0183 Poway;32.9871 Saint-Herblain;47.2122 Cedar Hill;32.5810 Martina Franca;40.7000 Inabanga;10.0333 Yamaga;33.0169 Pedreira;-22.7419 Erlin;23.9229 Omitama;36.2393 Barotac Viejo;11.0500 Waingapu;-9.6500 Shuishang;23.4319 Brandon;49.8483 Orangetown;41.0527 Sanare;9.7822 Prudentópolis;-25.2128 Lal-lo;18.2000 Poções;-14.5300 Maţāy;28.4167 Portage;42.2000 Rowland Heights;33.9716 Niagara Falls;43.0921 Jasdan;22.0300 Suwa;36.0391 Ami;36.0308 Bacolor;14.9984 Hørsholm;55.8803 Kozlu;41.4333 Nyaungu;21.2000 Boucan Carré;18.9667 Shādegān;30.6497 Garmsār;35.2183 Settimo Torinese;45.1333 Congonhas;-20.5000 ’Aïn Azel;35.8433 New Washington;11.6483 Rimouski;48.4500 Masagua;14.2084 Cuxhaven;53.8611 Angol;-37.8000 Begoro;6.3833 Wakema;16.6000 Leiktho;19.2222 Voluntari;44.4925 Giddarbāha;30.2000 Jalandhar Cantonment;31.2860 Nāngal Township;31.3850 Namtu;23.0925 Siocon;7.7067 Dublin;40.1112 El Dorado Hills;38.6750 Gumia;23.8106 Dongshan;22.0635 Straubing;48.8833 Barnstable;41.6655 Homnābād;17.7707 Bokāro;23.7871 Corato;41.1500 Ōamishirasato;35.5167 Mangapet;18.2500 Zutphen;52.1400 Sanmu;35.6000 Helsingør;56.0361 Ridderkerk;51.8667 Beveren;51.2000 Kurganinsk;44.8667 Saint-Priest;45.6972 Mahayag;8.1297 Gavá;41.3072 Soest;51.5711 Duyên Hải;9.6331 San Pedro Mixtepec;16.0000 Vite;17.2711 West Orange;40.7893 Hoskote;13.0721 Tuba;16.3928 Tibigan;9.9500 Ishigaki;24.3406 Dornbirn;47.4167 Babaeski;41.4325 Itapagé;-3.6869 Évreux;49.0200 Kołobrzeg;54.1761 Hojāi;26.0028 Damāvand;35.7178 Mooresville;35.5847 Spring Hill;35.7437 Aubagne;43.2908 Sadāseopet;17.6203 Longtoushan Jiezi;27.1157 Kalima;-2.5667 Ibiporã;-23.2689 Chinchiná;4.9825 Děčín;50.7736 Roswell;33.3730 Tarnobrzeg;50.5833 Durham;54.7761 Xique-Xique;-10.8229 Inhumas;-16.3578 Lilh;32.6900 Campi Bisenzio;43.8167 Ash Shaykh Badr;34.9833 Pangkou;38.6457 Kefamenanu;-9.4467 Zhujiacun;26.3164 Rio Grande da Serra;-23.7439 Beypazarı;40.1703 Titao;13.7667 Bhatkal;13.9853 Çan;40.0275 Zawiercie;50.5000 Leesburg;39.1057 Brianka;48.5133 Turda;46.5667 Joshīmath;30.5550 Finike;36.3000 Donetsk;48.3333 Rancho Santa Margarita;33.6318 Tuckahoe;37.5878 Alta Gracia;-31.6667 Balimbing;5.0728 Shimotsuchō-kominami;34.1500 Titusville;28.5727 Paterno;37.5667 Glenview;42.0825 Yongping;37.0103 Kashira;54.8333 Shelekhov;52.2167 Vila Verde;41.6500 Ena;35.4492 Mengmeng;23.4718 Perth;56.3958 Carepa;7.7578 Tobias Barreto;-11.1839 Saint-Malo;48.6500 Skierniewice;51.9667 Wauwatosa;43.0616 Okaya;36.0671 Stillwater;36.1317 Otradnyy;53.3667 Tonami;36.6475 Pallipālaiyam;11.3679 Stade;53.6008 Bhamo;24.2667 Minot;48.2375 Santa Cruz do Rio Pardo;-22.8989 Basoko;1.2333 Higashine;38.4313 San Francisco;16.8017 Diamantina;-18.2489 Ban Plai Bua Phatthana;13.9032 Mattanūr;11.9310 Santana do Ipanema;-9.3778 Mīt Salsīl;31.1667 Takeochō-takeo;33.1947 Soest;52.1833 Wilson;35.7311 Charleville-Mézières;49.7719 Golpāyegān;33.4536 Pativilca;-10.6958 Vigia;-0.8578 Rio de Mouro;38.7689 Jiquilisco;13.3167 Pakenham;-38.0712 Lousada;41.3000 Dumka;24.4200 Talisay;14.1000 Águeda;40.5744 Río Cauto;20.5636 Kuilsrivier;-34.0333 Yecun;33.7663 Hennef;50.7833 Malinao;13.4000 Media;46.1500 Ono;34.8497 Güroymak;38.5769 Luwuk;-0.9396 Newark;37.5201 Penco;-36.7333 Sogod;10.3833 Herzogenrath;50.8667 Vyshniy Volochëk;57.5833 Al Madrah Samā’il;23.3103 Ambatondrazaka;-17.8256 Vuyyūru;16.3667 Hitachi-ota;36.5383 Amahai;-3.3331 Pilar;11.4878 Kikugawa;34.7500 Umarkhed;19.6000 Roseville;42.5074 Oberursel;50.2033 Snizhne;48.0167 Ipiaú;-14.1369 Ban Bang Mae Nang;13.8815 Rieti;42.4044 ‘Āmūdā;37.1042 San Lorenzo;-28.1200 Jitaicun;36.3533 Vikramasingapuram;8.6700 Chaïdári;38.0167 Bunawan;8.1781 Manappārai;10.6075 Catriel;-37.8667 Brive-la-Gaillarde;45.1583 Donggang;22.4667 Kutno;52.2333 Punalūr;9.0170 La Vega;2.0008 Chenalhó;16.9333 Monroe;40.3191 Carcassonne;43.2100 Livny;52.4239 Aznā;33.4558 Cunén;15.3379 Poso;-1.4000 Capão Bonito;-24.0058 Isabel;10.9300 Landau;49.2000 Nandikotkūr;15.8670 Santa Ana Chiautempan;19.3167 Mēla Gūdalūr;9.4405 Pilar;14.6667 Nanto;36.5878 Atalaia;-9.5019 Qingyuan;24.5004 Mataquescuintla;14.5336 Borgne;19.8500 San Andrés de Sotavento;9.1500 Sumisip;6.4167 East Lansing;42.7480 Sanski Most;44.7667 Chaparral;3.7500 Liulin;36.5570 Jobabo;20.9078 Villa Constitución;-33.2333 Gosen;37.7444 Paracambi;-22.6108 Perinton;43.0781 General Martín Miguel de Güemes;-24.6667 Mentor;41.6893 Tangdukou;26.9949 Bothell;47.7735 Daxiang;22.3775 Yehe;37.9416 Ouled Moussa;36.6831 Talghar;43.3000 Kingisepp;59.3667 Yabrūd;33.9667 Aquidauana;-20.4708 Videira;-27.0078 Tomioka;36.2599 Albuera;10.9186 Fellbach;48.8086 Şowme‘eh Sarā;37.3117 Bairāgnia;26.7406 Ayapel;8.3167 Ayungon;9.8584 Haeryong;34.9131 Kampli;15.4063 Saravena;6.9556 As Sa‘dīyah;34.1906 San Luis Obispo;35.2669 Kasumbalesa;-12.2564 Waspán;14.7408 Burleson;32.5170 Selu;19.4551 Douz;33.4500 Zarinsk;53.7089 East Providence;41.8065 Huatan;24.0316 Marhanets;47.6480 Padra;22.2300 Middletown;41.5476 Schwerte;51.4458 Urrao;6.3156 Tres Arroyos;-38.3667 Dülmen;51.8308 Prievidza;48.7711 Egg Harbor;39.3787 Blois;47.5939 Nysa;50.4714 Qiutouzhen;37.9841 Calimaya;19.1608 Hà Tiên;10.3833 Saranambana;-17.2500 Neunkirchen;49.3500 Banisilan;7.5000 Norala;6.5500 Noboribetsu;42.4128 Al Quşayr;34.5119 Dracena;-21.4825 Guajará-Mirim;-10.7828 Frosinone;41.6333 Kabasalan;7.7968 Fujiyoshida;35.4875 Puñal;19.4000 Garhwa;24.1100 Wangtan;39.2847 Sanuki;34.3200 Tuy;14.0167 Roldanillo;4.4167 Filderstadt;48.6803 Fastiv;50.0747 Nanzhuangzhen;23.7210 Rondon do Pará;-4.7758 Quva;40.5247 Yashan;22.4776 Bebandem;-8.4046 Valencia;8.2592 Agrínio;38.6167 Cornélio Procópio;-23.1808 Obra;24.4200 Veldhoven;51.4200 Sumpango;14.6464 Dongzhuosu;38.0658 Melle;52.2044 San Rafael;10.0417 Brea;33.9254 Ciudad Serdán;18.9833 Lohja;60.2500 Borča;44.8700 Salina;38.8136 Potomac;39.0141 Lábrea;-7.2589 Marechal Cândido Rondon;-24.5558 Hyvinkää;60.6333 Wodzisław Śląski;50.0000 Lukavac;44.5500 Taebaek;37.1667 Hioki;31.6117 Oranienburg;52.7544 Shimeo;33.5914 Fangguan;39.3237 Nilambūr;11.2769 La Caleta;18.4639 Cordon;16.6667 Coelho Neto;-4.2569 Lubny;50.0186 Tamagawa;33.6389 San Francisco de los Romo;22.0833 Evesham;39.8605 Berëzovskiy;55.6167 Montevista;7.7053 Vercelli;45.3256 Zwijndrecht;51.8167 Jablonec nad Nisou;50.7244 Aland;17.5642 Thoubāl;24.6300 Stepnogorsk;52.3497 Hof;50.3167 Cabreúva;-23.3075 Anandpur;21.2141 Izunokuni;35.0333 Farmington;36.7555 Suluova;40.8333 Mānvi;15.9833 Jeomchon;36.6028 Faratsiho;-19.4000 Bocaiúva;-17.1078 Yalvaç;38.2956 Muara Teweh;-0.9535 Nakai;33.5833 Beringen;51.0333 Puerto Villarroel;-16.8667 Shimenzhai;40.0892 Jocotepec;20.2861 Sicklerville;39.7452 South Brunswick;40.3840 Puerto Tejada;3.2500 Eusébio;-3.8900 Todupulai;9.8969 Narvacan;17.4192 Ciudad Tecún Umán;14.6833 Bhālki;18.0435 Lower Tungawan;7.6047 Cornwall;45.0275 Cristalina;-16.7689 Dhupgāri;26.6000 Itapema;-27.0900 Ocoee;28.5796 Hilo;19.6883 Nawāshahr;31.1167 Rodgau;50.0167 Gryazi;52.5000 Ḩadīthah;34.1397 Oro Valley;32.4208 Sena Madureira;-9.0658 La Independencia;16.2000 Dendermonde;51.0333 Conceição do Araguaia;-8.2578 Río Tercero;-32.1833 Kurayoshi;35.4333 Dharmaragar;24.3785 Montecristi;-1.0500 Dingle;11.0500 Minster;51.4210 Kasaoka;34.5039 Torzhok;57.0333 Terracina;41.2833 Fort Pierce;27.4255 Tallbīsah;34.8333 Zhongzhai;25.7783 Jangamguda;17.5092 Wake Forest;35.9632 Chichigalpa;12.5722 Camp Perrin;18.3167 Kilmarnock;55.6111 Gotha;50.9489 Anshan;39.7144 Capannori;43.8500 Jammalamadugu;14.8500 Cavaillon;18.3000 Lodi;45.3167 Fuxing;24.0341 Banī ‘Ubayd;31.0234 Pagalungan;7.0592 San Lorenzo;-32.7500 Henrietta;43.0555 Joal-Fadiout;14.1667 Beavercreek;39.7310 Takashima;35.3500 Labutta;16.1467 Santiago Atitlán;14.6386 Marino;41.7667 São Gabriel da Cachoeira;-0.1300 Lupao;15.8794 Bünde;52.2000 Acaxochitlán;20.1667 Laguna;-28.4828 Batroûn;34.2500 Kaufbeuren;47.8800 Puertollano;38.6833 Mairinque;-23.5464 Maragogipe;-12.7778 Tingo María;-9.2953 Megion;61.0500 Kafr al Baţţīkh;31.3234 Strongsville;41.3128 Xingji;38.4682 Amboasary;-25.0500 Wittenberg;51.8671 Campechuela;20.2333 Koupéla;12.1794 Bājil;15.0583 Saint-Brieuc;48.5136 Manono;-7.2947 Rockwall;32.9169 Attleboro;41.9311 Kikuchi;32.9833 Bruchsal;49.1333 Hokota;36.1587 Tayug;16.0267 El Valle del Espíritu Santo;10.9833 Farrukhnagar;17.0778 Krosno;49.6833 Shima;34.3333 Châlons-en-Champagne;48.9575 Albstadt;48.2119 Chepén;-7.2271 Bridgewater;40.5934 Nawai;26.3824 Nova Venécia;-18.7108 Lagoa da Prata;-20.0228 Livramento de Nossa Senhora;-13.6428 Weinheim;49.5500 Piro;25.3300 Salon-de-Provence;43.6406 Ourém;39.6500 Freeport City;26.5286 Salto de Pirapora;-23.6489 Mindat;21.3667 Phan Rí Cửa;11.1739 Pagsanjan;14.2667 Erkrath;51.2239 Maluso;6.5500 Karuvambram;11.0271 Paraguaçu Paulista;-22.4197 Caiguantun;26.3363 Lumba-a-Bayabao;7.8833 Winter Garden;28.5421 Mladá Boleslav;50.4125 Pearl City;21.4031 Haymana;39.4311 Acatlán de Pérez Figueroa;18.5333 Middletown;40.1790 Changchong;26.3404 Mata de São João;-12.5300 Aribinda;14.2292 Chajarí;-30.7667 Tantangan;6.6167 Xisa;23.4372 Nocera Inferiore;40.7500 Kumta;14.4264 Alcira;39.1500 Salvador;20.2094 Voi;-3.3906 Stouffville;43.9667 Kariya;34.7517 Monte Carmelo;-18.7250 Warud;21.4167 Cascina;43.6833 Haltom City;32.8176 Hokuto;41.8241 Rājgarh;24.0300 Santa María Huatulco;15.8500 Olhão;37.0260 Altamonte Springs;28.6615 Beaufort West;-32.3500 Remedios;22.4922 Jones;16.5583 Westfield;40.0341 Kitakata;37.6511 Rāmeswaram;9.2880 Sète;43.4053 Erith;51.4800 Drachten;53.1000 Elmhurst;41.8973 Aliwal North;-30.7000 Talagutong;6.2667 Cuautepec de Hinojosa;20.1500 Baiji;26.0231 Alcamo;37.9778 Brühl;50.8333 Radā‘;14.4151 Gaya;11.8878 Majurwa;26.1036 Seohāra;29.2200 Arantāngi;10.1686 São Sebastião do Passé;-12.5128 San Isidro;9.3403 Bom Conselho;-9.1700 Heusden;51.6998 Guacharachi;27.1500 Ortega;3.9167 Busaar;2.6722 Itabirito;-20.2528 Manacor;39.5700 Port Macquarie;-31.4333 Chimteppa;38.4667 Xiluo;23.7827 Bountiful;40.8721 San Pelayo;8.9667 Nāyudupet;13.9000 Pinamar;-37.1000 Littleton;39.5915 Otwock;52.1167 Nongzhangjie;24.6160 Shazhou;40.1376 Bāfq;31.6033 Carnot;4.9400 Ciudad de Huajuapam de León;17.8097 Huntsville;30.7009 Dehgolān;35.2781 Birāk;27.5333 Georgina;44.3000 Dębica;50.0500 Sainthia;23.9451 Dunakeszi;47.6303 Siyāna;28.6269 West Seneca;42.8374 Kamen’-na-Obi;53.7833 Garango;11.8000 Memmingen;47.9878 San Jacinto;16.0725 Victoria;14.2250 Medemblik;52.6833 Næstved;55.2250 Aguilar;15.8899 Charkhi Dādri;28.5900 Svitlovodsk;49.0503 Bingmei;25.7408 Victoriaville;46.0500 Basud;14.0667 Parigi;-7.6914 Vallenar;-28.5708 Ruzayevka;54.0667 Falkensee;52.5583 Gaoua;10.3167 Gračanica;44.6892 Biella;45.5667 Mamanguape;-6.8389 Dowlaiswaram;16.9506 Morgan Hill;37.1325 João Pinheiro;-17.7442 Brejo Santo;-7.4928 Tank;32.1300 Lørenskog;59.8989 Jedeïda;36.8333 Barnagar;23.0489 Brejo da Madre de Deus;-8.1458 Numata;36.6460 Angul;20.8381 Sundargarh;22.1200 San Pedro Perulapán;13.7667 Seabra;-12.4189 Qarabulaq;42.5167 Sugito;36.0258 Nimule;3.5961 Nanjō;26.1631 Campo Maior;-4.8278 Cheramkod;11.6000 Angadanan;16.7571 Āstāneh-ye Ashrafīyeh;37.2597 Wiener Neustadt;47.8167 Beiwanglizhen;37.7604 Keller;32.9335 Shangshan;23.4792 Tangjiacun;20.8425 Aksay;47.2500 Guledagudda;16.0502 Radomsko;51.0667 Concepcion;11.2000 Liuquan;39.3658 Melūr;10.0313 Hassi Messaoud;31.7000 Kyle;29.9932 Tremembé;-22.9578 Yirga ‘Alem;6.7500 Montemorelos;25.1872 Sakura;36.6853 Webster;43.2294 Buguias;16.8033 Châteauroux;46.8103 Little Elm;33.1858 Yaopu;26.1708 Nova Kakhovka;46.7550 Tateyama;34.9966 Tres Valles;18.2367 Rabinal;15.0833 Iwanuma;38.1043 Maria Aurora;15.7967 Prescott;34.5849 Sayreville;40.4656 Anekal;12.7105 Sidcup;51.4263 Urbandale;41.6390 Shaḩḩāt;32.8250 Colotenango;15.4054 Kimry;56.8667 Shchūchīnsk;52.9333 Darcheh;32.6153 Kutum;14.2056 Parma;65.9230 Senigallia;43.7131 San Jose;13.6981 Borlänge;60.4856 Yatağan;37.3425 ‘Ayn al ‘Arab;36.8910 Pederneiras;-22.3517 Rasskazovo;52.6667 Manjuyod;9.6833 Baião;-2.7908 Bacnotan;16.7197 Lapa;-25.7700 Ashburn;39.0277 Al ‘Azīzīyah;32.9083 Sierra Vista;31.5630 Nadym;65.5333 Āz̄arshahr;37.7722 Marau;-28.4489 Bolívar;1.9708 Turnhout;51.3167 Santa Bárbara;15.3167 Neustadt am Rübenberge;52.5000 Nandigāma;16.7833 Dráma;41.1500 Pemangkat;1.1768 Nasipit;8.9884 Houbu;36.4370 Volkhov;59.9167 Barras;-4.2469 Cutler Bay;25.5765 Zhovti Vody;48.3500 Zevenaar;51.9167 Nsawam;5.8000 Katipunan;8.5134 Mount Pleasant;41.1119 Jisr ash Shughūr;35.8126 Nagaizumi;35.1377 Kaarst;51.2167 Noordwijk;52.2333 Jacaltenango;15.6667 Balcarce;-37.8456 Palm Springs;33.8017 Uruará;-3.7178 Presidente Dutra;-5.2900 Manchester;39.9652 Ellenabad;29.4500 Nidadavole;16.9200 Riverton;40.5176 Dolores;12.0378 Piendamó;2.6408 Pyu;18.4819 Yaoquan;34.5851 Tori-Bossito;6.5031 Abū al Maţāmīr;30.9084 Lancaster;42.9099 Taketoyo;34.8511 Lehrte;52.3667 Lake Ridge;38.6847 Culasi;11.4272 Mawlaik;23.2833 Deinze;50.9833 Świdnik;51.2167 Etten-Leur;51.5667 Seferhisar;38.2000 Alaplı;41.1833 San Pascual;13.1286 Arboletes;8.8517 Loon;9.8000 Garça;-22.2153 Rājaldesar;28.0300 Medak;18.0460 Kikuyō;32.8625 Vernon;50.2670 Tiberias;32.7944 Ribeirão;-8.5139 Dalli Rājhara;20.5800 Yuanli;24.4168 Jora;26.3421 Denia;38.8444 Banaybanay;6.9664 Fairfield;39.3301 Vlissingen;51.4500 Labangan;7.8667 Bietigheim-Bissingen;48.9667 Masuda;34.6667 Qingfengdian;38.6084 Changtoushang;19.3441 General Pacheco;-34.4500 Barwāh;22.2539 West Lafayette;40.4432 Bulalacao;12.3333 Goose Creek;32.9927 Tsiroanomandidy;-18.7698 Petatlán;17.5383 Prostějov;49.4722 Albufeira;37.0897 Mount Laurel;39.9483 Porsa;26.6700 San Juan y Martínez;22.2667 Paraíso do Tocantins;-10.1758 Tongoma;8.6500 Ananipalle;13.6500 Wallingford;41.4589 Sihora;23.0000 Los Banos;37.0630 Svishtov;43.6167 Fond du Lac;43.7718 Bassano del Grappa;45.7667 General Mamerto Natividad;15.6030 Çayeli;41.0880 Tuodian;24.6907 Brentwood;35.9918 Eqlīd;30.8989 Maitum;6.0333 Raisen;23.3300 Volksrust;-27.3667 Rheden;52.0167 Manakara;-22.1500 Lombard;41.8741 São Benedito;-4.0489 Plainfield;41.6207 Sonqor;34.7836 Cateel;7.7900 Mauriti;-7.3889 Mandaon;12.2259 Korogwe;-5.1558 Char Fasson;22.1861 Dounan;23.6833 Tinajdad;31.5000 Dikili;39.0667 Guinayangan;13.9000 Krasnodon;48.3000 Xanxerê;-26.8769 Ogimachi;33.2500 Addanki;15.8110 Nilothi;28.8167 Las Matas de Farfán;18.8700 Lompoc;34.6618 Harsīn;34.2722 Panambi;-28.2928 Nijkerk;52.2333 Jānakammapeta;14.8840 Labason;8.0647 Burke;38.7773 Hagi;34.4000 Kuybyshev;55.4500 Jinku;23.0398 Alghero;40.5600 Roghun;38.6978 Saint-Eustache;45.5700 Yutiancun;25.8747 Otofuke;42.9942 Badian;9.8694 San Cristóbal Totonicapán;14.9197 Yartsevo;55.0667 Moorhead;46.8673 Wānkāner;22.6161 Talatamaty;-18.8333 Goshikichō-aihara-minamidani;34.3000 Barpeta;26.3200 Kaštel Stari;43.5500 Cimerak;-7.7422 San Vicente de Tagua Tagua;-34.4394 Al Midhnab;25.8601 Rudauli;26.7500 Pantanal;18.5333 Maştağa;40.5328 Maracaju;-21.6139 Bakamune;7.7831 Xinjun;28.7232 Boyabat;41.4653 De Bilt;52.1167 Menghan;21.8526 Limache;-33.0167 Venray;51.5333 Abqaiq;25.9350 Kamen;51.5917 Istres;43.5151 Pinhal;-22.1908 Comendador;18.8833 Zongolica;18.6642 Jiaohe;38.0228 Mucaba;-7.1667 Hinatuan;8.3661 Casale;18.8014 Surčin;44.8000 Jaro;11.1894 Pardés H̱anna Karkur;32.4711 Khutubi;44.1874 San Germán;20.6011 São Gonçalo do Amarante;-3.6058 Mankato;44.1715 Himi;36.8567 Gravina in Puglia;40.8167 Qiryat Moẕqin;32.8333 Yatomi;35.1167 Nandazhang;38.1031 Loreto;8.1856 Bāniyās;35.1822 Jaito;30.4514 Pittsfield;42.4517 Kalilangan;7.7464 Villeta;5.0083 ’Aïn Arnat;36.1833 Marki;52.3333 Tavas;37.5728 Āzādshahr;37.0869 Ciudad Constitución;25.0322 Erkelenz;51.0833 Chikodi;16.4300 Tanghin-Dassouri;12.2667 Şān al Ḩajar al Qiblīyah;30.9769 El Asintal;14.6000 Kāyalpattanam;8.5638 Chefchaouene;35.1714 Laurel;14.0500 Toboso;10.7167 Wismar;53.9000 Al Kiswah;33.3500 Sevilla;4.2689 Mahbūbābād;17.5973 Santa Elena;14.1797 Madridejos;11.2667 Tamu;24.2167 Kattivākkam;13.2167 Moises Padilla;10.2667 Barauli;26.3815 Chusovoy;58.3167 Zhangliangcun;37.1256 Konārak;25.3603 Kharik;25.3679 Macenta;8.5504 Barhiya;25.2833 Eṭ Ṭaiyiba;32.2667 Takāb;36.4008 Taniyama-chūō;31.5211 Roseller Lim;7.6583 Shinshiro;34.9159 Waslala;13.3367 Xihuachi;35.8164 Danville;37.8121 Of;40.9450 San Sebastián Huehuetenango;15.3833 Guambog;7.3092 Paoskoto;13.7833 North Brunswick;40.4505 Alès;44.1281 Çeşme;38.3236 Cové;7.2333 Ponte de Lima;41.7667 Angoulême;45.6500 Zhongbu;23.4082 Birsk;55.4167 Ambāsamudram;8.7037 Dildārnagar;25.4309 Raduzhnyy;62.1333 Quinte West;44.1833 Presidente Epitácio;-21.7633 El Cuá;13.3678 Lota;-37.0833 Pontes e Lacerda;-15.2258 Yeola;20.0420 Bahía Honda;22.9064 Maïssade;19.1667 Santa Cruz;32.6833 Zhangcun;38.1262 Hokuto;35.7765 Abucay;14.7222 Anse Rouge;19.6333 The Colony;33.0925 Odenton;39.0661 Wangyuanqiao;38.3849 Southington;41.6050 Dilbeek;50.8333 Berubāri;26.3603 Pedro Celestino Negrete;24.7260 Muttayyāpuram;8.7498 Makinohara;34.7333 Heist-op-den-Berg;51.0833 Zharkent;44.1667 Siegburg;50.8014 South Upi;6.8548 Bolvadin;38.7167 Gurrampod;16.8667 Berkeley;39.9156 Sawākin;19.1025 Alenquer;39.0500 Gifhorn;52.4886 West Babylon;40.7129 Shakopee;44.7745 Linden;40.6251 Estância Velha;-29.6478 Douar Bni Malek;34.7800 Altadena;34.1927 Shimabara;32.7878 Pirthīpur;25.1853 Shināş;24.7500 Hueyapan de Ocampo;18.1500 Heinsberg;51.0631 Tešanj;44.6142 Saoner;21.3858 Lokeren;51.1000 Shōranūr;10.7700 Esperanza;-31.4167 King’s Lynn;52.7540 Sūrampatti;11.3411 Gallatin;36.3782 Vahdat;38.5531 Akbarpur;26.5200 Rantepao;-2.9690 San Lorenzo;13.4217 San Clemente;-35.5500 Maḩallāt;33.9111 Kemer;36.6000 Civitanova Marche;43.3000 Kōta;34.8645 Andes;5.5833 Anakaputtur;12.9823 Paso de los Libres;-29.7167 Germering;48.1333 Shuilou;22.3360 Borken;51.8333 Hố Nai;10.9725 Nova Viçosa;-17.8919 Aguazul;5.1731 Quirinópolis;-18.4478 Sevanagala;6.3867 Chicamán;15.3483 Kalaiyā;27.0333 Wentzville;38.8153 Véroia;40.5203 Ciechanów;52.8667 Vergína;40.5167 Yepocapa;14.5000 Laatzen;52.3167 Kobayashi;31.9944 Nansan;23.7784 Hoddesdon;51.7590 Přerov;49.4556 Tiel;51.8833 Villaba;11.2167 Ban Pet;16.4379 Cumaribo;4.4333 Singhanakhon;7.2389 São Lourenço do Sul;-31.3650 Laungowāl;30.2167 Santo Tomas;15.0000 Miura;35.1500 Lubao;-5.3850 Jesús María;21.9667 Nueva Valencia;10.5259 Musina;-22.3381 Buffalo Grove;42.1675 Kārmegh;26.5307 Naranjo;10.1053 Alhaurín de la Torre;36.6667 Entre Rios;-11.9419 Panitan;11.4639 Amadeo;14.1728 Periyakulam;10.1200 La Mata;19.1000 Gualeguay;-33.1500 Nettetal;51.3167 Jatibonico;21.9464 Albano Laziale;41.7333 Oakley;37.9929 Jājpur;21.2140 Miranda;3.2500 Osinniki;53.6167 Cuchi;-14.6500 Bourg-en-Bresse;46.2056 Itapa-Ekiti;7.8131 Akdağmadeni;39.6658 Sidi Mohamed Lahmar;34.7167 Limbdi;22.5685 Loufan;38.0694 Atenco;19.5167 Palmas;-26.4839 Samborondón;-1.9611 Zvolen;48.5706 Eagle Mountain;40.3137 Sieradz;51.6000 Shenjiatun;40.7695 Dreieich;50.0000 Huizen;52.3000 Lucena;37.4000 Thomassique;19.0833 Nurmijärvi;60.4667 Awaji;34.4333 Orlândia;-20.7203 Majhaul;25.5570 Kārsiyāng;26.8800 Vawkavysk;53.1667 Bukama;-9.2000 San Francisco Menéndez;13.8425 Proper Bansud;12.8594 Vera Cruz;-12.9600 Catonsville;39.2646 Obita;32.8252 Amberg;49.4444 Hillsborough;40.4985 Santa Quitéria;-4.3319 Eisenach;50.9761 Shetou;23.9076 Santo Antônio da Platina;-23.2950 Aurich;53.4714 Cabo Bojador;26.1333 Woodbridge;38.6409 Tucuran;7.8547 Leigh;53.4975 Fianga;9.9153 Mombaça;-5.7428 Sarıkamış;40.3381 Ormond Beach;29.2985 Currais Novos;-6.2608 Sansanné-Mango;10.3556 Hwange;-18.3647 União;-4.5858 Cacocum;20.7439 Avezzano;42.0411 Yaguate;18.3300 Moline;41.4821 Mariel;22.9936 Llanera;15.6625 Huber Heights;39.8595 Kotabumi;-4.8333 La Orotava;28.3667 Edmonds;47.8115 Dusheng;38.3804 Nuevo San Carlos;14.6000 Nyunzu;-5.9500 La Tebaida;4.4500 Imperia;43.8833 Charlottetown;46.2403 Köniz;46.9333 Balabac;7.9833 Manassas;38.7479 Bhatpalli;18.4314 Maroantsetra;-15.4333 Maulavi Bāzār;24.4808 Sanarate;14.7950 Thān;22.5667 Machalí;-34.1825 Kasai;34.9333 Nāndūra Buzurg;20.8333 Danville;36.5831 Villa Bisonó;19.5600 Akaiwa;34.7553 El Charco;2.4775 Liloy;8.1228 Tineghir;31.5147 San Rafael del Sur;11.8458 Araioses;-2.8900 Shebekino;50.4167 Castres;43.6000 Cortlandt;41.2552 Mascouche;45.7500 Cuihua;27.7527 Santa Rita;11.4522 Futtsu;35.3041 Carranglan;15.9608 Enfield;41.9839 Santa Cruz;13.0831 Alerce;-41.4000 West Vancouver;49.3667 Capão da Canoa;-29.7608 Pahrump;36.2235 Polanco;8.5319 Belleville;38.5164 Quezon;15.5542 Shepetivka;50.1833 Beverly;42.5681 Shū;43.5983 President Quirino;6.7000 Tomé;-36.6167 Midland;43.6241 Tactic;15.3167 Yangcunzai;23.4338 Coppell;32.9639 Salaberry-de-Valleyfield;45.2500 Balykchy;42.4611 Uden;51.6500 Channelview;29.7914 Myanaung;18.2833 São Lourenço;-22.1158 Liantang;22.9352 Itápolis;-21.5958 Wancheng;37.6273 Claveria;12.9035 Machang;26.5634 Puyallup;47.1793 Niquelândia;-14.4739 Porangatu;-13.4408 Nāthdwāra;24.9300 Nalhāti;24.3000 Aritao;16.2973 Quesada;10.3441 Ban Wat Lak Hok;13.5633 Nakano;36.7420 Qəzyan;39.2914 Rouyn-Noranda;48.2333 Qā’en;33.7256 Seevetal;53.3833 San Donà di Piave;45.6298 Rancho Palos Verdes;33.7554 Homburg;49.3167 Maniwa;35.0758 Bilāsipāra;26.2328 Beitbridge;-22.2167 Remanso;-9.6219 Coronel Suárez;-37.4667 Pratāpgarh;24.0300 Morarano Chrome;-17.7500 Ansbach;49.3000 Matnog;12.5856 Nārāyanpet;16.7445 Bacong;9.2464 Dacun;34.7201 Kollo;13.0167 Macerata;43.3003 Gay;51.4167 Mozdok;43.7333 Mampong;7.0667 Sarakhs;36.5450 Alcalde Díaz;9.1200 Sbiba;35.5467 Puerto Real;36.5292 Nordhausen;51.5050 Safonovo;55.1167 Diyadin;39.5400 Puerto del Rosario;28.5000 Saidpur Dabra;25.3207 Golāghāt;26.5200 Schwabach;49.3292 Minamishimabara;32.6667 Monterotondo;42.0500 Greenock;55.9500 Sidrolândia;-20.9319 Montenegro;4.5671 Memāri;23.2000 Amontada;-3.3608 Cáceres;7.6667 Puerto Galera;13.5000 Lingquan;36.9985 Coachella;33.6905 Monchegorsk;67.9333 Dronten;52.5167 Soran;49.7833 Anda;16.2896 Litian Gezhuang;39.8151 Coburg;50.2667 Poti;42.1500 Sarāb;37.9414 Su-ngai Kolok;6.0333 Yagoua;10.3428 Linton Hall;38.7551 Barcellona-Pozzo di Gotto;38.1500 Coyuca de Catalán;18.3256 Korenovsk;45.4686 San José de Bocay;13.5428 Myedu;23.1833 Peachtree Corners;33.9670 Woodlawn;39.3054 Xinleitou;37.9752 Soliman;36.7000 Kibawe;7.5678 Tequila;20.8794 Moatize;-16.1167 Merano;46.6696 Santa Rosa del Sur;7.9667 Vasto;42.1116 Shimotsuma;36.1844 Yinggen;19.0372 Fenoarivo Atsinanana;-17.3814 Kolchugino;56.3167 Billerica;42.5587 Beigang;23.5667 Rajaori;33.3800 Tādif;36.3481 Spassk-Dal’niy;44.6000 Kirchheim unter Teck;48.6483 Nürtingen;48.6333 Pine Bluff;34.2116 Vargem Grande do Sul;-21.8319 Pāmban;9.2790 Spanish Fork;40.1101 Hongfengcun;21.9231 San;13.3004 Medianeira;-25.2950 Ishaka;-0.5450 Tulun;54.5667 Wunstorf;52.4275 Pedra Branca;-5.4539 Bragadiru;44.3708 Naga;7.7978 San Pedro Necta;15.5000 Kasibu;16.3181 Daram;11.6341 Bayt al Faqīh;14.5161 Jaraguá;-15.7569 Nanbaishezhen;37.7526 Colomiers;43.6139 Sassuolo;44.5517 San Miguel;8.9322 Jardinópolis;-21.0178 Tramandaí;-29.9850 Socorro;13.0583 Wijchen;51.8000 Ihnāsyā al Madīnah;29.0833 Tabogon;10.9333 Dhamdāha;25.7435 Sānand;22.9800 Oqtosh Shahri;39.9269 Satka;55.0500 Santa Cruz;17.0853 Santa Maria da Vitória;-13.3978 Ciudad Vieja;14.5233 Tagudin;16.9361 Eberswalde;52.8333 Chojnice;53.7000 Amuntai;-2.4177 Wujie;24.7000 Fitchburg;42.5912 Hempfield;40.2847 Nanbei;35.3167 Kearny;40.7528 Timmins;48.4667 Mikołów;50.1667 Malilipot;13.3167 Königswinter;50.6736 Indi;17.1773 Patzicía;14.6336 Pīleru;13.6549 Buddh Gaya;24.6981 Dacun;23.9911 Myski;53.7000 Santa Cruz de Los Taques;11.8231 Rājgīr;25.0300 Gonzaga;18.2614 Borba;-4.3878 Strezhevoy;60.7333 Pueblo Nuevo;8.5008 Kwidzyn;53.7358 Podilsk;47.7419 Varash;51.3444 Panchimalco;13.6167 Yakacık;36.7500 Sarandë;39.8750 Rāmganj Mandi;24.6472 Ust’-Kut;56.7833 Santo Domingo;9.9880 Dohazāri;22.1628 Gatunda;-1.4350 Świnoujście;53.9167 Sipe Sipe;-17.4500 Jayamkondacholapuram;11.2127 Nanmeng;39.1773 Maplewood;44.9842 Yangtangxu;21.6172 Patulul;14.4167 Arraiján;8.9500 Tuchín;9.1858 Herstal;50.6667 Yuzawa;39.1641 Lidköping;58.5000 Saint-Germain-en-Laye;48.8989 Cantù;45.7333 Copán;14.8375 Fredericia;55.5667 Peddapalli;18.6162 Paranaíba;-19.6769 Mableton;33.8133 Sorel-Tracy;46.0333 Naini Tal;29.3919 Masaki;33.7833 Pilate;19.6667 Karabulak;43.3167 Schertz;29.5650 Ambohitrimanjaka;-18.8667 Ladispoli;41.9544 Yarumal;7.0000 Newnan;33.3767 Thionville;49.3589 Ayagöz;47.9667 Kitaibaraki;36.8019 Amancio;20.8197 Almenara;-16.1005 Hellevoetsluis;51.8167 Gaolincun;39.0887 Clermont;28.5325 Mondragon;12.5167 North Fort Myers;26.7244 Schwäbisch Hall;49.1122 Baichigan;39.5275 Tālcher;20.9500 Geel;51.1667 Sinanpaşa;38.7500 Maayon;11.3903 Alcala;17.9031 Paliā Kalān;28.4500 Verkhnyaya Salda;58.0456 Marlborough;42.3494 Vineyard;38.4740 Erzin;36.9533 Agar;23.7118 Santo Tomas;16.2833 Komoro;36.3275 Morrinhos;-17.7319 Bury Saint Edmunds;52.2474 Zduńska Wola;51.6000 Novo Horizonte;-21.4678 Leinfelden-Echterdingen;48.6928 New Tecumseth;44.0833 Mersa;11.6667 Tabarka;36.9544 Coram;40.8812 Nowa Sól;51.8000 Villa Ángela;-27.5833 Lasam;18.0667 Szigetszentmiklós;47.3456 Antequera;37.0183 Sovetsk;55.0808 Patti;31.2800 Sherghāti;24.5700 La Libertad;10.0333 Compiègne;49.4149 Hückelhoven;51.0608 Tanxia;23.9475 Tiegan;38.1536 Marlboro;40.3427 El Tocuyo;9.7822 Qiryat Yam;32.8333 Vavveru;14.5594 Guácimo;10.2025 Bensheim;49.6667 Niquero;20.0472 Arauquita;7.0303 Girau do Ponciano;-9.8839 Chaves;41.7406 Hatfield;51.7636 Ganta;7.2375 Freiberg;50.9119 São Mateus do Sul;-25.8739 Kovvūr;17.0170 Sumoto;34.3500 Järvenpää;60.4750 Anyuan;34.8839 San Jose de Urquico;15.4781 Villanueva;8.5833 Yingzhou Linchang;18.4199 San Juan;16.6667 Unzen;32.8353 Constitución;-35.3333 Temryuk;45.2667 Halfeti;37.2489 Anglet;43.4850 São Manuel;-22.7308 Amami;28.3783 Vallehermoso;10.3333 Tagbina;8.4578 Guaduas;5.0694 Paraíba do Sul;-22.1619 Matiguás;12.8372 Shanhe;35.4844 Ardeşen;41.1900 Penn Hills;40.4762 Ambositra;-20.5167 Bartlett;41.9804 Tampakan;6.4500 Pilāni;28.3700 Kabuga;-1.9667 Marcianise;41.0333 Jeremoabo;-10.0669 Mobo;12.3372 Ostfildern;48.7333 Lemgo;52.0272 Jiquipilas;16.5333 Mucuri;-18.0858 Bazar-Korgon;41.0417 Hollister;36.8555 Upper Hutt;-41.1333 Ciudad Darío;12.7322 Bullhead City;35.1205 Dois Vizinhos;-25.7500 Bolesławiec;51.2667 Lancaster;32.5922 Pirmasens;49.2000 Chambas;22.1967 Sint-Truiden;50.8167 Nabas;11.8278 Maarssen;52.1351 Segovia;7.0781 Uozu;36.8273 Grove City;39.8654 Marion;42.0451 Brookfield;43.0640 Bagua Grande;-5.7572 Puerto Gaitán;4.3142 Pyt’-Yakh;60.7500 Dowlatābād;32.7997 Delaware;40.2851 Gap;44.5594 Maddela;16.3411 Mambusao;11.4300 Katō;34.9167 Novozybkov;52.5333 Jogbani;26.4024 Larantuka;-8.3133 Taicheng;38.7206 Barr Eliâs;33.7750 Lobo;13.6500 Osório;-29.8869 Woburn;42.4869 Columbia;35.6238 Germantown;35.0829 Partāpnagar;25.4145 Krishnarājāsāgara;12.4398 Inhambupe;-11.7839 Chebarkul;54.9833 Shelton;41.3060 Romny;50.7428 Maragondon;14.2667 Covington;39.0334 Delījān;33.9906 Balaoan;16.8167 Westfield;42.1382 Ciudad Piar;7.4522 Yomitan;26.3961 Tirukkoyilūr;11.9500 Kitob;39.1336 Pugachev;52.0167 Barreiros;-8.8167 Apóstoles;-27.9167 Woodstock;43.1306 Mangaratiba;-22.9600 Nowgong;25.0574 Promissão;-21.5369 Kadūr;13.5529 Rio Real;-11.4833 Ciudad Barrios;13.7667 Wageningen;51.9667 Chiredzi;-21.0389 Ahlat;38.7528 Tianzhong;23.8595 Cuito Cuanavale;-15.1667 Valladolid;10.4667 Abadan;38.0517 Caeté;-19.8800 São Bento;-2.6958 Zoumi;34.8000 Santa Eulalia del Río;38.9847 Samālkha;29.2300 Zhujiezhen;23.7460 Bañga;11.6389 Tomar;39.6000 Pereslavl’-Zalesskiy;56.7381 Neumarkt;49.2833 Sabalgarh;26.2500 Gonābād;34.3528 Friendswood;29.5110 Pālampur;32.1097 The Acreage;26.7740 Jhābua;22.7677 Lugoj;45.7056 Qeshm;26.9581 Pie de Pató;5.5333 Weißenfels;51.2000 Kīlakkarai;9.2314 Boucherville;45.6000 Khowrmūj;28.6489 Yalutorovsk;56.6500 Pachrūkha;26.7373 Sivaganga;9.8433 Arys;42.4333 Siay;7.7056 Essex;39.3021 Komono;35.0200 Buchholz in der Nordheide;53.3167 Sciacca;37.5092 Barra de São Francisco;-18.7550 Nanuque;-17.8389 Nabarūh;31.0942 Channarāyapatna;12.9000 Vellakkovil;10.9300 Buxtehude;53.4769 Montélimar;44.5581 Khawr Fakkān;25.3333 La Unión;4.5331 Bayındır;38.2192 Soe;-9.8667 Santo Antônio de Pádua;-21.5389 Pechora;65.1667 Löhne;52.2000 Douar Laouamra;31.8000 Manalapan;40.2800 Leyte;11.3667 Panglao;9.5833 Rovereto;45.8833 Braine-l’Alleud;50.6806 Lordegān;31.5081 Rizal;12.4667 Agano;37.8344 Huaura;-11.1000 Islāmpur;25.1407 Xinguara;-7.0950 Iesi;43.5236 Ban Bang Khu Lat;13.9134 Mailapur;13.0210 Maihar;24.2620 Duncanville;32.6460 Taysan;13.7833 Murtajāpur;20.7300 Uray;60.1333 Freital;51.0167 Cabatuan;16.9589 Dingras;18.1036 Cumbal;0.9078 Payabon;9.7667 Augusto Correa;-1.0219 Halberstadt;51.8958 Yian;47.8804 Al Qā‘idah;13.7569 Maintal;50.1500 Lilio;14.1300 Cedar Falls;42.5195 Andradas;-22.0678 Magburaka;8.7169 Milton;34.1353 Corigliano Calabro;39.6000 Yajalón;17.1833 Olintepeque;14.8833 Cauquenes;-35.9667 Lishaocun;22.7236 Hofheim;50.0833 Camaná;-16.6167 Aracataca;10.5937 Heemskerk;52.5167 Aizawa;35.5288 Sirinhaém;-8.5908 Zainsk;55.3000 Weslaco;26.1599 Begamganj;23.5992 Lake Oswego;45.4129 Junnar;19.2000 Imbituba;-28.2400 Ōzu;33.5000 Uchqŭrghon Shahri;41.1214 Jipijapa;-1.3486 Ilıca;39.9458 Malalag;6.6000 Túquerres;1.0833 Glenrothes;56.1980 Fonds Verrettes;18.3833 Lancaster;39.7248 Eboli;40.6169 Beyneū;45.3247 Ampana;-0.8667 Hailākāndi;24.6839 Schorndorf;48.8000 Plaridel;8.6214 Jāle;26.3800 Molina;-35.1144 Balud;12.0369 Majibacoa;20.9172 Findlay;41.0469 Leramatang;-8.4000 Belaya Kalitva;48.1833 San Fernando de Henares;40.4256 Tietê;-23.1019 Gourcy;13.2167 Arawa;-6.2250 New Berlin;42.9726 Kunigal;13.0232 Tosya;41.0172 Águas Belas;-9.1167 Oświęcim;50.0381 Považská Bystrica;49.1139 L’Asile;18.3833 Santo Niño;6.4333 Itoigawa;37.0390 Sarāqib;35.8636 Kasumigaura;36.1517 Havza;40.9667 Uttarkāshi;30.7300 Secunda;-26.5161 Nawābganj;28.5400 Sejenane;37.0564 Sindgi;16.9200 Bāsudebpur;21.1197 Camargo;27.6670 Tipton;52.5259 Santiago Nonualco;13.5167 Hutchinson;38.0671 Holly Springs;35.6526 Mtsensk;53.2833 Tutayev;57.8833 French Valley;33.5999 Vyshneve;50.3869 Hangu;33.5333 Longtang;26.1984 Kālimpong;27.0600 Raxruhá;15.8666 Ninove;50.8333 Draguignan;43.5403 Medgidia;44.2503 Security-Widefield;38.7489 Kambia;9.1167 Soavinandriana;-19.1667 Oroqen Zizhiqi;50.5667 Sagae;38.3809 Vendrell;41.2201 Raub;3.7931 Zacualpa;15.0272 Neu Isenburg;50.0500 Skövde;58.3833 Hurst;32.8353 Amursk;50.2167 Baksan;43.6825 Otavalo;0.2303 Romeoville;41.6318 Maddaloni;41.0333 Crystal Lake;42.2333 Altagracia de Orituco;9.8504 Siniloan;14.4167 Unión de Reyes;22.8003 Fondi;41.3500 Janīn;32.4611 Jayrūd;33.8067 Jalalabad;35.8800 Zhongbai;26.7723 Alashankou;45.1733 Ettlingen;48.9333 Vavuniya;8.7500 Tarauacá;-8.1608 Matsoandakana;-14.9667 Revelganj;25.7800 Jiaozishan;26.3342 Bangkinang;0.3500 Völklingen;49.2500 Pamplona;9.4667 Baco;13.3584 Alimodian;10.8196 Kyaukme;22.5392 Tago;9.0192 Pojuca;-12.3659 Fasano;40.8333 Idangansālai;11.6272 Langen;49.9833 Huamachuco;-7.8120 Halle;50.7361 Voghera;44.9925 Zimapan;20.7333 Ossining;41.1559 Rauma;61.1333 San Nicolas;16.0700 Acevedo;1.8192 Villa de San Diego de Ubaté;5.3072 Xinlizhuang;39.2832 Yecapixtla;18.8833 Tivaouane;14.9500 Atmakūr;15.8779 Clinton;38.7499 Poblacion;7.5086 Ahaus;52.0667 Weibo;38.0290 São José de Mipibu;-6.0750 Palauig;15.4336 Ayirāpuram;9.8781 Meadow Woods;28.3698 Magsaysay;12.3117 Carol Stream;41.9181 Brighton;39.9716 Rio Negrinho;-26.2539 Agoncillo;13.9334 Écija;37.5333 Kīsh;26.5578 Nueva Santa Rosa;14.3811 San Nicolas;18.1750 Kırkağaç;39.1039 Northampton;40.2104 Matías Romero;16.8798 Betun;-9.4953 Michalovce;48.7553 Guaíra;-20.3178 Soria;41.7667 Wālājāpet;12.9255 Nūrpur;29.1500 Srebrenik;44.7000 Winslow;39.7027 Würselen;50.8167 Clarin;8.2000 Pamplona;13.5925 Bādepalli;16.7549 Waxahachie;32.4035 Kurobeshin;36.8667 Veruela;8.0698 Villamontes;-21.2608 Toukountouna;10.4986 Apsheronsk;44.4667 Reynoldsburg;39.9588 Líbano;4.9167 Bosconia;9.9761 Dolores;16.5142 Mibu;36.4422 La Rinconada;37.4833 Kalingalan Caluang;5.8833 Todos Santos Cuchumatán;15.5116 Buldon;7.5167 Sogod;10.7500 Talacogon;8.4488 Niederkassel;50.8167 Steyr;48.0333 Benenitra;-23.4522 Caraga;7.3283 Salcedo;19.3780 Pirna;50.9622 Isfisor;40.2433 Solan;30.9050 Shengang;24.1648 Shankarpur Khawās;25.2016 Mettmann;51.2500 Lloret de Mar;41.7000 Plasencia;40.0275 Asamankese;5.8667 Streamwood;42.0209 Città di Castello;43.4608 Tafí Viejo;-26.7333 Santo Antônio;-29.8178 Yomra;40.9667 Arucas;28.1184 Quincy;39.9336 Ţabas;33.5958 Khāchrod;23.4232 Schio;45.7111 Alicia;7.5060 Xonqa;41.4742 Manay;7.2108 Rāni;25.5564 La Chaux-de-Fonds;47.1013 Socorro;-22.5908 Modugno;41.0833 Novodvinsk;64.4167 Rosário;-2.9339 Ramallah;31.9000 Charaut;26.5322 Valrico;27.9193 Joué-lés-Tours;47.3514 Marratxi;39.6421 Arcos;-20.2914 Villa del Carbón;19.7272 Seram;17.1830 Takikawa;43.5578 Stirling;56.1166 Indian Trail;35.0699 Stendal;52.6000 Tianchang;37.9981 Sanjiang;24.7265 San Giuliano Milanese;45.4000 Xiushui;24.0356 Oneşti;46.2586 Farīmān;35.7069 Vetapālem;15.7800 Trofa;41.3374 Yangfang;40.5723 San José de Ocoa;18.5500 Gradačac;44.8789 Mitsuke;37.5316 Pokrov;47.6533 Narón;43.5500 Oliveira;-20.6958 San Juan del Cesar;10.7667 Benjamin Constant;-4.3755 Damulog;7.4853 Niutuo;39.2618 Hobbs;32.7281 Doğanşehir;38.0958 Tlalixcoyan;18.8031 Presidente Venceslau;-21.8761 Santa Maria da Boa Vista;-8.8089 Çiftlikköy;40.6603 Carini;38.1333 Sayansk;54.1167 Abinsk;44.8667 Parral;-36.1500 Rusape;-18.5367 Tonbridge;51.1987 Tangpingcun;22.0292 Cape Girardeau;37.3109 Eccles;53.4824 Oviedo;28.6580 Tāki;22.5900 Whanganui;-39.9325 Axochiapan;18.5000 Sirohi;24.8850 Sual;16.0661 Teboulba;35.6700 Pehowa;29.9800 Draa el Mizan;36.5333 Canela;-29.3658 Abrantes;39.4633 Moanda;-1.5667 Sopot;54.4500 Apan;19.7000 Puerto Berrío;6.4900 San Dionisio;11.2711 Shibata;38.0566 Tamba-Sasayama;35.0725 Shijiazhuangnan;37.9383 Warren;41.2390 São Francisco do Conde;-12.6278 Mapandan;16.0260 Rosário do Sul;-30.2578 Cacuso;-8.2333 Marcos Paz;-34.7667 Køge;55.4561 Oraiókastro;40.7333 Cabugao;17.7947 Tarazá;7.5881 Kirkkonummi;60.1167 Betamcherla;15.4667 Thakraha;26.7419 Sikandra Rao;27.7000 Plant City;28.0140 Sandino;22.0803 Higashimatsushima;38.4263 Sidi Bibi;30.2333 Basista;15.8524 Ilmenau;50.6839 Ospino;9.3000 Amla;21.9248 Szczecinek;53.7167 San Raimundo;14.7647 Sungandiancun;36.2221 Yelizovo;53.1833 Brasschaat;51.2931 Waregem;50.8811 Penumūr;13.3667 Caluya;11.9320 Salinas;-16.1185 Cayambe;0.0439 Birmitrapur;22.4000 Urbana;40.1107 Manggar;-2.8900 Jacarèzinho;-23.1608 Tuntum;-5.2578 Dubbo;-32.2569 Androka;-25.1167 Dwārka;22.2403 José María Morelos;19.7500 Xiwanzi;40.9717 Baikonur;45.6167 San Fernando;13.5661 Changchunpu;27.2388 Colinas;-6.0258 Sama;43.3000 São Mateus do Maranhão;-4.0400 Olive Branch;34.9610 Al Mālikīyah;37.1667 Santa Helena;-2.2308 Ílhavo;40.6000 Susurluk;39.9139 Wesseling;50.8167 Monreale;38.0817 Ambatomainty;-17.6833 Kachkanar;58.7000 Gabaldon;15.4506 Souma;36.5183 Gorinchem;51.8306 Severna Park;39.0870 Wheeling;42.1308 Landgraaf;50.9083 Nuevitas;21.5403 Bougara;36.5333 Mehidpur;23.4888 Roy;41.1714 Penfield;43.1602 Toumodi;6.5520 Bangar;16.9000 Issaquah;47.5439 Inashiki;35.9565 Kurchatov;51.6667 Caivano;40.9500 Pala Oua;9.3500 Rtishchevo;52.2667 Los Palacios y Villafranca;37.1625 Ancud;-41.8667 Margosatubig;7.5783 Valencia;9.2833 Yoshinogawa;34.0631 Dibaya-Lubwe;-4.1500 Itabaianinha;-11.2739 Samal;14.7678 Kadiyam;16.9167 Vasylkiv;50.1775 Buchireddipālem;14.5380 Mira;45.4375 Umi;33.5675 Westerville;40.1241 Hitachiomiya;36.5425 Tshela;-4.9667 Dubno;50.3931 Galeana;24.8333 Akçakoca;41.0833 Khairtal;27.8346 Teijlingen;52.2150 Grants Pass;42.4333 Bitterfeld;51.6167 Marinha Grande;39.7500 Ōmagari;39.4531 Sakuragawa;36.3273 Haverstraw;41.2055 Taşköprü;41.5097 Altos;-5.0389 Lluchmayor;39.4900 Skelmersdale;53.5500 Campina Grande do Sul;-25.3058 Bautzen;51.1814 Veghel;51.6167 Mission;49.1337 Huzūrābād;18.2000 Ibusuki;31.2528 Pervomaisk;48.6297 Rājula;21.0500 Sarikishty;38.4667 Dabola;10.7500 Tuusula;60.4333 Fürstenfeldbruck;48.1778 Yako;12.9667 Lynnwood;47.8284 Calexico;32.6849 Cajibío;2.6333 Gujō;35.7486 Aborlan;9.4386 Los Palacios;22.5822 Olutanga;7.3106 Simão Dias;-10.7378 Nishiwaki;34.9903 Jamindan;11.4094 Maasin;10.8833 Naushahro Firoz;26.8422 Real;14.6667 Dzhankoi;45.7086 Barbacoas;1.6717 Alingsås;57.9300 Sibaté;4.4908 Braintree;42.2039 Buug;7.7286 Mamun;32.2824 San Andres;13.5961 Champerico;14.2930 Royal Palm Beach;26.7038 Mechelen-aan-de-Maas;50.9967 Great Yarmouth;52.6060 Nové Zámky;47.9856 Tōkai;36.4730 Groton;41.3598 Königs Wusterhausen;52.2917 Metlaoui;34.3333 Land O' Lakes;28.2075 Misawa;40.6831 Vangaindrano;-23.3500 La Unión;14.9667 San José de Las Matas;19.3300 Ábrego;8.0000 Mariinsk;56.2000 Bayan;36.1148 Xiaguanying;35.9427 Guariba;-21.3600 Az̧ Z̧āhirīyah;31.4078 Alderetes;-26.8167 Bettendorf;41.5657 Krasnoufimsk;56.6167 Muaná;-1.5278 Nindirí;12.0031 Darāw;24.4000 San Felipe Orizatlán;21.1719 Pacifica;37.6113 Nigel;-26.4203 Lanyi;38.7048 Żary;51.6333 Cottage Grove;44.8161 Uryupinsk;50.8000 Yuzhnoukrainsk;47.8217 Monan;38.2621 Phulbāni;20.4700 Boa Esperança;-21.0900 Falāvarjān;32.5553 Gomoh;23.8735 Dubrājpur;23.8000 Patnongon;10.9142 Minas;21.4894 Kardítsa;39.3667 Khalkhāl;37.6272 Kakching;24.6085 Caibarién;22.5158 Jarosław;50.0186 Konakovo;56.7000 Siyang;27.2116 Itiúba;-10.6908 Américo Brasiliense;-21.7361 Backnang;48.9464 Clovis;34.4376 Vestavia Hills;33.4518 Borbon;10.8333 Santo Domingo;13.2350 Ventanas;-1.4500 Barru;-4.3587 Villenave-d’Ornon;44.7806 Várzea Alegre;-6.7889 Holyoke;42.2125 Yanqi;42.0586 Villagarcía de Arosa;42.5977 Coto Brus;8.8890 Evans;33.5619 Curuçá;-0.7339 Kamp-Lintfort;51.5000 Genç;38.7500 Chacabuco;-34.6333 Nakhyaungcharīpāra;21.4167 Antsohihy;-14.8762 San Jacinto de Buena Fe;-0.8986 Hanumānnagar;26.5394 Tayasan;9.9167 Venlo;51.3667 Port Talbot;51.5906 Kaka;37.3500 Caltagirone;37.2375 Ibara;34.6000 Laur;15.5797 Usinsk;66.0000 Sharypovo;55.5333 Atamyrat;37.8531 Alekseyevka;50.6333 Bol’shoy Kamen’;43.1167 Gūdārah;34.8169 Moncada;41.4867 Dom Pedrito;-30.9828 Torre-Pacheco;37.7333 Navgilem;40.1333 Şarkışla;39.3500 Katsuragi;34.4833 Sabang;5.8942 Grimbergen;50.9333 Krasnyy Sulin;47.8833 Jagdīspur;25.4674 Pathanāmthitta;9.2648 Lyudinovo;53.8667 Apache Junction;33.3985 Goribidnūr;13.6111 Pitangueiras;-21.0094 Maule;-35.5333 Zavolzhye;56.6500 Shrewsbury;42.2842 Pen;18.7500 Tāsgaon;17.0300 Gampola;7.1647 Dicle;38.3750 Bahādurganj;26.2617 Barrancas;11.0000 Bat Khela;34.6200 Cerveteri;42.0000 Taounate;34.5358 Cheyyār;12.6620 Orange;-33.2833 Su’ao;24.6000 Anosiala;-18.7833 Menomonee Falls;43.1487 Phenix City;32.4585 Dengtangcun;23.6821 Papenburg;53.0667 Bagabag;16.6044 Savelugu;9.6167 Luna;16.8500 Guayacanes;18.4533 Xinpo;21.6645 Sitalkuchi;26.1697 San Juan Cancuc;16.9333 Post Falls;47.7213 Izobil’nyy;45.3667 Xinqiao;39.2694 Goes;51.5000 Raalte;52.3833 La Vergne;36.0200 Oleiros;43.3333 Vaudreuil-Dorion;45.4000 Sampués;9.1833 Rūdsar;37.1375 Visconde do Rio Branco;-21.0103 Boryslav;49.2881 Clifton Park;42.8587 Parkland;47.1417 Yamen;22.3017 Uster;47.3500 Yuzhnouralsk;54.4500 Spoleto;42.7565 San Andres;13.3231 Dālkola;25.8758 Masamba;-2.5531 Mount Juliet;36.1990 San Pédro Jocopilas;15.0953 Güira de Melena;22.8019 Hot Springs;34.4892 Les Abricots;18.6333 Aristóbulo del Valle;-27.1167 Amambaí;-23.1039 Qaratog;38.5500 Formia;41.2667 Coondapoor;13.6313 Hajīn;34.6894 Winter Springs;28.6889 Mixquiahuala de Juarez;20.2311 Andkhōy;36.9500 Catende;-8.6669 Monrovia;34.1650 Bom Jardim;-7.7958 Luuk;5.9676 Haugesund;59.4464 Obala;4.1667 Olot;42.1822 Mullaittivu;9.2833 Prattville;32.4597 Ohangaron;40.9061 Greven;52.0917 Argun;43.2944 Kapiri Mposhi;-13.9772 Tunduma;-9.3000 Gaspar Hernández;19.6200 Sanchahe;36.3780 Srīnagar;30.2200 Carpentersville;42.1227 Ichchāpuram;19.1200 Amarante do Maranhão;-5.5669 Santiago;25.4333 Sighetu Marmaţiei;47.9286 Sītākund;22.6200 Puri;-7.6833 Česká Lípa;50.6886 West Fargo;46.8573 Shakhtīnsk;49.7100 Basavana Bāgevādi;16.5728 Akhtubinsk;48.2833 East Point;33.6696 Amargosa;-13.0300 Ipueiras;-4.5419 Spišská Nová Ves;48.9439 Kehl;48.5667 Dibulla;11.2667 Tupelo;34.2692 Rosenberg;29.5456 Peachtree City;33.3942 Santa Rita do Sapucaí;-22.2519 Thāna Bhawan;29.5833 Ma‘arratmişrīn;36.0000 Mahanoro;-19.9000 La Quinta;33.6536 Sunbury;-37.5811 Patía;2.1667 Mabuhay;7.4176 Lampa;-33.2833 Malavalli;12.3800 Guaraciaba do Norte;-4.1669 Emirdağ;39.0167 Al Qaryatayn;34.2283 Rāwatbhāta;24.9300 Marantao;7.9500 Jalapa;17.7000 Dajiecun;36.2965 San Marcelino;14.9742 Esperantina;-3.9019 Malmesbury;-33.4500 Santiago Sacatepéquez;14.6530 Hatta;24.1341 Curitibanos;-27.2828 Geraldton;-28.7744 Afuá;-0.1569 Sārangpur;23.5700 Trou du Nord;19.6333 Matina;10.0099 Mechanicsville;37.6263 Tambulig;8.0700 Daiyue;39.5284 Yolöten;37.3000 Uchaly;54.3167 Villa González;19.5333 Galloway;39.4914 Nalayh;47.7725 Baocheng;18.6431 Kalbā;25.0742 Granadero Baigorria;-32.8500 Sun City;33.6165 Bamendjou;5.4000 Houghton le Spring;54.8410 Kahrīzak;35.5175 São Gonçalo dos Campos;-12.4328 Mahē;11.7011 Magallanes;12.8283 Bonito;-8.4700 Kalinkavichy;52.1250 Oras;12.1414 San Antonio;14.9486 Porteirinha;-15.7428 Chieri;45.0139 Lahār;26.1940 Greenfield;42.9619 Kanigiri;15.4000 Rio Pardo;-29.9900 Esplanada;-11.7958 Bantay;17.5839 Bayur;-0.2610 Igarapé-Açu;-1.1269 Salinópolis;-0.6289 Morsi;21.3180 Bouznika;33.7897 Risalpur Cantonment;34.0049 Jelilyüzi;43.9749 São Fidélis;-21.6458 Wangsicun;37.9975 Al Hāshimīyah;32.3664 Paraty;-23.2194 Knurów;50.2167 Progreso;21.2800 Frolovo;49.7667 Oleśnica;51.2000 Alapayevsk;57.8500 El Bordo;2.1142 Owasso;36.2878 Bindki;26.0300 Aliança;-7.6028 Shiji;22.2198 Tosno;59.5500 Liwonde;-15.0667 Portage;41.5856 Fermo;43.1608 Kishmat Dhanbāri;24.6167 Ratia;29.6833 Fengguangcun;23.9062 Chanderi;24.7200 Dalnegorsk;44.5500 Maibara;35.3167 Itarema;-2.9200 Pedro II;-4.4338 Rock Island;41.4699 Mankayan;16.8567 Rafiganj;24.8200 Goryachiy Klyuch;44.6333 Malgobek;43.5000 Nandaime;11.7569 Itaqui;-29.1250 Comapa;14.1114 Chrzanów;50.1333 Gajendragarh;15.7363 Santa Rosa de Osos;6.6500 Pilibangan;29.4888 Mölndal;57.6500 Shaxi;22.3067 Lebanon;36.2040 Agudos;-22.4739 Tamamura;36.3044 Châtelet;50.4000 Warendorf;51.9539 Kérou;10.8250 Holubivske;48.6375 Yugorsk;61.3167 Nakodar;31.1300 Tudela;42.0653 Pingxiangcheng;36.9819 Melgar;4.2039 Dīnhāta;26.1300 Punārakh;25.4930 Besni;37.6942 Moerdijk;51.6500 Lierre;51.1333 Don Benito;38.9545 Sitangkai;4.6615 Bochil;17.0167 Seika;34.7667 New Albany;38.3090 Maşyāf;35.0653 Mol;51.1833 Cheltenham;40.0781 Daying;39.0786 Petroşani;45.4122 Patāmundai;20.5700 Kizilyurt;43.2167 Tibú;8.6500 Basilisa;10.0654 Martinez;37.9985 Lębork;54.5500 Riviera Beach;26.7813 Chorkŭh;39.9739 Koryazhma;61.3167 Hendon;51.5837 Coatepec Harinas;18.9000 Musiri;10.9333 Ewing;40.2650 Mieres;43.2508 Tunzi;35.5782 Aguelmous;33.1500 Holstebro;56.3572 Kaman;39.3575 Mangai;-4.0499 San Enrique;11.0697 Andilamena;-17.0167 Irbit;57.6667 Frankston;-38.1580 Cisterna di Latina;41.5908 Varberg;57.1167 Hekou;22.5174 Adelanto;34.5814 Pleasant Grove;40.3716 Moalboal;9.9500 Obburdon;40.4278 Imbatug;8.3128 Västervik;57.7500 Leavenworth;39.3239 Licata;37.1083 Muskogee;35.7432 Suhl;50.6106 Ciudad Manuel Doblado;20.7303 Protvino;54.8833 Silves;37.1869 Rezh;57.3667 Anzhou;38.8663 Zīra;30.9700 Baishi Airikecun;40.8035 Kannan;35.0889 Kāramadai;11.2428 Yasynuvata;48.1167 Oregon City;45.3418 Languyan;5.2667 Manazary;-19.0500 Beslan;43.1833 La Ciotat;43.1769 Erding;48.2833 Pāthri;19.2500 Yongjing;23.9223 Zuojiawu;39.9514 Nanshuicun;22.0316 José de Freitas;-4.7558 Qulsary;46.9833 Mandoto;-19.5667 Elixku;38.6803 Beni Khiar;36.4667 Ariyalūr;11.1333 Tuensang;26.2716 Sagnay;13.6039 Bartlesville;36.7365 Kampene;-3.5968 Dar Chabanne;36.4700 Timbó;-26.8228 Tuttlingen;47.9850 Tsubata;36.6692 Magsaysay;9.0167 Coatepeque;13.9167 Kyshtym;55.7000 Beckum;51.7550 Lesozavodsk;45.4667 Robertsganj;24.7000 Canaman;13.6481 Autazes;-3.5864 Manucan;8.5161 Partizansk;43.1167 Azuqueca de Henares;40.5647 San Luis;13.8333 Monatélé;4.2667 Villeta;-25.5100 Colón;8.0378 Dabutou;36.0708 Brighton;43.1175 Woodley;51.4530 Santa Ana;7.6453 Lakshmeshwar;15.1300 Saratoga Springs;40.3450 Netishyn;50.3300 Yovon;38.3167 Şuhut;38.5311 San Miguel Chicaj;15.1000 Caimito;22.9575 Xindian;37.1341 Aourir;30.4833 Uruaçu;-14.5250 Fuchūchō;34.5650 Santaluz;-11.2558 Aurora;16.9918 Sōma;37.7967 Sokol;59.4667 Dagua;3.6667 Mizunami;35.3618 Numancia;11.7042 Chimboy Shahri;42.9295 Komatsushimachō;34.0003 Pihānī;27.6199 Beiya;36.4640 Accrington;53.7534 Enrile;17.5622 Agogo;6.8000 Sayula;19.8836 Sankaridrug;11.4800 Pimenta Bueno;-11.6725 Yejituo;39.8634 Mangalia;43.8172 Guilderland;42.7080 Cuenca;13.9167 Ikongo;-21.8833 Torres Novas;39.4667 Qalādizay;36.1833 Kanada;33.7760 Rossano;39.5667 Pennsauken;39.9649 Porsgrunn;59.1156 Taxisco;14.0716 Zhaoqiao;37.9398 Trumbull;41.2602 Lajedo;-8.6639 Mek’ī;8.1500 Yasugichō;35.4283 Jaral del Progreso;20.3667 Wangtuan;36.8624 Santa Pola;38.1897 La Macarena;2.1828 Köneürgench;42.3333 Dargaz;37.4444 Mbulu;-3.8500 Chippenham;51.4590 Kambove;-10.8764 Danihe;39.8489 Tucker;33.8436 Esik;43.3500 Las Navas;12.3400 Pūrna;19.1800 Araçuaí;-16.8500 Namlea;-3.2500 Brant;43.1167 Huzūrnagar;16.9000 Cloppenburg;52.8500 Esquel;-42.9000 Cantanhede;40.3462 Cambrils;41.0670 Castricum;52.5500 Franklin;42.8854 Oakton;38.8887 Zhetisay;40.7753 Kulp;38.5017 Pinerolo;44.8873 Maur;30.0833 Belalcázar;2.6547 Neiba;18.4900 Coesfeld;51.9500 Kinel;53.2222 Bulicun;24.3657 Guápiles;10.2070 Kodaikānal;10.2300 Shirdi;19.7700 Natick;42.2847 Uddevalla;58.3500 Santa Cruz Verapaz;15.3736 Haomen;37.3760 Lewiston;44.0915 Bindé;11.7500 Lakeshore;42.2399 Montluçon;46.3408 Songo;-15.6142 Sholinghur;13.1200 Dinas;7.6161 Tocancipá;4.9657 South Valley;35.0093 Nuoro;40.3167 Limburg;50.3833 Ekpé;6.4000 ‘Afrīn;36.5083 Dashtobod;40.1269 Beloit;42.5230 Upper Arlington;40.0272 Innisfil;44.3000 Chandanais;22.2000 Barra Bonita;-22.4947 Mingjian;23.8510 Francavilla Fontana;40.5333 Voerde;51.6000 Şile;41.1764 Gasan;13.3167 Taastrup;55.6500 Masasi;-10.7296 Nargund;15.7200 Águilas;37.4042 São Paulo de Olivença;-3.4500 Kharagpur;25.1245 Villajoyosa;38.5053 DeLand;29.0225 Mamfe;5.7667 Kaniama;-7.5696 Arroyomolinos;40.2667 Aparecida;-22.8472 Simbahan;6.3000 Wildomar;33.6173 Emsdetten;52.1728 Cassino;41.5000 Nueve de Julio;-35.4500 Santa Helena de Goiás;-17.8139 San Juan Cotzal;15.4353 San Miguel Acatán;15.7000 Ma‘alot Tarshīḥā;33.0167 Selçuk;37.9500 Camamu;-13.9450 Mühlhausen;51.2167 Sonsón;5.7097 Sanok;49.5500 Pakribarawān;24.9540 Zaventem;50.8833 Huehuetán;15.0000 Aketao;39.1468 Jalārpet;12.5703 Estahbān;29.1267 Careiro;-3.7678 Dama;30.5009 Taguasco;22.0050 Cabanglasan;8.0772 Uto;32.6828 Saint-Raphaël;43.4252 Yorktown;41.2727 Kotma;23.2038 Udhampur;32.9160 Sankt Ingbert;49.3000 Isla de Maipo;-33.7500 Petit-Trou de Nippes;18.5333 Banaz;38.7333 Morohongō;35.9415 Nyaungdon;17.0461 Kerava;60.4000 Dagami;11.0611 La Vallée de Jacmel;18.2667 Morecambe;54.0730 Farafenni;13.5667 Rapu-Rapu;13.1833 Princeton;25.5396 Rende;39.3333 Jasło;49.7478 Narsampet;17.9285 San Pablo;7.4825 Yahyalı;38.1000 Kurtalan;37.9261 Capalonga;14.3314 Oeiras;-7.0250 São Miguel d’Oeste;-26.7250 Santiago Juxtlahuaca;17.3333 Bronkhorst;52.0833 Czechowice-Dziedzice;49.9131 Vempalle;14.3667 Andover;42.6466 Wai;17.9500 Mahna;30.2280 San Bernardo del Viento;9.3500 Rifu;38.3303 Claremont;34.1259 Medchal;17.6297 Siayan;8.2517 Garhākota;23.7791 Central Islip;40.7836 Georgetown;38.2247 Pandan;11.7206 Chelmsford;42.6000 Pingshang;23.3974 Oak Creek;42.8803 Anloga;5.7919 Commack;40.8435 Andújar;38.0392 Bad Vilbel;50.1781 San Carlos Sija;14.9833 Barira;7.4706 Smarhon’;54.4836 Onteniente;38.8222 Mālpura;26.2800 Zhmerynka;49.0500 Palapye;-22.5500 Bombardopolis;19.7000 Wokha;26.1000 Kalāleh;37.3808 Moorpark;34.2861 Ban Mueang Na Tai;19.5932 Ingelheim;49.9747 Tlokweng;-24.6667 Aţ Ţurrah;32.6368 Merrillville;41.4728 Hellendoorn;52.3667 Estero;26.4276 Cumberland;41.9703 Lunsar;8.6833 Augusta;37.2500 Tomelloso;39.1578 Zacatepec;18.6833 Ampasina-Maningory;-17.2167 Gutalac;7.9833 Bautista;15.8103 Al Jabāyish;30.9549 Tambolaka;-9.4333 Paiçandu;-23.4578 Meyzieu;45.7667 Claveria;9.5742 Avdiivka;48.1333 Tarikere;13.7104 Somma Vesuviana;40.8725 Virú;-8.4143 Partūr;19.5911 Roseville;45.0155 Vohipeno;-17.1667 Canicattì;37.3667 Dunedin;28.0329 Dietzenbach;50.0167 San Antonio;13.9000 Cento;44.7333 Sibuco;7.2928 Mokokchūng;26.3200 Rāmsar;36.9031 Farmers Branch;32.9272 Saparua;-3.5749 Matanog;7.4667 Calumet City;41.6134 Adrano;37.6667 Sarıgöl;38.2403 Itaitinga;-3.9689 Marion;40.5973 Cativá;9.3600 Conegliano;45.8868 Jagna;9.6500 Kalakkādu;8.5138 Dumbéa;-22.1500 Zvishavane;-20.3333 Segrate;45.4900 Shiggaon;14.9910 Velasco Ibarra;-1.0439 Guayaramerín;-10.8000 Pylaía;40.6000 Casiguran;12.8731 Porta Westfalica;52.2167 Itapicuru;-11.3169 Fraiburgo;-27.0258 San Andrés Itzapa;14.6167 Salinas;-2.2167 Miyajima;33.1500 Addison;41.9313 La Libertad;13.4833 Chortoq;41.0689 Sinsheim;49.2500 Chipinge;-20.2000 Hole Narsipur;12.7863 Zhentang;21.8662 Boyarka;70.7670 Praya;-8.7223 Geiro;-6.1428 San Lorenzo de Guayubín;19.6200 Farafangana;-22.8167 Kamenka;53.1833 Winsen;53.3667 Belluno;46.1403 Winchester;51.0632 Ambohibary;-18.9000 Kakhovka;46.8000 Chernyakhovsk;54.6347 Prince Albert;53.2000 Chītāpur;17.1201 Setouchi;34.6667 Toretsk;48.3917 Vyazniki;56.2500 Leyland;53.6920 Kumo;10.0431 Six-Fours-les-Plages;43.1009 Dayr Ḩāfir;36.1569 Sambir;49.5167 Mengdong;23.1499 Tallkalakh;34.6683 Miranda de Ebro;42.6833 Ẕefat;32.9658 Sion;46.2333 Prestea;5.4333 Savanūr;14.9731 Karak;33.1167 Santa Cruz;-6.2289 Richmond;39.8318 Medina;8.9167 Estreito;-6.5608 Atami;35.0960 Kita Chauhāttar;25.6770 Carrollwood;28.0577 Guacarí;3.7667 Kundian;32.4522 Umarga;17.8400 Pak Chong;14.6796 Várzea da Palma;-17.5978 Olney;39.1465 Guimbal;10.6667 Usuki;33.1333 Leganes;10.7833 Berg en Dal;51.8167 Sūrandai;8.9773 Vavatenina;-17.4667 Isumi;35.2539 Uravakonda;14.9500 Teruel;40.3456 Al Ḩişn;32.4583 Svetlograd;45.3500 Santiago de Tolú;9.5333 Santa Ana;18.4589 Guane;22.2006 Matanzas;18.2428 Burriana;39.8894 Baraidih;25.9798 Sochaczew;52.2333 Ouro Preto d’Oeste;-10.7481 Pambujan;12.5667 Camiri;-20.1000 Zaidpur;26.8300 Orangevale;38.6881 Macomia;-12.2377 Imzouren;35.1500 Balasan;11.4728 Evergem;51.0167 Třebíč;49.2150 Shīyāli;11.2390 Santa Fe;11.1500 Boyarka;50.3292 Cieszyn;49.7500 Lixingcun;23.0852 Mendez-Nuñez;14.1286 Slagelse;55.4049 Pola;13.1439 Gürpınar;38.3269 Bedēsa;6.8830 Câmara de Lobos;32.6953 Kilimli;41.4833 Garibaldi;-29.2558 Karoi;-16.8100 Dundee;-28.1725 Haguenau;48.8200 Ypané;-25.4500 Lökbatan;40.3272 Randallstown;39.3723 Al ‘Aqīq;20.2685 Usilampatti;9.9700 Bariri;-22.0744 Guskhara;23.4928 Cha-am;12.7992 Riverhead;40.9408 Schoten;51.2500 Palmares;10.0466 Gahanna;40.0251 Meridian;32.3846 Alatyr;54.8500 Nova Cruz;-6.4778 Saint-Chamond;45.4775 Mörfelden-Walldorf;50.0000 Gladstone;-23.8427 Minami-Bōsō;35.0432 Meppen;52.6936 Coevorden;52.6667 Dimitrovgrad;42.0500 Buhriz;33.7000 Juban;12.8478 Karumattampatti;11.1093 Gigante;2.3867 Hamidiye;40.0997 Tilingzhai;40.2353 Yangambi;0.7675 José Bonifácio;-21.0528 Leer;53.2308 Yerköy;39.6381 Simiganj;38.6525 Douzhuang;39.4323 Toritama;-8.0067 La Porte;29.6689 Ōfunato;39.0680 Şaḩneh;34.4814 La Troncal;-2.4000 Zorgo;12.2500 Morong;14.6800 Newton;8.3333 Guamá Abajo;19.9758 Quimbaya;4.6333 Angra do Heroísmo;38.6558 Ikot Abasi;4.5704 Nanzhiqiu;37.7492 Sungaiselam;-2.3839 Inver Grove Heights;44.8247 Mahabo;-20.3833 Savonlinna;61.8681 Freehold;40.2233 Akouda;35.8714 Hilliard;40.0353 Cutral-Có;-38.9333 Sun Prairie;43.1825 Lanciano;42.2333 Yayladağı;35.9025 Yeonil;36.0000 Kraśnik;50.9167 També;-7.4100 Bathurst;-33.4200 Vichuga;57.2000 Lage;51.9889 Shenjiabang;30.5783 Charqueadas;-29.9550 Saint-Benoît;-21.0339 Kahoku;36.7198 Cegléd;47.1743 Cartagena del Chairá;1.3500 Rio Brilhante;-21.8019 Além Paraíba;-21.8878 Bom Jesus do Itabapoana;-21.1339 Kamata;33.5632 Aïne Draham;36.7833 Radebeul;51.1000 Tamuín;22.0000 Auxerre;47.7986 Copperas Cove;31.1192 Tahuna;3.6119 Torrington;41.8349 Armenia;13.7500 Pilar;-9.5972 Riachão do Jacuípe;-11.8100 Pilão Arcado;-10.0018 Unnan;35.2833 Buxin;23.9523 Pentecoste;-3.7928 Media Luna;20.1444 Casimiro de Abreu;-22.4808 Ürgüp;38.6314 El Mirage;33.5905 Millerovo;48.9167 Ouro Branco;-20.5208 Datteln;51.6539 La Palma;22.7472 Osimo;43.4833 Cieza;38.2392 Itaberaí;-16.0200 Coventry;41.6934 Wildwood;38.5800 San Juan;26.1903 Sulop;6.5986 Akçadağ;38.3450 Galapagar;40.5764 Winsford;53.1940 Yarumal;7.0306 Oakville;38.4479 Qadsayyā;33.5333 Allacapan;18.2270 Saarlouis;49.3167 Tangancícuaro de Arista;19.8889 Langford Station;48.4506 Ōzu;32.8790 Bradford West Gwillimbury;44.1333 San Juan Capistrano;33.5008 Jieshang;25.6989 Yellandu;17.6000 Ieper;50.8508 Gandara;12.0130 Guaramirim;-26.4728 Trinidad;10.0795 Yecla;38.6167 Nyamata;-2.2050 San Luis;32.4911 Yarīm;14.2972 Bou Salem;36.6167 Luga;58.7333 Giddalūr;15.3784 Tall Salḩab;35.2609 Baba I;6.0622 Brunswick;41.2465 Crema;45.3667 Mananara Avaratra;-16.1667 Cansanção;-10.6708 Mahasolo;-19.1167 Voznesensk;47.5725 Fonseca;10.8333 Salisbury;35.6658 Lingig;8.0381 Massapê;-3.5228 Ville Bonheur;18.8167 Gorizia;45.9333 Tecuci;45.8467 Abra de Ilog;13.4448 Muddebihāl;16.3300 Safidon;29.4200 San Luis;8.4964 Wermelskirchen;51.1500 Mirnyy;62.5500 Tooele;40.5393 Izkī;22.9339 Curaçá;-8.9919 Vedāranniyam;10.3774 Kōṯah-ye ‘As̲h̲rō;34.4492 Venaria Reale;45.1167 Yefremov;53.1500 Rāwatsār;29.2800 Umarkot;25.3614 Vadigenhalli;13.2900 Munai;7.9758 Crailsheim;49.1347 Rūdarpur;26.4293 Nevers;46.9933 Afogados da Ingazeira;-7.7508 Bodocó;-7.7778 Třinec;49.6778 Ahrensburg;53.6747 Owings Mills;39.4115 Igarapé;-20.0700 Nova Olinda do Norte;-3.8878 Mâcon;46.3063 Tamra;32.8536 Lingsugūr;16.1700 Wedel;53.5833 Tienen;50.8000 Matou;39.5503 Greer;34.9330 Jiaoxi;24.8167 Paxtaobod;40.9283 Ngororero;-1.8650 Ikalamavony;-21.1500 San Vicente de Chucurí;6.8833 Hīrākud;21.5250 Campbell River;50.0244 Lake Stevens;48.0024 Adamantina;-21.6847 Wavre;50.7167 Al Ḩamdānīyah;36.2697 Azazga;36.7453 Tamura;37.4333 San Luis de Sincé;9.2500 Northbrook;42.1292 Iyo;33.7500 Bagamoyo;-6.4444 Ringsaker;61.0242 Puerto López;4.0833 Kempen;51.3658 Payao;7.5857 Piskent;40.8992 Fereydūn Kenār;36.6864 Casilda;-33.0500 Formigine;44.6072 Talanga;14.4047 Āllagadda;15.1322 Seelze;52.3961 Firavahana;-18.6333 Glastonbury;41.6921 Mlimba;-8.7786 Fotadrevo;-24.0500 Caucagua;10.2822 Kulittalai;10.9357 Beidou;23.8747 Mānāmadurai;9.6956 Kalaa Srira;35.8236 Blagoveshchensk;55.0350 Aloguinsan;10.2229 Tarkwa;5.3000 Campoalegre;2.6867 Ḑulay‘ Rashīd;25.5067 Shaqrā’;25.2483 Hatibanda;24.2105 Dabaga;17.2667 Menen;50.7956 Korkino;54.9000 Meppel;52.7000 Ngaoundal;6.5000 Balingen;48.2731 Camoapa;12.3842 Dhāmnod;22.2093 Bethlehem;42.5856 Dueñas;11.0667 Baja;46.1833 Armação dos Búzios;-22.7469 Milaor;13.5956 Laoac East;16.0333 Seiyo;33.3628 Eşme;38.4000 Steinfurt;52.1475 Khodābandeh;36.1194 Arsin;40.9500 Odorheiu Secuiesc;46.3139 Anse-à-Veau;18.5167 Santa Cruz;-34.6372 Misungwi;-2.8500 Gundlupēt;11.8000 Aguaí;-22.0603 San Javier;37.8037 Balarāmpur;26.2432 Aznakayevo;54.8500 Victoria;-38.2333 Barugo;11.3167 Deggendorf;48.8333 Barra do Choça;-14.8808 Goch;51.6839 Năvodari;44.3211 Feijó;-8.1639 North Ridgeville;41.3852 Sint-Pieters-Leeuw;50.7833 Angamāli;10.2000 Vemalwāda;18.4667 Paithan;19.4800 Tôrres;-29.3350 Kalarūch;34.5731 Madhubani;26.5147 Santa Cruz das Palmeiras;-21.8269 Diaowo;39.4812 Bugasong;11.0447 Guying;38.0887 Mosonmagyaróvár;47.8737 Capelinha;-17.6908 Mogpog;13.4833 Del Rio;29.3708 Dabouziya;33.3064 Santa Maria;14.4750 Narsīpatnam;17.6650 Iława;53.5964 Aksay;51.1678 Houlong;24.6167 Castelo;-20.6039 Goshen;41.5743 Abulug;18.4441 Yangquan;37.0749 Jalajala;14.3557 Apodi;-5.6639 Espiye;40.9500 Bonifacio;8.0527 Ballesteros;18.4108 San Dimas;34.1082 Dupnitsa;42.2650 Sōsa;35.7075 Yhú;-25.0600 Shingū;33.7153 Springville;40.1638 Awa;34.1014 Benton;34.5776 Phra Phutthabat;14.7212 Zhitiqara;52.1908 Hinesville;31.8248 Banda;24.0449 Biharamulo;-2.6333 Sarpol-e Z̄ahāb;34.4611 Dzierżoniów;50.7281 Zelenokumsk;44.4167 Buenos Aires;2.9167 Abashiri;44.0167 Santa Maria;15.9808 Shangzhen;33.7116 São Luís do Quitunde;-9.3178 La Blanca;14.5791 Merseburg;51.3544 Zweibrücken;49.2500 Kasba;25.8564 Socorro;31.6383 IJsselstein;52.0167 Madakalavāripalli;14.7475 Ofunato;39.0819 Inca;39.7167 Spanaway;47.0979 Richmond;37.7307 Qaşr al Qarabūllī;32.7500 Randolph;42.1778 Barsinghausen;52.3000 Tábor;49.4144 Bugojno;44.0572 Heunghae;36.1167 Betafo;-19.8400 Pleasant Hill;37.9539 Falls;40.1686 Ankola;14.6605 Guarambaré;-25.4900 Cachoeira;-12.6178 Bayog;7.8474 Esposende;41.5333 Playas;-2.6300 Zhangzhengqiao;38.4042 Parnarama;-5.6819 Ocuilan de Arteaga;19.0000 Sabaneta;19.4833 Cañete;-37.7994 Tiruvūr;17.1000 Hemer;51.3833 University Place;47.2147 Stow;41.1765 Skhira;34.3006 Kópavogur;64.1108 Cameron Highlands;4.5000 Songcaozhen;37.7562 Centenario;-38.8000 Humenné;48.9358 Cícero Dantas;-10.6000 Quilevo;-7.6167 New Panamao;5.9667 Bāft;29.2331 Mnasra;34.7667 Áno Liósia;38.0833 Kaneohe;21.4062 Gotō;32.7000 Banes;20.9697 Oława;50.9333 Gerāsh;27.6672 Iturama;-19.7278 Xincheng;38.2679 Inabe;35.1156 Degāna;26.8951 Curuzú Cuatiá;-29.7833 Hisuā;24.8336 Vassouras;-22.4039 Nola;40.9333 Fruit Cove;30.0972 Monroe;35.0063 Youwangjie;24.8695 Minas de Matahambre;22.5822 Simunul;4.8980 Sirīpur;25.9970 Shinjō;38.7667 Risod;19.9700 Râmnicu Sărat;45.3800 Exmouth;50.6200 Pingtang;22.7542 Jaguaribe;-5.8908 Belhi;26.5769 Shamsābād;17.2603 Zamānia;25.4194 Douglasville;33.7384 Casale Monferrato;45.1342 Almora;29.5971 Fairborn;39.8010 Biwong;3.1333 Korschenbroich;51.1833 Talayan;6.9844 San Manuel;17.0167 Tandubas;5.1340 Lommel;51.2333 Kallidaikurichi;8.6859 Santa Ana Nextlalpan;19.7167 Butte;45.9020 Vibo Valentia;38.6753 Sibutu;4.8500 Dakota Ridge;39.6192 Machachi;-0.5100 Tōon;33.7833 Toda Bhīm;26.9167 Oildale;35.4293 Kohtla-Järve;59.3978 Qingyang;36.1985 Cariari;10.4349 Mason;39.3571 Missão Velha;-7.2500 Oswego;41.6834 Borne;25.5431 São Sebastião;-9.9339 Pinukpuk;17.5731 Gadsden;34.0086 Plainfield;39.6954 Penalva;-3.2939 Guindulman;9.7620 Úbeda;38.0117 Gulkevichi;45.3594 Businga;3.3397 Bogorodsk;56.1167 Znojmo;48.8556 Pangururan;2.6075 Lautaro;-38.5167 San Quintin;15.9844 San José Poaquil;14.8167 Cambrai;50.1767 São Desidério;-12.3628 Puqiancun;20.0263 Manitowoc;44.0991 Lufkin;31.3217 Çınarcık;40.6422 Caldono;2.8000 General Nakar;14.7631 Tepeji del Río de Ocampo;19.9039 Komárno;47.7633 Cedar City;37.6834 Datang;26.3909 Shāhīn Dezh;36.6792 Vitrolles;43.4600 Nāz̧erābād;31.5775 Deer Park;29.6898 Gyapekurom;7.5833 Lexington;42.4456 Quinchía;5.3379 Rafaḩ;31.2808 Kokrajhar;26.4000 McMinnville;45.2110 Dao;11.3800 Palapag;12.5470 Goianira;-16.4958 Tobelo;1.7319 Cravinhos;-21.3403 Tabango;11.3067 Şereflikoçhisar;38.9444 Tynaarlo;53.0833 Initao;8.5000 Peyziwat;39.4905 Cantilan;9.3356 Derry;42.8888 Nowy Targ;49.4667 Viernheim;49.5417 Marignane;43.4160 Termoli;42.0000 Geldern;51.5197 Byādgi;14.6733 Grodzisk Mazowiecki;52.1167 Biberach;48.1000 Shisō;35.0000 San Martín Sacatepéquez;14.8246 Cishan;36.5780 Río Verde Arriba;19.3200 Pulgaon;20.7260 Irvine;55.6201 Valuyki;50.2167 Woodridge;41.7370 Bassin Bleu;19.7833 Romans-sur-Isère;45.0464 Paşcani;47.2494 Binche;50.4000 Fungurume;-10.6167 Uonuma;37.2301 Dimona;31.0667 San Agustín;1.9000 Mascalucia;37.5667 Annigeri;15.4251 Boğazlıyan;39.1942 Câmpina;45.1300 San Remigio;10.8331 Santa Ana;11.0680 Chand Chaur;25.7276 Villena;38.6350 Pontneddfechan;51.7554 Prairieville;30.3151 New City;41.1542 Tobias Fornier;10.5178 Piombino;42.9348 Zhuqi;23.5065 Lochem;52.1615 Victoria Falls;-17.9333 Mahasoabe;-21.5833 Atiquizaya;13.9667 Matozinhos;-19.5578 Stuhr;53.0167 Castelfranco Veneto;45.6667 Lianga;8.6330 San Antonio de los Baños;22.8889 Réo;12.3167 Zhaitangcun;24.5133 Cookeville;36.1482 Lupi Viejo;13.7908 Spruce Grove;53.5450 San Juan Nepomuceno;9.9500 Yinchengpu;39.8189 Shikārpūr;28.2814 Dartmouth;41.6138 Catmon;10.6667 Majagual;8.5000 Westlake;41.4524 Ankazomiriotra;-19.6500 Sneek;53.0325 Tortosa;40.8125 Dīvāndarreh;35.9139 Eirunepé;-6.6597 Brumadinho;-20.1428 Maromandia;-14.2167 Sibagat;8.8219 Mizuho;35.7720 Merritt Island;28.3139 Shamsābād;27.0200 Bacarra;18.2519 Baltiysk;54.6500 Vadavalli;11.0258 Oulad Zemam;32.3500 Naguilian;17.0167 Kondapalle;16.6167 Elmina;5.0833 Lewiston;46.3934 Pôrto de Moz;-1.7478 Furmanov;57.2500 Descalvado;-21.9039 Ojiya;37.3144 Turiaçu;-1.6628 Sındırgı;39.2400 Almendralejo;38.6833 Datu Paglas;6.7669 Kadingilan;7.6003 Nizhneudinsk;54.9333 Midlothian;32.4669 Lebowakgomo;-24.2000 Xinyuan;22.5149 Batan;11.5853 Ben Ahmed;33.0655 Dhekiajuli;26.7037 Capoocan;11.2944 Moose Jaw;50.3933 Ken Caryl;39.5770 Landskrona;55.8706 Debagrām;23.6833 Bozdoğan;37.6728 Bāmaur;26.3390 Custódia;-8.0875 Tiruchendūr;8.4946 Uelzen;52.9647 Camiri;-20.0386 Cooper City;26.0463 Ban Pak Phun;8.5391 El Ksar;34.3900 Tekes;43.2181 Woodstock;34.1026 La Trinidad;12.9678 Côtes de Fer;18.1833 Golungo Alto;-9.1333 Bell Ville;-32.6333 Bhawānīpur Rājdhām;25.6501 Didouche Mourad;36.4484 Martinez;33.5209 South Riding;38.9120 Geraardsbergen;50.7667 Valparaiso;41.4783 Batobato;6.8361 Churi;23.6549 Villafranca di Verona;45.3500 Reghin;46.7758 Xiadian;39.9435 Léo;11.1000 Mazarrón;37.5983 Sertânia;-8.0706 Olkusz;50.2813 Dej;47.0872 Poço Redondo;-9.8058 Bad Nauheim;50.3667 Masiu;7.8167 ‘Ajab Shīr;37.4831 Parkland;26.3219 Villa Riva;19.1667 Mella;20.3694 Walla Walla;46.0671 Jishi;35.8511 Penticton;49.4911 Jaguarari;-10.2600 Leawood;38.9075 Fatehpur Sīkri;27.0911 Belen;36.4889 Encrucijada;22.6169 Chilecito;-29.1667 Midsalip;8.0328 Graham;47.0322 Rahachow;53.1000 Sapa Sapa;5.0899 Rubengera;-2.0519 Sukhoy Log;56.9167 Warwick;52.2800 Comé;6.4000 Puerto Rico;1.9142 Vyatskiye Polyany;56.2239 Ronda;36.7372 Carmel;41.3899 Tucumã;-6.7519 Banate;11.0500 Getafe;10.1500 Jamikunta;18.2864 Patrātu;23.6700 San Antero;9.3833 Schwedt (Oder);53.0500 Vechta;52.7306 Rexburg;43.8226 Ōno;35.9797 Macuro;10.6500 Menlo Park;37.4685 Sicuani;-14.2720 Lianmuqin Kancun;42.8833 Korsakov;46.6333 Ibaté;-21.9550 Barra do Bugres;-15.0728 São Domingos do Maranhão;-5.5758 Corinto;3.1739 Mildura;-34.1889 Zhanjia;34.7564 Asse;50.9000 Sakaiminato;35.5333 Chascomús;-35.5750 Darsi;15.7667 Manatí;21.3144 Aleshtar;33.8633 Batouri;4.4333 Rheinfelden (Baden);47.5611 Fomento;22.1053 São Bento;-6.4858 Ayvacık;39.6011 Shangcaiyuan;24.6817 Cottonwood Heights;40.6137 Jhanjhārpur;26.2647 Makhdumpur;25.0720 Manlius;43.0490 Iriba;15.1167 Ōsawa;33.2000 An’gang;35.9900 Castro;-42.4667 Columbio;6.7000 Iperó;-23.3503 Shaliuhe;39.8728 Caçapava do Sul;-30.5119 Kearney;40.7011 Mercedes;-29.2000 Kavundappādi;11.4248 Port Moody;49.2831 Pikesville;39.3893 Orodara;10.9800 Niangoloko;10.2833 Taishi;34.8333 Slobodskoy;58.7208 Rodas;22.3428 Pôrto União;-26.2378 San Vicente;10.5281 Ar Raḩmānīyah;31.1062 Sahuarita;31.9323 Crown Point;41.4143 Calape;9.8833 Ārumuganeri;8.5717 Nānjikkottai;10.7453 Timberwood Park;29.6995 Kos;36.8500 Pruszcz Gdański;54.2667 Bôca do Acre;-8.7519 City Bell;-34.8500 Madikeri;12.4209 Boxtel;51.5833 Hlukhiv;51.6765 Date;42.4719 Al Mindak;20.1833 Shalingzicun;40.6807 Chimbarongo;-34.7089 Vác;47.7752 Xinbu;24.8478 Nautan Dube;26.7118 Castro-Urdiales;43.3844 Warminster;40.2043 Nokia;61.4767 Gölbaşı;37.7839 Shōbara;34.8544 Aranda de Duero;41.6833 Udaipur;23.5389 Baturité;-4.3289 Arteijo;43.3044 San Sebastián de Mariquita;5.2500 Abbiategrasso;45.4000 Loughton;51.6494 Epe;52.3500 Radnor;40.0287 Kolín;50.0281 La Virginia;4.9167 Hayama;35.2725 Vintar;18.2306 Perşembe;41.0656 Umaria;23.5247 Dupax Del Norte;16.3075 Brejo;-3.6839 Southport;-27.9667 Conceição de Jacuípe;-12.3269 Pandami;5.5333 Tayshet;55.9500 Tavda;58.0500 Milot;19.6047 Huangzhuang;39.9905 Bethel Park;40.3238 Amparafaravola;-17.5833 Pantelimon;44.4500 Gīmbī;9.1667 Salina;43.1023 Glossop;53.4430 Police;53.5333 Sherpur;25.6539 Kusapín;9.1800 Kareli;22.9153 Kārkala;13.2000 Hāngal;14.7646 Tynda;55.1667 Kaarina;60.4000 Mohnyin;24.7833 São Luís Gonzaga;-28.4078 Los Gatos;37.2304 Obukhiv;50.1109 Brooklyn Center;45.0681 Watari;38.0442 Canavieiras;-15.6750 Anserma;5.2381 Oldenzaal;52.3167 Almeirim;-1.5228 Alamnagar;25.5610 Muconda;-10.6000 Wisbech;52.6640 Rencun;22.6585 Temascalapa;19.8000 Palmira;22.2444 Waikabubak;-9.6358 Pivijay;10.4667 Shatura;55.5667 Puerto Cumarebo;11.4861 Camacan;-15.4189 Zgorzelec;51.1500 San Roque;36.2097 Djenné;13.9000 Bragado;-35.1167 Coxim;-18.5069 Epsom;51.3360 Petersburg;37.2043 Ross;40.5256 Dipaculao;15.9833 Andırın;37.5764 Castelfranco Emilia;44.5967 Goldsboro;35.3778 Fucheng;35.3678 Dumalinao;7.8167 Minamikyūshū;31.3778 Khvāf;34.5764 De Aar;-30.6500 San Lazzaro di Savena;44.4716 Příbram;49.6883 Aravan;40.5150 Correntina;-13.3428 Lucera;41.5000 Lawrence;40.2954 Forchheim;49.7197 Asakuchi;34.5247 Redmond;44.2612 Kakira;0.5036 Água Preta;-8.7000 Shimotoba;34.8838 Upper Merion;40.0902 Lower Makefield;40.2309 Vohitromby;-23.3000 Kamaishi;39.2758 Fuquay-Varina;35.5953 Laojiezi;26.8600 Palmeira das Missões;-27.8989 Lampertheim;49.6000 Alabaster;33.2198 Kovūr;14.4833 Lumbang;14.2970 Rio das Pedras;-22.8428 Bungoōno;32.9833 Massafra;40.5833 Sānchor;24.7517 Knokke-Heist;51.3414 Umarkot;19.6653 Mashiki;32.8006 Ambodimanga II;-17.2667 San Luis;22.2828 Wakabadai;45.4156 Puebloviejo;10.9972 Mushie;-3.0167 Kadınhanı;38.2397 Purāini;25.1426 Forbe Oroya;-11.5220 Didy;-18.1167 Franklin;42.0862 Rusera;25.7600 Gillette;44.2752 Sarıkaya;39.4936 Biritiba-Mirim;-23.5728 Yamanashi;35.6934 Maragogi;-9.0122 Kennesaw;34.0260 Dolores;14.0157 Las Mercedes;8.5104 Liptovský Mikuláš;49.0842 Itzehoe;53.9250 Carapeguá;-25.7690 Osvaldo Cruz;-21.7967 Bardejov;49.2933 Gumdag;39.2061 Gyzylgaya;40.6219 Zumbo;-15.6167 Hinda;-4.6133 Colombia;20.9906 Fallbrook;33.3693 Jülich;50.9222 Maḩallat Damanah;31.0754 Pueblo West;38.3465 Leamington;42.0667 Ayorou;14.7350 Teykovo;56.8547 Charthāwal;29.5500 Esperança;-7.0333 Chernushka;56.5167 Sayram;42.3000 Pullman;46.7336 Massango;-11.3167 La Uruca;9.9575 Örnsköldsvik;63.2908 Bembe;-7.1000 Dara;15.3500 Dellys;36.9133 Bernburg;51.8000 Badoc;17.9267 Baraguá;21.6819 Dehlorān;32.6942 Buloqboshi;40.6222 Gandu;-13.7439 Ketti;11.4000 Aalsmeer;52.2667 Sō;31.6536 Ōiso;35.3069 Ţafas;32.7356 Wernigerode;51.8350 Ciudad del Plata;-34.7667 Trëkhgornyy;54.8000 La Mesa;4.6303 Tawsalun;21.4060 St. Charles;41.9193 Erraguntla;14.6333 Araban;37.4247 Curralinho;-1.8139 Kifrī;34.6833 Unchagao;16.6988 Oisterwijk;51.5833 Arbaoua;34.9000 Manises;39.4833 Şāfītā;34.8167 San Felipe;21.4833 Puerto Escondido;8.9500 Kuji;40.1905 Spring Valley;32.7317 Satānā;20.5797 Achim;53.0653 Quţūr;30.9735 Campos Novos;-27.4019 Matalom;10.2833 Baraawe;1.1133 American Fork;40.3783 São João da Barra;-21.6400 Presidente Figueiredo;-2.0172 Buco Zau;-4.7950 Monteiro;-7.8889 São Miguel Arcanjo;-23.8778 Ylöjärvi;61.5500 Payshamba Shahri;40.0078 Épinal;48.1744 Yufu;33.1833 Turaiyūr;11.1686 Bocas de Satinga;2.3469 Mānwat;19.3000 Kaizu;35.2206 Ansongo;15.6650 Ingenio;27.9214 Chancay;-11.5653 Elk Grove Village;42.0064 Tārānagar;28.6689 Mandaguari;-23.5478 Rushden;52.2880 Motosu;35.4830 Pombal;-6.7700 Altenburg;50.9850 Bilzen;50.8667 Silvia;2.6167 Baiyan;26.3584 Nurlat;54.4333 Clarence;43.0196 East Lake;28.1206 Istra;55.9167 Diplahan;7.6911 Redan;33.7394 Deurne;51.4667 Camaiore;43.9333 Espigão D’Oeste;-11.5247 Mayantoc;15.6203 Concón;-32.9167 Favara;37.3186 Oğuzeli;36.9650 Dobryanka;58.4667 Shiroishi;38.0025 Goleta;34.4361 El Difícil;9.8500 Akivīdu;16.5823 Yorii;36.1183 Plaisir;48.8183 Ptolemaḯda;40.5167 Santa Cruz del Norte;23.1556 Redcliff;-19.0333 Sadābād;27.4500 Mateur;37.0400 Cururupu;-1.8278 East Kelowna;49.8625 Avanos;38.7150 Paratinga;-12.6908 Naumburg;51.1500 Balindong;7.9167 São José do Belmonte;-7.8608 Ostrogozhsk;50.8667 Des Moines;47.3914 Inzá;2.5500 Jaguariaíva;-24.2508 Nagdha Simla;24.6143 Şarköy;40.6039 Sanha;25.4020 Minamisatsuma;31.4169 Kävlinge;55.7939 Peñaranda;15.3531 Plettenberg Bay;-34.0500 Shimanto;33.0000 Cranberry;40.7104 Lugo;44.4167 Vught;51.6500 Wobulenzi;0.7200 Utraula;27.3200 Fürstenwalde;52.3667 Mioveni;44.9569 Kulebaki;55.4167 Nāspur;18.8300 Czeladź;50.3333 Réthymno;35.3689 Samdhin;27.1370 Sherwood;34.8507 Kalyandrug;14.5500 Nossa Senhora da Glória;-10.2178 Zarqān;29.7742 Alubijid;8.5714 Hampden;40.2602 Slantsy;59.1167 Yankou;27.5950 Falmouth;41.5913 Kolachel;8.1767 Goiatuba;-18.0128 Espinho;41.0100 Levice;48.2164 Milazzo;38.2170 Chiché;15.0106 Amés;42.9000 Geesthacht;53.4375 Barreirinha;-2.8025 Nouna;12.7333 Jimalalud;9.9797 Beixinzhuang;38.7914 Shiyeli;44.1789 La Calera;-31.3439 Delbrück;51.7667 Andover;45.2571 São José da Tapera;-9.5578 Kāngayam;11.0054 San Pedro de Ribas;41.2592 Staryy Beyneu;45.1834 Landecy;46.1834 Plottier;-38.9500 Mallig;17.2086 Rokhaty;38.6000 Virei;-15.7167 Totana;37.7711 Kaysville;41.0290 Maputsoe;-28.8950 La Concepción;11.9369 Draa Ben Khedda;36.7349 Bijaynagar;25.9300 Zangang;39.0524 Tawaramoto;34.5566 Madīnat al Ḩabbānīyah;33.3667 Igbaras;10.7167 Zentsujichó;34.2167 Gramado;-29.3789 Waddinxveen;52.0500 Cachoeira Paulista;-22.6650 Herrenberg;48.5967 Salamína;37.9667 Georgsmarienhütte;52.2000 Statesboro;32.4375 Zaojiao;34.4727 Pekin;40.5678 Balimbing;7.9000 Dumfries;55.0700 San Pedro de Urabá;8.2833 Bedworth;52.4750 Point Pedro;9.8167 Foça;38.6667 Tubao;16.3500 Āreka;7.0710 Dhahran;26.2667 Carangola;-20.7328 Guararapes;-21.2608 Kōnan;33.5667 Kukshi;22.2068 Kristianstad;56.0337 Ferreñafe;-6.7183 North Olmsted;41.4149 Kiranomena;-18.2833 Kalisizo;-0.5350 Menglie;22.5833 Nagarote;12.2650 Dracut;42.6832 Rādhanpur;23.8300 Narsinghgarh;23.7000 Nacogdoches;31.6134 Buguey;18.2882 Canton;34.2467 São Raimundo Nonato;-9.0150 Wheat Ridge;39.7728 Châtellerault;46.8178 Berasia;23.6146 Nieuw-Vennep;52.2644 Sébaco;12.8553 Oudenaarde;50.8500 Catubig;12.4000 Curanilahue;-37.4764 Harker Heights;31.0572 Namerikawa;36.7644 Rochester;43.2990 Miguel Alves;-4.1658 Rome;43.2260 Mangūr;17.9373 Bambadinca;12.0333 Gödöllő;47.6000 Ribeira Grande;37.8167 Ilhabela;-23.7778 Salug;8.1075 Kottaikuppam;11.9613 Messaména;3.7333 San Carlos Alzatate;14.5000 Shimizuchō;35.0990 Bramsche;52.4000 Jaguaruana;-4.8339 Nagato;34.3722 Capulhuac;19.2000 João Câmara;-5.5378 Kangasala;61.4639 Tamparan;7.8790 Lower Macungie;40.5303 Tummapāla;17.7166 Oer-Erkenschwick;51.6422 Caramoran;13.9833 Cheb;50.0794 Antsohimbondrona;-13.0833 Bauko;16.9917 Rosmalen;51.7167 Windsor;51.4791 Wevelgem;50.8081 Primorsko-Akhtarsk;46.0500 Xangda;32.2056 Poconé;-16.2569 Kingman;35.2170 Pedana;16.2667 Grottaglie;40.5333 Bizen;34.7453 Września;52.3333 Massillon;40.7838 Jāsim;32.9667 Aguilares;-27.4333 Bandeirantes;-23.1100 Novovoronezh;51.3167 Dongxianpo;39.5610 Itamarandiba;-17.8569 Buyan;23.9961 Saint-Médard-en-Jalles;44.8964 Cruces;22.3419 Waiyuanshan;24.7443 Ōdachō-ōda;35.1833 Shima;39.5544 Douar Oulad Hssine;33.0680 Wallsend;54.9910 Malangas;7.6317 Pervari;37.9331 Termas de Río Hondo;-27.4833 Palmeira;-25.4289 Quintero;-32.7833 Yaguarón;-25.5621 Colíder;-10.8128 Nālchiti;22.6350 Yaese;26.1582 Radolfzell am Bodensee;47.7333 Jiming;40.1884 Mutki;38.4092 Guaratuba;-25.8828 Serdobsk;52.4667 Yuzhne;46.6300 Masatepe;11.9147 Donggou;35.5621 Quitilipi;-26.8667 Zapala;-38.9000 Dar Ould Zidouh;32.3167 Shentang;22.2915 Tongxiao;24.4833 Savage;44.7545 Sami;21.2933 Alba;44.7000 Tirebolu;41.0056 Renkum;51.9833 Partinico;38.0500 Tongkou;38.7952 San Lucas Tolimán;14.6333 Remedios;7.0275 Uglich;57.5333 Norak;38.3833 Claveria;18.6061 Stadskanaal;52.9833 Cabaiguán;22.0839 Vohilava;-21.0667 Gurupá;-1.4050 Oristano;39.9058 Bekoratsaka;-16.1000 Polillo;14.7167 Lumding;25.7500 Bahharet Oulad Ayyad;34.7702 Chūō;35.5996 Courcelles;50.4578 Itampolo;-24.6833 Kochugaon;26.5518 Pandua;23.0800 Moche;-8.1706 Asilah;35.4667 Andriba;-17.5833 Mataas Na Kahoy;13.9667 Nilka;43.7826 Sigma;11.4214 Sergio Osmeña Sr;8.3003 Deptford;39.8157 Desert Hot Springs;33.9550 Nanyuki;0.0167 Sakhnīn;32.8667 Mohyliv-Podilskyi;48.4500 Centereach;40.8681 Pichucalco;17.5167 Paipa;5.8333 Windsor;40.4690 Namegata;35.9905 Santa Rita;10.5367 Warwick;41.2597 Shahedian;37.6546 O'Fallon;38.5974 Marialva;-23.4850 Calubian;11.4467 Cañuelas;-35.0333 Komagane;35.7288 Santa Catarina Mita;14.4500 Fangyuan;23.9250 Majidpur;23.5796 I‘zāz;36.5866 Yenice;39.9308 Lālgola;24.4200 Sitges;41.2339 São Gabriel;-19.0169 Jyväskylän Maalaiskunta;62.2889 Tuzantán;15.1333 Marrero;29.8871 West Odessa;31.8389 Dreux;48.7372 Bayaguana;18.7500 Bangor;44.8323 Galapa;10.9167 Piuí;-20.4650 Bagrāmī;34.4911 Guachavés;1.2219 Baradères;18.4825 Temascaltepec de González;19.0433 Merida;10.9098 Hoyerswerda;51.4333 Tuljāpur;18.0000 Turicato;19.0500 Cacolo;-10.1333 Gongguan;24.5053 Montebelluna;45.7753 Igrejinha;-29.5739 Sasaguri;33.6239 El Oro de Hidalgo;19.8008 Ruy Barbosa;-12.2839 Katagami;39.8832 Louvain-la-Neuve;50.6667 Domingos Martins;-20.3628 São José do Egito;-7.4733 Níjar;36.9667 Touba;8.2833 Gevelsberg;51.3167 Ubajara;-3.8544 Val-d’Or;48.1000 Amatenango de la Frontera;15.5333 São Gotardo;-19.3108 Ivisan;11.5217 Buzovna;40.5161 Masi-Manimba;-4.7790 Ortaköy;38.7372 Boghni;36.5437 Pantabangan;15.8086 Newburgh;41.5531 ’Aïn Abid;36.2325 Farrokh Shahr;32.2714 Avola;36.9167 Gallipoli;40.4139 Ivaiporã;-24.2478 San Sebastián;14.5667 Owen Sound;44.5667 Al ‘Aydābī;17.2370 Velikiy Ustyug;60.7667 Needham;42.2814 Shikārpur;14.2700 Ganderkesee;53.0358 Weil am Rhein;47.5947 Clearfield;41.1030 Al ’Attawia;31.8347 Gubbio;43.3500 Kavār;29.2050 Mława;53.1167 San Giuliano Terme;43.7625 Chum Phae;16.5431 Rahīmpur;25.4894 Bocana de Paiwas;12.7878 Valkenswaard;51.3500 Springfield;38.7810 General José de San Martín;-26.5375 Rāhatgarh;23.7800 Willingboro;40.0280 Monção;-3.4919 Yıldızeli;39.8642 Jalpatagua;14.1364 Uithoorn;52.2333 Kasimov;54.9333 Jamālpur;25.4112 Fengrenxu;24.1757 Sri Mādhopur;27.4667 Fair Oaks;38.6504 Kuroishi;40.6426 Paracuru;-3.4100 Sitionuevo;10.7833 Holladay;40.6600 Haan;51.1667 Aguada de Pasajeros;22.3847 Kotovsk;52.5833 Jesús María;-30.9833 Dongmaying;39.1221 Mirnyy;62.7667 Siquirres;10.0901 El Palmar;14.6500 Balyqshy;47.0667 Maebara;35.1140 Sarno;40.8167 Syracuse;41.0859 Dania Beach;26.0593 Jerez;14.1000 Gadarpur;29.0437 Florence;38.9899 Žepče;44.4333 Navalcarnero;40.2847 Pontefract;53.6910 Irituia;-1.7689 Weyhe;52.9936 Magsingal;17.6850 Lemery;11.2333 Nueva Imperial;-38.7433 Tekkali;18.6057 President Roxas;11.4297 Chmistâr;33.9667 Tecozautla;20.5333 Exu;-7.5133 Ennery;19.4833 North Huntingdon;40.3294 Castelvetrano;37.6833 Cueto;20.6481 Tanmen;19.2429 Morro Agudo;-20.7314 Deori Khās;23.3902 Mussoorie;30.4500 Chorbog;38.6667 Ayabe;35.3000 Ouro Fino;-22.2828 Titlāgarh;20.3000 Mahemdāvād;22.8300 Kreuztal;50.9667 Frederico Westphalen;-27.3589 Inkhil;33.0000 Bābura;25.6838 Waterloo;50.7167 San Andrés Xecul;14.9000 Mae Sot;16.7131 Carballo;43.2167 Yawatahama-shi;33.4667 Mundelein;42.2693 Tredyffrin;40.0663 Nardò;40.1797 Kosonsoy;41.2500 Naugatuck;41.4890 Pasadena;39.1552 Whitstable;51.3610 South Kingstown;41.4458 Ózd;48.2192 Cicero;43.1662 Husainābād;24.5285 Santa Cruz;10.1819 Los Altos;37.3684 Zima;53.9167 Dimataling;7.5297 Sosnogorsk;63.6000 Wanghong Yidui;38.1993 Huangxicun;24.4684 Qo‘rg‘ontepa;40.7336 Changuinola;9.4300 Dasol;15.9896 Ibaraki;36.2869 Łowicz;52.1000 Puerto Wilches;7.3500 Verbania;45.9228 Okagaki;33.8536 Carlsbad;32.4010 Masqaţ;23.5889 Chinnālapatti;10.2875 Bünyan;38.8486 Aiken;33.5303 Marks;51.7167 Rheinberg;51.5467 Kostopil;50.8833 Duluth;34.0053 Laramie;41.3099 Bagac;14.5951 Westville;-29.8310 Jam;27.8236 Santa Fé do Sul;-20.2108 Tongeren;50.7794 Neira;5.1664 Halden;59.1264 Cibolo;29.5634 Savé;8.0333 Qārā;29.8833 Hitoyoshi;32.2167 Riga;26.6553 Mograne;34.4167 Netivot;31.4167 Tiquipaya;-17.3333 Stratford;43.3708 Yingyangcun;22.0974 Kaminokawa;36.4393 Monjas;14.5010 Çay;38.5926 Araçoiaba da Serra;-23.5053 Barrinha;-21.1936 Periyanāyakkanpālaiyam;11.1544 Trebinje;42.7089 Făgăraş;45.8447 Posse;-14.0928 Pulupandan;10.5167 La Verne;34.1207 Chantal;18.2000 Wenxian Chengguanzhen;32.9421 Mārgrām;24.1516 Devanhalli;13.2300 Lloydminster;53.2783 Laguna Hills;33.5918 Río Bravo;14.4011 Guamo;4.0833 Shepparton;-36.3833 Bolobo;-2.1667 Atwater;37.3529 Buey Arriba;20.1736 Newark;39.6776 Al Ḩusaynīyah;30.8617 Radcliffe;53.5615 Mamaroneck;40.9443 Pointe-Claire;45.4500 Podili;15.6040 Kudymkar;59.0167 Yahşihan;39.8503 Burgdorf;52.4500 Vinkovci;45.2911 Eyvān;33.8272 Shrīgonda;18.6160 Buenavista;13.7394 Ngudu;-2.9667 Cândido Mota;-22.7464 Cuauhtémoc;19.3281 Araguatins;-5.6508 Ostuni;40.7333 Caracal;44.1125 Kandalaksha;67.1569 Manduria;40.4000 Yaita;36.8067 Parambu;-6.2108 Duanshan;25.7943 Vienne;45.5242 Iporá;-16.4419 Siraway;7.5853 Yby Yaú;-22.9631 Mānsa;23.4300 Bittou;11.2575 Palaiya Āyakkudi;10.4560 Rosignano Marittimo;43.4000 Guanhães;-18.7750 Karera;25.4581 Maryville;35.7468 Culleredo;43.2883 Gata;7.8500 Pindaré-Mirim;-3.6078 Taozhuangcun;30.9694 Vitória do Mearim;-3.4619 Perico;22.7753 Norwood;42.1861 Coria del Río;37.2833 San Antonio Ilotenango;15.0544 Maināguri;26.5640 Burgess Hill;50.9535 Bielsk Podlaski;52.7667 Shawnee;35.3531 Cambita Garabitos;18.4500 Brasília de Minas;-16.2078 Paracelis;17.1811 Khānah Sūr;36.4731 San Juan Ixcoy;15.6000 Lobos;-35.1833 Yingshouyingzi;40.5451 Lake Magdalene;28.0875 Ambatomiady;-19.6833 Marigliano;40.9333 Maubeuge;50.2775 Alamogordo;32.8837 Malakanagiri;18.3500 Sakaraha;-22.9167 Danghara;38.0983 Eshtehārd;35.7239 Lagos;37.1028 Saratoga;37.2684 San Narciso;15.0167 Lushnjë;40.9333 Bogoroditsk;53.7667 Varto;39.1731 Flores da Cunha;-29.0289 Werl;51.5500 Orillia;44.6000 Minaçu;-13.5328 Łuków;51.9272 Chitral;35.8511 Sibanicú;21.2389 Sabana Grande de Boyá;18.9500 Tewksbury;42.6120 Tokmak;47.2514 North Royalton;41.3138 Puthupalli;9.5594 Sovetskiy;61.3614 Kalgoorlie;-30.7489 Ski;59.7419 Espinosa;-14.9081 Touros;-5.1989 Hadagalli;15.0200 Nantan;35.1000 Lakeside;30.1356 Benguema;8.3333 Lavras da Mangabeira;-6.7528 Kūttānallūr;10.7069 Tairan Camp;6.6500 Sakhipur;24.3167 Portão;-29.7019 Bembèrèkè;10.2250 Vohitrandriana;-20.7500 Neuruppin;52.9331 Livingston;40.7855 Egra;21.9000 Itabela;-16.5750 Illescas;40.1167 Muritiba;-12.6258 Oak Ridge;35.9639 Tāramangalam;11.7000 Best;51.5167 Mānāvadar;21.5000 Tijucas;-27.2408 Schönebeck;52.0167 Guanta;10.2383 Nicholasville;37.8906 Marar;25.5392 Əmircan;40.4264 Leusden;52.1333 Chagne;10.9500 New Bern;35.0955 San Miguel;10.7833 LaGrange;33.0274 Itapemirim;-21.0108 Bochnia;49.9833 Andranovory;-23.1333 Canguaretama;-6.3800 Matões;-5.5189 Ambanja;-13.6786 Omaezaki;34.6379 Chengam;12.3112 Săcele;45.6200 Parsuram;23.2135 Kamisato;36.2516 Arcos de la Frontera;36.7500 Inami;34.7488 Dyurtyuli;55.4833 Nova Russas;-4.7000 Xingang;23.5600 Mocímboa da Praia;-11.3500 Nazaré da Mata;-7.7419 Balatan;13.3167 Ballwin;38.5950 Santa Vitória do Palmar;-33.5189 La Paz;8.2801 Dolo Odo;4.1667 Cleburne;32.3568 Nartkala;43.5500 Warrnambool;-38.3833 João Alfredo;-7.8558 San Isidro;11.4167 Niles;42.0278 Bel Air North;39.5543 Rostov;57.1833 Einbeck;51.8167 Salou;41.0796 Westfield;40.6516 Tanguá;-22.7300 Taiobeiras;-15.8078 Mossendjo;-2.9500 Orcutt;34.8691 Żywiec;49.6892 Kanzakimachi-kanzaki;33.3167 Cornelius;35.4724 Tunduru;-11.0667 Ganassi;7.8269 Fabriano;43.3333 Lohmar;50.8167 Pichanal;-23.3167 Bela Cruz;-3.0508 Bannūr;12.3329 Alma;48.5500 Aglipay;16.4889 Gamu;17.0500 Cabrobó;-8.5119 Devarshola;11.5437 Tsuruno;40.8087 Colinas do Tocantins;-8.0589 Rāghopur;26.1785 Manāwar;22.2300 Uran;18.8900 Leh;34.1642 SeaTac;47.4444 Beverley;53.8450 Vicência;-7.6569 Voorhees;39.8450 San Gregorio de Nigua;18.3833 Garner;35.6936 Tepetlaoxtoc;19.5731 Písek;49.3089 Unterschleißheim;48.2833 Sarayköy;37.9265 Ennepetal;51.2833 Río Blanco;12.9325 Capela;-10.5028 Myszków;50.5833 Gurnee;42.3708 Anilao;10.9785 West Warwick;41.6986 Laranjeiras do Sul;-25.4078 Qahderījān;32.5767 Jonuta;18.0333 Minas Novas;-17.2189 Opelika;32.6612 Novoyavorovskoye;49.9311 Alegre;-20.7639 Rasrā;25.8500 Keles;41.4033 Estrêla;-29.5019 Hajdúböszörmény;47.6667 Hopkinsville;36.8381 Jarinu;-23.1014 Tekeli;44.8300 Guaíra;-24.0800 Colgong;25.2633 San Ramón;12.9236 Southlake;32.9545 Duanzhuang;36.5796 Synelnykove;48.3178 Novo Cruzeiro;-17.4678 Xinzhaidian;37.8136 Treviglio;45.5214 Catolé do Rocha;-6.3439 Amagá;6.0500 Orangeville;43.9167 Nanwucun;37.3885 Toribío;2.9581 Emmen;47.0833 Olopa;14.6833 Princeton;40.3562 Bowling Green;41.3776 Fort Erie;42.9167 Sāgwāra;23.6681 Madruga;22.9164 Pápa;47.3237 Anse-à-Foleur;19.9000 Juquitiba;-23.9319 Ciudad Melchor de Mencos;17.0667 Andernach;50.4397 Rio Branco do Sul;-25.1900 Milford Mill;39.3444 Fārsān;32.2553 Tirwa;26.9633 Gobernador Virasora;-28.0500 Yanai;33.9667 Yunoshima;35.8059 To‘raqo‘rg‘on;41.0000 Taunusstein;50.1333 Phú Mỹ;10.5906 North Andover;42.6713 Bielawa;50.7000 Banamba;13.5500 Aungban;20.6667 Thung Song;8.1669 Torrijos;13.3167 Osterholz-Scharmbeck;53.2167 Villa Donato Guerra;19.3083 Canyon Lake;29.8761 Holbæk;55.7156 Kuttuparamba;11.8300 Décines-Charpieu;45.7694 Suzak;40.8981 Victoria;-32.6167 Anjad;22.0417 Thuận Tiến;10.0894 San Andrés del Rabanedo;42.6167 North Chicago;42.3172 Wuyuan;41.0896 Mont-de-Marsan;43.8900 Rapallo;44.3500 Chimākurti;15.5819 Middle River;39.3436 Founougo;11.4808 Sokuluk;42.8600 Drexel Heights;32.1453 Motomiya;37.5132 Uzynaghash;43.2297 Alcázar de San Juan;39.4056 Sardrūd;38.0286 North Tonawanda;43.0457 Puerto de la Cruz;28.4167 Pokhrām;25.9358 Rehli;23.6300 Kirov;54.0833 Draveil;48.6852 Blagodarnyy;45.1000 Metu;8.3000 Gorna Oryahovitsa;43.1333 Shāhpura;25.6300 Kailahun;8.2772 G’ijduvon Shahri;40.1000 Rio Preto da Eva;-2.6989 Asprópyrgos;38.0667 Nanyō;38.0551 Hellín;38.5167 Calafell;41.2004 Ferry Pass;30.5205 Ob;54.9917 Gaggenau;48.8039 Ar Ruḩaybah;33.7436 Askøy;60.4667 Quillabamba;-12.8681 Bad Hersfeld;50.8683 Xinpo;19.7738 Jima Abajo;19.1300 Lalla Mimouna;34.8500 El Fanar;33.8667 Mukumbura;-16.2000 Newington;41.6870 Pantao-Ragat;8.0500 Feteşti;44.4150 Afonso Cláudio;-20.0739 Ono;35.9797 Motema;8.6167 Walsrode;52.8667 Tchindjendje;-12.8167 Santa Maria;17.3719 Beşiri;37.9210 Slavgorod;52.9833 Maguing;7.9000 Kórinthos;37.9386 Tskhinvali;42.2257 Itaporanga d’Ajuda;-10.9978 Bandar-e Lengeh;26.5581 Ash Shinān;27.1782 Aarschot;50.9842 Ypacaraí;-25.3833 Kalpatta;11.6088 Ust’-Dzheguta;44.0872 Friedberg;48.3500 Sali;14.4383 Bālā Kōh;36.5753 Horki;54.2667 Balungao;15.9000 Hirakawachō;40.5841 Mahugaon;22.5748 Togitsu;32.8289 Candijay;9.8180 Iguape;-24.7081 Dalin;23.5989 Huanta;-12.9397 Télimélé;10.9050 Granger;41.7374 Adilcevaz;38.8058 Morristown;36.2043 Weatherford;32.7536 Tsuru;35.5515 Pop;40.8736 Koratgi;15.6081 Dumalag;11.3039 Carlos Barbosa;-29.2978 Kafr Nubl;35.6139 Myōkō;37.0252 Kālappatti;11.0794 Madhepur;26.1775 Abū Şuwayr;30.5633 Villa Comaltitlán;15.1667 Pittsford;43.0733 Rotterdam;42.8133 Al Madad;13.7167 Candelaria;15.6333 Graneros;-34.0647 Bretten;49.0364 Balete;11.5553 Shetang;34.5568 Daheba;28.0259 Iguig;17.7517 Northport;33.2586 Galdácano;43.2306 Chimichagua;9.2500 Salay;8.8667 Garaimāri;24.0217 Anse à Pitre;18.0500 Souk et Tnine Jorf el Mellah;34.4833 Kondopoga;62.2000 Talwāra;31.9376 Jāmtāra;23.9500 Lawrenceville;33.9523 Mannārgudi;11.2761 Galesburg;40.9506 Pānakkudi;8.3492 Xunjiansi;23.9620 Māgadi;12.9700 Las Nieves;8.7351 Wallkill;41.4854 Kibiti;-7.7296 Gukeng;23.6500 Cihuatlán;19.2500 Dauin;9.2000 Kuah;6.3167 Valdepeñas;38.7667 Gūdalūr;11.1455 Kombissiri;12.0667 Vinces;-1.5500 Dayr Abū Sa‘īd;32.5025 Jinka;5.7833 Navāpur;21.1700 Calintaan;12.5756 Temse;51.1167 Friedberg;50.3333 Quezaltepeque;14.6333 Salāya;22.3200 Augustów;53.8436 Lewe;19.6333 Zionsville;39.9897 Matsubushi;35.9258 Gorodets;56.6500 Socorro;6.5333 Budaörs;47.4607 Highland Park;42.1823 Trutnov;50.5606 Padada;6.6333 Sue;33.5872 Petite Rivière de Nippes;18.4833 Pinhão;-25.6958 Bergen;52.6667 Piagapo;8.0000 Mozhaysk;55.5167 Shangpa;26.9052 ‘Aynkāwah;36.2292 Qarataū;43.1667 Candelária;-29.6689 Yoshida;34.7709 Marogong;7.6667 Zaragoza;14.6498 Rendsburg;54.3000 Neuburg;48.7333 Tameslouht;31.5000 Kottūru;14.8262 Northfleet;51.4400 Hafnarfjörður;64.0200 Dahana;38.0583 Agde;43.3108 LaSalle;42.2167 Austintown;41.0932 Makato;11.7120 Greenville;33.3850 Plympton;50.3860 Birūr;13.5972 Tizi Gheniff;36.5891 Crevillente;38.2486 Ikoto;4.0783 El Kseur;36.6844 Salem;42.7902 Les Palmes;18.3333 Guotang;23.8414 Taşova;40.7500 Paraipaba;-3.4389 Bra;44.7000 Planadas;3.1964 Milford;42.1565 Mathba;22.3000 Liberty;39.2394 Miramar;-38.2667 Tabio;4.9158 Brodnica;53.2597 Xiaba;27.8825 Mucari;-9.4667 Mīnūdasht;37.2289 Agno;16.1161 Sottaiyampālaiyam;11.4053 Conguaco;14.0470 Du Yar;15.2617 L’Arbaa Naït Irathen;36.6367 Motala;58.5333 Krishnarājpet;12.6662 Sainte-Julie;45.5833 Shibancun;22.1539 Barbosa;5.9330 Placer;9.6570 Hakmana;6.0796 Landhaura;29.8200 Bontoc;10.3500 Shubrākhīt;31.0275 Ragan Sur;17.3167 Yong’ancun;23.1788 Nattam;10.2249 Pervomaiskyi;49.3869 Bugarama;-2.6972 San Roque;12.5330 Condega;13.3617 Mundi;22.0700 Selargius;39.2537 São Luís de Montes Belos;-16.5250 Soledade;-28.8178 Heesch;51.7333 Sheohar;26.5200 Uychi;41.0294 Ambalavao;-21.8333 Nelson;53.8346 Tacuba;13.9000 Meschede;51.3500 Waltrop;51.6167 Irará;-12.0500 Villa Regina;-39.1000 Culemborg;51.9500 Saint-Étienne-du-Rouvray;49.3786 Hengchun;22.0000 Gongyefu;41.8378 Chikuzen;33.4570 Malème Hodar;14.0883 Werne an der Lippe;51.6667 Saalfeld;50.6500 Campo Alegre de Lourdes;-9.5158 Canosa di Puglia;41.2167 Jalawlā’;34.2719 Nan’ao;38.5162 Ambinanitelo;-15.3500 Ibrāhīmpatnam;16.6056 Degeh Bur;8.2167 Devarkonda;16.6919 Sāho;25.9718 Andranomanelatra;-19.7833 Casma;-9.4742 Ārda;32.3087 Sanford;35.4875 Santa Rita;14.8667 Buenavista;10.0833 Merzig;49.4500 Bourgoin-Jallieu;45.5861 Monterey;36.5919 Pitanga;-24.7569 Uar Esgudud;1.2667 Baxdo;5.7889 Sabanilla;17.3167 Carpentras;44.0558 Bourèm Guindou;16.9004 Tsitondroina;-21.3000 Asipovichy;53.2933 Evaz;27.7600 Vernon;41.8364 East Lake-Orient Park;27.9970 Pitou;23.8775 Kelkheim (Taunus);50.1378 San Lucas Sacatepéquez;14.6095 Leduc;53.2594 Balabagan;7.5333 Dala;-11.0342 Sint-Michielsgestel;51.6433 Southgate;42.2047 Murlīganj;25.9000 Hengshuicun;22.3674 Arlon;49.6833 Eastern Goleta Valley;34.4448 Pattikonda;15.4000 Santana do Acaraú;-3.4608 Izegem;50.9172 Esmeralda;21.8561 Tönisvorst;51.3208 Carinhanha;-14.3050 Vyshhorod;50.5833 Giannitsá;40.7833 Allūru;15.1317 Pignon;19.3333 Paripiranga;-10.6878 Oosterend;53.0036 Puente-Genil;37.3833 Fresno;5.1556 Schwelm;51.2667 San Jacinto;12.5683 Santiago de Baney;3.7000 Zamboanguita;9.1000 Benito Soliven;16.9833 Dzyarzhynsk;53.6833 General Alvear;-34.9667 Casa Branca;-21.7739 San Rafael;20.1889 Ancón;8.9700 Meißen;51.1667 Mandalī;33.7333 Inagawa;34.8951 Zhangguzhuang;38.0584 Chamblee;33.8842 Raytown;38.9944 Villa Dolores;-31.9333 Leonding;48.2792 Pārdi;20.5200 Xalatlaco;19.1811 Aral;46.7833 Tequisquiapan;20.5206 Villa Gesell;-37.2556 Mocajuba;-2.5839 Nepānagar;21.4558 Carney;39.4050 San Luis;15.7167 Algonquin;42.1629 Changzhi;22.6833 Kınık;39.0939 Savanette;18.6833 Vaihingen an der Enz;48.9328 N’Gaous;35.5550 Capim;-1.6750 Lākheri;25.6700 Heywood;53.5900 Comiso;36.9500 Olocuilta;13.5667 Kabalo;-6.0500 Hosdurga;13.7963 Landsberg;48.0478 Purificación;3.8667 Dedovsk;55.8500 Rota;36.6167 Mbanga;4.5092 Nauhata;25.9971 Rietberg;51.8000 Mahón;39.8894 Antsampandrano;-19.9167 Pasuquin;18.3342 Nanga Eboko;4.6708 Bella Vista;36.4667 Vadakku Valliyūr;8.3833 Swarzędz;52.4083 Pilón;19.9053 Gloucester;42.6260 Hattersheim;50.0722 Crofton;39.0144 Jāmai;22.1964 Petrich;41.3953 Rhennouch;33.9300 Misilmeri;38.0333 Igarapava;-20.0383 Parabiago;45.5583 Flandes;4.2833 Manjo;4.8500 Artëmovskiy;57.3564 Alushta;44.6672 Rancharia;-22.2289 Brecht;51.3500 Zaltbommel;51.8000 Santiago do Cacém;38.0167 Spinea;45.4931 Ban Bang Khu Wat;13.9576 Atascadero;35.4827 Alquízar;22.8067 Calimete;22.5339 Bonou;6.9000 Shuangtian;22.8640 Carmo do Paranaíba;-19.0008 Mangdongshan;24.1169 Labasa;-16.4311 Redondela;42.2833 Central;30.5593 Chiknāyakanhalli;13.4161 Mashan;37.0017 Bayramiç;39.8128 Atbasar;51.8000 Xinmin;25.4831 Vattalkundu;10.1630 Piraju;-23.1936 Kitaakita;40.2260 Vallentuna;59.5333 Asha;55.0000 Bosanska Krupa;44.8833 Ozumba;19.0392 Manticao;8.4042 Paraparaumu;-40.9167 Licab;15.5400 Brunssum;50.9500 South Ubian;5.1833 Toksun;42.7918 Sumilao;8.3281 Matinhos;-25.8178 San Agustín Chahal;15.7500 North Cowichan;48.8236 Gotse Delchev;41.5667 Nowy Dwór Mazowiecki;52.4333 New Smyrna Beach;29.0249 Kumalarang;7.7478 Winter Park;28.5989 Arniquet;18.1500 Orchard Park;42.7516 Eagle;43.7223 Tournefeuille;43.5853 Banning;33.9460 Bāglung;28.2667 Albany;-35.0228 Dembī Dolo;8.5333 Güstrow;53.7939 Ātmakūr;14.6167 Kottakota;13.6573 Lavezares;12.5333 Juchitepec;19.0997 Uttamapālaiyam;9.8000 Yangshuling;40.9942 Antsahalava;-19.5833 Oelde;51.8333 Mūdbidri;12.9101 Riesa;51.3081 Lādwa;29.9958 East Windsor;40.2606 Poás;10.1060 Sevenoaks;51.2781 Königsbrunn;48.2689 Sonāmukhi;23.3000 Khunti;23.0140 Garfield Heights;41.4199 Winnenden;48.8764 Bad Zwischenahn;53.1836 Mahendragarh;28.2800 Santo Domingo;17.6414 Figuil;9.7667 Udaipur;27.7289 Garchitorena;13.8833 Johnston;41.8274 Chichaoua;31.5333 Budaka;1.0167 Nakrekal;17.1667 Ja‘ār;13.2231 Danville;40.1426 Takanezawa;36.6310 Burton;42.9974 McCandless;40.5836 Riihimäki;60.7333 Nazaré;-13.0350 Pueblo Nuevo Viñas;14.2333 Hobart;41.5140 Buenos Aires;9.1985 Fleming Island;30.0988 Fountain;38.6886 Texarkana;33.4360 Schwandorf;49.3283 Munsan;37.8529 Oslob;9.5500 Monfalcone;45.8000 Fritissa;33.6167 Nieuwkoop;52.1833 Orlová;49.8452 Tōmi;36.3594 Desenzano del Garda;45.4689 Maddagiri;13.6600 Hisor;38.5264 Xiaobazi;27.3401 Brāhmana Periya Agrahāram;11.3690 Timizart;36.8000 East Fishkill;41.5567 Rösrath;50.9000 Likino-Dulevo;55.7167 Reddish;53.4383 Ans;50.6625 Poggibonsi;43.4667 Bulung’ur Shahri;39.7600 Phuntsholing;26.8500 Kavajë;41.1842 Vinaroz;40.4686 Windsor;41.8710 Fridley;45.0841 Gyula;46.6500 Kiskunfélegyháza;46.7052 Matthews;35.1195 Kamalāpuram;15.3044 Capim Grosso;-11.3808 Lakewood Ranch;27.4185 Lāmerd;27.3423 Parsa;26.0021 Ath;50.6167 Sexmoan;14.9333 Kostomuksha;64.5833 Jacksonville;34.8807 Ra’s al ‘Ayn;36.8503 Schererville;41.4861 Sopó;4.9167 Northampton;42.3266 Şūrān;35.2897 Benhao;18.6122 Shibushi;31.5000 Erie;40.0404 Wellesley;42.3043 Fitchburg;42.9859 Pālpā;27.8667 Agui;34.9329 Quivicán;22.8247 Prainha;-1.8000 Matouying;39.2922 Hazleton;40.9505 Novaya Usman’;51.6439 Kailāras;26.3050 Paragould;36.0555 Dapa;9.7578 Boxmeer;51.6500 Hinunangan;10.4000 Gragnano;40.6957 Rāikot;30.6500 Divnogorsk;55.9594 Mondragone;41.1000 Glenville;42.8869 Carmona;37.4667 Orange;44.1383 Capitán Bermúdez;-32.8167 Northeim;51.7067 Namaacha;-25.9667 Khmilnyk;49.5569 Plaine du Nord;19.6833 Duzhuang;40.0121 Poona-Piagapo;8.0833 Brétigny-sur-Orge;48.6114 Floresta;-8.6008 Seguin;29.5891 Blankenfelde;52.3500 Anadia;40.4333 Bühl;48.6953 Moñitos;9.2503 Bethany;45.5614 Carmagnola;44.8500 Truskavets;49.2806 San Marcos;11.9081 Jimenez;8.3333 Kaminoyama;38.1496 Lugus;5.7000 Cañada de Gómez;-32.8167 Shiqiao;34.1418 Fundão;40.1333 Viacha;-16.6533 Shotley Bridge;54.8700 Ikaruga;34.6088 Fandriana;-20.2333 Tabuleiro do Norte;-5.2481 Amarpur;25.0397 Prado;-17.3408 Deodrug;16.4167 Kirkwood;38.5788 Sherobod;37.6667 Winterswijk;51.9667 Garupá;-27.4833 Bethlehem;31.7031 Aipe;3.2167 Patjirwa;26.8084 West Windsor;40.2897 Cottica;3.8542 Ciying;25.3595 Santa Quitéria do Maranhão;-3.5158 Falkenberg;56.9053 Nalbāri;26.4450 Shaker Heights;41.4744 Reinbek;53.5089 Pontedera;43.6625 Aleysk;52.5000 Ituberá;-13.7319 Goriar;25.5513 Oktyabr’sk;49.4556 Kakiri;0.4200 La Concordia;-0.0067 Sant’Anastasia;40.8667 Perry Hall;39.4068 Evesham;52.0920 Primero de Enero;21.9453 Gonglang;24.8391 San Cristóbal;22.7169 Braço do Norte;-28.2750 Reda;54.6167 Glória do Goitá;-7.9992 Kodumur;15.6833 Cyangugu;-2.4833 Bogdanovich;56.7803 Catandica;-18.0564 Madīnat Zāyid;23.6522 Springe;52.2167 Laplace;30.0731 Magna;40.7634 Kōshū;35.7043 Ban Phai;16.0730 Gates;43.1514 Old Harbour;17.9333 Laranjal Paulista;-23.0118 Pawāyan;28.0667 Chambly;45.4311 Baixo Guandu;-19.5189 Tabuelan;10.8500 Rio Pardo de Minas;-15.6100 Narsimlāpet;17.5100 Vestal;42.0492 Ixtapa;16.8167 Nāhan;30.5500 Nirasaki;35.7089 Chili;43.0845 Ponot;8.4428 Bella Vista;-28.4667 Benavente;38.9667 Mirandópolis;-21.1336 Dalfsen;52.5000 Babatngon;11.4207 Itaperuçu;-25.2200 Bagre;-1.9000 Kūdligi;14.9050 Somotillo;13.0447 Candelaria;28.3547 La Dorada;0.3436 San Manuel;15.7975 Alatri;41.7250 Dongwang;38.3242 Gagarin;55.5500 Russellville;35.2762 Ampatuan;6.8348 Tocache Nuevo;-8.1889 San Javier;-35.5924 Roseaux;18.6000 Cervia;44.2586 Ofaqim;31.3167 Śrem;52.0886 Dhabauli;25.8346 Limoeiro de Anadia;-9.7414 Emmendingen;48.1214 Stoughton;42.1192 Tanglou;22.1888 Langedijk;52.6936 Thabazimbi;-24.6000 Cajati;-24.7361 Hendījān;30.2364 Tondela;40.5167 Lake in the Hills;42.1913 Sortöbe;42.8600 Tudiyalūr;11.0816 La Calera;4.7531 Höxter;51.7667 Germī;39.0297 Baleno;12.4739 Nivelles;50.5833 Macau;-5.1150 Rosa Zarate;0.3300 Dīnānagar;32.1500 Mabini;9.8650 Xizhou;24.1156 Toqsu;41.5417 Vihti;60.4051 Hanawa;40.2158 Jauja;-11.7750 Krotoszyn;51.6833 Caicedonia;4.3333 Tetela de Ocampo;19.8167 Las Rosas;16.3667 Barabinsk;55.3500 Nava;28.4214 Acatenango;14.5544 Pucón;-39.2767 Hirado;33.3667 Sébikhotane;14.7469 Pilar;9.8333 Chahe;27.1746 Lansing;41.5648 Queensbury;43.3568 Heshancun;30.6344 Petrovsk;52.3167 Itatiaia;-22.4914 Whitehall;40.6571 Balakliia;49.4564 Srungavarapukota;18.1167 Kroměříž;49.2989 Cifuentes;22.6208 Quakers Hill;-33.7344 Zhonghechang;27.8886 Henstedt-Ulzburg;53.7833 Borzya;50.3833 Kuzhittura;8.3165 Tudela;8.2472 Rehoboth;-23.3167 Novyi Rozdil;49.4703 Okotoks;50.7250 Bāgha Purāna;30.6881 Takahashi;34.7914 Shangluhu;23.2538 Asago;35.3333 Mateare;12.2356 Carapó;-22.6339 Tibiao;11.2892 Stanford le Hope;51.5140 Summerlin South;36.1242 Laoaoba;26.8377 Goio-Erê;-24.1850 Ogawa;36.0567 Bentley;53.5510 Herentals;51.1833 Shāhpura;27.3897 Sønderborg;54.9138 Balzar;-1.3600 Nongstoin;25.5200 Oyabe;36.6755 New Iberia;30.0049 Harelbeke;50.8567 Losal;27.4000 Lepe;37.2542 Zhongtai;35.0675 Pijnacker;52.0167 Santa Rita de Cássia;-11.0089 Prosper;33.2394 Heerenveen;52.9500 Morrisville;35.8368 Andoharanomaitso;-21.4667 Kalwākurti;16.6500 Jamestown;42.0976 Mount Olive;40.8662 West Springfield;42.1253 Oakleaf Plantation;30.1689 Ambatolampy;-19.3861 Jeffersontown;38.2049 Bānswāda;18.3833 Bussy-Saint-Georges;48.8422 Pires do Rio;-17.3008 Ibaiti;-23.8489 Zhailuo;26.8794 Águas Santas;41.2090 San Salvador;20.2833 Yangiyer;40.2667 Leichlingen;51.1167 Amaliáda;37.8000 Esparza;9.9959 Fichē;9.8000 Cecina;43.3167 Oulad Yaïch;32.4167 Taza;35.0639 Malmal;26.4833 Cheshire;41.5113 Rajpur;22.3053 Santiago;9.2654 Pitoa;9.3833 Zeitz;51.0478 Hatonuevo;11.0694 Propriá;-10.2111 Casselberry;28.6624 Naranjito;-2.1667 Nueva Concepción;14.1333 Alagoa Grande;-7.0822 Hājipur;31.9771 Idar-Oberstein;49.7114 Ḩarīr;36.5517 Vittorio Veneto;45.9833 Mahārājpur;25.0194 Novi Ligure;44.7592 Toucheng;24.8500 Madison Heights;42.5073 Karakoçan;38.9500 Extrema;-22.8550 Agawam;42.0657 Passira;-7.9950 Palotina;-24.2839 Qincun;37.8360 Griesheim;49.8639 Jujutla;13.7833 Basay;9.4167 Matihāni;25.3595 Maracanã;-0.7650 Gyöngyös;47.7833 Ferdows;34.0186 Saint Bernard;10.2833 Harūr;12.0510 Douar Oulad Aj-jabri;32.2567 Doña Remedios Trinidad;15.0000 Witney;51.7800 Saito;32.1167 Taungup;18.8500 Umargām;20.1700 Pāvugada;14.1000 Quijingue;-10.7528 Mehlville;38.5018 Olching;48.2000 Byaroza;52.5500 Aioi;34.8000 Geislingen an der Steige;48.6244 Bihpur;25.3889 San Carlos;8.8006 Walnut;34.0334 Gevaş;38.2978 Teltow;52.4022 Pazarcık;37.4860 Fenoarivobe;-18.4497 Valencia;9.6097 İhsaniye;39.0333 Baunatal;51.2589 Harrison;41.0236 Mampikony;-16.0917 Kartaly;53.0500 Isnos;1.9333 Magdalena;14.2000 Baishaling;24.0950 Pokhvistnevo;53.6500 Monroeville;40.4262 Oeiras do Pará;-2.0028 Dubăsari;47.2667 Cabricán;15.0768 Santo Niño;17.8861 Rūdehen;35.7378 Naranjal;-2.6728 Tarāna;23.3300 Lushoto;-4.7928 El Copey;10.1500 Kpandu;7.0000 McDonough;33.4397 Lacey;39.8564 Izu;34.9765 Stockbridge;33.5254 Villaviciosa de Odón;40.3583 Baradero;-33.8000 Arivonimamo;-19.0333 Lillehammer;61.1167 Tha Yang;12.9658 Azángaro;-14.9099 Giarre;37.7333 Ilave;-16.0836 Wegberg;51.1417 Kobyłka;52.3333 Mechernich;50.6000 Sept-Îles;50.2167 Mūvattupula;9.9798 Lučenec;48.3286 Triggiano;41.0667 Bamba;17.0333 Gingee;12.2528 Barão de Cocais;-19.9458 Seaford;50.7700 Saugus;42.4681 Oulad Hammou;33.2499 Arari;-3.4539 Venezuela;21.7511 Carauari;-4.8828 Glen Ellyn;41.8666 Villa Allende;-31.3000 Perumbalam;10.8000 Assisi;43.0758 Longaví;-35.9667 Biłgoraj;50.5500 Aberdeen;45.4649 Tsushima;34.2000 Imbituva;-25.2300 Stafford;39.7049 Higashikagawa;34.2500 Seseña;40.1036 Líšeň;49.2075 Coyaima;3.8333 Bonito Oriental;15.7333 Mpika;-11.8300 Rāisinghnagar;29.5342 Gorlice;49.6547 Kolambugan;8.1144 Chortkiv;49.0167 Datu Piang;7.0178 Karanjiā;21.7626 Conceição da Barra;-18.5928 Circasia;4.6167 Bom Jesus;-4.4200 Kotagiri;11.4167 Serra Negra;-22.6119 Carbonia;39.1672 Mogliano Veneto;45.5619 Guararema;-23.4150 Rāmanayyapeta;17.3203 Almus;40.3748 Yemanzhelinsk;54.7667 Luís Correia;-2.8789 Taiwa;38.4373 Allen Park;42.2595 Raseborg;59.9750 AshShajarah;32.6417 Alotenango;14.4878 Huanghuajing;24.1989 Manaquiri;-3.4281 Brownsburg;39.8337 Ciro Redondo;22.0189 Maner;25.6500 Jeffrey’s Bay;-34.0333 Pongoz;40.7500 Warin Chamrap;15.2008 Pershotravensk;48.3464 Ar Ruţbah;33.0381 San Giovanni in Persiceto;44.6408 Altınova;40.6975 Gaotan;32.3227 Pādiyanallūr;13.2004 Reina Mercedes Viejo;16.9872 Tangutūru;15.3400 Libacao;11.4833 Ban Chang;12.7208 Milton;42.2412 Sighişoara;46.2169 Toli;45.9313 Bermejo;-22.7322 Bacoli;40.8000 Orchards;45.6890 Sāngola;17.4378 Eagle Pass;28.7125 Lagdo;9.0500 Bārughutu;23.8038 Benicarló;40.4167 Pedras de Fogo;-7.4019 Olindina;-11.3669 Talusan;7.4263 Pilar do Sul;-23.8128 Khust;48.1814 Guamal;9.1472 Bridgewater;41.9728 Baesweiler;50.9000 Samayac;14.5821 Shakīso;5.7500 Mollendo;-17.0231 Djugu;1.9184 Três Marias;-18.2058 Forest Hills;42.9577 Leimen;49.3481 Bakhor;38.5500 Spring;40.3038 Gusev;54.5833 Oltinko‘l;43.0758 Grimma;51.2386 Illkirch-Graffenstaden;48.5300 Wetter (Ruhr);51.3881 Esztergom;47.7856 Arerāj;26.5527 Bellaa;30.0314 Piešťany;48.5842 Kevelaer;51.5833 Belpasso;37.5833 Majayjay;14.1463 Vakfıkebir;41.0475 Svendborg;55.0594 Mainit;9.5369 Ajka;47.1006 Cabangan;15.1333 Alga;49.9028 Safājā;26.7333 Åkersberga;59.4833 Hiji;33.3694 Obama;35.4957 Zacoalco de Torres;20.2333 Independence;38.9510 Cuartero;11.3428 Bad Neuenahr-Ahrweiler;50.5447 Dar Bel Hamri;34.1889 Santa Cruz Cabrália;-16.2778 Bardaskan;35.2608 Enterprise;31.3275 Nainijor;25.6811 Nunspeet;52.3833 Čapljina;43.1118 Branford;41.2843 Caucete;-31.6500 Tuku;23.6911 Çiftlik;38.1667 Navahrudak;53.5833 San Miniato;43.6833 Kłodzko;50.4333 Almazora;39.9403 Rapperswil-Jona;47.2167 Qujingpu;38.0814 Honchō;41.8957 Āron;24.3811 Henderson;37.8397 Guma;37.6168 Kushtagi;15.7562 Orvault;47.2717 Centre Wellington;43.7000 Bo‘ka;40.8136 Babar;35.1692 Glen Cove;40.8709 Shuiding;44.0500 Dajabón;19.5667 Arqalyq;50.2481 Geilenkirchen;50.9653 Aloran;8.4146 Oppegård;59.7925 Khrestivka;48.1464 Mima;34.0500 Szentendre;47.7044 Ipubi;-7.6519 San Felipe Jalapa de Díaz;18.0716 La Paz Centro;12.3397 Sandrandahy;-20.3500 Tsinjoarivo;-19.6333 Llallagua;-18.4167 Vadnagar;23.7850 Ambohijanahary;-17.4000 Bayang;7.7930 Maryland Heights;38.7189 Talisay;14.1356 Greenville;33.1116 Pô;11.1667 Moulay Bousselham;34.8786 Langdu;23.3129 Shaler;40.5229 Imito;-20.4167 Yangyuhe;33.8614 Guaimaca;14.5333 Blerick;51.3667 Peniche;39.3500 Nakhal;23.3833 Tainai;38.0597 Sahavato;-20.6000 Nallıhan;40.1836 Koewarasan;5.7697 Hazar;39.4450 Khulm;36.6833 Tara;56.8833 Xochistlahuaca;16.7914 Bixby;35.9454 Mādhura;26.3388 Neptune;40.2105 Kushva;58.2833 Citong;23.7729 Lathrop;37.8090 New Milford;41.6042 Aşağı Göycəli;41.1158 Villazón;-22.0911 Vícar;36.8317 Ahmadābād;25.3019 Shirley;40.7936 Lincoln;-34.8500 Tehuipango;18.5167 East Haven;41.2988 Stratford-upon-Avon;52.1928 Yahaba;39.6060 Bujaru;-1.5150 Sorochinsk;52.4167 Sueca;39.2026 Kakuda;37.9770 Dame-Marie;18.5667 Khātegaon;22.5957 San Pedro del Pinatar;37.8167 San Andrés Sajcabajá;15.1756 La Oliva;28.6167 Muchun;39.8833 Garden City;37.9753 Gameleira;-8.5844 Oakdale;44.9876 Anajás;-0.9869 Varandarapilli;10.4167 Timbiras;-4.2550 Uchturpan;41.2136 Kasangulu;-4.5911 Punceres;9.9661 Charkhāri;25.4000 South Laurel;39.0603 Dois Irmãos;-29.5800 Quiroga;19.6638 Careiro da Várzea;-3.2208 Pachor;23.7098 Tsinjoarivo;-18.9333 Ridgecrest;35.6308 Kriens;47.0333 Hikawa;35.3929 Ampasimanolotra;-18.8194 Kupiansk;49.7064 Chíos;38.3725 Pedernales;18.0333 San Isidro;12.3880 Huitiupán;17.2333 Niuchangqiao;26.6247 San Antonio del Monte;13.7167 Bouka;10.2167 Kholmsk;47.0500 Wilmslow;53.3250 Dingalan;15.3833 Imatra;61.1833 Ternivka;48.5231 Danvers;42.5740 Ambohitompoina;-19.7500 Nochistlán de Mejía;21.3642 San Martín de los Andes;-40.1667 Algemesí;39.1897 Mocímboa;-11.3196 Shalqar;47.8333 Kashima;33.1036 Dahmani;35.9500 Bitam;2.0833 Mateus Leme;-19.9858 Millville;39.3903 Jaguarão;-32.5658 Sundern;51.3167 Sabaneta;8.7522 Thetford;52.4100 Qibray;41.3897 El Sauce;12.8861 Tenares;19.3700 Wappinger;41.5899 Kahului;20.8715 Larreynaga;12.5939 Tlalpujahua de Rayón;19.8052 Santa Bárbara;14.4333 Valente;-11.4119 Santa Bárbara;-19.9589 Rambouillet;48.6444 Shilan;21.9053 Kent;41.1490 Guaçuí;-20.7758 Kongsberg;59.6694 Ludwigsfelde;52.2997 Williamsport;41.2399 Sens;48.1975 Alitagtag;13.8650 Dodge City;37.7611 Arraial do Cabo;-22.9658 Cártama;36.7114 Wilmette;42.0771 Piraí;-22.6289 Andenne;50.4833 Molíns de Rey;41.4139 Thātha;25.4988 Pomerode;-26.7408 Bignona;12.8167 Mahaplag;10.6072 Hādīshahr;38.8367 Topki;55.2833 Villa Luvianos;18.9200 Chalhuanca;-14.2950 Salinas Victoria;25.9667 Atlautla;19.0000 Mboro;15.1500 Traipu;-9.9708 Porto Sant’Elpidio;43.2667 Apaxco de Ocampo;19.9800 Lufeng;24.5738 Zolotonosha;49.6833 Ðakovo;45.3100 Sandūr;15.1000 Ōuda-yamaguchi;34.5167 Soron;27.8800 Soignies;50.5667 Pau dos Ferros;-6.1108 Fūman;37.2239 Veendam;53.1000 Chicago Heights;41.5100 Bernards;40.6761 Burnie;-41.0636 Clinton;32.3540 Maloyaroslavets;55.0167 Arnstadt;50.8342 Lapuyan;7.6325 Tualatin;45.3772 Iguaí;-14.7558 Pāyakarāopeta;17.4161 Viñales;22.6153 Andapa;-14.6500 Uchinada;36.6536 Zarraga;10.8167 Pitogo;7.4536 Lagoa Vermelha;-28.2089 Penukonda;14.0847 Zakopane;49.3000 Hayang;35.9167 Mukher;18.7008 Havran;39.5583 Gabasumdo;35.2554 Axim;4.8667 Mananjary;-21.2311 Limoeiro do Ajuru;-1.8950 Statesville;35.7842 Gioia del Colle;40.8000 Borşa;47.6553 Long Thành;10.8667 Jarrow;54.9814 Kitsuki;33.4167 Mirano;45.5000 Di Linh;11.5778 Marion;40.5497 Itako;35.9471 Pirapòzinho;-22.2753 Lainate;45.5667 Nova Prata;-28.7839 K’olīto;7.3122 Leon Postigo;8.1514 Lebrija;36.9194 Boca da Mata;-9.6408 Mazenod;-29.4642 Batgram;34.6833 Bandipura;34.4225 Ágios Nikólaos;35.1833 Timaru;-44.3931 Zongdi;25.5909 Sūlūru;13.7000 Ayyagarpet;17.2197 Troyan;42.8833 Longtan;40.7830 Zarechnyy;56.8167 Supía;5.4667 Argelia;2.2431 Narat;43.3198 Kapellen;51.3167 Ukiha;33.3500 Curtea de Argeş;45.1392 Margate;-30.8500 Ocean;40.2520 Mankāchar;25.5300 North Kingstown;41.5687 San Juan Guichicovi;16.9667 Miramas;43.5822 Washington;37.1303 Lohne;52.6667 Choba;4.8906 Hulst;51.2833 Padre Bernardo;-15.1600 Vīrakeralam;11.0077 Lier;59.8675 Ibotirama;-12.1850 Santa Perpetua de Moguda;41.5375 Gojō;34.3486 Villareal;11.5667 Wangtang;19.9327 Immokalee;26.4253 Mont-Dore;-22.2157 Ambodibonara;-20.3388 Xima;37.9763 Malargüe;-35.4750 Erice;38.0369 Shāhganj;26.0560 Itacaré;-14.2778 Manihāri;25.3500 Oktyabrsk;53.1667 Kobuleti;41.8111 Morón de la Frontera;37.1222 Maple Valley;47.3659 Bien Unido;10.1333 Campos Gerais;-21.2350 Wiesloch;49.2942 Parimpūdi;17.1171 East Hampton;41.0117 Orosháza;46.5667 Karlskoga;59.3333 Sola;58.8561 Duayaw-Nkwanta;7.1667 Baytūnyā;31.8889 Shencottah;8.9733 Jaynagar;26.5833 Sādri;25.1800 Cabrero;-37.0333 Corralillo;22.9856 Malebennūr;14.3537 Mu’tah;31.1000 Bluffton;32.2135 Santuario;6.1375 Conner;17.8086 Waddān;29.1611 New London;41.3502 Nizao;18.2441 Bhānder;25.7358 Tongyangdao;41.7676 Tortona;44.8942 Marolambo;-20.0500 Mount Pleasant;42.7129 Haiyang;39.9534 Marshalltown;42.0343 Gurlan;41.8500 Piracuruca;-3.9278 Lapão;-11.3828 Roşiori de Vede;44.1114 Mulchén;-37.7167 Niimi;34.9739 Malekān;37.1508 West Islip;40.7041 Santa María de Jesús;14.4933 Ciudad Bolívar;5.8500 Coromandel;-18.4728 Miguel Calmon;-11.4289 Neckarsulm;49.1917 Drimmelen;51.6833 Temescal Valley;33.7581 Rasiāri;26.0464 Teno;-34.8667 Overath;50.9500 New Windsor;41.4742 İpsala;40.9217 Ashford;51.4340 Santa Rita do Passa Quatro;-21.7100 Cangas;42.2642 Panchānandapur;24.9339 Abarkūh;31.1267 Macalelon;13.7500 Montbéliard;47.5100 Chillán Viejo;-36.6229 Bidur;27.8961 Schloß Holte-Stukenbrock;51.8833 Ramon Magsaysay;8.0053 Verkhniy Ufaley;56.0667 Strausberg;52.5833 Manbengtang;22.0896 Terlizzi;41.1333 Ilchester;39.2187 Reoti;25.8500 Sürmene;40.9142 Marapanim;-0.7139 Wasco;35.5938 Villa El Carmen;11.9794 Aleksandrovskoye;44.7167 Lagoa Sêca;-7.1708 Lunel;43.6769 Xovos;40.2197 Tracuateua;-1.0719 Novo Oriente;-5.5339 Palanas;12.1464 Bo’ao;19.1606 San Felipe;14.6206 Ronse;50.7500 Kedu;25.7103 Longdian;37.9033 Rockledge;28.3203 Navrongo;10.8847 East Grinstead;51.1300 Chaska;44.8164 Madison;32.4738 Silao;25.0836 Zirndorf;49.4500 Ticuantepe;12.0231 Sipacapa;15.2128 Digboi;27.3932 Rheinbach;50.6333 Oak Forest;41.6054 Rāver;21.2431 Citrus Park;28.0730 Burntwood;52.6831 Barcelos;-0.9750 Vimercate;45.6167 Chettipālaiyam;11.1667 Sliedrecht;51.8167 Paouignan;7.6937 San Isidro;16.8667 Panauti;27.5844 Zhongshan;34.9430 Klosterneuburg;48.3042 Cuerámaro;20.6258 Tall Rif‘at;36.4733 Haliyāl;15.3294 Nuevo Arraiján;8.9200 Ramsey;45.2617 Itaí;-23.4178 Gonesse;48.9875 Badiangan;10.9860 Bakhchysarai;44.7528 Tsaravary;-21.2500 Aalten;51.9333 Frankfort;-27.2833 Behara;-24.9500 Porto Calvo;-9.0450 Takahagi;36.7192 Staraya Russa;58.0000 Aytos;42.7000 Boralday;43.3603 Huaquechula;18.7667 Aschersleben;51.7500 Newtown;41.3988 Iúna;-20.3458 Tāzah Khūrmātū;35.3028 Mason City;43.1487 Garmisch-Partenkirchen;47.5000 Wassenaar;52.1500 Sangyuan;38.6211 Haqqulobod;40.9061 Chintalapalle;15.0400 Balch Springs;32.7194 Williston;48.1814 Rāmpur;26.2126 Bathnāha;26.6433 Santana do Paraíso;-19.3639 Ourilândia do Norte;-6.7550 Converse;29.5091 Saint-Constant;45.3700 Hennigsdorf;52.6378 Clarksburg;39.2246 Vādippatti;10.0843 Ban Phonla Krang;14.9192 Ubatã;-14.2139 Sihecun;22.4729 Awara;36.2113 Magarao;13.6619 Granite City;38.7296 Short Pump;37.6549 Savur;37.5375 Heiligenhaus;51.3167 Ermelo;52.3000 Castro Alves;-12.7658 San Jose;10.0083 Gradignan;44.7725 Atitalaquia;20.0583 Ichikikushikino;31.7167 Miranda;-20.2408 Enna;37.5633 Swatara;40.2463 Huntley;42.1599 Grimsby;43.2000 Ixchiguán;15.1642 Teutônia;-29.4478 Buldan;38.0450 Tepehuacán de Guerrero;21.0131 Albignasego;45.3500 Akitakata;34.6631 Bergerac;44.8500 Bhuban;20.8820 Duptiair;23.9910 El Alia;37.1667 Santa Eugenia;42.5667 Madattukkulam;10.5587 Sandanski;41.5681 Bourzanga;13.6781 Conchal;-22.3300 Jajce;44.3417 Rappang;-3.8447 San Giovanni Rotondo;41.7000 Santana;-12.9828 Afzalpur;17.2000 Garden City;42.3244 Sibinal;15.1333 Liugoucun;40.9455 Green;40.9483 Guanxi;24.8000 Baden;48.0075 San Juan de Urabá;8.7667 Brzozów;49.7000 Dongen;51.6333 Āmangal;16.8499 Märsta;59.6167 Qingshan;27.3500 Sebeş;45.9600 Estarreja;40.7500 Taraka;7.8994 Hénin-Beaumont;50.4217 Pereyaslav-Khmel’nyts’kyy;50.0650 Fremont;41.4395 Coffs Harbour;-30.3022 Rangia;26.4700 Prior Lake;44.7246 Gubkinskiy;64.4333 Caboolture;-27.0667 McHenry;42.3387 Margherita;27.2800 Saguiaran;8.0333 Wągrowiec;52.8000 Nelliyalam;11.5255 Mel Bhuvanagiri;11.4380 Miki;34.2682 Galatina;40.1667 Wangen im Allgäu;47.6858 Hohen Neuendorf;52.6667 Sadalgi;16.4200 Bāgepalli;13.7800 Liqizhuang;39.9703 San Agustín Tlaxiaca;20.1144 Neenah;44.1669 Talāja;21.3500 Benicia;38.0725 Tauramena;5.0167 Jirwa;26.0064 Vilyeyka;54.4980 Mundo Novo;-11.8589 Fidenza;44.8667 Willebroek;51.0667 Quezon;17.3119 Porto da Folha;-9.9169 Twentynine Palms;34.1478 West Linn;45.3670 Nguti;5.3167 Mayskiy;43.6500 Westport;41.1428 Alakamisy Itenina;-21.6333 Wethersfield;41.7013 Werkendam;51.8000 Binə;40.4539 Turrialba;9.8897 Szentes;46.6510 Sikonge;-5.6333 Mantena;-18.7819 Tianwei;23.9023 Zomin Shaharchasi;39.9631 Bankāpur;14.9230 La Garde;43.1256 Ksebia;34.2933 Soanierana Ivongo;-16.9167 Joaçaba;-27.1778 Assèmini;39.2833 Miandrarivo;-19.4333 Suār;29.0270 Plum;40.5024 Kāko;25.2259 Bijbiāra;33.7938 Ambinanisakana;-16.9500 Nayoro;44.3559 Antsiatsiaka;-17.0000 Labrador;16.0339 Scicli;36.7914 Tlaxcoapan;20.0953 Sol’-Iletsk;51.1667 Vohipaho;-23.5500 San Juan;17.7431 Daphne;30.6263 Libenge;3.6500 Tarragona;7.0491 Pāmidi;14.9500 Geldermalsen;51.8833 Bamessi;6.0333 Falconara Marittima;43.6333 Unterhaching;48.0658 Amatepec;18.6500 Almuñécar;36.7339 San Sebastián de Yalí;13.3061 Laranjeiras;-10.8061 Dasūya;31.8168 Ružomberok;49.0786 Hamminkeln;51.7319 Jiangdi;27.0120 Mesagne;40.5667 Paracuellos de Jarama;40.5500 Springettsbury;39.9907 Mandi;31.7200 Pueblo Bello;10.4167 Wakefield;42.5035 Srīnivāspur;13.3378 Chengannūr;9.3200 Blagnac;43.6364 Quarrata;43.8475 Ar Rudayyif;34.3833 Buriti;-3.9419 Chiriguaná;9.3667 Zuitou;34.0622 Santa Margarita;12.0378 Tarui;35.3663 Parit Buntar;5.1167 Pacasmayo;-7.4003 Pearl;32.2730 Marcos Juárez;-32.7000 Andranomavo;-16.5667 Kantābānji;20.4671 Aragua de Barcelona;9.4575 Rājgarh;27.2360 Mandabe;-21.0500 Albania;11.1597 Idigny;7.4833 Wellington;11.3655 Gilarchāt;22.0703 Saumur;47.2600 Ourika Wawrmas;30.7167 Lochearn;39.3461 Celendín;-6.8667 Aripuanã;-10.1767 Alvin;29.3872 Paianía;37.9500 Pantar;8.0667 Qā’emīyeh;29.8519 Manjacaze;-24.7117 Hutto;30.5396 Noicattaro;41.0333 Ibimirim;-8.5408 Chuimatan;35.7166 San Luis Jilotepeque;14.6500 Uyuni;-20.4667 Karpinsk;59.7667 Puerto Colombia;11.0167 Lagindingan;8.5833 Holbrook;40.7944 Santa María Ixhuatán;14.1833 Skopin;53.8167 Lauf;49.5103 Hancun;39.4062 Şafāshahr;30.6131 Kamyshlov;56.8333 Sapian;11.4939 Niscemi;37.1500 Acapetahua;15.2333 Turek;52.0167 Putignano;40.8500 Al Quţayfah;33.7389 Lauri;25.1396 New Lenox;41.5095 Thomasville;35.8813 Znamensk;48.5833 Khānābād;36.6831 Crestview;30.7477 Yōrō;35.3084 Miracema;-21.4119 Pemberton;39.9562 Buritizeiro;-17.3508 Nādbai;27.2300 Sun City Center;27.7150 Jaltenco;19.7511 Pınarbaşı;38.7220 Ehingen an der Donau;48.2833 Ambodiangezoka;-14.6000 Sabaa Aiyoun;33.9000 Karasuk;53.7167 Auburn;42.9338 Iglesias;39.3167 Butzbach;50.4367 Sarayönü;38.2661 Flémalle-Haute;50.6011 Boisbriand;45.6200 Miyanaga;33.7167 Majagua;21.9244 Jinju;22.7073 Cândido Sales;-15.5050 Tamorot;34.9333 Karumāndi Chellipālaiyam;11.3019 Shoreview;45.0842 Pérama;37.9667 Bozyazı;36.1000 Moon;40.5081 Heppenheim;49.6431 Ipixuna;-7.0508 Shangxiao;35.4969 Sofiyivs’ka Borshchahivka;50.4114 Lamego;41.1008 Santa Rosa de Viterbo;-21.4728 Lauaan;11.1429 Pérez;-33.0000 Bafoulabé;13.8064 Zottegem;50.8667 Solânea;-6.7778 Bugho;10.8000 Tamayo;18.4000 Mauganj;24.6800 San Pablo;7.6578 Ulubey;40.8761 Gladstone;39.2134 Chivasso;45.1910 Nepomuceno;-21.2358 Qarah Ẕīā’ od Dīn;38.8914 Dyatkovo;53.6000 Fortul;6.7922 Cavaillon;43.8375 Deer Park;40.7623 Dardoq;40.8156 Obertshausen;50.0667 Bishunpur Sundar;26.0579 Sombrio;-29.1039 Alhaurín el Grande;36.6331 Sachse;32.9726 Los Córdobas;8.9000 Jarocin;51.9667 Novoaleksandrovsk;45.5000 Ōtake;34.2333 Guruzāla;16.5800 Macrohon;10.0797 Tōin;35.0741 Malitbog;8.5361 Apam;5.2789 Eidsvoll;60.3475 Codajás;-3.8369 Key West;24.5642 Santeramo in Colle;40.8000 Francavilla al Mare;42.4167 Semiluki;51.6833 Carrollton;33.5818 Morales;2.7603 Mariano Comense;45.7000 Chuanliaocun;28.2611 Narasannapeta;18.4151 Eureka;40.7943 Merrimack;42.8547 Montreux;46.4333 Piracaia;-23.0539 Ḩukūmatī Baghrān;33.0669 Vadakkanandal;11.7739 Wooster;40.8172 Poytug‘;40.9000 Jardim;-7.5819 Polysayevo;54.6014 Muchamiel;38.4136 Wanlaweyn;2.6167 Pfaffenhofen;48.5333 Tekkalakote;15.5348 Hukeri;16.2300 Shingū;33.7167 Weiterstadt;49.9000 Asheboro;35.7158 Fortuna Foothills;32.6616 Shelek;43.5972 Manāsa;24.4800 Santo Antônio do Tauá;-1.1519 Vernon Hills;42.2340 South Windsor;41.8353 Iesolo;45.5331 Taloda;21.5607 Maevatanana;-16.9500 Kirzhach;56.1500 Brasiléia;-11.0100 Ratau;-29.3828 Beuningen;51.8667 Tubize;50.6930 Kiruna;67.8489 Yuanyangzhen;34.7847 Kiskunhalas;46.4319 Dondon;19.5333 Kudachi;16.4800 Krasnoarmeysk;56.1000 Betong;5.7731 Nova Esperança;-23.1839 Dum Duma;27.5688 Lanquín;15.5758 Masanwa;-3.1833 Huşi;46.6742 Wieliczka;49.9894 Mizque;-17.9333 Az Zabadānī;33.7250 Segezha;63.7333 Maski;15.9581 San Giovanni Lupatoto;45.3833 Plainview;40.7832 Pandag;6.7411 Siófok;46.9239 Planalto;-14.6700 Kirovsk;48.6333 Jászberény;47.5000 Sibi;12.3786 Las Flores;-36.0139 Caetés;-8.7728 Melgaço;-1.8039 Frías;-28.6333 Paramus;40.9455 Poro;10.6290 Cuajinicuilapa;16.4717 Maçka;40.8167 Deogarh;21.5383 Rich;32.2583 Bétera;39.5922 Lemoore;36.2949 Veranópolis;-28.9358 Jumilla;38.4792 Horn Lake;34.9512 Giussano;45.7000 Aş Şanamayn;33.0711 Mirassol d’Oeste;-15.6750 Upper Dublin;40.1502 Okuta;9.2199 Mühlacker;48.9500 Dalnerechensk;45.9333 San Benito Abad;8.9333 Chintalapūdi;17.0667 Pura;15.6248 Mangaldai;26.4300 Casiguran;16.2833 Superior;46.6941 Cambuí;-22.6119 Wyszków;52.5928 San Alberto;7.7525 Kirovsk;67.6167 Khā̃dbāri̇̄;27.3667 Oldbury;52.5050 Werota;11.9167 Temple Terrace;28.0436 Lichtenburg;-26.1500 Samokov;42.3371 Beohāri;24.0242 Nilakkottai;10.1700 Schleswig;54.5181 Farmington;41.7288 Dois Córregos;-22.3661 Severouralsk;60.1500 San Antonio del Sur;20.0569 Fălticeni;47.4597 Windsor;38.5422 Bubong;8.0167 Groß-Gerau;49.9192 Mabini;16.0697 Quilalí;13.5683 Ruskin;27.7065 Cesenatico;44.2000 La Unión;-40.2833 Valdagno;45.6500 Redenção;-4.2258 Ganjing;35.3338 Madalum;7.8530 Campos Sales;-7.0739 Budhlāda;29.9300 Safīpur;26.7300 Zhaicun;22.6174 Del Gallego;13.9233 Nacimiento;-37.5000 Rīngas;27.3700 Kayapa;16.3583 Migdal Ha‘Emeq;32.6714 Charata;-27.2167 Mount Gambier;-37.8294 Manoli;15.7800 Mannachchanellūr;10.9078 Bouknadel;34.1333 São João Nepomuceno;-21.5400 Haines City;28.1102 Xincun;21.6960 Santa Josefa;7.9842 Terdāl;16.5000 Vadakku Viravanallur;8.6979 Horsham;40.1993 São Bernardo;-3.3608 East Chicago;41.6469 Água Azul do Norte;-6.7908 Makubetsu;42.9085 El Monte;-33.6833 Tabira;-7.5908 Al ‘Awwāmīyah;26.5833 Ubaidullāhganj;22.9983 Nordenham;53.5000 Santa Lucia;17.1222 Licey al Medio;19.4300 Aurillac;44.9261 Airmadidi;1.4333 Aït Faska;31.5058 Hjørring;57.4636 Lilancheng;39.2011 Renigunta;13.6500 Ksour Essaf;35.4300 Siquinalá;14.3082 Tālīkota;16.4800 Balboa;2.0436 Ōra;36.2524 Kirovsk;59.8667 Aru;2.8617 Selm;51.6833 Pati do Alferes;-22.4289 Bingen am Rhein;49.9667 Wall;40.1674 Gūdūru;15.7748 San Juan Atitán;15.4333 Arida;34.0833 Caririaçu;-7.0419 Randolph;40.8434 Brawley;32.9783 Mansfield;41.7892 Thatcham;51.4050 South Portland;43.6310 Liushuquan;39.3512 Arrecifes;-34.0667 Kami;33.6000 Canarana;-11.6850 Sanger;36.6990 Ondokuzmayıs;41.4944 Vignola;44.4808 Sangerhausen;51.4667 Topoľčany;48.5542 Port Alfred;-33.5917 Walpole;42.1464 Sapouy;11.5544 Cabot;34.9766 Navabad;38.5667 Manito;13.1235 Whitehaven;54.5480 Lamut;16.6517 Panchari Bazar;23.2900 Burgos;17.0667 Sinait;17.8667 La Teste-de-Buch;44.6200 Cotoca;-17.7536 Pensilvania;5.5000 São João Batista;-27.2758 Ilha Solteira;-20.4272 İncesu;38.6222 Kernersville;36.1065 Conversano;40.9667 Frauenfeld;47.5500 Bourdoud;34.5922 Poldokhtar;33.1536 Owatonna;44.0912 Buenavista;13.2500 Pukekohe East;-37.2000 Termini Imerese;37.9833 San Pablo;17.4478 Kalfou;10.2833 Susquehanna;40.3111 Sun City West;33.6695 Arzignano;45.5167 Rāman;29.9504 Bad Honnef am Rhein;50.6450 Chitarpur;23.5794 Tiruppattūr;10.1300 Shuanghe;33.0320 Šumperk;49.9653 Kurchaloy;43.2044 Maheshwar;22.1769 Beramanja;-13.3333 Bangzha;24.8345 Sora;41.7167 Kluczbork;50.9833 Buddayyakota;13.6593 Dengjiazhuang;37.7051 Mahitsy;-18.7500 Lindau;47.5458 Chinna Salem;11.6342 Bushey;51.6429 Dugda;23.7452 Teustepe;12.4206 Farnham;51.2150 Bozkır;37.1886 Geretsried;47.8667 Fleetwood;53.9220 Saikaichō-kobagō;32.9333 Sanchez-Mira;18.5611 Friedrichsdorf;50.2556 Kottapeta;16.7167 Boureït;34.9833 Perevalsk;48.4333 Buesaco;1.3833 Polasara;19.7815 Montemor-o-Velho;40.1667 Itaíba;-8.9478 Yapacani;-17.4028 Ambatomborona;-19.4000 Union City;33.5942 Semīrom;31.4142 Rancho Grande;13.2353 Gulām;25.7965 Batavia;41.8479 Elefsína;38.0333 Vertou;47.1689 Lanaken;50.8925 Étampes;48.4343 Xishan;23.0636 Paducah;37.0711 Iradan;40.2600 Infanta;15.8208 Paz de Ariporo;5.8833 Dongfeng;22.2479 Cañas;10.4457 La Máquina;14.3021 Mint Hill;35.1783 Gressier;18.5500 Świecie;53.4167 Malyn;50.7689 Burlington;42.5022 Trindade;-7.7619 Köthen;51.7500 Attimarappatti;8.7297 Sierra Bullones;9.8167 Bom Jesus dos Perdões;-23.1350 Barki Ballia;25.4223 Potrerillos;15.2167 Conde;-11.8139 Haoping;32.5992 Vsetín;49.3387 El Progreso;14.3500 Phulera;26.8700 New Milton;50.7600 Khashuri;41.9975 Labytnangi;66.6572 Tavira;37.1167 Baykan;38.1639 Raghunāthpur;23.5500 Homewood;33.4619 Gembloux;50.5667 Humberto de Campos;-2.5978 Yuanchang;23.6420 Casillas;14.4171 Big Spring;32.2389 Winona;44.0505 Conception Bay South;47.5167 Nagar;27.4247 Ambatofotsy;-21.7500 Castel Volturno;41.0500 Kulmbach;50.1000 Lice;38.4500 Iaçu;-12.7669 Tholen;51.5333 Puerto Varas;-41.3167 Marimba;-8.3667 El Quetzal;14.7667 Rosario de la Frontera;-25.8000 Norco;33.9252 Aracoiaba;-4.3708 Wetteren;51.0000 Bessemer;33.3712 Achern;48.6333 Socorro;9.6181 Taquari;-29.8000 Bobon;12.5167 Emmeloord;52.7097 Attili;16.7000 Novopavlovsk;43.9500 Mahates;10.2333 Sasovo;54.3500 Uherské Hradiště;49.0697 Itapuranga;-15.5619 Oum Hadjer;13.2833 Nij Khari;22.0929 Madira;16.9167 Dancagan;7.6119 Grevená;40.0850 Toktogul;41.8722 Santiago Papasquiaro;25.0439 Mungaoli;24.4084 Oltiariq;40.3917 Lakshmīcharīpāra;22.7900 Mizdah;31.4450 Helmstedt;52.2281 Saint-Bruno-de-Montarville;45.5333 Requínoa;-34.2786 Queimadas;-10.9778 Zacualtipán;20.6500 Mahambo;-17.4833 Seondha;26.1542 Troy;40.0437 East Hempfield;40.0825 San Sebastián Coatán;15.7333 South Salt Lake;40.7057 Mantua;22.2908 Sankt Wendel;49.4667 Shāhedshahr;35.5714 Odemira;37.5833 Ligang;22.8011 Lockport;41.5906 Lübbecke;52.3081 Roseto degli Abruzzi;42.6833 Bāsopatti;26.5780 Tinipuka;-4.5485 Vierzon;47.2225 Zavodoukovsk;56.4833 Puerto Píritu;10.0667 Binidayan;7.8000 Traun;48.2217 Cocal;-3.4708 Stein;50.9667 Sahaspur;29.1210 Vijāpur;23.5700 Hercules;38.0064 Tsarazaza;-20.1333 Coração de Jesus;-16.6850 Duiven;51.9500 Villanueva de la Serena;38.9667 Kadamalaikkundu;9.8110 Sirumugai;11.3214 Gaoya;36.4609 Gajwel;17.8517 Tabina;7.4655 Austin;43.6721 Montichiari;45.4161 Malaimbandy;-20.3333 Diapaga;12.0667 Dano;11.1436 Ōguchi;35.3325 Medina;41.1358 Rāghopur;25.5323 Ponta de Pedras;-1.3900 Santo Antônio do Monte;-20.0869 Oupeye;50.7083 Correggio;44.7703 Istmina;5.1667 Ridgewood;40.9821 Maozhou;38.8600 Kimovsk;53.9667 Sirmatpur;25.3235 Tekman;39.6450 Kīranūr;11.6920 Traralgon;-38.1958 Marmara Ereğlisi;40.9697 Koko;11.4232 Tubay;9.1650 Demre;36.2472 Nāgod;24.5692 Maintirano;-18.0667 Natividad;16.0422 El Jícaro;13.7225 Nagai;38.1075 Santa Ana;9.3190 Dolores;-36.3167 Torrelodones;40.5756 San Vicente de Cañete;-13.0833 Novelda;38.3850 Tosa;33.5000 Talisayan;8.9917 Santa Maria;17.4667 Yinajia;26.8239 Mimoso do Sul;-21.0639 Grandview;38.8802 Ōmachi;36.5030 Malaṅgawā;26.8667 Columbine;39.5879 De Witt;43.0501 Wetzikon;47.3167 Āgaro;7.8500 Lourinhã;39.2333 Tarangnan;11.9000 Alegria;9.7243 San Martín;3.6944 Beersel;50.7631 Alaçam;41.6100 Singarāyakonda;15.2500 Ciempozuelos;40.1592 Santa María Colotepec;15.8833 Teniente Primero Manuel Irala Fernández;-22.8140 Mola di Bari;41.0667 São Joaquim de Bicas;-20.0489 Haisyn;48.8094 Wilsonville;45.3109 Erandio;43.3047 West Chicago;41.8960 Felixstowe;51.9639 Mar de Ajó;-36.7203 Woodburn;45.1472 Vitez;44.1585 Miyaki;33.3249 Altavas;11.5383 Busselton;-33.6478 Korkut;38.7386 Pszczyna;49.9833 Kunisakimachi-tsurugawa;33.5653 Andasibe;-17.3333 Upper Moreland;40.1572 Oxford;34.3627 Nova Soure;-11.2328 Santa Lucia La Reforma;15.1333 Roth;49.2461 Cochrane;51.1890 Verl;51.8831 Inkster;42.2935 Pāveh;35.0433 Oulad Barhil;30.6408 Baliqchi;40.9000 Saintes;45.7464 Yany Kapu;45.9675 Fort Washington;38.7339 Rāmdiri;25.3118 Scandiano;44.5925 Harsewinkel;51.9667 Woodstock;42.3096 Colleyville;32.8913 Bagno a Ripoli;43.7500 Triunfo;-29.9428 Nechí;8.1000 Huejotzingo;19.1594 Kireyevsk;53.9333 Hasköy;38.6822 Upper Macungie;40.5694 Cláudio;-20.4428 Louis Trichardt;-23.0500 Prata;-19.3069 Baheri;25.9426 Batabanó;22.7167 Golborne;53.4758 Merkānam;12.1942 Ureshinomachi-shimojuku;33.1333 Pereira Barreto;-20.6383 Marshfield;42.1140 Batalha;-4.0239 Montgomery;40.2411 Mogtédo;12.2833 Nanpingcun;39.7530 Adra;36.7478 Diébougou;10.9667 São Miguel do Iguaçu;-25.3478 Lopez Jaena;8.5500 Firmat;-33.4500 Harbiye;36.1167 Camas;45.6005 Kouhu;23.5763 West Melbourne;28.0694 Forest Grove;45.5243 Ban Piang Luang;19.6493 Allen;12.5013 Vaterstetten;48.1050 Holt;42.6416 Stevens Point;44.5241 Lanling;23.0033 Jaruco;23.0428 Bandar-e Deylam;30.0542 Oulad Said;32.6320 Ayuquitan;9.4644 Tipo-Tipo;6.5333 Pfungstadt;49.8056 Los Arabos;22.7400 Overijse;50.7833 Xaxim;-26.9619 Olesa de Montserrat;41.5450 La Esperanza;14.8719 Nogi;36.2332 Ishii;34.0742 Rinteln;52.1906 Carahue;-38.7089 Panelas;-8.6639 Manyoni;-5.7500 Pinillos;8.9167 Salor;41.3833 Lamzoudia;31.5833 Thetford Mines;46.1000 Londonderry;42.8796 North Tustin;33.7636 Mimata;31.7307 Zaprešić;45.8572 Uttaramerūr;12.6160 Ditzingen;48.8264 Villanueva;12.9647 Igaci;-9.5369 Lennestadt;51.1236 Haiwei;19.4275 Libourne;44.9200 Balabanovo;55.1833 Otradnoye;59.7833 Busuanga;12.1335 Vnukovo;55.5997 Sangrāmpur;26.4752 Irugūr;11.0178 Budai;23.3600 Yolombó;6.5978 Kamonkoli;1.0750 Witham;51.7978 Cayo Mambí;20.6647 Palma;-10.7758 Nesher;32.7711 Fujikawaguchiko;35.4973 San Pedro Masahuat;13.5500 Ḩaql;29.2833 Unisan;13.8413 Boquim;-11.1469 Chanhassen;44.8546 Veenoord;52.9875 La Valette-du-Var;43.1383 Bejucal;22.9328 Tegalbuleud;-7.3578 Jardim;-21.4800 Sarrat;18.1578 Rottweil;48.1681 Pauri;30.1500 Nabīnagar;24.6200 Mata Grande;-9.1178 Fengruncun;34.8537 Andhra Thārhi;26.3771 Nāmrup;27.1939 Ḑubā;27.3493 Wiehl;50.9500 Erlun;23.7910 Oliva;38.9194 Courtenay;49.6878 Podatūrpeta;13.2817 Şarkîkaraağaç;38.0803 Ấp Khánh Hòa;10.6333 Horb am Neckar;48.4453 Vynohradiv;48.1397 Lower Providence;40.1485 Mərdəkan;40.4922 Kazincbarcika;48.2531 Ash Shūnah ash Shamālīyah;32.6100 Wałcz;53.2667 Pangil;14.4000 Independência;-5.3958 Vāsudevanallūr;9.2417 Kocaali;41.0547 Legnago;45.1929 Plettenberg;51.2167 San Nicolas;13.9283 Jalpan;21.2167 Delitzsch;51.5264 Tougué;11.4400 Penha;-26.7689 Camalaniugan;18.2756 Ilaiyānkudi;9.6271 Abay;49.6311 Panāgar;23.3000 Araira;10.4525 San Miguel;10.0000 Yanyan;24.4166 Lātehār;23.7442 Oud-Beijerland;51.8167 Rudolstadt;50.7169 Mimasaka;35.0086 Staßfurt;51.8667 Idstein;50.2206 Solano;0.6983 Zhongcun;35.3615 Thiene;45.7072 Baarn;52.2167 Timurni;22.3712 Wādi;17.0700 Jaisinghpur;26.6318 Shrīrangapattana;12.4140 Yanaul;56.2833 Boo;59.3167 Mahwah;41.0816 Rajākheri;23.8593 Maaseik;51.1019 Tirorā;21.4072 Brilon;51.3956 Ladera Ranch;33.5492 São José do Norte;-32.0150 Saidpur;25.5500 Borçka;41.3636 Demba;-5.5000 Rosemount;44.7466 Iraquara;-12.2489 Avanigadda;16.0197 Tumbao;7.1167 Ain Aicha;34.4833 Mercer Island;47.5661 Talugtug;15.7789 Tanabi;-20.6258 Salaga;8.5500 Bungku;-2.5333 Somoniyon;38.4422 Jevargi;17.0139 Palm River-Clair Mel;27.9239 Skawina;49.9833 Korostyshiv;50.3186 Clayton;35.6590 Palm City;27.1735 Kirkby in Ashfield;53.0990 Zheleznovodsk;44.1394 Harbel;6.2833 Dalmine;45.6500 Reading;42.5351 Hermanus;-34.4167 Senador Pompeu;-5.5878 Limonar;22.9561 Almonte;37.2667 Caloto;3.0333 Xiaoli;38.9999 Jilikŭl;37.4928 Exeter;40.3139 Sandoná;1.2833 Elk River;45.3314 Vistahermosa;3.1239 Hazelwood;38.7931 Tamboril;-4.8319 Mudgal;16.0119 Sabanagrande;10.8000 Ban Khamen;13.8823 San José;1.6967 Ulongué;-14.7167 Samtredia;42.1625 Leandro N. Alem;-27.6000 Baar;47.2000 Markkleeberg;51.2778 Hamme;51.0833 Wumayingcun;38.0094 Inta;66.0833 Halfmoon;42.8640 Ruvo di Puglia;41.1167 La Unión;1.6019 Yamagata;35.5061 Warrington;40.2489 Suşehri;40.1658 Ozëry;54.8500 Rānia;29.5300 Vangviang;18.9333 Farkhor;37.5000 Şaydnāyā;33.6967 Öhringen;49.2000 Chiromo;-16.5500 Corrente;-10.4428 Kingsville;27.5094 Nagtipunan;16.2167 Heiloo;52.6000 Albergaria-a-Velha;40.6936 Xenia;39.6829 Acatlán;20.4242 Cuijk;51.7296 Tapa;30.2979 Meckenheim;50.6333 Bom Jardim;-22.1519 Paete;14.3667 Keystone;28.1312 Areia Branca;-4.9558 Galt;38.2698 Lafayette;37.8919 Ottumwa;41.0195 Dhing;26.4679 Formosa do Rio Preto;-11.0478 Kasrāwad;22.1274 Mengdan;24.2752 Rockaway;40.9602 Cota;4.8167 Derby;37.5571 Kariba;-16.5167 Mombin Crochu;19.3667 Silver Springs Shores;29.1126 Monreal;12.6440 Banga;31.1887 Staunton;38.1593 Magog;45.2667 Ula;37.1036 Florence;33.0590 Kola;22.4300 Pitangui;-19.6828 Pallappatti;10.7198 Mednogorsk;51.4222 Workington;54.6365 Chocontá;5.1467 Moscow;46.7308 Madūru;14.7000 Westerlo;51.0833 Salem;37.2864 Salamanca;-31.7667 Camillus;43.0539 Kovancılar;38.7217 Moron;18.5600 Onda;39.9625 Paoay;18.0625 Pinal de Amoles;21.1342 Requena;-5.0569 Fairland;39.0803 Newport;41.4801 Khalāri;23.6506 Hudson;42.7639 Burē;10.7000 Kochubeyevskoye;44.6706 Anajatuba;-3.2639 Bad Oldesloe;53.8117 Lengir;42.1819 Barberton;41.0095 Dedham;42.2467 Merelbeke;51.0000 Khed Brahma;24.0299 Siswa;26.7027 Dhāriwāl;31.9561 Norden;53.5967 Poblacion;10.1614 Salzkotten;51.6708 Laon;49.5639 Caledonia;42.7986 Kalayaan;14.3280 Petershagen;52.3833 Mejorada del Campo;40.3967 Pooler;32.1043 Flores Costa Cuca;14.6500 Guéné;11.7306 Elói Mendes;-21.6100 Drodro;1.7667 Cajuru;-21.2753 Sprockhövel;51.3667 El Ghiate;32.0331 Xiaolongtan;23.8099 Nijverdal;52.3667 San Felipe;15.0619 Safsaf;34.5581 Barra dos Coqueiros;-10.9089 Catembe;-26.0050 Cave Spring;37.2254 Hastings;40.5961 Tiruvattār;8.3307 Kościerzyna;54.1167 Pezinok;48.2919 Muret;43.4611 Aridagawa;34.0575 Sastamala;61.3417 Apiaí;-24.5097 Edwardsville;38.7922 Nagarpāra;25.4155 Zittau;50.8961 Jaleshwar;26.6500 Solin;43.5317 Pagudpud;18.5614 Genzano di Roma;41.7022 Quirino;17.1356 De Pere;44.4308 Seal Beach;33.7542 Shaqlāwah;36.4056 Morozovsk;48.3667 Cambre;43.2830 Athens;34.7843 Tōno;39.3279 Ghoti Budrukh;19.7167 Jenks;35.9981 Sahāwar;27.8000 Steenbergen;51.5833 Göle;40.7928 Portogruaro;45.7833 Sezze;41.5000 Piranhas;-9.6239 Bayonet Point;28.3254 Otuzco;-7.9000 Reserva;-24.6500 Great Sankey;53.3918 Aş Şaqlāwīyah;33.3964 Santa María Tonameca;15.7458 Mayāng Imphāl;24.6000 Umga;24.6396 Raahe;64.6833 Ban Pa Sak;16.8477 Belvidere;42.2543 Okemos;42.7057 Sikandarpur;26.0333 Minowa;35.9150 San Francisco Zapotitlán;14.5833 Valls;41.2883 Nedre Eiker;59.7647 Watertown;43.9734 Purwā;26.4700 Reedley;36.5988 Gyál;47.3861 Icatu;-2.7758 Saiha;22.4800 Bardsīr;29.9275 Aklvidu;16.6000 Aimorés;-19.4958 Atkarsk;51.8667 Rangāpāra;26.8377 Boussé;12.6667 Pālakodu;12.3037 Sovetskaya Gavan’;48.9667 Walker;42.9853 Novi Travnik;44.1748 Villa Tapia;19.3000 Ninohe;40.2713 Ereymentaū;51.6167 Sohāgpur;22.7000 Tarpon Springs;28.1493 Sho‘rchi;38.0111 Saltpond;5.2000 Wolfsberg;46.8419 Āshkhāneh;37.5614 Barstow;34.8661 Tulsīpur;27.5500 Calpe;38.6450 Saky;45.1336 Le Ray;44.0771 Franklin;39.4948 Sahjanwa;26.7500 São João da Ponte;-15.9289 Newberg;45.3075 Bystrc;49.2247 Dhāmnagar;20.9141 Wil;47.4667 Sand;59.1343 Tirumuruganpūndi;11.1649 Dalupo;19.3908 Ādīs Zemen;12.1167 Santo Tomás;-14.4510 Uauá;-9.8419 Dbarwa;15.1000 Montalbán;10.2522 Kabanga;-2.9022 Matriz de Camarajibe;-9.1519 Pallapatti;9.4664 Preah Vihear;13.8167 Vicuña;-30.0333 Krems an der Donau;48.4167 Kamo;37.6663 Whitehorse;60.7029 Nittedal;60.0731 Pitsea;51.5690 Antsirabe Avaratra;-13.9667 Tateyama;36.6636 Dimasalang;12.1933 Fidirana;-19.5000 Mundargi;15.2070 Jitotol;17.0833 Ma‘bar;14.7940 Waldkraiburg;48.2167 Sandusky;41.4468 Ternate;14.2833 Narasingapuram;11.6038 Arnold;39.0437 Wujiaying;33.1871 Nainpur;22.4300 Ridgefield;41.3065 Piritiba;-11.7300 Iki;33.7497 General Luna;13.6881 Espelkamp;52.3772 Białogard;54.0000 Quisqueya;18.5542 Rădăuţi;47.8425 Yuzhang;25.3561 Slavutych;51.5206 Bloomingdale;27.8784 Turnu Măgurele;43.7517 Norton Shores;43.1621 Rāmshīr;30.8947 Zhuangwei;24.7702 Żagań;51.6167 Dainyor;35.9194 Sultepec;18.8667 Uvarovo;51.9833 Ampahana;-14.7500 Tirumala;13.6833 Trussville;33.6405 Calliaqua;13.1308 Vila do Conde;-7.2600 Baroy;8.0333 Malanguan;40.1873 Lüdinghausen;51.7667 Santiago de Chuco;-8.1502 Ehden;34.2919 Mahatalaky;-24.7833 Balete;14.0167 San Enrique;10.4167 Kobo;12.1500 West Milford;41.1060 Ankazomborona;-16.1167 Zwevegem;50.8000 Villarrobledo;39.2667 Woolwich;43.5667 Caballococha;-3.9058 Avon Lake;41.4944 Edgewood;39.4190 Morton Grove;42.0423 Chandili;19.2467 Solsona;18.0961 Übach-Palenberg;50.9197 Valparaíso;-21.2278 Bay Point;38.0329 Warstein;51.4500 Magenta;45.4603 Wyandotte;42.2113 Muskego;42.8860 Santa Gertrudes;-22.4569 Mogalturru;16.4167 Ronnenberg;52.3194 Voorst;52.2333 Caransebeş;45.4214 Mātābhānga;26.3420 Madaoua;14.0762 Piat;17.7919 Tsaratanana;-16.7972 Romulus;42.2237 Olpe;51.0167 Shivganj;25.1500 Echemmaia Est;32.0786 Balarāmpur;23.0972 Norfolk;42.0324 Ajnāla;31.8400 Easton;42.0362 Mulakumūd;8.2681 Ponneri;13.3200 Sandomierz;50.6833 Taquaritinga do Norte;-7.9001 Rishton;40.3567 Meiningen;50.5500 Schmallenberg;51.1490 Dodola;6.9833 Patnāgarh;20.7083 São João dos Patos;-6.4950 Jabonga;9.3431 Tugaya;7.8840 Shitan;22.4546 Trebišov;48.6278 Santo Tomas;17.4000 Newton Abbot;50.5290 Pāta Kalidindi;16.5014 Pamplona;18.4661 Northdale;28.1058 Qabāţīyah;32.4097 Omurtag;43.1000 Marysville;40.2279 Harstad;68.8011 Francisco Sá;-16.4102 Nemuro;43.3301 Marín;42.3933 Calahorra;42.3000 El Milagro;-8.0284 Broadstairs;51.3589 Ouaoula;31.8667 Kungsbacka;57.4833 Sahasoa;-21.9833 Litoměřice;50.5342 Sharya;58.3667 Rawtenstall;53.6990 Üshtöbe;45.2422 Ottaviano;40.8500 Douar Olad. Salem;32.8739 Yarmouth;41.6756 Sinj;43.7000 Verukulambu;8.2953 Liangwu;23.6012 Brandon;32.2778 Pavlovsk;50.4578 Aso;32.9483 Shamgarh;24.1800 Anivorano Avaratra;-12.7333 Stutterheim;-32.5667 Vinnamāla;13.9074 Venice;27.1184 Araquari;-26.3700 Leoben;47.3817 Tirutturaippūndi;10.5300 Campo Magro;-25.3689 Zhuchangba;26.6615 Bad Soden am Taunus;50.1333 Montevarchi;43.5286 Dakṣiṇkāli̇̄;27.6089 Dongxiaozhai;40.1149 Lucena;10.8833 Břeclav;48.7589 Nāyanakulam;9.9611 Suchitoto;13.9333 Pindwāra;24.7945 Tamilisan;7.9761 Chāpar;26.2839 Santa Rosa;10.4456 Tumwater;46.9891 Anastácio;-20.4839 Vēttakkāranpudūr;10.5637 Dickinson;46.8918 Springfield;39.9281 Gemerek;39.1819 Būndu;23.1609 Sebastian;27.7822 Kulachi;31.9286 Gorantla;13.9892 Zhenbeibu;38.6275 Sarandi;-27.9439 Zarautz;43.2833 Pão de Açúcar;-9.7478 Ardmore;34.1949 Pweto;-8.4667 Somerset;40.5083 Fort Dodge;42.5098 São Joaquim;-28.2939 Delfzijl;53.3333 São Mamede de Infesta;41.2000 Junqueiro;-9.9250 Navalgund;15.5700 Isernhagen-Süd;52.4342 Mequon;43.2352 Wright;30.4445 San Benito;26.1298 Pepa;-7.7106 Tekāri;24.9425 Zanesville;39.9565 Patian;5.8444 Bhirāha;25.7978 Scotch Plains;40.6332 Wierden;52.3500 Hulbuk;37.8050 Oga;39.8868 Raisio;60.4858 Apollo Beach;27.7618 Armidale;-30.5000 Velūr;11.1114 Tuburan;6.6000 Fort Hood;31.1357 Derry;40.2709 Pāsighāt;28.0700 Kakonko;-3.2796 Villanueva;4.6087 G’allaorol Shahri;40.0214 Burdeos;14.8436 Ziketan;35.5885 Moses Lake;47.1279 Český Těšín;49.7461 Buy;58.4833 Hodonín;48.8489 Şamaxı;40.6303 Seia;40.4220 Luzilândia;-3.4578 Tres Isletas;-26.3500 Belsand;26.4436 Astorga;-23.2328 Aparecida do Taboado;-20.0869 Algeciras;2.5333 Tamahú;15.3069 Chinobod;40.8767 Jalolquduq;40.7194 Oirase;40.5992 Marco;-3.1239 Bintuni;-2.1167 Extremoz;-5.7058 Kungälv;57.8667 Ipameri;-17.7219 Dix Hills;40.8033 Petrinja;45.4406 Miguel Pereira;-22.4539 Giulianova;42.7500 Čadca;49.4358 Firuzoba;40.3000 Monte Cristi;19.8667 Gose;34.4667 Jacaré;-18.9058 Chebba;35.2372 Canindé de São Francisco;-9.6419 São Geraldo do Araguaia;-6.4008 Loma Linda;34.0450 Calamar;10.2500 Sucre;-1.2600 Igaraçu do Tietê;-22.5092 Channagiri;14.0240 Juayúa;13.8333 Medford;40.8220 Tanglin;37.4377 Albenga;44.0500 Arad;31.25 Vizela;41.3667 Khārupatia;26.5184 Takhli;15.2667 Srvanampatti;11.0764 Mummidivaram;16.6500 Shangtianba;28.0390 Dorohoi;47.9597 Oleshky;46.6333 Sagbayan;9.9167 Ashton in Makerfield;53.4870 Carrascal;9.3683 Itabaiana;-7.3289 Port St. John;28.4757 Rickmansworth;51.6383 Las Cabras;-34.2917 Macia;-25.0333 West Memphis;35.1530 Álvares Machado;-22.0789 Homer Glen;41.6043 Khajurāho;24.8500 Konstancin-Jeziorna;52.0833 Zion;42.4603 Hannoversch Münden;51.4167 Rochefort;45.9421 Shama;5.0167 Socastee;33.6871 Sūleswaranpatti;10.6388 Sanharó;-8.3608 Clinton;41.8435 Malinao;11.6431 Larvik;59.0811 Buôn Trấp;12.4833 Putten;52.2667 Igreja Nova;-10.1253 Carolina Forest;33.7651 Al Qubbah;32.7667 Ashwarāopeta;17.2500 Perrysburg;41.5377 Nossa Senhora das Dores;-10.4919 Westmont;41.7948 Collinsville;38.6770 Ain Dfali;34.5990 Borre;59.3799 Ālangulam;8.8640 Garcia Hernandez;9.6144 Freha;36.7620 Cartaxo;39.1500 Yanbu;22.7544 Attendorn;51.1167 San Borja;-14.8583 Murādpur;25.8000 Gelemso;8.8167 Panglong;24.1003 Païta;-22.1337 Greenbelt;38.9953 Bacolod;8.1892 Les Irois;18.4000 Sugar Hill;34.1080 Pātakākāni;16.3422 Karataş;36.5625 Nasukarasuyama;36.6569 Mau;26.2658 Mina;10.9333 Chanaur;25.8510 Kauswagan;8.1917 Aurora;-6.9428 Urbano Santos;-3.2078 Sulmona;42.0480 Amatán;17.3833 Yādiki;15.0500 Corsicana;32.0824 Buzen;33.6167 Alicia;9.8957 Grombalia;36.6000 Zernograd;46.8500 Valdivia;7.1636 Tuzluca;40.0494 Canhotinho;-8.8819 Mekla;36.6876 Starkville;33.4608 La Huerta;19.4833 Pirajuí;-21.9989 Herzogenaurach;49.5700 Bela Vista de Goiás;-16.9728 Borne;52.3000 Ravānsar;34.7153 El Retorno;2.3306 Bhadās;25.5184 Cedro;-6.6069 Fossano;44.5500 Bainbridge Island;47.6439 Syke;52.9131 Condado;-7.5858 Itaporanga;-7.3039 Sakai;36.1085 Rugeley;52.7599 Kaniv;49.7447 Selma;36.5715 Avon;41.4485 Encruzilhada do Sul;-30.5439 Khairā Tolā;26.8958 Watsa;3.0372 Kosigi;15.8542 Senboku;39.7017 Waldshut-Tiengen;47.6231 Sālār;23.7748 Westford;42.5864 San Agustín Loxicha;16.0167 Ōgawara;38.0494 Dubnica nad Váhom;48.9606 Roissy-en-Brie;48.7906 White Bear Lake;45.0656 Goirle;51.5167 Zungeru;9.8128 Clarence-Rockland;45.4833 Mahdīshahr;35.7108 Bereket;39.2431 Sevan;40.5550 Athens;39.3270 Haaksbergen;52.1500 Copertino;40.2667 Limbach-Oberfrohna;50.8667 Pýrgos;37.6667 Sanaur;30.3018 Coín;36.6667 Kumar Khad;26.5918 Ponca City;36.7235 Spremberg;51.5717 Gollapūdi;16.5412 Neftekumsk;44.7506 Vilankulo;-22.0000 Xinxing;47.1601 El Congo;13.9000 Windham;41.7102 Bīrpur;26.5082 Lagonglong;8.8000 Gaz;32.8022 Liushui;32.5959 Pacho;5.1306 Riverside;39.7835 Loha;18.9623 Simsbury;41.8729 Shāhpur;21.2374 Lukaya;-0.1508 Fish Hawk;27.8511 Tocopilla;-22.0964 Maksi;23.2600 Senftenberg;51.5167 Vernon;49.0900 Caibiran;11.5667 Ajim;33.7167 Tupanatinga;-8.7528 Ramain;7.9667 Pennādam;11.4039 North Potomac;39.0955 Paris;33.6688 Stroitel;50.7833 Soamanandrariny;-19.6500 Hujiachi;37.8917 Annūr;11.2325 Helleland;58.8922 Yokadouma;3.5167 Porto de Mós;39.6017 Bad Mergentheim;49.5000 Almansa;38.8682 Shamsābād;27.5364 Bijāwar;24.6235 El Bosque;17.0333 Martos;37.7167 Kotelnich;58.3078 Orbassano;45.0073 Bormujos;37.3667 Lisse;52.2500 Selydove;48.1500 Qiaomaichuan;39.7866 Ambohimasina;-19.6500 Santa Ana;16.8069 Jocotenango;14.5868 El Factor;19.3200 Koili Simra;25.4714 Wijk bij Duurstede;51.9833 Craíbas;-9.6178 Newport;50.7010 Vila Rica;-10.0119 Sendamangalam;11.2825 Rathenow;52.6000 Husum;54.4667 Dole;47.0931 Vulcan;45.3811 Nocera Superiore;40.7417 Ţayyibat al Imām;35.2661 Dinuba;36.5453 Lanester;47.7647 Joaquim Gomes;-9.1328 Fishkill;41.5129 Środa Wielkopolska;52.2333 Dighwa;26.2437 Horquetas;10.3195 Marple;53.3970 Amarapura;21.8530 Gollalagunta;17.1672 Dalān;25.6030 Puerto Guzmán;0.9636 Asino;57.0000 Tadjmout;33.8667 Latauna;26.0912 Volendam;52.4994 Tirukkalikkunram;12.6067 Buturlinovka;50.8333 Dunaharaszti;47.3539 Kiên Lương;10.2856 La Montañita;1.4792 Mulgund;15.2807 Cedros;14.6000 Sarykemer;43.0106 Curacaví;-33.4000 Kuju;23.7254 Ridgeland;32.4236 Capão do Leão;-31.7628 Nerópolis;-16.4058 Palmetto Bay;25.6219 Nueva Paz;22.7633 Denison;33.7672 Donmatías;6.4833 Douar ’Ayn Dfali;33.9500 Sīlappādi;10.3940 La Paz;-30.7500 Novo Aripuanã;-5.1214 Stjørdal;63.4750 Faribault;44.2996 Katrineholm;59.0000 Saint-Laurent-du-Maroni;5.4976 Cogua;5.0667 Marsella;4.9167 Rolling Meadows;42.0747 Cabrera;19.6300 Dazhuang;38.6951 Alfter;50.7356 Manage;50.5000 Boulsa;12.6667 Mahaiza;-19.9000 San Fernando;12.3167 Quipapá;-8.8278 Rimavská Sobota;48.3811 San Agustin;12.5689 Sattahip;12.6636 Mairena del Alcor;37.3667 Centerville;39.6339 Hajdúszoboszló;47.4333 Gotvand;32.2514 Gazojak;41.1833 Qiloane;-29.3625 Pallikondai;12.9167 Formby;53.5586 San Juan Nepomuceno;-26.1167 Oak Harbor;48.2964 Medford;39.8639 Oiapoque;3.8428 Lebedyn;50.5831 Pout;14.7739 Kalispell;48.2153 Skoura;31.0606 Feriana;34.9500 Noto;36.8833 Haren;52.7667 Ambalamanasy II;-14.5167 South Plainfield;40.5748 Benbrook;32.6788 Chandrakona;22.7300 Salqīn;36.1333 Gauripur;26.0800 Kyonpyaw;17.3000 Diest;50.9847 North Laurel;39.1285 Ibirapitanga;-14.1639 Pietrasanta;43.9452 Tehri;30.3800 Bela Vista;-22.1089 Ivrea;45.4674 Emporia;38.4028 Middleborough;41.8803 North Haven;41.3818 Setti Fatma;31.2256 Varel;53.3969 Baraúna;-5.0800 Krychaw;53.7194 Lālgudi;10.8700 Uchquduq Shahri;42.1567 Kailāshahar;24.3300 Ndora;-2.6033 Auburn Hills;42.6735 Tupaciguara;-18.5928 Pingtang;26.0677 Velingrad;42.0167 Kaltenkirchen;53.8397 Sarea Khās;26.6350 Isa;32.0572 Saint-Louis;47.5900 Huilongping;28.1887 Westchase;28.0597 Angatuba;-23.4903 Konnūr;16.2014 Quezon;16.4894 Guaiuba;-4.0400 Bulusan;12.7522 Magallanes;14.1833 Baiheqiao;22.9764 Lihe;34.0090 Taupo;-38.6875 Grootfontein;-19.5658 Bedburg;51.0000 Anjozorobe;-18.4033 Kamiamakusa;32.5000 Pombos;-8.1492 Burlington;40.8071 Suonan;35.6634 Quedlinburg;51.7917 Baharly;38.4303 Rioblanco;3.5000 Fort Saskatchewan;53.7128 Bình Hòa;10.9353 Beni Amrane;36.6686 Los Barrios;36.1833 Liria;39.6258 São José da Laje;-9.0100 Calw;48.7167 Jequitinhonha;-16.4339 Ambatotsipihina;-19.6333 Pelham;33.3114 Ibicaraí;-14.8650 Castiglione delle Stiviere;45.4000 Koilkuntla;15.2333 Lopary;-23.1833 Tsiatosika;-21.2000 Canton;42.1750 Tegina;10.0706 Vänersborg;58.3806 Elkridge;39.1941 Misato;38.5444 Ban Phru;6.9480 Bieruń Stary;50.0897 Amstetten;48.1167 Deyr;27.8417 Inhapim;-19.5489 Sonneberg;50.3500 Yangi Marg‘ilon;40.4311 Zaragoza;7.4940 Cavinti;14.2450 As Sulayyil;20.4597 Mapoteng;-29.1097 Laguna Salada;19.6500 Itapissuma;-7.7764 Rita;10.4337 Badou;7.5833 Freeport;42.2891 Monsefú;-6.8778 Montecchio Maggiore;45.5037 Semënov;56.8000 Hungund;16.0621 Rio Tinto;-6.8028 Kerrville;30.0398 Solon;41.3865 Marple;39.9654 Fort Mill;35.0061 Ventimiglia;43.7903 Gueznaia;35.7200 São Miguel do Guaporé;-11.6936 Pullappalli;9.7133 Hingham;42.2176 Zawyat an Nwaçer;33.3611 Pitogo;10.1210 Ibi;38.6272 Yugawara;35.1479 Guapí;2.5703 Puerto San José;13.9333 Wekiwa Springs;28.6984 Zolochiv;49.8075 Miarinarivo;-17.6500 Elgin;57.6500 Tayga;56.0667 Kourou;5.1600 Litvínov;50.6008 Horley;51.1740 Ocara;-4.4908 Ponda;15.4034 Piracanjuba;-17.3028 Hoh Ereg;41.0955 Antonibe;-15.1167 Kelīshād va Sūdarjān;32.5542 Tangalan;11.7775 Si Racha;13.1740 Waltham Abbey;51.6846 Catigbian;9.8333 Bcharré;34.2511 North Augusta;33.5214 Sheghnān;37.6167 Antanimieva;-22.2333 Limbuhan;11.8836 Döbeln;51.1194 Oldebroek;52.4500 Pakil;14.3833 Tranquebar;11.0292 Allendale;42.9845 Ambohimahamasina;-21.9333 Sehnde;52.3161 Santa Apolonia;14.7833 Bloemendaal;52.4000 Baud;20.8333 Quellón;-43.0992 Sulya;12.5610 East Gwillimbury;44.1333 Farmington;40.9845 Macaparana;-7.5550 Bagulā;23.3350 Lisle;41.7918 Lalganj;26.1277 Fairfax;38.8531 Perungudi;8.2792 Tecoluca;13.5333 Stepanavan;41.0096 Três Coroas;-29.5169 Freudenstadt;48.4633 Kafr Sa‘d;31.3594 El Zulia;7.9333 Kingston;41.9295 Embarcación;-23.2167 Paxtakor Shahri;40.3153 Plainsboro;40.3377 Aldeias Altas;-4.6278 Hollola;60.9886 La Maná;-0.9300 Wajimazakimachi;37.3906 Hopa;41.3903 Kalach-na-Donu;48.7000 Anchieta;-20.8056 Biancavilla;37.6500 Rodniki;57.1167 Columbus;41.4366 São Gonçalo do Sapucaí;-21.8919 Laje;-13.1819 Cambará;-23.0458 Loon op Zand;51.6667 El Tejar;14.6500 Três Passos;-27.4558 Jüchen;51.1011 Maldegem;51.2000 Nanxingguo;37.6306 Gungu;-5.7333 Carás;-9.0472 Akwatia;6.0500 Três de Maio;-27.7728 Bogotol;56.2000 Burgos;16.0465 Bhawānīgarh;30.2700 Garmdarreh;35.7642 Capoterra;39.1744 Quesada;14.2723 Lubliniec;50.6833 Rajpur;21.9402 Kalawit;7.9051 Fresno;29.5357 Dongsheng;36.9996 Andoany;-13.4000 Ringsted;55.4425 Ikeda;35.4423 Bazhajiemicun;38.8967 Hadžići;43.8217 Honda;5.2042 Monte Aprazível;-20.7728 Sacramento;-19.8650 Jawor;51.0500 Acton;42.4843 Majdel Aanjar;33.7075 Khenichet-sur Ouerrha;34.4333 Barnegat;39.7668 Chinoz;40.9375 San Miguel Ocotenco;18.9894 Sokolov;50.1814 Mirandela;41.4853 Alice Springs;-23.7000 Jurh;44.6961 Starnberg;47.9972 Champlin;45.1702 Oostkamp;51.1544 Papillion;41.1511 Douar Ouled Ayad;32.4167 Candoni;9.8167 Tecolutla;20.4797 Gaojiayingcun;40.8814 Palu;38.7039 Zemst;50.9722 Nyūzen;36.9335 Ibiapina;-3.9228 Makhmūr;35.7756 Saint-Dizier;48.6383 Columbus;33.5088 Pedra Azul;-16.0050 Zeya;53.7333 Upper Providence;40.1654 Popovo;43.3500 Abbeville;50.1058 Kasongo-Lunda;-6.4783 Lustenau;47.4271 Hillegom;52.2833 Berehove;48.2025 Quartier Militaire;-20.2500 Alexânia;-16.0819 King of Prussia;40.0963 Horgen;47.2667 Vélingara;13.1500 Kenilworth;52.3410 Jucás;-6.5250 Belton;38.8192 Hirpardangal;23.8200 Takehara;34.3333 El Maknassi;34.6042 Padre Burgos;13.9226 Grande-Synthe;51.0139 Lutz;28.1396 Chrudim;49.9511 Baliguian;7.8088 Cranford;40.6564 Mirandola;44.8873 Webster Groves;38.5866 Ramos;15.6667 Yesagyo;21.6333 San José;1.4744 Makouda;36.7909 Manuel B. Gonnet;-34.8500 Nísia Floresta;-6.0908 Cockeysville;39.4804 Wandlitz;52.7500 Brody;50.0781 Ostrów Mazowiecka;52.8000 Ituango;7.1667 Auburn;44.0851 Wadsworth;41.0279 Lincoln;43.1300 Elburg;52.4500 Mandan;46.8290 Honāvar;14.2809 Oulad Hassoune;31.6503 Tata;47.6526 Dunajská Streda;47.9944 Qifţ;25.9956 Nový Jičín;49.5944 Mosbach;49.3522 Belmonte;-15.8628 Kuvandyk;51.4667 Hachimantai;39.9561 Korbach;51.2833 Mansfield;42.0163 Burlington;40.0641 Cuyo;10.8500 Kuna;43.4880 Fria;10.3804 Ceccano;41.5667 Conil de la Frontera;36.2667 Maracás;-13.4408 Samrāla;30.8360 Tecali;18.9000 Luninyets;52.2500 Muscatine;41.4195 Funes;-32.9167 Dalyoni Bolo;39.7422 Frontignan;43.4483 Willoughby;41.6459 Bariārpur;25.2885 Santa Lucía Utatlán;14.7667 South Elgin;41.9906 Tadotsu;34.2729 Herdecke;51.4000 Ayancık;41.9500 Raul Soares;-20.1019 Icod de los Vinos;28.3500 Tatarsk;55.2500 Eisenhüttenstadt;52.1450 Gelnhausen;50.2000 Kedia;23.7945 Vilaseca de Solcina;41.1110 Salzwedel;52.8500 Ayt Mohamed;32.5667 Metekora;22.1881 Lebu;-37.6000 Soledad Atzompa;18.7550 Yellāpur;14.9637 Toda Rai Singh;26.0167 Beni Khalled;36.6500 Khromtaū;50.2503 Munster;41.5468 Alfreton;53.0970 San Juan Cotzocón;17.1667 Badnāwar;23.0218 San Lorenzo;12.3783 Bauta;22.9919 Kreuzlingen;47.6333 Yukon;35.5201 Highland;41.5483 Sarai Ranjan;25.7671 Miantso;-18.7167 Vadakarai Kīl Pidāgai;9.0401 Santo Antônio do Içá;-3.1019 Mandāwa;28.0500 Bonoufla;7.1333 Ravulapalem;16.7530 Qādirganj;24.9138 Płońsk;52.6333 Altea;38.5986 Corinto;-18.3808 Saginaw;32.8657 Sredneuralsk;56.9833 Gitega;-3.4260 Quảng Trị;16.7469 Saint-Ghislain;50.4500 Pokhuria;25.1307 Koziatyn;49.7167 Bulle;46.6167 Poggiomarino;40.8000 Carletonville;-26.3581 Qianmotou;37.7952 Thiotte;18.2500 Zheleznogorsk-Ilimskiy;56.5833 Dillenburg;50.7333 Krnov;50.0906 Leutkirch im Allgäu;47.8256 Fountain Hills;33.6073 Lentini;37.2833 Fucecchio;43.7333 Lupeni;45.3603 Lermontov;44.1167 Nova Milanese;45.5833 Benemérito;16.3333 Achhnera;27.1800 Caba;16.4316 Burhar;23.2149 Kyeintali;18.0061 Princesa Isabel;-7.7369 Rāmpura;24.4670 Lubartów;51.4667 Partizánske;48.6258 Cupira;-8.6169 Havlíčkŭv Brod;49.6078 Kulgam;33.6400 Káto Polemídia;34.6931 Sobradinho;-9.4550 Aşkale;39.9211 Ipatovo;45.7167 Urucurituba;-3.1308 Bronnitsy;55.4261 Severobaykalsk;55.6500 Xiaping;24.6168 Mallasamudram;11.4933 Parsāgarhi;26.0935 Caculé;-14.5028 Westerstede;53.2500 Unecha;52.8461 Dumaran;10.5333 Zărneşti;45.5667 Canalete;8.7900 Neusäß;48.4000 San Felíu de Guixols;41.7806 Alcalá;4.6667 Ādampur;31.4322 Southold;41.0432 San Ignacio de Velasco;-16.3667 Gamay;12.3833 Maibog;10.1500 São Sepé;-30.1608 Wallenhorst;52.3500 El Paso;9.6622 Gerede;40.8006 Bad Salzungen;50.8117 Chantilly;38.8868 West Springfield;38.7771 Montgomery;40.4260 Pesochin;49.9539 Kenmore;47.7516 Nagla;29.0100 Floridia;37.0833 Roseburg;43.2231 Dongshi;23.4748 Sonāri;27.0246 Ruwa;-17.8897 Tugatog;14.6629 Brodósqui;-20.9908 North Platte;41.1263 Bni Frassen;34.3833 Kurugodu;15.3460 Ron;15.6700 Elektrogorsk;55.8833 Karahrūd;34.0600 Ahualulco de Mercado;20.6992 Cataño;18.4375 Frederikshavn;57.4410 Santo Tomás;10.7667 Miyoshi;34.0260 Maigo;8.1500 Mégara;37.9964 Ribas do Rio Pardo;-20.4428 Vega Baja;18.4406 Pace;30.6188 Nagykőrös;47.0332 Bondo;3.8167 Eschborn;50.1436 Senden;48.3167 Davlekanovo;54.2167 Nijlen;51.1667 Gersthofen;48.4167 Patterson;37.4758 Tirat Karmel;32.7667 Jieshang;26.2663 Muang Sing;21.2000 Sederot;31.5228 Maple Heights;41.4094 Villanueva de la Cañada;40.4500 Neihuzhai;22.9745 Barbasa;11.1961 Pissila;13.1667 Rock Springs;41.5947 Sonepur;20.8333 Teguise;29.0611 Assaré;-6.8739 Nasu;37.0198 Martí;22.9522 Medeiros Neto;-17.3739 Muttupet;10.3950 Jacksonville Beach;30.2782 Haar;48.1000 Monte Caseros;-30.2500 Gālīkesh;37.2719 Ban Thoet Thai;20.3084 Almeirim;39.2000 Lensk;60.7333 ’s-Gravenzande;52.0000 Sakleshpur;12.8930 Ḑank;23.5500 Ambohimiadana;-19.2333 Belo Oriente;-19.2200 Sluis;51.3083 Shuilin;23.5662 Calamba;8.5583 Frascati;41.8167 Lunbei;23.7789 Pajapita;14.7243 Santa Helena;-24.8600 Happy Valley;45.4358 Piraí do Sul;-24.5258 Waukee;41.5985 Brotas;-22.2839 Rucphen;51.5333 Ortona;42.3500 Nakūr;29.9189 Kumano;34.3358 Brookings;44.3022 Raritan;40.5070 Doctor Mora;21.1425 Épernay;49.0403 Riehen;47.5833 Gaoshu;22.8202 Johnston;41.6910 Laguna de Duero;41.5833 Veinticinco de Mayo;-35.4167 Moskva;37.6567 Calabasas;34.1375 Torre del Mar;36.7500 Ban Bang Rin;9.9531 Teorama;8.4353 Çağlayancerit;37.7506 Scherpenheuvel;51.0103 General Luna;9.7836 Cordeirópolis;-22.4819 Cuilapan de Guerrero;16.9972 San Lorenzo de Esmeraldas;1.2658 Carandaí;-20.9539 Akkuş;40.7931 Calbiga;11.6333 Vernāg;33.5377 Achkhoy-Martan;43.1939 Ortigueira;-24.2078 Farragut;35.8731 Chākia;26.4160 San José Ojetenam;15.2167 Tobatí;-25.2600 Khirkiyān;22.1673 Salcajá;14.8833 Cajidiocan;12.3667 Beshariq;40.4358 Avellaneda;-29.1175 Qaţanā;33.4333 Nyakrom;5.6167 Cullera;39.1639 Karben;50.2322 Piteå;65.3333 Goumori;11.1833 Caniço;32.6412 Vengattūr;13.0999 La Prairie;45.4200 Melchor Romero;-34.9492 Palmeiras de Goiás;-16.8050 Cide;41.8922 Westerly;41.3635 Taylors;34.9157 San Pablo Jocopilas;14.5833 Olivet;47.8639 Nakagusuku;26.2678 Píritu;9.9178 Miantsoarivo;-19.2000 Monteros;-27.1667 Waverly;42.7401 Bermo;23.7878 Collipulli;-37.9500 Telica;12.5228 Moncada;39.5456 Tiruverumbūr;10.7937 Wilmington;42.5609 Simaria;25.5671 Santo Tomé;-28.5500 Taquarituba;-23.5328 Douar Imoukkane;35.0298 Kirkintilloch;55.9380 Fangliao;22.3656 Combs-la-Ville;48.6700 Baile Átha Luain;53.4236 Linda;39.1241 Moirāng;24.3490 Yoshioka;36.4474 Warrenton;-28.1114 Griffin;33.2418 Jiucangzhou;38.2060 Kaler;25.1180 Netphen;50.9147 Vilnohirsk;48.4781 Minamata;32.2167 Zaruma;-3.6833 New Brighton;45.0658 Añatuya;-28.4667 Oliveira do Bairro;40.5167 Tubungan;10.7833 Iguaba Grande;-22.8389 La Cruz de Río Grande;13.1122 Qiryat Mal’akhi;31.7333 Teodoro Sampaio;-22.5328 Alcobaça;-17.5189 Mine;34.1631 Kulattuppālaiyam;10.7613 Sheybān;31.4086 Shāhbāzpur;24.0531 Shiling;27.3576 East Retford;53.3228 Velugodu;15.7817 Qiryat Shemona;33.2075 Tsaratanana;-21.1833 Riachão das Neves;-11.7458 Puchheim;48.1500 Perāvūrani;10.3000 Ulukışla;37.5467 Gobō;33.8833 Culion;11.8944 Karlsfeld;48.2167 Blangmangat;5.1881 Palmar de Varela;10.7500 Tambaú;-21.7050 Selim;40.4633 Ozuluama de Mascareñas;21.6667 Farmington;44.6572 Asafābād;19.3650 Senador Guiomard;-10.1497 Kapolei;21.3399 Carmen;8.9999 Nakashunbetsu;43.5552 Devonport;-41.1800 Pilar de la Horadada;37.8667 Kūn Puhāl;33.0058 Tototlán;20.5333 Tighedouine;31.4236 Lagoa;37.1333 Puenteareas;42.1667 Mentana;42.0167 Tadmaït;36.7427 Tecumseh;42.2431 Ibiá;-19.4778 Loves Park;42.3364 Nevyansk;57.4833 Itaporã;-22.0789 ‘Ālī Shahr;28.9306 Tūkrah;32.5322 Bassar;9.2500 Linstead;18.1368 Touboro;7.7708 Warburg;51.5000 Beek en Donk;51.5347 Van Buren;35.4483 Kolpashevo;58.3000 Haywards Heath;51.0048 Coração de Maria;-12.2328 Guaraí;-8.8339 Vranov nad Topľou;48.8808 Yásica Arriba;19.6333 Colón;-33.8833 Karhula;60.5155 Molodohvardiisk;48.3444 Areia;-6.9628 Clinton;41.1395 Simpsonville;34.7287 Gormi;26.6003 Hulikal;11.3193 Abreus;22.2806 Montgomery;41.5399 Himora;14.2861 Esparraguera;41.5381 Colón;-32.2167 Fokino;42.9667 Ankireddikuntapālem;16.2884 Christiansburg;37.1406 Kodarmā;24.4675 Tnine Lgharbia;32.5719 Allison Park;40.5730 Koło;52.2000 Trotwood;39.7926 Bedford;42.9406 Tapaktuan;3.2500 Puerto Concordia;2.6833 Ambohitrarivo;-17.4667 Wareham;41.7662 Roxbury;40.8822 Sandviken;60.6167 Crystal;45.0377 Bafilo;9.3500 Zunheboto;25.9667 Sidi Ifni;29.3833 Kilkís;40.9833 Mountain House;37.7673 Balen;51.1667 Fleurus;50.4833 Laguna Beach;33.5455 Okahandja;-21.9833 Gif-sur-Yvette;48.7018 Losser;52.2667 Paravai;9.9690 Mékhé;15.1167 Ballenger Creek;39.3807 Baclayon;9.6227 Komló;46.1912 Weilheim;47.8333 Tashtagol;52.7667 Pitogo;13.7850 Tupiza;-21.4375 Senda;24.4976 Prieto Diaz;13.0408 Cholavandān;10.0167 Yunak;38.8172 Mangidy;-21.2500 Nanyangcun;34.7015 Sīwah;29.2000 Niskayuna;42.8030 Nazareth;8.5562 Ambohitoaka;-16.0167 Câmpia Turzii;46.5486 Eastmont;47.8968 Gusinoozërsk;51.2833 Garden City;40.7266 Gainsborough;53.4016 Mahela;-20.9667 Azové;6.9500 Yuli;41.3351 Stoneham;42.4741 Bhatpurī;29.1700 Santa Maria do Pará;-1.3519 Nikaho;39.2030 Kapfenberg;47.4394 Wulongpu;37.9356 Amoucha;36.3880 Searcy;35.2418 Oyonnax;46.2561 Meiwa;34.5500 Überlingen;47.7667 Mercato San Severino;40.7833 Vigonza;45.4500 Strakonice;49.2615 Lexington;33.9890 Wichian Buri;15.6565 Achí;8.5667 Minbu;20.1800 Farahalana;-14.4333 Gardner;38.8122 Keta;5.9167 Seva;5.9845 Dindori;22.9414 Conceição das Alagoas;-19.9150 Gyêgu;33.0166 Union Hill-Novelty Hill;47.6788 Lysander;43.1799 Rastede;53.2500 Andalatanosy;-24.6667 Shurugwi;-19.6667 Niamtougou;9.7667 Morris;40.7959 Huixtán;16.6833 Cheriāl;17.9264 Yeadon;53.8690 Analamisampy;-22.4667 Curumaní;9.2000 Huaibaijie;35.7056 Guanajay;22.9306 Orobó;-7.7450 Tiruttangal;9.4833 Junction City;39.0277 Barbate de Franco;36.1833 Makīnsk;52.6292 Tembagapura;-4.1428 Ondangwa;-17.9167 Grajewo;53.6500 Watertown;43.1893 Pawni;20.7800 Ōno;35.4706 Marovato;-15.9000 Aiud;46.3122 Greenfield;39.7937 Hopewell;37.2915 Ankaramy;-13.9833 Vale de Cambra;40.8500 Alâattin;37.4667 Büdingen;50.2908 Pirenópolis;-15.8519 Araçariguama;-23.4386 Oakdale;37.7618 Viga;13.8667 Famaillá;-27.0500 Ankilizato;-20.4000 Krasnouralsk;58.3500 Zedelgem;51.1431 Xinsi;34.6503 Krasnoarmeysk;51.0231 Yanshanbu;26.3326 Soure;-0.7169 Fenyuan;24.0154 Shelbyville;35.4987 Beixingzhuang;35.7033 Hudson;41.2399 Middletown;39.4450 Roselle;41.9809 Barakī Barak;33.9692 Nagold;48.5519 Abu;24.5925 Vagos;40.5500 Kerūr;16.0138 Guadalupe;2.0250 Danwān;25.5292 Al Fuḩayş;32.0167 Ivatsevichy;52.7167 Forbach;49.1900 Brushy Creek;30.5128 Luruaco;10.6083 Sarāri;26.2523 Keene;42.9494 Las Breñas;-27.0667 Wädenswil;47.2333 Dabeiba;7.0000 Kulkent;40.1500 Zeewolde;52.3333 Laupheim;48.2289 Kanchika;16.4125 Manghit;42.1167 Dokuchaievsk;47.7506 Pointe à Raquettes;18.7833 Palma di Montechiaro;37.1936 Mount Pearl Park;47.5189 La Democracia;14.2333 Itaocara;-21.6789 Edgewater;28.9594 Znamianka;48.7136 Świebodzice;50.8667 Edewecht;53.1258 Rivera;2.7853 Wertheim;49.7500 Malacatancito;15.3167 Bad Kissingen;50.2000 West Goshen;39.9756 Oteiza;8.7437 Maying;36.0448 Ayagawa;34.2496 Nasrullāhganj;22.6837 Candelaria de La Frontera;14.1167 Saint Austell;50.3400 Pillaiyārkuppam;11.8119 Goulburn;-34.7547 Espera Feliz;-20.6500 Selvazzano Dentro;45.3833 Vreden;52.0333 Manosque;43.8342 Nixa;37.0453 Tervuren;50.8167 Mihama;34.7789 Lumbatan;7.7850 Gōtsuchō;35.0167 Ayos;3.9069 Isilkul;54.9500 São Sebastião da Boa Vista;-1.7178 Mutuípe;-13.2289 Jogipet;17.8356 Eckernförde;54.4742 Thebes;38.3208 El Hermel;34.3914 Kitzingen;49.7333 Buriti Bravo;-5.8369 Camp Springs;38.8052 Haderslev;55.2428 Patacamaya;-17.2333 Trezzano sul Naviglio;45.4333 Conceição da Feira;-12.5058 Garoua Boulaï;5.8833 Prāntij;23.4384 Arcos de Valdevez;41.8472 Kalat;29.0258 Robertson;-33.8000 Guryevsk;54.2833 Lengerich;52.1750 Allen;-38.9667 Nazca;-14.8289 Patharia;23.8992 Santa Teresita;13.8664 Candler-McAfee;33.7267 Moncagua;13.5333 Catamayo;-3.9833 Winchester;42.4518 Machesney Park;42.3666 Glauchau;50.8233 San Cataldo;37.4833 Banovići;44.4000 Belton;31.0525 Marshall;32.5370 Aināpur;16.4800 Naciria;36.7500 Manki;14.1789 Alnif;31.1167 Alangāyam;12.6223 Aybastı;40.6867 Amtali;22.1370 Karachayevsk;43.7731 Ecoporanga;-18.3728 Saint-Jean-de-Braye;47.9128 Kihei;20.7653 Radcliff;37.8204 Butig;7.7239 Shaomi;26.4727 Nogoyá;-32.4000 Goldasht;32.6267 Jarqo‘rg‘on;37.5083 Cartagena;-33.5425 Āmodei;26.8975 Singur;22.8100 Działdowo;53.2333 Røyken;59.7336 Chincholi;17.4651 Itambacuri;-18.0364 Phillaur;31.0300 Ivato;-20.6167 Tomarza;38.4472 Katangi;23.4412 Chaves;-0.1600 Onondaga;42.9686 Khosrowshahr;37.9497 Nāḩiyat al Kifl;32.2242 Whyalla;-33.0333 Couëron;47.2156 Cinco Saltos;-38.8167 Curchorem;15.2500 Prairie Village;38.9874 Kāchhāri;22.0732 Kadiapattanam;8.1312 Santa Fe de Antioquia;6.5500 Itaparica;-12.8878 Wettingen;47.4659 Kutina;45.4667 Penzance;50.1190 Lazi;9.1280 Howli;26.4224 Acatlán;20.1444 Béguédo;11.7833 Diriomo;11.8753 Magallanes;9.0225 Leland;34.2042 Cachoeira do Arari;-1.0108 Agrestina;-8.4500 Kigumba;1.8150 Oxford;39.5061 Naqādah;25.9000 Velsk;61.0667 Oliveira dos Brejinhos;-12.3169 Cottage Lake;47.7466 Rixensart;50.7167 Sarzana;44.1136 Baravāt;29.0664 Lagoa Grande;-8.9969 Svetlyy;54.6667 Tiruvankod;8.2452 Forney;32.7440 Asaita;11.5667 Valašské Meziříčí;49.4718 Montilla;37.5833 Mrągowo;53.8667 Alhama de Murcia;37.8514 Łęczna;51.3000 Beşikdüzü;41.0494 Massarosa;43.8667 Petrila;45.4500 Villa Park;41.8865 San Miguel de Papasquiaro;24.8304 Moguer;37.2747 Iracemápolis;-22.5808 Kalachinsk;55.0500 Anthem;33.8560 Bambuí;-20.0058 Parabcan;13.7100 Ladyzhyn;48.6897 Byala Slatina;43.4667 Sarangani;5.4120 Arroyito;-31.4167 Dom Pedro;-5.0328 Buritis;-15.6178 Perundurai;11.2777 Pinheiral;-22.5128 Buenavista;8.2233 Donaueschingen;47.9531 Timmāpur;19.0484 Rosita;13.9239 Amalfi;6.9167 Rāvar;31.2656 Alta Floresta D’Oeste;-11.9283 Staraya Kupavna;55.8000 Árta;39.1650 Peters;40.2739 Aljaraque;37.2667 Manzanares;5.2519 Metzingen;48.5367 Raymore;38.8029 Seynod;45.8889 Frameries;50.4088 Sāmbhar;26.9103 Ambriz;-7.8500 Lefkáda;38.7178 Šaľa;48.1503 Apolda;51.0167 East Peoria;40.6736 Supaul;25.9469 Friesoythe;53.0206 Mondovì;44.3833 Alnāvar;15.4273 Mandelieu-la-Napoule;43.5464 Pushkar;26.4903 Naawan;8.4333 Zhengtun;25.1494 Magdalena;20.9092 Uenohara;35.6302 Khaw Zar Chaung Wa;15.0375 Oytal;42.9153 Pickerington;39.8890 Lucala;-9.6333 Sondrio;46.1697 Mānjhi;25.8384 Llaillay;-32.8403 Souk Tlet El Gharb;34.6211 Pachino;36.7167 Forquilhinha;-28.7469 Lochristi;51.1000 Le Creusot;46.8014 Duncan;34.5408 Sant’Arcangelo di Romagna;44.0633 Huangzhai;38.0605 Talwandi Sābo;29.9838 Quemado de Güines;22.7900 Ouani;-12.1350 Lajas;22.4164 Darton;53.5850 Taulahā;26.9566 Annecy-le-Vieux;45.9192 Pudsey;53.7970 Easley;34.8188 Tocantinópolis;-6.3289 Singampunari;10.1815 Goleniów;53.5636 Saint-Genis-Laval;45.6960 Qādiān;31.8192 Morombe;-21.7500 Bom Jesus;-9.0739 Lempäälä;61.3167 Artémida;37.9667 Sek’ot’a;12.6253 Yahualica de González Gallo;21.1781 Eislingen;48.6933 Medjez el Bab;36.6436 Cartersville;34.1644 Los Llanos;18.6167 Bukkarāyasamudram;14.6944 Sun Valley;39.6104 Omagh;54.5900 Umbaúba;-11.3828 Pāpanāsam;10.9273 Junnārdev;22.2000 Bhadaur;30.4764 Corcoran;36.0841 Stadthagen;52.3247 Gonghe;35.3308 Quaraí;-30.3878 Burg;52.2725 Flörsheim;50.0167 Furukawa;36.2381 Bellview;30.4620 Bilthoven;52.1283 Makó;46.2170 Ganapavaram;16.1233 Şirvan;38.0633 Isperih;43.7167 Gurmatkāl;16.8677 Biswanath Chariali;26.7278 Yokoshibahikari;35.6658 Puente Nacional;19.3333 San Antonio La Paz;14.7500 Chinameca;13.5000 Huautla;21.0308 Paraopeba;-19.2739 Sardinata;8.0833 Summit;40.7154 Goianinha;-6.2669 Pāmarru;16.3270 Sivagiri;11.1200 Chāgallu;16.9833 Dawmat al Jandal;29.8153 Gölhisar;37.1408 Oshwe;-3.4188 Porto Torres;40.8369 Sandbach;53.1460 Piddig;18.1647 Watertown;44.9094 Batad;11.4167 Bélabo;4.9333 Paese;45.6667 Ariano Irpino;41.1528 Martellago;45.5467 Kadiria;36.5333 Biggleswade;52.0855 Fougères;48.3525 Río Ceballos;-31.1667 Wenceslau Guimarães;-13.6869 Caravelas;-17.7319 Braine-le-Comte;50.6000 Buftea;44.5700 Ginosa;40.5000 Cruz;-2.9178 Barra Velha;-26.6319 Doutou;6.5500 Las Torres de Cotillas;38.0264 Agropoli;40.3500 Pogradec;40.9000 Heide;54.1961 Santo Antônio de Posse;-22.6058 Eustis;28.8563 Jaipur Chuhar;25.4187 Sevlievo;43.0256 Quickborn;53.7333 Mutatá;7.2442 Kundgol;15.2561 Árgos;37.6167 Radevormwald;51.2000 Jebba;9.1539 Nadvirna;48.6333 Gaoguzhuang;37.8364 Chester;37.3531 Santa Fe;11.1856 Khadyzhensk;44.4256 Saktī;22.0300 Ambohimanga;-18.7667 Auch;43.6465 Vilcún;-38.6500 Geertruidenberg;51.7000 Kannānendal;9.9649 Sucre;8.8139 Maribojoc;9.7500 Karnobat;42.6333 Upper Allen;40.1800 Malaba;0.6444 Zhedao;24.8098 Taixi;23.7000 Kālchīni;26.6979 Ấp Phú Mỹ;9.7500 Vendram;16.5360 Zambrów;52.9833 Rupauli;25.8695 Gorgonzola;45.5333 Padmanābhapuram;8.2446 South Ockendon;51.5207 Srīperumbūdūr;12.9680 Chatra Gobraura;26.4973 Dasnāpur;19.6536 Halluin;50.7836 Ibatiba;-20.2364 Koksijde;51.1000 Betsizaraina;-19.9500 Soyāgaon;20.5514 El Colegio;4.5808 Yuli;23.3875 Zirāpur;24.0222 Boquira;-12.8228 Namayumba;0.5281 Tepexi de Rodríguez;18.5797 Gardelegen;52.5264 Bloomingdale;41.9497 Ōtsuki;35.6106 Kopřivnice;49.5995 Aleksandrów Łódzki;51.8194 Otopeni;44.5500 Arouca;40.9338 Schwetzingen;49.3833 Juatuba;-19.9519 Palmas de Monte Alto;-14.2669 Arbi’a Tighadwiyn;31.4236 Sarapāka;17.6922 Şā al Ḩajar;30.9647 Tiruppuvanam;9.8550 Târnăveni;46.3297 Lincoln;41.9171 Vakon;6.5167 Barra da Estiva;-13.6258 Zurbāţīyah;33.1500 Gubbi;13.3122 Klatovy;49.3956 Santa Isabel do Rio Negro;-0.4139 Hockenheim;49.3181 Phulpur;25.5500 Daxin;26.6210 Vernon;41.1973 Zvenigorod;55.7333 Dudelange;49.4833 Casal di Principe;41.0167 Borgomanero;45.7000 Mendeleyevsk;55.9000 Cornaredo;45.5000 Goiás;-15.9339 Sevilimedu;12.8083 Zhucaoying;40.1759 Chaumont;48.1117 Guérou;16.8167 Chankou;35.7754 La Cruz;-32.8281 Atuntaqui;0.3317 Masis;40.0633 Binnish;35.9500 Takahata;38.0027 Guaratinga;-16.5839 Yahualica;20.9531 Greenwood;34.1947 Senekane;-29.2525 Chapa de Mota;19.8144 Caraí;-17.1889 Pākāla;13.4667 San Jose;9.4167 Gīlān-e Gharb;34.1422 Cisnădie;45.7128 Hoogezand;53.1572 Ulus;41.5861 Vohimasina;-21.7333 Rajauli;24.6449 Ḩārim;36.2000 Anamalais;10.5830 Morinda;30.7900 Waldkirch;48.1000 Anini-y;10.4325 Qorako‘l Shahri;39.5028 Colorado;-22.8378 Fantino;19.1200 Natagaima;3.5833 Ingabu;17.8167 Saurh;25.3789 Mārutūru;15.9862 Fernley;39.5627 Guabo;-3.2388 Montville;40.9135 Bronkhorstspruit;-25.8050 Irauçuba;-3.7458 Palestrina;41.8333 Sulejówek;52.2442 Murayama;38.4833 Ampasimanjeva;-21.7333 Lyman;48.9853 El Doncello;1.6833 Biddeford;43.4673 El Tortuguero;12.8183 São João do Paraíso;-15.3139 Salcedo;11.1500 Acworth;34.0566 Hallim;33.3729 Priego de Córdoba;37.4333 Husnābād;17.0667 Bahçe;37.2000 Guazacapán;14.0751 Rioja;-6.0500 Alakamisy-Ambohimaha;-21.3167 Pingtan;23.2524 Nikolskoye;59.7000 Feira Grande;-9.9000 Ban Pa Tueng;20.1339 Gardanne;43.4553 Ituporanga;-27.4139 Kuppam;12.7450 Lyndhurst;40.7965 Ouardenine;35.7200 Djinet;36.8770 Digor;40.3756 Laja;-37.2667 Bristol;41.6827 Zoersel;51.2667 Banbishancun;40.3700 Herent;50.9081 Gujan-Mestras;44.6364 West Deptford;39.8431 Tupanciretã;-29.0808 Basi;30.5872 São Miguel;-6.2119 Santo Antônio;-6.3108 Taveta;-3.3956 São Miguel do Araguaia;-13.2750 Colfontaine;50.4057 Ruston;32.5328 Koduvilārpatti;9.9695 Corail;18.5667 Ilsede;52.2667 Mandirituba;-25.7789 Unchahra;24.3825 Bad Harzburg;51.8811 Ḩamīdīyeh;31.4850 Woensdrecht;51.4167 Aswāpuram;17.8348 San Felipe;31.0275 Hillsborough;40.5069 Reigate;51.2370 Bressanone;46.7165 Ugong;14.5842 Albertville;34.2633 Comacchio;44.7000 Les Pennes-Mirabeau;43.4106 Lihuzhuang;39.6520 Mūlki;13.1000 Koāth;25.3264 Thuân An;16.5489 Itigi;-5.7000 Repelón;10.5500 Conselheiro Pena;-19.1719 Jāwad;24.5992 Naregal;15.5732 Martha Lake;47.8479 Salvaterra de Magos;39.0167 Eppingen;49.1333 Caojiachuan;34.9016 Golden Valley;44.9901 Marina;36.6810 Afourar;32.2167 Rio Formoso;-8.6639 Normanton;53.6970 Takelsa;36.7833 Andranovorivato;-21.6333 Corinto;12.4819 Sahil;40.2200 Yavuzeli;37.3192 Ladário;-19.0050 Shabestar;38.1800 Bonney Lake;47.1791 Coralville;41.6990 Hazebrouck;50.7250 Nova Zagora;42.4911 Corinth;33.1434 Moineşti;46.4333 Itamaracá;-7.7478 Oxford;33.5967 Gümüşhacıköy;40.8667 Castrovillari;39.8167 Bato;13.6000 Flores;-7.8658 Imbert;19.7500 Mandapam;9.2822 Mountain Brook;33.4871 Villeneuve-sur-Lot;44.4081 Nangavaram;10.8692 Chesham;51.7120 Luofa;39.4343 Laventille;10.6500 San Agustin;16.5167 Madakasīra;13.9369 Bavly;54.3833 Bad Rappenau;49.2333 Jugiāl;32.3684 Zundert;51.4667 Qarazhal;48.0253 Clarksville;38.3220 Belonia;23.2500 Tabursuq;36.4572 Bellavista;-7.0668 Sem;59.2792 Oberwingert;47.5170 Qapqal;43.8340 São Jerônimo;-29.9589 Jiuru;22.7333 Selouane;35.0667 Silvania;4.4033 Huejúcar;22.3167 Lom;43.8256 Ambalaroka;-22.1333 Florida Ridge;27.5805 Machico;32.7000 Waynesboro;38.0674 Madamba;7.8833 Hardi;26.0785 Chambersburg;39.9315 Patpāra;26.4328 Sandhurst;51.3490 Karlovo;42.6436 Belas;38.7754 Kommunar;59.6333 Beni Douala;36.6167 Neu Wulmstorf;53.4628 Bemanonga;-20.2833 Honmachi;36.0609 Palmer;40.7007 Millau;44.0986 Ampanotokana;-18.7167 Nirmāli;26.3140 São Sebastião do Caí;-29.5869 Świedbodzin;52.2500 Maravilha;-26.7700 Iarintsena;-21.8167 Pailitas;8.9667 Arroyito;-23.1733 Villa de Leyva;5.6333 Salémata;12.6333 Una;-15.2928 Vecsés;47.4057 Crowborough;51.0600 Shiroishi;33.1811 Silver Firs;47.8635 Mailavaram;16.7833 Cortona;43.2756 Cherry Hill;38.5696 El Pinar;-34.7972 Guilford;41.3345 Watertown;41.6160 Bloomington;34.0601 Palafrugell;41.9174 Cunha;-23.0744 Muelle de los Bueyes;12.0664 Yaojiafen;40.7158 Palmital;-22.7889 Manlleu;42.0000 Paramirim;-13.4428 Lower;38.9819 Merefa;49.8197 Kontich;51.1333 Māngrol;25.3300 Timbiquí;2.7667 Bataguaçu;-21.7139 Darien;41.7448 Versmold;52.0436 Tacaratu;-9.1058 Ambodimotso Atsimo;-15.3833 Ust’-Katav;54.9333 Cuncolim;15.1773 Horizon City;31.6799 Elsdorf;50.9333 San Martín;-31.4397 Jafaro;-24.9167 Tiquisio;8.5667 Semikarakorsk;47.5167 Hastings;44.7318 Taminango;1.5667 Tola;11.4392 Shendurjana;21.5217 Mazagão;-0.1150 Kueneng;-29.0161 Sonthofen;47.5158 Póvoa de Lanhoso;41.5667 Chillicothe;39.3393 Tufānganj;26.3200 Ankiabe-Salohy;-15.6000 Phulaut;25.5114 Seligenstadt;50.0333 Hlohovec;48.4333 Kozmodemyansk;56.3367 Poço Verde;-10.7078 Imi-n-Tanout;31.1600 Chalchihuitán;17.0333 Amaraji;-8.3758 San Bonifacio;45.4000 Khānāpur;15.6394 Vilyuchinsk;52.9306 Kotli;33.5156 Carutapera;-1.1950 Bānapur;19.7789 Rancho San Diego;32.7624 Antsakabary;-15.0500 Omutninsk;58.6667 DeBary;28.8815 Ensley;30.5259 Picasent;39.3611 Tewkesbury;51.9900 Barhampur;26.3042 Tân Phong;19.7322 Manakambahiny;-17.8000 Andilanatoby;-17.9333 Machagai;-26.9167 Gigaquit;9.5947 Bungotakada;33.5500 Bañolas;42.1194 Thenia;36.7278 Khowai;24.0650 San Luis Talpa;13.4667 Kotovo;50.3000 Richmond;51.4560 Conway;33.8401 Pascagoula;30.3666 ‘Adrā;33.6000 Mangalvedha;17.5167 Axapusco;19.7194 Pitrufquén;-38.9833 Birkerød;55.8333 La Porte;41.6069 Vereshchagino;58.1000 New Castle;40.9956 San Rafael del Yuma;18.4333 Xanten;51.6622 Brambleton;38.9803 Coreaú;-3.5328 Kemi;65.7361 Remígio;-6.9028 Chalmette;29.9438 Isernia;41.6028 Guía de Isora;28.2110 Guipos;7.7350 Winter Gardens;32.8376 Mühldorf;48.2456 Yoqne‘am ‘Illit;32.6594 Nøtterøy;59.2011 Polkowice;51.5000 Anūppur;23.1034 Iisalmi;63.5667 Opoczno;51.3772 East Ridge;34.9973 Amposta;40.7106 Looc;12.2605 Pedernales;0.0700 El Ksiba;32.5681 Montclair;38.6111 Caaporã;-7.5158 West Pensacola;30.4263 Trinidad;-33.5333 Gummudipūndi;13.3995 Port Hueneme;34.1618 Isla-Cristina;37.1992 MacArthur;10.8356 Membakut;5.4667 Goodlands;-20.0350 Halle;52.0667 Tornio;65.8500 Rātu;23.4204 Darreh Shahr;33.1397 Bhānpura;24.5108 Amarpātan;24.3137 Tongzhou;25.7716 Mehnatobod;37.7167 Cabucgayan;11.4719 Osterode;51.7286 Matinha;-3.1008 Colangute;15.5450 Amherstburg;42.1000 Hallein;47.6667 Parsippany;40.8645 Katy;29.7905 Samba Cango;-9.1000 Caxambu;-21.9769 Parramos;14.6078 Los Palmitos;9.3811 Lālru;30.4867 Lohutí;40.2500 Sermādevi;8.6873 Józefów;52.1333 Freire;-38.9558 Caranavi;-15.8333 Almoradí;38.1097 Ayvacık;41.0036 Dax;43.7100 Gallup;35.5182 Potters Bar;51.6980 Had Oulad Issa;32.9519 Azambuja;39.0667 Casablanca;-33.3167 Sardulgarh;29.6970 Colônia Leopoldina;-8.9089 Shinhidaka;42.3413 Uruçuca;-14.5928 Orzesze;50.1500 Colle di Val d’Elsa;43.4000 Ginebra;3.7500 South Euclid;41.5240 New Hartford;43.0586 Mount Pleasant;43.5966 San Fernando;12.4858 Ayamonte;37.2000 Gros Islet;14.0810 Nāranammālpuram;8.7599 Maghalleen;-30.0942 Halewood;53.3599 Tādikombu;10.4390 Fukuyoshi;33.6832 Alcântara;-2.4089 Sainte-Suzanne;19.5833 Farakka;24.8200 Yabu;35.4000 Alliance;40.9107 Nogales;-32.7350 Mashiko;36.4673 Codlea;45.6969 Scarborough;43.5911 Olho d’Água das Flores;-9.5358 Smithfield;41.9014 Tigbao;7.8205 Santa Teresa;-19.9358 Brockville;44.5833 Ramona;33.0474 Sapulpa;36.0091 Mbandjok;4.4500 Ocotlán de Morelos;16.7914 Essex;44.5196 Tidili Masfiywat;31.4667 Ozoir-la-Ferrière;48.7780 Cagwait;8.9181 Piazza Armerina;37.3833 Nowa Ruda;50.5833 Siilinjärvi;63.0750 Pueblo Juárez;17.7000 East Patchogue;40.7704 Forquilha;-3.7978 Itajuípe;-14.6778 Ma’mūnīyeh;35.2992 Kumla;59.1167 Fairhope;30.5209 San Martín de las Pirámides;19.7333 Chełmno;53.3492 Nellimarla;18.1667 Bornem;51.1000 Sanford;43.4244 Pendurti;17.8278 Puerto Triunfo;5.8667 São Francisco de Paula;-29.4478 São Lourenço d’Oeste;-26.3589 Kosgi;16.9839 San Bernardo;1.5108 Amizmiz;31.2167 Ambatomanoina;-18.3167 Allauch;43.3369 Collingwood;44.5000 G‘azalkent;41.5625 Gülşehir;38.7464 Monsummano;43.8667 Puruk Cahu;-0.6384 Del City;35.4483 Pergine Valsugana;46.0667 El Callao;7.3463 Etajima;34.2167 Sori;10.7281 Unity;40.2811 Corciano;43.1333 Zara;39.8950 Palo del Colle;41.0500 Can-Avid;12.0000 Mezdra;43.1500 Srīvaikuntam;8.6318 Bilohorodka;50.4000 Chaudfontaine;50.5833 Qal’acha;40.1333 Miguelópolis;-20.1794 Zonhoven;50.9853 Shanywathit;17.4244 Kurikka;62.6167 Vicksburg;32.3173 Aichach;48.4500 São Marcos;-28.9708 Chernogolovka;56.0000 Tinja;37.1667 Moularès;34.4833 Huy;50.5167 Gopavaram;14.7841 Sihu;23.6411 American Canyon;38.1796 Germersheim;49.2167 Gökçebey;41.3081 Göd;47.6906 Kaikalūr;16.5667 Birmingham;42.5446 Kibeho;-2.6333 Evans;40.3660 Stadtallendorf;50.8333 Cujubim;-9.3628 Kavak;41.0736 Duarte;34.1610 Kobilo;15.9251 Xinzhai;26.6959 Brent;30.4727 Kami;38.5718 Novopokrovka;42.8708 Noāmundi;22.1609 Klamath Falls;42.2195 Shirhatti;15.2313 Alcalá la Real;37.4500 Eyl;7.9667 Nāmagiripettai;11.4700 Nuenen;51.4733 Lisieux;49.1500 Manor;39.9848 Nerja;36.7469 Zerbst;51.9681 Montecatini Terme;43.8828 Vicente Noble;18.3833 Tlaltenango de Sánchez Román;21.7815 Bayou Cane;29.6243 Łaziska Górne;50.1500 Sedalia;38.7059 Marvast;30.4775 Ashland;38.4592 Mānikganj;23.8500 San Jacinto;9.8311 São Felipe;-12.8469 Cassilândia;-19.1128 Nova Brasilândia d’Oeste;-11.7197 Follonica;42.9189 Wülfrath;51.2833 Āndippatti;9.9980 New Hope;45.0375 Muhlenberg;40.3955 Büren;51.5500 Makulubita;0.5122 Entroncamento;39.4653 Kangal;39.2367 Yenice;41.2000 Granite Bay;38.7601 Waghäusel;49.2500 Kant;42.8833 Nefta;33.8833 Gīnīr;7.1333 Rancheria Payau;7.8509 Bhojpur Kadīm;25.5841 Yucca Valley;34.1234 Senaki;42.2689 Palestina de los Altos;14.9333 Crisópolis;-11.5108 Rāmnagar Farsāhi;25.8904 Denain;50.3286 Paraíso;9.8432 Mililani Mauka;21.4756 Shāmgarh;25.8935 Pandan;14.0500 San Nicolás;22.7819 Frederickson;47.0916 Challans;46.8467 Hazel Dell;45.6797 Lindlar;51.0167 Murree;33.9042 Nueva Granada;9.8031 Millburn;40.7394 Tamgrout;30.2667 Middleton;43.1064 Lakato;-19.1833 La Resolana;19.6031 San Justo;-30.7833 Geseke;51.6500 Zhoucun;37.4509 Hoogstraten;51.4000 Leerdam;51.9000 Tolosa;11.0633 Bānki;20.3791 Bear;39.6189 Almasi;38.6833 Bad Krozingen;47.9167 Chilca;-12.5208 Scugog;44.0900 Sondershausen;51.3667 Günzburg;48.4527 Inopacan;10.5000 Galeras;9.1667 Zele;51.0667 Kara-Suu;40.7000 Sāmba;32.5624 Tehata;23.7274 Belén de Umbría;5.2000 Wātrāp;9.6353 Rosario de Lerma;-24.9833 Anagni;41.7422 Nang Rong;14.6283 Tong’anyi;35.3041 Farum;55.8083 Argenta;44.6153 Acatic;20.7803 Brunswick;43.9007 Dolton;41.6284 Naryn;38.2000 Eeklo;51.1858 Āthagarh;20.5600 Ambohitromby;-18.9667 Santa Rosa de Lima;14.3881 Pancas;-19.2250 Grottaferrata;41.7883 Pudupattanam;12.5037 Shelby;35.2904 Clarin;9.9667 Paiganapalli;12.4760 Qia’erbagecun;37.9766 Buritirama;-10.7078 Jämsä;61.8667 Chinchali;16.5647 Kingsville;42.1000 Savigliano;44.6500 Nambiyūr;11.3581 Linamon;8.1833 Áno Sýros;37.4500 Porto Franco;-6.3378 Tukwila;47.4750 Mundgod;14.9714 Denderleeuw;50.8833 Barro;-7.1769 Romano di Lombardia;45.5167 Bishunpur;26.0868 Chatiā;26.5396 Cajueiro;-9.3967 Hajnówka;52.7333 Mabitac;14.4333 Baie-Comeau;49.2167 Alejandro Korn;-34.9667 Gherla;47.0200 Bogandé;12.9667 Santa Bárbara;5.8747 Rāybāg;16.4800 Deán Funes;-30.4333 Villagarzón;1.0294 South Holland;41.5977 Mpwapwa;-6.3500 Wieluń;51.2206 Blankenberge;51.3000 Sarreguemines;49.1100 Rocky River;41.4702 Caombo;-8.7000 Parasi;24.1908 Takaba;3.3963 Parkāl;18.2000 Miorimivalana;-17.2167 Terrier Rouge;19.6333 La Mornaghia;36.7667 Wang Nam Yen;13.5004 Livadeiá;38.4333 Paramonga;-10.6744 Panagyurishte;42.5000 Sidi Rahal;33.4720 Llorente;11.4125 Suzzara;44.9927 Manhumirim;-20.3578 Atchoupa;6.5333 Aspe;38.3456 Volnovakha;47.5994 Nubl;36.3756 Juli;-16.2125 Sulphur;30.2286 Almaguer;1.9167 Xintian;23.1427 Hojambaz;38.1167 Darien;41.0798 El Paujíl;1.5644 Ryazhsk;53.7000 Rosolini;36.8167 La Unión;37.6192 Anori;-3.7728 Gökdepe;38.1578 Puerto Natales;-51.7333 Geneva;41.8833 Enköping;59.6356 Loncoche;-39.3667 Rose Hill;38.7872 Khotkovo;56.2500 Aishō;35.1688 Chāgalamarri;14.9667 Tubbergen;52.4000 Schramberg;48.2269 El Bâzoûrîyé;33.2539 Prudnik;50.3228 Givors;45.5906 Elverum;60.8819 Palatka;60.1000 Hafshejān;32.2269 Bīrpur;25.5209 Chodavaram;17.8333 Mandza;-11.4167 Tarko-Sale;64.9167 Jindřichŭv Hradec;49.1442 Sint-Katelijne-Waver;51.0667 Galūgāh;36.7272 Metahāra;8.9000 Naraura;28.1967 Kutná Hora;49.9483 Sanhe;36.5643 Traunreut;47.9667 Secaucus;40.7810 Holiday;28.1864 Fatehgarh Chūriān;31.8643 Cumaral;4.2694 Orleães;-28.3589 Cugir;45.8436 Güimar;28.3150 Navalpattu;10.7557 Tizi-n-Bechar;36.4311 Matsuura;33.3333 Soltau;52.9833 Orós;-6.2439 Mī’ēso;9.2333 Urk;52.6667 Somers;41.3056 Ambinanindrano;-20.6833 Cournon-d’Auvergne;45.7422 Ban Wat Sala Daeng;13.8097 Lehāra;26.1515 Karaiyāmpudūr;11.0077 North Salt Lake;40.8439 Mukilteo;47.9096 Liberty Triangle;29.0760 Sanxing;24.6739 Pio XII;-3.8939 Aígio;38.2500 Groß-Umstadt;49.8667 Kitcharao;9.4581 Teteven;42.9167 São João;-8.8758 Bloomfield;41.8426 San Isidro;10.0299 Itapecerica;-20.4728 Colleferro;41.7333 Lorton;38.6983 Los Vilos;-31.9167 Carthage;36.8528 Sestu;39.2994 Recanati;43.4000 Narkher;21.4383 Paradise;47.5333 Sessa Aurunca;41.2333 Lakeside;32.8560 St. Andrews;34.0510 Umrāpur;24.5863 Nāgamangala;12.8194 Pompéia;-22.1089 Ronda;10.0003 Silva Jardim;-22.6508 Park Forest;41.4817 Beaune;47.0250 Bad Schwartau;53.9194 Concepción Huista;15.6167 Skive;56.5667 Gryfino;53.2531 Concepción Chiquirichapa;14.8500 South Lake Tahoe;38.9393 Bananeiras;-6.7500 Varadero;23.1394 Ashland;42.1905 Coswig;51.1333 Shisui;35.7249 Schenefeld;53.6028 Kaltan;53.5167 Lubań;51.1181 Mobetsu;44.3564 Garforth;53.7920 Aralık;39.8728 Grand Island;43.0198 Lyskovo;56.0289 Mitry-Mory;48.9839 Youganning;34.7356 Dickinson;29.4548 Belovodskoe;42.8300 Asni;31.2500 Luna;18.3311 Barcelona;12.8694 Katākos;25.3339 Asarganj;25.1500 Daboh;26.0024 Akonolinga;3.7667 Oshoba;40.7347 Matanga;-23.5167 Algete;40.5978 Ādēt;11.2667 Krishnapur;24.4123 Villanueva;10.4456 Puzol;39.6167 Lugait;8.3411 Injibara;10.9500 Araripe;-7.2128 Voiron;45.3642 Jiangjiadong;24.4985 Silverdale;47.6663 Carei;47.6839 Tanhaçu;-14.0208 Traunstein;47.8667 Bruchköbel;50.1833 Sādpur;25.4758 Kattanam;9.1833 Pushchino;54.8333 Musselburgh;55.9420 Bülach;47.5167 Nerekhta;57.4500 Dupax del Sur;16.2842 Santa María Chilchotla;18.2333 Manuel Tames;20.1803 Monte Sião;-22.4328 Vittal;12.7660 Baldwin;40.3690 Alfena;41.2381 Conceição de Macabu;-22.0850 Bonito;-21.1333 Huseni;26.3768 Monte Santo de Minas;-21.1900 Palavūr;8.2055 Cranendonck;51.2853 Rio Maior;39.3333 Nizāmpatam;15.9000 Hindang;10.4339 Selden;40.8714 Moorestown;39.9784 Aguadas;5.6167 Lucélia;-21.7203 Timmarāsanāyakkanūr;10.0015 Fierenana;-18.5500 San Antonio Sacatepéquez;14.9667 Adigaratti;11.3357 Erval d’Oeste;-27.1939 Ferentino;41.6833 Brezno;48.8039 Itororó;-15.1169 Montecristo;8.2994 San Isidro de Lules;-26.9333 Carrboro;35.9259 Chokkampatti;9.1259 Jagatpur;23.6048 San Juan de Río Coco;13.5444 Peritoró;-4.3828 Vili;12.2833 Gannavaram;16.5333 Shāzand;33.9275 Sadao;6.6386 Hirekerūr;14.4551 Los Llanos de Aridane;28.6500 Bobonong;-21.9704 San Víctor Abajo;19.4500 Baixa Grande;-11.9600 Westborough;42.2681 Komatipoort;-25.4333 Saponé;12.0528 San Salvo;42.0455 Loanda;-22.9228 Monroe;41.3043 Gauting;48.0678 Maraú;-14.1028 Agan;35.9167 Hancha;37.8572 Cauto Cristo;20.5619 Anping;39.7034 Varkaus;62.3167 Seria;4.6141 Pleasant Prairie;42.5266 Ghatkesar;17.4494 Seymour;38.9476 Yachiyo;36.1816 Bastos;-21.9167 Bezhetsk;57.7667 Stange;60.6494 Antanambao;-19.6667 Gardner;42.5845 Diamantino;-14.4089 East Moline;41.5199 Ambalavato;-22.9667 Lino Lakes;45.1679 Wipperfürth;51.1167 Tsarasaotra;-20.4333 Tittagudi;11.4167 Palāshi;23.7898 Uxbridge;44.1167 Xinnongcun;42.2357 Oulad Embarek;32.2833 Florencia;22.1475 Suaza;1.9833 Bologoye;57.8833 Al Brouj;32.5050 Nahorkatiya;27.2891 Aguinaldo;16.9789 Sahamadio;-22.5167 Praia da Vitória;38.7333 Goris;39.5078 Luna;16.9667 Luisiana;14.1850 Taperoá;-13.5378 Quirima;-10.9000 Welench’ītī;8.6667 Bambalang;5.8867 Carlos Casares;-35.6167 El Calafate;-50.3333 Villapinzón;5.2167 Schifferstadt;49.3833 Brummen;52.0833 Fröndenberg;51.4719 Karaağaç;36.5667 Chāndpur;25.5059 Skegness;53.1436 Mariāni;26.6700 Fredonia;5.9167 Hino;35.0180 Damua;22.1929 Camanducaia;-22.7550 Vikhorevka;56.1167 Andolofotsy;-18.7500 Fót;47.6181 Hyde Park;41.8011 Jangy-Nookat;40.2500 Ampanety;-18.9750 Bohumín;49.9042 Werdau;50.7333 Milwaukie;45.4445 Jatāra;25.0096 Furano;43.3420 Trecate;45.4333 Aurād;18.2540 Imi n’Oulaoun;31.3094 Akora;33.9925 Payson;40.0355 Bahía de Caráquez;-0.6000 Louisville;39.9710 Cherakhera;25.6869 Khampat;23.7823 Sidi Taibi;34.1917 La Colonia Tovar;10.4056 Essa;44.2500 Sirnia;25.4903 Santo Anastácio;-21.9769 Kochas;25.2340 Sotouboua;8.5667 Elektrougli;55.7167 Vyškov;49.2775 Toguchin;55.2333 Zhangjiazhuang;39.7804 Pindobaçu;-10.7428 Pájara;28.3500 Fiadanana;-20.4333 Alcudia;39.8525 Saka;34.6167 Ţorqabeh;36.3103 Steinhagen;52.0050 El Retén;10.6167 Sysert;56.5000 Pathrajolhania;26.1369 Ambohipandrano;-19.1500 Ambohimanambola;-19.8000 Esperalvillo;18.8200 Ludlow;42.1921 Pālkonda;18.6000 Chợ Phước Hải;10.4283 Lindenwold;39.8173 Sidi Abdelkader;33.0494 Ālampālaiyam;11.3635 Candiac;45.3800 Andāl;23.6000 Sokal;50.4833 Sitampiky;-16.6667 Bni Rzine;35.0108 Karianga;-22.4167 Gallipoli;40.0556 Ankililoaka;-22.7667 Ambondro;-25.2500 Tsiamalao;-15.0500 Abano Terme;45.3603 Oro-Medonte;44.5667 Chelak;39.9203 Žďár nad Sázavou;49.5627 Banting;5.7167 Clemmons;36.0319 Liubotyn;49.9489 Soanindrariny;-19.9000 Reichenbach/Vogtland;50.6167 Luckenwalde;52.0833 Bugalagrande;4.2167 Piedra Blanca;18.8438 Pontal do Paraná;-25.5768 Klaukkala;60.3820 Ampasimatera;-15.9333 Morafeno;-15.4833 Zaliohouan;6.7833 San José de Jáchal;-30.2333 Osmaneli;40.3500 Barki Saria;24.1759 Az Zuwaytīnah;30.9522 Dudinka;69.4064 Pulicat;13.4161 Tongobory;-23.5167 Palma del Río;37.7000 El Coco;8.8834 Nurota;40.5650 Hualqui;-36.9600 Alto-Cuilo;-8.2371 Lambari;-21.9758 Castel San Pietro Terme;44.4000 Roshal;55.6667 Teghra English;25.4072 Cacongo;-5.2333 Anah;34.3722 Xincheng;24.0514 Varennes;45.6833 Koskāpur;26.0216 Merrick;40.6515 Rivalta di Torino;45.0340 Kyaukpyu;19.4264 Arnaud;18.4500 Torhout;51.0661 Madre de Deus;-12.7408 Achampet;16.6299 Enger;52.1333 San Guillermo;16.7244 Bholsar;25.2133 Mössingen;48.4064 Ripley;53.0500 Nördlingen;48.8511 Banganapalle;15.3167 Nidderau;50.2500 Bussolengo;45.4667 Mae Sai;20.4333 Rangra;25.3803 Debark’;13.1333 Pedra;-8.5006 Guará;-20.4283 Ås;59.6603 Nizhniy Lomov;53.5167 Kāmalāpuram;14.5983 Purranque;-40.9167 Punturin;14.7381 Paldorak;40.4833 Quela;-9.3833 Oderzo;45.7808 Sonkach;22.9717 Xo‘jaobod;40.6653 Kuknūr;15.4900 Bandar-e Gaz;36.7742 Veresegyház;47.6505 Oliveira do Hospital;40.3589 Moniquirá;5.9167 Harvey;29.8876 Bhainsdehi;21.6449 Narwar;25.6439 Dillingen;49.3500 Madukkūr;10.4800 Upper St. Clair;40.3336 Baroda;25.5000 Jericho;31.8711 Ararat;39.8303 Beldaur;25.5947 ’Aïn el Hammam;36.5714 Sánchez;19.2282 Zestaponi;42.1083 Herborn;50.6825 Vico Equense;40.6667 São Vicente Ferrer;-2.8939 Kurovskoye;55.5833 Cercado Abajo;18.7300 Osa;57.3000 Rio Rico;31.4957 Kamamaung;17.3469 Ubaitaba;-14.3128 Guácima;9.9613 Wuustwezel;51.3833 Gerlingen;48.8000 Morauna;26.1706 Chennimalai;11.1638 South Whitehall;40.6154 Wildeshausen;52.9000 Vinany;-19.6167 Samacá;5.5000 Kremenets;50.1081 Pallazzolo sull’Oglio;45.6000 Yangping;27.7607 Ovejas;9.5000 Pico Truncado;-46.7950 Schortens;53.5333 Sotomayor;1.4933 Bālugān;19.7333 East Pennsboro;40.2886 Buinsk;54.9667 Gamboma;-1.8764 Wade Hampton;34.8821 Coquimatlán;19.2038 Lockport;43.1698 Rājgarh;22.6800 Vadamadurai;10.4408 Sultanpur;31.2235 Ozark;37.0365 Hays;38.8821 Honnāli;14.2399 Pertuis;43.6950 Strathroy-Caradoc;42.9575 Horten;59.4208 Bourg-lès-Valence;44.9475 Makakilo;21.3591 Kolāras;25.2193 Derinkuyu;38.3736 Miches;18.9800 Māvalli;14.1000 Olenegorsk;68.1500 Valkeakoski;61.2667 Kiseljak;43.9431 Skanderborg;56.0417 Hexiang;19.5285 Sêrro;-18.6050 Mahināwān;26.0208 Boussu;50.4331 Avon;39.7601 Carcagente;39.1222 Letlhakane;-21.4167 Blieskastel;49.2333 Ellamanda;16.1847 Matheu;-34.3667 Junqueirópolis;-21.5147 Carquefou;47.2969 Haßloch;49.3500 Capinzal;-27.3439 Cugnaux;43.5378 Humanes de Madrid;40.2539 Grayslake;42.3405 Jicomé;19.6500 Karmana;40.1422 Wachtberg;50.6242 Samboan;9.5288 Hanmayingcun;41.2636 Willmar;45.1216 Aomar;36.5000 Fālākāta;26.5300 Ceres;-15.3078 Archena;38.1150 Husainpur;31.3387 Feltre;46.0186 Pacatu;-11.9578 Tamandaré;-8.7600 Dharmkot;30.9456 Ubaíra;-13.2678 Blaj;46.1753 Murrysville;40.4456 Arnold;38.4297 Giovinazzo;41.1833 Pita;11.0800 Fort Walton Beach;30.4255 Kōtekāra;12.7929 Lebedinovka;42.8800 Suroth;26.8098 Zülpich;50.7000 Bladel;51.3667 Kortenberg;50.8833 Tielt;50.9989 Buckingham;40.3188 Kihihi;-0.7489 Ino;33.5487 Sivaslı;38.4833 Polonne;50.1167 Kawayan;11.7000 Vinjamūr;14.8330 Monte Alegre;-6.0678 Nadikūde;16.5941 Dumbarton;55.9500 Pontassieve;43.7750 South Milwaukee;42.9120 Scobinţi;47.3833 Lastra a Signa;43.7667 Germantown;43.2343 Sint-Andries;51.2000 Forst (Lausitz);51.7333 Dugulubgey;43.6500 Medina;-16.2228 Mattigiri;12.6980 Lebanon;39.4254 Ghafurov;40.2200 Sidhapa;26.5355 Tala Yfassene;36.4583 Bruges;44.8828 Arroyo Seco;-33.1667 Gyömrő;47.4250 Parma Heights;41.3865 Illapel;-31.6333 Somanya;6.1039 Mougins;43.6000 Banaue;16.9119 Luacano;-11.2167 Nova Granada;-20.5339 Sivrihisar;39.4500 Chachahuantla;20.2756 Harvey;41.6076 Morrinhos;-3.2289 Dolyna;48.9703 Na Klang;17.3043 Sultonobod;40.7644 Blansko;49.3631 Fonte Boa;-2.5139 Bom Jesus;-18.2150 Elvas;38.8667 Pukhrāyān;26.2300 Senica;48.6806 Melena del Sur;22.7881 Zhangatas;43.5705 Moḩammadābād;28.6689 Hernani;43.2667 Iriona;15.9694 Green Valley;31.8393 Cartaya;37.2833 Mäntsälä;60.6331 Pueblo Nuevo;13.3797 Campestre;-21.7108 Quaregnon;50.4333 Sondiha;24.8950 Xionglin;24.7742 Foya Tangia;8.2713 Sugaon;26.7317 Monte Azul;-15.1553 Okha;53.5889 Stadtlohn;51.9925 Senapparetti;10.9625 Kātpādi;12.9695 Kekem;5.1667 Yakkabog‘;38.9800 Meinerzhagen;51.1167 Lagoa do Itaenga;-7.9358 Sapang Dalaga;8.5500 Ménaka;15.9167 Lincolnia;38.8158 North Ogden;41.3123 Pozantı;37.4278 Sabanitas;9.3400 Nootdorp;52.0333 Mead Valley;33.8333 Caoayan;17.5469 Sidi Chiker;31.7453 San Julián;-16.9064 Hörstel;52.2972 Pastores;14.5967 Shimogamo;34.6795 Cittadella;45.6500 Little Egg Harbor;39.5969 Miracema do Tocantins;-9.5669 Cirencester;51.7190 Gaeta;41.2167 Pendembu;8.1000 Botevgrad;42.9073 Coolbaugh;41.1837 Hailsham;50.8647 Senden;51.8572 Rocky Hill;41.6572 Profesor Salvador Mazza;-22.0500 Arhavi;41.3333 Helena;33.2837 Sabaudia;41.2998 São João de Pirabas;-0.7689 Duderstadt;51.5125 Wasaga Beach;44.5206 Ware;51.8109 Adrian;41.8994 Rhenen;51.9667 Lannion;48.7325 Nové Mesto nad Váhom;48.7547 Alagoa Nova;-7.0473 Pittsburg;37.4129 Neufahrn bei Freising;48.3159 Mill Creek;47.8631 Chapulhuacán;21.1547 Bagou;10.8147 Encantado;-29.2358 Batī;11.1833 Murphy;33.0186 Harpālpur;25.2877 Gandujie;35.8908 Dingolfing;48.6333 Ban Tha Pha;13.8437 Bulnes;-36.7333 Marānchi;25.3544 Baião;41.1667 Miandrivazo;-19.5239 Jefferson;41.0003 South St. Paul;44.8877 Senec;48.2189 Sārangpur;25.7389 Kapay;8.0833 Lubalo;-9.1500 Bilgi;16.3472 Karlapālem;15.9333 Mường Lay;22.0678 Suwanee;34.0508 Beroun;49.9642 Bobrov;51.1000 Coronado;32.6649 Pohādi;26.0809 Mazinde;-4.8073 Newmarket;52.2459 Floreşti;46.7475 Itanhém;-17.1658 Nalua;22.1051 Āmta;22.5834 Korntal-Münchingen;48.8306 Sultanhisar;37.8897 Hiddenhausen;52.1667 Gil;30.8469 Acquaviva delle Fonti;40.9000 Mar’’ina Horka;53.5167 Pak Phanang;8.3538 Mbala;-8.8333 Libertyville;42.2870 Nishigō;37.1417 Spennymoor;54.7000 Albertville;45.6758 La Cañada Flintridge;34.2097 Kawambwa;-9.7914 Mealhada;40.3833 Bhasāwar;27.0361 Coronel Vivida;-25.9800 New Glasgow;45.5926 Covington;47.3667 Cordeiro;-22.0289 Gorāya;31.1241 San Bernardino;-25.3106 Salvaterra;-0.7528 Loja;37.1667 Makhtal;16.5012 Douar Lamrabih;34.8167 Sidi Jaber;32.3833 Tullahoma;35.3721 Magsaysay;8.0333 Station des Essais M.V.A.;34.9352 Sunchales;-30.9333 Ely;52.3981 Aldan;58.6167 Hashtrūd;37.4714 Ryūyō;34.6783 Springfield;40.0986 Gjøvik;60.7957 Casarano;40.0167 Nikolsk;53.7167 Veroli;41.6833 Harmanli;41.9333 Donggou;19.6593 Samthar;25.8400 Bibai;43.3329 Gioia Tauro;38.4333 Sivagiri;9.3300 Susaki;33.3925 Rosamond;34.8658 New Canaan;41.1592 Baden;47.4667 Mundra;22.8500 Qagan Us;36.3972 Arsk;56.1000 Aïn Cheggag;33.8833 Hem;50.6553 Timaná;1.9833 San Mauro Torinese;45.1039 Schwanewede;53.2333 Marquette;46.5440 Alatsinainy-Bakaro;-19.3167 Sidney;40.2891 Kurshab;40.6917 Sanrh Majhgawan;24.6845 Rāmchandrapur;23.9208 Namegawa;36.0660 Miracatu;-24.2808 Altônia;-23.8739 Colonia General Felipe Ángeles;23.9167 Wilmot;43.4000 Acri;39.5000 Bethany;35.5071 Loutété;-4.2961 Laboulaye;-34.1167 Rawicz;51.6092 Caracaraí;1.8158 Teonthar;24.9821 Battle Ground;45.7766 Carmen de Patagones;-40.7833 Bouguenais;47.1775 Kothia;25.7612 Tyrnyauz;43.4000 Pleszew;51.8833 Eruh;37.7497 Wittmund;53.5750 Bayt Ūmmar;31.6214 Ban Patong;7.8931 Khvānsār;33.2206 Nova Xavantina;-14.6728 Rifādpur;25.2635 Sakae;35.8408 Letterkenny;54.9566 Chodzież;52.9833 Sacile;45.9667 Baskil;38.5697 Cinfães;41.1000 Seydi;39.4167 Tondi;9.7417 Fara;11.5333 Pleasantville;39.3900 Stephenville;32.2147 Yorkville;41.6563 Chandragiri;13.5833 Gostyń;51.8792 Sartalillo;9.9759 São Joaquim do Monte;-8.4319 Becerril;9.7000 Dorchester;50.7154 Bad Waldsee;47.9211 L’Isle-sur-la-Sorgue;43.9194 Karlshamn;56.1667 Tobe;33.7492 Garhshankar;31.2154 Takanabe;32.1280 San Lorenzo;1.5039 Taşlıçay;39.6333 Mārahra;27.7500 Puerto Quito;0.1272 Muzambinho;-21.3758 Uetze;52.4661 Beforona;-18.9667 Montalto Uffugo;39.4000 West St. Paul;44.9018 Pargi;17.1756 Snellville;33.8562 Cantanhede;-3.6328 San Martín;8.0472 Bhitarwār;25.7922 Frøyland;58.8308 Bhojpur Jadīd;25.5880 Nguékhokh;14.5128 Sompeta;18.9300 Chepo;9.1700 Kashkar-Kyshtak;40.6200 Canarana I;-13.5500 Piove di Sacco;45.2977 Santa Rita;15.2000 Annaberg-Buchholz;50.5800 Tenjo;4.9167 Belūr;13.1642 Villefontaine;45.6133 Mirador;-6.3708 Kampong Thum;12.7120 Ypsilanti;42.2440 Pennāgaram;12.1343 Ibirubá;-28.6278 Carmo do Rio Claro;-20.9731 Kudra;25.0463 Vianen;51.9833 Kanniyākumāri;8.0911 Ostrov;57.3500 Piñan;8.4822 Baihar;22.1013 Akanavāritota;16.5984 Yabayo;5.9500 Kubinka;55.5667 Butiá;-30.1200 Dereli;40.7333 Çilimli;40.9000 Kuala Pembuang;-3.3871 Five Corners;45.6883 Essex;42.0833 Chã Grande;-8.2378 Ghogardīha;26.2799 Middle;39.0852 Cruzeiro do Oeste;-23.7850 Tolosa;43.1333 Durbat;38.5333 Kulpahār;25.3190 Baza;37.4833 Khilchipur;24.0394 Lower Southampton;40.1541 Oroville;39.4999 La Corredoría;43.3853 Medina del Campo;41.3000 Reggane;26.7000 João Lisboa;-5.4478 Bryant;34.6152 Marblehead;42.4992 Concordia;6.0490 Terenos;-20.4419 Parelhas;-6.6878 Elko;40.8381 Tilothu;24.8051 Mahājerān-e Kamar;34.0183 Torrox;36.7500 Złotów;53.3603 Birnagar;25.9785 Kissa;7.0333 Ocós;14.5138 Bouansa;-4.2186 Old Jamestown;38.8394 Hanahan;32.9302 Huanuni;-18.2900 Jandaia do Sul;-23.6028 Melzo;45.5000 Mandrosohasina;-19.5833 Vallam;10.7199 Parsāhi Sirsia;26.4676 Northfield;44.4550 Bequimão;-2.4489 Danao;10.0167 Sandwich;41.7137 Ėsanboy;38.0833 Mayluu-Suu;41.2457 Teculután;14.9877 Kloten;47.4500 Sonhauli;25.5108 Forest Lake;45.2536 Villa Verde;16.6067 Bourne;41.7233 Khowrzūq;32.7781 Wilnsdorf;50.8167 Agoura Hills;34.1510 Eastwood;53.0180 Le Puy-en-Velay;45.0433 Nhamundá;-2.1858 Mělník;50.3506 Abaré;-8.7208 São José de Piranhas;-7.1208 Tsotsin-Yurt;43.2419 Rosas;42.2633 Oberkirch;48.5333 Cuité;-6.4850 Piqua;40.1506 Aguas Zarcas;10.4223 Depālpur;22.8509 Monroe;41.9155 Lohur;38.3833 Valença do Piauí;-6.4078 Encruzilhada;-15.5308 Sátiro Dias;-11.6000 Greiz;50.6547 Mirfield;53.6807 Dan Gorayo;8.7278 Cardedeu;41.6406 São José do Rio Preto;-22.1508 Donauwörth;48.7000 Lonār;19.9800 Luperón;19.9000 Cabra;37.4667 Abhayāpuri;26.3225 Nambūru;16.3600 Rumoi;43.9410 Hatvan;47.6681 Farāshband;28.8714 Atescatempa;14.1750 Orvieto;42.7183 Santa Rosa;-26.8667 Hirokawa;33.2415 Selendi;38.7444 Itaiópolis;-26.3358 Marneuli;41.4969 Jdour;32.1269 Siripur;26.7336 Azacualpa;15.3667 Painesville;41.7240 Comăneşti;46.4297 Kyakhta;50.3500 Urussanga;-28.5178 Jirkov;50.4999 Bamumkumbit;5.8167 Leinefelde;51.3833 Crest Hill;41.5723 Rajaudha;26.7046 Nāri Bhadaun;26.0065 Concarneau;47.8753 Villa Hidalgo;21.6762 Lichtenfels;50.1333 Rosedale;39.3266 Paraguaçu;-21.5331 Sergach;55.5333 Porto Real do Colégio;-10.1858 Bethel;41.3747 Simití;7.9667 Neustrelitz;53.3647 Bīrpur;25.5181 Glassboro;39.7014 Smørumnedre;55.7333 Rovira;4.2500 Sherwood;45.3594 East Northport;40.8791 Ogden;43.1646 Limerick;40.2323 Hakui;36.8936 Água Branca;-9.2608 Huanímaro;20.3675 Minturno;41.2667 Diguapo;25.6988 Sbeitla;35.2297 Quissamã;-22.1069 Tepetlixpa;19.0006 Voyenno-Antonovka;42.8722 Náchod;50.4167 Belém de São Francisco;-8.7578 Telwa;25.8796 Simri;25.6405 Shīrūru;13.9080 Lalín;42.6500 Agarpur;25.1957 Pujehun;7.3506 Stebnyk;49.3000 Del Carmen;9.8690 Anicuns;-16.4608 Requena;39.4861 Pedro Carbo;-1.8333 Torgau;51.5603 Haren;53.1667 Bella Vista;15.6167 Edam;52.5167 Brandýs nad Labem-Stará Boleslav;50.1894 Tsararafa;-22.7333 Cahors;44.4475 Rāmjībanpur;22.8300 Zossen;52.2167 Acqui Terme;44.6761 Goole;53.6992 Viadana;44.9267 Parnamirim;-8.0908 Puerto Viejo;10.5737 Cruz Grande;16.7333 Boromo;11.7500 Perry;32.4720 Dračevo;41.9367 Abrīsham;32.5550 Holzminden;51.8297 Erragondapālem;16.0397 Yeniseysk;58.4667 Bideford;51.0160 Schopfheim;47.6500 Río Grande;19.7300 Kākdwīp;21.8791 Pāmūru;15.0944 Sukhāsan;25.4944 Málaga;6.7833 Bichena;10.4500 Kleppe;58.7772 Celorico de Basto;41.3833 Vīrarājendrapet;12.1964 Kęty;49.9000 Bhīkhi;30.0700 Otegen Batyr;43.3333 Saco;43.5390 Telgte;51.9819 Qorasuv;40.7222 Giengen an der Brenz;48.6217 Ampefy;-19.0419 Bekatra Maromiandra;-22.1000 Hazlet;40.4265 Kannod;22.6676 Taquarana;-9.6450 Pikalëvo;59.5333 El Viso del Alcor;37.3833 Marion Oaks;29.0011 Imouzzer Kandar;33.7300 Bara Malehra;24.5681 Whitpain;40.1578 Ivoti;-29.5908 Chipiona;36.7333 Irlam;53.4450 Kotelnikovo;47.6333 Bothell West;47.8056 Plainview;34.1911 Snina;48.9881 São Luís Gonzaga do Maranhão;-4.3800 Ayaviri;-14.8818 Frontino;6.7833 Toprakkale;37.0669 Antsampandrano;-19.5333 Foley;30.3983 Kuala Lipis;4.1800 Heusenstamm;50.0500 Baragoi;1.7833 Manaratsandry;-16.1833 Thimiri;12.8283 Perdões;-21.0908 Satsuma;31.9057 Huércal-Overa;37.3833 Fort St. John;56.2465 Weesp;52.3000 Mithi;24.7333 Sesheke;-17.4667 Uruçuí;-7.2333 Saint-Dié-des-Vosges;48.2842 Naaldwijk;51.9931 Kfar Kiddé;34.2028 Haka;22.9833 Guding;26.4876 Paraibano;-6.4328 Lancing;50.8320 Jāmbai;11.4678 Yuancun;27.4540 Jiangjiehe;27.2618 La Chapelle-sur-Erdre;47.2989 Jasidih;24.5138 Pattamadai;8.6726 Plaisance-du-Touch;43.5656 Araruna;-6.5578 Villabate;38.0833 Mahaditra;-21.7500 Sangīn;32.0733 Dagohoy;9.9167 Schilde;51.2333 Kürten;51.0500 Fukagawa;43.7233 Udalguri;26.7537 Clifton;39.0764 Bhulwāl;32.8094 Dicholi;15.5932 Naranjos;21.3472 Mairi;-11.7108 ’Ali Ben Sliman;31.9100 San Martín de la Vega;40.2094 Palm Valley;30.2011 Moaña;42.2833 Mayfield Heights;41.5174 Uetersen;53.6872 Nkoteng;4.5167 Aquidabã;-10.2833 Nesoddtangen;59.8086 Berlin;41.6114 Anagé;-14.6119 Şiran;40.1894 Tolú Viejo;9.4500 Shāhpur;25.6029 Artik;40.6172 Itarantim;-15.6600 Mölln;53.6269 Ambatosoratra;-17.6167 Guapiaçu;-20.7950 Montrose;38.4689 Peshtera;42.0333 Adel;53.8489 San Marcos;14.4000 El Mansouria;33.7500 Faversham;51.3177 Issaba;7.0833 Vasiana;-19.6333 Mandiavato;-19.0833 Guaymango;13.7500 Slobozia;46.7333 Callosa de Segura;38.1225 Kwinana;-32.2394 Montgomery;41.7237 Zhaodianzi;39.9373 Makurazaki;31.2728 Castillo de Teayo;20.7500 Cassano d’Adda;45.5333 Ochtrup;52.2056 Padre Las Casas;18.7333 Glinde;53.5406 Kébémer;15.3667 Mohiuddinnagar;25.5737 Tsarabaria;-13.7500 Shirahama;33.6781 Westbrook;43.6954 Bachi-Yurt;43.2242 Zhosaly;45.4893 Zverevo;48.0200 Silla;39.3618 Nārāyankher;18.0333 Zwijndrecht;51.2167 Shenandoah;30.4019 Liancourt;19.1356 L’Assomption;45.8333 Nejo;9.5000 Ban Tha Kham;9.1058 Shakhunya;57.6667 Alagir;43.0333 Kanekallu;14.8864 Krasnohrad;49.3719 Shōnai;38.8499 Soavina Antanety;-19.8000 Carmo do Cajuru;-20.1839 Malabuyoc;9.6500 Tarhzirt;32.4333 Altagracia;11.5672 Tiruvādi;10.8805 Sāmalāpuram;11.0724 Benicasim;40.0553 Yumbel;-37.1333 Puerto Leguízamo;-0.1939 Atlapexco;21.0042 Valderrama;11.0036 Caridade;-4.2319 South Burlington;44.4622 Golden;39.7406 Frankfort;41.4892 Nīlgiri;21.6466 Carira;-10.3581 Hässleholm;56.1667 Hudson;42.3887 La Esmeralda;3.1739 Casamassima;40.9500 Rantabe;-15.7000 Ambalakirajy;-15.8333 Canto do Buriti;-8.1100 Analapatsy;-24.2500 Midalam;8.2167 Ban Tha Ton;20.0608 Mesquite;36.8032 Ampasimena;-24.3667 Trostyanets’;50.4811 Haya;18.3461 Bulolo;-7.2000 Andirá;-23.0508 Giszowiec;50.2333 El Arenal;20.7754 Jubbah;28.0200 Kāmākhyānagar;20.9338 Whitehall;39.9682 Zemio;5.0333 Calatayud;41.3500 Nykøbing Falster;54.7654 East Hemet;33.7301 Cartavio;-7.8833 Emet;39.3333 Törökszentmiklós;47.1833 Gebre Guracha;9.8000 Rohār;25.9917 Tettnang;47.6708 Ennigerloh;51.8367 Ceglie Messapico;40.6500 Had Zraqtane;31.4500 Imperial;32.8390 Usman;52.0500 North Liberty;41.7438 Jhalidā;23.3654 Mulundo;7.9333 Leek;53.1667 World Golf Village;29.9654 Sīāhkal;37.1528 Mangualde;40.6042 Sleaford;52.9960 Plattsburgh;44.6951 Wenden;50.9667 Uruburetama;-3.6250 Hauppauge;40.8211 Aït Tamlil;31.4800 Tomaszów Lubelski;50.4500 Xiaqiaotou;27.2167 Coruche;38.9569 Quatro Barras;-25.3658 Olten;47.3500 Bressuire;46.8400 Rovato;45.5670 El Paraíso;15.0167 Morlanwelz-Mariemont;50.4500 Neviges;51.3128 Liuliang;35.2695 Salsomaggiore Terme;44.8148 Rolla;37.9459 São João Batista;-2.9550 Doany;-14.3667 San Miguel;7.6500 Allanridge;-27.7544 Poperinge;50.8547 Blankenburg;51.7953 Pecan Grove;29.6235 Mathurāpur;25.5112 Quepos;9.4571 Ennis;32.3254 Äänekoski;62.6000 Gázi;35.3250 Volketswil;47.3906 Forest Park;39.2861 Komárom;47.7403 Mapai;-22.7306 Oregon;41.6524 Sand Springs;36.1353 Poshkent;39.8833 Pradópolis;-21.3594 Ba;-17.5333 Ozorków;51.9667 Utebo;41.7141 Waldbröl;50.8789 Comapa;19.1667 Hasanparti;18.0681 Amorebieta;43.2192 Ban Na San;8.7997 Hanumana;24.7789 Schmalkalden;50.7167 Portland;27.8942 Eschwege;51.1881 Kittūr;15.6000 Major Isidoro;-9.5319 Lebbeke;51.0000 Yosano;35.5653 Vīrapāndi;9.9633 Os;60.2269 Signa;43.7833 Brandsen;-35.1667 Nottuln;51.9278 Spoltore;42.4550 Nizhnyaya Tura;58.6208 Tevāram;9.8967 Dęblin;51.5667 Mokena;41.5327 Shafter;35.4794 Idhnā;31.5586 Hamina;60.5667 Prunedale;36.8064 Castilho;-20.8722 Santiago;17.2947 San Miguel;11.2942 Saint-Lazare;45.4000 Kūysinjaq;36.0833 Port Angeles;48.1141 Kinston;35.2747 Chestermere;51.0500 Līkak;30.8950 Campamento;14.5500 Santo Amaro da Imperatriz;-27.6878 Jaggampeta;17.1833 Monroe;47.8595 Miamisburg;39.6322 El Castillo de La Concepción;11.0444 Sarstedt;52.2394 Cognac;45.6958 Saint-Lô;49.1144 Nīlambūr;11.0590 Salangaippālaiyam;11.4260 Cajolá;14.9252 Sinacaban;8.2854 Carlisle;40.2000 Pichor;25.1756 Gubakha;58.8706 Southbury;41.4745 Atarfe;37.2229 Castellana Grotte;40.8833 Ariccia;41.7167 Kuusankoski;60.9083 Mihona;26.2837 Volokolamsk;56.0333 Cantagalo;-21.9808 Forest Park;33.6209 Arcore;45.6333 Puerto Armuelles;8.2833 Nosy Varika;-20.5833 Quartier Morin;19.7000 Mustang;35.3917 Piratini;-31.4478 Bhawānīpur;26.4361 Buşrá ash Shām;32.5167 Barroso;-21.1869 Ystrad Mynach;51.6419 Echizen;35.9742 Ranbīrsinghpura;32.6079 Alhandra;-7.3504 Sil-li;39.4880 Arlington;48.1701 Pescia;43.9017 Coaraci;-14.6408 Nogales;31.3624 Massy;41.0608 Howard;44.5703 Kitatajima;35.9821 Liuguang;26.9970 Broadview Heights;41.3196 Masterton;-40.9700 Maple Shade;39.9520 Huntsville;45.3333 Agryz;56.5219 Burghausen;48.1667 Bieruń;50.1333 Salmon Creek;45.7099 Sulzbach-Rosenberg;49.5000 Guantiankan;28.2966 Lake Zurich;42.1956 Corner Brook;48.9287 St. John;41.4429 Casa de Oro-Mount Helix;32.7640 Çekerek;40.0731 Ad Dir‘īyah;24.7333 Zaouïa Aït Ishak;32.7600 Dillingen;48.5667 Paulistana;-8.1439 Tingloy;13.6500 Mandaguaçu;-23.3469 Mosopa;-24.7792 Grumo Nevano;40.9333 Sélestat;48.2594 Desavilakku;11.6476 Nyandoma;61.6667 Karcag;47.3111 Alpinópolis;-20.8639 Westminster;39.5796 Maḩmūdābād Nemūneh;36.2886 Simonésia;-20.1239 Pichidegua;-34.3586 Jnane Bouih;32.0308 Mickleover;52.9010 Karuizawa;36.3484 Āsasa;7.1000 Katangi;21.7737 Arbaoun;36.4667 Enkhuizen;52.7000 Calvillo;21.8500 Menzel Abderhaman;37.2300 Carate Brianza;45.7667 Itatinga;-23.1017 Brie-Comte-Robert;48.6925 Lower Allen;40.2082 Lakhna;25.4532 Moosburg;48.4667 Narsāpur;17.7374 Marsberg;51.4500 Trzebinia;50.1667 Punjai Puliyampatti;11.3516 Casalgrande;44.5833 Culpeper;38.4704 Tradate;45.7000 Regente Feijó;-22.2208 Wanluan;22.5727 Borna;51.1167 Várpalota;47.1989 Río Segundo;-31.6501 Wittlich;49.9869 Vazante;-17.9869 Oschersleben;52.0167 Hongshui;38.5077 Taketa;32.9667 Andaingo Gara;-18.2000 Ayyāmpettai;10.8971 Luénoufla;7.0667 Rybnoye;54.7333 Suárez;2.9589 Tash-Kömür;41.3500 Polonuevo;10.7833 Colotlán;22.1000 Homewood;41.5591 Meise;50.9333 Eupen;50.6333 Talwandi Bhai;30.8558 Babanūsah;11.3333 Stroud;41.0001 Kufstein;47.5833 Shelbyville;39.5352 Comodoro;-13.6593 Chopinzinho;-25.8558 Puerto El Triunfo;13.2833 Doujing;26.4789 Lackawanna;42.8182 Lieto;60.5000 Ban Bueng;13.3142 Ciriè;45.2353 Santa Teresita;18.2519 Rhar el Melah;37.1667 Urumita;10.5667 Anguillara Sabazia;42.0833 Nakło nad Notecią;53.1403 Englewood;26.9717 Bad Tölz;47.7603 Dharmsāla;32.2153 Universal City;29.5521 Žiar nad Hronom;48.5842 Sarsāwa;30.0160 Oulad Salmane;34.3356 Jakobstad;63.6667 Pôrto Grande;0.7128 Yuanhucun;44.1977 Someren;51.3833 Gilching;48.1103 Cabusao;13.7275 Mayen;50.3333 Riverview;46.0613 Konstantynów Łódzki;51.7500 Durağan;41.4167 Arbutus;39.2428 Holden;42.3561 Tīrthahalli;13.6884 Lajinha;-20.1508 Majhua;26.0140 Barreira;-4.2869 Tirmitine;36.6618 Yahotyn;50.2569 Capoeiras;-8.7350 Ait Bousarane;31.7917 Andrychów;49.8550 Hushihacun;40.8899 Kopa;25.8513 Weil der Stadt;48.7508 Waxhaw;34.9363 Prichard;30.7735 Lloydminster;53.2807 Pugo;16.2833 Alamo;26.1810 Gunri;25.6408 Venadillo;4.7500 Rizal;17.5000 West Mifflin;40.3581 Keszthely;46.7696 Indargarh;25.9109 Jagalūr;14.5196 Eersel;51.3500 Kasba Maker;25.9591 Manjathala;11.3624 Malacky;48.4358 Kalghatgi;15.1832 Rāwah;34.4686 Ranst;51.2000 Alzey;49.7458 Middelkerke;51.1847 Monte Alegre de Minas;-18.8708 Cherepanovo;54.2333 Okhmalynka;47.5333 Nanuet;41.0957 Utinga;-12.0819 Sint-Gillis-Waas;51.2167 Sparta;41.0486 Joliette;46.0167 Mogiyon;39.2500 Staveley;53.2694 Tublay;16.4764 Ban Thap Kwang;14.6103 Midway;30.4169 Atasū;48.6903 Bückeburg;52.2608 Lodhwe;24.6404 Tolentino;43.2086 Hechingen;48.3517 Diinsoor;2.4106 Ansermanuevo;4.8000 Rožňava;48.6583 Khamaria;23.2129 Dāchepalle;16.6000 Crespo;-32.0333 Adria;45.0500 Palera;25.0201 Riom;45.8936 Kolbermoor;47.8500 Diepenbeek;50.9072 Nocatee;30.0927 Xapuri;-10.6519 Yershov;51.3500 Engelskirchen;50.9833 Heinola;61.2000 Capela do Alto;-23.4706 Meerssen;50.8858 Bad Pyrmont;51.9867 Bad Aibling;47.8667 El Arahal;37.2667 Hemmingen;52.3236 Caraúbas;-5.7928 Pidhorodne;48.5737 Candelaria;22.7439 Acıgöl;38.5500 Jasien;54.3399 Sokołów Podlaski;52.4000 Jājarm;36.9500 Pastrana;11.1333 Rhede;51.8333 Aysha;10.7500 Garot;24.3234 Ituzaingó;-27.6000 Buruanga;11.8438 Meadowbrook;37.4301 Stowmarket;52.1900 Hoxut;42.2552 Puerto Morelos;20.8536 Altamont;42.1980 Yellowknife;62.4709 Ibicoara;-13.4108 Arvin;35.1944 Essen;51.4667 Monkseaton;55.0430 Otsego;45.2660 Fukusaki;34.9503 Xihuangcun;37.1411 São João do Piauí;-8.3578 Haiger;50.7422 Töle Bī;43.6768 São Gabriel;-11.2289 Waterford;41.3692 Siruma;14.0219 Hunters Creek;28.3610 Bagaces;10.5157 Rolante;-29.6508 Valle Vista;33.7436 Thamaga;-24.7167 Terek;43.4833 Aradíppou;34.9478 Beloyarskiy;63.7167 Ashland;40.8668 Ibigawa;35.4873 Grafton;42.2085 West Whiteland;40.0227 Bābai;22.7026 Sjöbo;55.6333 Bruz;48.0247 Madisonville;37.3409 Cervantes;16.9917 Seesen;51.8931 Kumārapuram;8.2922 Valavanūr;11.9205 Radeberg;51.1167 Müllheim;47.8000 Stevenson Ranch;34.3894 Somandepalle;14.0078 Rio Verde de Mato Grosso;-18.9178 Capinota;-17.7150 Yusufeli;40.8106 Sirvār;16.1739 Bad Berleburg;51.0497 Squamish;49.7017 Kempston;52.1140 Pułtusk;52.7000 Warrensburg;38.7627 Jesús María;20.6068 Teminabuan;-1.4333 Traiskirchen;48.0189 Erlanger;39.0109 Espiritu;17.9806 Marihatag;8.8008 Chinna Ganjām;15.6930 Regensdorf;47.4333 Whitemarsh;40.1040 Hurricane;37.1487 Pūranpur;28.5200 Heishanzuicun;41.0354 Los Álamos;-37.6272 Alcoy;9.7082 Bužim;45.0500 Orinda;37.8808 Holtsville;40.8124 Sorada;19.7608 La Nucía;38.6172 Ardon;43.1833 Hoveyzeh;31.4619 Magitang;35.9386 Echt;51.1000 Gogogogo;-24.2667 Palamós;41.8458 Gōdo;35.4174 Jiabong;11.7625 Yandrapalle;15.9062 Kantharalak;14.6536 Heishuikeng;37.7905 Yumurtalık;36.7678 Ramsbottom;53.6484 Tadian;16.9961 East Dereham;52.6810 Łańcut;50.0667 Kāveripatnam;12.4219 Douar Ait Sidi Daoud;31.6345 Vila Real de Santo António;37.1939 Kushiro;42.9961 Binka;21.0263 Vellmar;51.3500 Pozzallo;36.7303 Lodeynoye Pole;60.7167 Sipoo;60.3764 Sorgues;44.0083 Time;58.7228 Schwalmtal;51.2225 Dhānsāria;24.6400 Wervik;50.7797 Royan;45.6231 Ban Fang Tuen;19.5497 Lake Forest;42.2380 Onga;33.8479 Fécamp;49.7575 Rivière-du-Loup;47.8333 Pariquera-Açu;-24.7150 Perūru;14.3500 Quixeré;-5.0739 Albolote;37.2306 Tirunāgeswaram;10.9646 Salgado;-11.0319 Chiari;45.5197 Dashtigulho;37.7000 Starodub;52.5833 Sirālkoppa;14.3807 Cobourg;43.9667 Druten;51.8833 La Paz;10.8911 Paraisópolis;-22.5539 East St. Louis;38.6155 Dalanzadgad;43.5700 San Antonio Huista;15.6500 Tangbian;25.6539 Holalkere;14.0429 Mino;35.5448 Srikhanda;23.5984 Barhampur;25.5976 Nārapala;14.7206 Lynn Haven;30.2337 Koropí;37.9000 Vence;43.7225 Riachão do Dantas;-11.0689 Ituaçu;-13.8128 Kapangan;16.5764 Scorzè;45.5719 Purushottampur;19.5202 Mingjiujie;23.4558 Angleton;29.1721 Frutillar Alto;-41.1228 Izra;32.8454 Krasnystaw;50.9833 Rayevskiy;54.0658 Cabildo;-32.4275 Kamiichi;36.6984 Wassenberg;51.1000 Tajerouine;35.8833 Cocos;-14.1839 Motru;44.8036 Makronia;23.8476 Stabroek;51.3333 Montemurlo;43.9333 Halstenbek;53.6334 Lake Shore;39.1030 Bādkulla;23.2800 Juquiá;-24.3208 Balta;47.9361 Ambatomirahavavy;-18.9333 San Vito dei Normanni;40.6556 Pauini;-7.7139 Chilkūru;16.9611 Monte Azul Paulista;-20.9072 Sukhsena;25.7952 Paranapanema;-23.3886 Iiyama;36.8516 Brigham City;41.5035 Mukaiengaru;44.0621 Pfullingen;48.4656 Chautāpal;17.2508 Buerarema;-14.9589 Haldensleben;52.2833 Marchena;37.3333 Bagahi;26.7632 Krasnogorskiy;41.1542 Hardinxveld-Giessendam;51.8167 Burscheid;51.1000 Raita;32.5495 Khonj;27.8914 Bracciano;42.1000 Bergneustadt;51.0333 Tibagi;-24.5089 Narni;42.5167 Almel;16.9200 Sebt Gzoula;32.1167 Lexington;35.8018 Syosset;40.8157 Venceslau Brás;-23.8739 Hidrolândia;-4.4078 Altoona;41.6483 Diadi;16.6600 Guru Har Sahāi;30.7086 Texcaltitlán;18.9297 Liberal;37.0466 Athār;26.0392 Aci Sant’Antonio;37.6000 Zerong;24.9692 Chaita;25.7671 Haslett;42.7525 Kānp;25.8337 Ābomsa;8.5833 Taka;35.0503 Campina Verde;-19.5358 Bouanri;10.2000 Orange;30.1226 Korsholm;63.1125 Horseheads;42.1625 Khetia;21.6712 Ban Mae Hia Nai;18.7433 Monção;42.0733 Sidi Redouane;34.6833 Tapejara;-28.0678 Kirovgrad;57.4350 Soure;40.0500 Koppal;15.3500 Lebedyan;53.0167 Kralupy nad Vltavou;50.2411 Mâncio Lima;-7.6139 Horizontina;-27.6258 Cutlerville;42.8403 Agliana;43.9000 Schkeuditz;51.4000 Kronberg;50.1833 Santa Marinella;42.0333 Dumri;25.5356 Qandala;11.4711 Atok;16.6272 Ban Mae Kha Tai;18.7433 Henichesk;46.1667 Dolynska;48.1111 Marechal Taumaturgo;-8.9408 Bakal;54.9333 Groesbeek;51.7833 Söğüt;40.0186 Lebach;49.4100 Mohana;25.9027 Shāhgarh;24.3137 Vohimasy;-22.1000 Siroda;15.3292 Corbetta;45.4667 Pahārpur;25.5681 Tsumeb;-19.2500 Ampataka;-23.3500 Zachary;30.6643 Çal;38.0836 Ban Mon Pin;19.9168 Püttlingen;49.2833 Amboanana;-19.1500 Águas Formosas;-17.0819 Norton;41.9640 La Crau;43.1497 Shovot;41.6500 Lgov;51.6667 Sömmerda;51.1617 Quitandinha;-25.8719 Gātāda;18.2844 Kamatgi;16.1190 Rāmpur Tilak;25.8513 Gülağaç;38.3956 Huércal de Almería;36.8833 Cranbrook;49.5097 Žatec;50.3273 Nakhl-e Taqī;27.5014 Lede;50.9667 Aroeiras;-7.5450 Kabūdarāhang;35.2083 Hochheim am Main;50.0167 Rāmpur;26.4424 Ismailpur;25.3113 Noci;40.8000 Stillwater;45.0573 Kottakota;17.7513 Jalakandāpuram;11.6986 Kīlmangalam;10.0933 Torredembarra;41.1457 Abdulino;53.6667 Ferguson;40.7432 Bandar-e Kong;26.5992 Karia Ba Mohamed;34.3667 Júlio de Castilhos;-29.2269 Dolný Kubín;49.2106 Jamiltepec;16.2783 Rizal;17.8478 Nova Olímpia;-14.7969 Punta Gorda;26.8941 Meylan;45.2086 Pokaran;26.9194 Deerfield;42.1654 Badger;64.8006 Seminole;27.8435 Riacho das Almas;-8.1339 Twinsburg;41.3220 Uzhur;55.3175 Bhagabānpur;24.7765 Oberasbach;49.4219 Hindoria;23.9035 La Unión;5.9736 Gomīshān;37.0717 Ellensburg;46.9999 La Eliana;39.5661 Lumphat;13.5070 Ainan;32.9667 Wallingford Center;41.4499 Mure;34.3376 Mount Eliza;-38.1890 Forio;40.7333 Louviers;49.2153 Navraftor;37.7333 Griffith;-34.2900 Rurrenabaque;-14.4422 Antas;-10.4000 Boiro;42.6500 Naantali;60.4667 Huehuetla;20.1075 Marwa;25.3994 Carambeí;-24.9178 Valenza;45.0140 Bredene;51.2336 Sukumo;32.9333 Kalmthout;51.3833 Vera;-29.4667 Naples;26.1504 Lumberton;34.6312 Maracaçumé;-2.0428 Palmi;38.3667 Karuppur;11.7170 Dadeldhurā;29.3000 Aberdeen;40.4165 Cuquío;20.9275 San Teodoro;13.4358 Vargem Alta;-20.6708 Tazishan;41.0937 Tora;21.3262 Vilavūr;8.2669 Claremore;36.3146 Ấp Khánh Hưng;10.2000 Tak Bai;6.2592 Toshloq;40.4808 Pastavy;55.1167 Bad Driburg;51.7333 La Ligua;-32.4500 Ait Ben Daoudi;31.6345 Diamante;-32.0667 Castelfidardo;43.4642 Five Forks;34.8069 El Rosal;4.8519 Lumbayanague;7.7833 Aporá;-11.6600 Maitland;28.6295 Arita;33.2106 Ifanirea;-22.1833 Tlalnelhuayocan;19.5667 Raisāri;26.1319 Nanthankulam;8.3331 Akune;32.0167 Kurumbapālaiyam;11.1053 Aizumisato;37.4649 West Manchester;39.9456 Cerqueira César;-23.0356 Santa Ana Jilotzingo;19.5333 Kasterlee;51.2500 Shichigahama;38.3045 Hermiston;45.8326 Lakshmaneswaram;16.4082 Varzelândia;-15.7008 Shemonaīkha;50.6269 Nikolayevsk-na-Amure;53.1333 Londerzeel;51.0000 Nayāgarh;20.1288 Moissy-Cramayel;48.6261 Ashkezar;32.0006 Valle;-2.9500 Moreni;44.9803 Banni;25.4692 Malanday;14.7194 Fond des Blancs;18.2833 White Oak;39.2106 Águas de Lindóia;-22.4758 Eiheiji;36.0922 Ranomena;-23.4167 Lunéville;48.5894 Achocalla;-16.5833 Amares;41.6167 Dianópolis;-11.6258 Beaconsfield;45.4333 Ambodinonoka;-20.9833 Nanjanād;11.3669 Abdurahmoni Jomí;37.9458 Bình Minh;10.0961 Viradouro;-20.8728 Barhampur;26.3023 Ixtlahuacán del Río;20.8667 Ban Na Yang;12.8339 Buriti dos Lopes;-3.1750 Sint-Genesius-Rode;50.7450 Palma Campania;40.8667 Paillaco;-40.0667 Sendamaram;9.0648 Silvânia;-16.6589 Inajá;-8.9030 Sartell;45.6188 Ballincollig;51.8879 Mumbwa;-14.9853 Jadcherla;16.7667 Bezaha;-23.5000 Shirin;40.2269 Xinhua;37.8286 San Gabriel;16.6667 Malimono;9.6183 Preakness;40.9382 Shimomura;36.0696 Saugerties;42.0891 Mahazoarivo;-22.3167 Telpaneca;13.5319 Valenzano;41.0500 Gostynin;52.4167 Rawmarsh;53.4636 Préveza;38.9500 Maumelle;34.8522 Hazorasp;41.3167 Târgu Neamţ;47.2025 Lakhnādon;22.6005 Cabaceiras do Paraguaçu;-12.5358 Zuidhorn;53.2500 Umburanas;-10.7328 Pinole;37.9931 Silver Spring;40.2503 Anjahabe;-16.3667 Akureyri;65.6833 Miarinavaratra;-20.2167 Springwater;44.4333 Cofradía;15.4168 Duero;9.7167 Tinton Falls;40.2708 Guernica y Luno;43.3167 Kosatarosh;39.4000 Haaltert;50.9000 Ambalanirana;-18.7500 Tanambe;-17.3667 Nova Petrópolis;-29.3758 Kalugumalai;9.1494 Pruzhany;52.5567 Matteson;41.5095 Caowotan;37.2739 Santander;9.4500 Proletarsk;46.7000 Manantenina;-24.2833 Belamoty;-23.5500 Schneverdingen;53.1167 Bajo Boquete;8.7800 El Bolsón;-41.9667 Bronte;37.7833 Dixon;38.4469 Arcata;40.8615 Alsip;41.6701 Dankov;53.2500 Martigny;46.1000 Bukungu;1.4361 Pitseng;-29.0097 Mill Hill;51.6200 Onega;63.9167 Ambatomena;-19.8500 Cumberland;39.6515 Renningen;48.7661 Ghedi;45.4020 Oirschot;51.5000 San Rafael La Independencia;15.7167 Peduasi;5.8500 Aburi;5.8500 Devadānappatti;10.1467 Ambatosia;-14.6667 Eitorf;50.7697 Khokha;25.9741 Caconde;-21.5289 Midlothian;37.4856 Scituate;42.1992 Shimanovsk;52.0000 Cortes;9.7167 Dŭstí;37.3486 Befandriana Atsimo;-22.1000 Beshkent Shahri;38.8167 Uwchlan;40.0522 Haukipudas;65.1833 Heerde;52.3833 Laukaa;62.4167 Tiruvadamarudūr;10.9065 Mitsinjo;-16.0000 San Sebastián Salitrillo;13.9500 Beni Zouli;30.4839 Stord;59.8081 Bánovce nad Bebravou;48.7186 Odžak;45.0106 Warwick;40.1558 Kamienna Góra;50.7833 Heanor;53.0140 Las Terrenas;19.3200 Wodonga;-36.1214 Aci Castello;37.5556 Winchester;38.0018 Sylvania;41.7100 Ribera;37.4994 Rizal;14.1083 Zola Predosa;44.4883 Vitré;48.1233 Motomachi;43.8242 Sebaste;11.5901 Chichiriviche;10.9343 Chorozinho;-4.3000 Raghunāthpur;25.8440 Dorval;45.4500 Prenzlau;53.3167 Qitai;41.5494 Jiaoxiyakou;26.1274 Avion;50.4097 Rāzole;16.4761 Korsimoro;12.8250 Altamira;19.6667 Nal Khera;23.8357 Ajacuba;20.0833 Qo’shko’pir;41.5333 Rawa Mazowiecka;51.7658 Marktoberdorf;47.7667 Guachucal;0.9667 Appley Bridge;53.5790 Montceau-les-Mines;46.6669 Ban Bo Haeo;18.3048 Madalag;11.5269 Bourbonnais;41.1830 Kalynivka;49.4472 San Juan Ermita;14.7667 Durango;37.2659 Jucuapa;13.5167 Itapororoca;-6.8300 Santo Antônio do Sudoeste;-26.0700 Āb Pakhsh;29.3606 Villaquilambre;42.6167 Central Point;42.3764 Choghādak;28.9864 Ansonia;41.3443 Castel Maggiore;44.5667 Be’er Ya‘aqov;31.9436 Rubiataba;-15.1639 Pindoretama;-4.0278 Baena;37.6167 Bergeijk;51.3167 Gautier;30.4106 Fihaonana;-18.6000 Kakraul;26.3620 Elumalai;9.8650 Monroe;41.3379 Náousa;40.6333 Affton;38.5499 Tadó;5.2667 Narangba;-27.2015 Nauen;52.6000 Cachoeirinha;-8.4858 Contamana;-7.3333 Baturbāri;26.2281 Guareí;-23.3728 Ponmana;8.3867 Itatira;-4.5289 Ipaba;-19.4139 Polohy;47.4796 Pirkkala;61.4667 Highland;40.4276 Antonina;-25.4289 Sudbury;42.3847 San Sebastián de Buenavista;9.2403 Chapada dos Guimarães;-15.4608 Fakfak;-2.9167 Fox Crossing;44.2228 Massaguet;12.4742 Paks;46.6220 Franklin Farm;38.9133 Mo i Rana;66.3128 Arroio do Meio;-29.4008 Pālaiyampatti;9.5392 Tonantins;-2.8728 Kabala;9.5833 Kāriyāpatti;9.6741 Togba;7.1000 Bad Reichenhall;47.7247 Augusta;44.3341 Hoppegarten;52.5167 Hala;26.8272 Nelidovo;56.2167 Conceição;-7.5619 Louny;50.3571 Cocoa;28.3820 Anzoátegui;4.6339 Sestri Levante;44.2733 Padre Paraíso;-17.0719 Vélez;6.0103 Tādigadapa;16.4713 Alfeld;51.9886 Bartow;27.8868 Kalabahi;-8.2167 Muttenz;47.5167 Kovylkino;54.0403 San Julián;13.6976 Boissy-Saint-Léger;48.7503 Villanueva;10.6000 Rosedale;35.3886 Fushë-Krujë;41.4833 Tessenderlo;51.0697 Sirvel;15.3170 Araç;41.2422 Ebbw Vale;51.7800 Piatykhatky;48.4126 Mellacheruvu;16.8173 Százhalombatta;47.3004 Taft;11.9058 Nunuñgan;7.8167 Tocaima;4.5000 Nefas Mewch’a;11.7333 Brownwood;31.7127 Futog;45.2333 Oulmes;33.4450 Tādikonda;16.4167 Maimón;18.7700 Molakālumuru;14.7178 Hopkins;44.9259 Kara-Köl;41.6311 Kalach;50.4333 Umirim;-3.6769 Monor;47.3475 Milton;43.0406 Le Pontet;43.9642 Alzenau in Unterfranken;50.0667 Anandpur;31.2393 Berkovitsa;43.2333 Taufkirchen;48.0500 Bradfordville;30.5735 Crimmitschau;50.8181 Ngathainggyaung;17.4000 Târgu Secuiesc;45.9969 Villa Nueva;-32.4331 Heguri;34.6292 Thomasville;30.8394 Antotohazo;-18.4667 Yeddumailāram;17.5011 Balilihan;9.7500 Osny;49.0592 Khorabar;26.7403 Vestby;59.5750 Dzüünharaa;48.8500 Koriāpatti;26.1149 Paglat;6.7811 Gossau;47.4167 Pôrto Acre;-9.5878 Springboro;39.5615 Mennzel Bou Zelfa;36.6817 Chowchilla;37.1095 Ferguson;38.7490 Altus;34.6565 Lukovit;43.2000 Māḩiş;31.9833 Starobilsk;49.2829 Rājmahal;25.0500 Avon;41.7907 Or ‘Aqiva;32.5 Ascención de Guarayos;-15.8922 Carlos Spegazzini;-34.8833 Kegalle;7.2531 Soalkuchi;26.1700 Santa Fe Springs;33.9329 Marsciano;42.9167 Sortavala;61.7056 Batalha;-9.6778 Mukkūdal;8.7431 Szamotuły;52.6000 Montrouis;18.9506 Lessines;50.7167 Siderno Marina;38.2667 Middelburg;-31.4939 Bensenville;41.9579 Thorold;43.1167 Palmeiras;-2.6450 East Lyme;41.3668 Ban Mai;14.9629 Owego;42.0881 Novyy Oskol;50.7583 Mānullahpatti;26.0619 Bremervörde;53.4833 Kiliia;45.4500 Bni Bouayach;35.0986 Catarina;-6.1308 Weißenburg;49.0306 Villa Rica;2.5167 Doñihue;-34.2261 San Lorenzo de El Escorial;40.5936 Engenheiro Coelho;-22.4883 Bīdestān;36.2311 Guaranésia;-21.2989 Colonia;40.5926 Ronkonkoma;40.8037 Kappiyara;8.2466 Yupiltepeque;14.1941 Beltsville;39.0394 Ključ;44.5333 Kinzau-Vuete;-5.4967 Shuangluan;40.9608 Oxon Hill;38.7884 Emba;48.8267 Stekene;51.2000 Goyty;43.1642 Ibititá;-11.5469 Concord;38.5117 Onalaska;43.8883 Retiro;-36.0333 Konz;49.7000 Bad Dürkheim;49.4594 Reota;25.8194 Farias Brito;-6.9308 Ajaigarh;24.8988 Blooming Grove;41.3948 Buckhall;38.7239 Rufino;-34.2667 Longjia;19.1487 Camrose;53.0167 Ranquitte;19.4167 Los Lagos;-39.8500 Gāndarbal;34.2262 Domodossola;46.1161 Salamina;5.4083 Tottiyam;10.9880 Cambridge;-37.8833 Jbabra;34.4314 Schönefeld;52.3886 Kumbhrāj;24.3734 Berea;41.3696 Ammāpettai;10.7948 Miyatoko;33.6992 Round Lake;42.3435 Ottawa;41.3555 Cafarnaum;-11.6939 Édessa;40.8000 Murambi;-1.8133 Wisconsin Rapids;44.3927 Jora Khurd;26.4893 Abong Mbang;3.9833 Sanpaicun;24.6642 Destelbergen;51.0500 Tsivory;-24.0683 Morales;8.3133 Inhapi;-9.2189 Çerkeş;40.8139 Leteri;24.0598 Laives;46.4228 Picuí;-6.5550 Rutigliano;40.9333 Asakapalle;17.7364 Caiapônia;-16.9569 Sidi Allal Tazi;34.5197 Surovikino;48.6000 Toflea;46.0637 Qaryat al Qī‘ān;32.0167 Andéranboukan;15.4283 Doura;12.0167 Mumford;5.2625 Ibipeba;-11.6408 Keskin;39.6731 Pitimbu;-7.4708 Santiago de María;13.4833 Sendārappatti;11.4373 Carlos Chagas;-17.7028 Tummalapenta;14.8997 Nīār;38.2369 Liuma;25.6682 Jhagarua;26.0059 Vovchansk;50.2881 Capua;41.1056 Marshfield;44.6627 Jaguaripe;-13.1128 Bagnols-sur-Cèze;44.1625 Vlotho;52.1667 Terzigno;40.8000 Boa Vista do Tupim;-12.6600 Raghunāthpur;26.1145 Orăştie;45.8500 Kavalerovo;44.2702 Siddāpur;14.3470 Filomeno Mata;20.2000 Sheridan;44.7961 Thorne;53.6083 Baliangao;8.6667 San Rafael Pie de la Cuesta;14.9333 Itapiúna;-4.5639 Jiadong;22.4305 Bilar;9.7000 Pohādi;26.0387 Freilassing;47.8333 Acharipallam;8.1700 Myślenice;49.8347 Malnate;45.8000 Creve Coeur;38.6620 Ughara;26.0563 Johnstown;40.3260 Bönen;51.5986 Ashland;42.2573 South Frontenac;44.5081 Guanzhai;26.2697 Antombana;-15.0000 Bonanza;14.0275 Sāhna;30.4293 Lora del Río;37.6500 Limay;48.9933 Tamri;30.6950 Kulu;31.9600 Unhel;23.3379 Cajari;-3.3208 Fayetteville;33.4501 Sivandipuram;8.7811 Solrød Strand;55.5167 Bīmgal;18.7000 Jawkatiā;26.7308 Malacacheta;-17.8419 Porto Real;-22.4200 Bistāria;26.1262 Skara;58.3833 Bombon;13.6867 Manganj;26.1388 Vanino;49.0873 Cimarron Hills;38.8597 Carnaíba;-7.8050 Shaogang;38.1584 Knightdale;35.7911 Tata;29.7428 Beerse;51.3167 Cluses;46.0603 Unquillo;-31.2333 Tatsuno;35.9824 Orestiáda;41.5000 Olho d’Água das Cunhãs;-4.1389 Tarifa;36.0140 Amīnpur;17.5241 Busovača;44.1000 Montville;41.4636 Saylac;11.3539 Pottanūr;11.1098 Gidi;23.6909 Sam Phran;13.7270 Misato;39.4616 Taió;-27.1158 Bhawānīpur;25.3944 Lahnstein;50.3011 Yoshimi;36.0399 Kuttālam;11.0758 Kunitomi;31.9906 Chubek;37.6167 Balş;44.3500 Timoktene;27.0217 Payyannūr;12.1500 Phelan;34.4398 Paluan;13.4167 Firminy;45.3881 Brand;50.7489 Denizciler;36.6500 Budrio;44.5500 Brook Park;41.4036 Vissannapeta;16.9500 San Carlos Park;26.4765 Veternik;45.2533 Gerd Farāmarz Shāhedīyeh;31.9408 Abrandābād-e Shāhedīyeh;31.9414 Cariús;-6.5369 Conceição do Almeida;-12.8089 Zhangshicun;24.5477 Tinajeros;14.6733 Presidente Olegário;-18.4178 Dapi;23.6494 Mūkondapalli;12.7514 Presidente Médici;-11.1758 Kasongan;-1.8959 Colina;-20.7136 Neópolis;-10.3200 Moloacán;17.9833 Newton;38.0368 Kristiansund;63.1104 Bel Air;-20.2582 Oskarshamn;57.2650 Pitt Meadows;49.2333 Zagarolo;41.8333 Nigrán;42.1419 Capanema;-25.6719 Priozërsk;61.0500 San Celoni;41.6895 Pizarro;4.9500 Santa Cruz Zenzontepec;16.5333 Kottá Kalidindi;16.5032 Melville;40.7824 Springfield;36.4949 San Diego;10.3375 Wheatfield;43.0975 Turuvanūr;14.2200 Ranzan;36.0565 Feilding;-40.2167 Chouafaa;34.7667 Tinoc;16.6750 Tyāgadurgam;11.7411 Poulton le Fylde;53.8470 Guadix;37.3000 Berkhampstead;51.7600 Asunción Nochixtlán;17.4592 Shaoyu;34.0629 Oskū;37.9108 Borgo San Lorenzo;43.9500 Heusweiler;49.3375 Langen;53.6116 Międzyrzecz;52.4483 Vera;37.2500 Sarotar;26.4291 Guabiruba;-27.0858 Toyono;34.9189 Cordenons;45.9833 Palestine;31.7544 Fátima;-10.6000 Bramhall;53.3570 San Blas Atempa;16.3167 Tha Bo;17.8494 Campo de la Cruz;10.3778 Kherāmeh;29.5000 Walcourt;50.2500 Littau;47.0494 Bitkine;11.9817 Siqueira Campos;-23.6889 Ponedera;10.6500 Rhaude;53.1667 Plácido de Castro;-10.2758 Foum el Anser;32.3718 Mūlanūr;10.7943 Kivsharivka;49.6303 Kitanakagusuku;26.3011 Cândido Mendes;-1.4469 Dniprorudne;47.3907 Kostrzyn nad Odrą;52.5883 Hatfield;40.2758 Beloozërskiy;55.4589 Somma Lombardo;45.6833 Savignano sul Rubicone;44.0881 North Myrtle Beach;33.8232 Nederland;29.9707 Martinsburg;39.4582 Hopkinton;42.2255 Mayorga;10.9000 San Vicente Pacaya;14.4161 Apastepeque;13.6667 Nara;15.1800 Trenton;42.1394 Parole;38.9863 Qaşr-e Shīrīn;34.5156 Wilton;41.2070 Piatã;-13.1519 Mirangaba;-10.9539 Los Alcázares;37.7436 Deoria;26.1791 Arroio Grande;-32.2378 Itapaci;-14.9508 Udaipura;23.0743 Fairwood;47.4467 Sharon;42.1085 Middelharnis;51.7500 Cesário Lange;-23.2267 Nerviano;45.5500 Taft;31.7492 Lakeway;30.3544 Logansport;40.7472 Lamrasla;32.0247 Bakhmach;51.1810 Otrokovice;49.2099 Elizabeth City;36.2942 Albino;45.7606 Felanitx;39.4692 Astrea;9.5000 Villorba;45.7333 Békés;46.7667 Bad Segeberg;53.9167 Levin;-40.6219 Yur’yev-Pol’skiy;56.5000 Zielonka;52.3008 Gheorgheni;46.7200 Bettioua;35.8000 Korsun-Shevchenkivskyi;49.4261 Duffel;51.1000 San José Tenango;18.1500 São José da Coroa Grande;-8.8978 Montornés del Vallés;41.5444 Nipomo;35.0319 Kreminna;49.0500 Amvrosiivka;47.7958 Fulwood;53.3650 Phoenixville;40.1359 Svatove;49.4150 Icapuí;-4.7128 Campos Belos;-13.0369 Franklin Park;41.9361 Badantola;25.2402 Reriutaba;-4.1419 Albert Lea;43.6548 Almondbury;53.6344 Foxborough;42.0627 Schwechat;48.1411 Selma;32.4166 Fontaine-l’Évêque;50.4167 Sengés;-24.1128 Buenavista;8.6833 Hampton;40.5844 Heidenau;50.9833 Durleşti;47.0333 Concord;42.4620 Cagdianao;9.9167 Arroyo Grande;35.1241 Bodegraven;52.0822 Budhni;22.7825 Harrisburg;35.3125 Belo Campo;-15.0378 Wörth am Rhein;49.0517 Harinākunda;23.6500 Caldas de Montbuy;41.6328 Schwalmstadt;50.9333 Tallmadge;41.1023 Omalūr;11.7300 Santa Maria di Sala;45.5089 Schwarzenbek;53.5042 Pinecrest;25.6650 L’Isle-d’Abeau;45.6194 Taku;33.2833 Visé;50.7333 Mineros;-17.1178 Kadaň;50.3761 Formoso do Araguaia;-11.7969 Serra Dourada;-12.7608 Cinco Ranch;29.7395 Monthey;46.2500 Vechelde;52.2608 Ommen;52.5167 Gálvez;-32.0333 Burlington;39.0223 Carvin;50.4931 Nytva;57.9500 Iati;-9.0458 Cassano al Ionio;39.7833 Garwolin;51.9000 Vettavalam;12.1077 Ekazhevo;43.2081 Yoichi;43.1953 Hessisch Oldendorf;52.1667 Palos Hills;41.6986 Borgaon;16.4200 Jussara;-15.8650 Shirāli;14.0297 Yutz;49.3589 Gothini;26.1626 Do’stlik Shahri;40.5247 Portsmouth;38.7538 Fairmont;39.4768 Aït Bouchta;35.1056 Assa;28.6086 Novopavlovka;42.8700 Piúma;-20.8350 Memuro-minami;42.9119 Talata-Volonondry;-18.7500 Saladas;-28.2500 Galaat el Andeless;37.0625 Durant;33.9949 Großenhain;51.2833 Sørum;59.9871 East Goshen;39.9934 Cariré;-3.9508 Stonington;41.3738 Mirante do Paranapanema;-22.2919 Bet She’an;32.5 Alauli;25.6440 Qishe;24.9232 Końskie;51.2000 Hāvi Bhauār;26.1091 Shika;37.0062 Calera de Tango;-33.6302 Jalpa de Méndez;18.1764 Castelo do Piauí;-5.3219 Araçoiaba;-7.7900 Windham;43.7981 Serrita;-7.9328 Santa Lucía Milpas Altas;14.5757 Piossasco;44.9906 Flórina;40.7833 Dobrush;52.4167 Waalre;51.4000 Illertissen;48.2167 Gitagum;8.5956 Aragarças;-15.8978 Monforte de Lemos;42.5164 Baependi;-21.9589 Wantagh;40.6686 Shaying;25.9740 Pembroke;42.0655 Siloe;-29.9836 Braniewo;54.3833 Garching bei München;48.2500 Dehti;26.2115 Clive;41.6147 Santa Fe;16.1592 Niles;41.1879 Vertentes;-7.9028 Meishan;23.5607 Oyama;35.3601 Tillmans Corner;30.5819 Republic;37.1452 Sikhio;14.9000 Concepción de Ataco;13.8667 Naarden;52.2953 Bekalta;35.6167 Manchester;38.5830 Nehbandān;31.5419 Port Colborne;42.8833 Kosvik;58.1500 Kikube;1.3328 Pocinhos;-7.0769 Pisz;53.6333 San Cristóbal Cucho;14.9000 Garopaba;-28.0228 Ribat Al Khayr;33.8200 Slyudyanka;51.6333 Koratagere;13.5220 Sikandra;24.9564 Svilengrad;41.7667 Eski-Nookat;40.2658 Şenkaya;40.5619 Naula;25.5535 Tavares;28.7920 Savaştepe;39.3839 Vennandūr;11.5150 Paraibuna;-23.3861 Antanambao;-15.1667 Condeúba;-14.8950 Newburyport;42.8124 Knik-Fairview;61.5082 Sipacate;13.9333 Panórama;40.5833 Xochiatipan de Castillo;20.8333 Nyborg;55.3122 Dergaon;26.7000 Ocean Springs;30.4082 Goffstown;43.0190 Bouarouss;34.3667 Isfana;39.8389 Somerset;41.7404 Werdohl;51.2667 Auerbach;50.5094 San Kamphaeng;18.7500 Vāyalpād;13.6500 Almeria;11.6206 Khutāha;25.2645 Seagoville;32.6530 Kishi;35.3364 Kottapalle;17.2918 Washington;40.7877 Marcos;18.0444 Gangwuzhen;25.9644 Veldurti;15.5667 Ikniwn;31.1736 Wadowice;49.8833 San Antonio de Ibarra;0.3627 Ambāla;17.6500 Ōi;35.3266 Kudavāsal;10.8582 Quispamsis;45.4322 Bad Säckingen;47.5500 Kiyama;33.4270 Greene;39.9543 Gibsonton;27.8260 Sunbury;51.4230 Andalucía;4.1667 San Pedro;4.0000 Púchov;49.1200 Sierpc;52.8833 Čitluk;43.2000 Ploemeur;47.7358 Ţāqah;17.0372 Băicoi;45.0453 Maripād;17.4031 Tordera;41.7008 Manga;-14.7558 Pinabacdao;11.6167 Chapelle;19.4167 San Jose;12.5310 Rikuzen-Takata;39.0280 Pieksämäki;62.3000 Loay;9.6000 McAlester;34.9257 Łask;51.5903 North Hykeham;53.1833 Trujillo;4.2500 Padugupādu;14.4885 Drăgăşani;44.6611 Dombóvár;46.3820 Alpignano;45.1000 Amsterdam;42.9420 Wemmel;50.9167 Gikongoro;-2.4697 Kingsland;30.8194 Nagnur;16.1400 Son en Breugel;51.5167 Sarvestān;29.2736 Baaqlîne;33.6797 Lake Ronkonkoma;40.8297 São Francisco de Assis;-29.5500 Īlām;26.9080 Ipanema;-19.8008 Amīngarh;16.0572 Kiangara;-17.9667 Mŭynoq;43.7667 Glencoe;-28.1833 Vordingborg;55.0000 Ingá;-7.2678 Belākoba;26.5744 Lenoir;35.9094 Concord;39.8741 St. Marys;30.7567 Mont-Saint-Hilaire;45.5622 Kukraun;25.6858 Klaeng;12.7778 Tremedal;-14.9758 Cudahy;42.9467 ‘Anbarābād;28.4758 Pānchgrām;24.1996 Antsampanimahazo;-19.6400 Maroaloka;-25.1667 Lavāsān;35.8231 Quibaxi;-8.5000 Ismaning;48.2264 Avtury;43.1667 Trinity;28.1809 Steiner Ranch;30.3654 Santo Antônio do Leverger;-15.8658 Rāmpur;21.0735 Santa Cruz Naranjo;14.3833 Ghouazi;34.4667 Sint-Oedenrode;51.5636 Parchim;53.4167 Pratteln;47.5167 Beruri;-3.9022 Vīrapāndi;11.1723 Três Barras;-26.1058 Almolonga;14.8122 New Castle;41.1841 Central;-11.1542 Hranice;49.5525 Alto Araguaia;-17.3150 Sangenjo;42.4017 Bafanji;5.8361 Châteauneuf-les-Martigues;43.3831 Birstall;53.7320 Menasha;44.2125 Juanacatlán;20.5000 Manīn;33.6422 Minamishibetsuchō;44.1786 Bathurst;47.6200 Rāyappanpatti;9.7997 Shirva;13.2465 Cuautitlán;19.4333 Tapauá;-5.6278 Libjo;10.1960 Lebanon;44.5317 Willimantic;41.7171 St. Michael;45.2014 Calenzano;43.8667 Santa Vitória;-18.8389 Sycamore;41.9957 North Druid Hills;33.8185 Alakamisy;-20.2000 Saint-Augustin-de-Desmaures;46.7333 Upper Grand Lagoon;30.1690 Mahesh Khunt;25.4500 Bexbach;49.3494 Shedbal;16.6892 Solonópole;-5.7328 Goshaingaon;26.4395 São Miguel do Tapuio;-5.5039 Patar;40.3333 Huitán;15.0486 Atalaia do Norte;-4.3719 Ankofa;-15.4000 Charleston;39.4842 Buadiposo-Buntong;7.9667 Marcon;45.5543 Maranello;44.5333 Coronda;-31.9667 Bucay;17.5388 Manari;-8.9639 Colonial Heights;37.2650 Feyẕābād;35.0144 San Vicente;6.2819 Blythe;33.6219 Sona;45.4333 Steubenville;40.3653 Whitestown;43.1350 Mendes;-22.5269 Vohilengo;-17.3000 Ambongo;-23.4500 Ambiula;-7.4333 Madeley;52.6370 Kelsterbach;50.0617 Mengjiacun;40.0186 Kuchinda;21.7436 Matigou;37.6043 Gorham;43.7034 Jacksonville;39.7292 Quanzhang;35.6613 Dīzīcheh;32.3836 Bobingen;48.2667 San Pedro;8.7333 Pescantina;45.4833 Manampatrana;-21.6500 Southeast;41.4032 Analaiva;-20.3333 Madalena;-4.8569 Tsararivotra;-19.1714 Natchitoches;31.7315 Port-de-Bouc;43.4050 Inkollu;15.8300 Herzele;50.8833 General Juan Madariaga;-37.0167 Gunjur;13.1833 Alatsinainy Ialamarina;-21.5333 Oak Bay;48.4264 Muḩradah;35.2500 Tall ‘Aran;36.1231 Bhucho Mandi;30.2129 Kharhiāl;20.2885 Ashtabula;41.8805 Putte;51.0500 Ricaurte;1.2108 Behenjy;-19.2000 Piaçabuçu;-10.4086 Santa Fe;12.1500 Aliyābād;12.6317 Lattes;43.5678 Valguarnera Caropepe;37.5000 Pastos Bons;-6.6019 Canby;45.2652 Plymouth;40.1115 Manthani;18.6500 Stilfontein;-26.8428 Turmalina;-17.2858 Villanueva del Pardillo;40.4833 Abangaritos;10.2492 Anda;9.7440 Cortes;9.2753 La Marque;29.3683 Biro;9.9000 Scarsdale;40.9902 Freudenberg;50.8997 Tsianisiha;-22.9000 Pūlla;16.8060 San Rafael;11.1728 Dahibhāt Mādhopur;26.2763 Tall Qaşab;36.2583 Maryland City;39.1016 Jaicós;-7.3589 Hijuelas;-32.7986 Boone;36.2111 Mabéhiri;5.6833 Cruz Machado;-26.0169 Curti;15.4167 Sorrento;40.6278 South Hadley;42.2567 Fuente-Álamo de Murcia;37.7394 Belobaka;-18.9833 Amboahangibe;-14.1333 Silvāni;23.3026 White Settlement;32.7554 Munguía;43.3547 Vilāttikulam;9.1312 Sinzig;50.5453 Sevierville;35.8872 Zuhres;48.0181 Antanambe;-16.4333 Oroszlány;47.4833 Batié;9.8833 Ibrāhīmpatan;17.1017 Qal‘ah-ye Zāl;37.0150 Itaberá;-23.8619 Maroteza;-22.3000 Shirosato;36.4792 Ambovombe Afovoany;-20.7667 Kolonodale;-1.9833 Matsakabanja;-15.9167 Texistepeque;14.1333 Polignano a Mare;41.0000 Tranoroa;-24.7000 Pomáz;47.6431 Sileby;52.7310 Mosbrough;53.3250 Yoboki;11.5167 Castaic;34.4818 Maridi;4.9100 Busolwe;0.8492 Beantake;-23.8000 Neftegorsk;52.8000 Tárrega;41.6464 Namayingo;0.3450 Mahazoma;-17.1667 Ginsheim-Gustavsburg;49.9833 General Deheza;-32.7564 Bemahatazana-Belobaka;-19.3500 Pimentel;19.1833 Doruma;4.7333 Ashby de la Zouch;52.7460 Thānga;24.5500 Rypin;53.0667 Baronissi;40.7462 Alice;27.7556 Brielle;51.9000 Flora;18.2147 Oborniki;52.6500 Ervália;-20.8400 Arco;45.9167 Ibirataia;-14.0669 Alton;26.2884 Outat Oulad Al Haj;33.3333 Messias;-9.3828 Centralia;46.7223 Hương Canh;21.2833 Remagen;50.5786 Odaipatti;9.8323 Towamencin;40.2417 Sabana de Torres;7.4000 Crowley;32.5781 Puurs;51.0761 Kharabali;47.4050 Oconomowoc;43.0996 Kongsvinger;60.1905 Antenor Navarro;-6.7289 Marabut;11.1167 Pontarlier;46.9061 Redland;39.1339 Melfi;41.0000 Jovellar;13.0667 Laurel;37.6375 Farmington;37.7822 Pelaya;8.6833 Policoro;40.2000 Susner;23.9467 Papanduva;-26.3700 Kakkalapalle;14.6415 Kreuzau;50.7470 Frankenberg;51.0589 Coltauco;-34.3000 Zapotlán del Rey;20.4674 Gaoniang;26.8394 Harwich;51.9340 Shorewood;41.5169 Lagoa da Canoa;-9.8300 Unión de San Antonio;21.1280 Lobogo;6.6333 Juazeirinho;-7.0678 Rājbalhāi;22.7600 Pyskowice;50.3833 Manambaro;-25.0333 McKeesport;40.3418 Ban Thum;16.4523 Cohoes;42.7732 Saint Andrews;56.3404 Tres de Mayo;-26.4794 Golitsyno;55.6147 Weilerswist;50.7525 Bhai Rupa;30.4311 Buchen in Odenwald;49.5217 Guapiara;-24.1850 Kristinehamn;59.3000 Yaguachi Nuevo;-2.1200 Āyikudi;9.0032 Marmande;44.5000 Monselice;45.2333 Cary;42.2129 Broken Hill;-31.9500 Bay City;28.9838 Nguigmi;14.2532 Kirsanov;52.6500 Madanpur;26.2103 Biliran;11.5800 South Fayette;40.3556 Boqueirão;-7.4819 Anadia;-9.6844 Puquio;-14.6939 Benavente;42.0031 Berlín;13.5000 Herve;50.6333 Międzyrzec Podlaski;51.9833 Pālaiyam;10.7256 Galich;58.3833 Serafina Corêa;-28.7119 Eslöv;55.8392 Yuanquan;40.5004 Khirhar;26.5168 Ambohitralanana;-15.2333 Sakardih;25.2280 Porto San Giorgio;43.1833 Groveland;28.6098 Catarman;9.1333 San Bartolo Tutotepec;20.4000 Wangaratta;-36.3583 Settiyārpatti;9.3935 Cullman;34.1775 North Aurora;41.8086 Lillerød;55.8681 Gazantarak;39.9667 Ocean Acres;39.7430 Norrtälje;59.7667 Arai;34.6833 San Rafael del Norte;13.2128 North Canton;40.8742 Panama City Beach;30.2370 Tiffin;41.1165 Banhatti;16.3853 Holzwickede;51.5000 Al Qbab;32.7333 Jacupiranga;-24.6925 Jaguaretama;-5.6128 Birhana;25.4489 Alton;51.1498 Ban Mae Ngon Khilek;19.8021 Buguda;19.8081 Arinos;-15.9169 Montanha;-18.1269 Cenoví;19.2500 Douar Oulad Mbarek;34.2833 Taree;-31.9000 Frederikssund;55.8333 Yanggezhuang;39.3831 Kāgvād;16.4800 Carlentini;37.2833 Amba Icharua;25.6140 Kukmor;56.1855 Barão do Grajaú;-6.7558 Nazaré Paulista;-23.1808 Rio Claro;-22.7228 Salgar;5.9617 Bon Air;37.5187 Manzanares;38.9964 Mitake;35.4344 Schwalbach;49.2833 Colón;9.9096 Ash Shaykhān;36.6917 Cesson-Sévigné;48.1208 Orta Nova;41.3308 Wadgassen;49.2667 Isola Capo Rizzuto;38.9589 Shangjing;24.6076 Zhongguyue;38.2833 Gretna;29.9101 Miaojiaping;37.5777 Longjia;36.0608 Sobinka;56.0000 Ngara;-2.5122 Seyah Cheshmeh;39.0631 Salonta;46.8000 Doylestown;40.2962 Las Heras;-46.5500 Pinili;17.9540 Bovisio Masciago;45.6167 Pātan;23.2864 Porciúncula;-20.9628 Bechem;7.0833 São Benedito do Rio Preto;-3.3339 Bāţūfah;37.1744 Agua Blanca;14.4833 Sapeaçu;-12.7278 Köping;59.5167 Godfrey;38.9577 Daimiel;39.0833 Ban Phe;12.6287 Hlybokaye;55.1333 Pirapora do Bom Jesus;-23.3972 Sirari;-1.2244 Não-Me-Toque;-28.4589 Oerlinghausen;51.9667 Latsia;35.1000 Portsmouth;41.5922 Dirba;30.0700 Santa Elena;-30.9500 Trossingen;48.0756 El Dorado;33.2184 Grenchen;47.1906 Esperanza;11.7369 Morros;-2.8639 Cameron Park;38.6738 Orsay;48.6981 Portchester;50.8420 Beni Fouda;36.2861 Poyo;42.4333 Gautampura;22.9866 Reni;45.4575 Kiangan;16.7775 Guttal;14.8333 Minquan;37.4420 Swellendam;-34.0231 Rockland;42.1295 Bishunpura;26.5436 Calverton;39.0578 Cairu;-13.4869 Winder;33.9917 Kahoku;38.4263 Sanary-sur-Mer;43.1192 Troy;31.8021 Balaguer;41.7904 Monzón;41.9100 Zaslawye;54.0083 Turuvekere;13.1637 Nova Pazova;44.9500 Laç;41.6353 Sāram;23.7625 Zalţan;32.9500 Tolūprpatti;11.0244 Banora Point;-28.2225 Balighattam;17.6510 Złotoryja;51.1333 Glen Parva;52.5867 Dingjiagouxiang;35.5307 Duijiang;27.0782 Stegen;47.4760 Canapi;-9.1269 Anoka;45.2099 Qal’at Mgouna;31.2414 Laatatra;32.6315 Nīkshahr;26.2258 Eastlake;41.6581 Ruhango;-2.2325 Madison;41.3398 Linbian;22.4333 Talitay;7.0353 Cipó;-11.1000 Amacuzac;18.6000 Sierre;46.3000 Schrobenhausen;48.5333 Kodumudi;11.0769 Ban Pong;13.8174 Itacarambi;-15.1019 Bahon;19.4694 Lousã;40.1125 Tábara Arriba;18.5694 New River;33.8835 Estancia Pozo Colorado;-23.4136 Ban Mae Ka Hua Thung;19.0942 Cerrillos;-24.9000 Pirayú;-25.4800 Schaesberg;50.9000 Tanque Novo;-13.5458 Yerrapālem;16.1500 Mount Washington;38.0430 Thị Trấn Ngải Giao;10.6406 Tizi Rached;36.6718 Ambolomadinika;-21.9500 Spanish Lake;38.7884 Loboc;9.6333 Sibutao;8.6131 Sānwer;22.9742 Milanówek;52.1333 Fraserpet;12.4587 Jucurutu;-6.0339 Rijen;51.5833 Calceta;-0.8400 Delran;40.0170 Verdun;49.1597 Salmon Arm;50.7022 Alcochete;38.7500 Fanandrana;-18.1944 Palanan;17.0589 Asarcık;41.0314 Ashton;-44.0333 Matipó;-20.2839 Nedugula;11.4764 Rio Maria;-7.3108 Tlahuelilpan;20.1297 Dharampuri;22.1495 Sokółka;53.4000 Benito Juárez;20.8833 Kempele;64.9125 Fléron;50.6167 Kitahiroshima;34.6746 East Lampeter;40.0375 Pionki;51.4833 Dourbali;11.8050 Varjota;-4.1939 Preganziol;45.6000 Al ‘Ashārah;34.9203 Matagob;11.1469 Libertad;11.7690 Baxt;40.7139 Tufanbeyli;38.2646 Cerca Carvajal;19.2667 Latifpur;24.1181 Zhoujia;35.2976 Villiers;-27.0333 Killingly;41.8311 La Garriga;41.6804 Louth;53.3669 Pio IX;-6.8378 Port Alberni;49.2339 Verneuil-sur-Seine;48.9797 Chhapra Bahās;26.7266 Guidan Roumdji;13.6575 Boljoon;9.6333 Arsanjān;29.9125 Schijndel;51.6167 Santos Reyes Nopala;16.1000 Attappampatti;11.4820 Novo Airão;-2.6208 El Carmen de Chucurí;6.6981 Restrepo;4.2500 Totoró;2.5117 Ribeirão Branco;-24.2208 Seoni Chhapāra;22.3939 San Alejo;13.4333 Bendorf;50.4297 Pianoro;44.3833 Aït Majdane;31.8514 Webster;42.0521 Nerang;-27.9956 Baquerizo Moreno;-1.9167 Dighaun;25.5549 Yayas de Viajama;18.6000 Dugo Selo;45.8058 Pernamitta;15.5333 Strzelce Opolskie;50.5000 Polyarnyy;69.2000 Abejorral;5.7894 San Rafael Cedros;13.7333 Agourai;33.6333 San Jorge;11.9833 Arauco;-37.2500 Dabas;47.1890 Estelle;29.8447 Rāni Shakarpura;25.5534 Lam Luk Ka;13.9297 Kalanaur;32.0200 Manilva;36.3833 Tricase;39.9333 Bailén;38.0833 Inza;53.8500 San Bernardino;14.5333 Totutla;19.2167 Southbridge;42.0604 Mawkanin;15.5106 Sìnnai;39.3026 Aïn Zaouïa;36.5483 Same;-4.0667 Chedaopo;36.4008 Tougan;13.0667 Noniyā;26.6763 Saint-Cyr-sur-Loire;47.4028 Rocca di Papa;41.7667 Jomboy Shahri;39.6989 Vallières;19.4333 As Suqaylibīyah;35.3697 Para;5.5167 Barrhead;55.8010 Magalhães de Almeida;-3.3958 Balma;43.6103 Bethpage;40.7495 Bad Münder am Deister;52.1992 La Primavera;5.4906 Awlouz;30.7000 Wayne;42.2774 Hinode;35.7421 New Philadelphia;40.4860 Colbún;-35.7000 Şavşat;41.2433 Belém;-6.7469 Gokavaram;17.2667 Filadélfia;-10.7408 Poção de Pedras;-4.7500 Gudipallipādu;14.4588 Brignoles;43.4058 Yondó;7.0000 Kamudi;9.4090 Humacao;18.1520 Kembhāvi;16.6500 Pisticci;40.3833 Zaysan;47.4667 Bakouma;5.6986 Kirksville;40.1986 Cubellas;41.2100 Marche-en-Famenne;50.2167 Pataskala;40.0110 Afrânio;-8.5000 Pont-à-Celles;50.5000 San Giovanni Valdarno;43.5644 Mansingha;26.7807 Wendlingen am Neckar;48.6747 Ivanovka;42.8864 Vimodrone;45.5139 Lubang;13.8586 Phirangipuram;16.3000 St. Matthews;38.2497 Tenango de Doria;20.3356 Sulaco;14.9167 Toba;34.4813 Compostela;21.2389 Rubano;45.4333 Bad Langensalza;51.1081 Freienbach;47.2000 Sedeh Lanjān;32.3781 Sagrada Familia;-35.0000 Cortland;42.6004 Ban Song;8.6603 Nova Era;-19.7606 Kasimkota;17.6736 Mudukulattūr;9.3440 Kawa;17.0897 Sahel;34.9667 Neerpelt;51.2333 Passagem Franca;-6.1800 Tabernes de Valldigna;39.0722 Regeneração;-6.2378 Havelock;34.9078 Jenison;42.9063 Ebino;32.0333 Perupālem;16.3705 Thale;51.7500 Raunheim;50.0167 Marktredwitz;50.0000 Shin-Kamigotō;32.9844 Coalinga;36.1420 Derhachi;50.1114 Rojales;38.0886 Sapucaia;-21.9950 Fanipal;53.7500 Paragaticherla;16.3324 Rio Bananal;-19.2650 Miharu;37.4406 Tasquillo;20.6167 Zandvoort;52.3667 Ketama;34.9158 Chorhat;24.4274 Morarano-Gara;-18.7100 Tagana-an;9.6964 Colchester;44.5545 Bílina;50.5481 Laguna Woods;33.6099 Höganäs;56.2000 San Juan Nonualco;13.5072 Castelfiorentino;43.6000 Dighirpār;22.3034 Yeşilhisar;38.3500 Guanagazapa;14.2333 Cherān;25.3610 Miramichi;47.0196 Wittenberge;53.0000 Băileşti;44.0308 Lemont;41.6695 Chadchan;17.1700 Puerto Rico;-26.8000 Ban Tha Mai I;18.7461 Mārtahalli;11.9919 Damme;52.5208 Adelfia;41.0000 Kombai;9.8475 Plast;54.3833 Scott;40.3875 Thornton;53.7898 Karukh;34.4922 Kishundāspur;25.3334 Windlesham;51.3600 Dharmāpuri;18.9475 Dar Chaifat;32.5500 Goodlettsville;36.3330 Pudu;12.8667 Cafelândia;-21.8025 Agaram;10.4433 Challapalle;16.1175 Stowbtsy;53.4833 Kushk;33.2956 Plainville;41.6741 Hatfield;53.5800 Puerto Guzmán;0.9667 Conceição do Mato Dentro;-19.0369 Niagara-on-the-Lake;43.2553 Soumagne;50.6167 Radzionków Nowy;50.3833 Lalībela;12.0317 Krasnokumskoye;44.1778 Berga;42.1000 Ziracuaretiro;19.4189 Luz;-19.8008 Kolokondé;9.9000 Ambatolahy;-20.0000 Cheraro;14.3958 Woodcrest;33.8789 Forssa;60.8167 Gunjāpalle;14.3845 Kottapeta;15.7913 Karachev;53.1167 Santa Magdalena;12.6489 Samaná;5.5833 Sake;-1.5741 Carmo;-21.9339 Bahutāl;24.5600 Puerto Caimito;8.8700 Kadogawa;32.4712 Sant’Elpidio a Mare;43.2295 Tirukkāttuppalli;10.8481 Suvorov;54.1500 Lara;-38.0167 Concorezzo;45.5897 Pavullo nel Frignano;44.3327 Aurāhi;26.1895 Nāranattenvanpatti;9.7233 Sulzbach;49.2833 São Raimundo das Mangabeiras;-7.0219 Burgdorf;47.0500 Penugonda;16.6569 San Isidro;12.9283 Osuna;37.2333 Correntes;-9.1289 Guaranda;8.4672 Epitacio Huerta;20.1348 Camilo Ponce Enríquez;-3.0500 Skadovsk;46.1167 Lake Butler;28.4862 New Castle;39.9191 Sigmaringen;48.0867 Malhada;-14.3358 Mỹ Lương;20.8667 Saint-Lin--Laurentides;45.8500 Mori;34.8356 Calafat;43.9858 Nidda;50.4128 Chambellan;18.5667 Alcantara;12.2584 Bijni;26.4959 Anosiarivo;-19.9167 Centerton;36.3566 Zarumilla;-3.5014 Mareth;33.6333 Bolhrad;45.6855 Montemor-o-Novo;38.6500 Galaz;34.5500 Mahālandi;24.0738 Ottakkadai;9.9580 Mwingi;-0.9333 Saguday;16.5394 Calayan;19.2619 Conchas;-23.0134 Mujikharf;38.8500 Puerto Aysén;-45.4000 Kafr Zaytā;35.3736 Cássia;-20.5828 Mehrān;33.1222 Anacortes;48.4878 Srīkūrmam;18.1600 Nadugadda;16.3539 Veinticinco de Mayo;-37.7712 Brookfield;41.4674 Arachchalūr;11.1627 Cadoneghe;45.4500 Gokarn;14.5500 Tapiramutá;-11.8469 Dijiasuoxiang;35.6883 Gachancipá;4.9908 Hope Mills;34.9710 Gescher;51.9569 Bedford;41.2250 Raychikhinsk;49.7833 Hidrolândia;-16.9619 Suesca;5.1000 Mannō;34.1923 Norcross;33.9379 Bad Münstereifel;50.5531 Saint-Égrève;45.2317 Gennep;51.7000 La Algaba;37.4500 Pirapemas;-3.7269 Arani;13.3346 Çat;39.6111 Nümbrecht;50.9053 Fate;32.9430 Nizhnyaya Salda;58.0667 Khunti Dhanaili;25.9376 Bakeshiyingcun;40.7237 Ammon;43.4745 Ibirama;-27.0569 Baie de Henne;19.6667 Soavina;-18.9500 Beaumont;53.3572 Péruwelz;50.5167 Rattaphum;7.1412 Alcantara;9.9715 Emmiganūru;15.1500 Yeni Suraxanı;40.4311 Morganton;35.7408 Staphorst;52.6500 Arpaçay;40.8483 Kouka;11.9000 Betulia;6.1122 Palhālan;34.1823 Brenham;30.1584 Künzell;50.5500 Dazhangzi;40.6239 Saint-Pierre-des-Corps;47.3908 Oak Grove;45.4156 Eutin;54.1378 Santo Antônio do Amparo;-20.9458 Stockelsdorf;53.8833 Fairview;40.1735 Berezhany;49.4464 Heilbad Heiligenstadt;51.3789 Kamenz;51.2667 Figline Valdarno;43.6167 Milla’ab;31.4737 Laurel;31.6956 Bad Wildungen;51.1167 Barbastro;42.0361 Riva del Garda;45.8833 Saint-Julien-en-Genevois;46.1442 Stockerau;48.3858 Cosautlán;19.3333 Horn-Bad Meinberg;51.8833 Ayacucho;-37.1333 G‘uzor;38.6208 Wohlen;47.3506 Ampère;-25.9150 Domont;49.0275 Sohtha;25.6449 Pital;10.6024 Stockach;47.8514 East Finchley;51.5902 Sudipen;16.9000 South Ogden;41.1722 Saint-Maximin-la-Sainte-Baume;43.4533 Penn;39.7994 Freiberg am Neckar;48.9333 Krasnoslobodsk;48.7000 Diepholz;52.6072 Guryevsk;54.7833 Tonj;7.2800 São Domingos do Prata;-19.8650 Saviano;40.9167 Souto Soares;-12.0889 Gussago;45.6000 Shumikha;55.2333 Muniz Freire;-20.4639 Volgorechensk;57.4439 Jaguaruna;-28.6150 Cajamarca;4.4167 Hrubieszów;50.8167 Pécel;47.4893 Camocim de São Félix;-8.3589 Nederweert;51.2833 Tíogollo;10.3333 Betroka;-23.2683 Tekanpur;25.9940 Finnentrop;51.1731 Amboanjo;-22.0000 Wilton;43.1502 Banabuiú;-5.3100 San Casciano in Val di Pesa;43.6569 Bareggio;45.4667 Przasnysz;53.0167 Uryzhar;47.0900 Pisaflores;21.1933 Gonegandla;15.7170 Yizhu;23.3565 Urandi;-14.7708 Handlová;48.7272 Holmdel;40.3768 Hayange;49.3297 Heysham;54.0460 Sundarpur;26.3037 Mount Holly;35.3136 Noordwijkerhout;52.2667 Sawankhalok;17.3099 Svalyava;48.5472 Peso da Régua;41.1653 Attūr;8.3224 Pindorama;-21.1858 Ambarès-et-Lagrave;44.9247 Königstein im Taunus;50.1833 Kaatsheuvel;51.6667 Achaljāmu;24.0243 Braunau am Inn;48.2583 Cruz do Espírito Santo;-7.1400 Linnei;23.7591 Aubange;49.5667 Lyepyel;54.8750 Rustampur;25.5700 Mohanpur;25.5620 Tomas Oppus;10.2500 Gokarna;24.0306 Altena;51.3000 Mont-Organisé;19.4000 Boerne;29.7847 Nunna;16.5788 Kangaba;11.9333 Amesbury;42.8530 Toledo;7.3131 Rentachintala;16.5524 Poing;48.1667 Streetsboro;41.2396 Carovigno;40.7000 Raspur Patasia;25.5616 Sītāmau;24.0147 Tecolotlán;20.2024 Milledgeville;33.0874 Peçanha;-18.5489 Bermeo;43.4200 Podporozhye;60.9000 Shahritus;37.2667 Salesópolis;-23.5319 Kinel’-Cherkassy;53.4683 Shongzhy;43.5417 Stuart;27.1959 Peka;-28.9667 Bom Sucesso;-21.0328 Grójec;51.8656 Semra;26.6523 Chandia;23.6565 Guoxing;24.0550 West Columbia;33.9932 Baymak;52.5833 Erba;45.8167 Middlesex Centre;43.0500 Bakarpur Ogairah;25.3294 Beckley;37.7877 Lérida;4.9000 Kodikulam;9.9932 Eggenstein-Leopoldshafen;49.0778 Fulshear;29.6930 Piranga;-20.6850 Mula;38.0419 Xintangcun;23.9423 Talsint;32.5398 Calnali;20.9000 Ambohipihaonana;-19.4333 Tamallalt;31.8289 Cacimba de Dentro;-6.6419 Kāmavarapukota;17.0033 Piçarras;-26.7500 Villa Isabela;19.8200 Saint Ives;52.3344 Pinehurst;35.1922 Ilaka Atsinanana;-19.5531 Bawgalegyi;18.9144 El Rosario;13.5000 Minakami;36.6786 Minamichita;34.7151 El Segundo;33.9170 Fort Thomas;39.0801 Murray;36.6146 São João dos Poleiros;-5.1139 Mohács;45.9959 Cham;49.2167 Cáqueza;4.4053 Pollensa;39.8772 Ribeirópolis;-10.5389 Clemson;34.6837 Çukurca;37.2470 Glenvar Heights;25.7090 Camano;48.1865 Inverness;46.2000 Massé;7.1578 Hīrna;9.2167 Penzberg;47.7500 Bārnia;23.7311 Sada;43.3500 Terre Neuve;19.6000 Ustka;54.5833 Cestas;44.7444 Chuarrancho;14.8167 Elbeuf;49.2858 Guadarrama;40.6728 Nāyakanhatti;14.4644 Dour;50.3979 Huruta;8.1500 Xianxi;24.1334 Aabenraa;55.0444 Tsuiki;33.6561 Pozoblanco;38.3833 Santa Maria das Barreiras;-8.8550 Kozienice;51.5833 Marienberg;50.6333 Sāyarpuram;8.6822 Math Lohiyār;26.6247 Condeixa-a-Nova;40.1167 Barrington;41.7443 Fiorano Modenese;44.5333 Shoufeng;23.8667 Araçagi;-6.8528 Lebanon;40.0324 Rubeho;-6.2578 Santana do Cariri;-7.1878 Balmazújváros;47.6167 Mebane;36.0852 Itirapina;-22.2528 Karratha;-20.7364 Saran;47.9514 Stony Plain;53.5264 Jičín;50.4367 Petawawa;45.9000 Rāmāyampet;18.1166 Campagna;40.6667 Brzesko;49.9667 Canudos;-9.9639 Sedan;49.7019 Lagoa Formosa;-18.7789 Rio Linda;38.6875 Azaourissè;6.6944 Adustina;-10.5328 Saluzzo;44.6453 Rio Pomba;-21.2750 Borda da Mata;-22.2739 Konstantinovsk;47.5667 Babenhausen;49.9667 Sidi Daoud;36.8500 Bloemhof;-27.6500 Knaresborough;54.0084 Stafford;29.6271 Traiguén;-38.2500 Elias Fausto;-23.0428 Ganeshpur;25.7678 Burbaliq;40.3247 Fairview Park;41.4419 Brixham;50.3940 Hannibal;39.7098 El Reno;35.5429 Danville;37.6418 Urucará;-2.5358 Penamalūru;16.4681 Dilasag;16.4000 Gubin;51.9500 Cañon City;38.4430 Kruibeke;51.1667 Defiance;41.2813 West Lampeter;39.9947 Benešov;49.7828 Kusa;55.3500 São Vicente Férrer;-7.5908 Anosibe-Ifanja;-18.8667 Ejea de los Caballeros;42.1292 Douar Lamjaara;34.6147 Sa‘ādat Shahr;30.0775 Manlin;23.6630 Capistrano;-4.4700 Jaboticatubas;-19.5139 Belterra;-2.6358 Codru;46.9753 Broxburn;55.9340 Ignacio de la Llave;18.6618 Amarante;-6.2408 Hinsdale;41.8007 Lerum;57.7667 Ginatilan;9.6000 Sidi Yakoub;31.6667 Gainesville;33.6390 Piñas;-3.6806 Shāhkot;31.0800 Livinjipuram;8.1535 Xixinzhuangzhen;37.0165 San Juan del Sur;11.2533 Castellaneta;40.6333 Xinlong;18.9534 Basārh;25.9808 Zvenyhorodka;49.0833 Pontalina;-17.5258 Gurh;24.5026 Avenel;40.5842 Bhit Bhagwānpur;26.1160 Rancho Mirage;33.7635 Landerneau;48.4508 Kronach;50.2411 Long’e;25.8061 Nandigaon;17.1190 Geddes;43.0762 Rotselaar;50.9511 Wādī Ḩalfā’;21.8000 Lakhipur;26.2897 Svitavy;49.7560 San Giovanni in Fiore;39.2642 Bargteheide;53.7167 Tolongoina;-21.5500 Kibungan;16.6939 Navalmoral de la Mata;39.8983 Preetz;54.2367 Mandritsara;-15.8333 Villeneuve-Loubet;43.6581 Eppelborn;49.4022 Podalakūr;14.3667 Espartinas;37.3833 Yangi Mirishkor;38.8514 Anosivelo;-22.7333 Tagounite;29.9833 Ferndale;39.1869 Nidgundi;16.7000 Taxtako‘pir;43.0225 Tifton;31.4624 Néa Mákri;38.0833 Pelham;43.0333 Aurora;41.3118 Famy;14.4333 Kežmarok;49.1336 Abelardo Luz;-26.5650 Oswego;43.4516 Resplendor;-19.3258 Nova Canaã;-14.7939 Knemis Dades;31.3090 Analanampotsy;-17.1667 Thandla;23.0096 El Socorro;8.9936 Xinying;35.7060 Łęczyca;52.0500 Bargūr;12.5429 Yabuki;37.2013 Conyers;33.6646 Ilakatra;-22.3500 Tullinge;59.2000 Mahires;34.5333 San Esteban;-32.7992 Croatá;-4.4000 Safety Harbor;28.0080 Chopadandi;18.5833 Carmópolis de Minas;-20.5408 Rāmachandrapuran;17.3000 Stone;52.9000 Macatuba;-22.5022 Sallanches;45.9364 Ghonchí;39.9589 Fundão;-19.9328 Terrell;32.7340 Martinsicuro;42.8833 Iluppur;10.5137 Gainesville;38.7931 Piera;41.5222 Sadabe;-18.6333 Shklow;54.2236 Hindarx;40.0700 Gavrilov-Yam;57.3167 Ambondromisotra;-20.3333 Miqiao;35.4991 Groves;29.9457 Oulad Amrane;32.2833 Xiulin;24.2167 Menzelinsk;55.7333 Torre Maggiore;41.6833 Mauguio;43.6164 Los Lunas;34.8115 Dhulkot;21.6095 Mullānwāla;31.0619 Ban Cho Ho;15.0311 Druzhba;41.2222 Água Fria;-11.8669 Schwarzenberg;50.5453 Denville;40.8890 Holzkirchen;47.8833 Stanley;53.7145 Annonay;45.2400 Tortum;40.2981 Santo Tomás de Jánico;19.4000 Grottammare;42.9897 Gunzenhausen;49.1147 Turinsk;58.0333 Milanoa;-13.5833 Ambohitrolomahitsy;-18.7000 Pokrov;55.9117 Capela;-9.4075 Hosakote;14.2817 Tsarahonenana;-15.4833 Sahamadio;-20.3000 Selwyn;44.4167 Bruckmühl;47.8833 Arıcak;38.5644 Seltso;53.3678 Khutauna;26.4969 Sendurai;10.3934 Zafra;38.4167 Asten;51.4000 Vedelago;45.6833 Ettaiyāpuram;9.1474 Sughrāin;25.7460 Filiaşi;44.4000 Befotaka;-14.5333 Norwalk;41.2443 Pelitli;40.9833 Sint-Kruis;51.2117 Khed;17.7189 San Miguelito;11.4025 Dzitbalché;20.3167 Ambohitsimanova;-19.9500 Antsahavaribe;-13.9833 Marofoty;-22.9333 Manombo Atsimo;-22.9500 Ambolidibe Atsinanana;-15.1000 Bykhaw;53.5167 Ashwaubenon;44.4796 Antanimora Atsinanana;-24.8167 Lazarivo;-23.9000 Etrotroka;-22.8833 Seeheim-Jugenheim;49.7667 Ambila;-21.9833 Dongjiangshui;33.3792 Dacaozhuang;37.5546 Retiro;6.0572 Valencia;-0.9525 Ankarongana;-15.4167 Cernavodă;44.3381 Santa María;-26.6833 Andranofasika;-16.3833 Costa Marques;-12.4450 Haslemere;51.0900 Liuchuan;26.6549 Marosangy;-21.0000 Guben;51.9533 Namysłów;51.0728 Lakhaura;26.7522 West Hempfield;40.0564 Montagu;-33.7833 Batuco;-33.2308 Bytów;54.1333 Myrtle Grove;30.4158 Beahitse;-24.1667 Aḑ Ḑulū‘īyah;34.0500 Scordia;37.3000 Swansea;41.7571 Kompalle;17.4993 Rāmnagar;26.0773 Isoanala;-23.8333 Samarate;45.6167 Huntington;40.8810 Rosário Oeste;-14.8358 Garagoa;5.0833 Middelfart;55.4986 Lentate sul Seveso;45.6784 Marahōm;33.8303 Liuguoju;38.2571 Usgao;15.4333 Kesath;25.4208 San Juan Lalana;17.4667 Peñamiller;21.0519 Rātan;25.4349 Aiuaba;-6.5739 Springfield;40.6994 Lamas;-6.4167 Jericoacoara;-2.7939 Ashmyany;54.4250 Kelheim;48.9167 Guisborough;54.5350 Piriyāpatna;12.3365 Chettināyakkanpatti;10.3940 Bristol;36.6181 Malta;42.9853 Steffisburg;46.7667 Osowa;54.4272 Guadalupe Victoria;19.2833 Grangemouth;56.0120 Seara;-27.1489 Bhainsoda;24.4427 Vári;37.8333 Areia Branca;-10.7578 Escoublac;47.2858 Asthānwān;25.2215 Sonāpur;25.3561 Cessnock;-32.8342 Belle Glade;26.6916 Middletown;41.5175 Mattoon;39.4774 Aklanpa;8.1684 Giaveno;45.0420 Azeffoun;36.8961 Belaur;25.4477 Jilotepec;19.6113 Reinheim;49.8269 Mugumu;-1.8333 Abington;42.1180 Tourlaville;49.6408 Erumād;11.5681 Loyalist;44.2500 Santa Teresa;11.8039 Heumen;51.7833 Khirpai;22.7110 Flowing Wells;32.2937 Ambohimanambola;-18.9500 Northallerton;54.3378 Domchānch;24.4748 Ripon;54.1380 Kalundborg;55.6814 Belo;25.8747 Marion;37.7345 Tobré;10.2000 Acoyapa;11.9708 Terra Santa;-2.1039 La Rinconada;-14.6325 Mátészalka;47.9500 Sarıoğlan;39.0833 La Sierpe;21.7606 Riemst;50.8089 San Jacinto Amilpas;17.1006 Cêrro Azul;-24.8239 Pran Buri;12.3939 Bek’ojī;7.5833 Krapkowice;50.4667 Live Oak;36.9860 Fetromby;-18.5833 Kollūru;16.1833 Vadugappatti;10.1036 Damascus;39.2701 Vatutine;49.0167 Telua;26.6396 América Dourada;-11.4550 San José de Chiquitos;-17.8500 Siloam Springs;36.1844 Fındıklı;41.1333 Ban Thung Tam Sao;6.9581 Mohanūr;11.0594 Bad Wörishofen;48.0058 Dēra;8.3333 Tubaran;7.7167 Iziaslav;50.1167 Bellingham;42.0777 Shelbyville;38.2067 Neratovice;50.2593 Westervoort;51.9667 Kaukauna;44.2773 Dormentes;-8.4469 Hennebont;47.8042 Severínia;-20.8089 Turhāpatti;26.8511 Agrate Brianza;45.5833 Bogatynia;50.9069 Ratba;34.7833 Kodala;19.6243 Río Bueno;-40.3167 Beldānga;24.8106 Dehāqān;31.9400 Sāhpur;25.7035 Shahbā;32.8542 Calhoun;34.4910 Takhatgarh;25.3300 West Haven;41.2083 Cinnaminson;40.0008 Tsundupalle;13.9809 São Caetano de Odivelas;-0.7500 Easton;38.7760 Dilijan;40.7408 Japaratuba;-10.5928 Upper Gwynedd;40.2144 Farmingville;40.8390 Zhukovka;53.5333 Maşīf Sarsink;37.0333 Morteros;-30.7000 Bürstadt;49.6333 Kumage;34.0495 Ōyodo;34.3906 Katsuura;35.1500 Tichi;36.6675 Trzcianka;53.0500 São Pedro do Sul;40.7500 Alfajayucan;20.4000 Ichhāwar;23.2278 Vincennes;38.6759 Hajdúnánás;47.8500 Mitrapur;24.4371 Tongluo;24.4833 Montbrison;45.6075 Alto Santo;-5.5208 Bonito;-11.9658 Hünfeld;50.6667 ’Aïn Abessa;36.3000 Roxana;10.3586 Mount Vernon;40.3854 Posoltega;12.5447 Janakkala;60.9167 Bridgeview;41.7403 Marbach am Neckar;48.9333 La Quiaca;-22.1042 Tāwargeri;15.7667 Leinì;45.1846 Stará Ľubovňa;49.3094 Sobrado de Paiva;41.0333 Hohenems;47.3667 Medicina;44.4833 Khaniādhāna;25.0298 Bina;26.0792 Meiti;24.3517 Kajur;24.8768 Boysun;38.2000 Boden;65.8256 Quepem;15.2200 Midland;44.7500 Villa Rica;33.7294 Upper Saucon;40.5364 Este;45.2333 Jāsk;25.6439 Pendleton;45.6757 Sin-le-Noble;50.3631 Belaya Glina;46.0833 El Escorial;40.5817 Meghauna;25.6904 Colwood;48.4236 Sinūnī;36.4575 Leopoldshöhe;52.0167 Bella Vista;-22.1167 Wixom;42.5243 Diksmuide;51.0333 Chinampa de Gorostiza;21.3667 Truckee;39.3455 Periyakoduveri;11.4811 Tejuçuoca;-3.9889 Ouaouzgane;35.0167 Bandora;15.4082 Locarno;46.1667 Vallegrande;-18.4833 Sāhar;26.5408 Aberdeen;46.9757 Lille;51.2333 Libiąż;50.1000 Saalfelden am Steinernen Meer;47.4269 Monesiglio;44.4667 Beinan;22.7833 Grecia;10.0693 Westchester;41.8492 Strzegom;50.9611 Panajachel;14.7361 Campo do Brito;-10.7328 Iglino;54.8385 Sudak;44.8514 Fuying;40.8754 Bagnacavallo;44.4167 Nässjö;57.6500 Sidi Lamine;32.9000 Pindaí;-14.4928 Câmpulung Moldovenesc;47.5308 Auburn;42.1972 Jiblah;13.9167 Cayetano Germosén;19.3300 Salūmbar;24.0800 Itariri;-24.2888 Bethlehem;-28.2240 Madison;40.7586 Tromsdalen;69.6442 Saidpur;25.5436 Pampa;35.5479 Altıntaş;39.0615 Ocho Rios;18.4167 Wurzen;51.3667 Pedra Preta;-16.6228 Central Saanich;48.5142 La Paz;17.6739 Ban Nong Han;18.9000 Buford;34.1192 Yağlıdere;40.9000 Říčany;49.9917 Daireaux;-36.6000 Patilār;27.0361 Port Washington;40.8268 Buntok;-1.7190 Upper Chichester;39.8414 Ostrov;50.3060 Rājāpur;26.2861 Sumé;-7.6719 Ada;34.7662 San Roque;6.4853 Ekma;25.9670 Simaria;24.7950 Kushima;31.4681 Lonigo;45.3833 Kauhava;63.1000 Vendôme;47.7928 Mikumi;-7.4072 Klippansbruk;56.1167 Graham;36.0589 Anna;33.3472 Carnaubal;-4.1669 Adjahomé;7.0618 East Greenbush;42.6122 Großostheim;49.9167 Laconia;43.5725 La Vista;41.1816 Storrs;41.8083 El Piñón;10.4039 Jurāwanpur Karāri;25.5273 Micoud;13.8190 Terra Roxa d’Oeste;-24.1569 Cabeceiras de Basto;41.5333 Aue;50.5853 Liangyi;35.2698 Ban Wang Nok Aen;16.8333 Islāmpur;24.1513 Kawagoe;35.0167 Madrid;9.2619 Vodil;40.1789 Tirmalgiri;17.4746 Kaiwen;27.1548 Kudatini;15.1500 Ceadîr-Lunga;46.0500 Katosi;0.1528 Sūrappalli;11.7187 Shintomi;32.0689 Aston;39.8719 Sainte-Catherine;45.4000 Bhagwāngola;24.3485 Potirendaba;-21.0428 Chorfa;36.3617 Illzach;47.7822 Pozos;9.9536 Port Hope;43.9500 Ayyampālaiyam;10.2253 Cerea;45.2000 Dhusar Tikāpatti;25.5214 Moraga;37.8439 Caravaggio;45.4978 Machelen;50.9103 Ervādi;9.2500 Bad Lippspringe;51.7833 Country Club Hills;41.5636 Wyckoff;40.9989 Douar Tabouda;34.7167 Yerbas Buenas;-35.7500 Turgutreis;37.0167 Fort Carson;38.7095 Donna;26.1468 Ponte de Sôr;39.2500 Seacombe;53.4090 La Cruz;1.6047 Novomichurinsk;54.0500 Merchtem;50.9667 Saint-Basile-le-Grand;45.5333 Mariestad;58.7000 Povorino;51.2000 San Juan;9.1590 Murehwa;-17.6500 Al Mazār ash Shamālī;32.4725 Seymour;41.3810 Rygge;59.3747 Hernando;34.8500 Anamorós;13.7333 Francheville;45.7364 Ukiah;39.1464 Sidi Ettiji;32.1717 Khirbat Ghazālah;32.7333 Monsenhor Tabosa;-4.7889 Chestnuthill;40.9568 Sunagawa;43.4948 Madhuban;26.4386 Menomonie;44.8893 Uherský Brod;49.0251 Zirara;32.3500 Colle Salvetti;43.6000 Porto Empedocle;37.2944 Gālivedu;14.0333 Capurso;41.0500 Mensora;34.8356 Ubrique;36.6833 Kade;6.0833 Centerville;40.9284 Ansfelden;48.2083 Ibicuí;-14.8419 Wentang;23.9918 Ibitiara;-12.6519 Pipra Latīf;25.3579 Pochëp;52.9333 Tantéga;10.8500 Tiahounkossi;10.8167 Illingen;49.3764 Rumilly;45.8667 Zumarraga;11.6390 Manamelkudi;10.0419 Powder Springs;33.8659 Rāikal;18.9000 Kāmayakkavundanpatti;9.7386 Yoshinogari;33.3212 Zequ;35.0376 Hueytown;33.4239 Mariyammanahalli;15.1600 Bālasamudram;10.4190 Jamālpur;25.9549 Rožnov pod Radhoštěm;49.4585 Pôrto Murtinho;-21.6989 Ujre;12.9961 Segbwema;8.0000 Gasparillo;10.3167 Marui;24.8639 Beek;50.9333 Princeton;33.1778 Banbridge;54.3430 Puerto Pilón;9.3600 Rājāsūr;17.8600 Todi;42.7789 Palagonia;37.3333 Ban Na Sai;17.7334 Federación;-30.9833 Reggello;43.6833 Shiraoi;42.5512 Jinji;22.1650 Chorbogh;39.8667 Millbrook;32.5027 Zeulenroda;50.6486 Grimari;5.7167 Mennecy;48.5653 Horad Smalyavichy;54.1000 East Highland Park;37.5770 Chittayankottai;10.2686 Noale;45.5501 Aladağ;37.5465 Lake St. Louis;38.7846 Harborcreek;42.1498 Washougal;45.5825 Dallas;44.9221 Codogno;45.1600 Miyazu;35.5333 Añisoc;1.8500 Īlkhchī;37.9378 Brunico;46.7963 Ādra;23.5000 Mangueirinha;-25.9408 Sainte-Luce-sur-Loire;47.2494 Barkot;30.8200 Al Laţāminah;35.3208 Long Beach;30.3608 Kājha;25.7747 Odumase;7.3667 Khargāpur;24.8230 Northenden;53.4075 Denby Dale;53.5720 Tirhassaline;32.7833 Falköping;58.1750 Ad Dīs;14.9100 Mifune;32.7146 Santa María Petapa;16.8167 Bolivia;22.0750 Tapes;-30.6728 Kovdor;67.5594 Nakanoto;36.9889 Bacuri;-1.7028 Hannut;50.6667 Hatti;16.1984 Digne-les-Bains;44.0925 Zhutian;22.5890 Guaymate;18.5800 Ippy;6.2500 Água Branca;-5.8900 Zapotlán de Juárez;19.9667 Gryfice;53.9147 Vesoul;47.6222 Rājnagar;24.8893 Ramón Santana;18.5500 Lumbreras;37.5633 Umbertide;43.3000 Zhytkavichy;52.2333 Peer;51.1328 Batán;10.1041 Xinyuan;37.2953 Chełmża;53.1847 Ina;9.9833 Swift Current;50.2881 Pupiales;0.8667 Humble;29.9921 Słubice;52.3500 Vemuladīvi;16.3408 Valdez;1.2500 Fairview Heights;38.5974 Macetown;-44.8650 San Estanislao;10.4000 Benipati;26.4442 Red Wing;44.5817 Johnstown;40.3499 Beaver Dam;43.4688 Punta Umbría;37.1667 Guardamar del Segura;38.0897 Morton;40.6135 Obra;24.8910 Saraikela;22.6996 Gangāpur;25.5136 Shamsa;25.6230 Kirchlengern;52.2000 Buritama;-21.0667 Champāpur;26.8881 Corabia;43.7736 Kurtamysh;54.9167 Ahmetli;38.5289 Wyke;53.7333 Kota;14.0333 Keflavík;64.0167 Pirque;-33.6333 Edmundston;47.3765 Cristinápolis;-11.4758 Bluffdale;40.4744 Bree;51.1333 Chinde;-18.5833 Weybridge;51.3620 Brackenheim;49.0833 Centre de Flacq;-20.2002 Fiano Romano;42.1667 Hønefoss;60.1667 Birsinghpur;24.7981 Nobsa;5.7667 Çarşıbaşı;41.0833 Besana in Brianza;45.7000 Franklin;39.5954 Quarteira;37.0690 Ḩammām al ‘Alīl;36.1581 Loxstedt;53.4699 Berezan;50.3197 Horodok;49.1667 Nanbu;40.4206 Yaypan;40.3758 Palanisettipatti;9.9998 Ségou;6.6167 Rāhon;31.0527 Uchkeken;43.9333 Hampton Bays;40.8695 Langenthal;47.2167 Ban Lam Narai;15.2000 Bayat;40.6460 Celina;33.3154 Az Zaydīyah;15.3292 Pedregulho;-20.2569 Charqueada;-22.5097 Ciney;50.3000 Mapiri;-15.2500 Lugu;23.7500 Chennūr;14.5667 Garðabær;64.0833 Differdange;49.5222 Srīmushnam;11.4012 Hövelhof;51.8167 Châteaurenard;43.8825 Saint-Amand-les-Eaux;50.4481 Ivanava;52.1333 Petersberg;50.5667 Qarqīn;37.4128 Mastchoh;40.3667 Bobangui;4.0500 Santomera;38.0617 Russell;45.2569 Antanambao Mahatsara;-19.3167 Sultānābād;18.5333 Maruim;-10.7378 Sānātikri;22.0198 Al Ghāţ;26.0267 Karema;-6.8205 Kisvárda;48.2167 Makhu;31.1033 Langrucun;36.9014 Sabotsy;-19.2333 Bady Bassitt;-20.9178 Longchang;27.6627 San Miguel;11.4920 Dison;50.6167 Zhongzai;26.6877 Saunshi;15.2167 Nyahanga;-2.3829 Regenstauf;49.1236 Tōhoku;40.7279 Tadikalapūdi;16.8991 Tafresh;34.6919 Az Zuwāydah;31.4284 Belén;11.5028 Changji;26.9471 Toro;4.6117 Dinmānpur;25.9112 Sunland Park;31.8201 Loimaa;60.8500 Barbacoas;9.4833 Kirchhain;50.8167 Harenkarspel;52.7344 Clevelândia;-26.3958 Cloverly;39.1064 Yunshan;34.7610 Ambohimalaza;-18.9167 Jadia;26.0937 Rakovník;50.1038 Douglas;31.3602 Kozelsk;54.0353 Loma Plata;-22.3833 Clearlake;38.9589 Bovolone;45.2500 Vienna;38.8996 Jakkampālaiyam;10.5264 Slaný;50.2305 Bilohirsk;45.0544 Itaeté;-12.9858 Tasīl;32.8353 Lake Mary;28.7592 Anorí;7.0736 Pocking;48.4000 Chittūr;11.6470 Las Cabezas de San Juan;36.9817 McKinleyville;40.9488 Arteche;12.2694 Odaiyakulam;10.5679 Ban Samo Khae;16.8408 Hayes;51.3780 Taohongpozhen;36.9854 Dilārpur;25.3968 Harlingen;53.1736 Turkaguda;17.2728 Rolleston;-43.5833 Olecko;54.0333 Straelen;51.4500 North Grenville;44.9667 Ispica;36.7833 Al Ḩībah;28.7736 Shuili;23.7989 Panukulan;14.9333 Pātapatnam;18.7500 Angara-Débou;11.3289 Verrières-le-Buisson;48.7475 Islām Qal‘ah;34.6667 Bruntál;49.9883 Sava;40.4003 Āgiripalle;16.6833 Sandhausen;49.3439 Rajni;25.8132 Les Herbiers;46.8711 Alegria;9.4667 Agudo;-29.6450 Domoni;-12.2586 Spárti;37.0739 Boshrūyeh;33.8683 Curacautín;-38.4333 Verkhivtseve;48.4812 South Venice;27.0444 Bosobolo;4.1833 Uckfield;50.9700 Center Point;33.6447 Dyer;41.4977 Portachuelo;-17.3572 Neykkārappatti;10.4489 Anantapalle;16.9767 La Falda;-31.0833 Sebt Aït Saghiouchen;34.0122 Chai Prakan;19.7322 Erlensee;50.1333 Jambaló;2.8500 Bangor;53.2280 Heber;40.5070 Bissegem;50.8167 Chinácota;7.6167 Schmelz;49.4167 Banigbé;6.9000 Éghezée;50.5833 Addison;32.9590 Herisau;47.3833 Telfs;47.3069 Kannampālaiyam;10.9954 Copparo;44.9000 Cotorra;9.0500 Markranstädt;51.3017 Wittstock;53.1636 Baipingshan;26.1960 Wolf Trap;38.9395 Beko;24.0415 Acarape;-4.2242 Penicuik;55.8260 Ustroń;49.7194 Thompson;41.6474 Do‘stobod;40.8564 Damdama;24.6300 Przeworsk;50.0667 Carcarañá;-32.8500 Ham Lake;45.2545 Pearl River;41.0615 Bālkonda;18.8667 East Longmeadow;42.0597 Hebli;15.4858 Bassum;52.8494 Realeza;-25.7669 Huliyār;13.5833 Divriği;39.3667 Codroipo;45.9625 Nawada;25.5431 Porumāmilla;15.0167 Sopelana;43.3814 Hude;53.1111 Chiva;39.4714 Montivilliers;49.5461 Sahatavy;-17.4489 Eilenburg;51.4608 Fillmore;34.3989 Middletown;39.9094 San Dionisio;12.7625 Momil;9.2333 North Decatur;33.8074 Warragul;-38.1500 Douar Azla;35.5564 Karambakkudi;10.4587 Villa San José;-32.2000 Brakel;51.7167 Solapuram;9.3757 Sultonobod;38.4500 Novoukrainka;48.3156 Bang Phae;13.6983 Isrāin Kalān;25.9844 Sananduva;-27.9500 Kierspe;51.1333 Kallūr;17.2000 Néa Alikarnassós;35.3167 Otacílio Costa;-27.4828 Pinhalzinho;-26.8478 Bingawan;11.2333 Ōarai;36.3133 Grosse Pointe Woods;42.4366 Troutdale;45.5372 Guelendeng;10.9183 El Peñol;6.2186 Dieburg;49.9000 Narlıca;36.2333 Wardenburg;53.0617 New Port Richey;28.2468 Restrepo;3.8250 Orimattila;60.8042 Puerto Pimentel;-6.8367 Sunnyside;46.3157 Yorkton;51.2139 Corbélia;-24.7989 Ellington;41.9152 Aranyaprathet;13.6928 Oyten;53.0611 Ourém;-1.5478 Michelstadt;49.6786 Palagiano;40.5833 Shahriston;39.7667 Peddaboddepalle;17.6606 Chamgardān;32.3936 Halver;51.1833 Tarquinia;42.2492 Queensbury;53.7683 Musāpur;25.6417 Ostashkov;57.1500 Villalbilla;40.4339 Schiffweiler;49.3667 Fort Hunt;38.7361 Ponsacco;43.6167 Morro da Fumaça;-28.6508 Leopoldsburg;51.1169 Belāri;25.9655 Faxinal;-24.0008 Wiefelstede;53.2581 Poplar Bluff;36.7632 Albemarle;35.3594 Yotoco;3.8667 Acahay;-25.9100 Lyss;47.0667 Vallirana;41.3878 Lemay;38.5325 Walldorf;49.3000 Opa-locka;25.8997 Guérande;47.3281 Stahnsdorf;52.3922 Timezgana;34.5833 Leso;11.6697 Novoanninskiy;50.5333 Garou;11.8053 Susanville;40.4206 Sabana de La Mar;19.0700 Adjud;46.1000 Ambalabe;-15.1667 Ponders End;51.6460 Kodikkulam;9.6493 Kumiyama;34.8814 Bikou;32.7522 Carlet;39.2264 Ospitaletto;45.5553 ’Ayn Bni Mathar;34.0889 Northbridge;42.1300 Montalvânia;-14.4228 Borodino;55.9056 Sayville;40.7478 Tiverton;41.6090 Chaona;35.0895 Kabo;7.6994 Frogn;59.6989 Kotoura;35.5000 Antsahanoro;-14.8333 Crawfordsville;40.0428 Nanzuo;37.8369 Civita Castellana;42.2961 Leixlip;53.3643 Niepołomice;50.0339 Adwick le Street;53.5677 General Villegas;-35.0333 Mercedes;26.1533 Ransiki;-1.5000 Château-Thierry;49.0464 Concepción;-11.9185 Sadhoa;25.4049 Petrovsk-Zabaykal’skiy;51.2667 San Antonio Oeste;-40.7333 Ban Kao;13.8667 Xiaozhengzhuang;39.6250 Santa Cruz Muluá;14.5833 Brahmadesam;11.5449 Pallappatti;10.3564 Erwitte;51.6167 Olivehurst;39.0795 Welby;39.8403 Hilltown;40.3415 Mezőkövesd;47.8167 Llanquihue;-41.2581 Dewangarh;24.8637 Shāhpur;23.8937 Porto Belo;-27.1578 Landen;50.7547 Vero Beach;27.6463 Sered’;48.2886 Hathīaundha;25.7223 Horodok;49.7822 Benjamín Aceval;-24.9703 Bruck an der Mur;47.4106 Gangoli;13.6500 Merimandroso;-18.7500 Carhué;-37.1796 Upala;10.8645 Sapatgrām;26.3373 Nonantola;44.6777 Westport;41.5886 Igaporã;-13.7728 Andemaka;-22.3000 Zegzel;34.8407 Vemulūru;16.9337 Udayagiri;14.8667 Perchtoldsdorf;48.1167 Bois-Guillaume;49.4700 Hermitage;41.2305 Ampahimanga;-19.0833 Yaojia;28.4547 Merate;45.7000 Pianezza;45.1058 Wülflingen;47.5100 Lagoa do Carro;-7.8450 Certaldo;43.5478 Großenkneten;52.9500 Moreau;43.2469 Dyersburg;36.0465 Ishtixon Shahri;39.9664 Araputanga;-15.4708 Wolcott;41.6007 Chautham;25.5439 Jasper;38.3933 Chilgazí;40.1500 Payson;34.2433 Buwama;0.0633 Clausthal-Zellerfeld;51.8050 Mistrató;5.3000 Bīleh Savār;39.3778 Çaykara;40.7475 Hibbing;47.3980 San Pedro de Lloc;-7.4167 Jhandāpur;25.3995 Wendelstein;49.3536 Galliate;45.4833 Breaza;45.1872 Kolakalūru;16.3036 Fenoarivo;-23.1167 San Antonio Palopó;14.7000 Fairburn;33.5496 Ikast;56.1333 Santa Anita;30.4500 San Gabriel;0.5983 Tahlequah;35.9112 Griffith;41.5277 Alabat;14.1023 Köprüköy;39.9756 Elandakuttai;11.3992 San Cesareo;41.8167 Fenoarivo;-20.8667 Bouchabel;34.3833 Radford;37.1229 Conwy;53.2800 Zulte;50.9167 Ābīy Ādī;13.6231 Ponto Novo;-10.8619 Kujwa;33.5025 Caidat Sidi Boubker El Haj;34.9148 Taucha;51.3800 Ouadhia;36.5500 Tangainony;-22.7000 Altinópolis;-21.0231 Subachoque;4.9281 Les Sables-d’Olonne;46.4972 Masmouda;34.7862 Alpedrete;40.6583 Trélazé;47.4461 Burrillville;41.9706 Sardoba;40.5422 Totteridge;51.6354 Barra de Santo Antônio;-9.4000 Gavinivāripālem;15.8378 Babayurt;43.6003 Puduppatti;11.5500 Bar;49.0781 Huarmey;-10.0686 Lewes;50.8747 Bumpe;7.8919 Sulechów;52.0833 Nowogard;53.6667 Pelhřimov;49.4314 Yurihama;35.4899 Santa Ana;13.9333 Aberdeen;39.5151 As Sukhnah;34.8868 Khaira;26.3433 Parma;43.2651 Lake Wales;27.9195 Sāyalkudi;9.1692 Finsterwalde;51.6282 Pursa;26.3019 Rylsk;51.5667 Brüggen;51.2417 São Pedro do Sul;-29.6208 Skhour Rehamna;32.4833 Amarwāra;22.2978 Tankal;8.0000 Vontimitta;14.3833 Füssen;47.5667 Hampton;42.9391 Owase;34.0708 Getúlio Vargas;-27.8900 Seven Pagodas;12.6208 Tarqui;2.1106 Taylor;30.5729 Potenza Picena;43.3667 Ukrainka;50.1500 Bni Tajjit;32.2833 Ropczyce;50.0500 Caldera;-27.0667 Paidiipalli;18.0172 Parrita;9.5471 Sukth;41.3833 Hasanpur;26.1147 Toul;48.6750 Americus;32.0736 Aruvikkara;8.4219 Baléyara;13.7840 West Norriton;40.1308 Aki;33.5000 Mezőtúr;47.0000 Warrington;30.3835 Vardannāpet;17.7735 Bni Quolla;34.7380 Bad Essen;52.3214 Los Muermos;-41.4000 Sassenheim;52.2258 Ambarakaraka;-13.5000 Ndioum;16.5167 Frankfort;40.2810 Colonial Park;40.2987 Coulommiers;48.8156 Spanish Springs;39.6568 Fullerton;40.6309 Mölnlycke;57.6667 Qazyan;40.4425 Antadinga;-22.1500 Cunit;41.1976 Utnūr;19.3667 Hongsi;35.5113 Santa Marta de Tormes;40.9494 Saint-Avertin;47.3667 Ebersbach an der Fils;48.7147 Calera;33.1254 Chinggil;46.6660 Kālikāpur;26.4956 Weener;53.1692 Andapafito;-16.9167 Undi;16.6000 Samsikāpuram;9.4156 Buffalo;45.1794 Rasulpur Dhuria;25.5693 Quiindy;-25.9730 Easthampton;42.2651 Csongrád;46.7113 Vieux Fort;13.7280 Altopascio;43.8167 Kodinsk;58.6833 Chāilāha;26.6738 Valmontone;41.7833 Mahavelona;-18.5167 Tracadie;47.5124 Ternat;50.8667 Alsfeld;50.7511 Dalachi;36.6392 Minamiminowa;35.8729 Kanan;34.4917 Şemdinli;37.3080 Quezon;14.0500 Bad Bentheim;52.3031 Fartura;-23.3883 Prinzapolka;13.5008 Chenôve;47.2911 Jitwārpur Kumhra;25.7852 Mananasy-Tsitakondaza;-19.1500 Degtyarsk;56.7000 Aquitania;5.5833 Readington;40.5822 Matelândia;-25.2408 Hüyük;37.9519 Hariharpāra;24.0468 Cipanas;-6.7330 Sérarou;9.5833 Venturosa;-8.5747 Myrza-Ake;40.7500 Barharwā;24.8571 Khat Azakane;32.2226 Qarabulaq;44.9089 Fiadanana;-20.8667 Beaucaire;43.8072 Alcañiz;41.0511 Nagasu;32.9297 Thundersley;51.5700 Clarksburg;39.2862 Glória;-9.3389 Shāhpura;23.1366 Léré;15.7117 Lübbenau/Spreewald;51.8667 Paola;39.3667 Despujols;12.5183 Ambohibe;-17.4500 Federal;-30.9500 Leanja;-15.5500 Androrangavola;-20.5167 Heggadadevankote;12.0881 Vijayapuri North;16.6028 Antarvedi;16.3333 Darién;3.9167 Naivasha;-0.7167 Ambalatany;-22.5333 Issoire;45.5442 Alarobia;-18.9667 Schlüchtern;50.3500 Dublin;32.5360 Mount Pleasant;33.1586 Betanty;-25.5667 Westwood;42.2202 Palmitos;-27.0678 Zunil;14.7836 Sāligrāma;12.5602 Boudjima;36.8140 Carterton;51.7600 Stäfa;47.2400 Dinan;48.4556 Neustadt in Holstein;54.1072 Vohiposa;-20.9833 Tachiarai;33.3724 Red Hill;33.7777 L’Oulja;34.2894 Doğanhisar;38.1447 River Falls;44.8609 Tsiningia;-15.4833 Vohilengo;-22.5333 Ranohira;-22.4333 Doria Sonāpur;26.1830 Sikeston;36.8854 Bay Village;41.4851 Azzano Decimo;45.8833 Sansepolcro;43.5756 Dūbacherla;16.9116 Taromske;48.4619 Inanantonana;-19.6500 Bambous;-20.2600 Hetton le Hole;54.8210 Maigh Nuad;53.3816 Bevonotra;-14.0500 Sveti Ivan Zelina;45.9596 Grandville;42.9004 Majdal Shams;33.2692 Antindra;-14.1333 Nova Ipixuna;-4.9208 Anjangoveratra;-14.1333 Milford;42.8178 Türkeli;41.9486 Mohanpur Gaughāta;25.3345 South Middleton;40.1324 Corbera de Llobregat;41.4169 Kumārīpur;25.4425 Todmorden;53.7130 Dianga;34.0587 Chornobaivka;46.7006 Dhemāji;27.4833 Tizi-n-Tleta;36.5457 Analila;-14.4500 Bitburg;49.9667 Forks;40.7358 Az Zintān;31.9306 Pornic;47.1156 Antsenavolo;-21.4000 Buçimas;40.8917 Kasli;55.9000 Tarascon;43.8050 Manompana;-16.6833 Mangabe;-16.7167 San Tomas;15.8792 Vīraghattam;18.6833 Prospect Heights;42.1039 Ankazondandy;-18.6833 Bilpura;23.2216 Shchigry;51.8667 Manturovo;58.3333 Villers-lès-Nancy;48.6731 Râşnov;45.5933 Neston;53.2890 Bekitro;-24.5500 Fīshvar;27.7975 Yorito;15.0600 Mandrosonoro;-20.5833 Piancó;-7.1978 Bracebridge;45.0333 Świdwin;53.7833 Kings Park;40.8881 Washington;40.7050 Kastsyukovichy;53.3333 San Miguel Dueñas;14.5167 Ban Dung;17.6986 Duxbury;42.0465 Summerfield;38.9042 Kamyzyak;46.1167 Alloa;56.1160 Kafia Kingi;9.2731 Vlagtwedde;52.9500 Caetano;-14.3378 Pamiers;43.1164 Darpa;26.8400 Tanakpur;29.0740 Gatesville;31.4445 Chiconquiaco;19.7500 Yaransk;57.3167 Üsharal;46.1697 Poço Fundo;-21.7808 Barysh;53.6500 Chervonopartyzansk;48.0833 Santa Catalina;17.5917 West Richland;46.3115 Ernagūdem;17.0020 Bastogne;50.0042 Chivolo;10.0261 Androy;-21.3333 Ganjām;19.3870 Haddada;34.2236 Sabana Grande de Palenque;18.2667 Pochampalli;17.3473 Gargždai;55.7128 Ghomrassen;33.0592 Murraysville;34.2919 Avalpūndurai;11.2310 Middleburg Heights;41.3696 Yelur;15.7817 Puliyūr;10.9499 Kyabé;9.4514 Calbuco;-41.7667 Chembagarāmanpudūr;8.2461 Hot Springs Village;34.6566 Coquimbito;-32.9667 Muscle Shoals;34.7432 Dehaq;33.1047 Kadaiyam;8.8320 Blanquefort;44.9106 Pawai;24.2664 Middle Smithfield;41.0918 Dunaivtsi;48.8873 Bhagta;30.4882 Ripon;37.7417 Batalha;39.6500 Ərkivan;39.0183 Sahambala;-17.9417 Kumaralingam;10.4894 Xixucun;36.6940 Vatomandry;-19.3308 Fremont;41.3535 Augustinópolis;-5.4658 Rescaldina;45.6167 Talitsa;57.0167 Aveiro;-3.6058 Esquipulas;12.6639 San Julián;61.5240 Firestone;40.1565 Bocşa;45.3747 Tarancón;40.0167 Brofodoumé;5.5167 Plavsk;53.7167 Contenda;-25.6758 Galván;18.5039 Chhāpia;26.0831 Jitwārpur Nizāmat;25.8489 Castanet-Tolosan;43.5156 The Dalles;45.6053 Saraland;30.8479 Lidzbark Warmiński;54.1167 Chitila;44.5083 Bikin;46.8167 Maardu;59.4781 Jastrebarsko;45.6719 La Grange;41.6787 Aramil;56.7000 Luzhou;23.3687 Rutland;43.6092 Marghita;47.3500 Stallings;35.1088 Amāri;25.7179 North Fayette;40.4204 Kolnād;12.9000 Volpiano;45.2000 Marco Island;25.9330 San Martino Buon Albergo;45.4167 Aïn Mediouna;34.5000 Comarapa;-17.9158 Tabatinga;-21.7169 Bellevue;44.4592 New Haven;41.0676 Coos Bay;43.3789 Muhammadābād;16.8731 Maryborough;-25.5375 Bredasdorp;-34.5333 Gardendale;33.6677 Dassari;10.8158 Poxoréo;-15.8369 Mottola;40.6333 Sertã;39.8000 Tocantins;-21.1750 Fairhaven;41.6394 Cardonal;20.6167 Roh;24.8905 São Paulo do Potengi;-5.8950 Overland;38.6966 Curtorim;15.2800 Königslutter am Elm;52.2500 Beernem;51.1428 San Bartolomé Jocotenango;15.1928 Warsaw;41.2448 Joaquim Nabuco;-8.6239 Ezzhiliga;33.3000 Puraini;25.5968 Richmond Hill;31.9012 Lewiston;43.1793 Tamazouzt;31.3833 Fort Drum;44.0450 Dvůr Králové nad Labem;50.4331 Bituruna;-26.1608 Sidi Allal el Bahraoui;33.9830 Berea;34.8802 Greater Napanee;44.2500 Breisach am Rhein;48.0333 Skvyra;49.7333 Julita;10.9731 Al Mu‘abbadah;37.0164 Yuvileine;48.5531 Ivdel;60.6833 Nuth;50.9167 Aldine;29.9123 Pichhor;25.9602 Opelousas;30.5252 Voss;60.7025 Budakeszi;47.5123 Katav-Ivanovsk;54.7500 Hedongcun;37.7546 Indiaroba;-11.5189 Giruá;-28.0278 Sakhā;31.0881 Miguelturra;38.9667 Casalpusterlengo;45.1778 Ponnamarāvati;10.2803 Udayendram;12.6962 Galatone;40.1500 Rumst;51.0833 Coto de Caza;33.5959 Chhājli;30.0348 Waterville;44.5441 Fort Mohave;35.0004 Tillsonburg;42.8667 Ince-in-Makerfield;53.5402 Kabayan;16.6233 Machang;33.1912 Wadern;49.5167 Mandialaza;-18.6167 Saint-Avold;49.1042 Bariariyā;26.5105 João Neiva;-19.7578 St. Simons;31.1775 Ait Yazza;30.5063 Suffield;41.9945 Longmeadow;42.0475 Mountain Home;43.1324 Vatlūru;16.7009 Tsukumiura;33.0667 Hilvarenbeek;51.4833 Kavarna;43.4333 Seshambe;38.5333 Adrasmon;40.6486 Jussara;-11.0469 Ixhuatlán del Sureste;18.0170 Hollins;37.3434 Overpelt;51.2167 Fos-sur-Mer;43.4544 Dixon;41.8439 Dickson;36.0637 Rakovski;42.2884 Baeza;37.9833 Barrocas;-11.5289 Ash Shaddādah;36.0561 Sulat;11.8167 Amawom;5.4667 Bāisāri;22.7900 Warren;40.6323 Chenlu;35.0287 Gonohe;40.5312 Derzhavīnsk;51.1000 Kumano;33.8886 Ibateguara;-8.9728 Exeter;42.9901 Patton;40.8258 Künzelsau;49.2833 Itaquitinga;-7.6678 Ammūr;12.9750 Surbo;40.4000 Jataúba;-7.9900 Carugate;45.5500 Phulwār;26.8148 Nioro du Rip;13.7500 Ankli;16.4200 Skalica;48.8422 Kalininsk;51.5000 Mount Dora;28.8142 Balīgaon;25.4196 Steinbach;49.5258 Lagunia Surajkanth;25.8304 Biritinga;-11.6169 Castillo;19.2200 Muttunāyakkanpatti;11.7125 Patos;40.6833 Bad Neustadt;50.3219 Sulphur Springs;33.1421 Lithia Springs;33.7811 Matuga;0.4603 Mamadysh;55.7131 Nymburk;50.1861 Jamestown;46.9063 Kurāwar;23.5118 Józefosław;52.1005 Vakhsh;37.7108 Taurianova;38.3500 Santa Rosa;-33.2500 Haftkel;31.4469 Chilamattūru;13.8394 Kerman;36.7248 Lagoa Real;-14.0350 Torelló;42.0492 Babhangāwān;25.3674 Ovruch;51.3244 Verkhnodniprovsk;48.6561 Diang;4.5833 Saint-Omer;50.7483 Ras el Oued;34.1500 Harduli;22.9278 Juma Shahri;39.7161 Putyvl;51.3347 Lower Salford;40.2639 Yasnyy;51.0500 San Miguel;13.6411 Goubellat;36.5333 Santa Cruz de la Palma;28.6825 Maddikera;15.2500 Macedo de Cavaleiros;41.5389 Melonguane;4.0045 Pereiro;-6.0450 Castenaso;44.5097 Ulladulla;-35.3486 Plochingen;48.7117 Omegna;45.8781 Fontainebleau;48.4089 Itajibá;-14.2839 Hautmont;50.2481 Alūr;15.3944 Holly Springs;34.1681 Santa Úrsula;28.4253 Talladega;33.4333 Uusikaupunki;60.8000 Pijiño del Carmen;9.3333 Victor;42.9894 Chinnavādampatti;11.0615 Bad Bramstedt;53.9186 Kyazanga;-0.3864 Niederkrüchten;51.1989 Acajutiba;-11.6619 Guasca;4.8658 Korsør;55.3336 Abadiânia;-16.2039 Chokkanāthapuram;9.9921 Santa Cecília;-26.9608 Cassina de’ Pecchi;45.5167 Kalocsa;46.5335 Highland Village;33.0897 Molinella;44.6167 Crixás;-14.5489 Saboeiro;-6.5419 Silvi Paese;42.5500 Weißwasser/Oberlausitz;51.5000 Restinga Sêca;-29.8128 Funyan Bīra;9.3500 Ershui;23.8167 Miyota;36.3223 Wenwu;24.7413 Matsushige;34.1339 Barajor;24.7992 Barod;23.7889 Zaṟah Sharan;33.1300 Jiaojiazhuang;38.2636 Simmerath;50.6000 Udala;21.5781 Grenzach-Wyhlen;47.5517 Drensteinfurt;51.7944 Cuarte de Huerva;41.5833 General Pinedo;-27.3167 Bunnik;52.0500 Ascheberg;51.7889 Kasumi;35.6333 Mvurwi;-17.0167 Guinguinéo;14.2667 Iguatemi;-23.6800 San Giorgio Ionico;40.4500 Vellālāpuram;11.6525 Morretes;-25.4769 Glenn Heights;32.5506 Si Satchanalai;17.4155 Pakaryā Harsidhi;26.6526 Talaināyar Agrahāram;10.5614 Aartselaar;51.1333 Arumbāvūr;11.3810 Mount Clemens;42.5977 Mallāpuram;11.9823 Hanover;49.4433 Kela Khera;29.0900 Tēkkampatti;11.2559 Spittal an der Drau;46.7917 Coelemu;-36.4878 Finale Emilia;44.8333 Sebt Bni Smith;35.1373 Ulverstone;-41.1667 Oswestry;52.8598 Elkton;39.6066 Riposto;37.7333 Terrace;54.5164 Yasnogorsk;54.5000 Tīrān;32.7025 Rattihalli;14.4167 Chapelle-lez-Herlaimont;50.4667 Yazu;35.4092 Maskanah;35.9632 Kukkundūr;13.2404 Mwinilunga;-11.7172 Zhuolan;24.3222 Langenau;48.4967 Hewitt;31.4520 Castelnuovo Rangone;44.5500 Crépy-en-Valois;49.2344 Auburndale;28.0963 Discovery Bay;37.9063 Renai;24.0462 Buggānipalle;15.4741 Vallūr;13.2560 Ifanadiana;-21.3000 Ostroh;50.3333 Kanding;22.5194 Zhirnovsk;50.9833 Toui;8.6833 Dejen;10.1667 Los Osos;35.3065 Poté;-17.8069 Einsiedeln;47.1167 Kuusamo;65.9667 Yakumo;42.2560 ‘Alavīcheh;33.0528 Newton;41.6963 Ashikita;32.2991 Kinross;-26.4167 Vipparla;16.3023 Loma de Cabrera;19.4220 Kysucké Nové Mesto;49.2983 Mayoyao;16.9736 Şaḩnāyā;33.4242 Marivorahona;-13.0833 Lady Lake;28.9241 Kot Bhāi;30.2678 Lagoa dos Gatos;-8.6578 Tondangi;17.2500 Macomb;40.4709 Brühl;49.4000 Navelim;15.2428 Varnsdorf;50.9116 Barela;23.0968 Bengonbeyene;1.6931 Matlock;53.1400 Mitchell;43.7294 Templin;53.1167 Aç-çahrij;31.8000 Dammāj;16.8939 Novouzensk;50.4667 Opera;45.3833 Pontivy;48.0686 Mecitözü;40.5200 Wailuku;20.8834 Badarpur;24.8685 North Whitehall;40.6797 Sertanópolis;-23.0589 Rakhiv;48.0500 Kasagi;34.2965 Manjīl;36.7433 Dacheng;23.8483 Valeggio sul Mincio;45.3500 Casalmaggiore;44.9858 Szarvas;46.8667 Mortara;45.2500 Waremme;50.6975 Porcia;45.9667 Chickasha;35.0409 Tōbetsu;43.2237 Kambadūru;14.3575 Udiyāvara;13.3097 Metković;43.0500 Manūjān;27.4064 Alpine;32.8439 Tupi Paulista;-21.3808 Newberry;40.1286 Itki Thākurgaon;23.3456 Gaillac;43.9006 Huasca de Ocampo;20.2028 Lindås;60.6247 Rio do Antônio;-14.4108 Kinna;57.5167 Bhoj;16.5333 Dolo;45.4269 Kolwāra;25.3538 Tone;35.8578 Passa Quatro;-22.3900 Shively;38.1970 Bolotnoye;55.6833 Serpa;37.9447 Kallakkudi;10.9767 Mindelheim;48.0333 Kohīr;17.6000 Česká Třebová;49.9019 Rossville;39.3572 Seymour;35.8783 Cunco;-38.9167 Kara-Kulja;40.6333 Fort Leonard Wood;37.7562 Tuineje;28.3167 Olmué;-32.9953 Baia-Sprie;47.6592 Weston;53.4130 Bŭston;40.5217 Ban Na Kham;14.0681 Rahden;52.4167 Siswa;26.6214 Anao-aon;9.7778 Shizukuishi;39.6963 Mangqu;35.5707 Montijo;38.9100 Jędrzejów;50.6333 Espumoso;-28.7250 Corridonia;43.2500 Weston;44.8906 Boppard;50.2314 Akdepe;42.0500 Pyāpali;15.2669 Grovetown;33.4503 Bohodukhiv;50.1608 Tiruvāsaladi;11.4013 Ágios Athanásios;34.7087 Mangawān;24.6675 Northborough;42.3231 Qaryat Sulūq;31.6686 Saint-Rambert;45.4994 At-Bashy;41.1700 Pihra;24.6424 Fameck;49.2992 Khamir;15.9889 Antrim;39.7862 Ipuã;-20.4381 Mandal;58.0267 Kāuriya;26.1517 Yığılca;40.9667 Borborema;-21.6200 Najasa;21.0836 Soliera;44.7381 Pānr;25.6884 Cumru;40.2811 Hille;52.3331 Ban Pang Mu;19.3336 Blaydon;54.9630 Bonheiden;51.0333 Southchase;28.3793 Castellarano;44.5667 Bonito;-1.3628 Pewaukee;43.0701 Maullín;-41.6167 Windham;42.8076 Dastgerd;32.8019 São João Evangelista;-18.5478 Arvika;59.6542 Tredegar;51.7776 Caudry;50.1250 Ribnitz-Damgarten;54.2500 Pequannock;40.9627 Pahou;6.3833 Mastic;40.8096 Svetogorsk;61.1167 Haldībāri;26.3300 Meco;40.5539 Banqiao;35.8912 Radzyń Podlaski;51.7828 Corinto;13.8167 Urziceni;44.7181 El Chal;16.6333 Ambaguio;16.5316 Chiaravalle;43.6000 Butiama;-1.7667 Wanda;-25.9667 Davorlim;15.2722 Wawizaght;32.1586 Kusāha;26.2077 Shimanto;33.2117 Garrel;52.9581 San Policarpo;12.1791 Solec Kujawski;53.0833 Monkey Bay;-14.0833 Seven Oaks;34.0475 Ipecaetá;-12.3000 Monroe;39.4461 Sayō;35.0042 Baiceng;25.3885 Castiglione del Lago;43.1386 Live Oak;29.5545 Wittenheim;47.8075 Amānganj;24.4266 Kawasaki;33.6000 Schriesheim;49.4736 Colchester;41.5621 Küsnacht;47.3167 Mujuí dos Campos;-2.6847 Vadugapatti;11.1518 Khao Yoi;13.2403 Siemiatycze;52.4272 North Reading;42.5816 Azalea Park;28.5473 Luduş;46.4778 Anār;30.8733 Sovetsk;57.5833 Greenwood Village;39.6153 Alvarães;-3.2000 Ayaş;40.0172 Muttukūru;14.2667 Mering;48.2625 Indianola;41.3629 La Roda;39.2070 Romit;38.7167 Brooklyn Park;39.2170 Hugo;45.1671 Bobleshwar;16.8300 Doda;30.3844 Ventaquemada;5.4167 Daigo;36.7681 Magdiwang;12.4833 Amboavory;-17.3000 Bhānumukkala;15.3119 Bokoro;12.3667 Saren;25.1149 Zelenogradsk;54.9667 Rellingen;53.6500 Niceville;30.5290 Fiorenzuola d’Arda;44.9333 Viškovo;45.3778 Tremelo;50.9911 Pudtol;18.2356 Grenaa;56.4161 Sutihār;25.8482 Gehrden;52.3117 Ban Duea;16.1253 El Palmar;8.0244 Isorana;-21.3167 Jelcz-Laskowice;51.0333 Hartford;43.3223 Feucht;49.3758 Abaza;52.6481 Wilmington Island;32.0033 Bouc-Bel-Air;43.4544 Sinha;25.6902 Genappe;50.6000 Barra de Santa Rosa;-6.7200 Pannimadai;11.0823 Ban Nong Kathao;16.9833 Teodoro Schmidt;-38.9949 Bailleul;50.7375 Bilaua;26.0501 Cumbum;15.5767 Limanowa;49.7006 Parapatti;12.1083 Strängnäs;59.3667 Cerro Maggiore;45.6000 Senlis;49.2072 Bunkeflostrand;55.5500 Barwon Heads;-38.2500 Vicovu de Sus;47.9258 Ifrane;33.5333 Bankheri;22.7696 Panorama;-21.3564 Linguère;15.3944 Nālwār;16.9333 Horsham;-36.7167 Passo de Camarajibe;-9.2378 Neustadt bei Coburg;50.3289 Santa Croce sull’ Arno;43.7202 Candelaria;18.4042 Batavia;42.9987 Forfar;56.6442 Galanta;48.1889 Capotille;19.4500 Shchuchyn;53.6167 Rio Grande City;26.3808 Piquet Carneiro;-5.8039 Madagh;35.0133 Salinas da Margarida;-12.8708 Ober-Ramstadt;49.8333 Kalongo;3.0400 Mainburg;48.6500 Hofgeismar;51.4833 Ban Bang Muang;13.8273 Ratekau;53.9500 Ljungby;56.8333 Altunhisar;37.9981 Tamaki;34.4902 Sembedu;13.1298 Manohisoa;-19.7833 Campanha;-21.8389 Flers;48.7483 Relangi;16.7050 Ilovaisk;47.9250 Riegelsberg;49.2942 La Solana;38.9414 Yeşilova;37.5069 Salitre;-7.2839 Churumuco de Morelos;18.6167 Nancagua;-34.6667 Suknadānga;24.4600 Taperoá;-7.2064 Berea;37.5904 Ezine;39.7900 Itapiranga;-27.1689 Outreau;50.7039 Jamao al Norte;19.6500 Sher Chakla;25.3848 Mian Sahib;28.1559 Erjie;24.6997 Pudur;9.0001 Bludenz;47.1533 Satyāmangala;13.0193 Al Hammam;33.1868 Lahoysk;54.2000 Büttelborn;49.9022 Anazzou;30.6333 Vieiro;43.6481 Konārka;19.8878 Ala-Buka;41.4083 Fortuna;10.4483 Coremas;-7.0139 Featherstone;53.7000 Broxbourne;51.7495 Scottsboro;34.6438 Chubbuck;42.9263 North Strabane;40.2279 Clark;40.6203 Méridjonou;6.4619 Victoria;12.4500 Morlaix;48.5775 Tiruvalanjuli;10.9449 Kérouané;9.2704 Odenthal;51.0333 Moul El Bergui;32.5113 Grain Valley;39.0171 Agidel;55.9000 Głuchołazy;50.3131 Sabinópolis;-18.6658 Jangīd;18.4475 Wālājābād;12.4994 Aravakkurichchi;10.7760 Glenn Dale;38.9833 Tūprān;17.8447 Klimavichy;53.6167 Schroeder;-26.4128 Kanegasaki;39.1957 Rubiera;44.6500 Narahia;26.3681 Jacaraci;-14.8500 Gomboussougou;11.4333 Putaparti;14.1652 Landázuri;6.2181 Razan;35.3867 Andrakata;-14.6167 Tulle;45.2658 Amatitán;20.8333 Ban Khek Noi;16.8118 Daisen;35.5000 Atyrá;-25.2786 Villa del Rosario;-31.5833 Neutraubling;48.9936 Evans;42.6528 Nova Resende;-21.1258 Grande Saline;19.2500 Barro Alto;-11.7608 Dattapulia;23.2404 Mendrisio;45.8667 Lynden;48.9502 Durham;43.1174 Azpeitia;43.1819 Serinyol;36.3667 Ambongamarina;-18.3250 Vila Bela da Santíssima Trindade;-15.0078 Chak Husaini;25.5169 Bāra;24.3146 Calolziocorte;45.8000 Edingen-Neckarhausen;49.4469 Basāon;26.2082 Libagon;10.3000 Mulakaledu;14.3648 Carthage;37.1503 Markgröningen;48.9047 Mantua;39.7618 Springdale;39.8769 Rochedale;-27.6000 Warni;18.5436 Ashtead;51.3100 Thoen;17.6100 Tendrara;33.0500 Olonne-sur-Mer;46.5361 Guipavas;48.4336 Māndleshwar;22.1760 Vişeu de Sus;47.7092 Veys;31.4825 Groß-Zimmern;49.8726 Lokomby;-22.1833 Serra Preta;-12.1600 Castro Daire;40.9000 Mirzāpur;26.1616 Chaltyr;47.2848 Tiruppāchūr;13.1384 Oulad Bou Rahmoun;32.2954 Tubod;9.5547 Uvalde;29.2152 Barakī;33.9333 Pedara;37.6167 Baulia;25.3990 Swallownest;53.3623 Great Baddow;51.7190 Lakkundi;15.3897 Saint-Genis-Pouilly;46.2433 Saloá;-8.9758 Hohenstein-Ernstthal;50.8000 Three Lakes;25.6415 Putaendo;-32.6278 Münster;49.9167 Mackworth;52.9277 Motiong;11.7833 Wakuya;38.5397 Antarvedipālem;16.3319 Greeneville;36.1680 Krasnovishersk;60.4167 Gräfelfing;48.1189 Avrillé;47.5069 San Luis del Palmar;-27.5167 Brake;53.3333 Maydolong;11.5000 Bar-le-Duc;48.7717 Hückeswagen;51.1450 Liangwancun;28.4466 Horw;47.0167 Chilcuautla;20.3333 Chīpurupalle;18.3114 Poynton;53.3500 Eldorado;-24.5200 Guática;5.3167 Alta;69.9689 Şüvəlan;40.4889 Raposos;-19.9669 Bennington;42.8854 Kāttupputtūr;10.9833 Sanyi;24.4167 Port Glasgow;55.9340 Glasgow;39.6015 Springfield;49.9292 Nazaré;39.6000 Malo;45.6582 Seekonk;41.8379 Amāha;26.1284 Noto;37.3103 Mastic Beach;40.7664 Devendranagar;24.6169 Yankton;42.8901 Junín;-11.1500 Douar El Arbaa Bou Quorra;34.7490 Loviisa;60.4583 Fraga;41.5200 Castellammare del Golfo;38.0264 Hindalgi;16.1364 Villa Paranacito;-33.7000 Kishanpur Ratwāra;25.4822 Zevio;45.3728 Shāl;35.8983 Penacova;40.2706 Belokurikha;51.9833 Kurate;33.7919 Kawaminami;32.1920 Driouch;34.9833 Eden;36.5027 Guastalla;44.9167 Hayden;47.7680 Niquinohomo;11.9047 Port Orchard;47.5163 Capinópolis;-18.6819 Tazert;31.6597 Poronaysk;49.2167 Ledyard;41.4400 Kankipādu;16.4500 Ibipitanga;-12.8819 Sannicandro Garganico;41.8333 Wentorf bei Hamburg;53.4931 Santa Luzia;-6.8719 San Vito al Tagliamento;45.9000 Aïn Jemaa;34.0333 Hallstahammar;59.6167 Hartselle;34.4391 Tagoloan;8.1333 Tenafly;40.9176 Pineto;42.6167 Blomberg;51.9333 Kurichchi;11.5701 Sippola;60.7392 Dengshangcun;41.3442 Privolzhsk;57.3825 Sárvár;47.2500 Tapolca;46.8828 Kamitonda;33.6963 Selb;50.1667 Langelsheim;51.9381 Santa Maria a Vico;41.0333 Roanoke Rapids;36.4452 Chislehurst;51.4120 Ribeiro do Amparo;-11.0469 Bennane;35.6833 Pāli;26.0576 Xihuangni;38.3575 Mexborough;53.4992 Witzenhausen;51.3422 Chellaston;52.8671 Bétérou;9.2000 Mani;13.2600 Grodzisk Wielkopolski;52.2333 Yinhua;33.4530 Uiraúna;-6.5178 Vubatalai;11.3597 Pestovo;58.6000 Grefrath;51.3363 Robbinsville;40.2220 Ban Ton Thong Chai;18.3375 Città Sant’Angelo;42.5167 Oria;40.5000 Támesis;5.6667 Johnson City;42.1230 Souk Et-Tleta des Oulad Hamdane;33.1047 Jackson;37.3792 Pullambādi;10.9667 Çatalpınar;40.8790 Majiagoucha;37.5033 Gagarin Shahri;40.6619 Winkfield;51.4318 Laxou;48.6856 Valangimān;10.8897 Władysławowo;54.8000 Laterza;40.6333 Fray Luis A. Beltrán;-32.7833 Traverse City;44.7546 Katkol;15.9500 Robinson;40.4578 Västerhaninge;59.1167 Spitak;40.8372 La Apartada;8.1006 Pâ;11.5500 Sabbah;33.8036 São Lourenço da Serra;-23.8528 Paralímni;35.0333 Humayingcun;41.1145 Boksitogorsk;59.4833 Barāri;25.5068 Ilha de Moçambique;-15.0367 Salyan;28.3500 Kujūkuri;35.5333 Northview;43.0427 Afzala;25.9319 Peddapalle;14.4046 Kingersheim;47.7914 Ritterhude;53.1861 Dehmoí;40.2167 Upper Southampton;40.1723 White;40.6210 Beckingen;49.3928 Brejões;-13.1039 Campodarsego;45.5000 Dentsville;34.0754 Charām;30.7461 São Félix;-12.6050 Terra Rica;-22.7089 Méru;49.2358 Laurinburg;34.7602 Thibodaux;29.7949 Uchiko;33.5330 Berlare;51.0250 Ahram;28.8833 Majholi;23.5011 Rizal;8.5272 Franklin Park;40.5903 Qantīr;30.8000 Revūr;16.8216 Bradley;41.1641 Murray Bridge;-35.1170 Tarumã;-22.7469 Attibele;12.7781 Vammala;61.3417 Teolândia;-13.6019 Río San Juan;19.6400 Belchertown;42.2788 Andrésy;48.9808 Naduvattam;11.4808 Florencia;10.3665 Santa Adélia;-21.2428 Clinton;42.4119 Nakanojōmachi;36.5898 Bedfordview;-26.1794 Ambahive;-22.2000 Masty;53.4170 Hārohalli;12.6807 Tartarugalzinho;1.5058 Gloversville;43.0491 Domaniç;39.8033 Talata Ampano;-21.5500 Samobor;45.8000 Itajobi;-21.3178 Ben Taieb;35.0837 Kaguchi;11.4519 Mahaly;-24.1667 Gayéri;12.6500 Kaintragarh;20.7211 Choszczno;53.1667 Beech Grove;39.7157 Nitte;13.1823 Romilly-sur-Seine;48.5158 Bellavista;-34.9333 Sisian;39.5208 Huchuan;34.9249 Alvinópolis;-20.1069 Aiyetoro Gbede;7.9833 Arnedo;42.2167 Antsoso;-19.8167 Lakkampatti;11.4461 Boguszów-Gorce;50.7667 Kesamudram;17.6875 Depew;42.9118 Chāprā;23.5371 Hola Prystan;46.5167 Sāmbre;15.8800 Kurumbalūr;11.2360 Richterswil;47.2167 Niedernhausen;50.1617 Moura;38.1333 Tazzarine;30.7722 Dembecha;10.5500 Kutchan;42.9017 Ianantsony;-23.5500 San Pedro Tapanatepec;16.3667 Mahuwa Singhrai;25.8168 Mata Roma;-3.6250 Lānjī;21.5018 Spring Creek;40.7450 Midar;34.9500 Landsberg;51.5333 Zozocolco de Hidalgo;20.1333 Meilen;47.2667 Andondabe;-17.7667 Ban Mae Sun Luang;19.8305 Kimyogarlar;39.6672 Sakabansi;10.0442 Eloy;32.7470 Ambodiriana;-19.5833 Gondomar;42.1111 Zacualpan;18.7197 Pont-à-Mousson;48.9044 Silvino Lobos;12.3281 Youngsville;30.0963 Paramati;11.1544 Joghtāy;36.6350 Australind;-33.2800 Sommacampagna;45.4000 Gaspé;48.8333 Vennesla;58.3106 Busko-Zdrój;50.4667 Andover;37.6873 Mellila;33.3833 Bayserke;43.4797 Hudiksvall;61.7333 Cossato;45.5667 Gardabani;41.4500 Andoain;43.2167 Patnanungan;14.7833 Miraí;-21.1950 Ambohimierambe-Andranofito;-19.7833 Elk Plain;47.0425 El Arenal;20.2167 Southern Pines;35.1927 Bella Vista;-27.0333 Vandalia;39.8791 Bangui;18.5378 Oued Jdida;33.9333 Chirpan;42.1998 Şefaatlı;39.5017 Kaleyānpur;26.4297 Rosarno;38.4850 Piney Green;34.7498 Cirò Marina;39.3694 Ifatsy;-22.4000 Phillipsburg;40.6894 Amlash;37.0975 Alangānallūr;10.0470 Nopala de Villagran;20.2528 Anororo;-17.5167 Dorfen;48.2667 Ipauçu;-23.0569 Bela Vista do Paraíso;-22.9969 Devarapalle;17.0347 Staffanstorp;55.6333 Gualdo Tadino;43.2333 Tsiatajavona-Ankaratra;-19.3833 Halwāra;30.7167 Kaonke;30.7659 Bhimphedi;27.5510 Tonawanda;43.0105 Sindhnūr;15.7700 Trepuzzi;40.4000 Camp Pendleton South;33.2329 Canutama;-6.5339 Porteiras;-7.5350 Rāmechhāp;27.3260 Santa;17.4860 Tavagnacco;46.1333 Kochkor-Ata;41.0319 Ratzeburg;53.7000 Saint-Jacques-de-la-Lande;48.0903 Burshtyn;49.2600 Tatarikan;7.7333 Nadisāl;13.1326 Râs Baalbek;34.2597 Barţalah;36.3522 Clarksdale;34.1933 Andranovelona;-19.6500 La Carolina;38.2667 Natividade do Carangola;-21.0419 Ambesisika;-16.5167 Fortuna;-5.7328 Anpachi;35.3353 Ban Ho Mae Salong;20.1631 Nannestad;60.2456 Nisko;50.5200 Santa Brígida;-9.7358 Guayos;22.0497 Sahave;-21.0667 Antequera;9.7812 Whitman;42.0800 Greenlawn;40.8632 Çifteler;39.3831 Bemidji;47.4828 Kulunda;52.5667 Tayum;17.6165 Mutukula;-1.0000 Lohfelden;51.2716 Burbach;50.7444 Yellāreddi;18.1859 Pākala;15.2694 Opwijk;50.9667 Hanamsāgar;15.8722 Kyaukmyaung;22.5833 Juruá;-3.4808 Vohilava;-20.7000 Castañuelas;19.7000 Alarobia Bemaha;-20.2000 Teixeira;-7.2228 Khallikot;19.6091 Sultandağı;38.5333 La Flèche;47.6997 Punata;-17.5500 Joubb Jannîne;33.6333 Tulchyn;48.6744 Pilisvörösvár;47.6211 Kök-Janggak;41.0333 Jiquiriçá;-13.2569 Kenora;49.7667 Kodāngipatti;9.9920 Rosà;45.7167 Amirlī;34.7250 Skipton;53.9625 Julianadorp;52.8833 Chebrolu;16.8206 Tāti;23.3772 Hilchenbach;50.9983 Biatorbágy;47.4712 Özdere;38.0175 Longwood;28.7014 Triunfo;-7.8378 Henderson;36.3256 Vil’nyans’k;47.9445 Mahinog;9.1500 Nawāda;26.0881 Ahigbé Koffikro;5.4000 Ambatoharanana;-17.3000 Kyzyl-Suu;42.3392 Horta;38.5333 Thouars;46.9750 Matungao;8.1333 Roquebrune-sur-Argens;43.4433 Şalkhad;32.4917 Ramainandro;-19.3000 Leninsk;48.7000 Alexándreia;40.6333 Lolotique;13.5500 Ambinanindrano;-20.6500 Bāladharmāram;17.6703 Petrovaradin;45.2500 Bijai;25.4711 Zollikon;47.3422 Aizubange;37.5615 Ylivieska;64.0750 Veliki Preslav;43.1667 San Juan;10.2667 Chépica;-34.7333 Stein bei Nürnberg;49.4167 Milenaka;-22.8333 Calanogas;7.7500 Manambondro;-23.8000 Lagangilang;17.6103 Sasso Marconi;44.4000 Taguatinga;-12.4061 Barahari;25.7652 Uhingen;48.7058 Manandona;-20.0500 Ālampur;15.8793 Perunkolattūr;12.0430 Mykolaiv;49.5247 Marofototra;-20.8667 Obando;4.5833 Bihpuriāgaon;26.8031 Kesariyā;26.3650 Antanifotsy;-16.8667 Buggenhout;51.0000 Puerto Salgar;5.5000 Pontinia;41.4083 Sorso;40.7983 Worthington;40.0950 Koungheul;13.9833 Kopoky;-25.2000 Carmignano;43.8167 Haga;36.5483 Ankisabe;-19.2833 Hereford;34.8225 Swampscott;42.4757 Esbiaat;32.2044 Itaú de Minas;-20.7389 Bevoay;-24.4833 Imanombo;-24.4333 Rukhāe;25.3269 Front Royal;38.9260 Breukelen;52.1717 Lummen;50.9833 Villaviciosa;43.4833 Mutyālapalle;16.4019 Ambatomanjaka;-18.8833 Swansea;38.5507 Bajala;12.8537 Bacuag;9.6081 Dayton;39.2592 Qumqo‘rg‘on;37.8278 San Mateo del Mar;16.2000 San Vito;8.8400 Tiorpāra;22.2351 Taywarah;33.5100 Canteleu;49.4511 Ankilimivory;-24.4667 Beroy Atsimo;-23.9833 Markt Schwaben;48.1917 Grão Mogol;-16.5589 Tetāri;25.3757 Ravels;51.3667 Saint-Brevin-les-Pins;47.2464 Novyi Buh;47.6833 Quixelô;-6.2539 Bakel;14.9042 Shatrāna;29.9102 Analamary;-24.2333 Jisr ez Zarqā;32.5379 Clarines;9.9433 Malkhaid;17.1950 Anahidrano;-15.0833 Cherrapunji;25.2792 Penrith;-33.7511 Dargeçit;37.5440 Neu-Anspach;50.2931 Anjoma-Ramartina;-19.6333 Bachhauta;25.5242 Sarmīn;35.9033 Antanimenabaka;-16.9333 Tarlapalli;18.8345 Lapseki;40.3439 Soalala;-16.1000 Turnov;50.5873 Douar Souk L‘qolla;35.0718 Massalubrense;40.6167 Qiaoyang;35.0716 Quan’ancun;25.1345 Kezi;-20.9167 Befasy;-20.5667 Riverdale;33.5639 Lālejīn;34.9747 Dalkeith;55.8958 Lieusaint;48.6322 Behisatse;-21.8000 Akora;26.4721 Aghbal;34.9394 Chaguaramas;9.3333 Babhanganwa;26.1398 Wān Long;22.1667 Mitane;40.1016 Bikkavolu;16.9624 Lipno;52.8500 Villa Ojo de Agua;-29.5167 Iharan̈a;-13.3500 Madanpur;23.0200 Shlisselburg;59.9536 Dimiao;9.6167 Nakagawa;36.7363 Doukouya;6.4333 Usingen;50.3344 Verdal;63.7930 Mashpee;41.6178 Tabant;31.6581 Otley;53.9050 Auriflama;-20.6858 ’Tlat Bni Oukil;32.5770 Bni Darkoul;35.0563 Padavēdu;12.6730 Omiš;43.4333 Candói;-25.6628 Plainfield;41.6992 Linganaboyinacherla;16.3961 Yuchi;23.9000 Dahé;6.5167 Iporã;-24.0028 Srīnagar;26.7830 Drahichyn;52.1833 Greensburg;40.3113 Coribe;-13.8289 Jinta;37.8573 Krasnohorivka;48.0116 Woippy;49.1511 Saint-Fargeau;48.5328 Coconuco;2.2500 Langenselbold;50.1833 Sidmant al Jabal;29.0856 Sardinal;10.5343 Gourrama;32.3333 Tiszaújváros;47.9333 Łapy;52.9833 Serhetabat;35.2833 Botelhos;-21.6424 Cumayeri;40.8736 Kothanūru;16.0022 Terkuvengānallūr;9.4052 Vohipeno;-22.3500 Cold Lake;54.4642 Efkarpía;40.6833 Assaí;-23.3728 Tall Abyaḑ;36.6975 Kārai;12.9377 Anzegem;50.8336 Raynham;41.9312 Detva;48.5514 Ālamūru;16.7833 Joaíma;-16.6539 Tendūkheda;23.3962 Kānjikkovil;11.3689 Shepshed;52.7711 Williamstown;39.6874 Fritzlar;51.1333 Tanque Verde;32.2687 Sullivan;43.0923 Pallasovka;50.0500 Heswall;53.3280 Saint-Jean-de-Luz;43.3903 Telmar;25.4237 Nioro;15.1833 Nobres;-14.7200 Vinanivao;-15.8833 Człuchów;53.6667 Sumidouro;-22.0500 Presidente Getúlio;-27.0508 Stolac;43.0825 Sera;34.5868 Echuca;-36.1333 Rixheim;47.7486 Magione;43.1500 Nastola;60.9500 Dhanauri;29.7833 Sahakevo;-20.2833 Radzymin;52.4167 Levoča;49.0253 Misaki;34.3169 Cabañas;14.9333 Mineral Wells;32.8169 Potsdam;44.6774 Montataire;49.2556 Nerchinsk;51.9944 Tarauna;26.2371 Cabral;18.2500 Glasgow;37.0048 Shuichecun;24.0900 Vinci;43.7833 Sterling;41.7996 Impruneta;43.6833 Edenburg;-29.7347 Farnborough;51.3591 Woudrichem;51.8167 Busto Garolfo;45.5478 Buhuşi;46.7150 Mineral del Monte;20.1333 California City;35.1578 Hima;0.2906 Mukāsi Pidāriyūr;11.2069 Meerane;50.8519 Lons;43.3150 Bad Wurzach;47.9094 Amersham;51.6770 Uppunda;13.8333 Lumino;0.3250 Areado;-21.3589 Sanquelim;15.5027 Malsch;48.8808 Kondrovo;54.8000 Kharki;23.9165 Kizel;59.0500 San Julian;11.7536 Mascote;-15.5628 Jever;53.5744 Poděbrady;50.1425 Holalagondi;15.4873 Mions;45.6631 Mori;42.1050 Holliston;42.1977 Mondolfo;43.7500 Vrbovec;45.8833 Bom Jesus do Galho;-19.8289 Arona;45.7569 Dumariā;26.7652 Penrith;54.6648 Enniskillen;54.3447 Bhalil;33.8500 Nyzhnia Krynka;48.1144 Metuchen;40.5424 Collecchio;44.7500 Lebanon;37.6717 Dharphari;26.1501 Ban San Phak Wan Luang;18.7049 Wąbrzeźno;53.2833 Hendersonville;35.3247 Nagongera;0.7700 Ayní;39.3975 Stony Point;41.2593 Narasingapuram;12.9728 Vuhledar;47.7794 Gafanha da Nazaré;40.6350 Balassagyarmat;48.0786 Château-d’Olonne;46.5042 Batuan;12.4222 Manavālakurichi;8.1478 Madānpur;25.8670 Boulder City;35.8407 Fujisaki;40.6561 Balhāpur;25.3511 Oftringen;47.3167 Great Bend;38.3593 Audincourt;47.4828 Kannamanāyakkanūr;10.5527 Thuin;50.3333 Nzalat Laadam;32.1000 Inhuma;-6.6678 Branchburg;40.5629 Ternitz;47.7167 Chegdomyn;51.1178 Cambuci;-21.5750 Pattensen;52.2667 Ekwāri;25.2833 Bom Lugar;-4.2200 Pouso Redondo;-27.2578 Ferrier;19.6167 Dionísio Cerqueira;-26.2550 Rotenburg an der Fulda;50.9961 Lucao;23.4133 Gaundrā;26.3683 Cehegín;38.0925 Navashino;55.5500 Gabane;-24.6667 Cuevas del Almanzora;37.3000 Santo Domingo Xenacoj;14.6822 Monterrey;4.8783 Amasra;41.7494 Grândola;38.1700 Belison;10.8381 Sprimont;50.5000 Grimes;41.6779 Mercaderes;1.8000 Spenge;52.1333 Kontiolahti;62.7667 Romsey;50.9890 Betsukai;43.3938 Huasuo;35.4043 Brwinów;52.1417 Fiadanana;-18.2167 Darihat;24.9702 Ighrem n’Ougdal;31.2333 Bairia;25.5563 Brasil Novo;-3.2619 Broadlands;39.0168 Bissendorf;52.2333 Anomabu;5.1667 Rājpur;25.0768 Patrocínio Paulista;-20.6394 Wombourn;52.5302 Fortim;-4.4500 Bishops Cleeve;51.9470 Warwick;40.2503 Slochteren;53.2167 Summerside;46.4000 Katyr-Yurt;43.1700 Nevel;56.0333 Markacho;24.3263 Comox;49.6733 Odlābāri;26.8364 Chikhli Kalān;22.2152 Purísima de la Concepción;9.2333 Karabanovo;56.3167 Plérin;48.5344 Montelupo Fiorentino;43.7333 Sinmpérékou;11.2333 Las Guáranas;19.2000 Kottavalasa;17.9000 Amay;50.5478 Highland Springs;37.5516 Surajpura;25.2576 Maglie;40.1167 Sierra Vista Southeast;31.4525 Ampohibe;-15.0333 Ankadinandriana;-19.0667 Nova Timboteua;-1.2058 Kalasa;13.2340 Montabaur;50.4375 Cândido de Abreu;-24.5669 Baiersbronn;48.5058 Cassano delle Murge;40.8833 Murgod;15.7800 Sylvan Lake;52.3083 Gundlapelle;16.4102 Schwabmünchen;48.1789 Sozopol;42.4167 Cocorná;6.0569 Ostwald;48.5511 Alginet;39.2625 Hemsworth;53.6100 Ivanić-Grad;45.7081 Sanjiang Nongchang;19.8798 Gryazovets;58.8833 Sumbal;34.2307 Morpeth;55.1675 Hirono;40.4085 Alindao;5.0333 Bayeux;49.2786 Andorinha;-10.3450 Muthallath al Azraq;31.8342 Tabuse;33.9547 Tornesch;53.7000 Buritirama;-5.5928 Presidente Dutra;-11.2958 Ramsey;41.0595 Tepetzintla;21.1452 Oldsmar;28.0506 Obanazawa;38.6000 Herselt;51.0500 Castel San Giorgio;40.7833 Bradley Gardens;40.5711 Kelamangalam;12.6031 Roncade;45.6246 Lampa;-15.3636 Saint Joseph;10.6556 Schiffdorf;53.5355 Burbage;52.5277 Garuva;-26.0269 Nāgireddipalli;14.2701 Villa San Giovanni;38.2167 Gantt;34.7837 Shimubi;19.1645 Washington;39.7494 Mānjri;16.4200 Briniamaro;10.7411 Glen Allen;37.6660 Morwell;-38.2333 Aït Yaïch;36.5811 Piano di Sorrento;40.6333 Krasnogvardeyskoye;45.8500 Ambérieu-en-Bugey;45.9581 Ferndale;48.8526 Sirmaur;24.8365 Berettyóújfalu;47.2167 Onklou;9.5000 Brunswick;31.1449 Hooksett;43.0709 Kārkūdalpatti;11.5002 Harhorin;47.1972 Kushimoto;33.4667 Riofrío;4.1561 Cabarete;19.7511 Béré;9.3156 Nishinoomote;30.7322 Tiltil;-33.0817 Alexander City;32.9229 Muri;46.9333 Rokycany;49.7428 Much;50.9167 Essau;13.4833 Sanderstead;51.3358 Polāia Kalān;23.2119 Tsivilsk;55.8667 Ekenäs;59.9750 Muy Muy;12.7625 Bo’ness;56.0168 Neqāb;36.7081 Narragansett;41.4291 Maqat;47.6500 Danilov;58.1833 San Ramón;-11.1247 Pilar;-31.6833 Sukhinichi;54.1000 Vargem da Roça;-11.6069 Roncq;50.7536 Monteroni di Lecce;40.3333 Piraziz;40.9333 Badhoevedorp;52.3333 Çiçekdağı;39.6069 Törökbálint;47.4367 Teignmouth;50.5515 Trinitapoli;41.3500 Tumbippādi;11.8080 Wildwood;28.7752 Nampicuan;15.7342 Oulad Driss;31.9536 Penuganchiprolu;16.9167 Larena;9.2490 Orbetello;42.4394 Lope de Vega;12.2983 Hanover;42.1224 Maravilla Tenejapa;16.2167 Soledade;-7.0569 Ząbkowice Śląskie;50.5833 Pandhāna;21.6982 Wilsdruff;51.0522 Xincun;27.6739 Río Negro;-40.7833 Hedehusene;55.6547 Água Clara;-20.4500 Androrangavola;-21.5000 Greendale;42.9371 Pānsemāl;21.6598 Măgurele;44.3494 Angical;-12.0069 Uch-Korgon;40.2300 Karpi;25.1612 Barhauna;25.6043 Uzun;38.3667 Arzgir;45.3694 Wymondham;52.5700 Deming;32.2631 Fort Payne;34.4559 Suwāsra;24.0698 Negreşti-Oaş;47.8694 Ashiya;33.8938 Haacht;50.9767 Chandauli;25.8972 Askim;59.5861 Solim;15.6200 Cowes;50.7595 Newtown;39.9920 Villa Alegre;-35.6667 Sikat;25.5186 Chifubu;-12.9333 Eraurā;24.6130 Bhānukumāri;26.3395 Kottacheruvu;14.1886 Wschowa;51.8000 Lingāl;16.2833 Ochakiv;46.6186 Dohta;26.2864 Zemamra;32.6215 Barhi;23.9033 Travagliato;45.5240 Vail;32.0217 Villasanta;45.6053 Kerepestarcsa;47.5478 Ālangudi;10.3604 Massaranduba;-26.6108 Gulf Shores;30.2764 Ottweiler;49.3667 Ladson;33.0093 White Oak;39.0451 Suō-Ōshima;33.9276 Troon;55.5400 Chelsea;33.3262 Owosso;42.9955 Ngoulemakong;3.0833 Bombinhas;-27.1378 Guilford;39.8811 Carmen de Areco;-34.3858 Lubbeek;50.8817 Antioch;42.4742 Mittweida;50.9856 Rehlingen-Siersburg;49.3686 Fraser;42.5388 Candiba;-14.4108 Shintō;36.4384 Ardestān;33.3761 Mosina;52.2467 Versoix;46.2833 Berck-sur-Mer;50.4083 Ranomafana;-18.9583 Barka Parbatta;25.3396 Hulyaypole;47.6644 Pataili;25.7872 Lauda-Königshofen;49.5686 Hammonton;39.6572 Zhutang;23.8528 Claxton Bay;10.3405 Paniem;15.5167 Korangal;17.1070 Great Falls;39.0110 Mmopone;-24.5669 Isny im Allgäu;47.6919 Zontecomatlán de López y Fuentes;20.7667 Port Lincoln;-34.7322 Baie du Tombeau;-20.1138 Sisa;-6.6142 Mahatsinjo;-17.7333 Esquipulas Palo Gordo;14.9333 Gland;46.4200 Ożarów Mazowiecki;52.2167 Ban Du;19.9691 Quinapundan;11.1578 Mangamila;-18.5667 Cafelândia;-24.6178 Waunakee;43.1829 Mascali;37.7500 Bollullos par del Condado;37.3358 Condoto;5.1000 Udaipur Bithwār;26.2872 Chikitigarh;19.2023 Monroe;33.7990 Fruitville;27.3328 Ilampillai;11.6066 Canyon;34.9911 Bischofsheim;49.9890 El Paisnal;13.9667 Latiano;40.5333 Dennis;41.7064 Sepīdān;30.2625 Bartabwa;0.8300 Gouré;13.9874 Bahçesaray;38.1286 Boston;7.8697 Kulharia;25.3382 Bicas;-21.7250 Guajiquiro;14.1000 Chandhaus;25.2977 Cicciano;40.9667 Lopik;51.9667 Barja;33.6497 Bijaipur;26.0556 Harsefeld;53.4500 Maria;9.1960 Forster;-32.1806 Palmview;26.2318 Astravyets;54.6136 Brad;46.1294 Lakhsetipet;18.8873 Pineville;31.3414 Dornakal;17.4447 Ichikawamisato;35.5652 Neustadt an der Donau;48.8000 Münsingen;48.4128 Arugollu;16.8253 Kuurne;50.8519 Słupca;52.3000 Kātrāvulapalle;17.1277 Ban Kaeng;16.4119 Sailāna;23.4622 Cómbita;5.7500 Davidson;35.4840 San Sebastián;13.7333 Sukhāsan;25.9644 Jurema;-8.7178 Kalývia Thorikoú;37.8333 Aleksandrovka;42.8528 Anapoima;4.5503 Ayt ’Attou ou L’Arbi;31.5456 Radomyshl;50.4947 Rahika;26.3803 Salīmpur;25.2480 Plewiska;52.3664 Alzano Lombardo;45.7317 Eberbach;49.4667 Markdorf;47.7208 Melmangalam;10.1000 Tavares;-7.6358 Lapua;62.9700 Salem Lakes;42.5366 Copalchí;9.8473 Klášterec nad Ohří;50.3846 Tonya;40.8856 Kiri;-1.4955 Barjora;23.4333 Mohania;25.1692 Náfplio;37.5667 Rønne;55.0986 Sainte-Maxime;43.3089 Havre de Grace;39.5480 Vedasandūr;10.5310 Godella;39.5200 Urbino;43.7252 Moultrie;31.1591 Sokone;13.8833 Noya;42.7833 Baoshan;24.7428 Hershey;40.2806 Cruzília;-21.8389 Jaitpur;25.9132 Priverno;41.4667 Candeias;-20.7669 Bo;20.6736 Langwedel;52.9999 Mount Vernon;38.3140 Zhabinka;52.2006 Gua;22.2136 Olsberg;51.3500 San Francisco;15.6667 Majia;35.4599 Dargahān;26.9636 Villa Yapacaní;-17.4028 Satwās;22.5363 Cadaval;39.2333 Washington;38.5515 Oschatz;51.3003 Pualas;7.8167 Grünstadt;49.5692 Ayomi;6.7833 Río Branco;-32.5972 Ungutūru;16.8230 Tapejara;-23.7328 Boninal;-12.7019 Morris;41.3749 Wilbraham;42.1270 Yanhewan;36.7523 Sumbha;25.5789 Darłowo;54.4208 Marofinaritra;-15.0333 Ōki;33.2104 Anderson Creek;35.2657 Rendon;32.5789 Kottaiyūr;10.1096 Boa Nova;-14.3628 Weigelstown;39.9852 Qazyqurt;41.7598 Ústí nad Orlicí;49.9739 Apuiarés;-3.9489 Ludvika;60.1333 Boula’wane;32.8607 Ganguvārpatti;10.1694 Qobustan;40.0842 Chelsfield;51.3582 Hasami;33.1379 Santa Branca;-23.3969 Mūrak;35.3753 Bakun;16.7925 Tostado;-29.2333 Domkonda;18.2536 Kārempūdi;16.4333 Breza;44.0167 Killarney;52.0588 Raghunāthpur;25.5615 Kambainellūr;12.2071 Chippewa Falls;44.9358 Mocha;13.3203 Paina;25.5406 Hatten;53.0083 Chaugāin;25.4801 Eggertsville;42.9665 Paraíso;18.0000 Douarnenez;48.0922 Miho;36.0045 Tlayacapan;18.9556 Perdūr;13.3844 Stanytsia Luhanska;48.6449 Boa Esperança do Sul;-21.9925 Leno;45.3703 East Milton;30.6175 West Freehold;40.2324 Dumas;35.8613 Ouled Sidi Brahim;36.3667 Mount Barker;-35.0667 Bezou;32.0833 Ollioules;43.1394 Hanover;40.8197 Bagulin;16.6079 Iakora;-23.1000 Tijucas do Sul;-25.9278 Pucioasa;45.0742 Altınekin;38.3078 Goonellabah;-28.8167 Red Bluff;40.1735 Ghaura;24.5059 Tepecoyo;13.7003 Sur Singh;31.3992 Haaren;51.6000 Sison;9.6592 Tālbahat;25.0420 Benaulim;15.2700 Tolland;41.8786 Tsimlyansk;47.6167 Keāl;25.1218 Amarāvati;16.5730 Löbau;51.0944 Corinth;34.9474 Ust’-Ordynskiy;52.8056 Poço das Trincheiras;-9.3128 Çobanlar;38.7014 Belpukur;21.9851 Kearsley;53.5300 Asagiri;32.2403 Barra do Mendes;-11.8100 Kurikuppi;15.0700 Água Boa;-17.9958 Minignan;10.0000 General MacArthur;11.2486 Tiruppanandāl;11.0919 Paulino Neves;-2.7189 Plumtree;-20.4781 Ankaramena;-25.0167 Guiratinga;-16.3489 Jericho;40.7875 Mbuyapey;-26.2000 San Martín de Loba;8.8333 Farap;39.1667 Bad Camberg;50.3000 Robbinsdale;45.0261 Bayt Jālā;31.7150 Abensberg;48.8000 Greenwood;33.5126 Morroa;9.3333 Iflissen;36.8636 Sopetrán;6.5017 Bāzidpur;26.1536 Vatra Dornei;47.3461 Pinhalzinho;-22.7800 Cefalù;38.0333 Barroquinha;-3.0189 Middlesex;40.5744 Undrājavaram;16.7866 Wörgl;47.4831 Scottsbluff;41.8684 Murra;13.7592 Oleggio;45.6000 Eching;48.3000 Alausí;-2.1900 Uibaí;-11.3369 Alterosa;-21.2190 Lotte;52.2764 Malvik;63.3728 Orikhiv;47.5677 La Carlota;37.6667 Araceli;10.5529 Ceres;-29.8667 Hutchinson;44.8855 Painan;-1.3511 Abony;47.1894 Retirolândia;-11.4950 Squinzano;40.4333 Al Mu‘aḑḑamīyah;33.7421 Bela Simri;25.5295 Annāram;16.7840 San Carlos;6.1897 Tostedt;53.2833 Short Hills;40.7389 Hudson;44.9639 West Lincoln;43.0667 Belvedere Park;33.7489 Khagra;25.3511 Shamalgan;43.3775 Liedekerke;50.8667 Bongaree;-27.0813 Ban Don Kaeo;18.8567 Artondale;47.3021 Pé de Serra;-11.8339 Krasnyy Kut;50.9500 Manoel Vitorino;-14.1450 Denzlingen;48.0683 Serinhisar;37.5807 Schwaz;47.3500 Immenstadt im Allgäu;47.5667 Wangjiaxian;36.5443 Maisach;48.2167 Schneeberg;50.5942 Bokod;16.4914 Villa Vásquez;19.7400 Fiesole;43.8072 Locorotondo;40.7558 Hickory Hills;41.7248 Bensville;38.6176 Torrijos;39.9833 Santa María;-32.7469 Lockhart;29.8785 Doddipatla;16.5167 Belazao;-19.8833 Verona;40.8323 Sidi Azzouz;31.7667 Cliftonville;51.3881 Rovinj;45.0833 Barranco de Loba;8.9500 Vellodu;10.3048 Ourtzagh;34.5500 Pomorie;42.5683 Vembārpatti;10.2500 Mizil;45.0000 Gernsbach;48.7633 Colesville;39.0730 Zemrane;31.5339 Argentan;48.7444 Daulatnagar;25.3216 Vadasseri;8.1937 Tagalft;32.2389 Reidsville;36.3376 Ulchin;37.0013 Matane;48.8500 Lāla;24.5542 Ponnampatti;10.3668 Riedisheim;47.7483 Ömerli;37.4025 Graça;-4.0458 Holzgerlingen;48.6392 Mantaly;-13.1667 Bålsta;59.5833 Geldagana;43.2162 Pueblo Rico;5.2500 Dahāria;26.1807 Luino;46.0000 Peddavadlapūdi;16.4098 Lindenhurst;42.4175 El Rodeo;14.3906 Alagoinha;-6.9500 Wingene;51.0500 Brooks;50.5642 Middlewich;53.1920 Vempalle;13.5382 Sai Buri;6.7012 Yedapalli;18.6789 Brazópolis;-22.4739 Taggia;43.8439 Amantea;39.1333 Nattappettai;12.8187 Kihoku;34.2114 Mannūr;17.2994 Tianguistengo;20.7278 Hlučín;49.8967 Caselle Torinese;45.1775 Yamada;39.4677 Sassenberg;51.9897 Lagoa;37.7500 Belmont;35.2212 Auria;24.8563 Temsamane;35.1167 Muqui;-20.9519 Washington Court House;39.5381 Cojedes;9.6167 Tokoroa;-38.2167 Tounfit;32.4667 Ihaddadene;31.2000 Adams;40.7092 Bāri;25.8769 San Antonio;3.9167 Polyarnyye Zori;67.3667 Salempur;26.4588 Natchez;31.5437 Ascensión;-15.6996 Sariosiyo;38.4133 Tururu;-3.5808 Pine;40.6437 Palmer Ranch;27.2286 Lohiyār Ujain;26.6284 Zaouiat Moulay Bouchta El Khammar;34.4833 Lilburn;33.8897 Anantarāzupeta;14.0014 Marostica;45.7456 Igrapiúna;-13.8258 Sanpozhen;39.6683 Kinogitan;8.9855 New Paltz;41.7577 Sainte-Anne-des-Plaines;45.7617 Kukdeshwar;24.4828 Santa Maria do Suaçuí;-18.1900 Balangiga;11.1097 Itinga;-16.6128 Sunset;25.7060 San Andrés Timilpan;19.8667 Tezu;27.9167 Beasain;43.0458 Sīdī Barānī;31.6108 Besārh;25.9537 Fisciano;40.7667 Grafing bei München;48.0500 Alsager;53.0961 Sitalpur;26.4050 Cotegipe;-12.0278 Peumo;-34.3961 Wulong;23.3202 Niederzier;50.8831 Vemulapūdi;17.6087 Taglio;45.0167 Modakkurichchi;11.2329 Chudovo;59.1167 Angermünde;53.0333 Groveton;38.7605 Pazar;40.2620 Ayni;38.6667 Ban Nong Kula;16.6500 Det Udom;14.9060 Kodigenahalli;13.8608 Üzümlü;39.7100 Saldaña;3.9347 Ozark;31.4508 Mānikpur;25.1339 Hopatcong;40.9541 San Cristóbal;-30.3167 Moorestown-Lenola;39.9659 Tibri;31.9854 East Bridgewater;42.0352 Kannivādi;10.3794 Hettstedt;51.6333 Koundara;12.4800 Dom Feliciano;-30.7039 Sankt Leon-Rot;49.2653 Yanshuiguan;36.8252 Royal Kunia;21.4053 Lockhart;28.6270 Tafrant;34.6250 Langerwehe;50.8167 Valday;57.9667 Pak Thong Chai;14.7167 Tamār;23.0488 Vöhringen;48.2833 Nether Providence;39.8971 Gangaikondān;11.5389 Doctor Juan Eulogio Estigarribia;-25.3700 Feldkirchen;46.7236 Kartuzy;54.3333 Emerald;-23.5208 Zolote;48.6833 Abhwar;26.1740 Nacozari Viejo;30.4833 O‘nhayot;41.0103 Lake Arbor;38.9105 Black Forest;39.0608 Itakura;36.2259 Furukawamen;33.2384 Matiçan;42.6449 Jacaraú;-6.6119 Rheinfelden;47.5500 Rozenburg;51.9058 Ciénega de Flores;25.9500 Abadla;31.0167 Quakenbrück;52.6772 Jasper;33.8508 Whitewater;42.8372 Værløse;55.7819 Neuri;26.0076 Nawāda Gobindganj;26.4868 Barkuhi;22.1901 Lakinsk;56.0333 Artena;41.7333 West Nipissing / Nipissing Ouest;46.3667 Sidmouth;50.6800 Assenede;51.2333 Manoharpur;22.3746 Uslar;51.6597 Montfoort;52.0500 Corat;40.5725 Mathurāpur;24.9073 Cervignano del Friuli;45.8231 North Mankato;44.1810 Tsukawaki;33.2832 Mapanas;12.4750 Duvva;16.7792 Yakhroma;56.2833 Romitan Shahri;39.9333 Bog Walk;18.1020 Buda;30.0758 Marhamat;40.5000 Bhīmadolu;16.8144 Caln;40.0014 Estremoz;38.8500 Sadovoye;46.1269 Ban Mueang Nga;18.5997 Tergnier;49.6558 Ishikawa;37.1571 Doranahalli;16.7324 Nyasvizh;53.2167 Kot Īsa Khān;31.1351 Eski Īkan;43.1833 Dharampur Bānde;25.6609 Lengede;52.2049 Medvezhyegorsk;62.9000 Sumbas;37.4431 Ambohimanga Atsimo;-20.8667 Tenerife;9.8989 El Barrio de la Soledad;16.8000 Beekman;41.6042 San Rafael Petzal;15.4167 Coronel Bogado;-27.1700 Oud-Turnhout;51.3167 Phulwaria;25.9395 Boha;29.8348 Bua Yai;15.5858 Zapolyarnyy;69.4167 Juan L. Lacaze;-34.4311 Ūttukkottai;13.3354 Bideipur;21.0147 Midlothian;41.6254 Algarrobo;10.1000 Raydah;15.8233 Mirandiba;-8.1189 Khiria;25.6047 Rājnagar;23.9500 Simri;25.7523 Sibundoy;1.2033 Ortahisar;38.6208 Tāmaraikkulam;10.1085 Gonzalez;30.5822 Gesuba;6.7242 Vikravāndi;12.0369 Novohrodivka;48.2000 Viera West;28.2467 Daean;35.4038 Byāhatti;15.4468 Rezzato;45.5150 Thame;51.7500 Takerbouzt;36.4180 Ocotepec;17.2167 Cristópolis;-12.2339 Santiago Ixtayutla;16.5667 Wang’anzhen;39.3989 Santol;16.7667 Lebanon;43.6353 Vellarivalli;11.6003 Dippoldiswalde;50.8933 Korneuburg;48.3453 Rānāpur;22.6470 Mankal;17.2014 Svetlogorsk;54.9500 Makarska;43.3000 Bārah;25.3885 Saddle Brook;40.9033 Tarumirim;-19.2808 Marechal Floriano;-20.4128 Frei Miguelinho;-7.9408 Sines;37.9547 Orizona;-17.0308 North Auburn;38.9306 Santo Antônio dos Lopes;-4.8689 Nova Olinda;-7.0919 Mongeri;8.3167 Bousso;10.4825 Wennigsen;52.2742 Kouroussa;10.6530 Gudūr;17.7956 Kashin;57.3500 Bondeno;44.8833 Castelsarrasin;44.0400 Frankenberg;50.9108 Sālotgi;17.1700 Taki;34.4961 Dayr al Barshā;27.7500 Rosemère;45.6369 Bellair-Meadowbrook Terrace;30.1796 Bedford;42.4969 Vinto;-17.3833 Hasanpur;25.6330 Tranås;58.0333 Avalēpalli;12.7714 Lubsko;51.8000 Eastham;53.3130 Benito Juárez;-37.6667 Nirakpurpāli;25.3244 Kawaii;38.0045 Almoloya de Alquisiras;18.8500 Kelafo;5.5889 Bishenpur;24.6282 Goshen;41.3817 Thonotosassa;28.0464 Staszów;50.5642 Bökönbaev;42.1100 Itanhandu;-22.2958 Entre Rios de Minas;-20.6708 Kīāshahr;37.4194 Hamworthy;50.7207 Ya‘bad;32.4467 Jodoigne;50.7167 Rasauli;26.1270 Samāi;24.9788 El Porvenir de Velasco Suárez;15.4333 Lamont;35.2659 Cêrro Largo;-28.1489 Pilas;37.3017 Hard;47.4892 Leirvik;59.7798 Ewarton;18.1833 Santa Cruz de Bezana;43.4442 Juan de Acosta;10.8333 Satuba;-9.5628 Douar Hammadi;31.6000 Vīraganūr;11.4761 Saraykent;39.6936 Jiānganj;25.8661 Germasógeia;34.7181 Beloeil;50.5333 Arklow;52.7941 Shichinohe;40.7447 Nowy Tomyśl;52.3167 Bickley;51.4003 Kurwa Mathiā;26.8572 Thisted;56.9569 Hokuei;35.4833 Cambados;42.5000 Annāmalainagar;11.4000 Ahrensfelde;52.5762 Orkney;-26.9789 Cromwell;41.6122 Mistassini;48.8229 Sihaul;25.9438 Mittegroßefehn;53.3833 Valença;42.0167 Bou Izakarn;29.1667 Ichtegem;51.1000 Uitgeest;52.5333 Chatham;39.6733 Amriswil;47.5500 Ochër;57.8833 Daparkha;26.1293 Champasak;14.8833 Mill Valley;37.9086 Milnrow;53.6101 Cecil;40.3147 Eppstein;50.1333 Burglengenfeld;49.2000 Van Buren;43.1211 Zherdevka;51.8500 Hayashima;34.6006 Friedland;51.4167 Monticello;45.2991 Zayda;32.8167 Mercerville;40.2360 Krishnarāyapuram;10.9563 Aḑ Ḑab‘ah;31.0338 Boa Esperança;-18.5400 Brasilândia;-17.0100 Venëv;54.3500 Varde;55.6200 Mahrail;26.2952 Murrhardt;48.9800 Sakaki;36.4618 Püspökladány;47.3167 Dumri;26.9873 Belambo;-19.4000 Lauchhammer;51.5000 Haidarnagar;24.4883 Fort Bliss;31.8396 Campos Altos;-19.6958 Bariyārpur;26.6347 Orange;41.2827 Nilo Peçanha;-13.5989 Sanlúcar la Mayor;37.3831 Kaoma;-14.8000 Mirinzal;-2.0650 Iskapalli;14.7363 Capitão Enéas;-16.3239 Minamiaizu;37.2004 Storozhynets;48.1597 Bad Ischl;47.7115 Aqköl;52.0000 Yulee;30.6350 Kubādupuram;16.4680 Little Lever;53.5630 Paithān Kawai;26.1755 Singalāndāpuram;11.4166 Malepur;24.9740 Bainbridge;30.9052 Adakplamé;7.4500 Nasiyanūr;11.3381 West Bradford;39.9633 Aldenhoven;50.8958 Rebouças;-25.6208 Uppidamangalam;10.9034 Beronono;-18.2000 Brookside;39.6665 Medapādu;17.0044 Papagaios;-19.4489 Piquete;-22.6136 Karma;23.6840 Baianópolis;-12.3058 Summerstrand;-33.9914 Padaivedu;11.4401 Valbonne;43.6414 Marchtrenk;48.1917 Ksibet el Mediouni;35.6900 Neuenkirchen;52.2411 Simões;-7.5989 Belm;52.3000 Bhadarwāh;32.9800 Hunasagi;16.4575 Bovenden;51.5897 Titiribí;6.0667 Seirō;37.9745 Beilen;52.8567 Mŭ’minobod;38.1083 Głowno;51.9642 Pedro Velho;-6.4389 Rio Piracicaba;-19.9289 Puerto Deseado;-47.7500 Wezep;52.4625 Vellakkinar;11.0736 Kadena;26.3616 Lauriyā Nandangarh;26.9833 Knottingley;53.7050 Mucambo;-3.9089 Veglie;40.3333 Galatge;16.4200 Sidi Abdelkarim;33.1869 Grand Falls;48.9578 Rose Belle;-20.4025 Olgiate Olona;45.6333 Mayrtup;43.2036 Polukallu;15.8284 Kerben;41.5000 Ostrzeszów;51.4167 Jefferson Valley-Yorktown;41.3180 Lakoucun;28.3412 Nioaque;-21.1350 Turki;26.0381 Eggenfelden;48.4039 Ipiranga;-25.0239 Jīran;24.3087 San Ferdinando di Puglia;41.3000 Kakunodatemachi;39.5963 Náfpaktos;38.3939 Lakeland North;47.3374 Arenoso;19.1800 Novoulyanovsk;54.1667 Pélézi;7.2833 Kisanuki;31.3444 San Vicente;17.5947 Uxbridge;42.0593 Arlington;35.2594 Narvik;68.4363 Fara in Sabina;42.2167 Kępno;51.2833 Santiago Jocotepec;17.5833 Zimnicea;43.6522 Thong Pha Phum;14.7382 Rāyapalle;16.2836 Waianae;21.4568 Lübben (Spreewald);51.9500 Sonbarsa;25.7069 Skippack;40.2165 Clearview;44.3981 Sungai Guntung;0.2956 Aginiparru;16.6817 Nelas;40.5167 Leverano;40.2833 Eynesil;41.0500 Nesebar;42.6500 Cheadle;52.9849 Rambha;19.4433 Boutilimit;17.5504 Vetralla;42.3106 San José Acatempa;14.2667 Bhakua;26.5167 Gangavalli;11.4381 Sobrāon;31.1833 Chōsei;35.4167 Netāpur Tānda;16.8321 Jabbeke;51.1833 Ovidiu;44.2700 Debre Werk’;10.6667 Torre del Campo;37.7667 Vedappatti;10.9988 Naţanz;33.5133 Spaichingen;48.0758 Mitontic;16.8667 Söğütlü;40.9000 Felixlândia;-18.7578 Fokino;53.4500 Fujikawa;35.5612 Dundigal;17.5781 California;38.2969 Ricaurte;-2.8667 Niška Banja;43.2933 Leuna;51.3167 Frederick;40.1088 Sābang;22.1830 Sāgar;16.6249 Somerton;32.6007 Rāmpurwā;26.7544 Maria da Fé;-22.3078 Gurinhém;-7.1239 Sher;26.3422 Tamm;48.9167 Bebra;50.9711 Sülüktü;39.9400 Sahalanona;-22.0500 Lukoyanov;55.0333 Balua;26.3272 Tempio Pausania;40.9015 Hòa Thượng;21.6472 Astolfo Dutra;-21.3150 Kaluvāya;14.5117 Lich;50.5217 Saghar Sultānpur;26.1583 Antanambaobe;-16.2500 Rio Azul;-25.7328 Luling;29.9008 Mahatsinjony;-21.4167 Tibasosa;5.8333 East Wenatchee;47.4174 Elizabethton;36.3367 Tola Khadda;26.7396 Kalkar;51.7389 Alvorada D’Oeste;-11.3417 Italva;-21.4208 Santiago de Anaya;20.3844 Stroud;51.7440 Golub-Dobrzyń;53.1000 El Guetar;34.3372 Fatehpur;26.2813 Farmington;42.9895 Rāmpur Shāmchand;25.5664 Mjölby;58.3333 Lomas de Sargentillo;-1.8833 Xexéu;-8.8019 Damalcheruvu;13.4833 Fiadanana;-20.3333 Zazafotsy;-22.2000 Bamble;59.0197 Santa Catalina;10.6039 Sint-Gillis-bij-Dendermonde;51.0189 Pelham;42.7335 Zhipingxiang;35.2949 Aracatu;-14.4278 Chaparral;32.0442 Gudikallu;15.7441 Villacidro;39.4578 Galmi;13.9660 Ambohimitombo;-20.7167 Kivertsi;50.8331 Shitāb Diāra;25.7563 Gouveia;40.5000 Sarezzo;45.6500 Mahavelona;-19.1667 Dryden;42.4786 Mbini;1.5833 St. Clair;42.7833 McFarland;35.6781 Adh Dhakhīrah;25.7347 Petrovske;48.2833 Gondalga;17.5097 Talwat;31.2883 Akropong;5.9742 Meckenbeuren;47.7000 Mignouré;7.4833 Livingston;37.3875 Bang Racham;14.9000 Latham;42.7427 Moudjbara;34.5037 Kovilūr;12.5537 Frunze;40.1267 Enghien;50.7000 Casatenovo;45.6983 Andalgalá;-27.6000 Allouez;44.4721 Xikou;23.5947 Ambatomarina;-20.5833 Marokarima;-21.2167 Oberhaching;48.0167 Montepulciano;43.1000 Amborondra;-21.9167 Schmölln;50.8950 Wauconda;42.2749 Río de Oro;8.2917 Vanono;-16.0333 Covington;33.6049 Wanding;24.0833 Gullapuram;10.0657 El Carmen de Atrato;5.8983 Tsarahasina;-15.7667 Gekhi;43.1636 Mór;47.3717 Tall Banāt;36.2550 Bēylul;13.2644 Huron;44.3623 Hantsavichy;52.7500 Múggia;45.6000 Srīnagar;25.9823 Córdoba;0.8550 Māndalgarh;25.2000 Antohobe;-19.7667 Chumpak;41.8585 Brainerd;46.3553 New Ulm;44.3120 Fāmenīn;35.1142 Kamituga;-3.0600 Shāndīz;36.3953 Shofirkon Shahri;40.1167 La Entrada;15.0500 Itaguaçu;-19.8019 Kumirimora;22.6939 Ambodihara;-14.7500 Wadlakonda;17.7736 Buchloe;48.0375 Chesterton;41.5997 Lariano;41.7333 Campina da Lagoa;-24.5919 Genthin;52.4000 Talata-Vohimena;-20.8500 Villalba;43.3000 Bealanana;-14.5500 Aßlar;50.5833 Killai;11.4493 Havelock North;-39.6667 Erbach;49.6569 Taiyong;26.4726 Itamonte;-22.2839 Solhan;38.9681 Wood Dale;41.9668 Biscarrosse;44.3931 Devipattinam;9.4770 Skoczów;49.8006 College Park;33.6363 Ambohimiera;-21.0500 Donji Vakuf;44.1500 Alexandria;45.8776 Iraci;-8.8078 Jacksonville;31.9642 Beraketa;-24.1833 Ambahita;-24.0000 Kalandy;-15.7500 Vieste;41.8833 Bevato;-18.6833 Mahajamba;-15.7000 Sharonville;39.2825 Saint-Gilles;43.6778 Jānapādu;16.4617 Thala;35.5667 Tierra Amarilla;-27.4822 Batuan;9.8000 Boechout;51.1500 Ankiliabo;-21.7000 Mirdaul;26.2363 Taphan Hin;16.2108 Ban Khlong;16.8353 Montesarchio;41.0667 Sale;-38.1000 San Bartolo;15.0844 Teploklyuchenka;42.5000 Landau an der Isar;48.6667 Taishi;34.5187 Marotsiraka;-24.2833 Zeven;53.3000 Oostakker;51.1000 Erbach;48.3281 San Rafael Las Flores;14.4814 Vatolatsaka;-23.3000 Statte;40.5667 Forest City;28.6619 San Pedro Pochutla;15.7476 Woudenberg;52.0833 Poço Branco;-5.6228 Miarinarivo;-16.6167 Dunmore;41.4152 Bekipay;-16.2500 Bhikkiwind Uttār;31.3400 Chityāl;17.2333 Nachikatsuura;33.6261 Beharona;-21.5167 Hilpoltstein;49.1833 Guapó;-16.8308 Marudūr;10.9160 Sonupur;25.8000 Rafaï;4.9731 Kurtkoti;15.3681 Dhāna;23.7470 Misano Adriatico;43.9667 Huangyadong;36.8039 Weißenhorn;48.3000 Dubovka;49.0500 Gorbea;-39.1000 Çaybaşı;41.0333 Midleton;51.9160 Khe Sanh;16.6193 Bairia;26.7373 Tako;35.7356 Titara;26.2311 Kuraymah;18.5500 Canmore;51.0890 Kadoli;15.8800 Lauterbach;50.6378 Kathevaram;16.2608 Nehrəm;39.1122 Mahuākherāganj;29.1300 Boyovut;40.2822 Dueville;45.6333 Altınoluk;39.5823 Philippsburg;49.2333 Beesel;51.2833 Lyndhurst;41.5172 Aleksandrów Kujawski;52.8767 Dhutauli;25.5274 Chodov;50.2414 Alfredo Chaves;-20.6350 Bolívar;4.3386 Alcanena;39.4667 Wanzleben;52.0667 Bandar-e ‘Asalūyeh;27.4744 Baduriātola;24.0928 Sterling;40.6205 East Norriton;40.1506 Berre-l’Étang;43.4756 Polavaram;17.2500 East Greenwich;41.6362 Pacaembu;-21.5622 Fergus Falls;46.2854 Vilanova del Camí;41.5733 Noyon;49.5811 Salaverry;-8.2214 Kiskőrös;46.6204 Topliţa;46.9236 Primeira Cruz;-2.5100 Hobe Sound;27.0729 San Agustín de Guadalix;40.6781 Petrolândia;-9.1828 Spiesen-Elversberg;49.3167 Anapurus;-3.6719 Jhaua;25.6250 Athens;35.4573 Palkūr;15.4144 Vedi;39.9106 Sesquilé;5.0453 Antonivka;46.6791 Sugauna;26.4077 Willow Grove;40.1469 Nynäshamn;58.9000 Novellara;44.8500 Red Oak;32.5212 Tapilula;17.2500 Polotitlán de la Ilustración;20.2231 Panapākkam;12.9210 Fagnano Olona;45.6667 Tucacas;10.7978 Ping’anbao;40.4901 Binkolo;8.9500 Badarwās;24.9752 Bahādurpur;25.4522 Velānganni;10.6814 Curiúva;-24.0328 Gömeç;39.3911 St. James;40.8761 Wakasa;35.5489 Champādānga;22.8275 Bheja;26.1046 Chợ Mới;10.5500 Olean;42.0819 Fujimi;35.9146 Maracaí;-22.6106 Hidalgo;26.1090 Moralzarzal;40.6750 Pirallahı;40.4708 Zhovkva;50.0667 Aurora;42.7382 Marmeleiro;-26.1489 Lorsch;49.6539 Šamorín;48.0267 Brejo do Cruz;-6.3489 Oulad Chikh;32.8544 Toyloq Qishlog’i;39.6014 Moberly;39.4179 Circleville;39.6063 Kalādgi;16.2040 Gien;47.6981 Urupês;-21.2019 Newhaven;50.8000 Manor;30.3562 Bihārīganj;25.7341 Puxinanã;-7.1608 Paikpar;26.0966 Kamtaul;26.3280 Kamigōri;34.8736 Barghāt;22.0306 Diego de Almagro;-26.3911 Kettering;38.8888 Llanera;43.4667 Pitlam;18.2227 Shepherdsville;37.9813 San Prisco;41.0833 McPherson;38.3714 Bayt Sāḩūr;31.7000 Maumee;41.5696 Karaund;25.9741 Krumbach;48.2500 Littleborough;53.6440 New Franklin;40.9525 Arbaa Laaounate;32.7446 Bergen;54.4167 Melsungen;51.1333 Sun Lakes;33.2172 Engenheiro Beltrão;-23.7969 Strada;43.5833 Nāgāwaram;17.4875 Frei Paulo;-10.5489 Tomiño;41.9833 Kodriva;22.5342 Qiblaí;38.6167 Khmis Sidi al ’Aydi;33.1228 Pīrnagar;25.5982 Hājan;34.2989 Chaoyangdicun;42.0221 Hīdaj;36.2547 Plouzané;48.3800 Jālihalli;16.3643 San Antonio de las Vueltas;22.5167 Avanhandava;-21.4608 Turki;26.0294 Panjgirāin Kalān;30.6096 Waycross;31.2108 Bodagudipādu;14.7669 Ivanec;46.2264 Ipanguaçu;-5.4978 Douar Lehgagcha;32.5500 Lanuvio;41.6833 Tummalapenta;15.0278 Cavarzere;45.1370 Higashimiyoshi;34.0368 Zhashkiv;49.2500 North Battleford;52.7575 Guapé;-20.7619 Corupá;-26.4250 Jutaí;-2.7469 Kauhajoki;62.4319 Gura Humorului;47.5539 Wanze;50.5353 Dongshicuo;23.7021 Hatoyama;35.9815 Paruchūru;15.9667 Wommelgem;51.2000 Luozi;-4.9480 Pembroke;45.8167 Jinmingsi;38.0512 Ettenheim;48.2556 Avenal;36.0311 Miami Springs;25.8195 Clemencia;10.5833 Powell;40.1689 Guamal;3.8800 Arroio dos Ratos;-30.0769 Santo Augusto;-27.8508 Casinhas;-7.7411 Găeşti;44.7194 Lower Moreland;40.1346 Zhongliao;23.9039 Haßfurt;50.0167 Toulal;32.3036 Zábřeh;49.8826 Mangalāpuram;11.5667 Cabo Verde;-21.4719 Sant’Angelo Lodigiano;45.2389 Oosterzele;50.9500 Canals;38.9611 Wayland;42.3586 Helensburgh;56.0166 Hammam el Rhezez;36.8900 Powell;36.0358 Ban Tha Luang Lang;12.6376 Alamo;37.8548 Arcozelo;41.0555 Dhubaria;24.0044 Couvin;50.0500 Margraten;50.8167 Grünberg;50.6000 West Lealman;27.8192 Konāje;12.8162 San Pietro Vernotico;40.4833 Ajjampur;13.7279 Uruana;-15.4978 Plumstead;40.3878 Horodyshche;49.2925 Sirugamani;10.8975 San Juan;10.6500 Tiruppālai;9.9825 Pilich;25.2379 Apostolove;47.6595 Forlimpopoli;44.1833 Peri-Mirim;-2.5778 Wells Branch;30.4433 Dammennu;17.0308 Joaquim Pires;-3.5078 Machados;-7.6858 Latisana;45.7833 Ampasinambo;-20.5167 Sariq;37.6722 Yamanobe;38.2891 Granville;48.8381 Rājupālem;15.1378 Aberystwyth;52.4140 Achuapa;13.0536 Sauk Rapids;45.5981 Fátima;39.6255 Ak-Dovurak;51.1833 Alagoinha;-8.4658 Dowbarān;28.4061 Millbury;42.1925 Jupi;-8.7119 Lyaskovets;43.1000 Selston;53.0700 Cherry Hill Mall;39.9384 Balha;25.5468 Höchstadt an der Aisch;49.7000 Rutherford;-32.7150 Bundāla;31.5333 Chāhatpur;26.2331 Jindayris;36.3947 Ghorādal;22.0519 Michendorf;52.3129 Arzachena;41.0833 Crispiano;40.6000 Uppāda;17.0883 Leżajsk;50.2667 Gouka;8.1333 Kings Park West;38.8151 Loganville;33.8353 Jardín;5.5986 St. Helens;45.8572 Eichstätt;48.8919 Atomé-Avégamé;7.2333 Zarghūn Shahr;32.8500 Beachwood;41.4759 Ampthill;52.0263 Sóller;39.7667 Teays Valley;38.4482 Heliópolis;-10.6828 Melenki;55.3333 Kadattūr;12.0861 Kolagallu;15.1500 Neckargemünd;49.3939 Sarāyān;33.8603 Verona;42.9892 Puduppatti;9.7639 Isāgarh;24.8391 Carmópolis;-10.6478 Forest Hill;32.6619 Marienheide;51.0833 Nallajerla;16.9500 South Sioux City;42.4627 Nantucket;41.2831 Curuá;-1.8878 Carmo de Minas;-22.1219 Bellefontaine;40.3627 Amrābād;16.3833 Pakra;25.3711 Sansare;14.7478 Chinna Mushidivāda;17.8057 Verín;41.9408 Shōdoshima;34.4798 Kyzyl-Kyshtak;40.5444 Bhulath Gharbi;31.5344 Dārat ‘Izzah;36.2828 Rostamābād;36.8983 Kupino;54.3667 Pierrelatte;44.3775 Worthington;43.6281 Pondūru;18.3508 Kaua Kol;24.8447 Ketsch;49.3658 Atner;21.6238 Chiran;31.3783 Torul;40.5572 Beeville;28.4053 Mont-Laurier;46.5500 Münchenstein;47.5167 Castel San Giovanni;45.0500 Richland;40.4490 ‘Alem T’ēna;8.3000 Gołdap;54.3161 Uzwil;47.4369 Tarumizu;31.5228 Bad Soden-Salmünster;50.2667 Ajas;34.3316 Mata;14.0436 Chandla;25.0715 Dyykan-Kyshtak;40.5100 Sattar;25.9550 Ephrata;40.1811 Arsali;24.3754 Mansidão;-10.7158 Gülchö;40.3167 Gosaingaon;25.3724 Coldwater;41.9465 Villas;26.5504 Khomām;37.3892 Biedenkopf;50.9128 Włodawa;51.5500 Ba Chúc;10.5000 Sampaloc;14.1625 Chinique;15.0411 Nurkot;32.2017 Msata;-6.3362 Bollène;44.2803 Châlette-sur-Loing;48.0117 Strathmore;51.0378 Palmeirais;-5.9778 Bandarbeyla;9.4833 Buttar;31.0038 Filandia;4.6667 Trzebnica;51.3050 San Juan La Laguna;14.7000 Destin;30.3950 Alleroy;43.2500 Santa Coloma de Farnés;41.8624 Bhachhi Asli;26.0147 Schoonhoven;51.9500 Brandermill;37.4340 Dharmaram;18.3038 Bully-les-Mines;50.4419 Futrono;-40.1333 Nybro;56.7333 Kādachchinallūr;11.3686 Río Caribe;10.7008 Caledon;-34.2300 Kotgīr;18.5722 Xireg;36.9257 Ipswich;42.6857 Kafr ‘Awān;32.4167 East Whiteland;40.0474 Sátoraljaújhely;48.4000 Hünxe;51.6417 Auburn;38.8950 Warrenville;41.8209 Bondāda;16.5295 Carlópolis;-23.4250 Fallsburg;41.7391 Saksohāra;25.3635 Tejutla;14.1667 Genas;45.7314 Solonytsivka;49.9942 São José do Cedro;-26.4550 Camponogara;45.3833 Sabbavaram;17.7900 Bangāwān;26.0427 Neunkirchen;50.7861 Mangha;23.3700 Andaraí;-12.8069 Motupe;-6.1519 Kawatana;33.0728 Marshall;44.4488 Namīn;38.4256 Hazel Crest;41.5732 Kollipara;16.2877 Cobh;51.8510 Bedford;38.8602 Cedar Lake;41.3696 Altos del Rosario;8.8000 Kuleshovka;47.0833 Busogo;-1.5572 São João do Triunfo;-25.6828 Hinabangan;11.7000 Schermbeck;51.6950 Massaranduba;-7.2000 Blegny;50.6667 Nules;39.8525 Snoqualmie;47.5293 Yongcong;26.0451 Manta;10.3564 Mahalpur;31.3618 El Crucero;11.9894 Tamsaout;29.5333 Mansāpur;26.5262 Konaklı;36.5833 Arcadia;43.0870 Saugeen Shores;44.4333 Beacon;41.5036 Palmito;9.3333 Chanal;16.6000 Minneola;28.6067 Las Rosas;-32.4833 Quartucciu;39.2529 Meruoca;-3.5419 Mount Vernon;38.7140 Kachchippalli;11.5950 Dallas;33.9152 Raamsdonksveer;51.6833 Quatá;-22.2475 Ban Nong Hoi;18.7500 Pallappālaiyam;10.9951 Bairo;26.0144 Lone Tree;39.5309 Canton;40.5632 Sirigeri;15.6300 Ad Duraykīsh;34.8969 Friesenheim;48.3731 McMinnville;35.6864 Kākan;26.1509 Winsum;53.3312 Camapuã;-19.5308 South Park;40.2988 Grammichele;37.2147 Warrensville Heights;41.4363 Channahon;41.4210 Birstall;52.6736 Lakeland;35.2585 Uarini;-2.9900 Jimbolia;45.7931 Fontenay-le-Comte;46.4669 Duvergé;18.3800 Cidelândia;-5.1739 Lanuza;9.2322 San Jorge;14.9253 Pātiram;25.3167 Löningen;52.7167 Franklin Park;40.4439 Chāmpāhāti;22.4000 Ihtiman;42.4374 Basantpur;26.1331 Lappersdorf;49.0525 Dinant;50.2667 Thompson;55.7433 Gopālnagar;22.8289 Beverstedt;53.4340 Karnāwad;22.7361 An Thành B;10.1958 Palestina;5.0833 Vázquez;21.1399 Dragør;55.5833 Bunhe;48.2206 Castelfranco di Sotto;43.7000 Sirhāli Kalān;31.2783 Melissa;33.2891 Vigodarzere;45.4500 Nikolayevsk;50.0333 Sivalārkulam;8.8700 Royal Wootton Bassett;51.5330 Saint-Orens-de-Gameville;43.5514 Caldeirão Grande;-11.0200 Echelon;39.8482 Ettāpur;11.6625 Alto Paraná;-23.1289 São Pedro do Piauí;-5.9289 Saint-Paul-lès-Dax;43.7256 Quierschied;49.3167 Itapagipe;-19.9089 Bad Laasphe;50.9303 Salzano;45.5333 Eloxochitlán;18.5088 Llanes;43.4214 Garag;15.5750 Doumé;8.0167 Beyne-Heusay;50.6167 Monte Alegre de Sergipe;-10.0269 Velilla de San Antonio;40.3667 Ratnāpuram;18.6283 Shawangunk;41.6335 São Félix da Marinha;41.0340 Parjuār;26.4836 Šternberk;49.7305 Lavaltrie;45.8833 Mutterstadt;49.4417 Amboasary-Gara;-18.4333 Callaway;30.1349 Mantenópolis;-18.8628 Alto Longá;-5.2508 Ihorombe;-23.0000 Caldas;-21.9239 Spring Garden;39.9454 Bridport;50.7336 Okinoshima;36.2091 Gran;60.4411 Ixcatepec;21.2333 Nicosia;37.7500 Pfullendorf;47.9242 Głubczyce;50.2011 Rasūlpur;25.9938 Kankandighi;21.9744 Cajabamba;-7.6237 Salisbury;40.5768 Chāwalhāti;26.4614 San Martino di Lupari;45.6557 Santa Luzia;-15.4289 North Branford;41.3645 Talne;48.8863 Hösbach;50.0000 Barāon;25.4551 Sowerby Bridge;53.7100 New Fairfield;41.4880 San Martín Zapotitlán;14.6000 Castilla La Nueva;3.8333 Shiloh;38.5534 Bergen;52.8103 Tilāri;25.0040 Cayce;33.9459 Virgem da Lapa;-16.8039 Mulakalūru;16.2776 Beach Park;42.4260 Crevalcore;44.7167 Hathwān;25.6672 Dakit;10.0600 Takkolam;13.0164 Mangalapur;13.6245 Manivilundān;11.6004 Trubchevsk;52.5667 Torredonjimeno;37.7667 Sunjiayan;27.8776 Kirchheim bei München;48.1766 Conway;28.4968 Haldipur;14.3333 Cañasgordas;6.7497 Kyzyl-Adyr;42.6200 Peabiru;-23.9128 Rio de Contas;-13.5789 Maheswa;25.9718 Cossimbāzār;24.1200 Żnin;52.8500 Karivalamvandanallūr;9.2715 Montefiascone;42.5333 Nemocón;5.0500 Paranhos;-23.8928 Zelzate;51.2000 Marion;35.2035 Masangshy;42.9289 Chīmalapādu;16.8902 Udayagiri;20.1242 Kastoriá;40.5167 Saint-Martin-de-Crau;43.6397 Hersham;51.3681 Fenton;52.9977 Melilli;37.1833 Kingston;41.9862 Manching;48.7186 Kaimana;-3.6444 Afir;36.7676 Terra Boa;-12.3919 Gopālasamudram;8.6747 Khānāpur;19.0333 Speedway;39.7937 Bad Dürrheim;48.0167 Korahia;26.5325 Zhukovo;55.0333 Plougastel-Daoulas;48.3725 Urucuia;-16.1328 San Javier;-30.5833 Grez-Doiceau;50.7333 San Gennaro Vesuviano;40.8667 Qingxicun;24.5300 Combarbalá;-31.1833 Baras;13.6667 Brecksville;41.3079 Wolsztyn;52.1167 Betanzos;43.2792 Ceuti;38.0789 Ugo;39.1993 Nidzica;53.3667 Majārhāt;25.9654 Montespertoli;43.6500 Haaren;50.7956 San Jacinto;14.6667 Usmānpur;25.3487 Jānkinagar;25.8955 Ráquira;5.5333 Aghbala;32.4833 Mahārājpur;25.1040 Santaquin;39.9708 Ban Mae Kaluang;19.0778 Bisee;14.0243 Hajdúsámson;47.6000 Lakewood Park;27.5390 Rommerskirchen;51.0347 Schüttorf;52.3167 Hulkoti;15.4333 Valdemorillo;40.5017 Saarwellingen;49.3542 Auta;25.3882 South Charleston;38.3426 Drøbak;59.6667 Maribondo;-9.5769 Walton-on-the-Naze;51.8480 Kharv-e Soflá;36.1686 Sūrān;27.2856 Quatis;-22.4069 Sidi Yahia;30.4969 Endicott;42.0980 Itapetim;-7.3778 Kaharlyk;49.8522 Comines;50.7611 Ban Bung Kha;16.1675 Shangping;25.0897 Marly;50.3489 San Rafael Oriente;13.3833 Kozy;49.8667 High River;50.5808 San Marco in Lamis;41.7117 Serra Branca;-7.4828 Bataredh;26.4250 Arara;-6.8278 York;43.1860 Manerbio;45.3667 Spiez;46.6833 West End;26.6867 Sabana Iglesia;19.3300 Sigli;15.0631 Novi Iskar;42.8000 Ouargaye;11.5000 Eichenau;48.1667 Presidente Bernardes;-22.0061 Palangarai;11.1978 Carluke;55.7340 New Providence;40.6996 Kewatgāwān;25.8012 Feldbach;46.9550 Mario Campos;-20.0558 Pānchgani;17.9200 Schinnen;50.9500 Lourdes;43.0950 Mqam at Tolba;33.9375 Nārāyangarh;24.2708 Arruda dos Vinhos;38.9833 Buşrá al Ḩarīr;32.8425 Barhī;24.3045 Amilly;47.9731 Reeuwijk;52.0500 Primavera;-8.3378 Oak Park;34.1850 Kolappalūr;11.5100 Lake Wylie;35.0997 Bilga;31.0500 Marietta;39.4241 Blytheville;35.9321 Creutzwald;49.2053 Clinton;40.6315 Trebur;49.9242 Nawāda;25.1021 Connersville;39.6582 Châteaudun;48.0708 Thara;26.7027 Vernouillet;48.7208 Grass Valley;39.2238 Western Springs;41.8023 Malone;44.7956 Jardim de Piranhas;-6.3789 Chalma;21.2167 Ilamatlán;20.7833 Bhankarpur;30.6500 Bāsmenj;37.9964 Flint;53.2482 Calderara di Reno;44.5667 Chañaral;-26.3444 Alexandria;-6.4128 San Rafael;14.7333 L’Union;43.6564 Knittelfeld;47.2150 Mechtras;36.5448 Sant Sadurní d’Anoia;41.4261 Patate;-1.3167 Le Relecq-Kerhuon;48.4086 Neustadt;49.5967 Charxin;39.6967 Port Neches;29.9765 Samālsar;30.6364 Moissac;44.1047 Wiesmoor;53.4000 Chimá;9.1500 Glen Carbon;38.7580 Tezze sul Brenta;45.6862 Arendonk;51.3167 Besigheim;48.9983 Patti;38.1389 Gex;46.3333 Küssnacht;47.0667 Lake Forest Park;47.7574 Aubergenville;48.9583 Gricignano d’Aversa;41.0000 General Belgrano;-35.7667 Lavagna;44.3167 Kofelē;7.0000 Libertad;8.5583 Águas Vermelhas;-15.7469 Sault Ste. Marie;46.4817 San Juan de Betulia;9.2756 Dok Kham Tai;19.1620 Çayıralan;39.3050 Manatí;10.4450 Matias Barbosa;-21.8689 Lislique;13.8000 Roudnice nad Labem;50.4254 Birdāban;26.4012 Mayenne;48.3031 Mosgiel;-45.8750 Pôrto Esperidião;-15.8528 Loreto;43.4389 Kissane Ltouqi;34.6000 Casale sul Sile;45.6000 Keisen;33.5788 Neunkirchen;47.7269 Pentapādu Kasba;16.7939 Royse City;32.9762 Pervomaysk;54.8667 Lavello;41.0500 Luchenza;-16.0167 Goudomp;12.5778 Vitry-le-François;48.7247 Trogir;43.5169 Pāiker;24.4388 Yecuatla;19.8521 Bacup;53.7040 Sondho Dullāh;25.9016 Ubstadt-Weiher;49.1656 Quattro Castella;44.6333 Andasibe;-18.9333 Brunn am Gebirge;48.1069 Bāgli;22.6412 Henderson;32.1576 Zehdenick;52.9833 Tuchola;53.6000 Māyamānkurichchi;8.8855 Tummanatti;11.4284 Fereydūnshahr;32.9411 Govindgarh;24.3785 Hathauri;25.9593 Formello;42.0833 Kādiyāmpatti;11.8720 Standish;53.5860 Seabrook;29.5751 San Juan de Limay;13.1739 Unión de Tula;19.9570 Bischwiller;48.7664 Pathra;24.8804 Ataco;3.6000 Rionero in Vulture;40.9167 Várzea Nova;-11.2589 Wasserburg am Inn;48.0617 Al Ḩā’ir;25.7900 Frederiksværk;55.9667 Jucuarán;13.2544 San Juan;14.4167 Dambal;15.2960 Aguilar;37.5167 Rio Casca;-20.2258 Kalārdasht;36.5167 Severn;44.7500 Martinsville;36.6826 Mangarwāra;26.0119 Paripueira;-9.4650 Les Ponts-de-Cé;47.4244 Jobat;22.4160 Tenente Portela;-27.3708 Wickede;51.4964 Hissāramuruvani;15.7710 Dinklage;52.6667 Kaspi;41.9250 Abre Campo;-20.3008 General Viamonte;-35.0000 Jaca;42.5500 Zehak;30.8939 Costessey;52.6602 Albertirsa;47.2400 Lakkavaram;17.0628 Wake;34.8029 Tirmaigiri;16.7230 Wehr;47.6297 Bteghrîne;33.9300 Kalaiyamputtūr;10.4542 Hockessin;39.7837 Pendências;-5.2600 San Antonio Aguas Calientes;14.5333 Sirsia Hanumānganj;26.1381 Bad Salzdetfurth;52.0653 Sathmalpur;25.8749 Bichkunda;18.4000 Ŭrtaowul;41.1908 Harnes;50.4450 Muisne;0.6108 Barßel;53.1703 Irondale;33.5439 Pont-Sainte-Maxence;49.3011 Fatehābād;26.0652 Testour;36.5500 Brig-Glis;46.3167 Tenambākkam;12.8102 Butler;40.8616 Nakskov;54.8333 San Jacinto del Cauca;8.2500 Bhagwānpur Desua;25.8124 Chiampo;45.5500 Khoyniki;51.8892 Itano;34.1443 Ruza;55.7000 Lumberton;30.2562 Elavanasūr;11.7154 Amondara;39.5178 Araruna;-23.9319 San Clemente;15.7119 Glenpool;35.9488 Eatontown;40.2913 Sangar;37.1811 Jimboomba;-27.8300 Arboga;59.3939 Gulni;24.9309 Englewood;39.8643 Oboyan;51.2000 Dores do Indaiá;-19.4628 Wernau;48.6886 Akhty;41.4647 Jupiter Farms;26.9222 Inawashiro;37.5578 Capaci;38.1667 Keerbergen;51.0031 River Ridge;29.9593 Plover;44.4615 Kamalāpuram;11.7680 Nangavalli;11.7619 Tucapel;-37.2833 Pegnitz;49.7564 Marshall;39.1147 Golyshmanovo;56.3819 Tauberbischofsheim;49.6225 Calcinaia;43.6833 Bardstown;37.8175 Itapuí;-22.2333 Barsbüttel;53.5667 El Tabo;-33.4586 Strand;59.0633 Befandriana;-15.2667 King City;36.2164 Newington;38.7358 Bollnäs;61.3481 Barlinek;53.0000 Sendenhorst;51.8439 Aconibe;1.3000 Nemours;48.2686 Khombole;14.7667 Koskāpur;26.2669 Saka;34.3412 Nishon Tumani;38.6558 Wath upon Dearne;53.5022 Lewisville;36.1030 Pottsville;40.6798 Seyitgazi;39.4456 Mozarlândia;-14.7450 Santa Bárbara;-37.6706 Singhbāri;25.3946 Fåberg;61.1684 Pomfret;42.4029 Serrolândia;-11.4158 Eraniel;8.2059 Mahtha;26.5988 Espita;21.0128 Qigexingcun;42.0200 Shanyincun;37.9151 Plattling;48.7667 Pritzwalk;53.1500 Rocky Point;40.9357 Rosas;2.2667 Lakhipur;24.8000 Atlantic Beach;30.3375 Morąg;53.9167 Kalikiri;13.6333 Palos Verdes Estates;33.7872 Carapebus;-22.1869 Buchs;47.1667 Luís Antônio;-21.5550 Chüy;42.8100 Oxford;42.1286 Pandireddigūdem;16.8000 Gmunden;47.9181 Dossenheim;49.4492 Cariamanga;-4.3200 Matsushima;38.3802 Tahannawt;31.3514 Veyrier;46.1667 Paso de los Toros;-32.8112 Zelenodolsk;47.5631 Kallanai;10.0374 Crissiumal;-27.5000 Nolensville;35.9572 Dommasandra;12.8781 Carpenedolo;45.3654 Pushing;38.1833 Eiras;40.2421 Grimstad;58.3405 Yengema;8.6167 Varazze;44.3600 Santa Marcela;18.2872 Shibām;15.9269 Velen;51.8939 Soyaniquilpan;19.9892 Santa Filomena;-8.1628 Pāpampeta;14.6855 Calle Larga;-32.8831 Port Victoria;0.1000 Hopewell;40.5906 Carros;43.7725 Affoltern am Albis;47.2833 Rüti;47.2667 Mogeiro;-7.2989 Joaquín V. González;-25.0833 Tachov;49.7954 San José de Maipo;-33.6833 Chalfont Saint Peter;51.6070 El Almendro;11.6781 Ribeira Brava;32.6833 Sainte-Sophie;45.8200 Guéret;46.1706 Itagi;-14.1628 Diósd;47.4042 Roßdorf;49.8583 Peddakūrapādu;16.4833 Palombara Sabina;42.0667 Cavallino;40.3167 Elfers;28.2140 Forrest City;35.0135 Barentin;49.5444 Bag‘dod;40.4606 Yozyovon;40.6619 Rödental;50.2833 Autun;46.9511 Tōnoshō;35.8372 La Queue-en-Brie;48.7894 Farādonbeh;32.0078 North Walsham;52.8214 Katagon;6.6333 Kilgore;32.3980 Yakage;34.6275 Narhat;24.7774 Qal‘at al Maḑīq;35.4100 Fatehpur;25.2463 Dębno;52.7333 Ingeniero Maschwitz;-34.3667 Lincolnwood;42.0054 Novi Marof;46.1667 Ottersberg;53.1000 Ambodilazana;-18.1194 Kūdangulam;8.1903 Lüderitz;-26.6458 Dahu;24.3978 Sudbury;52.0417 Nova Veneza;-28.6369 Puerto Morazán;12.7669 Kamikawa;36.2139 Flitwick;52.0038 Bugugoucun;41.6904 Shīn;34.7833 Aubenas;44.6197 Český Krumlov;48.8111 Oudenbosch;51.5892 Nordestina;-10.8228 Coolidge;32.9363 Tanagura;37.0299 Shetpe;44.1667 Castiglion Fiorentino;43.3439 Tehachapi;35.1276 Itiruçu;-13.5319 Cowdenbeath;56.1100 Odder;55.9725 Kasumkent;41.6667 Kūhbil;36.5133 Tarhūnah;32.4339 Ficarazzi;38.0833 Moorreesburg;-33.1500 Porto Recanati;43.4322 Tirumakūdal Narsipur;12.2121 Hüllhorst;52.2833 Baş Göynük;41.3228 Demerval Lobão;-5.3578 Shāhpura;23.1828 Guillena;37.5333 Bombarral;39.2681 Trebaseleghe;45.5833 Ban Chomphu Nuea;16.6833 Arma;25.1966 Abrera;41.5165 Picaña;39.4361 Chiroqchi;39.0336 Al Muzayrīb;32.7109 Jitaúna;-14.0189 Engenheiro Paulo de Frontin;-22.5500 Puerto San Martín;-32.7167 Amity;40.2905 Miarinarivo;-18.9608 Harwich;41.6957 Cremlingen;52.2489 Aš;50.2239 Sítio do Mato;-13.0850 Weinsberg;49.1518 Vilāngurichchi;11.0709 Kallūr;16.1405 Sint-Michiels;51.1833 Mānākondūr;18.3981 Wolfhagen;51.3167 Fort Campbell North;36.6631 Pfarrkirchen;48.4167 Berezne;51.0000 Washington;40.1741 Reggada;29.5716 Saint-Charles-Borromée;46.0500 Capo d’Orlando;38.1500 Rabot;38.6167 Hashikami;40.4525 Fene;43.4667 Nosivka;50.9300 Bhagwatpur;25.7484 Motobu;26.6575 Glendale;43.1287 Nesconset;40.8467 Charlton;42.1351 Stolin;51.8833 Kalavai;12.7691 Zella-Mehlis;50.6597 Zandhoven;51.2167 Sweden;43.1791 Ban Chorakhe Samphan;14.3258 Brønderslev;57.2694 Ergolding;48.5833 Bocholt;51.1722 Mercedes Umaña;13.5667 Turbihāl;15.7614 Palmetto;27.5251 Portage La Prairie;49.9728 Cocotitlán;19.2167 Röthenbach an der Pegnitz;49.4847 Qal‘eh Ra’īsī;31.1900 El Tablón;1.4269 Sahri;25.4721 Usumatlán;14.9489 Kolattūr;13.3295 Beyla;8.6833 Fruita;39.1548 La Victoria;4.5214 Maní;4.8167 Borodyanka;50.6474 Franconia;40.3055 Herbrechtingen;48.6253 Lake Station;41.5729 Terebovlya;49.3000 Kiiminki;65.1333 Chợ Lách;10.2647 Seravezza;44.0000 Caçu;-18.5569 Olfen;51.7167 Los Chiles;10.9639 Rābor;29.2911 Dhanaura;25.1905 Kamianka;49.0333 Arakere;12.4133 Slobozhanske;48.5342 Sargūr;11.9997 Baruāri;26.0305 Montegranaro;43.2333 Chenggong;23.1167 Gärtringen;48.6408 Lawang Bato;14.7300 Superior;39.9340 Clinton;41.2980 Fyzabad;10.1833 Chulumani;-16.4102 Ammavārikuppam;13.1784 Calcinato;45.4581 Auburn;41.3666 Svirsk;53.0833 Harrow on the Hill;51.5655 Panjīpāra;26.1369 Mohgaon;21.6394 Haselünne;52.6667 Cairo Montenotte;44.4000 Ambazoa;-25.3139 Gaura;24.9643 Ocaña;39.9569 Sebt Labrikiyne;32.2944 Pontecorvo;41.4626 Campo de Criptana;39.4000 Sarria;42.7833 Finneytown;39.2159 Pácora;5.5258 Ambohidronono;-18.7500 Los Alamos;35.8927 Ulvila;61.4333 Shin'onsen;35.6235 Esneux;50.5333 Mahao;26.8675 Palangavāngudi;10.7244 Nawā Nagar Nizāmat;26.2895 Monteux;44.0356 Ajijic;20.3000 Skoghall;59.3333 Gvardeysk;54.6667 Barkāgaon;23.8651 Brignais;45.6739 Hardiyā;26.9443 Kalappālangulam;9.1889 San Biagio di Callalta;45.6867 Parvomay;42.1000 Smithfield;41.8349 Serra Azul;-21.3108 Manamodu;11.1965 El Kansera;34.0419 Qazmalar;40.9814 Lahaina;20.8848 Bergen;51.6000 North Tidworth;51.2370 Sillamäe;59.3931 Östringen;49.2194 Torquay;-38.3333 Alto Alegre dos Parecis;-12.1278 Damaishan;24.5038 Bridgetown;39.1552 Īnderbor;48.5500 Vohitrindry;-22.3833 Empedrado;-27.9333 Rāghopur;25.3180 Giporlos;11.1208 Douar Sidi Laaroussi;31.8450 Kachavaram;16.5700 Apricena;41.7667 Beverungen;51.6628 Sovetskoe;41.0600 Bridgnorth;52.5350 Woodinville;47.7570 Las Vegas;35.6011 Argentona;41.5558 Clayton;39.8689 Bedburg;51.7667 Great Bookham;51.2780 Castelnuovo di Verona;45.4333 Balatonfüred;46.9500 Radomir;42.5500 Morbegno;46.1333 San Jerónimo;6.4417 Chigwell;51.6225 Sabinov;49.1061 Rāpūr;14.2015 Gudgeri;15.1225 Mehsāri;25.7554 Baghauni;25.9338 Plaza Huincul;-38.9338 Ceyu;37.7939 Yamato;32.6858 Andrews;32.3207 Joure;52.9667 Rudrūr;18.6700 Lakshmīnārāyanapuram;11.7914 Blue Ash;39.2480 Guābāri;26.1511 Brenes;37.5500 Bolintin Vale;44.4472 McCalla;33.3023 Monfort Heights;39.1823 Cicuco;9.2667 Ocna Mureş;46.3900 North Greenbush;42.6706 Raffadali;37.4047 Vila Pouca de Aguiar;41.5006 Lincoln;40.1508 Chakwai;25.0543 Sankt Georgen im Schwarzwald;48.1247 Weilburg;50.4833 Vasylivka;47.4435 Asjen;34.8500 Messadine;35.7619 La Grande;45.3242 Agutaya;11.1520 Gangelt;50.9831 Auriol;43.3694 Marojala;-14.4833 Çamardı;37.8330 Sarrebourg;48.7347 Huguan Nongchang;21.2015 Dedemsvaart;52.6000 Wolvega;52.8761 Fouriesburg;-28.6227 Kanabūr;13.3523 Orocó;-8.6200 Xaafuun;10.4167 Sharon;41.2340 Jāmi;18.0458 Ansião;39.9167 Oncativo;-31.9167 Miranda do Corvo;40.1000 Baghambarpur;26.8075 Barrafranca;37.3667 Matinilla;9.9246 Maglód;47.4439 Simijaca;5.5019 Mandrāmo;24.1822 Uttoxeter;52.8980 Belëv;53.8167 Carnot-Moon;40.5187 Yzeure;46.5658 Spilamberto;44.5333 Alaverdi;41.1333 Hanımçiftliği;38.3833 Tinglayan;17.2650 Nako;-29.6481 Quzanlı;40.1600 Kotturu;17.2958 Bou Arada;36.3500 Steamboat Springs;40.4777 La Vega;4.9992 Mazatlán Villa de Flores;18.0167 Manuel Ribas;-24.5158 Pūdūru;16.1520 Krasnozavodsk;56.4333 Thames Centre;43.0300 Bagadó;5.4167 Duvvūru;14.8333 Lingamparti;17.2827 Presidente Jânio Quadros;-14.6889 Altos;-25.2333 Mounds View;45.1071 Toura;11.2436 Bolsover;53.2304 Singuilucan;19.9675 Rogers;45.1865 Somain;50.3575 Yelmalla;18.8241 Baños;-1.3964 Sevūr;12.6865 Abarán;38.2031 Seclin;50.5483 Silago;10.5291 Uyar;55.8267 Gavere;50.9333 Phalaborwa;-23.9333 Pacatuba;-10.4528 Kirano;-21.8333 Venkatagirikota;13.0000 Villa Sandino;12.0500 Bachrā;23.6886 Oftersheim;49.3706 Mīnākshipuram;9.8815 Qal‘eh Ganj;27.5236 Agdangan;13.8758 Arcachon;44.6586 Tracunhaém;-7.8050 Terrasini Favarotta;38.1500 São João do Araguaia;-5.3578 Stezzano;45.6508 Santa María de Ipire;8.6638 Vetlanda;57.4333 Velykodolynske;46.3425 Mississippi Mills;45.2167 Ambatondrakalavao;-19.4333 Güçlükonak;37.4711 Kumarkhāli;22.3598 Mavorano;-21.7833 Iona;26.5160 Teror;28.0590 Yvetot;49.6169 Begowāl;31.6125 Schleiden;50.5331 Raymond Terrace;-32.7615 Spreitenbach;47.4167 Monteprandone;42.9167 Domahani;23.7569 Sinincay;-2.8333 Stoughton;42.9237 Zabaykalsk;49.6514 Kotla;32.7522 Mazıdağı;37.4792 Azle;32.8955 Powell River;49.8353 Sete Barras;-24.3878 Adi Keyh;14.8333 Prudhoe;54.9610 Bonyhád;46.3006 Triel-sur-Seine;48.9808 Kannudaiyāmpatti;10.6377 Balya;39.7500 Gouandé;10.7828 Alovera;40.5967 Waltershausen;50.8975 South Glengarry;45.2000 Petushki;55.9333 Adygeysk;44.8832 Hadamar;50.4500 Culaba;11.6578 Mettet;50.3192 Shīnīlē;9.6667 Bargaon;25.3455 Claiborne;32.5379 Berkeley Heights;40.6764 Barsaun;25.6316 Castellbisbal;41.4767 Ganapavaram;15.9232 Betatao;-18.2000 Ganapatipālaiyam;11.0309 Mūlaikkaraippatti;8.5454 Kapelle;51.4833 Ringnod;22.6113 Ambalakindresy;-21.1667 Sali;26.9833 Chabāl Kalān;31.4818 Santa Maria a Monte;43.7000 Upperu;16.6500 Qulan;42.9100 Tapiratiba;-21.4678 Strunino;56.3667 Olivar Bajo;-34.2330 Tsiately;-23.3167 Mahaboboka;-22.9000 Belemoka;-21.7833 Springbok;-29.6667 Bad Doberan;54.1069 San Antonio del Tequendama;4.6328 North Perth;43.7300 Kaspiyskiy;45.3908 Tezontepec;19.8833 General Enrique Mosconi;-22.6000 Jennings;38.7231 Gurgunta;16.2875 Bamessing;5.9847 Piratininga;-22.4128 Pharkia;25.9384 Loveland;39.2677 Periya Pattanam;9.2726 Honggu;36.2930 Mahatsinjo;-22.8000 Neuhausen auf den Fildern;48.6844 Rosbach vor der Höhe;50.2986 Noeux-les-Mines;50.4797 Sulingen;52.6667 Sirpur;19.4833 Befeta;-21.2333 Ambodiampana;-14.5333 Anjiajia;-16.4833 Bihtā;25.5731 Wantage;51.5890 Nartan;43.5167 Mekra;25.4705 Dărmăneşti;46.3700 Sagay;9.1167 Tarnos;43.5406 Mercier;45.3200 Milhã;-5.6750 Kaufungen;51.2811 South Stormont;45.0833 Governador Celso Ramos;-27.3150 Jensen Beach;27.2437 Lokāpur;16.1656 Andranomamy;-16.6000 Leonforte;37.6417 Medway;42.1535 Ksar Sghir;35.8419 Olen;51.1500 West Monroe;32.5120 Saraiya;25.6467 Kouti;6.5542 Scheeßel;53.1706 Sakoabe;-22.2000 Moody;33.5986 Ban Bang Lamung;13.0470 Çavdır;37.1550 Kathāniān;31.6373 Ratanpur;24.8996 North Palm Beach;26.8217 Hersbruck;49.5081 Chos Malal;-37.3833 Gumani;9.4500 Vannivedu;12.9173 Song Phi Nong;14.2306 Ambohitsilaozana;-17.6833 Monte Belo;-21.3258 Pudūr;11.2960 Bagaura;26.0388 Kūshk;32.6428 Al Jumaylīyah;25.6208 Soddy-Daisy;35.2571 Atmākūr;16.3364 Semri;25.3404 Ambondrona;-21.2667 Tonoshō;34.4869 Akseki;37.0519 Glencullen;53.2230 Fannūj;26.5758 Lawaan;11.1408 Dover;40.5304 Antônio Prado;-28.8578 Macará;-4.3833 Noceto;44.8167 Bagnolo Mella;45.4333 Burgos;17.3331 Harrison;36.2438 Newton;35.6630 Maryport;54.7128 Saint-Colomban;45.7300 Grand Terrace;34.0312 Ampanefena;-13.8667 Honiton;50.8000 Colesberg;-30.7167 Treuchtlingen;48.9553 Pingtiancun;25.2225 Jacksonville;33.8088 Cusset;46.1344 Nova Londrina;-22.7658 Morarano;-18.9000 Ambodisakoana;-15.4167 Cedar Grove;40.8565 Bedford;41.3919 Claye-Souilly;48.9450 Dorandā;24.4710 Ben;32.5444 Morafeno;-21.1000 Graulhet;43.7608 Mandritsara;-19.5500 Lowshān;36.6206 Vöcklabruck;48.0086 Herzberg am Harz;51.6575 Crayford;51.4491 Chernigovka;44.3406 Arohi;23.4270 Frecheirinha;-3.7600 Gamboula;4.1333 Chinnakkāmpālaiyam;10.7064 Peißenberg;47.7950 San Lucas;13.4144 Ortaklar;37.8833 Mirabela;-16.2628 Kharar;22.7000 Annapolis Neck;38.9409 Stjørdalshalsen;63.4712 Battulapalle;14.5167 Albatera;38.1786 Hosir;23.7735 Tetiiv;49.3708 Vieira do Minho;41.6333 Lacombe;52.4683 Amersfoort;-27.0078 Ātmakūru;14.6452 Manalalondo;-19.2500 Marovandrika;-22.5167 Evato;-22.6000 Bānsbāri;26.1934 Kaji;26.0249 Kyotera;-0.6317 Alcácer do Sal;38.3725 Campiglia Marittima;43.0667 Tenmalai;9.3129 Manambolo;-15.2833 Juan de Herrera;18.8667 Sebt Bni Garfett;35.2500 West Carrollton;39.6701 Abū Dīs;31.7625 Čelákovice;50.1606 Wächtersbach;50.2547 Rochester;42.6866 Siderópolis;-28.5978 Halásztelek;47.3608 Villa Nueva;-32.9000 Amborompotsy;-24.6667 Marovato;-25.5333 Santo Stino di Livenza;45.7333 Notodden;59.6294 Boortmeerbeek;50.9833 Vikāsnagar;30.4680 Rehti;22.7378 Parigi;13.8929 Bryn;53.4990 Bemanevika;-14.1333 Pharāha;24.8522 Antsirabe Afovoany;-15.9500 Fehmarnsund;54.4454 El Marsa;36.8111 Kotra;22.7062 Ankilimalinika;-22.9500 Sandrakatsy;-16.3333 Manambidala;-22.7500 Shahr-e Herāt;30.0547 Isaka-Ivondro;-24.8000 Andreba;-14.5833 Ambararata;-15.0167 Gárdony;47.1973 La Chapelle-Saint-Luc;48.3119 Tabocas do Brejo Velho;-12.7058 Pallejá;41.4242 Mudhol;18.9667 Solofra;40.8333 Sahavalanina-Antenina;-16.5667 Ankilivalo;-20.2833 Bolongongo;-8.4667 Linnich;50.9789 Portland;36.5921 Mamborê;-24.3189 Schodack;42.5297 Mangindrano;-14.2833 Lansdowne;39.0844 Mahāgama;25.0342 Ambohimahavelona;-23.4333 Tāran;26.1570 Mineiros do Tietê;-22.4089 Amboaboa;-15.9333 Cambridge;38.5515 Ambahoabe;-16.7667 Ban Tat;17.2791 Puttānattam;10.4670 Oulad Rahmoun;32.3278 Anolaima;4.7617 Solana Beach;32.9943 Andohajango;-15.9000 North Adams;42.6844 Shiwan;37.4786 Ampitasimo;-17.7833 Ilafy;-17.8833 Morungaba;-22.8800 Kirikera;13.7690 Brieselang;52.5833 Maktar;35.8606 Tranovaho;-25.3000 Ampanavoana;-15.6667 Ambatolampy;-18.9000 Parker;34.8513 Hanumantanpatti;9.7858 Bonneville;46.0789 Antongomena-Bevary;-15.9500 Trezzo sull’Adda;45.6089 Plan-de-Cuques;43.3469 Zhdanivka;48.1500 Lauritsala;61.0708 Sunne;59.8333 Bni Khloug;32.6500 Wanderley;-12.1200 Haslev;55.3333 Ambakireny;-17.6667 Galashiels;55.6194 Bejofo;-17.8333 Chapantongo;20.2833 Kalingiyam;11.4324 Gomparou;11.3000 Kanel;15.4833 Sirūr;16.0965 Santa Luzia do Itanhy;-11.3508 Dorado;18.4657 Divnoye;45.9072 Ḩarmah;25.9261 St. Ann;38.7266 Upanema;-5.6419 Kesabpur;22.9700 Khajuri;25.9144 Maroviro;-21.2333 Chero;25.2463 Bou Adel;34.5428 Loyish Shaharchasi;39.8839 Guisser;32.7667 Amboise;47.4114 Sooke;48.3761 Green Valley;39.3414 Meadville;41.6476 Chaiyo;14.6666 Miami;36.8878 Antri;26.0581 Rozhyshche;50.9131 Dagmāra;26.3953 Oatfield;45.4125 Joppatowne;39.4181 Antigua;28.4160 Bruay-sur-l’Escaut;50.3983 Teutschenthal;51.4500 Maizières-lès-Metz;49.2122 Brzeg Dolny;51.2708 Mercogliano;40.9231 Perez;14.1833 Mariánské Lázně;49.9647 Montgomeryville;40.2502 Ifs;49.1383 Nagar;24.0914 San Juan Tecuaco;14.0836 Motkūr;17.4500 Franeker;53.2000 Piketberg;-32.9000 Borgosesia;45.7167 Conceição do Rio Verde;-21.8808 South Daytona;29.1657 Itayanagi;40.6959 Gornyak;50.9833 Dāmarcherla;16.7269 Chinna Gollapālem;16.3701 Montecorvino Rovella;40.7000 Schongau;47.8167 Licínio de Almeida;-14.6819 Nyāmti;14.1400 Pianiga;45.4583 Kent;41.4735 Dawson Creek;55.7606 Palmital;-24.8928 Karāhal;25.4909 Yavoriv;49.9469 Anderlues;50.4080 Saposoa;-6.9364 Tashi;34.4977 Trenton;39.4792 Grave;51.7667 Kashasha;-1.7578 Larkspur;37.9393 Timberlake;37.3167 Ulubey;38.4167 Arluno;45.5000 Bonita;32.6651 Kafr Buhum;35.0611 Gibraleón;37.3753 Saúde;-10.9408 Peraiyūr;9.7341 Monte San Giovanni Campano;41.6333 Syston;52.7000 Santiago;9.8291 Sante Bennūr;14.1697 Lipari;38.4667 Sun Village;34.5596 Santo Tomás La Unión;14.6333 Broussard;30.1396 Moka;15.2380 Overlea;39.3642 Cantagalo;-25.3739 Nandyālampeta;14.7218 Yuza;39.0148 Hajdúhadház;47.6833 Herk-de-Stad;50.9406 Provins;48.5589 Üllő;47.3854 Ribeirão do Pinhal;-23.4167 Claremont;43.3790 Mogocha;53.7333 Kisara;17.5233 Beelitz;52.2333 Mándra;38.0667 Kuyganyor;40.8611 Andijon;40.6444 Lucena;-6.9000 Rushall;52.6110 Rokupr;9.0167 Misaki;34.9979 Santo Niño;11.9263 Japoatã;-10.3469 Jagdīshpur;26.1526 Croxley Green;51.6470 Mablethorpe;53.3409 Sothgaon;26.6046 Balbalan;17.4436 Arês;-6.1939 Ibaretama;-4.8039 Ribeirão Bonito;-22.0669 Velten;52.6833 Mar de Espanha;-21.8669 San Pablo;1.6725 De Haan;51.2731 Hāthidāh Buzurg;25.3716 Kingsbury;43.3440 Ridge;40.9068 Vadnais Heights;45.0570 Castel Goffredo;45.2981 El Qâa;34.3436 Takamori;35.5138 Gerpinnes;50.3369 Rivarolo Canavese;45.3312 Kakching Khunou;24.4047 Santa Margarita;39.7033 Vaddāpalli;18.5324 Pell City;33.5610 Zhongling;28.9391 Lake Country;50.0833 Chamical;-30.3667 Veľký Krtíš;48.2150 Ban Laem;13.2168 Douar Oulad Youssef;32.4807 Kaviti;19.0167 Asahi;35.0343 Peligros;37.2333 Sim;54.9833 Kaeng Khoi;14.5864 Big Bear City;34.2536 Hungen;50.4667 Mādugula;17.9167 San Mateo;9.7400 Brzeziny;51.8000 Magdalena Milpas Altas;14.5453 Frutillar;-41.1258 Avrig;45.7081 Palacagüina;13.4550 Saint-André-de-Cubzac;44.9947 Niefern-Öschelbronn;48.9164 Uruoca;-3.3139 Ambalarondra;-18.4667 Milovice;50.2260 Kot Shamir;30.1191 Trent Hills;44.3142 Tomar do Geru;-11.3728 Malaryta;51.7833 Beaufort;32.4597 Möckern;52.1406 Tizgane;35.4136 Mesolóngi;38.3692 Purén;-38.0319 Āppukkudal;11.4684 Kilindoni;-7.9167 Esmoriz;40.9550 Vyazemskiy;47.5250 Pilar;10.8070 Marikal;16.6020 Kasba Tanora;32.4751 Woodhaven;42.1320 Holiday City-Berkeley;39.9639 Sainte-Marie;46.4500 Barud;21.7497 Belagal;15.8183 Sonāpur;26.3716 Valencia West;32.1355 Villeneuve-lès-Avignon;43.9664 Kuala Kurun;-1.1016 Le Chambon-Feugerolles;45.3961 Florange;49.3214 Muthuswāmipuram;9.3900 Brugg;47.4864 Gajhara;26.5237 Chodavaram;17.4380 Bassersdorf;47.4431 Ālwārkurichchi;8.7838 Siur;24.8166 Laarne;51.0167 Varzaneh;32.4197 New Hanover;40.3145 Kārvetnagar;13.4167 Strombeek-Bever;50.9100 Hemsbach;49.5903 Surkhakhi;43.1878 Garkha;25.8293 Lynnfield;42.5356 Novhorod-Siverskyi;51.9972 Saint-Jean;43.6653 Arrigorriaga;43.2078 Pebberu;16.2167 Mānegaon;23.2062 L’Isle-Adam;49.1111 Choró;-4.8428 Dharhara;25.2543 Raun;25.6462 Churchdown;51.8800 Richton Park;41.4816 Arhribs;36.8022 Lubaczów;50.1500 Bhandārso;26.1780 Grumo Appula;41.0167 Berja;36.8453 Chiatura;42.2903 Almirante;9.3000 Limburgerhof;49.4222 Cornwall;41.4195 Oxapampa;-10.5740 Lauenburg;53.3833 Voerendaal;50.8833 Partiāla;16.4537 Mora;61.0167 Rasaunk;25.5537 New Kingman-Butler;35.2645 East Hampton;41.5696 Magsaysay;10.8667 Umri;26.5106 Costa de Caparica;38.6446 Dorking;51.2325 Bohmte;52.3667 Newcastle;47.5304 El Dorado;37.8210 Dorog;47.7194 Pittalavānipālem;15.9806 Bretzfeld;49.1833 Levelland;33.5806 Chino Valley;34.7594 Guelph/Eramosa;43.6300 Puerto Jiménez;8.5338 Andernos-les-Bains;44.7425 Betma;22.6800 Zaragoza;28.4869 Weddington;35.0228 Umag;45.4333 Pastpār;25.8275 Altenstadt;50.2856 Pola de Laviana;43.2358 Bloomsburg;41.0027 Cuisnahuat;13.6333 Nong Khae;14.3352 Donwari;11.1197 Malar;26.2237 Sala Consilina;40.4000 Locri;38.2160 Samashki;43.2906 Flexeiras;-9.2728 Naduhatti;11.4043 Santa Rosa de Calamuchita;-32.0667 Florida City;25.4418 Webb City;37.1412 Méricourt;50.4022 Kulattūr;10.7052 Urbana;39.3274 Bershad;48.3728 Syasstroy;60.1500 Cheval;28.1459 M’dhilla;34.2500 Sahasmal;26.2461 Raismes;50.3892 Chettikulam;8.0943 Fuldatal;51.3484 Gohpur;26.8818 Ivaí;-25.0108 Northlake;41.9142 Jaroměř;50.3503 Rafína;38.0167 Salobreña;36.7467 Baghra;25.5597 Sebiston;38.2500 Braselton;34.1087 Holly Hill;29.2442 Chitauria;25.4869 Canet-en-Roussillon;42.7056 Attanūr;11.5006 Francofonte;37.2333 Manjanādi;12.8700 Ấp Phú Hải;11.1667 Canillá;15.1671 Bamhnī;22.4822 Truro;45.3647 Gyomaendrőd;46.9361 Bozkurt;37.8167 Steinheim;51.8658 Amos;48.5667 Kafr Rūmā;35.6342 Újfehértó;47.7989 General Las Heras;-34.9333 Playa Grande;15.9833 Barahkurwa;26.1460 Karedu;15.1833 Bānu Chhapra;26.8098 White House;36.4648 Thikriwāla;30.4328 Souakene;35.1167 Miraíma;-3.5689 Kriftel;50.0828 Mamarappatti;11.4845 Atri;42.5833 Dubak;18.1914 Perehinske;48.8103 Nova Ponte;-19.1983 Amha;26.0518 Wawarsing;41.7526 Gorokhovets;56.2000 Jilava;44.3328 Bamber Bridge;53.7281 Kortemark;51.0286 Buharkent;37.9617 Ban Sop Tia;18.3895 Pecica;46.1700 Krupka;50.6846 Si Mustapha;36.7247 Rānti;26.3519 Rosario del Tala;-32.3000 Teroual;34.6667 Fonsorbes;43.5361 Taldom;56.7333 The Nation / La Nation;45.3500 Sonosari;-7.8014 Chandwā;23.6756 Raia;15.4969 Hvardiiske;45.1142 Guymon;36.6903 Coringa;16.8000 Landeh;30.9817 Pachchaimalaiyankottai;10.2736 Karagwe;0.6769 Pichilemu;-34.3919 Harrison;39.2584 Ban Muang Ngam;7.3536 Rompicherla;16.2098 Oulad Ouchchih;35.0939 San Vicente;14.1061 Suzu;37.4363 Medleri;14.6667 Mikashevichy;52.2203 Zuvvaladinne;14.8080 Vohimarina;-21.6400 San Anselmo;37.9821 Hedensted;55.7725 Sankt Veit an der Glan;46.7667 Vinings;33.8608 Oissel;49.3419 Porecatu;-22.7558 Burj al ‘Arab;30.8489 Terrabona;12.7311 Fontanafredda;45.9667 Sławno;54.3583 Ashibetsu;43.5182 Sarahandrano;-14.7833 Novi Pazar;43.3500 Utehia;26.4800 Ikhlāspur;25.0589 Bad Urach;48.4931 San Miguel Panán;14.5333 Gaimersheim;48.8167 Vidauban;43.4272 Antanandava;-19.0833 Kishunpur;25.3272 Puliyampatti;11.6653 Le Haillan;44.8717 Greenville;40.1043 Fostoria;41.1600 Kalyazin;57.2333 Laamarna;31.8944 Santa Ana de Yacuma;-13.7444 Sangtŭda;38.0333 Simeria;45.8500 São Sebastião do Uatumã;-2.5719 Nacajuca;18.1692 Santa Fe;29.3889 San Francisco de Mostazal;-33.9799 Stupava;48.2747 Kanra;36.2430 Tacaimbó;-8.3158 Malmédy;50.4167 Diari;9.8452 Valverde del Camino;37.5667 Orangeburg;33.4928 Rognac;43.4878 Wadersloh;51.7386 Novo Lino;-8.9150 Kłobuck;50.9167 Puduvayal;10.1034 Medfield;42.1848 Enamadala;16.2281 Suamico;44.6354 Fort Lewis;47.0955 Waconia;44.8422 Oakland;41.0313 Santiago Amoltepec;16.6167 Samahuta;25.8542 Majanji;0.2408 St. Marys;41.4574 Iferhounene;36.5338 Laukaria;26.7184 Lymm;53.3834 Knowsley;53.4498 Sāvalgi;16.6710 Rantoul;40.3031 Los Hidalgos;19.7300 Oneonta;42.4551 Brock Hall;38.8604 Zag;28.0167 Tepetzintla;19.9667 Hirschaid;49.8167 Joanópolis;-22.9303 Dalby;-27.1813 Sant’Agata di Militello;38.0680 Erumaippatti;11.1467 Ingersoll;43.0392 Kumari;25.6564 Kyōtamba;35.1699 Āvadattūr;11.7014 Eufaula;31.9102 Parasurāmpūr;14.3200 Lumberton;39.9569 Rio Vermelho;-18.2939 Barka Gaon;26.1440 Dināra;25.2500 Olintla;20.1000 Muswellbrook;-32.2654 Senduriā;26.7469 Orange City;28.9348 San Salvador;-31.6167 Saint David’s;12.0444 Isāpur;25.4122 Pont-du-Château;45.7983 Oxford;41.4313 Brunsbüttel;53.8964 Lamballe;48.4686 Pīr Maker;25.9622 Warwick;-28.2152 Shirataka;38.1831 Cheney;47.4901 Bad Abbach;48.9333 Steinheim am der Murr;48.9667 Alpen;51.5750 Yemva;62.6000 Palos de la Frontera;37.2283 Baker;30.5832 D'Iberville;30.4709 Snezhnogorsk;69.2000 Beniel;38.0464 Villa Aldama;19.6506 Qiaotouba;33.8116 Port Royal;32.3557 Hattem;52.4667 Kuçovë;40.8039 Sinalunga;43.2167 Río Segundo;10.0013 Ban Bang Non;9.9923 Chakai;26.0884 Altavilla Vicentina;45.5167 Sason;38.3803 Pidigan;17.5703 Dabat;12.9842 Librazhd-Qendër;41.1969 Jefferson;34.1373 Panzgām;34.4840 Paullo;45.4167 Gundugolanu;16.7833 Betton;48.1825 Saint-Germain-lès-Arpajon;48.5953 Saint-Estève;42.7133 Brus Laguna;15.7500 Mountain Home;36.3351 Vrchlabí;50.6270 Dongyuancun;28.3190 Nemili;12.9783 Anao;15.7304 Sagopshi;43.4847 Athens;32.2041 Ələt;39.9411 Blaricum;52.2667 Itāmāti;20.1333 Made;51.6764 Ounagha;31.5333 Spondon;52.9200 Kāttukkottai;11.6026 Basbiti;26.0939 Ladenburg;49.4667 Morazán;14.9322 Vejer de la Frontera;36.2500 Ahermoumou;33.8184 Kallupatti;9.7167 Zofingen;47.2833 Chennampatti;11.7011 Pandalkudi;9.3947 Derventa;44.9800 Gundelfingen;48.0425 Khasanya;43.4333 Grand Baie;-20.0184 Lalmatie;-20.0184 Sulz am Neckar;48.3628 Graben-Neudorf;49.1592 Hobro;56.6333 Chok Chai;14.7333 Vardenis;40.1806 Bayona;42.1178 Grünwald;48.0483 Kānkon;15.0109 Makuyu;-0.9000 Challapata;-18.9000 Fernandina Beach;30.6571 Lakeland South;47.2786 Beaconsfield;51.6009 Winterberg;51.2000 Olgiate Comasco;45.7833 San Miguel de Salcedo;-1.0500 Leibnitz;46.7831 Olagadam;11.5656 Horndean;50.9136 Roding;49.2000 Fulton;38.8551 Pieve di Soligo;45.8833 Ozatlán;13.3833 Feuchtwangen;49.1667 Aït Youssef Ou Ali;31.9833 Lakeside;37.6132 Los Santos;6.9167 Dardenne Prairie;38.7565 Wickliffe;41.6072 Arroio do Tigre;-29.3328 Ban Tom Klang;19.1961 Moncks Corner;33.1730 Ataléia;-18.0439 Chettipulam;10.4743 Zawiat Moulay Brahim;31.2858 Florence;40.0977 Lienz;46.8297 Alampur;26.0251 Chāmarru;16.6500 Lexington Park;38.2528 Mountain Park;33.8458 Blaubeuren;48.4119 Orzinuovi;45.4000 Kamianka-Dniprovska;47.4922 Darfield;53.5380 Grenada;33.7816 Pionerskiy;54.9517 Kasaishi;37.2528 Bni Drar;34.8578 Yenipazar;37.8269 Lakri;26.2323 Sarjāpur;12.8600 Concepción Las Minas;14.5167 Ronchi dei Legionari;45.8333 Shilka;51.8500 Capul;12.4230 Kantang;7.4067 Cidreira;-30.1608 Chaval;-3.0339 Greetland;53.6869 Buenos Aires;-7.7258 Paradise Valley;33.5434 Karrāpur;23.9489 Sahsaul;25.7024 Heddesheim;49.5053 Eski Arab;40.3686 Amherst;41.4022 Tarwāra;26.2007 Drongen;51.0500 Mèze;43.4267 Shirguppi;16.6187 Nāttarampalli;12.5920 Devāpur;19.0443 Capitán Bado;-23.2600 Patarrá;9.8637 Penal;10.1667 Winkler;49.1817 Balpyq Bī;44.8947 Loiyo;23.7928 Trophy Club;33.0040 New Germany;-29.8000 Verde Village;34.7119 Winnetka;42.1064 Antilla;20.8411 Avigliana;45.0779 Gurramkonda;13.7833 Florham Park;40.7773 Wetaskiwin;52.9694 Mende;44.5183 Shchastia;48.7381 Villafranca de los Barros;38.5667 Cambuquira;-21.8583 El Adjiba;36.3333 Dunkirk;42.4801 Emiliano Zapata;19.6500 Chauny;49.6156 Fatehpur Shāhbāz;25.5698 Galaosiyo Shahri;39.8500 Nāysar;35.3267 Erkner;52.4167 Māndu;23.7946 Viotá;4.4381 Anzola dell’Emilia;44.5472 Baykalsk;51.5167 Koyulhisar;40.3053 Pennsville;39.6266 Chudamani;21.1379 Newington Forest;38.7370 Rielasingen-Worblingen;47.7347 Saltcoats;55.6352 Panj;37.2353 Ubud;-8.5069 Bishunpur;26.1561 Artesia;32.8497 Tagoloan;8.1092 Twistringen;52.8000 Yenkuvārigūdem;16.9989 Zaladanki;14.8843 Santa Bárbara de Pinto;9.4333 Kawamata;37.6650 Miranorte;-9.5289 Kalajoki;64.2667 Xiaping;33.4047 Pannaikkādu;10.2761 Pajo;-8.5167 Virālippatti;10.1081 Beaumont;18.4833 Kaujalgi;16.1400 Rochefort;50.1667 Junín de los Andes;-39.9167 Le Pont-de-Claix;45.1231 Zonnebeke;50.8667 Goldenrod;28.6114 Godohou;7.0333 Ulster;41.9699 Monte Compatri;41.8081 Jaciara;-15.9650 Adré;13.4667 Lana;46.6160 Mahavanona;-12.4500 Hullatti;11.4771 Justice;41.7495 Wadhraf;33.9833 Adalaj;23.1700 Cardoso Moreira;-21.4878 Cunda diá Baze;-8.9167 Martinho Campos;-19.3319 Sarıveliler;36.6964 Edemissen;52.3667 Çukurçayır;40.9667 Itiquira;-17.2089 Durmersheim;48.9383 Manpaur;26.5081 Oshikango;-17.4000 Kākalūr;13.1394 San Marino;34.1224 Cinisi;38.1667 Smyrna;39.2935 Mamakating;41.5860 San Mauro Pascoli;44.1000 Paramanandal;12.3585 Tuzdybastaū;43.3189 Paramankurichi;8.4785 Baikunthpur;24.7277 Wieringerwerf;52.8506 Brandfort;-28.7000 Kayattār;8.9469 Rong Kwang;18.3392 Duartina;-22.4144 Tundhul;23.3247 Cananéia;-25.0150 Camposampiero;45.5667 Yuzha;56.5833 Isla Ratón;5.1311 San Rafael;6.2975 Borger;35.6598 Wietmarschen;52.5331 Brunswick;42.7558 Taguaí;-23.4519 Festus;38.2194 Phulparās;26.3549 Spresiano;45.7833 Central Elgin;42.7667 San Pietro in Casale;44.7000 Gavardo;45.5875 Matsukawa;35.5972 Steenokkerzeel;50.9189 Quimperlé;47.8728 Hinundayan;10.3500 Tiszavasvári;47.9511 Provadia;43.1833 Cevicos;19.0000 Vălenii de Munte;45.1856 Goražde;43.6667 Birni;9.9892 Anklam;53.8500 Ambātturai;10.2603 Shamsābād;23.8149 Chaukhata;25.0247 Elland;53.6830 Bryne;58.7354 Casciana Terme;43.5281 Mehnatobod;40.2000 Ierápetra;35.0000 East Islip;40.7257 Paal;51.0392 Balneário do Rincão;-28.8344 Cumpăna;44.1128 Khujner;23.7860 North Valley;35.1736 Qārah;34.1542 Eral;8.6258 Neuenburg am Rhein;47.8147 Linkenheim-Hochstetten;49.1261 Maba;0.7000 Ottawa;38.5996 Norwalk;41.4895 Bānk;24.9539 Byram;32.1890 Burām;25.9725 Ongwediva;-17.7833 Anna Regina;7.2633 Barra do Ribeiro;-30.2908 Santa Ana Maya;20.0000 Buchach;49.0647 Valu lui Traian;44.1650 Nanakuli;21.3892 Tiadiaye;14.4167 Mecatlán;20.2135 Sarare;9.7839 Pyrzyce;53.1333 Iconha;-20.7928 Mattenhof;47.4870 General Acha;-37.3833 Khānda;28.9167 Berwick-Upon-Tweed;55.7692 Menfi;37.6078 Beydağ;38.0833 Payariq Shahri;39.9892 Branson;36.6509 Bhaur;26.2751 Harri;26.3238 Drouin;-38.1333 Geneva;42.8645 Sântana;46.3500 San Andrés de Llevaneras;41.5733 Russi;44.3764 Prairie Ridge;47.1438 Bishnāh;32.6106 Ponnai;13.1276 Currimao;18.0203 Kataysk;56.3000 Tezoatlán de Segura y Luna;17.6500 Bandipur;27.9381 Vohindava;-22.4167 Kongnolli;16.4200 Lake Tapps;47.2307 Elkhotovo;43.3458 Gethaura;25.6879 Sheerness;51.4410 Quirihue;-36.2833 Neerijnen;51.8333 Kelso;46.1236 Mira;40.4333 Kurhani;25.9803 Borgo San Dalmazzo;44.3333 Bandalli;12.1640 Revúca;48.6831 Lindenberg im Allgäu;47.6000 Cloquet;46.7221 Tilbury;51.4606 Brown Deer;43.1743 Reshuijie;24.4580 Leutenbach;48.8883 Vianópolis;-16.7419 Ban Krang;16.8775 Aywaille;50.4733 Gaffney;35.0743 Calanasan;18.2550 Sabugal;40.3500 Windsor Locks;41.9267 Awānkh;32.1392 Bolbec;49.5722 Benifayó;39.2847 Namchi;27.1700 Armadale;55.8978 Diamou;14.0939 Petrov Val;50.1500 Koriukivka;51.7753 Correia Pinto;-27.5850 Bou Arkoub;36.5400 Hisarcık;39.2497 Kamin-Kashyrskyi;51.6242 Preußisch Oldendorf;52.2833 Kishanganj;25.6844 Frohburg;51.0561 Gandorhun;7.5564 Vysoké Mýto;49.9533 Uppukkottai;9.9587 Alkhan-Kala;43.2586 Simrāhi;26.3135 Chichli;22.8336 Lindsay;36.2082 Lachute;45.6500 Biddupur;25.6464 Srīpur;25.5861 Koluszki;51.7500 Xavantes;-23.0389 Bhaisālotan;27.4500 Sangrām;26.3141 Mettuppālaiyam;11.4503 Huodoushancun;40.7797 Oberwil;47.5135 Ouédo-Aguéko;6.4963 Pillānallūr;11.4322 Ammāpettai;11.6197 Woolwich;39.7400 La Carlota;-33.4333 Monte Quemado;-25.8036 Melavāyi;13.9397 Enumclaw;47.2018 Nanzhai;26.6299 Lila;9.6000 Laguna Paiva;-31.3039 Pfäffikon;47.3667 Bad Windsheim;49.5000 Nihāl Singhwāla;30.5919 Ichora;19.4333 Ebersberg;48.0833 Gulshan;37.5833 Uludere;37.4460 Bickenhill;52.4390 North Smithfield;41.9727 Tendūkheda;23.1708 Gobardhanpur Kanāp;25.0682 Jaidte Lbatma;31.6806 Ugento;39.9333 Charmahīn;32.3378 Harrai;22.6143 Tamalpais-Homestead Valley;37.8793 Mendota;36.7555 Melvindale;42.2802 Béjar;40.3833 Ottappārai;11.1936 Motta Sant’Anastasia;37.5000 Minnāl;13.0744 Fort Atkinson;42.9253 Niel;51.1167 Shoeburyness;51.5316 La Motte-Servolex;45.5967 Manne Ekeli;17.7200 Wilmington;39.4362 Kameshkovo;56.3500 Merāl;24.1876 Samādh Bhai;30.5985 Viswanāthaperi;9.3359 Valmadrera;45.8463 Nossa Senhora do Livramento;-15.7750 Sawādah;28.0775 Estepa;37.2917 San Severino Marche;43.2289 Dayālpur;26.0511 Harsinghpur;26.0504 West Grey;44.1833 Dom Basílio;-13.7600 Xinyingheyan;35.3369 Boumalne;31.3738 Parksville;49.3150 Murukondapādu;15.9174 Codegua;-34.0347 Taviano;39.9833 Arumanallūr;8.3167 Sylacauga;33.1780 Dorridge;52.3720 Álamos;27.0275 Boone;42.0531 Cascades;39.0464 Kamalnagar;18.2310 Sátão;40.7333 Baūyrzhan Momyshuly;42.6189 Nandipeta;18.9622 Lodhīkheda;21.5824 Viterbo;5.0667 Vilsbiburg;48.4500 Tīkar;24.4194 Dokkum;53.3269 Rainhill;53.4157 Koilakh;26.3358 Nobeji;40.8644 Chachagüí;1.3605 Munnūru;12.8283 Datoda;22.5713 Dhaula;30.2856 Lake Norman of Catawba;35.5995 Valentim Gentil;-20.4219 Bajpe;12.9803 Massena;44.9609 Kingri;25.6326 Brzeszcze;50.0000 Deerlijk;50.8533 Lake Los Angeles;34.6097 Mortugaba;-15.0228 Shichuanxiang;34.5866 Kuhsān;34.6500 Matāla;17.8244 La Argentina;2.1961 Hasroûn;34.2419 El Tambo;1.4131 Jüterbog;51.9933 Buttāyagūdem;17.2089 Chambray-lès-Tours;47.3375 Cornedo Vicentino;45.6167 Los Bellosos;19.8333 Oulad Aïssa;30.5580 Iarinarivo;-18.9167 Cowansville;45.2000 Feliz;-29.4508 Anjuna;15.5833 Möglingen;48.8883 Bad Sassendorf;51.5831 Kīrippatti;11.5357 Finspång;58.7000 Nina Rodrigues;-3.4658 Kewanee;41.2399 Pothia;25.5413 Gamarra;8.3333 Obernai;48.4622 Shuangxianxiang;35.3300 Xiushuicun;25.1728 Lulhaul;25.8787 Fortuna;40.5862 Doddanahalli;12.3892 Xinchangcun;26.4249 Plan-les-Ouates;46.1667 Hessisch Lichtenau;51.2000 Duggirāla;16.3281 Epping;51.7004 Araçás;-12.2200 Tsuruta;40.7588 Georgetown;31.9849 Ulverston;54.1930 Eyvānekey;35.3433 Kesarimangalam;11.5423 Yangiariq;41.3628 An Phú;10.8500 Cortês;-8.4700 Monóvar;38.4369 Putussibau;0.8575 Diamniadio;14.7219 Kolanpāk;17.6942 Pecos;31.3971 Sędziszów Małopolski;50.0667 Ban Bang Yai;13.8369 Norīa;32.5210 Złocieniec;53.5269 Emstek;52.8167 Riverview;42.1723 Gladenbach;50.7681 Acalá del Río;37.5167 Troy;38.9708 Tharīke;30.8669 Candolim;15.5200 Balchik;43.4269 Refahiye;39.9011 Datiāna;25.4834 Novalukoml;54.6569 Commerce;33.9963 Vallejuelo;18.6500 Sangān;34.3986 Vaddepalli;15.9363 Huai Yot;7.7894 Kinrooi;51.1450 Sablé-sur-Sarthe;47.8400 Angalakurichchi;10.5282 Lakeland Village;33.6480 Khānjahānpur;25.6055 Al Awjām;26.5583 Hizan;38.2256 Bernalda;40.4167 Leominster;52.2282 Siki;10.1833 Guamaré;-5.0950 Gaildorf;49.0000 Sever do Vouga;40.7333 Marysville;39.1518 Xinbocun;42.3037 Mugdampalli;17.6167 Reẕvānshahr;37.5511 Dehqonobod;37.6333 Murnau am Staffelsee;47.6833 Kika;9.3000 Teningen;48.1269 Profondeville;50.3769 Romanshorn;47.5635 Oulad Fares;35.5167 Baraboo;43.4695 Kachnār;25.9699 Mesker-Yurt;43.2514 Susa;5.4528 Kálymnos;36.9900 Castelnaudary;43.3181 Santa Mariana;-23.1500 Dharmastala;12.9479 Santañy;39.3542 El Carmen;13.3500 Razlog;41.8833 Teano;41.2500 Cahokia;38.5650 Escanaba;45.7477 Neosho;36.8437 Sher Muhammadpuram;18.2997 Quinta de Tilcoco;-34.3547 Mato Verde;-15.3969 Bécancour;46.3333 Nawalpur;26.9366 Asfarvarīn;35.9333 Enkesen;51.5006 Terranuova Bracciolini;43.5531 Iaciara;-14.0958 Palmer;42.1888 Rehoboth;41.8439 Srīrāmpuram;10.4346 São Luís do Curu;-3.6700 Tring;51.7962 Teotepeque;13.5853 El Ayote;12.4972 Pipariya;25.2593 Meru;24.0097 Maqu;35.9451 Alwa Tirunagari;8.6100 Nagykáta;47.4120 Valentigney;47.4625 Obikiik;38.1586 Cacequi;-29.8839 Mays Chapel;39.4343 Doi Lo;18.4667 Ambatomainty;-20.9000 Ghambiraopet;18.3000 Shamshernagar;25.0862 Solliès-Pont;43.1900 Velair;18.0071 Pira;8.5000 Sagon;7.1500 Sarābleh;33.7667 Issum;51.5389 Bankya;42.7000 Saboyá;5.7000 Chã da Alegria;-8.0008 Josefina;8.2144 Veurne;51.0722 Taiynsha;53.8478 Caapiranga;-3.3167 Quatipuru;-0.9008 Anndevarapeta;17.0937 Saint Ives;50.2110 Valencia;10.6500 Harrison;44.1935 Dingman;41.3226 Las Parejas;-32.6833 Meerzorg;5.8072 Cookstown;54.6470 Moldova Nouă;44.7178 Chāltābāria;21.9996 Nevele;51.0333 Cuichapa;18.7667 Kingsburg;36.5244 Baozhong;23.6956 Kegeyli Shahar;42.7767 Minooka;41.4507 Hengshan;24.7100 Tucson Estates;32.1792 Juvignac;43.6131 Budakalász;47.6215 Lālsaraia;26.7388 Pößneck;50.7000 Ambinanynony;-18.6000 Kamlāpur;17.5786 Sūreshjān;32.3156 Błonie;52.2000 Harrislee;54.7972 Higashiagatsuma;36.5714 Eraclea;45.5833 Dhakaich;25.5835 São Sebastião da Grama;-21.7108 Khvalynsk;52.4833 Ilhota;-26.9000 Lizzanello;40.3051 Glyká Nerá;37.9917 Itaguara;-20.3919 Pulakurti;15.7502 Las Lomitas;-24.7072 Vedène;43.9775 Vobkent Shahri;40.0333 Bad Fallingbostel;52.8675 Jadayāmpālaiyam;11.2930 McComb;31.2442 Cruzeiro do Sul;-29.5128 Fojnica;43.9667 Herrin;37.7983 Gisors;49.2806 Rankweil;47.2667 Catarama;-1.5700 Yedtare;13.9246 North St. Paul;45.0137 Māhta;31.6647 Tabontabon;11.0333 Prieska;-29.6683 Khonobod;40.2000 Monte Sant’Angelo;41.7000 São José do Campestre;-6.3158 East Renton Highlands;47.4718 Partāp Tānr;25.8897 Huntington;53.9926 Parbata;25.3195 Bishunpur Hakīmābād;25.8453 Sânnicolau Mare;46.0636 Châteaubriant;47.7169 Gistel;51.1561 Jhāua;25.7487 Ludwigslust;53.3244 Derby;41.3265 Tüp;42.7300 Paxtaobod;40.3453 Governador Dix-Sept Rosado;-5.4589 Cesson;48.5658 Leeds;33.5436 Oulad Amrane el Mekki;35.2167 Rinconada de Malloa;-34.4464 Bāgewādi;16.2900 Haddonfield;39.8955 Upper;39.2563 Sandy;45.3988 Granarolo del l’Emilia;44.5500 Pantepec;17.1833 Poquoson;37.1318 San Antonio;10.0000 Villa Literno;41.0096 Mangalkot;23.5213 Gharbia;35.5153 Klazienaveen;52.7333 Nkheila;32.9572 Jork;53.5344 Tyngsborough;42.6662 Sala;59.9167 Ban;-8.2333 Bueu;42.3167 Monteforte Irpino;40.8928 Inverell;-29.7667 Barharia;26.3191 Maxhütte-Haidhof;49.2000 Arbeláez;4.2725 Sabaur;25.2428 G’oliblar Qishlog’i;40.4953 Gainza;13.6167 Alto do Rodrigues;-5.2878 Mixtla de Altamirano;18.6000 Cardoso;-20.0819 Doctor Phillips;28.4474 Siruvāchchūr;11.6380 Tissa;34.2833 Sarasota Springs;27.3087 Aurāhi;25.5709 Robertsville;40.3395 Occhiobello;44.9216 Shamaldy-Say;41.1972 ‘Anadān;36.2936 Bad Freienwalde;52.7856 Yakouren;36.7348 Parora;25.8022 Kladanj;44.2256 Logan;40.5263 Bind;25.3035 Sremska Kamenica;45.2206 Khem Karan;31.1600 Hnivan;49.0833 Shady Hills;28.4042 Makhmālpur;25.2870 Te Awamutu;-38.0167 Morab;15.5833 Chikni;26.0075 Bayanaūyl;50.7889 Vijayāpati;8.1913 Titisee-Neustadt;47.9122 Egelsbach;49.9694 German Flatts;42.9868 Saint-Avé;47.6867 Martuni;40.1400 Ban Tap Tao;19.7280 Sindalakkundu;10.3665 Ain Kansara;34.1500 Talen;23.5695 Soklogbo;7.6937 Foča;43.5000 Thogapalle;17.2603 Saādatpur Aguāni;25.2830 Crestwood;38.5569 Hanūr;12.0874 Sīpālakottai;9.8493 Karadge;16.4200 Kaurihār;26.9650 Barhi;26.5714 Malloussa;35.7333 Mallikkundam;11.8715 Sangalbahīta;25.3295 Wołów;51.3414 Rāsingapuram;9.9448 Palmares Paulista;-21.0828 Sengurichchi;10.3756 Mutoko;-17.4000 Jequeri;-20.4558 Robinson;31.4501 Tega Cay;35.0390 Meitingen;48.5333 Melle;51.0000 Cypress Lake;26.5392 Sonāda;27.0000 Tomball;30.0951 Bukkapatnam;14.1997 Sernovodsk;43.3117 Nurhak;37.9658 Węgrów;52.4000 Grantsville;40.6148 Hatillo de Loba;8.9586 North Castle;41.1331 Matina;-13.9089 Farciennes;50.4313 Sedriano;45.4833 Hagenow;53.4167 Udawantnagar;25.5054 La Palma;14.3167 Tittachcheri;10.8674 Anamã;-3.5800 Lake City;30.1901 Nordstemmen;52.1605 Aubière;45.7508 Mulug;18.1910 Uren;57.4500 Belūr;11.7075 Fairfax Station;38.7942 Ban Huai So Nuea;20.0536 Sant’Ambrogio di Valpolicella;45.5209 San Pedro La Laguna;14.6918 Laren;52.2500 Spilimbergo;46.1281 Mirante da Serra;-11.0297 Scotts Valley;37.0555 Bibbiena;43.7000 Bodaybo;57.8667 Falmouth;43.7476 Barberton;-25.7861 Vellār;11.8938 Teulada;38.7292 Khargrām;24.0232 Qorao‘zak;43.0275 Chainpur;25.0345 Gravenhurst;44.9167 Gatumba;-3.3333 Sākib;32.2854 Torri di Quartesolo;45.5167 Sālamedu;11.9088 Baños;-2.9000 Aj Jourf;31.4903 Tadapurambākkam;13.3205 Dalippur;25.4222 Staryye Atagi;43.1126 Lehre;52.3167 Budha Thēh;31.5177 Capdepera;39.7000 Darauli;26.0781 Concepción Batres;13.3500 Lahra Muhabbat;30.2421 Mandasa;19.0600 Gandara West;5.9500 New Mills;53.3670 Enns;48.2167 Marumori;37.9114 Opmeer;52.7000 Fakirtaki;22.3815 Agcogon;12.0667 Córdoba;9.5867 Županja;45.0700 Pargas;60.3000 Bucheya;26.3421 Tibbar;31.9697 Laichingen;48.4897 Weiz;47.2189 Sakawa;33.5008 Gulbahor;41.0747 Wells;51.2094 Kibungo;-2.1608 Tamarana;-23.7228 Saint-Saulve;50.3697 Nīdāmangalam;10.7720 Eklahra;22.2036 Ouénou;9.7870 Lissegazoun;6.6167 Stradella;45.0833 Pāta Putrela;17.0173 Rānko;25.5181 Ramabitsa;-29.7625 Moss Point;30.4241 Lagoa Dourada;-20.9139 Grootegast;53.2167 Wasilków;53.2050 Marquetalia;5.3333 Webster;29.5317 El Campo;29.2000 Weinfelden;47.5698 Tzintzuntzán;19.6283 Guntupalle;16.5681 Razua;22.0529 Zeuthen;52.3667 Teus;25.2493 Conneaut;41.9275 Beatrice;40.2736 Rottofreno;45.0579 Lauffen am Neckar;49.0833 Kenduadīh;23.7757 Prattipādu;17.2333 Leingarten;49.1500 Port Washington;43.3846 Pinneli;16.5689 Emboscada;-25.1233 Upper Uwchlan;40.0817 Ambatomasina;-18.7333 Chennūr;14.1554 Tortoreto;42.8000 Arth;47.0644 Antardipa;24.6442 Vegarai;11.0903 Burladingen;48.2903 Taurisano;39.9500 Antônio Cardoso;-12.4350 Tholey;49.4833 Ocean Pines;38.3851 Sedro-Woolley;48.5112 Essenbach;48.6167 Villa Unión;-29.3000 Maida Babhangāwān;25.4793 Sathiāla;31.5833 Tetagunta;17.3140 Amarāpuuram;14.1333 Piripá;-14.9400 Casièr;45.6500 Dharmasāgaram;17.9933 Perth East;43.4700 Strzelin;50.7833 Vecchiano;43.7833 Velddrif;-32.7833 Citlaltépec;21.3366 Götzis;47.3342 Bharwelī;21.8373 Belén;-27.6500 Kampenhout;50.9413 Alucra;40.3167 Oberschleißheim;48.2500 Morūr;11.4221 Botlikh;42.6650 Jaguapitã;-23.1128 Kathu;-27.7000 Bībīpet;18.2101 Palestina;-20.3900 Na Wa;17.4692 Market Drayton;52.9044 Darmahā;26.3663 Sonbarsa;26.8474 Khānsāhibpuram;9.6304 Phulgāchhi;26.3273 Woodward;36.4247 Boloso;2.0333 Penne;42.4500 Mirik;26.8870 Vulcăneşti;45.6833 Atripalda;40.9167 Saubara;-12.7378 Torroella de Montgrí;42.0439 Hamilton Square;40.2248 Porto Valter;-8.2689 Miller Place;40.9374 Accokeek;38.6745 Lower Pottsgrove;40.2537 Sapna;44.4917 Dumri;25.6243 Ngou;5.2000 Paulista;-6.5939 Presidencia de la Plaza;-26.9833 Fómeque;4.4847 Sokouhoué;6.9000 Dăbuleni;43.8011 Yakoma;4.0982 Laukāha;26.0336 Santa Elena;14.0833 Aleksandrovsk;59.1667 Guaraniaçu;-25.1008 Madison;38.7581 Château-Gontier;47.8286 Gopālapuram;17.1007 Diré;16.2667 Kālipatnam;16.3904 Bampūr;27.1944 Antônio Gonçalves;-10.5728 Pūvalūr;10.9003 Benoy;8.9908 Korb;48.8417 West Deer;40.6351 Parkes;-33.1330 Biknūr;18.2150 New Kensington;40.5712 Wolgast;54.0500 Pasca;4.3075 Lewisboro;41.2697 Krasnyy Yar;46.5331 Mulungu do Morro;-11.9658 Nova Trento;-27.2867 Pettaivāyttalai;10.9014 Abertillery;51.7300 Prince Rupert;54.3122 Andrelândia;-21.7400 Effingham;39.1205 Fourmies;50.0172 Ārutla;17.1346 Gundrājukuppam;13.3406 Burnham;51.5400 Jefferson Hills;40.2926 Santa Leopoldina;-20.1006 Dinkelsbühl;49.0708 Ōsako;31.4292 Ivoamba;-21.4000 Canóvanas;18.3693 Ielmo Marinho;-5.8239 Dendulūru;16.7606 Chinnatadāgam;11.0816 Rovinari;44.9125 Salgado de São Félix;-7.3569 Saint-Gaudens;43.1081 Mettlach;49.4917 Hārohalli;12.3204 Lobería;-38.1333 Piprāhi;26.5871 Puerto Nare;6.1917 Ebéjico;6.3333 Santa Cruz Michapa;13.7333 Portoferraio;42.8167 Unguía;8.0500 Saclepea;7.1167 Bellegarde-sur-Valserine;46.1075 Anjanazana;-15.3833 Pinheiro Machado;-31.5778 Jitwārpur Chauth;25.8499 Santo Domingo;12.2631 Kalaidasht;38.6333 Pacé;48.1478 Isola del Liri;41.6794 Suchanino;54.3566 Cuers;43.2375 Zhengdong;22.4871 Maromiandra;-21.6833 Neuenrade;51.2839 Appingedam;53.3167 New Britain;40.3084 Ibiraci;-20.4619 Rocca Priora;41.7833 Richland;40.2842 Lagoa do Ouro;-9.1269 Zlatograd;41.3833 Manchester;37.4902 Pedda Adsarlapalli;16.7086 Lonate Pozzolo;45.6000 Kornepādu;16.2444 Saint-Pierre-du-Perray;48.6131 As Sars;36.0833 Rijkevorsel;51.3500 Highlands;41.3601 Brattleboro;42.8619 Tiszakécske;46.9312 Coventry;41.7828 Tanakallu;13.9198 Hātod;22.7938 Villamartín;36.8667 Jerome;42.7179 Arganil;40.2180 Fuensalida;40.0500 Kozloduy;43.7833 Ban Bo Luang;18.1476 Ban Noen Phoem;17.1167 Mettingen;52.3167 Ilmajoki;62.7333 Eldorado;-23.7869 Mālthone;24.3055 Zwönitz;50.6167 Betmangala;13.0085 Alken;50.8761 Bishunpur;24.7631 Bayyanagūdem;17.1250 Unterföhring;48.1917 Vylgort;61.6275 Hirao;33.9379 Pilis;47.2844 Chebrolu;16.1967 Panhar;25.0936 Chorleywood;51.6500 Nový Bor;50.7577 Qoubaiyat;34.5683 Prévost;45.8700 Theux;50.5349 Hōdatsushimizu;36.8627 Honeygo;39.4055 Oyón;-10.6692 Little Bookham;51.2804 Waikanae;-40.8750 Liperi;62.5333 Steha;35.3460 Antanamalaza;-19.4000 Vengikkal;12.2642 Havixbeck;51.9778 Moparipālaiyam;11.1332 El Sobrante;33.8724 Sahoria Subhai;25.9028 Malhador;-10.6578 Dora;22.1221 Guebwiller;47.9075 Tranent;55.9450 Mitchellville;38.9358 Albox;37.3833 Roda;30.6820 Allūr;14.6800 Sisia;25.4539 Faradābād;23.7445 Ganapavaram;16.7000 Nußloch;49.3236 San José El Ídolo;14.4500 Beryslav;46.8333 Nova Gradiška;45.2500 Iwanai;42.9798 Planegg;48.1047 Portales;34.1754 Pasłęk;54.0500 São Francisco do Maranhão;-6.2508 Nariman;40.5972 Bad Vöslau;47.9669 Eura;61.1333 Schwieberdingen;48.8778 New Baltimore;42.6904 Jītpur;26.8149 Jericó;5.7833 Jardim do Seridó;-6.5839 Justo Daract;-33.8667 Boskovice;49.4875 Kőszeg;47.3819 Minden;32.6187 Ajjanahalli;12.0376 Kotha Gurū;30.4419 Wyndham;37.6924 Jacinto;-16.1439 Kanhāipur;25.4542 Sontha;26.1861 Bernissart;50.4833 Mayate;32.2667 Denkendorf;48.6958 Gulgam;34.5500 Basaithi;26.0284 Khamānon Kalān;30.8200 Corbas;45.6681 Tabapuã;-20.9639 Sainte-Adèle;45.9500 Ampasimbe;-16.8167 Capela do Alto Alegre;-11.6678 Halstead;51.9451 Streator;41.1245 Nosiarina;-14.2167 Malkā;32.6653 Pulsano;40.3833 Sorombo;-22.1000 Edgewood;47.2309 Babhnoul;25.3202 Berlaar;51.1167 Tábua;40.3603 Islamey;43.6756 Lewisburg;35.4510 Annappes;50.6269 Hagfors;60.0333 Belampona;-14.6833 Socuéllamos;39.2933 Mostardas;-31.1069 Choctaw;35.4802 Bajiao;27.6573 Cherlak;54.1605 Rosdorf;51.5000 Antaritarika;-25.4000 Zetel;53.4197 Lakhipur;26.3281 Bolaños de Calatrava;38.8831 Ichinomiya;35.3667 Ponte da Barca;41.8000 Middleburg;30.0502 Ambohimandroso;-21.8833 Ambalavero;-21.8000 Montrose;56.7080 Ban Pae;18.2108 Saint-Cyr-sur-Mer;43.1836 Perleberg;53.0667 Pedda Vegi;16.8095 Bramhabarada;20.6683 Stamboliyski;42.1333 Sunkarevu;16.3904 Befody;-20.7667 Kōteshwar;13.6070 Archdale;35.9032 Palos Heights;41.6637 Jhakhra;25.7528 Sogndal;61.2297 Ciudad-Rodrigo;40.5969 Curití;6.6667 Bicske;47.4907 Uzyn;49.8242 Cernay;47.8067 Rudrāngi;18.6262 Iwate;39.9728 Lauria Inferiore;40.0500 Son Servera;39.6208 Auchel;50.5083 Bømlo;59.7794 Cedarburg;43.2990 Acandí;8.5333 Freeport;10.4500 Agadi;14.8190 Sassenage;45.2050 Phước Long;9.4194 Union;38.4399 Falimāri;26.3856 Umm ar Rizam;32.5325 Palmácia;-4.1500 Caimito;8.8333 Wargal;17.7751 Manorville;40.8574 Blackfoot;43.1940 San Carlos Yautepec;16.5000 Arasūr;11.0866 Oiba;6.2667 Carmiano;40.3458 Liuba;38.1634 Gbanhi;8.4497 Ubaporanga;-19.6350 Kévé;6.4278 Omatjete;-21.0500 Nová Dubnica;48.9331 Margherita di Savoia;41.3667 Vosselaar;51.3167 Nonoai;-27.3619 Ankafina Tsarafidy;-21.2000 Warka;51.7833 Kerāi;25.7510 Susegana;45.8500 Madna;26.3963 Glen Rock;40.9601 Rio do Pires;-13.1278 Oulad Daoud;34.4058 Mādhopur Hazāri;26.2623 Abhia;25.3499 Comarnic;45.2511 Tori-Cada;6.5833 Jacala;21.0053 Priolo Gargallo;37.1667 Mmadinare;-21.8746 Oqqo‘rg‘on;40.8764 Seyyedān;30.0042 Mataili Khemchand;25.5612 Kentville;45.0775 Ryūō;35.0608 Simplício Mendes;-7.8539 Baohe;33.2033 Söderhamn;61.3000 Baxiangshan;23.7630 Wichelen;51.0000 Anorombato;-22.0167 Ampondra;-13.4167 Macedonia;41.3147 Góra Kalwaria;51.9733 Ried im Innkreis;48.2100 Çüngüş;38.2128 Gambettola;44.1167 Edlapādu;16.1686 Sam;11.0333 Grafton;43.3204 Sendrisoa;-22.0000 Panpuli;9.0214 Gonzales;30.2132 Atça;37.8833 Chevigny-Saint-Sauveur;47.3017 Xincheng;36.0311 Welver;51.6167 Wrentham;42.0513 Anjahambe;-17.3833 Erfelek;41.8833 Tummalacheruvu;16.5246 Wang Saphung;17.2995 Pedappai;12.8854 Bitetto;41.0333 Bahādarpur;21.2922 Cottonwood;34.7195 Saint-Martin-Boulogne;50.7258 Al Majma‘ah;25.9039 Snihurivka;47.0708 Tefam;5.2667 Büdelsdorf;54.3167 Dang‘ara;40.5831 Flores de Goiás;-14.4489 Neustadt;51.0239 Jādopur Shukul;26.5250 Novoīshīmskīy;53.1981 Bhānuvalli;14.4333 Odatturai;11.4577 Ronneby;56.2000 Oestrich-Winkel;50.0000 Goiatins;-7.7100 Alfonsine;44.5000 Valley Falls;41.9233 Fanambana;-13.5500 Ambohinamboarina;-21.0333 Souq Jamaa Fdalate;33.5911 Villepreux;48.8300 Centralia;38.5224 São Miguel das Matas;-13.0478 Mainvilliers;48.4531 Don Sak;9.3169 Tiruvādānai;9.7841 Mahazoarivo;-21.3667 Alavus;62.5917 Fiume Veneto;45.9333 Loano;44.1167 Garden City;43.6526 Kissing;48.3000 Vaux-le-Pénil;48.5264 Lajia;34.6818 Toropets;56.5000 Ipaumirim;-6.7900 Baitoa;19.3200 Dário Meira;-14.4358 Kuangfu;23.6351 Tlacolulan;19.6667 Castelginest;43.6936 Sewāi;23.6175 Lapinig;12.3150 Kabira;25.6897 Saharefo;-21.6667 Moreira Sales;-24.0619 Weeze;51.6267 Dąbrowa Tarnowska;50.1667 Béna;12.0804 Nedelišće;46.3833 Kountouri;10.4050 Thiers;45.8564 Ghal Kalān;30.8189 Doranāla;15.9076 Stony Brook;40.9061 West Plains;36.7377 Barai;26.3717 Ialysós;36.4167 São Pedro da Água Branca;-5.0850 Kājhi Hridenagar;25.9320 Huité;14.9175 Wildau;52.3167 Benisa;38.7145 Hardia;26.6184 Beandrarezona;-14.4833 Whitburn;55.8621 Beniaján;37.9833 Darabani;48.1864 Bhangha;25.5780 Jaitwār;24.7320 Balua Rāmpur;26.7777 Apen;53.2214 Al Fayd;30.6167 Kinattukkadavu;10.8225 Enniscorthy;52.5021 Aranda;26.0850 Bālupur;25.2611 Ambaliha;-14.4667 Villa Berthet;-27.2667 Villefranche-de-Rouergue;44.3525 Weatherford;35.5380 Anjiamangirana I;-15.1667 Upper Montclair;40.8433 Holalu;15.0200 Spearfish;44.4909 Altstätten;47.3833 Boldājī;31.9383 Farkhâna;35.2833 Villeneuve-Tolosane;43.5236 Comasagua;13.6333 Andonabe;-21.4667 Arkansas City;37.0726 Maliāl;18.7000 Osterhofen;48.7019 Jīdigunta;16.9098 Ambolomoty;-16.1667 Krosūru;16.5500 Benaguacil;39.5933 Abergele;53.2800 Southwick;54.9193 Nong Bua;15.8647 Ḩukūmatī Gīzāb;33.3813 Pornichet;47.2658 Viera East;28.2613 Chinna Orampādu;14.0613 Cave;41.8167 Galvarino;-38.4000 Guimarães;-2.1328 Gladstone;45.3864 Buqkoosaar;4.5108 Tryavna;42.8669 Totogalpa;13.5631 Bara Belun;23.4007 Urakawa;42.1684 Washington;38.6586 Maromby;-24.3500 Rājāram;18.9870 Bilovodsk;49.2076 Qorashina;38.3394 Bissorã;12.0400 Ain Beida;31.5850 Borgaro Torinese;45.1500 Bou Merdès;35.4500 Bekapaika;-16.7500 Andrembesoa;-20.1500 Betânia;-8.2767 Dakhrām;26.0542 Riviera Beach;39.1623 Ghabāghib;33.1839 Andranovao;-17.6167 Basibasy;-22.1667 Louvres;49.0439 Beauharnois;45.3200 Les Îles-de-la-Madeleine;47.3833 Coussé;6.8500 Kasaji;-10.3662 Zlaté Moravce;48.3783 Kibi;6.1667 El Carmen;8.5128 Princetown;5.9049 Itondy;-19.0500 Sassenburg;52.5167 Volosovo;59.4333 Waltikon;47.3667 Madina do Boé;11.7500 Umm Badr;14.2167 Belp;46.8914 Issoudun;46.9481 Poranga;-4.7450 Largo;38.8800 Andramy;-17.9635 Landsmeer;52.4333 Avesta;60.1456 Krasnousol’skiy;53.8947 North Middleton;40.2462 Nyírbátor;47.8333 Alegría;13.5000 Doesburg;52.0167 Patu;-6.1100 Mikkelin Maalaiskunta;61.6776 Sabnima;25.4583 Naini;25.8320 Casteldaccia;38.0500 Sárbogárd;46.8878 El Arba Des Bir Lenni;34.3272 Hildburghausen;50.4167 Matmata;34.1000 Lichtenstein;50.7564 Sajószentpéter;48.2169 Longuenesse;50.7356 Ambhua;24.5568 Rudra Nagar;24.3841 Ullūr;10.9706 Mahmūda;25.0531 Guidel;47.7906 Attippattu;13.2633 Arakvāz-e Malekshāhī;33.3828 Nueva Era;17.9167 Ichhāpur;21.1551 Erbaocun;42.9633 Geisenheim;49.9844 Wallan;-37.4167 Distracción;10.9000 Jamhor;24.8486 Great Wyrley;52.6593 Diez;50.3708 Kautālam;15.7710 Alijó;41.2761 Lower Gwynedd;40.1880 Caldogno;45.6000 Çamaş;40.9131 Cangas de Narcea;43.1714 Martinsville;40.6030 Sapahi;26.6517 Santa Flavia;38.0833 Paispamba;2.2500 Naīgarhi;24.7869 Hadim;36.9883 Beladi;13.1464 Kargıpınar;36.6667 Chaumont-Gistoux;50.6839 Mykhailivka;47.2717 North Lebanon;40.3668 Janów Lubelski;50.7167 Jiajin;25.6743 Gardone Val Trompia;45.6833 Kadalādi;12.4040 Şalpazarı;40.9422 Zārach;31.9911 Aït I’yach;32.6908 Tosashimizu;32.7833 Kāranchedu;15.8823 Raghunāthpur;26.6418 Oswaldtwistle;53.7430 Tectitán;15.3073 Serravalle Pistoiese;43.9000 Zumbagua;-0.9558 Tigzirt;36.8931 Santana do Matos;-5.9578 Levashi;42.4333 Somersworth;43.2534 São Sebastião de Lagoa de Roça;-7.0828 Spencer;42.2471 Sandy;52.1310 Sultanhanı;38.2481 Cogolin;43.2525 Periya Soragai;11.7394 Villebon-sur-Yvette;48.7000 Bad Dürrenberg;51.2833 Meghraj;23.5000 Groß-Enzersdorf;48.2000 Popovača;45.5697 Manchester;35.4630 Onet Village;44.3656 Vehkalahti;60.5756 Pratāparāmpuram;10.6741 Stuarts Draft;38.0188 Santoña;43.4414 Drolshagen;51.0333 Tizi Nisly;32.4667 Dranesville;38.9955 Zaoqiao;24.6500 Arenzano;44.4042 Brejetuba;-20.1458 Fenton;42.7994 Möhnesee;51.4958 Campobello di Mazara;37.6333 Sidi Amer El Hadi;34.7992 Saint-Jean-de-Védas;43.5764 Nāgalāpuram;13.3889 Dalmatovo;56.2667 Porto;-3.8928 Kunkalagunta;16.2969 Gamharia;25.8973 Hawaiian Paradise Park;19.5828 Camp Verde;34.5690 Daroji;15.0700 Tirupporūr;12.7259 Mudgere;13.1300 Doddappanāyakkanūr;9.9810 Guraahai;33.6449 Finale Ligure;44.1714 Mayūreswar;23.9851 Nambour;-26.6269 Wootton;52.2007 Mülheim-Kärlich;50.3869 Yangiqo‘rg‘on;41.1872 Patori;25.9665 Nārāyanavanam;13.4200 Pongode;16.9246 Myjava;48.7492 Bhado Khara;24.9567 Mamqān;37.8431 Clermont;49.3789 Zriba-Village;36.3333 Niles;41.8346 Nova Crixás;-14.0989 Bechloul;36.3167 Bhaurādah;26.2520 Halsūr;17.8600 Ikkādu;13.1724 Pozharan;42.3648 Gudibanda;13.6711 San Michele al Tagliamento;45.7636 Neuville-en-Ferrain;50.7467 Poselikha;51.9833 La Homa;26.2796 Andhana;25.2574 Aheqi;40.9372 Bom Princípio;-29.4889 Athol;42.5841 Castenedolo;45.4704 Salem;40.9049 Lachhmīpur;25.5248 Yamamoto;37.9627 Amelia;42.5500 Sixaola;9.5579 Bokākhāt;26.6402 Crowley;30.2175 Downham Market;52.6000 Mahazoarivo;-20.3833 Miesbach;47.7833 Maniago;46.1667 Torton;52.4522 Wellington North;43.9000 Nuvem;15.3174 St. Andrews;50.2700 Bakwa;26.0601 Tālsur;25.3667 Somerville;-38.2260 Burgthann;49.3563 Kagamino;35.0918 Nieuwpoort;51.1167 Ploufragan;48.4894 Péonga;10.3333 Talakād;12.1887 Lendinara;45.0850 Arkadak;51.9333 Langgöns;50.5000 Loran;33.8346 Turvo;-28.9258 Ibirá;-21.0800 La Puebla del Río;37.2667 Ekerö;59.2833 Punjai Kālāmangalam;11.2322 Jamsher;31.2700 Newport East;41.5159 Rapho;40.1576 North Union;39.9101 Nowe Miasto Lubawskie;53.4256 Eranāpuram;11.5581 Höhenkirchen-Siegertsbrunn;48.0167 Carleton Place;45.1333 Podor;16.6167 Nellipāka;17.7679 Eijsden;50.7778 Mādhopur;25.3453 Sainte-Savine;48.2947 Vendas Novas;38.6833 Giffoni Valle Piana;40.7167 Olivenza;38.6858 Randaberg;59.0017 Kać;45.3000 Kete Krachi;7.8000 Ivybridge;50.3890 Rāibāri Mahuawa;27.0980 Gbéroubouè;10.5333 Palmilla;-34.6042 Rio Paranaíba;-19.1939 Berilo;-16.9519 Crossville;35.9526 Pellezzano;40.7333 Lopon;30.6715 Ojuelos de Jalisco;21.8642 Jataìzinho;-23.2539 Dammapeta;17.2667 Dudley;42.0550 Fateha;25.6073 Tiruvennanallūr;11.8589 Kongupatti;11.8593 Green River;41.5127 Guatajiagua;13.6667 Castelló de Ampurias;42.2582 Mutia;8.4176 Nallūr;14.0871 Macerata Campania;41.0667 Ban Phan Don;17.1290 Sartana;47.1708 Novaya Lyalya;59.0667 Rangsdorf;52.2831 Ukrainsk;48.1000 Scottburgh;-30.2833 Kabīrpur;26.2661 Mazzarino;37.3000 Keolāri;22.3697 Rosaryville;38.7672 Toualet;32.7333 Itanhomi;-19.1719 Iacanga;-21.8900 Andraitx;39.5746 Agamé;6.7333 Bopfingen;48.8569 Atmākūr;18.0712 Kushmanchi;17.2263 Zaragoza;17.9487 Owk;15.2167 Āzamnagar;25.5456 Rodenbach;50.1500 Matino;40.0333 Rupenaguntla;16.3043 Santa Cruz da Baixa Verde;-7.8208 Chintalavādi;10.9511 Horodnia;51.8924 Abasolo;24.0559 Ovidiopol;46.2667 Chauki Hasan Chauki Makhdum;26.2333 Devikāpuram;12.4744 Vellavādanparappu;11.1854 Kingston;-42.9769 Azcoitia;43.1792 Alamedin;42.8900 Broome;-17.9619 Satoshō;34.5138 Bugongi;-0.6356 Ingurti;17.6695 Kurabalakota;13.6500 Gerstetten;48.6225 Coswig;51.8833 Hàng Trạm;20.3944 Oberderdingen;49.0625 Lābhgaon;25.5062 Baroni Khurd;25.6852 Kuruman;-27.4597 Aguasay;9.3203 Carmo da Cachoeira;-21.4608 Nandavaram;16.0170 Shāhpur;24.0312 Rājānagaram;17.0833 Kuchinarai;16.5318 Red Bank;35.1117 Brookhaven;31.5803 Chikni;26.0664 Seringueiras;-11.7981 Whistler;50.1208 Macajuba;-12.1358 Mương Theng;21.3869 Chilonga;-12.0244 Narasāpuram;17.1016 Labbaikkudikkādu;11.3922 Yakushima;30.3903 Lebon Régis;-26.9289 Recreo;-29.2667 Monschau;50.5600 Martinsville;39.4149 Worb;46.9306 Perches;19.5167 Wolnzach;48.6000 Santuario;5.0725 Holešov;49.3333 Pipra Naurangiā;26.8591 Cricova;47.1333 Hauzenberg;48.6500 Ziyodin Shaharchasi;40.0342 Brighton;44.1222 Tolcayuca;19.9500 Ban Wiang Phan;20.4128 Bocaina;-22.1361 Qâna;33.2092 Khimlāsa;24.2058 Casino;-28.8667 Chhātāpur;26.2197 Bhanghi;26.3612 Olivença;-9.5186 Monción;19.4167 Medina Sidonia;36.4667 Iskapālem;14.5416 Roessleville;42.6969 Scorniceşti;44.5700 Puliyara;9.0041 Kīlkottai;10.2861 Santa Ana;14.0667 Opatija;45.3333 Nāranāpuram;11.0254 Gulfport;27.7463 Paraíso do Norte;-23.2808 Dolo Bay;4.1833 Kadrābād;25.5793 Khandpara;20.2644 Tirodi;21.6852 Mömbris;50.0667 Destrehan;29.9626 Kummarapurugupālem;16.3653 On Top of the World Designated Place;29.1058 Kāla Diāra;25.5092 Moulay Driss Zerhoun;34.0542 Al Hāmah;33.5581 Hardās Bigha;25.4994 Senirkent;38.1081 Neuhausen am Rheinfall;47.6833 Septèmes-les-Vallons;43.3983 Lommedalen;59.9500 Chaplygin;53.2333 Itaipé;-17.4019 Xuân Trùng;21.0500 Jalalaqsi;3.4000 Befandefa;-22.1333 Haradok;55.4667 Nandimandalam;14.4052 Sant’Ilario d’Enza;44.7667 Überherrn;49.2500 Richland;40.6440 East Bethel;45.3557 Raghunāthpur;26.3448 Kaniyambādi;12.8118 Vilpatti;10.2672 Winfield;37.2740 Umurlu;37.8500 Santa Lucía;10.3167 Bīrpur;26.5767 Saverne;48.7414 Rewtith;26.2853 Porto-Vecchio;41.5908 Shāhpur Undi;25.6370 Castano Primo;45.5500 Codigoro;44.8333 Tifni;31.6281 Mareeba;-16.9833 Amarchinta;16.3740 Tarrytown;41.0647 Tādepalle;16.8454 Campagnano di Roma;42.1333 Cisneros;6.5383 Weston;42.3589 Sarauni Kalān;25.7579 Kadwa;25.0316 Okhargara;24.2141 Comalapa;12.2842 Oignies;50.4692 Oak Grove;33.9780 Baranivka;50.3000 Evanston;41.2602 Labin;45.0833 Venosa;40.9618 Scharbeutz;54.0214 Olaippatti;11.7676 Kantilo;20.3615 Cafayate;-26.0833 Zayukovo;43.6119 Rostam Kolā;36.6778 College;64.8694 San Pedro;17.9214 La Roche-sur-Foron;46.0669 Jagannāthpur;25.6573 Phon;15.8084 Amelia;39.0269 Endwell;42.1184 Ngerengere;-6.7500 Carnaubeira da Penha;-8.3219 Carmen;9.2289 Iguaraci;-7.8350 Oxelösund;58.6667 Kāri;24.8368 Guspini;39.5333 Half Moon Bay;37.4685 Pallipattu;13.3361 Tegueste;28.5233 Târgu Lăpuş;47.4525 Pasadena Hills;28.2881 Pampas;-12.3989 Tiny;44.6833 Lohiān;31.3156 Irupi;-20.3450 Pūngulam;12.5727 Allonnes;47.9686 Kankaanpää;61.8000 Riolândia;-19.9900 Trofarello;44.9833 Cologno al Serio;45.5833 Liman;38.8733 Jarābulus;36.8175 Lom Sak;16.7775 Fairview Shores;28.6021 Nirpur;25.7192 Sítio do Quinto;-10.3500 Moldava nad Bodvou;48.6064 Ventersburg;-28.0833 Los Alamitos;33.7971 Darwa;25.6690 Imaculada;-7.3900 Ringwood;41.1065 Putnam Valley;41.3980 Gänserndorf;48.3406 Picayune;30.5322 Courrières;50.4581 Jangy-Kyshtak;40.5500 Bilopillya;51.1474 Lieksa;63.3167 Raesfeld;51.7667 Platteville;42.7280 Elgóibar;43.2142 Darnétal;49.4447 Ponte San Pietro;45.6978 Kusugal;15.3667 Ross on Wye;51.9140 Campo Alegre;-26.1928 Óbidos;39.3581 Vlašim;49.7064 Étaples;50.5178 Bullas;38.0497 Arataca;-15.2628 Richmond;29.5824 Jacó;9.6200 Lansing;42.5667 Cisternino;40.7333 Ambatofisaka II;-20.0833 Singhāna;27.9800 Pontiac;40.8894 Kundurpi;14.2833 Brejinho;-6.1908 Ārambākkam;13.5258 Ghogaon;21.9100 Zero Branco;45.6000 Vasylkivka;48.2084 Gardnerville Ranchos;38.8957 Srikrishnapur;22.9717 Wetherby;53.9276 Sulebhāvi;15.8800 Englefield Green;51.4301 Hollabrunn;48.5667 Góra;51.6667 São João do Manhuaçu;-20.3939 Stokke;59.2400 Puente Nacional;5.8833 Dolores;17.6490 Maghra;25.1903 Lwakhakha;0.7967 Krujë;41.5000 Aesch;47.4694 Rājghāt Garail;25.7618 Pachauth;25.5760 Sinzheim;48.7619 Padiham;53.7970 Bajestān;34.5164 Ban Mae Kham Lang Wat;20.2225 Lundazi;-12.3000 Namli;23.4612 Wolmirstedt;52.2519 Carbonera;45.6833 Ballston;42.9542 Bordentown;40.1420 Ambohimahasoa;-21.1064 Briançon;44.8958 Elk City;35.3862 Kalakada;13.8167 Peiting;47.8000 River Forest;41.8950 Bāra;25.9286 Raubling;47.7881 Kopervik;59.2801 Magdagachi;53.4500 Colares;-0.9369 Saint-Cyprien;42.6181 Goasi;25.7643 Trinidad;5.4089 Vauvert;43.6933 Isaszeg;47.5333 Pimenteiras;-6.2450 Bonito de Santa Fé;-7.3128 Zhetibay;43.5942 Río Colorado;-38.9908 Markham;41.6000 Nemyriv;48.9794 Missões;-14.8839 Biri;12.6667 Querência do Norte;-23.0839 Velké Meziříčí;49.3553 Sonwān;25.6258 Orchha;25.3500 Deutschlandsberg;46.8161 Harsum;52.2054 Gräfenhainichen;51.7167 Sint Anthonis;51.6258 Nagar Nahusa;25.3959 Rengāli;21.6460 Salkhua;25.6677 Matca;45.8500 Leidschendam;52.0833 Al M’aziz;33.6667 Yamkanmardi;16.2900 Poggio a Caiano;43.8167 Telwa;26.0877 Vakhrusheve;48.1667 Varadarājampettai;11.3553 Elwood;40.8462 Portomaggiore;44.7000 Macusani;-14.0692 Walldürn;49.5831 Satyavedu;13.4370 Mierlo;51.4411 Hoeilaart;50.7667 Pike Creek Valley;39.7294 Rāyavaram;17.1830 Shanmukhasundarapuram;10.0065 Candelaria;10.4592 Musile di Piave;45.6178 Mountain Top;41.1353 Manabo;17.4331 Devanakavundanūr;11.5048 Tamezmout;30.8075 Lamosina;-21.6400 Conceição do Castelo;-20.3678 Amherst;42.8706 Durbuy;50.3522 Sääminki;61.8675 Pacoti;-4.2250 Isola della Scala;45.2692 Ghanpur;17.4989 El Ançor;35.6833 Patera;23.9960 Mangalam;12.3298 Plymouth;41.6642 Yāllūru;15.3063 Kasempa;-13.4550 Richfield;43.2372 Lower Burrell;40.5818 Ravanusa;37.2678 Masanasa;39.4083 Bistān;21.6979 Prymorsk;46.7353 Isaka;-21.1500 Altdorf;48.5667 Santa Comba Dão;40.4000 Geisenfeld;48.6667 Taormina;37.8518 Sirgora;22.2063 St. Peter;44.3295 Kamiita;34.1213 Motegi;36.5321 Sablan;16.4967 Norton;41.0294 Nossa Senhora dos Milagres;-12.8700 Heerlerbaan;50.8692 Stelle;53.3667 Dérassi;10.1667 South Yarmouth;41.6692 Somerset;37.0834 Seven Hills;41.3803 Melsele;51.2209 Rakhwāri;26.3603 Campos del Puerto;39.4306 Somavārappatti;10.6779 Ubaí;-16.2850 Mena;51.5167 Dolinsk;47.3167 Gouvêa;-18.4539 Laredo;43.4144 Befotaka;-17.0839 Oliva;-32.0333 Aniche;50.3300 Billerbeck;51.9792 Jiwachhpur;26.3120 Mitai;32.7117 Pāpireddippatti;11.9140 Koori;37.8547 Frenštát pod Radhoštěm;49.5483 Minabe;33.7725 Bucyrus;40.8054 Muzaffarnagar;29.4820 Sandy;41.1447 Gander;48.9569 East Glenville;42.8648 Bālakrishnanpatti;11.2511 Ovada;44.6392 Mūdashedde;12.9300 Hanover;40.6668 Chak Thāt;25.7347 Jigani;12.7861 Ostrhauderfehn;53.1167 Eningen unter Achalm;48.4831 Naryn;41.1306 Sunninghill;51.4025 Sultānpur;23.1381 Āhiro;24.9192 Caorle;45.6000 Mendota Heights;44.8815 Hinwil;47.3033 Schiller Park;41.9586 Douglas;31.5065 Santa Clara;44.1154 Bytča;49.2242 Chantilly;49.1869 Yirol;6.5600 Tāzhakudi;8.2348 Rājpur Kalān;25.6792 Udachnyy;66.4000 Guaiçara;-21.6219 Parvatgiri;17.7417 Bad Nenndorf;52.3369 Tulshia;26.3468 Capela de Santana;-29.7000 Lawrenceburg;38.0332 Murapāka;18.2282 Yercaud;11.7794 Shuinancun;23.2995 Myrtle Grove;34.1230 Pīr Bakrān;32.4689 La Calamine;50.7000 Pepperell;42.6713 Loutráki;37.9750 Golet;19.2390 Surinam;-20.5139 Diao’ecun;40.7227 Xiaoba;26.7217 Tlachichilco;20.6217 Lyuban;52.7819 Schwaigern;49.1333 De Pinte;51.0000 Rosario de Mora;13.5833 Port Morant;17.9000 Tārar;25.1821 Sidi Tabet;36.9139 Yelandūr;12.0700 Saint Helena Bay;-32.7583 Cocentaina;38.7450 Esperanza;22.4472 Vernouillet;48.9722 Eureka;38.5004 Huandacareo;19.9906 Alburquerque;9.6104 Halavāgalu;14.7083 Milattūr;10.8576 Jódar;37.8333 Hallbergmoos;48.3333 Rudersberg;48.8856 Hidaka;42.4803 Mestrino;45.4500 Rothesay;45.3831 Mangabe;-16.9500 Radnevo;42.3000 Elma;42.8231 Singapperumālkovil;12.7595 Tonse West;13.3968 Ionia;42.9773 Fatehpur;25.3032 Port Lavaca;28.6181 Fortaleza dos Nogueiras;-6.9639 Kamānpur;18.6667 Ban Mae Tuen;18.0100 Trecastagni;37.6167 Little Chute;44.2906 Poulsbo;47.7417 Kuřim;49.2985 Oulad Friha;32.6108 Franklin;39.5538 Bonate di Sopra;45.6833 Center;40.6483 Lardero;42.4261 Annoeullin;50.5294 Sagada;17.0842 Drawsko Pomorskie;53.5333 Mala Vyska;48.6500 Auhar Sheikh;26.7216 Liberty Lake;47.6686 Mistelbach;48.5667 Tudela;10.6360 Sidi Moussa Ben Ali;33.5594 Ross-Bétio;16.2667 Brock;44.3167 Hipperholme;53.7258 Carver;41.8739 Kalmiuske;47.6667 Nzeto;-7.2290 Okpo;18.1167 Santa Cruz Itundujia;16.8667 Jamaat Shaim;32.3500 Sahuria;25.8197 Ban Bang Toei;14.0656 Videle;44.2833 Anatolí;39.6386 Khair Khān;26.2727 Motīpur;25.8159 Dautphe;50.8583 Lenvik;69.3836 Bellmawr;39.8666 Dumri;25.5263 Jaqueira;-8.7269 Möhlin;47.5583 Quebrangulo;-9.3189 Asbury Lake;30.0472 Spring Lake;35.1843 Montegrotto Terme;45.3333 Santo Antônio do Jacinto;-16.5339 Lunenburg;42.5897 Dodvad;15.7900 Chikkārampālaiyam;11.2416 Calheta;32.7258 Show Low;34.2671 Winton;37.3854 Warden;-27.8539 Camisano Vicentino;45.5167 Hlaingbwe;17.1333 Staden;50.9750 Shāhzādpur;25.6541 Oñate;43.0333 East Greenwich;39.7903 Schöningen;52.1333 Chegem Vtoroy;43.5667 Fuente de Oro;3.4667 Ulricehamn;57.7833 Sidi Kasem;35.5339 Derdara;35.1103 Qaşr-e Qand;26.2483 Gravelines;50.9864 James Island;32.7353 La Unión;8.8606 Lézignan-Corbières;43.2006 Summerland;49.6006 Den Chai;17.9835 Valpovo;45.6667 Ihumwa;-6.1667 Big Lake;45.3417 Rāmbilli;17.4644 Cadale;2.7500 Hisar;26.5495 Hanover;43.7156 Sīrpanandal;11.9741 Santa Lucía;-28.9833 Selsey;50.7350 Krynica;49.4117 Batemans Bay;-35.7081 Likhoslavl;57.1333 Firminópolis;-16.5819 Marktheidenfeld;49.8500 Boscotrecase;40.7833 Gelves;37.3333 Kem;64.9500 Ban Saeo;20.2158 Anuppampattu;13.3018 Nauheim;49.9447 Baghānt;26.1748 Bela;24.9689 Shengping;28.4865 Ingelmunster;50.9208 Hexham;54.9710 Kurgunta;17.2000 Mortād;18.8167 Bāra Khurd;25.2599 Okuizumo;35.1973 Khorramābād;36.7828 Uchchangidurgam;14.5614 Sangrāmpur;25.0711 Temamatla;19.2028 Svidník;49.3056 Barahbatta;25.7727 Yellayapālem;14.5378 Sursee;47.1667 ’Aïn Roua;36.3344 Dzouz;31.8900 Thanh Xuân;10.2308 Timahdit;33.2369 Săcueni;47.3525 De Panne;51.1019 Konanūr;12.6333 Padre Burgos;10.0333 Ghinda’e;15.4500 La Riche;47.3892 Caraguatay;-25.2333 Kuppachchipālaiyam;11.0273 Nāgojanahalli;12.3570 Tadley;51.3506 Gandhāri;18.3932 Mexico;39.1625 Ouro Branco;-9.1667 Bannewitz;50.9931 Usuppūr;11.3815 Siniscola;40.5743 Muroto-misakicho;33.2900 Odugattūr;12.7679 Stollberg;50.7083 Emmaus;40.5352 Rāmchandrapur;22.9000 Damonojodi;18.7632 Epazoyucan;20.0177 Risch;47.1411 Werther;52.0750 Ātharga;16.9875 Bhagirathpur;24.0912 Sesto Calende;45.7333 Cranleigh;51.1363 Konakondla;15.1053 Rutesheim;48.8097 Huanian;24.0781 Simri;26.3825 Santa Genoveva de Docordó;4.2586 Mango;27.9914 Val-des-Monts;45.6500 Kodmiāl;18.6333 Adendorf;53.2833 Irmo;34.1018 Rounia;25.5179 Kuttappatti;11.7939 Gympie;-26.1900 Recke;52.3700 Ézanville;49.0278 Khawaspur;26.2331 Vineyard;40.3059 Comala;19.3208 Pfastatt;47.7689 Leatherhead;51.2950 Konganāpuram;11.5710 Schönaich;48.6569 Mallagunta;12.6343 Trostberg an der Alz;48.0167 Sheron;30.1582 Koch;24.9259 Bharhopur;25.9488 Elizabethtown;40.1533 Patterson;41.4849 Navarro;-35.0167 Greensburg;39.3518 Bek-Abad;40.8472 Babhniyāwān;25.4943 Riesi;37.2833 Tawnza;32.0944 Masakkavundanchettipālaiyam;11.1473 Bora;23.6585 Middle Valley;35.1877 Yuasa;34.0294 Bouabout;31.2667 Gustavsberg;59.3333 Taché;49.7081 Kaniyūr;11.0930 Luathaha;26.6316 Chettimangurichchi;11.6393 Phanat Nikhom;13.4458 Satuek;15.3008 Grobbendonk;51.2000 Puerto Suárez;-18.9667 Severnyy;67.6083 Itārhi;25.4832 Angicos;-5.6658 Salaya;13.8023 Barni;25.3484 Shankarpalli;17.4523 Whitchurch;51.4064 Pāta Ellamilli;16.8473 Maxéville;48.7114 Estiva Gerbi;-22.2708 Kovūrupalli;14.7772 Kirchhundem;51.1000 Kadūr Sāhib;31.4239 Lantana;33.0926 Douar Trougout;35.1800 Chassieu;45.7444 Melito di Porto Salvo;37.9167 Bad Schwalbach;50.1333 Ciechocinek;52.8833 Mogilno;52.6500 Ferreiros;-7.4478 Wittingen;52.7167 Valdivia;7.2890 Esil;51.9556 Tagami;37.6988 Sevilla;9.7000 Bārīgarh;25.2325 Narasingam;9.9658 Oskaloosa;41.2922 Miami Shores;25.8670 Maurilândia;-17.9708 Omaruru;-21.4333 Nettanige;12.6101 Kin;26.4562 Rheinau;48.6678 Kenzhe;43.4911 Riorges;46.0428 Cadolzburg;49.4500 Malkanūr;18.0821 Diamond Springs;38.6920 Pedro Afonso;-8.9678 Nyazepetrovsk;56.0500 Kaboua;8.2500 Cholpon-Ata;42.6500 Wambrechies;50.6853 Nembro;45.7439 Kamalāpuram;18.2925 Hasbergen;52.2167 Masinigudi;11.5683 Mutlūru;16.1500 Higashiizu;34.7667 Alto Garças;-16.9439 Wakefield;38.8230 Chintakunta;14.6466 Sebastião Laranjeiras;-14.5728 Plankstadt;49.3933 Bochaha;25.5675 Matulji;45.3667 Bhattiprolu;16.1000 Kulgo;24.0154 Buzhum;40.0100 Bondoufle;48.6133 Bath;42.3219 Norfolk;42.1163 Sant Joan de Vilatorrada;41.7456 Sonbāri;24.2286 Utiel;39.5672 Kuriyama;43.0563 Castelvetro di Modena;44.5000 Grosse Pointe Park;42.3794 Saint-Hilaire-de-Riez;46.7211 Moyamba;8.1606 Villerupt;49.4697 Box Elder;44.1120 Chorrochó;-8.9800 San Carlos de Guaroa;3.7111 Areal;-22.2308 Morgan City;29.7041 Fatehpur;24.6297 Pirangi;-21.0914 Yuncos;40.0833 San Lázaro;-22.1083 Zvenigovo;55.9833 Ilicínia;-20.9358 Campo do Meio;-21.1069 Montopoli in Val d’Arno;43.6667 Panazol;45.8389 New Baltimore;38.7495 Lokhvytsya;50.3610 Dhorgaon;25.9545 Belém de Maria;-8.6139 Guayabal;4.9633 Lantana;26.5834 Kapaa;22.0910 Karlsdorf-Neuthard;49.1364 Cherdakly;54.3594 Welzheim;48.8747 Lakeville;41.8310 Myronivka;49.6500 Târgu Ocna;46.2800 Rothenburg ob der Tauber;49.3833 Coronel Dorrego;-38.7000 Dobbs Ferry;41.0127 Holíč;48.8122 Newtown;52.5132 Māshyāl;17.3226 Scalea;39.8167 Uničov;49.7709 Zierikzee;51.6497 Dammartin-en-Goële;49.0542 Balve;51.3333 Sidéradougou;10.6876 Lençóis;-12.5628 Beclean;47.1797 Divinolândia;-21.6614 Yakymivka;46.6977 Minnehaha;45.6577 Lurate Caccivio;45.7667 Gohuma Bairia;26.3938 Vermillion;42.7811 Crikvenica;45.1833 Lutry;46.5000 Gig Harbor;47.3352 Lovington;32.9128 Poldasht;39.3475 Leon Valley;29.4954 Burhia Dhanghatta;25.9004 Sociedad;13.7000 Māli;25.1269 Paceco;37.9833 Lenzburg;47.3833 Amnéville;49.2608 Buriti do Tocantins;-5.3158 Dalāwarpur;26.3466 Mānikpur;25.3129 Muquém de São Francisco;-12.0650 Kīlrājakularāman;9.3969 Cerese;45.0500 El Khemis des Beni Chegdal;32.4441 Er Regueb;34.8667 Tibau do Sul;-6.1869 Jādupatti;26.1186 Konen Agrahār;12.8574 Aginskoye;51.1031 Baldock;51.9900 Ochsenfurt;49.6500 Lahfayr;30.5700 Ain Legdah;34.1667 Florennes;50.2514 Waupun;43.6314 Sonakhal;22.2213 Medulla;27.9570 Domažlice;49.4406 Malapannanagudi;15.2800 Bom Retiro do Sul;-29.6089 Salisbury;40.0380 Montmagny;46.9833 Cornate d’Adda;45.6500 Terku Valliyūr;8.3570 Kostinbrod;42.8167 Amjhār;25.0641 Ahlaf;33.2833 Klyetsk;53.0636 Chkalovsk;56.7667 Bátonyterenye;47.9906 Tinogasta;-28.0667 Yorktown;40.1830 Xambioá;-6.4108 Varzobkala;38.7667 Jem’at Oulad ’Abbou;33.1156 Fort Morgan;40.2537 Nekarikallu;16.3833 Avigliano;40.7314 Loyalsock;41.2743 Farmington;42.4614 Orono;44.8867 Senador José Porfírio;-2.5908 Antônio Carlos;-21.3178 Paduma;26.5766 Ortakent;37.1035 Kuzuculu;36.8833 Dinard;48.6325 Tāmba;17.0001 Prien am Chiemsee;47.8560 Gayāspur;25.9217 Lajosmizse;47.0264 Ridgefield;40.8313 Naliya;23.2611 Gracemere;-23.4391 Sales Oliveira;-20.7719 Raghunāthpur;26.0019 Rāmanāyakkanpālaiyam;11.6291 Ḑurumā;24.6000 Calatrava;12.6167 Retie;51.2667 Stafford;41.9876 Gerasdorf bei Wien;48.2950 Byerazino;53.8333 Herbolzheim;48.2219 Abjīj;29.2861 Egersund;58.4497 Pueblo Viejo;18.4000 Kyjov;49.0102 Chandreru;16.8200 Mancha Real;37.7864 Ravutulapūdi;17.3833 Māngobandar;24.8162 Kennebunk;43.3972 Guinagourou;9.5667 Ontario;44.0259 New Garden;39.8119 Amdel;31.5617 Komorniki;52.2667 Baragaon;25.1348 Axixá;-2.8369 Hartsville;36.3921 Great Harwood;53.7860 Piazzola sul Brenta;45.5333 Grafton;-29.6833 Dharmavaram;17.2100 Fulton;43.3171 Hejiaji;37.3539 Peddapādu;16.6408 Lanham;38.9620 Lawrenceburg;35.2497 Griswold;41.5852 Kanamadi;16.8300 Maltby;47.8027 Tinkoni;26.8613 Resende;41.1060 Oud-Heverlee;50.8333 Écaussinnes-Lalaing;50.5667 Saint-Junien;45.8872 Būdili;13.9353 Pātrasāer;23.1968 Nelson Bay;-32.7150 Perali;15.8860 Moss Bluff;30.3039 Mack;39.1492 Seneffe;50.5333 Roma;26.4166 Tucson Mountains;32.2822 Santa Rosa de Viterbo;5.8833 Venafro;41.4844 La Maddalena;41.2167 Fagundes;-7.3550 Canton;44.5802 Douar Oulad Naoual;34.4936 Belén;-23.4660 Laqraqra;32.4333 Botticino Sera;45.5333 Eidsberg;59.5369 La Libertad;12.2156 Kalavapūdi;16.4623 Virton;49.5675 Kannūlu;12.8685 Nikel;69.4081 Port Salerno;27.1461 Nueva Helvecia;-34.2833 Körmend;47.0110 Town and Country;38.6317 Lafrayta;31.9167 Czarnków;52.9000 Healdsburg;38.6224 Erin;43.7667 Jimaní;18.4900 Palukudoddi;15.8610 Pedro de Toledo;-24.2750 Gloucester City;39.8924 Aratuba;-4.4178 Oraviţa;45.0403 Mărăşeşti;45.8800 Hato Corozal;3.1833 Douar Sgarta;32.1667 San José de Guaribe;9.7930 Hejamādi;13.1062 Puerto Rico;2.9383 Valley Center;33.2330 Zaō;38.0981 Flawil;47.4053 Boldeşti-Scăeni;45.0300 Barbosa Ferraz;-24.0300 Dagbé;6.5667 Dhilwān Kalān;30.5718 Kall;50.5497 Douchy-les-Mines;50.3014 Barbadanes;42.3003 Hatwāns;22.7683 Solymár;47.5910 Lamsabih;32.2933 Creazzo;45.5333 Telkap;24.7048 Gentio do Ouro;-11.4289 Pottireddippatti;11.1659 Jhundo;24.7756 Sorsk;54.0333 Yuryuzan;54.8667 Kastav;45.3750 Kochgāwān;25.0431 Teixeiras;-20.6508 Ghatāwān;25.0968 Gararu;-9.9678 Cazzago San Martino;45.5817 Coveñas;9.4167 Galsi;23.3427 Kawadgaon;17.9100 Portes-lès-Valence;44.8733 Abaiara;-7.3589 Waidhofen an der Ybbs;47.9596 Musāpur;25.6821 Bridgeton;38.7673 Khurān Milik;25.6000 Alassio;44.0000 Pāma;25.8122 Sào Amaro das Brotas;-10.7889 Ghagga;30.0198 Dūrpalli;18.5967 Vieux-Condé;50.4594 Kattāri;11.4593 Lumaco;-38.1500 Tiruvalam;12.9825 Zirə;40.3636 Snovsk;51.8203 Guachetá;5.3856 Tiruppālaikudi;9.5461 Suhr;47.3747 Charne;26.1185 Lijiacha;37.2467 Zambrano;9.7500 Pālakollu;16.5160 Sárospatak;48.3190 Alcântaras;-3.5889 Heule;50.8333 Lystrup;56.2353 Lasht-e Neshā;37.3661 Yelpur;18.7651 Chekmagush;55.1411 Sidhap Kalān;26.5456 Sussex;43.1346 Babayevo;59.3833 Telsang;16.7200 Maheshrām;25.2874 Cividale del Friuli;46.1000 Handewitt;54.7667 Grigny;45.6083 Dhanwār;24.4107 Moorslede;50.8906 Talaigua Nuevo;9.3069 Kingsnorth;51.1178 Loreto;-7.0839 Stannington;53.3960 Athy;52.9920 Kuttattuppatti;10.3759 Tilmi;31.8189 Hillsborough;37.5572 Băcioi;46.9122 Kincardine;44.1667 Lauingen;48.5667 Karukkalvādi;11.6714 Drezna;55.7453 Bitritto;41.0500 Lieshout;51.5194 Onoto;9.5958 Khaira;24.8727 Kochkor;42.2158 Saharbani;25.7201 East Grand Rapids;42.9464 Rio Novo do Sul;-20.8628 Elchūru;16.0813 Westerkappeln;52.3153 Ichikai;36.5431 Sankt Johann im Pongau;47.3500 El Ghourdane;32.3400 Gornozavodsk;58.3667 Canápolis;-18.7250 Călan;45.7361 Kiwoko;0.8442 Commune Sidi Youssef Ben Ahmed;33.7861 Santiago del Teide;28.2957 Ekma;26.0541 Lang Suan;9.9500 Catskill;42.2063 Humahuaca;-23.2000 Oxted;51.2570 Passy;45.9236 San José del Fragua;1.3286 Vila Franca do Campo;37.7167 Yangirabot;40.0253 Woods Cross;40.8731 Caotan;36.2501 Cepagatti;42.3667 Thevūr;11.5240 Anguera;-12.1508 Punjai Turaiyāmpālaiyam;11.5142 Ravenna;41.1612 Nalgora;22.0346 Snyder;32.7133 Burrel;41.6083 Gualcince;14.1167 Passa e Fica;-6.4358 Kiáto;38.0102 Herrsching am Ammersee;48.0000 Ibicuitinga;-4.9739 Arslanbob;41.3333 Sūlibele;13.1667 Hanover;41.2012 Ouaklim Oukider;31.4500 Ipaporanga;-4.9000 Makoua;-0.0047 Ratauli;26.1861 Hormigueros;18.1437 Sidi Abdellah Ben Taazizt;34.0019 Amatenango del Valle;16.5167 Langarivo;-14.6000 Jeumont;50.2944 Sabana Yegua;18.7200 Tortolì;39.9333 Bardipuram;18.6431 Ostbevern;52.0389 Champua;22.0667 San Giustino;43.5500 Santa Juliana;-19.3089 Yanahuanca;-10.4914 Rostraver;40.1690 Barāhi;25.9725 Charouine;29.0167 Miechów;50.3578 Palmerston;53.3500 Bang Ban;14.4247 Tsqaltubo;42.3264 Usmate Velate;45.6500 Yargatti;15.9667 Aramari;-12.0819 Ermenek;36.6389 Touama;31.5339 Gurwaliā Biswās;26.8372 Capitán Sarmiento;-34.1667 Lindon;40.3414 Floresta Azul;-14.8600 Le Luc;43.3947 Campbellsville;37.3445 Besalampy;-16.7500 Montanhas;-6.4858 Eichenzell;50.4934 Holbrook;42.1471 Woodmere;29.8493 Sebba;13.4333 Petal;31.3477 Karebilachi;14.1449 Iuiú;-14.4139 La Chapelle-Saint-Mesmin;47.8897 Nandasmo;11.9247 Chirak;30.7206 El Quisco;-33.3913 Tirumayam;10.2449 Tarare;45.8961 Konnūr;15.8595 Sankhavaram;17.2704 Rāmchandarpur;25.2365 Rumburk;50.9516 Søgne;58.0942 Ichinohe;40.2129 Ambotaka;-21.7500 Allāhdurg;17.9667 Acate;37.0339 Paramoti;-4.0969 Mudki;30.7800 Okmulgee;35.6134 Mülsen;50.7447 Chiyoda;36.2178 Satai;24.7220 Balsa Nova;-25.5839 Cislago;45.6500 Krosno Odrzańskie;52.0333 Kasba;25.5856 Shiloh;39.8159 Sant’Agata de’ Goti;41.0893 Lakeland Highlands;27.9572 Ūttukkuli;11.1689 Pālamedu;10.1050 Vadamugam Vellodu;11.2366 Talupula;14.2500 Sendurai;11.2530 Lansing;39.2428 Chittārkottal;9.4276 Burtonwood;53.4302 Parwāha;26.2336 Ollerton;53.2000 Eisenberg;50.9667 Honmachi;43.9115 Vargaūr;11.1452 Bailin;33.4850 Cocoa Beach;28.3327 Chantepie;48.0886 Golfito;8.6526 Rolla;13.8331 Honwāda;16.8111 Bom Jesus;-28.6678 Abbeville;29.9751 Gloucester Point;37.2767 Bollullos de la Mitación;37.3333 Kpandae;8.4700 Rejiche;35.4667 Montlouis-sur-Loire;47.3883 Illintsi;49.1000 Tyāmagondal;13.2137 Enebakk;59.7639 Inhassoro;-21.5333 Nhandeara;-20.6939 Mpraeso;6.5800 Barahpur;25.4192 Imbaú;-24.4450 Le Pradet;43.1056 Devgeri;14.8512 Svitlodarsk;48.4358 Chāoke;30.1847 Billdal;57.5833 Cristais;-20.8758 Getulina;-21.7986 Thouaré-sur-Loire;47.2689 Antsambalahy;-14.7167 Kaset Wisai;15.6556 Hullahalli;12.1000 Tysvær;59.3617 Ban Kat;18.1764 Pedersöre;63.6642 Shenfield;51.6297 Masdi;25.2441 Poção;-8.1858 Wālūr;19.4872 Castle Pines;39.4625 Monmouth;51.8100 Pattanam;11.4728 Derecik;37.0830 Rosario;12.5167 Iazizatene;35.2544 Spencer;43.1468 Brooklyn;41.4349 Cabanillas del Campo;40.6383 Chota Mollakhāli;22.2177 Pareo;25.5582 Cabestany;42.6806 El Refugio;13.9750 Las Charcas;18.4500 Areiópolis;-22.6681 Sakkamapatti;9.9250 Boguchar;49.9500 Progress;40.2901 Uropá;-11.1406 Arvand Kenār;29.9789 Arvand Kenār;30.0617 Cleveland;33.7440 Wanaque;41.0440 Apt;43.8761 Derbisek;41.5608 Iver;51.5210 Uraí;-23.1978 Aukštieji Paneriai;54.6000 Garrucha;37.1842 Piên;-26.0978 Sant’Antìoco;39.0664 Kleinblittersdorf;49.1583 North Dundas;45.0833 Arouca;10.6333 Meulebeke;50.9497 Itikalapalle;14.6013 Aulla;44.2167 Boekel;51.6000 El Playón;7.4767 Maraial;-8.8028 Two Rivers;44.1565 Enfida;36.1353 Marcali;46.5858 São Domingos;-13.3978 Burley;42.5379 Edéia;-17.3389 Križevci;46.0258 Belmonte Mezzagno;38.0500 Lowes Island;39.0471 Drochtersen;53.7000 Poko;3.1500 Nieder-Olm;49.9083 Hamilton;39.9432 Melres;41.0667 Fiľakovo;48.2683 Sonseca;39.7000 Ganga Sāgar;21.6571 Zaouiet Says;32.7931 Douar Ain Maatouf;34.4352 Tetyushi;54.9333 Westwood;40.9878 Joquicingo;19.0556 Mineral de Angangueo;19.6206 Largs;55.7950 Capilla del Monte;-30.8500 Pasaco;13.9789 Santa Cruz;-0.5333 Kowdalli;12.0670 Edd;13.9333 Celebration;28.3102 Bānāvar;13.4167 Madhuban;25.8948 Laurel;27.1507 Feldkirchen-Westerham;47.9000 Kamargani;22.5475 Errahalli;12.3911 Monaragala;6.8726 Drākshārāma;16.7928 Erraguntla;15.2821 Barton upon Humber;53.6833 Wellesley;43.5500 Meiwa;36.2113 Lubawa;53.5000 Moyuta;14.0381 Engen;47.8528 Norwell;42.1608 Estevan;49.1392 Pūdimadaka;17.5000 Duraiswāmipuram;9.4233 Raibhīr;25.9978 Jeseník;50.2297 Bewdley;52.3758 Santa Clara La Laguna;14.7167 Potukonda;17.2708 Santa Cruz Balanyá;14.6869 Quintanar de la Orden;39.5906 Douar Jwalla;31.8900 Polistena;38.4000 Groton;42.6137 Alpu;39.7667 Pottipuram;9.9709 Sierra Madre;34.1687 Fort Meade;39.1061 Beaumont-sur-Oise;49.1425 Saddlebrooke;32.5576 Štúrovo;47.7992 Lorch;48.7983 Nova Laranjeiras;-25.3069 Glückstadt;53.7917 Kirchseeon;48.0731 Milford;38.9091 North Saanich;48.6142 Kapasiāwān;25.2783 Inhangapi;-1.4300 Linluo;22.6506 Otar;43.5375 Sokhodewara;24.8358 Sanjiaocheng;36.8993 Mishrikot;15.2465 Saltsjöbaden;59.2861 Paināl;25.5900 Sahidganj;25.6627 Friedrichsthal;49.3256 Vire;48.8386 Therwil;47.4997 Chicholi;22.0100 Quincy-sous-Sénart;48.6711 Korablino;53.9167 Thạnh Phú;9.9539 Kusumha;25.1859 Los Almácigos;19.4083 Chamestān;36.4772 Xintianfeng;24.4575 Lake Elmo;44.9944 Goldach;47.4831 Lavandevīl;38.3089 Bataiporã;-22.2950 Banagi;13.6100 Thepaha Rāja Rām;26.2229 Ibiraçu;-19.8319 Dois Riachos;-9.3928 Cerreto Guidi;43.7667 Gold Canyon;33.3639 Boguchany;58.3667 Kāza;16.3887 Kranenburg;51.7897 Kursavka;44.4500 Hudson;28.3595 Jalkaura;25.5034 Īmani;16.3302 Bayou Blue;29.6341 Týrnavos;39.7333 Nallamada;14.2164 Fino Mornasco;45.7500 Palsud;21.8262 Lindome;57.5667 Nueva Toltén;-39.1786 Braunfels;50.5167 Moda;13.8246 Mapleton;40.1188 Boujediane;35.1114 Vanj;38.3731 Jardim Alegre;-24.1789 Rāmabhadrapuram;18.5000 Bagamanoc;13.9408 Banta;24.1025 San José de Aerocuar;10.6027 Plaine Magnien;-20.4286 Santa Croce Camerina;36.8272 Jalālpur;26.0433 Stonehaven;56.9640 Chulym;55.1167 Aroāli;23.9146 Dhamsāin;26.0938 Armstrong;-32.7833 Salcedo;17.1517 Shahmīrzād;35.7728 Efatsy-Anandroza;-23.1167 Yakınca;38.3000 Kamalāpuram;18.1600 Tinchlik;40.4264 Shahrinav;38.5667 Māruteru;16.6237 Iygli;29.5001 Ocean City;39.2681 Balingoan;9.0000 Staryya Darohi;53.0394 São Félix do Araguaia;-11.6169 Lloró;5.5000 Hardia;25.8657 Ban Wang Daeng;17.4790 Siktiāhi;26.4812 Santa Inês;-13.2938 Anderson;40.4497 Sangam;14.5956 Santa Ana Huista;15.6833 Kasavanampatti;10.3692 Karuvelampatti;9.8448 Nādendla;16.2186 Valkeala;60.9389 Woodbury;41.3284 Gottmadingen;47.7356 Rychnov nad Kněžnou;50.1629 Magnolia;33.2775 Kīlminnal;12.9447 Węgorzewo;54.2167 Kothri Kalān;23.0722 Hernando;-32.4167 Pihuamo;19.1889 Bernardino de Campos;-23.0167 Trofaiach;47.4261 Gengenbach;48.4000 São João de Ver;40.9540 Fīnch’a’ā;9.9000 Bobil;25.6269 Biganos;44.6442 Rāmpur Kalān;26.1649 Kazarman;41.4000 Ambinanin’i Sakaleona;-20.5333 Mhangura;-16.9000 Ebreichsdorf;47.9611 San Nicolas Buenos Aires;19.1433 Castalla;38.5967 East Windsor;41.9049 Katālpur;26.2255 Sušice;49.2312 Rianxo;42.6500 Pipalrawān;23.1625 Takad Sahel;30.2500 Bisaria;25.9789 Tadhwa Nandpur;26.7556 Oporapa;2.0500 Ilfracombe;51.2080 Willistown;40.0010 Raipur Buzurg;25.7118 Isselburg;51.8331 Humpolec;49.5417 Congaz;46.1083 Nāthpur;26.3261 Mānkur;23.4312 Swarna;15.8542 Cameri;45.5000 Kariat Ben Aouda;34.7667 Säter;60.3500 Sannieshof;-26.5333 Si Wilai;18.1865 Corleone;37.8167 Fagersta;60.0042 Uchagaon;15.8800 Ban Bo Phlap;13.8439 Ogose;35.9645 Pādiyūr;10.4234 Serris;48.8564 Barrington;42.1515 Castel Mella;45.5000 Imerimandroso;-17.4333 Xinyaoshang;26.8350 Pipraun;26.5990 Potosí;11.4942 Petua;22.4143 Suhāgi;23.2206 San Valentino Torio;40.7911 Simpelveld;50.8333 Mahādeopur;18.7316 Mangalmé;12.3547 Miastko;54.0167 Smithfield;35.5133 Chikha;23.8898 Regen;48.9667 Gokinepalle;16.9228 Pine Castle;28.4651 Burr Ridge;41.7485 Tanippādi;12.1078 Hagen im Bremischen;53.3577 Storm Lake;42.6431 Urbana;40.1085 Warren;41.7282 Zakamensk;50.3833 Buxerolles;46.5975 La Escala;42.1136 Trevignano;45.7335 Inzago;45.5333 Taylorville;39.5328 Begogo;-23.4833 Rājagiri;10.9333 Szprotawa;51.5667 Hârlău;47.4278 Waggaman;29.9373 Byelaazyorsk;52.4500 Héricourt;47.5775 Ichikawa;34.9893 Barmstedt;53.7833 Wells;43.3267 Baley;51.6000 Covington;30.4810 Osterwieck;51.9667 Ilāmi;24.6776 Karadichittūr;11.8289 Betzdorf;50.7856 Chicureo Abajo;-33.2833 Shepperton;51.3900 Barros Cassal;-29.0928 Marudūr;11.2346 Kerap;24.8332 Bairiyā;26.3392 Yamanouchi;36.7446 Longtaixiang;34.5988 Şā’īn Qal‘eh;36.3058 Wisła;49.6549 Besagarahalli;12.6333 Bhadsara;25.3696 Hājīpur;25.2657 Māraneri;9.4333 Barahra;26.2356 Urlāha;25.7440 Oued Amlil;34.2000 Chandūr;16.9800 Nakagawa;33.9511 Marilândia;-19.4128 Belzig;52.1422 Pandāravādai;10.9133 Tha Chang;9.2674 Bir Ghbalou;36.2642 Gucheng;34.4545 Al ‘Amādīyah;37.0925 São Jerônimo da Serra;-23.7278 Kumbhāri;21.2088 Wulingshancun;40.4755 Nandayure;9.9014 Ekamba;25.9689 Flöha;50.8558 Charlton Kings;51.8877 Kirensk;57.7833 Manandroy;-21.1333 Vinaninkarena;-19.9500 Chinna Annalūru;14.8913 Bertinoro;44.1500 Hikawadai;32.5825 De Witt;43.0300 Jyllinge;55.7511 Tolna;46.4236 Westtown;39.9417 Wimauma;27.6964 Leers;50.6817 Clayton;37.9404 Timonium;39.4459 Shōō;35.0418 Puerto Caicedo;0.6850 Ekchāri;25.2093 Yairipok;24.6779 Four Corners;29.6705 Tepetlán;19.6667 Fronteiras;-7.0878 Damme;51.2500 Campolongo Maggiore;45.3333 Wen’anyi;36.8658 Wangjiabian;38.2412 Semuto;0.6200 Middle Island;40.8857 Aduku;2.0194 Alella;41.4953 Miānpur Dubauli;26.7381 Gachetá;4.8176 Sabana Grande;18.0832 East Hanover;40.8192 Lloyd;41.7286 Tournon-sur-Rhône;45.0672 Kongaralli;12.1500 Ráckeve;47.1608 Arakeri;16.9181 Agua de Dios;4.3781 El Águila;4.9167 Mawu;34.4310 Amjhera;22.5578 Weinböhla;51.1667 Kumāravādi;10.5264 Peru;40.7593 Itainópolis;-7.4469 Fanlu;23.4494 Parthenay;46.6486 Mātsavaram;16.5270 Aliganj;24.9365 Campinorte;-14.3139 Hammelburg;50.1167 Aydıncık;36.1417 Kārīz;34.8128 Sigatoka;-18.1414 Gainrha;25.9916 Tiri;25.8871 Union City;36.4267 Appārāopeta;16.8987 Sosnivka;50.2946 Karapa;16.9000 El Trébol;-32.1833 Río Cuarto;10.4076 Batesville;35.7687 Antanandava;-15.8500 Narot Mehra;32.2673 Moita Bonita;-10.5778 Niémasson;10.3078 San Bernardo;-27.2667 Calçado;-8.7419 Akhnūr;32.8667 Maryānaj;34.8311 Myślibórz;52.9333 Pompton Lakes;41.0024 Santo André;38.0500 Montale;43.9333 Alberique;39.1167 Kataha;26.7159 Bolkhov;53.4500 Asahni;26.0079 Merriam;39.0186 Lake Grove;40.8586 Târgu Frumos;47.2097 New Port Richey East;28.2605 Mittahalli;12.4210 Griñón;40.2167 Rāmpur;25.8864 Dolhasca;47.4303 Alipur;18.8621 Fuller Heights;27.9227 Mahīn;34.2425 Miyato;36.1772 Veselí nad Moravou;48.9536 Waasmunster;51.1097 Nazyvayevsk;55.5667 Soamanova;-23.2833 Parapuã;-21.7681 Panguipulli;-39.6444 Mocharim;24.6794 Herxheim;49.1469 Vijes;3.7000 Singoli;24.9667 Oregon;42.9253 Janāpul;22.8615 Fao Rai;18.0175 Barhi;26.4629 Goldbach;49.9889 Shiloh;39.9732 Manaíra;-7.7058 Snodland;51.3280 Guilherand;44.9344 Kaunra;25.5231 Gorha;26.2267 Dhilwan;31.5143 Pontchâteau;47.4369 Marly;49.0611 Pallattūr;10.1461 Vilyuysk;63.7500 Ujjini;14.9100 Karīmpur;23.9667 Satellite Beach;28.1782 Roscoe;42.4256 Shahmīrpet;17.5947 Belén de los Andaquíes;1.4161 São José do Jacuípe;-11.4119 Mahālgaon;26.0466 Columbia;38.4581 Grand Gosier;18.1833 Narimanov;46.6833 Motta di Livenza;45.7797 Ban Yang Hom;19.9222 Milicz;51.5333 Khokri Kalān;30.8456 Greasley;53.0200 São Ludgero;-28.3258 Szydłowiec;51.2333 Āwash;8.9944 Jandaíra;-11.5639 Ānavatti;14.5645 Andacollo;-30.2303 Marreddipalli;17.7989 Mahād;36.6472 Altamira do Maranhão;-4.1650 Leicester;42.2400 Side;36.7667 Avelino Lopes;-10.1369 Fotsialanana;-16.9333 Burgstädt;50.9167 Mondeville;49.1739 Nova Ubiratã;-12.9908 Brotas de Macaúbas;-11.9989 Olbernhau;50.6667 Pāraippatti;10.3144 San Carlos Centro;-31.7333 Hulshout;51.0833 Hoek van Holland;51.9811 Truşeni;47.0667 Struer;56.4856 Sampona;-25.1500 Lower Saucon;40.5881 Canyon Lake;33.6885 Amporoforo;-22.4667 Cachoeira de Minas;-22.3550 Maryville;40.3428 Dayr ‘Aţīyah;34.0833 Dent;39.1915 Andranomeva;-15.8000 Villa La Angostura;-40.7625 Baltāra;25.5116 Haspra;44.4361 Sturgis;41.7991 Borna;25.4373 Manambolosy;-16.0333 Brunete;40.4000 Donzdorf;48.6833 Costeşti;46.8678 Onna;26.4833 Beiuş;46.6500 Cidade Gaúcha;-23.3800 Bevata;-23.2833 Gemona del Friuli;46.2833 Stropkov;49.2050 Antakotako;-15.3167 Chornomorske;45.5019 Magny-le-Hongre;48.8631 Amāyan;26.3205 Obuse;36.6975 Zumaia;43.2972 Waihee-Waiehu;20.9188 Elon;36.1016 Mahazony;-21.9833 Ambinanindovoka;-21.9167 Antsambahara;-14.5333 Sosnovka;56.2500 Santana do Mundaú;-9.1678 Urdorf;47.3867 Kishunpur;25.7947 La Esperanza;7.6392 Pūliguntā;12.4395 Pingtouchuanxiang;35.8763 Grand Rapids;47.2380 Tân Sơn;21.2600 Young;-34.3000 Dek’emhāre;15.0667 Kalafotsy;-22.2833 Acari;-6.4358 Torrejón de la Calzada;40.2000 Malente;54.1667 Lavaur;43.6989 Travilah;39.0570 Coshocton;40.2618 Acarlar;37.8333 Ponte Serrada;-26.8719 Antsoha;-15.7667 Marovantaza;-15.3833 Fairview;37.6758 Rogoźno;52.7492 Shalushka;43.5261 Brumunddal;60.8836 Paranatama;-8.9208 Ixtacomitán;17.4167 Santa Teresinha (2);-7.3778 Schkopau;51.3833 Pasrāha;25.3973 Forest;37.3728 Schleusingen;50.5167 Saraiya;24.8012 Stephanskirchen;47.8500 Borovsk;55.2000 La Palma;5.3606 Igarapé Grande;-4.5850 Pai Bigha;25.0511 Methil;56.1844 Sansa;25.0291 Morafeno;-19.0833 Sabalito;8.8828 Hakone;35.1894 Op;25.2092 Céu Azul;-25.1469 Botuporã;-13.3819 Jhitkahiyā;26.7860 Vera Cruz;-22.2200 Gernsheim;49.7500 Gleisdorf;47.1039 Ambodimangavolo;-17.5167 Jhundpura;26.3473 Khāspur;25.6466 Martinengo;45.5704 Maroamalona;-15.3000 Zasechnoye;53.1142 Bowral;-34.4792 Waldkirchen;48.7305 Koronowo;53.3167 Al Atārib;36.1389 Mossley;53.5147 Pélissanne;43.6314 Franklin Lakes;41.0086 Pragadavaram;17.0167 Lyndon;38.2645 Ranomafana;-24.5667 Ambodiampana;-16.8167 Orlu;5.7837 Hulgūr;15.0833 Analamitsivalana;-20.3167 Gignac-la-Nerthe;43.3932 Vellipālaiyam;11.3314 Marotandrano;-16.1667 Taiyūr;12.7833 Mānrar;25.8895 Ambohidranandriana;-19.8833 Fairview;42.0261 Nowra;-34.8808 Abbigeri;15.5862 Panaon;8.3667 The Hills;40.6561 Tripurāntakam;16.0007 Antsahabe;-14.8000 Marolinta;-25.1000 Androndrona Anava;-15.7500 Itzer;32.8833 Locate di Triulzi;45.3500 Zhydachiv;49.3833 Summerfield;36.1973 Wyomissing;40.3317 Maroambihy;-14.4667 Völkermarkt;46.6622 Belvādi;15.7900 Marinette;45.0871 Tsararano;-16.1667 Rüthen;51.4933 Montechiarugolo;44.6934 Qal‘at an Nakhl;29.9000 Springdale;39.2909 Tsinjomitondraka;-15.6667 Cacaopera;13.7667 Jafra;31.5145 Grezzana;45.5167 Bernex;46.1784 Ambodimanary;-15.2167 Mulungu;-4.3058 Antsakanalabe;-15.4167 Ugrinovci;44.8783 Ruisui;23.4333 Racale;39.9667 Warman;52.3219 Munagapāka;17.6390 Miandrarivo;-18.9167 Xudat;41.6281 Karabash;55.4833 Antanandava;-17.4833 Drăgăneşti-Olt;44.1697 Kiskunmajsa;46.4923 Soham;52.3338 Celano;42.0864 Lyakhavichy;53.0333 Ankavandra;-18.7722 Punacha;12.9000 Danau Kändimarg;33.5646 Puente de Piedra;10.0298 Adda-Douéni;-12.3000 Knowle;52.3881 Belāo;25.1476 Oak Ridge;41.0323 Totowa;40.9039 Voloina;-15.5667 Akyaka;40.7444 Memphis;27.5435 Montecchio Emilia;44.6986 Küçük Dalyan;36.2167 Kotharpettai;12.6780 San Felice sul Panaro;44.8393 Bananal;-22.6839 Kalanivāsal;10.0700 Westview;25.8825 Tranomaro;-24.6000 Altensteig;48.5864 Angalakudūru Malepalle;16.2392 Bithauli;26.0219 Soaserana;-21.1167 Verkhniy Tagil;57.3833 Rodeio;-26.9228 Sângeorz-Băi;47.3700 ‘Utaybah;33.4861 Wiener Neudorf;48.0856 Texcatepec;20.5833 Ma’ai;34.5937 Majhariyā;26.8696 Harīpur;26.2580 Soyaló;16.9333 Hemāvati;14.0232 Raceland;29.7282 La Tuque;48.0652 Norwich;42.9833 Vadakādu;10.3418 Kondakomarla;14.0678 Bocaiúva do Sul;-25.2058 Terryville;40.9093 Highland City;27.9633 Guéoul;15.4833 Shagonar;51.5500 Chinnamandem;13.9419 Hosahalli;15.3140 Chenango;42.1954 Khāpdeh;26.2706 Deokali;25.9068 Canandaigua;42.8608 Goulds;25.5614 Phibun Mangsahan;15.2482 Nariār;25.8875 Meaford;44.5800 Sankhavaram;15.0497 Ed Dâmoûr;33.7333 Wanderlândia;-6.8489 Siswār;26.4292 Gülbaar;40.4800 Bommārbettu;13.3390 Khān Bebīn;37.0372 Diedorf;48.3500 Nallippālaiyam;11.2388 Estanzuela;14.9979 Enumulapalle;14.1446 Monte San Pietro;44.4578 San Juanito de Escobedo;20.8000 Höllviken;55.4167 Süßen;48.6797 Mutukūru;16.3829 Appleton;53.3508 General Salgado;-20.6478 Marck;50.9481 Wronki;52.7000 Palmares do Sul;-30.2578 Paraparaumu Beach;-40.8938 El Tarra;8.5756 Bonthe;7.5264 Atlit;32.6872 Nörvenich;50.8000 Kattipūdi;17.2500 Los Corrales de Buelna;43.2617 Prachatice;49.0130 Broomall;39.9688 Haigerloch;48.3647 Kalyānpur;26.4802 Lomazzo;45.7000 Keila;59.3086 Hagondange;49.2542 Adjala-Tosorontio;44.1333 Rokytne;49.6897 Phrai Bueng;14.7490 Capitão de Campos;-4.4569 Collegedale;35.0526 Daulatpur;25.3682 Tarrá;8.0475 Cáchira;7.7500 Medesano;44.7500 Minamiise;34.3521 Granby;41.9694 Burlington;42.6744 Gardere;30.3582 Saint-Laurent-de-la-Salanque;42.7736 Sidi Namane;36.7581 Newcastle;35.2401 West Caldwell;40.8488 Dighāwāni;22.2061 Bog’ot;41.3500 Yeşilli;37.3406 Aylesford;51.3033 Wooburn;51.5810 Saray;40.5322 Bela;26.4989 Karaağaç;41.3000 Matsuda-sōryō;35.3482 Punitaqui;-30.9000 Perumpāndi;10.9808 La Palma del Condado;37.3842 Semri;22.6833 Neuhof;50.4333 Desborough;52.4398 West Point;41.1220 Minamisanriku;38.6806 Chambly;49.1667 New Albany;40.0809 Quimilí;-27.6333 La Puebla de Cazalla;37.2222 Attleborough;52.5183 Sirugudi;10.2627 Hurzuf;44.5528 Neman;55.0333 Salò;45.6000 Tömük;36.6667 Canelli;44.7208 Duas Barras;-22.0508 Cristuru Secuiesc;46.2917 Linganore;39.4127 Shamsābād;32.2983 Shahar Telpa;25.1330 Pont-Saint-Esprit;44.2564 Carmo da Mata;-20.5578 Erstein;48.4219 Canton;32.5975 Bischofswerda;51.1275 Inácio Martins;-25.5708 Ertvelde;51.1783 Ketugrām;23.6957 Metsamor;40.1428 Velappādi;12.6499 Zwettl;48.6033 Bad Iburg;52.1592 Koppāka;16.7494 Boizenburg;53.3667 Gerzat;45.8258 Wijnegem;51.2333 Jerônimo Monteiro;-20.7889 Garmeh;36.9869 Fox Lake;42.4239 Mandrem;15.6581 Cerro Corá;-6.0458 Bedford Heights;41.4041 Hamilton Township;44.0540 Burgkirchen an der Alz;48.1667 Rājiāna;30.6923 Patlūr;11.6000 Bassian;30.6559 Lagoa do Mato;-6.0469 Kandry;54.5667 Kanur;17.0318 Seridó;-6.9339 Villers-la-Ville;50.5833 Rāmgarha;25.7983 Guantingzhan;40.2492 Sanjiangkou;24.7579 Gohi Bishunpur;25.9275 Cadelbosco di Sopra;44.7667 Mae Wang;18.6567 Villa Nougues;-26.8578 Barberino di Mugello;44.0014 Grand Haven;43.0553 Gangaur;26.5666 Ilvesheim;49.4725 Kombai;10.6143 Massa Lombarda;44.4500 Kaufering;48.0833 Khajuri;26.0630 Resende Costa;-20.9219 Karath;25.2010 Feyzin;45.6728 Star;43.7026 Cacimbinhas;-9.4000 Bargas;39.9400 Casaluce;41.0000 Adohoun;6.6333 Alto Paraguai;-14.5139 Montignoso;44.0167 Figuig;32.1167 Nanjundāpuram;11.0857 Madhuban Bediban;26.5062 Lincolnton;35.4748 Villers-Cotterêts;49.2531 Muscoy;34.1552 Easttown;40.0281 Kowary;50.7917 Pau Brasil;-15.4639 Bueno Brandão;-22.4408 San Gaspar Ixchil;15.3833 Pānrepatti;25.5537 Chencha;6.2500 Kisújszállás;47.2169 Bogalusa;30.7812 Delhi;37.4306 Gok;27.1065 Berkley;39.8045 Cold Springs;39.6927 Chupaca;-12.0620 Gudlavalleru;16.3500 Kamen’-Rybolov;44.7667 Nyakosoba;-29.5047 Itarana;-19.8739 Felton;51.5100 Cassá de la Selva;41.8893 Bobrovytsia;50.7424 Fortuna;38.1789 Narmeta;17.8861 Malibu;34.0370 Bobrynets;48.0578 Langenzenn;49.4944 Shevington;53.5720 Jaladurgam;15.2840 Eski Yakkabog‘;38.9314 Jordbro;59.1500 Barano d’Ischia;40.7167 Kurdi;16.0511 Petrovka;42.8389 Chatham;40.7274 Hozin;6.5333 Dhanur Kalyānwādi;17.7700 Bodippatti;10.5642 Le Crès;43.6472 Büyükyoncalı;41.3833 Khlung;12.4525 Riverdale;41.6441 Worth;41.6877 Yungay;-37.1194 Paittūr;11.5356 Enez;40.7333 Kivistö;60.3236 Crestwood;41.6454 Gajiginhālu;15.6969 Braine-le-Château;50.6833 São Bento do Sapucaí;-22.6889 San Maurizio Canavese;45.2171 Madathapatti;9.1321 Riedlingen;48.1553 Borgloon;50.8022 Nova Vodolaha;49.7197 Meßstetten;48.1806 Waterloo;38.3403 Ratnahalli;12.1922 Setubinha;-17.6000 Hānsa;26.0911 Sagarejo;41.7333 Sultānpur;29.1600 Suhiya;25.6482 Mount Sinai;40.9372 Mount Kisco;41.2016 Yacopí;5.4667 Tettuppatti;10.3979 Kulriān;29.7994 Ramacca;37.3833 Kenzingen;48.1917 Davos;46.8000 Dourdan;48.5289 Asahi;36.9462 Governador Lindenberg;-19.2519 Ciudad Insurgentes;25.2617 Bhawānandpur;25.5400 Munnūru;12.9000 Pua;19.1799 Cori;41.6445 Nivala;63.9250 Bofete;-23.1022 Tecoh;20.7419 Aşağı Ayıblı;40.9368 Court-Saint-Étienne;50.6333 Grandview;46.2443 Yoko;6.7000 Jānpur;25.2958 Itapebi;-15.9508 Daganzo de Arriba;40.5433 Vikrutamāla;13.6206 San Roque;-28.5667 Mesyagutovo;55.5322 Lágos;41.0667 Shāhpur Chaumukhi;25.7683 Humlebæk;55.9611 Ambohidanerana;-19.1833 Kotūr;17.1447 Celina;40.5550 Rāmāreddi;18.4110 Gudibanda;13.9753 Estiva;-22.4628 Pithaura;26.2620 Tekkāttūr;10.2997 Kūcheşfahān;37.2783 Yazoo City;32.8619 Haysville;37.5648 Vashon;47.4122 Stenungsund;58.0833 Plano;41.6757 Le Locle;47.0532 Erlenbach am Main;49.8039 Scartho;53.5399 Katra;32.9917 Mogotes;6.4833 Ricaurte;4.2792 Alto Rio Doce;-21.0258 Arapgir;39.0333 Damascus;45.4233 Sérékali;9.9186 Komarolu;15.2667 Valaparla;15.9167 Lollar;50.6497 Amioûn;34.3000 Lincoln Park;40.9239 Hariāna;31.6351 St. Clements;50.2689 Ambato;-18.6000 Tallimarjon Shahri;38.2967 Tokkavādi;11.3702 Khorol;44.4289 Novgorodskoye;48.3328 Taimali;22.6167 Blackhawk;37.8159 Villiersdorp;-33.9833 Yedrāmi;16.8667 Hambühren;52.6333 Waynesboro;39.7524 Saint-Amable;45.6500 Weyburn;49.6611 Lauterach;47.4772 Fife;47.2329 Strasshof an der Nordbahn;48.3194 Sorbhog;26.5000 Triangle;38.5483 Sam Ko;14.6013 Itri;41.2833 Karayılan;36.7167 Krzeszowice;50.1333 Paraúna;-16.9478 Tacima;-6.4878 Ripoll;42.2011 Keza;-2.7540 Ghattu;13.6602 San Pablo Huixtepec;16.8167 Fredericksburg;30.2660 Freeport;28.9453 Doi Saket;18.8667 Gairtganj;23.4102 Worsley;53.5093 Beachwood;39.9286 Guaitarilla;1.1333 Afonso Bezerra;-5.4978 Fürth;49.6502 Swanage;50.6080 Garhi;24.7904 Kara-Tash;40.2167 Manville;40.5420 Kharika;25.7173 Grigiškės;54.6667 Mios;44.6050 Scherpenzeel;52.0833 Sarmiento;-45.6000 Shāmpur;23.4032 Troy;38.7268 Pocono;41.0612 Kākhandiki;16.6010 Apía;5.1000 San Juan;17.6834 Cantillana;37.6000 Key Largo;25.1224 Cedro;-7.7219 Suyo;16.9839 Tosagua;-0.7800 Reguengos de Monsaraz;38.4167 Raeren;50.6833 Ngorongoro;-3.2496 Burkburnett;34.0746 Vembūr;10.5893 Dasai;22.7200 Chesterfield;53.2358 Laranja da Terra;-19.8989 Arāvelli;18.7190 Amaturá;-3.3639 Manevychi;51.2925 Nijoní;39.9667 Sharg‘un;38.4600 Icononzo;4.1833 Kadanganeri;8.9200 Ørsta;62.2003 Chuhr Chak;30.7775 Pencoed;51.5228 Devi Hosūr;14.7858 Nawāda;25.5037 Borshchiv;48.8024 Gangāpur;25.8565 Celldömölk;47.2557 Capena;42.1403 Burgos;18.5114 Kanhai;25.9761 South Union;39.8705 St. Albans;38.3769 Hrebinka;50.1180 Kitee;62.1000 Puduppatti;9.6171 São Tomé;-5.9728 Bestwig;51.3667 Primeiro de Maio;-22.8508 Kuršėnai;55.9833 Akbarpur;24.6281 Agouna;7.5667 Yampil;48.2450 Pattīswaram;10.9253 Weingarten;49.0514 Maur;47.3417 Gaohucun;28.3368 Piraúba;-21.2758 Baryshivka;50.3703 Uchti;25.9197 Laćarak;45.0000 South Dundas;44.9167 Sanson;9.2833 Emiliano Zapata;16.5000 Yeghvard;40.3217 Chapeltique;13.6333 Bārah;35.6830 Nova Floresta;-6.4550 Wantage;41.2431 Xiaozui;35.6912 Rincão;-21.5869 Fiuggi;41.8000 Pipra;26.3460 Volda;62.1468 Kaimūh;33.7197 Munhall;40.3935 Diavatá;40.6883 Léognan;44.7286 Itapitanga;-14.4228 Jaipur;23.4313 Oppatavādi;12.5690 Hombal;15.5164 Mallan;30.4015 Tivim;15.6000 Aurahi;25.8263 Dibrāghani;25.8261 Ousseltia;35.8400 East Cocalico;40.2242 Khrystynivka;48.8333 Sonoma;38.2902 Euxton;53.6620 Asālem;37.7306 Braunsbedra;51.2833 Isnapuram;17.5443 Pāikpāra;24.3149 Wang Sombun;13.3515 Randazzo;37.8833 Nurmo;62.8278 Saint-Gély-du-Fesc;43.6922 Chandera;25.0829 Tamiang Layang;-2.1160 Ogano;36.0171 Dasaut;25.8997 Abram;53.5080 Turmānīn;36.2333 Rush;53.5220 Al Karak;32.6872 Kurman;45.4978 Simarbani;26.1953 Tököl;47.3203 Le Taillan-Médoc;44.9044 Imi Mokorn;30.1675 Jhonkar;23.2361 Wald-Michelbach;49.5724 Montignies-le-Tilleul;50.3833 Mangrāwān;25.0929 Kāmepalle;16.5191 Huétor-Tájar;37.1947 Ararendá;-4.7528 Soquel;36.9978 Foum Jam’a;31.9600 Juripiranga;-7.3728 Lehman;41.1518 Fontaine-lès-Dijon;47.3433 Shannon;52.7137 Antenetibe;-18.7833 Wittelsheim;47.8053 Tiszaföldvár;46.9739 Dáli;35.0211 Narkatpalli;17.2030 Villeneuve-lès-Maguelone;43.5322 Hochdorf;47.1664 Uzda;53.4661 Santo Domingo;6.4708 Gounarou;10.8667 Sāngi;26.3237 Kolno;53.4106 Montgomery;39.2496 Douar Bouchfaa;34.1014 Tarabha;20.7325 Poiana Mare;43.9333 Tiszafüred;47.6167 Əliabad;41.4783 Guaraci;-20.4986 Sisai;23.1794 La Trinité;43.7408 Ban Ko;13.6486 Raitar;25.0577 Van Wert;40.8651 Baretha;25.5258 Zuchwil;47.2056 Gharyāla;31.2294 Totolapan;18.9869 Madison Heights;37.4487 Castleton;53.5907 Dumri;26.8648 Torpa;22.9361 Mākhar;25.7693 Nkokonjeru;0.2394 Morro Bay;35.3682 Joutseno;61.1230 Antanandehibe;-19.6833 Bāsht;30.3611 Oloron-Sainte-Marie;43.1942 Pompton Plains;40.9679 Méry-sur-Oise;49.0636 Itatuba;-7.3750 Demmin;53.9050 Babhantoli;26.2168 Itacurubí de la Cordillera;-25.4500 Nāgambhotlapālem;15.6804 Nowa Dęba;50.4333 Harpur Bhindi;25.7746 Jaguari;-29.4969 Swan Hill;-35.3333 Borim;15.3604 Honwād;16.8100 Bleicherode;51.4167 Choachí;4.5297 Placerville;38.7308 Alcoa;35.8076 Nāhargarh;24.1692 Yeşilköy;36.8667 La Londe-les-Maures;43.1381 Astorga;42.4589 Kings Mountain;35.2349 Vigonovo;45.3852 Takon;6.6500 Qamīnis;31.6572 Tanant;31.8667 Ānandpur;25.6192 Parmānpur;26.1457 Pasupatikovil;10.8893 Joaquim Távora;-23.4989 Urlaţi;44.9911 Atchison;39.5625 Ōtsuchi;39.3582 Balderton;53.0549 Gateway;26.5793 La Farlède;43.1678 Yuzhno-Sukhokumsk;44.6667 Centenário do Sul;-22.8208 Sabaneta de Yásica;19.6667 Galena Park;29.7452 Birkenfeld;48.8697 Râs el Metn;33.8500 Wellington;40.7000 Al Musayfirah;32.6322 Kauniainen;60.2167 Kāmthi;14.5000 Alto Parnaíba;-9.1108 Guthrie;35.8424 Belsara;26.0152 Kelilalina;-21.2833 Bhargaon;26.0750 Dumra;25.5907 Lerici;44.0764 Felsberg;51.1333 Kūhbanān;31.4103 Tisma;12.0831 Curimatá;-10.0358 Arcola;44.1167 Kopparam;16.0841 Coité do Nóia;-9.6319 Iwai;35.5833 Pintadas;-11.8128 Goianápolis;-16.5108 Lakkireddipalle;14.1667 Köngen;48.6819 Lehigh;40.7678 Nizza Monferrato;44.7747 Bāsdeopur;25.3909 Periyanegamam;10.7432 Ngaputaw;16.5378 Caernarfon;53.1400 Taouloukoult;31.2167 Andovoranto;-18.9544 L’Île-Perrot;45.3833 Notre-Dame-de-l'Île-Perrot;45.3667 Ban Lueak;13.7065 Campo Largo;-26.8000 Elattūr;11.3866 Momanpet;17.5175 Sūlagiri;12.6645 Antsahadinta;-19.0167 Tangerhütte;52.4333 Hasanpura;26.0751 Khāsbalanda;22.5881 Williams Lake;52.1294 Nonea;26.9746 Bischofshofen;47.4172 Estaimpuis;50.6756 Santa Bárbara;26.8133 Telkapalli;16.4500 Arbaa Sahel;29.5993 Santa Catarina Ayotzingo;19.2647 Böhl-Iggelheim;49.3714 Nadezhda;45.0448 Vanzago;45.5268 Surazh;53.0167 Svedala;55.5000 Seberi;-27.4778 Aigle;46.3167 Qal‘eh Tall;31.6325 São Luís do Paraitinga;-23.2219 Alberobello;40.7833 Shŭrobod;37.8403 Richmond Heights;41.5589 Oyim;40.8242 Coriano;43.9667 Kīramangalam;10.2859 Leninskoe;42.9806 Eriyodu;10.5172 Opglabbeek;51.0500 Brandon;43.5928 Darling;-33.3833 Chewara;25.0756 Itabirinha de Mantena;-18.5658 North Branch;45.5137 Mount Evelyn;-37.7830 Lamhadi;32.3000 Kaithāhi;26.3488 Orthez;43.4881 Southwater;51.0238 Elliot Lake;46.3833 Lichtenau;51.6000 Presidente Vargas;-3.4069 Sarmera;25.2564 Praskoveya;44.7444 Sāīnkhera;22.9589 Darb-e Behesht;29.2417 Villahermosa;5.0000 Rāmpur Parhat;25.5916 Benalla;-36.5519 Monmouth;44.8505 Tamanar;31.0000 Dharir;22.3337 Bourg-de-Péage;45.0378 Tarboro;35.9046 Pleasant View;41.3249 Čáslav;49.9117 Yādavolu;17.0619 São Brás de Alportel;37.1500 Fô-Bouré;10.1167 Branquinha;-9.2458 Lohārda;22.5918 Murtosa;40.7333 Senjān;34.0497 Ankarabato;-16.3500 Jackson;43.4720 Ichnia;50.8500 Dießen am Ammersee;47.9500 Zavitinsk;50.1281 Latteri;12.9712 Clute;29.0256 Mangalpur Gudaria;26.6513 San Ricardo;9.9167 Ubalá;4.7439 Harsola;22.5694 Sivrice;38.4467 Lālmunia Munhāra;26.5741 Pānchi;25.1123 Abadou;31.5797 Miāni;31.7092 Mudgee;-32.6125 Bolivar;37.6059 Sukhsena;25.6881 Poirino;44.9208 Katigang;25.4157 Parauli;26.2227 Kingsteignton;50.5458 Kiełczów;51.1406 Sidi Brahim;35.2606 Rocafuerte;-0.9200 Sütçüler;37.4944 Bondues;50.7017 Göynücek;40.3833 Geneseo;42.8038 San Rafael Obrajuelo;13.5000 Nahulingo;13.7000 Ponnāda;18.2536 Bakhariā;26.7666 Villa Purificación;19.7858 Urubici;-28.0150 Lake Morton-Berrydale;47.3325 Bedum;53.3000 Corning;42.1470 Harpur;25.6537 Tājpur;25.9022 Nakayama;38.3331 Kanteru;16.3906 Salemi;37.8167 North Logan;41.7759 Veppattūr;11.0154 Rombas;49.2494 Mantasoa;-19.0167 Little Canada;45.0244 Chavusy;53.8075 Sebt Ait Ikkou;33.6690 Máncora;-4.1056 Angwāli;23.7317 Brejolândia;-12.4828 Tárnok;47.3597 Bellefontaine Neighbors;38.7529 Maynard;42.4264 Wendeburg;52.3167 Kollankulam;8.7964 Taufkirchen;48.3439 Sidi Bousber;34.5667 DeForest;43.2301 Bāgalūr;12.8333 Ahogbeya;7.0333 Silea;45.6547 Cantley;45.5667 Mehdīpur;25.3902 Zapotitlán;14.1674 Torre Santa Susanna;40.4667 Gravatal;-28.3308 Jankampet;18.7067 Antsaravibe;-13.0500 College;40.8144 Dinagat;9.9561 Villa Jaragua;18.4800 Peralillo;-34.4875 Inkerman;44.6142 Newburn;54.9830 Hajeb el Aïoun;35.3900 Urcos;-13.6861 Nova Olinda;-7.6319 Nakonde;-9.3272 Jucati;-8.7058 Trovagunta;15.5509 Russellville;34.5055 Nāgasamudram;15.0556 Kodūru;13.8692 Tafalla;42.5132 Badru Khān;30.2523 Dombāchcheri;9.9604 Hämeenkyrö;61.6333 Chalco;41.1817 Milton;44.6429 Agdz;30.6978 Cowra;-33.8183 Pirpirituba;-6.7800 Baetov;41.2100 Duga Resa;45.4472 Lohariandava;-18.7833 Neuenhaus;52.5000 Had Laaounate;32.6128 Akim Swedru;5.8940 Dehqonobod;40.5314 Jhabrera;29.8091 El Paraíso;14.0833 Anoviara;-14.7333 Bālia;24.2433 Wepangandla;15.9359 Duque Bacelar;-4.1558 Itaueira;-7.6028 Lake Barcroft;38.8514 Aver-o-Mar;41.4039 Fondettes;47.4042 Betsiaka;-13.1500 Hilter;52.1357 Orşova;44.7253 Peschiera del Garda;45.4386 Merchweiler;49.3603 Szigetvár;46.0475 Jibou;47.2667 Togamalai;10.7251 Markt Indersdorf;48.3667 Vienna;39.3240 Amtar;35.2385 Almoloya;19.7000 Gangania;25.2395 Atessa;42.0667 Kilibo;8.5717 Burgau;48.4322 Phulhara;25.8937 Ghusiya;25.1832 Recco;44.3667 Peñarroya-Pueblonuevo;38.3000 Canelinha;-27.2650 Chuqung;33.3743 Kolárovo;47.9169 Ardrossan;55.6432 Raymond;43.0322 Lonkly;7.1333 Hetane;32.8403 Zawyat Ahançal;31.8325 Monroe;42.6030 Roccapiemonte;40.7617 Santa Margarita de Mombúy;41.5756 Rupāna;30.4070 Kiến Giang;17.2250 Novotroitske;46.3544 Ruppichteroth;50.8456 Lyons;41.8119 Castelli Calepio;45.6333 Paimio;60.4500 Nelson;49.5000 Knin;44.0414 Bel Air;39.5348 Weilheim an der Teck;48.6150 Tubará;10.8667 Rincon;32.2947 Ribeirão Claro;-23.1939 Cunha Porã;-26.8939 Jandiāla;31.2157 Mel Seval;8.6722 Peddāpuram;18.0289 Querfurt;51.3833 Pelāgor;25.5992 Devanakonda;15.5333 Faxinal dos Guedes;-26.8528 Ringkøbing;56.0897 Bovingdon;51.7231 Scaggsville;39.1416 Kuiyibagecun;38.0836 Perumuchchi;13.0560 Baran;54.4833 Castrolibero;39.3167 Tokigawa;36.0086 Flămânzi;47.5500 Priol;15.4167 Cajobi;-20.8800 Vysokovsk;56.3167 Corocoro;-17.1667 Las Matas de Santa Cruz;19.6700 Lagunia Raghukanth;25.8195 Mara Rosa;-14.0169 Sosenskiy;54.0500 İnebolu;41.9747 Shirako;35.4543 Escaudain;50.3344 Zdzieszowice;50.4192 Morbach;49.8167 Králŭv Dvŭr;49.9499 Gudofredo Viana;-1.4028 Filipstad;59.7167 Rettanai;12.1953 Csömör;47.5500 Castelletto sopra Ticino;45.7167 Marum;53.1500 Ratnagiri;13.8111 Māchalpur;24.1277 Altötting;48.2267 Ironton;38.5323 Difficult Run;38.9016 Trets;43.4469 Vittuone;45.4833 Conselve;45.2333 Pakka Kalān;30.0323 Scotchtown;41.4765 Camden;33.5672 Oţelu Roşu;45.5186 Tadas;15.1333 Mouans-Sartoux;43.6200 Adigappādi;12.1459 Abay;43.2092 Kottaipatti;10.1533 Vidalia;32.2125 Potavaram;17.0194 Murata;38.1185 Hussepur;26.1801 Fenglin;23.7500 Sint-Lievens-Houtem;50.9167 Grevesmühlen;53.8667 Spa;50.4925 Arimalam;10.2550 Urrugne;43.3622 Khovaling;38.3386 Cortalim;15.3978 Pine Hill;39.7879 Aïn Zora;34.6600 Harlākhi;26.6353 Küçükkuyu;39.5500 Madhura;25.5418 Chausa;25.5149 Madanpur;24.6554 Bobenheim-Roxheim;49.5839 Pasil;17.3894 Vairampatti;10.5515 Laheji;26.0957 Indalvai;18.5403 Lambton Shores;43.1833 Santiago Suchilquitongo;17.2500 Al Ghizlānīyah;33.3986 Pola de Lena;43.1583 Sugarmill Woods;28.7299 Isola Vicentina;45.6333 Ingeniero White;-38.7667 Malvern;34.3734 Tarawān;24.7295 Canteras;37.6122 Begijnendijk;51.0186 Souq Sebt Says;32.7773 Toundout;31.2667 Mokrisset;34.9100 Höchberg;49.7831 Forest Acres;34.0323 Parczew;51.6333 Maevka;42.9250 Mussomeli;37.5794 Leninaul;43.0878 Sālehpur;25.6119 Riverton;43.0421 Motibennur;14.7150 Vouzela;40.7167 Krishnamsettipalle;15.3695 Gölpazarı;40.2847 Candelaria Loxicha;15.9262 Ozieri;40.5849 Bariariya Tola Rājpur;26.4567 Ekangar Sarai;25.2234 Monnickendam;52.4667 Chandankiāri;23.5781 Pārtibanūr;9.5855 Jablanica;43.6583 Mold;53.1660 Nuriston;38.4892 Holmen;43.9706 Prineville;44.2985 Doraville;33.9073 Tourza;29.4778 Kimpese;-5.5631 Vadakku Ariyanāyakipuram;8.7208 Biskupiec;53.8667 Aerzen;52.0496 Peschanokopskoye;46.1958 Kamikawa;35.0642 Khilok;51.3500 Schalksmühle;51.2403 Gommern;52.0667 Tarazona de Aragón;41.9044 Monte San Juan;13.7667 Ludlow;52.3680 Malaya Vishera;58.8500 Satwār;26.2163 Meerhout;51.1333 Kambaliyampatti;10.3671 Pāra;23.5142 Sweetwater;32.4692 Cajapió;-2.9667 Bibbiano;44.6667 Sadovoye;42.8528 Yacuanquer;1.1167 Pinto;-36.7000 Hartford;43.6644 Konkavāripalle;13.7125 Aït Hani;31.7786 Pulpí;37.4019 Ghosrāwān;25.0910 Alīpura;25.1753 Tnine Sidi Lyamani;35.3700 Mildenhall;52.3446 Hanson;42.0558 Selma;29.5866 Santa Teresinha;-12.7719 Garden City;33.5926 Ambohitromby;-18.4333 Ambolotarakely;-18.2667 Rokunohe;40.6095 Wustermark;52.5497 Jurbise;50.5333 Jiménez;10.1797 Plombières;50.7333 Imst;47.2394 Pia;42.7447 Walker Mill;38.8758 Rignano Flaminio;42.2000 Songo;-7.3496 La Ferté-sous-Jouarre;48.9492 Excelsior Springs;39.3390 Lavínia;-21.1683 Evander;-26.4719 Beverly Hills;42.5220 Kanyāna;12.9000 Armanāz;36.0833 Villanueva de Arosa;42.5628 Boiling Springs;35.0450 Wahlstedt;53.9500 Patsanda;24.8580 Borgampād;17.6500 Strehaia;44.6222 Suganwān;25.1176 Atlapadu;16.7833 Korosavāda;18.7256 Lejanías;3.5268 Čelić;44.7167 Nanticoke;41.2005 Potunūru;16.7442 Ouando;6.5542 Mfou;3.9600 Kursaha;25.5588 Modisi;0.4517 Ban Ueam;18.4246 Ascope;-7.7138 Tiztoutine;34.9833 Basla;33.3833 Millington;35.3350 Nossen;51.0500 Bangshang;32.2575 Barcs;45.9601 Scottdale;33.7950 Vejen;55.4774 Dumri;25.8605 Castelnovo ne’ Monti;44.4333 Agramonte;22.6761 Mohanpur;25.5507 Sawla;9.2833 Khagaur;25.1744 São Tiago;-20.9128 Bom Jesus da Serra;-14.3719 Doorn;52.0333 Timrå;62.4869 Alcora;40.0667 Vadapalanji;9.9266 Westphalia;38.8356 El Álamo;40.2306 Ben N’Choud;36.8624 Whitnash;52.2680 Montividiu;-17.4439 Naganuma;43.0102 Mādhopur;26.7474 Valpoy;15.5324 West Hanover;40.3635 Rāmpur;25.1826 Ambahy;-20.7667 Forestville;38.8518 Asārhi;25.2892 La Bañeza;42.2975 Raymondville;26.4759 Eugenópolis;-21.0989 Sirsa;26.2667 Lepākshi;13.8032 Dallgow-Döberitz;52.5331 Petmanhalli;17.7688 River Grove;41.9243 Madhopur;26.7333 Hirehadagalli;14.9267 Santa Maria de Itabira;-19.4489 Usmat Shaharchasi;39.7386 Ban Bang Phlap;13.9241 Umarizal;-5.9908 Lowell;41.2917 Douglass;40.3438 Pebble Creek;28.1583 Cottage Grove;43.7960 Rasebetsane;-29.8992 Tilougguit;32.0333 Kujri;26.2392 Bacliff;29.5085 Nanzhou;22.4789 Hōki;35.3852 Khesht;29.5636 Borjomi;41.8389 Kadama;1.0167 Coleford;51.7910 Navabad;38.5278 Chitagá;7.1333 Rialma;-15.3150 Borger;52.9236 Augustdorf;51.9094 Rebola;3.7192 Santa Teresa di Riva;37.9400 Luanco;43.6100 Gonghaur;26.4346 Vermilion;41.4103 Kendall Park;40.4135 Marotaolana;-14.0167 Skilloúnta;37.6167 Ickenham;51.5580 Pazaryeri;40.0000 Douar Mzoura;34.3167 Serra do Salitre;-19.1108 Rosario;-34.3139 Dombasle-sur-Meurthe;48.6250 Kharahara;24.9142 Sujāpur;25.4815 Singanamane;13.7167 Belgrade;45.7789 Mallapuram;9.8280 Jesenice;49.9683 Kandulāpuram;15.5976 Mahīnāthpur;26.6554 Haripura;34.0410 Talugai;11.3752 Altenholz;54.4000 Montalegre;41.8231 Frontera;-31.4278 Bad Orb;50.2167 Villacarrillo;38.1000 Paricônia;-9.2539 Veitshöchheim;49.8328 Bordj Okhriss;36.0833 Rupahi;26.4110 Bluffton;40.7424 Partanna;37.7289 Zhatay;62.1641 Novodnistrovsk;48.5778 Campi Salentina;40.4000 Mount Airy;36.5083 Sumner;47.2189 Torotoro;-18.1342 Kamifurano;43.4556 Herīs;38.2467 Blandford Forum;50.8560 Ban Thung Khao Phuang;19.5342 Arceburgo;-21.3639 Galimuyod;17.1833 La Virgen;10.4312 Argelès-sur-Mer;42.5461 Trescore Balneario;45.7000 Cutro;39.0328 Zmeinogorsk;51.1667 Reiskirchen;50.6000 Bad Lauterberg;51.6317 Kilkunda;11.2569 Magalia;39.8228 Tepperumālnallūr;10.9694 Litomyšl;49.8719 Martin;36.3385 Arraias;-12.9308 Altenberge;52.0458 Koila Belwā;26.3753 Miribel;45.8244 Dona Inês;-6.6178 Kihō;33.7333 Kibichūō;34.8634 Wilkau-Haßlau;50.6667 Madhurāpur;25.9456 Karghar;25.1267 Sortland;68.6982 Andonabe Atsimo;-19.8833 San Vicente de Castellet;41.6655 Mamdāpur;16.1400 Ibiassucê;-14.2589 Lighthouse Point;26.2785 Novi Banovci;44.9500 Concordia Sagittaria;45.7667 Perket;18.7942 Peravali;15.2861 Arlesheim;47.4922 Avelgem;50.7753 Bagnara Calabra;38.2833 Angostura;6.8667 Sotkamo;64.1333 Alachua;29.7779 Mapleton;43.7358 Baisa;25.3552 Dnestrovsc;46.6167 Brahmānandapuram;16.9583 Māldah;25.1740 Soyaux;45.6403 Bīrpur Bārāpatti Pindraun;26.5475 Sidi Abdallah;32.5783 Fayzobod;38.5500 Shumanay;42.6333 Sangão;-28.6378 Reading;39.2242 Appenweier;48.5397 Bāgh-e Bahādorān;32.3772 Lake Hopatcong;40.9599 Maxaranguape;-5.5158 Whitwick;52.7403 Acushnet;41.7138 El Chol;14.9611 Tonse East;13.3963 Matias Olímpio;-3.7158 Rhosllanerchrugog;53.0110 Congonhal;-22.1528 Albert;50.0019 Garešnica;45.5667 Höchst im Odenwald;49.7992 Dandkhora;25.5729 Khurmi;39.5167 Barāgaon;24.5682 Nānan;25.0905 Greentree;39.8989 Maduvanalli;12.1500 Pérols;43.5650 Jessup;39.1488 Regidor;8.6667 Vaals;50.7667 San Giorgio del Sannio;41.0667 Prakhon Chai;14.6092 Sādiqpur Maraul;25.9966 Valdobbiadene;45.9000 Paravāda;17.6283 Jettihalli;12.0800 Dinnington;53.3667 Sinaia;45.3500 Siruvalūr;11.3600 Bolekhiv;49.0669 Susuz;40.7800 Gaunivāripalle;13.9756 Schwaikheim;48.8714 Phai Sali;15.6000 Pilar;9.8639 Pleasanton;28.9642 Virginópolis;-18.8228 Kennett;36.2403 Bithlo;28.5644 Likiškiai;54.3950 Dar El Kebdani;35.1203 Altofonte;38.0500 Kirlampūdi;17.1919 Najrīj;30.9667 Old Saybrook;41.3017 Mahārājapuram;9.6588 Barnāon;25.4809 Kirchzarten;47.9667 Lāpangā;23.6333 Natuba;-7.6408 Maria Enzersdorf;48.1000 Saks;33.7118 Nova Europa;-21.7783 Inungūr;10.8507 Vaddādi;17.8474 Ban Dan Na Kham;17.7167 Thilogne;15.9239 Bom Repouso;-22.4708 Port Wentworth;32.1951 Ayntap;40.0986 Palestina;1.7500 Jefferson;29.9609 Fuveau;43.4522 Valley;32.8088 Fatehābād;31.3811 Lahstedt;52.2500 San Vendemiano;45.8914 Topoloveni;44.8069 Newberry;34.2813 Bad Wildbad;48.7503 Florestópolis;-22.8628 Praia do Carvoeiro;37.1200 Villamarchante;39.5678 Sidi Boushab;30.0740 Oneida;43.0769 Iramaia;-13.2858 Huittinen;61.1750 Ivangorod;59.3667 Langhirano;44.6167 Española;36.0044 Lyngdal;58.1333 Rāparla;15.9569 Kannamangalam;12.7499 Lyuboml’;51.2158 Marlton;39.9014 Olëkminsk;60.3667 Bērikai;12.8056 Heum;59.0955 Karahallı;38.3167 Blackwells Mills;40.4773 Chākand;24.8907 Steinfeld;52.6000 Wildberg;48.6239 Bellmead;31.6026 Pitkyaranta;61.5750 Rishivandiyam;11.8170 Wittenbach;47.4667 Forestville;39.0711 North Bellport;40.7868 Douar Echbanat;34.2167 Costeşti;44.6697 Emsworth;50.8490 Rāmpur Khajuriyā;26.3923 Gossau;47.3081 Königsbach-Stein;48.9664 Corzuela;-26.9333 Etropole;42.8333 Karkamış;36.8340 Colonia Nicolich;-34.8167 Nanfang;23.3568 Georgian Bluffs;44.6500 Bajwāra;31.5150 Samalpur;25.1961 Luís Alves;-26.7208 Somireddipalle;14.8365 Pintuyan;9.9500 Alcaudete;37.5833 Klipphausen;51.0833 Adyār;12.8756 Jomasho‘y;40.8633 Puerto Santander;8.3636 Jaltocan;21.1333 Kangning;38.0176 Minobu;35.4675 Hachīrūd;36.6864 Olevsk;51.2278 Aradeo;40.1333 Pembroke Dock;51.6933 Altusried;47.8000 Aulendorf;47.9542 Essex Junction;44.4902 Carmaux;44.0492 Salanso;12.1833 Minnāmpalli;11.6758 Niebüll;54.7881 Somers Point;39.3167 Annavāsal;10.4667 Bujari;-9.8308 Campton Hills;41.9498 Trumbull Center;41.2415 Tomblaine;48.6856 Neuenhof;47.4469 Frouzins;43.5161 Koranampatti;11.6085 Bogen;48.9167 Corcuera;12.8000 Roelofarendsveen;52.2000 Manakana;-19.8167 Irineópolis;-26.2389 Tangermünde;52.5408 Fairmont;43.6441 Shenjiaba;32.9441 Isua;25.2330 Dobhāwān;25.3987 Türkan;40.3639 Takkali;8.2461 Sidi Bou Ali;35.9561 Dharmavaram;18.2164 Le Mars;42.7810 Santiago;-14.1892 Felling;54.9500 Mörlenbach;49.5990 Saint-Grégoire;48.1511 Àrvorezinha;-28.8719 Kangaroo Flat;-36.7833 Haikoucun;28.3237 Loudoun Valley Estates;38.9770 Sudogda;55.9500 Neuenstadt am Kocher;49.2333 Kothi;24.7526 Bogué;16.5904 Vellatūru;16.1209 Isabela;18.4991 Burtonsville;39.1166 Rāoke Kalān;30.8219 Karor;25.6018 Zuyevka;58.4000 Sucupira do Norte;-6.4769 Pôrto Firme;-20.6728 Sūndekuppam;12.4567 Oudewater;52.0167 Khiram;33.7320 Brikama Ba;13.5333 Granbury;32.4475 Sylva;57.3139 Trabia;38.0000 Brikcha;34.9667 Năsăud;47.2833 Kiso;35.9363 Roche-la-Molière;45.4339 Alagarai;10.9826 Mungod;17.0667 Reddipalle;14.1993 Cittanova;38.3500 Achchippatti;10.6989 Kourani;-11.8511 Phon Charoen;18.0258 Ivankiv;50.9328 Oulad Slim;32.7775 Mawai;24.8043 Muhammadganj;26.1506 Kottapālem;16.5787 Sovata;46.5961 Erraballa;14.3971 Sidi Rahhal;31.6667 Pierrelaye;49.0225 Dougba;8.4497 Villasāgar;18.4736 Gages Lake;42.3519 Conceição dos Ouros;-22.4128 Fanjā’;23.4675 Gauli Palāsiya;22.5323 Cunupia;10.5500 Warr Acres;35.5285 Schwaig;49.4692 Farmersville;36.3050 Lachen;47.1833 Masindi Port;1.6983 Māli;25.6549 Kalanak;39.0833 Santa Fé;-23.0378 Fairview;45.5469 Flat Rock;42.0991 Srīrēmapuram;16.8600 Lake Monticello;37.9210 Semmarikulan;8.4815 São Gonçalo do Pará;-19.9828 Großburgwedel;52.4933 Edwards;39.6215 Silvārpatti;10.4473 Balikumbat;5.8928 Shirbadgi;16.0417 Dihri;25.3796 Tansandra;13.0554 Awfouss;31.6833 Krasnoilsk;48.0167 Quinto di Treviso;45.6500 Rāmannapeta;17.2833 Ridgefield;45.8114 Nevelsk;46.6833 Bomporto;44.7333 Coroneo;20.1333 Manalūrpettai;12.0079 Ontario;43.2408 Baraçlândia;-7.2050 Dan;7.3167 Kragerø;58.8869 Prestonpans;55.9597 Heath;40.0241 Īdupugallu;16.4609 Molango;20.7844 Myers Corner;41.5864 Volpago del Montello;45.7833 Modavāndisatyamangalam;11.2635 Tiddas;33.5665 Bāghīn;30.1811 Beni Oulid;34.5897 Danville;39.7584 Chinchinim;15.2000 Roseira;-22.8978 Tha Muang;13.9611 Scituate;41.7926 Brumath;48.7319 Waimea;20.0124 Devarāpalle;17.9886 Aydıncık;40.1167 Eerbeek;52.1053 Ban Klang;18.5791 Limoux;43.0569 Werlte;52.8500 Wabash;40.8034 Agatogba;6.4000 Vedurupāvalūru;16.5519 Corral de Bustos;-33.2833 Agua Blanca Iturbide;20.3500 Portet-sur-Garonne;43.5219 Plaridel;13.9511 Pirapetinga;-21.6558 Basāha;26.1329 Nāgulapādu;16.3090 Machchand;26.3241 Planura;-20.1378 Merzenich;50.8262 Waterford;39.7415 Rawdon;46.0500 Pella;41.4052 Amurrio;43.0528 São José do Calçado;-21.0250 Anröchte;51.5667 Dhāntola;26.2016 Baldeogarh;24.7562 Plattekill;41.6478 Pūmalakkundu;9.8871 Halgūr;12.2500 Emsbüren;52.3925 Lingampet;18.2383 Bordighera;43.7833 Hamilton;-37.7333 Garden Acres;37.9637 Campbellton;48.0050 Chong-Aryk;42.8139 Reddigūdem;16.8939 Belaya Kholunitsa;58.8500 Vettaikkāraniruppu;10.5739 Lemmer;52.8437 Fasintsara;-20.7000 View Royal;48.4517 Belle Chasse;29.8558 Piraí do Norte;-13.7619 Gilbués;-9.8319 Majhgawān;23.4039 Leones;-32.6617 Chiankī;24.0051 Silverton;45.0030 Galion;40.7385 Santa Catarina Masahuat;13.7833 Weil im Schönbuch;48.6214 Puduppattanam;10.7626 Dāmu;26.5461 Jogiāra;26.4083 Terralba;39.7197 Jacinto Machado;-28.9969 Jaguaribara;-5.6578 Great Missenden;51.7042 Palatka;29.6493 Shasta Lake;40.6790 Suthālia;23.9955 Erravaram;17.5458 Somers;41.9949 Mercês;-21.1939 Huachipato;-36.7481 Pavittiram;11.1406 Bad Ems;50.3381 Lajes;-5.7000 Vūtukūru;14.5778 Millstone;40.2123 Rangamāti;26.8040 Mahem;15.5796 Sambhu Chak;26.4081 Kīlakkurichchi;10.7645 Libanté;10.7936 Kudūru;13.1081 Puerto Quijarro;-17.7796 Simbach am Inn;48.2667 Pasewalk;53.5000 Dera Baba Nanak;32.0321 Ianca;45.1350 Morsbach;50.8667 Dholbāja;26.2674 Pararia;25.6492 Tavriisk;46.7559 Cholchol;-38.6000 Knezha;43.5000 Felida;45.7138 Iraiyūr;11.7834 Vaprio d’Adda;45.5667 Lipova;46.0917 Chavkandak;39.9000 Bimun;34.0588 Peixe;-12.0250 Barhauna;25.3040 Ukkali;16.5900 Alguazas;38.0514 Terakanāmbi;11.8000 El Realejo;12.5428 Keshwāri;24.1878 Úmbita;5.2167 Bowen;-20.0102 Kunnūr;9.5882 Vālāntaravai;9.3390 Kapyl;53.1500 Sīlamalai;9.9621 Pirojgarh;26.2769 Kanakpur;24.4976 Vatana;-22.2167 Jaqma;33.2920 Bad Frankenhausen;51.3558 Badia Polesine;45.1000 Natonin;17.1089 Lima;-23.9000 Southborough;42.3012 Tanichchiyam;10.0412 Pedda Kotayalanka;16.1567 Golakpur;25.1777 Verucchio;43.9833 Fervedouro;-20.7258 Qiushanxiang;34.3562 Satghara;26.3977 Khariāl;30.1056 Köflach;47.0639 Cabañaquinta;43.1000 Frýdlant nad Ostravicí;49.5928 Valaiyāmpattu;12.6927 Steinau an der Straße;50.3167 Induno Olona;45.8500 Quétigny;47.3144 Robstown;27.7940 Mettuppālaiyam;11.1747 São Domingos;-10.7908 Winchendon;42.6667 Corumbá de Goiás;-15.9239 South Lebanon;40.3058 Kudelstaart;52.2339 Werneck;49.9833 Essey-lès-Nancy;48.7058 María Pinto;-33.5167 Chuhal;31.5950 Pernes-les-Fontaines;43.9978 Biot;43.6286 Pont Sondé;19.1493 Shamva;-17.3167 Kemin;42.7861 San Diego Country Estates;33.0094 Firou;10.9192 Nanbu;35.3403 Głogów Małopolski;50.1667 Rājepur;25.5377 Levanger;63.7465 Førde;61.4522 Vaiano;43.9667 Bethanie;-26.4833 Blairgowrie;56.5916 Kondakindi Agrahāram;14.6790 Benedito Novo;-26.7828 Letychiv;49.3833 Datian;25.4379 Rāmpatti;26.3333 London;39.8935 Kut Chap;17.4262 Sylva;58.0333 Münchberg;50.2000 Victoria;44.8634 Nideggen;50.7000 Bernay;49.0886 Bounaamane;29.5283 Kirkel;49.2833 Capriolo;45.6373 San Simón;13.8333 Nallikodūr;17.5811 Nordkirchen;51.7381 Taksimo;56.3315 Hire Megalageri;14.5440 Alderwood Manor;47.8146 Banga;-8.7333 Pālepalli;12.5667 Oostzaan;52.4333 Neustadt;50.8500 Aydarken;39.9400 Kalgi;17.3500 São Carlos;-27.0778 Santa María de Palautordera;41.6953 Berwick;41.0555 Kiklah;32.0683 Bowdon;53.3760 Buckie;57.6760 Payerne;46.8167 Padugaipattu;8.3642 Venecia;5.9167 Srīsailain;16.0833 Korolevo;48.1500 Sobral de Monte Agraço;39.0180 Goito;45.2500 Portage;43.5489 Murowana Goślina;52.5667 Monsenhor Gil;-5.5639 Nagyatád;46.2227 Panasapādu;17.0185 Conceição da Aparecida;-21.0939 El Jicaral;12.7286 Rancho Arriba;18.7147 Kīl Vālūr;10.7659 Monte Alegre do Piauí;-9.7539 Ottendorf-Okrilla;51.1792 Exeter;36.2941 South Huntington;40.8225 Kazo;-0.0528 Roddam;14.1000 Santa Isabel Ishuatán;13.6167 Murfatlar;44.1736 Sandalpur;25.5639 Gagnef;60.6000 Iapu;-19.4369 Örkelljunga;56.2833 Tiruchchuli;9.5348 Vorsma;55.9833 Ksar Lmajaz;35.8428 Recreio;-21.5250 Jordânia;-15.9000 Teocuitatlán de Corona;20.0918 Kalakeri;16.6667 Buzdyak;54.5711 Normandia;3.8808 Karattuppālaiyam;11.4402 Kalinagar;22.4206 Fort Madison;40.6207 Koila;25.4550 Launaguet;43.6739 Marpingen;49.4500 Weston;41.2284 Paranã;-12.6150 Aich;48.6228 Dāra;26.1546 Rouvroy;50.3936 Ganapavaram;16.4314 Gislaved;57.3000 Cedral;-2.0000 Ben Nasseur;33.1107 Winterville;35.5291 Winchester;41.9218 Darsur;25.9711 Lenguazaque;5.3069 Engenheiro Caldas;-19.2189 Horti;17.1700 Biandanshan;26.0409 Union Park;28.5657 Santa Maria Madalena;-21.9550 Roca Sales;-29.2839 Fochville;-26.4833 Bilehra;23.6463 Arkadelphia;34.1253 Paris;36.2933 Tirumangalakkudi;11.0214 Presidente Kennedy;-21.0989 Friedeburg;53.4500 Oliveira de Frades;40.7167 Volterra;43.4000 Crosia;39.5667 Al Jazīrah al Ḩamrā’;25.7089 Lolokhur;26.4136 Nāvinipatti;10.0439 Aperibé;-21.6208 Northampton;40.6866 Bieber;50.6000 Bangaon;26.3081 Bhatauliā;26.4771 Kottampatti;10.6705 London Colney;51.7260 Caparrapí;5.3442 Kranuan;16.7081 Dettingen an der Erms;48.5300 Vytegra;61.0000 Chavinda;20.0167 Welkenraedt;50.6606 Aghbalou n’Kerdous;31.6767 Lexington;40.7779 Rifle;39.5362 Wood River;38.8631 Dari;23.8283 Manglūr;15.5177 Pithiviers;48.1719 Dahua;24.7742 Binéfar;41.8500 Kafr Takhārīm;36.1164 Hipparga;17.4568 Ngọc Sơn;21.3500 Pedras de Maria da Cruz;-15.6069 Ulft;51.8908 Kampong Tunah Jambu;4.9957 Clay;33.6951 Ballina;-28.8333 São Geraldo;-20.9228 Dāita;24.4548 Pineville;35.0864 Cadillac;44.2493 Cordele;31.9563 Qanliko‘l;42.8333 Tyukalinsk;55.8667 Grand-Couronne;49.3575 Montemarciano;43.6333 Cumnock;55.4529 Campo Redondo;-6.2428 Tanaina;61.6576 Coldstream;50.2200 Sirdala;24.6559 Kurşunlu;40.8333 Masquefa;41.5036 Belagola;13.3833 Urucânia;-20.3508 Mandello del Lario;45.9167 Manteswar;23.4225 Bellamkonda;16.4923 Terra Alta;-1.0378 Chākicherla;15.1127 Shankarampet;18.0490 Santa Terezinha de Goiás;-14.4378 Sgamna;32.7333 Cermenate;45.7000 Chester;44.6500 Vālavandānkottai;10.7667 Āttūrkuppam;12.6000 Argayash;55.4889 Ellon;57.3660 Telpur;27.0548 Santo Stefano di Magra;44.1625 Zuidlaren;53.0942 Queens;44.0333 Trajano de Morais;-22.0628 Bilenke;48.7664 Potengi;-7.0908 Kambarka;56.2667 Newport;44.6242 Southampton;39.9137 Banská Štiavnica;48.4581 Saint-Loubès;44.9172 Rāyen;29.5978 Soanpeta;18.9586 Arlöv;55.6333 Rio dos Cedros;-26.7378 Ajjipuram;12.1500 Petrolina de Goiás;-16.0950 Novi di Modena;44.8934 Gafour;36.3400 Sermoneta;41.5500 Higashikagura;43.6966 Międzychód;52.6000 Cêrro Grande;-30.5900 Pilar;17.4168 Erdőkertes;47.6667 Buenópolis;-17.8742 Dalgān;27.4753 Centralina;-18.5839 Roßtal;49.4000 Lontras;-27.1658 Bonham;33.5880 Schönwalde-Siedlung;52.6500 Zaozërnyy;55.9667 Teixeira Soares;-25.3678 Nehoiu;45.3531 Velakkuttai;12.6519 Worcester;40.1899 Tello;3.0667 Groaíras;-3.9128 Villa Elisa;-32.1667 Kāsarkod;14.2500 Hecelchakán;20.1667 Bertem;50.8500 Fairmount;43.0414 Tokunoshima;27.7266 Laligam;12.0500 Chitrāda;17.0802 Jamhra;25.7777 Khiriāwān;25.1120 Ambalavao;-19.1000 Ban Bong Tai;17.4057 Angelim;-8.8833 Alvorada do Sul;-22.7800 Nieuw-Lekkerland;51.8833 Shelton;47.2186 Fenggeling;34.5312 San José de Feliciano;-30.3833 Fiumefreddo di Sicilia;37.8000 Bomareddipalli;18.7044 Minamiaso;32.8167 Corfe Mullen;50.7701 Sanjāt;25.6046 Jogaili;25.9096 Bāsmanpur;26.6433 Miradouro;-20.8908 Dhānga;26.4630 Sant’Egidio alla Vibrata;42.8333 Chivhu;-19.0000 Bhirua;25.8509 Caetanópolis;-19.2950 Ouistreham;49.2792 Enriquillo;17.9000 Sātulūru;16.2541 Pliezhausen;48.5586 Ertil;51.8500 Pushpattūr;10.5438 Brewster;41.7463 Hirni;25.8323 Mondaí;-27.1028 Advi Devalpalli;16.6631 Karariyā;26.5249 São Romão;-16.3689 Belpāra;20.5889 Kenafif;30.4167 Paranacity;-22.9300 Parnaguá;-10.2269 Spout Springs;35.2724 Sapkyo;36.6864 Guadalupe;-6.7869 Waverly;42.7250 Selkirk;50.1436 Saint-Félicien;48.6500 Primavera;-0.9428 Bierbeek;50.8333 Piranhas;-16.4269 Heubach;48.7881 Tūlin;23.3700 Rewāhi;26.2698 East Bradford;39.9590 São Francisco;-5.1228 Lillers;50.5636 Burgum;53.2000 Tabubil;-5.2750 Erada;-25.2833 Sihanamaro;-25.1833 Magstadt;48.7422 Slatina;45.7022 Kodikkulam;9.9811 Ghāriyah al Gharbīyah;32.6828 Amparihitsokatra;-17.5167 Ennamangalam;11.6449 Linthicum;39.2088 Jangalapalle;14.8844 Erenler;38.8197 Vétraz-Monthoux;46.1742 Iizuna;36.7547 Madridejos;39.4667 Sewa;24.8753 Carice;19.3833 Ommangi;17.2597 Manubolu;14.1833 Elkhorn;42.6713 Kapuvár;47.6000 Mālingaon;26.5467 Ban Pa Hung;19.5672 Pachchāmpālaiyam;11.5723 Rockport;28.0290 Rum;47.2872 Busca;44.5167 Bareh;26.6786 Garden City;32.0867 Hawkesbury;45.6000 Steinen;47.6453 Soverato Marina;38.6833 Oster;50.9486 Adamankottai;12.0742 Amritpur;28.1167 Bimāwān;25.5111 Dhanauli;26.0026 Kenār;25.2916 Carroll;42.0699 Vīrapāndiyanpattanam;8.5200 São João d’Aliança;-14.7058 Kearney;39.3550 Halfway;39.6162 Fontoura Xavier;-28.9828 Moimenta da Beira;40.9797 Shankarpur;26.1821 Birkenau;49.5607 Karajgi;14.8641 Pires Ferreira;-4.2469 Três Cachoeiras;-29.4558 Jiji;23.8300 Minatitlán;19.3833 Pabégou;9.8333 Saint-Jean-le-Blanc;47.8919 Francisco Badaró;-16.9928 Lonquimay;-38.4333 Bhāgsar;30.4417 Lanškroun;49.9122 Kāttāgaram;12.4110 Itapé;-14.8978 Lūgovoy;42.9472 Meuselwitz;51.0500 Ankalgi;16.0333 Tiana;41.4831 Alāwalpur;31.4967 Martinópole;-3.2258 Mucugê;-13.0050 Rangasamudram;14.9852 Rājhanpur;25.7885 La’tamna;31.9100 Ban Kang;18.5444 Elmas;39.2667 Were Īlu;10.6000 Kengarai;11.4144 Marawī;18.4833 Bisignano;39.5167 Ronda Alta;-27.7669 Divonne-les-Bains;46.3567 Palafolls;41.6692 Columbia;40.0347 Fanzhao;26.6615 Bandixon;37.8614 Santa María;2.9500 Salvatierra de Miño;42.0833 Chegūr;17.1758 Andergrove;-21.0931 Caspe;41.2333 Nishi;32.2011 Boriguma;19.0468 Senhora dos Remédios;-21.0278 Chaungtha;16.9667 Radyvyliv;50.1286 Hadiāya;30.3413 Fountain Inn;34.6989 Ittikelakunta;16.6797 Beverly Hills;28.9176 Viravāda;17.1194 Charalá;6.2500 Luza;60.6500 Caracol;-9.2789 Hosur;15.8201 Tanggemu Nongchang;36.0750 Balangkayan;11.4728 Xishrov;39.6383 Wingles;50.4942 Cuorgnè;45.3897 Oppeano;45.3000 Kaimāti;26.0854 Pôrto Xavier;-27.9058 Sedico;46.1167 Tarashcha;49.5500 Eumseong;36.9353 Blumberg;47.8392 Kanchanpur;24.6096 Honwāda;16.7333 Kamdoli;15.2048 Ekhari;26.5246 Algarrobo;-33.3911 Makri;24.3860 Redentora;-27.6639 Mariluz;-24.0019 Qovlar;40.9419 Roberval;48.5200 Cumaru;-8.0058 Dhauni;24.9901 Kannāndahalli;12.4164 Doiwāla;30.1760 Velpūru;16.1775 North Bend;43.4075 Sniatyn;48.4500 Cypress Gardens;28.0036 Sainte-Agathe-des-Monts;46.0500 Shyroke;47.6882 Padbury;51.9710 Borzna;51.2539 San Miguel Sigüilá;14.9000 Spelle;52.3667 Ammanford;51.8000 Jemaat Oulad Mhamed;33.0938 Hackettstown;40.8540 Ain Karma;34.0071 Marāi Kalān;24.0211 Foz;43.5694 Elambalūr;11.2669 Jocoro;13.6167 Calasparra;38.2306 Bendrahallī;12.2642 Syców;51.3100 Majhariyā Sheikh;26.7356 Msoga;-6.5667 Teplodar;46.5036 Mangasamudram;13.2291 Mstsislaw;54.0167 Eleşkirt;39.7981 Highland;38.7602 North Dumfries;43.3200 Göytəpə;39.1167 Tefenni;37.3111 Beli Manastir;45.7667 Mādarpākkam;13.4439 Hooglede;50.9781 San Marcos;9.6703 Carqueiranne;43.0950 Lohafary;-23.2500 Ammanabrolu;15.5802 Medina;4.5092 Tutzing;47.9089 Potosí;0.8081 Ankadindambo;-21.4667 Houthulst;50.9783 Rehburg-Loccum;52.4508 Lagoa da Confusão;-10.7939 Chestnut Ridge;41.0829 Smithville;39.3919 Caudete;38.7044 Shioya;36.7776 Governador Archer;-5.0219 Plymouth;41.3483 Alpine;40.4629 Mosquera;2.4903 Ainring;47.8156 Peryavaram;13.9317 Lovejoy;33.4426 São João;-25.8278 Rideau Lakes;44.6667 Tolmezzo;46.4000 Asola;45.2167 Vernon;34.1479 Yelm;46.9398 Pechea;45.6333 Molsheim;48.5428 Pleasant Hill;41.5867 Castellamonte;45.3820 Nārāyanraopet;18.2114 Paris;38.2016 Tipp City;39.9644 Comstock Park;43.0438 Kushijima;32.7401 Olovo;44.1275 Mahabo-Mananivo;-23.1833 Jāmunia;25.3676 Patūt;25.4658 Msila;35.2000 Aleşd;47.0572 Itamogi;-21.0778 Sechelt;49.4742 Tiruvāduturai;11.0379 Cetraro;39.5000 San Blas;21.5397 Gunbarrel;40.0632 São Pedro do Ivaí;-23.8650 Yermolino;55.2000 Satravāda;13.3201 Nong Ki;14.6867 Kulundu;40.1069 Tovāla;8.2482 Tuminkatti;14.4131 Versailles;38.0486 Fort Oglethorpe;34.9318 Aïn Zohra;34.1000 Pullach im Isartal;48.0500 Tissaf;33.4000 Auterive;43.3503 Itamukkala;15.3731 Vigasio;45.3167 Kolnūr;18.5095 Valkurti;18.7171 Lint;51.1167 Friendly;38.7601 Mikhaylov;54.2333 Aldenham;51.6723 Basāpatna;15.4302 Skidal’;53.5861 Cabriès;43.4411 Beccles;52.4580 Nandamūru;16.6400 Kokiladānga;26.3452 Berthoud;40.3071 Sātgāchia;23.2641 Vysokyi;49.8850 Cingoli;43.3667 Cheat Lake;39.6670 San Sebastiano al Vesuvio;40.8333 Venkatāpuram;18.2232 Harrison;40.6374 Za’roura;35.2167 Takahama;35.4903 Tocina;37.6000 Catanduvas;-25.2028 Gobindpura;30.2805 Antanananivo;-16.3000 Chilpur;18.2388 Standish;43.7811 Milford;40.4291 Sundarapāndiyam;9.6127 Swāmimalai;10.9575 Bāsudebpur;21.8256 Pādarti;15.4690 Alpena;45.0740 Přelouč;50.0399 Almoloya del Río;19.1586 Uchtepa Qishlog’i;40.2050 Kokoszki;54.3541 Toca;5.5667 Annan;54.9830 Kawara;33.6680 Oelsnitz;50.4167 Jāvagallu;13.3000 Tut;37.7967 Elūrupādu;16.5167 Fort William;56.8198 Bayyavaram;17.6638 Kirkland;43.0368 Pińczów;50.5333 Souama;36.6417 Le Passage;44.2014 Sleepy Hollow;41.0936 Gambissara;13.2333 Caatiba;-14.9769 Pariyāri;25.2128 Ugargol;15.7800 Remada;32.3061 Ippagūdem;17.7668 Novo Horizonte;-11.7100 Mālior;25.3900 Shpola;49.0333 Naters;46.3237 Granada;6.1470 Covasna;45.8492 Tellār;12.4011 Yatton;51.3855 Mazamet;43.4917 Garliava;54.8167 Batán;-38.0078 Muurame;62.1292 Jamunāmukh;26.1015 Chada;17.4992 Woodburn;38.8503 San Pablo;-40.4000 Moree;-29.4650 Siechnice;51.0367 Nové Město na Moravě;49.5615 Visbek;52.8333 Cachoeira dos Índios;-6.9269 Kingaroy;-26.5408 Ech Chaïbat;31.6000 Thāthūpur;25.9731 Astoria;46.1856 Hillsdale;41.0074 Racconigi;44.7667 Fouesnant;47.8933 Mālipākar;25.3567 Erquelinnes;50.3101 Laakirchen;47.9828 Majhaulia;25.9635 Zumárraga;43.0831 Willstätt;48.5417 Canápolis;-13.0700 Calçoene;2.4978 Bucine;43.4833 Caém;-11.1000 Phak Hai;14.4626 Holsbeek;50.9167 Sung Noen;14.8965 Citrus Springs;28.9931 Gangaura Behra;25.9541 Heeze;51.3825 Byarozawka;53.7167 Embrach;47.5103 Ban Bu Sung;14.9602 Kāndra;22.8517 Avalūrpet;12.3800 Orange Lake;41.5369 Gakuch;36.1736 Pepinster;50.5667 Bāghduma;24.8223 Mainaschaff;49.9833 West Vero Corridor;27.6363 Kele;6.0833 Bad Breisig;50.5092 Mangalam;11.6368 Opochka;56.7000 Meymand;28.8678 Jucuruçu;-16.8428 Siddarāmpuram;15.5300 Gavirate;45.8500 Banbhāg;25.7919 Marlton;38.7620 Ogdensburg;44.7088 Chunakhali;22.3010 Sîngera;46.9139 La Misión;21.1000 Loudonville;42.7068 Ozimek;50.6731 Liminka;64.8083 White City;42.4316 Goldenstedt;52.7833 Yaotsu;35.4760 West Point;33.6064 Choele Choel;-39.2667 Ānjukulippatti;10.2444 Kishanpūra Kalān;30.9337 Génova;1.6442 Liberty;41.8132 Atāri;31.6008 Corte Madera;37.9238 Kodakkal;13.0685 Wald;47.2753 Davutlar;37.7333 Reddippatti;11.2100 Bruino;45.0167 Huldenberg;50.7833 Rinópolis;-21.7258 Marondry;-18.4167 Manvel;29.4798 Rokkasho;40.9672 Elūrpatti;11.0234 Carrillos;10.0369 Steinhaus;47.1969 Nathāna;30.3155 Pyetrykaw;52.1333 Zavolzhsk;57.4667 Matadepera;41.6036 Alhendín;37.1167 Santa Isabel Cholula;19.0000 Iwashita;32.6516 Serebryansk;49.6819 Arrapalli;18.0718 Schotten;50.5000 Morrovalle;43.3167 Chamusca;39.3500 Mengibar;37.9683 Sugbongkogon;8.9500 Tolbazy;54.0242 Ūttumalai;8.9916 Ban Pong Yaeng Nai;18.8833 Indūrti;18.2233 Canton;41.8600 Ardal;31.9989 Rāsak;26.2361 Sofiivka;48.2683 Oldenburg in Holstein;54.3000 Massi;9.9167 Progress Village;27.8831 Touwu;24.5833 Hermantown;46.8057 Sovicille;43.2833 Kumçatı;37.4710 Kuroshio;33.0167 Rio do Fogo;-5.2728 Alexandria;38.9621 Shohimardon;39.9833 Keuruu;62.2583 Hathiākān;25.6060 Mashhad Rīzeh;34.7922 Namorona;-21.6500 Großhansdorf;53.6667 Ipupiara;-11.8200 Água Branca;-7.5119 Tungāvi;10.6263 Hull;42.2861 Le Beausset;43.1983 Parūr;11.5793 Satipo;-11.2542 Chansolme;19.8833 Rüdesheim am Rhein;49.9833 Judenburg;47.1725 Cambridge;40.0221 Tuam;53.5150 Iretama;-24.4239 Suttamalli;8.6987 Bālgudar;25.2000 Murungattoluvu;11.1651 Dhanwāda;16.6500 Lysá nad Labem;50.2015 Bagnolo in Piano;44.7667 Sulzbach;50.1331 Dumont;-21.2364 Red Hook;42.0188 Klötze;52.6263 Orotina;9.9024 Kirchlinteln;52.9428 Saladoblanco;2.0167 Ōnan;34.8939 Coal;40.7873 Zaggota;34.1667 Boucau;43.5236 North Glengarry;45.3333 Hosahalli;14.6480 Makaya;25.2242 Ledegem;50.8531 Digora;43.1581 Nisarpur;22.1088 Fairfield;33.4747 Airway Heights;47.6459 Gambolò;45.2586 Cavriago;44.7000 Melendugno;40.2667 Sarız;38.4792 Pavlikeni;43.2428 Madera Acres;37.0123 Kotli Ablu;30.3504 Poienile de sub Munte;47.8167 Meldola;44.1333 Condé-sur-l’Escaut;50.4492 Maiquinique;-15.6208 Dylym;43.0710 Parsād;26.1586 Aigali;16.7200 Macomer;40.2667 Roboré;-18.3333 Lescar;43.3250 Heves;47.6000 Krivodanovka;55.0881 Lenggries;47.6803 Kamalasai;16.3383 Santa Teresa;-25.0519 Oberhausen-Rheinhausen;49.2606 Nogliki;51.8333 Kinhālu;15.4431 Nayāgaon;24.5622 Kurort Steinbach-Hallenberg;50.7006 Töging am Inn;48.2500 Bela Vista de Minas;-19.8300 Tomboutou;11.8550 South Huron;43.3200 Terku Narippaiyūr;9.1167 Sakuho;36.1610 Cairo;30.8790 Penn;40.1864 Brembate;45.6000 Marieville;45.4333 Mengen;48.0497 Rainbow City;33.9336 Beilngries;49.0333 Crigglestone;53.6440 Mori;45.8500 Wölfersheim;50.3975 Bilāspur;30.3044 Bou Nouh;36.5000 Kidira;14.4167 Boaz;34.1985 Snohomish;47.9276 Cedartown;34.0223 Bamora;24.0554 Hingyon;16.8522 Çeltik;39.0244 Francisville;39.1068 Sinimbu;-29.5389 Uppūr;13.3945 San Felice Circeo;41.2353 Waynesville;35.4854 Levski;43.3667 Waldwick;41.0133 Chavuttahalli;12.4202 Kösching;48.8167 Niederhasli;47.4822 Makale;-3.1000 Lesquin;50.5897 Ramiriquí;5.4000 Hongliuwan;39.6348 Khathjari;24.7046 Arques;50.7356 Pasaul;26.2647 Harewa;25.6831 Figeac;44.6086 Bīrpur;32.6617 Chilakhāna;26.2990 Quartz Hill;34.6527 Dharāwat;25.0592 Kümmersbruck;49.4167 Bergambacht;51.9333 Bhagatpur;25.4098 La Leonesa;-27.0500 Thogadūru;12.0957 Buenavista;9.3222 Sannicandro di Bari;41.0000 Rellivalasa;17.9108 San Agustín de las Juntas;17.0000 San Isidro;9.8000 Tiruvengadam;9.2586 Bad König;49.7500 Ikryanoye;46.0903 Sendamangalam;11.7431 Kharagbani;26.4852 Harrisonville;38.6530 Red Bank;33.9309 Berd;40.8808 Tsunō;32.2565 Bāg;22.3590 Hazle;40.9558 Ware;42.2806 Woodbury;39.8379 Zoudjamé;6.8167 Aveley;51.5018 Hayle;50.1860 Lovendegem;51.1000 Chegurumomadi;18.2361 Taghbalt;30.6200 Portland;-38.3333 Sakhua;26.1734 Maków Mazowiecki;52.8667 Littleton;42.5350 Pescaria Brava;-28.3833 Planalto;-27.3289 Aire-sur-la-Lys;50.6386 Banāso;23.9884 Kosh-Agach;49.9927 Fairview;33.1399 Birch Bay;48.9243 Inverigo;45.7333 North Versailles;40.3785 Kalaīkunda;22.3392 Büyükorhan;39.7500 Lambesc;43.6539 Haria;-3.5833 Molalla;45.1502 DeRidder;30.8468 Nūtakki;16.4139 Ivančice;49.1014 Saint-Pierre-du-Mont;43.8825 Struthers;41.0510 Muskegon Heights;43.2023 Paradarāmi;13.0833 Czersk Pomorski;53.7928 Kargopol;61.5000 Pachalum;14.9269 Guaraciaba;-26.5989 San Pancrazio Salentino;40.4167 Bhatkhori;25.9156 Dabhaura;25.1162 Garlasco;45.2000 San Ignacio de Moxo;-14.9961 Cody;44.5213 Nandiyālam;12.9237 La Plata;38.5352 Ahmadpur;23.8301 Başmakçı;37.8833 Calimesa;33.9874 Browns Mills;39.9737 Blain;47.4761 Rödinghausen;52.2550 Barwān;23.9409 Pine Ridge;28.9330 East Liverpool;40.6333 Golden Hills;35.1409 Moba;-7.0398 Tiruvambalapuram;8.2514 Worpswede;53.2222 Lambertville;41.7484 Mānbāzār;23.0615 Simrol;22.5387 ’Aïn Leuh;33.2833 Santamāgulūru;16.1303 Barga;44.0750 Shyamnagar;22.9700 Lentvaris;54.6500 Sankt Andrä;46.7667 Frickenhausen;48.5928 Vemulanarva;16.9660 Geylegphug;26.8706 Bo‘z;40.6833 Banino;54.3922 Mayfield;36.7371 Muddāda;18.2385 Milton;30.6286 Osthofen;49.7078 Puszczykowo;52.2817 Janhapāra;21.3390 Būdalūr;10.7861 Piploda;23.6073 Šurany;48.0872 Bou’nane;32.0275 Vernal;40.4517 Nohfelden;49.5867 Nallamadu;16.8802 Pullūru;16.8301 San Pedro Huamelula;16.0167 Sola;21.6744 Santa María Ajoloapan;19.9692 Madhubani;27.0001 Yorkshire;38.7882 Tay;44.7167 Santa Margherita Ligure;44.3333 Kalladai;10.7272 Dubove;48.1781 Suaita;6.1019 Punjai Lakkāpuram;11.3050 Neglūr;14.9000 Pāchhāpur;16.2900 Coycoyan de las Flores;17.2713 Sulęcin;52.4444 Periyapuliyūr;11.4296 Huejuquilla el Alto;22.5333 Valozhyn;54.0833 Gudensberg;51.1833 Khandāich;24.8511 Monument;39.0736 San Elizario;31.5793 Diabougou;14.5431 Volodarsk;56.2167 Urgnano;45.5972 Gassino Torinese;45.1271 Howell;42.6078 Caraíbas;-14.6000 Bude;50.8240 Moravská Třebová;49.7580 Wielsbeke;50.9089 Koumpentoum;13.9833 Bikrampur Bānde;25.8415 Umbrete;37.3667 Valea lui Mihai;47.5200 Ohrdruf;50.8281 Galleh Dār;27.6594 Hickam Housing;21.3311 Purkersdorf;48.2092 Kami-kawabe;35.4866 Ilsfeld;49.0500 Kusmaul;26.1911 Puigcerdá;42.4317 Devmaudaldal;24.6312 Kanangle;16.2900 Flowood;32.3359 Taltal;-25.4000 Senanga;-16.1167 Dodarasinakere;12.5083 Grosse Pointe Farms;42.4068 Fredonia;42.4407 Vuktyl;63.7000 Maisenhausen;50.0172 Glenwood Springs;39.5455 Mont-Saint-Martin;49.5406 Alajärvi;63.0000 Navani;11.3709 Analalava;-14.6333 Hakka;25.9163 Lakhna;25.8865 Nogent-le-Rotrou;48.3217 Rahta;25.9213 Kapelle-op-den-Bos;51.0167 Bāba Bakāla;31.5500 Şūfīān;38.2722 El Cairo;4.7500 Cabo Rojo;18.0867 Palankottai;9.1356 Menzel Kamel;35.6333 Hiranai;40.9259 Jaisinghnagar;23.6260 Alkhan-Yurt;43.2317 Fuente Palmera;37.7000 Vadavālam;10.4286 Paratdiha;24.1638 Hoeselt;50.8500 Uchoa;-20.9528 Saint-Doulchard;47.0997 Glocester;41.8934 Muturkha;24.1044 Kanhauli;26.4815 Kittery;43.0998 Gandikunta;16.1699 Aytré;46.1342 Aizenay;46.7400 Raipur;25.4226 Bānki;26.1497 Pathāri;23.9333 Port Townsend;48.1220 Warrenton;38.7176 Ghadāmis;30.1333 Saint-Amand-Montrond;46.7228 Al Mazyūnah;17.8486 Gar;32.1166 Bad Liebenzell;48.7742 Arauá;-11.2619 Merville;50.6439 Kadimetla;15.7376 Baker City;44.7749 Gūdalūr;10.7830 San Calixto;8.4000 Giesen;52.2000 Kinnelon;40.9847 Ratne;51.6500 Gundi;18.6237 Vardenik;40.1331 Oudenburg;51.1833 Fernán-Núñez;37.6667 Lapinlahti;63.3667 Pagidyāla;15.9330 Hlinsko;49.7622 Lizzano;40.3919 Tarhjicht;29.0564 Ponneri;12.5972 Velpūru;16.1496 Chinnakkavundanūr;11.4559 Oued Laou;35.4500 Uniontown;39.8993 Bundehra;25.3776 Zafargarh;17.7686 Ruffano;39.9833 Corozal;18.3410 Turgutalp;39.1833 Vegachí;6.7731 Foix;42.9653 Matias Cardoso;-14.8550 Magny-les-Hameaux;48.7239 Tadangam;12.1056 Yenice;36.9667 Magnago;45.5792 Heilsbronn;49.3167 Chilanga;13.7167 Coronel Freitas;-26.9089 São Sebastião do Maranhão;-18.0839 Clinton;36.0981 Cristino Castro;-8.8178 Campobello di Licata;37.2500 Somasamudram;15.1500 Sauk Village;41.4906 Safford;32.8335 La Sierra;2.2500 Boves;44.3333 Greencastle;39.6432 Vlist;51.9667 Teotitlán;18.1333 Cape Canaveral;28.3933 Margny-lès-Compiègne;49.4261 Bastak;27.1992 Keokuk;40.4095 Karankot;17.2866 Gudimūlakhandrika;16.4090 Älmhult;56.5500 Cedar Hills;40.4135 DuPont;47.1079 Ii;65.3167 Naqneh;31.9336 Punnaikkāyal;8.6322 Chinnāmpālaiyam;10.6604 Budd Lake;40.8733 Boudinar;35.1500 Pasian di Prato;46.0500 Ventania;-24.2458 Budhma;25.6451 Consuegra;39.4619 Honge;15.8800 Mīāndasht;33.0736 Pereshchepyne;49.0179 Daruvar;45.5929 Saint-Lys;43.5142 Immingham;53.6139 Wāngjing;24.3862 Santa Cruz Atizapán;19.1756 Dongcha;34.3800 San Francisco la Unión;14.9167 Strzelce Krajeńskie;52.8756 Imaruí;-28.3408 Liesveld;51.9156 Shāhganj;22.8467 Catunda;-4.6478 Jaisinghnagar;23.6858 Airmont;41.0992 Coapilla;17.1167 Ḩās;35.6169 Cuicatlan;17.8000 Itatiaiuçu;-20.1969 Ribadeo;43.5336 Ploërmel;47.9317 Dessel;51.2333 Coroaci;-18.6219 Leppävirta;62.4917 Sorkheh;35.4633 Mareno di Piave;45.8409 Höhr-Grenzhausen;50.4350 Sande;59.5936 Gessate;45.5500 Genemuiden;52.6244 Maihma Sarja;30.3110 Novyye Atagi;43.1328 Tha Mai;12.6196 Sparta;43.9377 Manduri;-23.0033 Chintapalle;17.8667 Lenoir City;35.8111 Estavayer-le-Lac;46.8500 Pinos Puente;37.2500 Māmidipalli;18.7772 Fair Oaks Ranch;29.7467 Piedras Blancas;43.5600 Monteriggioni;43.4000 Treillières;47.3308 Terra de Areia;-29.5850 Holywell;53.2740 Wadgira;16.5858 Kourimat;31.4500 Mādhavaram;16.8900 Néa Moudaniá;40.2386 Bukowno;50.2681 Großröhrsdorf;51.1444 Colindres;43.3967 Govindāpuram;16.1548 Worth;51.1130 Towcester;52.1300 Vidor;30.1291 Malhārgarh;24.2829 Umburetama;-7.6958 Hamīra;31.4589 Bellavista;6.5236 Reedsburg;43.5348 Panjāb;34.3833 Mae Ai;20.0296 Conselice;44.5167 North Lakes;61.6191 Fairmount;39.7931 Outa Bouabane;34.2606 Zell am See;47.3233 Saint-Rémy-de-Provence;43.7894 Gunnedah;-30.9817 Jagannādapuram;13.2745 Pokrovske;47.9853 Majali;14.9000 Ingré;47.9206 Zaozërsk;69.3978 Lagbé;6.6833 Gidha;26.0507 Pangunattam;12.1112 Nambutalai;9.7277 Kostrzyń;52.3942 Montescaglioso;40.5500 Tafersit;35.0167 Keora;25.4374 Katahra;26.2235 Deruta;42.9833 Dināra;25.4613 Caldas de Reyes;42.6028 Rubim;-16.3750 Rurka Kalān;31.0700 Little River;33.8786 Willoughby Hills;41.5873 Bad Gandersheim;51.8719 George Mason;38.8356 Urzhum;57.1167 Kendallville;41.4441 Marysville;42.9084 Byalynichy;53.9956 Kattamūru;17.0800 Castel Bolognese;44.3167 Mount Vista;45.7373 Jilotlán de los Dolores;19.3719 Beneditinos;-5.4550 Vargem;-22.8889 Pórto Ráfti;37.8844 Hardiya;25.5301 Brandis;51.3347 Saarijärvi;62.7050 Waltenhofen;47.6667 Saidia;35.0850 Kaikaram;16.8120 East Setauket;40.9210 Wapakoneta;40.5664 Bee Ridge;27.2855 Cosne sur Loire;47.4103 Anantpur;16.8890 Temiskaming Shores;47.5167 Carregal do Sal;40.4333 Ngaparou;14.4631 East Rutherford;40.8179 Mount Holly;39.9950 Idumbāvanam;10.4224 Altmünster;47.9000 Coello;4.3333 Juru;-7.5369 Argelato;44.5758 Talachyn;54.4167 Valtoha;31.2074 Poggio Renatico;44.7650 Non Sung;15.1788 Fällanden;47.3717 Tiptree;51.8100 Aranzazu;5.3000 Mirante;-14.2419 Kudowa-Zdrój;50.4383 San Pedro Ixtlahuaca;17.0500 Ingichka;39.7389 Revel;43.4586 Margarita;9.0833 Rāmpura;14.8810 Rute;37.3167 Rye Brook;41.0302 Punnavalli;16.4062 Konidena;16.0156 Colac;-38.3403 Mīnākshipuram;9.9908 New Richmond;45.1250 Ellisville;38.5897 Agoué;6.2500 Monticello Conte Otto;45.6000 Brinkmann;-30.8669 Rautara;25.6630 Worsborough;53.5200 Adiyakkamangalam;10.7626 Sasaima;4.9650 Rotonda;26.8844 Queensferry;55.9900 Csorna;47.6167 Trancoso;40.7833 Balatonalmádi;47.0333 Redon;47.6514 Devanāngurichchi;11.3895 Jesup;31.5992 Lago Ranco;-40.3167 La Grange;38.3987 Nāgathān;16.8300 Khutha Baijnāth;26.0268 Alcorta;-33.5333 Dagarua;25.8000 San Giovanni in Marignano;43.9393 Tenente Ananias;-6.4650 Tāmganj;26.2478 Bisaul;26.6151 Karczew;52.0833 Åstorp;56.1347 Nong Wua So;17.2702 Qiziltepa;40.0361 Néa Artáki;38.5167 Bellinzago Novarese;45.5833 Jāwalgeri;15.8656 Patchūr;12.5949 Resana;45.6333 Ashukino;56.1611 Chimay;50.0500 Rothrist;47.3028 Mezőberény;46.8167 Cachipay;4.7308 Chevy Chase;38.9944 Nadimpālem;16.2123 Siano;40.8025 River Vale;41.0138 Uppalaguptam;16.5667 Vaikuntam;11.5197 Bjärred;55.7167 Saidoke;30.5267 Kushnarënkovo;55.1049 Sundarsi;23.2688 Gangādhar;18.5883 Franklin;36.7177 Alukkuli;11.4458 Santa María Jacatepec;17.8500 Mission;39.0270 Narikombu;12.9000 Oakbrook;38.9996 Bockenem;52.0117 Hinton;53.4114 Sānampūdi;16.0860 Alaçatı;38.2814 Kondalahalli;14.7200 Saint-Sauveur;45.9000 Toudja;36.7586 Powdersville;34.7826 Zinkiv;50.2081 Luçon;46.4547 Bastrop;32.7748 Quesnel;52.9784 Beuvry;50.5197 Torrinha;-22.4258 Dodworth;53.5417 San Bartolomé Milpas Altas;14.6046 Muddanūru;14.6667 Ruoqiang;39.0181 Moyogalpa;11.5403 Varna;53.3806 Baisuhalli;12.2064 Qahjāvarestān;32.7039 Norosí;8.5261 Singhāna;22.1902 Ferryhill;54.6900 Hawera;-39.5933 Hobart;44.4967 Dasso;7.0167 Titu;44.6622 Pobiedziska;52.4833 Baikunthapur;22.9117 Guateque;5.0056 Pinjranwān;25.1702 Serafimovskiy;54.4333 Flossmoor;41.5391 Arden Hills;45.0721 Giardini;37.8333 Nāgasamudra;14.7200 Manchenahalli;13.5007 Villas;39.0157 Hornsby Bend;30.2388 Kobeliaky;49.1474 Gāndlapenta;14.0500 Pedavīdu;16.8558 Bueng Khong Long;17.9667 Berezivka;47.2039 Quilombo;-26.7258 Beltangadi;12.9795 Sarapuí;-23.6408 Havanūr;14.8667 Atoka;35.4239 Belomorsk;64.5253 Broni;45.0619 Peru;41.3482 Ciudad de Loreto;-28.3000 Makariv;50.4648 Piombino Dese;45.6067 Aulnoye-Aymeries;50.2047 San Vicente;-1.9000 Croydon;40.0911 Renaico;-37.6667 Jerissa;35.8500 Litovel;49.7012 Kambaneri Pudukkudi;9.0685 Seysses;43.4981 Łobez;53.6333 Independent Hill;38.6404 Elizabethtown-Kitley;44.7000 Sabana Larga;18.5850 Nersingen;48.4289 Mora;39.6840 Pailón;-17.6594 Mulungu;-7.0239 Brandywine;38.6963 Winfield;41.8787 Mosjøen;65.8370 Photharam;13.6918 Vanukūru;16.4406 Chom Thong;18.4901 Rio Vista;38.1765 Independence;44.8547 Plüderhausen;48.7950 Gold;33.5874 Morinville;53.8022 El Roble;9.1000 Wendell;35.7819 The Pinery;39.4462 Breuillet;48.5661 Drezdenko;52.8333 Kondayampālaiyam;11.5130 Artigues-près-Bordeaux;44.8606 Virālimalai;10.6023 Badagabettu;13.3335 Çıldır;41.1289 Saint-Barthélemy-d’Anjou;47.4675 Gangūru;16.4833 Ekalbehri;21.8933 Radekhiv;50.2828 Stansbury Park;40.6356 Veauche;45.5619 Trebbin;52.2167 Jennings;30.2233 Santa Bárbara;9.6000 Panganiban;13.9000 Roztoky;50.1585 Nurobod Shahri;39.6086 Kannāl;18.6938 Ban Nong Tong;18.6115 Zorneding;48.0833 Rāmasingavaram;16.8863 Schlitz;50.6667 Lāila;12.9900 Manchi;12.9000 Nykøbing Mors;56.7953 Włoszczowa;50.8542 Mallappādi;12.5273 San Sebastián;1.8439 Raun;26.3431 Socotá;6.0500 Bishunpur;25.5665 Pulivalam;10.7525 Rasht;39.0167 Couzeix;45.8761 St. Augustine Shores;29.8039 Haţeg;45.6075 Murājpur;25.9506 Støvring;56.8867 Kirangūr;12.4316 Ulipuram;11.4667 Suntar;62.1575 Plains;41.2657 Guia Lopes da Laguna;-21.4578 Ninheira;-15.3208 Osterburg;52.7833 Perumbālai;11.9635 Imām Şāḩib;37.1844 Halls;36.0817 Yazıkonak;38.6167 Madhubani;26.3272 East Stroudsburg;41.0023 Puraini;26.0607 Gökçeada;40.1608 Oulunsalo;64.9333 Uppugunduru;15.6730 Oosterwolde;52.9903 Alcanar;40.5430 General Alvear;-36.0333 Udburu;23.0333 Berchha;23.2823 Sairé;-8.3278 Triuggio;45.6667 Inole;17.8682 Almusafes;39.2903 Pizarra;36.7667 Parol;32.3460 Dolianova;39.3833 Mozzate;45.6833 Eisenberg;49.5614 Masku;60.5708 Gorom-Gorom;14.4500 Dattapāra;22.8491 Bādanahatti;15.3176 Dorogobuzh;54.9167 Brockworth;51.8500 Totma;59.9667 Cobham;51.3290 T’q’ibuli;42.3503 Løgten;56.1643 Brownsville;35.5890 Calca;-13.3230 Thīkri;22.0634 Sorbolo;44.8463 Everswinkel;51.9250 Chiang Klang;19.2930 Grey Highlands;44.3333 São Gonçalo do Rio Abaixo;-19.8258 Ban Krot;14.3121 Jujhārpur;25.7514 Tišnov;49.3487 Vettweiß;50.7389 Aldona;15.5800 Nerinjippettai;11.6543 Basse-Goulaine;47.2153 Ban Nam Dip Luang;18.4500 Wepener;-29.7333 Harbatpur;30.4500 Constantina;-27.7350 Guntramsdorf;48.0483 Piru;25.1009 Heiligenhafen;54.3739 Thap Khlo;16.1600 Barracão;-26.2539 Yekāmbarakuppam;13.3168 Leeton;-34.5667 Tadla;32.4409 Adjarra;6.5333 Wiang Sa;8.6364 Marshall;40.6453 Biei;43.5883 Chikkāla;16.9698 Jambukuttaippatti;12.3246 Bhogāpuram;18.0667 Tesalia;2.4833 Nyurba;63.2833 Marlboro Village;38.8307 Highfields;-27.4633 Otočac;44.8667 Ostercappeln;52.3500 Saidābād;25.5489 Alto Piquiri;-24.0278 Matelica;43.2566 Salmānshahr;36.7092 Honganur;12.6044 Irungalūr;10.9399 Belalcázar;5.0000 Hueyotlipan;18.9000 Neshannock;41.0509 Eksjö;57.6669 Palomares del Río;37.3167 Flero;45.4835 La Ravoire;45.5569 Carnaubais;-5.3408 Barbana;26.0719 Aklim;34.9167 Suzdal;56.4211 Polorós;13.8108 Kemberg;51.7833 Alfaro;42.1783 Eschenbach;47.2709 Mae Rim;18.9163 Puerto Tirol;-27.3667 Sohta;26.2149 Caturama;-13.3289 Hostotipaquillo;21.0603 Pettāmpālaiyam;11.3499 Scionzier;46.0572 Zāhed Shahr;28.7450 Colmenarejo;40.5608 Tweed Heads;-28.1833 Hattula;61.0556 Nordwalde;52.0833 Santo Tomás de los Plátanos;19.1817 Mońki;53.4000 Kāliganj;23.7348 Concordia;10.2667 Studénka;49.7234 Çamoluk;40.1333 Quiculungo;-8.5167 Sarpamāri;26.3841 Basavilbaso;-32.3667 Vardhamānkota;17.3775 Galten;56.1533 Alcarraz;41.5638 Joigny;47.9822 Būdanūr;12.5500 Nirna;17.7700 Gobindpur;24.7820 Hacarí;8.3167 Loria;45.7333 Ócsa;47.2934 Plainville;42.0141 Lādhuka;30.5082 Podu Iloaiei;47.2167 Villarrubia de los Ojos;39.2167 Mesetas;3.3781 Wysokie Mazowieckie;52.9192 La Matanza de Acentejo;28.4403 Grimmen;54.1100 Picnic Point;47.8744 Sabangan;17.0044 Wagner;-12.2869 Washington;35.5586 Fateh Nangal;31.9453 El Haouaria;37.0500 Cardeal da Silva;-11.9419 Altlandsberg;52.5667 Glencoe;-46.1833 Aghbalou Aqourar;33.9341 Sarkad;46.7500 San Fausto de Campcentellas;41.5061 Puerto Nariño;-3.7733 Oberstdorf;47.4097 Nueva Esparta;13.7833 Sidi Ahmed El Khadir;32.5167 Argostóli;38.1739 Saint-Vith;50.2833 Dushanovë;42.2347 Bijeraghogarh;23.9955 Tazarka;36.5500 Felpham;50.7905 Douar Lehouifrat;32.2800 Schmitten;50.2697 Nashtīfān;34.4344 Senmanat;41.6086 San Juan del Puerto;37.3167 Fürstenau;52.5167 Eidson Road;28.6677 Sint Willebrord;51.5503 Léguevin;43.5989 Kukraun;25.7647 Ussel;45.5481 Tepe-Korgon;40.6000 Irigny;45.6731 Sivamalai;11.0319 Somvārpet;12.5970 College Place;46.0419 San Pedro;-33.8944 Unāo;25.5784 Vesele;47.0189 Fuli;23.1333 Nakao;35.3307 Sevilla La Nueva;40.3475 Gorgāb;32.8658 Dāmargidda;16.8189 Aleksandrov Gay;50.1333 Pingree Grove;42.0855 Corella;9.6833 Punta del Este;-34.9667 Voitsberg;47.0483 Valsequillo de Gran Canaria;27.9808 Yalagüina;13.4858 Zafferana Etnea;37.6833 Tuta;5.7000 Kadiyadda;16.8774 Pathrāha;26.4330 Sekha;30.3586 Nolinsk;57.5667 Ayas;24.2508 Sunbury;40.8616 Rājapūdi;17.1830 Kātūria;24.7476 Huari;-9.3689 Monmouth Junction;40.3754 Loreto;10.3586 Harran;36.8708 Nhân Trạch;19.0500 Tremonton;41.7187 Garrison;39.4023 Chandwārā;24.3930 Middleton;42.6043 Alsbach-Hähnlein;49.7413 Urangānpatti;9.9984 Saint-Sulpice-la-Pointe;43.7742 Golbey;48.1958 Avon Park;27.5898 Loudéac;48.1778 Majra;25.7131 Sedona;34.8581 Kommūru;16.0667 Idanha-a-Nova;39.9167 Halen;50.9481 Paso Canoas;8.5333 Spanish Fort;30.7254 Kottūr;9.9038 Shchuchye;55.2167 Akabira;43.5581 Liteni;47.5200 Imías;20.0767 Detroit Lakes;46.8060 Khorāgāchhi;26.3344 Bni Boufrah;35.1000 Nattakkādaiyūr;11.0882 Kasāp;25.4702 Pāppampatti;10.4446 Beckett Ridge;39.3448 Sweet Home;44.4023 Khem Karan Saray;25.1233 Ghorbanki;26.5697 Wehrheim;50.3033 Arenys de Munt;41.6128 Chełmek;50.1167 Lüchow;52.9667 Nové Město nad Metují;50.3446 Bardmoor;27.8574 Berlin;44.4869 Makaha;21.4739 White Horse;40.1919 Bisingen;48.3119 Ehningen;48.6589 El Espinar;40.7186 San Marzano di San Giuseppe;40.4500 Bohechío;18.7700 Mentone;34.0609 Fort Salonga;40.9068 Sarauni;24.8613 Fultondale;33.6174 Helena-West Helena;34.5313 Timmapuram;17.1036 Muppālla;16.3198 Nowy Dwór Gdański;54.2167 Stratford;46.2167 Lavis;46.1333 Ferros;-19.2319 Alamosa;37.4752 Foiano della Chiana;43.2500 Khajuri;26.5216 Xinpi;22.4880 Māmā Khēl;34.2500 Akhuryan;40.7814 Cavriglia;43.5167 Maserada sul Piave;45.7500 Bluefield;37.2608 Awans;50.6669 Barjhar;22.6012 Coweta;35.9683 Fallston;39.5332 Gokhulāpur;26.2479 Murrells Inlet;33.5560 Silver City;32.7783 Alayor;39.9342 Cervelló;41.3962 Dobre Miasto;53.9875 Valerik;43.1797 Kola;68.8833 Ulaş;39.2725 Uppinangadi;12.7700 Solsona;41.9944 Kharī;33.3890 Hohenmölsen;51.1564 Visselhövede;52.9667 Lindesberg;59.5833 Dassel;51.8033 Grado;43.3881 Castelfranco di Sopra;43.6236 Sellersburg;38.4028 Pechory;57.8167 Kuchai Kot;26.5559 Oil City;41.4281 Gopālpur;19.2586 Bāgchīni;26.4817 Salcea;47.6500 Cébazat;45.8314 Tlagasana;-7.1786 Elne;42.6003 Castelleone;45.2958 Neu Bleckede;53.3000 Laubach;50.5333 Sowān;25.5540 Jawāsa;26.6259 Sémbé;1.6481 Alfred and Plantagenet;45.5667 Filadelfia;5.3000 Niederwerrn;50.0667 Indianola;33.4492 Aranya Kalān;23.2448 Cholavaram;13.2389 Ilsenburg;51.8667 Tsrār Sharīf;33.8632 Farsund;58.0947 Morrisville;40.2074 Rāsol;20.6298 Vempatti;17.4488 Monte Porzio Catone;41.8167 Segarai;10.7300 Porto Tolle;44.9500 Korwār;16.9200 Tzitzio;19.4449 Algūn;31.2795 Kalvārpatti;10.6250 Mādepalli;12.5491 Attnang-Puchheim;48.0167 La Victoria de Acentejo;28.4348 Tandarāmpattu;12.1533 Khodoriv;49.4100 Südlohn;51.9436 Andiyappanūr;12.5342 Luís Gomes;-6.4139 Ấp Tân Ngãi;10.2369 Country Club;37.9687 Trzebiatów;54.0625 Krasnogvardeyskoye;45.1278 Thomaston;32.8907 Anjēhalli;12.1395 Kadanādu;11.4607 Cuéllar;41.4009 Santa Monica;10.0200 Douar Messassa;34.2803 White Marsh;39.3819 Rangāpuram;15.4092 Tālakulam;8.1824 Barpathār;26.2871 Armutlu;40.5194 Olivares;37.4167 Rockcreek;45.5526 Kunnattūr;11.2727 Adelsdorf;49.7112 Agareb;34.7414 Kovilpatti;10.0283 Lincoln City;44.9751 Babhangaon;25.7897 Gamail;25.7062 Hârşova;44.6833 Fairfield;41.0064 Kankanālapalle;15.9923 Parsons;37.3405 Besozzo;45.8500 Fauske;67.2594 Prudente de Morais;-19.4819 Oulad Imloul;32.0333 Santa Ana;9.2444 Tirúa;-38.3414 Lake Arrowhead;34.2531 Pinhel;40.7833 Brand-Erbisdorf;50.8689 Traversetolo;44.6399 Maserà di Padova;45.3167 Hockley;51.6014 Mont-Tremblant;46.1167 Roncador;-24.6028 Jagta;26.1321 Imielin;50.1333 Mortágua;40.4333 Martensville;52.2897 Sidi Bou Othmane;31.9033 Kākarāti;24.5639 Durgi;16.4242 Twist;52.6167 Karuveppampatti;11.3983 Barleben;52.2000 Kaglipur;12.8006 Canonsburg;40.2643 Runkel;50.4053 Himmatpura;30.5271 Szubin;53.0167 Eemnes;52.2500 Zwiesel;49.0167 Stary Sącz;49.5625 Pleasant Grove;33.4940 Zadonsk;52.4000 Chokkalingapuram;10.1930 Striano;40.8167 Morières-lès-Avignon;43.9417 Ban San Pong;18.9424 Landivisiau;48.5092 Onda;25.2452 Izium;49.1958 Uracoa;8.9129 Decatur;40.8286 Lowell;36.2561 Clay Cross;53.1637 Elhovo;42.1667 Luckau;51.8500 Oststeinbek;53.5442 Corona de Tucson;31.9503 Kensington;41.6284 Sztum;53.9217 El Molar;40.7336 Gatteo;44.1000 Itasca;41.9772 Molagavalli;15.3567 Santa Lucia di Piave;45.8500 Sarakkayhalli;12.2191 Marathon;24.7263 Środa Śląska;51.1500 Mount Airy;39.3742 Borogani;46.3667 Vallet;47.1617 Apiúna;-27.0358 Vranjska Banja;42.5556 Elsenfeld;49.8500 Sierning;48.0447 Bikkatti;11.3741 Gorē;8.1490 Saint-Raymond;46.9000 Rāmachandrapuram;16.5689 Bellūdi;14.4500 Bilozerka;46.6317 Maḑāyā;33.6878 Ralla;30.1200 Levokumskoye;44.8228 Mittenwalde;52.2667 Santa María;-31.2611 Vanavāsi;11.7523 Kanavāypudūr;11.9282 Brugnera;45.9000 Chhapera;23.8948 Mizhhiria;48.5286 Zschopau;50.7500 LaSalle;41.3589 Kungsängen;59.4833 Brewer;44.7835 El Cacao;18.5264 Velyka Dymerka;50.5928 North College Hill;39.2175 Rafelbuñol;39.5922 Kanchanadit;9.1653 Marano Vicentino;45.7000 Ban Wat Chan;16.8033 Ichenhausen;48.3667 Palmeira d’Oeste;-20.4158 Eagle Point;42.4677 Ubbergen;51.8333 Sarahs;36.5333 Voreppe;45.2978 Sebnitz;50.9667 Suan;10.3333 Petersberg;51.6000 Wertingen;48.5333 La Reina;14.1833 Wābāgai;24.5309 Irthlingborough;52.3240 Parasbani;25.8249 Bad Bevensen;53.0792 Uppalapādu;16.3862 Gūdalūr;10.7898 Henderson;-36.2833 Belley;45.7592 Kāyatha;23.2370 Čepin;45.5167 Blachownia;50.7833 Obersiggenthal;47.4869 Mokri;25.0184 Eunice;30.4904 Belmont;39.0622 Odobeşti;45.7667 Valréas;44.3842 San Pedro Nonualco;13.6000 Buñol;39.4194 Sankt Johann in Tirol;47.5225 Tatoufet;35.0339 Lototla;20.8392 San Juan Bautista;14.4167 Bakaly;55.1789 Nangal Lālchand;29.9200 Manuel Urbano;-8.8389 Miajadas;39.1500 Vareš;44.1619 Dahi;22.1122 Paray-le-Monial;46.4511 Bezliudivka;49.8694 Aschheim;48.1733 Rāni Sāgar;25.6079 Sanganakallu;15.1847 Reinfeld;53.8333 Periyamuttūr;12.4630 Ūnagatla;16.9611 Ingenbohl;47.0028 La Grande-Motte;43.5606 Boddikūrapādu;15.6591 Lwówek Śląski;51.1167 Rodeo;38.0367 Bni Gmil;35.0833 Sarenja;25.4449 Bolívar;5.9911 Obernkirchen;52.2664 Ehringshausen;50.6000 Ingleside;27.8703 Yamakita;35.3606 Girard;41.1665 Santa Rosa del Peñón;12.8003 Ryki;51.6333 Schaafheim;49.9242 Santiponce;37.4353 Guria;26.1633 Zawyat Sidi Ben Hamdoun;33.0450 Kayyngdy;42.8300 Ouroeste;-20.0008 Rāmgarh;25.2889 Carlton Colville;52.4540 Ālampur;24.8721 Steger;41.4723 Bay St. Louis;30.3281 Schübelbach;47.1733 Néa Michanióna;40.4644 Chinnāyagūdem;17.0470 Catanduvas;-27.0708 Cervera;41.6657 Pararia;26.3400 Bendapūdi;17.2673 Cunday;4.0833 La Paz;-33.4667 Sədərək;39.7175 Pudukkottai;10.6118 Sosale;12.2330 Bhui;25.0894 Chaplynka;46.3629 Langnau;46.9433 Gurmaila;25.4829 Onchan;54.1750 Aksay;43.3725 Parempuyre;44.9492 Brandon;52.4474 Chita;6.1667 Mogadouro;41.3333 Nittendorf;49.0256 Nepi;42.2436 Rauco;-34.9167 Ipuiúna;-22.0989 Nalbach;49.3833 Lügde;51.9500 Bang Khla;13.7268 Castel Gandolfo;41.7469 Juprelle;50.7167 Penaballi;17.2103 Amherst;45.8167 Pohrebyshche;49.4869 Chahār Borj-e Qadīm;37.1231 Legnaro;45.3500 El Amim;32.2067 Ghālib Kalān;30.8372 Bull Mountain;45.4125 Sankt Valentin;48.1747 Istrana;45.6833 Fossombrone;43.7000 Mendicino;39.2628 Amityville;40.6696 Ambatolahy;-19.7333 Neuried;48.0933 Iaras;-22.8667 Tadworth;51.2940 Águia Branca;-18.9828 Ecatzingo;18.9500 Rehau;50.2500 Sturgeon Bay;44.8228 Shanklin;50.6333 Rāmapattanam;10.7106 Morānha;27.1874 Viagrande;37.6167 Pokotylivka;49.9139 Burayevo;55.8425 Eurajoki;61.2000 Aragona;37.4019 Morehead City;34.7308 Rudraprayāg;30.2800 Modra;48.3317 Sukkāmpatti;10.5267 Pagqên;33.9739 Hemau;49.0519 Bāgeshwar;29.8380 Villa Castelli;40.5833 La Loggia;44.9577 Ouédémè;6.7000 Hagaribommanahalli;15.0400 Carencro;30.3126 Bastrop;30.1113 Succasunna;40.8530 Hernando;28.9451 Waltham Cross;51.6860 Whitestown;39.9706 Periyapōdu;10.6099 Orchies;50.4747 Sogām;34.5014 Gandhwāni;22.3400 Varre-Sai;-20.9308 Ganāram;18.5275 Ikeda;36.4212 Hostivice;50.0814 Vallahbhāpuram;16.3528 Gomaringen;48.4519 Angichettippālaiyam;11.7742 San José Guayabal;13.8500 Bom Jesus;-5.9839 Zacualpan de Amilpas;18.7836 Gonfreville-l’Orcher;49.5053 Tomah;43.9879 Pasivedalajimma;17.0072 Miltenberg;49.7039 Halgeri;14.5551 Rauenberg;49.2678 Saßnitz;54.5164 Monett;36.9218 Cherniakhiv;50.4550 Chechen-Aul;43.2000 Cedar Hills;45.5047 The Village;35.5706 Aniskino;55.9417 Ja‘farīyeh;34.7894 Mosciano Sant’Angelo;42.7500 Claymont;39.8032 Nyzhnohirskyi;45.4464 Nakoushi;26.6825 Cajvana;47.7044 Oakville;41.5893 Tambura;5.5900 Aleksandrovsk-Sakhalinskiy;50.9000 Triunfo;-6.5789 Halge;15.8800 Bohemia;40.7717 Wallerfangen;49.3278 Lake Mohawk;41.0149 Meadowbrook;33.3935 Pallippatti;11.8722 Lesnoy Gorodok;55.6417 Govindapalle;15.3525 Sungal;32.9392 Jūraqān;34.8850 Savignano sul Panaro;44.4833 An Châu;21.3333 Nelali;10.9282 Lanta;7.1000 Andanappettai;10.7498 Adjido;7.0333 Ananás;-6.3658 Greenwood;35.2134 Louisville;40.8370 Sauzal;28.4799 Martano;40.2000 Carácuaro;19.0167 Rudnya;54.9500 Herenthout;51.1500 Gaggiano;45.4048 North Gates;43.1718 Middlesborough;36.6127 Binfield;51.4320 Onnaing;50.3878 Iseo;45.6586 Périgny;46.1528 Belvedere Marittimo;39.6167 Elgin;30.3526 Ramara;44.6333 Aadorf;47.4939 Lamarão;-11.7828 Dharhwa;26.9104 Burela de Cabo;43.6500 Matawan;40.4127 Suances;43.4333 Kadlabālu;15.0400 Lititz;40.1540 Minano;36.0708 Síndos;40.6706 Planaltino;-13.2589 Granada;4.5186 Harvard;42.4296 Esperantina;-5.3428 Grinnell;41.7359 Soubakaniédougou;10.4824 Yutsa;43.9625 Jājireddigūdem;17.3278 Dhāmnod;23.4421 Terra Roxa;-20.7889 Santanópolis;-12.0169 Castellabate;40.2789 Conning Towers Nautilus Park;41.3850 Itiki;14.9510 Susāri;22.1797 Huedin;46.8700 Arenápolis;-14.4500 Parabita;40.0500 Riachuelo;-10.7278 Hombrechtikon;47.2500 Vallendar;50.3971 Perwez;50.6241 Zwenkau;51.2175 Middletown;38.2410 Nieuwleusen;52.5833 Mhâjâr;35.1200 Bystrzyca Kłodzka;50.3000 Dielheim;49.2825 Opalenica;52.3078 Sidi El Hattab;32.2667 Oakwood;39.7202 Villacañas;39.6333 Middletown;40.2010 Gooik;50.8000 Grossos;-4.9800 Ablu;30.3391 Dāvulūru;16.2625 Dānesfahān;35.8108 Leeds and the Thousand Islands;44.4500 Filottrano;43.4333 Várzea do Poço;-11.5289 Bethel;39.8458 Carignan;45.4500 Brockton;44.1667 São Domingos;-26.5578 East Brandywine;40.0364 Tārazu;34.2714 Vega Alta;18.4151 Hartland;43.1029 Fársala;39.2833 La Chapelle d’Armentières;50.6728 Budenheim;50.0167 Wau;-7.3389 Kirs;59.3500 Missaglia;45.7000 South Abington;41.4900 La Ferté-Bernard;48.1867 Cambridge;45.5612 La Mujer;36.7523 Almagro;11.9108 Shira;54.4939 Capbreton;43.6419 San Francisco Libre;12.5061 Mohelnice;49.7770 Mandalavādi;12.6073 Hegde;14.4667 Dubrovytsya;51.5667 Olho d’Água do Casado;-9.5000 Villa Cañás;-34.0000 Cape Elizabeth;43.5891 Midland;47.1734 Asolo;45.8000 Alawandi;15.2303 Ban Tha Phra;16.3298 Lillebonne;49.5208 Great Dunmow;51.8730 Clarksville;35.4570 Kodigenahalli;13.7214 Madanāncheri;12.7034 Harqalah;36.0333 Nāgāyalanka;15.9500 Donabagatta;13.8951 Yanchep;-31.5500 Esanai;11.3004 Adolfo Gonzáles Chaves;-38.0333 Pandino;45.4000 Incline Village;39.2639 Sirāli;22.1519 Niesky;51.3000 Pryor Creek;36.2996 Lovosice;50.5151 Raunds;52.3450 Braslaw;55.6391 Les Sorinières;47.1483 Borovskoy;53.7964 Tissint;29.9006 Pizzo;38.7333 Bārun;24.8602 Sīlaiyampatti;9.8732 Trebisacce;39.8667 ’s-Gravendeel;51.7833 Livron-sur-Drôme;44.7728 Kottapālem;17.4360 Vochaïkó;37.9333 Grigoriopol;47.1503 Cross Lanes;38.4351 Kondūru;16.6780 Maddūr;16.8667 Ledbury;52.0339 Waiuku;-37.2490 Teisendorf;47.8500 San Fructuoso de Bagés;41.7507 Kurichchi;11.7276 Moḩammad Yār;36.9831 Huinca Renancó;-34.8333 Mooresville;39.6022 Laufenburg (Baden);47.5656 Le Muy;43.4725 Turín;13.9667 Abra Pampa;-22.7167 Teghra;26.4929 Wunsiedel;50.0167 Alma;43.3799 Dunblane;56.1838 Bad Laer;52.1031 Catral;38.1594 San Sebastián de la Gomera;28.0922 Castelli;-25.9500 Country Club Estates;31.2113 Alfredo Wagner;-27.7000 Santa Maria;-24.9389 Būdamangalam;12.3792 Montes Altos;-5.8308 Orivesi;61.6750 Mudhol;15.6406 Gammasa;31.4182 Varzedo;-12.9708 Dehri;25.4252 Diamondhead;30.3791 Greenville;40.9986 Havre;48.5427 Cistérniga;41.6167 Spáta;37.9667 Negrete;-37.5833 Krasnoslobodsk;54.4333 San Ignacio;9.7997 Bethalto;38.9015 Pūttai;11.8957 Sa Pa;22.3356 Mutis;6.2167 Savenay;47.3611 Mendig;50.3744 Aniva;46.7167 Amtala;26.1000 Santa Comba;43.0383 Stevenston;55.6450 Lidzbark;53.2603 Sorala;19.1492 Gouvieux;49.1878 Treia;43.3114 Gangapatnam;14.5237 Duvvūru;14.5507 Nārāyanpur;17.8589 Laghzawna;33.1890 Sandy Hook;41.4128 Velampatti;10.2311 Bridge City;30.0298 Le Rheu;48.1019 Nandnāwān;25.0785 Ameskroud;30.5308 Verkhneyarkeyevo;55.4458 Yenmangandla;16.8839 La Bruyère;50.5000 Hallstadt;49.9333 Kandel;49.0833 Melpanaikkādu;10.2599 Morubāgalu;13.9702 Schlangen;51.8167 Lower Swatara;40.2188 Muro del Alcoy;38.7797 Porcari;43.8415 Madeira;39.1856 Helsinge;56.0222 Närpes;62.4667 Sæby;57.3294 St. Stephens;35.7642 Biblis;49.6841 Santa María de Cayón;43.3114 Lamorlaye;49.1550 Tysmenytsia;48.9008 Sibilia;15.0000 Cavalcante;-13.7978 Pfedelbach;49.1750 Le Mont-sur-Lausanne;46.5217 Tonneins;44.3897 Séné;47.6197 Köse;40.2100 Kod;22.8850 Bovalino Marina;38.1500 Columbia City;41.1612 Denham Springs;30.4743 Tāla;24.3735 Douar Ezzerarda;34.7667 Sompting;50.8303 Chaponost;45.7103 Gūgi;16.7292 Chesapeake Ranch Estates;38.3574 Laurentian Valley;45.7681 Sabiñánigo;42.5186 Savādi;15.6469 Kolkwitz;51.7496 San Lorenzo;14.0333 Chinna Kalaiyamputtūr;10.4714 Countryside;39.0518 University of Virginia;38.0405 Coccaglio;45.5633 Curepto;-35.0833 Hirehalli;14.3200 Lempdes;45.7711 Pedda Muppāram;17.4792 Ifigha;36.6667 Rāmpatti;26.0028 Irshava;48.3172 Igaratá;-23.2044 Ouled Rached;36.2119 Rochelle;41.9197 Açucena;-19.0728 Borio;25.0344 West Glens Falls;43.3057 Navappatti;11.7393 Neginhāl;15.7900 Tettu;13.6275 Dardilly;45.8056 Avabodji;6.4533 Mīrjāveh;29.0147 Sherborne;50.9469 Kargat;55.1956 Currumbin;-28.1580 Le Petit-Couronne;49.3856 Munagāla;17.0500 East St. Paul;49.9772 Edelény;48.2967 Kurichedu;15.9026 Folignano;42.8167 La Jigua;15.0333 La Fare-les-Oliviers;43.5517 Belakvādi;12.2550 Gudivāda;17.3974 Slateng Dua;-8.1324 Kruszwica;52.6772 Riacho dos Machados;-16.0058 Manhattan;41.4274 Risaralda;5.1667 Mound;44.9328 Newmarket;43.0691 Aidlingen;48.6792 Warren;41.8434 Lubuagan;17.3500 Siteía;35.2000 Heath;32.8439 Richterich;50.8086 Prospect;41.4993 Sangam;17.8958 Hamsāvaram;17.2938 Hāthāpur;26.5609 Tecklenburg;52.2194 Mosrāh;18.6155 Tvrdošín;49.3343 Kibaya;-5.3014 Bābai Kalān;22.8313 Dowlatābād;35.2822 Jori Kalān;24.2045 Pesca;5.5833 Mahāgaon;17.5211 Kopong;-24.4686 Longvic;47.2878 Ingleshwār;16.5900 Moka;-20.2190 Florence;43.9916 Nová Paka;50.4945 Monteforte d’Alpone;45.4167 Lorraine;45.6833 Isbergues;50.6233 Green Cove Springs;29.9904 Verkhneuralsk;53.8833 Podenzano;44.9500 Crestline;34.2486 Hirehāluhosahalli;15.0105 Haiku-Pauwela;20.9156 Ariranha;-21.1878 Khotyn;48.5078 Nagykálló;47.8833 Riverside;41.8310 Quirino;17.1383 Ipiranga do Piauí;-6.8278 Merrill;45.1820 Ghāt Borūl;17.7700 Segni;41.6833 Hillsborough;36.0679 Betania;5.7500 Nārsingi;18.0446 Werneuchen;52.6333 Fort Stewart;31.8811 Grabels;43.6481 Kyritz;52.9500 Evergreen;39.6349 Willow Street;39.9810 Vila Nova de Cerveira;41.9667 Vendin-le-Vieil;50.4739 Gramsh;40.8667 Telaprolu;16.5860 Cherasco;44.6500 Bojacá;4.7336 Sangaree;33.0327 Ineu;46.4258 Koheda;18.1709 Dodji-Bata;6.6833 Nansio;-2.1078 Porangaba;-23.1758 Hohenhameln;52.2600 Banārūyeh;28.0842 Ventnor City;39.3457 Yeldūrti;17.9074 Kaleybar;38.8664 Lacchiarella;45.3250 Sainte-Julienne;45.9700 Binisalem;39.6831 Rensselaer;42.6465 Hyrum;41.6325 Tirano;46.2164 Kot Umachigi;15.5426 Seydunganallūr;8.6624 Masandra;44.5167 Concepcion;8.4167 Shimizu;43.0113 Ecorse;42.2489 Blackfalds;52.3833 Adaklı;39.2308 Tirumalaippatti;11.3353 Yvoir;50.3333 Belousovo;55.0833 Reggiolo;44.9167 Velakalnattam;12.5505 Altınyayla;39.2725 Hatti Mattūr;14.9435 Dores de Campos;-21.1089 Nakhon Thai;17.1011 Manzanares el Real;40.7272 Borgo a Buggiano;43.8833 Landquart;46.9688 Sint-Martens-Lennik;50.8000 Caluco;13.7167 Skwierzyna;52.6000 Kirrāyach;26.6322 San Jorge;13.6833 Inveruno;45.5167 Panjampatti;10.3178 Kūnimedu;12.0885 Kadganchi;17.4437 Khetko;23.7554 Dhobipet;17.4716 Miryal;17.5661 Hualañe;-34.9765 Mechanicsburg;40.2115 Lansdowne;39.2365 Woodway;31.4988 Koshanam;11.3701 Dāmal;12.8860 Bad Liebenwerda;51.5167 Mels;47.0497 Plymouth;42.3718 Mīlājerd;34.6219 Clermont-l’Hérault;43.6272 San José La Arada;14.7167 Cinderford;51.8225 Igaratinga;-19.9550 Magdalena;-35.0833 Paula Cândido;-20.8739 Sheffield;34.7570 Highland Park;42.4052 Terra Nova;-8.2300 Nittenau;49.2000 Capilla del Señor;-34.2833 Lo Miranda;-34.1900 Teolo;45.3500 Heusden;51.0281 Velykyi Bychkiv;47.9714 Seddouk Oufella;36.6061 Veľké Kapušany;48.5503 Springs;41.0212 Guryongpo;35.9833 Srīrāmapuram;16.4071 Stăuceni;47.0875 Kiskunlacháza;47.2000 Winchester;35.1898 Fairview;35.9815 Augusta;37.6955 Nong Kung Si;16.6500 Mahomet;40.1888 Corgao;15.7096 Hohenbrunn;48.0500 Akālgarh;30.8019 Zörbig;51.6167 Chennayyanakote;12.2719 Yellanda;17.7929 Kambūr;10.1599 Borgosatollo;45.4761 Washington;40.9884 Malahide;42.7928 Saūmalköl;53.2914 Molbergen;52.8667 Urbach;48.8133 Baláo;-2.9100 Moman Barodiya;23.6042 Didymóteicho;41.3500 Babadag;44.8981 Tanakoub;35.1091 Monte Rico;-24.4514 Carignano;44.9069 Philippeville;50.2000 Antônio Dias;-19.6528 Axixá do Tocantins;-5.6169 Gonzaga;44.9500 Hünenberg;47.1761 Kirchberg;47.4000 Quakertown;40.4398 Gerstungen;50.9625 Tlahuiltepa;20.9233 Suwannaphum;15.6078 Piliscsaba;47.6167 Varatanapalli;12.5828 Pedda Pendyāla;17.9230 Kotabommāli;18.5333 Herculândia;-22.0036 Burscough;53.5960 Juruaia;-21.2528 Pozo Almonte;-20.2667 Riverdale;41.1732 Gnarrenburg;53.3864 Momchilgrad;41.5333 Guttikonda;16.4300 Mallāram;18.7540 Aljustrel;37.9167 Sucha Beskidzka;49.7403 Torgelow;53.6167 Zhur;42.1639 Horodenka;48.6675 Westerland;54.9100 Kanavāypatti;10.1857 Chikkerūr;14.5326 Fuldabrück;51.2667 Sucre;5.9230 Zavyalovo;56.7922 Santa María del Tule;17.0465 Oulad Cherif;31.7667 Vignate;45.5000 Tummapūdi;16.3780 Roanoke;33.0144 Kawara;14.0706 Tillaivilāgam;10.4127 Chotěboř;49.7208 Neustadt;50.7333 Sidi Lahsene;34.0999 Chala;-15.8519 Stansted Mountfitchet;51.8980 Guararé;7.8200 Frodsham;53.2950 Salto Grande;-22.8928 Altenbeken;51.7667 Biberist;47.1828 Sugar Grove;41.7759 Valky;49.8386 Maddūr;13.2397 Reshetylivka;49.5636 Paredes de Coura;41.9127 Chettiyapatti;10.4240 Tumberi;12.6986 Rio del Mar;36.9607 Dodda Siddavvanahalli;14.2200 Palocabildo;5.1333 Gródek Nad Dunajcem;49.7333 Kaniwāra;22.2145 Pokrovsk;61.4833 Troina;37.7833 Guatapé;6.2333 Dirusumarru;16.4722 Rājod;22.9501 Malalbergo;44.7167 Nizhniye Sergi;56.6667 Zabok;46.0333 St. Francis;42.9716 Falan;5.1333 Southside;33.9007 Chillicothe;39.7953 Melgaço;42.1167 Nato;-22.3000 Kelso;-33.4186 Laurens;34.5022 Forestdale;33.5737 Dunn Loring;38.8945 Kalanchak;46.2581 Fort Polk South;31.0512 Ashland;39.8782 Ardooie;50.9667 Zūlakallu;16.4442 Lanivo;-22.2833 Le Thor;43.9292 Salzhemmendorf;52.0667 Montoro;38.0167 Pong Nam Ron;12.9057 Zoeterwoude;52.1333 Ambara;22.1892 Naduvalūr;11.4800 Colorno;44.9333 Hosūru;15.3797 Abaíra;-13.2500 Obukhivka;48.5442 Sūrak;36.5950 Rosário do Catete;-10.6958 Carneiros;-9.4828 Oborniki Śląskie;51.2986 Une;4.4019 Gholia Kalān;30.6726 Waseca;44.0827 Aliquippa;40.6155 Ādamī Tulu;7.8667 Aslanapa;39.2167 Bahābād;31.8719 Honganūr;11.9400 Ziębice;50.6000 Dahbed;39.7636 Nangis;48.5550 Haram;62.6665 Tummalacheruvu;17.7667 Āgadāllanka;16.7123 Yaxley;52.5200 Saint-Paul-Trois-Châteaux;44.3489 Segorbe;39.8519 Saint-Claude;46.3872 Potangal;18.5661 Saint-Philbert-de-Grand-Lieu;47.0350 Dharmājigūdem;16.9000 Freystadt;49.2000 Douar Snada;35.0667 Kovvali;16.7333 Berndorf;47.9428 Soresina;45.2865 Shawano;44.7748 Montagnana;45.2333 Luzzi;39.4500 Hirayama;33.6467 Furth im Wald;49.3097 Balvādi;21.4412 Moss Vale;-34.5500 I-n-Amenas;28.0500 Vannikkonendal;8.9959 Stryzhavka;49.3103 Pásztó;47.9202 Charlotte;42.5662 Roquevaire;43.3494 Almargem;38.8475 Baluntaicun;42.7594 Golbāf;29.8850 Oromocto;45.8488 Bassenge;50.7586 Ponte Buggianese;43.8408 Flanders;40.8412 Burlington;48.4676 Harleysville;40.2792 Vadacheri;10.7324 Lakhanāpuram;18.7504 Serramanna;39.4228 Nort-sur-Erdre;47.4394 Mejillones;-23.1000 Peresecina;47.2500 Tarcento;46.2167 Chtiba;32.2000 Washington Terrace;41.1683 Monserrat;39.3575 Allāhpur;26.3434 Sholaqqorghan;43.7650 Frankfort Square;41.5219 Perondi;40.7833 Krompachy;48.9139 Beauraing;50.1092 Chécy;47.8936 Wanaka;-44.7000 San Antonio;12.4140 Dalavāypattanam;10.6747 Notre-Dame-de-Gravenchon;49.4892 Maisaram;17.1329 Padakanti;18.6942 Eduttavāynattam;11.8057 Canal Winchester;39.8437 Grants;35.1538 O'Hara;40.5092 Bhelsi;24.7866 Trittau;53.6167 Thorigné-Fouillard;48.1597 Fredensborg;55.9750 Kanchanpalli;17.7427 Borgoricco;45.5167 Nānguneri;8.4906 Sidi Ouassay;30.0500 Bellerive-sur-Allier;46.1164 Oak Hills Place;30.3690 L’Isle-Jourdain;43.6136 San Manuel Chaparrón;14.5167 Yeşilyurt;40.0000 Rockingham;34.9386 Nellutla;17.7034 Unterägeri;47.1386 Furtwangen im Schwarzwald;48.0503 Bir Tam Tam;33.9833 Glanmire;51.9167 Copceac;45.8500 Wenzenbach;49.0747 Pepillo Salcedo;19.7000 Rivesaltes;42.7689 Santo Domingo Petapa;16.8167 Nettādahalli;12.1330 Lorgues;43.4933 Gudlūru;15.0729 Flowery Branch;34.1712 Gretz-Armainvilliers;48.7411 Olesno;50.8750 Pike Road;32.2939 Vinjam;13.2544 Elsfleth;53.2333 Egg;47.3019 Blackstone;42.0399 Richmond Heights;38.6309 Bandamūrlanka;16.5170 Amapá;2.0528 Tibaná;5.3167 Plön;54.1622 Fujisawachō-niinuma;38.8585 Yel’sk;51.8167 Oggiono;45.7833 Vanduvāncheri;10.4292 Lunner;60.2528 Neuhaus am Rennweg;50.5167 Consacá;1.2000 Nagaoki;32.9781 Olds;51.7928 Jerez de los Caballeros;38.3203 Kalicherla;13.8833 Piriápolis;-34.9000 Mandurah;-32.5289 Villamediana de Iregua;42.4264 Maidencreek;40.4618 Mwaline al Oued;33.4467 Kilānkundal;10.7543 Tullukuttināyakkanūr;9.8149 Kożuchów;51.7500 Zarbdor Shaharchasi;40.0747 Zogno;45.7939 Waldheim;51.0667 Mustafābād;18.2787 Aratuípe;-13.0789 Willowbrook;41.7641 Żychlin;52.2453 Janglot;32.4353 Horokhiv;50.4994 Rompicherla;13.7228 Modachchūr;11.4415 Palm Beach;26.6945 Rāmnagar;32.8073 Neuötting;48.2167 Banak;27.8722 Çardak;37.8247 Beecher;43.0903 Benton Harbor;42.1159 Dörverden;52.8500 Timmendorfer Strand;53.9944 Channubanda;17.0331 Petilia Policastro;39.1167 Bundāla;31.1429 Bridgeport;39.3036 Mikhaylovka;43.9283 Memmelsdorf;49.9328 Le Teil;44.5453 Tulbagh;-33.2850 Tournan-en-Brie;48.7406 Ortenberg;50.3558 Abalessa;22.8900 Borgholzhausen;52.1000 Chalgeri;14.5652 Verdejante;-7.9256 Elze;52.1167 Jainagar;24.3756 Aviano;46.0667 Glens Falls North;43.3350 Rain;48.6833 Coqueiral;-21.1889 Usakos;-22.0000 Vīrapperumānallūr;11.7763 Bingham;52.9520 Monteroni d’Arbia;43.2333 Carbonita;-17.5269 Kandanāti;15.6997 Sângeorgiu de Mureş;46.5500 Bruchhausen-Vilsen;52.8333 Costa Volpino;45.8306 Bishamagiri;19.3849 Kara-Bak;40.1583 Salisbury;42.8465 Solita;0.8667 Castagneto Carducci;43.1667 Ḩīsh;35.5464 Al Bardīyah;31.7600 Pā’īn Chāf;37.2294 Valasa;14.1632 Koppunur;16.4867 Carmen de Carupa;5.3503 Hundested;55.9667 Boukhralfa;36.6144 Bichura;50.5864 Gundumāl;16.8939 Pine Lake Park;40.0017 Villa Chalcatongo de Hidalgo;16.9929 Penugolanu;16.9771 Kressbronn am Bodensee;47.5958 Loeches;40.3833 Duchcov;50.6039 Pedda Nindrakolanu;16.7342 Ranjal;18.7458 Dospat;41.6500 San Zenón;9.2450 Tiburon;37.8854 San Lorenzo de Descardazar;39.6090 St. Anthony;45.0278 Palmeiras;-12.5289 Trilj;43.6167 Crvenka;45.6583 Panthersville;33.7059 Haddington;55.9560 Katoomba;-33.7150 Urânia;-20.2458 Llagostera;41.8292 Chadan;51.2833 Iijima;35.6767 Kißlegg;47.7900 Ochsenhausen;48.0722 Estanzuelas;13.6500 Kottadindulu;16.4830 East Grand Forks;47.9286 Huron East;43.6300 Castelnuovo Berardenga;43.3474 Harahan;29.9374 Zeydābād;29.6000 Campamento;6.9789 Quéven;47.7886 Thāndewāla;30.4720 Scenic Oaks;29.7038 Oberriet;47.3164 Keregodu;12.6333 Oakengates;52.6950 Burgos;16.5250 Riverside;41.0318 Lichtervelde;51.0333 Velyki Luchky;48.4200 Mühlhausen;49.2475 Gtarna;32.9724 Kot Fatah;30.1122 Rio Acima;-20.0878 Iguidiy;30.7467 Kaeng Khro;16.1086 Coronel Murta;-16.6189 Smithville;39.4929 Lugoff;34.2113 Nunihāt;24.4843 Thung Sai;16.2955 Seini;47.7478 Anísio de Abreu;-9.1889 Civitella in Val di Chiana;43.4167 Guardiagrele;42.2000 Kalas;15.0981 Garsekurti;18.5083 Cogoleto;44.3894 Oak Hills;34.3910 Silvārpatti;10.1180 Kharsod B;23.2225 Tarusa;54.7333 Embalse;-32.1833 Velden am Wörthersee;46.6125 Sini;22.7933 Sirikonda;17.1947 Neuenkirchen;52.5167 Doberlug-Kirchhain;51.6167 Sauce;-30.0667 Dzhalka;43.3186 Bolokhovo;54.0833 Cutrofiano;40.1167 Terrace Heights;46.6045 Entraigues-sur-la-Sorgue;44.0031 Tiachiv;48.0114 Tineo;43.3095 Polakala;13.3458 La Florida;1.3017 Tagapul-an;12.0500 Mnichovo Hradiště;50.5273 Kodavatipūdi;17.4887 Vergiate;45.7167 Woodbury;40.8176 Nelkattumseval;9.2400 Bernolákovo;48.1992 Navoloki;57.4667 Farra di Soligo;45.8833 Nimmekal;17.2372 Paranaiguana;-18.9158 Pullalacheruvu;16.1584 Kratovo;55.5911 Somero;60.6333 Roccastrada;43.0097 Trissino;45.5667 Tutrakan;44.0500 Big Rapids;43.6992 Felino;44.6936 Kosum Phisai;16.2430 Kharovsk;59.9500 Stará Turá;48.7767 Akat Amnuai;17.5898 Deshnur;15.7900 Bālumāth;23.8298 Amingaon;26.2300 Aransas Pass;27.8876 Waldenbuch;48.6372 Waterford;37.6429 Fort Irwin;35.2477 Herseh Chhīna;31.7453 Rathdrum;47.7948 Osicala;13.8000 Igana;7.0333 Rada Tilly;-45.9333 Wasilla;61.5770 Yakakent;41.6222 Salem;40.0539 Säffle;59.1333 Cheste;39.4797 Barhagarh;19.5676 Des Peres;38.5973 Cresskill;40.9405 Lynwood;41.5235 Madnūr;18.5000 Zalishchyky;48.6392 Pântano Grande;-30.1908 Epanomí;40.4261 Pedrinhas;-11.1919 Redlands;39.0886 Calamar;1.9206 Galbiate;45.8000 Malangām;34.4383 Middleton;43.7113 Chiţcani;46.7833 Nāgaiyampatti;11.4639 Portlethen;57.0610 Chinna Mupparam;17.6319 Breckerfeld;51.2667 Åmål;59.0500 Shar;49.5858 La Celia;5.0019 Tha Luang;15.0697 Bad Schussenried;48.0067 Pāppākudi;8.7520 Bérégadougou;10.7667 Herzberg;51.6833 Deodora;22.6149 Casca;-28.5608 Picture Rocks;32.3274 Nanzhuang;24.5699 Silves;-2.8389 Vista Alegre do Alto;-21.1708 Harīke;31.1661 Grenade;43.7714 Siklós;45.8519 San Francisco;14.1167 Hāta;25.0460 Livno;43.8269 Íquira;2.6500 Elsmere;38.9948 Kandanūr;10.1037 Adigoppula;16.4402 Vinhais;41.8167 Caputira;-20.1719 Sorisole;45.7375 Lançon-Provence;43.5925 Verkhnyaya Tura;58.3594 Dhībān;35.0025 Khānāpur;17.9004 Chitvel;14.1667 Edgewater;38.9373 Muhos;64.8000 Chrysoúpoli;40.9833 Waldfeucht;51.0667 Saline;42.1740 Rafard;-23.0117 Pa Mok;14.4921 Bellheim;49.1981 Polička;49.7147 Nierstein;49.8694 Kings Grant;34.2664 Villa Aberastain;-31.6500 Acobamba;-12.8431 Tilvalli;14.6268 Beaver Falls;40.7619 White Meadow Lake;40.9241 Bni Sidel;35.1392 Perkasie;40.3720 Fallon;39.4737 Gisborne;-37.4900 Whippany;40.8233 Had Dra;31.5833 Cherry Hinton;52.1849 Noventa Vicentina;45.2833 Borgo;42.5539 Brownfield;33.1757 Abcoude;52.2700 Hiramandalam;18.6708 Wollert;-37.5833 Buriti Alegre;-18.1439 Flekkefjord;58.2969 Dékanmé;7.1333 Großbeeren;52.3551 Sunset Hills;38.5310 Westampton;40.0168 Saudade;-26.9239 Bansko;41.8385 Lanark;55.6749 Januário Cicco;-6.1578 Palazzolo Acreide;37.0617 Miramar Beach;30.3854 Obernburg am Main;49.8400 Monistrol-sur-Loire;45.2925 Glenshaw;40.5391 Hirson;49.9217 Subiaco;41.9333 Greenville;43.1797 Bayabas;8.9678 Ceprano;41.5500 Saint-Jean-d’Illac;44.8097 Laveno-Mombello;45.9089 Thaon-les-Vosges;48.2505 Linares;1.3500 Northwest Harborcreek;42.1494 São Jorge d’Oeste;-25.7058 Amadalli;14.7667 Crest;44.7283 Angelópolis;6.1333 Morlupo;42.1435 Beldibi;36.8667 Çayırhan;40.1000 Little Falls;45.9862 Davenport;28.1587 Bernalillo;35.3127 Newark;43.0418 Neves Paulista;-20.8458 San Pablo Villa de Mitla;16.9170 Toccoa;34.5810 Zunilito;14.6167 Māgam;34.4595 Santa Sylvina;-27.7833 Dāla;30.7773 Punnappatti;10.2243 Sai Ngam;16.4669 Kozova;49.4318 Ballenstedt;51.7200 Dunavarsány;47.2781 Nuevo Paysandú;-32.2667 Hemmoor;53.7025 Absecon;39.4229 Ayr;-19.5744 Karlıova;39.2992 Stanley;49.1331 Çayırlı;39.8056 Yapraklı;40.7500 Khoni;42.3244 Bommagondanahalli;13.8984 San Giorgio di Piano;44.6500 Maravilha;-9.2358 Delta;38.7560 Rhymney;51.7590 Uspenka;48.3939 Bharno;23.2204 Coffeyville;37.0519 Sande;53.5022 Albinea;44.6167 Coronel Du Graty;-27.6667 Abirāmam;9.4423 Candeal;-11.8078 Fitzgerald;31.7134 Fairless Hills;40.1783 Wervershoof;52.7300 Phopnār Kalān;21.2365 Craig;40.5171 McFarland;43.0203 Thap Than;15.4570 Libonik;40.7500 Berching;49.1000 Sítio Novo de Goiás;-5.6008 Bolnisi;41.4500 Bargersville;39.5412 Cloverdale;38.7962 Mburucuyá;-28.0500 Devnya;43.2167 Florstadt;50.3158 Pomichna;48.2500 Bersenbrück;52.5333 Nāttarasankottai;9.8690 Mānpur;22.4315 Rāgampet;18.6383 Çatalağzı;41.5000 Mādāram;19.1653 Hope;33.6682 Koekelare;51.0906 Auerbach;49.6833 Hoyo de Manzanares;40.6333 Kattirippulam;10.4640 Buşteni;45.4117 Kodaimangalam;10.4733 Puerto Octay;-40.9667 Karkkila;60.5333 Engerwitzdorf;48.3397 Sarlat-la-Canéda;44.8900 Volkach;49.8667 Natividade;-11.7100 Ḩalāwah;32.3828 Santa Venerina;37.6833 Urdinarrain;-32.6856 Arboledas;7.6667 San Francisco Ixhuatan;16.3514 Czarna Białostocka;53.3000 Hailey;43.5141 Yelnya;54.5833 Pāta Uppāl;18.1799 Bachchannapet;17.7883 Kālkuni;14.6000 Rondon;-23.4108 Uricani;45.3364 Marguerittes;43.8600 Mbamba Bay;-11.2833 Pyālakurti;15.7286 Olney;38.7285 Mount Pleasant;40.9625 Iscuandé;2.4444 Rolesville;35.9223 Szentgotthárd;46.9488 Pillutla;16.5390 Chop;48.4333 Foum Zguid;30.0833 Dobříš;49.7811 Perkiomen;40.2316 Fully;46.1333 Montalto di Castro;42.3500 Murillo;26.2642 Ospina;1.0581 Domérat;46.3603 Tepechitlán;21.6667 Sępólno Krajeńskie;53.4500 Park Ridge;41.0352 Cameron;39.7444 Puerto Lleras;3.2694 Zawyat Sidi al Mekki;33.2120 Sheffield Lake;41.4883 Fehrbellin;52.8144 Pulaski;37.0528 Divisópolis;-15.7258 Locogahoué;6.8000 Great Cornard;52.0245 Breganze;45.7000 Veľký Meder;47.8564 Psyzh;44.2333 Vijayapuri;11.2300 Potiraguá;-15.5950 Huasco;-28.4664 Tuscumbia;34.7204 Florânia;-6.1269 Teranikallu;15.6392 Siachoque;5.5000 Săbăoani;47.0167 Angallu;13.6287 Garching an der Alz;48.1167 Monastyryshche;48.9900 Capim Branco;-19.5489 Nuquí;5.7167 Yaguará;2.6661 Tena;4.6547 Galmaarden;50.7500 Choceň;50.0017 ’s-Heerenberg;51.8764 Kui Buri;12.0702 André Fernandes;-15.9658 Morpará;-11.5589 Maina;23.1712 Lake Park;26.7998 Saint-Vallier;46.6419 Woodfield;34.0587 Bumbeşti-Jiu;45.1786 Bhalaiana;30.3290 Fair Oaks;33.9193 Ulātu;23.2766 Povarovo;56.0767 Innsbrook;37.6552 Huntertown;41.2185 Borskoye;53.0261 Kambhampādu;16.9822 Vicopisano;43.6991 Minerbio;44.6175 Emirgazi;37.9022 St. Pete Beach;27.7235 Luzzara;44.9667 Penetanguishene;44.7667 Dashouping;23.6488 Ala;45.7500 Winslow;35.0243 Skidaway Island;31.9372 Sukand;26.6444 Kāoni;30.4000 Schleiz;50.5833 Pallappālaiyam;11.3891 Mudhol;16.3500 Hawthorn Woods;42.2313 Ranod;25.0748 Sint-Job-in-’t-Goor;51.3000 Velaux;43.5225 Strijen;51.7500 Tlumach;48.8669 Itapiranga;-2.7489 Greenville;41.8820 Naunhof;51.2778 Old Orchard Beach;43.5239 Vīrapalle;14.1500 Bad Lauchstädt;51.3667 Harrodsburg;37.7654 Kusterdingen;48.5222 Arizona City;32.7506 Sekimachi;33.0598 Ladue;38.6377 Sucre;2.0333 Eslohe;51.2500 Hampstead;42.8821 Carmo do Rio Verde;-15.3539 Khaur;32.8229 Raonta;30.5619 Argelia;5.7425 Beni Hassane;35.5700 Shahr-e Pīr;28.3106 Groß Kreutz;52.3997 Bargaon;23.1795 Peebles;55.6519 Florø;61.5996 Ahuimanu;21.4379 Qualicum Beach;49.3500 Almagro;38.8878 Grünheide;52.4255 Tepetitlan;20.1842 Santa Rita de Caldas;-22.0289 Hooper;41.1599 Dourado;-22.1000 Hanko;59.8333 Frei Inocêncio;-18.5450 Nesārg;15.9069 Nunchía;5.6333 Sarzeau;47.5272 Helotes;29.5687 Clusone;45.8833 Paulo de Faria;-20.0308 Iskourane;30.8433 Bockhorn;53.4000 Deutsch-Wagram;48.2994 Mikhaylovsk;56.4500 Kulu;39.0892 Qashyr;53.0804 Park Forest Village;40.7996 Tonk Khurd;23.0983 Meine;52.3833 Eceabat;40.1839 Nārona;17.5156 Carmen de Apicalá;4.1500 Taxkorgan;37.7728 San Sebastian;11.7000 Nottampatti;9.9772 İliç;39.4536 Cockermouth;54.6613 Lequile;40.3000 Cumberland Hill;41.9736 Mannegudam;17.4601 San Giuseppe Iato;37.9667 Vidapanakallu;15.0667 Florida;-36.8167 González;8.4000 Ararat;-37.2833 Achacachi;-16.0444 Karuppūr;10.4918 Orte;42.4603 Suoyarvi;62.0833 Araújos;-19.9478 Nerk’in Getashen;40.1467 Westwood;42.3031 Ilarionove;48.4059 Serafimovich;49.5833 Srīrangāpur;16.1917 Hilzingen;47.7653 Edgewater Park;40.0540 Metsemotlhaba;-24.5531 São Sebastião do Alto;-21.9569 Schwarzenbruck;49.3500 Angola;41.6433 Penig;50.9336 Navipet;18.8022 Bemiss;30.9318 Edgemere;39.2273 Saint-Chamas;43.5503 Jagannāthpur;22.2211 Charters Towers;-20.0765 Basrūr;13.6308 Malhada de Pedras;-14.3878 Topchikha;52.8211 Silleda;42.7000 Baía da Traição;-6.6878 Camenca;48.0167 Lapeer;43.0447 Vairichettipālaiyam;11.2872 Chapel en le Frith;53.3220 Shelawādi;15.5833 Purcellville;39.1378 Kozlovka;55.8500 Pibrac;43.6169 Fishersville;38.1050 Takua Pa;8.8658 Polegate;50.8216 Makhambet;47.6667 Napoleon;41.3977 Wattwil;47.2957 San José de Gracia;22.1500 Guntapalli;14.7385 Nueva Guadalupe;13.5333 Rāiparthi;17.7042 Barth;54.3667 Taftanāz;35.9969 Halikko;60.3972 Hale Dyāmavvanahalli;14.2682 Yarmouth;43.7978 Holagondi;15.0200 Alvarado;4.5667 Obertraubling;48.9658 Caister-on-Sea;52.6510 Bāgalvād;16.0528 Hunduan;16.8333 Msemrir;31.7028 Brejão;-9.0300 Pudozh;61.8000 Minervino Murge;41.1000 Hürtgenwald;50.7172 Summit;47.1694 Ouriçangas;-12.0169 Ait Ikkou;33.5667 Highland Park;32.8311 Shoshong;-23.0333 Teruel;2.7500 Santa María Xadani;16.3667 Manteno;41.2470 North Haledon;40.9628 Hlevakha;50.2604 Belsh;40.9833 Pedda Penki;18.5853 Merrydale;30.4998 San Martín de Valdeiglesias;40.3640 Indian Harbour Beach;28.1529 Koror;7.3419 Rodelas;-8.8508 Kamień Pomorski;53.9700 Imilchil;32.1550 Kolbuszowa;50.2500 Belhatti;15.0818 Bussy;46.5500 Palhano;-4.7450 Anaurilândia;-22.1878 Gilgit;35.9208 Seosaeng;35.3536 Ak’ordat;15.5500 Plymouth;43.7447 Audubon;40.1304 Vif;45.0553 Nova Bassano;-28.7239 Vráble;48.2408 Rignano sull’Arno;43.7237 San Martín Hidalgo;20.4350 Carlosama;0.8658 Tādināda;16.5470 Dilāwarpur;19.0908 Zanica;45.6394 Guichen;47.9675 Brandizzo;45.1766 Loenen;52.2419 Āmudālapalle;15.9301 Old Jefferson;30.3776 Aït Ouaoumana;32.7128 Lāndupdīh;23.1478 Föritz;50.3500 Oak Grove;45.3409 Neya;58.3000 Maipú;-36.8667 Buda-Kashalyova;52.7167 Notre-Dame-des-Prairies;46.0500 Campogalliano;44.6833 Reddiyapatti;10.1581 Bouhlou;34.1333 Weilmünster;50.4333 Temperance;41.7653 Erchie;40.4333 West Perth;43.4700 Kuvshinovo;57.0333 Medikunda;15.9581 Ponta do Sol;32.6806 Moe;-38.1722 Roetgen;50.6500 Perungulam;8.6413 Shiddāpūr;13.6635 Roxborough Park;39.4492 Entrerríos;6.5667 Agadir Melloul;30.2167 Presque Isle;46.6868 Iaboutene;35.0670 Bala Cynwyd;40.0116 Coutras;45.0408 Talwandi Chaudhriān;31.3000 Lajedo do Tabocal;-13.4750 São José da Bela Vista;-20.5928 Pa Sang;18.5261 Taragi;32.2640 Soeda;33.5718 Jacareacanga;-6.2239 Rice Lake;45.4864 Maysville;38.6455 Cordeiros;-15.0389 Cullinan;-25.6728 Rrëshen;41.7667 Mangala;11.9998 Deniliquin;-35.5331 Jolfā;38.9308 Nagykovácsi;47.5800 Tausa;5.1964 Castelnuovo di Porto;42.1333 Clinton;38.3716 Toppenish;46.3806 West Donegal;40.1297 Woodmoor;39.1063 Efringen-Kirchen;47.6556 Amarzgane;31.0500 Großbottwar;49.0014 Marilândia do Sul;-23.7450 Saint-Jean-de-Monts;46.7928 Phimai;15.2229 Rudravaram;15.2660 Pianella;42.4000 Jeannette;40.3277 North Londonderry;40.3227 Ban Ratchakrut;9.7571 Shopokov;42.8400 Minyar;55.0667 Trujillo;39.4653 Lienen;52.1461 Bainbridge;41.3855 Zavāreh;33.4489 Dornstadt;48.4692 Sonsbeck;51.6089 Sopot;42.6500 Bálsamo;-20.7350 Castelbuono;37.9333 Ōtaki;35.2852 Fort Valley;32.5520 Cavan Monaghan;44.2000 El Marmouta;32.0838 Carbondale;41.5714 Karis;60.0708 Kolumalapalle;15.4774 Boville Ernica;41.6500 Bertrix;49.8542 Reyes;-14.2958 Gulf Hills;30.4367 Mansfeld;51.5942 Glencoe;42.1347 Benahavís;36.5190 Tordesillas;41.5000 Anantasāgaram;14.5715 Kryzhopil;48.3842 Hosuru;13.7399 Mürzzuschlag;47.6075 Yaragol;16.9047 Pol-e Sefīd;36.1178 Serra Caiada;-6.1058 Nakaseke;0.7300 Guajeru;-14.5469 Yacimiento Río Turbio;-51.5333 Clayton;39.6627 Beibu;24.6639 Kappeln;54.6614 Tarīchar Kalān;25.4118 Locust Grove;33.3446 Perryton;36.3928 G‘ozg‘on;40.5944 Caimanera;19.9947 Gorodoviki;46.1353 Soltsy;58.1333 Padmapuram;19.2428 Nanzhangcheng;37.9108 Bayport;40.7461 Pitman;39.7335 Cedral;-20.9028 Meshkān;29.4769 Agnita;45.9731 Monmouth;40.9140 Lopatcong;40.7091 Yang Talat;16.3997 Nam Som;17.7694 Sidi Dahbi;33.0500 Kuhmo;64.1250 Yellāreddi;18.5239 Rock Falls;41.7724 Tsallagundla;16.3522 Sidi el Mokhfi;34.6039 Heek;52.1167 Hassi Berkane;34.8333 Oakland;35.2256 Iles;0.9667 Chintakommadinne;14.4267 Arico el Nuevo;28.1904 Mascoutah;38.5192 Flieden;50.4231 Vestignè;45.3833 Aurisina;45.7333 Tiqqi;29.8667 Fairfield Glade;36.0028 Kukrahill;12.2428 San Ignacio;14.6500 Vohburg an der Donau;48.7667 Arnprior;45.4333 Conthey;46.2167 Savoy;40.0600 Tudela de Duero;41.5842 Thief River Falls;48.1108 Mariinskiy Posad;56.1000 Bad Sooden-Allendorf;51.2833 Tirschenreuth;49.8833 Qorovul;41.5569 Belozërsk;60.0333 Zapatoca;6.8167 Ivins;37.1742 Village St. George;30.3598 Audenge;44.6836 Iernut;46.4536 Hūdem;14.9100 Habo;57.9167 Sodankylä;67.4167 Orting;47.0967 Merksplas;51.3667 Candiota;-31.5578 Smiths Falls;44.9000 Stainz;46.8942 Nūlvi;15.2728 Roznov;46.8356 Kulpsville;40.2440 Tanudan;17.2814 Āshtīān;34.5219 Miyada;35.7689 Port Jervis;41.3783 Monte San Savino;43.3333 Azandarīān;34.5075 Signal Mountain;35.1448 Boonton;40.9047 Tleta Taghramt;35.7877 Tashir;41.1244 Lago Vista;30.4519 Mücheln;51.3000 Franklin;43.4499 Mandalapalle;14.0209 Cape St. Claire;39.0433 Hajdúdorog;47.8167 Gigmoto;13.7833 Ustrzyki Dolne;49.4297 Bloomingdale;36.5793 Großräschen;51.5831 Calbe;51.9033 Hausjärvi;60.7884 Tifra;36.6664 Hainichen;50.9697 Perry Heights;40.7977 Majhgawān;24.8000 Jondor Shaharchasi;39.7333 Porkhov;57.7833 Valley Cottage;41.1162 Schuylkill;40.1086 Anthony;32.0131 Tuba City;36.1250 Skovorodino;53.9833 Roverbella;45.2667 Chinaur;25.9467 Ibirapuã;-17.6878 Junín;4.7903 Höshööt;48.9408 Santa Lúcia;-21.6850 Ter Apel;52.8756 Volchansk;59.9333 Vásárosnamény;48.1267 Chachersk;52.9161 Cortez;37.3503 Gremyachinsk;58.5667 Poteau;35.0282 Lake Villa;42.4184 Strzyżów;49.8833 Štětí;50.4531 North Madison;41.8297 Amalou;36.4778 Moranbah;-22.0016 Forbes;-33.3817 Chanute;37.6695 Sokyriany;48.4500 San Juan de Arama;3.3736 Brooksville;28.5404 Atmore;31.0927 Seneca;34.6818 Ortaköy;40.2830 Collier;40.3991 Rava-Rus’ka;50.2500 Baraolt;46.0750 Gangājalghāti;23.4200 Barntrup;51.9831 Sanger;33.3715 Taft;35.1268 Kościelisko;49.2833 Reichelsheim;49.7149 Ogulin;45.2669 Steinheim am Albuch;48.6922 Monforte del Cid;38.3792 Solotvyno;47.9597 Barro Alto;-14.9683 Sanatoga;40.2497 Pont-Rouge;46.7500 Chamonix-Mont-Blanc;45.9222 San Isidro;9.9369 Redlynch;-16.8833 Sarbīsheh;32.5756 Fürstenfeld;47.0500 Szeghalom;47.0272 Bath;43.9346 Graham;33.1017 Smižany;48.9556 Farob;39.2408 Catuípe;-28.2500 Le Poiré-sur-Vie;46.7686 Maryborough;-37.0500 Tăşnad;47.4772 Inékar;15.9492 Tlanalapa;19.8167 Itapeva;-22.7678 Casorate Primo;45.3167 Sredets;42.3500 Rakitovo;41.9833 Gyümai;33.7560 Aigues-Mortes;43.5667 Park Hills;37.8211 Midutūru;15.7667 Clarendon Hills;41.7981 Żuromin;53.0667 Piranguinho;-22.4008 Champlain;45.5333 Ulstein;62.3564 Briceño;7.1111 Chiusi;43.0167 Spello;42.9889 Hernani;11.3239 Booneville;34.6643 Orosi;36.5433 Borgentreich;51.5667 Hoquiam;46.9863 Ronciglione;42.2894 Mrakovo;52.7161 Großenlüder;50.5925 Coaticook;45.1333 Tibro;58.4167 São Miguel;-5.1250 Andriivka;49.5325 Marāveh Tappeh;37.9042 San Gavino Monreale;39.5499 Uglegorsk;49.0667 Santana do Manhuaçu;-20.1078 Poniatowa;51.1833 Quezalguaque;12.5078 Wyoming;39.2297 Çamlıyayla;37.1703 Ueckermünde;53.7389 Fair Lakes;38.8530 Reserve;30.0741 Gubden;42.5658 Lamesa;32.7333 Tangará;-27.1050 Olmsted Falls;41.3657 Gibsonville;36.0993 Plabennec;48.5019 Chyhyryn;49.0833 Villanueva de Córdoba;38.3167 Orange Park;30.1706 Sollefteå;63.1667 Perl;49.4667 El Espinal;16.4906 Chamba;8.7000 Cordisburgo;-19.1250 Busk;49.9667 Barvynkove;48.9067 Nádudvar;47.4167 Pleasant Hill;38.8059 Minto;43.9167 East York;39.9687 Jalhay;50.5572 Kalaun;29.8300 Wiang Haeng;19.5500 Demirözü;40.1639 Ad Darbāsīyah;37.0728 Dunaföldvár;46.8089 Hollymead;38.1266 Bryan;41.4706 Morden;49.1919 La Libertad;16.7804 Bicaz;46.9108 Commerce;33.2421 Hakubachō;36.6981 Covington;35.5660 Wieruszów;51.3000 Volvera;44.9500 Ferreira do Zêzere;39.6833 Unterwellenborn;50.6586 Reginópolis;-21.8878 Wesley Chapel;34.9985 Litchfield Beach;33.4773 Lakhzazra;33.0333 Zuera;41.8692 Highland Heights;41.5518 Oxford;36.3155 Bushtyno;48.0503 Dobanovci;44.8333 Sortino;37.1667 Ouro Verde;-21.4894 Penkridge;52.7252 Virgínia;-22.3328 Dobrada;-21.5167 Mauléon;46.9228 Long Hill;40.6838 Tecumseh;42.0066 Usiacurí;10.7500 McKee City;39.4465 Lambarkiyine;33.2000 Andrushivka;50.0167 Hillview;38.0562 Bösel;53.0058 Nefasīt;15.3333 Dorgali;40.3000 São Sebastião da Amoreira;-23.4650 Nariño;5.6092 Arealva;-22.0286 Goundam;16.4144 Laishevo;55.4000 Takiéta;13.6806 McCordsville;39.8966 San Lorenzo;18.1875 Gonzales;36.5055 Ustyuzhna;58.8333 La Belleza;5.8614 Yasinia;48.2728 Worplesdon;51.2720 Meßkirch;47.9928 Lützen;51.2597 Canford Cliffs;50.7000 Gavorrano;42.9250 Micco;27.8683 Fort Mitchell;39.0460 Ittiri;40.6000 Oulad Khallouf;34.7167 Dunn;35.3114 Solosuchiapa;17.4000 Lipki;53.9333 Verkhoturye;58.8667 Kosiv;48.3150 Ogden;34.2656 Clinton;35.5069 Druid Hills;33.7842 Takaharu;31.9284 Osternienburg;51.8000 Mono;44.0167 Baía Formosa;-6.3689 Francisco Santos;-6.9928 Kiuruvesi;63.6500 Muzo;5.5313 Pratápolis;-20.7450 Na Yung;17.9142 Yalí;6.6767 Japurá;-23.4700 Itauçu;-16.2008 Ferrandina;40.5000 Riacho dos Cavalos;-6.4428 Wharton;29.3177 Calvisano;45.3489 Summit View;47.1343 Village Green-Green Ridge;39.8639 Tanquinho;-11.9789 Glenwood;41.5410 Jauru;-15.3419 Torre de Moncorvo;41.2000 Mataraca;-6.6008 San Fernando;13.6767 Independence;37.2119 Cagli;43.5500 Löwenberg;52.8960 Kirk of Shotts;55.8230 Rio das Flores;-22.1678 Kennedy;40.4768 Verkhniy Mamon;50.1678 Tarrafas;-6.6839 Itamari;-13.7778 Meltham;53.5920 Pöytyä;60.7167 Mahopac;41.3688 Closter;40.9733 Jóia;-28.6469 Karmaskaly;54.3694 Dayton;30.0315 Gresham Park;33.7053 San Sebastián;18.3356 Delavan;42.6282 Monticello;33.6257 South Kensington;39.0188 Alfonso Castañeda;15.7933 Novoselitskoye;44.7494 Monona;43.0540 Queimada Nova;-8.5789 Toro;41.5200 Iwaizumi;39.8431 Ağlasun;37.6494 Pochinok;54.4000 Lake of the Woods;38.3343 Lanco;-39.4500 Paris;39.6148 Socorro;34.0543 Dubovskoye;47.4092 Krupki;54.3167 Itaquara;-13.4508 Herencia;39.3669 Pokrovka;42.7500 Baxter;46.3426 Obluchye;49.0167 Westwego;29.9058 Bee Cave;30.3084 El Dovio;4.5167 Ladysmith;48.9975 Sulakyurt;40.1575 Tuneiras do Oeste;-23.8708 Laitila;60.8833 Aibonito;18.1398 Bridgewater;44.3700 Santa Isabel do Ivaí;-23.0053 Guaraçaí;-21.0283 Nova Glória;-15.1428 Gering;41.8275 Los Altos Hills;37.3669 Laanoussar;33.6833 Sitka;57.2401 Vyetka;52.5667 Lycksele;64.6000 Narrabri;-30.3317 Center Line;42.4805 Barre;44.1998 Lewistown;40.5964 Smithfield;36.9755 Blanchard;35.1524 Richland Hills;32.8095 Hastings-on-Hudson;40.9904 Sallisaw;35.4606 Bryans Road;38.6145 Milton;47.2524 Biryusinsk;55.9667 Namsos;64.4656 Chapaev;50.2000 Orocué;4.7942 As Sallūm;31.5500 Outjo;-20.1089 Būlaevo;54.9056 Alvorada;-12.4800 Moengo;5.6167 Zhänibek;49.4167 Pampa del Infierno;-26.5167 Hammerfest;70.6634 Aguelhok;19.4614 Heyin;36.0451 Qusmuryn;52.4580 Kibale;0.7911 Dilolo;-10.6833 San Julián;-49.3000 Ertis;53.3333 Kemijärvi;66.7167 Bairnsdale;-37.8333 Gaoual;11.7540 Kapoeta;4.7750 Port Augusta;-32.4925 Librazhd;41.1833 Wick;58.4540 Kiama;-34.6708 Coracora;-15.0170 Thames;-37.1383 Atherton;-17.2658 Aiquile;-18.1667 Rabaul;-4.2000 Seymour;-37.0300 Vanrhynsdorp;-31.6167 Port Saint John’s;-31.6288 Newman;-23.3539 Tranqueras;-31.1833 Kerikeri;-35.2244 Cooma;-36.2317 Carnarvon;-30.9667 Tumut;-35.3039 Kieta;-6.2158 Selfoss;63.9333 Roma;-26.5733 Nata;-20.2106 Yamba;-29.4400 Northam;-31.6531 Charagua;-19.7906 Kishkeneköl;53.6394 Rinconada;-22.4333 Awjilah;29.1081 Cliza;-17.6000 Stawell;-37.0500 Yeppoon;-23.1288 Makarov;48.6333 Kaitaia;-35.1125 Scone;-32.0483 San Ramón;-13.2672 Karasburg;-28.0167 Dalaba;10.6560 Ingeniero Guillermo N. Juárez;-23.9000 Oficina María Elena;-22.3451 Zouar;20.4500 Melut;10.4404 Comandante Luis Piedra Buena;-49.9830 San Carlos;-17.4044 Goondiwindi;-28.5461 Verkhnevilyuysk;63.4506 Përmet;40.2333 Cobram;-35.9667 Queanbeyan;-35.3533 Albury;-36.0806 Ingeniero Jacobacci;-41.3000 Bir Anzarane;23.8918 Lithgow;-33.4833 Gyangzê;28.9203 Richmond;-33.5983 Polýgyros;40.3783 Veintiocho de Noviembre;-51.6500 Dinguiraye;11.2990 Biloela;-24.4002 Chepes;-31.3500 Maltahöhe;-24.8333 Uncia;-18.4681 Chonchi;-42.6219 Vadsø;70.0803 Beni Ounif;32.0500 Mali;12.0840 Desaguadero;-16.5684 Byron Bay;-28.6483 General Conesa;-40.1000 San Antonio de los Cobres;-24.2178 Singleton;-32.5667 Wonthaggi;-38.6056 Bajram Curri;42.3581 Bilibino;68.0500 Kununurra;-15.7736 Berri;-34.2833 Otavi;-19.6500 Jinzhong;26.3504 Mayumba;-3.4167 Victor Harbor;-35.5500 Lismore;-28.8167 Igarka;67.4667 Ingham;-18.6508 Mitzic;0.7833 Turukhansk;65.7931 Susuman;62.7833 Oranjemund;-28.5517 Bagdarin;54.4444 Smithton;-40.8440 Svolvær;68.2353 Westport;-41.7581 Finnsnes;69.2294 Perito Moreno;-46.5886 Narrogin;-32.9360 Manjimup;-34.2411 Camargo;-20.6403 Gobernador Gregores;-48.7667 Tepelenë;40.2967 Pofadder;-29.1286 Victorica;-36.2167 Manica;-18.9344 Samaipata;-18.1794 Sokolo;14.7328 Magdalena;-13.2606 Merimbula;-36.8983 Dehiba;32.0167 Comandante Fontana;-25.3333 La Paloma;-34.6500 Port Hedland;-20.3100 Apolo;-14.7200 Ersekë;40.3333 Las Lajas;-38.6000 Çorovodë;40.5000 Pevek;69.7000 El Maitén;-42.0500 Karmah an Nuzul;19.6008 Nautla;20.2167 Sicasica;-17.3333 Vergara;-32.9500 Teseney;15.1100 Weipa;-12.6300 Tirupati;13.6500 Pukë;42.0500 Clare;-33.8333 Ulaan-Uul;44.3337 Proserpine;-20.4016 Wallaroo;-33.9167 Katanning;-33.6908 Lavumisa;-27.3167 Padilla;-19.3000 Port Douglas;-16.4834 Yomou;7.5660 Tessalit;20.2011 Turangi;-38.9889 Baltasar Brum;-30.7167 Kirkenes;69.7269 Srednekolymsk;67.4667 Zhigansk;66.7708 Trancas;-26.2172 Charleville;-26.4016 Mopipi;-21.2019 Mezen;65.8333 Rørvik;64.8619 Juradó;7.1114 Hokitika;-42.7156 Mkokotoni;-5.8800 Teeli;51.0086 Sinnamary;5.3800 Bordertown;-36.3118 Karungu;-0.8496 Aiguá;-34.2000 Buur Gaabo;-1.2175 Mangbwalu;1.9352 Tom Price;-22.6939 I-n-Amguel;23.6936 Esperance;-33.8611 Longreach;-23.4422 Puerto Villamil;-0.9568 Merredin;-31.4820 Urubamba;-13.3042 Donegal;54.6540 Hlatikulu;-26.9667 Río Mayo;-45.6869 Cochrane;-47.2547 Mount Barker;-34.6300 Saint-Georges;3.9105 Cloncurry;-20.7047 Scottsdale;-41.1667 Rodeo;-30.2164 Ísafjörður;66.0758 Bourke;-30.1000 Te Anau;-45.4167 Chumbicha;-28.8667 Exmouth;-21.9331 Tasiilaq;65.6136 Nauta;-4.5083 Severo-Kuril’sk;50.6667 Tarabuco;-19.1667 Queenstown;-42.0667 Baures;-13.6556 Al ‘Alamayn;30.8333 El Dorado;6.7167 Höfn;64.2540 Jaqué;7.5181 Boffa;10.1850 Katwe;-0.1296 Coroico;-16.1833 Egilsstaðir;65.2833 Saskylakh;71.9653 Lehututu;-23.9169 Sorata;-15.7733 Roura;4.7300 Kaikoura;-42.4000 José Batlle y Ordóñez;-33.4667 Tumby Bay;-34.3667 Alexander Bay;-28.6083 Rockhampton;-23.3781 Maitland;-32.7167 Penola;-37.3786 Borgarnes;64.5333 Mazatán;29.0167 Huinan;42.6229 Innisfail;-17.5238 Kalyān;19.2502 Mysore;12.3086 Novyy Port;67.6919 Nokaneng;-19.6639 Barcaldine;-23.5555 Kingston South East;-36.8167 Gawler;-34.5981 Peterborough;-32.9667 Streaky Bay;-32.8000 Puerto Williams;-54.9333 Cuevo;-20.4500 Alto Río Senguer;-45.0167 Kalbarri;-27.7100 Artëmovsk;54.3483 Uummannaq;70.6747 Sierra Colorada;-40.5833 Iracoubo;5.4804 Ouyen;-35.0667 Halls Creek;-18.2300 Chibemba;-15.7355 Port Denison;-29.2750 Wagin;-33.3167 Tajarhī;24.2622 Katherine;-14.4667 Lokwabe;-24.0961 Qasigiannguit;68.8201 Paamiut;61.9944 Tsau;-20.1686 Zaragoza;13.5833 Santa Cruz de la Sierra;-17.7520 Tarutung;2.0167 Kazachye;70.7522 Nakhodka;67.7206 Hāthras;27.3600 Port Pirie;-33.1858 Greytown;10.9467 Sitalpur;27.6300 Príncipe da Beira;-12.4167 Lavrentiya;65.5842 Meningie;-35.6883 Hughenden;-20.8438 Ambāla;30.3786 Verkhoyansk;67.5500 Ciudad Arce;13.8333 Cowell;-33.6833 Uad Damran;27.4181 Yulara;-25.2406 Susques;-23.4167 Upernavik;72.7869 Chumikan;54.7000 Yélimané;15.1180 Bicheno;-41.8667 Roebourne;-20.7667 Winton;-22.3913 Oatlands;-42.3000 Kempsey;-31.0833 Gingin;-31.3400 Godhavn;69.2472 Ayan;56.4583 Wilcannia;-31.5650 Harīpur;31.5322 Nanyangcun;36.0819 Laverton;-28.6280 Pīlibhīt;28.6333 Onslow;-21.6333 Dudhauni;28.0300 Tamworth;-31.0833 Omolon;65.2667 Leonora;-28.8845 Wyndham;-15.4825 Linxi;43.5171 Comallo;-41.0333 Eidsvold;-25.3667 Pannawonica;-21.6350 Zhilinda;70.1333 Meekatharra;-26.5931 Panying;33.0023 Ubombo;-27.5667 Southern Cross;-31.2500 Three Springs;-29.5333 Ituni;5.5333 Aguilares;13.9500 Kimba;-33.1333 Richmond;-20.7305 Qaanaaq;77.4667 Mālegaon;20.5500 Uelen;66.1594 Theodore;-24.9500 Gastre;-42.2667 Candelaria;13.7500 Norseman;-32.1961 Mikhalkino;69.4353 Navsāri;20.8504 Telsen;-42.3833 San José Villanueva;13.5833 Karumba;-17.4838 Isemi-Ile;7.9700 Morawa;-29.2111 Tonk;26.1505 Andamooka;-30.4470 Ravensthorpe;-33.5831 Georgetown;-18.3000 Chengde;40.9604 Mount Magnet;-28.0600 Oymyakon;63.4629 Boulia;-22.9000 Porbandar;21.6425 Espungabera;-20.4531 Halfmoon Bay;-46.9000 Santa María;13.3500 San Ignacio;14.3333 Adelaide River;-13.2381 Kairaki;-43.3860 Burketown;-17.7167 Scoresbysund;70.4853 Progress;49.7504 Ivanhoe;-32.8983 Thargomindah;-28.0000 Pine Creek;-13.8231 Santa Elena;13.3833 Ikela;-1.1833 Cazombo;-11.9058 Shoyna;67.8778 Enurmino;66.9500 Timbedgha;16.2447 Santa Rosa de Lima;13.6167 Conchagua;13.3000 El Tránsito;13.3500 Camooweal;-19.9167 Carnarvon;-24.8672 Hubli;15.3619 Korf;60.3667 Birdsville;-25.8989 Bedourie;-24.3500 Mount Isa;-20.7261 Windorah;-25.4206 Punta Prieta;28.9289 Sharbaqty;52.4800 Chirilagua;13.2167 Al ‘Uqaylah;30.2558 Victoria;13.9500 Pasaquina;13.5844 Sesori;13.7167 Kovda;66.6919 Bhuj;23.2500 Kingoonya;-30.9000 Tiyerbes;64.3728 Ust’-Nyukzha;56.5608 Chegga;25.3733 Ust’-Olenëk;72.9855 Olenëk;68.5000 Ambarchik;69.6167 Logashkino;70.8536 Charlotte Amalie;18.3420 Vohitrafeno;-21.6667 Vinanitelo;-21.7167 Bolsward;53.0667 Gūlyam;15.3598 Antsoantany;-19.7000 Miadanandriana;-19.0333 Mahabako;-21.7500 Ambahatrazo;-21.8667 Puttige;13.0751 Vondrozo;-22.8206 Ambatolava;-23.5667 Isahara;-23.7167 Antaretra;-21.0667 Tamponala;-19.3000 Ambodivoara;-14.4000 Morafeno;-21.2000 Závora;-24.5167 Erraguntlakota;13.9622 Ambohitrambo;-18.9167 Ankirondro;-19.6333 Ambohimahazo;-20.6667 Mataguá;22.2370 Ţālkhvoncheh;32.2631 Andranambolava;-20.9000 Marosakoa;-16.2333 Belavabary;-18.8333 Ankerana;-21.0333 Sahatona-Tamboharivo;-20.9667 Ampitahana;-21.1333 Soatanana;-21.3833 Ambararatabe;-18.9900 Aboso;5.3633 Zoma-Bealoka;-18.8500 Sahanivotry-Manandona;-20.1167 Boanamary;-15.8333 Vinanitelo;-22.0167 Beanana;-17.3667 Ambodimadiro;-14.6000 Ampasimazava;-17.6667 Ifarantsa;-24.9333 Tanambao-Daoud;-13.9833 Tujg;32.0686 Mora;14.3278 Kalimala;18.0738 Ambatomivary;-23.8333 Vohitany;-24.1333 Thị Trấn Mậu A;21.8781 Maroharatra;-20.7333 Miarinarivo;-22.0833 Ambatomifanongoa;-20.2667 Ambovonomby;-14.3667 Los Ríos;18.5219 Daraina;-13.2000 Ambohimiarivo;-19.8500 Dujiashigou;37.7691 Anosimparihy;-21.5000 Sandravinany;-24.0333 Murgap;37.4964 Mazoe;-17.5167 Bunji;35.6422 Soavimbahoaka;-18.6833 Amparihy;-15.6667 Ambatolahy;-22.5333 Fanjakana;-21.1833 Masiaboay;-23.9000 Ambodisikidy;-14.2333 Ebelo;-24.4833 Mboki;5.3160 Chartoûn;33.7711 Miary-Taheza;-23.1333 Andranomenatsa;-23.3167 Vohitsaoka;-22.0333 Esira;-24.3333 Soahany;-18.6667 Vodiriana;-19.1667 Bekodoka;-16.9667 Ianapera;-23.6167 Jangany;-22.8500 Namakadu;14.0060 Bedidy;-17.4667 Ambodimandresy;-14.7833 Tsararano;-17.3333 Gadoon;5.6897 Beheloka;-23.9017 Ambalanjanakomby;-16.7000 Mahabe;-17.0833 Andranopasy;-21.2833 Tīgaon;21.6456 Anontsibe-Sakalava;-21.3667 Katsepy;-15.7667 Tanamarina;-21.5731 Amborompotsy;-20.6000 Betrandraka;-17.0333 Puerto America;-11.5500 Sorab;14.3814 Lenīnskīy;52.2528 Yueyaquan;40.1256 Williston;44.4345 Colts Neck;40.2928 Ban Non Sombun;18.2983 P’yŏngch’ang;37.3675 Pālkot;22.8748 Birni Lafia;11.9783 Ban Phan Chali;16.6333 Narasimharājapura;13.6108 Kodivalasa;13.2547 Ambinanintromby;-21.6667 Gamba;-2.7250 Kouarfa;10.4833 Karimunjawa;-5.8192 Mae O;19.6500 Nödinge-Nol;57.9000 Penn Forest;40.9571 Mohlanapeng;-29.6975 Rangasamudram;13.7140 Imlil;31.7567 Ankazotsifantatra;-19.9500 Ambodiriana;-17.8861 Krasnyy Yar;53.3239 Jixian;35.7321 Vanipenta;14.7906 Basso;10.5000 Ban Pha Bong;19.2266 Sūknah;29.0669 Dulce Nombre de Jesús;10.0838 Djangoa;-13.7833 Alden;42.9114 Conway;44.0085 Pakhtaobod;38.4667 Sirka;9.5719 Anjahamana;-18.3667 Pisac;-13.4242 Oulad ’Azzouz;32.7693 Ottappidāram;8.9127 Piprai;24.5097 Qarabalyq;53.7506 Fandrandava;-21.5167 Ban Bueng Kok;16.6833 Philipstown;41.4189 Sävja;59.8167 Repatriación;-25.5300 Andrainjato;-21.4667 San Fernando;9.2797 Ban Mae Sam Laep;17.9750 Olmos;-5.9855 Mohdra;24.1849 Sturbridge;42.1076 Pindra;24.9595 Mikun;62.3667 Pondalūru;14.2535 Ban Mae Chedi;19.1833 Ban Dong Mada;19.7237 Dangcheng;39.5161 Bavānāt;30.4667 Southport;42.0400 Xiba;40.1645 Woodbury;41.5615 Seforong;-30.1028 Tsaramasoandro;-17.9833 Shiyuan;35.7991 Tālavādi;11.7780 Ranomafana;-21.2500 Ak-Suu;42.8000 Chervyen;53.7078 Tayakou;10.5500 Zengjiaba;32.1263 Pleasant Valley;41.7697 Ban Lao Yao;18.3667 Gonikoppal;12.1830 Barwādih;23.8478 Mallampalli;18.1105 Venecia;10.3357 Aqadyr;48.2749 Tounfafi;14.0464 Torihama;35.6006 Datori;10.4017 Pueblo Viejo;19.1781 Canoas;8.5333 Ādivāla;13.9131 Capitán Mauricio José Troche;-25.7500 Aweitancun;47.7251 Alberdi;-26.1900 Avsallar;36.5833 Langar;39.4500 Maizal;19.6500 Ramree;19.0833 Marne;13.3284 Gaada;35.1594 Herkimer;43.0610 Ban Wiang Ka Long;19.2332 Ankadimanga;-18.9667 Xiada;24.0391 Naurhiya;24.2069 Sultan-Yangiyurt;43.2167 Bou Zemou;32.1114 Lisbon;44.0265 South Strabane;40.1756 Bougou;9.4333 Åhus;55.9167 Iāwar;23.0094 Vəndam;40.9447 Sonsoro;11.0875 Taisar;22.4867 Dombarovskiy;50.7550 Fitampito;-20.9667 Jianshi;24.5761 Igrim;63.1933 Soldato-Aleksandrovskoye;44.2659 Falam;22.9136 Zhangping;37.6339 Cusseta;32.3470 Kesli;23.4183 Syurte;48.5033 Sokotindji;10.8528 Butler;41.0358 Burlington;41.7598 Ukwā;21.9710 Benbutucun;42.0263 Pedro Luro;-39.5000 Nūlivedu;14.1002 Esopus;41.8425 Deh-e Shū;30.4344 Douar Oulad Bouziane;34.2083 Berkine;33.7665 Ban Sathan;18.2667 Kanajanahalli;14.1092 Oppicherla;16.4444 Alakamisy Anativato;-19.8833 Bāyaram;18.0506 Portland;41.5988 Jantho;5.3000 Mariyādau;24.2762 Karajgi;17.2827 Topsham;43.9614 Ban Khi Lek;19.0625 Timmāpuram;15.4887 Katteragandla;15.0091 Manchester;42.9921 Shendē;10.6333 Hastings;43.3215 Anan’evo;42.7300 Ghattupal;17.0725 Anaconda;46.0608 Mēga;4.0167 Ratangarh;24.8167 Moisei;47.6561 Newfane;43.2818 Barei;9.6833 Āltūn Kawbrī;35.7533 Ban Kham Pom;15.9653 Sutton;42.1337 Xalqobod;37.4597 Moore;40.7798 Kondrukota;17.1717 Boali;4.8000 Bandio;13.8888 Douar Oulad Sidi Moussa;32.2726 Helena Valley Southeast;46.6219 Douar El Mellaliyine;35.6264 Padinska Skela;44.9500 Chesterfield;40.1166 Analaroa;-18.4083 Rancho Mission Viejo;33.5140 Southwick;42.0544 Tasso;9.6737 Mikhaylovskoye;43.0997 Xiangping;24.5892 Prabhāt Pattan;21.6407 Walworth;43.1633 Barrington;43.2139 Talata-Angavo;-18.2000 Veinticinco de Diciembre;-24.7000 Tanamarina-Sakay;-21.4667 Monte Cristo;-31.3431 Phagu;26.8403 Càbras;39.9333 Putnam;41.9093 Qorovulbozor;39.5000 Bīkē;9.5297 Macedon;43.0792 Borja;-25.9528 Romang;-29.5000 Ḩorr-e Rīāḩī;32.1447 Umreth;22.1262 Intich’o;14.2667 Leichi;36.3351 La Cruz;11.0845 Jackson;40.3774 Ban Yaeng;16.8833 Ban Ngio Ngam;17.6671 Qarqaraly;49.4167 Thompson;41.9798 Podstepki;53.5151 Crawford;41.5685 Marale;14.9167 As Sidrah;30.6294 Qahramon;40.3050 Settivāripalle;14.7543 Canela Baja;-31.3989 Chārakunda;16.6916 Jumlā;29.2500 Freetown;41.7714 Kharsāwān;22.7909 Morarano;-19.4333 Aïn el Mediour;30.4000 Dulce Nombre de María;14.1500 Ban Wang Krachae;14.2333 Usworth;54.9400 Ust’-Nera;64.5666 Townsend;42.6671 Dallas;41.3608 Dazhuangzi;40.2321 Vinsady;44.0817 Argudan;43.4200 Gāzulapalle;15.4036 Morafeno;-14.4000 Ankarana-Miraihina;-23.0833 Venkatādripālem;16.0506 Mendon;42.9859 Sānchi;23.4865 Tuskegee;32.4409 Hebron;41.6593 North Codorus;39.8646 Ambodivoanio;-16.1833 Alakamisy-Ambohimahazo;-20.4000 Andilana Avaratra;-17.3500 Nizhniy Odes;63.6500 Ixtapa Zihuatanejo;17.6367 Bazimini;-12.1833 Anteza;-21.6667 Antanankambano;-22.0500 Coatetelco;18.7294 Mahamaibe;-21.7500 Vohilava;-21.7667 Peddannavāripalle;14.2535 Salobe;-23.5333 Bemarivo;-17.6333 Ampary;-19.1833 Yangiobod;41.1192 Springfield;43.2907 Diabugu;13.3833 Antsahavaribe;-13.8500 Anjialava;-14.0667 Mitanty;-21.7333 Ranopiso;-25.0500 Sarasambo;-25.1000 Woodbridge;41.3566 Weare;43.0813 New Scotland;42.6047 Ban Nikhom Phatthana;16.7242 Tsaratanana;-15.6500 Economy;40.6411 Fierenana;-18.4833 Kegen;43.0197 Soamahamanina;-18.9833 Sulahpet;17.4014 Belinta;-19.9500 Antsatramidola;-15.6333 Barskoon;42.1561 Ambalaromba;-14.6667 Mandīshah;28.3515 Ambariokorano;-15.8833 Ivandrika;-22.8667 Iara;-23.0833 Ambodimahabibo;-15.7333 Vatananto;-23.6667 Ambinanin’ Andravory;-13.7667 Ambatoharanana;-16.4333 Soanierana;-25.0000 Benato-Toby;-23.0833 Bemaharivo;-16.1333 Ampasimpotsy-Gara;-18.9667 Marovatolena;-15.1833 Ambatoria;-14.5000 West Manheim;39.7458 Middlebury;44.0043 Maropaika;-22.7000 Sahatsiho-Ambohimanjaka;-20.2000 Ambarimaninga;-16.5000 Akpassi;8.4500 Marotolana;-14.6500 Antaly;-24.3833 Nanmucun;25.1619 Ban Noen Kum Nueng;16.5500 Andribavontsona;-15.4000 Manampaneva;-16.0500 Antambohobe;-22.3000 Nosibe;-13.1500 Ankiliabo;-24.6833 Amboronabo;-22.6667 Bekopaka;-19.0167 Stillwater;42.9701 Mahabo;-24.2833 Beparasy;-19.1667 Manevy;-24.3833 Soamanonga;-23.8667 Tsimafana;-19.7167 Viale;-31.8667 Soanenga;-16.5000 Chanco;-36.2667 Ankirihitra;-16.7667 Antsaidoha-Bebao;-17.3667 Tandrano;-22.0833 Ambalajia;-17.4833 Antseza;-16.2167 Befotaka;-20.8333 Manja;-21.4333 Jonnagiri;15.2303 Mānsong;27.1662 Lieşti;45.6193 Rutland;42.3848 Ladan Kara;40.9264 Douglas;42.0524 East Haddam;41.4798 Miyār;13.1945 Ban Muang Kham;19.5008 Bansang;13.4333 Galela;1.8236 Marcy;43.1731 East Nottingham;39.7621 Kishtwār;33.3135 Angor;37.4639 Upper Leacock;40.0801 Daping;24.6501 Australia;22.4995 West Caln;40.0237 Seneca Falls;42.9136 Douar Ait Taleb;32.3839 Moulay Abdelkader;34.6422 Toulou;14.1688 El Valle;18.9764 Iklod;26.0222 Francisco Caballero Álvarez;-24.1543 Takouta;10.3000 Pendekallu;15.3753 Jabera;23.5582 Ban Si Don Chai;20.1300 Fort Knox;37.8915 Chīchkah;37.0706 Qızılhacılı;40.5808 Ban Pong Tao;18.8333 Mousoulou;7.3899 Pardanjān;32.2539 Al Abraq;32.7867 Mālaimārpuram;14.2096 Rocas de Santo Domingo;-33.6358 Amberomanga;-19.2500 Mahela;-19.4833 Indūrti;17.0055 Ban Sai Yoi;16.4167 Mirdoddi;18.0786 Lenox;43.1113 Mansfield;40.0853 Redding;41.3050 Río Jiménez;10.2556 Pittsgrove;39.5404 Falla;22.1704 Buenavista;9.3333 Aşağı Quşçu;40.9533 Pacuarito;10.1093 Salar;33.8789 Rudewa;-10.1008 Upper Makefield;40.2941 Nayāgaon;24.8015 Hire Vadvatti;15.2235 Marrupa;-13.1833 Hamlin;43.3213 Brighton;40.7023 Andalusia;31.3102 Segaon;21.8585 Gidan Idèr;14.0131 Tilarán;10.4709 Sobhāpur;22.7737 Sèmèrè;9.6268 Verkhniye Achaluki;43.3469 Buved;37.5833 Ellicott;42.1330 Karavan;40.2944 Marlborough;41.6337 Solebury;40.3676 Al Quway‘īyah;24.0464 Clanton;32.8444 Kaldsletta;69.6956 Poninguinim;14.9679 Ankatafa;-13.6167 Newstead;43.0196 London Grove;39.8327 Peralta;18.5167 Ban Charoen Mueang;19.6075 Hamilton;40.9334 Marginea;47.8167 Tanmpègré;10.4800 Chāpalamadugu;16.0730 Munnelli;14.9200 Panfilovka;42.7917 Vostochnyy;39.9222 Nerubaiske;46.5467 Chartiers;40.2505 Freeport;43.8556 Anjahamarina;-18.0167 Carneys Point;39.6967 Jackson;39.9057 East Donegal;40.0823 Windsor;43.2405 Chermen;43.1486 Vícam Pueblo;27.6422 South Londonderry;40.2424 Zhujiagua;38.2242 Tionk Essil;12.7856 Corman Park No. 344;52.2291 Montague;42.5549 Barton;42.0812 Guntersville;34.3671 Hongtuliang;40.9986 Skowhegan;44.7554 Helena Valley West Central;46.6634 Aqsū;52.4502 Olamzé;2.2167 Olovyannaya;50.9500 Putina;-15.4700 Oldeani;-3.3500 Osakarovka;50.5619 Kontcha;7.9667 Ciudad Cortés;9.0221 Ch’osan-ŭp;40.8255 Iqaluit;63.7598 Kalabo;-14.9911 Qazaly;45.7667 Bayghanīn;48.6917 Neiafu;-18.6508 Luân Châu;21.7400 Bossembele;5.2667 Bestöbe;52.4997 Tobyl;52.6980 Okondja;-0.6519 Melekeok;7.5006 Zambezi;-13.5500 Nicoadala;-17.6077 Karibib;-21.9381 Zholymbet;51.7502 Khandyga;62.6660 Ndendé;-2.4014 San Matías;-16.3611 Umba;66.6814 San Javier;-16.2748 Tazovskiy;67.4833 Mundybash;53.2333 Aiyomojok;5.7504 Piggs Peak;-25.9610 Fdérik;22.6783 Tiksi;71.6269 Vossevangen;60.6300 Okhotsk;59.3830 Fort-Shevchenko;44.5167 Witu;-2.3889 Tura;64.2833 San Quintín;30.4837 Ouadda;8.0667 Hohenau;-27.0796 Torghay;49.6260 Chernyshevskiy;63.0128 Villa del Rosario;-24.4167 McMinns Lagoon;-12.5329 Qaşr al Farāfirah;27.0583 Puerto Casado;-22.2896 Ust’-Kamchatsk;56.2167 Mékambo;1.0167 Betanzos;-19.5533 Brownsweg;5.0164 Bongandanga;1.5100 Sangar;63.9241 Khatanga;71.9797 Al Qaşr;25.6959 Saryshaghan;46.1167 Bekily;-24.2162 Batagay;67.6560 Omsukchan;62.5333 Novyy Uoyan;56.1350 Quime;-16.9817 Araouane;18.9050 P’ungsan;40.8175 Vitim;59.4515 Palana;59.0840 Cherskiy;68.7501 Ceduna;-32.1167 Zyryanka;65.7360 De-Kastri;51.4666 Villa Ygatimí;-24.0800 Ligonha;-15.1757 Uspallata;-32.5667 Darregueira;-37.6996 Bukachacha;52.9833 Ugol’nyye Kopi;64.7333 Lukulu;-14.4083 Krasnogorsk;48.4172 Arroyos y Esteros;-25.0500 Ust’-Maya;60.4566 Abaí;-26.0296 Taoudenni;22.6667 San Lorenzo;-21.4167 Saranpaul;64.2600 Villalonga;-39.8829 Entre Ríos;-21.5264 Al Jaghbūb;29.7425 Bîr Mogreïn;25.2167 Sauðárkrókur;65.7461 Provideniya;64.4235 Chokurdakh;70.6183 Marādah;29.2333 Mariscal José Félix Estigarribia;-22.0333 Sohano;-5.4297 Ypejhú;-23.9100 Toltén;-39.2166 Mwenga;-3.0382 Egvekinot;66.3221 El Manteco;7.3483 Pozo Colorado;-23.4300 Omboué;-1.5667 Konza;-1.7496 Evensk;61.9500 Altata;24.6333 Abunã;-9.6954 Taedong;40.6171 Beringovskiy;63.0655 Nasir;8.6000 Capitán Pablo Lagerenza;-19.9200 Kipili;-7.4329 Oktyabr’skiy;52.6636 Ust’-Kuyga;70.0171 Eldikan;60.8000 Nyimba;-14.5495 Fulacunda;11.7730 Lubutu;-0.7431 Regedor Quissico;-24.7257 Bala Cangamba;-13.6833 Villa Rumipal;-32.1879 Ñacunday;-26.0200 Ağdam;40.9053 Villa Martín Colchak;-20.7406 Buluko;-0.7570 Çeleken;39.4362 Puerto Acosta;-15.5333 Los Blancos;-23.6000 Mirbāţ;16.9886 Dikson;73.5070 Klyuchi;56.3167 General Eugenio A. Garay;-20.5200 Daraj;30.1500 Luanza;-8.6996 Hoskins;-5.4746 Charaña;-17.6000 Muhembo;-18.2996 Dibaya;-6.5095 Yerëma;60.3808 Satadougou;12.6170 Zhaltyr;51.6324 Manily;62.4908 Calatrava;1.1164 Massangena;-21.5378 Panda;-24.0629 Villa O’Higgins;-48.4683 Kullorsuaq;74.5792 Quilpie;-26.6161 Chiramba;-16.8921 Sabaya;-19.0147 Mereeg;3.7667 Llica;-19.8500 Calenga;-11.3196 Caluula;11.9667 Tournavista;-8.9322 Tchitado;-17.3167 Yakossi;5.6170 Puerto Pinasco;-22.7167 Tmassah;26.3667 Woomera;-31.1496 Sherlovaya Gora;50.5306 Tsavo;-2.9828 Nizhneyansk;71.4333 Toconao;-23.1903 Tasiusaq;73.3689 Burubaytal;44.9350 Kanyato;-4.4565 Kulusuk;65.5753 Umm al ‘Abīd;27.5170 Bugrino;68.7831 Put’ Lenina;68.5166 Yaupi;-2.8379 Amderma;69.7631 Kangersuatsiaq;72.3797 Amau;-10.0426 Androka;-25.0219 Lusanga;-5.5808 Kraulshavn;74.1111 Hurdiyo;10.5667 Buton;4.2170 Narsarsuaq;61.1458 Bafwasende;1.0103 Ban Huai Hin;12.5697 Bifoun;-0.3333 Il’pyrskiy;59.9600 Savissivik;76.0194 Cuya;-19.1597 Gyda;70.8814 Güeppí;-0.1166 Chuquicamata;-22.3169 Puerto Heath;-12.5200 Yessey;68.4652 Lemsid;26.5482 Mukhomornoye;66.4171 Vorontsovo;71.6983 Grytviken;-54.2806 Piso Firme;-13.6830 Rocafuerte;-0.9329 Peregrebnoye;62.9670 Laryak;61.1012 Lagunas;-20.9829 Andoas;-2.9042 Puca Urco;-2.3328 Zillah;28.5489 Barnīs;23.9460 Soldado Bartra;-2.5161 Ulkan;55.9004 Strelka;61.8670 Bol’sheretsk;52.4390 Karamken;60.2004 Djado;21.0150 Siglan;59.0337 Omchak;61.6333 Shalaurova;73.2204 Khorgo;73.4833 Komsa;61.8680 Pakhachi;60.5816 Indiga;67.6898 Chagda;60.1000 Trofimovsk;72.5997 Tunguskhaya;64.9004 Podkamennaya Tunguska;61.5995 Varnek;69.7153 Utkholok;57.5504 Matochkin Shar;73.2700 Khakhar;57.6666 Menkerya;67.9886 Zvëzdnyy;70.9333 Starorybnoye;72.7666 Sagastyr;73.3779 Zemlya Bunge;74.8983 Agapa;71.4504 Tukchi;57.3670 Numto;63.6667 Nord;81.7166 Timmiarmiut;62.5333 San Rafael;-16.7795 Nordvik;74.0165 ================================================ FILE: etc/eclipse-formatter-config.xml ================================================ ================================================ FILE: etc/license.txt ================================================ Copyright 2023 The original authors 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. ================================================ FILE: evaluate.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # set -eo pipefail if [ -z "$1" ] then echo "Usage: evaluate.sh ( ...)" echo " for each fork, there must be a 'calculate_average_.sh' script and an optional 'prepare_.sh'." exit 1 fi BOLD_WHITE='\033[1;37m' CYAN='\033[0;36m' GREEN='\033[0;32m' PURPLE='\033[0;35m' BOLD_RED='\033[1;31m' RED='\033[0;31m' BOLD_YELLOW='\033[1;33m' RESET='\033[0m' # No Color MEASUREMENTS_FILE="measurements_1B.txt" RUNS=10 DEFAULT_JAVA_VERSION="21.0.1-open" : "${BUILD_JAVA_VERSION:=21.0.1-open}" RUN_TIME_LIMIT=300 # seconds TIMEOUT="" if [ "$(uname -s)" == "Linux" ]; then TIMEOUT="timeout -v $RUN_TIME_LIMIT" else # MacOs if [ -x "$(command -v gtimeout)" ]; then TIMEOUT="gtimeout -v $RUN_TIME_LIMIT" # from `brew install coreutils` else echo -e "${BOLD_YELLOW}WARNING${RESET} gtimeout not available, benchmark runs may take indefinitely long." fi fi function check_command_installed { if ! [ -x "$(command -v $1)" ]; then echo "Error: $1 is not installed." >&2 exit 1 fi } function print_and_execute() { echo "+ $@" >&2 "$@" } check_command_installed java check_command_installed hyperfine check_command_installed jq check_command_installed bc # Validate that ./calculate_average_.sh exists for each fork for fork in "$@"; do if [ ! -f "./calculate_average_$fork.sh" ]; then echo -e "${BOLD_RED}ERROR${RESET}: ./calculate_average_$fork.sh does not exist." >&2 exit 1 fi done ## SDKMAN Setup # 1. Custom check for sdkman installed; not sure why check_command_installed doesn't detect it properly if [ ! -f "$HOME/.sdkman/bin/sdkman-init.sh" ]; then echo -e "${BOLD_RED}ERROR${RESET}: sdkman is not installed." >&2 exit 1 fi # 2. Init sdkman in this script source "$HOME/.sdkman/bin/sdkman-init.sh" # 3. make sure the default java version is installed if [ ! -d "$HOME/.sdkman/candidates/java/$DEFAULT_JAVA_VERSION" ]; then print_and_execute sdk install java $DEFAULT_JAVA_VERSION fi # 4. Install missing SDK java versions in any of the prepare_*.sh scripts for the provided forks for fork in "$@"; do if [ -f "./prepare_$fork.sh" ]; then grep -h "^sdk use" "./prepare_$fork.sh" | cut -d' ' -f4 | while read -r version; do if [ ! -d "$HOME/.sdkman/candidates/java/$version" ]; then print_and_execute sdk install java $version fi done || true # grep returns exit code 1 when no match, `|| true` prevents the script from exiting early fi done ## END - SDKMAN Setup # Check if SMT is enabled (we want it disabled) if [ -f "/sys/devices/system/cpu/smt/active" ]; then if [ "$(cat /sys/devices/system/cpu/smt/active)" != "0" ]; then echo -e "${BOLD_YELLOW}WARNING${RESET} SMT is enabled" fi fi # Check if Turbo Boost is enabled (we want it disabled) if [ -f "/sys/devices/system/cpu/cpufreq/boost" ]; then if [ "$(cat /sys/devices/system/cpu/cpufreq/boost)" != "0" ]; then echo -e "${BOLD_YELLOW}WARNING${RESET} Turbo Boost is enabled" fi fi print_and_execute sdk use java $BUILD_JAVA_VERSION print_and_execute java --version print_and_execute ./mvnw --quiet clean verify print_and_execute rm -f measurements.txt print_and_execute ln -s $MEASUREMENTS_FILE measurements.txt echo "" # check if measurements_xxx.out exists if [ ! -f "${MEASUREMENTS_FILE%.txt}.out" ]; then echo -e "${BOLD_RED}ERROR${RESET}: ${MEASUREMENTS_FILE%.txt}.out does not exist." >&2 echo "Please create it with:" echo "" echo " ./calculate_average_baseline.sh > ${MEASUREMENTS_FILE%.txt}.out" echo "" exit 1 fi # Run tests and benchmark for each fork filetimestamp=$(date +"%Y%m%d%H%M%S") # same for all fork.out files from this run failed=() for fork in "$@"; do set +e # we don't want prepare.sh, test.sh or hyperfine failing on 1 fork to exit the script early # Run prepare script if [ -f "./prepare_$fork.sh" ]; then print_and_execute source "./prepare_$fork.sh" else print_and_execute sdk use java $DEFAULT_JAVA_VERSION fi # Run the test suite print_and_execute $TIMEOUT ./test.sh $fork if [ $? -ne 0 ]; then failed+=("$fork") echo "" echo -e "${BOLD_RED}FAILURE${RESET}: ./test.sh $fork failed" continue fi echo "" # Run the test on $MEASUREMENTS_FILE; this serves as the warmup print_and_execute $TIMEOUT ./test.sh $fork $MEASUREMENTS_FILE if [ $? -ne 0 ]; then failed+=("$fork") echo "" echo -e "${BOLD_RED}FAILURE${RESET}: ./test.sh $fork $MEASUREMENTS_FILE failed" continue fi echo "" # re-link measurements.txt since test.sh deleted it print_and_execute rm -f measurements.txt print_and_execute ln -s $MEASUREMENTS_FILE measurements.txt # Use hyperfine to run the benchmark for each fork HYPERFINE_OPTS="--warmup 0 --runs $RUNS --export-json $fork-$filetimestamp-timing.json --output ./$fork-$filetimestamp.out" # check if this script is running on a Linux box if [ "$(uname -s)" == "Linux" ]; then check_command_installed numactl # Linux platform # prepend this with numactl --physcpubind=0-7 for running it only with 8 cores numactl --physcpubind=0-7 hyperfine $HYPERFINE_OPTS "$TIMEOUT ./calculate_average_$fork.sh 2>&1" else # MacOS hyperfine $HYPERFINE_OPTS "$TIMEOUT ./calculate_average_$fork.sh 2>&1" fi # Catch hyperfine command failed if [ $? -ne 0 ]; then failed+=("$fork") # Hyperfine already prints the error message echo "" continue fi done set -e # Summary echo -e "${BOLD_WHITE}Summary${RESET}" for fork in "$@"; do # skip reporting results for failed forks if [[ " ${failed[@]} " =~ " ${fork} " ]]; then echo -e " ${RED}$fork${RESET}: command failed or output did not match" continue fi # Trimmed mean = The slowest and the fastest runs are discarded, the # mean value of the remaining three runs is the result for that contender trimmed_mean=$(jq -r '.results[0].times | sort_by(.|tonumber) | .[1:-1] | add / length' $fork-$filetimestamp-timing.json) raw_times=$(jq -r '.results[0].times | join(",")' $fork-$filetimestamp-timing.json) if [ "$fork" == "$1" ]; then color=$CYAN elif [ "$fork" == "$2" ]; then color=$GREEN else color=$PURPLE fi echo -e " ${color}$fork${RESET}: trimmed mean ${BOLD_WHITE}$trimmed_mean${RESET}, raw times ${BOLD_WHITE}$raw_times${RESET}" done echo "" ## Leaderboard - prints the leaderboard in Markdown table format echo -e "${BOLD_WHITE}Leaderboard${RESET}" # 1. Create a temp file to store the leaderboard entries leaderboard_temp_file=$(mktemp) # 2. Process each fork and append the 1-line entry to the temp file for fork in "$@"; do # skip reporting results for failed forks if [[ " ${failed[@]} " =~ " ${fork} " ]]; then continue fi trimmed_mean=$(jq -r '.results[0].times | sort_by(.|tonumber) | .[1:-1] | add / length' $fork-$filetimestamp-timing.json) # trimmed_mean is in seconds # Format trimmed_mean as MM::SS.mmm # using bc trimmed_mean_minutes=$(echo "$trimmed_mean / 60" | bc) trimmed_mean_seconds=$(echo "$trimmed_mean % 60 / 1" | bc) trimmed_mean_ms=$(echo "($trimmed_mean - $trimmed_mean_minutes * 60 - $trimmed_mean_seconds) * 1000 / 1" | bc) trimmed_mean_formatted=$(printf "%02d:%02d.%03d" $trimmed_mean_minutes $trimmed_mean_seconds $trimmed_mean_ms) # Get Github user's name from public Github API (rate limited after ~50 calls, so results are cached in github_users.txt) set +e github_user__name=$(grep "^$fork;" github_users.txt | cut -d ';' -f2) if [ -z "$github_user__name" ]; then github_user__name=$(curl -s https://api.github.com/users/$fork | jq -r '.name' | tr -d '"') if [ "$github_user__name" != "null" ]; then echo "$fork;$github_user__name" >> github_users.txt else github_user__name=$fork fi fi set -e # Read java version from prepare_$fork.sh if it exists, otherwise assume 21.0.1-open java_version="21.0.1-open" # Hard-coding the note message for now notes="" if [ -f "./prepare_$fork.sh" ]; then java_version=$(grep -F "sdk use java" ./prepare_$fork.sh | cut -d' ' -f4) if grep -F "native-image" -q ./prepare_$fork.sh ; then notes="GraalVM native binary" fi fi # check if Java source file uses Unsafe if grep -F "theUnsafe" -q ./src/main/java*/dev/morling/onebrc/CalculateAverage_$fork.java ; then # if notes is not empty, append a comma and space before the unsafe note notes="${notes:+$notes, }uses Unsafe" fi echo -n "$trimmed_mean;" >> $leaderboard_temp_file # for sorting echo -n "| # " >> $leaderboard_temp_file echo -n "| $trimmed_mean_formatted " >> $leaderboard_temp_file echo -n "| [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_$fork.java)" >> $leaderboard_temp_file echo -n "| $java_version " >> $leaderboard_temp_file echo -n "| [$github_user__name](https://github.com/$fork) " >> $leaderboard_temp_file echo -n "| $notes " >> $leaderboard_temp_file echo "|" >> $leaderboard_temp_file done # 3. Sort leaderboard_temp_file by trimmed_mean and remove the sorting column sort -n $leaderboard_temp_file | cut -d ';' -f 2 > $leaderboard_temp_file.sorted # 4. Print the leaderboard echo "" echo "| # | Result (m:s.ms) | Implementation | JDK | Submitter | Notes |" echo "|---|-----------------|--------------------|-----|---------------|-----------|" # If $leaderboard_temp_file.sorted has more than 3 entires, include rankings if [ $(wc -l < $leaderboard_temp_file.sorted) -gt 3 ]; then head -n 1 $leaderboard_temp_file.sorted | tr '#' 1 head -n 2 $leaderboard_temp_file.sorted | tail -n 1 | tr '#' 2 head -n 3 $leaderboard_temp_file.sorted | tail -n 1 | tr '#' 3 tail -n+4 $leaderboard_temp_file.sorted | tr '#' ' ' else # Don't show rankings cat $leaderboard_temp_file.sorted | tr '#' ' ' fi echo "" # 5. Cleanup rm $leaderboard_temp_file ## END - Leaderboard # Finalize .out files echo "Raw results saved to file(s):" for fork in "$@"; do if [ -f "$fork-$filetimestamp-timing.json" ]; then cat $fork-$filetimestamp-timing.json >> $fork-$filetimestamp.out rm $fork-$filetimestamp-timing.json fi if [ -f "$fork-$filetimestamp.out" ]; then echo " $fork-$filetimestamp.out" fi done ================================================ FILE: evaluate_10K.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # set -eo pipefail if [ -z "$1" ] then echo "Usage: evaluate.sh ( ...)" echo " for each fork, there must be a 'calculate_average_.sh' script and an optional 'prepare_.sh'." exit 1 fi BOLD_WHITE='\033[1;37m' CYAN='\033[0;36m' GREEN='\033[0;32m' PURPLE='\033[0;35m' BOLD_RED='\033[1;31m' RED='\033[0;31m' BOLD_YELLOW='\033[1;33m' RESET='\033[0m' # No Color MEASUREMENTS_FILE="measurements_10K_1B.txt" RUNS=5 DEFAULT_JAVA_VERSION="21.0.1-open" : "${BUILD_JAVA_VERSION:=21.0.1-open}" RUN_TIME_LIMIT=300 # seconds TIMEOUT="" if [ "$(uname -s)" == "Linux" ]; then TIMEOUT="timeout -v $RUN_TIME_LIMIT" else # MacOs if [ -x "$(command -v gtimeout)" ]; then TIMEOUT="gtimeout -v $RUN_TIME_LIMIT" # from `brew install coreutils` else echo -e "${BOLD_YELLOW}WARNING${RESET} gtimeout not available, benchmark runs may take indefinitely long." fi fi function check_command_installed { if ! [ -x "$(command -v $1)" ]; then echo "Error: $1 is not installed." >&2 exit 1 fi } function print_and_execute() { echo "+ $@" >&2 "$@" } check_command_installed java check_command_installed hyperfine check_command_installed jq check_command_installed bc # Validate that ./calculate_average_.sh exists for each fork for fork in "$@"; do if [ ! -f "./calculate_average_$fork.sh" ]; then echo -e "${BOLD_RED}ERROR${RESET}: ./calculate_average_$fork.sh does not exist." >&2 exit 1 fi done ## SDKMAN Setup # 1. Custom check for sdkman installed; not sure why check_command_installed doesn't detect it properly if [ ! -f "$HOME/.sdkman/bin/sdkman-init.sh" ]; then echo -e "${BOLD_RED}ERROR${RESET}: sdkman is not installed." >&2 exit 1 fi # 2. Init sdkman in this script source "$HOME/.sdkman/bin/sdkman-init.sh" # 3. make sure the default java version is installed if [ ! -d "$HOME/.sdkman/candidates/java/$DEFAULT_JAVA_VERSION" ]; then print_and_execute sdk install java $DEFAULT_JAVA_VERSION fi # 4. Install missing SDK java versions in any of the prepare_*.sh scripts for the provided forks for fork in "$@"; do if [ -f "./prepare_$fork.sh" ]; then grep -h "^sdk use" "./prepare_$fork.sh" | cut -d' ' -f4 | while read -r version; do if [ ! -d "$HOME/.sdkman/candidates/java/$version" ]; then print_and_execute sdk install java $version fi done || true # grep returns exit code 1 when no match, `|| true` prevents the script from exiting early fi done ## END - SDKMAN Setup # Check if SMT is enabled (we want it disabled) if [ -f "/sys/devices/system/cpu/smt/active" ]; then if [ "$(cat /sys/devices/system/cpu/smt/active)" != "0" ]; then echo -e "${BOLD_YELLOW}WARNING${RESET} SMT is enabled" fi fi # Check if Turbo Boost is enabled (we want it disabled) if [ -f "/sys/devices/system/cpu/cpufreq/boost" ]; then if [ "$(cat /sys/devices/system/cpu/cpufreq/boost)" != "0" ]; then echo -e "${BOLD_YELLOW}WARNING${RESET} Turbo Boost is enabled" fi fi print_and_execute sdk use java $BUILD_JAVA_VERSION print_and_execute java --version # print_and_execute ./mvnw --quiet clean verify print_and_execute rm -f measurements.txt print_and_execute ln -s $MEASUREMENTS_FILE measurements.txt echo "" # check if measurements_xxx.out exists if [ ! -f "${MEASUREMENTS_FILE%.txt}.out" ]; then echo -e "${BOLD_RED}ERROR${RESET}: ${MEASUREMENTS_FILE%.txt}.out does not exist." >&2 echo "Please create it with:" echo "" echo " ./calculate_average_baseline.sh > ${MEASUREMENTS_FILE%.txt}.out" echo "" exit 1 fi # Run tests and benchmark for each fork filetimestamp=$(date +"%Y%m%d%H%M%S") # same for all fork.out files from this run failed=() for fork in "$@"; do set +e # we don't want prepare.sh, test.sh or hyperfine failing on 1 fork to exit the script early # Run prepare script if [ -f "./prepare_$fork.sh" ]; then print_and_execute source "./prepare_$fork.sh" else print_and_execute sdk use java $DEFAULT_JAVA_VERSION fi # Run the test suite print_and_execute $TIMEOUT ./test.sh $fork if [ $? -ne 0 ]; then failed+=("$fork") echo "" echo -e "${BOLD_RED}FAILURE${RESET}: ./test.sh $fork failed" continue fi echo "" # Run the test on $MEASUREMENTS_FILE; this serves as the warmup print_and_execute $TIMEOUT ./test.sh $fork $MEASUREMENTS_FILE if [ $? -ne 0 ]; then failed+=("$fork") echo "" echo -e "${BOLD_RED}FAILURE${RESET}: ./test.sh $fork $MEASUREMENTS_FILE failed" continue fi echo "" # re-link measurements.txt since test.sh deleted it print_and_execute rm -f measurements.txt print_and_execute ln -s $MEASUREMENTS_FILE measurements.txt # Use hyperfine to run the benchmark for each fork HYPERFINE_OPTS="--warmup 0 --runs $RUNS --export-json $fork-$filetimestamp-timing.json --output ./$fork-$filetimestamp.out" # check if this script is running on a Linux box if [ "$(uname -s)" == "Linux" ]; then check_command_installed numactl # Linux platform # prepend this with numactl --physcpubind=0-7 for running it only with 8 cores numactl --physcpubind=0-7 hyperfine $HYPERFINE_OPTS "$TIMEOUT ./calculate_average_$fork.sh 2>&1" else # MacOS hyperfine $HYPERFINE_OPTS "$TIMEOUT ./calculate_average_$fork.sh 2>&1" fi # Catch hyperfine command failed if [ $? -ne 0 ]; then failed+=("$fork") # Hyperfine already prints the error message echo "" continue fi done set -e # Summary echo -e "${BOLD_WHITE}Summary${RESET}" for fork in "$@"; do # skip reporting results for failed forks if [[ " ${failed[@]} " =~ " ${fork} " ]]; then echo -e " ${RED}$fork${RESET}: command failed or output did not match" continue fi # Trimmed mean = The slowest and the fastest runs are discarded, the # mean value of the remaining three runs is the result for that contender trimmed_mean=$(jq -r '.results[0].times | sort_by(.|tonumber) | .[1:-1] | add / length' $fork-$filetimestamp-timing.json) raw_times=$(jq -r '.results[0].times | join(",")' $fork-$filetimestamp-timing.json) if [ "$fork" == "$1" ]; then color=$CYAN elif [ "$fork" == "$2" ]; then color=$GREEN else color=$PURPLE fi echo -e " ${color}$fork${RESET}: trimmed mean ${BOLD_WHITE}$trimmed_mean${RESET}, raw times ${BOLD_WHITE}$raw_times${RESET}" done echo "" ## Leaderboard - prints the leaderboard in Markdown table format echo -e "${BOLD_WHITE}Leaderboard${RESET}" # 1. Create a temp file to store the leaderboard entries leaderboard_temp_file=$(mktemp) # 2. Process each fork and append the 1-line entry to the temp file for fork in "$@"; do # skip reporting results for failed forks if [[ " ${failed[@]} " =~ " ${fork} " ]]; then continue fi trimmed_mean=$(jq -r '.results[0].times | sort_by(.|tonumber) | .[1:-1] | add / length' $fork-$filetimestamp-timing.json) # trimmed_mean is in seconds # Format trimmed_mean as MM::SS.mmm # using bc trimmed_mean_minutes=$(echo "$trimmed_mean / 60" | bc) trimmed_mean_seconds=$(echo "$trimmed_mean % 60 / 1" | bc) trimmed_mean_ms=$(echo "($trimmed_mean - $trimmed_mean_minutes * 60 - $trimmed_mean_seconds) * 1000 / 1" | bc) trimmed_mean_formatted=$(printf "%02d:%02d.%03d" $trimmed_mean_minutes $trimmed_mean_seconds $trimmed_mean_ms) # Get Github user's name from public Github API (rate limited after ~50 calls, so results are cached in github_users.txt) set +e github_user__name=$(grep "^$fork;" github_users.txt | cut -d ';' -f2) if [ -z "$github_user__name" ]; then github_user__name=$(curl -s https://api.github.com/users/$fork | jq -r '.name' | tr -d '"') if [ "$github_user__name" != "null" ]; then echo "$fork;$github_user__name" >> github_users.txt else github_user__name=$fork fi fi set -e # Read java version from prepare_$fork.sh if it exists, otherwise assume 21.0.1-open java_version="21.0.1-open" # Hard-coding the note message for now notes="" if [ -f "./prepare_$fork.sh" ]; then java_version=$(grep -F "sdk use java" ./prepare_$fork.sh | cut -d' ' -f4) if grep -F "native-image" -q ./prepare_$fork.sh ; then notes="GraalVM native binary" fi fi # check if Java source file uses Unsafe if grep -F "theUnsafe" -q ./src/main/java*/dev/morling/onebrc/CalculateAverage_$fork.java ; then # if notes is not empty, append a comma and space before the unsafe note notes="${notes:+$notes, }uses Unsafe" fi echo -n "$trimmed_mean;" >> $leaderboard_temp_file # for sorting echo -n "| # " >> $leaderboard_temp_file echo -n "| $trimmed_mean_formatted " >> $leaderboard_temp_file echo -n "| [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_$fork.java)" >> $leaderboard_temp_file echo -n "| $java_version " >> $leaderboard_temp_file echo -n "| [$github_user__name](https://github.com/$fork) " >> $leaderboard_temp_file echo -n "| $notes " >> $leaderboard_temp_file echo "|" >> $leaderboard_temp_file done # 3. Sort leaderboard_temp_file by trimmed_mean and remove the sorting column sort -n $leaderboard_temp_file | cut -d ';' -f 2 > $leaderboard_temp_file.sorted # 4. Print the leaderboard echo "" echo "| # | Result (m:s.ms) | Implementation | JDK | Submitter | Notes |" echo "|---|-----------------|--------------------|-----|---------------|-----------|" # If $leaderboard_temp_file.sorted has more than 3 entires, include rankings if [ $(wc -l < $leaderboard_temp_file.sorted) -gt 3 ]; then head -n 1 $leaderboard_temp_file.sorted | tr '#' 1 head -n 2 $leaderboard_temp_file.sorted | tail -n 1 | tr '#' 2 head -n 3 $leaderboard_temp_file.sorted | tail -n 1 | tr '#' 3 tail -n+4 $leaderboard_temp_file.sorted | tr '#' ' ' else # Don't show rankings cat $leaderboard_temp_file.sorted | tr '#' ' ' fi echo "" # 5. Cleanup rm $leaderboard_temp_file ## END - Leaderboard # Finalize .out files echo "Raw results saved to file(s):" for fork in "$@"; do if [ -f "$fork-$filetimestamp-timing.json" ]; then cat $fork-$filetimestamp-timing.json >> $fork-$filetimestamp.out rm $fork-$filetimestamp-timing.json fi if [ -f "$fork-$filetimestamp.out" ]; then echo " $fork-$filetimestamp.out" fi done ================================================ FILE: github_users.txt ================================================ giovannicuccu;Giovanni Cuccu Ujjwalbharti;Ujjwal Bharti abfrmblr;Abhilash ags313;ags anandmattikopp;twohardthings armandino;Arman Sharif artpar;Parth Mudgal asun;Alan Sun bjhara;Hampus coolmineman;Cool_Mineman criccomini;Chris Riccomini davecom;David Kopec davery22;Daniel Avery ddimtirov;Dimitar Dimitrov ebarlas;Elliot Barlas entangled90;Carlo fatroom;Roman Romanchuk felix19350;Bruno Félix filiphr;Filip Hrisafov flippingbits;Stefan Sprenger fragmede;Samson gabrielreid;Gabriel Reid hchiorean;Horia Chiorean imrafaelmerino;Rafael Merino García isolgpus;Jamie Stansfield iziamos;John Ziamos jgrateron;Jairo Graterón jotschi;Johannes Schüth kevinmcmurtrie;Kevin McMurtrie kgeri;Gergely Kiss khmarbaise;Karl Heinz Marbaise kuduwa-keshavram;Keshavram Kuduwa merykitty;Quan Anh Mai moysesb;Moysés Borges Furtado mudit-saxena;Mudit Saxena nstng;Nils Semmelrock obourgain;Olivier Bourgain padreati;Aurelian Tutuianu palmr;Nick Palmer rby;Ramzi Ben Yahya richardstartin;Richard Startin spullara;Sam Pullara royvanrijn;Roy van Rijn seijikun;Markus Ebner semotpan;Serghei Motpan thomaswue;Thomas Wuerthinger truelive;Roman Schweitzer twobiers;Tobi yavuztas;Yavuz Tas yehwankim23;김예환 Ye-Hwan Kim (Sam) hundredwatt;Jason Nochlin gnmathur;Gaurav Mathur vemana;Subrahmanyam jincongho;Jin Cong Ho yonatang;Yonatan Graber adriacabeza;Adrià Cabeza AlexanderYastrebov;Alexander Yastrebov elh;Eugene Huang ================================================ FILE: mvnw ================================================ #!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you 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. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Apache Maven Wrapper startup batch script, version 3.2.0 # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /usr/local/etc/mavenrc ] ; then . /usr/local/etc/mavenrc fi if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "$(uname)" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME else JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=$(java-config --jre-home) fi fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=$(cygpath --unix "$JAVA_HOME") [ -n "$CLASSPATH" ] && CLASSPATH=$(cygpath --path --unix "$CLASSPATH") fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" fi if [ -z "$JAVA_HOME" ]; then javaExecutable="$(which javac)" if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=$(which readlink) if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then if $darwin ; then javaHome="$(dirname "\"$javaExecutable\"")" javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" else javaExecutable="$(readlink -f "\"$javaExecutable\"")" fi javaHome="$(dirname "\"$javaExecutable\"")" javaHome=$(expr "$javaHome" : '\(.*\)/bin') JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=$(cd "$wdir/.." || exit 1; pwd) fi # end of workaround done printf '%s' "$(cd "$basedir" || exit 1; pwd)" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then # Remove \r in case we run on Windows within Git Bash # and check out the repository with auto CRLF management # enabled. Otherwise, we may read lines that are delimited with # \r\n and produce $'-Xarg\r' rather than -Xarg due to word # splitting rules. tr -s '\r\n' ' ' < "$1" fi } log() { if [ "$MVNW_VERBOSE" = true ]; then printf '%s\n' "$1" fi } BASE_DIR=$(find_maven_basedir "$(dirname "$0")") if [ -z "$BASE_DIR" ]; then exit 1; fi MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR log "$MAVEN_PROJECTBASEDIR" ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" if [ -r "$wrapperJarPath" ]; then log "Found $wrapperJarPath" else log "Couldn't find $wrapperJarPath, downloading it ..." if [ -n "$MVNW_REPOURL" ]; then wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" else wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" fi while IFS="=" read -r key value; do # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) safeValue=$(echo "$value" | tr -d '\r') case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; esac done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" log "Downloading from: $wrapperUrl" if $cygwin; then wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") fi if command -v wget > /dev/null; then log "Found wget ... using wget" [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" else wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" fi elif command -v curl > /dev/null; then log "Found curl ... using curl" [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" else curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" fi else log "Falling back to using Java to download" javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then javaSource=$(cygpath --path --windows "$javaSource") javaClass=$(cygpath --path --windows "$javaClass") fi if [ -e "$javaSource" ]; then if [ ! -e "$javaClass" ]; then log " - Compiling MavenWrapperDownloader.java ..." ("$JAVA_HOME/bin/javac" "$javaSource") fi if [ -e "$javaClass" ]; then log " - Running MavenWrapperDownloader.java ..." ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" fi fi fi fi ########################################################################################## # End of extension ########################################################################################## # If specified, validate the SHA-256 sum of the Maven wrapper jar file wrapperSha256Sum="" while IFS="=" read -r key value; do case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; esac done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" if [ -n "$wrapperSha256Sum" ]; then wrapperSha256Result=false if command -v sha256sum > /dev/null; then if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then wrapperSha256Result=true fi elif command -v shasum > /dev/null; then if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then wrapperSha256Result=true fi else echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." exit 1 fi if [ $wrapperSha256Result = false ]; then echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 exit 1 fi fi MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$JAVA_HOME" ] && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") [ -n "$CLASSPATH" ] && CLASSPATH=$(cygpath --path --windows "$CLASSPATH") [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") fi # Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain # shellcheck disable=SC2086 # safe args exec "$JAVACMD" \ $MAVEN_OPTS \ $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" ================================================ FILE: mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Apache Maven Wrapper startup batch script, version 3.2.0 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM set title of command window title %0 @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @REM This allows using the maven wrapper in projects that prohibit checking in binary data. if exist %WRAPPER_JAR% ( if "%MVNW_VERBOSE%" == "true" ( echo Found %WRAPPER_JAR% ) ) else ( if not "%MVNW_REPOURL%" == "" ( SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... echo Downloading from: %WRAPPER_URL% ) powershell -Command "&{"^ "$webclient = new-object System.Net.WebClient;"^ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ "}"^ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ "}" if "%MVNW_VERBOSE%" == "true" ( echo Finished downloading %WRAPPER_JAR% ) ) @REM End of extension @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file SET WRAPPER_SHA_256_SUM="" FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B ) IF NOT %WRAPPER_SHA_256_SUM%=="" ( powershell -Command "&{"^ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ " exit 1;"^ "}"^ "}" if ERRORLEVEL 1 goto error ) @REM Provide a "standardized" way to retrieve the CLI args that will @REM work with both Windows and non-Windows executions. set MAVEN_CMD_LINE_ARGS=%* %MAVEN_JAVA_EXE% ^ %JVM_CONFIG_MAVEN_PROPS% ^ %MAVEN_OPTS% ^ %MAVEN_DEBUG_OPTS% ^ -classpath %WRAPPER_JAR% ^ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%"=="on" pause if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% cmd /C exit /B %ERROR_CODE% ================================================ FILE: pom.xml ================================================ 4.0.0 dev.morling.demos average 1.0.0-SNAPSHOT true 21 UTF-8 UTF-8 My OSS Project My Latest OSS Project 2021 tbd. Apache-2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo org.junit junit-bom 5.8.1 pom import org.junit.jupiter junit-jupiter test org.assertj assertj-core 3.21.0 test com.mycila license-maven-plugin 4.1 net.revelc.code.formatter formatter-maven-plugin 2.16.0 etc/eclipse-formatter-config.xml ${project.build.sourceDirectory} ${project.basedir}/src/main/java-22 net.revelc.code impsort-maven-plugin 1.9.0 java.,javax.,org.,com. true true org.apache.maven.plugins maven-compiler-plugin 3.12.1 true --enable-preview --add-modules java.base,jdk.incubator.vector --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/jdk.internal.util=ALL-UNNAMED org.apache.maven.plugins maven-clean-plugin 3.1.0 org.apache.maven.plugins maven-deploy-plugin 3.1.1 org.apache.maven.plugins maven-enforcer-plugin 3.3.0 org.apache.maven.plugins maven-install-plugin 3.1.1 org.apache.maven.plugins maven-jar-plugin 3.2.0 org.apache.maven.plugins maven-resources-plugin 3.2.0 org.apache.maven.plugins maven-site-plugin 3.12.1 org.apache.maven.plugins maven-surefire-plugin 3.2.3 org.apache.maven.plugins maven-wrapper-plugin 3.2.0 ci net.revelc.code.formatter formatter-maven-plugin validate-format validate validate qa !quick com.mycila license-maven-plugin
etc/license.txt
true true LICENSE.txt **/.dontdelete **/measurements*.txt **/measurements*.out out_expected.txt github_users.txt src/main/java/dev/morling/onebrc/CalculateAverage_cliffclick.java .sdkmanrc
check
net.revelc.code.formatter formatter-maven-plugin format format process-sources org.apache.maven.plugins maven-enforcer-plugin enforce-plugin-versions enforce ${maven.compiler.release} true true true clean,deploy,site
quick quick true jdk22 22 maven-compiler-plugin 22 ${project.basedir}/src/main/java-22
================================================ FILE: prepare_3j5a.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_AlexanderYastrebov.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # DOCKER_BUILDKIT=1 docker build -o target/AlexanderYastrebov src/main/go/AlexanderYastrebov ================================================ FILE: prepare_C5H12O5.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_EduardoSaverin.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_JaimePolidura.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_JaimePolidura_image ]; then OPTS="--gc=epsilon -O3 --enable-preview --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_JaimePolidura" native-image $OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_JaimePolidura_image dev.morling.onebrc.CalculateAverage_JaimePolidura fi ================================================ FILE: prepare_JamalMulla.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_JamalMulla_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -march=native --enable-preview --strict-image-heap --link-at-build-time -R:MaxHeapSize=64m -da -dsa --no-fallback --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_JamalMulla" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_JamalMulla_image dev.morling.onebrc.CalculateAverage_JamalMulla fi ================================================ FILE: prepare_Judekeyser.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_Kidlike.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # if [ ! -e target/image_calculateaverage_Kidlike ]; then source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal #NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -march=native --enable-preview" NATIVE_IMAGE_OPTS="-O3 -march=native --enable-preview" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/image_calculateaverage_Kidlike dev.morling.onebrc.CalculateAverage_Kidlike fi ================================================ FILE: prepare_MeanderingProgrammer.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_PanagiotisDrakatos.sh ================================================ # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 if [ ! -f target/CalculateAverage_PanagiotisDrakatos_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -R:MaxHeapSize=10536m --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_PanagiotisDrakatos" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_PanagiotisDrakatos_image dev.morling.onebrc.CalculateAverage_PanagiotisDrakatos fi ================================================ FILE: prepare_PawelAdamski.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_SamuelYvon.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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 IS A DIRECT COPY OF royvanrijn's PREPARE SCRIPT; I AM NOT FAMILIAR WITH AOT STUFF ON JAVA. # THANKS royvanrijn!! source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_SamuelYvon_image ]; then JAVA_OPTS="--enable-preview -dsa" # No vector API because it does not link :( # JAVA_OPTS="--enable-preview -dsa --add-modules jdk.incubator.vector" # Enable the GC because I need memory :D NATIVE_IMAGE_OPTS="--gc=G1 -O3 -march=native --strict-image-heap $JAVA_OPTS" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_SamuelYvon_image dev.morling.onebrc.CalculateAverage_SamuelYvon fi ================================================ FILE: prepare_Smoofie.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_YannMoisan.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_abeobk.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_abeobk_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -march=native -H:InlineAllBonus=10 -H:-GenLoopSafepoints --enable-preview --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_abeobk" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_abeobk_image dev.morling.onebrc.CalculateAverage_abeobk fi ================================================ FILE: prepare_adriacabeza.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_agoncal.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-tem 1>&2 ================================================ FILE: prepare_ags313.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_anitasv.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_armandino.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_armandino_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -march=native --enable-preview -H:InlineAllBonus=10 -H:-ParseRuntimeOptions --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_armandino\$Scanner" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_armandino_image dev.morling.onebrc.CalculateAverage_armandino fi ================================================ FILE: prepare_artsiomkorzun.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_artsiomkorzun_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -march=native -H:TuneInlinerExploration=1 -R:MaxHeapSize=64m -H:-GenLoopSafepoints --enable-preview --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_artsiomkorzun" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_artsiomkorzun_image dev.morling.onebrc.CalculateAverage_artsiomkorzun fi ================================================ FILE: prepare_baseline.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_breejesh.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_cb0s.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_charlibot.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_chrisbellew.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_coolmineman.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_davecom.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_ddimtirov.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # also tried: 23.ea.3-open, 21.0.1-graalce, 21.0.1-graal, 21.0.1.crac-librca (tried CRaC API to see if it preserves JIT state) source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-tem ================================================ FILE: prepare_dpsoft.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 ================================================ FILE: prepare_dqhieuu.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_ebarlas.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_ebarlas_image ]; then NATIVE_IMAGE_OPTS="-H:+UnlockExperimentalVMOptions --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_ebarlas --gc=epsilon -O3 -march=native -R:MaxHeapSize=128m -H:-GenLoopSafepoints --enable-preview" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_ebarlas_image dev.morling.onebrc.CalculateAverage_ebarlas fi ================================================ FILE: prepare_elh.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # DOCKER_BUILDKIT=1 docker build -o target/elh src/main/go/elh ================================================ FILE: prepare_eriklumme.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_filiphr.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_flippingbits.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_gabrielfoo.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal > /dev/null 2>&1 ================================================ FILE: prepare_gnabyl.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_godofwharf.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-tem 1>&2 ================================================ FILE: prepare_hundredwatt.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal ================================================ FILE: prepare_imrafaelmerino.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_iziamos.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_iziamos_image ]; then NATIVE_IMAGE_OPTS="-H:+UnlockExperimentalVMOptions --gc=epsilon -O3 -march=native -R:MaxHeapSize=64m -H:-GenLoopSafepoints --enable-preview -H:InlineAllBonus=10 -H:-ParseRuntimeOptions --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_iziamos" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_iziamos_image dev.morling.onebrc.CalculateAverage_iziamos fi ================================================ FILE: prepare_jatingala.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_jbachorik.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_jerrinot.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_jerrinot_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -march=native --enable-preview -H:-GenLoopSafepoints -H:InlineAllBonus=10 --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_jerrinot" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_jerrinot_image dev.morling.onebrc.CalculateAverage_jerrinot fi ================================================ FILE: prepare_jonathan-aotearoa.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_jonathan-aotearoa_image ]; then # Enable preview features and disable system assertions. JAVA_OPTS="--enable-preview -dsa" # Use the no-op GC. # Enable CPU features (-march=native) and level-3 optimisations (-O3) NATIVE_IMAGE_OPTS="--initialize-at-build-time=dev.morling.onebrc.CalculateAverage_jonathanaotearoa --gc=epsilon -O3 -march=native --strict-image-heap $JAVA_OPTS" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_jonathan-aotearoa_image dev.morling.onebrc.CalculateAverage_jonathanaotearoa fi ================================================ FILE: prepare_justplainlaake.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 if [ ! -f target/CalculateAverage_justplainlaake_image ]; then #disable assertions #optimize code for best performance #native march gives best performance for machine image is built on #strict image heap allows all classes ot be used at build time #native image info prints the trace of the build #enable preview allows for preview features of current release #epsilon garbage collector is a gc that doesn't gc... haha native-image -dsa -O3 -march=native --strict-image-heap --native-image-info --enable-preview --gc=epsilon -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_justplainlaake_image dev.morling.onebrc.CalculateAverage_justplainlaake fi ================================================ FILE: prepare_kuduwa-keshavram.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_linl33.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 22.ea.32-open 1>&2 CLASS_NAME="CalculateAverage_linl33" JAVA_OPTS="-Xrs --enable-preview --add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED" JAVA_OPTS="${JAVA_OPTS} -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions" JAVA_OPTS="${JAVA_OPTS} -Xms128m -XX:+AlwaysPreTouch -XX:+AlwaysPreTouchStacks -XX:-UseTransparentHugePages" JAVA_OPTS="${JAVA_OPTS} -XX:-UseCompressedClassPointers -XX:+ForceUnreachable -XX:-CompactStrings" JAVA_OPTS="${JAVA_OPTS} -XX:CodeEntryAlignment=64 -XX:OptoLoopAlignment=64 -XX:MaxLoopPad=16 -XX:ObjectAlignmentInBytes=64" JAVA_OPTS="${JAVA_OPTS} -XX:-UseLoopPredicate -XX:LoopStripMiningIter=0 -XX:LoopStripMiningIterShortLoop=0" JAVA_OPTS="${JAVA_OPTS} -XX:-UseCountedLoopSafepoints -XX:GuaranteedSafepointInterval=0 -XX:AllocatePrefetchStyle=0" JAVA_OPTS="${JAVA_OPTS} -XX:+TrustFinalNonStaticFields -XX:LockingMode=2 -XX:+UseSystemMemoryBarrier" JAVA_OPTS="${JAVA_OPTS} -XX:-UseDynamicNumberOfCompilerThreads -XX:-UseDynamicNumberOfGCThreads" JAVA_OPTS="${JAVA_OPTS} -XX:ArchiveRelocationMode=0 -XX:-UsePerfData -XX:-UseNotificationThread -XX:-CheckIntrinsics" #JAVA_OPTS="${JAVA_OPTS} -XX:+UseZGC -XX:-ZProactive -XX:+ZCollectionIntervalOnly -XX:ZCollectionInterval=0 -XX:-ZUncommit -XX:-ZBufferStoreBarriers -XX:ZIndexDistributorStrategy=1" JAVA_OPTS="${JAVA_OPTS} -XX:+UseEpsilonGC -XX:-UseCompressedOops" #JAVA_OPTS="${JAVA_OPTS} -XX:+UseParallelGC -XX:-UseCompressedOops" #JAVA_OPTS="${JAVA_OPTS} -XX:+UseG1GC -XX:-UseCompressedOops" JAVA_OPTS="${JAVA_OPTS} -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0 -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.MethodHandle.DONT_INLINE_THRESHOLD=-1" JAVA_OPTS="${JAVA_OPTS} -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8" JAVA_OPTS="${JAVA_OPTS} -Ddev.morling.onebrc.CalculateAverage_linl33.measurementsPath=src/test/resources/samples/measurements-10000-unique-keys.txt" # create CDS archive java ${JAVA_OPTS} -Xshare:off -XX:DumpLoadedClassList=target/${CLASS_NAME}.classlist --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.${CLASS_NAME} java ${JAVA_OPTS} -Xshare:dump -XX:SharedClassListFile=target/${CLASS_NAME}.classlist -XX:SharedArchiveFile=target/${CLASS_NAME}.jsa --class-path target/average-1.0.0-SNAPSHOT.jar java ${JAVA_OPTS} -Xshare:on -XX:SharedArchiveFile=target/${CLASS_NAME}.jsa -XX:ArchiveClassesAtExit=target/${CLASS_NAME}_dynamic.jsa --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.${CLASS_NAME} ================================================ FILE: prepare_mahadev-k.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_manishgarg90.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_martin2038.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 ## #if [ ! -f target/CalculateAverage_martin2038 ]; then # MAIN=dev.morling.onebrc.CalculateAverage_martin2038 # NATIVE_IMAGE_OPTS="-H:+UnlockExperimentalVMOptions --initialize-at-build-time=$MAIN --gc=epsilon -O3 -march=native -R:MaxHeapSize=515m -H:-GenLoopSafepoints -H:InlineAllBonus=10 -H:-ParseRuntimeOptions" # native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_martin2038_image $MAIN #fi ================================================ FILE: prepare_maximz101.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 #sdk use java 21.0.1-amzn 1>&2 ================================================ FILE: prepare_melgenek.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-open 1>&2 ================================================ FILE: prepare_mtopolnik.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_mtopolnik_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -H:+UnlockExperimentalVMOptions -H:-GenLoopSafepoints -march=native --enable-preview -H:InlineAllBonus=10 -H:-ParseRuntimeOptions --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_mtopolnik" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_mtopolnik_image dev.morling.onebrc.CalculateAverage_mtopolnik fi ================================================ FILE: prepare_phd3.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_plevart.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-tem 1>&2 ================================================ FILE: prepare_rcasteltrione.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_ricardopieper.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_roman-r-m.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_roman_r_m_image ]; then JAVA_OPTS="--enable-preview -dsa" NATIVE_IMAGE_OPTS="--initialize-at-build-time=dev.morling.onebrc.CalculateAverage_roman_r_m --gc=epsilon -Ob -O3 -march=native --strict-image-heap $JAVA_OPTS" NATIVE_IMAGE_OPTS="$NATIVE_IMAGE_OPTS -R:MaxHeapSize=128m" NATIVE_IMAGE_OPTS="$NATIVE_IMAGE_OPTS -H:+UnlockExperimentalVMOptions -H:-GenLoopSafepoints -H:InlineAllBonus=10 -H:-ParseRuntimeOptions" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_roman_r_m_image dev.morling.onebrc.CalculateAverage_roman_r_m fi ================================================ FILE: prepare_royvanrijn.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_royvanrijn_image ]; then JAVA_OPTS="--enable-preview" NATIVE_IMAGE_OPTS="-H:+UnlockExperimentalVMOptions --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_royvanrijn --gc=epsilon -O3 -march=native -R:MaxHeapSize=515m -H:-GenLoopSafepoints -H:InlineAllBonus=10 -H:-ParseRuntimeOptions $JAVA_OPTS" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_royvanrijn_image dev.morling.onebrc.CalculateAverage_royvanrijn fi ================================================ FILE: prepare_seijikun.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_serkan-ozal.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-open 1>&2 JAVA_OPTS="--enable-preview --enable-native-access=ALL-UNNAMED --add-modules=jdk.incubator.vector " JAVA_OPTS="$JAVA_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions" JAVA_OPTS="$JAVA_OPTS -XX:-TieredCompilation -XX:MaxInlineSize=10000 -XX:InlineSmallCode=10000 -XX:FreqInlineSize=10000" JAVA_OPTS="$JAVA_OPTS -XX:-UseCountedLoopSafepoints -XX:GuaranteedSafepointInterval=0" JAVA_OPTS="$JAVA_OPTS -XX:+TrustFinalNonStaticFields -da -dsa -XX:+UseNUMA -XX:-EnableJVMCI" JAVA_OPTS="$JAVA_OPTS -Djdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK=0" JAVA_OPTS="${JAVA_OPTS} -Dfile.path=src/test/resources/samples/measurements-10000-unique-keys.txt" if [[ ! "$(uname -s)" = "Darwin" ]]; then JAVA_OPTS="$JAVA_OPTS -XX:+UseTransparentHugePages" fi # Set configs export USE_SHARED_ARENA=true export USE_SHARED_REGION=true export CLOSE_STDOUT_ON_RESULT=true CLASS_NAME="CalculateAverage_serkan_ozal" # Create CDS archive java ${JAVA_OPTS} -Xshare:off -XX:DumpLoadedClassList=target/${CLASS_NAME}.classlist --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.${CLASS_NAME} java ${JAVA_OPTS} -Xshare:dump -XX:SharedClassListFile=target/${CLASS_NAME}.classlist -XX:SharedArchiveFile=target/${CLASS_NAME}.jsa --class-path target/average-1.0.0-SNAPSHOT.jar java ${JAVA_OPTS} -Xshare:on -XX:SharedArchiveFile=target/${CLASS_NAME}.jsa -XX:ArchiveClassesAtExit=target/${CLASS_NAME}_cds.jsa --class-path target/average-1.0.0-SNAPSHOT.jar dev.morling.onebrc.${CLASS_NAME} ================================================ FILE: prepare_slovdahl.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-tem 1>&2 > /dev/null ./mvnw verify ================================================ FILE: prepare_spullara.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_stephenvonworley.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_stephenvonworley_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -H:TuneInlinerExploration=1 -march=native --enable-preview --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_stephenvonworley" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_stephenvonworley_image dev.morling.onebrc.CalculateAverage_stephenvonworley fi ================================================ FILE: prepare_sudhirtumati.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-open 1>&2 ================================================ FILE: prepare_thanhtrinity.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_thomaswue.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_thomaswue_image ]; then # Performance tuning flags, optimization level 3, maximum inlining exploration, and compile for the architecture where the native image is generated. NATIVE_IMAGE_OPTS="-O3 -H:TuneInlinerExploration=1 -march=native" # Need to enable preview for accessing the raw address of the foreign memory access API. # Initializing the Scanner to make sure the unsafe access object is known as a non-null compile time constant. NATIVE_IMAGE_OPTS="$NATIVE_IMAGE_OPTS --enable-preview --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_thomaswue\$Scanner" # There is no need for garbage collection and therefore also no safepoints required. NATIVE_IMAGE_OPTS="$NATIVE_IMAGE_OPTS --gc=epsilon -H:-GenLoopSafepoints" # Uncomment the following line for outputting the compiler graph to the IdealGraphVisualizer # NATIVE_IMAGE_OPTS="$NATIVE_IMAGE_OPTS -H:MethodFilter=CalculateAverage_thomaswue.* -H:Dump=:2 -H:PrintGraph=Network" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_thomaswue_image dev.morling.onebrc.CalculateAverage_thomaswue fi ================================================ FILE: prepare_tivrfoa.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 # ./mvnw clean verify removes target/ and will re-trigger native image creation. if [ ! -f target/CalculateAverage_tivrfoa_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -H:-GenLoopSafepoints -march=native --enable-preview -H:InlineAllBonus=10 -H:-ParseRuntimeOptions --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_tivrfoa\$Scanner" # Use -H:MethodFilter=CalculateAverage_tivrfoa.* -H:Dump=:2 -H:PrintGraph=Network for IdealGraphVisualizer graph dumping. native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_tivrfoa_image dev.morling.onebrc.CalculateAverage_tivrfoa fi ================================================ FILE: prepare_tonivade.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-tem 1>&2 ================================================ FILE: prepare_truelive.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graalce 1>&2 ================================================ FILE: prepare_twobiers.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-tem 1>&2 ================================================ FILE: prepare_vaidhy.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_vemana.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" #sdk1 use java 21.0.1-open 1>&2 sdk use java 21.0.1-graal 1>&2 #sdk1 use java 21.0.1-zulu 1>&2 #sdk1 use java 21.0.1-graalce 1>&2 ================================================ FILE: prepare_vemanaNonIdiomatic.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_yavuztas.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_yavuztas_image ]; then NATIVE_IMAGE_OPTS="--initialize-at-build-time=dev.morling.onebrc.CalculateAverage_yavuztas --gc=epsilon -O3 -march=native -R:MaxHeapSize=128m -H:-GenLoopSafepoints --enable-preview" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_yavuztas_image dev.morling.onebrc.CalculateAverage_yavuztas fi ================================================ FILE: prepare_yonatang.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # # Uncomment below to use sdk # source "$HOME/.sdkman/bin/sdkman-init.sh" # sdk use java 21.0.1-graal 1>&2 ================================================ FILE: prepare_zerninv.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # source "$HOME/.sdkman/bin/sdkman-init.sh" sdk use java 21.0.2-graal 1>&2 if [ ! -f target/CalculateAverage_zerninv_image ]; then NATIVE_IMAGE_OPTS="--gc=epsilon -O3 -march=native -R:MaxHeapSize=512m -H:-GenLoopSafepoints --enable-preview --initialize-at-build-time=dev.morling.onebrc.CalculateAverage_zerninv" native-image $NATIVE_IMAGE_OPTS -cp target/average-1.0.0-SNAPSHOT.jar -o target/CalculateAverage_zerninv_image dev.morling.onebrc.CalculateAverage_zerninv fi ================================================ FILE: process.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # if [ -z "$1" ] then echo "Usage: process_output.sh " exit 1 fi java --enable-preview --source=21 process_output.java out_expected.txt $1.out ================================================ FILE: process_output.java ================================================ /* * Copyright 2023 The original authors * * 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. */ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import java.time.Duration; public class process_output { public static void main(String... args) throws Exception { String expectedFile = args[0]; String actualFile = args[1]; String expected = new String(Files.readAllBytes(Paths.get(expectedFile))); List times = new ArrayList<>(); var outputLines = Files.lines(Paths.get(actualFile)) .collect(Collectors.toList()); int matched = 0; for (String line : outputLines) { if (line.contains("Hamburg")) { if (!line.equals(expected)) { System.err.println("FAILURE Unexpected output"); System.err.println(line); } else { matched++; } } else if (line.startsWith("real")) { times.add(line); } } if (matched == 5) { System.out.println("OK Output matched"); } else { System.err.println("FAILURE Output didn't match"); } System.out.println(); System.out.println(actualFile); System.out.println(times.stream() .map(t -> t.substring(5)) .map(t -> t.replace("s", "").replace("m", ":")) .collect(Collectors.joining(System.lineSeparator()))); var asDurations = times.stream() .map(t -> t.substring(5)) .map(t -> t.replace("s", "S").replace("m", "M")) .map(t -> "PT" + t) .map(Duration::parse) .collect(Collectors.toList()); var min = asDurations.stream().min(Comparator.naturalOrder()).get(); var max = asDurations.stream().max(Comparator.naturalOrder()).get(); var evaluated = asDurations.stream() .filter(d -> d != min && d != max) .collect(Collectors.toList()); var mean = evaluated.get(0).plus(evaluated.get(1)).plus(evaluated.get(2)).dividedBy(3); var result = String.format("%02d:%02d.%.0f", mean.toMinutesPart(), mean.toSecondsPart(), (double) mean.toNanosPart() / 1_000_000); var author = actualFile.replace(".out", ""); System.out.println(String.format("\n| | %s| [link](https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CalculateAverage_%s.java)| 21.0.1-open | [%s](https://github.com/%s)|", result, author, author, author)); } } ================================================ FILE: src/main/go/AlexanderYastrebov/Dockerfile ================================================ # # Copyright 2023 The original authors # # 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. # FROM golang AS build-stage COPY . src/ RUN cd src && go build . FROM scratch AS export-stage COPY --from=build-stage /go/src/1brc / ================================================ FILE: src/main/go/AlexanderYastrebov/README.md ================================================ # 1brc in go It uses Docker with BuildKit plugin to build and [export binary](https://docs.docker.com/engine/reference/commandline/build/#output) binary, see [prepare_AlexanderYastrebov.sh](../../../../prepare_AlexanderYastrebov.sh) and [calculate_average_AlexanderYastrebov.sh](../../../../calculate_average_AlexanderYastrebov.sh). Demo: ```sh $ ./test.sh AlexanderYastrebov [+] Building 0.2s (9/9) FINISHED => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 172B 0.0s => [internal] load metadata for docker.io/library/golang:latest 0.0s => [internal] load build context 0.0s => => transferring context: 145B 0.0s => [build-stage 1/3] FROM docker.io/library/golang 0.0s => CACHED [build-stage 2/3] COPY . src/ 0.0s => CACHED [build-stage 3/3] RUN cd src && go build . 0.0s => CACHED [export-stage 1/1] COPY --from=build-stage /go/src/1brc / 0.0s => exporting to client directory 0.1s => => copying files 2.03MB 0.0s Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-10000-unique-keys.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-10.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-1.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-20.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-2.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-3.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-boundaries.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-complex-utf8.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-dot.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-shortest.txt Validating calculate_average_AlexanderYastrebov.sh -- src/test/resources/samples/measurements-short.txt # Run once to setup the benchmark # ./create_measurements.sh 1000000000 # mv measurements.txt measurements_1B.txt # ln -s measurements_1B.txt measurements.txt # ./calculate_average_baseline.sh > out_expected.txt $ wc -l measurements_1B.txt 1000000000 measurements_1B.txt $ ./evaluate2.sh AlexanderYastrebov royvanrijn ... 0.0s Benchmark 1: ./calculate_average_AlexanderYastrebov.sh 2>&1 Time (mean ± σ): 16.786 s ± 0.545 s [User: 56.030 s, System: 10.068 s] Range (min … max): 15.918 s … 17.309 s 5 runs ... Benchmark 1: ./calculate_average_royvanrijn.sh 2>&1 Time (mean ± σ): 16.731 s ± 0.190 s [User: 56.485 s, System: 10.279 s] Range (min … max): 16.490 s … 16.951 s 5 runs Summary AlexanderYastrebov: trimmed mean 16.901712789513336, raw times 16.69836470718,17.30911065018,16.83413600418,15.91787706218,17.17263765718 royvanrijn: trimmed mean 16.738037123633333, raw times 16.4900939703,16.9513459953,16.5794539913,16.8297746273,16.8048827523 ``` ================================================ FILE: src/main/go/AlexanderYastrebov/calc.go ================================================ package main import ( "bytes" "fmt" "log" "math" "os" "runtime" "sort" "sync" "syscall" ) type measurement struct { min, max, sum, count int64 } func main() { if len(os.Args) != 2 { log.Fatalf("Missing measurements filename") } measurements := processFile(os.Args[1]) ids := make([]string, 0, len(measurements)) for id := range measurements { ids = append(ids, id) } sort.Strings(ids) fmt.Print("{") for i, id := range ids { if i > 0 { fmt.Print(", ") } m := measurements[id] fmt.Printf("%s=%.1f/%.1f/%.1f", id, round(float64(m.min)/10.0), round(float64(m.sum)/10.0/float64(m.count)), round(float64(m.max)/10.0)) } fmt.Println("}") } func processFile(filename string) map[string]*measurement { f, err := os.Open(filename) if err != nil { log.Fatalf("Open: %v", err) } defer f.Close() fi, err := f.Stat() if err != nil { log.Fatalf("Stat: %v", err) } size := fi.Size() if size <= 0 || size != int64(int(size)) { log.Fatalf("Invalid file size: %d", size) } data, err := syscall.Mmap(int(f.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED) if err != nil { log.Fatalf("Mmap: %v", err) } defer func() { if err := syscall.Munmap(data); err != nil { log.Fatalf("Munmap: %v", err) } }() return process(data) } func process(data []byte) map[string]*measurement { nChunks := runtime.NumCPU() chunkSize := len(data) / nChunks if chunkSize == 0 { chunkSize = len(data) } chunks := make([]int, 0, nChunks) offset := 0 for offset < len(data) { offset += chunkSize if offset >= len(data) { chunks = append(chunks, len(data)) break } nlPos := bytes.IndexByte(data[offset:], '\n') if nlPos == -1 { chunks = append(chunks, len(data)) break } else { offset += nlPos + 1 chunks = append(chunks, offset) } } var wg sync.WaitGroup wg.Add(len(chunks)) results := make([]map[string]*measurement, len(chunks)) start := 0 for i, chunk := range chunks { go func(data []byte, i int) { results[i] = processChunk(data) wg.Done() }(data[start:chunk], i) start = chunk } wg.Wait() measurements := make(map[string]*measurement) for _, r := range results { for id, rm := range r { m := measurements[id] if m == nil { measurements[id] = rm } else { m.min = min(m.min, rm.min) m.max = max(m.max, rm.max) m.sum += rm.sum m.count += rm.count } } } return measurements } func processChunk(data []byte) map[string]*measurement { // Use fixed size linear probe lookup table const ( // use power of 2 for fast modulo calculation, // should be larger than max number of keys which is 10_000 entriesSize = 1 << 14 // use FNV-1a hash fnv1aOffset64 = 14695981039346656037 fnv1aPrime64 = 1099511628211 ) type entry struct { m measurement hash uint64 vlen int value [128]byte // use power of 2 > 100 for alignment } entries := make([]entry, entriesSize) entriesCount := 0 // keep short and inlinable getMeasurement := func(hash uint64, value []byte) *measurement { i := hash & uint64(entriesSize-1) entry := &entries[i] // bytes.Equal could be commented to speedup assuming no hash collisions for entry.vlen > 0 && !(entry.hash == hash && bytes.Equal(entry.value[:entry.vlen], value)) { i = (i + 1) & uint64(entriesSize-1) entry = &entries[i] } if entry.vlen == 0 { entry.hash = hash entry.vlen = copy(entry.value[:], value) entriesCount++ } return &entry.m } // assume valid input for len(data) > 0 { idHash := uint64(fnv1aOffset64) semiPos := 0 for i, b := range data { if b == ';' { semiPos = i break } // calculate FNV-1a hash idHash ^= uint64(b) idHash *= fnv1aPrime64 } idData := data[:semiPos] data = data[semiPos+1:] var temp int64 // parseNumber { negative := data[0] == '-' if negative { data = data[1:] } _ = data[3] if data[1] == '.' { // 1.2\n temp = int64(data[0])*10 + int64(data[2]) - '0'*(10+1) data = data[4:] // 12.3\n } else { _ = data[4] temp = int64(data[0])*100 + int64(data[1])*10 + int64(data[3]) - '0'*(100+10+1) data = data[5:] } if negative { temp = -temp } } m := getMeasurement(idHash, idData) if m.count == 0 { m.min = temp m.max = temp m.sum = temp m.count = 1 } else { m.min = min(m.min, temp) m.max = max(m.max, temp) m.sum += temp m.count++ } } result := make(map[string]*measurement, entriesCount) for i := range entries { entry := &entries[i] if entry.m.count > 0 { result[string(entry.value[:entry.vlen])] = &entry.m } } return result } func round(x float64) float64 { return roundJava(x*10.0) / 10.0 } // roundJava returns the closest integer to the argument, with ties // rounding to positive infinity, see java's Math.round func roundJava(x float64) float64 { t := math.Trunc(x) if x < 0.0 && t-x == 0.5 { //return t } else if math.Abs(x-t) >= 0.5 { t += math.Copysign(1, x) } if t == 0 { // check -0 return 0.0 } return t } // parseNumber reads decimal number that matches "^-?[0-9]{1,2}[.][0-9]" pattern, // e.g.: -12.3, -3.4, 5.6, 78.9 and return the value*10, i.e. -123, -34, 56, 789. func parseNumber(data []byte) int64 { negative := data[0] == '-' if negative { data = data[1:] } var result int64 switch len(data) { // 1.2 case 3: result = int64(data[0])*10 + int64(data[2]) - '0'*(10+1) // 12.3 case 4: result = int64(data[0])*100 + int64(data[1])*10 + int64(data[3]) - '0'*(100+10+1) } if negative { return -result } return result } ================================================ FILE: src/main/go/AlexanderYastrebov/calc_test.go ================================================ package main import ( "fmt" "os" "testing" ) func TestRoundJava(t *testing.T) { for _, tc := range []struct { value float64 expected string }{ {value: -1.5, expected: "-1.0"}, {value: -1.0, expected: "-1.0"}, {value: -0.7, expected: "-1.0"}, {value: -0.5, expected: "0.0"}, {value: -0.3, expected: "0.0"}, {value: 0.0, expected: "0.0"}, {value: 0.3, expected: "0.0"}, {value: 0.5, expected: "1.0"}, {value: 0.7, expected: "1.0"}, {value: 1.0, expected: "1.0"}, {value: 1.5, expected: "2.0"}, } { if rounded := roundJava(tc.value); fmt.Sprintf("%.1f", rounded) != tc.expected { t.Errorf("Wrong rounding of %v, expected: %s, got: %.1f", tc.value, tc.expected, rounded) } } } func TestParseNumber(t *testing.T) { for _, tc := range []struct { value string expected string }{ {value: "-99.9", expected: "-999"}, {value: "-12.3", expected: "-123"}, {value: "-1.5", expected: "-15"}, {value: "-1.0", expected: "-10"}, {value: "0.0", expected: "0"}, {value: "0.3", expected: "3"}, {value: "12.3", expected: "123"}, {value: "99.9", expected: "999"}, } { if number := parseNumber([]byte(tc.value)); fmt.Sprintf("%d", number) != tc.expected { t.Errorf("Wrong parsing of %v, expected: %s, got: %d", tc.value, tc.expected, number) } } } var parseNumberSink int64 func BenchmarkParseNumber(b *testing.B) { data1 := []byte("1.2") data2 := []byte("-12.3") for i := 0; i < b.N; i++ { parseNumberSink = parseNumber(data1) + parseNumber(data2) } } func BenchmarkProcess(b *testing.B) { // $ ./create_measurements.sh 1000000 && mv measurements.txt measurements-1e6.txt // Created file with 1,000,000 measurements in 514 ms const filename = "../../../../measurements-1e6.txt" data, err := os.ReadFile(filename) if err != nil { b.Fatal(err) } measurements := process(data) rows := int64(0) for _, m := range measurements { rows += m.count } b.ReportAllocs() b.ResetTimer() b.ReportMetric(float64(rows), "rows/op") for i := 0; i < b.N; i++ { process(data) } } ================================================ FILE: src/main/go/AlexanderYastrebov/go.mod ================================================ module github.com/AlexanderYastrebov/1brc go 1.21.5 ================================================ FILE: src/main/go/elh/Dockerfile ================================================ # # Copyright 2023 The original authors # # 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. # FROM golang AS builder WORKDIR /app COPY . ./ RUN go build -ldflags "-w -s" -o /1brc-go . FROM scratch AS runner WORKDIR / COPY --from=builder /1brc-go / ================================================ FILE: src/main/go/elh/go.mod ================================================ module github.com/elh/1brc-go go 1.21.5 ================================================ FILE: src/main/go/elh/main.go ================================================ package main import ( "bufio" "fmt" "io" "log" "math" "os" "path/filepath" "runtime" "runtime/pprof" "sort" "strconv" "strings" "sync" "time" "unsafe" ) // go run main.go [measurements_file] // tune env vars for performance // // Environment variables: // - NUM_PARSERS: number of parsers to run concurrently. if unset, defaults // to runtime.NumCPU() // - PARSE_CHUNK_SIZE_MB: size of each chunk to parse. if unset, defaults to // defaultParseChunkSize // - PROFILE: if "true", enables profiling var ( // others: "heap", "threadcreate", "block", "mutex" profileTypes = []string{"goroutine", "allocs"} ) const ( defaultMeasurementsPath = "measurements.txt" maxNameLen = 100 maxNameNum = 10000 // tuned for a 2023 Macbook M2 Pro defaultParseChunkSizeMB = 64 mb = 1024 * 1024 // bytes ) type Stats struct { Min, Max, Sum float64 Count int } // rounding floats to 1 decimal place with 0.05 rounding up to 0.1 func round(x float64) float64 { return math.Floor((x+0.05)*10) / 10 } // parseFloatFast is a high performance float parser using the assumption that // the byte slice will always have a single decimal digit. func parseFloatFast(bs []byte) float64 { var intStartIdx int // is negative? if bs[0] == '-' { intStartIdx = 1 } v := float64(bs[len(bs)-1]-'0') / 10 // single decimal digit place := 1.0 for i := len(bs) - 3; i >= intStartIdx; i-- { // integer part v += float64(bs[i]-'0') * place place *= 10 } if intStartIdx == 1 { v *= -1 } return v } // size is the intended number of bytes to parse. buffer should be longer than size // because we need to continue reading until the end of the line in order to // properly segment the entire file and not miss any data. func parseAt(f *os.File, buf []byte, offset int64, size int) map[string]*Stats { stats := make(map[string]*Stats, maxNameNum) n, err := f.ReadAt(buf, offset) // load the buffer if err != nil && err != io.EOF { log.Fatal(err) } lastName := make([]byte, maxNameLen) // last name parsed var lastNameLen int isScanningName := true // currently scanning name or value? // if offset is non-zero, skip to the first new line var idx, start int if offset != 0 { for idx < n { if buf[idx] == '\n' { idx++ start = idx break } idx++ } } // tick tock between parsing names and values while accummulating stats for { if isScanningName { for idx < n { if buf[idx] == ';' { nameBs := buf[start:idx] lastNameLen = copy(lastName, nameBs) idx++ start = idx isScanningName = false break } idx++ } } else { for idx < n { if buf[idx] == '\n' { valueBs := buf[start:idx] value := parseFloatFast(valueBs) nameUnsafe := unsafe.String(&lastName[0], lastNameLen) if s, ok := stats[nameUnsafe]; !ok { name := string(lastName[:lastNameLen]) // actually allocate string stats[name] = &Stats{Min: value, Max: value, Sum: value, Count: 1} } else { if value < s.Min { s.Min = value } if value > s.Max { s.Max = value } s.Sum += value s.Count++ } idx++ start = idx isScanningName = true break } idx++ } } // terminate when we hit the first newline after the intended size OR // when we hit the end of the file if (isScanningName && idx >= size) || idx >= n { break } } return stats } func printResults(stats map[string]*Stats) { // doesn't help // sorted alphabetically for output names := make([]string, 0, len(stats)) for name := range stats { names = append(names, name) } sort.Strings(names) var builder strings.Builder for i, name := range names { s := stats[name] // gotcha: first round the sum to to remove float precision errors! avg := round(round(s.Sum) / float64(s.Count)) builder.WriteString(fmt.Sprintf("%s=%.1f/%.1f/%.1f", name, s.Min, avg, s.Max)) if i < len(names)-1 { builder.WriteString(", ") } } writer := bufio.NewWriter(os.Stdout) fmt.Fprintf(writer, "{%s}\n", builder.String()) writer.Flush() } // Read file in chunks and parse concurrently. N parsers work off of a chunk // offset chan and send results on an output chan. The results are merged into a // single map of stats and printed. func main() { // parse env vars and inputs shouldProfile := os.Getenv("PROFILE") == "true" var err error var numParsers int { if os.Getenv("NUM_PARSERS") != "" { numParsers, err = strconv.Atoi(os.Getenv("NUM_PARSERS")) if err != nil { log.Fatal(fmt.Errorf("failed to parse NUM_PARSERS: %w", err)) } } else { numParsers = runtime.NumCPU() } } var parseChunkSize int { if os.Getenv("PARSE_CHUNK_SIZE_MB") != "" { parseChunkSizeMB, err := strconv.Atoi(os.Getenv("PARSE_CHUNK_SIZE_MB")) if err != nil { log.Fatal(fmt.Errorf("failed to parse PARSE_CHUNK_SIZE_MB: %w", err)) } parseChunkSize = parseChunkSizeMB * mb } else { parseChunkSize = defaultParseChunkSizeMB * mb } } measurementsPath := defaultMeasurementsPath if len(os.Args) > 1 { measurementsPath = os.Args[1] } // profile code if shouldProfile { nowUnix := time.Now().Unix() os.MkdirAll(fmt.Sprintf("profiles/%d", nowUnix), 0755) for _, profileType := range profileTypes { file, _ := os.Create(fmt.Sprintf("profiles/%d/%s.%s.pprof", nowUnix, filepath.Base(measurementsPath), profileType)) defer file.Close() defer pprof.Lookup(profileType).WriteTo(file, 0) } file, _ := os.Create(fmt.Sprintf("profiles/%d/%s.cpu.pprof", nowUnix, filepath.Base(measurementsPath))) defer file.Close() pprof.StartCPUProfile(file) defer pprof.StopCPUProfile() } // read file f, err := os.Open(measurementsPath) if err != nil { log.Fatal(fmt.Errorf("failed to open %s file: %w", measurementsPath, err)) } defer f.Close() info, err := f.Stat() if err != nil { log.Fatal(fmt.Errorf("failed to read %s file: %w", measurementsPath, err)) } // kick off "parser" workers wg := sync.WaitGroup{} wg.Add(numParsers) // buffered to not block on merging chunkOffsetCh := make(chan int64, numParsers) chunkStatsCh := make(chan map[string]*Stats, numParsers) go func() { i := 0 for i < int(info.Size()) { chunkOffsetCh <- int64(i) i += parseChunkSize } close(chunkOffsetCh) }() for i := 0; i < numParsers; i++ { // WARN: w/ extra padding for line overflow. Each chunk should be read past // the intended size to the next new line. 128 bytes should be enough for // a max 100 byte name + the float value. buf := make([]byte, parseChunkSize+128) go func() { for chunkOffset := range chunkOffsetCh { chunkStatsCh <- parseAt(f, buf, chunkOffset, parseChunkSize) } wg.Done() }() } go func() { wg.Wait() close(chunkStatsCh) }() mergedStats := make(map[string]*Stats, maxNameNum) for chunkStats := range chunkStatsCh { for name, s := range chunkStats { if ms, ok := mergedStats[name]; !ok { mergedStats[name] = s } else { if s.Min < ms.Min { ms.Min = s.Min } if s.Max > ms.Max { ms.Max = s.Max } ms.Sum += s.Sum ms.Count += s.Count } } } printResults(mergedStats) } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_0xshivamagarwal.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.nio.file.StandardOpenOption.READ; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; public class CalculateAverage_0xshivamagarwal { private static final Path FILE = Path.of("./measurements.txt"); private static final byte COLON = ';'; private static final byte NEW_LINE = '\n'; private static final byte HYPHEN = '-'; private static final byte DOT = '.'; private static final int NO_OF_THREADS = Runtime.getRuntime().availableProcessors(); private static long[] mergeFn(final long[] v1, final long[] v2) { v1[0] = Math.min(v1[0], v2[0]); v1[1] = Math.max(v1[1], v2[1]); v1[2] += v2[2]; v1[3] += v2[3]; return v1; } private static String toString(final Map.Entry entry) { var m = entry.getValue(); return entry.getKey() + '=' + m[0] / 10.0 + '/' + Math.round(1.0 * m[2] / m[3]) / 10.0 + '/' + m[1] / 10.0; } private static Map parseData( final MemorySegment data, long offset, final long limit) { var map = new HashMap(10000, 1); var sep = false; var neg = false; var key = new byte[100]; var len = 0; var val = 0; while (offset < limit) { var b = data.get(JAVA_BYTE, offset++); if (sep) { if (b == NEW_LINE) { val = neg ? -val : val; map.merge( new String(key, 0, len), new long[]{ val, val, val, 1 }, CalculateAverage_0xshivamagarwal::mergeFn); sep = false; neg = false; len = 0; val = 0; } else if (b == HYPHEN) { neg = true; } else if (b != DOT) { val = val * 10 + (b - 48); } } else if (b == COLON) { sep = true; } else { key[len++] = b; } } return map; } public static void main(String[] args) throws IOException { final String result; try (var channel = FileChannel.open(FILE, READ); var arena = Arena.ofShared()) { var data = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size(), arena); var chunkSize = data.byteSize() / NO_OF_THREADS; var chunks = new long[NO_OF_THREADS + 1]; chunks[NO_OF_THREADS] = data.byteSize(); for (int i = 1; i < NO_OF_THREADS; ++i) { var chunkPos = i * chunkSize; while (data.get(JAVA_BYTE, chunkPos++) != NEW_LINE) { } chunks[i] = chunkPos; } result = IntStream.range(0, NO_OF_THREADS) .mapToObj(i -> parseData(data, chunks[i], chunks[i + 1])) .parallel() .reduce( (m1, m2) -> { m2.forEach((k, v) -> m1.merge(k, v, CalculateAverage_0xshivamagarwal::mergeFn)); return m1; }) .map( map -> map.entrySet().parallelStream() .sorted(Map.Entry.comparingByKey()) .map(CalculateAverage_0xshivamagarwal::toString) .collect(Collectors.joining(", ", "{", "}"))) .orElse(null); } System.out.println(result); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_3j5a.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.invoke.MethodHandle; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import static java.lang.Class.forName; import static java.lang.System.out; import static java.lang.invoke.MethodHandles.lookup; import static java.util.Comparator.comparing; public class CalculateAverage_3j5a { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException { try (RandomAccessFile measurementsFile = new RandomAccessFile(FILE, "r")) { var slices = slice(measurementsFile); var measurementsChannel = measurementsFile.getChannel(); slices.stream().parallel().map(slice -> { MappedByteBuffer measurementsSlice = map(slice, measurementsChannel); var measurementBuffer = new byte[rules.maxMeasurementLength]; var measurements = HashMap. newHashMap(rules.uniqueStationsCount); while (measurementsSlice.hasRemaining()) { var a = nextStationMeasurement(measurementBuffer, measurementsSlice); var stats = measurements.get(a.station); if (stats == null) { a.station.detachFromMeasurementBuffer(); stats = new StationMeasurementStatistics(a); measurements.put(a.station, stats); } else { stats.add(a); } } return measurements; }).reduce((aslice, bslice) -> { aslice.forEach((astation, astats) -> { var bstats = bslice.putIfAbsent(astation, astats); if (bstats != null) { bstats.merge(astats); } }); return bslice; }).ifPresent(measurements -> { var results = new StringBuilder(measurements.size() * (rules.maxStationNameLength + rules.maxStationStatisticsOutputLength)); measurements.values().stream() .sorted(comparing(StationMeasurementStatistics::getName)) .forEach(stationStats -> results.append(stationStats).append(", ")); out.println("{" + results.substring(0, results.length() - 2) + "}"); }); } } record Rules(int minMeasurementLength, int maxStationNameLength, int maxMeasurementLength, int maxStationStatisticsOutputLength, int uniqueStationsCount) { Rules() { this(5, 100, 106, 18, 10_000); } } private static final Rules rules = new Rules(); record MeasurementsSlice(long start, long length) { } static class Station { private byte[] name; final int length; private int hash; private static final MethodHandle vectorizedHashCode; private static final int T_BYTE = 8; static { try { var arraysSupport = forName("jdk.internal.util.ArraysSupport"); Class[] vectorizedHashCodeSignature = { Object.class, int.class, int.class, int.class, int.class }; var vectorizedHashCodeMethod = arraysSupport.getDeclaredMethod("vectorizedHashCode", vectorizedHashCodeSignature); vectorizedHashCode = lookup().unreflect(vectorizedHashCodeMethod); } catch (NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) { throw new RuntimeException(e); } } Station(byte[] name, int length) { this.name = name; this.length = length; } public void detachFromMeasurementBuffer() { var n = new byte[length]; System.arraycopy(name, 0, n, 0, length); this.name = n; } @Override public boolean equals(Object that) { return Arrays.mismatch(this.name, 0, length, ((Station) that).name, 0, length) < 0; } @Override public int hashCode() { if (hash == 0) { try { hash = (int) vectorizedHashCode.invokeExact((Object) name, 0, length, 1, T_BYTE); } catch (Throwable e) { throw new RuntimeException(e); } } return hash; } } record StationMeasurement(Station station, int temperature) { } private static class StationMeasurementStatistics { private final byte[] bname; private String name; private int min; private int max; private long sum; private int count = 1; StationMeasurementStatistics(StationMeasurement stationMeasurement) { this.bname = stationMeasurement.station.name; this.min = stationMeasurement.temperature; this.max = stationMeasurement.temperature; this.sum = stationMeasurement.temperature; } public String getName() { if (name == null) { name = new String(bname, StandardCharsets.UTF_8); } return name; } void add(StationMeasurement measurement) { var temperature = measurement.temperature; update(1, temperature, temperature, temperature); } void merge(StationMeasurementStatistics other) { update(other.count, other.min, other.max, other.sum); } private void update(int count, int min, int max, long sum) { this.count += count; if (this.min > min) { this.min = min; } if (this.max < max) { this.max = max; } this.sum += sum; } @Override public String toString() { var name = getName(); var min = this.min / 10f; var mean = Math.round(this.sum / (float) this.count) / 10f; var max = this.max / 10f; return new StringBuilder(name.length() + rules.maxStationStatisticsOutputLength) .append(name).append("=").append(min).append("/").append(mean).append("/").append(max) .toString(); } } private static StationMeasurement nextStationMeasurement(byte[] measurement, MappedByteBuffer memoryMappedSlice) { byte b; int i = rules.minMeasurementLength; memoryMappedSlice.get(measurement, 0, i); while ((b = memoryMappedSlice.get()) != '\n') { measurement[i] = b; i++; } var zeroOffset = '0'; int temperature = measurement[--i] - zeroOffset; i--; // skipping dot var base = 10; while ((b = measurement[--i]) != ';') { if (b == '-') { temperature = -temperature; } else { temperature = base * (b - zeroOffset) + temperature; base *= base; } } return new StationMeasurement(new Station(measurement, i), temperature); } private static MappedByteBuffer map(MeasurementsSlice slice, FileChannel measurements) { try { return measurements.map(FileChannel.MapMode.READ_ONLY, slice.start, slice.length); } catch (IOException e) { throw new RuntimeException(e); } } private static List slice(RandomAccessFile measurements) throws IOException { int chunks = Runtime.getRuntime().availableProcessors(); List measurementSlices; while ((measurementSlices = slice(measurements, chunks)) == null) { chunks++; } return measurementSlices; } private static List slice(RandomAccessFile measurements, int chunks) throws IOException { long measurementsFileLength = measurements.length(); long chunkLength = 0; long remainder; if (chunks < measurementsFileLength) { chunks--; do { chunkLength = measurementsFileLength / ++chunks; remainder = measurementsFileLength % chunkLength; } while (chunkLength + remainder > Integer.MAX_VALUE); } if (chunkLength <= rules.maxMeasurementLength) { return List.of(new MeasurementsSlice(0, measurementsFileLength)); } var measurementSlices = new ArrayList(chunks); var sliceStart = 0L; for (int i = 0; i < chunks - 1; i++) { var sliceLength = chunkLength; measurements.seek(sliceStart + sliceLength); while (measurements.readByte() != '\n') { measurements.seek(sliceStart + ++sliceLength); } sliceLength++; if (sliceLength > Integer.MAX_VALUE) { return null; } measurementSlices.add(new MeasurementsSlice(sliceStart, sliceLength)); sliceStart = sliceStart + sliceLength; } var previousSlice = measurementSlices.getLast(); var lastSliceStart = previousSlice.start + previousSlice.length; measurementSlices.addLast(new MeasurementsSlice(lastSliceStart, measurementsFileLength - lastSliceStart)); return measurementSlices; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_AbstractKamen.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class CalculateAverage_AbstractKamen { private static final String FILE = "./measurements.txt"; private static class Measurement { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private int sum; private long count; public String toString() { return round(min / 10.0) + "/" + round(sum / 10.0 / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } public static void main(String[] args) throws IOException { try (final FileChannel fc = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ); final RandomAccessFile raf = new RandomAccessFile(new File(FILE), "r")) { final Map res = getParallelBufferStream(raf, fc) .map(CalculateAverage_AbstractKamen::getMeasurements) .flatMap(m -> m.entrySet().stream()) .collect(Collectors.collectingAndThen( Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, CalculateAverage_AbstractKamen::aggregateMeasurements), TreeMap::new)); System.out.println(res); } } private static Measurement aggregateMeasurements(Measurement src, Measurement target) { target.min = Math.min(src.min, target.min); target.max = Math.max(src.max, target.max); target.sum = src.sum + target.sum; target.count = src.count + target.count; return target; } private static Map getMeasurements(BufferSupplier getBuffer) { final Map map = new HashMap<>(50_000); final ByteBuffer byteBuffer = getBuffer.get(); final byte[] bytes = new byte[512]; while (byteBuffer.hasRemaining()) { int nameLen = 0; String name; byte b; while ((b = byteBuffer.get()) != ';') { bytes[nameLen++] = b; } name = new String(bytes, 0, nameLen, StandardCharsets.UTF_8); int valueLen = 0; int neg = 1; while (byteBuffer.hasRemaining() && ((b = byteBuffer.get()) != '\n')) { if (b == '-') { neg = -1; } else if (b == '.' || b == '\r') { // skip the dot and retart char } else { bytes[valueLen++] = b; } } final int val = parseAsInt(valueLen, bytes); takeMeasurement(val * neg, map, name); } return map; } private static int parseAsInt(int valueLen, byte[] bytes) { int val; switch (valueLen) { case 2 -> val = (bytes[0] - 48) * 10 + (bytes[1] - 48); case 3 -> val = (bytes[0] - 48) * 100 + (bytes[1] - 48) * 10 + (bytes[2] - 48); default -> val = 0; } return val; } private static void takeMeasurement(int temperature, Map map, String name) { Measurement measurement = map.get(name); if (measurement != null) { measurement.min = Math.min(measurement.min, temperature); measurement.max = Math.max(measurement.max, temperature); measurement.sum += temperature; measurement.count++; } else { measurement = new Measurement(); map.put(name, measurement); measurement.min = temperature; measurement.max = temperature; measurement.sum = temperature; measurement.count = 1; } } private static Stream getParallelBufferStream(RandomAccessFile raf, FileChannel fc) throws IOException { final int availableProcessors = Runtime.getRuntime().availableProcessors(); return StreamSupport.stream( StreamSupport.stream( Spliterators.spliterator( new BufferSupplierIterator(raf, fc, availableProcessors), availableProcessors, Spliterator.IMMUTABLE | Spliterator.SIZED | Spliterator.SUBSIZED), false) .spliterator(), true); } } interface BufferSupplier extends Supplier { } class BufferSupplierIterator implements Iterator { private long start; private final RandomAccessFile raf; private final FileChannel fc; private final long fileLength; private final long chunkSize; public BufferSupplierIterator(RandomAccessFile raf, FileChannel fc, int numberOfParts) throws IOException { this.raf = raf; this.fc = fc; this.fileLength = fc.size(); this.chunkSize = Math.min(fileLength / numberOfParts, 1073741824); } @Override public boolean hasNext() { return start < fileLength; } @Override public BufferSupplier next() { try { if (hasNext()) { final long end = getEnd(); long s = start; this.start = end; return getBufferSupplier(s, end); } else { throw new NoSuchElementException(); } } catch (IOException e) { throw new RuntimeException(e); } } private long getEnd() throws IOException { long end = Math.min(start + chunkSize, fileLength); while (end < fileLength) { raf.seek(end++); if (raf.read() == '\n') break; } return end; } private BufferSupplier getBufferSupplier(long position, long end) { final long size = end - position; return new BufferSupplier() { private ByteBuffer bb; @Override public ByteBuffer get() { try { if (bb == null) { return (bb = fc.map(MapMode.READ_ONLY, position, size)); } else { return bb; } } catch (IOException e) { throw new RuntimeException(e); } } }; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_C5H12O5.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.CompletionHandler; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TransferQueue; /** * Results on Mac mini (Apple M2 with 8-core CPU / 8GB unified memory): *
 *   using AIO and multiple threads:
 *     120.15s user 4.33s system 710% cpu 17.522 total
 *
 *   reduce the number of memory copies:
 *      45.87s user 2.82s system 530% cpu  9.185 total
 *
 *   processing byte array backwards and using bitwise operation to find specific byte (inspired by thomaswue):
 *      25.38s user 3.44s system 342% cpu  8.406 total
 * 
* * @author Xylitol */ @SuppressWarnings("unchecked") public class CalculateAverage_C5H12O5 { private static final int AVAILABLE_PROCESSOR_NUM = Runtime.getRuntime().availableProcessors(); private static final int TRANSFER_QUEUE_CAPACITY = 1024 / 16 / AVAILABLE_PROCESSOR_NUM; // 1GB memory max private static final int BYTE_BUFFER_CAPACITY = 1024 * 1024 * 16; // 16MB one time private static final int EXPECTED_MAPPINGS_NUM = 10000; /** * Fragment the file into chunks. */ private static long[] fragment(Path path) throws IOException { long size = Files.size(path); long chunk = size / AVAILABLE_PROCESSOR_NUM; List positions = new ArrayList<>(); try (RandomAccessFile file = new RandomAccessFile(path.toFile(), "r")) { long position = chunk; for (int i = 0; i < AVAILABLE_PROCESSOR_NUM - 1; i++) { if (position >= size) { break; } file.seek(position); // move the position to the next newline byte while (file.read() != '\n') { position++; } positions.add(++position); position += chunk; } } if (positions.isEmpty() || positions.getLast() < size) { positions.add(size); } return positions.stream().mapToLong(Long::longValue).toArray(); } public static void main(String[] args) throws Exception { // fragment the input file Path path = Path.of("./measurements.txt"); long[] positions = fragment(path); // start the calculation tasks FutureTask>[] tasks = new FutureTask[positions.length]; for (int i = 0; i < positions.length; i++) { tasks[i] = new FutureTask<>(new Calculator(path, (i == 0 ? 0 : positions[i - 1]), positions[i])); new Thread(tasks[i]).start(); } // wait for the results Map result = HashMap.newHashMap(EXPECTED_MAPPINGS_NUM); for (FutureTask> task : tasks) { task.get().forEach((k, v) -> result.merge(k, v, MeasurementData::merge)); } // sort and print the results TreeMap sorted = new TreeMap<>(); for (Map.Entry entry : result.entrySet()) { sorted.put(new String(entry.getKey().bytes, StandardCharsets.UTF_8), entry.getValue()); } System.out.println(sorted); } /** * The calculation task. */ private static class Calculator implements Callable> { private final TransferQueue transfer = new LinkedTransferQueue<>(); private final AsynchronousFileChannel asyncChannel; private final long limit; private long position; public Calculator(Path file, long position, long limit) throws IOException { ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); this.asyncChannel = AsynchronousFileChannel.open(file, Set.of(StandardOpenOption.READ), executor); this.position = position; this.limit = limit; } @Override public Map call() throws InterruptedException { ByteBuffer buffer = ByteBuffer.allocateDirect(BYTE_BUFFER_CAPACITY); asyncChannel.read(buffer, position, buffer, new CompletionHandler<>() { @Override public void completed(Integer readSize, ByteBuffer buffer) { if (position + readSize >= limit) { buffer.limit(readSize - (int) (position + readSize - limit)); } else { for (int i = buffer.position() - 1; i >= 0; i--) { if (buffer.get(i) == '\n') { // truncate the buffer to the last newline byte buffer.limit(i + 1); break; } } } buffer.flip(); byte[] bytes = new byte[buffer.limit() + 1]; // add a newline byte at the beginning bytes[0] = '\n'; buffer.get(bytes, 1, buffer.limit()); transfer(bytes); if ((position += buffer.limit()) < limit) { buffer.clear(); asyncChannel.read(buffer, position, buffer, this); } else { // stop signal transfer(new byte[0]); } } @Override public void failed(Throwable exc, ByteBuffer buffer) { transfer(new byte[0]); } }); return process(); } /** * Transfer or put the bytes to the queue. */ private void transfer(byte[] bytes) { try { if (transfer.size() >= TRANSFER_QUEUE_CAPACITY) { transfer.transfer(bytes); } else { transfer.put(bytes); } } catch (InterruptedException e) { throw new RuntimeException(e); } } /** * Take and process the bytes from the queue. */ private Map process() throws InterruptedException { Map result = HashMap.newHashMap(EXPECTED_MAPPINGS_NUM); for (byte[] bytes = transfer.take(); bytes.length > 0; bytes = transfer.take()) { Station station = new Station(bytes); // read the bytes backwards for (int position = bytes.length - 2; position >= 1; position--) { // calculate the temperature value int temperature = bytes[position] - '0' + (bytes[position -= 2] - '0') * 10; byte unknownByte = bytes[--position]; int semicolon = switch (unknownByte) { case ';' -> position; case '-' -> { temperature = -temperature; yield --position; } default -> { temperature += (unknownByte - '0') * 100; if (bytes[--position] == '-') { temperature = -temperature; --position; } yield position; } }; // calculate the station name hash int hash = 1; while (true) { long temp = LineFinder.previousLong(bytes, position); int distance = LineFinder.NATIVE.fromRight(temp); if (distance == 0) { // current byte is '\n' break; } position -= distance; if (distance == 8) { // can't find '\n' in previous 8 bytes hash = 31 * hash + (int) (temp ^ (temp >>> 32)); continue; } // clear the redundant bytes temp = LineFinder.NATIVE.clearLeft(temp, distance); hash = 31 * hash + (int) (temp ^ (temp >>> 32)); } // merge data to the result map MeasurementData data = result.get(station.slice(hash, position + 1, semicolon)); if (data == null) { result.put(station.copy(), new MeasurementData(temperature)); } else { data.merge(temperature); } } } return result; } } /** * To find the nearest newline byte position in a long. */ private interface LineFinder { // choose the implementation according to the native byte order LineFinder NATIVE = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? LELineFinder.INST : BELineFinder.INST; Unsafe UNSAFE = initUnsafe(); int BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); int LONG_BYTES = Long.SIZE / Byte.SIZE; static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } static long previousLong(byte[] bytes, long offset) { return UNSAFE.getLong(bytes, BYTE_ARRAY_BASE_OFFSET + offset + 1 - LONG_BYTES); } /** * Mark the highest bit of newline byte (0x0A) to 1. */ static long markHighestBit(long longBytes) { long temp = longBytes ^ 0x0A0A0A0A0A0A0A0AL; return (temp - 0x0101010101010101L) & ~temp & 0x8080808080808080L; } /** * Find the nearest newline byte position from right to left. */ int fromRight(long longBytes); /** * Clear the left bytes out of the range. */ long clearLeft(long longBytes, int keepNum); enum LELineFinder implements LineFinder { INST; private static final long[] MASKS = new long[8]; static { for (int i = 1; i <= 7; i++) { MASKS[i] = 0xFFFFFFFFFFFFFFFFL << ((8 - i) << 3); } } @Override public int fromRight(long longBytes) { return Long.numberOfLeadingZeros(markHighestBit(longBytes)) >>> 3; } @Override public long clearLeft(long longBytes, int keepNum) { return longBytes & MASKS[keepNum]; } } enum BELineFinder implements LineFinder { INST; private static final long[] MASKS = new long[8]; static { for (int i = 1; i <= 7; i++) { MASKS[i] = 0xFFFFFFFFFFFFFFFFL >>> ((8 - i) << 3); } } @Override public int fromRight(long longBytes) { return Long.numberOfTrailingZeros(markHighestBit(longBytes)) >>> 3; } @Override public long clearLeft(long longBytes, int keepNum) { return longBytes & MASKS[keepNum]; } } } /** * The station name wrapper ( bytes[from, to) ). */ private static class Station { private final byte[] bytes; private int from; private int to; private int hash; public Station(byte[] bytes) { this(bytes, 0, 0, 0); } public Station(byte[] bytes, int hash, int from, int to) { this.bytes = bytes; this.slice(hash, from, to); } public Station slice(int hash, int from, int to) { this.hash = hash; this.from = from; this.to = to; return this; } public Station copy() { int length = to - from; byte[] newBytes = new byte[length]; System.arraycopy(bytes, from, newBytes, 0, length); return new Station(newBytes, hash, 0, length); } @Override public boolean equals(Object station) { Station other = (Station) station; return Arrays.equals(bytes, from, to, other.bytes, other.from, other.to); } @Override public int hashCode() { return hash; } } /** * The measurement data wrapper ( temperature * 10 ). */ private static class MeasurementData { private int min; private int max; private long sum; private int count; public MeasurementData(int value) { this.min = value; this.max = value; this.sum = value; this.count = 1; } public MeasurementData merge(int value) { return merge(value, value, value, 1); } public MeasurementData merge(MeasurementData other) { return merge(other.min, other.max, other.sum, other.count); } public MeasurementData merge(int min, int max, long sum, int count) { this.min = Math.min(this.min, min); this.max = Math.max(this.max, max); this.sum += sum; this.count += count; return this; } @Override public String toString() { return STR."\{min / 10.0}/\{Math.round((double) sum / count) / 10.0}/\{max / 10.0}"; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_EduardoSaverin.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static java.nio.file.StandardOpenOption.READ; public class CalculateAverage_EduardoSaverin { private static final Path FILE = Path.of("./measurements.txt"); private static final int NO_OF_THREADS = Runtime.getRuntime().availableProcessors(); private static final Unsafe UNSAFE = initUnsafe(); private static final int FNV_32_OFFSET = 0x811c9dc5; private static final int FNV_32_PRIME = 0x01000193; private static final Map resultRowMap = new HashMap<>(); private static final Lock lock = new ReentrantLock(); private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public record Chunk(long start, long length) { } record MapEntry(String key, ResultRow row) { } private static final class ResultRow { private double min; private double max; private double sum; private int count; private ResultRow(double v) { this.min = v; this.max = v; this.sum = v; this.count = 1; } public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } private double round(double value) { return Math.round(value) / 10.0; } } /** * 0xA - Represents New Line * * @param fileChannel * @return * @throws IOException */ static List getChunks(FileChannel fileChannel) throws IOException { int numThreads = 1; if (fileChannel.size() > 64000) { numThreads = NO_OF_THREADS; } final long fileBytes = fileChannel.size(); final long chunkSize = fileBytes / numThreads; final List chunks = new ArrayList<>(numThreads); final long mappedAddress = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileBytes, Arena.global()).address(); long chunkStart = 0; // Ensures that the chunk size does not exceed the remaining bytes in the file. long chunkLength = Math.min(fileBytes - chunkStart - 1, chunkSize); while (chunkStart < fileBytes) { MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, chunkStart + chunkLength, Math.min(Math.min(fileBytes - chunkStart - chunkLength, chunkLength), 100)); // Until \n found while (mappedByteBuffer.get() != 0xA) { chunkLength++; } chunks.add(new Chunk(mappedAddress + chunkStart, chunkLength + 1)); chunkStart += (chunkLength + 1); chunkLength = Math.min(fileBytes - chunkStart - 1, chunkSize); } return chunks; } static class SimplerHashMap { final int MAPSIZE = 65536; final ResultRow[] slots = new ResultRow[MAPSIZE]; final byte[][] keys = new byte[MAPSIZE][]; public void putOrMerge(final byte[] key, final short length, final int hash, final int temp) { int slot = hash; ResultRow slotValue; // Doing Linear Probing if Collision while ((slotValue = slots[slot]) != null && (keys[slot].length != length || !unsafeEquals(keys[slot], key, length))) { slot++; } // Existing Key if (slotValue != null) { slotValue.min = Math.min(slotValue.min, temp); slotValue.max = Math.max(slotValue.max, temp); slotValue.sum += temp; slotValue.count++; return; } // New Key slots[slot] = new ResultRow(temp); byte[] bytes = new byte[length]; System.arraycopy(key, 0, bytes, 0, length); keys[slot] = bytes; } static boolean unsafeEquals(final byte[] a, final byte[] b, final short length) { // byte by byte comparisons are slow, so do as big chunks as possible final int baseOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET; short i = 0; // Double for (; i < (length & -8); i += 8) { if (UNSAFE.getDouble(a, i + baseOffset) != UNSAFE.getDouble(b, i + baseOffset)) { return false; } } // Long for (; i < (length & -8); i += 8) { if (UNSAFE.getLong(a, i + baseOffset) != UNSAFE.getLong(b, i + baseOffset)) { return false; } } if (i == length) { return true; } // Int for (; i < (length - i & -4); i += 4) { if (UNSAFE.getInt(a, i + baseOffset) != UNSAFE.getInt(b, i + baseOffset)) { return false; } } if (i == length) { return true; } // Short for (; i < (length - i & -2); i += 2) { if (UNSAFE.getShort(a, i + baseOffset) != UNSAFE.getShort(b, i + baseOffset)) { return false; } } if (i == length) { return true; } // Byte for (; i < (length - i); i++) { if (UNSAFE.getByte(a, i + baseOffset) != UNSAFE.getByte(b, i + baseOffset)) { return false; } } return true; } // Get all pairs public List getAll() { final List result = new ArrayList<>(slots.length); for (int i = 0; i < slots.length; i++) { ResultRow slotValue = slots[i]; if (slotValue != null) { result.add(new MapEntry(new String(keys[i], StandardCharsets.UTF_8), slotValue)); } } return result; } } private static class Task implements Runnable { private final SimplerHashMap results; private final Chunk chunk; public Task(Chunk chunk) { this.results = new SimplerHashMap(); this.chunk = chunk; } @Override public void run() { // Max length of any city name final byte[] nameBytes = new byte[100]; short nameIndex = 0; int ot; int hash = FNV_32_OFFSET; long i = chunk.start; final long cl = chunk.start + chunk.length; while (i < cl) { byte c; // 0x3B is ; while ((c = UNSAFE.getByte(i++)) != 0x3B) { nameBytes[nameIndex++] = c; // FNV-1a hash : https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function hash ^= c; hash *= FNV_32_PRIME; } // Temperature just after Semicolon c = UNSAFE.getByte(i++); // 0x2D is Minus(-) // Below you will see -48 which is used to convert from ASCII to Integer, 48 represents 0 in ASCII if (c == 0x2D) { // X.X or XX.X if (UNSAFE.getByte(i + 3) == 0xA) { ot = (UNSAFE.getByte(i++) - 48) * 10; } else { ot = (UNSAFE.getByte(i++) - 48) * 100; ot += (UNSAFE.getByte(i++) - 48) * 10; } // Now dot i++; // Skipping Dot ot += (UNSAFE.getByte(i++) - 48); // Make Number Negative Since we detected (-) sign ot = -ot; } else { // X.X or XX.X if (UNSAFE.getByte(i + 2) == 0xA) { ot = (c - 48) * 10; } else { ot = (c - 48) * 100; ot += (UNSAFE.getByte(i++) - 48) * 10; } // Now dot i++; // Skipping Dot // Number after dot ot += (UNSAFE.getByte(i++) - 48); } // Since Parsed Line, Next thing must be newline i++; hash &= 65535; results.putOrMerge(nameBytes, nameIndex, hash, ot); // Reset nameIndex = 0; hash = FNV_32_OFFSET; } List all = results.getAll(); lock.lock(); try { for (MapEntry me : all) { ResultRow rr; ResultRow lr = me.row; if ((rr = resultRowMap.get(me.key)) != null) { rr.min = Math.min(rr.min, lr.min); rr.max = Math.max(rr.max, lr.max); rr.count += lr.count; rr.sum += lr.sum; } else { resultRowMap.put(me.key, lr); } } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } public static void main(String[] args) throws IOException, InterruptedException { FileChannel fileChannel = FileChannel.open(FILE, READ); List chunks = getChunks(fileChannel); List threads = new ArrayList<>(); for (Chunk chunk : chunks) { Thread thread = new Thread(new Task(chunk)); thread.setPriority(Thread.MAX_PRIORITY); // Make this thread of highest priority threads.add(thread); thread.start(); } for (Thread thread : threads) { thread.join(); } System.out.println(new TreeMap<>(resultRowMap)); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_JaimePolidura.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.util.Map; import java.util.TreeMap; public final class CalculateAverage_JaimePolidura { private static final String FILE = "./measurements.txt"; private static final Unsafe UNSAFE = initUnsafe(); private static final long SEMICOLON_PATTERN = 0X3B3B3B3B3B3B3B3BL; private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (Exception e) { throw new RuntimeException(e); } } public static void main(String[] args) throws Exception { Worker[] workers = createWorkers(); startWorkers(workers); joinWorkers(workers); Map results = mergeWorkersResults(workers); printResults(results); } private static void joinWorkers(Worker[] workers) throws InterruptedException { for (int i = 0; i < workers.length; i++) { workers[i].join(); } } private static void startWorkers(Worker[] workers) { for (int i = 0; i < workers.length; i++) { workers[i].start(); } } private static Worker[] createWorkers() throws Exception { FileChannel channel = new RandomAccessFile(FILE, "r").getChannel(); MemorySegment mmappedFile = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size(), Arena.global()); int nWorkers = channel.size() > 1024 * 1024 ? Runtime.getRuntime().availableProcessors() : 1; Worker[] workers = new Worker[nWorkers]; long quantityPerWorker = Math.floorDiv(channel.size(), nWorkers); long quantityLastWorker = quantityPerWorker + (channel.size() % nWorkers); for (int i = 0; i < nWorkers; i++) { boolean isLastWorker = i == nWorkers - 1; long startAddr = mmappedFile.address() + quantityPerWorker * i; long endAddr = startAddr + (isLastWorker ? quantityLastWorker : quantityPerWorker); workers[i] = new Worker(mmappedFile, channel.size(), startAddr, endAddr); workers[i].setPriority(Thread.MAX_PRIORITY); } return workers; } private static Map mergeWorkersResults(Worker[] workers) { Map mergedResults = new TreeMap<>(); for (int i = 0; i < workers.length; i++) { Worker worker = workers[i]; for (Result entry : worker.results.entries) { if (entry != null) { String name = new String(entry.name, 0, entry.nameLength); Result alreadyExistingResult = mergedResults.get(name); if (alreadyExistingResult != null) { alreadyExistingResult.min = Math.min(alreadyExistingResult.min, entry.min); alreadyExistingResult.max = Math.max(alreadyExistingResult.max, entry.max); alreadyExistingResult.count = alreadyExistingResult.count + entry.count; alreadyExistingResult.sum = alreadyExistingResult.sum + entry.sum; } else { mergedResults.put(name, entry); } } } } return mergedResults; } private static void printResults(Map results) { StringBuilder stringBuilder = new StringBuilder(results.size() * 32); stringBuilder.append('{'); for (Map.Entry entry : results.entrySet()) { if (stringBuilder.length() > 1) { stringBuilder.append(", "); } Result result = entry.getValue(); stringBuilder.append(entry.getKey()) .append('=') .append(round(((double) result.min) / 10.0)) .append('/') .append(round((double) result.sum / (result.count * 10))) .append('/') .append(round(((double) result.max) / 10.0d)); } stringBuilder.append('}'); System.out.println(stringBuilder); } static class Worker extends Thread { private final byte[] lastParsedNameBytes = new byte[100]; private int lastParsedNameLength; private long lastParsedNameHash; private int lastParsedTemperature; private final SimpleMap results; private final MemorySegment mmappedFile; private final long mmappedFileSize; private long currentAddr; // Will point to beginning of string private long endAddr; // Will point to \n public Worker(MemorySegment mmappedFile, long mmappedFileSize, long startAddr, long endAddr) { super("Worker[" + startAddr + ", " + endAddr + "]"); this.mmappedFileSize = mmappedFileSize; this.mmappedFile = mmappedFile; this.currentAddr = startAddr; this.endAddr = endAddr; this.results = new SimpleMap(roundUpToPowerOfTwo(1 << 16)); // 2^16 } @Override public void run() { adjustStartAddr(); adjustEndAddr(); if (this.currentAddr >= endAddr) { return; } while (currentAddr < endAddr) { parseName(); parseTemperature(); this.currentAddr++; // We don't want it to point to \n results.put(this.lastParsedNameHash, this.lastParsedNameBytes, this.lastParsedNameLength, this.lastParsedTemperature); } } // Idea from Quan Anh Mai's implementation private void parseTemperature() { long numberWord = UNSAFE.getLong(currentAddr); // The 4th binary digit of the ascii (Starting from left) of a digit is 1 while '.' is 0 int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000); // 28 = 4 + 8 * 3 (4 bytes is the number of tail zeros in the byte of decimalPos) // xxxn.nn- shift: 28 - 28 = 0 // xxxxxn.n shift: 28 - 12 = 16 // xxxxn.nn shift: 28 - 20 = 8 int shift = 28 - decimalSepPos; // Negative in ASCII: 00101101 2D. In ascii every digit starts with hex digit 3 // So in order to know if a number is positive, we simpy need the first bit of the 2º half // If signed is 0 the number is positive. If it is negative signed will be -1. long signed = (~numberWord << 59) >> 63; // If signed is 0 (positive), designMask will be 0xFFFFFFFFFFFFFFFF (-256) // If signed is -1, all 1s (negative), designMask will be 0xFFFFFFFFFFFFFF00 (-1) long designMask = ~(signed & 0xFF); // Align the number to a fixed position // (x represents any non-related character, _ represents 0x00, n represents the actual digit and - negative) // xxxn.nn- -> xxxn.nn- // xxxxxn.n -> xxxn.n__ // xxxxn.nn -> xxxn.nn_ long numberAligned = (numberWord & designMask) << shift; // We convert ascii representation to number value long numberConvertedFromAscii = numberAligned & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + // 0x00UU00TTHH000000 * 10 + // 0xUU00TTHH00000000 * 100 // Now TT * 100 has 2 trailing zeroes and HH * 100 + TT * 10 + UU < 0x400 // This results in our value lies in the bit 32 to 41 of this product // That was close :) long absValue = ((numberConvertedFromAscii * 0x640a0001) >>> 32) & 0x3FF; long signedValue = (absValue ^ signed) - signed; this.currentAddr += (((decimalSepPos - 4) / 8) + 2); this.lastParsedTemperature = (int) signedValue; } // I first saw this idea in Artsiom Korzun's implementation private void parseName() { this.lastParsedNameHash = 0; long totalWordHash = 0; int totalWordLength = 0; for (;;) { long actualWord = UNSAFE.getLong(currentAddr + totalWordLength); long hasSemicolon = hasByte(actualWord, SEMICOLON_PATTERN); if (hasSemicolon != 0) { int actualLength = Long.numberOfTrailingZeros(hasSemicolon) >> 3; if (actualLength == 0) { actualWord = 0; } actualWord = mask(actualWord, actualLength); UNSAFE.putLong(this.lastParsedNameBytes, Unsafe.ARRAY_BYTE_BASE_OFFSET + totalWordLength, actualWord); totalWordHash ^= actualWord; totalWordLength += actualLength; this.lastParsedNameLength = totalWordLength; this.lastParsedNameHash = totalWordHash; this.currentAddr += totalWordLength + 1; // +1 Because we don't want to point to ';' break; } else { UNSAFE.putLong(this.lastParsedNameBytes, Unsafe.ARRAY_BYTE_BASE_OFFSET + totalWordLength, actualWord); totalWordLength += 8; totalWordHash ^= actualWord; } } } // Removes "garbage" of a word byte private long mask(long word, int length) { int shift = (8 - length) * 8; return (word << shift) >> shift; } private long hasByte(long word, long pattern) { long patternMatch = word ^ pattern; return (patternMatch - 0x0101010101010101L) & (~patternMatch & 0x8080808080808080L); } private void adjustStartAddr() { if (currentAddr == this.mmappedFile.address()) { return; } while (UNSAFE.getByte(currentAddr) != '\n' && currentAddr != endAddr) { currentAddr++; } currentAddr++; // We want it to point to the first character instead of \n } private void adjustEndAddr() { long endAddressMmappedFile = mmappedFile.address() + mmappedFileSize; if (endAddr >= endAddressMmappedFile) { return; } while (UNSAFE.getByte(endAddr) != '\n' && endAddr != endAddressMmappedFile) { endAddr++; } } } static class SimpleMap { private final Result[] entries; private final long size; public SimpleMap(int size) { this.entries = new Result[size]; this.size = size; } public void put(long hashToPut, byte[] nameToPut, int nameLength, int valueToPut) { int index = toIndex(hashToPut); for (;;) { Result actualEntry = entries[index]; if (actualEntry == null) { byte[] nameToPutCopy = new byte[nameLength]; UNSAFE.copyMemory(nameToPut, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameToPutCopy, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameLength); entries[index] = new Result(hashToPut, nameToPutCopy, nameLength, valueToPut, valueToPut, valueToPut, 1); return; } if (actualEntry.isSameName(nameToPut, nameLength)) { actualEntry.min = Math.min(actualEntry.min, valueToPut); actualEntry.max = Math.max(actualEntry.max, valueToPut); actualEntry.count++; actualEntry.sum = actualEntry.sum + valueToPut; return; } index = toIndex(index + 31); } } private int toIndex(long hash) { return (int) (((hash >> 32) ^ ((int) hash)) & (this.size - 1)); } } static class Result { public byte[] name; public int nameLength; public int max; public int min; public int sum; public int count; public long hash; public Result(long hash, byte[] name, int nameLength, int max, int min, int sum, int occ) { this.nameLength = nameLength; this.count = occ; this.hash = hash; this.name = name; this.max = max; this.min = min; this.sum = sum; } public boolean isSameName(byte[] otherNameBytes, int otherNameLength) { return this.nameLength == otherNameLength && isSameNameBytes(otherNameBytes); } private boolean isSameNameBytes(byte[] otherNameBytes) { for (int i = 0; i < this.nameLength; i += 8) { long thisNameBytesAsLong = UNSAFE.getLong(this.name, Unsafe.ARRAY_BYTE_BASE_OFFSET + i); long otherNameBytesAsLong = UNSAFE.getLong(otherNameBytes, Unsafe.ARRAY_BYTE_BASE_OFFSET + i); int isPositiveAsInt = (((8 - nameLength + i) >> 31) & 1) ^ 0x01; int shift = ((8 - nameLength + i) * isPositiveAsInt) * 8; otherNameBytesAsLong = (otherNameBytesAsLong << shift) >>> shift; if (thisNameBytesAsLong != otherNameBytesAsLong) { return false; } } return true; } } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } private static int roundUpToPowerOfTwo(int number) { if (number <= 0) { return 1; } number--; number |= number >> 1; number |= number >> 2; number |= number >> 4; number |= number >> 8; number |= number >> 16; return number + 1; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_JamalMulla.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class CalculateAverage_JamalMulla { private static final long ALL_SEMIS = 0x3B3B3B3B3B3B3B3BL; private static final Map global = new TreeMap<>(); private static final String FILE = "./measurements.txt"; private static final Unsafe UNSAFE = initUnsafe(); private static final Lock lock = new ReentrantLock(); private static final long FXSEED = 0x517cc1b727220a95L; private static final long[] masks = { 0x0, 0x00000000000000FFL, 0x000000000000FFFFL, 0x0000000000FFFFFFL, 0x00000000FFFFFFFFL, 0x000000FFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x00FFFFFFFFFFFFFFL }; private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static final class ResultRow { private int min; private int max; private long sum; private int count; private final long keyStart; private final byte keyLength; private ResultRow(int v, final long keyStart, final byte keyLength) { this.min = v; this.max = v; this.sum = v; this.count = 1; this.keyStart = keyStart; this.keyLength = keyLength; } public String toString() { return round(min) + "/" + round((double) (sum) / count) + "/" + round(max); } private double round(double value) { return Math.round(value) / 10.0; } } private record Chunk(Long start, Long length) { } static Chunk[] getChunks(int numThreads, FileChannel channel) throws IOException { // get all chunk boundaries final long filebytes = channel.size(); final long roughChunkSize = filebytes / numThreads; final Chunk[] chunks = new Chunk[numThreads]; final long mappedAddress = channel.map(FileChannel.MapMode.READ_ONLY, 0, filebytes, Arena.global()).address(); long chunkStart = 0; long chunkLength = Math.min(filebytes - chunkStart - 1, roughChunkSize); int i = 0; while (chunkStart < filebytes) { while (UNSAFE.getByte(mappedAddress + chunkStart + chunkLength) != 0xA /* \n */) { chunkLength++; } chunks[i++] = new Chunk(mappedAddress + chunkStart, chunkLength + 1); // to skip the nl in the next chunk chunkStart += chunkLength + 1; chunkLength = Math.min(filebytes - chunkStart - 1, roughChunkSize); } return chunks; } private static void run(Chunk chunk) { // can't have more than 10000 unique keys but want to match max hash final int MAPSIZE = 65536; final ResultRow[] slots = new ResultRow[MAPSIZE]; byte nameLength; int temp; long hash; long i = chunk.start; final long cl = chunk.start + chunk.length; long word; long hs; long start; byte c; int slot; long n; ResultRow slotValue; while (i < cl) { start = i; hash = 0; word = UNSAFE.getLong(i); while (true) { n = word ^ ALL_SEMIS; hs = (n - 0x0101010101010101L) & (~n & 0x8080808080808080L); if (hs != 0) break; hash = (hash ^ word) * FXSEED; i += 8; word = UNSAFE.getLong(i); } i += Long.numberOfTrailingZeros(hs) >> 3; // hash of what's left ((hs >>> 7) - 1) masks off the bytes from word that are before the semicolon hash = (hash ^ word & (hs >>> 7) - 1) * FXSEED; nameLength = (byte) (i++ - start); // temperature value follows c = UNSAFE.getByte(i++); // we know the val has to be between -99.9 and 99.8 // always with a single fractional digit // represented as a byte array of either 4 or 5 characters if (c != 0x2D /* minus sign */) { // could be either n.x or nn.x if (UNSAFE.getByte(i + 2) == 0xA) { temp = (c - 48) * 10; // char 1 } else { temp = (c - 48) * 100; // char 1 temp += (UNSAFE.getByte(i++) - 48) * 10; // char 2 } temp += (UNSAFE.getByte(++i) - 48); // char 3 } else { // could be either n.x or nn.x if (UNSAFE.getByte(i + 3) == 0xA) { temp = (UNSAFE.getByte(i) - 48) * 10; // char 1 i += 2; } else { temp = (UNSAFE.getByte(i) - 48) * 100; // char 1 temp += (UNSAFE.getByte(i + 1) - 48) * 10; // char 2 i += 3; } temp += (UNSAFE.getByte(i) - 48); // char 2 temp = -temp; } i += 2; // xor folding slot = (int) (hash ^ hash >> 32) & 65535; // Linear probe for open slot while ((slotValue = slots[slot]) != null && (slotValue.keyLength != nameLength || !unsafeEquals(slotValue.keyStart, start, nameLength))) { slot = (slot + 1) % MAPSIZE; } // existing if (slotValue != null) { slotValue.sum += temp; slotValue.count++; if (temp > slotValue.max) { slotValue.max = temp; continue; } if (temp < slotValue.min) slotValue.min = temp; } else { // new value slots[slot] = new ResultRow(temp, start, nameLength); } } // merge results with overall results ResultRow rr; String key; byte[] bytes; lock.lock(); try { for (ResultRow resultRow : slots) { if (resultRow != null) { bytes = new byte[resultRow.keyLength]; // copy the name bytes UNSAFE.copyMemory(null, resultRow.keyStart, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, resultRow.keyLength); key = new String(bytes, StandardCharsets.UTF_8); if ((rr = global.get(key)) != null) { rr.min = Math.min(rr.min, resultRow.min); rr.max = Math.max(rr.max, resultRow.max); rr.count += resultRow.count; rr.sum += resultRow.sum; } else { global.put(key, resultRow); } } } } finally { lock.unlock(); } } static boolean unsafeEquals(final long a_address, final long b_address, final byte b_length) { // byte by byte comparisons are slow, so do as big chunks as possible byte i = 0; for (; i < (b_length & -8); i += 8) { if (UNSAFE.getLong(a_address + i) != UNSAFE.getLong(b_address + i)) { return false; } } if (i == b_length) return true; return (UNSAFE.getLong(a_address + i) & masks[b_length - i]) == (UNSAFE.getLong(b_address + i) & masks[b_length - i]); } public static void main(String[] args) throws IOException, InterruptedException { int numThreads = 1; FileChannel channel = new RandomAccessFile(FILE, "r").getChannel(); if (channel.size() > 64000) { numThreads = Runtime.getRuntime().availableProcessors(); } Chunk[] chunks = getChunks(numThreads, channel); Thread[] threads = new Thread[chunks.length]; for (int i = 0; i < chunks.length; i++) { int finalI = i; Thread thread = new Thread(() -> run(chunks[finalI])); thread.setPriority(Thread.MAX_PRIORITY); thread.start(); threads[i] = thread; } for (Thread t : threads) { t.join(); } System.out.println(global); channel.close(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_JesseVanRooy.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.stream.IntStream; //Disclaimer: The idea from the segmentation into #core amount of chunks came from previously submitted solutions. public class CalculateAverage_JesseVanRooy { private static final String FILE = "./measurements.txt"; private static final ValueLayout.OfByte DATA_LAYOUT = ValueLayout.JAVA_BYTE; private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static class Result { long nameStart; long nameSize; String name; int min; int max; long sum; int count; double min() { return min / 10.0; } double max() { return max / 10.0; } double mean() { return (sum / 10.0) / count; } } public static class ThreadResult { Result[] results; } static final int MAP_SIZE = 16384; static final int MAP_MASK = MAP_SIZE - 1; static final int VALUE_CAPACITY = 10000; static void process(MemorySegment memorySegment, ThreadResult threadResult) { // initialize hash table final int[] keys = new int[MAP_SIZE]; Arrays.fill(keys, -1); final Result[] values = new Result[MAP_SIZE]; // pre-create the result objects final Result[] preCreatedResults = new Result[VALUE_CAPACITY]; int usedPreCreatedResults = 0; for (int i = 0; i < VALUE_CAPACITY; i++) preCreatedResults[i] = new Result(); // load address info final long size = memorySegment.byteSize(); final long address = memorySegment.address(); final long end = address + size; for (long index = address; index < end;) { final long nameStart = index; byte next = UNSAFE.getByte(index); // hash the city name int hash = 0; while (next != ';') { hash = (hash * 33) + next; index++; next = UNSAFE.getByte(index); } final long nameEnd = index; // skip the separator index++; next = UNSAFE.getByte(index); // check for negative boolean negative = next == '-'; if (negative) { index++; next = UNSAFE.getByte(index); } // count the temperature int temperature = next - '0'; index++; next = UNSAFE.getByte(index); if (next != '.') { temperature = (temperature * 10) + (next - '0'); index++; } // skip the . index++; next = UNSAFE.getByte(index); // add the last digit to temperature temperature = (temperature * 10) + (next - '0'); index++; // negate the temperature if needed if (negative) { temperature = -temperature; } // skip the newline index++; // insert into map for (int i = hash; i < hash + MAP_SIZE; i++) { int mapIndex = i & MAP_MASK; if (keys[mapIndex] == -1) { Result result = preCreatedResults[usedPreCreatedResults++]; result.nameStart = nameStart; result.nameSize = nameEnd - nameStart; result.min = temperature; result.max = temperature; result.sum = temperature; result.count = 1; keys[mapIndex] = hash; values[mapIndex] = result; break; } if (keys[mapIndex] == hash) { Result result = values[mapIndex]; result.min = Math.min(result.min, temperature); result.max = Math.max(result.max, temperature); result.sum += temperature; result.count++; break; } } } threadResult.results = Arrays.stream(values).filter(Objects::nonNull).toArray(Result[]::new); for (Result result : threadResult.results) { result.name = new String(memorySegment.asSlice(result.nameStart - address, result.nameSize).toArray(DATA_LAYOUT)); } } public static void main(String[] args) throws IOException, InterruptedException { int numberOfChunks = Runtime.getRuntime().availableProcessors(); try (var fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { long fileSize = fileChannel.size(); MemorySegment allData = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); long segmentSize = (fileSize + numberOfChunks - 1) / numberOfChunks; long[] segmentBounds = new long[numberOfChunks + 1]; segmentBounds[0] = 0; for (int i = 1; i < numberOfChunks; i++) { long chunkAddress = i * segmentSize; while (chunkAddress < fileSize && allData.getAtIndex(DATA_LAYOUT, chunkAddress++) != '\n') { } segmentBounds[i] = Math.min(chunkAddress, fileSize); } segmentBounds[numberOfChunks] = fileSize; ThreadResult[] threadResults = IntStream.range(0, numberOfChunks) .parallel() .mapToObj(i -> { long size = segmentBounds[i + 1] - segmentBounds[i]; long offset = segmentBounds[i]; MemorySegment segment = allData.asSlice(offset, size); ThreadResult result = new ThreadResult(); process(segment, result); return result; }) .toArray(ThreadResult[]::new); HashMap combinedResults = new HashMap<>(1024); for (int i = 0; i < numberOfChunks; i++) { for (Result result : threadResults[i].results) { if (!combinedResults.containsKey(result.name)) { Result newResult = new Result(); newResult.name = result.name; newResult.min = result.min; newResult.max = result.max; newResult.sum = result.sum; newResult.count = result.count; combinedResults.put(result.name, newResult); } else { Result existingResult = combinedResults.get(result.name); existingResult.min = Math.min(existingResult.min, result.min); existingResult.max = Math.max(existingResult.max, result.max); existingResult.sum += result.sum; existingResult.count += result.count; } } } Result[] sortedResults = combinedResults.values().toArray(Result[]::new); Arrays.sort(sortedResults, Comparator.comparing(result -> result.name)); System.out.print("{"); for (int i = 0; i < sortedResults.length; i++) { Result sortedResult = sortedResults[i]; if (i != 0) { System.out.print(", "); } System.out.printf(Locale.US, "%s=%.1f/%.1f/%.1f", sortedResult.name, sortedResult.min(), sortedResult.mean(), sortedResult.max()); } System.out.printf("}\n"); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_Judekeyser.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorSpecies; import java.io.IOException; import java.io.RandomAccessFile; import java.io.UncheckedIOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.Future; import static java.lang.foreign.ValueLayout.OfByte.JAVA_BYTE; import static java.lang.foreign.ValueLayout.OfByte.JAVA_INT_UNALIGNED; public class CalculateAverage_Judekeyser { private static final String FILE = "./measurements.txt"; private static final int chunkSize = (1 << 7) << 12; // This can't go beyond 2^21, because otherwise we might exceed int capacity private static final int numberOfIOWorkers = 1 << 8; // We are going to need (numberOfIOWorkers-1) * chunkSize capacity private static final int numberOfParallelWorkers = Runtime.getRuntime().availableProcessors() - 1; private static final VectorSpecies SPECIES = ByteVector.SPECIES_PREFERRED; public static void main(String[] args) throws Exception { class SimpleStatistics { int min, max, sum, count; SimpleStatistics() { min = Integer.MAX_VALUE; max = Integer.MIN_VALUE; sum = 0; count = 0; } void accept(int value) { min = Math.min(min, value); max = Math.max(max, value); sum += value; count++; } } class Statistics { double min, max, avg; long count; Statistics(SimpleStatistics simple) { min = simple.min/10.; max = simple.max/10.; avg = simple.sum/10./simple.count; count = simple.count; } void accept(SimpleStatistics simple) { min = Math.min(min, simple.min/10.); max = Math.max(max, simple.max/10.); var nextCount = count + simple.count; avg = (avg * count + simple.sum/10.)/nextCount; count = nextCount; } static final DecimalFormat format; static { var decimalFormatSymbols = DecimalFormatSymbols.getInstance(); decimalFormatSymbols.setDecimalSeparator('.'); format = new DecimalFormat("#0.0", decimalFormatSymbols); } @Override public String toString() { return STR."\{format.format(round(min))}/\{format.format(round(avg))}/\{format.format(round(max))}"; } static double round(double d) { return Math.round(d*10.)/10.; } } class Name { final int[] data; final int hash; Name(int[] data) { this.data = data; { var hash = 0; for (var d : data) { hash = 31 * hash + d; } this.hash = hash; } } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { if(obj == this) return true; else if(obj instanceof Name name && name.data.length == data.length) { int size = 0; while(size < data.length) { if(data[size] != name.data[size]) { return false; } else size++; } return true; } else return false; } @Override public String toString() { var bdata = new byte[data.length * 4]; int j = 0; for(int i = 0;i < data.length; i++) { bdata[j++] = (byte)((data[i] >>> 0) & 255); bdata[j++] = (byte)((data[i] >>> 8) & 255); bdata[j++] = (byte)((data[i] >>> 16) & 255); bdata[j++] = (byte)((data[i] >>> 24) & 255); } while(bdata[--j] == 0); return new String(bdata, 0, j+1, StandardCharsets.UTF_8); } } record Line(Name name, int value) {} var results = new HashMap(); try(var file = new RandomAccessFile(Paths.get(FILE).toFile(), "r")) { class Ls implements Iterator { final int M = chunkSize; final Arena arena = Arena.ofShared(); final long length; long offset; Ls() throws IOException { offset = 0L; length = file.length(); } @Override public MemorySegment next() { MemorySegment memorySegment; try { memorySegment = file.getChannel().map( FileChannel.MapMode.READ_ONLY, offset, Math.min(M + 128L, file.getChannel().size() - offset), arena ); } catch (IOException e) { throw new UncheckedIOException(e); } var size = M; if (offset + M < length) { b: { for (int N = 0; N < 128; N++) { var b = memorySegment.get(JAVA_BYTE, size); size += 1; if (b == '\n') { break b; } } assert false : "Lines are smaller than 128 bytes"; } offset += size; } else { size = (int) (length - offset); offset = length; } return memorySegment.asSlice(0, size); } @Override public boolean hasNext() { return offset < length; } } class It implements Iterator { int offset; final int length; final MemorySegment memorySegment; final ByteOrder endian; It(MemorySegment memorySegment) { offset = 0; endian = ByteOrder.nativeOrder(); this.memorySegment = memorySegment; length = (int) memorySegment.byteSize(); assert '\n' == memorySegment.get(JAVA_BYTE, length - 1); } @Override public boolean hasNext() { return offset < length; } @Override public Line next() { int size; b: { /* * Vectorization does not seem to bring anything interesting. * This is a bit disappointing. What am I doing wrong? */ size = 0; while (offset+size+SPECIES.length() <= length) { var vector = ByteVector.fromMemorySegment( SPECIES, memorySegment, offset+size, endian ); var j = vector.eq((byte) '\n').firstTrue(); if (j < SPECIES.length()) { assert j >= 0; size += j; assert memorySegment.get(JAVA_BYTE, offset+size) == '\n'; break b; } else { assert j == SPECIES.length(); size += SPECIES.length(); } } { byte b; for (; size < 128; size++) { b = memorySegment.get(JAVA_BYTE, offset+size); if (b == '\n') break b; } assert false : "Lines are smaller than 128 bytes"; } assert memorySegment.get(JAVA_BYTE, offset+size) == '\n'; assert size < 128; } Name name; int value; { long cursor = offset+size - 1L; { value = memorySegment.get(JAVA_BYTE, cursor) - '0'; value += (memorySegment.get(JAVA_BYTE, cursor-2L) - '0') * 10; cursor -= 3L; if (memorySegment.get(JAVA_BYTE, cursor) == '-') { value *= -1; cursor -= 1L; } else if (memorySegment.get(JAVA_BYTE, cursor) != ';') { value += (memorySegment.get(JAVA_BYTE, cursor) - '0') * 100; cursor -= 1L; if (memorySegment.get(JAVA_BYTE, cursor) == '-') { value *= -1; cursor -= 1L; } } } //var data = memorySegment.asSlice(offset, cursor-offset).toArray(JAVA_BYTE); //System.arraycopy(chunk, 0, data, 0, data.length); //assert ';' != data[data.length - 1]; //name = new Name(data); { int mod4StringSize = ((int)(cursor-offset+3))/4 * 4; var data = memorySegment.asSlice(offset, mod4StringSize).toArray(JAVA_INT_UNALIGNED); switch(((int)(cursor - offset)) % 4) { case 0: break; case 1: { data[data.length - 1] &= 255; } break; case 2: { data[data.length - 1] &= 65535; } break; case 3: { data[data.length - 1] &= 16777215; } break; } name = new Name(data); } } offset += size + 1; return new Line(name, value); } } record Pair(MemorySegment segment, Map simple) { Pair(MemorySegment segment) { this(segment, apply(segment)); } private static Map apply(MemorySegment memorySegment) { try { return call(memorySegment); } catch (IOException e) { throw new UncheckedIOException(e); } } private static Map call(MemorySegment memorySegment) throws IOException { var it = new It(memorySegment); var simple = new HashMap(); while (it.hasNext()) { var line = it.next(); var name = line.name(); var value = line.value(); var statistics = simple.get(name); if (statistics == null) { statistics = new SimpleStatistics(); simple.put(name, statistics); } statistics.accept(value); } return simple; } } var ls = new Ls(); try( var nioService = Executors.newVirtualThreadPerTaskExecutor(); var parallelService =Executors.newFixedThreadPool(numberOfParallelWorkers) ) { var tasksQueue = new ArrayList>(); for(;;) { assert tasksQueue.size() <= numberOfIOWorkers; if(tasksQueue.size() < numberOfIOWorkers) { if(ls.hasNext()) { var memseg = ls.next(); var task = CompletableFuture.supplyAsync( () -> { memseg.load(); return memseg; }, nioService ).thenApplyAsync(Pair::new, parallelService); tasksQueue.add(task); } else if(tasksQueue.isEmpty()) break; } /* * Wait for the tasks and merge what's ready */ { var copy = new ArrayList>(tasksQueue.size()); for(var worker: tasksQueue) { if(worker.isDone()) { /* * Merge the maps */ var p = worker.get(); var simple = p.simple(); p.segment().unload(); for (var entry : simple.entrySet()) { var name = entry.getKey(); var statistics = results.get(name); if (statistics == null) { statistics = new Statistics(entry.getValue()); results.put(name, statistics); } else { statistics.accept(entry.getValue()); } } } else copy.add(worker); } tasksQueue.clear(); tasksQueue.addAll(copy); } } } } /* * Print */ { var sortedMap = new TreeMap(); for(var entry: results.entrySet()) { sortedMap.put( entry.getKey().toString(), entry.getValue() ); } var joiner = new StringJoiner(", ", "{", "}"); for (var entry : sortedMap.entrySet()) { joiner.add(STR. "\{ entry.getKey() }=\{ entry.getValue() }" ); } System.out.println(joiner); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_JurenIvan.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.IntStream; import static java.lang.Math.round; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.file.StandardOpenOption.READ; public class CalculateAverage_JurenIvan { private static final String FILE_NAME = "./measurements.txt"; public static void main(String[] args) throws IOException { long[] segments = getSegments(Runtime.getRuntime().availableProcessors()); var result = IntStream.range(0, segments.length - 1) .parallel() .mapToObj(i -> processSegment(segments[i], segments[i + 1])) .flatMap(m -> Arrays.stream(m.hashTable).filter(Objects::nonNull)) .collect(Collectors.toMap(m -> new String(m.city), m -> m, Measurement::merge, TreeMap::new)); System.out.println(result); } private static LinearProbingHashMap processSegment(long start, long end) { var results = new LinearProbingHashMap(1 << 19); try (var fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE_NAME), READ)) { var bb = fileChannel.map(READ_ONLY, start, end - start); var buffer = new byte[100]; int limit = bb.limit(); for (int startLine = bb.position(); startLine < limit; startLine = bb.position()) { int currentPosition = startLine; byte b; int hash = 7; int wordLen = 0; while (currentPosition < end && (b = bb.get(currentPosition++)) != ';') { buffer[wordLen++] = b; hash = hash * 31 + b; } int temp; int negative = 1; if (bb.get(currentPosition) == '-') { negative = -1; currentPosition++; } if (bb.get(currentPosition + 1) == '.') { temp = negative * ((bb.get(currentPosition) - '0') * 10 + (bb.get(currentPosition + 2) - '0')); currentPosition += 3; } else { temp = negative * ((bb.get(currentPosition) - '0') * 100 + ((bb.get(currentPosition + 1) - '0') * 10 + (bb.get(currentPosition + 3) - '0'))); currentPosition += 4; } currentPosition++; results.put(hash, buffer, wordLen, temp); bb.position(currentPosition); } } catch (IOException e) { throw new RuntimeException(e); } return results; } private static long[] getSegments(int segmentCount) throws IOException { try (var raf = new RandomAccessFile(FILE_NAME, "r")) { long fileSize = raf.length(); if (fileSize < 100000) { long[] chunks = new long[2]; chunks[1] = fileSize; return chunks; } while (fileSize / segmentCount >= (Integer.MAX_VALUE - 150)) { segmentCount *= 2; } long[] chunks = new long[segmentCount + 1]; chunks[0] = 0; long segmentSize = fileSize / segmentCount; for (int i = 1; i < segmentCount; i++) { long chunkOffset = chunks[i - 1] + segmentSize; raf.seek(chunkOffset); while (raf.readByte() != '\n') { } chunks[i] = raf.getFilePointer(); } chunks[segmentCount] = fileSize; return chunks; } } public static class LinearProbingHashMap { final Measurement[] hashTable; int slots; public LinearProbingHashMap(int slots) { this.slots = slots; this.hashTable = new Measurement[slots]; } void put(int hash, byte[] key, int len, int temperature) { hash = Math.abs(hash); int index = hash & (slots - 1); int i = index; while (hashTable[i] != null) { if (keyIsEqual(key, hashTable[i].city, len)) { // handling hash collisions hashTable[i].add(temperature); return; } i++; if (i == slots) { i = 0; } } var cityArr = new byte[len]; System.arraycopy(key, 0, cityArr, 0, len); hashTable[i] = new Measurement(cityArr, hash, temperature, temperature, 1, temperature); } private boolean keyIsEqual(byte[] one, byte[] other, int len) { if (len != other.length) return false; for (int i = 0; i < len; i++) { if (one[i] != other[i]) { return false; } } return true; } } static class Measurement { byte[] city; int hash; int min; int max; int count; long sum; public Measurement(byte[] city, int hash, int min, int max, int count, long sum) { this.city = city; this.hash = hash; this.min = min; this.max = max; this.count = count; this.sum = sum; } public void add(int temperature) { min = Math.min(min, temperature); max = Math.max(max, temperature); count++; sum += temperature; } public Measurement merge(Measurement other) { min = Math.min(min, other.min); max = Math.max(max, other.max); count += other.count; sum += other.sum; return this; } @Override public String toString() { return (min * 1.0) / 10 + "/" + round((sum * 1.0) / count) / 10.0 + "/" + (max * 1.0) / 10; } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { return Arrays.equals(city, ((Measurement) obj).city); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_Kidlike.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; import java.text.DecimalFormat; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Executors; import java.util.stream.Collectors; /** * Versions (correlate to git commits): *
    *
  1. 2m34s: parallel file read -> load byte chunks in memory -> sequentially process bytes for result
  2. *
  3. 0m59s: process byte chunks in parallel (had to introduce smarter byte chunking so it splits only on newlines)
  4. *
  5. 0m46s: smaller numeric types for MeasurementAggregator
  6. *
  7. 0m39s: implement custom byte[] to int parsing, instead of Double.parseDouble(new String(bytes))
  8. *
  9. 0m18s: run with GraalVM native-image
  10. *
  11. 0m14s: remove ConcurrentHashMap
  12. *
  13. 0m11s: remove Map#compute
  14. *
* *

* Hardware: *

    *
  • CPU: https://www.cpubenchmark.net/cpu.php?cpu=AMD+Ryzen+7+5800H&id=3907
  • *
  • NVMe: https://www.harddrivebenchmark.net/hdd.php?hdd=SKHynix%20HFS001TDE9X084N&id=28560
  • *
*

*/ public class CalculateAverage_Kidlike { public static void main(String[] args) { File file = new File("./measurements.txt"); long fileSize = file.length(); int processors = (fileSize < 1_000_000) ? 1 : Runtime.getRuntime().availableProcessors(); long chunkSize = fileSize / processors; MappedByteBuffer[] byteBuffers = new MappedByteBuffer[processors]; try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < processors; i++) { long start = i * chunkSize; long length = (i == processors - 1) ? fileSize - chunkSize * (processors - 1) : chunkSize; int processor = i; executor.execute(() -> { long realStart = start; try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { if (start != 0) { raf.seek(start); while (raf.readByte() != '\n') { // move pointer to newline, so that we can process the byte chunks in parallel later. } realStart = raf.getFilePointer(); } long realLength = fileSize - realStart; if (realStart + length < fileSize) { raf.seek(realStart + length); while (raf.readByte() != '\n') { // move pointer to newline, so that we can process the byte chunks in parallel later. } realLength = raf.getFilePointer() - realStart; } MappedByteBuffer byteBuffer = raf.getChannel().map(MapMode.READ_ONLY, realStart, realLength); byteBuffer.load(); byteBuffers[processor] = byteBuffer; } catch (IOException e) { throw new RuntimeException(e); } }); } } System.out.println(new TreeMap(calculateMeasurements(byteBuffers))); } private static Map calculateMeasurements(MappedByteBuffer[] buffers) { return Arrays.stream(buffers).parallel() .map(buffer -> { var results = new HashMap(); var state = State.NEXT_READ_CITY; var citySink = new CheapByteBuffer(100); var measurementSink = new CheapByteBuffer(10); while (buffer.hasRemaining()) { byte b = buffer.get(); char c = (char) b; if (c == ';') { state = State.NEXT_READ_MEASUREMENT; continue; } if (c == '\n') { String city = new String(citySink.getBytes(), UTF_8); int measurement = bytesToInt(measurementSink.getBytes()); var entry = results.get(city); if (entry == null) { entry = new MeasurementAggregator(); results.put(city, entry); } entry.count++; entry.sum += measurement; entry.min = (short) Math.min(entry.min, measurement); entry.max = (short) Math.max(entry.max, measurement); citySink.clear(); measurementSink.clear(); state = State.NEXT_READ_CITY; continue; } switch (state) { case NEXT_READ_CITY -> citySink.append(b); case NEXT_READ_MEASUREMENT -> measurementSink.append(b); } } return results; }) .flatMap(m -> m.entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, MeasurementAggregator::merge)); } /** * Removes decimal points and returns an integer. For example -12.3 would return -123 */ static int bytesToInt(byte[] bytes) { short index = (short) (bytes.length - 1); boolean isNegative = (bytes[0] == '-'); int number = (bytes[index] - '0'); index -= 2; number += (10 * (bytes[index--] - '0')); if (index == 1 || (!isNegative && index == 0)) { number += (100 * (bytes[index] - '0')); } if (isNegative) { return -number; } else { return number; } } private enum State { NEXT_READ_CITY, NEXT_READ_MEASUREMENT } /** * Numbers are stored as integers, because of {@link #bytesToInt}, and then divided by 10.0 to restore their decimal point. */ private static class MeasurementAggregator { private static final DecimalFormat rounder = new DecimalFormat("0.0"); short min = Short.MAX_VALUE; short max = Short.MIN_VALUE; long sum; int count; private MeasurementAggregator merge(MeasurementAggregator other) { min = (short) Math.min(min, other.min); max = (short) Math.max(max, other.max); sum += other.sum; count += other.count; return this; } @Override public String toString() { return rounder.format(min / 10.0) + "/" + rounder.format(Math.round((double) sum / count) / 10.0) + "/" + rounder.format(max / 10.0); } } private static class CheapByteBuffer { private final byte[] data; private int length; public CheapByteBuffer(final int startSize) { this.data = new byte[startSize]; this.length = 0; } public void append(final byte b) { data[length++] = b; } public void clear() { this.length = 0; } public byte[] getBytes() { return Arrays.copyOf(this.data, this.length); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_MahmoudFawzyKhalil.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.util.*; import java.util.concurrent.ForkJoinPool; // Solution using project Panama and Map Reduce public class CalculateAverage_MahmoudFawzyKhalil { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws Exception { mapReduce(); } private static void mapReduce() throws IOException { var f = new File(FILE); try (var raf = new RandomAccessFile(f, "r")) { FileChannel channel = raf.getChannel(); long fileSize = channel.size(); MemorySegment ms = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); long chunkSize = fileSize / ForkJoinPool.commonPool().getParallelism(); List chunks = getChunks(ms, chunkSize); Map result = chunks.stream() .parallel() .map(c -> readChunkToMap(c, ms)) .reduce(Collections.emptyMap(), (a, b) -> combine(a, b)); System.out.println(new TreeMap<>(result)); } } private static List getChunks(MemorySegment ms, long chunkSize) { List chunks = new ArrayList<>(32); long start = 0; long fileSize = ms.byteSize(); long end = chunkSize; while (start < fileSize) { byte b = ms.get(ValueLayout.JAVA_BYTE, end); if (b == '\n') { chunks.add(new Chunk(start, end)); start = end + 1; end = Math.min(end + chunkSize, fileSize - 2); } end++; } return chunks; } private static Map readChunkToMap(Chunk chunk, MemorySegment ms) { Map map = new HashMap<>(); long start = chunk.start(); while (start < chunk.end()) { long cityNameSize = 0; while (ms.get(ValueLayout.JAVA_BYTE, start + cityNameSize) != ';') { cityNameSize++; } String cityName = readString(ms, start, cityNameSize); start = start + cityNameSize + 1; long temperatureSize = 0; while (ms.get(ValueLayout.JAVA_BYTE, start + temperatureSize) != '\n') { temperatureSize++; } String temperature = readString(ms, start, temperatureSize); start = start + temperatureSize + 1; // System.out.println(STR."\{cityName};\{temperature}"); addMeasurement(map, cityName, temperature); } return map; } // Credit goes to imrafaelmerino for combine function private static Map combine(Map xs, Map ys) { Map result = new HashMap<>(); for (var key : xs.keySet()) { var m1 = xs.get(key); var m2 = ys.get(key); var combined = (m2 == null) ? m1 : (m1 == null) ? m2 : m1.combine(m2); result.put(key, combined); } for (var key : ys.keySet()) result.putIfAbsent(key, ys.get(key)); return result; } private static String readString(MemorySegment ms, long start, long size) { byte[] stringBytes = ms.asSlice(start, size) .toArray(ValueLayout.JAVA_BYTE); return new String(stringBytes); } private static void addMeasurement(Map measurements, String station, String reading) { measurements.compute(station, (_, oldMeasurements) -> oldMeasurements == null ? MeasurementAggregate.of(reading) : oldMeasurements.update(reading)); } record Chunk(long start, long end) { } private static final class MeasurementAggregate { private double min; private double max; private double sum; private long count; private MeasurementAggregate(double min, double max, double sum, long count) { this.min = min; this.max = max; this.sum = sum; this.count = count; } public static MeasurementAggregate of(String temperature) { double measurement = Double.parseDouble(temperature); return new MeasurementAggregate(measurement, measurement, measurement, 1); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || obj.getClass() != this.getClass()) return false; var that = (MeasurementAggregate) obj; return Double.doubleToLongBits(this.min) == Double.doubleToLongBits(that.min) && Double.doubleToLongBits(this.max) == Double.doubleToLongBits(that.max) && Double.doubleToLongBits(this.sum) == Double.doubleToLongBits(that.sum) && this.count == that.count; } @Override public int hashCode() { return Objects.hash(min, max, sum, count); } public MeasurementAggregate update(String part) { double measurement = Double.parseDouble(part); this.min = Math.min(this.min, measurement); this.max = Math.max(this.max, measurement); this.sum += measurement; this.count++; return this; } public String toString() { return min + "/" + round(round(sum) / count) + "/" + max; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } public MeasurementAggregate combine(MeasurementAggregate m2) { return new MeasurementAggregate( Math.min(this.min, m2.min), Math.max(this.max, m2.max), this.sum + m2.sum, this.count + m2.count); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_MeanderingProgrammer.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.*; import java.util.stream.*; /* * # Main Speed Drivers * * Changes were made in this order, each header includes the runtime before and after the change, * and whose implementation (if any) was used as a reference. * * ## Parallel Process Chunks [160.5 -> 18] [twobiers] * * Rather than reading data top to bottom and attempting to parallelize processing with batches * of the parsed data, we read chunks of data (about 1 MB) and parrallelize processing per chunk. * * Several implementations do this kind of processing using a FileChannel to map chunks to buffers, * the reference above gave the idea to use an iterator. * * ## Share Byte Array when Deserializing [18 -> 6.5] [Various] * * When deserializing names after going through the effort of processing one byte at a time * when processing a chunk of data we can re-use a single byte array to store the characters * that make up the name. This removes the need to allocate and de-allocate memory for the buffer. * * We can then use the new String(byte[], 0, length) constructor to create the String without * worrying about clearing the underlying byte array as we provide a length. * * For this one I did not use any particular implementation as a reference but have seen it in many. * * ## Store ints Compute Doubles at End [6.5 -> 6.2] [None] * * Since input has a single decimal only we can effectively ignore it, do all of our math with the * numbers as integers, then only when printing out divide by 10.0 to get the correct values. * * The impact of this is small, maybe even nothing in this implementation, but keeping it in place. * * ## Use graal [6.2 -> 5.3] [None] * * Change from 21.0.1-tem to 21.0.1-graal. * * ## Process ByteBuffer for Name then Value [5.3 -> 4.7] [None] * * This started as a refactor and turned out to have noticeable runtime impact, which is nice. * * Rather than processing the ByteBuffer in a single while (current != '\n') with a condition * to switch from getting the name to calculating the integer value on (current == ';') the * logic was split into 2 separate loops. * * The first, while (current != ';') and a second, while (current != '\n'). * * # For my Own Reference * * ## Constraints * * - Station name: non null UTF-8 string of length [1, 100] bytes * - Temperature value: non null double [-99.9, 99.9] with one fractional digit * - Station names: maximum of 10,000 unique names * * ## Run Commands * * ./mvnw clean verify && ./test.sh MeanderingProgrammer * * ./mvnw clean verify && ./calculate_average_MeanderingProgrammer.sh * * ## Runtimes * * Baseline: 2:40.597 * Current: 0:04.668 */ public class CalculateAverage_MeanderingProgrammer { private static final String FILE = "./measurements.txt"; private static class ChunkReader implements Iterator { private static final long CHUNK_SIZE = 1_024 * 1_024; private final FileChannel channel; private final long size; private long read; public ChunkReader(Path path) throws Exception { this.channel = FileChannel.open(path, StandardOpenOption.READ); this.size = this.channel.size(); this.read = 0; } public long estimateIterations() { return this.size / CHUNK_SIZE; } @Override public boolean hasNext() { return this.nextChunkSize() > 0; } @Override public ByteBuffer next() { ByteBuffer buffer = null; try { buffer = this.channel.map(FileChannel.MapMode.READ_ONLY, this.read, this.nextChunkSize()); } catch (Exception e) { throw new RuntimeException(e); } // Logic to clamp buffer to last complete line int bufferSize = buffer.limit(); while (buffer.get(bufferSize - 1) != '\n') { bufferSize--; } buffer.limit(bufferSize); this.read += bufferSize; return buffer; } private long nextChunkSize() { return Math.min(CHUNK_SIZE, this.size - this.read); } } private static record Row(String name, int value) { } private static class RowReader implements Iterator { private final ByteBuffer buffer; private final byte[] nameBuffer; public RowReader(ByteBuffer buffer) { this.buffer = buffer; this.nameBuffer = new byte[100]; } @Override public boolean hasNext() { return this.buffer.hasRemaining(); } @Override public Row next() { var index = 0; var current = buffer.get(); while (current != ';') { this.nameBuffer[index] = current; index++; current = buffer.get(); } var name = new String(this.nameBuffer, 0, index, StandardCharsets.UTF_8); var negative = false; var value = 0; current = buffer.get(); while (current != '\n') { if (current == '-') { negative = true; } else if (current != '.') { value = (value * 10) + (current - '0'); } current = buffer.get(); } if (negative) { value *= -1; } return new Row(name, value); } } private static class Measurement { private int min; private int max; private long sum; private int count; public Measurement(int value) { this.min = value; this.max = value; this.sum = value; this.count = 1; } public Measurement merge(Measurement other) { if (other.min < this.min) { this.min = other.min; } if (other.max > this.max) { this.max = other.max; } this.sum += other.sum; this.count += other.count; return this; } @Override public String toString() { return String.format( "%.1f/%.1f/%.1f", this.min / 10.0, (this.sum / 10.0) / this.count, this.max / 10.0); } } public static void main(String[] args) throws Exception { run(); } private static void run() throws Exception { var reader = new ChunkReader(Paths.get(FILE)); var iterator = Spliterators.spliterator(reader, reader.estimateIterations(), Spliterator.IMMUTABLE); var measurements = StreamSupport.stream(iterator, true) .flatMap(buffer -> toMeasurements(buffer).entrySet().stream()) .collect(Collectors.toConcurrentMap( entry -> entry.getKey(), entry -> entry.getValue(), Measurement::merge)); System.out.println(new TreeMap<>(measurements)); } private static Map toMeasurements(ByteBuffer buffer) { var iterator = Spliterators.spliteratorUnknownSize(new RowReader(buffer), Spliterator.IMMUTABLE); return StreamSupport.stream(iterator, false) .collect(Collectors.toMap( row -> row.name(), row -> new Measurement(row.value()), Measurement::merge)); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_PanagiotisDrakatos.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; public class CalculateAverage_PanagiotisDrakatos { private static final String FILE = "./measurements.txt"; private static final long MAP_SIZE = 1024 * 1024 * 12L; private static TreeMap sortedCities; public static void main(String[] args) throws IOException { SeekableByteRead(FILE); System.out.println(sortedCities.toString()); boolean DEBUG = true; } private static void SeekableByteRead(String path) throws IOException { FileInputStream fileInputStream = new FileInputStream(new File(FILE)); FileChannel fileChannel = fileInputStream.getChannel(); try { sortedCities = getFileSegments(new File(FILE), fileChannel).stream() .map(CalculateAverage_PanagiotisDrakatos::SplitSeekableByteChannel) .parallel() .map(CalculateAverage_PanagiotisDrakatos::MappingByteBufferToData) .flatMap(MeasurementRepository::get) .collect(Collectors.toMap(e -> e.cityName, MeasurementRepository.Entry::measurement, MeasurementObject::updateWith, TreeMap::new)); } catch (NullPointerException e) { } fileChannel.close(); } record FileSegment(long start, long end, FileChannel fileChannel) { } private static List getFileSegments(final File file, final FileChannel fileChannel) throws IOException { final int numberOfSegments = Runtime.getRuntime().availableProcessors(); final long fileSize = file.length(); final long segmentSize = fileSize / numberOfSegments; final List segments = new ArrayList<>(); if (segmentSize < 1000) { segments.add(new FileSegment(0, fileSize, fileChannel)); return segments; } try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) { long segStart = 0; long segEnd = segmentSize; while (segStart < fileSize) { segEnd = findSegment(randomAccessFile, segEnd, fileSize); segments.add(new FileSegment(segStart, segEnd, fileChannel)); segStart = segEnd; // Just re-use the end and go from there. segEnd = Math.min(fileSize, segEnd + segmentSize); } } return segments; } private static long findSegment(RandomAccessFile raf, long location, final long fileSize) throws IOException { raf.seek(location); while (location < fileSize) { location++; if (raf.read() == '\n') return location; } return location; } private static ByteBuffer SplitSeekableByteChannel(FileSegment segment) { try { MappedByteBuffer buffer = segment.fileChannel.map(FileChannel.MapMode.READ_ONLY, segment.start(), segment.end - segment.start()); return buffer; } catch (Exception ex) { long start = segment.start; long end = 0; try { end = segment.fileChannel.size(); } catch (IOException e) { throw new RuntimeException(e); } MappedByteBuffer buffer = null; ArrayList list = new ArrayList<>(); while (start < end) { try { buffer = segment.fileChannel.map(FileChannel.MapMode.READ_ONLY, start, Math.min(MAP_SIZE, end - start)); // don't split the data in the middle of lines // find the closest previous newline int realEnd = buffer.limit() - 1; while (buffer.get(realEnd) != '\n') realEnd--; realEnd++; buffer.limit(realEnd); start += realEnd; list.add(buffer.slice(0, realEnd - 1)); } catch (Exception e) { e.printStackTrace(); } } sortedCities = list.stream().parallel().map(CalculateAverage_PanagiotisDrakatos::MappingByteBufferToData).flatMap(MeasurementRepository::get) .collect(Collectors.toMap(e -> e.cityName, MeasurementRepository.Entry::measurement, MeasurementObject::updateWith, TreeMap::new)); return null; } } public static ByteBuffer concat(ByteBuffer[] buffers) { int overAllCapacity = 0; for (int i = 0; i < buffers.length; i++) overAllCapacity += buffers[i].limit() - buffers[i].position(); overAllCapacity += buffers[0].limit() - buffers[0].position(); ByteBuffer all = ByteBuffer.allocate(overAllCapacity); for (int i = 0; i < buffers.length; i++) { ByteBuffer curr = buffers[i]; all.put(curr); } all.flip(); return all; } private static TreeMap combineMaps(Stream stream1, Stream stream2) { Stream resultingStream = Stream.concat(stream1, stream2); return resultingStream.collect(Collectors.toMap(e -> e.cityName, MeasurementRepository.Entry::measurement, MeasurementObject::updateWith, TreeMap::new)); } private static int longHashStep(final int hash, final long word) { return 31 * hash + (int) (word ^ (word >>> 32)); } private static final long SEPARATOR_PATTERN = compilePattern((byte) ';'); private static long compilePattern(final byte value) { return ((long) value << 56) | ((long) value << 48) | ((long) value << 40) | ((long) value << 32) | ((long) value << 24) | ((long) value << 16) | ((long) value << 8) | (long) value; } private static MeasurementRepository MappingByteBufferToData(ByteBuffer byteBuffer) { MeasurementRepository measurements = new MeasurementRepository(); ByteBuffer bb = byteBuffer.duplicate(); int start = 0; int limit = bb.limit(); long[] cityNameAsLongArray = new long[16]; int[] delimiterPointerAndHash = new int[2]; bb.order(ByteOrder.nativeOrder()); final boolean bufferIsBigEndian = bb.order().equals(ByteOrder.BIG_ENDIAN); while ((start = bb.position()) < limit + 1) { int delimiterPointer; findNextDelimiterAndCalculateHash(bb, SEPARATOR_PATTERN, start, limit, delimiterPointerAndHash, cityNameAsLongArray, bufferIsBigEndian); delimiterPointer = delimiterPointerAndHash[0]; // Simple lookup is faster for '\n' (just three options) if (delimiterPointer >= limit) { return measurements; } final int cityNameLength = delimiterPointer - start; int temp_counter = 0; int temp_end = delimiterPointer + 1; try { // bb.position(delimiterPointer++); while (bb.get(temp_end) != '\n') { temp_counter++; temp_end++; } } catch (IndexOutOfBoundsException e) { // temp_counter--; // temp_end--; } ByteBuffer temp = bb.duplicate().slice(delimiterPointer + 1, temp_counter); int tempPointer = 0; int abs = 1; if (temp.get(0) == '-') { abs = -1; tempPointer++; } int measuredValue; if (temp.get(tempPointer + 1) == '.') { measuredValue = abs * ((temp.get(tempPointer)) * 10 + (temp.get(tempPointer + 2)) - 528); } else { measuredValue = abs * (temp.get(tempPointer) * 100 + temp.get(tempPointer + 1) * 10 + temp.get(tempPointer + 3) - 5328); } measurements.update(cityNameAsLongArray, bb, cityNameLength, delimiterPointerAndHash[1]).updateWith(measuredValue); if (temp_end + 1 > limit) return measurements; bb.position(temp_end + 1); } return measurements; } private static void findNextDelimiterAndCalculateHash(final ByteBuffer bb, final long pattern, final int start, final int limit, final int[] output, final long[] asLong, final boolean bufferBigEndian) { int hash = 1; int i; int lCnt = 0; for (i = start; i <= limit - 8; i += 8) { long word = bb.getLong(i); if (bufferBigEndian) { word = Long.reverseBytes(word); // Reversing the bytes is the cheapest way to do this } final long match = word ^ pattern; long mask = ((match - 0x0101010101010101L) & ~match) & 0x8080808080808080L; if (mask != 0) { final int index = Long.numberOfTrailingZeros(mask) >> 3; output[0] = (i + index); final long partialHash = word & ((mask >> 7) - 1); asLong[lCnt] = partialHash; output[1] = longHashStep(hash, partialHash); return; } asLong[lCnt++] = word; hash = longHashStep(hash, word); } // Handle remaining bytes near the limit of the buffer: long partialHash = 0; int len = 0; for (; i < limit; i++) { byte read; if ((read = bb.get(i)) == (byte) pattern) { asLong[lCnt] = partialHash; output[0] = i; output[1] = longHashStep(hash, partialHash); return; } partialHash = partialHash | ((long) read << (len << 3)); len++; } output[0] = limit; // delimiter not found } static class MeasurementRepository { private int tableSize = 1 << 20; // can grow in theory, made large enough not to (this is faster) private int tableMask = (tableSize - 1); private int tableLimit = (int) (tableSize * LOAD_FACTOR); private int tableFilled = 0; private static final float LOAD_FACTOR = 0.8f; private Entry[] table = new Entry[tableSize]; record Entry(int hash, long[] nameBytesInLong, String cityName, MeasurementObject measurement) { @Override public String toString() { return cityName + "=" + measurement; } } public MeasurementObject update(long[] nameBytesInLong, ByteBuffer bb, int length, int calculatedHash) { final int nameBytesInLongLength = 1 + (length >>> 3); int index = calculatedHash & tableMask; Entry tableEntry; while ((tableEntry = table[index]) != null && (tableEntry.hash != calculatedHash || !arrayEquals(tableEntry.nameBytesInLong, nameBytesInLong, nameBytesInLongLength))) { // search for the right spot index = (index + 1) & tableMask; } if (tableEntry != null) { return tableEntry.measurement; } // --- This is a brand new entry, insert into the hashtable and do the extra calculations (once!) do slower calculations here. MeasurementObject measurement = new MeasurementObject(); // Now create a string: byte[] buffer = new byte[length]; bb.get(buffer, 0, length); String cityName = new String(buffer, 0, length); // Store the long[] for faster equals: long[] nameBytesInLongCopy = new long[nameBytesInLongLength]; System.arraycopy(nameBytesInLong, 0, nameBytesInLongCopy, 0, nameBytesInLongLength); // And add entry: Entry toAdd = new Entry(calculatedHash, nameBytesInLongCopy, cityName, measurement); table[index] = toAdd; // Resize the table if filled too much: if (++tableFilled > tableLimit) { resizeTable(); } return toAdd.measurement; } private void resizeTable() { // Resize the table: Entry[] oldEntries = table; table = new Entry[tableSize <<= 2]; // x2 tableMask = (tableSize - 1); tableLimit = (int) (tableSize * LOAD_FACTOR); for (Entry entry : oldEntries) { if (entry != null) { int updatedTableIndex = entry.hash & tableMask; while (table[updatedTableIndex] != null) { updatedTableIndex = (updatedTableIndex + 1) & tableMask; } table[updatedTableIndex] = entry; } } } public Stream get() { return Arrays.stream(table).filter(Objects::nonNull); } } private static boolean arrayEquals(final long[] a, final long[] b, final int length) { for (int i = 0; i < length; i++) { if (a[i] != b[i]) return false; } return true; } private static final class MeasurementObject { private int MAX; private int MIN; private long SUM; private int REPEAT; public MeasurementObject(int MAX, int MIN, long SUM, int REPEAT) { this.MAX = MAX; this.MIN = MIN; this.SUM = SUM; this.REPEAT = REPEAT; } public MeasurementObject() { this.MAX = -999; this.MIN = 9999; this.SUM = 0; this.REPEAT = 0; } public MeasurementObject(int MAX, int MIN, long SUM) { this.MAX = MAX; this.MIN = MIN; this.SUM = SUM; } public MeasurementObject(int MAX, int MIN) { this.MAX = MAX; this.MIN = MIN; } public static MeasurementObject combine(MeasurementObject m1, MeasurementObject m2) { var mres = new MeasurementObject(); mres.MIN = MeasurementObject.min(m1.MIN, m2.MIN); mres.MAX = MeasurementObject.max(m1.MAX, m2.MAX); mres.SUM = m1.SUM + m2.SUM; mres.REPEAT = m1.REPEAT + m2.REPEAT; return mres; } public static MeasurementObject updateWith(MeasurementObject m1, MeasurementObject m2) { var mres = new MeasurementObject(); mres.MIN = MeasurementObject.min(m1.MIN, m2.MIN); mres.MAX = MeasurementObject.max(m1.MAX, m2.MAX); mres.SUM = m1.SUM + m2.SUM; mres.REPEAT = m1.REPEAT + m2.REPEAT; return mres; } public MeasurementObject updateWith(int measurement) { MIN = MeasurementObject.min(MIN, measurement); MAX = MeasurementObject.max(MAX, measurement); SUM += measurement; REPEAT++; return this; } private static int max(final int a, final int b) { final int diff = a - b; final int dsgn = diff >> 31; return a - (diff & dsgn); } private static int min(final int a, final int b) { final int diff = a - b; final int dsgn = diff >> 31; return b + (diff & dsgn); } private double round(double value) { return Math.round(value) / 10.0; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MeasurementObject that = (MeasurementObject) o; return MAX == that.MAX && MIN == that.MIN && REPEAT == that.REPEAT; } @Override public int hashCode() { return Objects.hash(MAX, MIN, REPEAT); } @Override public String toString() { return round(MIN) + "/" + round((1.0 * SUM) / REPEAT) + "/" + round(MAX); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_PawelAdamski.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.util.*; import java.util.stream.Collectors; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.groupingByConcurrent; public class CalculateAverage_PawelAdamski { private static final long READ_SIZE = 100_000_000; private static final String FILE = "./measurements.txt"; private static record ResultRow(double min, double mean, double max) { public ResultRow(MeasurementAggregator ma) { this(ma.min / 10.0, ((Math.round(ma.sum * 100.0) / 100.0) / (double) ma.count) / 10.0, ma.max / 10.0); } public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static class Station { byte[] bytes; int hash; public Station(byte[] station) { this.bytes = station; this.hash = Arrays.hashCode(bytes); } @Override public int hashCode() { return hash; } @Override public boolean equals(Object o) { return Arrays.equals(bytes, ((Station) o).bytes); } } private static class MeasurementAggregator { private long min; private long max; private long sum; private long count; public MeasurementAggregator(long temp) { min = temp; max = temp; sum = temp; count = 1; } public MeasurementAggregator() { min = Long.MAX_VALUE; max = Long.MIN_VALUE; sum = 0; count = 0; } public MeasurementAggregator merge(MeasurementAggregator measurement) { MeasurementAggregator ma = new MeasurementAggregator(); ma.min = Math.min(min, measurement.min); ma.max = Math.max(max, measurement.max); ma.sum = sum + measurement.sum; ma.count = count + measurement.count; return ma; } } public static void main(String[] args) throws IOException { try (RandomAccessFile raf = new RandomAccessFile(FILE, "r")) { List parts = splitFileIntoParts(raf); Map rr = calculateTemperatureStats(parts, raf); Map results = prepareResults(rr); System.out.println(results); } } private static Map prepareResults(Map rr) { Map measurements = new TreeMap<>(); rr.forEach((k, v) -> measurements.put(new String(k.bytes, UTF_8), new ResultRow(v))); return measurements; } private static Map calculateTemperatureStats(List parts, RandomAccessFile raf) { return parts.parallelStream() .map(filePart -> parse(filePart, raf)) .flatMap(m -> m.entrySet().stream()) .collect(groupingByConcurrent( Map.Entry::getKey, Collectors.reducing( new MeasurementAggregator(), Map.Entry::getValue, MeasurementAggregator::merge))); } private static ArrayList splitFileIntoParts(RandomAccessFile raf) throws IOException { ArrayList parts = new ArrayList<>((int) (raf.length() / READ_SIZE)); long pointer = 0; long nextPointer = 0; long fileLength = raf.length(); while (pointer < fileLength) { if (pointer + READ_SIZE > fileLength) { nextPointer = fileLength; } else { nextPointer = findNextLine(raf, pointer + READ_SIZE); } parts.add(new FilePart(pointer, nextPointer - pointer)); pointer = nextPointer; } return parts; } private static Map parse(FilePart filePart, RandomAccessFile raf) { try { byte[] bytes = readBytesFromFile(filePart, raf); return parseBytesIntoStationsMap(bytes); } catch (IOException e) { throw new RuntimeException(e); } } private static HashMap parseBytesIntoStationsMap(byte[] bytes) { HashMap measurementAggregator = new HashMap<>(500); int semicolonIndex = 0; int newLineIndex = -1; for (int i = 0; i < bytes.length; i++) { if (bytes[i] == ';') { semicolonIndex = i; } else if (bytes[i] == '\n') { byte[] station = Arrays.copyOfRange(bytes, newLineIndex + 1, semicolonIndex); long temp = parseDouble(bytes, semicolonIndex + 1, i); MeasurementAggregator measurement = new MeasurementAggregator(temp); measurementAggregator.compute(new Station(station), (k, prevV) -> prevV == null ? measurement : prevV.merge(measurement)); newLineIndex = i; } } return measurementAggregator; } private static byte[] readBytesFromFile(FilePart filePart, RandomAccessFile raf) throws IOException { var bb = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, filePart.start(), filePart.len()); byte[] bytes = new byte[bb.remaining()]; bb.get(bytes); return bytes; } private static long parseDouble(byte[] text, int start, int end) { boolean negative = false; int result = 0; for (int i = start; i < end; i++) { byte c = text[i]; if (c == '-') { negative = true; } else if (c != '.') { result *= 10; result += c - '0'; } } if (negative) { return -result; } else { return result; } } private static long findNextLine(RandomAccessFile raf, long currentPosition) throws IOException { raf.seek(currentPosition); while (raf.readByte() != '\n') ; return raf.getFilePointer(); } record FilePart(long start, long len) { } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_SamuelYvon.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; //import jdk.incubator.vector.ByteVector; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; /** * Samuel Yvon's entry. *

* Explanation behind my reasoning: * - I want to make it as fast as possible without it being an unreadable mess; I want to avoid bit fiddling UTF-8 * and use the provided facilities * - I use the fact that we know the number of stations to optimize HashMap creation (75% rule) * - I stole branch-less compare from royvanrijn * - I assume valid ASCII encoding for the number part, which allows me to parse it manually * (should hold for valid UTF-8) * - I have not done Java in forever. Especially what the heck it's become. I've looked at the other submissions and * the given sample to get inspiration. I did not even know about this Stream API thing. *

* *

* Future ideas: * - Probably can Vector-Apirize the number parsing (but it's three to four numbers, is it worth?) *

* *

* Observations: * - [2024-01-09] The branch-less code from royvarijn does not have a huge impact *

* *

* Changelogs: * 2024-01-09: Naive multi-threaded, no floats, manual line parsing *

*/ public class CalculateAverage_SamuelYvon { private static final String FILE = "./measurements.txt"; private static final int MAX_STATIONS = 10000; private static final byte SEMICOL = 0x3B; private static final byte DOT = '.'; private static final byte MINUS = '-'; private static final byte ZERO = '0'; private static final String SLASH_S = "/"; private static final byte NEWLINE = '\n'; // The minimum line length in bytes (over-egg.) private static final int MIN_LINE_LENGTH_BYTES = 200; private static final int DJB2_INIT = 5381; /** * Branchless min (unprecise for large numbers, but good enough) * * @author royvanrijn */ private static int branchlessMax(final int a, final int b) { final int diff = a - b; final int dsgn = diff >> 31; return a - (diff & dsgn); } /** * Branchless min (unprecise for large numbers, but good enough) * * @author royvanrijn */ private static int branchlessMin(final int a, final int b) { final int diff = a - b; final int dsgn = diff >> 31; return b + (diff & dsgn); } /** * A magic key that contains references to the String and where it's located in memory */ private static final class StationName { private final int hash; private final byte[] value; private StationName(int hash, MappedByteBuffer backing, int pos, int len) { this.hash = hash; this.value = new byte[len]; backing.get(pos, this.value); } @Override public int hashCode() { return hash; } @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(Object obj) { // Should NEVER be true // if (!(obj instanceof StationName)) { // return false; // } StationName other = (StationName) obj; if (this.value.length != other.value.length) { return false; } // Byte for byte compare. This actually is a bug! I'm assuming the input // is UTF-8 normalized, which in real life would probably not be the case. // TODO: SIMD? return Arrays.equals(this.value, other.value); } @Override public String toString() { return new String(this.value, StandardCharsets.UTF_8); } } private static class StationMeasureAgg { private int min; private int max; private long sum; private long count; private final StationName station; private String memoizedName; public StationMeasureAgg(StationName name) { // Actual numbers are between -99.9 and 99.9, but we *10 to avoid float this.station = name; min = 1000; max = -1000; sum = 0; count = 0; } @Override public int hashCode() { return this.station.hash; } /** * Get the city name, but also memoized it to avoid building it multiple times * * @return the city name */ public String city() { if (null == this.memoizedName) { this.memoizedName = station.toString(); } return this.memoizedName; } public StationMeasureAgg mergeWith(StationMeasureAgg other) { min = branchlessMin(min, other.min); max = branchlessMax(max, other.max); sum += other.sum; count += other.count; return this; } public void accumulate(int number) { min = branchlessMin(min, number); max = branchlessMax(max, number); sum += number; count++; } @Override public String toString() { double min = Math.round((double) this.min) / 10.0; double max = Math.round((double) this.max) / 10.0; double mean = Math.round((((double) this.sum / this.count))) / 10.0; return min + SLASH_S + mean + SLASH_S + max; } public StationName station() { return this.station; } } private static HashMap parseChunk(MappedByteBuffer chunk) { HashMap m = HashMap.newHashMap(MAX_STATIONS); int i = 0; while (i < chunk.limit()) { int j = i; int hash = DJB2_INIT; // Implement a version of djb2 while we read until the semi, we read anyways for (; j < chunk.limit(); ++j) { byte b = chunk.get(j); if (b == SEMICOL) { break; } // How will this behave in java? Why do I get no control OF SIGNEDNESS FFS // Can I assume int is 32 bits? What is this language! In Java 1.7 it could be 16 bits? // Apparently not anymore?? hash = (((hash << 5) + hash) + b); } StationName name = new StationName(hash, chunk, i, j - i); // Skip the `;` j++; // Parse the int ourselves, avoids a 'String::replace' to remove the // digit. int temp = 0; boolean neg = chunk.get(j) == MINUS; for (j = j + (neg ? 1 : 0); j < chunk.limit(); ++j) { temp *= 10; byte c = chunk.get(j); if (c != DOT) { temp += (char) (c - ZERO); } else { j++; break; } } // The decimal point temp += (char) (chunk.get(j) - ZERO); i = j + 1; while (chunk.get(i++) != NEWLINE) ; if (neg) temp = -temp; m.computeIfAbsent(name, StationMeasureAgg::new).accumulate(temp); } return m; } private static int approximateChunks() { // https://stackoverflow.com/a/4759606 // I don't remember Java :D return Runtime.getRuntime().availableProcessors(); } private static List getFileChunks() throws IOException { int approxChunkCount = approximateChunks(); List fileChunks = new ArrayList<>(approxChunkCount * 2); // Compute chunks offsets and lengths try (RandomAccessFile file = new RandomAccessFile(FILE, "r")) { long totalOffset = 0; long fLength = file.length(); long approximateLength = Long.max(fLength / approxChunkCount, MIN_LINE_LENGTH_BYTES); while (totalOffset < fLength) { long offset = totalOffset; int length = (int) approximateLength; boolean eof = offset + length >= fLength; if (eof) { length = (int) (fLength - offset); } MappedByteBuffer out = file.getChannel().map(FileChannel.MapMode.READ_ONLY, totalOffset, length); while (out.get(length - 1) != NEWLINE) { length--; } out.position(0); out.limit(length); fileChunks.add(out); totalOffset += length; } } return fileChunks; } public static void main(String[] args) throws IOException { var fileChunks = getFileChunks(); // Map per core, giving the non-overlapping memory slices final Map allMeasures = fileChunks.parallelStream().map(CalculateAverage_SamuelYvon::parseChunk).flatMap(x -> x.values().stream()) .collect(Collectors.toMap(StationMeasureAgg::station, x -> x, StationMeasureAgg::mergeWith, HashMap::new)); // Give a capacity that should never be gone past StringBuilder sb = new StringBuilder(allMeasures.size() * (100 + 4 + 20)); sb.append('{'); allMeasures.values().stream().sorted(Comparator.comparing(StationMeasureAgg::city)).forEach(x -> { sb.append(x.city()); sb.append('='); sb.append(x); sb.append(", "); }); sb.delete(sb.length() - 2, sb.length()); // delete trailing comma and space sb.append('}'); System.out.println(sb); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_Smoofie.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.TreeMap; import java.util.concurrent.Executors; import java.util.stream.IntStream; public class CalculateAverage_Smoofie { private static final String FILE = "./measurements.txt"; private static final Unsafe unsafe = getUnsafe(); private static class MeasurementAggregator { private int min = -1000; private int max = 1000; private long sum = 0; private int count = 0; @Override public String toString() { return ((double) min) / 10 + "/" + round(sum / 10.0 / count) + "/" + ((double) max) / 10; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static final class CountResult { private final long cityHashTableAddress; private final long countsAddress; private int cityIdCounter; private long nextCollisionAddress; private CountResult( // cityId|cityLength|cityNameAddress|nextElementAddress|cityCountsAddress long cityHashTableAddress, long countsAddress, int cityIdCounter, long nextCollisionAddress) { this.cityHashTableAddress = cityHashTableAddress; this.countsAddress = countsAddress; this.cityIdCounter = cityIdCounter; this.nextCollisionAddress = nextCollisionAddress; } } private static int hash(long cityNameAddress, short cityLength) { if (cityLength < 17) { long[] city = new long[2]; unsafe.copyMemory(null, cityNameAddress, city, Unsafe.ARRAY_LONG_BASE_OFFSET, cityLength); long hash = city[0] ^ (city[1] >> 1); int foldedHash = (int) (hash ^ (hash >>> 31)); return (foldedHash & foldedHash >>> 15) & 0xffff; } else { long[] city = new long[cityLength >> 3 + 1]; unsafe.copyMemory(null, cityNameAddress, city, Unsafe.ARRAY_LONG_BASE_OFFSET, cityLength); long hash = city[0]; for (int i = 1; i < city.length; i++) { hash ^= city[i]; } int foldedHash = (int) (hash ^ (hash >>> 30)); return (foldedHash & foldedHash >>> 15) & 0xffff; } } private static Unsafe getUnsafe() { try { var field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static long locateSemicolon(long input) { long semiXor = input ^ 0x3B3B3B3B3B3B3B3BL; return (semiXor - 0x0101010101010101L) & ~semiXor & 0x8080808080808080L; } public static void main(String[] args) throws IOException, InterruptedException { var numberOfThreads = Runtime.getRuntime().availableProcessors(); var executorService = Executors.newFixedThreadPool(numberOfThreads); var resultMap = new TreeMap(); var subCountResults = new CountResult[numberOfThreads]; try (RandomAccessFile randomAccessFile = new RandomAccessFile(FILE, "r"); FileChannel fileChannel = randomAccessFile.getChannel()) { long fileSize = randomAccessFile.length(); if (fileSize < numberOfThreads * 1024) { numberOfThreads = fileSize < 1024 ? 1 : (int) (fileSize / 1024); } long chunkSize = fileSize / numberOfThreads; long inputFileAddress = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()).address(); final long[] inputFileMemoryOffsets = new long[numberOfThreads + 1]; inputFileMemoryOffsets[0] = inputFileAddress; inputFileMemoryOffsets[numberOfThreads] = inputFileAddress + fileSize; for (long i = inputFileAddress + chunkSize, index = 1; index < numberOfThreads; i += chunkSize, index++) { while (unsafe.getByte(i++) != '\n') ; inputFileMemoryOffsets[(int) index] = i; } for (int i = 0; i < numberOfThreads; i++) { final long start = inputFileMemoryOffsets[i]; final long end = inputFileMemoryOffsets[i + 1]; final int threadIndex = i; executorService.execute(() -> { var cityHashTableAddress = unsafe.allocateMemory(75536 * 32); unsafe.setMemory(cityHashTableAddress, 75536 * 32, (byte) 0); long nextCollisionAddress = cityHashTableAddress + (65536 << 5); var countsAddress = unsafe.allocateMemory(10000 * 2 * 1000 * 4); int cityId; int temperature = 0; int cityIdCounter = 0; long position = start; byte c; long input; long inputSemicolon; int cityHash; long hashAddress; short cityLength; long cityStart; long temperatureAddress; while (position < end) { cityStart = position; input = unsafe.getLong(position); inputSemicolon = locateSemicolon(input); if (inputSemicolon == 0) { position += 8; input = unsafe.getLong(position); inputSemicolon = locateSemicolon(input); if (inputSemicolon == 0) { // probably not gonna happen very often while (inputSemicolon == 0) { position += 8; input = unsafe.getLong(position); inputSemicolon = locateSemicolon(input); } } } position += Long.numberOfTrailingZeros(inputSemicolon) >> 3; cityLength = (short) (position - cityStart); cityHash = hash(cityStart, cityLength); hashAddress = cityHashTableAddress + ((long) cityHash << 5); cityId = -1; outer: for (;;) { if (cityLength != unsafe.getShort(hashAddress + 4)) { if (unsafe.getShort(hashAddress + 4) == 0) { // new hash slot init cityId = cityIdCounter++; unsafe.setMemory(countsAddress + cityId * 8000, 8000, (byte) 0); unsafe.putInt(hashAddress, cityId); unsafe.putShort(hashAddress + 4, cityLength); unsafe.putLong(hashAddress + 6, cityStart); unsafe.putLong(hashAddress + 22, countsAddress + cityId * 8000); break; } if (unsafe.getLong(hashAddress + 14) != 0) { hashAddress = unsafe.getLong(hashAddress + 14); continue; } break; } long cityNameAddress = unsafe.getLong(hashAddress + 6); int j; for (j = 0; j < cityLength >> 3 << 3; j += 8) { if (unsafe.getLong(cityStart + j) != unsafe.getLong(cityNameAddress + j)) { if (unsafe.getLong(hashAddress + 14) != 0) { hashAddress = unsafe.getLong(hashAddress + 14); continue outer; } break outer; } } if (j < cityLength) { if ((unsafe.getLong(cityStart + j) << ((0x8 - cityLength & 0x7) << 3)) != (unsafe .getLong(cityNameAddress + j) << ((0x8 - cityLength & 0x7) << 3))) { if (unsafe.getLong(hashAddress + 14) != 0) { hashAddress = unsafe.getLong(hashAddress + 14); continue; } break; } } cityId = unsafe.getInt(hashAddress); break; } if (cityId == -1) { // collision cityId = cityIdCounter++; unsafe.setMemory(countsAddress + cityId * 8000, 8000, (byte) 0); unsafe.putLong(hashAddress + 14, nextCollisionAddress); hashAddress = nextCollisionAddress; nextCollisionAddress += 32; unsafe.putInt(hashAddress, cityId); unsafe.putShort(hashAddress + 4, cityLength); unsafe.putLong(hashAddress + 6, cityStart); unsafe.putLong(hashAddress + 22, countsAddress + cityId * 8000); } position++; // skip semicolon // long inputDecimalPoint = locateDecimalPoint(unsafe.getLong(position)); // position += (Long.numberOfTrailingZeros(inputDecimalPoint) >> 3) + 3; temperature = 0; c = unsafe.getByte(position++); if (c == '-') { while ((c = unsafe.getByte(position++)) != '\n') { if (c != '.') { temperature = temperature * 10 + (c ^ 0x30); } } temperatureAddress = unsafe.getLong(hashAddress + 22) + (1000 + temperature) * 4; unsafe.putInt(temperatureAddress, unsafe.getInt(temperatureAddress) + 1); } else { temperature = c - '0'; while ((c = unsafe.getByte(position++)) != '\n') { if (c != '.') { temperature = temperature * 10 + (c ^ 0x30); } } temperatureAddress = unsafe.getLong(hashAddress + 22) + temperature * 4; unsafe.putInt(temperatureAddress, unsafe.getInt(temperatureAddress) + 1); } } subCountResults[threadIndex] = new CountResult(cityHashTableAddress, countsAddress, cityIdCounter, nextCollisionAddress); }); } executorService.shutdown(); executorService.awaitTermination(120, java.util.concurrent.TimeUnit.SECONDS); // aggregate results 1..n to 0 var subCountA = subCountResults[0]; for (int r = 1; r < numberOfThreads; r++) { CountResult subCountB = subCountResults[r]; for (int i = 0; i < 65536; i++) { long bHashAddress = subCountB.cityHashTableAddress + ((long) i << 5); if (unsafe.getShort(bHashAddress + 4) == 0) { continue; } long aHashAddress = subCountA.cityHashTableAddress + ((long) i << 5); // check if a initialized if (unsafe.getShort(aHashAddress + 4) == 0) { // new hash slot init for (long addressA = aHashAddress, addressB = bHashAddress; addressB != 0;) { unsafe.putInt(addressA, subCountA.cityIdCounter++); unsafe.putShort(addressA + 4, unsafe.getShort(addressB + 4)); unsafe.putLong(addressA + 6, unsafe.getLong(addressB + 6)); addressB = unsafe.getLong(addressB + 14); if (addressB != 0) { unsafe.putLong(addressA + 14, subCountA.nextCollisionAddress); addressA = subCountA.nextCollisionAddress; subCountA.nextCollisionAddress += 32; } } } else { // check to copy collision list too outerB: for (long addressB = bHashAddress; addressB != 0; addressB = unsafe.getLong(addressB + 14)) { short cityLength = unsafe.getShort(addressB + 4); long cityNameAddress = unsafe.getLong(addressB + 6); // compare to each city in A slot outerA: for (long aAddress = aHashAddress; aAddress != 0; aAddress = unsafe.getLong(aAddress + 14)) { if (unsafe.getShort(aAddress + 4) == cityLength) { long aCityNameAddress = unsafe.getLong(aAddress + 6); int j; for (j = 0; j < cityLength >> 3 << 3; j += 8) { if (unsafe.getLong(cityNameAddress + j) != unsafe.getLong(aCityNameAddress + j)) { // nope, not the same, try next continue outerA; } } if (j == cityLength || (unsafe.getLong(cityNameAddress + j) << ((0x8 - cityLength & 0x7) << 3)) == (unsafe .getLong(aCityNameAddress + j) << ((0x8 - cityLength & 0x7) << 3))) { // found the same city, continue with next city in B slot continue outerB; } } } // city not found in A slot, add it. It's a collision too long addressA = aHashAddress; while (unsafe.getLong(addressA + 14) != 0) { addressA = unsafe.getLong(addressA + 14); } unsafe.putLong(addressA + 14, subCountA.nextCollisionAddress); addressA = subCountA.nextCollisionAddress; subCountA.nextCollisionAddress += 32; unsafe.putInt(addressA, subCountA.cityIdCounter++); unsafe.putShort(addressA + 4, cityLength); unsafe.putLong(addressA + 6, cityNameAddress); } } } int[] cityIdMap = new int[10000]; for (int i = 0; i < 10000; i++) { cityIdMap[i] = -1; } for (int i = 0; i < 65536; i++) { long bHashAddress = subCountB.cityHashTableAddress + ((long) i << 5); long aHashAddress = subCountA.cityHashTableAddress + ((long) i << 5); if (unsafe.getShort(aHashAddress + 4) == 0) { continue; } // for each city in A slot outerA: for (long aAddress = aHashAddress; aAddress != 0; aAddress = unsafe.getLong(aAddress + 14)) { short cityLength = unsafe.getShort(aAddress + 4); long cityNameAddress = unsafe.getLong(aAddress + 6); int cityIdA = unsafe.getInt(aAddress); // compare to each city in B slot outer: for (long bAddress = bHashAddress; bAddress != 0; bAddress = unsafe.getLong(bAddress + 14)) { if (unsafe.getShort(bAddress + 4) == cityLength) { long bCityNameAddress = unsafe.getLong(bAddress + 6); int j; for (j = 0; j < cityLength >> 3 << 3; j += 8) { if (unsafe.getLong(cityNameAddress + j) != unsafe.getLong(bCityNameAddress + j)) { // nope, not the same, try next continue outer; } } if (j == cityLength || (unsafe.getLong(cityNameAddress + j) << ((0x8 - cityLength & 0x7) << 3)) == (unsafe .getLong(bCityNameAddress + j) << ((0x8 - cityLength & 0x7) << 3))) { cityIdMap[cityIdA] = unsafe.getInt(bAddress); // found the same city, continue with next city in A slot continue outerA; } } } } } for (int i = 0; i < subCountA.cityIdCounter; i++) { int cityId2 = cityIdMap[i]; if (cityId2 != -1) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 1000; k++) { unsafe.putInt(subCountA.countsAddress + i * 8000 + j * 4000 + k * 4, unsafe.getInt(subCountA.countsAddress + i * 8000 + j * 4000 + k * 4) + unsafe.getInt(subCountB.countsAddress + cityId2 * 8000 + j * 4000 + k * 4)); } } } } } var countResult = subCountResults[0]; var reverseCityIds = new String[10000]; for (int i = 0; i < 65536; i++) { long resultHashAddress = countResult.cityHashTableAddress + ((long) i << 5); if (unsafe.getShort(resultHashAddress + 4) != 0) { for (long address = resultHashAddress; address != 0; address = unsafe.getLong(address + 14)) { int cityId = unsafe.getInt(address); int cityLength = unsafe.getShort(address + 4); long cityNameAddress = unsafe.getLong(address + 6); byte[] cityBytes = new byte[cityLength]; unsafe.copyMemory(null, cityNameAddress, cityBytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, cityLength); reverseCityIds[cityId] = new String(cityBytes, StandardCharsets.UTF_8); } } } // count result as stream IntStream.range(0, 10000).parallel().forEach(cityId -> { var cityName = reverseCityIds[cityId]; if (cityName == null) { return; } var cityAddress = countResult.countsAddress + cityId * 8000; var cityResult = new MeasurementAggregator(); for (int i = 999; i > -1; i--) { if (unsafe.getInt(cityAddress + 4000 + i * 4) > 0) { cityResult.min = -i; break; } } if (cityResult.min == -1000) { for (int i = 0; i < 1000; i++) { if (unsafe.getInt(cityAddress + i * 4) > 0) { cityResult.min = i; break; } } } for (int i = 999; i > -1; i--) { if (unsafe.getInt(cityAddress + i * 4) > 0) { cityResult.max = i; break; } } if (cityResult.max == 1000) { for (int i = 0; i < 1000; i++) { if (unsafe.getInt(cityAddress + 4000 + i * 4) > 0) { cityResult.max = -i; break; } } } for (int i = 0; i < 1000; i++) { cityResult.sum += ((long) unsafe.getInt(cityAddress + i * 4)) * i; cityResult.sum -= ((long) unsafe.getInt(cityAddress + 4000 + i * 4)) * i; cityResult.count += unsafe.getInt(cityAddress + i * 4); cityResult.count += unsafe.getInt(cityAddress + 4000 + i * 4); } synchronized (resultMap) { resultMap.put(cityName, cityResult); } }); System.out.println(resultMap); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_Ujjwalbharti.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.*; import java.util.stream.Collector; import static java.util.stream.Collectors.groupingBy; public class CalculateAverage_Ujjwalbharti { private static final String FILE = "./measurements.txt"; private static final List> results = new CopyOnWriteArrayList<>(); private static record Measurement(String station, double value) { private Measurement(String[] parts) { this(parts[0], Double.parseDouble(parts[1])); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } ; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } private static class FileReaderCallable implements Callable { private final Path filePath; private final long startPos; private final long endPos; public FileReaderCallable(Path filePath, long startPos, long endPos) { this.filePath = filePath; this.startPos = startPos; this.endPos = endPos; } @Override public Void call() { try (FileChannel channel = FileChannel.open(filePath, StandardOpenOption.READ)) { channel.position(startPos); ByteBuffer buffer = ByteBuffer.allocate((int) (endPos - startPos + 1)); channel.read(buffer); String chunk = new String(buffer.array()); String[] chunkLines = chunk.split("\n"); Collector collector = Collector.of( MeasurementAggregator::new, (a, m) -> { a.min = Math.min(a.min, m.value()); a.max = Math.max(a.max, m.value()); a.sum += m.value(); a.count++; }, (agg1, agg2) -> { var res = new MeasurementAggregator(); res.min = Math.min(agg1.min, agg2.min); res.max = Math.max(agg1.max, agg2.max); res.sum = agg1.sum + agg2.sum; res.count = agg1.count + agg2.count; return res; }); Map result = Arrays.stream(chunkLines) .map(line -> new Measurement(line.split(";"))) .collect(groupingBy(Measurement::station, collector)); results.add(result); } catch (IOException e) { e.printStackTrace(); } return null; } } private static long calculateEndPosition(Path path, long startPos, long chunkSize) throws IOException { try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) { if (startPos >= channel.size()) { return -1; } long currentPos = startPos + chunkSize; if (currentPos >= channel.size()) { currentPos = channel.size() - 1; } channel.position(currentPos); ByteBuffer buffer = ByteBuffer.allocate(1024); int readBytes = channel.read(buffer); if (readBytes > 0) { for (int i = 0; i < readBytes; i++) { if (buffer.get(i) == '\n') { break; } currentPos++; } } return currentPos; } } public static void main(String[] args) throws IOException { Path path = Paths.get(FILE); long fileSize = Files.size(path); long chunkSize = 1000 * 1000; int nThread = (int) (fileSize / chunkSize); var database = new HashMap(); try (ExecutorService customExecutor = Executors.newFixedThreadPool(275)) { var futures = new ArrayList>(); long startPos = 0; for (int i = 0; i <= nThread; i++) { long endPos = calculateEndPosition(path, startPos, chunkSize); if (endPos == -1) { break; } long finalStartPos = startPos; futures.add(CompletableFuture.runAsync(() -> new FileReaderCallable(path, finalStartPos, endPos).call(), customExecutor)); startPos = endPos + 1; } CompletableFuture allOfFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); allOfFuture.get(); } catch (Exception e) { System.out.println(e.getMessage()); } for (var map : results) { for (String key : map.keySet()) { if (database.containsKey(key)) { MeasurementAggregator mag1 = database.get(key); MeasurementAggregator mag2 = map.get(key); mag1.min = Math.min(mag1.min, mag2.min); mag1.max = Math.max(mag1.max, mag2.max); mag1.sum = mag1.sum + mag2.sum; mag1.count = mag1.count + mag2.count; database.put(key, mag1); } else { database.put(key, map.get(key)); } } } var measurements = new TreeMap(); for (String key : database.keySet()) { MeasurementAggregator mag = database.get(key); measurements.put(key, new ResultRow(mag.min, mag.sum / mag.count, mag.max)); } System.out.println(measurements); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_YannMoisan.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ForkJoinPool; import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * based on imrafaelmerino * ./calculate_average_imrafaelmerino.sh 129.10s user 4.73s system 1395% cpu 9.591 total * * ./calculate_average_baseline.sh 193.27s user 5.81s system 100% cpu 3:17.85 total * * addition to copied implementation * - use a Location object as a key in the Map to avoid String instantiations. * ./calculate_average_YannMoisan.sh 118.36s user 5.72s system 1425% cpu 8.705 total * * Model Name: MacBook Pro * Chip: Intel Core i9 * Total Number of Cores: 8 * Memory: 64 GB * */ public class CalculateAverage_YannMoisan { private static final String FILE = "./measurements.txt"; private static final int FIELD_SIZE = 128; public static void main(String[] args) throws IOException { var chunkSize = 1024 * 1024 * 50L; // Long.parseLong(args[0].trim()); var result = calculateStats(FILE, chunkSize); System.out.println(result); } private static Map calculateStats(String file, long chunkSize) throws IOException { try (var fileChannel = FileChannel.open(Paths.get(file), StandardOpenOption.READ)) { var stats = fileMemoryStream(fileChannel, chunkSize) .parallel() .map(p -> ManagedComputation.compute(() -> parse(p))) .reduce(Collections.emptyMap(), (stat1, stat2) -> combine(stat1, stat2)); var tm = new TreeMap(); stats.forEach((k, v) -> tm.put(new String(k.value, 0, k.value.length), v)); return tm; } } private static Map combine(Map xs, Map ys) { Map result = new HashMap<>(); for (var key : xs.keySet()) { var m1 = xs.get(key); var m2 = ys.get(key); var combined = (m2 == null) ? m1 : (m1 == null) ? m2 : Stat.combine(m1, m2); result.put(key, combined); } for (var key : ys.keySet()) result.putIfAbsent(key, ys.get(key)); return result; } private static Map parse(ByteBuffer bb) { Map stats = new HashMap<>(); var limit = bb.limit(); var field = new byte[FIELD_SIZE]; while (bb.position() < limit) { var fieldCurrentIndex = 0; field[fieldCurrentIndex++] = bb.get(); while (bb.position() < limit) { var fieldByte = bb.get(); if (fieldByte == ';') break; field[fieldCurrentIndex++] = fieldByte; } var fieldStr = new Location(Arrays.copyOfRange(field, 0, fieldCurrentIndex)); var number = 0; var sign = 1; while (bb.position() < limit) { var numberByte = bb.get(); if (numberByte == '-') sign = -1; else if (numberByte == '\n') break; else if (numberByte != '.') number = number * 10 + (numberByte - '0'); } var v = stats.get(fieldStr); if (v == null) { var vv = new Stat(); vv.update(sign * number); stats.put(fieldStr, vv); } else { v.update(sign * number); } } return stats; } private static Stream fileMemoryStream(FileChannel fileChannel, long chunkSize) throws IOException { var spliterator = Spliterators.spliteratorUnknownSize(fileMemoryIterator(fileChannel, chunkSize), Spliterator.IMMUTABLE); return StreamSupport.stream(spliterator, false); } private static Iterator fileMemoryIterator(FileChannel fileChannel, long chunkSize) throws IOException { return new Iterator<>() { private final long size = fileChannel.size(); private long start = 0; @Override public boolean hasNext() { return start < size; } @Override public ByteBuffer next() { try { var buffer = fileChannel.map(MapMode.READ_ONLY, start, Math.min(chunkSize, size - start)); var limmit = buffer.limit() - 1; while (buffer.get(limmit) != '\n') limmit--; limmit++; buffer.limit(limmit); start += limmit; return buffer; } catch (IOException ex) { throw new UncheckedIOException(ex); } } }; } private static final class Location { public final byte[] value; public Location(byte[] value) { this.value = value; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Location location = (Location) o; return Arrays.equals(value, location.value); } @Override public int hashCode() { return Arrays.hashCode(value); } } private static final class Stat { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum = 0L; private long count = 0L; public static Stat combine(Stat m1, Stat m2) { var stat = new Stat(); stat.min = Math.min(m1.min, m2.min); stat.max = Math.max(m1.max, m2.max); stat.sum = m1.sum + m2.sum; stat.count = m1.count + m2.count; return stat; } private void update(int value) { this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); this.sum += value; this.count++; } @Override public String toString() { return round(min / 10.0) + "/" + round((sum / 10.0) / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static final class ManagedComputation { static T compute(final Supplier supplier) { var managedBlocker = new ManagedSupplier<>(supplier); try { ForkJoinPool.managedBlock(managedBlocker); return managedBlocker.getResult(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } private static class ManagedSupplier implements ForkJoinPool.ManagedBlocker { private final Supplier task; private T result; private boolean isDone = false; private ManagedSupplier(final Supplier supplier) { task = supplier; } @Override public boolean block() { result = task.get(); isDone = true; return true; } @Override public boolean isReleasable() { return isDone; } T getResult() { return result; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_abeobk.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.IntStream; import sun.misc.Unsafe; public class CalculateAverage_abeobk { private static final int CPU_CNT = Runtime.getRuntime().availableProcessors(); private static final String FILE = "./measurements.txt"; private static final int BUCKET_SIZE = 1 << 16; private static final long BUCKET_MASK = BUCKET_SIZE - 1; private static final int MAX_STR_LEN = 100; private static final int MAX_STATIONS = 10000; private static final long CHUNK_SZ = 1 << 22; private static final Unsafe UNSAFE = initUnsafe(); private static final long[] HASH_MASKS = new long[]{ 0x0L, 0xffL, 0xffffL, 0xffffffL, 0xffffffffL, 0xffffffffffL, 0xffffffffffffL, 0xffffffffffffffL, 0xffffffffffffffffL, }; private static AtomicInteger chunk_id = new AtomicInteger(0); private static AtomicReference mapref = new AtomicReference<>(null); private static int chunk_cnt; private static long start_addr, end_addr; private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (Exception ex) { throw new RuntimeException(); } } /* * MAIN FUNCTION */ public static void main(String[] args) throws InterruptedException, IOException { // thomaswue trick if (args.length == 0 || !("--worker".equals(args[0]))) { spawnWorker(); return; } var file = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); long file_size = file.size(); start_addr = file.map(MapMode.READ_ONLY, 0, file.size(), Arena.global()).address(); end_addr = start_addr + file_size; // only use all cpus on large file int cpu_cnt = file_size < 1e6 ? 1 : CPU_CNT; chunk_cnt = (int) Math.ceilDiv(file_size, CHUNK_SZ); // spawn workers for (var w : IntStream.range(0, cpu_cnt).mapToObj(i -> new Worker(i)).toList()) { w.join(); } // collect results TreeMap ms = new TreeMap<>(); for (var crr : mapref.get()) { if (crr == null) continue; var prev = ms.putIfAbsent(crr.key(), crr); if (prev != null) prev.merge(crr); } // print result System.out.println(ms); System.out.close(); } /* * HELPER FUNCTIONS */ // Get semicolon pos code static final long getSemiCode(final long w) { long x = w ^ 0x3b3b3b3b3b3b3b3bL; // xor with ;;;;;;;; return (x - 0x0101010101010101L) & (~x & 0x8080808080808080L); } // Get new line pos code static final long getLFCode(final long w) { long x = w ^ 0x0A0A0A0A0A0A0A0AL; // xor with \n\n\n\n\n\n\n\n return (x - 0x0101010101010101L) & (~x & 0x8080808080808080L); } // Get decimal point pos code static final int getDotCode(final long w) { return Long.numberOfTrailingZeros(~w & 0x10101000); } // Convert semicolon pos code to position static final int getSemiPos(final long spc) { return Long.numberOfTrailingZeros(spc) >>> 3; } // Find next line address static final long nextLF(long addr) { long word = UNSAFE.getLong(addr); long lfpos_code = getLFCode(word); while (lfpos_code == 0) { addr += 8; word = UNSAFE.getLong(addr); lfpos_code = getLFCode(word); } return addr + (Long.numberOfTrailingZeros(lfpos_code) >>> 3) + 1; } // Parse number // great idea from merykitty (Quan Anh Mai) static final long num(long w, int d) { int shift = 28 - d; long signed = (~w << 59) >> 63; long dsmask = ~(signed & 0xFF); long digits = ((w & dsmask) << shift) & 0x0F000F0F00L; long abs_val = ((digits * 0x640a0001) >>> 32) & 0x3FF; return ((abs_val ^ signed) - signed); } // Hash mixer static final long mix(long hash) { long h = hash * 37; return (h ^ (h >>> 29)); } // Spawn worker (thomaswue trick private static void spawnWorker() throws IOException { ProcessHandle.Info info = ProcessHandle.current().info(); ArrayList workerCommand = new ArrayList<>(); info.command().ifPresent(workerCommand::add); info.arguments().ifPresent(args -> workerCommand.addAll(Arrays.asList(args))); workerCommand.add("--worker"); new ProcessBuilder() .command(workerCommand) .start() .getInputStream() .transferTo(System.out); } final static class Node { long addr; long hash; long word0; long sum; long min, max; int keylen; int count; public final String toString() { return (min / 10.0) + "/" + (Math.round(((double) sum / count)) / 10.0) + "/" + (max / 10.0); } final String key() { byte[] sbuf = new byte[MAX_STR_LEN]; UNSAFE.copyMemory(null, addr, sbuf, Unsafe.ARRAY_BYTE_BASE_OFFSET, keylen); return new String(sbuf, 0, (int) keylen, StandardCharsets.UTF_8); } Node(long a, long h, int kl, long v) { addr = a; min = max = v; keylen = kl; hash = h; } Node(long a, long h, int kl) { addr = a; hash = h; min = 999; max = -999; keylen = kl; } Node(long a, long w0, long h, int kl, long v) { addr = a; word0 = w0; hash = h; min = max = v; keylen = kl; } Node(long a, long w0, long h, int kl) { addr = a; word0 = w0; hash = h; min = 999; max = -999; keylen = kl; } final void add(long val) { sum += val; count++; if (val > max) { max = val; } if (val < min) { min = val; } } final void merge(Node other) { sum += other.sum; count += other.count; if (other.max > max) { max = other.max; } if (other.min < min) { min = other.min; } } final boolean contentEquals(long other_addr, long other_word0, long other_hash, long kl) { if (word0 != other_word0 || hash != other_hash) return false; // this is faster than comparision if key is short long xsum = 0; long n = kl & 0xF8; for (long i = 8; i < n; i += 8) { xsum |= (UNSAFE.getLong(addr + i) ^ UNSAFE.getLong(other_addr + i)); } return xsum == 0; } final boolean contentEquals(Node other) { if (hash != other.hash) return false; long n = keylen & 0xF8; for (long i = 0; i < n; i += 8) { if (UNSAFE.getLong(addr + i) != UNSAFE.getLong(other.addr + i)) return false; } return true; } } // Thread pool worker static final class Worker extends Thread { final int thread_id; // for debug use only Worker(int i) { thread_id = i; this.setPriority(Thread.MAX_PRIORITY); this.start(); } @Override public void run() { var map = new Node[BUCKET_SIZE + MAX_STATIONS]; // extra space for collisions int id; // process in small chunk to maintain disk locality (artsiomkorzun trick) while ((id = chunk_id.getAndIncrement()) < chunk_cnt) { long addr = start_addr + id * CHUNK_SZ; long end = Math.min(addr + CHUNK_SZ, end_addr); // find start of line if (id > 0) { addr = nextLF(addr); } final int num_segs = 3; long seglen = (end - addr) / num_segs; long a0 = addr; long a1 = nextLF(addr + 1 * seglen); long a2 = nextLF(addr + 2 * seglen); ChunkParser p0 = new ChunkParser(map, a0, a1); ChunkParser p1 = new ChunkParser(map, a1, a2); ChunkParser p2 = new ChunkParser(map, a2, end); while (p0.ok() && p1.ok() && p2.ok()) { long w0 = p0.word(); long w1 = p1.word(); long w2 = p2.word(); long sc0 = getSemiCode(w0); long sc1 = getSemiCode(w1); long sc2 = getSemiCode(w2); Node n0 = p0.key(w0, sc0); Node n1 = p1.key(w1, sc1); Node n2 = p2.key(w2, sc2); long v0 = p0.val(); long v1 = p1.val(); long v2 = p2.val(); n0.add(v0); n1.add(v1); n2.add(v2); } while (p0.ok()) { long w = p0.word(); long sc = getSemiCode(w); Node n = p0.key(w, sc); long v = p0.val(); n.add(v); } while (p1.ok()) { long w = p1.word(); long sc = getSemiCode(w); Node n = p1.key(w, sc); long v = p1.val(); n.add(v); } while (p2.ok()) { long w = p2.word(); long sc = getSemiCode(w); Node n = p2.key(w, sc); long v = p2.val(); n.add(v); } } // merge is cheaper than string casting (artsiomkorzun) while (!mapref.compareAndSet(null, map)) { var other_map = mapref.getAndSet(null); if (other_map != null) { for (int i = 0; i < other_map.length; i++) { var other = other_map[i]; if (other == null) continue; int bucket = (int) (other.hash & BUCKET_MASK); while (true) { var node = map[bucket]; if (node == null) { map[bucket] = other; break; } if (node.contentEquals(other)) { node.merge(other); break; } bucket++; } } } } } } static final class ChunkParser { long addr; long end; Node[] map; ChunkParser(Node[] m, long a, long e) { map = m; addr = a; end = e; } final boolean ok() { return addr < end; } final long word() { return UNSAFE.getLong(addr); } final void skip(int n) { addr += n; } final void skip(long n) { addr += n; } final long val0() { long w = word(); int d = getDotCode(w); return num(w, d); } final long val() { long w = word(); int d = getDotCode(w); skip((d >>> 3) + 3); return num(w, d); } // optimize for contest // save as much slow memory access as possible // about 50% key < 8chars, 25% key bettween 8-10 chars // keylength histogram (%) = [0, 0, 0, 0, 4, 10, 21, 15, 13, 11, 6, 6, 4, 2... final Node key(long word0, long semipos_code) { long row_addr = addr; // about 50% chance key < 8 chars if (semipos_code != 0) { int semi_pos = Long.numberOfTrailingZeros(semipos_code) >>> 3; skip(semi_pos + 1); long tail = word0 & HASH_MASKS[semi_pos]; long hash = mix(tail); int bucket = (int) (hash & BUCKET_MASK); while (true) { Node node = map[bucket]; if (node == null) { return (map[bucket] = new Node(row_addr, hash, semi_pos)); } if (node.hash == hash) { return node; } bucket++; } } skip(8); long word = UNSAFE.getLong(addr); semipos_code = getSemiCode(word); // 43% chance if (semipos_code != 0) { int semi_pos = Long.numberOfTrailingZeros(semipos_code) >>> 3; skip(semi_pos + 1); long tail = word0 ^ (word & HASH_MASKS[semi_pos]); long hash = mix(tail); int bucket = (int) (hash & BUCKET_MASK); while (true) { Node node = map[bucket]; if (node == null) { return (map[bucket] = new Node(row_addr, word0, hash, semi_pos + 8)); } if (node.word0 == word0 && node.hash == hash) { return node; } bucket++; } } // why not going for more? tested, slower long hash = word0; while (semipos_code == 0) { hash ^= word; skip(8); word = UNSAFE.getLong(addr); semipos_code = getSemiCode(word); } int semi_pos = Long.numberOfTrailingZeros(semipos_code) >>> 3; skip(semi_pos); long keylen = addr - row_addr; skip(1); long tail = hash ^ (word & HASH_MASKS[semi_pos]); hash = mix(tail); int bucket = (int) (hash & BUCKET_MASK); while (true) { Node node = map[bucket]; if (node == null) { return (map[bucket] = new Node(row_addr, word0, hash, (int) keylen)); } if (node.contentEquals(row_addr, word0, hash, keylen)) { return node; } bucket++; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_abfrmblr.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.util.concurrent.*; import java.util.stream.Stream; import static java.nio.file.Files.lines; import static java.nio.file.Paths.*; public class CalculateAverage_abfrmblr { private static final String FILE = "./measurements.txt"; private static record Stats (double min, double max, double mean, long count) { @Override public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static record MeasurementTuple (String station, double temp) { public MeasurementTuple (String[] values) { this(values[0], Double.parseDouble(values[1])); } } public static void main(String[] args) throws IOException { Stream lines = lines(get(FILE)); ConcurrentMap aggregatedStats = new ConcurrentSkipListMap<>(); lines.parallel().forEach(s -> { MeasurementTuple tuple = new MeasurementTuple(s.split(";")); aggregatedStats.compute(tuple.station(), (s1, stats) -> { if (stats == null) { return new Stats(tuple.temp, tuple.temp, tuple.temp, 1L); } else { long latestCount = stats.count + 1; double min = Math.min(stats.min, tuple.temp); double max = Math.max(stats.max, tuple.temp); double mean = ((stats.mean * stats.count) + tuple.temp) / latestCount; return new Stats(min, max, mean, latestCount); } }); }); System.out.println(aggregatedStats); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_adriacabeza.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * This class calculates average measurements from a file in a parallelized manner. */ public class CalculateAverage_adriacabeza { private static final Path FILE_PATH = Paths.get("./measurements.txt"); public static final int CITY_NAME_MAX_CHARACTERS = 128; private static final int N_PROCESSORS = Runtime.getRuntime().availableProcessors(); private static final int DJB2_INIT = 5381; private static final Map cityMap = new ConcurrentHashMap<>(10_000, 1, N_PROCESSORS); /** * Represents result containing a HashMap with city as key and ResultRow as value. */ private static class Result { public void addStation(int hash, int value) { resultMap.put(hash, new StationData(value)); } public StationData getData(int hash) { return resultMap.get(hash); } private static class StationData { private int min, sum, count, max; public StationData(int value) { this.count = 1; this.sum = value; this.min = value; this.max = value; } public void update(int value) { this.count++; this.sum += value; this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); } public String toString() { return "%.1f/%.1f/%.1f".formatted(min / 10.0, sum / 10.0 / count, max / 10.0); } } private final Map resultMap; public Result() { this.resultMap = new HashMap<>(10_000, 1); } public Map getResultMap() { return resultMap; } public void merge(Result other) { other.getResultMap().forEach((city, resultRow) -> resultMap.merge(city, resultRow, (existing, incoming) -> { existing.min = Math.min(existing.min, incoming.min); existing.max = Math.max(existing.max, incoming.max); existing.sum += incoming.sum; existing.count += incoming.count; return existing; })); } public String toString() { return this.resultMap.entrySet().parallelStream() .map(entry -> "%s=%s".formatted(cityMap.get(entry.getKey()), entry.getValue())) .sorted(Comparator.comparing(s -> s.split("=")[0])) .collect(Collectors.joining(", ", "{", "}")); } } /** * Finds the ending position in the file, ensuring it ends at the beginning of a line. * * @param channel File channel * @param position Current position in the file * @return Ending position at the beginning of a line * @throws IOException If an I/O error occurs */ private static long findEndPosition(FileChannel channel, long position) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1); // Iterate over the file from the given position to find the next newline character while (position < channel.size()) { channel.read(buffer, position); // Check if the current byte is a newline character if (buffer.get(0) == '\n') { return position + 1; // Return the position immediately after the newline } position++; buffer.clear(); } return channel.size(); // Return the end of the file if no newline is found after the current position } /** * Gets the mapped byte buffers for parallel processing. * * @param nProcessors Number of processors for parallelization * @return List of MappedByteBuffers * @throws IOException If an I/O error occurs */ private static List getMappedByteBuffers(int nProcessors) throws IOException { try (FileChannel channel = FileChannel.open(FILE_PATH, StandardOpenOption.READ)) { long fileSize = channel.size(); long chunkSize = (fileSize + nProcessors - 1) / nProcessors; long pos = 0; List buffers = new ArrayList<>(nProcessors); for (int i = 0; i < nProcessors; i++) { long endPosition = findEndPosition(channel, pos + chunkSize); long size = endPosition - pos; MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, pos, size); pos = pos + size; buffers.add(buffer); } return buffers; } } private static int readNumberFromBuffer(ByteBuffer buffer, int limit) { var number = 0; var sign = 1; while (buffer.position() < limit) { var numberByte = buffer.get(); if (numberByte == '-') sign = -1; else if (numberByte == '\n') break; else if (numberByte != '.') number = number * 10 + (numberByte - '0'); } return sign * number; } /** * Calculates average measurements from the file. * * @return Result containing min/mean/max values for each city */ private static Result calculateAverageMeasurements(List chunks) { // Process each buffer in parallel return chunks.parallelStream() .map(buffer -> { Result partialResult = new Result(); var limit = buffer.limit(); var field = new byte[CITY_NAME_MAX_CHARACTERS]; Set seenHashes = new HashSet<>(10_000, 1); while (buffer.position() < limit) { var fieldCurrentIndex = 0; var fieldByte = buffer.get(); field[fieldCurrentIndex++] = fieldByte; // implement djb2 hash: https://theartincode.stanis.me/008-djb2/ int hash = DJB2_INIT; while (buffer.position() < limit) { // hash = hash * 33 + fieldByte hash = (((hash << 5) + hash) + fieldByte); fieldByte = buffer.get(); if (fieldByte == ';') break; field[fieldCurrentIndex++] = fieldByte; } var number = readNumberFromBuffer(buffer, limit); if (!seenHashes.contains(hash)) { seenHashes.add(hash); cityMap.put(hash, new String(field, 0, fieldCurrentIndex)); partialResult.addStation(hash, number); } else { partialResult.getData(hash).update(number); } } return partialResult; }).reduce(new Result(), (partialResult1, partialResult2) -> { Result result = new Result(); result.merge(partialResult1); result.merge(partialResult2); return result; }); } /** * The main method to run the average measurements calculations program. * * @param args Command line arguments. Not utilized in this program. */ public static void main(String[] args) { try { // Get the MappedByteBuffers by splitting the file evenly across available processors var buffers = getMappedByteBuffers(Runtime.getRuntime().availableProcessors()); // Calculate the average measurements from the buffers obtained var measurements = calculateAverageMeasurements(buffers); // Print the measurements result to the console. System.out.println(measurements); } catch (IOException e) { // Handle any potential I/O exceptions by printing the error message to the console System.err.println(STR."Error processing file: \{e.getMessage()}"); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_agoncal.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Iterator; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; /** * This is the solution from GitHut Copilot Chat with the help of Antonio Goncalves (prompting and guiding, but trying not to change code directly on my own, always using Copilot). *

* List of prompts that has been used: *

* ============= * ============= * ============= * v1 - 73603 ms * You are entering The One Billion Row Challenge (1BRC) which is an exploration of how far modern Java can be pushed for aggregating one billion rows from a text file. Grab all the (virtual) threads, reach out to SIMD, optimize the GC, or pull any other trick, and create the fastest implementation for solving this task! * The text file contains temperature values for a range of weather stations. Each row is one measurement in the format ;, with the measurement value having exactly one fractional digit. The following delimited with --- shows ten rows as an example: * --- * Hamburg;12.0 * Bulawayo;8.9 * Palembang;38.8 * St. John's;15.2 * Cracow;12.6 * Bridgetown;26.9 * Istanbul;6.2 * Roseau;34.4 * Conakry;31.2 * Istanbul;23.0 * --- * You have to write a Java program which reads the file, calculates the min, mean, and max temperature value per weather station, and emits the results on stdout like the result below delimited by --- (i.e. sorted alphabetically by station name, and the result values per station in the format //, rounded to one fractional digit). Notice the curly braces: * --- * {Abha=-23.0/18.0/59.2, Abidjan=-16.2/26.0/67.3, Abéché=-10.0/29.4/69.0, Accra=-10.1/26.4/66.4, Addis Ababa=-23.7/16.0/67.0, Adelaide=-27.8/17.3/58.5, ...} * --- * You must use Java 21. * Create an algorithm in any way you see fit including parallelizing the computation, using the (incubating) Vector API, memory-mapping different sections of the file concurrently, using AppCDS, GraalVM, CRaC, etc. for speeding up the application start-up, choosing and tuning the garbage collector, and much more. * No external library dependencies may be used. * ============= * ============= * ============= * (Here I had to chat with Copilot about formatting the output, there were commas missing, the curly brackets were also missed) * ============= * ============= * ============= * v2 - 71831 ms * Being written in Java 21, please use records instead of classes for Measurement. * ============= * ============= * ============= * v3 - 69333 ms * If the temperatures are small numbers, why use double? Can't you use another datatype ? *

* The profiler mentions that this line of code has very bad performance. Can you refactor it so it has better performance: * --- * String[] parts = line.split(";") * --- *

* There is a maximum of 10000 unique station names. Can you optimize the code taking this into account? * ============= * ============= * ============= * v4 - 56417 ms * Which parameters can I pass to the JVM to make it run faster ? * Which GC can I use and what is the most optimized to run CalculateAverage ? */ public class CalculateAverage_agoncal { private static final String FILE = "./measurements.txt"; record Measurement(String station, double temperature) { } static class StationStats { double min; double max; double sum; int count; public StationStats(double temperature) { this.min = temperature; this.max = temperature; this.sum = 0; this.count = 0; } synchronized void update(double temperature) { min = Math.min(min, temperature); max = Math.max(max, temperature); sum += temperature; count++; } double getAverage() { return round(sum) / count; } @Override public String toString() { return String.format("%.1f/%.1f/%.1f", round(min), round(getAverage()), round(max)); } } public static void main(String[] args) throws IOException { Map stats = new ConcurrentHashMap<>(10_000); try (BufferedReader reader = Files.newBufferedReader(Paths.get(FILE))) { reader.lines().parallel().forEach(line -> { int separatorIndex = line.indexOf(';'); String station = line.substring(0, separatorIndex); String temperature = line.substring(separatorIndex + 1); Measurement m = new Measurement(station, Double.parseDouble(temperature)); stats.computeIfAbsent(m.station, k -> new StationStats(m.temperature)).update(m.temperature); }); } TreeMap sortedStats = new TreeMap<>(stats); Iterator> iterator = sortedStats.entrySet().iterator(); System.out.print("{"); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); StationStats s = entry.getValue(); if (iterator.hasNext()) { System.out.printf("%s=%s, ", entry.getKey(), s.toString()); } else { System.out.printf("%s=%s", entry.getKey(), s.toString()); } } System.out.println("}"); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_ags313.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_ags313 { private static final int THREAD_COUNT = 8; private static final int FUTURE_BUFFER = 1024; private static final int ALLOCATION = 128 * 1024 * 1024; // private static final String FILE = "./measurementsCut.txt"; private static final String FILE = "./measurements.txt"; // private static final String FILE = "./src/test/resources/samples/measurements-1.txt"; // private static final String FILE = "./src/test/resources/samples/measurements-20.txt"; private static final int NAME_LENGTH_LIMIT_BYTES = 128; private static final int BUFFER_SIZE = 108; private static final ExecutorService exec = Executors.newFixedThreadPool(THREAD_COUNT); /** * 1B rows * 8s multithreaded * 44s single threaded * * ideas: * 1) replace hashmap with something faster(er) * 2) play with graal **/ private static class Key implements Comparable { private final byte[] value = new byte[NAME_LENGTH_LIMIT_BYTES]; private int hashCode; private int length = 0; // https://stackoverflow.com/questions/20952739/how-would-you-convert-a-string-to-a-64-bit-integer public void accept(byte b) { value[length] = b; length += 1; hashCode = hashCode * 10191 + b; } @Override public boolean equals(Object that) { if (this == that) return true; if (that == null) return false; Key key = (Key) that; // not checking class, nothing else uses this if (hashCode != key.hashCode) return false; if (length != key.length) return false; for (int i = 0; i < length; i++) { if (UNSAFE.getByte(value, i) != UNSAFE.getByte(key.value, i)) { return false; } } return true; } @Override public int hashCode() { return hashCode; } @Override public String toString() { return new String(value, 0, length); } void reset() { length = 0; hashCode = 0; } @Override public int compareTo(Key other) { return toString().compareTo(other.toString()); } } private static HashMap readChunk(FileChannel from, long start, int bytesToRead) throws IOException { HashMap result = new HashMap<>(1024 * 16); var bbuffer = from.map(FileChannel.MapMode.READ_ONLY, start, bytesToRead); var key = new Key(); while (bbuffer.remaining() > 1) { key.reset(); boolean negative = false; var temperature = 0; while (bbuffer.remaining() > 0) { byte b = bbuffer.get(); if (b == ';') { break; } key.accept(b); } loop2: while (bbuffer.remaining() > 0) { byte b = bbuffer.get(); switch (b) { case '-': negative = true; break; case '.': temperature = (temperature * 10) + (bbuffer.get() - '0'); // single decimal break; case '\n': break loop2; default: temperature = (temperature * 10) + (b - '0'); } } int measure = negative ? -temperature : temperature; Stats stats = result.computeIfAbsent(key, c -> new Stats()); if (stats.count == 0) { key = new Key(); } stats.count++; stats.total += measure; if (measure < stats.min) { stats.min = measure; } if (measure > stats.max) { stats.max = measure; } } return result; } public static void main(String[] args) throws Exception { var channel = new RandomAccessFile(FILE, "r").getChannel(); long totalToRead = channel.size(); Future>[] futures = new Future[FUTURE_BUFFER]; long allocated = 0; int chunkCounter = 0; while (allocated < totalToRead) { var start = allocated; var bytesToReadInPass = Math.min(totalToRead - allocated, ALLOCATION); if (bytesToReadInPass < BUFFER_SIZE) { // System.out.println("Want to read: " + bytesToReadInPass + ", starting buffer at: " + startBufferAt); // System.out.println("Total: " + totalToRead + ", allocated: " + allocated + ", allocating: " + (correctedBytesToRead)); futures[chunkCounter++] = exec.submit(() -> readChunk(channel, start, (int) bytesToReadInPass)); allocated += bytesToReadInPass; } else { var startBufferAt = Math.max(0, allocated + bytesToReadInPass - BUFFER_SIZE); var buffer = channel.map(FileChannel.MapMode.READ_ONLY, startBufferAt, BUFFER_SIZE); // System.out.println("Want to read: " + bytesToReadInPass + ", starting buffer at: " + startBufferAt); var endOfLine = 0; for (int i = 0; i < BUFFER_SIZE; i++) { if (buffer.get() == '\n') { endOfLine = i; break; } } long correctedBytesToRead = bytesToReadInPass - BUFFER_SIZE + endOfLine; // System.out.println("Total: " + totalToRead + ", allocated: " + allocated + ", allocating: " + (correctedBytesToRead)); futures[chunkCounter++] = exec.submit(() -> readChunk(channel, start, (int) correctedBytesToRead)); allocated += correctedBytesToRead + 1; } } var accumulator = new HashMap(1024 * 16); for (int i = 0; i < chunkCounter; i++) { var aMap = futures[i].get(); aMap.forEach((key, value) -> accumulator.computeIfAbsent(key, __ -> new Stats()).merge(value)); } exec.shutdown(); System.out.println(new TreeMap(accumulator)); } static class Stats { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long total; private int count; Stats() { } @Override public String toString() { return BigDecimal.valueOf(min / (10.0)).setScale(1, RoundingMode.HALF_UP) + "/" + BigDecimal.valueOf(total / (10.0 * count)).setScale(1, RoundingMode.HALF_UP) + '/' + BigDecimal.valueOf(max / (10.0)).setScale(1, RoundingMode.HALF_UP); } void merge(Stats that) { max = Math.max(this.max, that.max); min = Math.min(this.min, that.min); total += that.total; count += that.count; } } private static final Unsafe UNSAFE = unsafe(); private static Unsafe unsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_albertoventurini.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.EOFException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * == File reading == * The file is read using RandomAccessFile, and split into chunks. Each thread is assigned a chunk. * E.g. if the file size is 100, and we have two threads, the first thread will read from 0 to 49, * the second from 50 to 99. * Each chunk is aligned to the next end-of-line (or to the end-of-file), so that each thread * consumes full input lines. * Further, each file chunk is split into smaller pieces (byte arrays), with each piece up to 2^22 bytes. * This particular size seems to work best on my machine. * == Data structure == * Each thread stores its results in a prefix tree (trie). Each node in the trie represents * one byte of a location's name. Non-ASCII characters are represented by multiple nodes in the trie. * Each leaf contains the statistics for a location. */ public class CalculateAverage_albertoventurini { // The maximum byte that can ever appear in a UTF-8-encoded string is 11110111, i.e., 0xF7 private static final int MAX_UTF8_BYTE_VALUE = 0xF7; // Define a prefix tree that is used to store results. // Each node in the trie represents a byte (NOT character) from a location name. // A nice side effect is, when traversing the trie to print results, // the names will be printed in alphabetical order. private static final class TrieNode { final TrieNode[] children = new TrieNode[MAX_UTF8_BYTE_VALUE]; int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; int sum; int count; } private static final int TWO_BYTE_TO_INT = 480 + 48; private static final int THREE_BYTE_TO_INT = 4800 + 480 + 48; // Process a chunk and write results in a Trie rooted at 'root'. private static void processChunk(final TrieNode root, final ChunkReader cr) { while (cr.ensureHasMoreRows()) { TrieNode node = root; // Process the location name navigating through the trie int b = cr.getNext(); do { b &= 0xFF; if (node.children[b] == null) { node.children[b] = new TrieNode(); } node = node.children[b]; b = cr.getNext(); } while (b != ';'); // Process the reading value (temperature) final int reading; final byte b1 = cr.getNext(); final byte b2 = cr.getNext(); if (b2 == '.') { // value is n.n reading = (b1 * 10 + cr.getNext() - TWO_BYTE_TO_INT); } else { final byte b3 = cr.getNext(); final byte b4 = cr.getNext(); if (b4 == '.') { // value is -nn.n reading = -(b2 * 100 + b3 * 10 + cr.getNext() - THREE_BYTE_TO_INT); } else if (b1 == '-') { // value is -n.n reading = -(b2 * 10 + b4 - TWO_BYTE_TO_INT); } else { // value is nn.n reading = (b1 * 100 + b2 * 10 + b4 - THREE_BYTE_TO_INT); } } cr.cursor++; // new line if (reading < node.min) { node.min = reading; } if (reading > node.max) { node.max = reading; } node.sum += reading; node.count++; } } // Print results. // Because there are multiple tries (one for each thread), this method // aggregates results from all tries. static class ResultPrinter { // Contains the bytes for the current location name. 100 bytes should be enough // to represent each location name encoded in UTF-8. final byte[] bytes = new byte[100]; boolean firstOutput = true; void printResults(final TrieNode[] roots) { System.out.print("{"); printResultsRec(roots, bytes, 0); System.out.println("}"); } private static double round(long value) { return Math.round(value) / 10.0; } // Find and print results recursively. private void printResultsRec(final TrieNode[] nodes, final byte[] bytes, final int index) { long min = Long.MAX_VALUE; long max = Long.MIN_VALUE; long sum = 0; long count = 0; for (final TrieNode node : nodes) { if (node != null && node.count > 0) { min = Math.min(min, node.min); max = Math.max(max, node.max); sum += node.sum; count += node.count; } } if (count > 0) { final String location = new String(bytes, 0, index); if (firstOutput) { firstOutput = false; } else { System.out.print(", "); } double mean = Math.round((double) sum / (double) count) / 10.0; System.out.print(location + "=" + round(min) + "/" + mean + "/" + round(max)); } for (int i = 0; i < MAX_UTF8_BYTE_VALUE; i++) { final TrieNode[] childNodes = new TrieNode[nodes.length]; boolean shouldRecurse = false; for (int j = 0; j < nodes.length; j++) { if (nodes[j] != null && nodes[j].children[i] != null) { childNodes[j] = nodes[j].children[i]; // Only recurse if there's at least one trie that has non-null child for index 'i'. shouldRecurse = true; } } if (shouldRecurse) { bytes[index] = (byte) i; printResultsRec(childNodes, bytes, index + 1); } } } } private static final String FILE = "./measurements.txt"; /** * Read a chunk of a {@link RandomAccessFile} file. * Internally, the chunk is further subdivided into "sub-chunks" (byte arrays). */ private static final class ChunkReader { // Byte arrays of size 2^20 seem to have the best performance on my machine. private static final int BYTE_ARRAY_SIZE = 1 << 20; private final byte[] bytes; private final RandomAccessFile file; // The initial position of this chunk. private final long chunkBegin; // The length of this chunk. private final long chunkLength; // The beginning of the current "sub-chunk", relative to the initial position of the chunk. private long offset = 0; // The size of the current "sub-chunk". private int subChunkSize = 0; // The current position within the current "sub-chunk". private int cursor = 0; // The maximum size of a row private static final int MAX_ROW_SIZE_BYTES = 107; ChunkReader( final RandomAccessFile file, final long chunkBegin, final long chunkLength) { this.file = file; this.chunkBegin = chunkBegin; this.chunkLength = chunkLength; int byteArraySize = chunkLength < BYTE_ARRAY_SIZE ? (int) chunkLength : BYTE_ARRAY_SIZE; this.bytes = new byte[byteArraySize]; readSubChunk(); } // Return true if this ChunkReader has more bytes available, false otherwise. // If this ChunkReader needs to read a new "sub-chunk", it does so in this method. boolean ensureHasMoreRows() { if (cursor >= subChunkSize) { offset += cursor; if (offset >= chunkLength) { return false; } readSubChunk(); } return true; } byte getNext() { return bytes[cursor++]; } private void readSubChunk() { try { synchronized (file) { file.seek(chunkBegin + offset); subChunkSize = file.read(bytes); } } catch (IOException e) { throw new RuntimeException(e); } // Always "pretend" that we've read a few bytes less, // so that we don't stop in the middle of reading a row subChunkSize -= MAX_ROW_SIZE_BYTES; cursor = 0; } } private static ChunkReader[] makeChunkReaders( final int count, final RandomAccessFile file) throws Exception { final ChunkReader[] chunkReaders = new ChunkReader[count]; // The total size of each chunk final long chunkReaderSize = file.length() / count; long previousPosition = 0; long currentPosition; for (int i = 0; i < count; i++) { // Go to the end of the chunk file.seek(chunkReaderSize * (i + 1)); // Align to the next end of line or end of file try { while (file.readByte() != '\n') ; } catch (EOFException e) { } currentPosition = file.getFilePointer(); long chunkBegin = previousPosition; long chunkLength = currentPosition - previousPosition; chunkReaders[i] = new ChunkReader(file, chunkBegin, chunkLength); previousPosition = currentPosition; } return chunkReaders; } // Spin up threads and assign a file chunk to each one. // Then use the 'ResultPrinter' class to aggregate and print the results. private static void processWithChunkReaders() throws Exception { final var randomAccessFile = new RandomAccessFile(FILE, "r"); final int nThreads = randomAccessFile.length() < 1 << 20 ? 1 : Runtime.getRuntime().availableProcessors(); final CountDownLatch latch = new CountDownLatch(nThreads); final ChunkReader[] chunkReaders = makeChunkReaders(nThreads, randomAccessFile); final TrieNode[] roots = new TrieNode[nThreads]; for (int i = 0; i < nThreads; i++) { roots[i] = new TrieNode(); } final ExecutorService executorService = Executors.newFixedThreadPool(nThreads); for (int i = 0; i < nThreads; i++) { final int idx = i; executorService.submit(() -> { processChunk(roots[idx], chunkReaders[idx]); latch.countDown(); }); } executorService.shutdown(); latch.await(); new ResultPrinter().printResults(roots); executorService.close(); } public static void main(String[] args) throws Exception { processWithChunkReaders(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_alesj.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.nio.file.Files; import java.nio.file.Paths; import java.util.DoubleSummaryStatistics; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; public class CalculateAverage_alesj { public static void main(String[] args) throws Exception { Map stations = new ConcurrentHashMap<>(); try (Stream stream = Files.lines(Paths.get("./measurements.txt")).parallel()) { stream.forEach(line -> { String[] split = line.split(";"); stations.computeIfAbsent(split[0], k -> new DoubleSummaryStatistics() { public synchronized void accept(double value) { super.accept(value); } public String toString() { return String.format("%.1f/%.1f/%.1f", getMin(), getAverage(), getMax()); } }) .accept(Double.parseDouble(split[1])); }); } System.out.println(new TreeMap<>(stations)); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_algirdasrascius.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; public class CalculateAverage_algirdasrascius { private static final String FILE = "./measurements.txt"; private static final int MAX_NAMES = 10000; private static final int MAP_ENTRY_COUNT = 20011; // Prime exceeding double MAX_NAMES count private static final int MAX_NAME_LENGTH_IN_CHARS = 100; private static final int MAX_NAME_LENGTH_IN_BYTES = MAX_NAME_LENGTH_IN_CHARS * 4; private static final int NAME_BUFFER_LENGTH = MAX_NAMES * MAX_NAME_LENGTH_IN_BYTES; private static class AggregatorMap { private final AggregatorMapEntry[] entries = new AggregatorMapEntry[MAP_ENTRY_COUNT]; private final byte[] nameBuffer = new byte[NAME_BUFFER_LENGTH]; private int nameBufferEnd = 0; void add(byte[] buffer, int nameStart, int nameEnd, int nameHash, short value) { getEntry(buffer, nameStart, nameEnd, nameHash).accumulate(value); } void combineWith(AggregatorMap other) { for (int i = 0; i < MAP_ENTRY_COUNT; i++) { AggregatorMapEntry entry = other.entries[i]; while (entry != null) { getEntry(other.nameBuffer, entry.nameStart, entry.nameEnd, entry.nameHash).combineWith(entry); entry = entry.nextEntry; } } } void printResult() { Map sortedMeasurements = new TreeMap<>(); for (int i = 0; i < MAP_ENTRY_COUNT; i++) { AggregatorMapEntry entry = entries[i]; while (entry != null) { String name = new String(nameBuffer, entry.nameStart, entry.nameEnd - entry.nameStart, StandardCharsets.UTF_8); sortedMeasurements.put(name, entry.result()); entry = entry.nextEntry; } } System.out.println(sortedMeasurements); } private AggregatorMapEntry getEntry(byte[] buffer, int nameStart, int nameEnd, int nameHash) { int index = (nameHash & 0x7FFFFFFF) % MAP_ENTRY_COUNT; AggregatorMapEntry firstEntry = entries[index]; AggregatorMapEntry entry = firstEntry; while (entry != null && (entry.nameHash != nameHash || !Arrays.equals(buffer, nameStart, nameEnd, nameBuffer, entry.nameStart, entry.nameEnd))) { entry = entry.nextEntry; } if (entry == null) { int entryNameStart = nameBufferEnd; int nameLength = nameEnd - nameStart; System.arraycopy(buffer, nameStart, nameBuffer, entryNameStart, nameLength); nameBufferEnd += nameLength; entry = new AggregatorMapEntry(entryNameStart, nameBufferEnd, nameHash, firstEntry); entries[index] = entry; } return entry; } } private static class AggregatorMapEntry { private final int nameStart; private final int nameEnd; private final int nameHash; private final AggregatorMapEntry nextEntry; private short min = Short.MAX_VALUE; private short max = Short.MIN_VALUE; private long sum; private int count; public AggregatorMapEntry(int nameStart, int nameEnd, int nameHash, AggregatorMapEntry nextEntry) { this.nameStart = nameStart; this.nameEnd = nameEnd; this.nameHash = nameHash; this.nextEntry = nextEntry; } void accumulate(short value) { if (min > value) { min = value; } if (max < value) { max = value; } sum += value; count++; } void combineWith(AggregatorMapEntry other) { if (min > other.min) { min = other.min; } if (max < other.max) { max = other.max; } sum += other.sum; count += other.count; } ResultRow result() { // return new ResultRow(min, (short) ((sum + count / 2) / count), max); return new ResultRow(min, (short) Math.round(((double) sum) / count), max); } } private record ResultRow(short min, short mean, short max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private String round(short value) { return value >= 0 ? ((value / 10) + "." + (value % 10)) : ("-" + (-value / 10) + "." + (-value % 10)); } } private static final int READ_CHUNK_SIZE = 1024 * 1024; private static final int MAX_LINE_SIZE = MAX_NAME_LENGTH_IN_BYTES + 10; private static final int READ_BUFFER_SIZE = READ_CHUNK_SIZE + MAX_LINE_SIZE; private static class ReaderTask implements Callable { private final AggregatorMap aggregatorMap = new AggregatorMap(); private final byte[] buffer = new byte[READ_BUFFER_SIZE]; private final ByteBuffer byteBuffer = ByteBuffer.wrap(buffer); private final FileChannel channel; private final AtomicLong nextChunkPosition; public ReaderTask(FileChannel channel, AtomicLong nextChunkPosition) { this.channel = channel; this.nextChunkPosition = nextChunkPosition; } @Override public AggregatorMap call() throws Exception { while (processChunk()) { } return aggregatorMap; } private boolean processChunk() throws IOException { long channelPosition = nextChunkPosition.getAndAdd(READ_CHUNK_SIZE); int endIndex = 0; byteBuffer.rewind(); do { int bytesRead = channel.read(byteBuffer, channelPosition + endIndex); if (bytesRead < 0) { break; } endIndex += bytesRead; } while (endIndex < READ_CHUNK_SIZE); int index = 0; // If this is not the first chunk skip till first line end if (channelPosition != 0) { byte v; while (index < endIndex && buffer[index] != '\n') { index++; } index++; } if (endIndex > READ_CHUNK_SIZE + 1) { endIndex = READ_CHUNK_SIZE + 1; } while (index < endIndex) { index = processLine(index); } return endIndex > READ_CHUNK_SIZE; } private int processLine(int index) { // Read station name byte v; int nameStart = index; int nameHash = 0; while ((v = buffer[index]) != ';') { index++; nameHash = 31 * nameHash + v; } int nameEnd = index; // Skip ; index++; // Read temperature value v = buffer[index++]; boolean negative = false; if (v == '-') { negative = true; v = buffer[index++]; } int value = v - '0'; v = buffer[index++]; if (v != '.') { value = value * 10 + (v - '0'); index++; } v = buffer[index++]; value = value * 10 + (v - '0'); if (negative) { value = -value; } // Skip line feed index++; // System.out.println(new String(buffer, nameStart, nameEnd - nameStart) + " = " + value); aggregatorMap.add(buffer, nameStart, nameEnd, nameHash, (short) value); return index; } } public static void main(String[] args) throws Exception { try (FileInputStream stream = new FileInputStream(FILE)) { FileChannel channel = stream.getChannel(); AtomicLong nextChunkPosition = new AtomicLong(0L); int threadCount = Runtime.getRuntime().availableProcessors() * 2; ExecutorService executorService = Executors.newFixedThreadPool(threadCount); ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService); for (int i = 0; i < threadCount; i++) { completionService.submit(new ReaderTask(channel, nextChunkPosition)); } AggregatorMap aggregatorMap = completionService.take().get(); for (int i = 1; i < threadCount; i++) { aggregatorMap.combineWith(completionService.take().get()); } aggregatorMap.printResult(); executorService.close(); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_anandmattikopp.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; public class CalculateAverage_anandmattikopp { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException { Map stationStatisticsMap = Files.lines(Paths.get(FILE)).parallel() .map(entry -> { String[] tokens = entry.split(";"); return new Station(tokens[0], Double.parseDouble(tokens[1])); }) .collect( Collectors.toConcurrentMap( station -> station.stationName, station -> new StationStatistics(station), StationStatistics::merge)); System.out.println(new TreeMap<>(stationStatisticsMap)); } private record Station(String stationName, double temperature) { } private record StationStatistics(String stationName, double minTemp, double meanTemp, double maxTemp, long totalCount) { StationStatistics(Station station) { //Calling canonical constructor this(station.stationName, station.temperature, station.temperature, station.temperature, 1); } //Merging two stats to create new stats public static StationStatistics merge(StationStatistics stats1, StationStatistics stats2) { assert stats1.stationName.equals(stats2.stationName); return new StationStatistics( stats1.stationName, Math.min(stats1.minTemp, stats2.minTemp), // minimum of both the temp (stats1.meanTemp * stats1.totalCount + stats2.meanTemp * stats2.totalCount) / (stats1.totalCount + stats2.totalCount), // average of both the average temps from stats Math.max(stats1.maxTemp, stats2.maxTemp), // maximum of both the temp stats1.totalCount + stats2.totalCount // increment the totalCount ); } @Override public String toString() { return round(minTemp) + "/" + round(meanTemp) + "/" + round(maxTemp); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_anestoruk.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static java.lang.Math.ceil; import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.Runtime.getRuntime; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.concurrent.CompletableFuture.supplyAsync; public class CalculateAverage_anestoruk { private static final String path = "./measurements.txt"; private static final int cpus = getRuntime().availableProcessors(); public static void main(String[] args) throws IOException { List rangeList = new ArrayList<>(); MemorySegment segment; try (FileChannel channel = FileChannel.open(Path.of(path))) { final long fileSize = channel.size(); final long chunkSize = calculateChunkSize(fileSize); final int chunks = (int) ceil((double) fileSize / chunkSize); segment = channel.map(READ_ONLY, 0, fileSize, Arena.global()); long startOffset = 0; long size = chunkSize; for (int i = 0; i < chunks && size > 0; i++) { long endOffset = startOffset + size; while (endOffset < fileSize && segment.get(JAVA_BYTE, endOffset) != '\n') { endOffset++; } rangeList.add(new SegmentRange(startOffset, endOffset)); startOffset = endOffset + 1; size = min(chunkSize, fileSize - startOffset); } } TreeMap result = new TreeMap<>(); try (ExecutorService executor = Executors.newFixedThreadPool(cpus)) { List> futures = new ArrayList<>(); for (SegmentRange range : rangeList) { futures.add(supplyAsync(() -> process(range, segment), executor)); } for (CompletableFuture future : futures) { try { Record[] partialResult = future.get(); mergeResult(result, partialResult); } catch (InterruptedException | ExecutionException ex) { throw new RuntimeException(ex); } } } System.out.println(result); } private static long calculateChunkSize(long fileSize) { int divisor = cpus; long chunkSize; if (fileSize > 10_000) { while ((chunkSize = fileSize / divisor) > Integer.MAX_VALUE - 512) { divisor *= 2; } return chunkSize; } return fileSize; } private static Record[] process(SegmentRange range, MemorySegment segment) { Record[] records = new Record[1024 * 100]; byte[] cityBuffer = new byte[100]; long offset = range.startOffset; byte b; while (offset < range.endOffset) { int cityLength = 0; int hash = 0; while ((b = segment.get(JAVA_BYTE, offset++)) != ';') { cityBuffer[cityLength++] = b; hash = hash * 31 + b; } hash = Math.abs(hash); int value = 0; boolean negative; if ((b = segment.get(JAVA_BYTE, offset++)) == '-') { negative = true; } else { negative = false; value = b - '0'; } while ((b = segment.get(JAVA_BYTE, offset++)) != '\n') { if (b != '.') { value = value * 10 + (b - '0'); } } int temperature = negative ? -value : value; addRecord(records, hash, cityBuffer, cityLength, temperature); } return records; } private static void addRecord(Record[] records, int hash, byte[] cityBuffer, int cityLength, int temperature) { int idx = hash % records.length; Record record; while ((record = records[idx]) != null) { if (record.hash == hash && Arrays.equals(record.city, 0, record.city.length, cityBuffer, 0, cityLength)) { record.add(temperature); return; } idx = (idx + 1) % records.length; } byte[] city = new byte[cityLength]; System.arraycopy(cityBuffer, 0, city, 0, cityLength); records[idx] = new Record(hash, city, temperature); } private static void mergeResult(TreeMap result, Record[] partialResult) { for (Record partialRecord : partialResult) { if (partialRecord == null) { continue; } String cityName = new String(partialRecord.city, UTF_8); result.compute(cityName, (_, record) -> { if (record == null) { return partialRecord; } record.merge(partialRecord); return record; }); } } private record SegmentRange(long startOffset, long endOffset) { } private static class Record { private final int hash; private final byte[] city; private int min; private int max; private long sum; private int count; public Record(int hash, byte[] city, int temperature) { this.hash = hash; this.city = city; this.min = temperature; this.max = temperature; this.sum = temperature; this.count = 1; } public void add(int temperature) { min = min(min, temperature); max = max(max, temperature); sum += temperature; count++; } public void merge(Record other) { min = min(min, other.min); max = max(max, other.max); sum += other.sum; count += other.count; } @Override public String toString() { return "%.1f/%.1f/%.1f".formatted( (min / 10.0), ((double) sum / count / 10.0), (max / 10.0)); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_anitasv.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.stream.IntStream; public class CalculateAverage_anitasv { private static final String FILE = "./measurements.txt"; private record Shard(MemorySegment mmapMemory, long chunkStart, long chunkEnd) { byte getByte(long address) { return mmapMemory.get(ValueLayout.JAVA_BYTE, address); } long indexOf(long position, byte ch) { ByteBuffer buf = mmapMemory.asSlice(position, Math.min(128, mmapMemory.byteSize() - position)) .asByteBuffer(); while (buf.hasRemaining()) { if (buf.get() == ch) { return position + (buf.position() - 1); } } return -1; } MemorySegment getRange(long start, long end) { return mmapMemory.asSlice(start, end - start); } int parseDouble(long start, long end) { int normalized = 0; boolean sign = true; long index = start; if (getByte(index) == '-') { index++; sign = false; } boolean hasDot = false; for (; index < end; index++) { byte ch = getByte(index); if (ch != '.') { normalized = normalized * 10 + (ch - '0'); } else { hasDot = true; } } if (!hasDot) { normalized *= 10; } if (!sign) { normalized = -normalized; } return normalized; } public int computeHash(long position, long stationEnd) { ByteBuffer buf2 = mmapMemory.asSlice(position, stationEnd - position) .asByteBuffer(); return buf2.hashCode(); } public long truncate(long index) { return Math.min(index, mmapMemory.byteSize()); } public long getLong(long position) { return mmapMemory.get(ValueLayout.JAVA_LONG_UNALIGNED, position); } } private record ResultRow(IntSummaryStatistics statistics, int keyLength, int next) { } private static class FastHashMap { private final byte[] keys; private final ResultRow[] values; private final int capacityMinusOne; private final MemorySegment keySegment; private int next = -1; private FastHashMap(int capacity) { this.capacityMinusOne = capacity - 1; this.keys = new byte[capacity << 7]; this.keySegment = MemorySegment.ofArray(keys); this.values = new ResultRow[capacity]; } IntSummaryStatistics find(int hash, Shard shard, long stationStart, long stationEnd) { int initialIndex = hash & capacityMinusOne; int lookupLength = (int) (stationEnd - stationStart); int lookupAligned = ((lookupLength + 7) & (-8)); int i = initialIndex; lookupAligned = (int) (shard.truncate(stationStart + lookupAligned) - stationStart) - 7; do { int keyIndex = i << 7; if (keys[keyIndex] != 0 && keys[keyIndex + lookupLength] == 0) { int mismatch = -1, j; for (j = 0; j < lookupAligned; j += 8) { long entryLong = keySegment.get(ValueLayout.JAVA_LONG_UNALIGNED, keyIndex + j); long lookupLong = shard.getLong(stationStart + j); if (entryLong != lookupLong) { int diff = Long.numberOfTrailingZeros(entryLong ^ lookupLong); mismatch = j + (diff >> 3); break; } } if (mismatch == -1) { for (; j < lookupLength; j++) { byte entryByte = keys[keyIndex + j]; byte lookupByte = shard.getByte(stationStart + j); if (entryByte != lookupByte) { mismatch = j; break; } } } if (mismatch == -1 || mismatch >= lookupLength) { return this.values[i].statistics; } } if (keys[keyIndex] == 0) { MemorySegment fullLookup = shard.getRange(stationStart, stationEnd); keySegment.asSlice(keyIndex, lookupLength) .copyFrom(fullLookup); keys[keyIndex + lookupLength] = 0; IntSummaryStatistics stats = new IntSummaryStatistics(); ResultRow resultRow = new ResultRow(stats, lookupLength, this.next); this.next = i; this.values[i] = resultRow; return stats; } if (i == capacityMinusOne) { i = 0; } else { i++; } } while (i != initialIndex); throw new IllegalStateException("Hash size too small"); } Iterable> values() { return () -> new Iterator<>() { int scan = FastHashMap.this.next; @Override public boolean hasNext() { return scan != -1; } @Override public Map.Entry next() { ResultRow resultRow = values[scan]; IntSummaryStatistics stats = resultRow.statistics; String key = new String(keys, scan << 7, resultRow.keyLength, StandardCharsets.UTF_8); scan = resultRow.next; return new AbstractMap.SimpleEntry<>(key, stats); } }; } } private static Iterable> process(Shard shard) { FastHashMap result = new FastHashMap(1 << 14); boolean skip = shard.chunkStart != 0; for (long position = shard.chunkStart; position < shard.chunkEnd; position++) { if (skip) { position = shard.indexOf(position, (byte) '\n'); skip = false; } else { long stationEnd = shard.indexOf(position, (byte) ';'); int hash = shard.computeHash(position, stationEnd); long temperatureEnd = shard.indexOf(stationEnd + 1, (byte) '\n'); int temperature = shard.parseDouble(stationEnd + 1, temperatureEnd); IntSummaryStatistics stats = result.find(hash, shard, position, stationEnd); stats.accept(temperature); position = temperatureEnd; } } return result.values(); } private static Map combineResults(List>> list) { Map output = HashMap.newHashMap(1024); for (Iterable> map : list) { for (Map.Entry entry : map) { output.compute(entry.getKey(), (ignore, val) -> { if (val == null) { return entry.getValue(); } else { val.combine(entry.getValue()); return val; } }); } } return output; } private static Map master(MemorySegment mmapMemory) { long totalBytes = mmapMemory.byteSize(); int numWorkers = Runtime.getRuntime().availableProcessors(); long chunkSize = Math.ceilDiv(totalBytes, numWorkers); return combineResults(IntStream.range(0, numWorkers) .parallel() .mapToObj(workerId -> { long chunkStart = workerId * chunkSize; long chunkEnd = Math.min(chunkStart + chunkSize + 1, totalBytes); return new Shard(mmapMemory, chunkStart, chunkEnd); }) .map(CalculateAverage_anitasv::process) .toList()); } public static Map start() throws IOException { try (FileChannel fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { long fileSize = fileChannel.size(); MemorySegment mmapMemory = fileChannel.map( FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); return master(mmapMemory); } } private static Map toPrintMap(Map output) { Map outputStr = new TreeMap<>(); for (Map.Entry entry : output.entrySet()) { IntSummaryStatistics stat = entry.getValue(); outputStr.put(entry.getKey(), statToString(stat)); } return outputStr; } private static String statToString(IntSummaryStatistics stat) { return STR."\{stat.getMin() / 10.0}/\{Math.round(stat.getAverage()) / 10.0}/\{stat.getMax() / 10.0}"; } public static void main(String[] args) throws IOException { System.out.println(toPrintMap(start())); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_arjenvaneerde.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.*; public class CalculateAverage_arjenvaneerde { static class Measure { byte[] station; int length; int count; int minTemp; int maxTemp; long sumTemp; Measure(final byte[] station, int count, int minTemp, int maxTemp, long sumTemp) { this.station = station; this.length = station.length; this.count = count; this.minTemp = minTemp; this.maxTemp = maxTemp; this.sumTemp = sumTemp; } Measure(byte[] bytes, int startPos, int endPos, int temp) { this.length = endPos - startPos; this.station = new byte[this.length]; System.arraycopy(bytes, startPos, this.station, 0, this.station.length); this.count = 1; this.minTemp = temp; this.maxTemp = temp; this.sumTemp = temp; } void add(int temp) { this.count++; this.minTemp = Math.min(this.minTemp, temp); this.maxTemp = Math.max(this.maxTemp, temp); this.sumTemp += temp; } public static Measure merge(Measure m1, Measure m2) { return new Measure(m1.station, m1.count + m2.count, Math.min(m1.minTemp, m2.minTemp), Math.max(m1.maxTemp, m2.maxTemp), m1.sumTemp + m2.sumTemp); } public String toString() { return String.format("%.1f/%.1f/%.1f", this.minTemp / 10.0, this.sumTemp / (10.0 * this.count), this.maxTemp / 10.0); } } static class Measures { static final int HASH_TABLE_SIZE = 8 * 1024; Measure[][] measureHashTable; Measures() { this.measureHashTable = new Measure[HASH_TABLE_SIZE][20]; } void add(byte[] bytes, int startPos, int endPos, int temp) { int len = endPos - startPos; int index = ((len - 2) & 0x0f | // 4 bits of the length ((bytes[startPos + 0] & 0x1f) << 4) | // 5 bits of first char ((bytes[startPos + 2] & 0x1f) << 9) // 5 bits of third char // ((bytes[startPos + 1] & 0x1f) << 14) // 5 bits of second char ) & (HASH_TABLE_SIZE - 1); Measure[] arr = this.measureHashTable[index]; int i = 0; boolean found = false; int arrLength = arr.length; while (i < arrLength) { Measure m = arr[i]; if (m == null) { // Not found. Add new entry. arr[i] = new Measure(bytes, startPos, endPos, temp); return; } if (m.length == len) { switch (len) { case 0: break; case 1: if (m.station[0] == bytes[startPos]) { found = true; } break; case 2: if (m.station[1] == bytes[startPos + 1] && m.station[0] == bytes[startPos]) { found = true; } break; case 3: if (m.station[2] == bytes[startPos + 2] && m.station[1] == bytes[startPos + 1] && m.station[0] == bytes[startPos]) { found = true; } break; case 4: if (m.station[3] == bytes[startPos + 3] && m.station[2] == bytes[startPos + 2] && m.station[1] == bytes[startPos + 1] && m.station[0] == bytes[startPos]) { found = true; } break; case 5: if (m.station[4] == bytes[startPos + 4] && m.station[3] == bytes[startPos + 3] && m.station[2] == bytes[startPos + 2] && m.station[1] == bytes[startPos + 1] && m.station[0] == bytes[startPos]) { found = true; } break; case 6: if (m.station[5] == bytes[startPos + 5] && m.station[4] == bytes[startPos + 4] && m.station[3] == bytes[startPos + 3] && m.station[2] == bytes[startPos + 2] && m.station[1] == bytes[startPos + 1] && m.station[0] == bytes[startPos]) { found = true; } break; case 7: if (m.station[6] == bytes[startPos + 6] && m.station[5] == bytes[startPos + 5] && m.station[4] == bytes[startPos + 4] && m.station[3] == bytes[startPos + 3] && m.station[2] == bytes[startPos + 2] && m.station[1] == bytes[startPos + 1] && m.station[0] == bytes[startPos]) { found = true; } break; default: found = Arrays.mismatch(m.station, 0, len, bytes, startPos, endPos) == -1; break; } if (found) { // Add info. m.add(temp); return; } } i++; } // throw new RuntimeException("Reached end of Measures array."); Measure[] newArr = new Measure[arr.length * 2]; System.arraycopy(arr, 0, newArr, 0, arr.length); newArr[i] = new Measure(bytes, startPos, endPos, temp); this.measureHashTable[index] = newArr; } } private static class BytesProcessor implements Runnable { final Measures measures; final byte[] buffer; long absoluteStartPos; int startPos; int endPos; BytesProcessor(byte[] buffer, long absoluteStartPos, int startPos, int endPos, final Measures measures) { this.buffer = buffer; this.absoluteStartPos = absoluteStartPos; this.startPos = startPos; this.endPos = endPos; this.measures = measures; } @Override public void run() { int startOfLinePos = startPos; int endOfLinePos = startOfLinePos; int sepPos; // Process all lines while (endOfLinePos < endPos) { while (buffer[endOfLinePos] != ';') { endOfLinePos++; } sepPos = endOfLinePos; int temperature = 0; byte lineByte; endOfLinePos++; lineByte = buffer[endOfLinePos]; while (lineByte != 0x0A) { if (lineByte >= '0' && lineByte <= '9') { temperature = temperature * 10 + (lineByte - '0'); } lineByte = buffer[++endOfLinePos]; } if (buffer[sepPos + 1] == '-') { temperature = -temperature; } measures.add(buffer, startOfLinePos, sepPos, temperature); startOfLinePos = ++endOfLinePos; } } } private static class MeasuresMergeProcessor implements Runnable { final Measures[] measures; final int startIndex; final int endIndex; final ConcurrentSkipListMap results; MeasuresMergeProcessor(final Measures[] measures, final int startIndex, final int endIndex, final ConcurrentSkipListMap results) { this.measures = measures; this.startIndex = startIndex; this.endIndex = endIndex; this.results = results; } @Override public void run() { for (int mIdx = 0; mIdx < this.measures.length; mIdx++) { for (int hashIdx = this.startIndex; hashIdx < this.endIndex; hashIdx++) { Measure[] mArr = this.measures[mIdx].measureHashTable[hashIdx]; int i = 0; Measure measure = mArr[i]; while (measure != null) { Measure finalMeasure = measure; this.results.compute(new String(measure.station, StandardCharsets.UTF_8), (k, v) -> v == null ? finalMeasure : Measure.merge(v, finalMeasure)); measure = mArr[++i]; } } } } } private static final String FILE = "./measurements.txt"; // private static final String FILE = "./src/test/resources/samples/measurements-1.txt"; // private static final String FILE = "./src/test/resources/samples/measurements-10000-unique-keys.txt"; private static final int NUM_THREADS = 8; // Runtime.getRuntime().availableProcessors(); private static final int BYTE_BUFFER_SIZE = 16 * 1024 * 1024; private static final ExecutorService threads = Executors.newFixedThreadPool(NUM_THREADS); private static final List> futures = new ArrayList<>(NUM_THREADS); private static final Measures[] measures = new Measures[NUM_THREADS]; public static void main(String[] args) throws Exception { long startTime = System.currentTimeMillis(); for (int i = 0; i < NUM_THREADS; i++) { measures[i] = new Measures(); } File file = new File(FILE); try (RandomAccessFile raFile = new RandomAccessFile(file, "r"); FileChannel inChannel = raFile.getChannel()) { ByteBuffer[] byteBuffers = new ByteBuffer[2]; byteBuffers[0] = ByteBuffer.allocate(BYTE_BUFFER_SIZE); byteBuffers[1] = ByteBuffer.allocate(BYTE_BUFFER_SIZE); int currrentByteBuffer = 0; long numBytesRead = 0; long numTotBytesRead = 0; numBytesRead = inChannel.read(byteBuffers[currrentByteBuffer]); byteBuffers[currrentByteBuffer].flip(); while (numBytesRead > 0 || byteBuffers[currrentByteBuffer].position() > 0) { if (numBytesRead > 0) { numTotBytesRead += numBytesRead; } // System.out.println((double) numTotBytesRead/(1024*1024)); // Distribute buffer chunks across threads. int bufferLimit = byteBuffers[currrentByteBuffer].limit(); byte[] fileBytes = byteBuffers[currrentByteBuffer].array(); int chunckSize = bufferLimit / NUM_THREADS; int startOfChunkPos = 0; int endOfChunkPos = 0; for (int chunk = 0; chunk < NUM_THREADS; chunk++) { if (chunk == NUM_THREADS - 1) { endOfChunkPos = bufferLimit - 1; } else { endOfChunkPos = Math.min((chunk + 1) * chunckSize, bufferLimit - 1); } while (endOfChunkPos > startOfChunkPos && fileBytes[endOfChunkPos] != 0x0A) { endOfChunkPos--; } if (endOfChunkPos > startOfChunkPos) { endOfChunkPos++; Future f = threads.submit(new BytesProcessor(fileBytes, numTotBytesRead - numBytesRead, startOfChunkPos, endOfChunkPos, measures[chunk]), chunk); futures.add(f); startOfChunkPos = endOfChunkPos; } } int nextByteBuffer = (currrentByteBuffer + 1) % 2; byteBuffers[nextByteBuffer].clear(); if (numBytesRead > 0) { // Copy over remaining bytes to next buffer. for (; endOfChunkPos < bufferLimit; endOfChunkPos++) { byteBuffers[nextByteBuffer].put(fileBytes[endOfChunkPos]); } // Read next set from channel. numBytesRead = inChannel.read(byteBuffers[nextByteBuffer]); byteBuffers[nextByteBuffer].flip(); // Wait for all threads to finish. for (Future future : futures) { future.get(); } futures.clear(); } currrentByteBuffer = nextByteBuffer; } } ConcurrentSkipListMap measurements = new ConcurrentSkipListMap<>(); int chunkSize = Measures.HASH_TABLE_SIZE / measures.length; for (int i = 0; i < measures.length; i++) { Future f = threads.submit(new MeasuresMergeProcessor(measures, i * chunkSize, (i + 1) * chunkSize, measurements), i); futures.add(f); } for (Future future : futures) { future.get(); } futures.clear(); threads.shutdown(); threads.awaitTermination(1, TimeUnit.MILLISECONDS); System.out.println(measurements); // long endTime = System.currentTimeMillis(); // System.out.printf("Duration : %.3f%n", (endTime - startTime) / 1000.0); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_arjenw.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.IntStream; // Calculate Average // * baseline: 3m7s // * single-threaded chunk-based reading: 0m45s // * multi-threaded chunk-based reading: 0m14s // * less branches in parsing: 0m12s // * list approach iso map: 0m5.5s // * chunk finetuning: 0m4.5s // * threadlocal result gathering: 0m4.3s (trying graalvm-ce) // * memory-mapped file approach: 0m3.2s (also way simpler and neater code; inspired by spullara) // * smarter number parsing: 0m2.95s (inspired by iziamos) // * switching back to 21-tem vm 0m2.6s // * small optimizations 0m2.5s (skip byte-array copy, optimal StationList array size avoiding collisions) public class CalculateAverage_arjenw { private static final int TWO_BYTE_TO_INT = 480 + 48; // 48 is the ASCII code for '0' private static final int THREE_BYTE_TO_INT = 4800 + 480 + 48; private static final String FILE = "./measurements.txt"; public static void main(String[] args) { var file = new File(args.length > 0 ? args[0] : FILE); var fileSize = file.length(); var numberOfProcessors = fileSize > 1_000_000 ? Runtime.getRuntime().availableProcessors() : 1; var segmentSize = (int) Math.min(Integer.MAX_VALUE, fileSize / numberOfProcessors); // bytebuffer position is an int, so can be max Integer.MAX_VALUE var segmentCount = (int) (fileSize / segmentSize); var results = IntStream.range(0, segmentCount) .mapToObj(segmentNr -> parseSegment(file, fileSize, segmentSize, segmentNr)) .parallel() .reduce(StationList::merge) .orElseGet(StationList::new) .toStringArray(); Arrays.sort(results, Comparator.comparing(o -> takeUntil(o, '='))); System.out.format("{%s}%n", String.join(", ", results)); } private static StationList parseSegment(File file, long fileSize, int segmentSize, int segmentNr) { long segmentStart = segmentNr * (long) segmentSize; long segmentEnd = Math.min(fileSize, segmentStart + segmentSize + 100); try (var fileChannel = (FileChannel) Files.newByteChannel(file.toPath(), StandardOpenOption.READ)) { var bb = fileChannel.map(FileChannel.MapMode.READ_ONLY, segmentStart, segmentEnd - segmentStart); if (segmentStart > 0) { // noinspection StatementWithEmptyBody while (bb.get() != '\n') ; // skip to first new line } StationList stationList = new StationList(); var buffer = new byte[100]; while (bb.position() < segmentSize) { byte b; var i = 0; int hash = 0; while ((b = bb.get()) != ';') { hash = hash * 31 + b; buffer[i++] = b; } int value; byte b1 = bb.get(); byte b2 = bb.get(); byte b3 = bb.get(); byte b4 = bb.get(); if (b2 == '.') {// value is n.n value = (b1 * 10 + b3 - TWO_BYTE_TO_INT); // b4 == \n } else { if (b4 == '.') { // value is -nn.n value = -(b2 * 100 + b3 * 10 + bb.get() - THREE_BYTE_TO_INT); } else if (b1 == '-') { // value is -n.n value = -(b2 * 10 + b4 - TWO_BYTE_TO_INT); } else { // value is nn.n value = (b1 * 100 + b2 * 10 + b4 - THREE_BYTE_TO_INT); } bb.get(); // new line } if (stationList.add(buffer, i, Math.abs(hash), value)) buffer = new byte[100]; // station was new, create new buffer to contain the next station's name } return stationList; } catch (IOException e) { throw new RuntimeException(e); } } private static final class Station { private final byte[] data; private final int hash; private final int length; private int min; private int max; private int total; private int count; private Station(byte[] data, int length, int hash, int value) { this.data = data; this.hash = hash; this.length = length; min = max = total = value; count = 1; } @Override public String toString() { return STR."\{new String(data, 0, length, StandardCharsets.UTF_8)}=\{min / 10.0}/\{Math.round(((double) total) / count) / 10.0}/\{max / 10.0}"; } private void append(int min, int max, int total, int count) { if (min < this.min) this.min = min; if (max > this.max) this.max = max; this.total += total; this.count += count; } public void append(int value) { append(value, value, value, 1); } public void merge(Station other) { append(other.min, other.max, other.total, other.count); } } private static class StationList implements Iterable { private final static int MAX_ENTRY = 65375; // choose a value that _eliminates_ collisions on the test set. private final Station[] array = new Station[MAX_ENTRY]; private int size = 0; private boolean add(int hash, Supplier create, Consumer update) { var position = hash % MAX_ENTRY; Station existing; while ((existing = array[position]) != null && existing.hash != hash) { position = (position + 1) % MAX_ENTRY; } if (existing == null) { array[position] = create.get(); size++; return true; } else { update.accept(existing); return false; } } public boolean add(byte[] data, int stationNameLength, int stationHash, int value) { return add(stationHash, () -> new Station(data, stationNameLength, stationHash, value), existing -> existing.append(value)); } public void add(Station station) { add(station.hash, () -> station, existing -> existing.merge(station)); } public String[] toStringArray() { var destination = new String[size]; var i = 0; for (Station station : this) destination[i++] = station.toString(); return destination; } public StationList merge(StationList other) { for (Station station : other) add(station); return this; } @Override public Iterator iterator() { return new Iterator<>() { private int index = 0; @Override public boolean hasNext() { Station station = null; while (index < MAX_ENTRY && (station = array[index]) == null) index++; return station != null; } @Override public Station next() { if (hasNext()) { return array[index++]; } throw new NoSuchElementException(); } }; } } private static String takeUntil(String s, char c) { var pos = s.indexOf(c); return pos > -1 ? s.substring(0, pos) : s; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_armandino.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.io.PrintStream; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Collection; import java.util.Map; import java.util.TreeMap; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.charset.StandardCharsets.UTF_8; public class CalculateAverage_armandino { private static final Path FILE = Path.of("./measurements.txt"); private static final int NUM_CHUNKS = Math.max(8, Runtime.getRuntime().availableProcessors()); private static final int INITIAL_MAP_CAPACITY = 8192; private static final byte SEMICOLON = 59; private static final byte NL = 10; private static final int PRIME = 1117; private static final int KEY_OFFSET = 0, // 100b HASH_OFFSET = 100, // int KEY_LENGTH_OFFSET = 104, // short MIN_OFFSET = 106, // short MAX_OFFSET = 108, // short COUNT_OFFSET = 110, // int SUM_OFFSET = 114; // long private static final long ENTRY_SIZE = 100 // key: offset=0 + 4 // keyHash: offset=100 + 2 // keyLength: offset=104 + 2 // min: 108; offset=106 + 2 // max: 110; offset=108 + 4 // count: 114; offset=110 + 8; // sum: 122; offset=118 private static final Unsafe UNSAFE = getUnsafe(); public static void main(String[] args) throws Exception { var channel = FileChannel.open(FILE, StandardOpenOption.READ); Chunk[] chunks = split(channel); ChunkProcessor[] processors = new ChunkProcessor[chunks.length]; for (int i = 0; i < processors.length; i++) { processors[i] = new ChunkProcessor(chunks[i].start, chunks[i].end); processors[i].start(); } Map results = new TreeMap<>(); for (int i = 0; i < processors.length; i++) { processors[i].join(); final long end = processors[i].map.mapEnd; for (long addr = processors[i].map.mapStart; addr < end; addr += ENTRY_SIZE) { final short keyLength = UNSAFE.getShort(addr + KEY_LENGTH_OFFSET); if (keyLength == 0) continue; final byte[] keyBytes = new byte[keyLength]; UNSAFE.copyMemory(null, addr, keyBytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, keyLength); final short min = UNSAFE.getShort(addr + MIN_OFFSET); final short max = UNSAFE.getShort(addr + MAX_OFFSET); final int count = UNSAFE.getInt(addr + COUNT_OFFSET); final long sum = UNSAFE.getLong(addr + SUM_OFFSET); final Stats s = new Stats(new String(keyBytes, 0, keyLength, UTF_8), min, max, count, sum); results.merge(s.key, s, CalculateAverage_armandino::mergeStats); } } print(results.values()); } private static Stats mergeStats(final Stats x, final Stats y) { x.min = Math.min(x.min, y.min); x.max = Math.max(x.max, y.max); x.count += y.count; x.sum += y.sum; return x; } private static class ChunkProcessor extends Thread { private final UnsafeMap map = new UnsafeMap(INITIAL_MAP_CAPACITY); final long chunkStart; final long chunkEnd; private ChunkProcessor(long chunkStart, long chunkEnd) { this.chunkStart = chunkStart; this.chunkEnd = chunkEnd; } @Override public void run() { long i = chunkStart; while (i < chunkEnd) { final long keyAddress = i; int keyHash = 0; byte b; while ((b = UNSAFE.getByte(i++)) != SEMICOLON) { keyHash = PRIME * keyHash + b; } final short keyLength = (short) (i - keyAddress - 1); final long numberWord = UNSAFE.getLong(i); final int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000); final short measurement = parseNumber(decimalSepPos, numberWord); final int addOffset = (decimalSepPos >>> 3) + 3; i += addOffset; map.addEntry(keyHash, keyAddress, keyLength, measurement); } } // credit: merykitty private static short parseNumber(int decimalSepPos, long numberWord) { int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise long signed = (~numberWord << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii to digit value long digits = ((numberWord & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + 0x00UU00TTHH000000 * 10 + 0xUU00TTHH00000000 * 100 long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; return (short) ((absValue ^ signed) - signed); } } private static class Stats { private final String key; private int min; private int max; private int count; private long sum; Stats(final String key, final int min, final int max, final int count, final long sum) { this.min = min; this.max = max; this.count = count; this.sum = sum; this.key = key; } void print(final PrintStream out) { out.print(key); out.print('='); out.print(round(min / 10f)); out.print('/'); out.print(round((sum / 10f) / count)); out.print('/'); out.print(round(max) / 10f); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static void print(final Collection sorted) { int size = sorted.size(); System.out.print('{'); for (Stats stats : sorted) { stats.print(System.out); if (--size > 0) { System.out.print(", "); } } System.out.println('}'); } private static Chunk[] split(final FileChannel channel) throws IOException { final long fileSize = channel.size(); long start = channel.map(READ_ONLY, 0, fileSize, Arena.global()).address(); final long endAddress = start + fileSize; if (fileSize < 10000) { return new Chunk[]{ new Chunk(start, endAddress) }; } final long chunkSize = fileSize / NUM_CHUNKS; final var chunks = new Chunk[NUM_CHUNKS]; long end = start + chunkSize; for (int i = 0; i < NUM_CHUNKS; i++) { if (i > 0) { start = chunks[i - 1].end; end = Math.min(start + chunkSize, endAddress); } if (end < endAddress) { while (UNSAFE.getByte(end) != NL) { end++; } end++; } chunks[i] = new Chunk(start, end); } return chunks; } private record Chunk(long start, long end) { } private static Unsafe getUnsafe() { try { Field unsafe = Unsafe.class.getDeclaredField("theUnsafe"); unsafe.setAccessible(true); return (Unsafe) unsafe.get(null); } catch (Exception e) { throw new RuntimeException(e); } } private static class UnsafeMap { long mapStart; long mapEnd; int capacity; // num entries UnsafeMap(int numEntries) { capacity = numEntries; final long size = ENTRY_SIZE * numEntries; mapStart = UNSAFE.allocateMemory(size); mapEnd = mapStart + size; UNSAFE.setMemory(mapStart, size, (byte) 0); } void addEntry(final int keyHash, final long keyAddress, final short keyLength, final short measurement) { final int pos = (capacity - 1) & keyHash; long addr = mapStart + pos * ENTRY_SIZE; int hash = UNSAFE.getInt(addr + HASH_OFFSET); if (hash == 0) { // new entry initEntry(addr, keyAddress, keyLength, measurement, keyHash); return; } if (hash == keyHash && keysEqual(addr, keyAddress, keyLength)) { updateEntry(addr, measurement); return; } // this can be improved to avoid clustering at the start. // should only affect the 10k test addr = mapStart; while (addr < mapEnd) { addr += ENTRY_SIZE; hash = UNSAFE.getInt(addr + HASH_OFFSET); if (hash == 0) { initEntry(addr, keyAddress, keyLength, measurement, keyHash); return; } if (hash == keyHash && keysEqual(addr, keyAddress, keyLength)) { updateEntry(addr, measurement); return; } } resize(keyHash, keyAddress, keyLength, measurement); } private void resize(final int keyHash, final long keyAddress, final short keyLength, final short measurement) { UnsafeMap newMap = new UnsafeMap(capacity * 2); for (long addr = mapStart; addr < mapEnd; addr += ENTRY_SIZE) { final short oKeyLength = UNSAFE.getShort(addr + KEY_LENGTH_OFFSET); final int oKeyHsh = UNSAFE.getInt(addr + HASH_OFFSET); final short oMin = UNSAFE.getShort(addr + MIN_OFFSET); final short oMax = UNSAFE.getShort(addr + MAX_OFFSET); final int oCount = UNSAFE.getInt(addr + COUNT_OFFSET); final long oSum = UNSAFE.getLong(addr + SUM_OFFSET); final int newPos = (newMap.capacity - 1) & oKeyHsh; long newAddr = newMap.mapStart + newPos * ENTRY_SIZE; UNSAFE.putShort(newAddr + KEY_LENGTH_OFFSET, oKeyLength); UNSAFE.putInt(newAddr + HASH_OFFSET, oKeyHsh); UNSAFE.putShort(newAddr + MIN_OFFSET, oMin); UNSAFE.putShort(newAddr + MAX_OFFSET, oMax); UNSAFE.putInt(newAddr + COUNT_OFFSET, oCount); UNSAFE.putLong(newAddr + SUM_OFFSET, oSum); } newMap.addEntry(keyHash, keyAddress, keyLength, measurement); this.mapStart = newMap.mapStart; this.mapEnd = newMap.mapEnd; this.capacity = newMap.capacity; } private static void initEntry(final long entry, final long keyAddress, final short keyLength, final short measurement, final int keyHash) { UNSAFE.copyMemory(keyAddress, entry, keyLength); UNSAFE.putInt(entry + HASH_OFFSET, keyHash); UNSAFE.putShort(entry + KEY_LENGTH_OFFSET, keyLength); UNSAFE.putShort(entry + MIN_OFFSET, Short.MAX_VALUE); UNSAFE.putShort(entry + MAX_OFFSET, Short.MIN_VALUE); updateEntry(entry, measurement); } private static void updateEntry(final long entry, final short measurement) { UNSAFE.putShort(entry + MIN_OFFSET, (short) Math.min(UNSAFE.getShort(entry + MIN_OFFSET), measurement)); UNSAFE.putShort(entry + MAX_OFFSET, (short) Math.max(UNSAFE.getShort(entry + MAX_OFFSET), measurement)); UNSAFE.putInt(entry + COUNT_OFFSET, UNSAFE.getInt(entry + COUNT_OFFSET) + 1); UNSAFE.putLong(entry + SUM_OFFSET, UNSAFE.getLong(entry + SUM_OFFSET) + measurement); } } private static boolean keysEqual(long key1Address, long key2Address, final int keyLength) { // credit: abeobk long xsum = 0; int n = keyLength & 0xF8; for (int i = 0; i < n; i += 8) { xsum |= (UNSAFE.getLong(key1Address + i) ^ UNSAFE.getLong(key2Address + i)); } return xsum == 0; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_artpar.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.time.Instant; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.stream.Collectors; public class CalculateAverage_artpar { public static final int N_THREADS = 8; private static final String FILE = "./measurements.txt"; private static final int INT_MAP_SIZE = 8192; // from calculateIntegerByteMapTest() final static int[] byteHashMapToInt = calculateIntegerByteMap(); private static final Unsafe UNSAFE = initUnsafe(); // private static final VectorSpecies SPECIES = IntVector.SPECIES_PREFERRED; // final int VECTOR_SIZE = 512; // final int VECTOR_SIZE_1 = VECTOR_SIZE - 1; final int AVERAGE_CHUNK_SIZE = 1024 * 64; final int AVERAGE_CHUNK_SIZE_1 = AVERAGE_CHUNK_SIZE - 1; public CalculateAverage_artpar() throws IOException { long start = Instant.now().toEpochMilli(); Path measurementFile = Paths.get(FILE); long fileSize = Files.size(measurementFile); // System.out.println("File size - " + fileSize); int expectedChunkSize = Math.toIntExact(Math.min(fileSize / N_THREADS, Integer.MAX_VALUE / 2)); ExecutorService threadPool = Executors.newFixedThreadPool(N_THREADS); long chunkStartPosition = 0; RandomAccessFile fis = new RandomAccessFile(measurementFile.toFile(), "r"); List>> futures = new ArrayList<>(); long bytesReadCurrent = 0; FileChannel fileChannel = FileChannel.open(measurementFile, StandardOpenOption.READ); for (int i = 0; chunkStartPosition < fileSize; i++) { int chunkSize = expectedChunkSize; chunkSize = fis.skipBytes(chunkSize); bytesReadCurrent += chunkSize; while (((char) fis.read()) != '\n' && bytesReadCurrent < fileSize) { chunkSize++; bytesReadCurrent++; } // System.out.println("[" + chunkStartPosition + "] - [" + (chunkStartPosition + chunkSize) + " bytes"); if (chunkStartPosition + chunkSize >= fileSize) { chunkSize = (int) Math.min(fileSize - chunkStartPosition, Integer.MAX_VALUE); } if (chunkSize < 1) { break; } if (chunkSize >= Integer.MAX_VALUE) { throw new RuntimeException(); } // MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, chunkStartPosition, // chunkSize); ReaderRunnable readerRunnable = new ReaderRunnable(chunkStartPosition, chunkSize, fileChannel); Future> future = threadPool.submit(readerRunnable::run); // System.out.println("Added future [" + chunkStartPosition + "][" + chunkSize + "]"); futures.add(future); chunkStartPosition = chunkStartPosition + chunkSize + 1; } fis.close(); Map globalMap = futures.parallelStream().flatMap(future -> { try { return future.get().entrySet().stream(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }).parallel().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, MeasurementAggregator::combine)); fileChannel.close(); Map results = globalMap.entrySet().stream().parallel() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().finish())); threadPool.shutdown(); Map measurements = new TreeMap<>(results); PrintStream printStream = new PrintStream(new BufferedOutputStream(System.out)); // PrintStream printStream = System.out; printStream.print("{"); boolean isFirst = true; for (Map.Entry stringResultRowEntry : measurements.entrySet()) { if (!isFirst) { printStream.print(", "); printStream.flush(); } printStream.flush(); printStream.print(stringResultRowEntry.getKey()); printStream.flush(); printStream.print("="); printStream.flush(); stringResultRowEntry.getValue().printTo(printStream); printStream.flush(); isFirst = false; } System.out.print("}\n"); // long end = Instant.now().toEpochMilli(); // System.out.println((end - start) / 1000); } public static int[] calculateIntegerByteMapTest() { int[] intToIntMap = null; for (int j = 0; j < 10000; j++) { int length = 2000 + j; intToIntMap = new int[length]; boolean hasHashClash = false; Map byteHashToInt = new HashMap<>(); for (int i = -999; i < 1000; i++) { int hashCode = hashInteger(i); // String s = new String(value); int position = hashCode & (length - 1); // System.out.printf("%.1f => %s length [%d] hash [%d] => %d\n", number, s, s.length(), hashCode, position); if (byteHashToInt.containsKey(hashCode) || intToIntMap[position] != 0) { // System.err.println("HashClash [" + hashCode + "] -> " + // byteHashToInt.get( // hashCode) + " vs " + number + " == [" + position + "] =>" + intToIntMap[position]); hasHashClash = true; break; } else { byteHashToInt.put(hashCode, i); intToIntMap[position] = i; } } if (!hasHashClash) { // 8192 System.out.println("NoHash clash at [" + length + "]"); // throw new RuntimeException("clash"); return intToIntMap; } } System.out.println("Fail"); return null; } private static int hashInteger(int i) { float number = i / 10f; String numberString = String.format("%.1f", number); byte[] value = numberString.getBytes(); int hashCode = 1; for (int k = 0; k < value.length; k++) { hashCode = hashCode * 31 + value[k]; } return hashCode; } public static int[] calculateIntegerByteMap() { long start = System.currentTimeMillis(); int[] intToIntMap = new int[INT_MAP_SIZE]; for (int i = -999; i < 1000; i++) { float number = i / 10f; byte[] value = String.format("%.1f", number).getBytes(); int hashCode = 1; for (byte b : value) { hashCode = hashCode * 31 + b; } int position = hashCode & (INT_MAP_SIZE - 1); intToIntMap[position] = i; } long end = System.currentTimeMillis(); // System.out.println("calculateIntegerByteMap " + (end - start) + " ms"); return intToIntMap; } public static void main(String[] args) throws IOException { new CalculateAverage_artpar(); } private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } static boolean unsafeEquals(long aStart, long aLength, long bStart, long bLength) { if (aLength != bLength) { return false; } for (int i = 0; i < aLength; ++i) { if (UNSAFE.getByte(aStart + i) != UNSAFE.getByte(bStart + i)) { return false; } } return true; } private record ResultRow(double min, double mean, double max) { public String toString() { return round(min / 10) + "/" + round(mean / 10) + "/" + round(max / 10); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } public void printTo(PrintStream out) { out.printf("%.1f/%.1f/%.1f", min / 10, mean / 10, max / 10); } } private static class MeasurementAggregator { private int min = 999; private int max = -999; private double sum; private long count; MeasurementAggregator combine(MeasurementAggregator other) { min = other.min + ((min - other.min) & ((min - other.min) >> (32 * 8 - 1))); max = max - ((max - other.max) & ((max - other.max) >> (32 * 8 - 1))); sum += other.sum; count += other.count; return this; } void combine(int value) { sum += value; count++; min = value + ((min - value) & ((min - value) >> (32 * 8 - 1))); // min(x, y) max = max - ((max - value) & ((max - value) >> (32 * 8 - 1))); // max(x, y) } ResultRow finish() { double mean = (count > 0) ? sum / count : 0; return new ResultRow(min, mean, max); } } static class StationName { public final int hash; private final ByteBuffer nameBytes; private final MeasurementAggregator measurementAggregator = new MeasurementAggregator(); public int count = 0; public StationName(ByteBuffer nameBytes, int hash) { this.nameBytes = nameBytes; this.hash = hash; } } private class ReaderRunnable { private final long startPosition; private final FileChannel fileChannel; private final int chunkSize; StationNameMap stationNameMap = new StationNameMap(); private ReaderRunnable(long startPosition, int chunkSize, FileChannel fileChannel) throws IOException { this.chunkSize = chunkSize; this.startPosition = startPosition; this.fileChannel = fileChannel; // mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, startPosition, chunkSize); } public Map run() throws IOException { MemorySegment mappedSegment = fileChannel.map(FileChannel.MapMode.READ_ONLY, startPosition, chunkSize, Arena.global()); long rawBufferAddress = UNSAFE.allocateMemory(100); int rawBufferReadIndex = 0; long position = mappedSegment.address(); long endPosition = position + chunkSize; byte b; int hash; int nameHash; hash = 1; while (position < endPosition) { while ((position < endPosition) && (b = UNSAFE.getByte(position++)) != ';') { UNSAFE.putByte(rawBufferAddress + rawBufferReadIndex++, b); hash = hash * 31 + b; } nameHash = hash; hash = 1; while ((position < endPosition) && (b = UNSAFE.getByte(position++)) != '\n') { hash = hash * 31 + b; } stationNameMap.getOrCreate(rawBufferAddress, rawBufferReadIndex, byteHashMapToInt[hash & (INT_MAP_SIZE - 1)], nameHash); rawBufferReadIndex = 0; hash = 1; } return Arrays.stream(stationNameMap.names).parallel().filter(Objects::nonNull).collect( Collectors.toMap(e -> StandardCharsets.UTF_8.decode(e.nameBytes).toString(), e -> e.measurementAggregator, MeasurementAggregator::combine)); } } class StationNameMap { int[] indexes = new int[AVERAGE_CHUNK_SIZE]; StationName[] names = new StationName[AVERAGE_CHUNK_SIZE]; int currentIndex = 0; ByteBuffer bytesForName = ByteBuffer.allocateDirect(1000 * 100); int nameBufferIndex = 0; public void getOrCreate(long stationNameBytesAddress, int length, int doubleValue, int hash) { int position = hash & AVERAGE_CHUNK_SIZE_1; while (indexes[position] != 0 && (names[indexes[position]].hash != hash)) { position = ++position & AVERAGE_CHUNK_SIZE_1; } if (indexes[position] != 0) { StationName stationName = names[indexes[position]]; stationName.measurementAggregator.combine(doubleValue); } else { ByteBuffer nameSlice = bytesForName.slice(nameBufferIndex, length); nameBufferIndex += length; for (int i = 0; i < length; i++) { nameSlice.put(UNSAFE.getByte(stationNameBytesAddress + i)); } nameSlice.flip(); StationName stationName = new StationName(nameSlice, hash); indexes[position] = ++currentIndex; names[indexes[position]] = stationName; stationName.measurementAggregator.combine(doubleValue); } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_artsiomkorzun.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; import java.util.Optional; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class CalculateAverage_artsiomkorzun { private static final Path FILE = Path.of("./measurements.txt"); private static final long SEGMENT_SIZE = 2 * 1024 * 1024; private static final long COMMA_PATTERN = 0x3B3B3B3B3B3B3B3BL; private static final long LINE_PATTERN = 0x0A0A0A0A0A0A0A0AL; private static final long DOT_BITS = 0x10101000; private static final long MAGIC_MULTIPLIER = (100 * 0x1000000 + 10 * 0x10000 + 1); private static final long[] WORD_MASK = { 0, 0, 0, 0, 0, 0, 0, 0, -1 }; private static final int[] LENGTH_MASK = { 0, 0, 0, 0, 0, 0, 0, 0, -1 }; private static final Unsafe UNSAFE; static { try { Field unsafe = Unsafe.class.getDeclaredField("theUnsafe"); unsafe.setAccessible(true); UNSAFE = (Unsafe) unsafe.get(Unsafe.class); } catch (Throwable e) { throw new RuntimeException(e); } } public static void main(String[] args) throws Exception { // for (int i = 0; i < 10; i++) { // long start = System.currentTimeMillis(); // execute(); // long end = System.currentTimeMillis(); // System.err.println("Time: " + (end - start)); // } if (isSpawn(args)) { spawn(); return; } execute(); } private static boolean isSpawn(String[] args) { for (String arg : args) { if ("--worker".equals(arg)) { return false; } } return true; } private static void spawn() throws Exception { ProcessHandle.Info info = ProcessHandle.current().info(); ArrayList commands = new ArrayList<>(); Optional command = info.command(); Optional arguments = info.arguments(); if (command.isPresent()) { commands.add(command.get()); } if (arguments.isPresent()) { commands.addAll(Arrays.asList(arguments.get())); } commands.add("--worker"); new ProcessBuilder() .command(commands) .start() .getInputStream() .transferTo(System.out); } private static void execute() throws Exception { MemorySegment fileMemory = map(FILE); long fileAddress = fileMemory.address(); long fileSize = fileMemory.byteSize(); int segmentCount = (int) ((fileSize + SEGMENT_SIZE - 1) / SEGMENT_SIZE); AtomicInteger counter = new AtomicInteger(); AtomicReference result = new AtomicReference<>(); int parallelism = Runtime.getRuntime().availableProcessors(); Aggregator[] aggregators = new Aggregator[parallelism]; for (int i = 0; i < aggregators.length; i++) { aggregators[i] = new Aggregator(counter, result, fileAddress, fileSize, segmentCount); aggregators[i].start(); } for (int i = 0; i < aggregators.length; i++) { aggregators[i].join(); } Map aggregates = result.get().build(); System.out.println(text(aggregates)); System.out.close(); } private static MemorySegment map(Path file) { try (FileChannel channel = FileChannel.open(file, StandardOpenOption.READ)) { long size = channel.size(); return channel.map(FileChannel.MapMode.READ_ONLY, 0, size, Arena.global()); } catch (Throwable e) { throw new RuntimeException(e); } } private static long word(long address) { return UNSAFE.getLong(address); /* * if (BYTE_ORDER == ByteOrder.BIG_ENDIAN) { * value = Long.reverseBytes(value); * } * * return value; */ } private static String text(Map aggregates) { StringBuilder text = new StringBuilder(aggregates.size() * 32 + 2); text.append('{'); for (Map.Entry entry : aggregates.entrySet()) { if (text.length() > 1) { text.append(", "); } Aggregate aggregate = entry.getValue(); text.append(entry.getKey()).append('=') .append(round(aggregate.min)).append('/') .append(round(1.0 * aggregate.sum / aggregate.cnt)).append('/') .append(round(aggregate.max)); } text.append('}'); return text.toString(); } private static double round(double v) { return Math.round(v) / 10.0; } private record Aggregate(int min, int max, long sum, int cnt) { } private static class Aggregates { private static final long ENTRIES = 64 * 1024; private static final long SIZE = 128 * ENTRIES; private static final long MASK = (ENTRIES - 1) << 7; private final long pointer; public Aggregates() { long address = UNSAFE.allocateMemory(SIZE + 4096); pointer = (address + 4095) & (~4095); UNSAFE.setMemory(pointer, SIZE, (byte) 0); } public long find(long word1, long word2, long hash) { long address = pointer + offset(hash); long w1 = word(address + 24); long w2 = word(address + 32); return (word1 == w1) && (word2 == w2) ? address : 0; } public long put(long reference, long word, long length, long hash) { for (long offset = offset(hash);; offset = next(offset)) { long address = pointer + offset; if (equal(reference, word, address + 24, length)) { return address; } int len = UNSAFE.getInt(address); if (len == 0) { alloc(reference, length, hash, address); return address; } } } public static void update(long address, long value) { long sum = UNSAFE.getLong(address + 8) + value; int cnt = UNSAFE.getInt(address + 16) + 1; short min = UNSAFE.getShort(address + 20); short max = UNSAFE.getShort(address + 22); UNSAFE.putLong(address + 8, sum); UNSAFE.putInt(address + 16, cnt); if (value < min) { UNSAFE.putShort(address + 20, (short) value); } if (value > max) { UNSAFE.putShort(address + 22, (short) value); } } public void merge(Aggregates rights) { for (long rightOffset = 0; rightOffset < SIZE; rightOffset += 128) { long rightAddress = rights.pointer + rightOffset; int length = UNSAFE.getInt(rightAddress); if (length == 0) { continue; } int hash = UNSAFE.getInt(rightAddress + 4); for (long offset = offset(hash);; offset = next(offset)) { long address = pointer + offset; if (equal(address + 24, rightAddress + 24, length)) { long sum = UNSAFE.getLong(address + 8) + UNSAFE.getLong(rightAddress + 8); int cnt = UNSAFE.getInt(address + 16) + UNSAFE.getInt(rightAddress + 16); short min = (short) Math.min(UNSAFE.getShort(address + 20), UNSAFE.getShort(rightAddress + 20)); short max = (short) Math.max(UNSAFE.getShort(address + 22), UNSAFE.getShort(rightAddress + 22)); UNSAFE.putLong(address + 8, sum); UNSAFE.putInt(address + 16, cnt); UNSAFE.putShort(address + 20, min); UNSAFE.putShort(address + 22, max); break; } int len = UNSAFE.getInt(address); if (len == 0) { UNSAFE.copyMemory(rightAddress, address, length + 24); break; } } } } public Map build() { TreeMap set = new TreeMap<>(); for (long offset = 0; offset < SIZE; offset += 128) { long address = pointer + offset; int length = UNSAFE.getInt(address); if (length != 0) { byte[] array = new byte[length - 1]; UNSAFE.copyMemory(null, address + 24, array, Unsafe.ARRAY_BYTE_BASE_OFFSET, array.length); String key = new String(array); long sum = UNSAFE.getLong(address + 8); int cnt = UNSAFE.getInt(address + 16); short min = UNSAFE.getShort(address + 20); short max = UNSAFE.getShort(address + 22); Aggregate aggregate = new Aggregate(min, max, sum, cnt); set.put(key, aggregate); } } return set; } private static void alloc(long reference, long length, long hash, long address) { UNSAFE.putInt(address, (int) length); UNSAFE.putInt(address + 4, (int) hash); UNSAFE.putShort(address + 20, Short.MAX_VALUE); UNSAFE.putShort(address + 22, Short.MIN_VALUE); UNSAFE.copyMemory(reference, address + 24, length); } private static long offset(long hash) { return hash & MASK; } private static long next(long prev) { return (prev + 128) & (SIZE - 1); } private static boolean equal(long leftAddress, long leftWord, long rightAddress, long length) { while (length > 8) { long left = UNSAFE.getLong(leftAddress); long right = UNSAFE.getLong(rightAddress); if (left != right) { return false; } leftAddress += 8; rightAddress += 8; length -= 8; } return leftWord == word(rightAddress); } private static boolean equal(long leftAddress, long rightAddress, long length) { do { long left = UNSAFE.getLong(leftAddress); long right = UNSAFE.getLong(rightAddress); if (left != right) { return false; } leftAddress += 8; rightAddress += 8; length -= 8; } while (length > 0); return true; } } private static class Aggregator extends Thread { private final AtomicInteger counter; private final AtomicReference result; private final long fileAddress; private final long fileSize; private final int segmentCount; public Aggregator(AtomicInteger counter, AtomicReference result, long fileAddress, long fileSize, int segmentCount) { super("aggregator"); this.counter = counter; this.result = result; this.fileAddress = fileAddress; this.fileSize = fileSize; this.segmentCount = segmentCount; } @Override public void run() { Aggregates aggregates = new Aggregates(); for (int segment; (segment = counter.getAndIncrement()) < segmentCount;) { long position = SEGMENT_SIZE * segment; long size = Math.min(SEGMENT_SIZE + 1, fileSize - position); long start = fileAddress + position; long end = start + size; if (segment > 0) { start = next(start); } long chunk = (end - start) / 3; long left = next(start + chunk); long right = next(start + chunk + chunk); Chunk chunk1 = new Chunk(start, left); Chunk chunk2 = new Chunk(left, right); Chunk chunk3 = new Chunk(right, end); while (chunk1.has() && chunk2.has() && chunk3.has()) { long word1 = word(chunk1.position); long word2 = word(chunk2.position); long word3 = word(chunk3.position); long word4 = word(chunk1.position + 8); long word5 = word(chunk2.position + 8); long word6 = word(chunk3.position + 8); long separator1 = separator(word1); long separator2 = separator(word2); long separator3 = separator(word3); long separator4 = separator(word4); long separator5 = separator(word5); long separator6 = separator(word6); long pointer1 = find(aggregates, chunk1, word1, word4, separator1, separator4); long pointer2 = find(aggregates, chunk2, word2, word5, separator2, separator5); long pointer3 = find(aggregates, chunk3, word3, word6, separator3, separator6); long value1 = value(chunk1); long value2 = value(chunk2); long value3 = value(chunk3); Aggregates.update(pointer1, value1); Aggregates.update(pointer2, value2); Aggregates.update(pointer3, value3); } while (chunk1.has()) { long word1 = word(chunk1.position); long word2 = word(chunk1.position + 8); long separator1 = separator(word1); long separator2 = separator(word2); long pointer = find(aggregates, chunk1, word1, word2, separator1, separator2); long value = value(chunk1); Aggregates.update(pointer, value); } while (chunk2.has()) { long word1 = word(chunk2.position); long word2 = word(chunk2.position + 8); long separator1 = separator(word1); long separator2 = separator(word2); long pointer = find(aggregates, chunk2, word1, word2, separator1, separator2); long value = value(chunk2); Aggregates.update(pointer, value); } while (chunk3.has()) { long word1 = word(chunk3.position); long word2 = word(chunk3.position + 8); long separator1 = separator(word1); long separator2 = separator(word2); long pointer = find(aggregates, chunk3, word1, word2, separator1, separator2); long value = value(chunk3); Aggregates.update(pointer, value); } } while (!result.compareAndSet(null, aggregates)) { Aggregates rights = result.getAndSet(null); if (rights != null) { aggregates.merge(rights); } } } private static long next(long position) { while (true) { long word = word(position); long match = word ^ LINE_PATTERN; long line = (match - 0x0101010101010101L) & (~match & 0x8080808080808080L); if (line == 0) { position += 8; continue; } return position + length(line) + 1; } } private static long find(Aggregates aggregates, Chunk chunk, long word1, long word2, long separator1, long separator2) { boolean small = (separator1 | separator2) != 0; long start = chunk.position; long hash; long word; if (small) { int length1 = length(separator1); int length2 = length(separator2); word1 = mask(word1, separator1); word2 = mask(word2 & WORD_MASK[length1], separator2); hash = mix(word1 ^ word2); chunk.position += length1 + (length2 & LENGTH_MASK[length1]) + 1; long pointer = aggregates.find(word1, word2, hash); if (pointer != 0) { return pointer; } word = (separator1 == 0) ? word2 : word1; } else { chunk.position += 16; hash = word1 ^ word2; while (true) { word = word(chunk.position); long separator = separator(word); if (separator == 0) { chunk.position += 8; hash ^= word; continue; } word = mask(word, separator); hash = mix(hash ^ word); chunk.position += length(separator) + 1; break; } } long length = chunk.position - start; return aggregates.put(start, word, length, hash); } private static long value(Chunk chunk) { long num = word(chunk.position); long dot = dot(num); long value = value(num, dot); chunk.position += (dot >> 3) + 3; return value; } private static long separator(long word) { long match = word ^ COMMA_PATTERN; return (match - 0x0101010101010101L) & (~match & 0x8080808080808080L); } private static long mask(long word, long separator) { long mask = separator ^ (separator - 1); return word & mask; } private static int length(long separator) { return Long.numberOfTrailingZeros(separator) >>> 3; } private static long mix(long x) { long h = x * -7046029254386353131L; h ^= h >>> 35; return h; // h ^= h >>> 32; // return (int) (h ^ h >>> 16); } private static long dot(long num) { return Long.numberOfTrailingZeros(~num & DOT_BITS); } private static long value(long w, long dot) { long signed = (~w << 59) >> 63; long mask = ~(signed & 0xFF); long digits = ((w & mask) << (28 - dot)) & 0x0F000F0F00L; long abs = ((digits * MAGIC_MULTIPLIER) >>> 32) & 0x3FF; return (abs ^ signed) - signed; } } private static class Chunk { final long limit; long position; public Chunk(long position, long limit) { this.position = position; this.limit = limit; } boolean has() { return position < limit; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_as-com.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.*; import sun.misc.Unsafe; import java.io.File; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.lang.reflect.Field; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLongArray; // based on spullara's submission class CalculateAverage_asun { private static final String FILE = "./measurements.txt"; private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_256; private static final VectorSpecies INT_SPECIES = IntVector.SPECIES_256; private static final int VECTOR_SIZE = 32; private static final ByteVector ASC; static { byte[] bytes = new byte[VECTOR_SIZE]; for (int i = 0; i < VECTOR_SIZE; i++) { bytes[i] = (byte) i; } ASC = ByteVector.fromArray(BYTE_SPECIES, bytes, 0); } private static final Unsafe UNSAFE; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); UNSAFE = (Unsafe) f.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static AtomicLongArray segmentQueue; @SuppressWarnings("FieldMayBeFinal") // @jdk.internal.vm.annotation.Contended private static volatile int head = 0; @SuppressWarnings("FieldMayBeFinal") // @jdk.internal.vm.annotation.Contended private static volatile int tail = 0; @SuppressWarnings("FieldMayBeFinal") // @jdk.internal.vm.annotation.Contended private static volatile boolean doneQueueing = false; private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); private static final VarHandle headHandle; private static final VarHandle tailHandle; private static final VarHandle doneHandle; static { try { headHandle = LOOKUP.findStaticVarHandle(CalculateAverage_asun.class, "head", int.class); tailHandle = LOOKUP.findStaticVarHandle(CalculateAverage_asun.class, "tail", int.class); doneHandle = LOOKUP.findStaticVarHandle(CalculateAverage_asun.class, "doneQueueing", boolean.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static final ArrayBlockingQueue workerOutput = new ArrayBlockingQueue<>(Runtime.getRuntime().availableProcessors()); private static class Worker implements Runnable { private long segmentStart; private long segmentEnd; private final MemorySegment ms; private Worker(MemorySegment ms) { this.ms = ms; } @Override public void run() { var resultMap = new ByteArrayToResultMap(); var ms = this.ms.asSlice(0); var msAddr = ms.address(); var actualLimit = ms.byteSize(); var buffer = new byte[100 + VECTOR_SIZE]; while (pollSegment()) { long startLine; long pos = segmentStart; long limit = segmentEnd; long vectorLimit = Math.min(limit, actualLimit - VECTOR_SIZE); long longLimit = Math.min(limit, actualLimit - 8); // int[] lastHashMult = new int[]{ 7, 31, 63, 15, 255, 127, 3, 511 }; // IntVector lastMul = IntVector.fromArray(INT_SPECIES, lastHashMult, 0); vector: while ((startLine = pos) < vectorLimit) { long currentPosition = startLine; ByteVector r; VectorMask m; int offset = 0; IntVector h = IntVector.zero(INT_SPECIES); while (true) { if (currentPosition >= vectorLimit) { break vector; } r = ByteVector.fromMemorySegment(BYTE_SPECIES, ms, currentPosition, ByteOrder.LITTLE_ENDIAN); r.intoArray(buffer, offset); offset += VECTOR_SIZE; m = r.eq((byte) ';'); if (m.anyTrue()) { int firstTrue = m.firstTrue(); currentPosition += firstTrue; // note: target platform likely does not have AVX-512, so manipulating and using m directly is likely to be slow ByteVector lastMask = (ByteVector) ASC.lt((byte) firstTrue).toVector(); h = h.mul(31); h = h.add(r.and(lastMask).reinterpretAsInts()); break; } else { currentPosition += VECTOR_SIZE; h = h.mul(31); h = h.add(r.reinterpretAsInts()); } } // h = h.mul(lastMul); int hash = h.reduceLanes(VectorOperators.ADD); // currentPosition now has index of semicolon int nameLen = (int) (currentPosition - startLine); currentPosition++; if (currentPosition >= longLimit) { break; } long g = UNSAFE.getLong(msAddr + currentPosition); // long g = ms.get(ValueLayout.JAVA_LONG_UNALIGNED, currentPosition); boolean minus = (g & 0xff) == '-'; long minusL = (minus ? 1L : 0L) - 1; int negative = minus ? -1 : 1; // 00101101 MINUS // 00101110 PERIOD // 00001101 CR // 00001010 LF // 00110000 0 // 00111001 9 // scan for LF long lf = ~g & 0x20202020202020L; int tzc = Long.numberOfTrailingZeros(lf); long bytesToLF = tzc / 8; int shift = 72 - tzc & 0b111000; long reversedDigits = Long.reverseBytes(g & (0xFFFFFFFFFFFFFF00L | minusL)) >> shift; long temp = (reversedDigits & 0xf) + 10 * ((reversedDigits >> 16) & 0xf) + 100 * ((reversedDigits >> 24) & 0xf); temp *= negative; currentPosition += bytesToLF + 1; resultMap.putOrMerge(buffer, 0, nameLen, temp, hash); pos = currentPosition; } while ((startLine = pos) < limit) { long currentPosition = startLine; byte b; int offset = 0; while (currentPosition != segmentEnd && (b = ms.get(ValueLayout.JAVA_BYTE, currentPosition++)) != ';') { buffer[offset++] = b; } // Invariant: the remaining length is less than VECTOR_SIZE, so we can just run the last round of hashing int hash = ByteVector.fromArray(BYTE_SPECIES, buffer, 0, ASC.lt((byte) offset)) .reinterpretAsInts() // .mul(lastMul) .reduceLanes(VectorOperators.ADD); int temp; int negative = 1; // Inspired by @yemreinci to unroll this even further if (ms.get(ValueLayout.JAVA_BYTE, currentPosition) == '-') { negative = -1; currentPosition++; } if (ms.get(ValueLayout.JAVA_BYTE, currentPosition + 1) == '.') { temp = negative * ((ms.get(ValueLayout.JAVA_BYTE, currentPosition) - '0') * 10 + (ms.get(ValueLayout.JAVA_BYTE, currentPosition + 2) - '0')); currentPosition += 3; } else { temp = negative * ((ms.get(ValueLayout.JAVA_BYTE, currentPosition) - '0') * 100 + ((ms.get(ValueLayout.JAVA_BYTE, currentPosition + 1) - '0') * 10 + (ms.get(ValueLayout.JAVA_BYTE, currentPosition + 3) - '0'))); currentPosition += 4; } if (ms.get(ValueLayout.JAVA_BYTE, currentPosition) == '\r') { currentPosition++; } currentPosition++; resultMap.putOrMerge(buffer, 0, offset, temp, hash); pos = currentPosition; } } workerOutput.add(resultMap); } private boolean pollSegment() { int head; int tail; do { head = (int) headHandle.getAcquire(); tail = (int) tailHandle.getAcquire(); while (head >= tail) { if ((boolean) doneHandle.getAcquire()) { return false; } head = (int) headHandle.getAcquire(); tail = (int) tailHandle.getAcquire(); } } while (!headHandle.compareAndSet(head, head + 1)); segmentStart = segmentQueue.getPlain(head * 2); segmentEnd = segmentQueue.getPlain(head * 2 + 1); return true; } } public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { // long start = System.currentTimeMillis(); var filename = args.length == 0 ? FILE : args[0]; var file = new File(filename); @SuppressWarnings("resource") var fileChannel = (FileChannel) Files.newByteChannel(Path.of(filename), StandardOpenOption.READ); var ms = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size(), Arena.global()); long fileSize = file.length(); long segmentSize = 10_000_000; int numberOfSegments = (int) (file.length() / segmentSize + 1) * 2; segmentQueue = new AtomicLongArray(numberOfSegments); int tail = 0; int processors = Runtime.getRuntime().availableProcessors(); Thread.ofPlatform().daemon().start(() -> { for (int i = 0; i < processors - 1; i++) { Thread.ofPlatform().daemon().start(new Worker(ms)); } new Worker(ms).run(); }); long segStart = 0; while (segStart < fileSize) { long segEnd = findSegment(ms, Math.min(segStart + segmentSize, fileSize), fileSize); segmentQueue.setRelease(tail * 2, segStart); segmentQueue.setRelease(tail * 2 + 1, segEnd); tailHandle.setRelease(++tail); segStart = segEnd; } doneHandle.setRelease(true); // System.out.println(System.currentTimeMillis() - start); var resultsMap = new TreeMap(); for (int i = 0; i < processors; i++) { var result = workerOutput.take(); // System.out.println(i + " " + (System.currentTimeMillis() - start)); for (Entry e : result.getAll()) { resultsMap.merge(new String(e.key()), e.value(), CalculateAverage_asun::merge); } // System.out.println(i + " " + (System.currentTimeMillis() - start)); } System.out.println(resultsMap); // System.out.println(System.currentTimeMillis() - start); Runtime.getRuntime().halt(0); } private static Result merge(Result v, Result value) { return merge(v, value.min, value.max, value.sum, value.count); } private static Result merge(Result v, long value, long value1, long value2, long value3) { v.min = Math.min(v.min, value); v.max = Math.max(v.max, value1); v.sum += value2; v.count += value3; return v; } private static long findSegment(MemorySegment ms, long location, long fileSize) { while (location < fileSize) { if (ms.get(ValueLayout.JAVA_BYTE, location) == '\n') { location++; break; } location++; } return location; } static class Result { long min, max, sum; long count; Result(long value) { min = max = sum = value; this.count = 1; } @Override public String toString() { return round(min) + "/" + round((sum / 10.0) / count) + "/" + round(max); } double round(double v) { return Math.round(v * 10.0) / 10.0; } double round(long v) { return v / 10.0; } } record Entry(byte[] key, Result value) { } static class ByteArrayToResultMap { public static final int MAPSIZE = 1024 * 128; Result[] slots = new Result[MAPSIZE]; byte[][] keys = new byte[MAPSIZE][]; public void putOrMerge(byte[] key, int offset, int size, long temp, int hash) { int slot = hash & (slots.length - 1); var slotValue = slots[slot]; // Linear probe for open slot while (slotValue != null && (keys[slot].length != size || !Arrays.equals(keys[slot], 0, size, key, offset, size))) { slot = (slot + 1) & (slots.length - 1); slotValue = slots[slot]; } Result value = slotValue; if (value == null) { slots[slot] = new Result(temp); byte[] bytes = new byte[size]; System.arraycopy(key, offset, bytes, 0, size); keys[slot] = bytes; } else { value.min = Math.min(value.min, temp); value.max = Math.max(value.max, temp); value.sum += temp; value.count += 1; } } // Get all pairs public List getAll() { List result = new ArrayList<>(slots.length); for (int i = 0; i < slots.length; i++) { Result slotValue = slots[i]; if (slotValue != null) { result.add(new Entry(keys[i], slotValue)); } } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_baseline.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.util.stream.Collectors.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collector; public class CalculateAverage_baseline { private static final String FILE = "./measurements.txt"; private static record Measurement(String station, double value) { private Measurement(String[] parts) { this(parts[0], Double.parseDouble(parts[1])); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } public static void main(String[] args) throws IOException { // Map measurements1 = Files.lines(Paths.get(FILE)) // .map(l -> l.split(";")) // .collect(groupingBy(m -> m[0], averagingDouble(m -> Double.parseDouble(m[1])))); // // measurements1 = new TreeMap<>(measurements1.entrySet() // .stream() // .collect(toMap(e -> e.getKey(), e -> Math.round(e.getValue() * 10.0) / 10.0))); // System.out.println(measurements1); Collector collector = Collector.of( MeasurementAggregator::new, (a, m) -> { a.min = Math.min(a.min, m.value); a.max = Math.max(a.max, m.value); a.sum += m.value; a.count++; }, (agg1, agg2) -> { var res = new MeasurementAggregator(); res.min = Math.min(agg1.min, agg2.min); res.max = Math.max(agg1.max, agg2.max); res.sum = agg1.sum + agg2.sum; res.count = agg1.count + agg2.count; return res; }, agg -> { return new ResultRow(agg.min, (Math.round(agg.sum * 10.0) / 10.0) / agg.count, agg.max); }); Map measurements = new TreeMap<>(Files.lines(Paths.get(FILE)) .map(l -> new Measurement(l.split(";"))) .collect(groupingBy(m -> m.station(), collector))); System.out.println(measurements); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_baseline_original_rounding.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.util.stream.Collectors.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collector; /** * This is the original version of the baseline implementation. It contains a * rounding bug, which can cause calculated mean values to be off by 0.1. See * {@link CalculateAverage_baseline} for the correct behavior. This version here * is only kept for reference, in particular for determining whether an * implementation is valid with the old behavior. Any new or updated entries to * the challenge must conform to the correct behavior as implemented by * {@code CalculateAverage_baseline}. */ public class CalculateAverage_baseline_original_rounding { private static final String FILE = "./measurements.txt"; private static record Measurement(String station, double value) { private Measurement(String[] parts) { this(parts[0], Double.parseDouble(parts[1])); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } public static void main(String[] args) throws IOException { // Map measurements1 = Files.lines(Paths.get(FILE)) // .map(l -> l.split(";")) // .collect(groupingBy(m -> m[0], averagingDouble(m -> Double.parseDouble(m[1])))); // // measurements1 = new TreeMap<>(measurements1.entrySet() // .stream() // .collect(toMap(e -> e.getKey(), e -> Math.round(e.getValue() * 10.0) / 10.0))); // System.out.println(measurements1); Collector collector = Collector.of( MeasurementAggregator::new, (a, m) -> { a.min = Math.min(a.min, m.value); a.max = Math.max(a.max, m.value); a.sum += m.value; a.count++; }, (agg1, agg2) -> { var res = new MeasurementAggregator(); res.min = Math.min(agg1.min, agg2.min); res.max = Math.max(agg1.max, agg2.max); res.sum = agg1.sum + agg2.sum; res.count = agg1.count + agg2.count; return res; }, agg -> { return new ResultRow(agg.min, agg.sum / agg.count, agg.max); }); Map measurements = new TreeMap<>(Files.lines(Paths.get(FILE)) .map(l -> new Measurement(l.split(";"))) .collect(groupingBy(m -> m.station(), collector))); System.out.println(measurements); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_berry120.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; public class CalculateAverage_berry120 { private static final String FILE = "./measurements.txt"; // TODO: Tweak this number? public static final int NUM_VIRTUAL_THREADS = 1000; public static final boolean DEBUG = false; static class TemperatureSummary implements Comparable { byte[] name; int min; int max; int total; int sampleCount; public TemperatureSummary(byte[] name, int min, int max, int total, int sampleCount) { this.name = name; this.min = min; this.max = max; this.total = total; this.sampleCount = sampleCount; } @Override public int compareTo(TemperatureSummary o) { return new String(name).compareTo(new String(o.name)); } @Override public String toString() { return "TemperatureSummary{" + "name=" + new String(name) + ", min=" + min + ", max=" + max + ", total=" + total + ", sampleCount=" + sampleCount + '}'; } } public static void main(String[] args) throws Exception { long time = System.currentTimeMillis(); Path path = Path.of(FILE); RandomAccessFile file = new RandomAccessFile(path.toFile(), "r"); FileChannel channel = file.getChannel(); long size = Files.size(path); int splitSize = size < 10_000_000 ? 1 : (NUM_VIRTUAL_THREADS - 1); long inc = (int) (size / splitSize); List positions = new ArrayList<>(); positions.add(0L); MemorySegment segment = channel.map(FileChannel.MapMode.READ_ONLY, 0, Files.size(path), Arena.ofShared()); long pos = 0; for (int i = 0; i < splitSize; i++) { long endPos = pos + inc - 1; while (segment.get(ValueLayout.JAVA_BYTE, endPos) != '\n') { endPos--; } pos = endPos + 1; positions.add(pos); } positions.add(size); if (DEBUG) System.out.println("WORKED OUT SPLITS: " + (System.currentTimeMillis() - time)); List threads = new ArrayList<>(NUM_VIRTUAL_THREADS); List> maps = Collections.synchronizedList(new ArrayList<>()); for (int split = 0; split < positions.size() - 1; split++) { long position = positions.get(split); long positionEnd = positions.get(split + 1); threads.add(Thread.ofVirtual().start(() -> { // TODO: Custom faster map? Map map = new HashMap<>(); maps.add(map); // Care much less about this map, only used if collisions in the first Map backupMap = new HashMap<>(); maps.add(backupMap); boolean processingPlaceName = true; byte[] placeName = new byte[100]; int placeNameIdx = 0; byte[] digits = new byte[100]; int digitIdx = 0; for (long address = position; address < positionEnd; address++) { byte b = segment.get(ValueLayout.JAVA_BYTE, address); if (b == 10) { int rollingHash = 5381; for (int i = 0; i < placeNameIdx; i++) { rollingHash = (((rollingHash << 5) + rollingHash) + placeName[i]) & 0xFFFFF; } var existingTemperatureSummary = map.get(rollingHash); int num = parse(digits, digitIdx - 1); if (existingTemperatureSummary == null) { byte[] thisPlace = new byte[placeNameIdx]; System.arraycopy(placeName, 0, thisPlace, 0, placeNameIdx); map.put(rollingHash, new TemperatureSummary(thisPlace, num, num, num, 1)); } else if (!Arrays.equals(placeName, 0, placeNameIdx, existingTemperatureSummary.name, 0, existingTemperatureSummary.name.length)) { /* * This block will be slow - don't really care, should be very rare */ if (DEBUG) System.out.println("BAD: COLLISION!"); byte[] thisPlace = new byte[placeNameIdx]; System.arraycopy(placeName, 0, thisPlace, 0, placeNameIdx); String backupKey = new String(thisPlace); var backupExistingTemperatureSummary = backupMap.get(backupKey); if (backupExistingTemperatureSummary == null) { backupMap.put(backupKey, new TemperatureSummary(thisPlace, num, num, num, 1)); } else { backupExistingTemperatureSummary.max = (Math.max(num, backupExistingTemperatureSummary.max)); backupExistingTemperatureSummary.min = (Math.min(num, backupExistingTemperatureSummary.min)); backupExistingTemperatureSummary.total += num; backupExistingTemperatureSummary.sampleCount++; } /* * End slow block */ } else { existingTemperatureSummary.max = (Math.max(num, existingTemperatureSummary.max)); existingTemperatureSummary.min = (Math.min(num, existingTemperatureSummary.min)); existingTemperatureSummary.total += num; existingTemperatureSummary.sampleCount++; } processingPlaceName = true; placeNameIdx = 0; digitIdx = 0; } else if (b == ';') { processingPlaceName = false; } else if (processingPlaceName) { placeName[placeNameIdx++] = b; } else { digits[digitIdx++] = b; } } })); } if (DEBUG) { System.out.println("STARTED THREADS: " + (System.currentTimeMillis() - time)); } for (Thread thread : threads) { thread.join(); } TreeMap mergedMap = new TreeMap<>(); for (var map : maps) { for (TemperatureSummary t1 : map.values()) { if (t1 == null) continue; var t2 = mergedMap.get(new String(t1.name)); if (t2 == null) { mergedMap.put(new String(t1.name), t1); } else { var merged = new TemperatureSummary(t1.name, Math.min(t1.min, t2.min), Math.max(t1.max, t2.max), t1.total + t2.total, t1.sampleCount + t2.sampleCount); mergedMap.put(new String(t1.name), merged); } } } boolean first = true; StringBuilder output = new StringBuilder(16_000); output.append("{"); for (var value : new TreeSet<>(mergedMap.values())) { if (first) { first = false; } else { output.append(", "); } output.append(new String(value.name)).append("=").append((double) value.min / 10).append("/") .append(String.format("%.1f", ((double) value.total / value.sampleCount / 10))).append("/").append((double) value.max / 10); } output.append("}"); System.out.println(output); // if (DEBUG) // System.out.println("CORRECT: " + output.toString().equals(CORRECT)); if (DEBUG) System.out.println("TOTAL TIME: " + (System.currentTimeMillis() - time)); } private static int parse(byte[] arr, int len) { // TODO: SIMD? int num = 0; for (int mI = len, m = 1; mI >= 0; mI--) { byte d = arr[mI]; if (d == '.') { } else if (d == '-') { num = -num; m *= 10; } else { num += (d & 0xF) * m; m *= 10; } } return num; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_bjhara.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.*; import java.nio.channels.*; import java.nio.file.*; import java.util.*; import java.util.stream.*; public class CalculateAverage_bjhara { private static final String FILE = "./measurements.txt"; private static class Measurement { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } public static Measurement combine(Measurement m1, Measurement m2) { var mres = new Measurement(); mres.min = m1.min < m2.min ? m1.min : m2.min; mres.max = m1.max > m2.max ? m1.max : m2.max; mres.sum = m1.sum + m2.sum; mres.count = m1.count + m2.count; return mres; } } public static void main(String[] args) throws IOException { try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE), EnumSet.of(StandardOpenOption.READ))) { var cities = splitFileChannel(fileChannel) .parallel() .map(CalculateAverage_bjhara::parseBuffer) .collect(Collectors.reducing(CalculateAverage_bjhara::combineMaps)); var sortedCities = new TreeMap<>(cities.orElseThrow()); System.out.println(sortedCities); } } private static Map combineMaps(Map map1, Map map2) { for (var entry : map2.entrySet()) { map1.merge(entry.getKey(), entry.getValue(), Measurement::combine); } return map1; } private static Stream splitFileChannel(final FileChannel fileChannel) throws IOException { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new Iterator() { private static final long CHUNK_SIZE = 1024 * 1024 * 10L; private final long size = fileChannel.size(); private long start = 0; @Override public boolean hasNext() { return start < size; } @Override public ByteBuffer next() { try { MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, Math.min(CHUNK_SIZE, size - start)); // don't split the data in the middle of lines // find the closest previous newline int realEnd = mappedByteBuffer.limit() - 1; while (mappedByteBuffer.get(realEnd) != '\n') realEnd--; realEnd++; mappedByteBuffer.limit(realEnd); start += realEnd; return mappedByteBuffer; } catch (IOException ex) { throw new UncheckedIOException(ex); } } }, Spliterator.IMMUTABLE), false); } private static Map parseBuffer(ByteBuffer bb) { Map cities = new HashMap<>(); final int limit = bb.limit(); final byte[] buffer = new byte[128]; while (bb.position() < limit) { final int currentPosition = bb.position(); // find the ; separator int separator = currentPosition; while (separator != limit && bb.get(separator) != ';') separator++; // find the end of the line int end = separator + 1; while (end != limit && !Character.isWhitespace((char) bb.get(end))) end++; // get the name as a string int nameLength = separator - currentPosition; bb.get(buffer, 0, nameLength); String city = new String(buffer, 0, nameLength); // get rid of the separator bb.get(); // get the double value int valueLength = end - separator - 1; bb.get(buffer, 0, valueLength); String valueStr = new String(buffer, 0, valueLength); double value = Double.parseDouble(valueStr); // and get rid of the new line (handle both kinds) byte newline = bb.get(); if (newline == '\r') bb.get(); // update the map with the new measurement Measurement agg = cities.get(city); if (agg == null) { agg = new Measurement(); cities.put(city, agg); } agg.min = agg.min < value ? agg.min : value; agg.max = agg.max > value ? agg.max : value; agg.sum += value; agg.count++; } return cities; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_breejesh.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CalculateAverage_breejesh { private static final String FILE = "./measurements.txt"; private static final int TWO_BYTE_TO_INT = 480 + 48; // 48 is the ASCII code for '0' private static final int THREE_BYTE_TO_INT = 4800 + 480 + 48; private static final class Measurement { private int min; private int max; private int total; private int count; public Measurement(int value) { this.min = value; this.max = value; this.total = value; this.count = 1; } @Override public String toString() { StringBuilder result = new StringBuilder(); result.append(min / 10.0); result.append("/"); result.append(Math.round(((double) total) / count) / 10.0); result.append("/"); result.append(max / 10.0); return result.toString(); } private void append(int min, int max, int total, int count) { if (min < this.min) this.min = min; if (max > this.max) this.max = max; this.total += total; this.count += count; } public void append(int value) { append(value, value, value, 1); } public void merge(Measurement other) { append(other.min, other.max, other.total, other.count); } } public static void main(String[] args) throws Exception { // long start = System.currentTimeMillis(); // Find system details to determine cores and var file = new File(args.length > 0 ? args[0] : FILE); long fileSize = file.length(); var numberOfCores = fileSize > 1_000_000 ? Runtime.getRuntime().availableProcessors() : 1; var splitSectionSize = (int) Math.min(Integer.MAX_VALUE, fileSize / numberOfCores); // bytebuffer position is an int, so can be max Integer.MAX_VALUE var segmentCount = (int) (fileSize / splitSectionSize); // Divide file into segments ExecutorService executor = Executors.newFixedThreadPool(segmentCount); List>> futures = new ArrayList<>(); for (int i = 0; i < segmentCount; i++) { long sectionStart = i * (long) splitSectionSize; long sectionEnd = Math.min(fileSize, sectionStart + splitSectionSize + 100); var fileChannel = (FileChannel) Files.newByteChannel(file.toPath(), StandardOpenOption.READ); CompletableFuture> future = CompletableFuture.supplyAsync(() -> { MappedByteBuffer currentBuffer = null; try { currentBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, sectionStart, sectionEnd - sectionStart); } catch (IOException e) { throw new RuntimeException(e); } // Skip till new line for unequal segments, not to be done for first section if (sectionStart > 0) { while (currentBuffer.get() != '\n') ; } Map map = new HashMap<>(); while (currentBuffer.position() < splitSectionSize) { // Read station String str = getStationFromBuffer(currentBuffer); // Read number int value = getValueFromBuffer(currentBuffer); if (map.containsKey(str)) { map.get(str).append(value); } else { map.put(str, new Measurement(value)); } } return map; }, executor); futures.add(future); } CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); Map finalMap = new TreeMap<>(); for (CompletableFuture> future : futures) { Map map = future.get(); map.keySet().stream().forEach( key -> { if (finalMap.containsKey(key)) { finalMap.get(key).merge(map.get(key)); } else { finalMap.put(key, map.get(key)); } }); } System.out.println(finalMap); // System.out.printf("Time %s", System.currentTimeMillis() - start); System.exit(0); } private static String getStationFromBuffer(MappedByteBuffer currentBuffer) { byte currentByte; var byteCounter = 0; var buffer = new byte[100]; while ((currentByte = currentBuffer.get()) != ';') { buffer[byteCounter++] = currentByte; } return new String(buffer, 0, byteCounter, StandardCharsets.UTF_8); } private static int getValueFromBuffer(MappedByteBuffer currentBuffer) { int value; byte[] nums = new byte[4]; currentBuffer.get(nums); if (nums[1] == '.') { // case of n.n value = (nums[0] * 10 + nums[2] - TWO_BYTE_TO_INT); } else { if (nums[3] == '.') { // case of -nn.n value = -(nums[1] * 100 + nums[2] * 10 + currentBuffer.get() - THREE_BYTE_TO_INT); } else if (nums[0] == '-') { // case of -n.n value = -(nums[1] * 10 + nums[3] - TWO_BYTE_TO_INT); } else { // case of nn.n value = (nums[0] * 100 + nums[1] * 10 + nums[3] - THREE_BYTE_TO_INT); } currentBuffer.get(); // new line } return value; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_bufistov.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import static java.lang.Math.toIntExact; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.io.FileInputStream; import java.io.IOException; import java.util.concurrent.Future; class ByteArrayWrapper { private final byte[] data; public ByteArrayWrapper(byte[] data) { this.data = data; } @Override public boolean equals(Object other) { return Arrays.equals(data, ((ByteArrayWrapper) other).data); } @Override public int hashCode() { return Arrays.hashCode(data); } } public class CalculateAverage_bufistov { static class ResultRow { byte[] station; String stationString; long min, max, count, suma; ResultRow() { } ResultRow(byte[] station, long value) { this.station = new byte[station.length]; System.arraycopy(station, 0, this.station, 0, station.length); this.min = value; this.max = value; this.count = 1; this.suma = value; } ResultRow(long value) { this.min = value; this.max = value; this.count = 1; this.suma = value; } void setStation(long startPosition, long endPosition) { this.station = new byte[(int) (endPosition - startPosition)]; for (int i = 0; i < this.station.length; ++i) { this.station[i] = UNSAFE.getByte(startPosition + i); } } public String toString() { stationString = new String(station, StandardCharsets.UTF_8); return stationString + "=" + round(min / 10.0) + "/" + round(suma / 10.0 / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } void update(long newValue) { this.count += 1; this.suma += newValue; if (newValue < this.min) { this.min = newValue; } else if (newValue > this.max) { this.max = newValue; } } ResultRow merge(ResultRow another) { this.count += another.count; this.suma += another.suma; this.min = Math.min(this.min, another.min); this.max = Math.max(this.max, another.max); return this; } } static class OpenHash { ResultRow[] data; int dataSizeMask; // ResultRow metrics = new ResultRow(); public OpenHash(int capacityPow2) { assert capacityPow2 <= 20; int dataSize = 1 << capacityPow2; dataSizeMask = dataSize - 1; data = new ResultRow[dataSize]; } int hashByteArray(byte[] array) { int result = 0; long mask = 0; for (int i = 0; i < array.length; ++i, mask = ((mask + 1) & 3)) { result += array[i] << mask; } return result & dataSizeMask; } void merge(byte[] station, long value, int hashValue) { while (data[hashValue] != null && !Arrays.equals(station, data[hashValue].station)) { hashValue += 1; hashValue &= dataSizeMask; } if (data[hashValue] == null) { data[hashValue] = new ResultRow(station, value); } else { data[hashValue].update(value); } // metrics.update(delta); } void merge(byte[] station, long value) { merge(station, value, hashByteArray(station)); } void merge(final long startPosition, long endPosition, int hashValue, long value) { while (data[hashValue] != null && !equalsToStation(startPosition, endPosition, data[hashValue].station)) { hashValue += 1; hashValue &= dataSizeMask; } if (data[hashValue] == null) { data[hashValue] = new ResultRow(value); data[hashValue].setStation(startPosition, endPosition); } else { data[hashValue].update(value); } } boolean equalsToStation(long startPosition, long endPosition, byte[] station) { if (endPosition - startPosition != station.length) { return false; } for (int i = 0; i < station.length; ++i, ++startPosition) { if (UNSAFE.getByte(startPosition) != station[i]) return false; } return true; } HashMap toJavaHashMap() { HashMap result = new HashMap<>(20000); for (int i = 0; i < data.length; ++i) { if (data[i] != null) { var key = new ByteArrayWrapper(data[i].station); result.put(key, data[i]); } } return result; } } static final Unsafe UNSAFE; static { try { Field unsafe = Unsafe.class.getDeclaredField("theUnsafe"); unsafe.setAccessible(true); UNSAFE = (Unsafe) unsafe.get(Unsafe.class); } catch (Throwable e) { throw new RuntimeException(e); } } static final long LINE_SEPARATOR = '\n'; public static class FileRead implements Callable> { private final FileChannel fileChannel; private long currentLocation; private long bytesToRead; private static final int hashCapacityPow2 = 18; static final int hashCapacityMask = (1 << hashCapacityPow2) - 1; public FileRead(FileChannel fileChannel, long startLocation, long bytesToRead, boolean firstSegment) { this.fileChannel = fileChannel; this.currentLocation = startLocation; this.bytesToRead = bytesToRead; } @Override public HashMap call() throws IOException { try { OpenHash openHash = new OpenHash(hashCapacityPow2); log("Reading the channel: " + currentLocation + ":" + bytesToRead); if (currentLocation > 0) { toLineBeginPrefix(); } toLineBeginSuffix(); var memorySegment = fileChannel.map(FileChannel.MapMode.READ_ONLY, currentLocation, bytesToRead, Arena.global()); currentLocation = memorySegment.address(); processChunk(openHash); log("Done Reading the channel: " + currentLocation + ":" + bytesToRead); return openHash.toJavaHashMap(); } catch (Exception e) { e.printStackTrace(); throw e; } } byte getByte(long position) throws IOException { MappedByteBuffer byteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, position, 1); return byteBuffer.get(); } void toLineBeginPrefix() throws IOException { while (getByte(currentLocation - 1) != LINE_SEPARATOR) { ++currentLocation; --bytesToRead; } } void toLineBeginSuffix() throws IOException { while (getByte(currentLocation + bytesToRead - 1) != LINE_SEPARATOR) { ++bytesToRead; } } void processChunk(OpenHash result) { long nameBegin = currentLocation; long nameEnd = -1; long numberBegin = -1; int currentHash = 0; int currentMask = 0; int nameHash = 0; long end = currentLocation + bytesToRead; byte nextByte; for (; currentLocation < end; ++currentLocation) { nextByte = UNSAFE.getByte(currentLocation); if (nextByte == ';') { nameEnd = currentLocation; numberBegin = currentLocation + 1; nameHash = currentHash & hashCapacityMask; } else if (nextByte == LINE_SEPARATOR) { long value = getValue(numberBegin, currentLocation); // log("Station name: '" + getStationName(nameBegin, nameEnd) + "' value: " + value + " hash: " + nameHash); result.merge(nameBegin, nameEnd, nameHash, value); nameBegin = currentLocation + 1; currentHash = 0; currentMask = 0; } else { currentHash += (nextByte << currentMask); currentMask = (currentMask + 1) & 3; } } } long getValue(long startLocation, long endLocation) { byte nextByte = UNSAFE.getByte(startLocation); boolean negate = nextByte == '-'; long result = negate ? 0 : nextByte - '0'; for (long i = startLocation + 1; i < endLocation; ++i) { nextByte = UNSAFE.getByte(i); if (nextByte != '.') { result *= 10; result += nextByte - '0'; } } return negate ? -result : result; } String getStationName(long from, long to) { byte[] bytes = new byte[(int) (to - from)]; for (int i = 0; i < bytes.length; ++i) { bytes[i] = UNSAFE.getByte(from + i); } return new String(bytes, StandardCharsets.UTF_8); } } public static void main(String[] args) throws Exception { String fileName = "measurements.txt"; if (args.length > 0 && args[0].length() > 0) { fileName = args[0]; } log("InputFile: " + fileName); FileInputStream fileInputStream = new FileInputStream(fileName); int numThreads = 2 * Runtime.getRuntime().availableProcessors(); if (args.length > 1) { numThreads = Integer.parseInt(args[1]); } log("NumThreads: " + numThreads); FileChannel channel = fileInputStream.getChannel(); final long fileSize = channel.size(); long remaining_size = fileSize; long chunk_size = Math.min((fileSize + numThreads - 1) / numThreads, Integer.MAX_VALUE - 5); ExecutorService executor = Executors.newFixedThreadPool(numThreads); long startLocation = 0; ArrayList>> results = new ArrayList<>(numThreads); var fileChannel = FileChannel.open(Paths.get(fileName)); boolean firstSegment = true; while (remaining_size > 0) { long actualSize = Math.min(chunk_size, remaining_size); results.add(executor.submit(new FileRead(fileChannel, startLocation, toIntExact(actualSize), firstSegment))); firstSegment = false; remaining_size -= actualSize; startLocation += actualSize; } executor.shutdown(); // Wait for all threads to finish while (!executor.isTerminated()) { Thread.yield(); } log("Finished all threads"); fileInputStream.close(); HashMap result = new HashMap<>(20000); for (var future : results) { for (var entry : future.get().entrySet()) { result.merge(entry.getKey(), entry.getValue(), ResultRow::merge); } } ResultRow[] finalResult = result.values().toArray(new ResultRow[0]); for (var row : finalResult) { row.toString(); } Arrays.sort(finalResult, Comparator.comparing(a -> a.stationString)); System.out.println("{" + String.join(", ", Arrays.stream(finalResult).map(ResultRow::toString).toList()) + "}"); log("All done!"); } static void log(String message) { // System.err.println(Instant.now() + "[" + Thread.currentThread().getName() + "]: " + message); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_bytesfellow.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.IntStream; public class CalculateAverage_bytesfellow { public static final String CPU_CORES_1BRC_ENV_VARIABLE = "CPU_CORES_1BRC"; private static final byte Separator = ';'; private static final double SchedulerCpuRatio = 0.4; private static final int availableCpu = System.getenv(CPU_CORES_1BRC_ENV_VARIABLE) != null ? Integer.parseInt(System.getenv(CPU_CORES_1BRC_ENV_VARIABLE)) : Runtime.getRuntime().availableProcessors(); private static final int SchedulerPoolSize = Math.max((int) (availableCpu * SchedulerCpuRatio), 1); private static final int SchedulerQueueSize = Math.min(SchedulerPoolSize * 3, 12); private static final int PartitionsNumber = Math.max((availableCpu - SchedulerPoolSize), 1); private static final int PartitionExecutorQueueSize = 1000; private static final int InputStreamBlockSize = 4096; private static final int InputStreamReadBufferLen = 250 * InputStreamBlockSize; static class Partition { private static final AtomicInteger cntr = new AtomicInteger(-1); private final Map partitionResult = new HashMap<>(10000); // as per requirement we have not more than 10K keys private final AtomicInteger leftToExecute = new AtomicInteger(0); private final String name = "partition-" + cntr.incrementAndGet(); private final Executor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(PartitionExecutorQueueSize) { // some limit to avoid OOM @Override public boolean offer(Runnable runnable) { try { put(runnable); // block if limit was exceeded } catch (InterruptedException e) { throw new RuntimeException(e); } return true; } }, r -> { Thread t = new Thread(r); t.setDaemon(true); t.setName(name); return t; }); public void scheduleToProcess(byte[] slice, List lines) { if (!lines.isEmpty()) { leftToExecute.incrementAndGet(); executor.execute( () -> { for (int i = 0; i < lines.size(); i++) { LineParams lineParams = lines.get(i); Measurement measurement = getMeasurement(slice, lineParams); MeasurementAggregator measurementAggregator = partitionResult.get(measurement.station); if (measurementAggregator == null) { partitionResult.put(new Station(measurement.station), new MeasurementAggregator().withMeasurement(measurement)); } else { measurementAggregator.withMeasurement(measurement); } } leftToExecute.decrementAndGet(); }); } } public void materializeNames() { partitionResult.keySet().forEach(Station::materializeName); } public Map getResult() { return partitionResult; } public boolean allTasksCompleted() { return leftToExecute.get() == 0; } } record LineParams(int start, int length) { } static class Partitioner { private final List allPartitions = new ArrayList<>(); private final int partitionsSize; AtomicInteger jobsScheduled = new AtomicInteger(0); final Executor scheduler = new ThreadPoolExecutor(SchedulerPoolSize, SchedulerPoolSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(SchedulerQueueSize) { // some limit to avoid OOM @Override public Runnable take() throws InterruptedException { return super.take(); } @Override public boolean offer(Runnable runnable) { try { put(runnable); // preventing unlimited scheduling due to possible OOM } catch (InterruptedException e) { throw new RuntimeException(e); } return true; } }, r -> { Thread t = new Thread(r); t.setDaemon(true); t.setName("scheduler"); return t; }); Partitioner(int partitionsSize) { IntStream.range(0, partitionsSize).forEach((i) -> allPartitions.add(new Partition())); this.partitionsSize = partitionsSize; } private int partitionsSize() { return partitionsSize; } void processSlice(byte[] slice) { jobsScheduled.incrementAndGet(); scheduler.execute(() -> { List> partitionedLines = new ArrayList<>(partitionsSize()); // allocate some capacity, assuming that on average lines are half of the max (407 bytes) length IntStream.range(0, partitionsSize()).forEach((p) -> partitionedLines.add(new ArrayList<>(slice.length / 407 / 2))); int start = 0; int i = 0; int startCharLen = 0; while (i < slice.length) { if (slice[i] == '\n' || i == (slice.length - 1)) { int lineLength = i - start + (i == (slice.length - 1) ? 1 : 0); LineParams lineParams = new LineParams(start, lineLength); int partitioningCode = getPartitioningCode(slice, start, getUtf8CharNumberOfBytes(slice[start])); int partition = computePartition(partitioningCode); partitionedLines.get(partition).add(lineParams); start = i + 1; } i++; } processPartitionedBatch(slice, partitionedLines); jobsScheduled.decrementAndGet(); }); } private static byte[] getLine(byte[] slice, int lineLength, int start) { byte[] line = new byte[lineLength]; System.arraycopy(slice, start, line, 0, lineLength); return line; } private void processPartitionedBatch(byte[] slice, List> partitionedLines) { for (int i = 0; i < partitionedLines.size(); i++) { allPartitions.get(i).scheduleToProcess(slice, partitionedLines.get(i)); } } private int computePartition(int code) { return Math.abs(code % partitionsSize()); } private static int getPartitioningCode(byte[] line, int start, int utf8CharNumberOfBytes) { // seems good enough if (utf8CharNumberOfBytes == 4) { return line[start] + line[start + 1] + line[start + 2] + line[start + 3]; } else if (utf8CharNumberOfBytes == 3) { return line[start] + line[start + 1] + line[start + 2]; } else if (utf8CharNumberOfBytes == 2) { return line[start] + line[start + 1]; } else { return line[start]; } } SortedMap getAllResults() { allPartitions.parallelStream().forEach(Partition::materializeNames); SortedMap result = new TreeMap<>(); allPartitions.forEach((p) -> result.putAll(p.getResult())); return result; } public boolean allTasksCompleted() { return allPartitions.stream().allMatch(Partition::allTasksCompleted); } } private static final String FILE = "./measurements.txt"; public static class Station implements Comparable { private final byte[] inputSlice; private final int hash; private final int startIdx; private final int len; private volatile String nameAsString; public Station(byte[] inputSlice, int startIdx, int len) { this.inputSlice = inputSlice; this.startIdx = startIdx; this.len = len; this.hash = hashcodeFast(); } public Station(Station from) { this.inputSlice = new byte[from.len]; System.arraycopy(from.inputSlice, from.startIdx, this.inputSlice, 0, from.len); this.startIdx = 0; this.len = from.len; this.hash = from.hash; } private int hashcodeFast() { if (len == 0) { return 0; } else if (len == 1) { return inputSlice[startIdx] * 109; } else if (len == 2) { return inputSlice[startIdx + 1] * 109 * 109 + inputSlice[startIdx]; } else if (len == 3) { return inputSlice[startIdx + 2] * 109 * 109 * 109 + inputSlice[startIdx + 1] * 109 * 109 + inputSlice[startIdx]; } else { return inputSlice[startIdx + 3] * 109 * 109 * 109 * 109 + inputSlice[startIdx + 2] * 109 * 109 * 109 + inputSlice[startIdx + 1] * 109 * 109 + inputSlice[startIdx]; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Station station = (Station) o; if (len != station.len) { return false; } return Arrays.equals(inputSlice, startIdx, startIdx + len, station.inputSlice, station.startIdx, station.startIdx + len); } @Override public int hashCode() { return hash; } @Override public int compareTo(Station o) { return materializeName().compareTo(o.materializeName()); // } public String materializeName() { if (nameAsString == null) { byte[] nameForMaterialization = new byte[len]; System.arraycopy(inputSlice, startIdx, nameForMaterialization, 0, len); nameAsString = new String(nameForMaterialization, StandardCharsets.UTF_8); } return nameAsString; } @Override public String toString() { return materializeName(); } } private record Measurement(Station station, long value) { } private record ResultRow(long min, long sum, long count, long max) { public String toString() { return fakeDouble(min) + "/" + round((double) sum / (double) count / 10.0) + "/" + fakeDouble(max); } private String fakeDouble(long value) { long positiveValue = value < 0 ? -value : value; long wholePart = positiveValue / 10; String positiveDouble = wholePart + "." + (positiveValue - wholePart * 10); return (value < 0 ? "-" : "") + positiveDouble; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } public static class MeasurementAggregator { private long min = Long.MAX_VALUE; private long max = Long.MIN_VALUE; private long sum; private long count; MeasurementAggregator withMeasurement(Measurement m) { min = Math.min(min, m.value); max = Math.max(max, m.value); sum += m.value; count++; return this; } @Override public String toString() { return new ResultRow(min, sum, count, max).toString(); } } private static long parseToLongIgnoringDecimalPoint(byte[] slice, int startIndex, int len) { long value = 0; int start = startIndex; if (slice[startIndex] == '-') { start = startIndex + 1; } for (int i = start; i < startIndex + len; i++) { if (slice[i] == '.') { continue; } if (i > 0) { value = multipleByTen(value); // *= 10; } value += digitAsLong(slice, i); } return start > startIndex ? -value : value; } private static long multipleByTen(long value) { return (value << 3) + (value << 1); } private static long digitAsLong(byte[] digits, int position) { return (digits[position] - 48); } public static void main(String[] args) throws IOException { Partitioner partitioner = new Partitioner(PartitionsNumber); try (FileInputStream fileInputStream = new FileInputStream(FILE)) { parseStreamWithBytes(fileInputStream, InputStreamReadBufferLen, partitioner::processSlice); } catch (Exception e) { throw new RuntimeException(e); } showResults(partitioner); } static void parseStreamWithBytes(InputStream inputStream, int bufferLen, Consumer sliceConsumer) throws IOException { byte[] byteArray = new byte[bufferLen]; int offset = 0; int lenToRead = bufferLen; int readLen; while ((readLen = inputStream.read(byteArray, offset, lenToRead)) > -1) { if (readLen == 0) { continue; } int traverseLen = Math.min(offset + readLen, bufferLen); int lastLineBreakInSlicePosition = traverseLen; for (int j = traverseLen - 1; j >= 0; j--) { if (byteArray[j] == '\n') { lastLineBreakInSlicePosition = j + 1; break; } } if (lastLineBreakInSlicePosition == traverseLen) { // todo: end of line was not found in a slice? } int sliceSize = lastLineBreakInSlicePosition / SchedulerPoolSize; int s = 0; int j = Math.min(sliceSize, lastLineBreakInSlicePosition - 1); while (s < lastLineBreakInSlicePosition && j < lastLineBreakInSlicePosition) { if (byteArray[j] == '\n') { int len = j - s; byte[] slice = new byte[len]; System.arraycopy(byteArray, s, slice, 0, len); sliceConsumer.accept(slice); s = j + 1; j = Math.min(s + sliceSize, lastLineBreakInSlicePosition - 1); } else { j++; } } if (s < traverseLen && lastLineBreakInSlicePosition < traverseLen) { // some tail left, carry it over to the next read int len = traverseLen - s; System.arraycopy(byteArray, s, byteArray, 0, len); offset = len; lenToRead = bufferLen - len; } else { offset = 0; lenToRead = bufferLen; } } } static int getUtf8CharNumberOfBytes(byte firstByteOfChar) { int masked = firstByteOfChar & 0b11111000; if (masked == 0b11110000) { return 4; } else if (masked == 0b11100000) { return 3; } else if (masked == 0b11000000) { return 2; } else { return 1; } } static void showResults(Partitioner partitioner) { CountDownLatch c = new CountDownLatch(1); partitioner.scheduler.execute(() -> { try { // check if any unprocessed slices while (partitioner.jobsScheduled.get() > 0) { } // check if anything left in partitions while (!partitioner.allTasksCompleted()) { } SortedMap result = partitioner.getAllResults(); System.out.println(result); // output aggregated measurements according to the requirement } catch (Exception e) { System.out.println(e); } c.countDown(); }); try { c.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } } private static Measurement getMeasurement(byte[] slice, LineParams lineParams) { int idx = lastIndexOfSeparator(slice, lineParams); return new Measurement( new Station(slice, lineParams.start, idx - lineParams.start), parseToLongIgnoringDecimalPoint(slice, idx + 1, lineParams.start + lineParams.length - (idx + 1))); } private static int lastIndexOfSeparator(byte[] slice, LineParams lineParams) { // hacky - we know that from the end of the line we have only // single byte characters // -2 is also hacky since we expect a particular format at the end of the line int lastIdx = lineParams.start + lineParams.length() - 1; if (slice[lastIdx - 3] == Separator) { return lastIdx - 3; } else if (slice[lastIdx - 4] == Separator) { return lastIdx - 4; } else if (slice[lastIdx - 5] == Separator) { return lastIdx - 5; } return -1; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_cb0s.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; public class CalculateAverage_cb0s { private static final String FILE = "./measurements.txt"; private static final int INPUT_BUFFER_SIZE = 1 << 16; // yields the best performance on my system... public static void main(String[] args) throws IOException, InterruptedException { run(); // benchmark(); } private static void benchmark() throws IOException { var startTime = System.currentTimeMillis(); for (int count = 0; count < 3; ++count) { run(); } var stopTime = System.currentTimeMillis(); System.out.println(STR."Running 3 times took: \{stopTime - startTime}ms (1 run: \{(stopTime - startTime) / 3}ms)"); } private static void run() throws IOException { var fileSize = getFileSize(); // for consistency for smaller files (actually a mess, could be solved more elegantly in the parsing step) var processors = Runtime.getRuntime().availableProcessors(); processors = Math.max(1, Math.min(processors, (int) fileSize / 106)); while (fileSize / processors < INPUT_BUFFER_SIZE && processors > 1) --processors; var chunkSize = fileSize / processors; System.out.write('{'); // for getting a bit more out of this solution, we don't check for null var mergedResults = IntStream.range(0, processors) .parallel() .mapToObj(i -> processChunk(i, chunkSize)) .reduce(TempResultStorage::merge).get(); var endResult = mergedResults.aggregatedResultsPreOrdered.stream() .map(Station::toString) .collect(Collectors.joining(", ")); System.out.write(endResult.getBytes()); System.out.write(new byte[]{ '}', '\n' }); } private static class MeasurementAggregator { public MeasurementAggregator(int initialValue) { min = initialValue; max = initialValue; count = 1; sum = initialValue; } public int min, max, count; // we need to long if the possible absolute sum is greater than 2^31 public long sum; } private record Station( MeasurementAggregator results, RawName rawName ) implements Comparable { @Override public boolean equals(Object otherObject) { if (otherObject instanceof Station otherStation) { return otherStation.rawName.equals(rawName); } return false; } @Override public int compareTo(Station otherStation) { return rawName.compareTo(otherStation.rawName); } @Override public String toString() { return STR."\{rawName}=\{results.min/10.0}/\{Math.round(results.sum / (float) results.count) / 10.0}/\{results.max/10.0}"; } @Override public int hashCode() { return rawName.hashCode(); } } private record RawName( byte[] rawName ) implements Comparable { @Override public boolean equals(Object otherObject) { RawName otherRawName = (RawName) otherObject; return Arrays.equals(otherRawName.rawName, this.rawName); /* * Although being safer, comparing actually is a small bottleneck * if (otherObject instanceof RawName otherRawName) { * return Arrays.equals(otherRawName.rawName, this.rawName); * } * return false; */ } @Override public int hashCode() { return Arrays.hashCode(rawName); } @Override public String toString() { return new String(rawName, 0, rawName.length, StandardCharsets.UTF_8); } @Override public int compareTo(RawName otherRawName) { int result = 0; // Math.min is SLIGHTLY less efficient, but we don't care at this point var lowerIndex = Math.min(rawName.length, otherRawName.rawName.length); for (int i = 0; i < lowerIndex && result == 0; ++i) { result = Byte.compareUnsigned(rawName[i], otherRawName.rawName[i]); } return result == 0 ? rawName.length - otherRawName.rawName.length : result; } } private static class TempResultStorage { public void insertMeasurement(byte[] dataRow, int from, int to) { // 1st parse measurement var sepIndex = from + 1; while (dataRow[sepIndex] != ';') ++sepIndex; var parsedMeasurement = parseMeasurement(dataRow, sepIndex + 1, to); // 2nd handle if city occurs the first time var rawName = new RawName(Arrays.copyOfRange(dataRow, from, sepIndex)); var tempIndex = indexCache.get(rawName); if (tempIndex == null) { var aggregator = new MeasurementAggregator(parsedMeasurement); var tempStation = new Station(aggregator, rawName); aggregatedResults.add(tempStation); indexCache.put(rawName, aggregatedResults.size() - 1); aggregatedResultsPreOrdered.add(tempStation); return; } // or update already existing station var tempResults = aggregatedResults.get(tempIndex).results; // TODO: compare to: add simd vector storage and process once every 8 iterations tempResults.sum += parsedMeasurement; tempResults.count++; if (tempResults.max < parsedMeasurement) { tempResults.max = parsedMeasurement; } else if (tempResults.min > parsedMeasurement) { tempResults.min = parsedMeasurement; } } public TempResultStorage() { aggregatedResults = new ArrayList<>(INITIAL_RESULT_SIZE); indexCache = new HashMap<>(INITIAL_RESULT_SIZE); aggregatedResultsPreOrdered = new TreeSet<>(); } public static TempResultStorage merge(TempResultStorage storage0, TempResultStorage storage1) { // default case if (storage0 == null) { return storage1; } // TODO: Implementation with SIMD commands for (var station1 : storage1.aggregatedResults) { // System.out.println(station1.results.count + " " + station1.results.sum); var key = storage0.indexCache.get(station1.rawName); if (key == null) { storage0.aggregatedResults.add(station1); storage0.indexCache.put(station1.rawName, storage0.aggregatedResults.size() - 1); storage0.aggregatedResultsPreOrdered.add(station1); continue; } var station0 = storage0.aggregatedResults.get(key); station0.results.count += station1.results.count; station0.results.sum += station1.results.sum; if (station0.results.min > station1.results.min) { station0.results.min = station1.results.min; } if (station1.results.max > station0.results.max) { station0.results.max = station1.results.max; } } return storage0; } // the closer it is to the actual value the better -> for 10_000 stations 10_000 is obviously better private static final int INITIAL_RESULT_SIZE = 420; // we use a custom name mapping for faster access to aggregatedResults and easier sorting private final List aggregatedResults; private final TreeSet aggregatedResultsPreOrdered; private final HashMap indexCache; /** * Parses a char[] array to the contained number in a fixed point format. * The number can be between [-99.9, 99.9] (i.e. has either 2 or 3 digits and might contain a sign) * and represents a temperature measurement. * Note that no checking takes place. Incorrect formats yield unexpected results. * * @param dataRow char array actually containing the number * @param from the start index of the number inside the array (included) * @param to the end index of the number (not included, i.e. the char after the number or the length) * @return fixed point (int) representation of the contained measurement */ private int parseMeasurement(byte[] dataRow, int from, int to) { // almost branch-less solution int sign = -1 + 2 * ((dataRow[from] >> 4) & 1); int floatingPoint = dataRow[to - 1] - 48; int lastIntDigit = dataRow[to - 3] - 48; int firstIntDigit = to - from - 4 >= 0 ? (sign + 1) / 2 * dataRow[to - 4] - 48 : 0; if (to - from >= 4) { firstIntDigit = dataRow[to - 4] - 48; if (to - from == 4 && sign == -1) { firstIntDigit = 0; } } return (firstIntDigit * 100 + lastIntDigit * 10 + floatingPoint) * sign; } } private static TempResultStorage processChunk(int i, long chunkSize) { var storage = new TempResultStorage(); var readBuffer = new byte[INPUT_BUFFER_SIZE]; try (var inputStream = new BufferedInputStream(new FileInputStream(FILE), INPUT_BUFFER_SIZE)) { var readBytes = 0L; // we set it to one because our first loop will not register last read byte var readBytesDelta = 0; // preparation if (i != 0) { --readBytes; inputStream.skip(i * chunkSize - 1); int c; while ((c = inputStream.read()) != '\n' && c != -1) ++readBytes; } // actual parsing // worst case: only \n is missing for a whole line var carryOver = new byte[107]; var carryOverSize = 0; while (readBytes < chunkSize && inputStream.available() > 0) { readBytes += (readBytesDelta = inputStream.read(readBuffer, 0, readBuffer.length)); int from = 0, to = 0; if (carryOverSize != 0) { while (readBuffer[to] != '\n') ++to; System.arraycopy(readBuffer, from, carryOver, carryOverSize, to - from + 1); storage.insertMeasurement(carryOver, 0, carryOverSize + to - from); from = ++to; } // Actually looking 5 ahead instead of 1 at each new line // Minimal line consists of: [name-byte];[first_digit].[last_digit]\n while (to <= readBytesDelta && (readBytes - readBytesDelta + to) < chunkSize) { to += 5; while (to < readBytesDelta && readBuffer[to] != '\n') ++to; if (to >= readBytesDelta) { System.arraycopy(readBuffer, from, carryOver, 0, readBytesDelta - from); carryOverSize = readBytesDelta - from; break; } storage.insertMeasurement(readBuffer, from, to); from = ++to; } } } catch (IOException e) { return null; // shouldn't happen } return storage; } private static long getFileSize() { return new File(CalculateAverage_cb0s.FILE).length(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_charlibot.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.*; import java.util.stream.Collectors; public class CalculateAverage_charlibot { private static final String FILE = "./measurements.txt"; private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { try { final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static final int MAP_CAPACITY = 16384; // Need at least 10,000 so 2^14 = 16384. Might need 2^15 = 32768. public static void main(String[] args) throws Exception { memoryMap(); } // Copied from Roy van Rijn's code // branchless max (unprecise for large numbers, but good enough) static int max(final int a, final int b) { final int diff = a - b; final int dsgn = diff >> 31; return a - (diff & dsgn); } // branchless min (unprecise for large numbers, but good enough) static int min(final int a, final int b) { final int diff = a - b; final int dsgn = diff >> 31; return b + (diff & dsgn); } static class Measurement { int min; int max; int sum; int count; Measurement(int value) { min = value; max = value; sum = value; count = 1; } @Override public String toString() { double minD = (double) min / 10; double maxD = (double) max / 10; double meanD = (double) sum / 10 / count; return round(minD) + "/" + round(meanD) + "/" + round(maxD); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } static class MeasurementMap3 { final Measurement[] measurements; final byte[][] cities; final int capacity = MAP_CAPACITY; MeasurementMap3() { measurements = new Measurement[capacity]; cities = new byte[capacity][128]; // 100 bytes for the city. Round up to nearest power of 2. } public void insert(long fromAddress, long toAddress, int hashcode, int value) { int index = hashcode & (capacity - 1); // same trick as in hashmap. This is the same as (% capacity). tryInsert(index, fromAddress, toAddress, value); } private void tryInsert(int mapIndex, long fromAddress, long toAddress, int value) { byte length = (byte) (toAddress - fromAddress); outer: while (true) { byte[] cityArray = cities[mapIndex]; Measurement jas = measurements[mapIndex]; if (jas != null) { if (cityArray[0] == length) { int i = 0; while (i < length) { byte b = UNSAFE.getByte(fromAddress + i); if (b != cityArray[i + 1]) { mapIndex = (mapIndex + 1) & (capacity - 1); continue outer; } i++; } jas.min = min(value, jas.min); jas.max = max(value, jas.max); jas.sum += value; jas.count += 1; break; } else { mapIndex = (mapIndex + 1) & (capacity - 1); } } else { // just insert int i = 0; cityArray[0] = length; while (i < length) { byte b = UNSAFE.getByte(fromAddress + i); cityArray[i + 1] = b; i++; } measurements[mapIndex] = new Measurement(value); break; } } } public HashMap toMap() { HashMap hashMap = new HashMap<>(); for (int mapIndex = 0; mapIndex < cities.length; mapIndex++) { byte[] cityArray = cities[mapIndex]; Measurement measurement = measurements[mapIndex]; if (measurement != null) { int length = cityArray[0]; String city = new String(cityArray, 1, length, StandardCharsets.UTF_8); hashMap.put(city, measurement); } } return hashMap; } public Set> entrySet() { return toMap().entrySet(); } } public static long[] getChunks(int numChunks) throws Exception { long[] chunks = new long[numChunks + 1]; try (FileChannel fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { long fileSize = fileChannel.size(); long sizeOfChunk = fileSize / numChunks; var address = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()).address(); chunks[0] = address; for (int processIdx = 1; processIdx < numChunks; processIdx++) { long chunkAddress = processIdx * sizeOfChunk + address; while (UNSAFE.getByte(chunkAddress) != '\n') { chunkAddress++; } chunkAddress++; chunks[processIdx] = chunkAddress; } chunks[numChunks] = address + fileSize; } return chunks; } public static void memoryMap() throws Exception { int numProcessors = Runtime.getRuntime().availableProcessors(); long[] chunks = getChunks(numProcessors); try (ExecutorService executorService = Executors.newWorkStealingPool(numProcessors)) { Future[] results = new Future[numProcessors]; for (int processIdx = 0; processIdx < numProcessors; processIdx++) { int finalProcessIdx = processIdx; Future> future = executorService.submit(() -> { long chunkIdx = chunks[finalProcessIdx]; long chunkEnd = chunks[finalProcessIdx + 1]; MeasurementMap3 measurements = new MeasurementMap3(); while (chunkIdx < chunkEnd) { long cityStart = chunkIdx; byte b; int hashcode = 0; while ((b = UNSAFE.getByte(chunkIdx)) != ';') { hashcode = 31 * hashcode + b; chunkIdx++; } long cityEnd = chunkIdx; chunkIdx++; int multiplier = 1; b = UNSAFE.getByte(chunkIdx); if (b == '-') { multiplier = -1; chunkIdx++; } int value = 0; while ((b = UNSAFE.getByte(chunkIdx)) != '\n') { if (b != '.') { value = (value * 10) + (b - '0'); } chunkIdx++; } value = value * multiplier; measurements.insert(cityStart, cityEnd, hashcode, value); chunkIdx++; } return measurements.toMap(); }); results[processIdx] = future; } final HashMap measurements = new HashMap<>(); for (Future f : results) { HashMap m = (HashMap) f.get(); m.forEach((city, measurement) -> { measurements.merge(city, measurement, (oldValue, newValue) -> { Measurement mmm = new Measurement(0); mmm.min = Math.min(oldValue.min, newValue.min); mmm.max = Math.max(oldValue.max, newValue.max); mmm.sum = oldValue.sum + newValue.sum; mmm.count = oldValue.count + newValue.count; return mmm; }); }); } System.out.print("{"); System.out.print( measurements.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Object::toString).collect(Collectors.joining(", "))); System.out.println("}"); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_chrisbellew.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.TreeMap; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; import sun.misc.Unsafe; /** * This is Chris Bellew's implementation. Here are the key points: * * - The file is equally split into ranges, one range per thread. * 18 threads was experimentally found to be optimal. * * - Each thread memory maps the file range it is responsible for and * then iterates through the range, one smaller buffer at a time. * * - The contents are parsed by using SIMD vector equality comparisons * between the source data and the newline character, effectively * delimiting each line. The measurement of each line is discovered * by moving back from the end of the line, parsing into an integer * as it goes. The integer representation is 10x the actual value * but is used because integer parsing was found to be much faster * than floating point parsing, and it's also immune to floating * point arithmetic errors when aggregating the measurements later. * * - Once the name and the measurement is parsed for a line, the name * is hashed and used a lookup into a hash table. The value of the * hash table at the given slot is an index into another array, this * time an array of SIMD vectors that represent that name as a series * of vectors. The vectors are used to compare equality of the name of * the source line with the name in the slot to confirm the slot is * occupied by the same city name. The indirection of having a hash * table storing lookups into another array of vectors is to allow * the hash table slots to have a fixed size, while allowing the city * names to be arbitrarily long. The hash table can then use open * addressing to resolve collisions and remain efficient for lookups. * * - After the range has been processed, the results are collected by * iterating through the hash table and looking up the corresponding * integer table for each slot then collecting the min, max, count * and sum of the measurements for each city. Then the results are * combined from all threads, using a treemap for sorting, and printed. */ public final class CalculateAverage_chrisbellew { public static final long FILE_SIZE = getFileSize(); /** * The overlap is the number of bytes that is peeked into the next buffer * in order to find the end of the last newline in the current buffer. * Every buffer ignores the characters before the first newline character * and peeks into the next buffer to find the first newline character. This * way no data is lost even though the buffers are arbitrarily sliced. * 100 is the maximum length of a city name, 1 is the semicolon character, * 5 is the maximum length of a measurement, 1 is the newline character, * 8 is one extra vector length so that we don't overflow the buffer. * If we overlap to this length then we will always be able to complete the * last line in the buffer. */ public static final int OVERLAP = 100 + 1 + 5 + 1 + 8; public static void main(String[] args) throws IOException { /** * The test cases use small test files. This causes issues because we * are trying to open the file at different locations on 16 threads. */ final int NUM_THREADS = FILE_SIZE < 12_000_000_000L ? 1 : 16; /** * Experimentally optimal buffer size for iterating over each * memory mapped segment of the file. */ final int BUFFER_SIZE = 1024 * 256; /** * Split the whole file into slices. One slice per thread. */ var ranges = getThreadRanges(NUM_THREADS); var processors = new ThreadProcessor[NUM_THREADS]; Thread[] threads = new Thread[NUM_THREADS]; for (var i = 0; i < NUM_THREADS; i++) { processors[i] = new ThreadProcessor(ranges[i].start, ranges[i].end, BUFFER_SIZE); threads[i] = new Thread(processors[i]); threads[i].start(); } var results = new TreeMap(); for (int i = 0; i < NUM_THREADS; i++) { try { threads[i].join(); processors[i].collectResults(results); } catch (InterruptedException e) { throw new RuntimeException(e); } } printResults(results); } private static void printResults(TreeMap results) { var builder = new StringBuilder(); builder.append("{"); boolean first = true; for (var entry : results.entrySet()) { var city = entry.getKey(); var result = entry.getValue(); var average = ((float) Math.round((float) result.sum / (float) result.count)) / 10.0; var min = ((float) result.min) / 10.0; var max = ((float) result.max) / 10.0; if (first) { first = false; } else { builder.append(", "); } builder.append(city).append("=").append(min).append("/").append(average).append("/").append(max); } builder.append("}"); System.out.println(builder.toString()); } /** * Splits the measurements file into ranges for each thread, ensuring that the last * range ends at the end of the file. */ public static final FileRange[] getThreadRanges(int threads) throws IOException { var chunkSize = FILE_SIZE / threads; var ranges = new FileRange[threads]; for (var i = 0; i < threads; i++) { var start = i * chunkSize; var end = i == threads - 1 ? FILE_SIZE : (i + 1) * chunkSize; ranges[i] = new FileRange(start, end); } return ranges; } private static final long getFileSize() { try (var stream = new FileInputStream("measurements.txt")) { return stream.getChannel().size(); } catch (IOException e) { throw new RuntimeException("Failed to get file size", e); } } /** * Processes a range of the file. The range is defined by a start and end * position. The start is inclusive and the end is exclusive. */ static final class ThreadProcessor implements Runnable { /** * The number of slots in the hash table. This number was found to be the * minimum number to use in conjunction with the hashing function to * produce no collisions on the test data. The test data is a hint, but the * correctness of the implementation is not coupled to the test data because * the hash table is able to handle collisions in other arbitrary source data. */ private static final int NUM_SLOTS = 12133; /** * The size of the SIMD vector to use when striding through the source data * in order to detect newlines, and when comparing equality of the source line * with a given slot in the hash table. */ private static final VectorSpecies SPECIES = ByteVector.SPECIES_64; /** * A precomputed lookup table of vector masks to use when comparing equality of * the source line and a given slot in the hash table. Each slot in the hash table * has a set of vectors associated with it. The source name is split into vectors * and each source vector is compared with the corresponding slot vector for equality. * Unless the length of the city name is a multiple of the vector length, the last * vector in the slot will be a partial vector. The masks are used to ignore the * unused bytes in the last vector. */ private static final VectorMask[] MASKS = generateMasks(SPECIES); /** * The unsafe instance is used to allocate memory for the hash table slots * and integer table slots. It skips the JVM's garbage collector and allows * the memory to be accessed directly without overhead such as bounds checks. */ private static final Unsafe unsafe = getUnsafe(); /** * The start and end positions this thread will iterate through. */ private final long start; private final long end; private final int bufferSize; /** * The main memory address at the beginning of the hash table slots. */ private final long slotsAddress; /** * The main memory address at the beginning of the integer table slots. */ private final long numbersAddress; /** * The main memory address at the beginning of the name length table slots. */ private final long lengthsAddress; /** * The SIMD vectors associated with each slot in the hash table. The * content of a given slot in a hash table is a lookup into this array. * The intent of having this array as an extra lookup is to allow N * vectors per slot while having fixed size slots. */ private ByteVector[] vectors = new ByteVector[200000]; private String[] cityNames = new String[NUM_SLOTS]; /** * The next available index in the vectors array. */ private short nextVectorIndex = 8; /** * A map of city name strings to their corresponding slot index in the * hash table. When the hash table slots will be sparsely populated it's * not efficient to iterate through the slots when collecting the results. * This map provides a way to discover the occupied slots. */ private final HashMap cityVectorLookup = new HashMap<>(); public ThreadProcessor(long start, long end, int bufferSize) { this.start = start; this.end = end; this.bufferSize = bufferSize; /** * Allocate memory for the hash table and the integer table. * Initialise the hash table slots to 0, so we can use 0 to * indicate an empty slot. */ slotsAddress = unsafe.allocateMemory(NUM_SLOTS * 2); for (int i = 0; i < NUM_SLOTS; i++) { unsafe.putShort(slotsAddress + i * 2, (short) 0); } numbersAddress = unsafe.allocateMemory(NUM_SLOTS * 16); lengthsAddress = unsafe.allocateMemory(NUM_SLOTS); } public final void run() { try (RandomAccessFile file = new RandomAccessFile("measurements.txt", "r")) { FileChannel fileChannel = file.getChannel(); /** * Work out whether we need to peek into the next range. If this is the last * range then the end of this range will be the end of the file, so we won't * peek. Otherwise, we'll peek just enough into the next slot to complete the * last line in this range. */ boolean lastRange = end == FILE_SIZE; long length = lastRange ? end - start : end - start + OVERLAP; MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, length); processRange(buffer, lastRange); } catch (IOException e) { throw new RuntimeException(e); } } /** * Iterates through the entire memory mapped range, one buffer at a time. * The buffers are made to overlap to allow each buffer to peek into the next * range to complete the last line. */ private final void processRange(MappedByteBuffer buffer, boolean lastRange) { byte[] buf = new byte[bufferSize]; int remaining; long globalPosition = start; while ((remaining = buffer.remaining()) != 0) { int numBytes = Math.min(remaining, bufferSize); boolean lastBuffer = remaining == numBytes; /** * Fill this buffer and process it. */ buffer.get(buf, 0, numBytes); processBuffer(buf, numBytes, lastRange, lastBuffer, globalPosition); /** * Start the next range slightly before the end of this range. */ if (!lastBuffer) { buffer.position(buffer.position() - OVERLAP); } globalPosition += numBytes; } } /** * Parses and processes each line from a buffer. */ private final void processBuffer(byte[] buffer, int numBytes, boolean lastRange, boolean lastBuffer, long globalPosition) { /** * Skip past any characters before the first newline because the previous * segment will have already processed them. That is unless this if the * first buffer in the first range (global position zero), in which case * we will start from the first character. */ int index = globalPosition == 0 ? 0 : findFirstNewline(buffer) + 1; /** * Keep track of the start of the city name. */ int nameStart = index; while (true) { /** * Take a slice of bytes and convert it into a vector so we can apply * SIMD operations to find newlines. */ ByteVector vector = ByteVector.fromArray(SPECIES, buffer, index); /** * Find the newline using SIMD. */ VectorMask newLineMask = vector.eq((byte) '\n'); int firstTrue = newLineMask.firstTrue(); if (firstTrue == SPECIES.length()) { /** * We haven't found a newline in this vector, so move on to the * next vector. */ index += SPECIES.length(); continue; } slice(buffer, index + firstTrue, nameStart); index = index + firstTrue + 1; nameStart = index; /** * If this is the last buffer in the last range then we want to * process every character until the very end of the file. */ if (lastRange && lastBuffer) { if (index == numBytes) { return; } /** * If we're less than one vector length away from the end * of the buffer then just take the remaining bytes as the * final line. If we tried to use a vector it would overflow. */ if (index >= numBytes - SPECIES.length()) { slice(buffer, numBytes - 1, nameStart); return; } continue; } /** * If it's not the last buffer or it's not the last range then * we want to overlap into the next buffer, but only by enough * to complete the last line. */ if (index > numBytes - OVERLAP) { return; } } } /** * Finds the first newline in a buffer using SIMD. Used to skip past a * partial line at the beginning of a buffer. */ private final int findFirstNewline(byte[] buffer) { int index = 0; while (true) { ByteVector vector = ByteVector.fromArray(SPECIES, buffer, index); VectorMask newLineMask = vector.eq((byte) '\n'); int firstTrue = newLineMask.firstTrue(); if (firstTrue == SPECIES.length()) { index += SPECIES.length(); continue; } return index + firstTrue; } } /** * Given the index in the buffer of where a name starts, and the index of * the next newline, creeps back from the next newline to find the structure * of the measurement, parsing it into a number as it goes. It is parsed * into an integer because it's faster than parsing as a float, and it's also * immune to floating point arithmetic errors when aggregating the measurements * later. * * Then proceeds to record the fully parsed name and measurement in the hash table. */ private final void slice(byte[] buffer, int newlineIndex, int nameStart) { int i = newlineIndex - 1; int measurement = buffer[i] - '0'; i -= 2; // Skip before the decimal point measurement += (buffer[i] - '0') * 10; i--; if (buffer[i] == ';') { // 1.2 record(buffer, nameStart, i, measurement); } else { // 12.3 or -1.2 or -12.3 if (buffer[i] == '-') { // -1.2 record(buffer, nameStart, i - 1, -measurement); } else { // 12.3 or -12.3 measurement += (buffer[i] - '0') * 100; i--; if (buffer[i] == '-') { // -12.3 record(buffer, nameStart, i - 1, -measurement); } else { // 12.3 record(buffer, nameStart, i, measurement); } } } } /** * Given a name and measurement, looks up a slot in the hash table by hashing * the city name as a key, then applies the measurement to the accumulated * aggregation of that city's measurements. */ private final void record(byte[] buffer, int nameStart, int nameEnd, int measurement) { int nameLength = nameEnd - nameStart; /** * The length of most city names will not be a multiple of the SIMD * vector length so there will be a remainder in the final vector * of extraneous bytes. We need to mask these bytes out when comparing. */ var remainder = nameLength % SPECIES.length(); var numVectors = nameLength / SPECIES.length() + (remainder == 0 ? 0 : 1); /** * Lookup the slot index in the hash table for the city name. */ var slotIndex = nameToSlotIndex(buffer, nameStart, nameLength); /** * Identify if the slot is occupied, then check the equality of the * slot with the city name. */ var vectorOffset = unsafe.getShort(slotsAddress + slotIndex * 2); while (vectorOffset != 0) { /** * Check the set of vectors in the slot match the city name */ if (slotEquals(buffer, nameStart, vectorOffset, numVectors, remainder, slotIndex)) { /** * Check the length of the slot name and city name match. This * check is needed because the vector equality check can give * false positives if one city name starts with another. */ byte slotNameLength = unsafe.getByte(lengthsAddress + slotIndex); if (slotNameLength == nameLength) { updateSlot(slotIndex, measurement); break; } } /** * If the slot is occupied but the city name doesn't match, then * we try the next slot in the hash table through linear probing. */ slotIndex = (slotIndex + 1) % NUM_SLOTS; vectorOffset = unsafe.getShort(slotsAddress + slotIndex * 2); } /** * If the slot was unoccupied, then we can initialise it with the * city name and measurement. */ if (vectorOffset == 0) { /** * Record where the city name length is recorded for this slot. */ unsafe.putByte(lengthsAddress + slotIndex, (byte) nameLength); /** * Record where the start of the set of vectors are recorded for */ unsafe.putShort(slotsAddress + slotIndex * 2, nextVectorIndex); /** * Records the vectors for the city name. */ for (int v = 0; v < numVectors; v++) { vectors[nextVectorIndex] = ByteVector.fromArray(SPECIES, buffer, nameStart + v * SPECIES.length()); nextVectorIndex++; } cityVectorLookup.put(new String(buffer, nameStart, nameLength), slotIndex); /** * Min, max, count, sum */ var numbersIndex = getNumbersIndex(slotIndex); unsafe.putInt(numbersIndex, measurement); unsafe.putInt(numbersIndex + 4, measurement); unsafe.putInt(numbersIndex + 8, 1); unsafe.putInt(numbersIndex + 12, measurement); cityNames[slotIndex] = new String(buffer, nameStart, nameLength); } } /** * Given the index bounds of a name in a buffer, creates a hash of the name * by multiplying the first twelve characters. This was experimentally found * to provide a good distribution of hash values for the test data. In * combination with the number of slots in the hash table, this produces no * collisions on the test data. The test data is a hint, but the correctness * of the implementation is not coupled to the test data because the hash * table is able to handle collisions in other arbitrary source data. */ private final int nameToSlotIndex(byte[] buffer, int nameStart, int nameLength) { var integer = 1; integer *= buffer[nameStart + 0]; if (nameLength > 1) { integer *= buffer[nameStart + 1]; if (nameLength > 2) { integer *= buffer[nameStart + 2]; if (nameLength > 3) { integer *= buffer[nameStart + 3]; if (nameLength > 4) { integer *= buffer[nameStart + 4]; if (nameLength > 5) { integer *= buffer[nameStart + 5]; if (nameLength > 6) { integer *= buffer[nameStart + 6]; if (nameLength > 7) { integer *= buffer[nameStart + 7]; if (nameLength > 8) { integer *= buffer[nameStart + 8]; if (nameLength > 9) { integer *= buffer[nameStart + 9]; if (nameLength > 10) { integer *= buffer[nameStart + 10]; if (nameLength > 11) { integer *= buffer[nameStart + 11]; } } } } } } } } } } } return Math.abs(integer) % NUM_SLOTS; } /** * Given a slot index and a measurement, updates the aggregation of the * measurements for the city in that slot. */ private final void updateSlot(int slotIndex, int measurement) { var numbersIndex = getNumbersIndex(slotIndex); var min = unsafe.getInt(numbersIndex); var max = unsafe.getInt(numbersIndex + 4); var count = unsafe.getInt(numbersIndex + 8); var sum = unsafe.getInt(numbersIndex + 12); unsafe.putInt(numbersIndex, Math.min(min, measurement)); unsafe.putInt(numbersIndex + 4, Math.max(max, measurement)); unsafe.putInt(numbersIndex + 8, count + 1); unsafe.putInt(numbersIndex + 12, sum + measurement); } /** * Given a name in a buffer, a slot index, and a number of vectors, checks * the equality of the name and the slot. * * The length of the name is not necessarily a multiple of the SIMD vector * length, so the last vector in the slot will be a partial vector. The * masks are used to ignore the unused bytes in the last vector. */ private final boolean slotEquals(byte[] buffer, int nameStart, int vectorOffset, int numVectors, int remainder, int slotIndex) { for (int v = 0; v < numVectors; v++) { var nameVector = ByteVector.fromArray(SPECIES, buffer, nameStart + v * SPECIES.length()); var slotVector = vectors[vectorOffset + v]; if (v == numVectors - 1) { if (remainder == 0) { if (!slotVector.eq(nameVector).allTrue()) { return false; } } else { var mask = MASKS[remainder - 1]; if (!slotVector.compare(VectorOperators.EQ, nameVector, mask).equals(mask)) { return false; } } break; } else { if (!slotVector.eq(nameVector).allTrue()) { return false; } } } return true; } /** * Given a slot index, returns the main memory address of the integer table * where the min, max, count and sum of the measurements are stored. */ private final long getNumbersIndex(int slotIndex) { return numbersAddress + slotIndex * 16; } public void collectResults(TreeMap results) { for (var entry : cityVectorLookup.entrySet()) { var city = entry.getKey(); var slotIndex = entry.getValue(); var numbersIndex = getNumbersIndex(slotIndex); var min = unsafe.getInt(numbersIndex); var max = unsafe.getInt(numbersIndex + 4); var count = unsafe.getInt(numbersIndex + 8); var sum = unsafe.getInt(numbersIndex + 12); results.compute(city, (k, v) -> { if (v == null) { return new CityResult(min, max, sum, count); } else { v.min = Math.min(v.min, min); v.max = Math.max(v.max, max); v.sum += sum; v.count += count; return v; } }); } } /** * Generates a lookup table of vector masks to use when comparing equality of * the last vector of the source line and a given slot in the hash table. */ private static final VectorMask[] generateMasks(VectorSpecies species) { VectorMask[] masks = new VectorMask[species.length() - 1]; masks[0] = VectorMask.fromArray(species, new boolean[]{ true, false, false, false, false, false, false, false }, 0); masks[1] = VectorMask.fromArray(species, new boolean[]{ true, true, false, false, false, false, false, false }, 0); masks[2] = VectorMask.fromArray(species, new boolean[]{ true, true, true, false, false, false, false, false }, 0); masks[3] = VectorMask.fromArray(species, new boolean[]{ true, true, true, true, false, false, false, false }, 0); masks[4] = VectorMask.fromArray(species, new boolean[]{ true, true, true, true, true, false, false, false }, 0); masks[5] = VectorMask.fromArray(species, new boolean[]{ true, true, true, true, true, true, false, false }, 0); masks[6] = VectorMask.fromArray(species, new boolean[]{ true, true, true, true, true, true, true, false }, 0); return masks; } private static final Unsafe getUnsafe() { Field field; try { field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException("Failed to get unsafe", e); } } } static final class CityResult { public int min; public int max; public int sum; public int count; public CityResult(int min, int max, int sum, int count) { this.min = min; this.max = max; this.sum = sum; this.count = count; } } static final class FileRange { public final long start; public final long end; public FileRange(long start, long end) { this.start = start; this.end = end; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_cliffclick.java ================================================ /* * Copyright 2024 Cliff Click * * 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. */ package dev.morling.onebrc; import java.io.*; import java.lang.reflect.Field; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.*; import java.util.Arrays; import sun.misc.Unsafe; abstract class CalculateAverage_cliffclick { // abstract class CNC { public static final int NCPUS = Runtime.getRuntime().availableProcessors(); public static final long HASSEMI = 0x3B3B3B3B3B3B3B3BL; private static final Unsafe UNSAFE; private static long MMAP_ADDRESS; static { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(Unsafe.class); Field f; try { f = java.nio.Buffer.class.getDeclaredField("address"); } catch (java.lang.NoSuchFieldException e) { throw new RuntimeException(e); } MMAP_ADDRESS = UNSAFE.objectFieldOffset(f); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws Exception { if (args.length < 1) args = new String[]{ "measurements.txt" }; Work w = work(args); String foo = w.toString(); byte[] bar = new byte[foo.length()]; foo.getBytes(0, foo.length(), bar, 0); System.out.write(bar); System.out.write('\n'); } // General work flow: // Spawn threads. Make empty hash for sums, counts. // Seek to offset. Skip till newline. Parse to end of chunk, plus rest of partial line. // SWAR out city name into n8; both hash and uhash name. // Lookup in sums; if miss: insert map uhash to 0 cnt; insert map uhash to String name also; // if hit: bump count; same index in sums array, bump sums with scaled decimal // At end, grab more work until done. // At end, across all threads total sums & counts. // Compute averages, lookup names and print. static Work work(String[] args) throws Exception { File f = new File(args[0]); // How many threads? int ncpus = (int) Math.min((f.length() >> 14) + 1, NCPUS); // Keep 1<<14 min work long len = (f.length() / ncpus) + 1; Work[] WS = new Work[ncpus]; Thread[] TS = new Thread[ncpus]; // Spawn work on threads for (int i = 0; i < ncpus; i++) { long s = i * len; Work w = WS[i] = new Work(); Thread T = TS[i] = new Thread() { public void run() { tstart(w, f, s, Math.min(len, f.length() - s)); } }; T.start(); } TS[0].join(); Work W = WS[0]; for (int i = 1; i < ncpus; i++) { TS[i].join(); W.reduce(WS[i]); } return W; } static void tstart(Work w, File f, long start, long len) { try { // Thread gets a chunk of work FileChannel fc = FileChannel.open(f.toPath(), StandardOpenOption.READ); final int MAX_MAP = 1 << 30; for (long s = start; s < start + len; s += MAX_MAP) { int maxlen = (int) Math.min(len + 1, MAX_MAP); // Length capped at MAX_MAP long rem = f.length() - s; int mlen = (int) Math.min(rem, maxlen + 100); // Add a little extra so can finish out a line int clen = (int) Math.min(rem, maxlen); // mmap is capped at MAX_MAP (plus change), or // to the end of the chosen parse length (plus change) // or the end of the file in any case MappedByteBuffer mmap = fc.map(FileChannel.MapMode.READ_ONLY, s, mlen); // Chunk runs to min(MAX_MAP, parse length, eof), plus it runs to the end // of any partial line. do_chunk(w, s > 0, clen, mmap); } } catch (IOException ioe) { throw new RuntimeException(ioe); } } // Has a zero byte in a long, copied straight from Hackers Delight static long has0(long x) { return (x - 0x0101010101010101L) & (~x) & 0x8080808080808080L; } // Parse a chunk, from 0 to limit in mmap. Runs past limit to finish any // partial line. If skip1, then skip any leading partial line. static void do_chunk(Work w, boolean skip1, int limit, MappedByteBuffer mmap) { assert mmap.isDirect(); int idx = 0; int max = mmap.limit(); long base = UNSAFE.getLong(mmap, MMAP_ADDRESS); // If start>0, skip until first newline if (skip1) idx = skipFirst(idx, base); // The very last entry will want to fetch 8 bytes, some of which may go // past the mmap max - do this entry now, before looping. if (limit == max) limit = skipLast(limit, w, base); // Edges of the ~2G region taken care of. Now do the giant middle part. // For this chunk of file do... while (idx < limit) { int cityx = idx; // Used if we find a new city name // SWAR read and build n8; the long-as-a-string value. Also track start // and end of the string, in case it is new and needs to be inserted into // the n8->city_name map. long n8 = 0; // Read a misaligned long long x = UNSAFE.getLong(base + idx); // Found semi ? long hasM = has0(x ^ HASSEMI); while (hasM == 0) { // Read 2nd word of city n8 ^= x; idx += 8; // Read a misaligned long x = UNSAFE.getLong(base + idx); // Found semi ? hasM = has0(x ^ HASSEMI); } // Found a semicolon this word. // The high bit of the byte in question is set. int shr = Long.numberOfTrailingZeros(hasM) + 1; if (shr > 8) { int shr2 = 72 - shr; n8 ^= (x << shr2) >> shr2; idx += (shr >> 3) - 1; } // Skip semicolon idx++; // Reading tempature, and add idx = parseData(idx, w, cityx, n8, base); } } // The very last entry will want to fetch 8 bytes, some of which may go // past the mmap max - do this entry now, before looping. private static int skipLast(int limit, Work w, long base) { limit--; while (limit > 0 && UNSAFE.getByte(base + limit - 1) != '\n') limit--; long n8 = 0, mask = 0, c; int i = limit; while ((c = UNSAFE.getByte(base + i)) != ';') { mask = (mask >> 8) | (c << 56); i++; if (((limit - i) & 7) == 0) { n8 ^= mask; mask = 0; } } int shr = (limit - i) & 7; n8 ^= (mask >> (shr << 3)); parseData(i + 1, w, limit, n8, base); return limit; } // Parse temp data, and insert entry into hash table private static int parseData(int idx, Work w, int cityx, long n8, long base) { // Reading tempature: int temp = 0; boolean neg = false; byte b = UNSAFE.getByte(base + idx++); if (b == '-') { neg = true; b = UNSAFE.getByte(base + idx++); } temp = b - '0'; b = UNSAFE.getByte(base + idx++); if (b != '.') { temp = temp * 10 + b - '0'; idx++; } // Read fraction digit; scaled decimal temp b = UNSAFE.getByte(base + idx++); temp = temp * 10 + b - '0'; if (neg) temp = -temp; // Skip newline idx++; // F*KING WINDOWS. // Skip CR // idx++; w.insert(n8, temp, base, cityx); return idx; } private static int skipFirst(int idx, long base) { while (UNSAFE.getByte(base + idx++) != '\n') ; // WINDOWS // idx++; return idx; } private static class Work { private static final int TAB_SIZE = 0x4000; // 512 for 413 cities // Fixed size hashtable. Longs are packed to hold the data. // cnt uhash // 8 7 6 5 4 3 2 1 // min max temp sum // 8 7 6 5 4 3 2 1 long[] table = new long[TAB_SIZE * 2]; // String[] cities = new String[TAB_SIZE]; // Same index holds city names // Gather for city bits final byte[] city = new byte[256]; int reprobes; void insert(long n8, int temp, long base, int cityx) { // 3 bytes uniquely id city, left at 4 int uhash = (int) uhash_final(n8); // Index in small table int ihash = hash_hash(uhash); long cnt_key = table[(ihash << 1)]; long min_max = table[(ihash << 1) + 1]; int key = key(cnt_key); while (key != uhash) { if (key == 0) { // Miss in hash table cnt_key = uhash & 0xFFFFFFFFL; min_max = min_max(0x7FFF, 0xF000, 0); // Put city name in cities new_city(ihash, base + cityx); break; } // Reprobe. Seeiong 53M reprobes out of 1000M rows, so a 5.3% reprobe rate ihash = reprobe(ihash, uhash); cnt_key = table[(ihash << 1)]; min_max = table[(ihash << 1) + 1]; key = key(cnt_key); } // assert cities[ihash].equals(toChar(n8)) : String.format("uhash=0x%08x %s %s, FAILS FOR %d",uhash,cities[ihash],toChar(n8),X0); // Break down parts int min = min(min_max); min = Math.min(min, temp); int max = max(min_max); max = Math.max(max, temp); int sum = temp(min_max); sum += temp; min_max = min_max(min, max, sum); // Back into table table[(ihash << 1)] = cnt_key + (1L << 32); table[(ihash << 1) + 1] = min_max; } // Hash the n8 value; the 3 bytes uniquely identify the city. static long uhash_final(long n8) { return n8 ^ (n8 >> 29); } // New city void new_city(int ihash, long base_cityx) { // Put city name in cities int i = 0; byte c; while ((c = UNSAFE.getByte(base_cityx++)) != ';') city[i++] = c; cities[ihash] = new String(city, 0, 0, i); } private static int hash_hash(int uhash) { // Index in small table int ihash = uhash; ihash = ihash ^ (ihash >> 17); ihash = ihash + 29 * uhash; ihash &= (TAB_SIZE - 1); return ihash; } private static int reprobe(int ihash, int uhash) { return (ihash + (uhash | 1)) & (TAB_SIZE - 1); } // Convert the large unique hash into a smaller table hash int ihash(int uhash) { // Index in small table int ihash = hash_hash(uhash); long cnt_key = table[ihash << 1]; int key = key(cnt_key); while (key != uhash) { if (key == 0) return ihash; // Reprobe ihash = reprobe(ihash, uhash); cnt_key = table[(ihash << 1)]; key = key(cnt_key); } return ihash; } void reduce(Work w) { for (int i = 0; i < w.cities.length; i++) { if (w.cities[i] == null) continue; // Break down parts long cnt_key = w.table[(i << 1)]; long min_max = w.table[(i << 1) + 1]; int cnt = cnt(cnt_key); int key = key(cnt_key); int min = min(min_max); int max = max(min_max); int sum = temp(min_max); // Find key in local table int ihash = ihash(key); long cnt_key0 = table[(ihash << 1)]; long min_max0 = table[(ihash << 1) + 1]; int cnt0 = cnt(cnt_key0); int key0 = key(cnt_key0); int min0 = min(min_max0); int max0 = max(min_max0); int sum0 = temp(min_max0); cnt0 += cnt; sum0 += sum; min0 = Math.min(min0, min); max0 = Math.max(max0, max); if (key0 == 0) { key0 = key; min0 = min; cities[ihash] = w.cities[i]; } table[(ihash << 1)] = cnt_key(cnt0, key0); table[(ihash << 1) + 1] = min_max(min0, max0, sum0); } } static int key(long cnt_key) { return (int) cnt_key; } static int cnt(long cnt_key) { return (int) (cnt_key >> 32); } static int min(long min_max) { return (int) (min_max >> 48); } // Signed right shift; min often negative static int max(long min_max) { return (short) ((min_max >>> 32) & 0xFFFF); }// Unsigned right shift; static int temp(long min_max) { return (int) min_max; }; // Low int static long cnt_key(int cnt, int key) { return ((long) cnt << 32) | (key & 0xFFFFFFFFL); } static long min_max(int min, int max, int sum) { return ((long) min << 48) | ((long) (max & 0xFFFF) << 32) | (((long) sum) & 0xFFFFFFFFL); } @Override public String toString() { int ncitys = 0; // totals 413 for (int i = 0; i < TAB_SIZE; i++) if (cities[i] != null) ncitys++; // Index of city entries Integer[] is = new Integer[ncitys]; for (int i = 0, j = 0; i < TAB_SIZE; i++) if (cities[i] != null) is[j++] = i; // Sort indices Arrays.sort(is, (x, y) -> cities[x].compareTo(cities[y])); StringBuilder sb = new StringBuilder().append("{"); for (int i : is) { String city = cities[i]; int cnt = cnt(table[(i << 1)]); long min_max = table[(i << 1) + 1]; double min = min(min_max) / 10.0; double max = max(min_max) / 10.0; double temp = temp(min_max) / 10.0; double mean = temp / cnt; sb.append(String.format("%s=%.1f/%.1f/%.1f, ", city, min, mean, max)); } if (sb.length() > 2) sb.setLength(sb.length() - 2); return sb.append("}").toString(); } } // Debugging utilities static String toHex(byte[] bs) { StringBuilder sb = new StringBuilder().append("["); for (byte b : bs) sb.append(String.format("%02X,", b)); sb.setLength(sb.length() - 1); return sb.append("]").toString(); } static String toChar(long x) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 8; i++) { char c = (char) (x & 0xFF); if (c != 0) sb.append(c); x >>= 8; } return sb.toString(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_coolmineman.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.HashMap; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_coolmineman { private static final String FILE = "./measurements.txt"; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; void add(double value) { min = Math.min(min, value); max = Math.max(max, value); sum += value; count++; } void merge(MeasurementAggregator o) { min = Math.min(min, o.min); max = Math.max(max, o.max); sum += o.sum; count += o.count; } public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } static class BytesKey implements Comparable { byte[] value; int hashcode; BytesKey(byte[] value) { this.value = value; } @Override public String toString() { return new String(value, StandardCharsets.UTF_8); } @Override public int hashCode() { return Arrays.hashCode(value); } @Override public boolean equals(Object obj) { if (obj instanceof BytesKey bk) { return Arrays.equals(value, bk.value); } return false; } @Override public int compareTo(BytesKey o) { return Arrays.compare(value, o.value); } } static void parse(ByteBuffer a, int as, ByteBuffer b, int bs, boolean isFirst, HashMap map) { var aa = a.array(); var ba = b.array(); int pos = 0; int nameEnd = 0; boolean parseDouble = false; int doubleStart = 0; int doubleEnd = 0; if (!isFirst) { while (aa[pos] != (byte) '\n') { pos++; } pos++; } int nameStart = pos; while (pos < as) { if (parseDouble) { if (aa[pos] == '\n') { doubleEnd = pos; BytesKey station = new BytesKey(Arrays.copyOfRange(aa, nameStart, nameEnd)); double value = parseDouble(aa, doubleStart, doubleEnd); MeasurementAggregator ma = map.get(station); if (ma == null) map.put(station, ma = new MeasurementAggregator()); ma.add(value); parseDouble = false; nameStart = pos + 1; nameEnd = 0; doubleStart = 0; doubleEnd = 0; } } else { if (aa[pos] == ';') { nameEnd = pos; parseDouble = true; doubleStart = pos + 1; } } pos++; } if (bs <= 0) return; for (;;) { if (parseDouble) { if (ba[pos - as] == '\n') { doubleEnd = pos; BytesKey station = new BytesKey(ofRange(a, as, b, bs, nameStart, nameEnd)); double value = parseDouble(ofRange(a, as, b, bs, doubleStart, doubleEnd), 0, doubleEnd - doubleStart); map.computeIfAbsent(station, k -> new MeasurementAggregator()).add(value); return; } } else { if (ba[pos - as] == ';') { nameEnd = pos; parseDouble = true; doubleStart = pos + 1; } } pos++; } } static byte[] ofRange(ByteBuffer a, int as, ByteBuffer b, int bs, int start, int end) { var aa = a.array(); var ba = b.array(); byte[] r = new byte[end - start]; for (int i = 0; i < r.length; i++) { int pos = start + i; if (pos < as) { r[i] = aa[pos]; } else { r[i] = ba[pos - as]; } } return r; } static double parseDouble(byte[] b, int start, int end) { boolean negative = false; if (b[start] == (byte) '-') { negative = true; start += 1; } double result = 0; for (int i = start; i < end; i++) { if (b[i] != (byte) '.') { result *= 10; result += (b[i] & 0xFF) - '0'; } } if (negative) result *= -1; return result * .1; } public static void main(String[] args) throws Exception { int pageSize = 1600000; long pos = 0; try (AsynchronousFileChannel fc = AsynchronousFileChannel.open(Paths.get(FILE), Set.of(StandardOpenOption.READ), Executors.newCachedThreadPool())) { var cp = Executors.newWorkStealingPool(Runtime.getRuntime().availableProcessors()); var bbs = new ByteBuffer[Runtime.getRuntime().availableProcessors() * 8]; for (int i = 0; i < bbs.length; i++) { bbs[i] = ByteBuffer.allocate(pageSize); } Future[] futures = new Future[bbs.length]; HashMap[] maps = new HashMap[bbs.length]; Future[] tasks = new Future[bbs.length]; for (int i = 0; i < futures.length; i++) { futures[i] = fc.read(bbs[i], pos); maps[i] = new HashMap<>(); pos += pageSize; } boolean first = true; l: for (;;) { for (int i = 0; i < bbs.length; i++) { int nextIndex = (i + 1) % bbs.length; if (tasks[nextIndex] != null) { tasks[nextIndex].get(); bbs[nextIndex].position(0); futures[nextIndex] = fc.read(bbs[nextIndex], pos); pos += pageSize; } var fa = futures[i]; var fb = futures[(i + 1) % futures.length]; var isFirst = first; int ra = fa.get(); if (ra < 0) break l; int rb = Math.max(0, fb.get()); var iLol = i; tasks[i] = cp.submit(() -> { parse(bbs[iLol], ra, bbs[nextIndex], rb, isFirst, maps[iLol]); }); first = false; } } for (Future t : tasks) { if (t != null) t.get(); } for (int i = 1; i < maps.length; i++) { merge(maps[0], maps[i]); } System.out.print('{'); boolean[] firstE = new boolean[]{ true }; maps[0].entrySet().stream().sorted((a, b) -> a.getKey().toString().compareTo(b.getKey().toString())).forEach(e -> { if (!firstE[0]) { System.out.print(','); System.out.print(' '); } firstE[0] = false; System.out.print(e.toString()); }); System.out.println('}'); System.exit(0); } } static void merge(HashMap a, HashMap b) { for (var e : b.entrySet()) { if (a.containsKey(e.getKey())) { a.get(e.getKey()).merge(e.getValue()); } else { a.put(e.getKey(), e.getValue()); } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_couragelee.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.*; import java.util.*; import java.util.concurrent.*; public class CalculateAverage_couragelee { private static class Temperature { private int cnt = 0; private double sum = 0; private double min; private double max; public Temperature(String tempStr) { double temp = Double.parseDouble(tempStr); this.min = temp; this.max = temp; this.sum = temp; this.cnt++; } public Temperature(int cnt, double sum, double min, double max) { this.cnt = cnt; this.sum = sum; this.min = min; this.max = max; } public Temperature addRecord(String tempStr) { double temp = Double.parseDouble(tempStr); Temperature newTemp = new Temperature(this.cnt, this.sum, this.min, this.max); newTemp.min = Math.min(temp, newTemp.min); newTemp.max = Math.max(temp, newTemp.max); newTemp.sum += temp; newTemp.cnt++; return newTemp; } public Temperature merge(Temperature newValue) { Temperature oldTemp = new Temperature(this.cnt, this.sum, this.min, this.max); oldTemp.min = Math.min(newValue.min, oldTemp.min); oldTemp.max = Math.max(newValue.max, oldTemp.max); oldTemp.sum += newValue.sum; oldTemp.cnt += newValue.cnt; return oldTemp; } public void update(String tempStr) { double temp = parseDouble(tempStr); this.min = Math.min(temp, this.min); this.max = Math.max(temp, this.max); this.sum += temp; this.cnt++; } @Override public String toString() { return STR."\{min}/\{Math.round((sum / cnt) * 10.0) / 10.0}/\{max}"; } } private static final String FILE_PATH = "./measurements.txt"; // 并行任务的数量 public static final int CONCURRENT_NUM = 20; private static FileChannel fc; private static long fcSize; private static int segmentSize; private static Map temperatureMap; // 需要拼接的行信息 private static Map tempBytesMap = new ConcurrentHashMap<>(); // 缓存double解析数据 private static Map doubleCache; public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { // 初始化 File file = new File(FILE_PATH); fc = new RandomAccessFile(file, "r").getChannel(); fcSize = fc.size(); segmentSize = (int) Math.ceil((double) fcSize / CONCURRENT_NUM); calculate(); String resStr = temperatureMap.toString(); System.out.println(resStr); } private static void calculate() throws IOException, InterruptedException, ExecutionException { ThreadPoolExecutor executor = new ThreadPoolExecutor(CONCURRENT_NUM, CONCURRENT_NUM, 0, TimeUnit.SECONDS, new LinkedBlockingQueue()); temperatureMap = new ConcurrentSkipListMap<>(); preHeatDoubleCache(); List>> res = new ArrayList<>(); long startPos = 0; if (fcSize < 1000000) { Future> partRes = executor.submit(new Task(startPos, fcSize)); Map map = partRes.get(); temperatureMap.putAll(map); } else { while (true) { if (startPos + segmentSize >= fcSize) { Future> partRes = executor.submit(new Task(startPos, fcSize - startPos)); res.add(partRes); break; } else { Future> partRes = executor.submit(new Task(startPos, segmentSize)); res.add(partRes); startPos += segmentSize; } } // 合并结果 for (Future> future : res) { Map stringTemperatureMap = future.get(); for (Map.Entry entry : stringTemperatureMap.entrySet()) { String station = entry.getKey(); Temperature value = entry.getValue(); temperatureMap.merge(station, value, (oldValue, newValue) -> oldValue.merge(newValue)); } } } executor.shutdown(); executor.awaitTermination(10, TimeUnit.MINUTES); // 处理拼接的行信息,不超过总并发数,顺序处理 for (Map.Entry entry : tempBytesMap.entrySet()) { String key = entry.getKey(); if (key.startsWith("E")) { continue; } byte[] part1 = entry.getValue(); byte[] part2 = tempBytesMap.getOrDefault("E" + key, new byte[0]); byte[] bytes = new byte[part1.length + part2.length]; System.arraycopy(part1, 0, bytes, 0, part1.length); System.arraycopy(part2, 0, bytes, part1.length, part2.length); String[] lines = convertToString1(bytes, 0, bytes.length - 1); for (String line : lines) { try { handleRecordConcurrently(line); } catch (Exception e) { e.printStackTrace(); System.out.println(line); } } } } private static class Task implements Callable> { private long startPos; private long size; public Task(long startPos, long size) throws IOException { this.startPos = startPos; this.size = size; } @Override public Map call() throws Exception { Map map = new HashMap<>(10000); try { // 1亿个byte boolean firstRowHandled = false; MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, startPos, size); byte[] lastLastRowBytes = null; while (buffer.hasRemaining()) { byte[] bytes = new byte[10000]; // 先拼上上一次的最后一行 int startIndex = 0; if (lastLastRowBytes != null) { for (byte lastLastRowByte : lastLastRowBytes) { bytes[startIndex++] = lastLastRowByte; } } int readLength = Math.min(buffer.remaining(), 10000 - startIndex); lastLastRowBytes = null; buffer.get(bytes, startIndex, readLength); // 处理第一行 int firstIndex = 0; if (!firstRowHandled) { firstRowHandled = true; if (startPos == 0) { // 全文第一行,不要特殊处理 } else { while (bytes[firstIndex] != 10) { firstIndex++; } byte[] firstRowBytes = Arrays.copyOfRange(bytes, 0, firstIndex + 1); tempBytesMap.put("E" + String.valueOf(startPos - 1), firstRowBytes); firstIndex++; } } // 分段的最后一行(可能不完整) int lastIndex = startIndex + readLength - 1; while (bytes[lastIndex] != 10) { lastIndex--; } if (lastIndex == startIndex + readLength - 1) { // 分段的最后一行是完整的 } else { // 暂存一下 lastLastRowBytes = Arrays.copyOfRange(bytes, lastIndex + 1, startIndex + readLength); } // [firstIndex, lastIndex] 这之间的数据是完整的多行数据 String[] lines = convertToString1(bytes, firstIndex, lastIndex); handleRecord(map, lines); } // 处理最后一行 if (lastLastRowBytes != null) { tempBytesMap.put(String.valueOf(startPos + size - 1), Arrays.copyOf(lastLastRowBytes, lastLastRowBytes.length)); } else { tempBytesMap.put(String.valueOf(startPos + size - 1), new byte[0]); } } catch (Exception e) { e.printStackTrace(); } return map; } } private static void handleRecord(Map map, String[] records) { if (records == null || records.length == 0) { return; } for (String record : records) { if ("".equals(record)) { continue; } int index = record.indexOf(";"); String station = record.substring(0, index); String stationValue = record.substring(index + 1); Temperature temperature = map.get(station); if (temperature == null) { temperature = new Temperature(stationValue); map.put(station, temperature); } else { temperature.update(stationValue); } } } private static void handleRecordConcurrently(String record) { if (record.isEmpty()) { return; } String[] split = record.split(";"); String station = split[0]; String stationValue = split[1]; // temperatureMap中只能新增值,不会删除 if (temperatureMap.get(station) == null) { if (temperatureMap.putIfAbsent(station, new Temperature(stationValue)) != null) { // 插入失败 temperatureMap.computeIfPresent(station, (key, oldValue) -> oldValue.addRecord(stationValue)); } } else { // 已经有值了 temperatureMap.computeIfPresent(station, (key, oldValue) -> oldValue.addRecord(stationValue)); } } /** * * @param bytes * @param start 起始索引,包含 * @param end 结束索引,包含 * @return */ private static String[] convertToString1(byte[] bytes, int start, int end) { if (bytes == null || bytes.length == 0) { return new String[0]; } String s = new String(bytes, start, (end - start + 1), StandardCharsets.UTF_8); String[] split = s.split("\n"); return split; } // 预热-99.9到99.9之间的数,且始终包含一位小数 private static void preHeatDoubleCache() { doubleCache = new ConcurrentHashMap<>(); for (int i = -99; i < 99; i++) { for (int j = 0; j < 10; j++) { String stand = String.valueOf(i); String v = stand + "." + j; doubleCache.put(v, Double.parseDouble(v)); } } for (int i = 0; i < 10; i++) { String stand = "-0"; String v = stand + "." + i; doubleCache.put(v, Double.parseDouble(v)); } } private static double parseDouble(String tempStr) { return doubleCache.get(tempStr); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_criccomini.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.*; public class CalculateAverage_criccomini { private static final String FILE = "./measurements.txt"; private static final long FILE_SIZE = new File(FILE).length(); private static final long SEGMENT_SIZE = 256_000_000; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static Map processSegment(MappedByteBuffer buffer, int length) { Map aggregates = new HashMap<>(); int lineStart = 0; int doubleStart = 0; String station = null; for (int i = 0; i < length; ++i) { byte b = buffer.get(i); if (b == ';') { byte[] stationBuffer = new byte[i - lineStart]; buffer.position(lineStart); buffer.get(stationBuffer); station = new String(stationBuffer, StandardCharsets.UTF_8); doubleStart = i + 1; } else if (b == '\n') { byte[] doubleBuffer = new byte[i - doubleStart]; buffer.position(doubleStart); buffer.get(doubleBuffer); Double temperature = Double.parseDouble(new String(doubleBuffer)); lineStart = i + 1; MeasurementAggregator aggregator = aggregates.computeIfAbsent(station, s -> new MeasurementAggregator()); aggregator.min = Math.min(aggregator.min, temperature); aggregator.max = Math.max(aggregator.max, temperature); aggregator.sum += temperature; aggregator.count++; } } return aggregates; } public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { ExecutorService executor = Executors.newFixedThreadPool(128); RandomAccessFile file = new RandomAccessFile(FILE, "r"); long position = 0; List>> futures = new ArrayList<>(); while (position < FILE_SIZE) { int end = (int) Math.min(position + SEGMENT_SIZE, FILE_SIZE); int length = (int) (end - position); MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, position, length); while (buffer.get(length - 1) != '\n') { --length; } position += length; int finalLength = length; futures.add(executor.submit(() -> processSegment(buffer, finalLength))); } executor.shutdown(); executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); // Merge results into a single TreeMap Map aggregates = new TreeMap<>(); for (Future> future : futures) { Map segmentAggregates = future.get(); for (Map.Entry entry : segmentAggregates.entrySet()) { MeasurementAggregator aggregator = aggregates.computeIfAbsent(entry.getKey(), s -> new MeasurementAggregator()); aggregator.min = Math.min(aggregator.min, entry.getValue().min); aggregator.max = Math.max(aggregator.max, entry.getValue().max); aggregator.sum += entry.getValue().sum; aggregator.count += entry.getValue().count; } } System.out.println(aggregates); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_davecom.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.math.RoundingMode; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.text.DecimalFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap; import java.util.IntSummaryStatistics; import java.util.List; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Collectors; public class CalculateAverage_davecom { /* * Original Header Could Not Be Changed to Match Checks so... * Copyright 2024 David Kopec * Licensed under the Apache License, Version 2.0 (the "License"); * Created by David Kopec with some inspiration from seijikun's solution * and assistance from GitHub Copilot. */ private static final String FILE = "./measurements.txt"; private static final ConcurrentHashMap mins = new ConcurrentHashMap<>(); private static final ConcurrentHashMap maxs = new ConcurrentHashMap<>(); private static final ConcurrentHashMap sums = new ConcurrentHashMap<>(); private static final ConcurrentHashMap counts = new ConcurrentHashMap<>(); public static void processChunk(MappedByteBuffer chunk, long chunkSize) { // setup chunk.load(); HashMap values = new HashMap<>(); // do the actual processing long end = chunk.position() + chunkSize; // byte[] name = new byte[128]; int value = 0; byte b = 0; boolean negate = false; long nameStart = 0; long nameEnd = 0; int nameLength = 0; while (chunk.position() < end) { // read name up to semicolon nameStart = chunk.position(); b = chunk.get(); while (b != ';') { b = chunk.get(); } nameEnd = chunk.position() - 1; nameLength = (int) (nameEnd - nameStart); // generate byte array for name ByteBuffer nameBuffer = ByteBuffer.allocate(nameLength); chunk.get(chunk.position() - nameLength - 1, nameBuffer.array(), 0, nameLength); // convert name to string // read value value = 0; b = chunk.get(); negate = false; while (b != '\n') { if (b == '.') { b = chunk.get(); continue; } else if (b == '-') { negate = true; b = chunk.get(); continue; } value = value * 10 + (b - '0'); b = chunk.get(); } if (negate) { value = -value; } if (values.containsKey(nameBuffer)) { values.get(nameBuffer).accept(value); } else { IntSummaryStatistics stats = new IntSummaryStatistics(); stats.accept(value); values.put(nameBuffer, stats); } } for (ByteBuffer nameBfr : values.keySet()) { IntSummaryStatistics stats = values.get(nameBfr); mins.compute(nameBfr, (k, v) -> v == null ? stats.getMin() : Math.min(v, stats.getMin())); maxs.compute(nameBfr, (k, v) -> v == null ? stats.getMax() : Math.max(v, stats.getMax())); sums.compute(nameBfr, (k, v) -> v == null ? (int) stats.getSum() : (v + (int) stats.getSum())); counts.compute(nameBfr, (k, v) -> v == null ? (int) stats.getCount() : (v + (int) stats.getCount())); } } public static void outputResults() { // output results sorted by name with format {name}={min}/{mean}/{max} in one giant string // fast string concatenation starting with { and ending with } with a comma between each (no newlines) StringBuilder sb = new StringBuilder(); sb.append('{'); // var sortedNames = mins.keySet().stream().sorted().toArray(String[]::new); DecimalFormat df = new DecimalFormat("0.0"); df.setRoundingMode(RoundingMode.HALF_UP); List sortedNames = mins.keySet().stream() .map(b -> new String(b.array(), 0, b.limit())) .sorted() .collect(Collectors.toList()); for (String nameStr : sortedNames) { ByteBuffer name = ByteBuffer.wrap(nameStr.getBytes()); double min = ((double) mins.get(name)) / 10; double max = ((double) maxs.get(name)) / 10; double average = ((double) sums.get(name)) / ((double) counts.get(name)) / 10; sb.append(nameStr); sb.append('='); sb.append(df.format(min)); sb.append('/'); sb.append(df.format(average)); sb.append('/'); sb.append(df.format(max)); sb.append(','); sb.append(' '); } if (sb.length() > 1) { sb.deleteCharAt(sb.length() - 2); sb.deleteCharAt(sb.length() - 1); } sb.append('}'); System.out.println(sb.toString()); } public static void main(String[] args) throws IOException { // create thread pool ExecutorService es = Executors.newVirtualThreadPerTaskExecutor(); // load file FileChannel fc = FileChannel.open(Path.of(FILE)); // configuration information long fileSize = fc.size(); int numProcessors = Runtime.getRuntime().availableProcessors(); int numChunks = numProcessors * 2000; // System.out.println("numProcessors: " + numProcessors); // System.out.println("numChunks: " + numChunks); // create check buffer ByteBuffer bb = ByteBuffer.allocateDirect(128); // find appropriate chunks // System.out.println("fileSize: " + fileSize); long chunkLimit = fileSize / numChunks; long chunkStart = 0; long chunkEnd = chunkLimit; // int chunkNum = 0; while (chunkEnd < fileSize) { // System.out.println("initiated chunkNum: " + chunkNum); // find the next newline fc.position(chunkEnd); bb.clear(); fc.read(bb); bb.flip(); while (bb.get() != '\n' && bb.position() < bb.limit()) { } chunkEnd = chunkEnd + bb.position(); if (chunkEnd > fileSize) { chunkEnd = fileSize - 1; } // process chunk long chunkSize = chunkEnd - chunkStart; if (chunkSize < 1) { break; } // System.out.println("chunkStart: " + chunkStart); // System.out.println("chunkEnd: " + chunkEnd); // System.out.println("chunkSize: " + chunkSize); MappedByteBuffer chunk = fc.map(FileChannel.MapMode.READ_ONLY, chunkStart, chunkSize); // final int chunkNumFinal = chunkNum; es.submit(() -> { // System.out.println("started chunkNum: " + chunkNumFinal); processChunk(chunk, chunkSize); // System.out.println("finished chunkNum: " + chunkNumFinal); }); chunkStart = chunkEnd; chunkEnd = chunkEnd + chunkLimit; if (chunkEnd > fileSize) { chunkEnd = fileSize - 1; } // chunkNum++; } es.close(); outputResults(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_davery22.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; public class CalculateAverage_davery22 { static final String FILE = "./measurements.txt"; static final int MAP_SIZE = 1 << 16; // Must be a power of two > 10000 static final int MAP_MASK = MAP_SIZE - 1; public static void main(String[] args) throws IOException, InterruptedException { FileChannel in = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ); int concurrency = Runtime.getRuntime().availableProcessors(); Thread[] threads = new Thread[concurrency - 1]; Worker[] workers = new Worker[concurrency - 1]; long fileSize = in.size(); int segmentSize = (int) (fileSize / concurrency); long fileCursor = 0; // Process each segment in its own thread for (int i = 0; i < concurrency - 1; i++) { MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, fileCursor, segmentSize); int limit = segmentSize; for (; limit > 0 && buf.get(limit - 1) != '\n'; limit--) { } buf.limit(limit); fileCursor += limit; Thread t = threads[i] = new Thread(workers[i] = new Worker(buf)); t.start(); } // Process last segment on main thread MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, fileCursor, fileSize - fileCursor); Worker main = new Worker(buf); main.run(); for (Thread t : threads) { t.join(); } // Merge maps for (int i = 0; i < concurrency - 1; i++) { long[][] entries = workers[i].entries; for (int j = 0; entries[j] != null; j++) { main.mergeEntry(entries[j]); } } // Estimate size of output buffer - okay to overestimate, not underestimate long[][] entries = main.entries; int platformCR = System.lineSeparator().length() > 1 ? 1 : 0; // May be different from file int bufferLen = 3 + platformCR; // '{' and '}' and '\n' (and '\r' if detected) int entriesLen = 0; for (; entries[entriesLen] != null; entriesLen++) { // Needs enough space for: '=//, ' where the stats are up to 5 bytes each bufferLen += (entries[entriesLen].length - 4) * 8 + 20; } // Sort by city name Arrays.sort(entries, 0, entriesLen, (a, b) -> { int n = Math.min(a.length, b.length) - 4; for (int i = 0; i < n; i++) { int cmp = Long.compareUnsigned(a[i], b[i]); if (cmp != 0) { return cmp; } } return a.length - b.length; }); // Fill the output buffer byte[] toPrint = new byte[bufferLen]; bufferLen = 0; toPrint[bufferLen++] = '{'; for (int i = 0; i < entriesLen; i++) { if (i > 0) { toPrint[bufferLen++] = ','; toPrint[bufferLen++] = ' '; } long[] entry = entries[i]; int j = 0; for (; j < entry.length - 5; j++) { long word = entry[j]; for (int k = 0; k < 8; k++) { toPrint[bufferLen++] = (byte) ((word >>> (56 - k * 8)) & 0xFF); } } long last = entry[j++]; int lastLen = 8 - (Long.numberOfTrailingZeros(last) >>> 3); for (int k = 0; k < lastLen; k++) { toPrint[bufferLen++] = (byte) ((last >>> (56 - k * 8)) & 0xFF); } long min = entry[j++]; long max = entry[j++]; long sum = entry[j++]; long count = entry[j++]; long mean = mean(sum, count); toPrint[bufferLen++] = '='; bufferLen = statToBytes(min, toPrint, bufferLen); toPrint[bufferLen++] = '/'; bufferLen = statToBytes(mean, toPrint, bufferLen); toPrint[bufferLen++] = '/'; bufferLen = statToBytes(max, toPrint, bufferLen); } toPrint[bufferLen++] = '}'; if (platformCR == 1) { toPrint[bufferLen++] = '\r'; } toPrint[bufferLen++] = '\n'; // Print FileOutputStream out = new FileOutputStream(FileDescriptor.out); out.write(toPrint, 0, bufferLen); } static class Worker implements Runnable { final MappedByteBuffer buf; final int[] indexes = new int[MAP_SIZE]; final long[][] entries = new long[MAP_SIZE][]; int lastIndex; Worker(MappedByteBuffer buf) { this.buf = buf; } @Override public void run() { // Big enough for max city bytes (100 bytes) + temperature value (1 long) long[] item = new long[14]; int itemLen = 0, bufCursor = 0, bufLimit = buf.limit(); int lineSeparatorLen = (bufLimit > 1 && buf.get(bufLimit - 2) == '\r') ? 2 : 1; boolean isLittleEndian = buf.order().equals(ByteOrder.LITTLE_ENDIAN); while (bufCursor < bufLimit) { // Parse next long of bytes, until we see a semicolon long word = 0; if (bufLimit - bufCursor >= 8) { word = isLittleEndian ? Long.reverseBytes(buf.getLong(bufCursor)) : buf.getLong(bufCursor); } else { for (int j = 0; bufCursor + j < bufLimit; j++) { word |= ((long) buf.get(bufCursor + j) & 0xFF) << (56 - j * 8); } } int idx = indexOfSemicolon(word); if (idx < 0) { item[itemLen++] = word; bufCursor += 8; continue; } // Zero-out everything after city bytes if (idx > 0) { item[itemLen++] = word & (-1L << (64 - idx * 8)); } // Parse the temperature value bufCursor += idx + 1; long sign = 1, magnitude; byte b1, b2; if ((b1 = buf.get(bufCursor)) == '-') { sign = -1; bufCursor += 1; b1 = buf.get(bufCursor); } if ((b2 = buf.get(bufCursor + 1)) == '.') { magnitude = 10 * (b1 - '0') + (buf.get(bufCursor + 2) - '0'); bufCursor += 3 + lineSeparatorLen; } else { magnitude = 100 * (b1 - '0') + 10 * (b2 - '0') + (buf.get(bufCursor + 3) - '0'); bufCursor += 4 + lineSeparatorLen; } // Merge to map item[itemLen++] = sign * magnitude; mergeItem(item, itemLen); itemLen = 0; } } static int hash(long word) { return (int) (word ^ (word >>> 16) * (word >>> 32) ^ (word >>> 48)) & MAP_MASK; } void mergeItem(long[] item, int len) { // format [n longs for key, 1 long for value] loop: for (int hash = hash(item[0]);; hash = hash + 1 < MAP_SIZE ? hash + 1 : 0) { // Linear probing int index = indexes[hash]; // Check if new if (index == 0) { long[] entry = new long[len + 3]; System.arraycopy(item, 0, entry, 0, len); entry[len] = item[len - 1]; // initial max entry[len + 1] = item[len - 1]; // initial sum entry[len + 2] = 1; // initial count indexes[hash] = ++lastIndex; entries[lastIndex - 1] = entry; return; } // Check if equal or conflict long[] entry = entries[index - 1]; if (entry.length != len + 3) { continue; } int i = 0; for (; i < len - 1; i++) { if (entry[i] != item[i]) { continue loop; } } // Equal - update stats entry[i] = Math.min(entry[i], item[i]); // min entry[i + 1] = Math.max(entry[i + 1], item[i]); // max entry[i + 2] += item[i]; // sum entry[i + 3] += 1; // count return; } } void mergeEntry(long[] item) { // format: [n longs for key, 4 longs for min/max/sum/count] loop: for (int hash = hash(item[0]);; hash = hash + 1 < MAP_SIZE ? hash + 1 : 0) { // Linear probing int index = indexes[hash]; // Check if new if (index == 0) { indexes[hash] = ++lastIndex; entries[lastIndex - 1] = item; return; } // Check if equal or conflict long[] entry = entries[index - 1]; if (entry.length != item.length) { continue; } int i = 0; for (; i < item.length - 4; i++) { if (entry[i] != item[i]) { continue loop; } } // Equal - update stats entry[i] = Math.min(entry[i], item[i]); // min entry[i + 1] = Math.max(entry[i + 1], item[i + 1]); // max entry[i + 2] += item[i + 2]; // sum entry[i + 3] += item[i + 3]; // count return; } } } // SWAR search based on royvanrijn's code static final long SEMICOLON_PATTERN = ((long) ';' << 56) | ((long) ';' << 48) | ((long) ';' << 40) | ((long) ';' << 32) | ((long) ';' << 24) | ((long) ';' << 16) | ((long) ';' << 8) | ((long) ';'); static int indexOfSemicolon(long word) { long match = word ^ SEMICOLON_PATTERN; // Only matching bytes are zero long mask = ((match - 0x0101010101010101L) & ~match) & 0x8080808080808080L; // Only matching bytes are non-zero (leftmost bit is 1) return mask == 0 ? -1 : Long.numberOfLeadingZeros(mask) >>> 3; // Return byte index of first 1 bit } static int statToBytes(long stat, byte[] buf, int pos) { // stat is fixed point, so the original guaranteed range of [-99.9, 99.9] becomes [-999, 999] if (stat < 0) { stat = -stat; buf[pos++] = '-'; } int digits = stat > 99 ? 3 : 2; buf[pos + digits] = (byte) ((stat % 10) + '0'); buf[pos + digits - 1] = '.'; stat /= 10; for (int i = digits - 2; i >= 0; i--) { buf[pos + i] = (byte) ((stat % 10) + '0'); stat /= 10; } return pos + digits + 1; } static long mean(long sum, long count) { long mean = sum / count; long remainder = sum % count; if (remainder < 0) { // Match the rounding behavior of the baseline, which uses Math.round(number * 10.0) / 10.0. // For positive numbers we need to round up between [.5, 1). // For negative numbers we need to round up between [-.5, 0). // Interestingly this differs from the rounding behavior of String.format("%.1f", number), // which would round -8.55 to -8.6, rather than -8.5. if (count < (-remainder << 1)) { return mean - 1; } } else if (count <= (remainder << 1)) { return mean + 1; } return mean; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_ddimtirov.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.time.Duration; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.LongAdder; public class CalculateAverage_ddimtirov { private static final String FILE = "./measurements.txt"; private static final int MAX_STATIONS = 100_000; private static final int MAX_STATION_NAME_LENGTH = 100; private static final int MAX_UTF8_CODEPOINT_SIZE = 3; private static final int OFFSET_MIN = 0; private static final int OFFSET_MAX = 1; private static final int OFFSET_COUNT = 2; private static final boolean assertions = CalculateAverage_ddimtirov.class.desiredAssertionStatus(); private static final Map hashCollisionOccurrences = new ConcurrentHashMap<>(); @SuppressWarnings("RedundantSuppression") public static void main(String[] args) throws IOException, InterruptedException { var path = Path.of(args.length>0 ? args[0] : FILE); Instant start = null;// Instant.now(); var desiredSegmentsCount = Runtime.getRuntime().availableProcessors(); var fileSegments = FileSegment.forFile(path, desiredSegmentsCount); var loaders = new ThreadGroup("Loaders"); var trackers = Collections.synchronizedList(new ArrayList()); var threads = fileSegments.stream().map(fileSegment -> Thread // manually start thread per segment .ofPlatform() .group(loaders) .name(STR."Segment \{fileSegment}") .start(() -> { try (var fileChannel = (FileChannel) Files.newByteChannel(path, StandardOpenOption.READ)) { var tracker = new Tracker(); var memorySegment = fileChannel.map(FileChannel.MapMode.READ_ONLY, fileSegment.start(), fileSegment.size(), Arena.ofConfined()); tracker.processSegment(memorySegment); trackers.add(tracker); } catch (IOException e) { throw new RuntimeException(e); } }) ).toList(); for (Thread thread : threads) thread.join(); assert trackers.size() == threads.size(); assert trackers.size() <= desiredSegmentsCount; var result = summarizeTrackers(trackers.toArray(Tracker[]::new)); System.out.println(result); // noinspection ConstantValue if (start != null) { System.err.println(Duration.between(start, Instant.now())); if (assertions) System.err.printf("hash clashes: %s%n", hashCollisionOccurrences); } assert Files.readAllLines(Path.of("measurements.out")).getFirst().equals(result); } record FileSegment(int index, long start, long size) { @Override public String toString() { return STR."#\{index} [\{start}..\{start + size}] \{size} bytes"; } public static List forFile(Path file, int desiredSegmentsCount) throws IOException { try (var raf = new RandomAccessFile(file.toFile(), "r")) { var segments = new ArrayList(); var fileSize = raf.length(); var segmentSize = Math.max(1024 * 1024, fileSize / desiredSegmentsCount); var i = 1; var prevEnd = 0L; while (prevEnd < fileSize-1) { var start = prevEnd; var end = findNewLineAfter(raf, prevEnd + segmentSize, fileSize); segments.add(new FileSegment(i, start, end - start)); prevEnd = end; } return segments; } } private static long findNewLineAfter(RandomAccessFile raf, long location, long fileSize) throws IOException { raf.seek(location); while (location < fileSize) { location++; int c = raf.read(); if (c == '\r' || c == '\n') break; } return Math.min(location, fileSize - 1); } } static class Accumulator { public final String name; public int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, count; public long sum; public Accumulator(String name) { this.name = name; } public void accumulate(int min, int max, int count, long sum) { if (this.min > min) this.min = min; if (this.max < max) this.max = max; this.count += count; this.sum += sum; } @Override public String toString() { var mean = Math.round((double) sum / count) / 10.0; return (min / 10.0) + "/" + mean + "/" + (max / 10.0); } } private static String summarizeTrackers(Tracker[] trackers) { var result = new TreeMap(); for (var i = 0; i < Tracker.SIZE; i++) { Accumulator acc = null; for (Tracker tracker : trackers) { var name = tracker.names[i]; if (name == null) { continue; } else if (acc == null || !name.equals(acc.name)) { acc = result.computeIfAbsent(name, Accumulator::new); } acc.accumulate( tracker.minMaxCount[i * 3 + OFFSET_MIN], tracker.minMaxCount[i * 3 + OFFSET_MAX], tracker.minMaxCount[i * 3 + OFFSET_COUNT], tracker.sums[i]); } } return result.toString(); } static class Tracker { public static final int SIZE = MAX_STATIONS * 10; private static final int CORRECTION_0_TO_9 = '0' * 10 + '0'; private static final int CORRECTION_10_TO_99 = '0' * 100 + '0' * 10 + '0'; private final byte[] tempNameBytes = new byte[MAX_STATION_NAME_LENGTH * MAX_UTF8_CODEPOINT_SIZE]; private final int[] minMaxCount = new int[SIZE * 3]; private final long[] sums = new long[SIZE]; private final String[] names = new String[SIZE]; private final byte[][] nameBytes = new byte[SIZE][]; private void processSegment(MemorySegment memory) { long limit = memory.byteSize(); var pos = 0; // skip newlines so the chunk limit check can work correctly while (pos < limit) { byte c = memory.get(ValueLayout.JAVA_BYTE, pos); if (c != '\r' && c != '\n') break; pos++; } while (pos < limit) { byte b; int nameLength = 0, nameHash = 0; while ((b = memory.get(ValueLayout.JAVA_BYTE, pos++)) != ';') { tempNameBytes[nameLength++] = b; nameHash = nameHash * 31 + b; } int sign; if (memory.get(ValueLayout.JAVA_BYTE, pos) == '-') { sign = -1; pos++; } else { sign = 1; } int temperature; // between [-99.9 and 99.9], mapped to fixed point int (scaled by 10) if (memory.get(ValueLayout.JAVA_BYTE, pos + 1) == '.') { // between -9.99 and 9.99 assert memory.get(ValueLayout.JAVA_BYTE, pos + 1) == '.'; temperature = memory.get(ValueLayout.JAVA_BYTE, pos) * 10 + memory.get(ValueLayout.JAVA_BYTE, pos + 2) - CORRECTION_0_TO_9; pos += 3; // #.# - 3 chars } else { // between [-99.9 and -9.99] OR [9.99 and 99.9] assert memory.get(ValueLayout.JAVA_BYTE, pos + 2) == '.'; temperature = memory.get(ValueLayout.JAVA_BYTE, pos) * 100 + memory.get(ValueLayout.JAVA_BYTE, pos + 1) * 10 + memory.get(ValueLayout.JAVA_BYTE, pos + 3) - CORRECTION_10_TO_99; pos += 4; // ##.# - 4 chars } processLine(nameHash, tempNameBytes, nameLength, temperature * sign); // skip newlines so the chunk limit check can work correctly while (pos < limit) { byte c = memory.get(ValueLayout.JAVA_BYTE, pos); if (c != '\r' && c != '\n') break; pos++; } } } public void processLine(int nameHash, byte[] nameBytesBuffer, int nameLength, int temperature) { var i = Math.abs(nameHash) % SIZE; while (true) { if (names[i] == null) { byte[] trimmedBytes = Arrays.copyOf(nameBytesBuffer, nameLength); names[i] = new String(trimmedBytes, StandardCharsets.UTF_8); nameBytes[i] = trimmedBytes; minMaxCount[i*3 + OFFSET_MIN] = Integer.MAX_VALUE; minMaxCount[i*3 + OFFSET_MAX] = Integer.MIN_VALUE; break; } else if (nameBytes[i].length==nameLength && Arrays.equals(nameBytes[i], 0, nameLength, nameBytesBuffer, 0, nameLength)) { break; } if (assertions) { var key = new String(nameBytesBuffer, 0, nameLength, StandardCharsets.UTF_8); hashCollisionOccurrences.computeIfAbsent(key, _ -> new LongAdder()).increment(); } i = (i + 1) % SIZE; } if (assertions) { var key = new String(nameBytesBuffer, 0, nameLength, StandardCharsets.UTF_8); if (hashCollisionOccurrences.containsKey(key)) { hashCollisionOccurrences.computeIfAbsent(STR."\{key}[\{i}]", _ -> new LongAdder()).increment(); } } sums[i] += temperature; int mmcIndex = i * 3; var min = minMaxCount[mmcIndex + OFFSET_MIN]; var max = minMaxCount[mmcIndex + OFFSET_MAX]; if (temperature < min) minMaxCount[mmcIndex + OFFSET_MIN] = temperature; if (temperature > max) minMaxCount[mmcIndex + OFFSET_MAX] = temperature; minMaxCount[mmcIndex + OFFSET_COUNT]++; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_deemkeen.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.*; public class CalculateAverage_deemkeen { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { File file = new File(FILE); long fileSize = file.length(); int numberOfSegments = 400; long segmentSize = fileSize / numberOfSegments; if (segmentSize < 100) { numberOfSegments = 1; segmentSize = fileSize; } List segments = new ArrayList<>(); try ( var randomAccessFile = new RandomAccessFile(file, "r"); var fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE), StandardOpenOption.READ)) { for (int i = 0; i < numberOfSegments; i++) { long segStart = i * segmentSize; long segEnd = (i == numberOfSegments - 1) ? fileSize : segStart + segmentSize; if (i != 0) { randomAccessFile.seek(segStart); while (segStart < segEnd) { segStart++; if (randomAccessFile.read() == '\n') break; } } if (i != numberOfSegments - 1) { randomAccessFile.seek(segEnd); while (segEnd < fileSize) { segEnd++; if (randomAccessFile.read() == '\n') break; } } segments.add(new SegmentPair(new FileSegment(segStart, segEnd), new ByteArrayToResultMap())); } try (ExecutorService es = Executors.newVirtualThreadPerTaskExecutor()) { var partitions = Collections.synchronizedList(new ArrayList()); for (var segment : segments) { var segmentResultMap = segment.value; es.execute(() -> { MappedByteBuffer bb; try { bb = fileChannel.map(FileChannel.MapMode.READ_ONLY, segment.key.start, segment.key.end - segment.key.start); } catch (IOException e) { throw new RuntimeException(e); } byte[] buffer = new byte[100]; int startLine; while ((startLine = bb.position()) < bb.limit()) { int currentPosition = startLine; byte b; int offset = 0; int hash = 0; while (currentPosition != segment.key.end && (b = bb.get(currentPosition++)) != ';') { buffer[offset++] = b; hash = 31 * hash + b; } int temp; int negative = 1; // Inspired by @yemreinci to unroll this even further if (bb.get(currentPosition) == '-') { negative = -1; currentPosition++; } if (bb.get(currentPosition + 1) == '.') { temp = negative * ((bb.get(currentPosition) - '0') * 10 + (bb.get(currentPosition + 2) - '0')); currentPosition += 3; } else { temp = negative * ((bb.get(currentPosition) - '0') * 100 + ((bb.get(currentPosition + 1) - '0') * 10 + (bb.get(currentPosition + 3) - '0'))); currentPosition += 4; } if (bb.get(currentPosition) == '\r') { currentPosition++; } currentPosition++; segmentResultMap.putOrMerge(buffer, 0, offset, temp / 10.0, hash); bb.position(currentPosition); } }); partitions.add(segmentResultMap); } try { es.shutdown(); while (!es.awaitTermination(24L, TimeUnit.HOURS)) { System.out.println("Still waiting for termination.."); } } catch (InterruptedException e) { // do nothing } TreeMap resultMap = new TreeMap<>(); for (ByteArrayToResultMap partition : partitions) { for (Entry e : partition.getAll()) { resultMap.merge(new String(e.key()), e.value(), CalculateAverage_deemkeen::merge); } } System.out.println(resultMap); } } } private static Result merge(Result v, Result value) { return merge(v, value.min, value.max, value.sum, value.count); } private static Result merge(Result v, double value, double value1, double value2, long value3) { v.min = Math.min(v.min, value); v.max = Math.max(v.max, value1); v.sum += value2; v.count += value3; return v; } record Pair(int slot, Result slotValue) { } record SegmentPair(FileSegment key, ByteArrayToResultMap value) { } record Entry(byte[] key, Result value) { } record FileSegment(long start, long end) { } static class Result { double min; double max; double sum; long count; Result(double value) { this.min = value; this.max = value; this.sum = value; this.count = 1; } @Override public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } double round(double v) { return Math.round(v * 10.0) / 10.0; } } static class ByteArrayToResultMap { public static final int MAPSIZE = 1024 * 128; Result[] slots = new Result[MAPSIZE]; byte[][] keys = new byte[MAPSIZE][]; public void putOrMerge(byte[] key, int offset, int size, double temp, int hash) { int slot = hash & (slots.length - 1); var slotValue = slots[slot]; // Linear probe for open slot while (slotValue != null && (keys[slot].length != size || !Arrays.equals(keys[slot], 0, size, key, offset, size))) { slot = (slot + 1) & (slots.length - 1); slotValue = slots[slot]; } Result value = slotValue; if (value == null) { slots[slot] = new Result(temp); byte[] bytes = new byte[size]; System.arraycopy(key, offset, bytes, 0, size); keys[slot] = bytes; } else { value.min = Math.min(value.min, temp); value.max = Math.max(value.max, temp); value.sum += temp; value.count += 1; } } private int hashCode(byte[] a, int fromIndex, int length) { int result = 0; int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { result = 31 * result + a[i]; } return result; } private Pair getPair(byte[] key, int offset, int size) { int hash = hashCode(key, offset, size); int slot = hash & (slots.length - 1); Result slotValue = slots[slot]; // Linear probe for open slot while (slotValue != null && (keys[slot].length != size || !Arrays.equals(keys[slot], 0, size, key, offset, size))) { slot = (slot + 1) & (slots.length - 1); slotValue = slots[slot]; } return new Pair(slot, slotValue); } public Result get(byte[] key, int offset, int size) { return getPair(key, offset, size).slotValue(); } // Get all pairs public List getAll() { List result = new ArrayList<>(); for (int i = 0; i < slots.length; i++) { Result slotValue = slots[i]; if (slotValue != null) { result.add(new Entry(keys[i], slotValue)); } } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_dkarampi.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; public class CalculateAverage_dkarampi { private static final String FILE = "./measurements.txt"; private static final int BUFFER_SIZE = (1 << 29); // 500mb private static final int HT_SIZE = nextPowerOfTwo(10000); private static final int NUM_THREADS = 8; private final List stationHashTables = new ArrayList<>(); public static void main(String[] args) throws Exception { new CalculateAverage_dkarampi().runFast(); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } private static boolean areEqual(byte[] a, int aLen, byte[] b, int bLen) { if (aLen != bLen) { return false; } for (byte i = 0; i < aLen; i++) { if (a[i] != b[i]) { return false; } } return true; } public static int nextPowerOfTwo(int n) { for (int i = 1; i < 32; i <<= 1) { n |= n >> i; } return n + 1; } private void runFast() throws Exception { createStationHashTables(); FileChannel channel = FileChannel.open(Path.of(FILE)); List> buffersList = new ArrayList<>(); for (int i = 0; i < NUM_THREADS; i++) { buffersList.add(new ArrayList<>()); } List buffers = createBuffers(channel); for (int i = 0; i < buffers.size(); i++) { buffersList.get(i % NUM_THREADS).add(buffers.get(i)); } List tasks = new ArrayList<>(); for (int i = 0; i < NUM_THREADS; i++) { tasks.add(new Task(stationHashTables.get(i), buffersList.get(i))); } ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS); Future[] futures = new Future[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; i++) { futures[i] = executorService.submit(tasks.get(i)); } for (Future future : futures) { future.get(); } sortAndPrint(); executorService.shutdown(); channel.close(); } private void createStationHashTables() { for (int i = 0; i < NUM_THREADS; i++) { Station[] stationsHashTable = new Station[HT_SIZE]; for (int j = 0; j < HT_SIZE; j++) { stationsHashTable[j] = new Station(); } stationHashTables.add(stationsHashTable); } } private List createBuffers(FileChannel channel) throws Exception { List buffers = new ArrayList<>(); long size = channel.size(); int lastByte; for (long offset = 0; offset < size; offset += lastByte + 1) { long sizeToMap = Math.min(size - offset, BUFFER_SIZE); MappedByteBuffer buffer = channel.map(READ_ONLY, offset, sizeToMap); lastByte = (int) sizeToMap - 1; while (buffer.get(lastByte) != '\n') --lastByte; buffers.add(new Buffer(buffer, lastByte + 1)); } return buffers; } private void sortAndPrint() { TreeMap sortedStations = new TreeMap<>(); for (Station[] stationHashTable : stationHashTables) { for (Station station : stationHashTable) { if (station.freq == 0) { continue; } String key = new String(station.name, 0, station.nameLen); Station st = sortedStations.get(key); if (st == null) { sortedStations.put(key, station); } else { st.min = Math.min(st.min, station.min); st.max = Math.max(st.max, station.max); st.sum += station.sum; st.freq += station.freq; } } } StringBuilder sb = new StringBuilder(); sb.append("{"); for (Map.Entry entry : sortedStations.entrySet()) { String name = entry.getKey(); Station station = entry.getValue(); sb.append(name); sb.append("="); sb.append(round(station.min)); sb.append("/"); sb.append(round(round(station.sum) / station.freq)); sb.append("/"); sb.append(round(station.max)); sb.append(", "); } sb.delete(sb.length() - 2, sb.length()); sb.append("}"); System.out.println(sb); } private record Buffer(ByteBuffer byteBuffer, int length) { } private static class Station { double sum; double min = 100; double max = -100; int freq; short nameLen; byte[] name = new byte[nextPowerOfTwo(100)]; } private record Task(Station[] stations, List buffers) implements Runnable { @Override public void run() { for (Buffer buffer : buffers) { process(buffer); } } private void process(Buffer buffer) { short nameLen = 0; int hash = 5381; int temperature; byte[] name = new byte[100]; for (int i = 0; i < buffer.length; i++) { byte c = buffer.byteBuffer.get(i); if (c == ';') { int sign = 1; c = buffer.byteBuffer.get(++i); if (c == '-') { sign = -1; c = buffer.byteBuffer.get(++i); temperature = (c - '0') * 10; c = buffer.byteBuffer.get(++i); if (c == '.') { c = buffer.byteBuffer.get(++i); temperature = temperature + c - '0'; } else { temperature = temperature + c - '0'; ++i; // dot c = buffer.byteBuffer.get(++i); temperature = temperature * 10 + c - '0'; } } else { temperature = (c - '0') * 10; c = buffer.byteBuffer.get(++i); if (c == '.') { c = buffer.byteBuffer.get(++i); temperature = temperature + c - '0'; } else { temperature = temperature + c - '0'; ++i; // dot c = buffer.byteBuffer.get(++i); temperature = temperature * 10 + c - '0'; } } hash = hash & 0x7FFFFFFF; updateStations(hash, name, nameLen, sign * (double) temperature / 10); ++i; // For '\n' nameLen = 0; hash = 5383; } else { name[nameLen++] = c; hash = ((hash << 5) + hash) + c; } } } private void updateStations(int hash, byte[] name, short nameLen, double temperature) { int idx; for (idx = hash % HT_SIZE; stations[idx].freq != 0; idx = (idx + 1) % HT_SIZE) { if (areEqual(stations[idx].name, stations[idx].nameLen, name, nameLen)) { stations[idx].sum += temperature; stations[idx].min = Math.min(stations[idx].min, temperature); stations[idx].max = Math.max(stations[idx].max, temperature); ++stations[idx].freq; return; } } stations[idx].sum = temperature; stations[idx].min = temperature; stations[idx].max = temperature; stations[idx].nameLen = nameLen; System.arraycopy(name, 0, stations[idx].name, 0, nameLen); stations[idx].freq = 1; } }} ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_dpsoft.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.Phaser; public class CalculateAverage_dpsoft { private static final String FILE = "./measurements.txt"; private static final int MAX_ROWS = 1 << 15; private static final int ROWS_MASK = MAX_ROWS - 1; public static void main(String[] args) throws IOException { final var cpus = Runtime.getRuntime().availableProcessors(); final var segments = getMemorySegments(cpus); final var tasks = new MeasurementExtractor[segments.size()]; final var phaser = new Phaser(segments.size()); for (int i = 0; i < segments.size(); i++) { tasks[i] = new MeasurementExtractor(segments.get(i), phaser); } phaser.awaitAdvance(phaser.getPhase()); final var allMeasurements = Arrays.stream(tasks) .parallel() .map(MeasurementExtractor::getMeasurements) .reduce(MeasurementMap::merge) .orElseThrow(); System.out.println(sortSequentially(allMeasurements)); System.exit(0); } private static Map sortSequentially(MeasurementMap allMeasurements) { final Map sorted = new TreeMap<>(); for (Measurement m : allMeasurements.measurements) { if (m != null) { sorted.put(new String(m.name, StandardCharsets.UTF_8), m); } } return sorted; } // Inspired by @spullara private static List getMemorySegments(int numberOfSegments) throws IOException { var file = new File(FILE); long fileSize = file.length(); long segmentSize = fileSize / numberOfSegments; List segments = new ArrayList<>(numberOfSegments); if (fileSize < 1_000_000) { segments.add(new FileSegment(0, fileSize)); return segments; } while (segmentSize >= Integer.MAX_VALUE) { numberOfSegments += 1; segmentSize = fileSize / numberOfSegments; } try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) { for (int i = 0; i < numberOfSegments; i++) { long segStart = i * segmentSize; long segEnd = (i == numberOfSegments - 1) ? fileSize : segStart + segmentSize; segStart = findSegment(i, 0, randomAccessFile, segStart, segEnd); segEnd = findSegment(i, numberOfSegments - 1, randomAccessFile, segEnd, fileSize); segments.add(new FileSegment(segStart, segEnd)); } } return segments; } private static long findSegment(int i, int skipSegment, RandomAccessFile raf, long location, long fileSize) throws IOException { if (i != skipSegment) { raf.seek(location); while (location < fileSize) { location++; if (raf.read() == '\n') break; } } return location; } record FileSegment(long start, long end) { } static final class MeasurementExtractor implements Runnable { private final FileSegment segment; private final Phaser phaser; private final MeasurementMap measurements = new MeasurementMap(); MeasurementExtractor(FileSegment memorySegment, Phaser phaser) { this.segment = memorySegment; this.phaser = phaser; (new Thread(this)).start(); } @Override public void run() { long segmentEnd = segment.end(); try (var fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { var mbb = fileChannel.map(FileChannel.MapMode.READ_ONLY, segment.start(), segmentEnd - segment.start()); mbb.order(ByteOrder.nativeOrder()); if (segment.start() > 0) { skipToFirstLine(mbb); } while (mbb.remaining() > 0 && mbb.position() <= segmentEnd) { int pos = mbb.position(); int nameHash = hashAndRewind(mbb); var m = measurements.getOrCompute(nameHash, mbb, pos); int temp = readTemperatureFromBuffer(mbb); m.sample(temp); } } catch (IOException e) { throw new RuntimeException("Error reading file", e); } finally { phaser.arriveAndAwaitAdvance(); } } // inspired by @lawrey private static int hashAndRewind(MappedByteBuffer mbb) { int hash = 0; int idx = mbb.position(); outer: while (true) { int name = mbb.getInt(); for (int c = 0; c < 4; c++) { int b = (name >> (c << 3)) & 0xFF; if (b == ';') { idx += c + 1; break outer; } hash ^= b * 82805; } idx += 4; } var rewind = mbb.position() - idx; mbb.position(mbb.position() - rewind); return hash; } private static int readTemperatureFromBuffer(MappedByteBuffer mbb) { int temp = 0; boolean negative = false; outer: while (mbb.remaining() > 0) { int b = mbb.get(); switch (b) { case '-': negative = true; break; default: temp = 10 * temp + (b - '0'); break; case '.': b = mbb.get(); temp = 10 * temp + (b - '0'); case '\r': mbb.get(); case '\n': break outer; } } if (negative) temp = -temp; return temp; } public MeasurementMap getMeasurements() { return measurements; } // Skips to the first line in the buffer, used for chunk processing. private static void skipToFirstLine(MappedByteBuffer mbb) { while ((mbb.get() & 0xFF) >= ' ') { // Skip bytes until reaching the start of a line. } } } // credits to @shipilev static class MeasurementMap { private final Measurement[] measurements = new Measurement[MAX_ROWS]; public Measurement getOrCompute(int hash, MappedByteBuffer mbb, int position) { int index = hash & ROWS_MASK; var measurement = measurements[index]; if (measurement != null && hash == measurement.nameHash && Measurement.equalsTo(measurement.name, mbb, position)) { return measurement; } else { return compute(hash, mbb, position); } } private Measurement compute(int hash, MappedByteBuffer mbb, int position) { var index = hash & ROWS_MASK; Measurement m; while (true) { m = measurements[index]; if (m == null || (hash == m.nameHash && Measurement.equalsTo(m.name, mbb, position))) { break; } index = (index + 1) & ROWS_MASK; } if (m == null) { int len = mbb.position() - position - 1; byte[] bytes = new byte[len]; mbb.position(position); mbb.get(bytes, 0, len); mbb.get(); measurements[index] = m = new Measurement(bytes, hash); } return m; } public MeasurementMap merge(MeasurementMap otherMap) { for (Measurement other : otherMap.measurements) { if (other == null) continue; int index = other.nameHash & ROWS_MASK; while (true) { Measurement m = measurements[index]; if (m == null) { measurements[index] = other; break; } else if (Arrays.equals(m.name, other.name)) { m.merge(other); break; } else { index = (index + 1) & ROWS_MASK; } } } return this; } } static final class Measurement { public final int nameHash; public final byte[] name; public long sum; public int count = 0; public int min = Integer.MAX_VALUE; public int max = Integer.MIN_VALUE; public Measurement(byte[] name, int nameHash) { this.name = name; this.nameHash = nameHash; } public static boolean equalsTo(byte[] name, MappedByteBuffer mbb, int position) { int len = mbb.position() - position - 1; if (len != name.length) return false; for (int i = 0; i < len; i++) { if (name[i] != mbb.get(position + i)) return false; } return true; } public void sample(int temp) { min = Math.min(min, temp); max = Math.max(max, temp); sum += temp; count++; } public Measurement merge(Measurement m2) { min = Math.min(min, m2.min); max = Math.max(max, m2.max); sum += m2.sum; count += m2.count; return this; } public String toString() { return round(((double) min) / 10.0) + "/" + round((((double) sum) / 10.0) / count) + "/" + round(((double) max) / 10.0); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_dqhieuu.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class CalculateAverage_dqhieuu { private static final String FILE = "measurements.txt"; private static double round(double value) { return Math.round(value * 10.0) / 10.0; } private static class MeasurementAggregator { private Lock lock = new ReentrantLock(); private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum = 0; private int count = 0; @Override public String toString() { return round(min) + "/" + round(round(sum) / count) + "/" + round(max); } } public static void main(String[] args) throws IOException { var lineStream = Files.lines(Paths.get(FILE)).parallel(); Map measurements = new ConcurrentHashMap<>(10_000); lineStream.forEach( l -> { var sepIdx = 0; while (l.charAt(sepIdx) != ';') { sepIdx++; } var station = l.substring(0, sepIdx); int valueInt = 0; int sign = l.charAt(sepIdx + 1) == '-' ? -1 : 1; var lineLength = l.length(); for (var i = sepIdx + 1; i < lineLength; i++) { var c = l.charAt(i); if (c == '-' || c == '.') { continue; } valueInt = valueInt * 10 + (c - '0'); } var value = ((double) valueInt / 10.0) * sign; var agg = measurements.computeIfAbsent(station, k -> new MeasurementAggregator()); agg.lock.lock(); if (value < agg.min) { agg.min = value; } if (value > agg.max) { agg.max = value; } agg.sum += value; agg.count++; agg.lock.unlock(); }); Map sortedEntries = new TreeMap<>(measurements); var res = new StringBuilder(); res.append("{"); var first = true; for (var entry : sortedEntries.entrySet()) { if (first) { first = false; } else { res.append(", "); } var k = entry.getKey(); var v = entry.getValue(); res.append(k); res.append('='); res.append(v); } res.append("}"); System.out.println(res); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_ebarlas.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.TreeMap; public class CalculateAverage_ebarlas { private static final Arena ARENA = Arena.global(); private static final int MAX_KEY_SIZE = 104; // 4 additional bytes to allow for single-int overflow due to padding private static final int MAX_VAL_SIZE = 5; // -dd.d private static final int MAX_LINE_SIZE = MAX_KEY_SIZE + MAX_VAL_SIZE + 2; // key, semicolon, val, newline private static final int HASH_TBL_SIZE = 131_071; // range of allowed hash values, inclusive private static final Unsafe UNSAFE = makeUnsafe(); private static Unsafe makeUnsafe() { try { var f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws IOException, InterruptedException { var path = Paths.get("measurements.txt"); var channel = FileChannel.open(path, StandardOpenOption.READ); var numPartitions = Runtime.getRuntime().availableProcessors(); var partitionSize = channel.size() / numPartitions; var partitions = new Partition[numPartitions]; var threads = new Thread[numPartitions]; for (int i = 0; i < numPartitions; i++) { var pIdx = i; var pStart = pIdx * partitionSize; var pEnd = pIdx == numPartitions - 1 ? channel.size() // last partition might be slightly larger : pStart + partitionSize; var pSize = pEnd - pStart; Runnable r = () -> { try { var ms = channel.map(FileChannel.MapMode.READ_ONLY, pStart, pSize, ARENA); partitions[pIdx] = processSegment(ms, pIdx == 0, pIdx == numPartitions - 1); } catch (IOException e) { throw new RuntimeException(e); } }; threads[i] = new Thread(r); threads[i].start(); } for (var thread : threads) { thread.join(); } var partitionList = List.of(partitions); foldFootersAndHeaders(partitionList); printResults(foldStats(partitionList)); } private static void printResults(Stats[] stats) { // adheres to Gunnar's reference code var result = new TreeMap(); for (var st : stats) { if (st != null) { var key = new String(convert(st.keyAddr, st.keyLen, st.lastBytes), StandardCharsets.UTF_8); result.put(key, format(st)); } } System.out.println(result); } private static byte[] convert(long keyAddr, int keyLen, int keyLastBytes) { var len = keyLastBytes == 4 ? keyLen * 4 // fully packed : (keyLen - 1) * 4 + keyLastBytes; // last int partially packed var bytes = new byte[len]; var idx = 0; for (long i = 0; i < keyLen; i++) { var offset = i << 2; var n = UNSAFE.getInt(keyAddr + offset); var bound = i == keyLen - 1 ? keyLastBytes : 4; for (int j = 0; j < bound; j++) { bytes[idx++] = (byte) (n & 0xFF); n >>>= 8; } } return bytes; } private static String format(Stats st) { // adheres to expected output format return round(st.min / 10.0) + "/" + round((st.sum / 10.0) / st.count) + "/" + round(st.max / 10.0); } private static double round(double value) { // Gunnar's round function return Math.round(value * 10.0) / 10.0; } private static Stats[] foldStats(List partitions) { // fold stats from all partitions into first partition var target = partitions.getFirst().stats; for (int i = 1; i < partitions.size(); i++) { var current = partitions.get(i).stats; for (int j = 0; j < current.length; j++) { if (current[j] != null) { var t = findInTable(target, current[j].hash, current[j].keyAddr, current[j].keyLen, current[j].lastBytes); t.min = Math.min(t.min, current[j].min); t.max = Math.max(t.max, current[j].max); t.sum += current[j].sum; t.count += current[j].count; } } } return target; } private static void foldFootersAndHeaders(List partitions) { // fold footers and headers into prev partition for (int i = 1; i < partitions.size(); i++) { var pNext = partitions.get(i); var pPrev = partitions.get(i - 1); var merged = mergeFooterAndHeader(pPrev.footer, pNext.header); if (merged != null && merged.length != 0) { if (merged[merged.length - 1] == '\n') { // fold into prev partition doProcessSegment(ARENA.allocateArray(ValueLayout.JAVA_BYTE, merged), 0, pPrev.stats, true); } else { // no newline appeared in partition, carry forward pNext.footer = merged; } } } } private static byte[] mergeFooterAndHeader(byte[] footer, byte[] header) { if (footer == null) { return header; } if (header == null) { return footer; } var merged = new byte[footer.length + header.length]; System.arraycopy(footer, 0, merged, 0, footer.length); System.arraycopy(header, 0, merged, footer.length, header.length); return merged; } private static Partition processSegment(MemorySegment ms, boolean first, boolean last) { var stats = new Stats[HASH_TBL_SIZE + 1]; // vals range from [0, size] inclusive var header = first ? null : readHeader(ms); var keyStart = doProcessSegment(ms, header == null ? 0 : header.offset, stats, last); // last segment is complete var footer = keyStart < ms.byteSize() ? readFooter(ms, keyStart) : null; return new Partition(header == null ? null : header.data, footer, stats); } private static long doProcessSegment(MemorySegment ms, long offset, Stats[] stats, boolean complete) { long cursor = ms.address() + offset; long keyBaseAddr = UNSAFE.allocateMemory(MAX_KEY_SIZE); // reusable target for current key data long lineStart = cursor; // start of key in segment used for footer calc long limit = ms.address() + (complete ? ms.byteSize() : ms.byteSize() - MAX_LINE_SIZE); // stop short of longest line, sweep up at the end while (cursor < limit) { // one line per iteration int keyHash = 0; // key hash code long keyAddr = keyBaseAddr; // address for next int int keyArrLen = 0; // number of key 4-byte ints int keyLastBytes; // occupancy in last byte (1, 2, 3, or 4) int val; while (true) { int n = UNSAFE.getInt(cursor); cursor += 4; if ((n & 0xFF) == ';') { // ;vvv UNSAFE.putInt(keyAddr, 0); // always pad with extra int to facilitate 8-byte aligned comparisons keyLastBytes = 4; byte b0 = (byte) ((n >> 8) & 0xFF); byte b1 = (byte) ((n >> 16) & 0xFF); byte b2 = (byte) ((n >> 24) & 0xFF); if (b0 == '-') { if (b2 != '.') { // 6 bytes: -dd.dn cursor++; // decimal point byte b4 = UNSAFE.getByte(cursor); cursor += 2; // adv beyond digit and newline val = -(((b1 - '0') * 10 + (b2 - '0')) * 10 + (b4 - '0')); } else { // 5 bytes: -d.dn byte b3 = UNSAFE.getByte(cursor); cursor += 2; // digit and newline val = -((b1 - '0') * 10 + (b3 - '0')); } } else { if (b1 != '.') { // 5 bytes: dd.dn var b3 = UNSAFE.getByte(cursor); cursor += 2; // digit and newline val = ((b0 - '0') * 10 + (b1 - '0')) * 10 + (b3 - '0'); } else { // 4 bytes: d.dn cursor++; // newline val = (b0 - '0') * 10 + (b2 - '0'); } } break; } else if ((n & 0xFF00) == 0x3b00) { // k;vv int k = n & 0xFF; UNSAFE.putLong(keyAddr, k); // pad with extra int for comparison alignment keyLastBytes = 1; keyArrLen++; keyHash += k; byte b0 = (byte) ((n >> 16) & 0xFF); byte b1 = (byte) ((n >> 24) & 0xFF); byte b2 = UNSAFE.getByte(cursor++); if (b0 == '-') { if (b2 != '.') { // 6 bytes: -dd.dn cursor++; // decimal point byte b4 = UNSAFE.getByte(cursor); cursor += 2; // adv beyond digit and newline val = -(((b1 - '0') * 10 + (b2 - '0')) * 10 + (b4 - '0')); } else { // 5 bytes: -d.dn byte b3 = UNSAFE.getByte(cursor); cursor += 2; // digit newline val = -((b1 - '0') * 10 + (b3 - '0')); } } else { if (b1 != '.') { // 5 bytes: dd.dn byte b3 = UNSAFE.getByte(cursor); cursor += 2; // newline val = ((b0 - '0') * 10 + (b1 - '0')) * 10 + (b3 - '0'); } else { // 4 bytes: d.dn cursor++; val = (b0 - '0') * 10 + (b2 - '0'); } } break; } else if ((n & 0xFF0000) == 0x3b0000) { // kk;v int k = n & 0xFFFF; UNSAFE.putLong(keyAddr, k); // pad with extra int for comparison alignment keyLastBytes = 2; keyArrLen++; keyHash += k; byte b0 = (byte) ((n >> 24) & 0xFF); if (b0 == '-') { n = UNSAFE.getInt(cursor); cursor += 4; byte b1 = (byte) (n & 0xFF); byte b2 = (byte) ((n >> 8) & 0xFF); byte b3 = (byte) ((n >> 16) & 0xFF); if (b2 != '.') { // 6 bytes: -dd.dn byte b4 = (byte) ((n >> 24) & 0xFF); cursor++; // newline val = -(((b1 - '0') * 10 + (b2 - '0')) * 10 + (b4 - '0')); } else { // 5 bytes: -d.dn val = -((b1 - '0') * 10 + (b3 - '0')); } } else { byte b1 = UNSAFE.getByte(cursor++); byte b2 = UNSAFE.getByte(cursor++); byte b3 = UNSAFE.getByte(cursor++); if (b1 != '.') { // 5 bytes: dd.dn cursor++; // newline val = ((b0 - '0') * 10 + (b1 - '0')) * 10 + (b3 - '0'); } else { // 4 bytes: d.dn val = (b0 - '0') * 10 + (b2 - '0'); } } break; } else if ((n & 0xFF000000) == 0x3b000000) { // kkk; int k = n & 0xFFFFFF; UNSAFE.putLong(keyAddr, k); // pad with extra int for comparison alignment keyLastBytes = 3; keyArrLen++; keyHash += k; n = UNSAFE.getInt(cursor); cursor += 4; byte b0 = (byte) (n & 0xFF); byte b1 = (byte) ((n >> 8) & 0xFF); byte b2 = (byte) ((n >> 16) & 0xFF); byte b3 = (byte) ((n >> 24) & 0xFF); if (b0 == '-') { if (b2 != '.') { // 6 bytes: -dd.dn byte b4 = UNSAFE.getByte(cursor); cursor += 2; // adv beyond digit and newline val = -(((b1 - '0') * 10 + (b2 - '0')) * 10 + (b4 - '0')); } else { // 5 bytes: -d.dn cursor++; // newline val = -((b1 - '0') * 10 + (b3 - '0')); } } else { if (b1 != '.') { // 5 bytes: dd.dn cursor++; // newline val = ((b0 - '0') * 10 + (b1 - '0')) * 10 + (b3 - '0'); } else { // 4 bytes: d.dn val = (b0 - '0') * 10 + (b2 - '0'); } } break; } else { // kkkk UNSAFE.putInt(keyAddr, n); keyArrLen++; keyAddr += 4; keyHash += n; } } keyHash ^= keyHash >>> 13; var idx = keyHash & HASH_TBL_SIZE; var st = stats[idx]; if (st == null) { // nothing in table, eagerly claim spot st = stats[idx] = newStats(keyBaseAddr, keyArrLen, keyLastBytes, keyHash); } else if (!equals(st.keyAddr, st.keyLen, keyBaseAddr, keyArrLen)) { st = findInTable(stats, keyHash, keyBaseAddr, keyArrLen, keyLastBytes); } st.min = Math.min(st.min, val); st.max = Math.max(st.max, val); st.sum += val; st.count++; lineStart = cursor; // preserve line start } return lineStart - ms.address(); } private static boolean equals(long key1, int len1, long key2, int len2) { if (len1 != len2) { return false; } if (len1 <= 2) { return UNSAFE.getLong(key1) == UNSAFE.getLong(key2); } if (len1 <= 4) { return UNSAFE.getLong(key1) == UNSAFE.getLong(key2) && UNSAFE.getLong(key1 + 8) == UNSAFE.getLong(key2 + 8); } for (int i = 0; i < len1; i += 2) { var offset = i << 2; if (UNSAFE.getLong(key1 + offset) != UNSAFE.getLong(key2 + offset)) { return false; } } return true; } private static Stats findInTable(Stats[] stats, int hash, long keyAddr, int keyLen, int keyLastBytes) { // open-addressing scan var idx = hash & HASH_TBL_SIZE; var st = stats[idx]; while (st != null && !equals(st.keyAddr, st.keyLen, keyAddr, keyLen)) { idx = (idx + 1) % (HASH_TBL_SIZE + 1); st = stats[idx]; } if (st != null) { return st; } return stats[idx] = newStats(keyAddr, keyLen, keyLastBytes, hash); } private static Stats newStats(long keyAddr, int keyLen, int keyLastBytes, int hash) { var bytes = (keyLen + 1) << 2; // include overflow chunk long k = UNSAFE.allocateMemory(bytes); UNSAFE.copyMemory(keyAddr, k, bytes); return new Stats(k, keyLen, keyLastBytes, hash); } private static byte[] readFooter(MemorySegment ms, long offset) { // read from line start to current pos (end-of-input) var footer = new byte[(int) (ms.byteSize() - offset)]; for (int i = 0; i < footer.length; i++) { footer[i] = ms.get(ValueLayout.JAVA_BYTE, offset + i); } return footer; } private static ByteArrayOffset readHeader(MemorySegment ms) { // read up to and including first newline (or end-of-input) long offset = 0; while (offset < ms.byteSize() && ms.get(ValueLayout.JAVA_BYTE, offset++) != '\n') ; var header = new byte[(int) offset]; for (int i = 0; i < offset; i++) { header[i] = ms.get(ValueLayout.JAVA_BYTE, i); } return new ByteArrayOffset(header, offset); } record ByteArrayOffset(byte[] data, long offset) { } private static class Partition { byte[] header; byte[] footer; Stats[] stats; Partition(byte[] header, byte[] footer, Stats[] stats) { this.header = header; this.footer = footer; this.stats = stats; } } private static class Stats { // min, max, and sum values are modeled with integral types that represent tenths of a unit final long keyAddr; // address of 4-byte integer array final int keyLen; // number of 4-byte integers starting at address final int lastBytes; // number of bytes packed into last key int (1, 2, 3 or 4) final int hash; int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; long sum; long count; Stats(long keyAddr, int keyLen, int lastBytes, int hash) { this.keyAddr = keyAddr; this.keyLen = keyLen; this.lastBytes = lastBytes; this.hash = hash; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_entangled90.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; import static java.lang.Math.round; public class CalculateAverage_entangled90 { private static final String FILE = "./measurements.txt"; public static final boolean DEBUG = false; public static void main(String[] args) throws IOException { Scanner scanner = new Scanner(); long start = System.currentTimeMillis(); PooledChunkProcessor chunkProcessor = new PooledChunkProcessor(Runtime.getRuntime().availableProcessors()); scanner.scan(FILE, chunkProcessor, 0L, -1); long finish = System.currentTimeMillis(); var map = chunkProcessor.result(); map.printResults(); if (DEBUG) { System.out.println("Took: " + TimeUnit.MILLISECONDS.toSeconds(finish - start) + "seconds"); } } } class AggregatedProcessor { public double max = Double.NEGATIVE_INFINITY; public double min = Double.POSITIVE_INFINITY; private double sum = 0L; public long count = 0L; public void addMeasure(double value) { max = Math.max(max, value); min = Math.min(min, value); sum += value; count++; } public void combine(AggregatedProcessor processor) { max = Math.max(max, processor.max); min = Math.min(min, processor.min); sum += processor.sum; count += processor.count; } public double mean() { return sum / count; } private static double round(double d) { return Math.round(d * 10.0) / 10.0; } @Override public String toString() { return String.format(Locale.US, "%.1f/%.1f/%.1f", round(min), round(mean()), round(max)); } } class ProcessorMap { Map processors = new HashMap<>(1024); public void printResults() { // System.out.println("Processed: " + processors.entrySet().stream().mapToLong(e -> e.getValue().count).sum()); System.out.print("{"); System.out.print( processors.entrySet().stream() .sorted(Map.Entry.comparingByKey()).map(Object::toString).collect(Collectors.joining(", "))); System.out.println("}"); } public void addMeasure(BytesWrapper city, double value) { var processor = processors.get(city); if (processor == null) { processor = new AggregatedProcessor(); processors.put(city, processor); } processor.addMeasure(value); } private void combine(BytesWrapper city, AggregatedProcessor processor) { var thisProcessor = processors.get(city); if (thisProcessor == null) { processors.put(city, processor); } else { thisProcessor.combine(processor); } } public static ProcessorMap combineAll(ProcessorMap... processors) { var result = new ProcessorMap(); for (var p : processors) { for (var entry : p.processors.entrySet()) { result.combine(entry.getKey(), entry.getValue()); } } return result; } } class PooledChunkProcessor implements Consumer { private final ArrayBlockingQueue queue; private final ProcessorMap[] results; private final CountDownLatch latch; private volatile boolean queueClosed = false; public PooledChunkProcessor(int n) { queue = new ArrayBlockingQueue<>(4 * 1024); results = new ProcessorMap[n]; latch = new CountDownLatch(n); for (int i = 0; i < n; i++) { int finalI = i; var t = new Thread(() -> { var processor = new ChunkProcessor(); while (!Thread.interrupted()) { try { var element = queue.poll(1, TimeUnit.MILLISECONDS); if (element != null) processor.processChunk(element); else if (queueClosed) { if (CalculateAverage_entangled90.DEBUG) { System.out.println("Queue closed, thread #" + finalI + " stopping"); } break; } } catch (InterruptedException e) { break; } } results[finalI] = processor.processors; latch.countDown(); }); t.start(); } } @Override public void accept(ByteBuffer byteBuffer) { try { queue.put(byteBuffer); } catch (InterruptedException e) { throw new RuntimeException(e); } } public ProcessorMap result() { try { queueClosed = true; latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } return ProcessorMap.combineAll(results); } } class ChunkProcessor implements Consumer { ProcessorMap processors = new ProcessorMap(); public void processChunk(ByteBuffer bb) { while (processRow(bb)) { } } @Override public void accept(ByteBuffer byteBuffer) { processChunk(byteBuffer); } // true if you can continue // false if input is missing public boolean processRow(ByteBuffer bb) { int colonIdx = findSemiColon(bb); if (colonIdx < 0) { return false; } while (bb.get(bb.position()) == '\n') { bb.get(); } var wrapper = wrapperFromBB(bb, colonIdx - bb.position()); bb.position(colonIdx + 1); double value = parseDoubleNewLine(bb); processors.addMeasure(wrapper, value); return true; } private static BytesWrapper wrapperFromBB(ByteBuffer bb, int length) { var bytes = new byte[length]; bb.get(bytes); return new BytesWrapper(bytes); } // don't advance bb private int findSemiColon(ByteBuffer bb) { for (int i = bb.position(); i < bb.limit(); i++) { if (bb.get(i) == (byte) ';') return i; } return -1; } // parses double until new line and advances buffer private static double parseDoubleNewLine(ByteBuffer bb) { int result = 0; int sign = 1; byte c; do { c = bb.get(); switch (c) { case '-': sign = -1; break; case '.', ',', '\r', '\n': break; default: result = result * 10 + (c - '0'); } } while (c != '\n' && bb.position() < bb.limit()); return result * sign / 10.0; } } class Scanner { private static final int MAX_MAPPED_MEMORY = 32 * 1024 * 1024; public void scan(String fileName, Consumer consumer, long offset, long len) throws IOException { try (var file = new RandomAccessFile(fileName, "r"); FileChannel channel = file.getChannel()) { int chunkId = 0; if (len < 0) { len = file.length(); } while (true) { if (offset > 0) { file.seek(offset); } MappedByteBuffer bb = channel.map(FileChannel.MapMode.READ_ONLY, offset, Math.min(MAX_MAPPED_MEMORY, len - offset)); var limitIdx = findLastNewLine(bb); bb.limit(limitIdx); // get last newline from the end consumer.accept(bb); chunkId++; offset += limitIdx; if (CalculateAverage_entangled90.DEBUG && chunkId % 10 == 0) { System.out.println(" read chunk " + chunkId + " at pointer " + offset + "(" + ((int) (offset * 100 / len)) + "%)"); } if (offset >= len - 1) { break; } } } } public int findLastNewLine(ByteBuffer bb) { for (int i = bb.limit() - 1; i > bb.position(); i--) { if (bb.get(i) == '\n') { return i; } } return -1; } } class BytesWrapper implements Comparable { private final byte[] bytes; public BytesWrapper(byte[] bytes) { this.bytes = bytes; } @Override public boolean equals(Object obj) { if (obj instanceof BytesWrapper) { return Arrays.equals(bytes, ((BytesWrapper) obj).bytes); } else { return false; } } @Override public int hashCode() { return Arrays.hashCode(bytes); } @Override public String toString() { return new String(bytes); } @Override public int compareTo(BytesWrapper bytesWrapper) { return this.toString().compareTo(bytesWrapper.toString()); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_eriklumme.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileInputStream; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_eriklumme { private static final String FILE = "./measurements.txt"; private static final int NUM_CPUS = Runtime.getRuntime().availableProcessors(); private static final int LINE_OVERHEAD = 208; private static final int NUM_TASKS = NUM_CPUS * 6; private final CountDownLatch countDownLatch = new CountDownLatch(NUM_TASKS); private final FileInputStream fileInputStream = new FileInputStream(FILE); private final FileChannel fileChannel = fileInputStream.getChannel(); private final long fileSize = fileChannel.size(); private final int fileSizePerThread = (int) Math.max(Math.ceil(fileSize / (float) NUM_TASKS), 1000); private CalculateAverage_eriklumme() throws Exception { Map map = new HashMap<>(); try (ExecutorService executorService = Executors.newFixedThreadPool(NUM_CPUS); fileInputStream; fileChannel) { long sizeAccountedFor = 0; List>> futures = new ArrayList<>(NUM_TASKS); for (int i = 0; i < NUM_TASKS; i++) { if (sizeAccountedFor >= fileSize) { // The file is so small that because of the minimum file size per thread, we've covered it in less // threads than expected countDownLatch.countDown(); continue; } futures.add(executorService.submit(new DataProcessor(i))); sizeAccountedFor += fileSizePerThread; } countDownLatch.await(); for (Future> future : futures) { Map futureMap = future.get(); futureMap.forEach((key, value) -> map.merge(key, value, (st1, st2) -> { st1.sum += st2.sum; st1.count += st2.count; st1.min = Math.min(st1.min, st2.min); st1.max = Math.max(st1.max, st2.max); return st1; })); } } StringBuilder result = new StringBuilder("{"); boolean first = true; List values = new ArrayList<>(map.values()); values.sort(Comparator.comparing(StationMeasurement::stringName)); for (StationMeasurement stationMeasurement : values) { if (!first) { result.append(", "); } first = false; result.append(new String(stationMeasurement.stationName.value, StandardCharsets.UTF_8)).append("="); result.append(DECIMAL_LOOKUP[stationMeasurement.min + 1000]); result.append(String.format("/%.1f/", (stationMeasurement.sum / (stationMeasurement.count * 10.0)))); result.append(DECIMAL_LOOKUP[stationMeasurement.max + 1000]); } result.append("}"); System.out.println(result); } private static class StationMeasurement { private final ByteArrayWrapper stationName; private StationMeasurement(ByteArrayWrapper stationName) { this.stationName = stationName; } private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum = 0; private int count = 0; public String stringName() { return new String(stationName.value, StandardCharsets.UTF_8); } } private enum Mode { UNINITIALIZED, READ_STATION, READ_VALUE } private record ByteArrayWrapper(byte[] value) { @Override public boolean equals(Object o) { if (this == o) return true; if (o instanceof ByteArrayWrapper that) { return Arrays.equals(value, that.value); } return false; } @Override public int hashCode() { return Arrays.hashCode(value); } } public class DataProcessor implements Callable> { private final int processorIndex; public DataProcessor(int processorIndex) { this.processorIndex = processorIndex; } @Override public Map call() throws Exception { Map map = new HashMap<>(); byte[] stationBuffer = new byte[200]; int stationIndex = 0; byte[] valueBuffer = new byte[10]; int valueIndex = 0; Mode mode = processorIndex == 0 ? Mode.READ_STATION : Mode.UNINITIALIZED; byte b; long offset = ((long) fileSizePerThread) * processorIndex; long sizeWithOverhead = Math.min(((long) fileSizePerThread) + LINE_OVERHEAD, fileSize - offset); try { MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, sizeWithOverhead); // Read from buffer in chunks for improved performance byte[] bytes = new byte[(int) (sizeWithOverhead / 6) + 1]; while (buffer.hasRemaining()) { long bytesRemaining = sizeWithOverhead - buffer.position(); int bytesOffset, bytesLength; if (bytesRemaining >= bytes.length) { bytesOffset = 0; bytesLength = bytes.length; } else { bytesOffset = (int) (bytes.length - bytesRemaining); bytesLength = (int) bytesRemaining; } buffer.get(bytes, bytesOffset, bytesLength); for (int i = bytesOffset; i < bytes.length; i++) { b = bytes[i]; if (b == '\n') { // We have a station to store if (mode == Mode.READ_VALUE) { storeStation(map, stationBuffer, stationIndex, valueBuffer, valueIndex); stationIndex = 0; valueIndex = 0; } mode = Mode.READ_STATION; // We've run past our size, can happen if (buffer.position() - bytes.length + i >= fileSizePerThread) { return map; } } else if (mode == Mode.UNINITIALIZED) { // Do-nothing, read more } else if (b == ';') { mode = Mode.READ_VALUE; } else if (mode == Mode.READ_STATION) { stationBuffer[stationIndex++] = b; } else { valueBuffer[valueIndex++] = b; } } } if (mode == Mode.READ_VALUE && valueIndex > 0) { // One value left to store storeStation(map, stationBuffer, stationIndex, valueBuffer, valueIndex); } } finally { countDownLatch.countDown(); } return map; } private void storeStation(Map map, byte[] stationBuffer, int stationIndex, byte[] valueBuffer, int valueIndex) { ByteArrayWrapper stationName = new ByteArrayWrapper(Arrays.copyOfRange(stationBuffer, 0, stationIndex)); int value = 0; for (int i = 0; i < valueIndex; i++) { byte b = valueBuffer[valueIndex - i - 1]; if (i == 1) { // Skip the decimal point } else if (b == '-') { // Number is negative value = (-value); } else { int valueAtIndex = b - 48; if (i == 0) { value += valueAtIndex; } else { value += valueAtIndex * (i == 2 ? 10 : 100); } } } StationMeasurement stationMeasurement = map.computeIfAbsent(stationName, StationMeasurement::new); stationMeasurement.count++; stationMeasurement.min = Math.min(value, stationMeasurement.min); stationMeasurement.max = Math.max(value, stationMeasurement.max); stationMeasurement.sum += value; } } public static void main(String[] args) throws Exception { Locale.setDefault(Locale.US); new CalculateAverage_eriklumme(); } private static final String[] DECIMAL_LOOKUP = new String[]{ "-100.0", "-99.9", "-99.8", "-99.7", "-99.6", "-99.5", "-99.4", "-99.3", "-99.2", "-99.1", "-99.0", "-98.9", "-98.8", "-98.7", "-98.6", "-98.5", "-98.4", "-98.3", "-98.2", "-98.1", "-98.0", "-97.9", "-97.8", "-97.7", "-97.6", "-97.5", "-97.4", "-97.3", "-97.2", "-97.1", "-97.0", "-96.9", "-96.8", "-96.7", "-96.6", "-96.5", "-96.4", "-96.3", "-96.2", "-96.1", "-96.0", "-95.9", "-95.8", "-95.7", "-95.6", "-95.5", "-95.4", "-95.3", "-95.2", "-95.1", "-95.0", "-94.9", "-94.8", "-94.7", "-94.6", "-94.5", "-94.4", "-94.3", "-94.2", "-94.1", "-94.0", "-93.9", "-93.8", "-93.7", "-93.6", "-93.5", "-93.4", "-93.3", "-93.2", "-93.1", "-93.0", "-92.9", "-92.8", "-92.7", "-92.6", "-92.5", "-92.4", "-92.3", "-92.2", "-92.1", "-92.0", "-91.9", "-91.8", "-91.7", "-91.6", "-91.5", "-91.4", "-91.3", "-91.2", "-91.1", "-91.0", "-90.9", "-90.8", "-90.7", "-90.6", "-90.5", "-90.4", "-90.3", "-90.2", "-90.1", "-90.0", "-89.9", "-89.8", "-89.7", "-89.6", "-89.5", "-89.4", "-89.3", "-89.2", "-89.1", "-89.0", "-88.9", "-88.8", "-88.7", "-88.6", "-88.5", "-88.4", "-88.3", "-88.2", "-88.1", "-88.0", "-87.9", "-87.8", "-87.7", "-87.6", "-87.5", "-87.4", "-87.3", "-87.2", "-87.1", "-87.0", "-86.9", "-86.8", "-86.7", "-86.6", "-86.5", "-86.4", "-86.3", "-86.2", "-86.1", "-86.0", "-85.9", "-85.8", "-85.7", "-85.6", "-85.5", "-85.4", "-85.3", "-85.2", "-85.1", "-85.0", "-84.9", "-84.8", "-84.7", "-84.6", "-84.5", "-84.4", "-84.3", "-84.2", "-84.1", "-84.0", "-83.9", "-83.8", "-83.7", "-83.6", "-83.5", "-83.4", "-83.3", "-83.2", "-83.1", "-83.0", "-82.9", "-82.8", "-82.7", "-82.6", "-82.5", "-82.4", "-82.3", "-82.2", "-82.1", "-82.0", "-81.9", "-81.8", "-81.7", "-81.6", "-81.5", "-81.4", "-81.3", "-81.2", "-81.1", "-81.0", "-80.9", "-80.8", "-80.7", "-80.6", "-80.5", "-80.4", "-80.3", "-80.2", "-80.1", "-80.0", "-79.9", "-79.8", "-79.7", "-79.6", "-79.5", "-79.4", "-79.3", "-79.2", "-79.1", "-79.0", "-78.9", "-78.8", "-78.7", "-78.6", "-78.5", "-78.4", "-78.3", "-78.2", "-78.1", "-78.0", "-77.9", "-77.8", "-77.7", "-77.6", "-77.5", "-77.4", "-77.3", "-77.2", "-77.1", "-77.0", "-76.9", "-76.8", "-76.7", "-76.6", "-76.5", "-76.4", "-76.3", "-76.2", "-76.1", "-76.0", "-75.9", "-75.8", "-75.7", "-75.6", "-75.5", "-75.4", "-75.3", "-75.2", "-75.1", "-75.0", "-74.9", "-74.8", "-74.7", "-74.6", "-74.5", "-74.4", "-74.3", "-74.2", "-74.1", "-74.0", "-73.9", "-73.8", "-73.7", "-73.6", "-73.5", "-73.4", "-73.3", "-73.2", "-73.1", "-73.0", "-72.9", "-72.8", "-72.7", "-72.6", "-72.5", "-72.4", "-72.3", "-72.2", "-72.1", "-72.0", "-71.9", "-71.8", "-71.7", "-71.6", "-71.5", "-71.4", "-71.3", "-71.2", "-71.1", "-71.0", "-70.9", "-70.8", "-70.7", "-70.6", "-70.5", "-70.4", "-70.3", "-70.2", "-70.1", "-70.0", "-69.9", "-69.8", "-69.7", "-69.6", "-69.5", "-69.4", "-69.3", "-69.2", "-69.1", "-69.0", "-68.9", "-68.8", "-68.7", "-68.6", "-68.5", "-68.4", "-68.3", "-68.2", "-68.1", "-68.0", "-67.9", "-67.8", "-67.7", "-67.6", "-67.5", "-67.4", "-67.3", "-67.2", "-67.1", "-67.0", "-66.9", "-66.8", "-66.7", "-66.6", "-66.5", "-66.4", "-66.3", "-66.2", "-66.1", "-66.0", "-65.9", "-65.8", "-65.7", "-65.6", "-65.5", "-65.4", "-65.3", "-65.2", "-65.1", "-65.0", "-64.9", "-64.8", "-64.7", "-64.6", "-64.5", "-64.4", "-64.3", "-64.2", "-64.1", "-64.0", "-63.9", "-63.8", "-63.7", "-63.6", "-63.5", "-63.4", "-63.3", "-63.2", "-63.1", "-63.0", "-62.9", "-62.8", "-62.7", "-62.6", "-62.5", "-62.4", "-62.3", "-62.2", "-62.1", "-62.0", "-61.9", "-61.8", "-61.7", "-61.6", "-61.5", "-61.4", "-61.3", "-61.2", "-61.1", "-61.0", "-60.9", "-60.8", "-60.7", "-60.6", "-60.5", "-60.4", "-60.3", "-60.2", "-60.1", "-60.0", "-59.9", "-59.8", "-59.7", "-59.6", "-59.5", "-59.4", "-59.3", "-59.2", "-59.1", "-59.0", "-58.9", "-58.8", "-58.7", "-58.6", "-58.5", "-58.4", "-58.3", "-58.2", "-58.1", "-58.0", "-57.9", "-57.8", "-57.7", "-57.6", "-57.5", "-57.4", "-57.3", "-57.2", "-57.1", "-57.0", "-56.9", "-56.8", "-56.7", "-56.6", "-56.5", "-56.4", "-56.3", "-56.2", "-56.1", "-56.0", "-55.9", "-55.8", "-55.7", "-55.6", "-55.5", "-55.4", "-55.3", "-55.2", "-55.1", "-55.0", "-54.9", "-54.8", "-54.7", "-54.6", "-54.5", "-54.4", "-54.3", "-54.2", "-54.1", "-54.0", "-53.9", "-53.8", "-53.7", "-53.6", "-53.5", "-53.4", "-53.3", "-53.2", "-53.1", "-53.0", "-52.9", "-52.8", "-52.7", "-52.6", "-52.5", "-52.4", "-52.3", "-52.2", "-52.1", "-52.0", "-51.9", "-51.8", "-51.7", "-51.6", "-51.5", "-51.4", "-51.3", "-51.2", "-51.1", "-51.0", "-50.9", "-50.8", "-50.7", "-50.6", "-50.5", "-50.4", "-50.3", "-50.2", "-50.1", "-50.0", "-49.9", "-49.8", "-49.7", "-49.6", "-49.5", "-49.4", "-49.3", "-49.2", "-49.1", "-49.0", "-48.9", "-48.8", "-48.7", "-48.6", "-48.5", "-48.4", "-48.3", "-48.2", "-48.1", "-48.0", "-47.9", "-47.8", "-47.7", "-47.6", "-47.5", "-47.4", "-47.3", "-47.2", "-47.1", "-47.0", "-46.9", "-46.8", "-46.7", "-46.6", "-46.5", "-46.4", "-46.3", "-46.2", "-46.1", "-46.0", "-45.9", "-45.8", "-45.7", "-45.6", "-45.5", "-45.4", "-45.3", "-45.2", "-45.1", "-45.0", "-44.9", "-44.8", "-44.7", "-44.6", "-44.5", "-44.4", "-44.3", "-44.2", "-44.1", "-44.0", "-43.9", "-43.8", "-43.7", "-43.6", "-43.5", "-43.4", "-43.3", "-43.2", "-43.1", "-43.0", "-42.9", "-42.8", "-42.7", "-42.6", "-42.5", "-42.4", "-42.3", "-42.2", "-42.1", "-42.0", "-41.9", "-41.8", "-41.7", "-41.6", "-41.5", "-41.4", "-41.3", "-41.2", "-41.1", "-41.0", "-40.9", "-40.8", "-40.7", "-40.6", "-40.5", "-40.4", "-40.3", "-40.2", "-40.1", "-40.0", "-39.9", "-39.8", "-39.7", "-39.6", "-39.5", "-39.4", "-39.3", "-39.2", "-39.1", "-39.0", "-38.9", "-38.8", "-38.7", "-38.6", "-38.5", "-38.4", "-38.3", "-38.2", "-38.1", "-38.0", "-37.9", "-37.8", "-37.7", "-37.6", "-37.5", "-37.4", "-37.3", "-37.2", "-37.1", "-37.0", "-36.9", "-36.8", "-36.7", "-36.6", "-36.5", "-36.4", "-36.3", "-36.2", "-36.1", "-36.0", "-35.9", "-35.8", "-35.7", "-35.6", "-35.5", "-35.4", "-35.3", "-35.2", "-35.1", "-35.0", "-34.9", "-34.8", "-34.7", "-34.6", "-34.5", "-34.4", "-34.3", "-34.2", "-34.1", "-34.0", "-33.9", "-33.8", "-33.7", "-33.6", "-33.5", "-33.4", "-33.3", "-33.2", "-33.1", "-33.0", "-32.9", "-32.8", "-32.7", "-32.6", "-32.5", "-32.4", "-32.3", "-32.2", "-32.1", "-32.0", "-31.9", "-31.8", "-31.7", "-31.6", "-31.5", "-31.4", "-31.3", "-31.2", "-31.1", "-31.0", "-30.9", "-30.8", "-30.7", "-30.6", "-30.5", "-30.4", "-30.3", "-30.2", "-30.1", "-30.0", "-29.9", "-29.8", "-29.7", "-29.6", "-29.5", "-29.4", "-29.3", "-29.2", "-29.1", "-29.0", "-28.9", "-28.8", "-28.7", "-28.6", "-28.5", "-28.4", "-28.3", "-28.2", "-28.1", "-28.0", "-27.9", "-27.8", "-27.7", "-27.6", "-27.5", "-27.4", "-27.3", "-27.2", "-27.1", "-27.0", "-26.9", "-26.8", "-26.7", "-26.6", "-26.5", "-26.4", "-26.3", "-26.2", "-26.1", "-26.0", "-25.9", "-25.8", "-25.7", "-25.6", "-25.5", "-25.4", "-25.3", "-25.2", "-25.1", "-25.0", "-24.9", "-24.8", "-24.7", "-24.6", "-24.5", "-24.4", "-24.3", "-24.2", "-24.1", "-24.0", "-23.9", "-23.8", "-23.7", "-23.6", "-23.5", "-23.4", "-23.3", "-23.2", "-23.1", "-23.0", "-22.9", "-22.8", "-22.7", "-22.6", "-22.5", "-22.4", "-22.3", "-22.2", "-22.1", "-22.0", "-21.9", "-21.8", "-21.7", "-21.6", "-21.5", "-21.4", "-21.3", "-21.2", "-21.1", "-21.0", "-20.9", "-20.8", "-20.7", "-20.6", "-20.5", "-20.4", "-20.3", "-20.2", "-20.1", "-20.0", "-19.9", "-19.8", "-19.7", "-19.6", "-19.5", "-19.4", "-19.3", "-19.2", "-19.1", "-19.0", "-18.9", "-18.8", "-18.7", "-18.6", "-18.5", "-18.4", "-18.3", "-18.2", "-18.1", "-18.0", "-17.9", "-17.8", "-17.7", "-17.6", "-17.5", "-17.4", "-17.3", "-17.2", "-17.1", "-17.0", "-16.9", "-16.8", "-16.7", "-16.6", "-16.5", "-16.4", "-16.3", "-16.2", "-16.1", "-16.0", "-15.9", "-15.8", "-15.7", "-15.6", "-15.5", "-15.4", "-15.3", "-15.2", "-15.1", "-15.0", "-14.9", "-14.8", "-14.7", "-14.6", "-14.5", "-14.4", "-14.3", "-14.2", "-14.1", "-14.0", "-13.9", "-13.8", "-13.7", "-13.6", "-13.5", "-13.4", "-13.3", "-13.2", "-13.1", "-13.0", "-12.9", "-12.8", "-12.7", "-12.6", "-12.5", "-12.4", "-12.3", "-12.2", "-12.1", "-12.0", "-11.9", "-11.8", "-11.7", "-11.6", "-11.5", "-11.4", "-11.3", "-11.2", "-11.1", "-11.0", "-10.9", "-10.8", "-10.7", "-10.6", "-10.5", "-10.4", "-10.3", "-10.2", "-10.1", "-10.0", "-9.9", "-9.8", "-9.7", "-9.6", "-9.5", "-9.4", "-9.3", "-9.2", "-9.1", "-9.0", "-8.9", "-8.8", "-8.7", "-8.6", "-8.5", "-8.4", "-8.3", "-8.2", "-8.1", "-8.0", "-7.9", "-7.8", "-7.7", "-7.6", "-7.5", "-7.4", "-7.3", "-7.2", "-7.1", "-7.0", "-6.9", "-6.8", "-6.7", "-6.6", "-6.5", "-6.4", "-6.3", "-6.2", "-6.1", "-6.0", "-5.9", "-5.8", "-5.7", "-5.6", "-5.5", "-5.4", "-5.3", "-5.2", "-5.1", "-5.0", "-4.9", "-4.8", "-4.7", "-4.6", "-4.5", "-4.4", "-4.3", "-4.2", "-4.1", "-4.0", "-3.9", "-3.8", "-3.7", "-3.6", "-3.5", "-3.4", "-3.3", "-3.2", "-3.1", "-3.0", "-2.9", "-2.8", "-2.7", "-2.6", "-2.5", "-2.4", "-2.3", "-2.2", "-2.1", "-2.0", "-1.9", "-1.8", "-1.7", "-1.6", "-1.5", "-1.4", "-1.3", "-1.2", "-1.1", "-1.0", "-0.9", "-0.8", "-0.7", "-0.6", "-0.5", "-0.4", "-0.3", "-0.2", "-0.1", "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0", "1.1", "1.2", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "1.9", "2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", "2.9", "3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6", "4.7", "4.8", "4.9", "5.0", "5.1", "5.2", "5.3", "5.4", "5.5", "5.6", "5.7", "5.8", "5.9", "6.0", "6.1", "6.2", "6.3", "6.4", "6.5", "6.6", "6.7", "6.8", "6.9", "7.0", "7.1", "7.2", "7.3", "7.4", "7.5", "7.6", "7.7", "7.8", "7.9", "8.0", "8.1", "8.2", "8.3", "8.4", "8.5", "8.6", "8.7", "8.8", "8.9", "9.0", "9.1", "9.2", "9.3", "9.4", "9.5", "9.6", "9.7", "9.8", "9.9", "10.0", "10.1", "10.2", "10.3", "10.4", "10.5", "10.6", "10.7", "10.8", "10.9", "11.0", "11.1", "11.2", "11.3", "11.4", "11.5", "11.6", "11.7", "11.8", "11.9", "12.0", "12.1", "12.2", "12.3", "12.4", "12.5", "12.6", "12.7", "12.8", "12.9", "13.0", "13.1", "13.2", "13.3", "13.4", "13.5", "13.6", "13.7", "13.8", "13.9", "14.0", "14.1", "14.2", "14.3", "14.4", "14.5", "14.6", "14.7", "14.8", "14.9", "15.0", "15.1", "15.2", "15.3", "15.4", "15.5", "15.6", "15.7", "15.8", "15.9", "16.0", "16.1", "16.2", "16.3", "16.4", "16.5", "16.6", "16.7", "16.8", "16.9", "17.0", "17.1", "17.2", "17.3", "17.4", "17.5", "17.6", "17.7", "17.8", "17.9", "18.0", "18.1", "18.2", "18.3", "18.4", "18.5", "18.6", "18.7", "18.8", "18.9", "19.0", "19.1", "19.2", "19.3", "19.4", "19.5", "19.6", "19.7", "19.8", "19.9", "20.0", "20.1", "20.2", "20.3", "20.4", "20.5", "20.6", "20.7", "20.8", "20.9", "21.0", "21.1", "21.2", "21.3", "21.4", "21.5", "21.6", "21.7", "21.8", "21.9", "22.0", "22.1", "22.2", "22.3", "22.4", "22.5", "22.6", "22.7", "22.8", "22.9", "23.0", "23.1", "23.2", "23.3", "23.4", "23.5", "23.6", "23.7", "23.8", "23.9", "24.0", "24.1", "24.2", "24.3", "24.4", "24.5", "24.6", "24.7", "24.8", "24.9", "25.0", "25.1", "25.2", "25.3", "25.4", "25.5", "25.6", "25.7", "25.8", "25.9", "26.0", "26.1", "26.2", "26.3", "26.4", "26.5", "26.6", "26.7", "26.8", "26.9", "27.0", "27.1", "27.2", "27.3", "27.4", "27.5", "27.6", "27.7", "27.8", "27.9", "28.0", "28.1", "28.2", "28.3", "28.4", "28.5", "28.6", "28.7", "28.8", "28.9", "29.0", "29.1", "29.2", "29.3", "29.4", "29.5", "29.6", "29.7", "29.8", "29.9", "30.0", "30.1", "30.2", "30.3", "30.4", "30.5", "30.6", "30.7", "30.8", "30.9", "31.0", "31.1", "31.2", "31.3", "31.4", "31.5", "31.6", "31.7", "31.8", "31.9", "32.0", "32.1", "32.2", "32.3", "32.4", "32.5", "32.6", "32.7", "32.8", "32.9", "33.0", "33.1", "33.2", "33.3", "33.4", "33.5", "33.6", "33.7", "33.8", "33.9", "34.0", "34.1", "34.2", "34.3", "34.4", "34.5", "34.6", "34.7", "34.8", "34.9", "35.0", "35.1", "35.2", "35.3", "35.4", "35.5", "35.6", "35.7", "35.8", "35.9", "36.0", "36.1", "36.2", "36.3", "36.4", "36.5", "36.6", "36.7", "36.8", "36.9", "37.0", "37.1", "37.2", "37.3", "37.4", "37.5", "37.6", "37.7", "37.8", "37.9", "38.0", "38.1", "38.2", "38.3", "38.4", "38.5", "38.6", "38.7", "38.8", "38.9", "39.0", "39.1", "39.2", "39.3", "39.4", "39.5", "39.6", "39.7", "39.8", "39.9", "40.0", "40.1", "40.2", "40.3", "40.4", "40.5", "40.6", "40.7", "40.8", "40.9", "41.0", "41.1", "41.2", "41.3", "41.4", "41.5", "41.6", "41.7", "41.8", "41.9", "42.0", "42.1", "42.2", "42.3", "42.4", "42.5", "42.6", "42.7", "42.8", "42.9", "43.0", "43.1", "43.2", "43.3", "43.4", "43.5", "43.6", "43.7", "43.8", "43.9", "44.0", "44.1", "44.2", "44.3", "44.4", "44.5", "44.6", "44.7", "44.8", "44.9", "45.0", "45.1", "45.2", "45.3", "45.4", "45.5", "45.6", "45.7", "45.8", "45.9", "46.0", "46.1", "46.2", "46.3", "46.4", "46.5", "46.6", "46.7", "46.8", "46.9", "47.0", "47.1", "47.2", "47.3", "47.4", "47.5", "47.6", "47.7", "47.8", "47.9", "48.0", "48.1", "48.2", "48.3", "48.4", "48.5", "48.6", "48.7", "48.8", "48.9", "49.0", "49.1", "49.2", "49.3", "49.4", "49.5", "49.6", "49.7", "49.8", "49.9", "50.0", "50.1", "50.2", "50.3", "50.4", "50.5", "50.6", "50.7", "50.8", "50.9", "51.0", "51.1", "51.2", "51.3", "51.4", "51.5", "51.6", "51.7", "51.8", "51.9", "52.0", "52.1", "52.2", "52.3", "52.4", "52.5", "52.6", "52.7", "52.8", "52.9", "53.0", "53.1", "53.2", "53.3", "53.4", "53.5", "53.6", "53.7", "53.8", "53.9", "54.0", "54.1", "54.2", "54.3", "54.4", "54.5", "54.6", "54.7", "54.8", "54.9", "55.0", "55.1", "55.2", "55.3", "55.4", "55.5", "55.6", "55.7", "55.8", "55.9", "56.0", "56.1", "56.2", "56.3", "56.4", "56.5", "56.6", "56.7", "56.8", "56.9", "57.0", "57.1", "57.2", "57.3", "57.4", "57.5", "57.6", "57.7", "57.8", "57.9", "58.0", "58.1", "58.2", "58.3", "58.4", "58.5", "58.6", "58.7", "58.8", "58.9", "59.0", "59.1", "59.2", "59.3", "59.4", "59.5", "59.6", "59.7", "59.8", "59.9", "60.0", "60.1", "60.2", "60.3", "60.4", "60.5", "60.6", "60.7", "60.8", "60.9", "61.0", "61.1", "61.2", "61.3", "61.4", "61.5", "61.6", "61.7", "61.8", "61.9", "62.0", "62.1", "62.2", "62.3", "62.4", "62.5", "62.6", "62.7", "62.8", "62.9", "63.0", "63.1", "63.2", "63.3", "63.4", "63.5", "63.6", "63.7", "63.8", "63.9", "64.0", "64.1", "64.2", "64.3", "64.4", "64.5", "64.6", "64.7", "64.8", "64.9", "65.0", "65.1", "65.2", "65.3", "65.4", "65.5", "65.6", "65.7", "65.8", "65.9", "66.0", "66.1", "66.2", "66.3", "66.4", "66.5", "66.6", "66.7", "66.8", "66.9", "67.0", "67.1", "67.2", "67.3", "67.4", "67.5", "67.6", "67.7", "67.8", "67.9", "68.0", "68.1", "68.2", "68.3", "68.4", "68.5", "68.6", "68.7", "68.8", "68.9", "69.0", "69.1", "69.2", "69.3", "69.4", "69.5", "69.6", "69.7", "69.8", "69.9", "70.0", "70.1", "70.2", "70.3", "70.4", "70.5", "70.6", "70.7", "70.8", "70.9", "71.0", "71.1", "71.2", "71.3", "71.4", "71.5", "71.6", "71.7", "71.8", "71.9", "72.0", "72.1", "72.2", "72.3", "72.4", "72.5", "72.6", "72.7", "72.8", "72.9", "73.0", "73.1", "73.2", "73.3", "73.4", "73.5", "73.6", "73.7", "73.8", "73.9", "74.0", "74.1", "74.2", "74.3", "74.4", "74.5", "74.6", "74.7", "74.8", "74.9", "75.0", "75.1", "75.2", "75.3", "75.4", "75.5", "75.6", "75.7", "75.8", "75.9", "76.0", "76.1", "76.2", "76.3", "76.4", "76.5", "76.6", "76.7", "76.8", "76.9", "77.0", "77.1", "77.2", "77.3", "77.4", "77.5", "77.6", "77.7", "77.8", "77.9", "78.0", "78.1", "78.2", "78.3", "78.4", "78.5", "78.6", "78.7", "78.8", "78.9", "79.0", "79.1", "79.2", "79.3", "79.4", "79.5", "79.6", "79.7", "79.8", "79.9", "80.0", "80.1", "80.2", "80.3", "80.4", "80.5", "80.6", "80.7", "80.8", "80.9", "81.0", "81.1", "81.2", "81.3", "81.4", "81.5", "81.6", "81.7", "81.8", "81.9", "82.0", "82.1", "82.2", "82.3", "82.4", "82.5", "82.6", "82.7", "82.8", "82.9", "83.0", "83.1", "83.2", "83.3", "83.4", "83.5", "83.6", "83.7", "83.8", "83.9", "84.0", "84.1", "84.2", "84.3", "84.4", "84.5", "84.6", "84.7", "84.8", "84.9", "85.0", "85.1", "85.2", "85.3", "85.4", "85.5", "85.6", "85.7", "85.8", "85.9", "86.0", "86.1", "86.2", "86.3", "86.4", "86.5", "86.6", "86.7", "86.8", "86.9", "87.0", "87.1", "87.2", "87.3", "87.4", "87.5", "87.6", "87.7", "87.8", "87.9", "88.0", "88.1", "88.2", "88.3", "88.4", "88.5", "88.6", "88.7", "88.8", "88.9", "89.0", "89.1", "89.2", "89.3", "89.4", "89.5", "89.6", "89.7", "89.8", "89.9", "90.0", "90.1", "90.2", "90.3", "90.4", "90.5", "90.6", "90.7", "90.8", "90.9", "91.0", "91.1", "91.2", "91.3", "91.4", "91.5", "91.6", "91.7", "91.8", "91.9", "92.0", "92.1", "92.2", "92.3", "92.4", "92.5", "92.6", "92.7", "92.8", "92.9", "93.0", "93.1", "93.2", "93.3", "93.4", "93.5", "93.6", "93.7", "93.8", "93.9", "94.0", "94.1", "94.2", "94.3", "94.4", "94.5", "94.6", "94.7", "94.8", "94.9", "95.0", "95.1", "95.2", "95.3", "95.4", "95.5", "95.6", "95.7", "95.8", "95.9", "96.0", "96.1", "96.2", "96.3", "96.4", "96.5", "96.6", "96.7", "96.8", "96.9", "97.0", "97.1", "97.2", "97.3", "97.4", "97.5", "97.6", "97.7", "97.8", "97.9", "98.0", "98.1", "98.2", "98.3", "98.4", "98.5", "98.6", "98.7", "98.8", "98.9", "99.0", "99.1", "99.2", "99.3", "99.4", "99.5", "99.6", "99.7", "99.8", "99.9" }; } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_faridtmammadov.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; public class CalculateAverage_faridtmammadov { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException { int availableProcessors = Runtime.getRuntime().availableProcessors(); var map = getSegments(availableProcessors).stream() .map(CalculateAverage_faridtmammadov::aggregate).parallel() .flatMap(f -> f.entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Aggregate::update, TreeMap::new)); printFormatted(map); } private static List getSegments(int numberOfChunks) throws IOException { try (var fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { var fileSize = fileChannel.size(); var segmentSize = fileSize / numberOfChunks; var segment = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); var baseAddress = segment.address(); var endAddress = baseAddress + fileSize; var segments = new ArrayList(); var startAddress = baseAddress; for (var i = 0; i < numberOfChunks; i++) { var pointer = startAddress + segmentSize; while (pointer < endAddress) { long offset = pointer - baseAddress; byte b = segment.get(ValueLayout.JAVA_BYTE, offset); if (b == '\n') { break; } pointer++; } if (pointer >= endAddress) { var offsetStart = startAddress - baseAddress; var offsetEnd = endAddress - baseAddress - offsetStart; segments.add(segment.asSlice(offsetStart, offsetEnd)); break; } var offsetStart = startAddress - baseAddress; var offsetEnd = pointer - baseAddress - offsetStart; segments.add(segment.asSlice(offsetStart, offsetEnd)); startAddress = pointer + 1; } return segments; } } private static Map aggregate(MemorySegment segment) { var map = new HashMap(); var iterator = new MemorySegmentIterator(segment); while (iterator.hasNext()) { String city = parseCity(iterator); long temperature = parseTemperature(iterator); map.compute(city, (key, value) -> { if (value == null) { return new Aggregate(temperature); } else { return value.update(temperature); } }); } return map; } private static String parseCity(MemorySegmentIterator iterator) { var byteStream = new ByteArrayOutputStream(); while (iterator.hasNext()) { var b = iterator.getNextByte(); if (b == ';') { return byteStream.toString(StandardCharsets.UTF_8); } byteStream.write(b); } return null; } public static long parseTemperature(MemorySegmentIterator iterator) { long value = 0L; int sign = 1; while (iterator.hasNext()) { byte b = iterator.getNextByte(); if (b >= '0' && b <= '9') { value = value * 10 + b - '0'; } else if (b == '\n') { return value * sign; } else if (b == '-') { sign = -1; } } return value * sign; } private static void printFormatted(Map map) { var iterator = map.entrySet().iterator(); var length = map.entrySet().size(); System.out.print("{"); for (int i = 0; i < length - 1; i++) { var entry = iterator.next(); System.out.printf("%s=%s, ", entry.getKey(), entry.getValue().toString()); } var lastEntry = iterator.next(); System.out.printf("%s=%s}\n", lastEntry.getKey(), lastEntry.getValue().toString()); } static class Aggregate { long min; long max; long sum; int count; public Aggregate(long temperature) { min = temperature; max = temperature; sum = temperature; count = 1; } public Aggregate update(long temp) { min = Math.min(min, temp); max = Math.max(max, temp); sum += temp; count++; return this; } public Aggregate update(Aggregate agg) { min = Math.min(min, agg.min); max = Math.max(max, agg.max); sum += agg.sum; count += agg.count; return this; } public String toString() { return String.format("%s/%s/%s", min / 10.0f, Math.round(sum * 1.0f / count) / 10.0f, max / 10.0f); } } static class MemorySegmentIterator { private long offset; private final MemorySegment segment; private final long segmentSize; public MemorySegmentIterator(MemorySegment segment) { this.segment = segment; this.segmentSize = segment.byteSize(); } public boolean hasNext() { return offset < segmentSize; } public byte getNextByte() { var b = segment.get(ValueLayout.JAVA_BYTE, offset); offset++; return b; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_fatroom.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.*; public class CalculateAverage_fatroom { private static final String FILE = "./measurements.txt"; private static class MeasurementAggregator { private double min; private double max; private double sum; private long count; public MeasurementAggregator() { this.min = 1000; this.max = -1000; this.sum = 0; this.count = 0; } public void consume(double value) { this.min = value > this.min ? this.min : value; this.max = this.max > value ? this.max : value; this.sum += value; this.count++; } public MeasurementAggregator combineWith(MeasurementAggregator that) { this.min = that.min > this.min ? this.min : that.min; this.max = this.max > that.max ? this.max : that.max; this.sum += that.sum; this.count += that.count; return this; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(min / 10.0).append("/").append(Math.round(sum / count) / 10.0).append("/").append(max / 10.0); return sb.toString(); } } public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { int SEGMENT_LENGTH = 256_000_000; // 256 MB RandomAccessFile file = new RandomAccessFile(FILE, "r"); long fileSize = file.length(); long position = 0; List> tasks = new LinkedList<>(); while (position < fileSize) { long end = Math.min(position + SEGMENT_LENGTH, fileSize); int length = (int) (end - position); MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, position, length); while (buffer.get(length - 1) != '\n') { length--; } final int finalLength = length; tasks.add(() -> processBuffer(buffer, finalLength)); position += length; } var executor = Executors.newFixedThreadPool(tasks.size()); Map aggregates = new TreeMap<>(); for (var future : executor.invokeAll(tasks)) { var segmentAggregates = future.get(); for (var entry : segmentAggregates.entrySet()) { aggregates.merge(entry.getKey(), entry.value, MeasurementAggregator::combineWith); } } executor.shutdown(); // no sense to wait longer than base case executor.awaitTermination(5, TimeUnit.MINUTES); System.out.println(aggregates); } private static StationMap processBuffer(MappedByteBuffer source, int length) { StationMap aggregates = new StationMap(); byte[] buffer = new byte[200]; byte[] measurement = new byte[5]; int measurementLength; int idx = 0; int hash = 1; for (int i = 0; i < length; ++i) { byte b = source.get(i); hash = 31 * hash + b; buffer[idx++] = b; if (b == ';') { measurementLength = 3; measurement[0] = source.get(++i); measurement[1] = source.get(++i); measurement[2] = source.get(++i); measurement[3] = source.get(++i); if (measurement[3] != '\n') { measurementLength++; measurement[4] = source.get(++i); if (measurement[4] != '\n') { i++; measurementLength++; } } aggregates.get(hash, buffer, idx - 1).consume(parseMeasurement(measurement, measurementLength)); idx = 0; hash = 1; } } return aggregates; } static double parseMeasurement(byte[] source, int size) { int isNegativeSignPresent = ~(source[0] >> 4) & 1; int firstDigit = source[isNegativeSignPresent] - '0'; int secondDigit = source[size - 3]; int thirdDigit = source[size - 1]; int has4 = (size - isNegativeSignPresent) >> 2; int value = has4 * firstDigit * 100 + secondDigit * 10 + thirdDigit - 528; return -isNegativeSignPresent ^ value - isNegativeSignPresent; } static class Station { private byte[] bytes; private int hash; private MeasurementAggregator value; private Station next; public Station(int hash, byte[] bytes, int length, Station next) { this.hash = hash; this.bytes = new byte[length]; System.arraycopy(bytes, 0, this.bytes, 0, length); this.value = new MeasurementAggregator(); this.next = next; } public String getKey() { return new String(bytes, 0, bytes.length, StandardCharsets.UTF_8); } } static class StationMap { private Station[] stations = new Station[16384]; MeasurementAggregator get(int hash, byte[] buffer, int length) { int bucketId = hash & 0x3fff; Station entry = stations[bucketId]; while (entry != null) { if (entry.hash == hash && Arrays.equals(entry.bytes, 0, entry.bytes.length, buffer, 0, length)) { return entry.value; } entry = entry.next; } stations[bucketId] = new Station(hash, buffer, length, stations[bucketId]); return stations[bucketId].value; } private List entrySet() { List result = new LinkedList<>(); for (var station : stations) { while (station != null) { result.add(station); station = station.next; } } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_felix19350.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.Stream; public class CalculateAverage_felix19350 { private static final String FILE = "./measurements.txt"; private static final int NEW_LINE_SEEK_BUFFER_LEN = 128; private static final int EXPECTED_MAX_NUM_CITIES = 15_000; // 10K cities + a buffer no to trigger the load factor private static class CityRef { final int length; final int fingerprint; final byte[] stringBytes; public CityRef(ByteBuffer byteBuffer, int startIdx, int length, int fingerprint) { this.length = length; this.stringBytes = new byte[length]; byteBuffer.get(startIdx, this.stringBytes, 0, this.stringBytes.length); this.fingerprint = fingerprint; } public String cityName() { return new String(stringBytes, StandardCharsets.UTF_8); } @Override public int hashCode() { return fingerprint; } @Override public boolean equals(Object other) { if (other instanceof CityRef otherRef) { if (fingerprint != otherRef.fingerprint) { return false; } if (this.length != otherRef.length) { return false; } for (var i = 0; i < this.length; i++) { if (this.stringBytes[i] != otherRef.stringBytes[i]) { return false; } } return true; } else { return false; } } } private static class ResultRow { private int min; private int max; private int sum; private int count; public ResultRow(int initialValue) { this.min = initialValue; this.max = initialValue; this.sum = initialValue; this.count = 1; } public void mergeValue(int value) { this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); this.sum += value; this.count += 1; } public String toString() { return round(min / 10.0) + "/" + round(sum / 10.0 / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } public void mergeResult(ResultRow value) { min = Math.min(min, value.min); max = Math.max(max, value.max); sum += value.sum; count += value.count; } } private record AverageAggregatorTask(ByteBuffer byteBuffer) { private static final int HASH_FACTOR = 31; // Mersenne prime public static Stream createStreamOf(List byteBuffers) { return byteBuffers.stream().map(AverageAggregatorTask::new); } public Map processChunk() { final var measurements = new HashMap(EXPECTED_MAX_NUM_CITIES); var lineStart = 0; // process line by line playing with the fact that a line is no longer than 106 bytes // 100 bytes for city name + 1 byte for separator + 1 bytes for negative sign + 4 bytes for number while (lineStart < byteBuffer.limit()) { lineStart = this.processLine(measurements, byteBuffer, lineStart); } return measurements; } private int processLine(Map measurements, ByteBuffer byteBuffer, int start) { var fingerPrint = 0; var separatorIdx = -1; var sign = 1; var value = 0; var lineEnd = -1; // Lines are processed in two stages: // 1 - prior do the city name separator // 2 - after the separator // this ensures less if clauses // stage 1 loop { for (int i = 0; i < NEW_LINE_SEEK_BUFFER_LEN; i++) { final var currentByte = byteBuffer.get(start + i); if (currentByte == ';') { separatorIdx = i; break; } else { fingerPrint = HASH_FACTOR * fingerPrint + currentByte; } } } // stage 2 loop: { for (int i = separatorIdx + 1; i < NEW_LINE_SEEK_BUFFER_LEN; i++) { final var currentByte = byteBuffer.get(start + i); switch (currentByte) { case '-': sign = -1; break; case '.': break; case '\n': lineEnd = start + i + 1; break; default: // only digits are expected here value = value * 10 + (currentByte - '0'); } if (lineEnd != -1) { break; } } } assert (separatorIdx > 0); final var cityRef = new CityRef(byteBuffer, start, separatorIdx,fingerPrint); value = sign * value; final var existingMeasurement = measurements.get(cityRef); if (existingMeasurement == null) { measurements.put(cityRef, new ResultRow(value)); } else { existingMeasurement.mergeValue(value); } return lineEnd; //to account for the line end } } public static void main(String[] args) throws IOException { // memory map the files and divide by number of cores final var numProcessors = Runtime.getRuntime().availableProcessors(); final var byteBuffers = calculateMemorySegments(numProcessors); final var tasks = AverageAggregatorTask.createStreamOf(byteBuffers); assert (byteBuffers.size() <= numProcessors); assert (!byteBuffers.isEmpty()); try (var pool = Executors.newFixedThreadPool(numProcessors)) { final Map aggregatedCities = tasks .parallel() .map(task -> CompletableFuture.supplyAsync(task::processChunk, pool)) .map(CompletableFuture::join) .reduce(new HashMap<>(EXPECTED_MAX_NUM_CITIES), (currentMap, accumulator) -> { currentMap.forEach((key, value) -> { final var prev = accumulator.get(key); if (prev == null) { accumulator.put(key, value); } else { prev.mergeResult(value); } }); return accumulator; }); var results = new HashMap(EXPECTED_MAX_NUM_CITIES); aggregatedCities.forEach((key, value) -> results.put(key.cityName(), value)); System.out.print("{"); String output = results.keySet() .stream() .sorted() .map(key -> key + "=" + results.get(key).toString()) .collect(Collectors.joining(", ")); System.out.print(output); System.out.println("}"); } } private static List calculateMemorySegments(int numChunks) throws IOException { try (FileChannel fc = FileChannel.open(Paths.get(FILE))) { var memMappedFile = fc.map(FileChannel.MapMode.READ_ONLY, 0L, fc.size(), Arena.ofAuto()); var result = new ArrayList(numChunks); var fileSize = fc.size(); var chunkSize = fileSize / numChunks; // TODO: if chunksize > MAX INT we will need to adjust var previousChunkEnd = 0L; for (int i = 0; i < numChunks; i++) { if (previousChunkEnd >= fileSize) { // There is a scenario for very small files where the number of chunks may be greater than // the number of lines. break; } var chunk = new long[]{ previousChunkEnd, 0 }; if (i == (numChunks - 1)) { // ensure the last chunk covers the full file chunk[1] = fileSize; } else { // all other chunks are end at a new line (\n) var theoreticalEnd = Math.min(previousChunkEnd + chunkSize, fileSize); var newLineOffset = 0; for (int j = 0; j < NEW_LINE_SEEK_BUFFER_LEN; j++) { var candidateOffset = theoreticalEnd + j; if (candidateOffset >= fileSize) { break; } byte b = memMappedFile.get(ValueLayout.OfByte.JAVA_BYTE, candidateOffset); newLineOffset += 1; if ((char) b == '\n') { break; } } chunk[1] = Math.min(fileSize, theoreticalEnd + newLineOffset); previousChunkEnd = chunk[1]; } assert (chunk[1] > chunk[0]); assert (chunk[1] <= fileSize); result.add(memMappedFile.asSlice(chunk[0], (chunk[1] - chunk[0])).asByteBuffer()); } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_filiphr.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.TreeMap; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * Initial submission: 1m 35s * Adding memory mapped files: 0m 55s (based on bjhara's submission) * Using big decimal and iterating the buffer once: 0m 20s * Using long parse: 0m 11s * Using array hash code for city key: 0m 7.1s (this is invalid since it can lead to hash collisions) * Manually compute the value: 0m 6.8s * Revert array hash code for city key: 0m 10s * Use array hash and Arrays#equals for city key: 0m 7.2s *

* Using 21.0.1 Temurin with ShenandoahGC on Macbook (Intel) Pro * `sdk use java 21.0.1-tem` * * When using Oracle GraalVM 21.0.1+12.1 * `sdk use java 21.0.1-graal` * It takes 0m 15s on my machine * `sdk use java 21.0.1-graalce` * It takes 0m 20s on my machine * * @author Filip Hrisafov */ public class CalculateAverage_filiphr { private static final String FILE = "./measurements.txt"; private static final long CHUNK_SIZE = 1024 * 1024 * 10L; // 1KB * 10KB ~ 10MB private static final class Measurement { private long min = Long.MAX_VALUE; private long max = Long.MIN_VALUE; private long sum = 0L; private long count = 0L; private void add(long value) { this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); this.sum += value; this.count++; } public static Measurement combine(Measurement m1, Measurement m2) { Measurement measurement = new Measurement(); measurement.min = Math.min(m1.min, m2.min); measurement.max = Math.max(m1.max, m2.max); measurement.sum = m1.sum + m2.sum; measurement.count = m1.count + m2.count; return measurement; } @Override public String toString() { return round(min / 10.0) + "/" + round((sum / 10.0) / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } public static void main(String[] args) throws IOException { // long start = System.nanoTime(); Map measurements; try (FileChannel fileChannel = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ)) { measurements = fineChannelStream(fileChannel) .parallel() .map(CalculateAverage_filiphr::parseBuffer) .reduce(Collections.emptyMap(), CalculateAverage_filiphr::mergeMaps); } Map finalMeasurements = new TreeMap<>(); for (Map.Entry entry : measurements.entrySet()) { StoredKey key = (StoredKey) entry.getKey(); Measurement measurement = entry.getValue(); finalMeasurements.put(new String(key.keyBytes), measurement); } System.out.println(finalMeasurements); // System.out.println("Done in " + (System.nanoTime() - start) / 1000000 + " ms"); } private static Map mergeMaps(Map map1, Map map2) { if (map1.isEmpty()) { return map2; } else { Set cities = new HashSet<>(map1.keySet()); cities.addAll(map2.keySet()); Map result = HashMap.newHashMap(cities.size()); for (Key city : cities) { Measurement m1 = map1.get(city); Measurement m2 = map2.get(city); if (m2 == null) { // When m2 is null then it is not possible for m1 to be null as well, // since cities is a union of the map key sets result.put(city, m1); } else if (m1 == null) { // When m1 is null then it is not possible for m2 to be null as well, // since cities is a union of the map key sets result.put(city, m2); } else { result.put(city, Measurement.combine(m1, m2)); } } return result; } } /** * This is an adapted implementation of the bjhara parseBuffer. * We are using {@code Map} because creating the string key on every single line is obsolete. * Instead, we create a hash key from the string, and we use that as a key in the map. */ private static Map parseBuffer(ByteBuffer bb) { Map measurements = HashMap.newHashMap(415); int limit = bb.limit(); byte[] cityBuffer = new byte[128]; while (bb.position() < limit) { int cityBufferIndex = 0; // Iterate through the byte buffer and fill the buffer until we find the separator (;) // While iterating we are also going to compute the city hash key int cityHash = 1; while (bb.position() < limit) { byte positionByte = bb.get(); if (positionByte == ';') { break; } cityBuffer[cityBufferIndex++] = positionByte; cityHash = 31 * cityHash + positionByte; } SearchKey searchKey = new SearchKey(cityBuffer, cityHash, cityBufferIndex); byte lastPositionByte = '\n'; boolean negative = false; long value = 0; while (bb.position() < limit) { byte positionByte = bb.get(); if (positionByte == '\r' || positionByte == '\n') { lastPositionByte = positionByte; break; } else if (positionByte == '-') { negative = true; } else if (positionByte != '.') { // The 0 to 9 characters have an int value of 48 (for 0) to 57 (for 9) // Therefore, in order to compute the digit we subtract with 48 int digit = positionByte - 48; // We are computing the value by hand (in order to avoid iterating the index twice) value = value * 10 + digit; } } if (negative) { value = -value; } Measurement measurement = measurements.get(searchKey); if (measurement == null) { byte[] keyBytes = new byte[cityBufferIndex]; System.arraycopy(cityBuffer, 0, keyBytes, 0, cityBufferIndex); StoredKey storedKey = new StoredKey(keyBytes, cityHash); measurement = new Measurement(); measurements.put(storedKey, measurement); } measurement.add(value); // and get rid of the new line (handle both kinds) if (lastPositionByte == '\r') { bb.get(); } } return measurements; } /** * Thanks to bjhara and royvanrijn for the idea of using (and learning about) memory mapped files. */ private static Stream fineChannelStream(FileChannel fileChannel) throws IOException { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(fileChannelIterator(fileChannel), Spliterator.IMMUTABLE), false); } private static Iterator fileChannelIterator(FileChannel fileChannel) throws IOException { return new Iterator<>() { private final long size = fileChannel.size(); private long start = 0; @Override public boolean hasNext() { return start < size; } @Override public ByteBuffer next() { try { MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, Math.min(CHUNK_SIZE, size - start)); // don't split the data in the middle of lines // find the closest previous newline int realEnd = mappedByteBuffer.limit() - 1; while (mappedByteBuffer.get(realEnd) != '\n') realEnd--; realEnd++; mappedByteBuffer.limit(realEnd); start += realEnd; return mappedByteBuffer; } catch (IOException ex) { throw new UncheckedIOException(ex); } } }; } /** * This is a class that is used to reference a city key using its bytes only. * It has the hash precomputed, and it is equal to a {@link SearchKey} when the key bytes are equal to the {@link SearchKey#buffer} up to the {@link SearchKey#limit}. */ private static final class StoredKey implements Key { private final byte[] keyBytes; private final int hash; private StoredKey(byte[] keyBytes, int hash) { this.keyBytes = keyBytes; this.hash = hash; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null) { return false; } if (o instanceof SearchKey key) { return Arrays.equals(keyBytes, 0, keyBytes.length, key.buffer, 0, key.limit); } else if (o instanceof StoredKey key) { return Arrays.equals(keyBytes, key.keyBytes); } return false; } @Override public int hashCode() { return hash; } } /** * A class that is used to lookup for a value in a map. * This key is equal to {@link StoredKey} when the buffer has the same contents as the {@link StoredKey#keyBytes}. */ private static final class SearchKey implements Key { private final byte[] buffer; private final int hash; private final int limit; private SearchKey(byte[] buffer, int hash, int limit) { this.buffer = buffer; this.hash = hash; this.limit = limit; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null) { return false; } if (o instanceof StoredKey key) { return Arrays.equals(buffer, 0, limit, key.keyBytes, 0, limit); } else if (o instanceof SearchKey key) { return Arrays.equals(buffer, 0, limit, key.buffer, 0, key.limit); } return false; } @Override public int hashCode() { return hash; } } private interface Key { } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_flippingbits.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ShortVector; import jdk.incubator.vector.VectorOperators; import sun.misc.Unsafe; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; /** * Approach: * - Use memory-mapped file to speed up loading data into memory * - Partition data, compute aggregates for partitions in parallel, and finally combine results from all partitions * - Apply SIMD instructions for computing min/max/sum aggregates * - Use Shorts for storing aggregates of partitions, so we maximize the SIMD parallelism */ public class CalculateAverage_flippingbits { private static final String FILE = "./measurements.txt"; private static final long MINIMUM_FILE_SIZE_PARTITIONING = 10 * 1024 * 1024; // 10 MB private static final int SIMD_LANE_LENGTH = ShortVector.SPECIES_MAX.length(); private static final int NUM_STATIONS = 10_000; private static final int HASH_MAP_OFFSET_CAPACITY = 200_000; private static final Unsafe UNSAFE = initUnsafe(); private static int HASH_PRIME_NUMBER = 31; private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws IOException { var result = Arrays.asList(getSegments()).parallelStream() .map(segment -> { try { return processSegment(segment[0], segment[1]); } catch (IOException e) { throw new RuntimeException(e); } }) .reduce(FasterHashMap::mergeWith) .get(); var sortedMap = new TreeMap(); for (Station station : result.getEntries()) { sortedMap.put(station.getName(), station); } System.out.println(sortedMap); } private static long[][] getSegments() throws IOException { try (var file = new RandomAccessFile(FILE, "r")) { var channel = file.getChannel(); var fileSize = channel.size(); var startAddress = channel .map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()) .address(); // Split file into segments, so we can work around the size limitation of channels var numSegments = (fileSize > MINIMUM_FILE_SIZE_PARTITIONING) ? Runtime.getRuntime().availableProcessors() : 1; var segmentSize = fileSize / numSegments; var boundaries = new long[numSegments][2]; var endPointer = startAddress; for (var i = 0; i < numSegments - 1; i++) { // Start of segment boundaries[i][0] = endPointer; // Extend segment until end of line or file endPointer = endPointer + segmentSize; while (UNSAFE.getByte(endPointer) != '\n') { endPointer++; } // End of segment boundaries[i][1] = endPointer++; } boundaries[numSegments - 1][0] = endPointer; boundaries[numSegments - 1][1] = startAddress + fileSize; return boundaries; } } private static FasterHashMap processSegment(long startOfSegment, long endOfSegment) throws IOException { var fasterHashMap = new FasterHashMap(); for (var i = startOfSegment; i < endOfSegment; i += 3) { // Read station name int nameHash = UNSAFE.getByte(i); final var nameStartAddress = i++; var character = UNSAFE.getByte(i); while (character != ';') { nameHash = nameHash * HASH_PRIME_NUMBER + character; i++; character = UNSAFE.getByte(i); } var nameLength = (int) (i - nameStartAddress); i++; // Read measurement var isNegative = UNSAFE.getByte(i) == '-'; var measurement = 0; if (isNegative) { i++; character = UNSAFE.getByte(i); while (character != '.') { measurement = measurement * 10 + character - '0'; i++; character = UNSAFE.getByte(i); } measurement = (measurement * 10 + UNSAFE.getByte(i + 1) - '0') * -1; } else { character = UNSAFE.getByte(i); while (character != '.') { measurement = measurement * 10 + character - '0'; i++; character = UNSAFE.getByte(i); } measurement = measurement * 10 + UNSAFE.getByte(i + 1) - '0'; } fasterHashMap.addEntry(nameHash, nameLength, nameStartAddress, (short) measurement); } for (Station station : fasterHashMap.getEntries()) { station.aggregateRemainingMeasurements(); } return fasterHashMap; } private static class Station { final short[] measurements = new short[SIMD_LANE_LENGTH * 2]; // Assume that we do not have more than Integer.MAX_VALUE measurements for the same station per partition int count = 1; long sum = 0; short min = Short.MAX_VALUE; short max = Short.MIN_VALUE; final long nameAddress; final int nameLength; final int nameHash; public Station(int nameHash, int nameLength, long nameAddress, short measurement) { this.nameHash = nameHash; this.nameLength = nameLength; this.nameAddress = nameAddress; measurements[0] = measurement; } public String getName() { byte[] name = new byte[nameLength]; UNSAFE.copyMemory(null, nameAddress, name, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameLength); return new String(name, StandardCharsets.UTF_8); } public void addMeasurementAndComputeAggregate(short measurement) { // Add measurement to buffer, which is later processed by SIMD instructions measurements[count % measurements.length] = measurement; count++; // Once lane is full, use SIMD instructions to calculate aggregates if (count % measurements.length == 0) { var firstVector = ShortVector.fromArray(ShortVector.SPECIES_MAX, measurements, 0); var secondVector = ShortVector.fromArray(ShortVector.SPECIES_MAX, measurements, SIMD_LANE_LENGTH); var simdMin = firstVector.min(secondVector).reduceLanes(VectorOperators.MIN); min = (short) Math.min(min, simdMin); var simdMax = firstVector.max(secondVector).reduceLanes(VectorOperators.MAX); max = (short) Math.max(max, simdMax); sum += firstVector.add(secondVector).reduceLanes(VectorOperators.ADD); } } public void aggregateRemainingMeasurements() { for (var i = 0; i < count % measurements.length; i++) { var measurement = measurements[i]; min = (short) Math.min(min, measurement); max = (short) Math.max(max, measurement); sum += measurement; } } public void mergeWith(Station otherStation) { min = (short) Math.min(min, otherStation.min); max = (short) Math.max(max, otherStation.max); count = count + otherStation.count; sum = sum + otherStation.sum; } public boolean nameEquals(long otherNameAddress) { var swarLimit = (nameLength / Long.BYTES) * Long.BYTES; var i = 0; for (; i < swarLimit; i += Long.BYTES) { if (UNSAFE.getLong(nameAddress + i) != UNSAFE.getLong(otherNameAddress + i)) { return false; } } for (; i < nameLength; i++) { if (UNSAFE.getByte(nameAddress + i) != UNSAFE.getByte(otherNameAddress + i)) { return false; } } return true; } public String toString() { return String.format( Locale.US, "%.1f/%.1f/%.1f", (min / 10.0), ((sum / 10.0) / count), (max / 10.0)); } } /** * Use two arrays for implementing the hash map: * - The array `entries` holds the map values, in our case instances of the class Station. * - The array `offsets` maps hashes of the keys to indexes in the `entries` array. * * We create `offsets` with a much larger capacity than `entries`, so we minimize collisions. */ private static class FasterHashMap { // Using 16-bit integers (shorts) for offsets supports up to 2^15 (=32,767) entries // If you need to store more entries, consider replacing short with int short[] offsets = new short[HASH_MAP_OFFSET_CAPACITY]; Station[] entries = new Station[NUM_STATIONS + 1]; int slotsInUse = 0; private int getOffsetIdx(int nameHash, int nameLength, long nameAddress) { var offsetIdx = nameHash & (offsets.length - 1); var offset = offsets[offsetIdx]; while (offset != 0 && (nameLength != entries[offset].nameLength || !entries[offset].nameEquals(nameAddress))) { offsetIdx = (offsetIdx + 1) % offsets.length; offset = offsets[offsetIdx]; } return offsetIdx; } public void addEntry(int nameHash, int nameLength, long nameAddress, short measurement) { var offsetIdx = getOffsetIdx(nameHash, nameLength, nameAddress); var offset = offsets[offsetIdx]; if (offset == 0) { slotsInUse++; entries[slotsInUse] = new Station(nameHash, nameLength, nameAddress, measurement); offsets[offsetIdx] = (short) slotsInUse; } else { entries[offset].addMeasurementAndComputeAggregate(measurement); } } public FasterHashMap mergeWith(FasterHashMap otherMap) { for (Station station : otherMap.getEntries()) { var offsetIdx = getOffsetIdx(station.nameHash, station.nameLength, station.nameAddress); var offset = offsets[offsetIdx]; if (offset == 0) { slotsInUse++; entries[slotsInUse] = station; offsets[offsetIdx] = (short) slotsInUse; } else { entries[offset].mergeWith(station); } } return this; } public List getEntries() { return Arrays.asList(entries).subList(1, slotsInUse + 1); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_fragmede.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.util.stream.Collectors.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collector; public class CalculateAverage_fragmede { private static final String FILE = "./measurements.txt"; // private static final String FILE = "./yaku.txt";// "./measurements.txt"; private static record Measurement(String station, int value) { public static Measurement of(String[] parts) { //System.out.printf("string: %s!\n", parts[1]); boolean negative = false; int idx = 0; if (parts[1].charAt(0) == '-') { negative = true; idx = 1; } int digit = (parts[1].charAt(idx)-48) * 10; int digit2 = 0; if (parts[1].charAt(idx+1) != '.') { // two digit temperature digit = (digit) * 10; //System.out.println(parts[1].charAt(idx+1)); digit2 = (parts[1].charAt(idx+1)-48)*10; idx++; } // if (parts[1].charAt(idx+2) != ".") { // abort(); // } int frac = parts[1].charAt(idx+2)-48; //System.out.printf("parts, %d:%d:%d\n", digit, digit2, frac); int value = digit + digit2 + frac; if (negative) { value = -value; } //System.out.printf("end value: %d\n", value); return new Measurement(parts[0], value); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return min + "/" + round(mean) + "/" + max; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } public static void main(String[] args) throws IOException { Collector collector = Collector.of( MeasurementAggregator::new, (a, m) -> { a.min = Math.min(a.min, m.value); a.max = Math.max(a.max, m.value); a.sum += m.value; a.count++; }, (agg1, agg2) -> { var res = new MeasurementAggregator(); res.min = Math.min(agg1.min, agg2.min); res.max = Math.max(agg1.max, agg2.max); res.sum = agg1.sum + agg2.sum; res.count = agg1.count + agg2.count; return res; }, agg -> { return new ResultRow(agg.min / 10.0, agg.sum / 10.0 / agg.count, agg.max / 10.0); }); Map measurements = new TreeMap<>(Files.lines(Paths.get(args[0])) .map(l -> Measurement.of(l.split(";"))) .collect(groupingBy(Measurement::station, collector))); System.out.println(measurements); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gabrielfoo.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.HashMap; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; public class CalculateAverage_gabrielfoo { private static final String FILE = "./measurements.txt"; private static final int UTF8_MAX_LEN_100_BYTES = 400; private static final int DOUBLE_DIGITS_MAX = 3; private static final int UNIQUE_STATION_NAMES = 10000; private static class ResultRow { private double min = Double.POSITIVE_INFINITY; private double sum = 0.0; private double max = Double.NEGATIVE_INFINITY; private int count = 0; public String toString() { return min + "/" + (Math.round(sum / count) / 10.0) + "/" + max; } public void updateMinMax(double incoming) { min = Math.min(min, incoming); max = Math.max(max, incoming); sum += incoming * 10.0; count += 1; } public void combine(ResultRow other) { min = Math.min(min, other.min); max = Math.max(max, other.max); sum += other.sum; count += other.count; } } public static MappedByteBuffer[] mapFileToMemory(final RandomAccessFile file, final int chunkCount) throws Exception { FileChannel channel = file.getChannel(); final long chunkSize = Math.ceilDiv(file.length(), chunkCount); MappedByteBuffer buffers[] = new MappedByteBuffer[chunkCount]; long position = 0; for (int i = 0; i < chunkCount - 1; ++i) { file.seek(position + chunkSize); long ptr = file.getFilePointer(); while (file.readByte() != '\n') { file.seek(++ptr); } buffers[i] = channel.map(FileChannel.MapMode.READ_ONLY, position, ptr - position + 1); position = ptr + 1; } buffers[buffers.length - 1] = channel.map(FileChannel.MapMode.READ_ONLY, position, file.length() - position); return buffers; } public static void main(String[] args) throws Exception { final RandomAccessFile file = new RandomAccessFile(FILE, "r"); final int coreCount = file.length() < 2147483647 ? 1 : Runtime.getRuntime().availableProcessors(); ArrayList> maps = new ArrayList<>(); final ThreadFactory threadFactory = new ThreadFactory() { public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setPriority(Thread.MAX_PRIORITY); return t; } }; ExecutorService executor = Executors.newFixedThreadPool(coreCount, threadFactory); Future initFuture = executor.submit(() -> { for (int i = 0; i < coreCount; ++i) { maps.add(new HashMap<>(UNIQUE_STATION_NAMES, 0.9f)); } }); MappedByteBuffer[] buffers = mapFileToMemory(file, coreCount); initFuture.get(); Future[] futures = new Future[buffers.length]; for (int k = 0; k < buffers.length; ++k) { final MappedByteBuffer buffer = buffers[k]; final var map = maps.get(k); futures[k] = executor.submit(() -> { int start = 0; byte[] stationArr = new byte[UTF8_MAX_LEN_100_BYTES]; double[] floatArr = new double[DOUBLE_DIGITS_MAX]; byte currentByte; while (buffer.hasRemaining()) { currentByte = buffer.get(); stationArr[buffer.position() - start - 1] = currentByte; if (currentByte == ';') { final int stationEnd = buffer.position() - 1; // convert to double now currentByte = buffer.get(); boolean neg = currentByte == '-'; if (neg) currentByte = buffer.get(); floatArr[0] = currentByte - '0'; currentByte = buffer.get(); if (currentByte == '.') { floatArr[1] = (buffer.get() - '0') / 10.0; floatArr[2] = 0.0; } else { floatArr[0] *= 10.0; floatArr[1] = (currentByte - '0'); buffer.get(); floatArr[2] = (buffer.get() - '0') / 10.0; } final double f = (neg ? -1 : 1) * (floatArr[0] + floatArr[1] + floatArr[2]); buffer.get(); // discard \n String station = new String(stationArr, 0, stationEnd - start); map.compute(station, (key, existingRow) -> { ResultRow row = (existingRow == null) ? new ResultRow() : existingRow; row.updateMinMax(f); return row; }); start = buffer.position(); } } }); } for (Future future : futures) { future.get(); } HashMap resultHashMap = maps.get(0); maps.stream().skip(1).flatMap(map -> map.entrySet().stream()).forEach(entry -> { resultHashMap.merge(entry.getKey(), entry.getValue(), (oldVal, newVal) -> { oldVal.combine(newVal); return oldVal; }); }); TreeMap res = new TreeMap<>(resultHashMap); executor.shutdown(); System.out.println(res); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gabrielreid.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorSpecies; import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * An attempt at using the "new" Vector API for determining where newline and semicolons are. */ public class CalculateAverage_gabrielreid { private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED; private static final int BYTE_SPECIES_LEN = BYTE_SPECIES.length(); private static final byte SEMICOLON_BYTE = (byte) ';'; private static final byte NEWLINE_BYTE = (byte) '\n'; private static final byte NEG_BYTE = (byte) '-'; private static final int BLOCK_READ_SIZE = 1024 * 1024 * 16; private static final int SUMMARY_TABLE_SIZE = 2048; private static final int MAP_INITIAL_SIZE = 450; /** * State with the full summary table, as well as leftover bytes between processed blocks that need to * be handled afterward. */ record State(Map map, byte[] remainderBytes) { } public static void main(String[] args) throws IOException { int numCores = Runtime.getRuntime().availableProcessors(); int numReadBuffers = numCores + 2; var blockBuilderQueue = new LinkedBlockingDeque(numReadBuffers); for (int i = 0; i < numReadBuffers; i++) { blockBuilderQueue.add(new BlockBuilder(BLOCK_READ_SIZE)); } try (var fjp = new ForkJoinPool(numCores)) { CompletableFuture stateFuture = CompletableFuture.completedFuture(new State(new HashMap<>(MAP_INITIAL_SIZE), new byte[0])); try (var fis = new FileInputStream("./measurements.txt")) { var blockBuilder = Objects.requireNonNull(blockBuilderQueue.poll()); boolean skipToNewline = false; int cnt; while ((cnt = fis.read(blockBuilder.readBuffer)) != -1) { var localBlockBuilder = blockBuilder; var localCnt = cnt; var localSkipToNewline = skipToNewline; skipToNewline = true; stateFuture = stateFuture.thenCombine( CompletableFuture.supplyAsync(() -> { var summaryMap = localBlockBuilder.buildSummaryTable(localCnt, localSkipToNewline); int unprocessedRemainderSize = localBlockBuilder.firstLineStart + (localCnt - localBlockBuilder.lastLineEnd); var unprocessedBytes = new byte[unprocessedRemainderSize]; System.arraycopy(localBlockBuilder.readBuffer, 0, unprocessedBytes, 0, localBlockBuilder.firstLineStart); System.arraycopy(localBlockBuilder.readBuffer, localBlockBuilder.lastLineEnd, unprocessedBytes, localBlockBuilder.firstLineStart, (localCnt - localBlockBuilder.lastLineEnd)); localBlockBuilder.reset(); blockBuilderQueue.add(localBlockBuilder); return new State(summaryMap, unprocessedBytes); }, fjp), (state, newState) -> { newState.map.forEach( (k, v) -> state.map.merge(k, v, CitySummary::add)); var newRemainderBytes = new byte[state.remainderBytes.length + newState.remainderBytes.length]; System.arraycopy(state.remainderBytes, 0, newRemainderBytes, 0, state.remainderBytes.length); System.arraycopy(newState.remainderBytes, 0, newRemainderBytes, state.remainderBytes.length, newState.remainderBytes.length); return new State(state.map, newRemainderBytes); }); try { blockBuilder = blockBuilderQueue.poll(1, TimeUnit.HOURS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } } stateFuture = stateFuture.thenApply(state -> { BlockBuilder blockBuilder; try { blockBuilder = blockBuilderQueue.poll(1, TimeUnit.HOURS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } System.arraycopy(state.remainderBytes, 0, blockBuilder.readBuffer, 0, state.remainderBytes.length); var m = blockBuilder.buildSummaryTable(state.remainderBytes.length, false); m.forEach( (k, v) -> state.map.merge(k, v, CitySummary::add)); return new State(state.map, new byte[0]); }); var state = stateFuture.join(); System.out.println(STR."{\{state.map.entrySet().stream().sorted(Map.Entry.comparingByKey()) .map(e -> String.format(Locale.US, "%s=%.1f/%.1f/%.1f", e.getKey(), e.getValue().min / 10f, (e.getValue().sum / (float) e.getValue().count) / 10f, e.getValue().max / 10f)) .collect(Collectors.joining(", "))}}"); }} /** * Parses number values as integers from the byte array. *

* The multiplier is 1 if positive and -1 if negative. */ static short parseNumFromLine(byte[] buf, int offset, int len) { return switch (len) { case 3 -> (short) ((((buf[offset] - '0') * 10) + buf[offset + 2] - '0')); case 4 -> (short) ((((buf[offset] - '0') * 100) + (buf[offset + 1] - '0') * 10 + buf[offset + 3] - '0')); default -> throw new IllegalStateException("Unexpected number length %d".formatted(len)); }; } /** * Aggregator of temperature values. All values are stored as integers internally (i.e. multiplied by 10). */ static class CitySummary { int max; int min; long sum; int count; public CitySummary(int value) { this.max = value; this.min = value; this.sum = value; this.count = 1; } void add(int value) { this.max = Math.max(value, this.max); this.min = Math.min(value, this.min); this.sum += value; this.count++; } CitySummary add(CitySummary other) { this.max = Math.max(other.max, this.max); this.min = Math.min(other.min, this.min); this.sum += other.sum; this.count += other.count; return this; } } /** * A wrapper around a city name. *

* Provides a view of a large read buffer, but can also be cloned and detached from the read buffer. */ static final class ByteSlice { private final byte[] buf; private final int offset; private final int len; public ByteSlice(byte[] buf, int offset, int len) { this.buf = buf; this.offset = offset; this.len = len; } public String valueAsString() { return new String(this.buf, this.offset, this.len, StandardCharsets.UTF_8); } public int hashCode() { return hashCode(this.buf, this.offset, this.len); } public static int hashCode(byte[] buf, int offset, int len) { int result = 1; int i = 0; for (; i + 3 < len; i += 4) { result = 31 * 31 * 31 * 31 * result + 31 * 31 * 31 * buf[offset + i] + 31 * 31 * buf[offset + i + 1] + 31 * buf[offset + i + 2] + buf[offset + i + 3]; } for (; i < len; i++) { result = 31 * result + buf[offset + i]; } return result; } @Override public boolean equals(Object obj) { if (obj instanceof ByteSlice otherByteSlice) { return ByteSlice.equal(this, otherByteSlice); } return false; } public static boolean equal(ByteSlice a, ByteSlice b) { return Arrays.equals(a.buf, a.offset, a.offset + a.len, b.buf, b.offset, b.offset + b.len); } public static boolean equal(ByteSlice a, byte[] buf, int offset, int len) { return Arrays.equals(a.buf, a.offset, a.offset + a.len, buf, offset, offset + len); } } record ValueNode(ByteSlice byteSlice, CitySummary citySummary) { } static final class SummaryTable { private static final int LOAD_FACTOR = 4; private int size; private ValueNode[] values; private int valueCount; private int resizeThreshold; private byte[] localBufferBytes = new byte[MAP_INITIAL_SIZE * 100]; private int localBufferPtr = 0; SummaryTable(int size) { this.size = size; this.values = new ValueNode[size]; this.resizeThreshold = size / LOAD_FACTOR; } void reset() { for (int i = 0; i < size; i++) { this.values[i] = null; } localBufferPtr = 0; } public void addAll(SummaryTable other) { for (int i = 0; i < other.size; i++) { var otherSlice = other.values[i]; if (otherSlice != null) { putValueNode(otherSlice); } } } private void putValueNode(ValueNode valueNode) { int hashCode = valueNode.byteSlice.hashCode(); int index = (hashCode & 0x7FFFFFFF) % size; while (values[index] != null) { if (ByteSlice.equal(values[index].byteSlice, valueNode.byteSlice)) { values[index].citySummary.add(valueNode.citySummary); return; } index = (index + (hashCode & 0xFF) + 1) % size; } values[index] = valueNode; valueCount++; resizeIfNecessary(); } public void putTemperatureValue(byte[] buf, int offset, int len, int value) { int hashCode = ByteSlice.hashCode(buf, offset, len); int index = (hashCode & 0x7FFFFFFF) % size; while (values[index] != null) { if (ByteSlice.equal(values[index].byteSlice, buf, offset, len)) { values[index].citySummary.add(value); return; } index = (index + (hashCode & 0xFF) + 1) % size; } System.arraycopy(buf, offset, this.localBufferBytes, this.localBufferPtr, len); var byteSlice = new ByteSlice(this.localBufferBytes, this.localBufferPtr, len); var valueNode = new ValueNode(byteSlice, new CitySummary(value)); localBufferPtr += len; values[index] = valueNode; valueCount++; resizeIfNecessary(); } private void resizeIfNecessary() { if (valueCount >= resizeThreshold) { int newSize = size * 2; var resized = new SummaryTable(newSize); for (int i = 0; i < this.size; i++) { if (this.values[i] != null) { resized.putValueNode(this.values[i]); } } resized.addAll(this); byte[] localBufferBytes = new byte[this.localBufferBytes.length * 2]; System.arraycopy(this.localBufferBytes, 0, localBufferBytes, 0, this.localBufferPtr); this.values = resized.values; this.size = newSize; this.valueCount = resized.valueCount; this.resizeThreshold = newSize / LOAD_FACTOR; this.localBufferBytes = localBufferBytes; } } public Map toMap() { HashMap m = HashMap.newHashMap(valueCount); for (int i = 0; i < size; i++) { var valueNode = this.values[i]; if (valueNode != null) { m.put(valueNode.byteSlice.valueAsString(), valueNode.citySummary); } } return m; } } /** * Performs actual building of a SummaryTable from a read buffer. */ static class BlockBuilder { final byte[] readBuffer; private final SummaryTable summaryTable; private int firstLineStart; private int lastLineEnd; public BlockBuilder(int readBufferSize) { this.readBuffer = new byte[readBufferSize]; this.summaryTable = new SummaryTable(SUMMARY_TABLE_SIZE); } void reset() { firstLineStart = -1; lastLineEnd = -1; this.summaryTable.reset(); } public Map buildSummaryTable(int readByteCount, boolean skipToNewline) { parseLineSegments(readByteCount, skipToNewline); return summaryTable.toMap(); } private void parseLineSegments(int byteCount, boolean skipToNewline) { var upperBound = BYTE_SPECIES.loopBound(byteCount) - BYTE_SPECIES_LEN; int idx = 0; if (skipToNewline) { while (this.readBuffer[idx] != NEWLINE_BYTE) { idx++; } idx++; } this.firstLineStart = idx; int lineStart = idx; while (idx < upperBound) { var byteVector = ByteVector.fromArray(BYTE_SPECIES, readBuffer, idx); var newlineIdx = byteVector.eq(NEWLINE_BYTE).firstTrue(); if (newlineIdx < BYTE_SPECIES_LEN) { var semicolonByteMask = byteVector.eq(SEMICOLON_BYTE); var semicolonIdx = semicolonByteMask.firstTrue(); int semicolonOffset = idx + semicolonIdx; int lineEnd = idx + newlineIdx; short negative = (short) ((readBuffer[idx + semicolonIdx + 1] == NEG_BYTE) ? 1 : 0); int numLength = (byte) ((lineEnd - (semicolonOffset + 1))); var num = parseNumFromLine(this.readBuffer, semicolonOffset + 1 + negative, numLength - negative); num = negative == 1 ? (short) -num : num; int nameLen = semicolonOffset - lineStart; summaryTable.putTemperatureValue(this.readBuffer, lineStart, nameLen, num); idx = lineEnd + 1; lastLineEnd = idx; lineStart = idx; } else { // TODO This is mostly just out of laziness so that we process a full line on every step idx += BYTE_SPECIES_LEN - 7; // Max length of semicolon, negative temp, and newline is 7 } } int semicolonIdx = -1; while (idx < byteCount) { if (readBuffer[idx] == SEMICOLON_BYTE) { semicolonIdx = idx; } if (readBuffer[idx] == NEWLINE_BYTE) { int lineEnd = idx; short negative = (short) ((readBuffer[semicolonIdx + 1] == NEG_BYTE) ? 1 : 0); int numLength = (byte) ((lineEnd - (semicolonIdx + 1))); var num = parseNumFromLine(this.readBuffer, semicolonIdx + 1 + negative, numLength - negative); num = negative == 1 ? (short) -num : num; int nameLen = semicolonIdx - lineStart; summaryTable.putTemperatureValue(this.readBuffer, lineStart, nameLen, num); idx = lineEnd + 1; lastLineEnd = idx; lineStart = idx; } idx++; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gamlerhart.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.*; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.util.ArrayList; import java.util.Iterator; import java.util.TreeMap; import java.util.stream.Collector; import java.util.stream.Collectors; import static java.lang.Double.doubleToRawLongBits; import static java.lang.Double.longBitsToDouble; import static java.lang.foreign.ValueLayout.*; /** * Broad experiments in this implementation: * - Memory-Map the file with new MemorySegments * - Use SIMD/vectorized search for the semicolon and new line feeds * - Use SIMD/vectorized comparison for the 'key' *

* Absolute stupid things / performance left on the table * - Single Threaded! Multi threading planned. * - The hash map/table is super basic. * - Hash table implementation / hashing has no resizing and is quite basic * - Zero time spend on profiling =) *

*

* Cheats used: * - Only works with Unix line feed \n * - double parsing is only accepting XX.X and X.X * - HashMap has no resizing, check, horrible hash etc. * - Used the double parsing from yemreinci */ public class CalculateAverage_gamlerhart { private static final String FILE = "./measurements.txt"; final static VectorSpecies byteVec = ByteVector.SPECIES_PREFERRED; final static Vector zero = byteVec.zero(); final static int vecLen = byteVec.length(); final static Vector semiColon = byteVec.broadcast(';'); final static VectorMask allTrue = byteVec.maskAll(true); final static ValueLayout.OfInt INT_UNALIGNED_BIG_ENDIAN = ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN); public static void main(String[] args) throws Exception { try (var arena = Arena.ofShared(); FileChannel fc = FileChannel.open(Path.of(FILE))) { long fileSize = fc.size(); MemorySegment fileContent = fc.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, arena); ArrayList

sections = splitFileIntoSections(fileSize, fileContent); var loopBound = byteVec.loopBound(fileSize) - vecLen; var result = sections.stream() .parallel() .map(s -> { return parseSection(s.start, s.end, loopBound, fileContent); }); var measurements = new TreeMap(); result.forEachOrdered(m -> { m.fillMerge(fileContent, measurements); }); System.out.println(measurements); } } private static PrivateHashMap parseSection(long start, long end, long loopBound, MemorySegment fileContent) { var map = new PrivateHashMap(); for (long i = start; i < end;) { long nameStart = i; int simdSearchEnd = 0; int nameLen = 0; // Vectorized Search if (i < loopBound) { do { var vec = byteVec.fromMemorySegment(fileContent, i, ByteOrder.BIG_ENDIAN); var hasSemi = vec.eq(semiColon); simdSearchEnd = hasSemi.firstTrue(); i += simdSearchEnd; nameLen += simdSearchEnd; } while (simdSearchEnd == vecLen && i < loopBound); } // Left-over search while (loopBound <= i && fileContent.get(JAVA_BYTE, i) != ';') { nameLen++; i++; } i++; // Consume ; // Copied from yemreinci. I mostly wanted to experiment the vector math, not with parsing =) double val; { boolean negative = false; if ((fileContent.get(JAVA_BYTE, i)) == '-') { negative = true; i++; } byte b; double temp; if ((b = fileContent.get(JAVA_BYTE, i + 1)) == '.') { // temperature is in either XX.X or X.X form temp = (fileContent.get(JAVA_BYTE, i) - '0') + (fileContent.get(JAVA_BYTE, i + 2) - '0') / 10.0; i += 3; } else { temp = (fileContent.get(JAVA_BYTE, i) - '0') * 10 + (b - '0') + (fileContent.get(JAVA_BYTE, i + 3) - '0') / 10.0; i += 4; } val = (negative ? -temp : temp); } i++; // Consume \n map.add(fileContent, nameStart, nameLen, val); } return map; } private static ArrayList
splitFileIntoSections(long fileSize, MemorySegment fileContent) { var cpuCount = Runtime.getRuntime().availableProcessors(); var roughChunkSize = fileSize / cpuCount; ArrayList
sections = new ArrayList<>(cpuCount); for (long sStart = 0; sStart < fileSize;) { var endGuess = Math.min(sStart + roughChunkSize, fileSize); for (; endGuess < fileSize && fileContent.get(JAVA_BYTE, endGuess) != '\n'; endGuess++) { } sections.add(new Section(sStart, endGuess)); sStart = endGuess + 1; } return sections; } private static class PrivateHashMap { private static final int SIZE_SHIFT = 14; public static final int SIZE = 1 << SIZE_SHIFT; public static int MASK = 0xFFFFFFFF >>> (32 - SIZE_SHIFT); public static long SHIFT_POS = 16; public static long MASK_POS = 0xFFFFFFFFFFFF0000L; public static long MASK_LEN = 0x000000000000FFFFL; // Encoding: // - Key: long // - 48 bits index, 16 bits length final long[] keys = new long[SIZE]; final Value[] values = new Value[SIZE]; private class Value { public Value(double min, double max, double sum, long count) { this.min = min; this.max = max; this.sum = sum; this.count = count; } public double min; public double max; public double sum; public long count; } // int debug_size = 0; // int debug_reprobeMax = 0; public PrivateHashMap() { } public void add(MemorySegment file, long pos, int len, double val) { int hashCode = calculateHash(file, pos, len); doAdd(file, hashCode, pos, len, val); } private static int calculateHash(MemorySegment file, long pos, int len) { if (len > 4) { return file.get(INT_UNALIGNED_BIG_ENDIAN, pos) + 31 * len; } else { int hashCode = len; int i = 0; for (; i < len; i++) { int v = file.get(JAVA_BYTE, pos + i); hashCode = 31 * hashCode + v; } return hashCode; } } private void doAdd(MemorySegment file, int hash, long pos, int len, double val) { int slot = hash & MASK; for (var probe = 0; probe < 20000; probe++) { var iSl = ((slot + probe) & MASK); var slotEntry = keys[iSl]; var emtpy = slotEntry == 0; if (emtpy) { long keyInfo = pos << SHIFT_POS | len; keys[iSl] = keyInfo; values[iSl] = new Value(val, val, val, 1); // debug_size++; return; } else if (isSameEntry(file, slotEntry, pos, len)) { var vE = values[iSl]; vE.min = Math.min(vE.min, val); vE.max = Math.max(vE.max, val); vE.sum = vE.sum + val; vE.count++; return; } else { // long keyPos = (slotEntry & MASK_POS) >> SHIFT_POS; // int keyLen = (int) (slotEntry & MASK_LEN); // System.out.println("Colliding " + new String(file.asSlice(pos,len).toArray(ValueLayout.JAVA_BYTE)) + // " with key" + new String(file.asSlice(keyPos,keyLen).toArray(ValueLayout.JAVA_BYTE)) + // " hash " + hash + " slot " + slot + "+" + probe + " at " + iSl); // debug_reprobeMax = Math.max(debug_reprobeMax, probe); } } throw new IllegalStateException("More than 20000 reprobes"); // throw new IllegalStateException("More than 100 reprobes: At " + debug_size + ""); } private boolean isSameEntry(MemorySegment file, long slotEntry, long pos, int len) { long keyPos = (slotEntry & MASK_POS) >> SHIFT_POS; int keyLen = (int) (slotEntry & MASK_LEN); var isSame = len == keyLen && isSame(file, keyPos, pos, len); return isSame; } private static boolean isSame(MemorySegment file, long i1, long i2, int len) { int i = 0; var i1len = i1 + vecLen; var i2len = i2 + vecLen; if (len < vecLen && i1len <= file.byteSize() && i2len <= file.byteSize()) { var v1 = byteVec.fromMemorySegment(file, i1, ByteOrder.nativeOrder()); var v2 = byteVec.fromMemorySegment(file, i2, ByteOrder.nativeOrder()); var isTrue = v1.compare(VectorOperators.EQ, v2, allTrue.indexInRange(0, len)); return isTrue.trueCount() == len; } while (8 < (len - i)) { var v1 = file.get(JAVA_LONG_UNALIGNED, i1 + i); var v2 = file.get(JAVA_LONG_UNALIGNED, i2 + i); if (v1 != v2) { return false; } i += 8; } while (i < len) { var v1 = file.get(JAVA_BYTE, i1 + i); var v2 = file.get(JAVA_BYTE, i2 + i); if (v1 != v2) { return false; } i++; } return true; } public void fillMerge(MemorySegment file, TreeMap treeMap) { for (int i = 0; i < keys.length; i++) { var ji = i; long keyE = keys[ji]; if (keyE != 0) { long keyPos = (keyE & MASK_POS) >> SHIFT_POS; int keyLen = (int) (keyE & MASK_LEN); byte[] keyBytes = new byte[keyLen]; MemorySegment.copy(file, JAVA_BYTE, keyPos, keyBytes, 0, keyLen); var key = new String(keyBytes); var vE = values[ji]; var min = vE.min; var max = vE.max; var sum = vE.sum; var count = vE.count; treeMap.compute(key, (k, e) -> { if (e == null) { return new ResultRow(min, max, sum, count); } else { return new ResultRow(Math.min(e.min, min), Math.max(e.max, max), e.sum + sum, e.count + count); } }); } } } // public String debugPrint(MemorySegment file) { // StringBuilder b = new StringBuilder(); // for (int i = 0; i < keyValues.length / 5; i++) { // var ji = i * 5; // long keyE = keyValues[ji]; // if (keyE != 0) { // long keyPos = (keyE & MASK_POS) >> SHIFT_POS; // int keyLen = (int) (keyE & MASK_LEN); // byte[] keyBytes = new byte[keyLen]; // MemorySegment.copy(file, JAVA_BYTE, keyPos, keyBytes, 0, keyLen); // var key = new String(keyBytes); // var min = longBitsToDouble(keyValues[ji + 1]); // var max = longBitsToDouble(keyValues[ji + 2]); // var sum = longBitsToDouble(keyValues[ji + 3]); // var count = keyValues[ji + 4]; // b.append("{").append(key).append("@").append(ji) // .append(",").append(min) // .append(",").append(max) // .append(",").append(sum) // .append(",").append(count).append("},"); // } // } // return b.toString(); // } } record Section(long start, long end) { } private static record ResultRow(double min, double max, double sum, long count) { public String toString() { return round(min) + "/" + round(((Math.round(sum * 10.0) / 10.0) / count)) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } ; } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gauravdeshmukh.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_gauravdeshmukh { private static final String FILE = "./measurements.txt"; private static final byte NEGATIVE_SIGN_BYTE = 0x2D; private static final byte DOT_BYTE = 0x2E; private static final int SEARCH_SPACE_BUFFER_SIZE = 140; private static final long SEMI_COLON_MASK = 0x3B3B3B3B3B3B3B3BL; private static final long EOL_MASK = 0x0A0A0A0A0A0A0A0AL; private static class ByteString { final private String string; final private int staticHashCode; public ByteString(byte[] bytes) { this.string = new String(bytes, StandardCharsets.UTF_8); this.staticHashCode = this.string.hashCode(); } public byte[] getBytes() { return string.getBytes(StandardCharsets.UTF_8); } @Override public boolean equals(Object bs) { return this.string.equals(bs.toString()); } @Override public int hashCode() { return staticHashCode; } @Override public String toString() { return this.string; } } private static class Measurement { public ByteString station; public int value; public Measurement(ByteString station, int value) { this.station = station; this.value = value; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append(station.toString()); sb.append(";"); sb.append(value); return sb.toString(); } } private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private int sum; private long count; public String toString() { return round(min / 10.0) + "/" + round(sum * 1.0 / 10.0 / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } public static void main(String[] args) throws Exception { // long st = System.currentTimeMillis(); int cores = 1; File file = new File(FILE); long fileSize = file.length(); if (fileSize > 1048576) { cores = Runtime.getRuntime().availableProcessors(); } long chunkSize = fileSize / cores; ExecutorService executorService = Executors.newFixedThreadPool(cores); List callableTasks = new ArrayList<>(cores); RandomAccessFile raf = new RandomAccessFile(file, "r"); long end = chunkSize, start = 0; for (int i = 0; i < cores; i++) { if (i < cores - 1) { MappedByteBuffer mbb = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, end, Math.min(SEARCH_SPACE_BUFFER_SIZE, fileSize - end)); int eolIndex = -1; int extraBytes = 0; while (true) { long word; try { word = mbb.getLong(); } catch (java.nio.BufferUnderflowException ex) { byte[] remainingBytes = ByteBuffer.allocate(8).putLong(0).array(); mbb.get(mbb.position(), remainingBytes, 0, mbb.remaining()); word = ByteBuffer.wrap(remainingBytes).getLong(); } eolIndex = findEolInLong(word); if (eolIndex > -1) { extraBytes = extraBytes + eolIndex + 1; break; } extraBytes += 8; } end = end + extraBytes; } callableTasks.add(new ParallelFileReaderTask(start, (end - start), raf.getChannel().map(FileChannel.MapMode.READ_ONLY, start, (end - start)))); start = end; end = Math.min(end + chunkSize, fileSize - 1); } List>> futures = executorService.invokeAll(callableTasks); List> resultList = new ArrayList<>(futures.size()); for (Future> future : futures) { resultList.add(future.get()); } Map resultMap = new TreeMap<>(); for (Map map : resultList) { for (Map.Entry entry : map.entrySet()) { MeasurementAggregator agg = resultMap.get(entry.getKey().toString()); if (agg == null) { agg = new MeasurementAggregator(); resultMap.put(entry.getKey().toString(), agg); } agg.min = Math.min(agg.min, entry.getValue().min); agg.max = Math.max(agg.max, entry.getValue().max); agg.sum = agg.sum + entry.getValue().sum; agg.count = agg.count + entry.getValue().count; } } System.out.println(resultMap); executorService.shutdown(); // System.out.println("Time taken: " + (System.currentTimeMillis() - st)); } private static int findEolInLong(long word) { return findPositionInLong(word, EOL_MASK); } private static int findSemiColonInLong(long word) { return findPositionInLong(word, SEMI_COLON_MASK); } private static int findPositionInLong(long word, long searchMask) { long maskedWord = word ^ searchMask; long tmp = (maskedWord - 0x0101010101010101L) & ~maskedWord & 0x8080808080808080L; return tmp == 0 ? -1 : (Long.numberOfLeadingZeros(tmp) >>> 3); } private static class ParallelFileReaderTask implements Callable> { private long start; private int size; private MappedByteBuffer mbf; byte[] bytes; private static final int BATCH_READ_SIZE = 64; Map map; public ParallelFileReaderTask(long start, long size, MappedByteBuffer mbf) { this.start = start; this.size = (int) size; this.mbf = mbf; this.bytes = new byte[BATCH_READ_SIZE]; this.map = new HashMap<>(10000); } @Override public Map call() throws Exception { int bytesReadTillNow = 0; int startOfStation = 0, startOfNumber = -1, endOfStation = -1, endOfNumber = -1; boolean isLastRead = false; try { while (bytesReadTillNow < this.size) { int semiColonIndex = -1; while (semiColonIndex == -1 && bytesReadTillNow < this.size) { long currentWord; try { currentWord = mbf.getLong(); } catch (java.nio.BufferUnderflowException ex) { int remainingBytesCount = this.size - bytesReadTillNow; byte[] remainingBytes = ByteBuffer.allocate(8).putLong(0).array(); mbf.get(bytesReadTillNow, remainingBytes, 0, remainingBytesCount); currentWord = ByteBuffer.wrap(remainingBytes).getLong(); } semiColonIndex = findSemiColonInLong(currentWord); if (semiColonIndex > -1) { endOfStation = bytesReadTillNow + semiColonIndex; startOfNumber = bytesReadTillNow + semiColonIndex + 1; mbf.position(startOfNumber); bytesReadTillNow += semiColonIndex + 1; } else { bytesReadTillNow += 8; } } int stationLength = endOfStation - startOfStation; byte[] stationBytes = new byte[stationLength]; mbf.get(startOfStation, stationBytes, 0, stationLength); int eolIndex = -1; while (eolIndex == -1 && bytesReadTillNow < this.size) { long currentWord; try { currentWord = mbf.getLong(); } catch (java.nio.BufferUnderflowException ex) { int remainingBytesCount = this.size - bytesReadTillNow; byte[] remainingBytes = ByteBuffer.allocate(8).putLong(0).array(); mbf.get(bytesReadTillNow, remainingBytes, 0, remainingBytesCount); currentWord = ByteBuffer.wrap(remainingBytes).getLong(); isLastRead = true; } eolIndex = findEolInLong(currentWord); if (eolIndex > -1) { endOfNumber = bytesReadTillNow + eolIndex; startOfStation = bytesReadTillNow + eolIndex + 1; mbf.position(startOfStation); bytesReadTillNow += eolIndex + 1; } else { bytesReadTillNow += 8; } if (isLastRead) { bytesReadTillNow = this.size; if (eolIndex == -1) { endOfNumber = this.size; } } } int numberLength = endOfNumber - startOfNumber; byte[] numberBytes = new byte[numberLength]; mbf.get(startOfNumber, numberBytes, 0, numberLength); Measurement measurement = new Measurement(new ByteString(stationBytes), getIntegerFromTemperatureBytes(numberBytes)); MeasurementAggregator aggregator = this.map.get(measurement.station); if (aggregator == null) { aggregator = new MeasurementAggregator(); this.map.put(measurement.station, aggregator); } aggregator.min = Math.min(aggregator.min, measurement.value); aggregator.max = Math.max(aggregator.max, measurement.value); aggregator.sum += measurement.value; aggregator.count++; } } catch (Exception ex) { throw ex; } return this.map; } private int getIntegerFromTemperatureBytes(byte[] numberBytes) { int firstDigitIndex = (numberBytes[0] ^ NEGATIVE_SIGN_BYTE) == 0 ? 1 : 0; int ret = 0; for (int i = firstDigitIndex; i < numberBytes.length; i++) { if ((numberBytes[i] ^ DOT_BYTE) != 0) { ret = (ret << 3) + (ret << 1) + ((int) numberBytes[i] - 48); } } return (firstDigitIndex > 0) ? -ret : ret; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gigiblender.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.TreeMap; public class CalculateAverage_gigiblender { private static final int AVAIL_CORES = Runtime.getRuntime().availableProcessors(); private static final HashTable[] tables = new HashTable[AVAIL_CORES]; private static Unsafe unsafe; static { Field theUnsafe = null; try { theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); unsafe = (Unsafe) theUnsafe.get(Unsafe.class); } catch (IllegalAccessException | NoSuchFieldException ignored) { } } private static final String FILE = "./measurements.txt"; static class HashTable { // 10_000 unique hashes -> private static final int ENTRY_SIZE = 32; private static final int NUM_ENTRIES = 16384; private static final int DATA_SIZE = NUM_ENTRIES * ENTRY_SIZE; /* * data[i -> i + 7] = 8 bytes hash * data[i + 8 -> i + 15] = 7 bytes masked address of the string in the file. 1 byte for the length of the string * data[i + 16 -> i + 19] = 4 bytes count * data[i + 20 -> i + 21] = 2 bytes max * data[i + 22 -> i + 23] = 2 bytes min -- sign preserved * data[i + 24 -> i + 31] = 8 bytes sum */ byte[] data; private static final int HASH_OFFSET = 0; private static final int ADDR_OFFSET = 8; private static final long ADDR_MASK = 0x00FFFFFFFFFFFFFFL; private static final int STRING_LENGTH_SHIFT = 56; private static final int COUNT_OFFSET = 16; private static final int SUM_OFFSET = 24; private int reprobe_count; public HashTable() { data = new byte[DATA_SIZE]; // reprobe_count = 0; } private long string_addr_and_length(long hash) { return unsafe.getLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + hash + ADDR_OFFSET); } private static long string_addr(long encoded_str_addr) { return (encoded_str_addr & ADDR_MASK); } private static long string_length(long encoded_str_addr) { return encoded_str_addr >>> STRING_LENGTH_SHIFT; } private long count_max_min(long hash) { return unsafe.getLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + hash + COUNT_OFFSET); } private static short mask_min(long count_max_min) { // Preserve the sign return (short) (count_max_min >> 6 * Byte.SIZE); } private static short mask_max(long count_max_min) { return (short) (count_max_min >>> 4 * Byte.SIZE); } private static int mask_count(long count_max_min) { return (int) count_max_min; } private static long encode_count_max_min(int count, short max, short min) { return ((long) count) | ((((long) max) & 0xFFFF) << 4 * Byte.SIZE) | (((long) min) << 6 * Byte.SIZE); } private long sum(long hash) { return unsafe.getLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + hash + SUM_OFFSET); } private static boolean string_equals(long string_addr, long entry_string_addr, int size_bytes) { int remaining_bytes = size_bytes % 8; int i = 0; for (; i < size_bytes - remaining_bytes; i += 8) { long entry_bytes = unsafe.getLong(entry_string_addr + i); long string_bytes = unsafe.getLong(string_addr + i); if (entry_bytes != string_bytes) { return false; } } // The hash function is not great, so I end up in this case a lot, so I take some risks. // This never caused a SIGSEGV even though it might :) If it does, fall back to the commented version below. // I will try to improve on the hash function if (remaining_bytes != 0) { long entry_bytes = unsafe.getLong(entry_string_addr + i); long string_bytes = unsafe.getLong(string_addr + i); // mask the bytes we care about long mask = (1L << (remaining_bytes * Byte.SIZE)) - 1; entry_bytes &= mask; string_bytes &= mask; return entry_bytes == string_bytes; } // for (; i < size_bytes; i++) { // byte entry_byte = unsafe.getByte(entry_string_addr + i); // byte string_byte = unsafe.getByte(string_addr + i); // if (entry_byte != string_byte) { // return false; // } // } return true; } public void insert(long hash, long string_addr, byte string_size, long final_number) { assert string_addr >>> 56 == 0 : String.format("Expected final 8 bytes to be 0, got %s", Long.toBinaryString(string_addr)); long encoded_string_addr_and_length = string_addr | ((long) string_size << STRING_LENGTH_SHIFT); assert string_addr(encoded_string_addr_and_length) == string_addr : String.format("Expected string addr to be %s, got %s", Long.toHexString(string_addr), Long.toHexString(string_addr(encoded_string_addr_and_length))); assert string_length(encoded_string_addr_and_length) == string_size : String.format("Expected string length to be %s, got %s", string_size, string_length(encoded_string_addr_and_length)); long map_entry = apply_mask(hash * ENTRY_SIZE); while (true) { int entry_count0 = unsafe.getInt(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + map_entry + COUNT_OFFSET); if (entry_count0 == 0) { // dump_insert(map_entry, hash, string_addr, string_size, final_number); // Found an empty slot. Insert the entry here unsafe.putLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + map_entry + HASH_OFFSET, hash); unsafe.putLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + map_entry + ADDR_OFFSET, encoded_string_addr_and_length); unsafe.putLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + map_entry + COUNT_OFFSET, encode_count_max_min(1, (short) final_number, (short) final_number)); unsafe.putLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + map_entry + SUM_OFFSET, final_number); assert mask_count(encode_count_max_min(1, (short) final_number, (short) final_number)) == 1 : String.format("Expected count to be 1, got %s", Integer.toBinaryString(mask_count(encode_count_max_min(1, (short) final_number, (short) final_number)))); assert mask_max(encode_count_max_min(1, (short) final_number, (short) final_number)) == (short) final_number : String.format("Expected max to be %s, got %s", final_number, Integer.toBinaryString(mask_max(encode_count_max_min(1, (short) final_number, (short) final_number)))); assert mask_min(encode_count_max_min(1, (short) final_number, (short) final_number)) == (short) final_number : String.format("Expected min to be %s, got %s", final_number, Integer.toBinaryString(mask_min(encode_count_max_min(1, (short) final_number, (short) final_number)))); return; } else { // Check if strings match. If yes, update. Otherwise, look for the next available slot long entry_string_addr_and_length = string_addr_and_length(map_entry); long entry_str_size = string_length(entry_string_addr_and_length); if (string_size != entry_str_size) { // Strings are not the same size. Continue looking for the next slot map_entry = apply_mask(map_entry + ENTRY_SIZE); // reprobe_count++; } else { long entry_string_addr = string_addr(entry_string_addr_and_length); if (string_equals(string_addr, entry_string_addr, string_size)) { // Strings are the same. Update the entry long entry_count_max_min = count_max_min(map_entry); int entry_count = mask_count(entry_count_max_min); short entry_max = mask_max(entry_count_max_min); short entry_min = mask_min(entry_count_max_min); entry_count++; assert (int) final_number == final_number : String.format("Expected final number to be an int, got %s", final_number); entry_max = (short) Math.max(entry_max, (int) final_number); entry_min = (short) Math.min(entry_min, (int) final_number); long entry_sum = sum(map_entry); entry_sum += final_number; unsafe.putLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + map_entry + COUNT_OFFSET, encode_count_max_min(entry_count, entry_max, entry_min)); unsafe.putLong(data, Unsafe.ARRAY_BYTE_BASE_OFFSET + map_entry + SUM_OFFSET, entry_sum); return; } else { // Strings are not the same. Continue looking for the next slot map_entry = apply_mask(map_entry + ENTRY_SIZE); // reprobe_count++; } } } } } private static long apply_mask(long hash) { return hash & (DATA_SIZE - 1); } public void update_res(TreeMap result_map) { // System.err.println("Reprobe count: " + reprobe_count); Result r = new Result(); for (int i = 0; i < NUM_ENTRIES; i++) { long entry_addr_offset = (long) i * ENTRY_SIZE; long entry_count_max_min = count_max_min(entry_addr_offset); int entry_count = mask_count(entry_count_max_min); if (entry_count == 0) { continue; } long entry_string_addr_and_length = string_addr_and_length(entry_addr_offset); long entry_string_addr = string_addr(entry_string_addr_and_length); long entry_string_length = string_length(entry_string_addr_and_length); // no reason to copy the byte array twice here but what can you do... byte[] bytes = new byte[(int) entry_string_length]; unsafe.copyMemory(null, entry_string_addr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, entry_string_length); String s = new String(bytes, StandardCharsets.UTF_8); short entry_max = mask_max(entry_count_max_min); short entry_min = mask_min(entry_count_max_min); long entry_sum = sum(entry_addr_offset); Result ret = result_map.putIfAbsent(s, r); if (ret == null) { r.count = entry_count; r.max = entry_max; r.min = entry_min; r.sum = entry_sum; r = new Result(); } else { ret.count += entry_count; ret.max = (short) Math.max(ret.max, entry_max); ret.min = (short) Math.min(ret.min, entry_min); ret.sum += entry_sum; } } } public void dump_insert(long map_entry, long hash, long string_addr, byte string_size, long final_number) { System.out.println("START dump_insert"); System.out.println("Inserting " + final_number + " with hash " + hash); System.out.println("Map entry: " + map_entry); System.out.println("String addr: " + string_addr + " with length " + string_size); dump(string_addr, string_addr + string_size); System.out.println("END dump_insert"); } } static class Result { public int count; public short max; public short min; public long sum; private double round(double value) { return Math.round(value * 10.0) / 10.0; } @Override public String toString() { return round(min / 10.) + "/" + round(sum / (double) (10 * count)) + "/" + round(max / 10.); } } private static void compute_slice(final long base_addr, final long slice_size, final long file_size, final int thread_index) { HashTable my_table; if (!SINGLE_CORE) { my_table = new HashTable(); tables[thread_index] = my_table; } else { if (tables[0] == null) { tables[0] = new HashTable(); } my_table = tables[0]; } long cur_addr = base_addr + (long) thread_index * slice_size; // Lookup the next newline. If thread_index == 0 then start right away if (thread_index != 0) { while (unsafe.getByte(cur_addr) != '\n') { cur_addr++; } cur_addr++; } long end_addr = base_addr + (long) (thread_index + 1) * slice_size; if (thread_index == (AVAIL_CORES - 1)) { // Last thread. We need to read until the end of the file end_addr = base_addr + file_size; } else { // look ahead for the next newline while (unsafe.getByte(end_addr) != '\n') { end_addr++; } end_addr++; } // We now have a well-defined interval [cur_addr, end_addr) to work on long hash = -2346162244362633811L; byte string_size = 0; long string_addr = cur_addr; while (cur_addr < end_addr) { long value_mem = unsafe.getLong(cur_addr); int semicolon_byte_index = get_semicolon_index(value_mem); string_size += (byte) semicolon_byte_index; // dump(cur_addr, cur_addr + semicolon_byte_index); if (semicolon_byte_index != 8) { long value_mem_up_to_semicolon = value_mem & ((1L << (semicolon_byte_index * Byte.SIZE)) - 1); // We have a semicolon, so the hash is complete now. We can construct the number // and insert it into the hash table long start_num_addr = cur_addr + semicolon_byte_index + 1; // Always read the next 8 bytes for the number. It seems that this is faster than // checking if the whole number is in the current 8 bytes and only reading if it is not long number_mem_value = unsafe.getLong(start_num_addr); long number_len_bytes = get_newline_index(number_mem_value); long final_number = extract_number(number_mem_value, number_len_bytes); // 0.2421196 % reprobe rate hash = compute_hash(hash ^ value_mem_up_to_semicolon); // We have the final number now. We can insert it into the hash table my_table.insert(hash, string_addr, string_size, final_number); // Now we can move on to the next line hash = -2346162244362633811L; string_size = 0; cur_addr = start_num_addr + number_len_bytes + 1; string_addr = cur_addr; } else { // No semicolon in the 8 bytes read. Continue reading hash = hash ^ value_mem; cur_addr += 8; } } assert cur_addr == end_addr : String.format("Expected cur_addr to be %s, got %s", end_addr, cur_addr); } private static long extract_number(long number_mem_value, long number_len_bytes) { // Pray for GVN/CSE and Sea of Nodes moving the mess below in the proper places because // I don't want to spend the time to do it properly :) long number_mem_dot_index = get_dot_index(number_mem_value); int fractional_part = get_fractional_part(number_mem_value, number_len_bytes); int sign = get_sign(number_mem_value); int skip_sign = skip_sign(number_mem_value); long number_mem_value_no_sign = number_mem_value >>> (skip_sign << 3); // Two cases: either there's a single digit before the dot, or there's two // Start from the dot index and go backwards long new_number_mem_dot_index = number_mem_dot_index - skip_sign; long read_byte_mask = 0xFFL << ((new_number_mem_dot_index - 1) * Byte.SIZE); long ones = ((number_mem_value_no_sign & read_byte_mask) >>> ((new_number_mem_dot_index - 1) * Byte.SIZE)) - 0x30; // Should be 0 due to the multiplication if there's only one digit before the dot long tens = ((number_mem_value_no_sign & 0xFFL) - 0x30) * (new_number_mem_dot_index - 1); long final_number = (tens * 100 + ones * 10 + fractional_part) * sign; return final_number; } private static int get_fractional_part(long number_mem_value, long number_len_bytes) { return (int) ((number_mem_value >>> ((number_len_bytes - 1) * Byte.SIZE)) & 0xFF) - 0x30; } private static int skip_sign(long number_mem_value) { // return 1 if char is '-', 0 if it is not long diff = (number_mem_value & 0xFF) - 0x2D; long sign = (diff | -diff) >>> 63; return (int) ((sign - 1) * -1); } private static int get_sign(long number_mem_value) { // return 1 if char is not '-', -1 if it is long diff = (number_mem_value & 0xFF) - 0x2D; long sign = (diff | -diff) >>> 63; return (int) (-2 * sign + 1) * -1; } private static long compute_hash(long x) { // Hash burrowed from artsiomkorzun and slightly changed long h = x * -7046029254386353131L; long h1 = h ^ (h >>> 32); h = h ^ (h << 32); return h1 ^ h; } private static void dump(long startAddr, long endAddr) { byte[] bytes = new byte[(int) (endAddr - startAddr)]; unsafe.copyMemory(null, startAddr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, bytes.length); String s = new String(bytes, StandardCharsets.UTF_8); System.out.println(s); // Dump the bytes to binary form for (byte b : bytes) { System.out.print(Integer.toBinaryString(b & 0xFF)); System.out.print(" "); } System.out.println(); // Dump the bytes to hex form for (byte b : bytes) { System.out.print(Integer.toHexString(b & 0xFF)); System.out.print(" "); } System.out.println(); } private static int get_byte_0_index(long value) { long res = (value - 0x0101010101010101L) & (~value & 0x8080808080808080L); res = Long.numberOfTrailingZeros(res) >> 3; return (int) res; } private static int get_dot_index(long value) { long temp = value ^ 0x2E2E2E2E2E2E2E2EL; return get_byte_0_index(temp); } private static int get_newline_index(long value) { long temp = value ^ 0x0A0A0A0A0A0A0A0AL; return get_byte_0_index(temp); } private static int get_semicolon_index(long value) { long temp = value ^ 0x3B3B3B3B3B3B3B3BL; return get_byte_0_index(temp); } private static final boolean SINGLE_CORE = false; public static void main(String[] args) throws IOException, InterruptedException { FileChannel file_channel = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ); long file_size = file_channel.size(); long base_addr = file_channel.map(FileChannel.MapMode.READ_ONLY, 0, file_size, Arena.global()).address(); if (!SINGLE_CORE) { int num_threads = AVAIL_CORES; Thread[] threads = new Thread[num_threads]; for (int i = 0; i < num_threads; i++) { int finalI = i; threads[i] = new Thread(() -> { long slice_size = file_size / AVAIL_CORES; compute_slice(base_addr, slice_size, file_size, finalI); }); threads[i].start(); } TreeMap result_map = new TreeMap<>(); for (int i = 0; i < num_threads; i++) { threads[i].join(); tables[i].update_res(result_map); } System.out.println(result_map); } else { for (int i = 0; i < AVAIL_CORES; i++) { int finalI = i; long slice_size = file_size / AVAIL_CORES; compute_slice(base_addr, slice_size, file_size, finalI); } TreeMap result_map = new TreeMap<>(); tables[0].update_res(result_map); System.out.println(result_map); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_giovannicuccu.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.IntVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; import static java.util.stream.Collectors.*; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.*; /* Solution without unsafe that borrows the ideas of splullara, thomasvue, royvanrijn and merykitty */ public class CalculateAverage_giovannicuccu { private static final String FILE = "./measurements.txt"; private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_256; private static final int BYTE_SPECIES_LANES = BYTE_SPECIES.length(); private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder(); public static final VectorSpecies INT_SPECIES = IntVector.SPECIES_256; public static final int INT_SPECIES_LANES = INT_SPECIES.length(); public static final int KEY_SIZE = 128; public static record PartitionBoundary(Path path, long start, long end) { } public static interface PartitionCalculator { List computePartitionsBoundaries(Path path); } public static class ProcessorPartitionCalculator implements PartitionCalculator { public List computePartitionsBoundaries(Path path) { try { int numberOfSegments = Runtime.getRuntime().availableProcessors(); long fileSize = path.toFile().length(); long segmentSize = fileSize / numberOfSegments; List segmentBoundaries = new ArrayList<>(numberOfSegments); try (RandomAccessFile randomAccessFile = new RandomAccessFile(path.toFile(), "r")) { long segStart = 0; long segEnd = segmentSize; for (int i = 0; i < numberOfSegments; i++) { segEnd = findEndSegment(randomAccessFile, segEnd, fileSize); segmentBoundaries.add(new PartitionBoundary(path, segStart, segEnd)); segStart = segEnd; segEnd = Math.min(segEnd + segmentSize, fileSize); } } return segmentBoundaries; } catch (IOException e) { throw new RuntimeException(e); } } private long findEndSegment(RandomAccessFile raf, long location, long fileSize) throws IOException { raf.seek(location); while (location < fileSize) { location++; if (raf.read() == 10) break; } return location; } } private static class MeasurementAggregatorVectorized { private int min; private int max; private double sum; private long count; private final int len; private final int hash; private final int offset; private byte[] data; public MeasurementAggregatorVectorized(byte[] data, int offset, int len, int hash, int initialValue) { min = initialValue; max = initialValue; sum = initialValue; count = 1; this.len = len; this.hash = hash; this.offset = offset; this.data = data; } public void add(int value) { if (value < min) { min = value; } if (value > max) { max = value; } sum += value; count++; } public void merge(MeasurementAggregatorVectorized other) { min = Math.min(min, other.min); max = Math.max(max, other.max); sum += other.sum; count += other.count; } @Override public String toString() { return round(min / 10.) + "/" + round(sum / (double) (10 * count)) + "/" + round(max / 10.); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } public int getMin() { return min; } public int getHash() { return hash; } public int getLen() { return len; } public boolean dataEquals(byte[] data, int offset) { return Arrays.equals(this.data, this.offset, this.offset + len, data, offset, offset + len); } public String getName() { return new String(data, offset, len, StandardCharsets.UTF_8); } public int getOffset() { return offset; } public byte[] getData() { return data; } } private static class MeasurementListVectorized { private static final int SIZE = 1024 * 64; private final MeasurementAggregatorVectorized[] measurements = new MeasurementAggregatorVectorized[SIZE]; private final byte[] keyData = new byte[SIZE * KEY_SIZE]; private final MemorySegment dataSegment = MemorySegment.ofArray(keyData); private final byte[] lineData = new byte[SIZE]; private final MemorySegment lineSegment = MemorySegment.ofArray(lineData); public void add(int len, int hash, int value, MemorySegment memorySegment, long offset) { MemorySegment.copy(memorySegment, offset, lineSegment, 0, len); int index = hash & (SIZE - 1); while (measurements[index] != null) { if (measurements[index].getHash() == hash && measurements[index].getLen() == len) { if (Arrays.equals(keyData, index * KEY_SIZE, index * KEY_SIZE + len, lineData, 0, len)) { measurements[index].add(value); return; } } index = (index + 1) & (SIZE - 1); } MemorySegment.copy(memorySegment, offset, dataSegment, (long) index * KEY_SIZE, len); measurements[index] = new MeasurementAggregatorVectorized(keyData, index * KEY_SIZE, len, hash, value); } public void addWithByteVector(ByteVector chunk1, int len, int hash, int value, MemorySegment memorySegment, long offset) { int index = hash & (SIZE - 1); while (measurements[index] != null) { if (measurements[index].getLen() == len && measurements[index].getHash() == hash) { var nodeKey = ByteVector.fromArray(BYTE_SPECIES, keyData, index * KEY_SIZE); long eqMask = chunk1.compare(VectorOperators.EQ, nodeKey).toLong(); long validMask = -1L >>> (64 - len); if ((eqMask & validMask) == validMask) { measurements[index].add(value); return; } } index = (index + 1) & (SIZE - 1); } MemorySegment.copy(memorySegment, offset, dataSegment, (long) index * KEY_SIZE, len); measurements[index] = new MeasurementAggregatorVectorized(keyData, index * KEY_SIZE, len, hash, value); } public void merge(MeasurementAggregatorVectorized measurementAggregator) { int index = measurementAggregator.getHash() & (SIZE - 1); while (measurements[index] != null) { if (measurements[index].getLen() == measurementAggregator.getLen() && measurements[index].getHash() == measurementAggregator.getHash()) { if (measurementAggregator.dataEquals(measurements[index].getData(), measurements[index].getOffset())) { measurements[index].merge(measurementAggregator); return; } } index = (index + 1) & (SIZE - 1); } measurements[index] = measurementAggregator; } public MeasurementAggregatorVectorized[] getMeasurements() { return measurements; } } private static class MMapReaderMemorySegment { private final Path path; private final List boundaries; private final boolean serial; private static final ValueLayout.OfLong JAVA_LONG_LT = ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN); public MMapReaderMemorySegment(Path path, PartitionCalculator partitionCalculator, boolean serial) { this.path = path; this.serial = serial; boundaries = partitionCalculator.computePartitionsBoundaries(path); } public TreeMap elaborate() throws IOException { try (ExecutorService executor = Executors.newFixedThreadPool(boundaries.size()); FileChannel fileChannel = (FileChannel) Files.newByteChannel((path), StandardOpenOption.READ); var arena = Arena.ofShared()) { List> futures = new ArrayList<>(); for (PartitionBoundary boundary : boundaries) { if (serial) { FutureTask future = new FutureTask<>(() -> computeListForPartition( fileChannel, boundary)); future.run(); futures.add(future); } else { Future future = executor.submit(() -> computeListForPartition( fileChannel, boundary)); futures.add(future); } } TreeMap ris = reduce(futures); return ris; } } private TreeMap reduce(List> futures) { try { TreeMap risMap = new TreeMap<>(); MeasurementListVectorized ris = new MeasurementListVectorized(); for (Future future : futures) { MeasurementListVectorized results = future.get(); merge(ris, results); } for (MeasurementAggregatorVectorized m : ris.getMeasurements()) { if (m != null) { risMap.put(m.getName(), m); } } return risMap; } catch (InterruptedException | ExecutionException ie) { System.err.println(ie); throw new RuntimeException(ie); } } private void merge(MeasurementListVectorized result, MeasurementListVectorized partial) { for (MeasurementAggregatorVectorized m : partial.getMeasurements()) { if (m != null) { result.merge(m); } } } private final long ALL_ONE = -1L; private static final long DELIMITER_MASK = 0x3B3B3B3B3B3B3B3BL; private static final byte SEPARATOR = ';'; private final static ByteVector SEPARATORS = ByteVector.broadcast(BYTE_SPECIES, SEPARATOR); private MeasurementListVectorized computeListForPartition(FileChannel fileChannel, PartitionBoundary boundary) { try (var arena = Arena.ofConfined()) { var memorySegment = fileChannel.map(FileChannel.MapMode.READ_ONLY, boundary.start(), boundary.end() - boundary.start(), arena); MeasurementListVectorized list = new MeasurementListVectorized(); long size = memorySegment.byteSize(); long offset = 0; long safe = size - KEY_SIZE; while (offset < safe) { int len = 0; var line = ByteVector.fromMemorySegment(BYTE_SPECIES, memorySegment, offset, NATIVE_ORDER); len = line.compare(VectorOperators.EQ, SEPARATORS).firstTrue(); if (len == BYTE_SPECIES_LANES) { int position1 = -1; int incr = BYTE_SPECIES_LANES; while (position1 == -1) { long readBuffer = memorySegment.get(JAVA_LONG_LT, offset + incr); long comparisonResult1 = (readBuffer ^ DELIMITER_MASK); long highBitMask1 = (comparisonResult1 - 0x0101010101010101L) & (~comparisonResult1 & 0x8080808080808080L); boolean noContent1 = highBitMask1 == 0; position1 = noContent1 ? -1 : Long.numberOfTrailingZeros(highBitMask1) >> 3; len += noContent1 ? 8 : position1; incr += 8; } int hash = hash(memorySegment, offset, len); long prevOffset = offset; offset += len + 1; long numberWord = memorySegment.get(JAVA_LONG_LT, offset); int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000); int value = convertIntoNumber(decimalSepPos, numberWord); offset += (decimalSepPos >>> 3) + 3; list.add(len, hash, value, memorySegment, prevOffset); } else { int hash = hash(memorySegment, offset, len); long prevOffset = offset; offset += len + 1; long numberWord = memorySegment.get(JAVA_LONG_LT, offset); int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000); int value = convertIntoNumber(decimalSepPos, numberWord); offset += (decimalSepPos >>> 3) + 3; list.addWithByteVector(line, len, hash, value, memorySegment, prevOffset); } } while (offset < size) { int len = 0; while (memorySegment.get(ValueLayout.JAVA_BYTE, offset + len) != ';') { len++; } int hash = hash(memorySegment, offset, len); long prevOffset = offset; offset += len + 1; int value = 0; if (offset < size - 8) { long numberWord = memorySegment.get(JAVA_LONG_LT, offset); int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000); value = convertIntoNumber(decimalSepPos, numberWord); offset += (decimalSepPos >>> 3) + 3; } else { long currentPosition = offset; int sign = 1; byte b = memorySegment.get(ValueLayout.JAVA_BYTE, currentPosition++); if (b == '-') { sign = -1; } else { value = b - '0'; } while ((b = memorySegment.get(ValueLayout.JAVA_BYTE, currentPosition++)) != '.') { value = value * 10 + (b - '0'); } b = memorySegment.get(ValueLayout.JAVA_BYTE, currentPosition); value = value * 10 + (b - '0'); if (sign == -1) { value = -value; } offset = currentPosition + 2; } list.add(len, hash, value, memorySegment, prevOffset); } return list; } catch (IOException e) { throw new RuntimeException(e); } } private static final int GOLDEN_RATIO = 0x9E3779B9; private static final int HASH_LROTATE = 5; private static int hash(MemorySegment memorySegment, long start, int len) { int x; int y; if (len >= Integer.BYTES) { x = memorySegment.get(ValueLayout.JAVA_INT_UNALIGNED, start); y = memorySegment.get(ValueLayout.JAVA_INT_UNALIGNED, start + len - Integer.BYTES); } else { x = memorySegment.get(ValueLayout.JAVA_BYTE, start); y = memorySegment.get(ValueLayout.JAVA_BYTE, start + len - Byte.BYTES); } return (Integer.rotateLeft(x * GOLDEN_RATIO, HASH_LROTATE) ^ y) * GOLDEN_RATIO; } private static int convertIntoNumber(int decimalSepPos, long numberWord) { int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise long signed = (~numberWord << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii code // to actual digit value in each byte long digits = ((numberWord & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + // 0x00UU00TTHH000000 * 10 + // 0xUU00TTHH00000000 * 100 // Now TT * 100 has 2 trailing zeroes and HH * 100 + TT * 10 + UU < 0x400 // This results in our value lies in the bit 32 to 41 of this product // That was close :) long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; long value = (absValue ^ signed) - signed; return (int) value; } } public static void main(String[] args) throws IOException { MMapReaderMemorySegment reader = new MMapReaderMemorySegment(Paths.get(FILE), new ProcessorPartitionCalculator(), false); Map measurements = reader.elaborate(); System.out.println(measurements); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gnabyl.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; public class CalculateAverage_gnabyl { private static final String FILE = "./measurements.txt"; private static final int NB_CHUNKS = Runtime.getRuntime().availableProcessors(); private static Map stationNameMap = new ConcurrentHashMap<>(10000, 0.9f, NB_CHUNKS); private static record Chunk(int bytesCount, MappedByteBuffer mappedByteBuffer) { } private static int reduceSizeToFitLineBreak(FileChannel channel, long startPosition, int startSize) throws IOException { long currentPosition = startPosition + startSize - 1; int realSize = startSize; if (currentPosition >= channel.size()) { currentPosition = channel.size() - 1; realSize = (int) (currentPosition - startPosition); } while (currentPosition >= startPosition) { channel.position(currentPosition); byte byteValue = channel.map(FileChannel.MapMode.READ_ONLY, currentPosition, 1).get(); if (byteValue == '\n') { // found line break break; } realSize--; currentPosition--; } return realSize; } private static List readChunks(int nbChunks) throws IOException { RandomAccessFile file = new RandomAccessFile(FILE, "rw"); List res = new ArrayList<>(nbChunks); FileChannel channel = file.getChannel(); long bytesCount = channel.size(); long bytesPerChunk = bytesCount / nbChunks; // Memory map the file in read-only mode // TODO: Optimize using threads long currentPosition = 0; int startSize; int realSize; for (int i = 0; i < nbChunks; i++) { startSize = (int) bytesPerChunk; realSize = startSize; if (i == nbChunks - 1) { realSize = (int) (bytesCount - currentPosition); MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, currentPosition, realSize); res.add(new Chunk(realSize, mappedByteBuffer)); break; } // Adjust size so that it ends on a newline realSize = reduceSizeToFitLineBreak(channel, currentPosition, startSize); MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, currentPosition, realSize); res.add(new Chunk(realSize, mappedByteBuffer)); currentPosition += realSize; } channel.close(); file.close(); return res; } private static class StationData { private float sum, min, max; private int count; public StationData(float value) { this.count = 1; this.sum = value; this.min = value; this.max = value; } public void update(float value) { this.count++; this.sum += value; this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); } public float getMean() { return sum / count; } public float getMin() { return min; } public float getMax() { return max; } public void mergeWith(StationData other) { this.sum += other.sum; this.count += other.count; this.min = Math.min(this.min, other.min); this.max = Math.max(this.max, other.max); } } static float round(float value) { return Math.round(value * 10.0f) * 0.1f; } private static class ChunkResult { private Map data; public ChunkResult() { data = new HashMap<>(); } public StationData getData(int hash) { return data.get(hash); } public void addStation(int hash, float value) { this.data.put(hash, new StationData(value)); } public void print() { PrintWriter out = new PrintWriter(System.out); out.println( this.data.keySet().parallelStream() .map(hash -> { var stationData = data.get(hash); var name = stationNameMap.get(hash); return String.format("%s=%.1f/%.1f/%.1f", name, round(stationData.getMin()), round(stationData.getMean()), round(stationData.getMax())); }) .sorted((a, b) -> a.split("=")[0].compareTo(b.split("=")[0])) .collect(Collectors.joining(", ", "{", "}"))); out.flush(); } public void mergeWith(ChunkResult other) { for (Map.Entry entry : other.data.entrySet()) { int stationName = entry.getKey(); StationData otherStationData = entry.getValue(); StationData thisStationData = this.data.get(stationName); if (thisStationData == null) { this.data.put(stationName, otherStationData); } else { thisStationData.mergeWith(otherStationData); } } } } private static ChunkResult processChunk(Chunk chunk) { ChunkResult result = new ChunkResult(); // Perform processing on the chunk data byte[] data = new byte[chunk.bytesCount()]; chunk.mappedByteBuffer().get(data); // Process each line float value; int iSplit, iEol; StationData stationData; int negative; int hash, prime = 31; Set seenHashes = new HashSet<>(10000, 0.9f); for (int offset = 0; offset < data.length; offset++) { // Find station name hash = 0; for (iSplit = offset; data[iSplit] != ';'; iSplit++) { hash = (hash << 5) - hash + (data[iSplit] & 0xFF); } if (!seenHashes.contains(hash)) { seenHashes.add(hash); stationNameMap.put(hash, new String(data, offset, iSplit - offset, StandardCharsets.UTF_8)); } // Find value iSplit++; negative = 1; value = 0; for (iEol = iSplit; data[iEol] != '\n'; iEol++) { if (data[iEol] == '-') { negative = -1; continue; } if (data[iEol] == '.') { value = value + (data[iEol + 1] - 48) * 0.1f; iEol += 2; break; } value = value * 10 + data[iEol] - 48; } value *= negative; // Init & count stationData = result.getData(hash); if (stationData == null) { result.addStation(hash, value); } else { stationData.update(value); } offset = iEol; } return result; } private static ChunkResult processAllChunks(List chunks) throws InterruptedException, ExecutionException { return chunks.parallelStream().map(CalculateAverage_gnabyl::processChunk).collect(ChunkResult::new, ChunkResult::mergeWith, ChunkResult::mergeWith); } public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { var chunks = readChunks(NB_CHUNKS); var result = processAllChunks(chunks); result.print(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gnmathur.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import static java.util.concurrent.Executors.newFixedThreadPool; /** * This solution uses an imperative approach. There's essentially two elements to performance tuning attempted in this * solution: * 1. Use a thread pool to process the file in chunks, setting it to the number of available processors * 2. Use a memory mapped file to read the file * * On an Intel(R) Core(TM) i9-10920X CPU @ 3.50GHz its taking around 38 seconds to aggregate the measurements. */ public class CalculateAverage_gnmathur { private static final ExecutorService es = newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private static final Map result = new ConcurrentHashMap<>(); private static final String FILE_NAME = "measurements.txt"; private static final long CHUNK_SIZE = 1024 * 1024 * 1024; // 1 GB public static final class Measurement { private double max = -5000; // impossibly low private double min = 5000; // impossibly high private double sum = 0; private double count = 0; public Measurement() { } public synchronized void addReading(double reading) { this.min = Math.min(this.min, reading); this.max = Math.max(this.max, reading); this.sum += reading; this.count++; } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } @Override public String toString() { double mean = sum / count; return round(min) + "/" + round(mean) + "/" + round(max); } } private static void updateMeasurement(String line) { String[] parts = line.split(";"); String station = parts[0]; double measurement = Double.parseDouble(parts[1]); Measurement m = null; if (result.containsKey(station)) { m = result.get(station); } else { m = new Measurement(); result.put(station, m); } m.addReading(measurement); } private record FileChunkProcessor(String fileName, ExecutorService es, long start, long end) implements Runnable { @Override public void run() { // Process a chunk of the file try (RandomAccessFile fp = new RandomAccessFile(FILE_NAME, "r"); FileChannel fpChannel = fp.getChannel()) { final MappedByteBuffer mappedByteBuffer = fpChannel.map(FileChannel.MapMode.READ_ONLY, start, end - start); final ByteBuffer bb = ByteBuffer.allocate(1024); while (mappedByteBuffer.hasRemaining()) { byte b = mappedByteBuffer.get(); if (b == '\n') { // We have read a line. Convert the tempBuffer to a string and process it bb.flip(); String line = StandardCharsets.UTF_8.decode(bb).toString(); updateMeasurement(line); bb.clear(); } else { bb.put(b); } } // There should be nothing in the byte buffer at this point if (bb.position() > 0) { throw new RuntimeException("byte buffer not empty"); } } catch (IOException e) { throw new RuntimeException(e); } } } // We want to make sure that we don't split the data in the middle of a line. So we find the closest next // newline character and adjust the end to that. public static long adjustEnd(final String fileName, long end) throws IOException { try (RandomAccessFile raf = new RandomAccessFile(fileName, "r")) { raf.seek(end); while (true) { int read = raf.read(); if (read == -1 || read == '\n') { break; } end++; } } return end; } // Read the file in chunks and submit each chunk to a thread for processing public static void readChunked(final String fileName, final long chunkSize) { try (RandomAccessFile fp = new RandomAccessFile(fileName, "r")) { long fileSize = fp.length(); long start = 0; while (start < fileSize) { long end = Math.min(start + chunkSize, fileSize); end = adjustEnd(fileName, end); es.submit(new FileChunkProcessor(fileName, es, start, end)); start = end + 1; } es.shutdown(); // Wait for all the threads to finish processing es.awaitTermination(4, java.util.concurrent.TimeUnit.MINUTES); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } } public static void main(String[] args) { readChunked(FILE_NAME, CHUNK_SIZE); // Print the results System.out.println(new TreeMap<>(result)); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_godofwharf.java ================================================ package dev.morling.onebrc; /* * Copyright 2023 The original authors * * 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. */ import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.Vector; import jdk.incubator.vector.VectorSpecies; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.management.ManagementFactory; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.BiConsumer; import java.util.stream.IntStream; import static java.nio.charset.StandardCharsets.UTF_8; public class CalculateAverage_godofwharf { private static final String FILE = "./measurements.txt"; private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("debug", "false")); private static final int NCPU = Runtime.getRuntime().availableProcessors(); private static final VectorSpecies PREFERRED_SPECIES = VectorSpecies.ofPreferred(byte.class); private static final Vector NEW_LINE_VEC = PREFERRED_SPECIES.broadcast('\n'); // This array is used for quick conversion of fractional part private static final double[] DOUBLES = new double[]{ 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 }; // This array is used for quick conversion from ASCII to digit private static final int[] DIGIT_LOOKUP = new int[]{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1 }; private static final int MAX_STR_LEN = 108; private static final int DEFAULT_HASH_TBL_SIZE = 4096; private static final int DEFAULT_PAGE_SIZE = 8_388_608; // 8 MB private static final int PAGE_SIZE = Integer.parseInt(System.getProperty("pageSize", STR."\{DEFAULT_PAGE_SIZE}")); public static void main(String[] args) throws Exception { long startTimeMs = System.currentTimeMillis(); Map measurements = compute(); long time1 = System.nanoTime(); System.out.println(measurements); printDebugMessage("Print took %d ns%n", (System.nanoTime() - time1)); printDebugMessage("Took %d ms%n", System.currentTimeMillis() - startTimeMs); printDebugMessage("Time spent on GC=%d ms%n", ManagementFactory.getGarbageCollectorMXBeans().get(0).getCollectionTime()); System.exit(0); } private static Map compute() throws Exception { int nThreads = Integer.parseInt( System.getProperty("threads", STR."\{NCPU}")); printDebugMessage("Running program with %d threads %n", nThreads); Job job = new Job(nThreads - 1); job.compute(FILE); return job.sort(); } public static class Job { private final int nThreads; private final State[] threadLocalStates; private final Map globalMap = new ConcurrentHashMap<>(DEFAULT_HASH_TBL_SIZE); private final ExecutorService executorService; public Job(final int nThreads) { this.threadLocalStates = new State[(nThreads << 4)]; IntStream.range(0, nThreads << 4) .forEach(i -> threadLocalStates[i] = new State()); this.nThreads = nThreads; this.executorService = Executors.newFixedThreadPool(nThreads); } public void compute(final String path) throws Exception { // Create a random access file so that we can map the contents of the file into native memory for faster access try (RandomAccessFile file = new RandomAccessFile(path, "r")) { // Create a memory segment for the entire file MemorySegment globalSegment = file.getChannel().map( FileChannel.MapMode.READ_ONLY, 0, file.length(), Arena.global()); long fileLength = file.length(); // Ensure that the split length never exceeds Integer.MAX_VALUE. This is because ByteBuffers cannot // be larger than 2 GiB. int splitLength = (int) Math.min(Integer.MAX_VALUE, Math.max(PAGE_SIZE, Math.rint(fileLength * 1.0 / nThreads))); printDebugMessage("fileLength = %d, splitLength = %d%n", file.length(), splitLength); long time1 = System.nanoTime(); // Break the file into multiple splits. One thread would process one split. // This routine makes sure that the splits are uniformly sized to the best extent possible. // Each split would either end with a '\n' character or EOF List splits = breakFileIntoSplits(file, splitLength, PAGE_SIZE, globalSegment, false); printDebugMessage("Number of splits = %d, splits = [%s]%n", splits.size(), splits); printDebugMessage("Splits calculation took %d ns%n", System.nanoTime() - time1); // consume splits in parallel using the common fork join pool long time = System.nanoTime(); List> futures = new ArrayList<>(splits.size() * 2); splits .forEach(split -> { // process splits concurrently using a thread pool futures.add(executorService.submit(() -> { MemorySegment splitSegment = globalSegment.asSlice(split.offset, split.length); splitSegment.load(); int tid = (int) Thread.currentThread().threadId(); byte[] currentPage = new byte[PAGE_SIZE + MAX_STR_LEN]; // iterate over each page in split for (Page page : split.pages) { // this byte buffer should end with '\n' or EOF MemorySegment segment = globalSegment.asSlice(page.offset, page.length); MemorySegment.copy(segment, ValueLayout.JAVA_BYTE, 0L, currentPage, 0, (int) page.length); SearchResult searchResult = findNewLinesVectorized(currentPage, (int) page.length); int prevOffset = 0; int j = 0; // iterate over search results while (j < searchResult.len) { int curOffset = searchResult.offsets[j]; byte ch1 = currentPage[curOffset - 4]; byte ch2 = currentPage[curOffset - 5]; int temperatureLen = 5; if (ch1 == ';') { temperatureLen = 3; } else if (ch2 == ';') { temperatureLen = 4; } int lineLength = curOffset - prevOffset; int stationLen = lineLength - temperatureLen - 1; byte[] station = new byte[stationLen]; System.arraycopy(currentPage, prevOffset, station, 0, stationLen); int hashcode = Arrays.hashCode(station); double temperature = NumberUtils.parseDouble2(currentPage, prevOffset + stationLen + 1, temperatureLen); Measurement m = new Measurement(station, temperature, hashcode); threadLocalStates[tid].update(m); prevOffset = curOffset + 1; j++; } // Explicitly commented out because unload seems to take a lot of time // segment.unload(); } mergeInternal(threadLocalStates[tid]); })); }); for (Future future : futures) { future.get(); } printDebugMessage("Aggregate took %d ns%n", (System.nanoTime() - time)); } } private void mergeInternal(final State state) { state.state.forEach((k, v) -> { globalMap.compute(k.toString(), (ignored, agg) -> { if (agg == null) { agg = v; } else { agg.merge(v); } return agg; }); }); } public Map sort() { long time = System.nanoTime(); Map sortedMap = new TreeMap<>(globalMap); printDebugMessage("Tree map construction took %d ns%n", (System.nanoTime() - time)); return sortedMap; } private static LineMetadata findNextOccurrenceOfNewLine(final ByteBuffer buffer, final int capacity, final int offset) { int maxLen = capacity - offset; byte[] src = new byte[Math.min(MAX_STR_LEN, maxLen)]; byte[] station = new byte[src.length]; byte[] temperature = new byte[5]; buffer.position(offset); buffer.get(src); int i = 0; int j = 0; int k = 0; boolean isAscii = true; boolean afterDelim = false; int hashCode = 0; for (; i < src.length; i++) { byte b = src[i]; if (b < 0) { isAscii = false; } if (!afterDelim && b != '\n') { if (b == ';') { afterDelim = true; } else { hashCode = hashCode * 31 + b; station[j++] = b; } } else if (b != '\n') { temperature[k++] = b; } else { return new LineMetadata( station, temperature, j, k, offset + i + 1, hashCode, isAscii); } } if (i == 0 & j == 0 && k == 0) { hashCode = -1; } return new LineMetadata( station, temperature, j, k, offset + i, hashCode, isAscii); } private static SearchResult findNewLinesVectorized(final byte[] page, final int pageLen) { SearchResult ret = new SearchResult(new int[pageLen / 5], 0); VectorSpecies species = PREFERRED_SPECIES; int loopBound = pageLen - species.length() * 4; int i = 0; int j = 0; while (j < loopBound) { Vector v1 = ByteVector.fromArray(species, page, j); Vector v2 = ByteVector.fromArray(species, page, j + species.length()); Vector v3 = ByteVector.fromArray(species, page, j + species.length() * 2); Vector v4 = ByteVector.fromArray(species, page, j + species.length() * 3); long l1 = NEW_LINE_VEC.eq(v1).toLong(); long l2 = NEW_LINE_VEC.eq(v2).toLong(); long l3 = NEW_LINE_VEC.eq(v3).toLong(); long l4 = NEW_LINE_VEC.eq(v4).toLong(); long r1 = l1 & 0xFFFFFFFFL | (l2 << species.length()); long r2 = l3 & 0xFFFFFFFFL | (l4 << (species.length())); int b1 = Long.bitCount(r1); int b2 = Long.bitCount(r2); int k = i; int it = b1; while (it > 0) { int idx = Long.numberOfTrailingZeros(r1); ret.offsets[k++] = j + idx; r1 &= (r1 - 1); it--; idx = Long.numberOfTrailingZeros(r1); ret.offsets[k++] = j + idx; r1 &= (r1 - 1); it--; idx = Long.numberOfTrailingZeros(r1); ret.offsets[k++] = j + idx; r1 &= (r1 - 1); it--; idx = Long.numberOfTrailingZeros(r1); ret.offsets[k++] = j + idx; r1 &= (r1 - 1); it--; idx = Long.numberOfTrailingZeros(r1); ret.offsets[k++] = j + idx; r1 &= (r1 - 1); it--; idx = Long.numberOfTrailingZeros(r1); ret.offsets[k++] = j + idx; r1 &= (r1 - 1); it--; } i += b1; j += species.length() * 2; k = i; it = b2; while (it > 0) { int idx = Long.numberOfTrailingZeros(r2); ret.offsets[k++] = j + idx; r2 &= (r2 - 1); it--; idx = Long.numberOfTrailingZeros(r2); ret.offsets[k++] = j + idx; r2 &= (r2 - 1); it--; idx = Long.numberOfTrailingZeros(r2); ret.offsets[k++] = j + idx; r2 &= (r2 - 1); it--; idx = Long.numberOfTrailingZeros(r2); ret.offsets[k++] = j + idx; r2 &= (r2 - 1); it--; idx = Long.numberOfTrailingZeros(r2); ret.offsets[k++] = j + idx; r2 &= (r2 - 1); it--; idx = Long.numberOfTrailingZeros(r2); ret.offsets[k++] = j + idx; r2 &= (r2 - 1); it--; } i += b2; j += species.length() * 2; } // tail loop while (j < pageLen) { byte b = page[j]; if (b == '\n') { ret.offsets[i++] = j; } j++; } ret.len = i; return ret; } private static List breakFileIntoSplits(final RandomAccessFile file, final int splitLength, final int pageLength, final MemorySegment memorySegment, final boolean enableChecks) throws IOException { final List splits = new ArrayList<>(); // Try to break the file into multiple splits while ensuring that each split has at least splitLength bytes // and ends with '\n' or EOF for (long i = 0; i < file.length();) { long splitStartOffset = i; long splitEndOffset = Math.min(file.length(), splitStartOffset + splitLength); // not inclusive if (splitEndOffset == file.length()) { // reached EOF List pages = breakSplitIntoPages(splitStartOffset, splitEndOffset, pageLength, memorySegment, enableChecks); splits.add(new Split(splitStartOffset, splitEndOffset - splitStartOffset, pages)); break; } // Look past the end offset to find next '\n' or EOF long segmentLength = Math.min(MAX_STR_LEN, file.length() - i); // Create a new memory segment for reading contents beyond splitEndOffset MemorySegment lookahead = memorySegment.asSlice(splitEndOffset, segmentLength); ByteBuffer bb = lookahead.asByteBuffer(); // Find the next offset which has either '\n' or EOF LineMetadata lineMetadata = findNextOccurrenceOfNewLine(bb, (int) segmentLength, 0); splitEndOffset += lineMetadata.offset; if (enableChecks && memorySegment.asSlice(splitEndOffset - 1, 1).asByteBuffer().get(0) != '\n') { throw new IllegalStateException("Page doesn't end with NL char"); } // Break the split further into multiple pages based on pageLength List pages = breakSplitIntoPages(splitStartOffset, splitEndOffset, pageLength, memorySegment, enableChecks); splits.add(new Split(splitStartOffset, splitEndOffset - splitStartOffset, pages)); i = splitEndOffset; lookahead.unload(); } return splits; } private static List breakSplitIntoPages(final long splitStartOffset, final long splitEndOffset, final int pageLength, final MemorySegment memorySegment, final boolean enableChecks) { List pages = new ArrayList<>(); for (long i = splitStartOffset; i < splitEndOffset;) { long pageStartOffset = i; long pageEndOffset = Math.min(splitEndOffset, pageStartOffset + pageLength); // not inclusive if (pageEndOffset == splitEndOffset) { pages.add(new Page(pageStartOffset, pageEndOffset - pageStartOffset)); break; } // Look past the end offset to find next '\n' till we reach the end of split long lookaheadLength = Math.min(MAX_STR_LEN, splitEndOffset - i); MemorySegment lookahead = memorySegment.asSlice(pageEndOffset, lookaheadLength); ByteBuffer bb = lookahead.asByteBuffer(); // Find next offset which has either '\n' or the end of split LineMetadata lineMetadata = findNextOccurrenceOfNewLine(bb, (int) lookaheadLength, 0); pageEndOffset += lineMetadata.offset; if (enableChecks && memorySegment.asSlice(pageEndOffset - 1, 1).asByteBuffer().get(0) != '\n') { throw new IllegalStateException("Page doesn't end with NL char"); } pages.add(new Page(pageStartOffset, pageEndOffset - pageStartOffset)); i = pageEndOffset; lookahead.unload(); } return pages; } } public static class State { private final Map state; public State() { this.state = new HashMap<>(DEFAULT_HASH_TBL_SIZE); // insert a DUMMY key to prime the hashmap for usage AggregationKey dummy = new AggregationKey("DUMMY".getBytes(UTF_8), -1); this.state.put(dummy, null); this.state.remove(dummy); } public void update(final Measurement m) { MeasurementAggregator agg = state.get(m.aggregationKey); if (agg == null) { state.put(m.aggregationKey, new MeasurementAggregator(m.temperature, m.temperature, m.temperature, 1L)); return; } agg.count++; agg.min = m.temperature <= agg.min ? m.temperature : agg.min; agg.max = m.temperature >= agg.max ? m.temperature : agg.max; agg.sum += m.temperature; } public static class AggregationKey { private final byte[] station; private final int hashCode; public AggregationKey(final byte[] station, final int hashCode) { this.station = station; this.hashCode = hashCode; } @Override public String toString() { return new String(station, UTF_8); } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object other) { if (!(other instanceof AggregationKey)) { return false; } AggregationKey sk = (AggregationKey) other; return station.length == sk.station.length && Arrays.mismatch(station, sk.station) < 0; } } } public static class MeasurementAggregator { private double min; private double max; private double sum; private long count; public MeasurementAggregator(final double min, final double max, final double sum, final long count) { this.min = min; this.max = max; this.sum = sum; this.count = count; } public String toString() { double min1 = round(min); double max1 = round(max); double mean = round(round(sum) / count); return min1 + "/" + mean + "/" + max1; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } private void merge(final MeasurementAggregator m2) { count += m2.count; min = Math.min(min, m2.min); max = Math.max(max, m2.max); sum += m2.sum; } } public static class NumberUtils { public static int toDigit(final char c) { return DIGIT_LOOKUP[c]; } public static int fastMul10(final int i) { return (i << 1) + (i << 3); } public static double parseDouble2(final byte[] b, final int offset, final int len) { try { char ch0 = (char) b[offset]; char ch1 = (char) b[offset + 1]; char ch2 = (char) b[offset + 2]; char ch3 = len > 3 ? (char) b[offset + 3] : ' '; char ch4 = len > 4 ? (char) b[offset + 4] : ' '; if (len == 3) { int decimal = toDigit(ch0); double fractional = DOUBLES[toDigit(ch2)]; return decimal + fractional; } else if (len == 4) { // -1.2 or 11.2 int decimal = (ch0 == '-' ? toDigit(ch1) : (fastMul10(toDigit(ch0)) + toDigit(ch1))); double fractional = DOUBLES[toDigit(ch3)]; if (ch0 == '-') { return Math.negateExact(decimal) - fractional; } else { return decimal + fractional; } } else { int decimal = fastMul10(toDigit(ch1)) + toDigit(ch2); double fractional = DOUBLES[toDigit(ch4)]; return Math.negateExact(decimal) - fractional; } } catch (ArrayIndexOutOfBoundsException e) { printDebugMessage("Array index out of bounds for string: %s%n", new String(b, 0, len)); throw new RuntimeException(e); } catch (StringIndexOutOfBoundsException e) { printDebugMessage("String index out of bounds for string: %s%n", new String(b, 0, len)); throw new RuntimeException(e); } } } // record classes record Measurement(byte[] station, double temperature, int hash, State.AggregationKey aggregationKey) { public Measurement(byte[] station, double temperature, int hashCode) { this(station, temperature, hashCode, new State.AggregationKey(station, hashCode)); } } record LineMetadata(byte[] station, byte[] temperature, int stationLen, int temperatureLen, int offset, int precomputedHashCode, boolean isAscii) { } record Split(long offset, long length, List pages) { } record Page(long offset, long length) { } public static class SearchResult { private int[] offsets; private int len; public SearchResult(final int[] offsets, final int len) { this.offsets = offsets; this.len = len; } } private static void printDebugMessage(final String message, final Object... args) { if (DEBUG) { System.err.printf(message, args); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gonix.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.Stream; public class CalculateAverage_gonix { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException { var file = new RandomAccessFile(FILE, "r"); var res = buildChunks(file).stream().parallel() .flatMap(chunk -> new Aggregator().processChunk(chunk).stream()) .collect(Collectors.toMap( Aggregator.Entry::getKey, Aggregator.Entry::getValue, Aggregator.Entry::add, TreeMap::new)); System.out.println(res); System.out.close(); } private static List buildChunks(RandomAccessFile file) throws IOException { var fileSize = file.length(); var chunkSize = Math.min(Integer.MAX_VALUE - 512, fileSize / Runtime.getRuntime().availableProcessors()); if (chunkSize <= 0) { chunkSize = fileSize; } var chunks = new ArrayList((int) (fileSize / chunkSize) + 1); var start = 0L; while (start < fileSize) { var pos = start + chunkSize; if (pos < fileSize) { file.seek(pos); while (file.read() != '\n') { pos += 1; } pos += 1; } else { pos = fileSize; } var buf = file.getChannel().map(FileChannel.MapMode.READ_ONLY, start, pos - start); buf.order(ByteOrder.nativeOrder()); chunks.add(buf); start = pos; } return chunks; } private static class Aggregator { private static final int MAX_STATIONS = 10_000; private static final int MAX_STATION_SIZE = Math.ceilDiv(100, 8) + 5; private static final int INDEX_SIZE = 1024 * 1024; private static final int INDEX_MASK = INDEX_SIZE - 1; private static final int FLD_COUNT = 0; private static final int FLD_SUM = 1; private static final int FLD_MIN = 2; private static final int FLD_MAX = 3; // Poor man's hash map: hash code to offset in `mem`. private final int[] index; // Contiguous storage of key (station name) and stats fields of all // unique stations. // The idea here is to improve locality so that stats fields would // possibly be already in the CPU cache after we are done comparing // the key. private final long[] mem; private int memUsed; Aggregator() { assert ((INDEX_SIZE & (INDEX_SIZE - 1)) == 0) : "INDEX_SIZE must be power of 2"; assert (INDEX_SIZE > MAX_STATIONS) : "INDEX_SIZE must be greater than MAX_STATIONS"; index = new int[INDEX_SIZE]; mem = new long[1 + (MAX_STATIONS * MAX_STATION_SIZE)]; memUsed = 1; } Aggregator processChunk(MappedByteBuffer buf) { // To avoid checking if it is safe to read a whole long near the // end of a chunk, we copy last couple of lines to a padded buffer // and process that part separately. int limit = buf.limit(); int pos = Math.max(limit - 16, -1); while (pos >= 0 && buf.get(pos) != '\n') { pos--; } pos++; if (pos > 0) { processChunkLongs(buf, pos); } int tailLen = limit - pos; var tailBuf = ByteBuffer.allocate(tailLen + 8).order(ByteOrder.nativeOrder()); buf.get(pos, tailBuf.array(), 0, tailLen); processChunkLongs(tailBuf, tailLen); return this; } Aggregator processChunkLongs(ByteBuffer buf, int limit) { int pos = 0; while (pos < limit) { int start = pos; long keyLong = buf.getLong(pos); long valueSepMark = valueSepMark(keyLong); if (valueSepMark != 0) { int tailBits = tailBits(valueSepMark); pos += valueOffset(tailBits); // assert (UNSAFE.getByte(pos - 1) == ';') : "Expected ';' (1), pos=" + (pos - startAddr); long tailAndLen = tailAndLen(tailBits, keyLong, pos - start - 1); long valueLong = buf.getLong(pos); int decimalSepMark = decimalSepMark(valueLong); pos += nextKeyOffset(decimalSepMark); // assert (UNSAFE.getByte(pos - 1) == '\n') : "Expected '\\n' (1), pos=" + (pos - startAddr); int measurement = decimalValue(decimalSepMark, valueLong); add1(buf, start, tailAndLen, hash(hash1(tailAndLen)), measurement); continue; } pos += 8; long keyLong1 = keyLong; keyLong = buf.getLong(pos); valueSepMark = valueSepMark(keyLong); if (valueSepMark != 0) { int tailBits = tailBits(valueSepMark); pos += valueOffset(tailBits); // assert (UNSAFE.getByte(pos - 1) == ';') : "Expected ';' (2), pos=" + (pos - startAddr); long tailAndLen = tailAndLen(tailBits, keyLong, pos - start - 1); long valueLong = buf.getLong(pos); int decimalSepMark = decimalSepMark(valueLong); pos += nextKeyOffset(decimalSepMark); // assert (UNSAFE.getByte(pos - 1) == '\n') : "Expected '\\n' (2), pos=" + (pos - startAddr); int measurement = decimalValue(decimalSepMark, valueLong); add2(buf, start, keyLong1, tailAndLen, hash(hash(hash1(keyLong1), tailAndLen)), measurement); continue; } long hash = hash1(keyLong1); do { pos += 8; hash = hash(hash, keyLong); keyLong = buf.getLong(pos); valueSepMark = valueSepMark(keyLong); } while (valueSepMark == 0); int tailBits = tailBits(valueSepMark); pos += valueOffset(tailBits); // assert (UNSAFE.getByte(pos - 1) == ';') : "Expected ';' (N), pos=" + (pos - startAddr); long tailAndLen = tailAndLen(tailBits, keyLong, pos - start - 1); hash = hash(hash, tailAndLen); long valueLong = buf.getLong(pos); int decimalSepMark = decimalSepMark(valueLong); pos += nextKeyOffset(decimalSepMark); // assert (UNSAFE.getByte(pos - 1) == '\n') : "Expected '\\n' (N), pos=" + (pos - startAddr); int measurement = decimalValue(decimalSepMark, valueLong); addN(buf, start, tailAndLen, hash(hash), measurement); } return this; } public Stream stream() { return Arrays.stream(index) .filter(offset -> offset != 0) .mapToObj(offset -> new Entry(mem, offset)); } private static long hash1(long value) { return value; } private static long hash(long hash, long value) { return hash ^ value; } private static int hash(long hash) { hash *= 0x9E3779B97F4A7C15L; // Fibonacci hashing multiplier return (int) (hash >>> 39); } private static long valueSepMark(long keyLong) { // Seen this trick used in multiple other solutions. // Nice breakdown here: https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord long match = keyLong ^ 0x3B3B3B3B_3B3B3B3BL; // 3B == ';' match = (match - 0x01010101_01010101L) & (~match & 0x80808080_80808080L); return match; } private static int tailBits(long valueSepMark) { return Long.numberOfTrailingZeros(valueSepMark >>> 7); } private static int valueOffset(int tailBits) { return (int) (tailBits >>> 3) + 1; } private static long tailAndLen(int tailBits, long keyLong, long keyLen) { long tailMask = ~(-1L << tailBits); long tail = keyLong & tailMask; return (tail << 8) | ((keyLen >> 3) & 0xFF); } private static int decimalSepMark(long value) { // Seen this trick used in multiple other solutions. // Looks like the original author is @merykitty. // The 4th binary digit of the ascii of a digit is 1 while // that of the '.' is 0. This finds the decimal separator // The value can be 12, 20, 28 return Long.numberOfTrailingZeros(~value & 0x10101000); } private static int decimalValue(int decimalSepMark, long value) { // Seen this trick used in multiple other solutions. // Looks like the original author is @merykitty. int shift = 28 - decimalSepMark; // signed is -1 if negative, 0 otherwise long signed = (~value << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii code // to actual digit value in each byte long digits = ((value & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + // 0x00UU00TTHH000000 * 10 + // 0xUU00TTHH00000000 * 100 // Now TT * 100 has 2 trailing zeroes and HH * 100 + TT * 10 + UU < 0x400 // This results in our value lies in the bit 32 to 41 of this product // That was close :) long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; return (int) ((absValue ^ signed) - signed); } private static int nextKeyOffset(int decimalSepMark) { return (decimalSepMark >>> 3) + 3; } private void add1(ByteBuffer buf, int start, long tailAndLen, int hash, int measurement) { int idx = hash & INDEX_MASK; for (; index[idx] != 0; idx = (idx + 1) & INDEX_MASK) { if (update1(index[idx], tailAndLen, measurement)) { return; } } index[idx] = create(buf, start, tailAndLen, measurement); } private void add2(ByteBuffer buf, int start, long keyLong, long tailAndLen, int hash, int measurement) { int idx = hash & INDEX_MASK; for (; index[idx] != 0; idx = (idx + 1) & INDEX_MASK) { if (update2(index[idx], keyLong, tailAndLen, measurement)) { return; } } index[idx] = create(buf, start, tailAndLen, measurement); } private void addN(ByteBuffer buf, int start, long tailAndLen, int hash, int measurement) { int idx = hash & INDEX_MASK; for (; index[idx] != 0; idx = (idx + 1) & INDEX_MASK) { if (updateN(index[idx], buf, start, tailAndLen, measurement)) { return; } } index[idx] = create(buf, start, tailAndLen, measurement); } private int create(ByteBuffer buf, int start, long tailAndLen, int measurement) { int offset = memUsed; mem[offset] = tailAndLen; int memPos = offset + 1; int memEnd = memPos + (int) (tailAndLen & 0xFF); int bufPos = start; while (memPos < memEnd) { mem[memPos] = buf.getLong(bufPos); memPos += 1; bufPos += 8; } mem[memPos + FLD_MIN] = measurement; mem[memPos + FLD_MAX] = measurement; mem[memPos + FLD_SUM] = measurement; mem[memPos + FLD_COUNT] = 1; memUsed = memPos + 4; return offset; } private boolean update1(int offset, long tailAndLen, int measurement) { if (mem[offset] != tailAndLen) { return false; } updateStats(offset + 1, measurement); return true; } private boolean update2(int offset, long keyLong, long tailAndLen, int measurement) { if (mem[offset] != tailAndLen || mem[offset + 1] != keyLong) { return false; } updateStats(offset + 2, measurement); return true; } private boolean updateN(int offset, ByteBuffer buf, int start, long tailAndLen, int measurement) { var mem = this.mem; if (mem[offset] != tailAndLen) { return false; } int memPos = offset + 1; int memEnd = memPos + (int) (tailAndLen & 0xFF); int bufPos = start; while (memPos < memEnd) { if (mem[memPos] != buf.getLong(bufPos)) { return false; } memPos += 1; bufPos += 8; } updateStats(memPos, measurement); return true; } private void updateStats(int memPos, int measurement) { mem[memPos + FLD_COUNT] += 1; mem[memPos + FLD_SUM] += measurement; if (measurement < mem[memPos + FLD_MIN]) { mem[memPos + FLD_MIN] = measurement; } if (measurement > mem[memPos + FLD_MAX]) { mem[memPos + FLD_MAX] = measurement; } } public static class Entry { private final long[] mem; private final int offset; private String key; Entry(long[] mem, int offset) { this.mem = mem; this.offset = offset; } public String getKey() { if (key == null) { int pos = this.offset; long tailAndLen = mem[pos++]; int keyLen = (int) (tailAndLen & 0xFF); var tmpBuf = ByteBuffer.allocate((keyLen << 3) + 8).order(ByteOrder.nativeOrder()); for (int i = 0; i < keyLen; i++) { tmpBuf.putLong(mem[pos++]); } long tail = tailAndLen >>> 8; tmpBuf.putLong(tail); int keyLenBytes = (keyLen << 3) + 8 - (Long.numberOfLeadingZeros(tail) >> 3); key = new String(tmpBuf.array(), 0, keyLenBytes, StandardCharsets.UTF_8); } return key; } public Entry add(Entry other) { int fldOffset = (int) (mem[offset] & 0xFF) + 1; int pos = offset + fldOffset; int otherPos = other.offset + fldOffset; long[] otherMem = other.mem; mem[pos + FLD_MIN] = Math.min((int) mem[pos + FLD_MIN], (int) otherMem[otherPos + FLD_MIN]); mem[pos + FLD_MAX] = Math.max((int) mem[pos + FLD_MAX], (int) otherMem[otherPos + FLD_MAX]); mem[pos + FLD_SUM] += otherMem[otherPos + FLD_SUM]; mem[pos + FLD_COUNT] += otherMem[otherPos + FLD_COUNT]; return this; } public Entry getValue() { return this; } @Override public String toString() { int pos = offset + (int) (mem[offset] & 0xFF) + 1; return round(mem[pos + FLD_MIN]) + "/" + round(((double) mem[pos + FLD_SUM]) / mem[pos + FLD_COUNT]) + "/" + round(mem[pos + FLD_MAX]); } private static double round(double value) { return Math.round(value) / 10.0; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_gonixunsafe.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import sun.misc.Unsafe; public class CalculateAverage_gonixunsafe { private static final String FILE = "./measurements.txt"; private static final int MAX_THREADS = Runtime.getRuntime().availableProcessors(); public static void main(String[] args) throws Exception { var file = new RandomAccessFile(FILE, "r"); var chunks = Aggregator.buildChunks(file, MAX_THREADS); var chunksCount = chunks.size(); var threads = new Thread[chunksCount]; var result = new AtomicReference(); for (int i = 0; i < chunksCount; ++i) { var agg = new Aggregator(); var chunk = chunks.get(i); var thread = new Thread(() -> { agg.processChunk(chunk); while (!result.compareAndSet(null, agg)) { Aggregator other = result.getAndSet(null); if (other != null) { agg.merge(other); } } }); thread.start(); threads[i] = thread; } for (int i = 0; i < chunksCount; ++i) { threads[i].join(); } System.out.println(result.get().toString()); System.out.close(); } private static class Aggregator { private static final int MAX_STATIONS = 10_000; private static final int INDEX_SIZE = 256 * 1024 * 8; private static final int INDEX_MASK = (INDEX_SIZE - 1) & ~7; private static final int HEADER_SIZE = 8; private static final int MAX_KEY_SIZE = 100; private static final int FLD_COUNT = 0; // long private static final int FLD_SUM = 8; // long private static final int FLD_MIN = 16; // int private static final int FLD_MAX = 20; // int private static final int FLD_HASH = 24; // int private static final int FIELDS_SIZE = 28 + 4; // +padding to align to 8 bytes private static final int MAX_STATION_SIZE = HEADER_SIZE + MAX_KEY_SIZE + FIELDS_SIZE; private static final Unsafe UNSAFE; static { try { Field unsafe = Unsafe.class.getDeclaredField("theUnsafe"); unsafe.setAccessible(true); UNSAFE = (Unsafe) unsafe.get(Unsafe.class); } catch (Throwable e) { throw new RuntimeException(e); } } private static long alloc(long size) { long addr = UNSAFE.allocateMemory(size); UNSAFE.setMemory(addr, size, (byte) 0); return addr; } // Poor man's hash map: hash code to offset in `mem`. private final long indexAddr = alloc(INDEX_SIZE); // Contiguous storage of key (station name) and stats fields of all // unique stations. // The idea here is to improve locality so that stats fields would // possibly be already in the CPU cache after we are done comparing // the key. private final long memAddr = alloc(MAX_STATIONS * MAX_STATION_SIZE); private long memUsed = memAddr; private int count = 0; static List buildChunks(RandomAccessFile file, int count) throws IOException { var fileSize = file.length(); var chunkSize = Math.min(Integer.MAX_VALUE - 512, fileSize / count); if (chunkSize <= 0) { chunkSize = fileSize; } var chunks = new ArrayList((int) (fileSize / chunkSize) + 1); var mmap = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); var fileStartAddr = mmap.address(); var fileEndAddr = mmap.address() + mmap.byteSize(); var chunkStartAddr = fileStartAddr; while (chunkStartAddr < fileEndAddr) { var pos = chunkStartAddr + chunkSize; if (pos < fileEndAddr) { while (UNSAFE.getByte(pos) != '\n') { pos += 1; } pos += 1; } else { pos = fileEndAddr; } chunks.add(new Chunk(mmap, chunkStartAddr, pos, fileStartAddr, fileEndAddr)); chunkStartAddr = pos; } return chunks; } Aggregator processChunk(Chunk chunk) { // As an optimization, we assume that we can read past the end // of file size if as we don't cross page boundary. final int WANT_PADDING = 8; final int PAGE_SIZE = UNSAFE.pageSize(); if (((chunk.chunkEndAddr + WANT_PADDING) / PAGE_SIZE) <= (chunk.fileEndAddr / PAGE_SIZE)) { return processChunk(chunk.chunkStartAddr, chunk.chunkEndAddr); } // Otherwise, to avoid checking if it is safe to read a whole long // near the end of a chunk, we copy the last couple of lines to a // padded buffer and process that part separately. long pos = Math.max(-1, chunk.chunkEndAddr - WANT_PADDING - 1); while (pos >= 0 && UNSAFE.getByte(pos) != '\n') { pos--; } pos++; if (pos > 0) { processChunk(chunk.chunkStartAddr, pos); } long tailLen = chunk.chunkEndAddr - pos; var tailAddr = alloc(tailLen + WANT_PADDING); UNSAFE.copyMemory(pos, tailAddr, tailLen); processChunk(tailAddr, tailAddr + tailLen); return this; } private Aggregator processChunk(long startAddr, long endAddr) { long pos = startAddr; while (pos < endAddr) { long start = pos; long keyLong = UNSAFE.getLong(pos); long valueSepMark = valueSepMark(keyLong); if (valueSepMark != 0) { int tailBits = tailBits(valueSepMark); pos += valueOffset(tailBits); // assert (UNSAFE.getByte(pos - 1) == ';') : "Expected ';' (1), pos=" + (pos - startAddr); long tailAndLen = tailAndLen(tailBits, keyLong, pos - start - 1); long valueLong = UNSAFE.getLong(pos); int decimalSepMark = decimalSepMark(valueLong); pos += nextKeyOffset(decimalSepMark); // assert (UNSAFE.getByte(pos - 1) == '\n') : "Expected '\\n' (1), pos=" + (pos - startAddr); int measurement = decimalValue(decimalSepMark, valueLong); add1(start, tailAndLen, hash(hash1(tailAndLen)), measurement); continue; } pos += 8; long keyLong1 = keyLong; keyLong = UNSAFE.getLong(pos); valueSepMark = valueSepMark(keyLong); if (valueSepMark != 0) { int tailBits = tailBits(valueSepMark); pos += valueOffset(tailBits); // assert (UNSAFE.getByte(pos - 1) == ';') : "Expected ';' (2), pos=" + (pos - startAddr); long tailAndLen = tailAndLen(tailBits, keyLong, pos - start - 1); long valueLong = UNSAFE.getLong(pos); int decimalSepMark = decimalSepMark(valueLong); pos += nextKeyOffset(decimalSepMark); // assert (UNSAFE.getByte(pos - 1) == '\n') : "Expected '\\n' (2), pos=" + (pos - startAddr); int measurement = decimalValue(decimalSepMark, valueLong); add2(start, keyLong1, tailAndLen, hash(hash(hash1(keyLong1), tailAndLen)), measurement); continue; } long hash = hash1(keyLong1); do { pos += 8; hash = hash(hash, keyLong); keyLong = UNSAFE.getLong(pos); valueSepMark = valueSepMark(keyLong); } while (valueSepMark == 0); int tailBits = tailBits(valueSepMark); pos += valueOffset(tailBits); // assert (UNSAFE.getByte(pos - 1) == ';') : "Expected ';' (N), pos=" + (pos - startAddr); long tailAndLen = tailAndLen(tailBits, keyLong, pos - start - 1); hash = hash(hash, tailAndLen); long valueLong = UNSAFE.getLong(pos); int decimalSepMark = decimalSepMark(valueLong); pos += nextKeyOffset(decimalSepMark); // assert (UNSAFE.getByte(pos - 1) == '\n') : "Expected '\\n' (N), pos=" + (pos - startAddr); int measurement = decimalValue(decimalSepMark, valueLong); addN(start, tailAndLen, hash(hash), measurement); } return this; } private static long hash1(long value) { return value; } private static long hash(long hash, long value) { return hash ^ value; } private static int hash(long hash) { hash *= 0x9E3779B97F4A7C15L; // Fibonacci hashing multiplier return (int) (hash >>> 39); } private static long valueSepMark(long keyLong) { // Seen this trick used in multiple other solutions. // Nice breakdown here: https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord long match = keyLong ^ 0x3B3B3B3B_3B3B3B3BL; // 3B == ';' match = (match - 0x01010101_01010101L) & (~match & 0x80808080_80808080L); return match; } private static int tailBits(long valueSepMark) { return Long.numberOfTrailingZeros(valueSepMark >>> 7); } private static int valueOffset(int tailBits) { return (int) (tailBits >>> 3) + 1; } private static long tailAndLen(int tailBits, long keyLong, long keyLen) { long tailMask = ~(-1L << tailBits); long tail = keyLong & tailMask; return (tail << 8) | (keyLen & 0xFF); } private static int decimalSepMark(long value) { // Seen this trick used in multiple other solutions. // Looks like the original author is @merykitty. // The 4th binary digit of the ascii of a digit is 1 while // that of the '.' is 0. This finds the decimal separator // The value can be 12, 20, 28 return Long.numberOfTrailingZeros(~value & 0x10101000); } private static int decimalValue(int decimalSepMark, long value) { // Seen this trick used in multiple other solutions. // Looks like the original author is @merykitty. int shift = 28 - decimalSepMark; // signed is -1 if negative, 0 otherwise long signed = (~value << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii code // to actual digit value in each byte long digits = ((value & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + // 0x00UU00TTHH000000 * 10 + // 0xUU00TTHH00000000 * 100 // Now TT * 100 has 2 trailing zeroes and HH * 100 + TT * 10 + UU < 0x400 // This results in our value lies in the bit 32 to 41 of this product // That was close :) long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; return (int) ((absValue ^ signed) - signed); } private static int nextKeyOffset(int decimalSepMark) { return (decimalSepMark >>> 3) + 3; } private void add1(long keyStartAddr, long tailAndLen, int hash, int measurement) { int idx = hash & INDEX_MASK; for (long entryAddr; (entryAddr = UNSAFE.getLong(indexAddr + idx)) != 0; idx = (idx + 8) & INDEX_MASK) { if (update1(entryAddr, tailAndLen, measurement)) { return; } } UNSAFE.putLong(indexAddr + idx, create(keyStartAddr, tailAndLen, hash, measurement, '1')); } private void add2(long keyStartAddr, long keyLong, long tailAndLen, int hash, int measurement) { int idx = hash & INDEX_MASK; for (long entryAddr; (entryAddr = UNSAFE.getLong(indexAddr + idx)) != 0; idx = (idx + 8) & INDEX_MASK) { if (update2(entryAddr, keyLong, tailAndLen, measurement)) { return; } } UNSAFE.putLong(indexAddr + idx, create(keyStartAddr, tailAndLen, hash, measurement, '2')); } private void addN(long keyStartAddr, long tailAndLen, int hash, int measurement) { int idx = hash & INDEX_MASK; for (long entryAddr; (entryAddr = UNSAFE.getLong(indexAddr + idx)) != 0; idx = (idx + 8) & INDEX_MASK) { if (updateN(entryAddr, keyStartAddr, tailAndLen, measurement)) { return; } } UNSAFE.putLong(indexAddr + idx, create(keyStartAddr, tailAndLen, hash, measurement, 'N')); } private long create(long keyStartAddr, long tailAndLen, int hash, int measurement, char _origin) { // assert (memUsed + MAX_STATION_SIZE < memAddr + MAX_STATION_SIZE * MAX_STATIONS) : "Too many stations"; final long entryAddr = memUsed; int keySize = (int) (tailAndLen & 0xF8); long fieldsAddr = entryAddr + HEADER_SIZE + keySize; memUsed += HEADER_SIZE + keySize + FIELDS_SIZE; count++; UNSAFE.putLong(entryAddr, tailAndLen); UNSAFE.copyMemory(keyStartAddr, entryAddr + HEADER_SIZE, keySize); UNSAFE.putLong(fieldsAddr + FLD_COUNT, 1); UNSAFE.putLong(fieldsAddr + FLD_SUM, measurement); UNSAFE.putInt(fieldsAddr + FLD_MIN, measurement); UNSAFE.putInt(fieldsAddr + FLD_MAX, measurement); UNSAFE.putInt(fieldsAddr + FLD_HASH, hash); return entryAddr; } private static boolean update1(long entryAddr, long tailAndLen, int measurement) { if (UNSAFE.getLong(entryAddr) != tailAndLen) { return false; } updateStats(entryAddr + HEADER_SIZE, measurement); return true; } private static boolean update2(long entryAddr, long keyLong, long tailAndLen, int measurement) { if (UNSAFE.getLong(entryAddr) != tailAndLen) { return false; } if (UNSAFE.getLong(entryAddr + 8) != keyLong) { return false; } updateStats(entryAddr + HEADER_SIZE + 8, measurement); return true; } private static boolean updateN(long entryAddr, long keyStartAddr, long tailAndLen, int measurement) { if (UNSAFE.getLong(entryAddr) != tailAndLen) { return false; } long memPos = entryAddr + HEADER_SIZE; long memEnd = memPos + ((int) (tailAndLen & 0xF8)); long bufPos = keyStartAddr; while (memPos != memEnd) { if (UNSAFE.getLong(memPos) != UNSAFE.getLong(bufPos)) { return false; } memPos += 8; bufPos += 8; } updateStats(memPos, measurement); return true; } private static void updateStats(long addr, int measurement) { long oldCount = UNSAFE.getLong(addr + FLD_COUNT); long oldSum = UNSAFE.getLong(addr + FLD_SUM); long oldMin = UNSAFE.getInt(addr + FLD_MIN); long oldMax = UNSAFE.getInt(addr + FLD_MAX); UNSAFE.putLong(addr + FLD_COUNT, oldCount + 1); UNSAFE.putLong(addr + FLD_SUM, oldSum + measurement); if (measurement < oldMin) { UNSAFE.putInt(addr + FLD_MIN, measurement); } if (measurement > oldMax) { UNSAFE.putInt(addr + FLD_MAX, measurement); } } private static void updateStats(long addr, long count, long sum, int min, int max) { long oldCount = UNSAFE.getLong(addr + FLD_COUNT); long oldSum = UNSAFE.getLong(addr + FLD_SUM); long oldMin = UNSAFE.getInt(addr + FLD_MIN); long oldMax = UNSAFE.getInt(addr + FLD_MAX); UNSAFE.putLong(addr + FLD_COUNT, oldCount + count); UNSAFE.putLong(addr + FLD_SUM, oldSum + sum); if (min < oldMin) { UNSAFE.putInt(addr + FLD_MIN, min); } if (max > oldMax) { UNSAFE.putInt(addr + FLD_MAX, max); } } public Aggregator merge(Aggregator other) { var otherMemPos = other.memAddr; var otherMemEnd = other.memUsed; merge: for (long entrySize; otherMemPos < otherMemEnd; otherMemPos += entrySize) { int keySize = (int) (UNSAFE.getLong(otherMemPos) & 0xF8); long otherKeyEnd = otherMemPos + HEADER_SIZE + keySize; entrySize = HEADER_SIZE + keySize + FIELDS_SIZE; int hash = UNSAFE.getInt(otherKeyEnd + FLD_HASH); int idx = hash & INDEX_MASK; search: for (long entryAddr; (entryAddr = UNSAFE.getLong(indexAddr + idx)) != 0; idx = (idx + 8) & INDEX_MASK) { var thisPos = entryAddr; var otherPos = otherMemPos; while (otherPos < otherKeyEnd) { if (UNSAFE.getLong(thisPos) != UNSAFE.getLong(otherPos)) { continue search; } thisPos += 8; otherPos += 8; } updateStats( thisPos, UNSAFE.getLong(otherPos + FLD_COUNT), UNSAFE.getLong(otherPos + FLD_SUM), UNSAFE.getInt(otherPos + FLD_MIN), UNSAFE.getInt(otherPos + FLD_MAX)); continue merge; } // create // assert (memUsed + MAX_STATION_SIZE < memAddr + MAX_STATION_SIZE * MAX_STATIONS) : "Too many stations (merge)"; long entryAddr = memUsed; memUsed += entrySize; count++; UNSAFE.copyMemory(otherMemPos, entryAddr, entrySize); UNSAFE.putLong(indexAddr + idx, entryAddr); } return this; } @Override public String toString() { if (count == 0) { return "{}"; } var entries = new Entry[count]; int i = 0; for (long pos = memAddr; pos < memUsed; pos += (int) (UNSAFE.getLong(pos) & 0xF8) + HEADER_SIZE + FIELDS_SIZE) { entries[i++] = new Entry(pos); } Arrays.sort(entries); var sb = new StringBuilder(count * 50); sb.append('{'); entries[0].appendTo(sb); for (int j = 1; j < entries.length; ++j) { sb.append(", "); entries[j].appendTo(sb); } sb.append('}'); return sb.toString(); } static class Chunk { final MemorySegment file; final long chunkStartAddr; final long chunkEndAddr; final long fileStartAddr; final long fileEndAddr; Chunk(MemorySegment file, long chunkStartAddr, long chunkEndAddr, long fileStartAddr, long fileEndAddr) { this.file = file; this.chunkStartAddr = chunkStartAddr; this.chunkEndAddr = chunkEndAddr; this.fileStartAddr = fileStartAddr; this.fileEndAddr = fileEndAddr; } } static class Entry implements Comparable { private final long entryAddr; private final int keySize; private final String key; Entry(long entryAddr) { this.entryAddr = entryAddr; this.keySize = (int) UNSAFE.getLong(entryAddr) & 0xF8; try (var arena = Arena.ofConfined()) { var ms = arena.allocate(keySize + 8); UNSAFE.copyMemory(entryAddr + HEADER_SIZE, ms.address(), keySize); UNSAFE.copyMemory(entryAddr + 1, ms.address() + keySize, 7); this.key = ms.getUtf8String(0); } } @Override public int compareTo(Entry other) { return key.compareTo(other.key); } @Override public String toString() { long pos = entryAddr + HEADER_SIZE + keySize; return round(UNSAFE.getInt(pos + FLD_MIN)) + "/" + round(((double) UNSAFE.getLong(pos + FLD_SUM)) / UNSAFE.getLong(pos + FLD_COUNT)) + "/" + round(UNSAFE.getInt(pos + FLD_MAX)); } void appendTo(StringBuilder sb) { long pos = entryAddr + HEADER_SIZE + keySize; sb.append(key); sb.append('='); sb.append(round(UNSAFE.getInt(pos + FLD_MIN))); sb.append('/'); sb.append(round(((double) UNSAFE.getLong(pos + FLD_SUM)) / UNSAFE.getLong(pos + FLD_COUNT))); sb.append('/'); sb.append(round(UNSAFE.getInt(pos + FLD_MAX))); } private static double round(double value) { return Math.round(value) / 10.0; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_hallvard.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.function.Consumer; import java.util.function.Function; public class CalculateAverage_hallvard { private static class ResultRow { private String name; private int min, max, sum; private int count; public ResultRow(String name) { this.name = name; this.min = Integer.MAX_VALUE; this.max = Integer.MIN_VALUE; this.sum = 0; this.count = 0; } public ResultRow(String name, int value) { this.name = name; this.sum = this.max = this.min = value; this.count = 1; } @Override public String toString() { return (min / 10.0d) + "/" + (Math.round((double) sum / count) / 10.0d) + "/" + (max / 10.0d); } void update(int value) { if (value < min) { min = value; } if (value > max) { max = value; } sum += value; count++; } void update(ResultRow row) { if (row.min < min) { min = row.min; } if (row.max > max) { max = row.max; } sum += row.sum; count += row.count; } }; private static class Trie { private final Node root = new Node(); String toString(String prefix, String separator, String suffix, Function formatter, Comparator comparator) { StringBuilder builder = new StringBuilder(); List payloads = new ArrayList<>(); forEach(payloads::add); if (comparator != null) { Collections.sort(payloads, comparator); } for (var item : payloads) { if (builder.isEmpty()) { if (prefix != null) { builder.append(prefix); } } else { if (separator != null) { builder.append(separator); } } builder.append(formatter != null ? formatter.apply(item) : item.toString()); } if (suffix != null) { builder.append(suffix); } return builder.toString(); } void forEach(Consumer consumer) { forEach(root, consumer); } private void forEach(Node node, Consumer consumer) { if (node.payload != null) { consumer.accept(node.payload); } for (int nodeIdx = 0; nodeIdx < node.rests.length; nodeIdx++) { Node rest = node.rests[nodeIdx]; if (rest != null) { forEach(rest, consumer); } } } Node getNode(ByteBuffer byteBuffer, int start, int end) { Node node = root; next: for (int byteIdx = start; byteIdx < end; byteIdx++) { byte b = byteBuffer.get(byteIdx); if (node.nexts != null) { for (int nodeIdx = 0; nodeIdx < node.nexts.length; nodeIdx++) { byte next = node.nexts[nodeIdx]; if (next == b) { // if found byte value, use corresponding node node = node.rests[nodeIdx]; continue next; } else if (next == 0) { // if empty slot add new node node.nexts[nodeIdx] = b; node = (node.rests[nodeIdx] = createDefaultNode()); continue next; } } // convert to full node Node[] newRests = new Node[Byte.MAX_VALUE - Byte.MIN_VALUE]; for (int i = 0; i < node.nexts.length; i++) { newRests[Node.idx(node.nexts[i])] = node.rests[i]; } // new entry Node newNode = createDefaultNode(); newRests[Node.idx(b)] = newNode; node.nexts = null; node.rests = newRests; node = newNode; } else { int idx = Node.idx(b); Node rest = node.rests[idx]; node = (rest != null ? rest : (node.rests[idx] = createDefaultNode())); } } return node; } final Node createDefaultNode() { return new Node(4); } private static class Node { private T payload; private byte[] nexts; private Node[] rests; // full node that covers all byte values, with byte as index Node() { nexts = null; rests = new Node[Byte.MAX_VALUE - Byte.MIN_VALUE]; } // sparse node that covers some byte values, index of value (in nexts) gives index of node (in rests) Node(int length) { nexts = new byte[length]; rests = new Node[length]; } static final int idx(byte b) { return b - Byte.MIN_VALUE; } } } private static boolean computeAverages(ByteBuffer byteBuffer, int start, Trie results) { // search backwards to first newline int startPos = start; while (startPos > 0 && byteBuffer.get(startPos - 1) != '\n') { startPos--; } byteBuffer.position(startPos); while (byteBuffer.hasRemaining()) { // find name range int nameStart = byteBuffer.position(), limit = byteBuffer.limit(), pos = nameStart; while (pos < limit && byteBuffer.get(pos) != ';') { pos++; } // is there room for ; a digit, decimal point, a decimal and the final newline if (pos + 4 >= limit) { return false; } int nameEnd = pos++; // parse value byte next = byteBuffer.get(pos++); boolean negative = false; if (next == '-') { negative = true; next = byteBuffer.get(pos++); } int value = next - '0'; int decimalPos = -1; while (pos < limit && (next = byteBuffer.get(pos)) != '\n') { if (next == '.') { if (decimalPos >= 0) { return false; } decimalPos = pos; } else { value = value * 10 + (next - '0'); } pos++; } if (next != '\n') { return false; } if (negative) { value = -value; } // skip newline byteBuffer.position(pos + 1); Trie.Node node = results.getNode(byteBuffer, nameStart, nameEnd); ResultRow result = node.payload; if (result == null) { byte[] bytes = new byte[nameEnd - nameStart]; byteBuffer.get(nameStart, bytes); result = new ResultRow(new String(bytes), value); node.payload = result; } else { result.update(value); } } return true; } private record TaskInfo(long chunkStart, int chunkSize, int start) { Trie doTask(FileChannel channel) { Trie results = new Trie<>(); try { //System.err.println("Mapping bytes " + chunkStart + " - " + (chunkStart + chunkSize)); //System.err.flush(); MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, chunkStart, chunkSize); //System.err.println("Computing averages from " + (chunkStart + start) + " (" + start + ")"); //System.err.flush(); computeAverages(buffer, start, results); //System.err.println("Read upto " + (chunkStart + buffer.position())); //System.err.flush(); } catch (IOException e) { throw new RuntimeException("Exception while doing " + this + ": " + e); } return results; } } public static void main(String[] args) throws IOException { Path measurementsPath = Paths.get("./measurements.txt"); try (FileChannel channel = FileChannel.open(measurementsPath)) { int ROW_SIZE = 50, CHUNK_SIZE = 100_000_000; long size = channel.size(), pos = 0; List tasks = new ArrayList<>(); while (pos >= 0 && pos < size) { long chunkStart = Math.max(pos - ROW_SIZE, 0); int chunkSize = (int) Math.min(size - chunkStart, CHUNK_SIZE + (pos - chunkStart)); tasks.add(new TaskInfo(chunkStart, chunkSize, (int) (pos - chunkStart))); pos = chunkStart + chunkSize; } Map results = new TreeMap<>(); tasks.parallelStream() .map(task -> task.doTask(channel)) .forEach(result -> { result.forEach(resultRow -> { synchronized (results) { ResultRow existing = results.get(resultRow.name); if (existing != null) { existing.update(resultRow); } else { results.put(resultRow.name, resultRow); } } }); }); System.out.println(results); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_hchiorean.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.FileChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_hchiorean { private static Map parseLines(Integer key, CharBuffer chars, ConcurrentMap leftoversMap) { Map data = new HashMap<>(); int startIdx = 0; int endIdx = chars.length() - 1; while (chars.charAt(startIdx) != '\n') { ++startIdx; } while (chars.charAt(endIdx) != '\n') { --endIdx; } if (startIdx < endIdx) { parseSanitizedCharBuffer(chars, data, startIdx, endIdx); } String firstPartBeforeDelim = chars.subSequence(0, startIdx + 1).toString(); String lastPartBeforeDelim = chars.subSequence(endIdx + 1, chars.length()).toString(); leftoversMap.put(key, firstPartBeforeDelim + lastPartBeforeDelim); chars = null; return data; } private static void parseSanitizedCharBuffer(CharSequence sequence, Map data, int startIdx, int endIdx) { StringBuilder parseBuffer = new StringBuilder(); String name = null; for (int i = startIdx; i < endIdx; ++i) { char c = sequence.charAt(i); if (c == '\r') { continue; } if (c == '\n') { if (parseBuffer.isEmpty()) { continue; } addParsedDataToMap(data, parseBuffer, name); parseBuffer.setLength(0); continue; } if (c == ';') { name = parseBuffer.toString(); parseBuffer.setLength(0); continue; } parseBuffer.append(c); } if (!parseBuffer.isEmpty()) { assert name != null; addParsedDataToMap(data, parseBuffer, name); } } private static void addParsedDataToMap(Map data, StringBuilder parseBuffer, String name) { String value = parseBuffer.toString(); double valueNum = Double.parseDouble(value); double[] existingMeasurements = data.putIfAbsent(name, new double[]{ valueNum, valueNum, 1, valueNum }); if (existingMeasurements != null) { existingMeasurements[0] = Math.min(existingMeasurements[0], valueNum); existingMeasurements[1] = Math.max(existingMeasurements[1], valueNum); ++existingMeasurements[2]; existingMeasurements[3] += valueNum; } } static Map readFile(File file) throws Exception { Map aggregate = new TreeMap<>(Comparator.naturalOrder()); List>> futures = new ArrayList<>(); ConcurrentMap leftoversMap = new ConcurrentSkipListMap<>(); Charset defaultCharset = Charset.defaultCharset(); CharsetDecoder decoder = defaultCharset.newDecoder(); int bufferCapacity = 10 * 1024 * 1024; long len = file.length(); int idCounter = 0; try ( ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); FileChannel chan = FileChannel.open(file.toPath(), StandardOpenOption.READ)) { ByteBuffer mainBuffer = ByteBuffer.allocate(bufferCapacity); int totalRead = 0; while (totalRead < len) { mainBuffer.clear(); long read = chan.read(mainBuffer); if (read == -1) { break; } totalRead += read; mainBuffer.flip(); CharBuffer chars = null; for (;;) { try { chars = decoder.decode(mainBuffer); break; } catch (CharacterCodingException e) { // keep reading byte by byte until a valid sequence is decoded mainBuffer.rewind(); ByteBuffer nextByte = ByteBuffer.allocate(1); chan.read(nextByte); nextByte.flip(); mainBuffer = ByteBuffer.allocate(mainBuffer.capacity() + 1).put(mainBuffer).put(nextByte); mainBuffer.flip(); } } int nextId = idCounter++; CharBuffer finalChars = chars; futures.add( executor.submit(() -> parseLines(Integer.valueOf(nextId), finalChars, leftoversMap))); } } for (Future> future : futures) { Map chunk = future.get(); aggregate(chunk, aggregate); } String leftovers = String.join("", leftoversMap.values()); parseSanitizedCharBuffer(leftovers, aggregate, 0, leftovers.length()); return aggregate; } private static void aggregate(Map chunks, Map aggregate) { for (Map.Entry chunk : chunks.entrySet()) { String name = chunk.getKey(); double[] chunkData = chunk.getValue(); double[] aggregateData = aggregate.putIfAbsent(name, chunkData); if (aggregateData != null) { aggregateData[0] = Math.min(aggregateData[0], chunkData[0]); aggregateData[1] = Math.max(aggregateData[1], chunkData[1]); aggregateData[2] += chunkData[2]; aggregateData[3] += chunkData[3]; } } } static void print(Map dataMap) { System.out.print("{"); for (Iterator> dataEntryIt = dataMap.entrySet().iterator(); dataEntryIt.hasNext();) { String entryOutput = format(dataEntryIt.next()); System.out.print(entryOutput); if (dataEntryIt.hasNext()) { System.out.print(", "); } } System.out.println("}"); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } private static String format(Map.Entry entry) { double[] dataPoints = entry.getValue(); double min = round(dataPoints[0]); double max = round(dataPoints[1]); double mean = round(dataPoints[3] / dataPoints[2]); return entry.getKey() + "=" + min + "/" + mean + "/" + max; } public static void main(String[] args) throws Exception { File file = new File("./measurements.txt"); Map data = readFile(file); print(data); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_hundredwatt.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.IntStream; public class CalculateAverage_hundredwatt { private static final String FILE = "./measurements.txt"; private static final int MAX_ROW_SIZE = 100 + 1 + 5 + 1; // 100 for city name, 1 for ;, 5 for temperature, 1 for \n private static final int THREAD_COUNT = Runtime.getRuntime().availableProcessors(); private static final long BUFFER_SIZE = 128 * 1024 * 1024; // 128MB private static final long CHUNK_SIZE = BUFFER_SIZE / THREAD_COUNT; private static final long FILE_CHUNK_SIZE = CHUNK_SIZE - MAX_ROW_SIZE; public static final int TEMPERATURE_SLOTS = 5003; // prime number private static final short[] TEMPERATURES = new short[TEMPERATURE_SLOTS]; private static final long PERFECT_HASH_SEED = -1982870890352534081L; // Construct a perfect hash function mapping temperatures encoded as longs (e.g., 0x2d342e3000000000 for -4.3) to // the corresponding short integer (e.g., -43). static { // Figure out encoding for all possible temperature values (1999 total) long start = System.currentTimeMillis(); Map decodeTemperatureMap = new HashMap<>(); for (short i = -999; i <= 999; i++) { long word = 0; int shift = 0; if (i < 0) { word |= ((long) '-') << shift; shift += 8; } if (Math.abs(i) >= 100) { int hh = Math.abs(i) / 100; int tt = (Math.abs(i) - hh * 100) / 10; word |= ((long) (hh + '0')) << shift; shift += 8; word |= ((long) (tt + '0')) << shift; } else { int tt = Math.abs(i) / 10; // convert to ascii word |= ((long) (tt + '0')) << shift; } shift += 8; word |= ((long) '.') << shift; shift += 8; int uu = Math.abs(i) % 10; word |= ((long) (uu + '0')) << shift; // 31302e3000000000 decodeTemperatureMap.put(word, i); } decodeTemperatureMap.entrySet().stream().forEach(e -> { var word = e.getKey(); var h = (word * PERFECT_HASH_SEED) & ~(1L << 63); var pos = (int) (h % TEMPERATURE_SLOTS); if (TEMPERATURES[pos] != 0) throw new RuntimeException("collision at " + pos); TEMPERATURES[pos] = e.getValue(); }); // System.out.println("Building table took " + (System.currentTimeMillis() - start) + "ms"); } static class Record { short min; short max; int sum; int count; public Record() { this.min = Short.MAX_VALUE; this.max = Short.MIN_VALUE; this.sum = 0; this.count = 0; } public void updateWith(short value) { min = (short) Math.min(min, value); max = (short) Math.max(max, value); sum += value; count++; } @Override public String toString() { return round(min / 10.0) + "/" + round(sum / 10.0 / count) + "/" + round(max / 10.0); } double round(double v) { return Math.round(v * 10.0) / 10.0; } } record Entry(long[] key, Record value) { } static class HashTable { private static final int INITIAL_SIZE = 16 * 1024; private static final float LOAD_FACTOR = 0.75f; private static final int GROW_FACTOR = 4; private final long[][] KEYS = new long[INITIAL_SIZE][]; private final Record[] VALUES = new Record[INITIAL_SIZE]; private final long[] HASHES = new long[INITIAL_SIZE]; private int size = INITIAL_SIZE; public HashTable() { for (int i = 0; i < INITIAL_SIZE; i++) { VALUES[i] = new Record(); } } public void putOrMerge(int hash, int length, long[] key, short value) { int idx = hash & (size - 1); // linear probing int i = 0; while (KEYS[idx] != null && (HASHES[idx] != hash) && (0 != Arrays.compareUnsigned(KEYS[idx], 0, KEYS[idx].length, key, 0, length))) { i++; idx = (idx + 1) & (size - 1); } if (KEYS[idx] == null) { KEYS[idx] = Arrays.copyOf(key, length); HASHES[idx] = hash; } VALUES[idx].updateWith(value); } public List getAll() { List result = new ArrayList<>(size); for (int i = 0; i < size; i++) { if (KEYS[i] != null) { result.add(new Entry(KEYS[i], VALUES[i])); } } return result; } } private static String keyToString(long[] key) { ByteBuffer kb = ByteBuffer.allocate(8 * key.length).order(ByteOrder.LITTLE_ENDIAN); Arrays.stream(key).forEach(kb::putLong); // remove trailing '\0' bytes from kb and // fix two ';' in word issue here (rather than in hot path) byte b; int limit = kb.position() - 8; kb.position(limit); while ((b = kb.get()) != 0 && b != ';' && limit < kb.capacity() - 1) { limit++; } kb.flip(); byte[] bytes = new byte[limit]; kb.get(bytes); return new String(bytes); } private static Record merge(Record v, Record value) { var record = new Record(); record.min = (short) Math.min(v.min, value.min); record.max = (short) Math.max(v.max, value.max); record.sum = v.sum + value.sum; record.count = v.count + value.count; return record; } private static int processChunk(ByteBuffer bb, HashTable hashTable, long start, long size) { bb.order(ByteOrder.LITTLE_ENDIAN); // Find first entry while (start != 0 && bb.get() != '\n') { } long word; long[] key = new long[13]; int offset; long arg, hasvalue, op1, op2; int position = bb.position(); long hash; long temperature_hash; int temperature_pos; short temperature_value; int hashInt; int rc = 0; int end = (int) (size - MAX_ROW_SIZE); while (position <= end) { // rc++; offset = -1; // Parse city name // First word hash = key[++offset] = bb.getLong(position + offset * 8); // From "Determine if a word has a byte equal to n" // https://graphics.stanford.edu/~seander/bithacks.html#ValueInWord arg = (key[offset]) ^ (0x0101010101010101L * (';')); op1 = (arg - 0x0101010101010101L); op2 = ~(arg); hasvalue = (op1 & op2 & 0x8080808080808080L); // Remaining words (if present) while (hasvalue == 0) { ++offset; key[offset] = bb.getLong(position + offset * 8); hash ^= key[offset]; arg = (key[offset]) ^ (0x0101010101010101L * (';')); op1 = (arg - 0x0101010101010101L); op2 = ~(arg); hasvalue = (op1 & op2 & 0x8080808080808080L); } hash ^= key[offset]; // unset last word since it will be updated key[offset] = key[offset] & ~(-(hasvalue >> 7)); hash ^= key[offset]; position = position + offset * 8 + Long.numberOfTrailingZeros(hasvalue) / 8 + 1; // +1 for \n // Parse temperature word = bb.getLong(position); hasvalue = (word - 0x0B0B0B0B0B0B0B0BL) & 0x8080808080808080L; int newlinePos = Long.numberOfTrailingZeros(hasvalue) - 8; word = word & (~(-(1L << newlinePos))); // Perfect hash lookup for temperature temperature_hash = (word * PERFECT_HASH_SEED) & ~(1L << 63); temperature_pos = (int) (temperature_hash % TEMPERATURE_SLOTS); temperature_value = TEMPERATURES[temperature_pos]; position = position + newlinePos / 8 + 2; // +1 for \n hashInt = (int) (hash ^ (hash >> 32) ^ (hash >> 17)); hashTable.putOrMerge(hashInt, offset + 1, key, temperature_value); } return rc; } public static void main(String[] args) throws IOException { final long fileSize = Files.size(Path.of(FILE)); // System.out.println("File size: " + fileSize); // AtomicLong rowCount = new AtomicLong(); // Read file in chunks using striping try (var fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE), StandardOpenOption.READ)) { var r = IntStream.range(0, THREAD_COUNT + 1).mapToObj(stripe -> { long start = stripe * FILE_CHUNK_SIZE; var hashTable = new HashTable(); if (stripe == THREAD_COUNT) { // last thread try { // handle trailing bytes in file in jankiest way possible (for now hopefully :) ) byte[] trailing = new byte[MAX_ROW_SIZE * 2]; fileChannel.read(ByteBuffer.wrap(trailing), Math.max(0, fileSize - MAX_ROW_SIZE)); var rc = processChunk(ByteBuffer.wrap(trailing), hashTable, Math.max(0, fileSize - MAX_ROW_SIZE), MAX_ROW_SIZE + Math.min(fileSize, MAX_ROW_SIZE) - 1); // rowCount.addAndGet(rc); return hashTable; } catch (IOException e) { throw new RuntimeException(e); } } // if file is smaller than max row size, we're done b/c the trailing bytes handler processed the whole file if (fileSize <= MAX_ROW_SIZE) { return hashTable; } while (start < fileSize) { long end = Math.min(start + CHUNK_SIZE, fileSize); MappedByteBuffer bb = null; try { bb = fileChannel.map(FileChannel.MapMode.READ_ONLY, start, Math.min(end - start + 8, fileSize - start)); } catch (IOException e) { throw new RuntimeException(e); } var rc = processChunk(bb, hashTable, start, end - start); // rowCount.addAndGet(rc); start += FILE_CHUNK_SIZE * THREAD_COUNT; } return hashTable; }).parallel().flatMap(partition -> partition.getAll().stream()) .collect(Collectors.toMap(e -> keyToString(e.key()), Entry::value, CalculateAverage_hundredwatt::merge, TreeMap::new)); System.out.println(r); // System.out.println(rowCount.get()); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_ianopolous.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.*; import java.nio.channels.*; import java.util.concurrent.*; import java.util.stream.*; import java.util.*; /* A simple implementation aiming for readability. * Features: * * memory mapped file * * read chunks in parallel * * minimise allocation * * no unsafe * * Timings on 4 core i7-7500U CPU @ 2.70GHz: * average_baseline: 4m48s * ianopolous: 36s */ public class CalculateAverage_ianopolous { public static final int MAX_LINE_LENGTH = 107; public static final int MAX_STATIONS = 10_000; public static void main(String[] args) throws Exception { File input = new File("./measurements.txt"); long filesize = input.length(); // keep chunk size between 256 MB and 1G (1 chunk for files < 256MB) long chunkSize = Math.min(Math.max(filesize / 32, 256 * 1024 * 1024), 1024 * 1024 * 1024L); int nChunks = (int) ((filesize + chunkSize - 1) / chunkSize); ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor(); List>>> allResults = IntStream.range(0, nChunks) .mapToObj(i -> pool.submit(() -> parseStats(i * chunkSize, Math.min((i + 1) * chunkSize, filesize)))) .toList(); TreeMap merged = allResults.stream() .parallel() .flatMap(f -> { try { return f.get().stream().filter(Objects::nonNull).flatMap(Collection::stream); } catch (Exception e) { return Stream.empty(); } }) .collect(Collectors.toMap(s -> s.name(), s -> s, (a, b) -> a.merge(b), TreeMap::new)); System.out.println(merged); } public static boolean matchingStationBytes(int start, int end, MappedByteBuffer buffer, Stat existing) { for (int i = start; i < end; i++) { if (existing.name[i - start] != buffer.get(i)) return false; } return true; } public static Stat parseStation(int start, int end, int hash, MappedByteBuffer buffer, List> stations) { int index = Math.floorMod(hash, MAX_STATIONS); List matches = stations.get(index); if (matches == null) { List value = new ArrayList<>(); byte[] stationBuffer = new byte[end - start]; buffer.position(start); buffer.get(stationBuffer); Stat res = new Stat(stationBuffer); value.add(res); stations.set(index, value); return res; } else { for (int i = 0; i < matches.size(); i++) { Stat s = matches.get(i); if (matchingStationBytes(start, end, buffer, s)) return s; } byte[] stationBuffer = new byte[end - start]; buffer.position(start); buffer.get(stationBuffer); Stat res = new Stat(stationBuffer); matches.add(res); return res; } } public static List> parseStats(long startByte, long endByte) { try { RandomAccessFile file = new RandomAccessFile("./measurements.txt", "r"); long maxEnd = Math.min(file.length(), endByte + MAX_LINE_LENGTH); long len = maxEnd - startByte; if (len > Integer.MAX_VALUE) throw new RuntimeException("Segment size must fit into an int"); int maxDone = (int) (endByte - startByte); MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, startByte, len); int done = 0; // read first partial line if (startByte > 0) { for (int i = 0; i < MAX_LINE_LENGTH; i++) { byte b = buffer.get(i); if (b == '\n') { done = i + 1; break; } } } List> stations = new ArrayList<>(MAX_STATIONS); for (int i = 0; i < MAX_STATIONS; i++) stations.add(null); int lineStart = done; int lineSplit = 0; short temperature = 0; int hash = 1; boolean negative = false; while (done < maxDone) { Stat station = null; for (int i = done; i < done + MAX_LINE_LENGTH && i < maxEnd; i++) { byte b = buffer.get(i); if (b == '\n') { done = i + 1; temperature = negative ? (short) -temperature : temperature; station.add(temperature); lineStart = done; station = null; hash = 1; break; } else if (b == ';') { lineSplit = i; station = parseStation(lineStart, lineSplit, hash, buffer, stations); temperature = 0; negative = false; } else if (station == null) { hash = 31 * hash + b; } else if (b == '-') { negative = true; } else if (b != '.') { temperature = (short) (temperature * 10 + (b - 0x30)); } } } return stations; } catch (IOException e) { throw new RuntimeException(e); } } public static class Stat { final byte[] name; int count = 0; short min = Short.MAX_VALUE, max = Short.MIN_VALUE; long total = 0; public Stat(byte[] name) { this.name = name; } public void add(short value) { if (value < min) min = value; if (value > max) max = value; total += value; count++; } public Stat merge(Stat value) { if (value.min < min) min = value.min; if (value.max > max) max = value.max; total += value.total; count += value.count; return this; } private static double round(double value) { return Math.round(value) / 10.0; } public String name() { return new String(name); } public String toString() { return round((double) min) + "/" + round(((double) total) / count) + "/" + round((double) max); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_ianopolousfast.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.nio.ByteOrder; import java.nio.channels.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.stream.*; import java.util.*; import static java.lang.foreign.ValueLayout.*; /* A fast implementation with no unsafe. * Features: * * memory mapped file using preview Arena FFI * * semicolon finding and name comparison using incubator vector api * * read chunks in parallel * * minimise allocation * * no unsafe * * process multiple lines in each thread for better ILP */ public class CalculateAverage_ianopolousfast { public static final int MAX_LINE_LENGTH = 107; public static final int MAX_STATIONS = 1 << 14; private static final OfLong LONG_LAYOUT = JAVA_LONG_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN); private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED.length() >= 16 ? ByteVector.SPECIES_128 : ByteVector.SPECIES_64; public static void main(String[] args) throws Exception { Arena arena = Arena.global(); Path input = Path.of("measurements.txt"); FileChannel channel = (FileChannel) Files.newByteChannel(input, StandardOpenOption.READ); long filesize = Files.size(input); MemorySegment mmap = channel.map(FileChannel.MapMode.READ_ONLY, 0, filesize, arena); int nChunks = filesize < 4 * 1024 * 1024 ? 1 : Runtime.getRuntime().availableProcessors(); long chunkSize = (filesize + nChunks - 1) / nChunks; List allResults = IntStream.range(0, nChunks) .parallel() .mapToObj(i -> parseStats(i * chunkSize, Math.min((i + 1) * chunkSize, filesize), mmap)) .toList(); TreeMap merged = allResults.stream() .parallel() .flatMap(f -> { try { return Arrays.stream(f).filter(Objects::nonNull); } catch (Exception e) { e.printStackTrace(); return Stream.empty(); } }) .collect(Collectors.toMap(s -> s.name(), s -> s, (a, b) -> a.merge(b), TreeMap::new)); System.out.println(merged); } public static boolean matchingStationBytes(long start, long end, MemorySegment buffer, Stat existing) { for (int index = 0; index < end - start; index += BYTE_SPECIES.vectorByteSize()) { ByteVector line = ByteVector.fromMemorySegment(BYTE_SPECIES, buffer, start + index, ByteOrder.nativeOrder(), BYTE_SPECIES.indexInRange(start + index, end)); ByteVector found = ByteVector.fromArray(BYTE_SPECIES, existing.name, index); if (!found.eq(line).allTrue()) return false; } return true; } private static final int GOLDEN_RATIO = 0x9E3779B9; private static final int HASH_LROTATE = 5; // hash from giovannicuccu private static int hash(MemorySegment memorySegment, long start, int len) { int x; int y; if (len >= Integer.BYTES) { x = memorySegment.get(JAVA_INT_UNALIGNED, start); y = memorySegment.get(JAVA_INT_UNALIGNED, start + len - Integer.BYTES); } else { x = memorySegment.get(JAVA_BYTE, start); y = memorySegment.get(JAVA_BYTE, start + len - Byte.BYTES); } return (Integer.rotateLeft(x * GOLDEN_RATIO, HASH_LROTATE) ^ y) * GOLDEN_RATIO; } public static Stat createStation(long start, long end, MemorySegment buffer) { byte[] stationBuffer = new byte[(int) (end - start)]; for (long off = start; off < end; off++) stationBuffer[(int) (off - start)] = buffer.get(JAVA_BYTE, off); return new Stat(stationBuffer); } public static Stat dedupeStation(long start, long end, MemorySegment buffer, Stat[] stations) { int hash = hash(buffer, start, (int) (end - start)); int index = hash & (MAX_STATIONS - 1); Stat match = stations[index]; while (match != null) { if (matchingStationBytes(start, end, buffer, match)) return match; index = (index + 1) % stations.length; match = stations[index]; } Stat res = createStation(start, end, buffer); stations[index] = res; return res; } public static short getMinus(long d) { return ((d & 0xff00000000000000L) ^ 0x2d00000000000000L) != 0 ? 0 : (short) -1; } public static void processTemperature(long lineSplit, int size, MemorySegment buffer, Stat station) { long d = buffer.get(LONG_LAYOUT, lineSplit); // negative is either 0 or -1 short negative = getMinus(d); d = d << (negative * -8); int dotIndex = size - 2 + negative; d = (d >> 8) | 0x30000000_00000000L; // add a leading 0 digit d = d >> 8 * (5 - dotIndex); short temperature = (short) ((byte) d - '0' + 10 * (((byte) (d >> 16)) - '0') + 100 * (((byte) (d >> 24)) - '0')); temperature = (short) ((temperature ^ negative) - negative); // negative treatment inspired by merkitty station.add(temperature); } private static int lineSize(long lineStart, MemorySegment buffer) { ByteVector line = ByteVector.fromMemorySegment(BYTE_SPECIES, buffer, lineStart, ByteOrder.nativeOrder()); int lineSize = line.compare(VectorOperators.EQ, '\n').firstTrue(); int index = lineSize; while (index == BYTE_SPECIES.vectorByteSize()) { index = ByteVector.fromMemorySegment(BYTE_SPECIES, buffer, lineStart + lineSize, ByteOrder.nativeOrder()).compare(VectorOperators.EQ, '\n').firstTrue(); lineSize += index; } return lineSize; } private static int keySize(int lineSize, long lineStart, MemorySegment buffer) { return lineSize - 6 + ByteVector.fromMemorySegment(BYTE_SPECIES, buffer, lineStart + lineSize - 6, ByteOrder.nativeOrder()).compare(VectorOperators.EQ, ';').firstTrue(); } public static Stat[] parseStats(long start1, long end2, MemorySegment buffer) { // read first partial line if (start1 > 0) { for (int i = 0; i < MAX_LINE_LENGTH; i++) { byte b = buffer.get(JAVA_BYTE, start1++); if (b == '\n') { break; } } } Stat[] stations = new Stat[MAX_STATIONS]; // Handle reading the very last few lines in the file // this allows us to not worry about reading beyond the end // in the inner loop (reducing branches) // We need at least the vector lane size bytes back if (end2 == buffer.byteSize()) { // reverse at least vector lane width end2 = Math.max(buffer.byteSize() - 2 * BYTE_SPECIES.vectorByteSize(), 0); while (end2 > 0 && buffer.get(JAVA_BYTE, end2) != '\n') end2--; if (end2 > 0) end2++; // copy into a larger buffer to avoid reading off end MemorySegment end = Arena.global().allocate(MAX_LINE_LENGTH + 2 * BYTE_SPECIES.vectorByteSize()); for (long i = end2; i < buffer.byteSize(); i++) end.set(JAVA_BYTE, i - end2, buffer.get(JAVA_BYTE, i)); int index = 0; while (end2 + index < buffer.byteSize()) { int lineSize1 = lineSize(index, end); int semiSearchStart = index + Math.max(0, lineSize1 - 6); int keySize1 = semiSearchStart - index + ByteVector.fromMemorySegment(BYTE_SPECIES, end, semiSearchStart, ByteOrder.nativeOrder()).compare(VectorOperators.EQ, ';').firstTrue(); Stat station1 = dedupeStation(index, index + keySize1, end, stations); processTemperature(index + keySize1 + 1, lineSize1 - keySize1 - 1, end, station1); index += lineSize1 + 1; } } while (start1 < end2) { int lineSize1 = lineSize(start1, buffer); long start2 = start1 + lineSize1 + 1; int lineSize2 = start2 < end2 ? lineSize(start2, buffer) : 0; int keySize1 = keySize(lineSize1, start1, buffer); int keySize2 = keySize(lineSize2, start2, buffer); Stat station1 = dedupeStation(start1, start1 + keySize1, buffer, stations); processTemperature(start1 + keySize1 + 1, lineSize1 - keySize1 - 1, buffer, station1); if (start2 < end2) { Stat station2 = dedupeStation(start2, start2 + keySize2, buffer, stations); processTemperature(start2 + keySize2 + 1, lineSize2 - keySize2 - 1, buffer, station2); start1 = start2 + lineSize2 + 1; } else start1 += lineSize1 + 1; } return stations; } public static class Stat { final byte[] name; final int namelen; int count = 0; short min = Short.MAX_VALUE, max = Short.MIN_VALUE; long total = 0; public Stat(byte[] name) { int vecSize = BYTE_SPECIES.vectorByteSize(); int arrayLen = (name.length + vecSize - 1) / vecSize * vecSize; this.name = Arrays.copyOfRange(name, 0, arrayLen); this.namelen = name.length; } public void add(short value) { if (value < min) min = value; if (value > max) max = value; total += value; count++; } public Stat merge(Stat value) { if (value.min < min) min = value.min; if (value.max > max) max = value.max; total += value.total; count += value.count; return this; } private static double round(double value) { return Math.round(value) / 10.0; } public String name() { return new String(Arrays.copyOfRange(name, 0, namelen)); } public String toString() { return round((double) min) + "/" + round(((double) total) / count) + "/" + round((double) max); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_imrafaelmerino.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ForkJoinPool; import java.util.function.Supplier; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * @author Rafael Merino García * *
 *
 *  Model Name: MacBook Pro
 *  Model Identifier: MacBookPro17,1
 *  Chip: Apple M1
 *  Total Number of Cores: 8 (4 performance and 4 efficiency)
 *  Memory: 16 GB
 *
 *  Executed 10 times in my machine with a chunk size of 20MB
 *
 *     21.0.1-graal
 *     avg: 15,366 sg | min: 14,878 sg | max: 15,937 sg | acc: 153,657 sg | times: 10
 *
 *     21-oracle
 *     avg: 17,032 sg | min: 16,448 sg | max: 17,424 sg | acc: 170,325 sg | times: 10
 *
 *
 *
 *  Credits:
 *      . bjhara: Really nice splitearator to be able to use the Stream API.
 *      . ebarlas: working with integers since we only have to consider one decimal
 *        (I don't think this makes a big difference though)
 *      . filiphr: It was my starting point, since it's the most natural way of approaching
 *        the problem using the nice spliterartor from bjhara. This solution has the potential
 *        for substantial improvement by actively pursuing a 
higher level of parallelization
. *
* * *
 * Generalization Note:
 *
 * - This solution is designed to be applicable to any CSV file under the following assumptions:
 *
 * - The line schema follows the pattern: name;value\n
 *
 * - The name is up to 128 characters (can be changed to hold any other size and irrelevant for the result)
 *
 * - The value is a decimal number with only one decimal digit.
 *
 * - The focus is on maintaining code simplicity without extreme optimization efforts,
 *   as achieving meaningful conclusions often requires substantial time and dedication,
 *   particularly with tools like JMH.
 *
 * - Emphasis on utilizing idiomatic Java and common data structures, following a pragmatic approach.
 *
 * - Addressing the question of whether the workload is CPU-bound or IO-bound is key; indications suggest
 *    both aspects are relevant. It's difficult to make the cores sweat! The observed trend in many solutions
 *    suggests the potential for increased parallelization to fully utilize multiple cores effectively.
 *    This solution brings to the table the Java class ManagedBlock, aiming to enhance parallelism in scenarios
 *    where threads from the Fork Join Pool are blocked.
 *
 *  - Commong guys! stop rolling the dice with fancy optimizations and reiventing hash maps structures and
 *   hash algorithms. This should be hard fun
 *   and not tedious. Dont get me wrong! just an opinion :)
 *
 * - Last but not least, Gunnar Morling, you rock man! Thanks for your time and effort.
 *
 * -
 *
 * 
*/ public class CalculateAverage_imrafaelmerino { private static final String FILE = "./measurements.txt"; private static final int FIELD_SIZE = 128; public static void main(String[] args) throws IOException { var chunkSize = Long.parseLong(args[0].trim()); var result = calculateStats(FILE, chunkSize); System.out.println(result); } private static Map calculateStats(String file, long chunkSize) throws IOException { try (var fileChannel = FileChannel.open(Paths.get(file), StandardOpenOption.READ)) { var stats = fileMemoryStream(fileChannel, chunkSize) .parallel() .map(p -> ManagedComputation.compute(() -> parse(p))) .reduce(Collections.emptyMap(), (stat1, stat2) -> combine(stat1, stat2)); return new TreeMap<>(stats); } } private static Map combine(Map xs, Map ys) { Map result = new HashMap<>(); for (var key : xs.keySet()) { var m1 = xs.get(key); var m2 = ys.get(key); var combined = (m2 == null) ? m1 : (m1 == null) ? m2 : Stat.combine(m1, m2); result.put(key, combined); } for (var key : ys.keySet()) result.putIfAbsent(key, ys.get(key)); return result; } private static Map parse(ByteBuffer bb) { Map stats = new HashMap<>(); var limit = bb.limit(); var field = new byte[FIELD_SIZE]; while (bb.position() < limit) { var fieldCurrentIndex = 0; field[fieldCurrentIndex++] = bb.get(); while (bb.position() < limit) { var fieldByte = bb.get(); if (fieldByte == ';') break; field[fieldCurrentIndex++] = fieldByte; } var fieldStr = new String(field, 0, fieldCurrentIndex); var number = 0; var sign = 1; while (bb.position() < limit) { var numberByte = bb.get(); if (numberByte == '-') sign = -1; else if (numberByte == '\n') break; else if (numberByte != '.') number = number * 10 + (numberByte - '0'); } stats.computeIfAbsent(fieldStr, k -> new Stat()) .update(sign * number); } return stats; } private static Stream fileMemoryStream(FileChannel fileChannel, long chunkSize) throws IOException { var spliterator = Spliterators.spliteratorUnknownSize(fileMemoryIterator(fileChannel, chunkSize), Spliterator.IMMUTABLE); return StreamSupport.stream(spliterator, false); } private static Iterator fileMemoryIterator(FileChannel fileChannel, long chunkSize) throws IOException { return new Iterator<>() { private final long size = fileChannel.size(); private long start = 0; @Override public boolean hasNext() { return start < size; } @Override public ByteBuffer next() { try { var buffer = fileChannel.map(MapMode.READ_ONLY, start, Math.min(chunkSize, size - start)); var limmit = buffer.limit() - 1; while (buffer.get(limmit) != '\n') limmit--; limmit++; buffer.limit(limmit); start += limmit; return buffer; } catch (IOException ex) { throw new UncheckedIOException(ex); } } }; } private static final class Stat { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum = 0L; private long count = 0L; public static Stat combine(Stat m1, Stat m2) { var stat = new Stat(); stat.min = Math.min(m1.min, m2.min); stat.max = Math.max(m1.max, m2.max); stat.sum = m1.sum + m2.sum; stat.count = m1.count + m2.count; return stat; } private void update(int value) { this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); this.sum += value; this.count++; } @Override public String toString() { return round(min / 10.0) + "/" + round((sum / 10.0) / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static final class ManagedComputation { static T compute(final Supplier supplier) { var managedBlocker = new ManagedSupplier<>(supplier); try { ForkJoinPool.managedBlock(managedBlocker); return managedBlocker.getResult(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } private static class ManagedSupplier implements ForkJoinPool.ManagedBlocker { private final Supplier task; private T result; private boolean isDone = false; private ManagedSupplier(final Supplier supplier) { task = supplier; } @Override public boolean block() { result = task.get(); isDone = true; return true; } @Override public boolean isReleasable() { return isDone; } T getResult() { return result; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_isolgpus.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.math.BigDecimal; import java.math.RoundingMode; import java.nio.BufferUnderflowException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.*; import java.util.stream.Collectors; public class CalculateAverage_isolgpus { public static final int HISTOGRAMS_LENGTH = 1024 * 32; public static final int HISTOGRAMS_MASK = HISTOGRAMS_LENGTH - 1; public static final int THREAD_COUNT = Runtime.getRuntime().availableProcessors(); private static final String FILE = "./measurements.txt"; public static final byte SEPERATOR = 59; public static final byte OFFSET = 48; public static final byte NEGATIVE = 45; public static final byte DECIMAL_POINT = 46; public static final int MAX_CHUNK_SIZE = Integer.MAX_VALUE - 100; // bit of wiggle room public static final byte NEW_LINE = 10; public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); File file = Paths.get(FILE).toFile(); long length = file.length(); long chunksCount = length < 8_000_000 ? 1 : Math.max(THREAD_COUNT, (int) Math.ceil(length / (double) MAX_CHUNK_SIZE)); long estimatedChunkSize = length / chunksCount; FileChannel channel = new RandomAccessFile(file, "r").getChannel(); List> futures = new ArrayList<>(); for (int i = 0; i < chunksCount; i++) { int finalI = i; futures.add(executorService.submit(() -> handleChunk(channel, estimatedChunkSize * finalI, estimatedChunkSize, length))); } List measurementCollectors = new ArrayList<>(); for (Future result : futures) { measurementCollectors.add(result.get()); } executorService.shutdown(); Map measurementCollectorsByCity = mergeMeasurements(measurementCollectors); List results = measurementCollectorsByCity.values().stream().map(MeasurementResult::from).toList(); System.out.println("{" + results.stream().map(MeasurementResult::toString).collect(Collectors.joining(", ")) + "}"); } private static Map mergeMeasurements(List resultsFromAllChunk) { Map mergedResults = new TreeMap<>(Comparator.naturalOrder()); for (int i = 0; i < HISTOGRAMS_LENGTH; i++) { for (MeasurementCollector[] resultFromSpecificChunk : resultsFromAllChunk) { MeasurementCollector measurementCollectorFromChunk = resultFromSpecificChunk[i]; while (measurementCollectorFromChunk != null) { MeasurementCollector currentMergedResult = mergedResults.get(new String(measurementCollectorFromChunk.name)); if (currentMergedResult == null) { currentMergedResult = new MeasurementCollector(measurementCollectorFromChunk.name, measurementCollectorFromChunk.nameSum); mergedResults.put(new String(currentMergedResult.name), currentMergedResult); } currentMergedResult.merge(measurementCollectorFromChunk); measurementCollectorFromChunk = measurementCollectorFromChunk.link; } } } return mergedResults; } // ----n--- private static MeasurementCollector[] handleChunk(FileChannel channel, long estimatedStart, long lengthOfChunk, long maxLengthOfFile) throws IOException { // -1 to see if we're starting on a brand new message // +200 for wiggle room to finish the final message long seekStart = Math.max(estimatedStart - 1, 0); long length = Math.min(lengthOfChunk + 200, maxLengthOfFile - seekStart); MappedByteBuffer r = channel.map(FileChannel.MapMode.READ_ONLY, seekStart, length); boolean isNegative; byte[] valueBuffer = new byte[3]; MeasurementCollector[] measurementCollectors = new MeasurementCollector[HISTOGRAMS_LENGTH]; int i = 0; // seek to the start of the next message if (estimatedStart != 0) { while (r.get() != NEW_LINE) { i++; } i++; } try { while (i <= lengthOfChunk) { int nameSum = 0; int hashResult = 0; int nameStart; byte aChar; nameStart = i; int nameBufferIndex = 0; int valueIndex = 0; // optimistically assume that the name is at least 4 bytes int firstInt = r.getInt(); nameBufferIndex = 4; nameSum = firstInt; hashResult = 31 * firstInt; while ((aChar = r.get()) != SEPERATOR) { nameSum += aChar; // hash as we go, stolen after a discussion with palmr hashResult = 31 * hashResult + aChar; nameBufferIndex++; // oh no we read too much, do it the byte for byte way instead if (aChar == NEW_LINE) { r.position(i); nameBufferIndex = 0; nameSum = 0; hashResult = 0; } } i += nameBufferIndex + 1; isNegative = (aChar = r.get()) == NEGATIVE; valueIndex = readNumber(isNegative, valueBuffer, valueIndex, aChar, r); int decimalValue = r.getShort() >> 8; int value = resolveValue(valueIndex, valueBuffer, decimalValue, isNegative); MeasurementCollector measurementCollector = resolveMeasurementCollector(measurementCollectors, hashResult, nameStart, nameBufferIndex, nameSum, r); measurementCollector.feed(value); i += valueIndex + (isNegative ? 4 : 3); } } catch (BufferUnderflowException e) { if (i != maxLengthOfFile - seekStart) { e.printStackTrace(); throw new RuntimeException(e); } } return measurementCollectors; } private static MeasurementCollector resolveMeasurementCollector(MeasurementCollector[] measurementCollectors, int hash, int nameStart, int nameBufferLength, int nameSum, MappedByteBuffer r) { MeasurementCollector measurementCollector = measurementCollectors[hash & HISTOGRAMS_MASK]; if (measurementCollector == null) { byte[] nameBuffer = new byte[nameBufferLength]; r.get(nameStart, nameBuffer, 0, nameBufferLength); measurementCollector = new MeasurementCollector(nameBuffer, nameSum); measurementCollectors[hash & HISTOGRAMS_MASK] = measurementCollector; } else { // collision unhappy path, try to avoid while (!nameEquals(measurementCollector.name, measurementCollector.nameSum, nameSum, nameBufferLength)) { if (measurementCollector.link == null) { byte[] nameBuffer = new byte[nameBufferLength]; r.get(nameStart, nameBuffer, 0, nameBufferLength); measurementCollector.link = new MeasurementCollector(nameBuffer, nameSum); measurementCollector = measurementCollector.link; break; } else { measurementCollector = measurementCollector.link; } } } return measurementCollector; } private static boolean nameEquals(byte[] existingName, int existingNameSum, int incomingNameSum, int nameBufferIndex) { if (existingName.length != nameBufferIndex) { return false; } return incomingNameSum == existingNameSum; } private static int resolveValue(int valueIndex, byte[] valueBuffer, int decimalValue, boolean isNegative) { int value; if (valueIndex == 1) { value = ((valueBuffer[0] - OFFSET) * 10) + (decimalValue - OFFSET); } else // it's 2 digits { value = ((valueBuffer[0] - OFFSET) * 100) + ((valueBuffer[1] - OFFSET) * 10) + (decimalValue - OFFSET); } if (isNegative) { value = Math.negateExact(value); } return value; } private static int readNumber(boolean isNegative, byte[] valueBuffer, int valueIndex, byte aChar, MappedByteBuffer r) { if (!isNegative) { valueBuffer[valueIndex++] = aChar; } // maybe one or two more while ((aChar = r.get()) != DECIMAL_POINT) { valueBuffer[valueIndex++] = aChar; } return valueIndex; } private static class MeasurementCollector { private final byte[] name; private final int nameSum; public MeasurementCollector link; private long sum; private int count; private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; public MeasurementCollector(byte[] name, int nameSum) { this.name = name; this.nameSum = nameSum; } public void feed(int value) { sum += value; count++; min = Math.min(value, min); max = Math.max(value, max); } public void merge(MeasurementCollector measurementCollector) { this.sum += measurementCollector.sum; this.count += measurementCollector.count; this.min = Math.min(measurementCollector.min, this.min); this.max = Math.max(measurementCollector.max, this.max); } } private static class MeasurementResult { private final String name; private final double mean; private final BigDecimal max; private final BigDecimal min; public MeasurementResult(String name, double mean, BigDecimal max, BigDecimal min) { this.name = name; this.mean = mean; this.max = max; this.min = min; } @Override public String toString() { return name + "=" + min + "/" + mean + "/" + max; } public static MeasurementResult from(MeasurementCollector mc) { double mean = Math.round((double) mc.sum / (double) mc.count) / 10d; BigDecimal max = BigDecimal.valueOf(mc.max).divide(BigDecimal.TEN, 1, RoundingMode.HALF_UP); BigDecimal min = BigDecimal.valueOf(mc.min).divide(BigDecimal.TEN, 1, RoundingMode.HALF_UP); return new MeasurementResult(new String(mc.name), mean, max, min); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_itaske.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.AbstractMap; import java.util.Comparator; import java.util.Map; import java.util.concurrent.ConcurrentSkipListMap; import java.util.stream.Collectors; public class CalculateAverage_itaske { private static final String FILE = "./measurements.txt"; private record Measurement(long count, double sum, double min, double max) { Measurement(double value) { this(1, value, value, value); } public String toString() { StringBuilder builder = new StringBuilder(); builder.append(round(min)); builder.append("/"); builder.append(round(sum/count)); builder.append("/"); builder.append(round(max)); return builder.toString(); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } public static void main(String[] args) throws IOException { Map resultMap = Files.lines(Path.of(FILE)).parallel() .map(line -> { int separatorIndex = line.indexOf(";"); String key = line.substring(0, separatorIndex); double value = Double.parseDouble(line.substring(separatorIndex + 1)); return new AbstractMap.SimpleEntry<>(key, value); }) .collect(Collectors.toConcurrentMap( entry -> entry.getKey(), entry -> new Measurement(entry.getValue()), ((measurement1, measurement2) -> new Measurement( measurement1.count + measurement2.count, measurement1.sum + measurement2.sum, Math.min(measurement1.min, measurement2.min), Math.max(measurement1.max, measurement2.max))))); System.out.println( resultMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Object::toString).collect(Collectors.joining(", ", "{", "}"))); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_ivanklaric.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.ConcurrentMap; import java.nio.file.Files; import java.nio.file.Paths; import java.util.stream.Stream; public class CalculateAverage_ivanklaric { private static final String FILE = "measurements.txt"; record CityTemps (double min, double max, double sum, long count) { public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } private double round(double num) { return Math.round(num * 10.0) / 10.0; } } public static void main(String[] args) throws IOException { Stream lines = Files.lines(Paths.get(FILE)); ConcurrentMap cityStats = new ConcurrentSkipListMap<>(); lines.parallel().forEach(line -> { int splitterLoc = line.indexOf(';'); double temp = Double.parseDouble(line.substring(splitterLoc + 1, line.length())); cityStats.merge(line.substring(0, splitterLoc), new CityTemps(temp, temp, temp, 1), (oldValue, defaultValue) -> { return new CityTemps(Math.min(oldValue.min, temp), Math.max(oldValue.max, temp), oldValue.sum + temp, oldValue.count + 1); }); }); System.out.println(cityStats); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_iziamos.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.foreign.Arena; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.CompletableFuture; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardOpenOption.READ; public class CalculateAverage_iziamos { private static final sun.misc.Unsafe UNSAFE = initUnsafe(); private static sun.misc.Unsafe initUnsafe() { try { java.lang.reflect.Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (sun.misc.Unsafe) theUnsafe.get(sun.misc.Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static final String FILE = "./measurements.txt"; private static final Arena GLOBAL_ARENA = Arena.global(); public static void main(String[] args) throws Exception { // final long chunkSize = Long.MAX_VALUE; final long chunkSize = 64 * 1024 * 1024; final FileChannel fileChannel; try { fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE), READ); } catch (final IOException e) { throw new UncheckedIOException(e); } final var seg = fileChannel.map(READ_ONLY, 0, fileChannel.size(), GLOBAL_ARENA); final long fileSize = seg.byteSize(); final long threadCount = 1 + fileSize / chunkSize; final var processingFutures = new CompletableFuture[(int) threadCount]; for (int i = 0; i < threadCount; ++i) { processingFutures[i] = processSegment(seg.address(), seg.address() + fileSize, i, chunkSize); } final long aggregate = (long) processingFutures[0].get(); for (int i = 1; i < processingFutures.length; i++) { final long r = (long) processingFutures[i].get(); ByteBackedResultSet.merge(aggregate, r); } final Map output = new TreeMap<>(); ByteBackedResultSet.forEach(aggregate, (name, min, max, sum, count) -> output.put(name, new ResultRow(min, (double) sum / count, max))); System.out.println(output); } private record ResultRow(long min, double mean, long max) { public String toString() { return STR."\{formatLong(min)}/\{round(mean)}/\{formatLong(max)}"; } private double formatLong(final long value) { return value / 10.0; } private double round(double value) { return Math.round(value) / 10.0; } } private static CompletableFuture processSegment(final long basePointer, final long endPointer, final long chunkNumber, final long chunkSize) { final var ret = new CompletableFuture(); Thread.ofVirtual().start(() -> { final long relativeStart = chunkNumber * chunkSize; final long absoluteStart = basePointer + relativeStart; final long absoluteEnd = computeAbsoluteEndWithSlack(absoluteStart + chunkSize, endPointer); final long startOffsetAfterSkipping = skipIncomplete(basePointer, absoluteStart); final long result = processEvents(startOffsetAfterSkipping, absoluteEnd); ret.complete(result); }); return ret; } private static long computeAbsoluteEndWithSlack(final long chunk, final long endPointer) { return Long.compareUnsigned(endPointer, chunk) > 0 ? chunk : endPointer; } private static long skipIncomplete(final long basePointer, final long start) { if (start == basePointer) { return start; } for (long i = 0;; ++i) { final byte b = UNSAFE.getByte(start + i); if (b == '\n') { return start + i + 1; } } } private static long processEvents(final long start, final long limit) { final long result = ByteBackedResultSet.createResultSet(); scalarLoop(start, limit, result); return result; } private static void scalarLoop(final long start, final long limit, final long result) { final LoopCursor cursor = new LoopCursor(start, limit); while (cursor.hasMore()) { final long address = cursor.getCurrentAddress(); final int length = cursor.getStringLength(); final int hash = cursor.getHash(); final int value = cursor.getCurrentValue(); ByteBackedResultSet.put(result, address, length, hash, value); } } public static class LoopCursor { private long pointer; private final long limit; private int hash = 0; public LoopCursor(final long pointer, final long limit) { this.pointer = pointer; this.limit = limit; } public long getCurrentAddress() { return pointer; } public int getStringLength() { int strLen = 0; hash = 0; byte b = UNSAFE.getByte(pointer); for (; b != ';'; ++strLen, b = UNSAFE.getByte(pointer + strLen)) { hash = 31 * hash + b; } pointer += strLen + 1; return strLen; } public int getHash() { return hash; } public int getCurrentValue() { return getCurrentValueMeryKitty(); } /** * No point rewriting what would essentially be the same code <3. */ public int getCurrentValueMeryKitty() { long word = UNSAFE.getLong(pointer); if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { word = Long.reverseBytes(word); } int decimalSepPos = Long.numberOfTrailingZeros(~word & 0x10101000); int shift = 28 - decimalSepPos; long signed = (~word << 59) >> 63; long designMask = ~(signed & 0xFF); long digits = ((word & designMask) << shift) & 0x0F000F0F00L; long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; int increment = (decimalSepPos >>> 3) + 3; pointer += increment; return (int) ((absValue ^ signed) - signed); } public boolean hasMore() { return pointer < limit; } } public interface ResultConsumer { void consume(final String name, final int min, final int max, final long sum, final long count); } static class ByteBackedResultSet { private static final int MAP_SIZE = 16384 * 4; private static final int MASK = MAP_SIZE - 1; private static final long STRUCT_SIZE = 64; private static final long BYTE_SIZE = MAP_SIZE * STRUCT_SIZE; private static final long STRING_OFFSET = 0; private static final long STRING_LEN_OFFSET = 8; private static final long HASH_OFFSET = 12; private static final long MIN_OFFSET = 16; private static final long MAX_OFFSET = 20; private static final long SUM_OFFSET = 24; private static final long COUNT_OFFSET = 32; public static long createResultSet() { final long baseAddress = UNSAFE.allocateMemory(BYTE_SIZE); UNSAFE.setMemory(baseAddress, BYTE_SIZE, (byte) 0); return baseAddress; } public static void put(final long baseAddress, final long address, final int length, final int hash, final int value) { final long slot = findSlot(baseAddress, hash, address, length); final long structBase = baseAddress + (slot * STRUCT_SIZE); final int min = UNSAFE.getInt(structBase + MIN_OFFSET); final int max = UNSAFE.getInt(structBase + MAX_OFFSET); final long sum = UNSAFE.getLong(structBase + SUM_OFFSET); final long count = UNSAFE.getLong(structBase + COUNT_OFFSET); UNSAFE.putLong(structBase, address); UNSAFE.putInt(structBase + STRING_LEN_OFFSET, length); UNSAFE.putInt(structBase + HASH_OFFSET, hash); UNSAFE.putInt(structBase + MIN_OFFSET, Math.min(value, min)); UNSAFE.putInt(structBase + MAX_OFFSET, Math.max(value, max)); UNSAFE.putLong(structBase + SUM_OFFSET, sum + value); UNSAFE.putLong(structBase + COUNT_OFFSET, count + 1); } public static void forEach(final long baseAddress, final ResultConsumer resultConsumer) { for (long i = 0; i < BYTE_SIZE; i += STRUCT_SIZE) { final long structBase = baseAddress + i; final long stringBase = UNSAFE.getLong(structBase); if (stringBase == 0) { continue; } final int min = UNSAFE.getInt(structBase + MIN_OFFSET); final int max = UNSAFE.getInt(structBase + MAX_OFFSET); final long sum = UNSAFE.getLong(structBase + SUM_OFFSET); final long count = UNSAFE.getLong(structBase + COUNT_OFFSET); final int strLen = UNSAFE.getInt(structBase + STRING_LEN_OFFSET); final byte[] bytes = new byte[strLen]; for (int j = 0; j < strLen; ++j) { bytes[j] = UNSAFE.getByte(stringBase + j); } resultConsumer.consume(new String(bytes, UTF_8), min, max, sum, count); } } public static void merge(final long baseAddress, final long other) { for (long i = 0; i < BYTE_SIZE; i += STRUCT_SIZE) { final long otherStructBase = other + i; if (UNSAFE.getLong(otherStructBase) == 0) { continue; } final long otherStringStart = UNSAFE.getLong(otherStructBase); final int otherStringLength = UNSAFE.getInt(otherStructBase + STRING_LEN_OFFSET); final int otherStringHash = UNSAFE.getInt(otherStructBase + HASH_OFFSET); final long slot = findSlot(baseAddress, otherStringHash, otherStringStart, otherStringLength); final long thisStructBase = baseAddress + (slot * STRUCT_SIZE); final int min = UNSAFE.getInt(thisStructBase + MIN_OFFSET); final int max = UNSAFE.getInt(thisStructBase + MAX_OFFSET); final long sum = UNSAFE.getLong(thisStructBase + SUM_OFFSET); final long count = UNSAFE.getLong(thisStructBase + COUNT_OFFSET); final int otherMin = UNSAFE.getInt(otherStructBase + MIN_OFFSET); final int otherMax = UNSAFE.getInt(otherStructBase + MAX_OFFSET); final long otherSum = UNSAFE.getLong(otherStructBase + SUM_OFFSET); final long otherCount = UNSAFE.getLong(otherStructBase + COUNT_OFFSET); UNSAFE.putLong(thisStructBase, otherStringStart); UNSAFE.putInt(thisStructBase + STRING_LEN_OFFSET, otherStringLength); UNSAFE.putInt(thisStructBase + HASH_OFFSET, otherStringHash); UNSAFE.putInt(thisStructBase + MIN_OFFSET, Math.min(otherMin, min)); UNSAFE.putInt(thisStructBase + MAX_OFFSET, Math.max(otherMax, max)); UNSAFE.putLong(thisStructBase + SUM_OFFSET, sum + otherSum); UNSAFE.putLong(thisStructBase + COUNT_OFFSET, count + otherCount); } } private static int findSlot(final long baseAddress, final int hash, final long otherStringAddress, final int otherStringLength) { for (int slot = mask(hash);; slot = mask(++slot)) { final long structBase = baseAddress + ((long) slot * STRUCT_SIZE); final long nameStart = UNSAFE.getLong(structBase); if (nameStart == 0) { UNSAFE.putInt(structBase + MIN_OFFSET, Integer.MAX_VALUE); UNSAFE.putInt(structBase + MAX_OFFSET, Integer.MIN_VALUE); return slot; } final int nameLength = UNSAFE.getInt(structBase + STRING_LEN_OFFSET); if (stringEquals(nameStart, nameLength, otherStringAddress, otherStringLength)) { return slot; } } } private static boolean stringEquals(final long thisNameAddress, final int thisStringLength, final long otherNameAddress, final long otherNameLength) { if (thisStringLength != otherNameLength) { return false; } int i = 0; for (; i < thisStringLength - 7; i += 8) { if (UNSAFE.getLong(thisNameAddress + i) != UNSAFE.getLong(otherNameAddress + i)) { return false; } } final long remainingToCheck = thisStringLength - i; final long finalBytesMask = ((1L << remainingToCheck * 8)) - 1; final long thisLastWord = UNSAFE.getLong(thisNameAddress + i); final long otherLastWord = UNSAFE.getLong(otherNameAddress + i); return 0 == ((thisLastWord ^ otherLastWord) & finalBytesMask); } public static int mask(final int value) { return MASK & value; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_japplis.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.*; /** * Maybe not the fastest but trying to get the most readable code for the performance. * * It allows: * - pass another file as argument * - the first lines can start with comments lines using '#' * - the temperatures can have more than one fraction digit but it needs to be constant in the file * - it does not require much RAM * - Java 8 as minimal Java version * Assumptions * - No temperatures are above 100 or below -100 * - the last character of the file is \n * * Changelog: * - First local attempt with FileReader and TreeMap: Way too long * - Switched to InputStream and ConcurrentHashMap: 23" * - Added Semaphore to avoid OOMException: 23" * - Replaced String with my own ByteText class: a bit slower (~10%) * - Replaced compute lambda call with synchronized(city.intern()): 43" (due to intern()) * - Removed BufferedInputStream and replaced Measurement with IntSummaryStatistics (thanks davecom): still 23" but cleaner code * - Execute same code on 1BRC server: 41" * - One HashMap per thread: 17" locally (12" on 1BRC server) * - Read file in multiple threads if available and * - Changed String to (byte[]) Text with cache: 18" locally (but 8" -> 5" on laptop) * * @author Anthony Goubard - Japplis */ public class CalculateAverage_japplis { private static final String DEFAULT_MEASUREMENT_FILE = "measurements.txt"; private static final int BUFFER_SIZE = 5 * 1024 * 1024; // 5 MB private static final int MAX_COMPUTE_THREADS = Runtime.getRuntime().availableProcessors(); private int precision = -1; private int precisionLimitTenth; private long fileSize; private Map cityMeasurementMap = new ConcurrentHashMap<>(10_000); private List previousBlockLastLine = new ArrayList<>(); private Semaphore readFileLock = new Semaphore(MAX_COMPUTE_THREADS); private Queue bufferPool = new ConcurrentLinkedQueue<>(); private void parseTemperatures(File measurementsFile) throws Exception { fileSize = measurementsFile.length(); int blockIndex = 0; int totalBlocks = (int) (fileSize / BUFFER_SIZE) + 1; ExecutorService threadPool = Executors.newFixedThreadPool(MAX_COMPUTE_THREADS); List parseBlockTasks = new ArrayList<>(); while (blockIndex < totalBlocks) { int availableReadThreads = Math.min(readFileLock.availablePermits(), totalBlocks - blockIndex); if (availableReadThreads == 0) { readFileLock.acquire(); // No need to loop in the 'while' if all threads are busy readFileLock.release(); } List> readBlockTasks = new ArrayList<>(); for (int i = 0; i < availableReadThreads; i++) { readFileLock.acquire(); // Wait if all threads are busy Callable blockReader = readBlock(measurementsFile, blockIndex); Future readBlockTask = threadPool.submit(blockReader); readBlockTasks.add(readBlockTask); blockIndex++; } for (Future readBlockTask : readBlockTasks) { ByteArray buffer = readBlockTask.get(); if (buffer.array().length > 0) { int startIndex = handleSplitLine(buffer.array()); readFileLock.acquire(); // Wait if all threads are busy Runnable blockParser = parseTemperaturesBlock(buffer, startIndex); Future parseBlockTask = threadPool.submit(blockParser); parseBlockTasks.add(parseBlockTask); } } } for (Future parseBlockTask : parseBlockTasks) { // Wait for all tasks to finish parseBlockTask.get(); } threadPool.shutdownNow(); } private Callable readBlock(File measurementsFile, long blockIndex) { return () -> { long fileIndex = blockIndex * BUFFER_SIZE; if (fileIndex >= fileSize) { readFileLock.release(); return new ByteArray(0); } try (InputStream measurementsFileIS = new FileInputStream(measurementsFile)) { if (fileIndex > 0) { long skipped = measurementsFileIS.skip(fileIndex); while (skipped != fileIndex) { skipped += measurementsFileIS.skip(fileIndex - skipped); } } long bufferSize = Math.min(BUFFER_SIZE, fileSize - fileIndex); ByteArray buffer = bufferSize == BUFFER_SIZE ? bufferPool.poll() : new ByteArray((int) bufferSize); if (buffer == null) { buffer = new ByteArray(BUFFER_SIZE); } int totalRead = measurementsFileIS.read(buffer.array(), 0, (int) bufferSize); while (totalRead < bufferSize) { byte[] extraBuffer = new byte[(int) (bufferSize - totalRead)]; int readCount = measurementsFileIS.read(extraBuffer); System.arraycopy(extraBuffer, 0, buffer.array(), totalRead, readCount); totalRead += readCount; } readFileLock.release(); return buffer; } }; } private Runnable parseTemperaturesBlock(ByteArray buffer, int startIndex) { Runnable countAverageRun = () -> { int bufferIndex = startIndex; Map blockCityMeasurementMap = new HashMap<>(10_000); Map textPool = new HashMap<>(10_000); byte[] bufferArray = buffer.array(); try { while (bufferIndex < bufferArray.length) { bufferIndex = readNextLine(bufferIndex, bufferArray, blockCityMeasurementMap, textPool); } } catch (ArrayIndexOutOfBoundsException ex) { // Done reading and parsing the buffer } if (bufferArray.length == BUFFER_SIZE) bufferPool.add(buffer); mergeBlockResults(blockCityMeasurementMap); readFileLock.release(); }; return countAverageRun; } private int handleSplitLine(byte[] buffer) { int bufferIndex = readFirstLines(buffer); List lastLine = new ArrayList<>(100); // Store the last (partial) line of the block int tailIndex = buffer.length; byte car = buffer[--tailIndex]; while (car != '\n') { lastLine.add(0, car); car = buffer[--tailIndex]; } if (previousBlockLastLine.isEmpty()) { previousBlockLastLine = lastLine; return bufferIndex; } bufferIndex = readSplitLine(buffer); previousBlockLastLine = lastLine; return bufferIndex; } private int readSplitLine(byte[] buffer) { int bufferIndex = 0; byte car = buffer[bufferIndex++]; while (car != '\n') { previousBlockLastLine.add(car); car = buffer[bufferIndex++]; } previousBlockLastLine.add((byte) '\n'); byte[] splitLineBytes = new byte[previousBlockLastLine.size()]; for (int i = 0; i < splitLineBytes.length; i++) { splitLineBytes[i] = previousBlockLastLine.get(i); } readNextLine(0, splitLineBytes, cityMeasurementMap, new HashMap<>()); return bufferIndex; } private int readFirstLines(byte[] buffer) { if (precision >= 0) return 0; // not the first lines of the file int bufferIndex = 0; while (buffer[bufferIndex] == '#') { // read comments (like in weather_stations.csv) while (buffer[bufferIndex++] != '\n') { } } int startIndex = bufferIndex; int dotPos = bufferIndex; byte car = buffer[bufferIndex++]; while (car != '\n') { if (car == '.') { dotPos = bufferIndex; } car = buffer[bufferIndex++]; } precision = bufferIndex - dotPos - 1; int precisionLimit = (int) Math.pow(10, precision); precisionLimitTenth = precisionLimit * 10; return startIndex; } private int readNextLine(int bufferIndex, byte[] buffer, Map blockCityMeasurementMap, Map textPool) { int startLineIndex = bufferIndex; while (buffer[bufferIndex] != (byte) ';') { bufferIndex++; } // String city = new String(buffer, startLineIndex, bufferIndex - startLineIndex, StandardCharsets.UTF_8); Text city = Text.getByteText(buffer, startLineIndex, bufferIndex - startLineIndex, textPool); bufferIndex++; // skip ';' int temperature = readTemperature(buffer, bufferIndex); bufferIndex += precision + 3; // digit, dot and CR if (temperature < 0) { bufferIndex++; } if (temperature <= -precisionLimitTenth || temperature >= precisionLimitTenth) { bufferIndex++; } addTemperature(city, temperature, blockCityMeasurementMap); return bufferIndex; } private int readTemperature(byte[] buffer, int bufferIndex) { boolean negative = buffer[bufferIndex] == (byte) '-'; if (negative) { bufferIndex++; } byte digit = buffer[bufferIndex++]; int temperature = 0; while (digit != (byte) '\n') { temperature = temperature * 10 + (digit - (byte) '0'); digit = buffer[bufferIndex++]; if (digit == (byte) '.') { // Skip '.' digit = buffer[bufferIndex++]; } } if (negative) { temperature = -temperature; } return temperature; } private void addTemperature(Text city, int temperature, Map blockCityMeasurementMap) { IntSummaryStatistics measurement = blockCityMeasurementMap.get(city); if (measurement == null) { measurement = new IntSummaryStatistics(); blockCityMeasurementMap.put(city, measurement); } measurement.accept(temperature); } private void mergeBlockResults(Map blockCityMeasurementMap) { blockCityMeasurementMap.forEach((city, measurement) -> { cityMeasurementMap.compute(city, (town, currentMeasurement) -> { if (currentMeasurement == null) { return measurement; } currentMeasurement.combine(measurement); return currentMeasurement; }); }); } private void printTemperatureStatsByCity() { Set sortedCities = new TreeSet<>(cityMeasurementMap.keySet()); StringBuilder result = new StringBuilder(cityMeasurementMap.size() * 40); result.append('{'); sortedCities.forEach(city -> { IntSummaryStatistics measurement = cityMeasurementMap.get(city); result.append(city); result.append(getTemperatureStats(measurement)); }); if (!sortedCities.isEmpty()) { result.delete(result.length() - 2, result.length()); } result.append('}'); String temperaturesByCity = result.toString(); System.out.println(temperaturesByCity); } private String getTemperatureStats(IntSummaryStatistics measurement) { StringBuilder stats = new StringBuilder(19); stats.append('='); appendTemperature(stats, measurement.getMin()); stats.append('/'); int average = (int) Math.round(measurement.getAverage()); appendTemperature(stats, average); stats.append('/'); appendTemperature(stats, measurement.getMax()); stats.append(", "); return stats.toString(); } private void appendTemperature(StringBuilder resultBuilder, int temperature) { String temperatureAsText = String.valueOf(temperature); int minCharacters = precision + (temperature < 0 ? 2 : 1); for (int i = temperatureAsText.length(); i < minCharacters; i++) { temperatureAsText = temperature < 0 ? "-0" + temperatureAsText.substring(1) : "0" + temperatureAsText; } int dotPosition = temperatureAsText.length() - precision; resultBuilder.append(temperatureAsText.substring(0, dotPosition)); resultBuilder.append('.'); resultBuilder.append(temperatureAsText.substring(dotPosition)); } public static final void main(String... args) throws Exception { CalculateAverage_japplis cityTemperaturesCalculator = new CalculateAverage_japplis(); String measurementFile = args.length == 1 ? args[0] : DEFAULT_MEASUREMENT_FILE; cityTemperaturesCalculator.parseTemperatures(new File(measurementFile)); cityTemperaturesCalculator.printTemperatureStatsByCity(); } private class ByteArray { private byte[] array; private ByteArray(int size) { array = new byte[size]; } private byte[] array() { return array; } } private static class Text implements Comparable { private final byte[] textBytes; private final int hash; private String text; private Text(byte[] buffer, int startIndex, int length, int hash) { textBytes = new byte[length]; this.hash = hash; System.arraycopy(buffer, startIndex, textBytes, 0, length); } private static Text getByteText(byte[] buffer, int startIndex, int length, Map textPool) { int hash = hashCode(buffer, startIndex, length); Text textFromPool = textPool.get(hash); if (textFromPool == null || !Arrays.equals(buffer, startIndex, startIndex + length, textFromPool.textBytes, 0, length)) { Text newText = new Text(buffer, startIndex, length, hash); textPool.put(hash, newText); return newText; } return textFromPool; } private static int hashCode(byte[] buffer, int startIndex, int length) { int hash = 31; int endIndex = startIndex + length; for (int i = startIndex; i < endIndex; i++) { hash = 31 * hash + buffer[i]; } return hash; } @Override public int hashCode() { return hash; } @Override public boolean equals(Object other) { return other != null && hashCode() == other.hashCode() && other instanceof Text && Arrays.equals(textBytes, ((Text) other).textBytes); } @Override public int compareTo(Text other) { return toString().compareTo(other.toString()); } @Override public String toString() { if (text == null) { text = new String(textBytes, StandardCharsets.UTF_8); } return text; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jatingala.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.*; public class CalculateAverage_jatingala { private static final String FILE = "./measurements.txt"; public static void main(final String[] args) throws IOException { final int processorCount = Math.max(Runtime.getRuntime().availableProcessors(), 8); try (final RandomAccessFile randomAccessFile = new RandomAccessFile(FILE, "r")) { final long[][] chunks = getChunkPositions(randomAccessFile, processorCount); final Map result = Arrays.stream(chunks) .parallel() .map(chunk -> { try { final MappedByteBuffer mappedByteBuffer = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, chunk[0], chunk[1]); return consumeChunk(mappedByteBuffer); } catch (final Exception e) { System.out.println(e.getMessage()); return new HashMap(); } }) .reduce((a, b) -> { a.forEach((k, v) -> { final Statistics s = b.get(k); if (s != null) v.merge(s); }); b.forEach(a::putIfAbsent); return a; }) .orElseGet(Collections::emptyMap); System.out.println(new TreeMap<>(result)); } } private static long[][] getChunkPositions(final RandomAccessFile file, final int chunks) throws IOException { final long[][] result = new long[chunks][]; final long fileSize = file.length(); final long chunkSize = Math.ceilDiv(fileSize, chunks); long chunkStartPosition = 0; for (int i = 0; i < chunks; i++) { file.seek(Math.min(chunkStartPosition + chunkSize, fileSize)); while (file.getFilePointer() < fileSize && file.readByte() != '\n') { // find next newline, noop } // startPointer & length result[i] = new long[]{ chunkStartPosition, file.getFilePointer() - chunkStartPosition }; chunkStartPosition = file.getFilePointer(); } return result; } private static Map consumeChunk(final MappedByteBuffer mappedByteBuffer) { final Map statisticsMap = new HashMap<>(); while (mappedByteBuffer.hasRemaining()) { final String key = parseKey(mappedByteBuffer); final double value = parseNumber(mappedByteBuffer); statisticsMap.computeIfAbsent(key, _ -> new Statistics()).update(value); } return statisticsMap; } private static String parseKey(final MappedByteBuffer mappedByteBuffer) { final ByteArrayOutputStream keyBytes = new ByteArrayOutputStream(); while (mappedByteBuffer.hasRemaining()) { final byte val = mappedByteBuffer.get(); if (val == ';') break; keyBytes.write(val); } return keyBytes.toString(); } private static double parseNumber(final MappedByteBuffer mappedByteBuffer) { boolean negate = false; int temp = 0; while (mappedByteBuffer.hasRemaining()) { final byte val = mappedByteBuffer.get(); if (val == '\n') break; if (val == '-') { negate = true; continue; } if (val == '.') continue; temp = 10 * temp + (val - '0'); } return (negate ? -1 : 1) * (temp / 10.0); } private static class Statistics { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; private static double round(final double value) { return Math.round(value * 10.0) / 10.0; } public void update(final double reading) { this.min = Math.min(min, reading); this.max = Math.max(max, reading); this.sum += reading; ++count; } public void merge(final Statistics other) { this.min = Math.min(min, other.min); this.max = Math.max(max, other.max); this.sum += other.sum; this.count += other.count; } @Override public String toString() { return STR."\{round(min)}/\{round(sum / count)}/\{round(max)}"; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_javamak.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.util.stream.Collectors.*; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collector; import java.util.stream.Stream; public class CalculateAverage_javamak { private static final String FILE = "./measurements.txt"; private static record Measurement(String station, double value) { private Measurement(String[] parts) { this(parts[0], Double.parseDouble(parts[1])); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } ; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } public static void main(String[] args) throws IOException { // Map measurements1 = Files.lines(Paths.get(FILE)) // .map(l -> l.split(";")) // .collect(groupingBy(m -> m[0], averagingDouble(m -> Double.parseDouble(m[1])))); // // measurements1 = new TreeMap<>(measurements1.entrySet() // .stream() // .collect(toMap(e -> e.getKey(), e -> Math.round(e.getValue() * 10.0) / 10.0))); // System.out.println(measurements1); Collector collector = Collector.of( MeasurementAggregator::new, (a, m) -> { a.min = Math.min(a.min, m.value); a.max = Math.max(a.max, m.value); a.sum += m.value; a.count++; }, (agg1, agg2) -> { var res = new MeasurementAggregator(); res.min = Math.min(agg1.min, agg2.min); res.max = Math.max(agg1.max, agg2.max); res.sum = agg1.sum + agg2.sum; res.count = agg1.count + agg2.count; return res; }, agg -> new ResultRow(agg.min, agg.sum / agg.count, agg.max)); var path = Paths.get(FILE); var a = calcChunks(path).entrySet().parallelStream() .flatMap(entry -> getLinesFromFile(path, entry)) // read file for each chunk and get the lines .map(l -> new Measurement(l.split(";")))// convert each line to measurement object .collect(groupingBy(Measurement::station, collector)); Map measurements = new TreeMap<>(a); System.out.println(measurements); } private static Stream getLinesFromFile(Path path, Map.Entry entry) { try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) { channel.position(entry.getKey()); ByteBuffer buffer = ByteBuffer.allocate((int) (entry.getValue() - entry.getKey() + 1)); channel.read(buffer); String chunk = new String(buffer.array()); return Arrays.stream(chunk.split("\n")); } catch (IOException e) { throw new RuntimeException(e); } } private static Map calcChunks(Path path) throws IOException { long startPos = 0; Map retMap = new HashMap<>(); while (true) { long endPos = calculateEndPosition(path, startPos, 1000 * 5000); if (endPos == -1) { break; } long finalStartPos = startPos; retMap.put(finalStartPos, endPos); startPos = endPos + 1; } return retMap; } private static long calculateEndPosition(Path path, long startPos, long chunkSize) throws IOException { try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) { if (startPos >= channel.size()) { return -1; } long currentPos = startPos + chunkSize; if (currentPos >= channel.size()) { currentPos = channel.size() - 1; } channel.position(currentPos); ByteBuffer buffer = ByteBuffer.allocate(1024); int readBytes = channel.read(buffer); if (readBytes > 0) { for (int i = 0; i < readBytes; i++) { if (buffer.get(i) == '\n') { break; } currentPos++; } } return currentPos; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jbachorik.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Arrays; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; public class CalculateAverage_jbachorik { private static final class Key { final ByteBuffer bb; final int offset; final int len; final long v0, v1; final int hash; Key(ByteBuffer bb, int offset, int len, long v0, long v1, int hash) { this.bb = bb; this.offset = offset; this.len = len; this.v0 = v0; this.v1 = v1; this.hash = hash; } public boolean equals(int offset, int len, long v0, long v1) { // byte[] bytes = new byte[len]; // bb.get(offset, bytes); // String str = new String(bytes); if (((this.len ^ len) | (this.v0 ^ v0) | (this.v1 ^ v1)) != 0) { return false; } for (int i = 0; i < len - 8; i += 8) { if (bb.getLong(this.offset + i) != bb.getLong(offset + i)) { return false; } } return true; } @Override public String toString() { byte[] bytes = new byte[len]; bb.get(offset, bytes); return new String(bytes); } } private static final class Stats { long min; long max; long count; long sum; Stats() { min = Integer.MAX_VALUE; max = Integer.MIN_VALUE; count = 0; sum = 0; } Stats add(long value) { min = Math.min(min, value); max = Math.max(max, value); count++; sum += value; return this; } Stats merge(Stats other) { synchronized (this) { min = Math.min(min, other.min); max = Math.max(max, other.max); count += other.count; sum += other.sum; } return this; } @Override public String toString() { return String.format("%.1f/%.1f/%.1f", min / 10.0d, sum / (double) count / 10.0d, max / 10.0d); } } private static final class StatsMap { private static class StatsHolder { private final Key key; private final Stats stats; StatsHolder(Key slice, Stats stats) { this.key = slice; this.stats = stats; } @Override public String toString() { return "StatsHolder{" + "key=" + key + ", stats=" + stats + '}'; } } private static final int BUCKETS = 65536; private static final int BUCKET_SIZE = 16; private final StatsHolder[][] map = new StatsHolder[BUCKETS][BUCKET_SIZE]; public Stats getOrInsert(ByteBuffer buffer, int offset, int len, int idx, long v0, long v1) { StatsHolder[] bucket = map[idx]; int bucketOffset = 0; do { StatsHolder statsHolder = bucket[bucketOffset]; if (statsHolder == null) { Stats stats = new Stats(); bucket[bucketOffset] = new StatsHolder(new Key(buffer, offset, len, v0, v1, idx), stats); return stats; } if (statsHolder.key.equals(offset, len, v0, v1)) { return statsHolder.stats; } bucketOffset++; } while (bucketOffset < BUCKET_SIZE - 1); throw new Error("Bucket overflow"); } public void forEach(BiConsumer consumer) { for (StatsHolder[] bucket : map) { for (StatsHolder statsHolder : bucket) { if (statsHolder != null) { consumer.accept(statsHolder.key, statsHolder.stats); } } } } } private static final long newLinePattern = compilePattern((byte) '\n'); private static final long semiPattern = compilePattern((byte) ';'); public static void main(String[] args) throws Exception { int workers = Runtime.getRuntime().availableProcessors(); if (args.length == 1) { workers = Integer.parseInt(args[0]); } Map map = new TreeMap<>(); File f = new File("measurements.txt"); try (FileInputStream fis = new FileInputStream(f)) { FileChannel fc = fis.getChannel(); int granularity = 32 * 1024 * 1024; int targetWorkers = Math.min(Math.max(1, (int) (fc.size() / granularity)), workers); long chunkSize = fc.size() / targetWorkers; ExecutorService workerPool = Executors.newFixedThreadPool(workers); // System.out.println("Chunk size: " + chunkSize + ", workers: " + targetWorkers); for (ByteBuffer bb : mmap(fc, (int) chunkSize)) { workerPool.submit(() -> { try { StatsMap data = processChunk(bb); synchronized (map) { data.forEach((k, v) -> { String str = k.toString(); map.merge(str, v, Stats::merge); }); } } catch (Throwable t) { t.printStackTrace(); } }); } workerPool.shutdown(); workerPool.awaitTermination(1, TimeUnit.HOURS); } finally { // System.out.println("Keys: " + map.size()); System.out.println(map); } } // unrolled FNV-1a 64 bit hash private static final long fnv64OffsetBasis = 0xCBF29CE484222325L; private static final long fnv64Prime = 0x100000001B3L; private static StatsMap processChunk(ByteBuffer bb) { StatsMap map = new StatsMap(); int offset = 0; int limit = bb.limit(); int readLimit = limit - 8; long v0 = 0; long v1 = 0; long hashCode = fnv64OffsetBasis; int lastNewLine = -1; while (offset < limit) { if (offset > readLimit) { int over = offset - readLimit; v1 = bb.getLong(limit - 8); v1 = v1 << (over * 8); } else { v1 = bb.getLong(offset); } long x = preprocess(v1, newLinePattern); if (x != 0) { long value = 0; int valueLen = 0; int pos = Long.numberOfLeadingZeros(x) >>> 3; int yoffset = offset; int semiPos = firstInstance(v1, semiPattern); if (semiPos >= pos) { yoffset -= 8; semiPos = firstInstance(v0, semiPattern); // semiPos will be at least 3 (new line is in the upper word and the value has at most 5 bytes) // a 64 bit value can not be rotated by 64 bits to 'clear' the bits // instead, it must be rotated by at most 56 bits and then, if 64 bit rotation was requested, by 8 bits more int rot2 = (8 - semiPos) >>> 3; int rot1 = (7 - semiPos) + (~rot2 & 0x1); long mask = ((0xFFFFFFFFFFFFFFFFL << (rot1 * 8)) << (rot2 * 8)); rot2 = (8 - pos) >>> 3; rot1 = (7 - pos) + (~rot2 & 0x1); long newlineMask = ((0xFFFFFFFFFFFFFFFFL << (rot1 * 8)) << (rot2 * 8)); value = semiPos == 7 ? 0L : (v0 << (semiPos + 1) * 8); value |= ((v1 & newlineMask) >> (7 - semiPos) * 8); // right-align the value bytes // getting the number of trailing zeros is the easiest way to figure out the shift // should be sufficiently fast but ... int zeros = (Long.numberOfTrailingZeros(value) >>> 3); value = value >>> zeros * 8; valueLen = 8 - zeros; v0 = v0 & mask; } else { hashCode ^= v0; hashCode *= fnv64Prime; long valMask = (0xFFFFFFFFFFFFFFFFL << (7 - semiPos) * 8); v0 = v1 & valMask; value = v1 & ~valMask; value = value >> (8 - pos) * 8; valueLen = pos - semiPos - 1; } v1 = 0; hashCode ^= v0; hashCode *= fnv64Prime; int len = (yoffset + semiPos - 1) - lastNewLine; hashCode ^= len; hashCode *= fnv64Prime; // byte[] strBuf = new byte[len]; // bb.get(lastNewLine + 1, strBuf); // String str = new String(strBuf); // System.out.println("===> " + str + ": " + Long.toHexString(value) + " :: " + fastParse(value, valueLen)); // projection of the hash code to 32 bits -> 65k buckets long idx = ((hashCode & 0xFFFFFFFF00000000L) >> 32) ^ (hashCode & 0x00000000FFFFFFFFL); idx = ((idx & 0x00000000FFFF0000L) >> 16) ^ (idx & 0x000000000000FFFFL); map.getOrInsert(bb, lastNewLine + 1, len, (int) idx, v0, v1).add(fastParse(value, valueLen)); offset += pos + 1; lastNewLine = offset - 1; // reset the previous value v0 = 0; // reset the hash hashCode = fnv64OffsetBasis; } else { offset += 8; hashCode ^= v0; hashCode *= fnv64Prime; v0 = v1; } } return map; } private static final long fastParserMask = 0x3030303030303030L; private static int fastParse(long word, int len) { assert (len <= 5); int signChar = (int) (word >> ((len - 1) * 8)) & 0xFF; int sign = signChar ^ 0x2d; int base = ~(sign | -sign); int offset = (base >> 7) & 0x01; int multiplier = -(~((sign - 1) >> 31) | 0x1); int shift = (8 - len + offset) * 8; long mask = (0xFFFFFFFFFFFFFFFFL >>> shift); word = (word ^ fastParserMask) & mask; int v1 = (int) word & 0xff; // skip decimal point // int v2 = 10 * ((int) (word >> 8) & 0xff); int v3 = 10 * ((int) (word >> 16) & 0xff); int v4 = 100 * ((int) (word >> 24) & 0xff); // v5 is either the sign or not used return ((v1 + v3 + v4) * multiplier); } private static ByteBuffer[] mmap(FileChannel fc, int splitSize) throws Exception { if (fc.size() > splitSize && splitSize < 128) { throw new IllegalArgumentException("Split size must be at least 128 bytes"); } byte[] byteBuffer = new byte[128]; int chunks = (int) (fc.size() / splitSize) + 1; ByteBuffer[] buffers = new ByteBuffer[chunks]; long remaining = fc.size(); int count = 0; for (int j = 0; j < chunks; j++) { if (remaining > splitSize) { ByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, fc.size() - remaining, splitSize); buffer.get(splitSize - 128, byteBuffer, 0, 128); int adjust = -1; for (int i = 0; i < 128; i++) { if (byteBuffer[127 - i] == '\n') { adjust = i; break; } } assert (adjust != -1); int size = splitSize - adjust; buffers[j] = fc.map(FileChannel.MapMode.READ_ONLY, fc.size() - remaining, size); remaining -= size; count = j + 1; } else { count = j + 1; if (fc.size() < 8) { // slow-path ByteBuffer bb = ByteBuffer.allocate(8); fc.read(bb, 0); buffers[j] = bb; break; } buffers[j] = fc.map(FileChannel.MapMode.READ_ONLY, fc.size() - remaining, remaining); break; } } // System.out.println("Chunks: " + count); return count < chunks ? Arrays.copyOf(buffers, count) : buffers; } private static long compilePattern(byte byteToFind) { long pattern = byteToFind & 0xFFL; return pattern | (pattern << 8) | (pattern << 16) | (pattern << 24) | (pattern << 32) | (pattern << 40) | (pattern << 48) | (pattern << 56); } private static int firstInstance(long word, long pattern) { long input = word ^ pattern; long tmp = (input & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL; tmp = ~(tmp | input | 0x7F7F7F7F7F7F7F7FL); return Long.numberOfLeadingZeros(tmp) >>> 3; } private static long preprocess(long word, long pattern) { long input = word ^ pattern; long tmp = (input & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL; tmp = ~(tmp | input | 0x7F7F7F7F7F7F7F7FL); return tmp; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jeevjyot.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.lang.Math.round; import static java.util.stream.Collectors.*; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collector; public class CalculateAverage_jeevjyot { public static final String MEAUREMENT_FILE = "./measurements.txt"; public static void main(String[] args) throws IOException { Map result = new ConcurrentHashMap<>(); Files.lines(Path.of(MEAUREMENT_FILE)) .parallel() .forEach(s -> { var separatorIndex = s.indexOf(";"); var stationName = s.substring(0, separatorIndex); var temp = s.substring(separatorIndex + 1); result.computeIfAbsent(stationName, d -> new tempMeasurement(parseDoubleFast(temp))) .recordTemp(parseDoubleFast(temp)); }); TreeMap sortedStats = new TreeMap<>(result); System.out.println(sortedStats); } public static double parseDoubleFast(String str) { // Simple implementation - can be improved with more error checking and support for different formats boolean negative = false; double result = 0; int length = str.length(); int i = 0; if (str.charAt(0) == '-') { negative = true; i++; } for (; i < length; i++) { char c = str.charAt(i); if (c == '.') { int divisor = 1; for (i++; i < length; i++) { result += (double) (str.charAt(i) - '0') / (divisor *= 10); } break; } result = result * 10 + (c - '0'); } return negative ? -result : result; } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } public static class tempMeasurement { double minTemp; double maxTemp; double sum; int count; public tempMeasurement(double temString) { this.minTemp = temString; this.maxTemp = temString; this.sum = 0.0; this.count = 0; } public synchronized void recordTemp(Double temp) { this.minTemp = Math.min(minTemp, temp); this.maxTemp = Math.max(maxTemp, temp); sum += temp; count++; } double getAverage() { return round(sum) / count; } @Override public String toString() { return String.format("%.1f/%.1f/%.1f", round(minTemp), round(getAverage()), round(maxTemp)); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jerrinot.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel.MapMode; import java.util.*; import java.util.concurrent.atomic.AtomicLong; /** * I figured out it would be very hard to win the main competition of the One Billion Rows Challenge. * but I think this code has a good chance to win a special prize for the Ugliest Solution ever! :) * * Anyway, if you can make sense out of not exactly idiomatic Java code, and you enjoy pushing performance limits * then QuestDB - the fastest open-source time-series database - is hiring: https://questdb.io/careers/core-database-engineer/ *

* Credit *

* I stand on shoulders of giants. I wouldn't be able to code this without analyzing and borrowing from solutions of others. * People who helped me the most: *

    *
  • Thomas Wuerthinger (thomaswue): The munmap() trick and work-stealing. In both cases, I shameless copy-pasted their code. * Including SWAR for detecting new lines. Thomas also gave me helpful hints on how to detect register spilling issues.
  • *
  • Quan Anh Mai (merykitty): I borrowed their phenomenal branch-free parser.
  • *
  • Marko Topolnik (mtopolnik): I use a hashing function I saw in his code. It seems the produce good quality hashes * and it's next-level in speed. Marko joined the challenge before me and our discussions made me to join too!
  • *
  • Van Phu DO (abeobk): I saw the idea with simple lookup tables instead of complicated bit-twiddling in their code first.
  • *
  • Roy van Rijn (royvanrijn): I borrowed their SWAR code and initially their hash code impl
  • *
  • Francesco Nigro (franz1981): For our online discussions about performance. Both before and during this challenge. * Francesco gave me the idea to check register spilling.
  • *
*/ public class CalculateAverage_jerrinot { private static final Unsafe UNSAFE = unsafe(); private static final String MEASUREMENTS_TXT = "measurements.txt"; // todo: with hyper-threading enable we would be better of with availableProcessors / 2; // todo: validate the testing env. params. private static final int EXTRA_THREAD_COUNT = Runtime.getRuntime().availableProcessors() - 1; // private static final int THREAD_COUNT = 1; private static final long SEPARATOR_PATTERN = 0x3B3B3B3B3B3B3B3BL; private static final long NEW_LINE_PATTERN = 0x0A0A0A0A0A0A0A0AL; private static final int SEGMENT_SIZE = 4 * 1024 * 1024; // credits for the idea with lookup tables instead of bit-shifting: abeobk private static final long[] HASH_MASKS = new long[]{ 0x0000000000000000L, // semicolon is the first char 0x00000000000000ffL, 0x000000000000ffffL, 0x0000000000ffffffL, 0x00000000ffffffffL, 0x000000ffffffffffL, 0x0000ffffffffffffL, 0x00ffffffffffffffL, // semicolon is the last char 0xffffffffffffffffL // there is no semicolon at all }; private static final long[] ADVANCE_MASKS = new long[]{ 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L, 0xffffffffffffffffL, }; private static Unsafe unsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws Exception { // credits for spawning new workers: thomaswue if (args.length == 0 || !("--worker".equals(args[0]))) { spawnWorker(); return; } calculate(); } private static void spawnWorker() throws IOException { ProcessHandle.Info info = ProcessHandle.current().info(); ArrayList workerCommand = new ArrayList<>(); info.command().ifPresent(workerCommand::add); info.arguments().ifPresent(args -> workerCommand.addAll(Arrays.asList(args))); workerCommand.add("--worker"); new ProcessBuilder() .command(workerCommand) .inheritIO() .redirectOutput(ProcessBuilder.Redirect.PIPE) .start() .getInputStream() .transferTo(System.out); } static void calculate() throws Exception { final File file = new File(MEASUREMENTS_TXT); final long length = file.length(); try (var raf = new RandomAccessFile(file, "r")) { long fileStart = raf.getChannel().map(MapMode.READ_ONLY, 0, length, Arena.global()).address(); long fileEnd = fileStart + length; var globalCursor = new AtomicLong(fileStart); Processor[] processors = new Processor[EXTRA_THREAD_COUNT]; Thread[] threads = new Thread[EXTRA_THREAD_COUNT]; for (int i = 0; i < EXTRA_THREAD_COUNT; i++) { Processor processor = new Processor(fileStart, fileEnd, globalCursor); Thread thread = new Thread(processor); processors[i] = processor; threads[i] = thread; thread.start(); } Processor processor = new Processor(fileStart, fileEnd, globalCursor); processor.run(); var accumulator = new TreeMap(); processor.accumulateStatus(accumulator); for (int i = 0; i < EXTRA_THREAD_COUNT; i++) { Thread t = threads[i]; t.join(); processors[i].accumulateStatus(accumulator); } printResults(accumulator); } } private static void printResults(TreeMap accumulator) { var sb = new StringBuilder(10000); boolean first = true; for (Map.Entry statsEntry : accumulator.entrySet()) { if (first) { sb.append("{"); first = false; } else { sb.append(", "); } var value = statsEntry.getValue(); var name = statsEntry.getKey(); int min = value.min; int max = value.max; int count = value.count; long sum2 = value.sum; sb.append(String.format("%s=%.1f/%.1f/%.1f", name, min / 10.0, Math.round((double) sum2 / count) / 10.0, max / 10.0)); } sb.append('}'); System.out.println(sb); System.out.close(); } public static int ceilPow2(int i) { i--; i |= i >> 1; i |= i >> 2; i |= i >> 4; i |= i >> 8; i |= i >> 16; return i + 1; } private static class Processor implements Runnable { private static final int MAX_UNIQUE_KEYS = 10000; private static final int MAPS_SLOT_COUNT = ceilPow2(MAX_UNIQUE_KEYS); private static final int STATION_MAX_NAME_BYTES = 104; private static final long MAP_COUNT_OFFSET = 0; private static final long MAP_MIN_OFFSET = 4; private static final long MAP_MAX_OFFSET = 8; private static final long MAP_SUM_OFFSET = 12; private static final long MAP_LEN_OFFSET = 20; private static final long SLOW_MAP_NAME_OFFSET = 24; // private int longestChain = 0; private static final int SLOW_MAP_ENTRY_SIZE_BYTES = Integer.BYTES // count // 0 + Integer.BYTES // min // +4 + Integer.BYTES // max // +8 + Long.BYTES // sum // +12 + Integer.BYTES // station name len // +20 + Long.BYTES; // station name ptr // 24 private static final long FAST_MAP_NAME_PART1 = 24; private static final long FAST_MAP_NAME_PART2 = 32; private static final int FAST_MAP_ENTRY_SIZE_BYTES = Integer.BYTES // count // 0 + Integer.BYTES // min // +4 + Integer.BYTES // max // +8 + Long.BYTES // sum // +12 + Integer.BYTES // station name len // +20 + Long.BYTES // station name part 1 // 24 + Long.BYTES; // station name part 2 // 32 private static final int SLOW_MAP_SIZE_BYTES = MAPS_SLOT_COUNT * SLOW_MAP_ENTRY_SIZE_BYTES; private static final int FAST_MAP_SIZE_BYTES = MAPS_SLOT_COUNT * FAST_MAP_ENTRY_SIZE_BYTES; private static final int SLOW_MAP_MAP_NAMES_BYTES = MAX_UNIQUE_KEYS * STATION_MAX_NAME_BYTES; private static final int MAP_MASK = MAPS_SLOT_COUNT - 1; private final AtomicLong globalCursor; private long slowMap; private long slowMapNamesPtr; private long cursorA; private long endA; private long cursorB; private long endB; private HashMap stats = new HashMap<>(1000); private final long fileEnd; private final long fileStart; // credit: merykitty private long parseAndStoreTemperature(long startCursor, long baseEntryPtr, long word) { long countPtr = baseEntryPtr + MAP_COUNT_OFFSET; int cnt = UNSAFE.getInt(countPtr); UNSAFE.putInt(countPtr, cnt + 1); long minPtr = baseEntryPtr + MAP_MIN_OFFSET; long maxPtr = baseEntryPtr + MAP_MAX_OFFSET; long sumPtr = baseEntryPtr + MAP_SUM_OFFSET; int min = UNSAFE.getInt(minPtr); int max = UNSAFE.getInt(maxPtr); long sum = UNSAFE.getLong(sumPtr); final long negateda = ~word; final int dotPos = Long.numberOfTrailingZeros(negateda & 0x10101000); final long signed = (negateda << 59) >> 63; final long removeSignMask = ~(signed & 0xFF); final long digits = ((word & removeSignMask) << (28 - dotPos)) & 0x0F000F0F00L; final long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; final int temperature = (int) ((absValue ^ signed) - signed); sum += temperature; UNSAFE.putLong(sumPtr, sum); if (temperature > max) { UNSAFE.putInt(maxPtr, temperature); } if (temperature < min) { UNSAFE.putInt(minPtr, temperature); } return startCursor + (dotPos / 8) + 3; } private static long getDelimiterMask(final long word) { // credit royvanrijn final long match = word ^ SEPARATOR_PATTERN; return (match - 0x0101010101010101L) & (~match & 0x8080808080808080L); } void accumulateStatus(TreeMap accumulator) { for (Map.Entry entry : stats.entrySet()) { String name = entry.getKey(); CalculateAverage_jerrinot.StationStats localStats = entry.getValue(); CalculateAverage_jerrinot.StationStats globalStats = accumulator.get(name); if (globalStats == null) { accumulator.put(name, localStats); } else { accumulator.put(name, globalStats.mergeWith(localStats)); } } } Processor(long fileStart, long fileEnd, AtomicLong globalCursor) { this.globalCursor = globalCursor; this.fileEnd = fileEnd; this.fileStart = fileStart; } private void transferToHeap(long fastMap) { for (long baseAddress = slowMap; baseAddress < slowMap + SLOW_MAP_SIZE_BYTES; baseAddress += SLOW_MAP_ENTRY_SIZE_BYTES) { long len = UNSAFE.getInt(baseAddress + MAP_LEN_OFFSET); if (len == 0) { continue; } byte[] nameArr = new byte[(int) len]; long baseNameAddr = UNSAFE.getLong(baseAddress + SLOW_MAP_NAME_OFFSET); for (int i = 0; i < len; i++) { nameArr[i] = UNSAFE.getByte(baseNameAddr + i); } String name = new String(nameArr); int min = UNSAFE.getInt(baseAddress + MAP_MIN_OFFSET); int max = UNSAFE.getInt(baseAddress + MAP_MAX_OFFSET); int count = UNSAFE.getInt(baseAddress + MAP_COUNT_OFFSET); long sum = UNSAFE.getLong(baseAddress + MAP_SUM_OFFSET); stats.put(name, new CalculateAverage_jerrinot.StationStats(min, max, count, sum)); } for (long baseAddress = fastMap; baseAddress < fastMap + FAST_MAP_SIZE_BYTES; baseAddress += FAST_MAP_ENTRY_SIZE_BYTES) { long len = UNSAFE.getInt(baseAddress + MAP_LEN_OFFSET); if (len == 0) { continue; } byte[] nameArr = new byte[(int) len]; long baseNameAddr = baseAddress + FAST_MAP_NAME_PART1; for (int i = 0; i < len; i++) { nameArr[i] = UNSAFE.getByte(baseNameAddr + i); } String name = new String(nameArr); int min = UNSAFE.getInt(baseAddress + MAP_MIN_OFFSET); int max = UNSAFE.getInt(baseAddress + MAP_MAX_OFFSET); int count = UNSAFE.getInt(baseAddress + MAP_COUNT_OFFSET); long sum = UNSAFE.getLong(baseAddress + MAP_SUM_OFFSET); var v = stats.get(name); if (v == null) { stats.put(name, new CalculateAverage_jerrinot.StationStats(min, max, count, sum)); } else { stats.put(name, new CalculateAverage_jerrinot.StationStats(Math.min(v.min, min), Math.max(v.max, max), v.count + count, v.sum + sum)); } } } private void doOne(long cursor, long end, long fastMap) { while (cursor < end) { // it seems that when pulling just from a single chunk // then bit-twiddling is faster than lookup tables // hypothesis: when processing multiple things at once then LOAD latency is partially hidden // but when processing just one thing then it's better to keep things local as much as possible? maybe:) long start = cursor; long currentWord = UNSAFE.getLong(cursor); long mask = getDelimiterMask(currentWord); long firstWordMask = ((mask - 1) ^ mask) >>> 8; final long isMaskZeroA = ((mask | -mask) >>> 63) ^ 1; long ext = -isMaskZeroA; firstWordMask |= ext; long maskedFirstWord = currentWord & firstWordMask; int hash = hash(maskedFirstWord); int mapIndex = hash & MAP_MASK; while (mask == 0) { cursor += 8; currentWord = UNSAFE.getLong(cursor); mask = getDelimiterMask(currentWord); } final int delimiterByte = Long.numberOfTrailingZeros(mask); final long semicolon = cursor + (delimiterByte >> 3); final long maskedWord = currentWord & ((mask - 1) ^ mask) >>> 8; int len = (int) (semicolon - start); if (len > 15) { long baseEntryPtr = getOrCreateEntryBaseOffsetSlow(len, start, hash, maskedWord); long temperatureWord = UNSAFE.getLong(semicolon + 1); cursor = parseAndStoreTemperature(semicolon + 1, baseEntryPtr, temperatureWord); } else { long baseEntryPtr = getOrCreateEntryBaseOffsetFast(mapIndex, len, maskedWord, maskedFirstWord, fastMap); long temperatureWord = UNSAFE.getLong(semicolon + 1); cursor = parseAndStoreTemperature(semicolon + 1, baseEntryPtr, temperatureWord); } } } private static int hash(long word) { // credit: mtopolnik long seed = 0x51_7c_c1_b7_27_22_0a_95L; int rotDist = 17; // long hash = word; hash *= seed; hash = Long.rotateLeft(hash, rotDist); return (int) hash; } private static long nextNewLine(long prev) { // again: credits to @thomaswue for this code, literally copy'n'paste while (true) { long currentWord = UNSAFE.getLong(prev); long input = currentWord ^ NEW_LINE_PATTERN; long pos = (input - 0x0101010101010101L) & ~input & 0x8080808080808080L; if (pos != 0) { prev += Long.numberOfTrailingZeros(pos) >>> 3; break; } else { prev += 8; } } return prev; } @Override public void run() { long fastMap = allocateMem(); for (;;) { long startingPtr = globalCursor.addAndGet(SEGMENT_SIZE) - SEGMENT_SIZE; if (startingPtr >= fileEnd) { break; } setCursors(startingPtr); mainLoop(fastMap); doOne(cursorA, endA, fastMap); doOne(cursorB, endB, fastMap); } transferToHeap(fastMap); } private long allocateMem() { this.slowMap = UNSAFE.allocateMemory(SLOW_MAP_SIZE_BYTES); this.slowMapNamesPtr = UNSAFE.allocateMemory(SLOW_MAP_MAP_NAMES_BYTES); long fastMap = UNSAFE.allocateMemory(FAST_MAP_SIZE_BYTES); UNSAFE.setMemory(slowMap, SLOW_MAP_SIZE_BYTES, (byte) 0); UNSAFE.setMemory(fastMap, FAST_MAP_SIZE_BYTES, (byte) 0); UNSAFE.setMemory(slowMapNamesPtr, SLOW_MAP_MAP_NAMES_BYTES, (byte) 0); return fastMap; } private void mainLoop(long fastMap) { while (cursorA < endA && cursorB < endB) { long currentWordA = UNSAFE.getLong(cursorA); long currentWordB = UNSAFE.getLong(cursorB); long delimiterMaskA = getDelimiterMask(currentWordA); long delimiterMaskB = getDelimiterMask(currentWordB); long candidateWordA = UNSAFE.getLong(cursorA + 8); long candidateWordB = UNSAFE.getLong(cursorB + 8); long startA = cursorA; long startB = cursorB; int trailingZerosA = Long.numberOfTrailingZeros(delimiterMaskA) >> 3; int trailingZerosB = Long.numberOfTrailingZeros(delimiterMaskB) >> 3; long advanceMaskA = ADVANCE_MASKS[trailingZerosA]; long advanceMaskB = ADVANCE_MASKS[trailingZerosB]; long wordMaskA = HASH_MASKS[trailingZerosA]; long wordMaskB = HASH_MASKS[trailingZerosB]; long maskedMaskA = advanceMaskA & 8; long maskedMaskB = advanceMaskB & 8; long negAdvanceMaskA = ~advanceMaskA; long negAdvanceMaskB = ~advanceMaskB; cursorA += maskedMaskA; cursorB += maskedMaskB; long nextWordA = (advanceMaskA & candidateWordA) | (negAdvanceMaskA & currentWordA); long nextWordB = (advanceMaskB & candidateWordB) | (negAdvanceMaskB & currentWordB); delimiterMaskA = getDelimiterMask(nextWordA); delimiterMaskB = getDelimiterMask(nextWordB); boolean slowA = delimiterMaskA == 0; boolean slowB = delimiterMaskB == 0; trailingZerosA = Long.numberOfTrailingZeros(delimiterMaskA) >> 3; trailingZerosB = Long.numberOfTrailingZeros(delimiterMaskB) >> 3; boolean slowSome = (slowA || slowB); long maskedFirstWordA = wordMaskA & currentWordA; long maskedFirstWordB = wordMaskB & currentWordB; int hashA = hash(maskedFirstWordA); int hashB = hash(maskedFirstWordB); currentWordA = nextWordA; currentWordB = nextWordB; if (slowSome) { doSlow(fastMap, delimiterMaskA, currentWordA, delimiterMaskB, currentWordB, startA, startB, hashA, hashB, slowA, maskedFirstWordA, slowB, maskedFirstWordB); } else { final long semicolonA = cursorA + trailingZerosA; final long semicolonB = cursorB + trailingZerosB; long digitStartA = semicolonA + 1; long digitStartB = semicolonB + 1; long lastWordMaskA = HASH_MASKS[trailingZerosA]; long lastWordMaskB = HASH_MASKS[trailingZerosB]; long temperatureWordA = UNSAFE.getLong(digitStartA); long temperatureWordB = UNSAFE.getLong(digitStartB); final long maskedLastWordA = currentWordA & lastWordMaskA; final long maskedLastWordB = currentWordB & lastWordMaskB; int lenA = (int) (semicolonA - startA); int lenB = (int) (semicolonB - startB); int mapIndexA = hashA & MAP_MASK; int mapIndexB = hashB & MAP_MASK; long baseEntryPtrA; long baseEntryPtrB; baseEntryPtrA = getOrCreateEntryBaseOffsetFast(mapIndexA, lenA, maskedLastWordA, maskedFirstWordA, fastMap); baseEntryPtrB = getOrCreateEntryBaseOffsetFast(mapIndexB, lenB, maskedLastWordB, maskedFirstWordB, fastMap); cursorA = parseAndStoreTemperature(digitStartA, baseEntryPtrA, temperatureWordA); cursorB = parseAndStoreTemperature(digitStartB, baseEntryPtrB, temperatureWordB); } } } private void doSlow(long fastMap, long delimiterMaskA, long currentWordA, long delimiterMaskB, long currentWordB, long startA, long startB, int hashA, int hashB, boolean slowA, long maskedFirstWordA, boolean slowB, long maskedFirstWordB) { int trailingZerosB; int trailingZerosA; while (delimiterMaskA == 0) { cursorA += 8; currentWordA = UNSAFE.getLong(cursorA); delimiterMaskA = getDelimiterMask(currentWordA); } while (delimiterMaskB == 0) { cursorB += 8; currentWordB = UNSAFE.getLong(cursorB); delimiterMaskB = getDelimiterMask(currentWordB); } trailingZerosA = Long.numberOfTrailingZeros(delimiterMaskA) >> 3; trailingZerosB = Long.numberOfTrailingZeros(delimiterMaskB) >> 3; final long semicolonA = cursorA + trailingZerosA; final long semicolonB = cursorB + trailingZerosB; long digitStartA = semicolonA + 1; long digitStartB = semicolonB + 1; long lastWordMaskA = HASH_MASKS[trailingZerosA]; long lastWordMaskB = HASH_MASKS[trailingZerosB]; long temperatureWordA = UNSAFE.getLong(digitStartA); long temperatureWordB = UNSAFE.getLong(digitStartB); final long maskedLastWordA = currentWordA & lastWordMaskA; final long maskedLastWordB = currentWordB & lastWordMaskB; int lenA = (int) (semicolonA - startA); int lenB = (int) (semicolonB - startB); int mapIndexA = hashA & MAP_MASK; int mapIndexB = hashB & MAP_MASK; long baseEntryPtrA; long baseEntryPtrB; if (slowA) { baseEntryPtrA = getOrCreateEntryBaseOffsetSlow(lenA, startA, hashA, maskedLastWordA); } else { baseEntryPtrA = getOrCreateEntryBaseOffsetFast(mapIndexA, lenA, maskedLastWordA, maskedFirstWordA, fastMap); } if (slowB) { baseEntryPtrB = getOrCreateEntryBaseOffsetSlow(lenB, startB, hashB, maskedLastWordB); } else { baseEntryPtrB = getOrCreateEntryBaseOffsetFast(mapIndexB, lenB, maskedLastWordB, maskedFirstWordB, fastMap); } cursorA = parseAndStoreTemperature(digitStartA, baseEntryPtrA, temperatureWordA); cursorB = parseAndStoreTemperature(digitStartB, baseEntryPtrB, temperatureWordB); } private void setCursors(long current) { // Credit for the whole work-stealing scheme: @thomaswue // I have totally stolen it from him. I changed the order a bit to suite my taste better, // but it's his code long segmentStart; if (current == fileStart) { segmentStart = current; } else { segmentStart = nextNewLine(current) + 1; } long segmentEnd = nextNewLine(Math.min(fileEnd - 1, current + SEGMENT_SIZE)); long size = (segmentEnd - segmentStart) / 2; long mid = nextNewLine(segmentStart + size); cursorA = segmentStart; endA = mid; cursorB = mid + 1; endB = segmentEnd; } private static long getOrCreateEntryBaseOffsetFast(int mapIndexA, int lenA, long maskedLastWord, long maskedFirstWord, long fastMap) { for (;;) { long basePtr = mapIndexA * FAST_MAP_ENTRY_SIZE_BYTES + fastMap; long namePart1 = UNSAFE.getLong(basePtr + FAST_MAP_NAME_PART1); long namePart2 = UNSAFE.getLong(basePtr + FAST_MAP_NAME_PART2); if (namePart1 == maskedFirstWord && namePart2 == maskedLastWord) { return basePtr; } long lenPtr = basePtr + MAP_LEN_OFFSET; int len = UNSAFE.getInt(lenPtr); if (len == 0) { return newEntryFast(lenA, maskedLastWord, maskedFirstWord, lenPtr, basePtr); } mapIndexA = ++mapIndexA & MAP_MASK; } } private static long newEntryFast(int lenA, long maskedLastWord, long maskedFirstWord, long lenPtr, long basePtr) { UNSAFE.putInt(lenPtr, lenA); // todo: this could be a single putLong() UNSAFE.putInt(basePtr + MAP_MAX_OFFSET, Integer.MIN_VALUE); UNSAFE.putInt(basePtr + MAP_MIN_OFFSET, Integer.MAX_VALUE); UNSAFE.putLong(basePtr + FAST_MAP_NAME_PART1, maskedFirstWord); UNSAFE.putLong(basePtr + FAST_MAP_NAME_PART2, maskedLastWord); return basePtr; } private long getOrCreateEntryBaseOffsetSlow(int lenA, long startPtr, int hash, long maskedLastWord) { long fullLen = lenA & ~7L; long mapIndexA = hash & MAP_MASK; for (;;) { long basePtr = mapIndexA * SLOW_MAP_ENTRY_SIZE_BYTES + slowMap; long lenPtr = basePtr + MAP_LEN_OFFSET; long namePtr = basePtr + SLOW_MAP_NAME_OFFSET; int len = UNSAFE.getInt(lenPtr); if (len == lenA) { namePtr = UNSAFE.getLong(basePtr + SLOW_MAP_NAME_OFFSET); if (nameMatchSlow(startPtr, namePtr, fullLen, maskedLastWord)) { return basePtr; } } else if (len == 0) { UNSAFE.putLong(namePtr, slowMapNamesPtr); UNSAFE.putInt(lenPtr, lenA); UNSAFE.putInt(basePtr + MAP_MAX_OFFSET, Integer.MIN_VALUE); UNSAFE.putInt(basePtr + MAP_MIN_OFFSET, Integer.MAX_VALUE); UNSAFE.copyMemory(startPtr, slowMapNamesPtr, lenA); long alignedLen = (lenA & ~7L) + 8; slowMapNamesPtr += alignedLen; return basePtr; } mapIndexA = ++mapIndexA & MAP_MASK; } } private static boolean nameMatchSlow(long start, long namePtr, long fullLen, long maskedLastWord) { long offset; for (offset = 0; offset < fullLen; offset += 8) { if (UNSAFE.getLong(start + offset) != UNSAFE.getLong(namePtr + offset)) { return false; } } long maskedWordInMap = UNSAFE.getLong(namePtr + fullLen); return (maskedWordInMap == maskedLastWord); } } record StationStats(int min, int max, int count, long sum) { StationStats mergeWith(StationStats other) { return new StationStats(Math.min(min, other.min), Math.max(max, other.max), count + other.count, sum + other.sum); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jgrateron.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; public class CalculateAverage_jgrateron { private static final String FILE = "./measurements.txt"; private static final int MAX_LENGTH_LINE = 255; private static final int MAX_BUFFER = 1024 * 8; private static boolean DEBUG = false; public static int DECENAS[] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }; public static int CENTENAS[] = { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900 }; public record Particion(long offset, long size) { } /* * Divide el archivo segun el nro de cores de la PC * La division se debe recalcular hasta encontrar un \n o \r (enter o return) */ public static List dividirArchivo(File archivo) throws IOException { var particiones = new ArrayList(); var buffer = new byte[MAX_LENGTH_LINE]; var length = archivo.length(); int cores = Runtime.getRuntime().availableProcessors(); var sizeParticion = length / cores; if (sizeParticion > MAX_BUFFER) { var ini = 0l; try (var rfile = new RandomAccessFile(archivo, "r")) { for (;;) { var size = sizeParticion; var pos = ini + size; if (pos > length) { pos = length - 1; size = length - ini; } rfile.seek(pos); int count = rfile.read(buffer); if (count == -1) { break; } for (int i = 0; i < count; i++) { size++; if (buffer[i] == '\n' || buffer[i] == '\r') { break; } } var particion = new Particion(ini, size); particiones.add(particion); if (count != buffer.length) { break; } ini += size; } } } else { particiones.add(new Particion(0, length)); } return particiones; } /* * cambiar el locale para que el separador decimal sea punto y no coma * crear un hilo por cada particion * totalizar las mediciones por cada hilo * ordenar y mostrar */ public static void main(String[] args) throws InterruptedException, IOException { Locale.setDefault(Locale.US); var startTime = System.nanoTime(); var archivo = new File(FILE); var tareas = new ArrayList(); var totalMediciones = new HashMap(); var particiones = dividirArchivo(archivo); for (var p : particiones) { var hilo = Thread.ofVirtual().start(() -> { try (var miTarea = new MiTarea(archivo, p)) { var mediciones = miTarea.calcularMediciones(); for (var entry : mediciones.entrySet()) { Medicion medicion; synchronized (totalMediciones) { medicion = totalMediciones.get(entry.getKey()); if (medicion == null) { totalMediciones.put(entry.getKey(), entry.getValue()); medicion = entry.getValue(); } } synchronized (medicion) { if (!medicion.equals(entry.getValue())) { var otraMed = entry.getValue(); medicion.update(otraMed.count, otraMed.tempMin, otraMed.tempMax, otraMed.tempSum); } } } } catch (IOException e) { System.exit(-1); } }); tareas.add(hilo); } Comparator> comparar = (a, b) -> { return a.getValue().getNombreEstacion().compareTo(b.getValue().getNombreEstacion()); }; for (var hilo : tareas) { hilo.join(); } var result = totalMediciones.entrySet().stream()// .sorted(comparar) .map(e -> e.getValue().toString())// .collect(Collectors.joining(", ")); System.out.println("{" + result + "}"); if (DEBUG) { System.out.println("Total: " + (System.nanoTime() - startTime) / 1000000 + "ms"); } } /* * Clase Index para reutilizar al realizar un get en el Map */ static class Index { private int hash; private byte[] data; private int fromIndex; private int length; public Index() { this.hash = 0; } public Index(byte data[], int fromIndex, int length) { this.data = data; this.fromIndex = fromIndex; this.length = length; this.hash = calcHashCode(length, data, fromIndex, length); } public void setData(byte data[], int fromIndex, int length) { this.data = data; this.fromIndex = fromIndex; this.length = length; this.hash = calcHashCode(length, data, fromIndex, length); } /* * Calcula el hash de cada estacion, * variation of Daniel J Bernstein's algorithm */ private int calcHashCode(int result, byte[] a, int fromIndex, int length) { int end = fromIndex + length; for (int i = fromIndex; i < end; i++) { result = ((result << 5) + result) ^ a[i]; } return result; } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } var otro = (Index) obj; return Arrays.equals(this.data, this.fromIndex, this.fromIndex + this.length, otro.data, otro.fromIndex, otro.fromIndex + otro.length); } } /* * Clase para procesar el archivo a la particion que corresponde * RandomAccessFile permite dezplazar el puntero de lectura del archivo * Tenemos un Map para guardar las estadisticas y un map para guardar los * nombres de las estaciones */ static class MiTarea implements AutoCloseable { private final RandomAccessFile rFile; private long maxRead; private Index index = new Index(); private Map mediciones = new HashMap<>(); public MiTarea(File file, Particion particion) throws IOException { rFile = new RandomAccessFile(file, "r"); maxRead = particion.size; rFile.seek(particion.offset); } @Override public void close() throws IOException { rFile.close(); } /* * Lee solo su particion * Divide el buffer por lineas usando los separadores \n o \r (enter o return) * obtiene la posicion de separacion ";" de la estacion y su temperatura * calcula el hash, convierte a double y actualiza las estadisticas */ public Map calcularMediciones() throws IOException { var buffer = new byte[MAX_BUFFER];// buffer para lectura en el archivo var rest = new byte[MAX_LENGTH_LINE];// Resto que sobra en cada lectura del buffer var lenRest = 0;// Longitud que sobró en cada lectura del buffer var totalRead = 0l; // Total bytes leidos for (;;) { if (totalRead == maxRead) { break; } long numBytes = rFile.read(buffer); if (numBytes == -1) { break; } numBytes = totalRead + numBytes > maxRead ? maxRead - totalRead : numBytes; totalRead += numBytes; int pos = 0; int len = 0; int idx = 0; int semicolon = 0; while (pos < numBytes) { var b = buffer[pos]; if (b == '\n' || b == '\r') { if (lenRest > 0) { // concatenamos el sobrante anterior con la nueva linea System.arraycopy(buffer, idx, rest, lenRest, len); len += lenRest; semicolon = buscarSemicolon(rest, len); lenRest = 0; updateMediciones(rest, 0, semicolon); } else { updateMediciones(buffer, idx, semicolon); } idx = pos + 1; len = 0; semicolon = 0; } else { if (b == ';') { semicolon = len; } len++; } pos++; } if (len > 0) { System.arraycopy(buffer, idx, rest, 0, len); lenRest = len; } } return mediciones; } /* * Buscamos en reverso ya que el ; esta mas cerca de numero que la estacion * ademas el minimo numero 0.0 asi que quitamos tres mas */ public int buscarSemicolon(byte data[], int len) { for (int i = len - 4; i >= 0; i--) { if (data[i] == ';') { return i; } } return 0; } /* * Busca una medicion por su hash y crea o actualiza la temperatura */ public void updateMediciones(byte data[], int pos, int semicolon) { var temp = strToInt(data, pos, semicolon); index.setData(data, pos, semicolon); var medicion = mediciones.get(index); if (medicion == null) { var estacion = new byte[semicolon]; System.arraycopy(data, pos, estacion, 0, semicolon); medicion = new Medicion(estacion, 1, temp, temp, temp); mediciones.put(new Index(estacion, 0, semicolon), medicion); } else { medicion.update(1, temp, temp, temp); } } /* * convierte de un arreglo de bytes a integer */ public int strToInt(byte linea[], int idx, int posSeparator) { int pos = idx + posSeparator + 1; boolean esNegativo = linea[pos] == '-'; pos = esNegativo ? pos + 1 : pos; int number = linea[pos + 1] == '.' ? DECENAS[(linea[pos] - 48)] + linea[pos + 2] - 48 : CENTENAS[(linea[pos] - 48)] + DECENAS[(linea[pos + 1] - 48)] + (linea[pos + 3] - 48); return esNegativo ? -number : number; } } /* * Clase para reservar las estadisticas por estacion */ static class Medicion { private int count; private int tempMin; private int tempMax; private int tempSum; private byte estacion[]; private String nombreEstacion; public Medicion(byte estacion[], int count, int tempMin, int tempMax, int tempSum) { super(); this.estacion = estacion; this.count = count; this.tempMin = tempMin; this.tempMax = tempMax; this.tempSum = tempSum; } public void update(int count, int tempMin, int tempMax, int tempSum) { this.count += count; this.tempMin = Math.min(tempMin, this.tempMin); this.tempMax = Math.max(tempMax, this.tempMax); this.tempSum += tempSum; } public double round(double number) { return Math.round(number) / 10.0; } public String getNombreEstacion() { if (nombreEstacion == null) { nombreEstacion = new String(estacion); } return nombreEstacion; } @Override public String toString() { var min = round(tempMin); var mid = round(1.0 * tempSum / count); var max = round(tempMax); var nombre = getNombreEstacion(); return "%s=%.1f/%.1f/%.1f".formatted(nombre, min, mid, max); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jincongho.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; /** * Changelog (based on Macbook Pro Intel i7 6-cores 2.6GHz): * * Initial 40000 ms * Parse key as byte vs string 30000 ms * Parse temp as fixed vs double 15000 ms * HashMap optimization 10000 ms * Simd + reduce memory copy 8000 ms * */ public class CalculateAverage_jincongho { private static final String FILE = "./measurements.txt"; private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } /** * Vectorization utilities with 1BRC-specific optimizations */ protected static class VectorUtils { // key length is usually less than 32 bytes, having more is just expensive public static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_256; /** Vectorized field delimiter search **/ public static int findDelimiter(MemorySegment data, long offset) { return ByteVector.fromMemorySegment(VectorUtils.BYTE_SPECIES, data, offset, ByteOrder.nativeOrder()) .compare(VectorOperators.EQ, ';') .firstTrue(); } /** Vectorized Hashing (explicit vectorization seems slower, overkill?) **/ // private static int[] HASH_ARRAY = initHashArray(); // private static final IntVector HASH_VECTOR = IntVector.fromArray(IntVector.SPECIES_256, HASH_ARRAY, 0); // private static final int HASH_ACCUM = HASH_ARRAY[0] * 31; // // private static int[] initHashArray() { // int[] x = new int[IntVector.SPECIES_256.length()]; // x[x.length - 1] = 1; // for (int i = x.length - 2; i >= 0; i--) // x[i] = x[i + 1] * 31; // // return x; // } /** * Ref: https://github.com/PaulSandoz/vector-api-dev-live-10-2021/blob/main/src/main/java/jmh/BytesHashcode.java * * Essentially we are doing this calculation: * h = h * 31 * 31 * 31 * 31 * 31 * 31 * 31 * 31 + * a[i + 0] * 31 * 31 * 31 * 31 * 31 * 31 * 31 + * a[i + 1] * 31 * 31 * 31 * 31 * 31 * 31 + * a[i + 2] * 31 * 31 * 31 * 31 * 31 + * a[i + 3] * 31 * 31 * 31 * 31 + * a[i + 4] * 31 * 31 * 31 + * a[i + 5] * 31 * 31 + * a[i + 6] * 31 + * a[i + 7]; */ // public static int hashCode(MemorySegment array, long offset, short length) { // int h = 1; // long i = offset, loopBound = offset + ByteVector.SPECIES_64.loopBound(length), tailBound = offset + length; // for (; i < loopBound; i += ByteVector.SPECIES_64.length()) { // // load 8 bytes, into a 64-bit vector // ByteVector b = ByteVector.fromMemorySegment(ByteVector.SPECIES_64, array, i, ByteOrder.nativeOrder()); // // convert 8 bytes into 8 ints (hashing calculation needs int!) // IntVector x = (IntVector) b.castShape(IntVector.SPECIES_256, 0); // h = h * HASH_ACCUM + x.mul(HASH_VECTOR).reduceLanes(VectorOperators.ADD); // } // // for (; i < tailBound; i++) { // h = 31 * h + array.get(ValueLayout.JAVA_BYTE, i); // } // return h; // } // scalar implementation // public static int hashCode(final MemorySegment array, final long offset, final short length) { // final long limit = offset + length; // int h = 1; // for (long i = offset; i < limit; i++) { // h = 31 * h + UNSAFE.getByte(array.address() + i); // } // return h; // } // fxhash public static int hashCode(final MemorySegment array, final long offset, final short length) { final int seed = 0x9E3779B9; final int rotate = 5; int x, y; if (length >= Integer.BYTES) { x = UNSAFE.getInt(array.address() + offset); y = UNSAFE.getInt(array.address() + offset + length - Integer.BYTES); } else { x = UNSAFE.getByte(array.address() + offset); y = UNSAFE.getByte(array.address() + offset + length - Byte.BYTES); } return (Integer.rotateLeft(x * seed, rotate) ^ y) * seed; } /** Vectorized Key Comparison **/ private static boolean notEquals(MemorySegment a, long aOffset, MemorySegment b, long bOffset, short length, VectorSpecies BYTE_SPECIES) { final long aLimit = aOffset + length, bLimit = bOffset + length; // main loop long loopBound = bOffset + BYTE_SPECIES.loopBound(length); for (; bOffset < loopBound; aOffset += BYTE_SPECIES.length(), bOffset += BYTE_SPECIES.length()) { ByteVector av = ByteVector.fromMemorySegment(BYTE_SPECIES, a, aOffset, ByteOrder.nativeOrder() /* , BYTE_SPECIES.indexInRange(aOffset, Math.min(aOffset + BYTE_SPECIES.length(), aLimit)) */); ByteVector bv = ByteVector.fromMemorySegment(BYTE_SPECIES, b, bOffset, ByteOrder.nativeOrder() /* , BYTE_SPECIES.indexInRange(bOffset, Math.min(bOffset + BYTE_SPECIES.length(), bLimit)) */); if (av.compare(VectorOperators.NE, bv).anyTrue()) return true; } // tail cleanup - load last N bytes with mask if (bOffset < bLimit) { ByteVector av = ByteVector.fromMemorySegment(BYTE_SPECIES, a, aOffset, ByteOrder.nativeOrder(), BYTE_SPECIES.indexInRange(aOffset, aLimit)); ByteVector bv = ByteVector.fromMemorySegment(BYTE_SPECIES, b, bOffset, ByteOrder.nativeOrder(), BYTE_SPECIES.indexInRange(bOffset, bLimit)); if (av.compare(VectorOperators.NE, bv).anyTrue()) return true; } return false; } // scalar implementation // private static boolean equals(byte[] a, int aOffset, byte[] b, int bOffset, int len) { // while (bOffset < len) // if (a[aOffset++] != b[bOffset++]) // return false; // return true; // } } /** * Measurement Hash Table (for each partition) * Uses contiguous byte array to optimize for cache-line (hopefully) * * Each entry: * - KEYS: keyLength (2 bytes) + key (100 bytes) * - VALUES: min (2 bytes) + max (2 bytes) + count (4 bytes) + sum ( 8 bytes) */ protected static class PartitionAggr { private static int MAP_SIZE = 1 << 14; // 2^14 = 16384, closes to 10000 private static int KEY_SIZE = 128; // key length (2 bytes) + key (100 bytes) private static int KEY_MASK = (MAP_SIZE - 1); private static int VALUE_SIZE = 16; // min (2 bytes) + max ( 2 bytes) + count (4 bytes) + sum (8 bytes) private MemorySegment KEYS = Arena.ofShared().allocate(MAP_SIZE * KEY_SIZE, 64); private MemorySegment VALUES = Arena.ofShared().allocate(MAP_SIZE * VALUE_SIZE, 16); public PartitionAggr() { // init min and max final long limit = VALUES.address() + (MAP_SIZE * VALUE_SIZE); for (long offset = VALUES.address(); offset < limit; offset += VALUE_SIZE) { UNSAFE.putShort(offset, Short.MAX_VALUE); UNSAFE.putShort(offset + 2, Short.MIN_VALUE); } } public void update(MemorySegment key, long keyStart, short keyLength, int keyHash, short value) { int index = keyHash & KEY_MASK; long keyOffset = KEYS.address() + (index * KEY_SIZE); while (((UNSAFE.getShort(keyOffset) != keyLength) || VectorUtils.notEquals(KEYS, ((index * KEY_SIZE) + 2), key, keyStart, keyLength, VectorUtils.BYTE_SPECIES))) { if (UNSAFE.getShort(keyOffset) == 0) { // put key UNSAFE.putShort(keyOffset, keyLength); MemorySegment.copy(key, keyStart, KEYS, (index * KEY_SIZE) + 2, keyLength); break; } else { index = (index + 1) & KEY_MASK; keyOffset = KEYS.address() + (index * KEY_SIZE); } } long valueOffset = VALUES.address() + (index * VALUE_SIZE); UNSAFE.putShort(valueOffset, (short) Math.min(UNSAFE.getShort(valueOffset), value)); valueOffset += 2; UNSAFE.putShort(valueOffset, (short) Math.max(UNSAFE.getShort(valueOffset), value)); valueOffset += 2; UNSAFE.putInt(valueOffset, UNSAFE.getInt(valueOffset) + 1); valueOffset += 4; UNSAFE.putLong(valueOffset, UNSAFE.getLong(valueOffset) + value); } public void mergeTo(ResultAggr result) { long keyOffset; short keyLength; for (int i = 0; i < MAP_SIZE; i++) { // extract key keyOffset = KEYS.address() + (i * KEY_SIZE); if ((keyLength = UNSAFE.getShort(keyOffset)) == 0) continue; // extract values (if key is not null) final long valueOffset = VALUES.address() + (i * VALUE_SIZE); result.compute(new ResultAggr.ByteKey(KEYS, (i * KEY_SIZE) + 2, keyLength), (k, v) -> { if (v == null) { v = new ResultAggr.Measurement(); } v.min = (short) Math.min(UNSAFE.getShort(valueOffset), v.min); v.max = (short) Math.max(UNSAFE.getShort(valueOffset + 2), v.max); v.count += UNSAFE.getInt(valueOffset + 4); v.sum += UNSAFE.getLong(valueOffset + 8); return v; }); } } } /** * Measurement Aggregation (for all partitions) * Simple Concurrent Hash Table so all partitions can merge concurrently */ protected static class ResultAggr extends HashMap { public static class ByteKey implements Comparable { private final MemorySegment data; private final long offset; private final short length; private String str; public ByteKey(MemorySegment data, long offset, short length) { this.data = data; this.offset = offset; this.length = length; } @Override public boolean equals(Object other) { return (length == ((ByteKey) other).length) && !VectorUtils.notEquals(data, offset, ((ByteKey) other).data, ((ByteKey) other).offset, length, VectorUtils.BYTE_SPECIES); } @Override public int hashCode() { return VectorUtils.hashCode(data, offset, length); } @Override public String toString() { if (str == null) { // finally has to do a copy! byte[] copy = new byte[length]; MemorySegment.copy(data, offset, MemorySegment.ofArray(copy), 0, length); str = new String(copy, StandardCharsets.UTF_8); } return str; } @Override public int compareTo(ByteKey o) { return toString().compareTo(o.toString()); } } protected static class Measurement { public short min = Short.MAX_VALUE; public short max = Short.MIN_VALUE; public int count = 0; public long sum = 0; @Override public String toString() { return ((double) min / 10) + "/" + (Math.round((1.0 * sum) / count) / 10.0) + "/" + ((double) max / 10); } } public ResultAggr(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); } public Map toSorted() { return new TreeMap(this); } } protected static class Partition implements Runnable { private final MemorySegment data; private long offset; private final long limit; private final PartitionAggr result; public Partition(MemorySegment data, long offset, long limit, PartitionAggr result) { this.data = data; this.offset = offset; this.limit = limit; this.result = result; } @Override public void run() { // measurement parsing final PartitionAggr aggr = this.result; // main loop (vectorized) final long loopLimit = limit - (VectorUtils.BYTE_SPECIES.length() * Math.ceilDiv(100, VectorUtils.BYTE_SPECIES.length()) + Long.BYTES); while (offset < loopLimit) { long offsetStart = offset; // find station name upto ";" int found; do { found = VectorUtils.findDelimiter(data, offset); offset += found; } while (found == VectorUtils.BYTE_SPECIES.length()); short stationLength = (short) (offset - offsetStart); int stationHash = VectorUtils.hashCode(data, offsetStart, stationLength); // find measurement upto "\n" (credit: merykitty) long numberBits = UNSAFE.getLong(data.address() + ++offset); final long invNumberBits = ~numberBits; final int decimalSepPos = Long.numberOfTrailingZeros(invNumberBits & 0x10101000); int shift = 28 - decimalSepPos; long signed = (invNumberBits << 59) >> 63; long designMask = ~(signed & 0xFF); long digits = ((numberBits & designMask) << shift) & 0x0F000F0F00L; long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; short fixed = (short) ((absValue ^ signed) - signed); offset += (decimalSepPos >>> 3) + 3; // update measurement aggr.update(data, offsetStart, stationLength, stationHash, fixed); } // tail loop (simple) while (offset < limit) { long offsetStart = offset; // find station name upto ";" short stationLength = 0; while (UNSAFE.getByte(data.address() + offset++) != ';') stationLength++; int stationHash = VectorUtils.hashCode(data, offsetStart, stationLength); // find measurement upto "\n" byte tempBuffer = UNSAFE.getByte(data.address() + offset++); boolean isNegative = (tempBuffer == '-'); short fixed = (short) (isNegative ? 0 : (tempBuffer - '0')); while (true) { tempBuffer = UNSAFE.getByte(data.address() + offset++); if (tempBuffer == '.') { fixed = (short) (fixed * 10 + (UNSAFE.getByte(data.address() + offset) - '0')); offset += 2; break; } fixed = (short) (fixed * 10 + (tempBuffer - '0')); } fixed = isNegative ? (short) -fixed : fixed; // update measurement aggr.update(data, offsetStart, stationLength, stationHash, fixed); } // measurement result collection // aggr.mergeTo(result); } } public static void main(String[] args) throws IOException, InterruptedException { // long startTime = System.currentTimeMillis(); try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(Path.of(FILE), EnumSet.of(StandardOpenOption.READ)); Arena arena = Arena.ofShared()) { // scan data MemorySegment data = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size(), arena); final int processors = Runtime.getRuntime().availableProcessors(); // partition split long[] partition = new long[processors + 1]; long partitionSize = Math.ceilDiv(data.byteSize(), processors); for (int i = 0; i < processors; i++) { partition[i + 1] = partition[i] + partitionSize; if (partition[i + 1] >= data.byteSize()) { partition[i + 1] = data.byteSize(); break; } // note: vectorize this made performance worse :( while (UNSAFE.getByte(data.address() + partition[i + 1]++) != '\n') ; } // partition aggregation var threadList = new Thread[processors]; PartitionAggr[] partAggrs = new PartitionAggr[processors]; for (int i = 0; i < processors; i++) { if (partition[i] == data.byteSize()) break; partAggrs[i] = new PartitionAggr(); threadList[i] = new Thread(new Partition(data, partition[i], partition[i + 1], partAggrs[i])); threadList[i].start(); } // result ResultAggr result = new ResultAggr(1 << 14, 1); for (int i = 0; i < processors; i++) { if (partition[i] == data.byteSize()) break; threadList[i].join(); partAggrs[i].mergeTo(result); } System.out.println(result.toSorted()); } // long elapsed = System.currentTimeMillis() - startTime; // System.out.println("Elapsed: " + ((double) elapsed / 1000.0)); } /** Unit Tests **/ public static void testMain(String[] args) { testHashCode(); testNotEquals(); } private static void testHashCode() { // test key length from 1 to 100 for (int i = 1; i <= 100; i++) { byte[] array = new byte[i]; for (int j = 0; j < i; j++) array[j] = (byte) j; // compare with java default implementation assertTrue(VectorUtils.hashCode(MemorySegment.ofArray(array), 0, (short) i) == Arrays.hashCode(array)); } } private static void testNotEquals() { byte[] a = new byte[128]; byte[] b = new byte[128]; // all equals for (int i = 1; i < 100; i++) { a[(i + 2) - 1] = 0; b[i - 1] = 0; a[(i + 2)] = 10; b[i] = 10; assertTrue(!VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_64)); assertTrue(!VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_128)); assertTrue(!VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_256)); assertTrue(!VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_512)); } // one el not equals for (int i = 1; i < 100; i++) { a[(i + 2) - 1] = 0; b[i - 1] = 0; a[(i + 2)] = 20; b[i] = 10; assertTrue(VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_64)); assertTrue(VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_128)); assertTrue(VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_256)); assertTrue(VectorUtils.notEquals(MemorySegment.ofArray(a), 2, MemorySegment.ofArray(b), 0, (short) 100, ByteVector.SPECIES_512)); } } private static void assertTrue(boolean condition) { if (!condition) { throw new RuntimeException("Failed test"); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jonathanaotearoa.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ForkJoinPool; import java.util.stream.Collectors; import java.util.stream.Stream; public class CalculateAverage_jonathanaotearoa { public static final Unsafe UNSAFE; static { try { final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(STR."Error getting instance of \{Unsafe.class.getName()}"); } } private static final int WORD_BYTES = Long.BYTES; private static final Path FILE_PATH = Path.of("./measurements.txt"); private static final Path SAMPLE_DIR_PATH = Path.of("./src/test/resources/samples"); private static final byte MAX_LINE_BYTES = 107; private static final byte NEW_LINE_BYTE = '\n'; private static final long SEPARATOR_XOR_MASK = 0x3b3b3b3b3b3b3b3bL; // A mask where the 4th bit of the 5th, 6th and 7th bytes is set to 1. // Leverages the fact that the 4th bit of a digit byte will 1. // Whereas the 4th bit of the decimal point byte will be 0. // Assumes little endianness. private static final long DECIMAL_POINT_MASK = 0x10101000L; // This mask performs two tasks: // Sets the right-most and 3 left-most bytes to zero. // Given a temp value be at most 5 bytes in length, .e.g -99.9, we can safely ignore the last 3 bytes. // Subtracts 48, i.e. the UFT-8 value offset, from the digits bytes. // As a result, '0' (48) becomes 0, '1' (49) becomes 1, and so on. private static final long TEMP_DIGITS_MASK = 0x0f000f0f00L; public static void main(final String[] args) throws IOException { assert ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN : "Big endian byte order is not supported"; System.out.println(resultsToString(processFile(FILE_PATH))); } /** * A custom version of AbstractMap's toString() method. *

* This should be more performant as we can: *

    *
  • Set the initial capacity of the string builder
  • *
  • Append double values directly, which avoids string creation
  • *
*

* * @param results the results. * @return a string representation of the results. */ private static String resultsToString(final Map results) { final Iterator> i = results.entrySet().iterator(); if (!i.hasNext()) { System.out.println("{}"); } // Capacity based the output for measurements.txt. final StringBuilder sb = new StringBuilder(1100).append('{'); while (i.hasNext()) { Map.Entry e = i.next(); sb.append(e.getKey()) .append('=') .append(e.getValue().getMin()) .append('/') .append(e.getValue().getMean()) .append('/') .append(e.getValue().getMax()); if (i.hasNext()) { sb.append(',').append(' '); } } sb.append('}'); return sb.toString(); } /** * Processes the specified file. *

* Extracted from the main method for testability. *

* * @param filePath the path of the file we want to process. * @return a sorted map of station data keyed by station name. * @throws IOException if an error occurs. */ private static SortedMap processFile(final Path filePath) throws IOException { assert filePath != null : "filePath cannot be null"; assert Files.isRegularFile(filePath) : STR."\{filePath.toAbsolutePath()} is not a valid file"; try (final FileChannel fc = FileChannel.open(filePath, StandardOpenOption.READ)) { final long fileSize = fc.size(); if (fileSize < WORD_BYTES) { // The file size is less than our word size. // Keep it simple and fall back to non-performant processing. return processTinyFile(fc, fileSize); } return processFile(fc, fileSize); } } /** * An unoptimised method for processing a tiny file. *

* Handling tiny files in a separate method reduces the complexity of {@link #processFile(FileChannel, long)}. *

* * @param fc the file channel to read from. * @param fileSize the file size in bytes. * @return a sorted map of station data keyed by station name. * @throws IOException if an error occurs reading from the file channel. */ private static SortedMap processTinyFile(final FileChannel fc, final long fileSize) throws IOException { final ByteBuffer byteBuffer = ByteBuffer.allocate((int) fileSize); fc.read(byteBuffer); return new String(byteBuffer.array(), StandardCharsets.UTF_8) .lines() .map(line -> line.trim().split(";")) .map(tokens -> { final String stationName = tokens[0]; final short temp = Short.parseShort(tokens[1].replace(".", "")); return new SimpleStationData(stationName, temp); }) .collect(Collectors.toMap( sd -> sd.name, sd -> sd, TemperatureData::merge, TreeMap::new)); } /** * An optimised method for processing files > {@link Long#BYTES} in size. * * @param fc the file channel to map into memory. * @param fileSize the file size in bytes. * @return a sorted map of station data keyed by station name. * @throws IOException if an error occurs mapping the file channel into memory. */ private static SortedMap processFile(final FileChannel fc, final long fileSize) throws IOException { assert fileSize >= WORD_BYTES : STR."File size cannot be less than word size \{WORD_BYTES}, but was \{fileSize}"; try (final Arena arena = Arena.ofConfined()) { final long fileAddress = fc.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, arena).address(); return createChunks(fileAddress, fileSize) .parallel() .map(CalculateAverage_jonathanaotearoa::processChunk) .flatMap(Repository::entries) .collect(Collectors.toMap( StationData::getName, sd -> sd, TemperatureData::merge, TreeMap::new)); } } /** * Divides the file into chunks that can be processed in parallel. *

* If dividing the file into {@link ForkJoinPool#getCommonPoolParallelism() parallelism} chunks would result in a * chunk size less than the maximum line size in bytes, then a single chunk is returned for the entire file. *

* * @param fileAddress the address of the file. * @param fileSize the size of the file in bytes. * @return a stream of chunks. */ private static Stream createChunks(final long fileAddress, final long fileSize) { // The number of cores - 1. final int parallelism = ForkJoinPool.getCommonPoolParallelism(); final long chunkStep = fileSize / parallelism; final long lastFileByteAddress = fileAddress + fileSize - 1; if (chunkStep < MAX_LINE_BYTES) { // We're dealing with a small file, return a single chunk. return Stream.of(new Chunk(fileAddress, lastFileByteAddress, true)); } final Chunk[] chunks = new Chunk[parallelism]; long startAddress = fileAddress; for (int i = 0, n = parallelism - 1; i < n; i++) { // Find end of the *previous* line. // We know there's a previous line in this chunk because chunkStep >= MAX_LINE_BYTES. // The last chunk may be slightly bigger than the others. // For a 1 billion line file, this has zero impact. long lastByteAddress = startAddress + chunkStep; while (UNSAFE.getByte(lastByteAddress) != NEW_LINE_BYTE) { lastByteAddress--; } // We've found the end of the previous line. chunks[i] = new Chunk(startAddress, lastByteAddress, false); startAddress = ++lastByteAddress; } // The remaining bytes are assigned to the last chunk. chunks[chunks.length - 1] = (new Chunk(startAddress, lastFileByteAddress, true)); return Stream.of(chunks); } /** * Does the work of processing a chunk. * * @param chunk the chunk to process. * @return a repository containing the chunk's station data. */ private static Repository processChunk(final Chunk chunk) { final Repository repo = new Repository(); long address = chunk.startAddress; while (address <= chunk.lastByteAddress) { // Read station name. long nameAddress = address; long nameWord; long separatorMask; int nameHash = 1; while (true) { nameWord = chunk.getWord(address); // Based on the Hacker's Delight "Find First 0-Byte" branch-free, 5-instruction, algorithm. // See also https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord final long separatorXorResult = nameWord ^ SEPARATOR_XOR_MASK; // If the separator is not present, all bits in the mask will be zero. // If the separator is present, the first bit of the corresponding byte in the mask will be 1. separatorMask = (separatorXorResult - 0x0101010101010101L) & (~separatorXorResult & 0x8080808080808080L); if (separatorMask == 0) { address += Long.BYTES; // Multiplicative hashing, as per Arrays.hashCode(). // We could use XOR here, but it "might" produce more collisions. nameHash = 31 * nameHash + Long.hashCode(nameWord); } else { break; } } // We've found the separator. // We only support little endian, so we use the *trailing* number of zeros to get the number of name bits. final int numberOfNameBits = Long.numberOfTrailingZeros(separatorMask) & ~7; final int numberOfNameBytes = numberOfNameBits >> 3; final long separatorAddress = address + numberOfNameBytes; if (numberOfNameBytes > 0) { // Truncate the word, so we only have the portion before the separator, i.e. the name bytes. final int bitsToDiscard = Long.SIZE - numberOfNameBits; // Little endian. final long truncatedNameWord = (nameWord << bitsToDiscard) >>> bitsToDiscard; nameHash = 31 * nameHash + Long.hashCode(truncatedNameWord); } final long tempAddress = separatorAddress + 1; final long tempWord = chunk.getWord(tempAddress); // "0" in UTF-8 is 48, which is 00110000 in binary. // The first 4 bits of any UTF-8 digit byte are therefore 0011. // Get the position of the decimal point... // "." in UTF-8 is 46, which is 00101110 in binary. // We can therefore use the 4th bit to check which byte is the decimal point. final int decimalPointIndex = Long.numberOfTrailingZeros(~tempWord & DECIMAL_POINT_MASK) >> 3; // Check if we've got a negative or positive number... // "-" in UTF-8 is 45, which is 00101101 in binary. // As per above, we use the 4th bit to check if the word contains a positive, or negative, temperature. // If the temperature is negative, the value of "sign" will be -1. If it's positive, it'll be 0. final long sign = (~tempWord << 59) >> 63; // Create a mask that zeros out the minus-sign byte, if present. // Little endian, i.e. the minus sign is the right-most byte. final long signMask = ~(sign & 0xFF); // To get the temperature value, we left-shift the digit bytes into the following, known, positions. // 0x00 0x00 0x00 0x00 0x00 // Because we're ANDing with the sign mask, if the value only has a single integer-part digit, the right-most one will be zero. final int leftShift = (3 - decimalPointIndex) * Byte.SIZE; final long digitsWord = ((tempWord & signMask) << leftShift) & TEMP_DIGITS_MASK; // Get the unsigned int value. final byte b100 = (byte) (digitsWord >> 8); final byte b10 = (byte) (digitsWord >> 16); final byte b1 = (byte) (digitsWord >> 32); final short unsignedTemp = (short) (b100 * 100 + b10 * 10 + b1); final short temp = (short) ((unsignedTemp + sign) ^ sign); final byte nameSize = (byte) (separatorAddress - nameAddress); repo.addTemp(nameHash, nameAddress, nameSize, temp); // Calculate the address of the next line. address = tempAddress + decimalPointIndex + 3; } return repo; } /** * Represents a portion of a file containing 1 or more whole lines. * * @param startAddress the memory address of the first byte. * @param lastByteAddress the memory address of the last byte. * @param lastWordAddress the memory address of the last whole word. * @param isLast whether this is the last chunk. */ private record Chunk(long startAddress, long lastByteAddress, long lastWordAddress, boolean isLast) { public Chunk(final long startAddress, final long lastByteAddress, final boolean isLast) { this(startAddress, lastByteAddress, lastByteAddress - (Long.BYTES - 1), isLast); assert lastByteAddress > startAddress : STR."lastByteAddress \{lastByteAddress} must be > startAddress \{startAddress}"; assert lastWordAddress >= startAddress : STR."lastWordAddress \{lastWordAddress} must be >= startAddress \{startAddress}"; } /** * Gets an 8 byte word from this chunk. *

* If the specified address is greater than {@link Chunk#lastWordAddress} and {@link Chunk#isLast}, the word * will be truncated. This ensures we never read beyond the end of the file. *

* * @param address the address of the word we want. * @return the word at the specified address. */ public long getWord(final long address) { assert address >= startAddress : STR."address must be >= startAddress \{startAddress}, but was \{address}"; assert address < lastByteAddress : STR."address must be < lastByteAddress \{lastByteAddress}, but was \{address}"; if (isLast && address > lastWordAddress) { // Make sure we don't read beyond the end of the file and potentially crash the JVM. final long word = UNSAFE.getLong(lastWordAddress); final int bytesToDiscard = (int) (address - lastWordAddress); // As with elsewhere, this assumes little endianness. return word >>> (bytesToDiscard << 3); } return UNSAFE.getLong(address); } } /** * Abstract class encapsulating temperature data. */ private static abstract class TemperatureData { private short min; private short max; private long sum; private int count; protected TemperatureData(final short temp) { min = max = temp; sum = temp; count = 1; } void addTemp(final short temp) { if (temp < min) { min = temp; } else if (temp > max) { max = temp; } sum += temp; count++; } TemperatureData merge(final TemperatureData other) { if (other.min < min) { min = other.min; } if (other.max > max) { max = other.max; } sum += other.sum; count += other.count; return this; } double getMin() { return round(((double) min) / 10.0); } double getMax() { return round(((double) max) / 10.0); } double getMean() { return round((((double) sum) / 10.0) / count); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } } /** * For use with tiny files. * * @see CalculateAverage_jonathanaotearoa#processTinyFile(FileChannel, long). */ private static final class SimpleStationData extends TemperatureData implements Comparable { private final String name; SimpleStationData(final String name, final short temp) { super(temp); this.name = name; } @Override public int compareTo(final SimpleStationData other) { return name.compareTo(other.name); } } private static final class StationData extends TemperatureData implements Comparable { private final int nameHash; private final long nameAddress; private final byte nameSize; private String name; StationData(final int nameHash, final long nameAddress, final byte nameSize, final short temp) { super(temp); this.nameAddress = nameAddress; this.nameSize = nameSize; this.nameHash = nameHash; } @Override public int compareTo(final StationData other) { return getName().compareTo(other.getName()); } String getName() { if (name == null) { final byte[] nameBytes = new byte[nameSize]; UNSAFE.copyMemory(null, nameAddress, nameBytes, UNSAFE.arrayBaseOffset(nameBytes.getClass()), nameSize); name = new String(nameBytes, StandardCharsets.UTF_8); } return name; } } /** * Open addressing, linear probing, hash map repository. */ private static final class Repository { private static final int CAPACITY = 100_003; private static final int LAST_INDEX = CAPACITY - 1; private final StationData[] table; public Repository() { this.table = new StationData[CAPACITY]; } /** * Adds a station temperature value to this repository. * * @param nameHash the station name hash. * @param nameAddress the station name address in memory. * @param nameSize the station name size in bytes. * @param temp the temperature value. */ public void addTemp(final int nameHash, final long nameAddress, final byte nameSize, short temp) { final int index = findIndex(nameHash, nameAddress, nameSize); if (table[index] == null) { table[index] = new StationData(nameHash, nameAddress, nameSize, temp); } else { table[index].addTemp(temp); } } public Stream entries() { return Arrays.stream(table).filter(Objects::nonNull); } private int findIndex(int nameHash, final long nameAddress, final byte nameSize) { // Think about replacing modulo. // https://lemire.me/blog/2018/08/20/performance-of-ranged-accesses-into-arrays-modulo-multiply-shift-and-masks/ int index = (nameHash & 0x7FFFFFFF) % CAPACITY; while (isCollision(index, nameHash, nameAddress, nameSize)) { index = index == LAST_INDEX ? 0 : index + 1; } return index; } private boolean isCollision(final int index, final long nameHash, final long nameAddress, final byte nameSize) { final StationData existing = table[index]; if (existing == null) { return false; } if (nameHash != existing.nameHash) { return true; } if (nameSize != existing.nameSize) { return true; } // Last resort; check if the names are the same. // This is real performance hit :( return !isMemoryEqual(nameAddress, existing.nameAddress, nameSize); } /** * Checks if two locations in memory have the same value. * * @param address1 the address of the first location. * @param address2 the address of the second locations. * @param size the number of bytes to check for equality. * @return true if both addresses contain the same bytes. */ private static boolean isMemoryEqual(final long address1, final long address2, final byte size) { // Checking 1 byte at a time, so we can bail as early as possible. for (int offset = 0; offset < size; offset++) { final byte b1 = UNSAFE.getByte(address1 + offset); final byte b2 = UNSAFE.getByte(address2 + offset); if (b1 != b2) { return false; } } return true; } } /** * Helper for running tests without blowing away the main measurements.txt file. * Saves regenerating the 1 billion line file after each test run. * Enable assertions in the IDE run config. */ public static final class TestRunner { public static void main(String[] args) throws IOException { final StringBuilder testResults = new StringBuilder(); try (DirectoryStream dirStream = Files.newDirectoryStream(SAMPLE_DIR_PATH, "*.txt")) { dirStream.forEach(filePath -> { testResults.append(STR."Testing '\{filePath.getFileName()}'... "); final String expectedResultFileName = filePath.getFileName().toString().replace(".txt", ".out"); try { final String expected = Files.readString(SAMPLE_DIR_PATH.resolve(expectedResultFileName)); final SortedMap results = processFile(filePath); // Appending \n to the results string to mimic println(). final String actual = STR."\{resultsToString(results)}\n"; if (actual.equals(expected)) { testResults.append("Passed\n"); } else { testResults.append("Failed. Actual output does not match expected\n"); } } catch (IOException e) { throw new RuntimeException(STR."Error testing '\{filePath.getFileName()}"); } }); } finally { System.out.println(testResults); } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jotschi.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout.OfByte; import java.lang.foreign.ValueLayout.OfChar; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.stream.Collectors; public class CalculateAverage_jotschi { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException { var filename = args.length == 0 ? FILE : args[0]; parseFile(filename); } @SuppressWarnings("preview") private static void parseFile(String filename) throws IOException { var file = new File(filename); RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); FileChannel fileChannel = randomAccessFile.getChannel(); MemorySegment memSeg = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size(), Arena.global()); var results = getFileSegments(memSeg).stream().map(segment -> { var resultMap = new ByteArrayToResultMap2(); long segmentEnd = segment.end(); MemorySegment slice = memSeg.asSlice(segment.start(), segmentEnd - segment.start()); // Up to 100 characters for a city name var buffer = new byte[100]; int startLine; int pos = 0; long limit = slice.byteSize(); while ((startLine = pos) < limit) { int currentPosition = startLine; byte b; int offset = 0; int hash = 0; while (currentPosition != segmentEnd && (b = slice.get(OfByte.JAVA_BYTE, currentPosition++)) != ';') { buffer[offset++] = b; hash = 31 * hash + b; } int temp; int negative = 1; // Inspired by @yemreinci to unroll this even further if (slice.get(OfByte.JAVA_BYTE, currentPosition) == '-') { negative = -1; currentPosition++; } if (slice.get(OfByte.JAVA_BYTE, currentPosition + 1) == '.') { temp = negative * ((slice.get(OfByte.JAVA_BYTE, currentPosition) - '0') * 10 + (slice.get(OfByte.JAVA_BYTE, currentPosition + 2) - '0')); currentPosition += 3; } else { temp = negative * ((slice.get(OfByte.JAVA_BYTE, currentPosition) - '0') * 100 + ((slice.get(OfByte.JAVA_BYTE, currentPosition + 1) - '0') * 10 + (slice.get(OfByte.JAVA_BYTE, currentPosition + 3) - '0'))); currentPosition += 4; } if (slice.get(OfByte.JAVA_BYTE, currentPosition) == '\r') { currentPosition++; } currentPosition++; resultMap.putOrMerge(buffer, 0, offset, temp / 10.0, hash); pos = currentPosition; } return resultMap; }).parallel().flatMap(partition -> partition.getAll().stream()) .collect(Collectors.toMap(e -> new String(e.key()), Entry2::value, CalculateAverage_jotschi::merge, TreeMap::new)); System.out.println(results); } private static List getFileSegments(MemorySegment memSeg) throws IOException { int numberOfSegments = Runtime.getRuntime().availableProcessors(); long fileSize = memSeg.byteSize(); long segmentSize = fileSize / numberOfSegments; List segments = new ArrayList<>(numberOfSegments); // Pointless to split small files if (segmentSize < 1_000_000) { segments.add(new FileSegment2(0, fileSize)); return segments; } // Split the file up into even segments that match up with the CPU core count // so that each core can process a segment of the file. // The findSegment call ensures that the segment terminates with a newline. for (int i = 0; i < numberOfSegments; i++) { long segStart = i * segmentSize; long segEnd = (i == numberOfSegments - 1) ? fileSize : segStart + segmentSize; segStart = findSegment(i, 0, memSeg, segStart, segEnd); segEnd = findSegment(i, numberOfSegments - 1, memSeg, segEnd, fileSize); segments.add(new FileSegment2(segStart, segEnd)); } return segments; } private static Result2 merge(Result2 v, Result2 value) { return merge(v, value.min, value.max, value.sum, value.count); } private static Result2 merge(Result2 v, double value, double value1, double value2, long value3) { v.min = Math.min(v.min, value); v.max = Math.max(v.max, value1); v.sum += value2; v.count += value3; return v; } private static long findSegment(int i, int skipSegment, MemorySegment memSeg, long location, long fileSize) throws IOException { if (i != skipSegment) { long remaining = fileSize - location; int bufferSize = remaining < 64 ? (int) remaining : 64; MemorySegment slice = memSeg.asSlice(location, bufferSize); for (int offset = 0; offset < slice.byteSize(); offset++) { if (slice.get(OfChar.JAVA_BYTE, offset) == '\n') { return location + offset + 1; } } } return location; } } class Result2 { double min, max, sum; long count; Result2(double value) { min = max = sum = value; this.count = 1; } @Override public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } double round(double v) { return Math.round(v * 10.0) / 10.0; } } record Pair2(int slot, Result2 slotValue) { } record Entry2(byte[] key, Result2 value) { } record FileSegment2(long start, long end) { } class ByteArrayToResultMap2 { public static final int MAPSIZE = 1024 * 128; Result2[] slots = new Result2[MAPSIZE]; byte[][] keys = new byte[MAPSIZE][]; public void putOrMerge(byte[] key, int offset, int size, double temp, int hash) { int slot = hash & (slots.length - 1); var slotValue = slots[slot]; // Linear probe for open slot while (slotValue != null && (keys[slot].length != size || !Arrays.equals(keys[slot], 0, size, key, offset, size))) { slot = (slot + 1) & (slots.length - 1); slotValue = slots[slot]; } Result2 value = slotValue; if (value == null) { slots[slot] = new Result2(temp); byte[] bytes = new byte[size]; System.arraycopy(key, offset, bytes, 0, size); keys[slot] = bytes; } else { value.min = Math.min(value.min, temp); value.max = Math.max(value.max, temp); value.sum += temp; value.count += 1; } } // Get all pairs public List getAll() { List result = new ArrayList<>(slots.length); for (int i = 0; i < slots.length; i++) { Result2 slotValue = slots[i]; if (slotValue != null) { result.add(new Entry2(keys[i], slotValue)); } } return result; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_jparera.java ================================================ //COMPILE_OPTIONS -source 21 --enable-preview --add-modules jdk.incubator.vector //RUNTIME_OPTIONS --enable-preview --add-modules jdk.incubator.vector /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; import java.util.TreeMap; import java.util.function.Function; import java.util.stream.Collectors; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorSpecies; import jdk.incubator.vector.VectorOperators; public class CalculateAverage_jparera { private static final String FILE = "./measurements.txt"; private static final VarHandle BYTE_HANDLE = MethodHandles .memorySegmentViewVarHandle(ValueLayout.JAVA_BYTE); private static final VarHandle INT_HANDLE = MethodHandles .memorySegmentViewVarHandle(ValueLayout.JAVA_INT_UNALIGNED); private static final VarHandle LONG_LE_HANDLE = MethodHandles .memorySegmentViewVarHandle(ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN)); private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED; private static final int BYTE_SPECIES_LANES = BYTE_SPECIES.length(); private static final ByteOrder NATIVE_ORDER = ByteOrder.nativeOrder(); private static final byte LF = '\n'; private static final byte SEPARATOR = ';'; private static final byte DECIMAL_SEPARATOR = '.'; private static final byte NEG = '-'; public static void main(String[] args) throws IOException, InterruptedException { try (var fc = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { try (var arena = Arena.ofShared()) { var fs = fc.map(MapMode.READ_ONLY, 0, fc.size(), arena); var cpus = Runtime.getRuntime().availableProcessors(); var output = chunks(fs, cpus).stream() .parallel() .map(Chunk::parse) .flatMap(List::stream) .collect(Collectors.toMap( Entry::key, Function.identity(), Entry::merge, TreeMap::new)); System.out.println(output); } } } private static List chunks(MemorySegment ms, int splits) { long fileSize = ms.byteSize(); long expectedChunkSize = Math.ceilDiv(fileSize, splits); var chunks = new ArrayList(); long offset = 0; while (offset < fileSize) { var end = Math.min(offset + expectedChunkSize, fileSize); while (end < fileSize && (byte) BYTE_HANDLE.get(ms, end++) != LF) { } long len = end - offset; chunks.add(new Chunk(ms.asSlice(offset, len))); offset = end; } return chunks; } private static final class Chunk { private static final int KEY_LOG2_BYTES = 7; private static final int KEY_BYTES = 1 << KEY_LOG2_BYTES; private static final int ENTRIES_LOG2_CAPACITY = 16; private static final int ENTRIES_CAPACITY = 1 << ENTRIES_LOG2_CAPACITY; private static final int ENTRIES_MASK = ENTRIES_CAPACITY - 1; private final MemorySegment segment; private final long size; private final Entry[] entries = new Entry[ENTRIES_CAPACITY]; private final byte[] keys = new byte[ENTRIES_CAPACITY * KEY_BYTES]; private final MemorySegment kms = MemorySegment.ofArray(this.keys); private static final int KEYS_MASK = (ENTRIES_CAPACITY * KEY_BYTES) - 1; private long offset; private byte current; private boolean hasCurrent = true; Chunk(MemorySegment segment) { this.segment = segment; this.size = segment.byteSize(); } public List parse() { long safe = size - KEY_BYTES; while (offset < safe) { vectorizedEntry().add(vectorizedValue()); } next(); while (hasCurrent()) { entry().add(value()); } var output = new ArrayList(entries.length); for (int i = 0, o = 0; i < entries.length; i++, o += KEY_BYTES) { var e = entries[i]; if (e != null) { e.setkey(keys, o); output.add(e); } } return output; } private Entry vectorizedEntry() { var separators = ByteVector.broadcast(BYTE_SPECIES, SEPARATOR); int len = 0; for (int i = 0;; i += BYTE_SPECIES_LANES) { var block = ByteVector.fromMemorySegment(BYTE_SPECIES, this.segment, offset + i, NATIVE_ORDER); int equals = block.compare(VectorOperators.EQ, separators).firstTrue(); len += equals; if (equals != BYTE_SPECIES_LANES) { break; } } var start = this.offset; this.offset = start + len + 1; int hash = hash(segment, start, len); int index = (hash - (hash >>> -ENTRIES_LOG2_CAPACITY)) & ENTRIES_MASK; int keyOffset = index << KEY_LOG2_BYTES; int count = 0; while (count < ENTRIES_MASK) { index = index & ENTRIES_MASK; keyOffset = keyOffset & KEYS_MASK; var e = this.entries[index]; if (e == null) { MemorySegment.copy(this.segment, start, kms, keyOffset, len); return this.entries[index] = new Entry(len, hash); } else if (e.hash == hash && e.keyLength == len) { int total = 0; for (int i = 0; i < KEY_BYTES; i += BYTE_SPECIES_LANES) { var ekey = ByteVector.fromArray(BYTE_SPECIES, keys, keyOffset + i); var okey = ByteVector.fromMemorySegment(BYTE_SPECIES, this.segment, start + i, NATIVE_ORDER); int equals = ekey.compare(VectorOperators.NE, okey).firstTrue(); total += equals; if (equals != BYTE_SPECIES_LANES) { break; } } if (total >= len) { return e; } } count++; index++; keyOffset += KEY_BYTES; } throw new IllegalStateException("Map is full!"); } private Entry entry() { long start = this.offset - 1; int len = 0; while (hasCurrent() && current != SEPARATOR) { len++; next(); } expect(SEPARATOR); int hash = hash(segment, start, len); int index = (hash - (hash >>> -ENTRIES_LOG2_CAPACITY)) & ENTRIES_MASK; int keyOffset = index << KEY_LOG2_BYTES; int count = 0; while (count < ENTRIES_MASK) { index = index & ENTRIES_MASK; keyOffset = keyOffset & KEYS_MASK; var e = this.entries[index]; if (e == null) { MemorySegment.copy(this.segment, start, kms, keyOffset, len); return this.entries[index] = new Entry(len, hash); } else if (e.hash == hash && e.keyLength == len) { int total = 0; for (int i = 0; i < len; i++) { if (((byte) BYTE_HANDLE.get(this.segment, start + i)) != this.keys[keyOffset + i]) { break; } total++; } if (total >= len) { return e; } } count++; index++; keyOffset += KEY_BYTES; } throw new IllegalStateException("Map is full!"); } private static final long MULTIPLY_ADD_DIGITS = 100 * (1L << 24) + 10 * (1L << 16) + 1; private int vectorizedValue() { long dw = (long) LONG_LE_HANDLE.get(this.segment, this.offset); int zeros = Long.numberOfTrailingZeros(~dw & 0x10101000L); boolean negative = ((dw & 0xFF) ^ NEG) == 0; dw = ((negative ? (dw & ~0xFF) : dw) << (28 - zeros)) & 0x0F000F0F00L; int value = (int) (((dw * MULTIPLY_ADD_DIGITS) >>> 32) & 0x3FF); this.offset += (zeros >>> 3) + 3; return negative ? -value : value; } private int value() { int value = 0; var negative = false; if (consume(NEG)) { negative = true; } while (hasCurrent()) { if ((current & 0xF0) == 0x30) { value *= 10; value += current - '0'; } else if (current != DECIMAL_SEPARATOR) { break; } next(); } if (hasCurrent()) { expect(LF); } return negative ? -value : value; } private static final int GOLDEN_RATIO = 0x9E3779B9; private static final int HASH_LROTATE = 5; private static int hash(MemorySegment ms, long start, int len) { int x, y; if (len >= Integer.BYTES) { x = (int) INT_HANDLE.get(ms, start); y = (int) INT_HANDLE.get(ms, start + len - Integer.BYTES); } else { x = (byte) BYTE_HANDLE.get(ms, start) & 0xFF; y = (byte) BYTE_HANDLE.get(ms, start + len - Byte.BYTES) & 0xFF; } return (Integer.rotateLeft(x * GOLDEN_RATIO, HASH_LROTATE) ^ y) * GOLDEN_RATIO; } private void expect(byte b) { if (!consume(b)) { throw new IllegalStateException("Unexpected token!"); } } private boolean consume(byte b) { if (current == b) { next(); return true; } return false; } private boolean hasCurrent() { return hasCurrent; } private void next() { if (offset < size) { this.current = (byte) BYTE_HANDLE.get(segment, offset++); } else { this.hasCurrent = false; } } } private static final class Entry { final int keyLength; final int hash; private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum; private int count; private String key; Entry(int keyLength, int hash) { this.keyLength = keyLength; this.hash = hash; } public String key() { return key; } void setkey(byte[] keys, int offset) { this.key = new String(keys, offset, keyLength, StandardCharsets.UTF_8); } public void add(int value) { min = Math.min(min, value); max = Math.max(max, value); sum += value; count++; } public Entry merge(Entry o) { min = Math.min(min, o.min); max = Math.max(max, o.max); sum += o.sum; count += o.count; return this; } @Override public String toString() { var average = Math.round(((sum / 10.0) / count) * 10.0); return decimal(min) + '/' + decimal(average) + '/' + decimal(max); } private static String decimal(long value) { var builder = new StringBuilder(); if (value < 0) { builder.append((char) NEG); } value = Math.abs(value); builder.append(value / 10); builder.append((char) DECIMAL_SEPARATOR); builder.append(value % 10); return builder.toString(); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_justplainlaake.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import sun.misc.Unsafe; /* Possibilities to improve: * Reduce Standard Memory Reads and/or Swaps for threading - For the read file; using Unsafe or MemorySegment to map the file to an existing register instead of keeping the bytes local - For normal variables; Most of the time reading a value performs a load from memory and registers it for faster lookups, but with multithreading causes each thread to re read and register each get [volatile] keyword * Add multithreading to process multiple segments at once (When you have 1,000,000,000 cars driving might as well open as many lanes as possible) * Improve Mapping of entries (More O(1) lookups the better, i.e. hashed key maps, preferebly open maps to skip needing linked lists or trees, also simplifies since we don't need to delete anything) * Remove use of java streams (They can be much slower than expected, good for developer readability but not for performance 90% of the time) * Reduce amount of bytecode instructions (Usually just a micro-optimization, but since we are reading 1,000,000,000 lines, then this is really helpful in the processing code) * Never use division in processing code, division is 2x+ slower than multiplication (Easy fix is multiplying by decimal 2/2 vs 2*0.5) My System: Device: Processor(16) 11th Gen Intel(R) Core(TM) i7-11700K @ 3.60GHz 3.60 GHz Installed RAM 32.0 GB (31.8 GB usable) System type 64-bit operating system, x64-based processor Pen and touch No pen or touch input is available for this display Windows Specification: Edition Windows 11 Home Version 23H2 OS build 22635.3061 Experience Windows Feature Experience Pack 1000.22684.1000.0 Runs (Only IDE open, just after complete shutdown, measured using System.nanoTime around main method): - Baseline * 144,403.3814ms - merrykittyunsafe (#1 on LB) * 2,757.8295ms - royvanrijn (#2 on LB) * 1,643.9123ms ??? Assuming this is because of my system specs compared to specs on testing system //Obviously there were more runs than this, but these were the significant jumps - Me run 1 (Initial attempt;multithreading, file mapped to global Unsafe, long hash of name, read byte by byte, store in hashmap and merge from threads) * 5,423.4432ms - Me run 2 (Read longs instead of bytes to determine name hash) * 3,937.3234ms - Me run 3 (Swap to using a rolling long hash with murmur3 hashing function, change hashmap to be an openmap with unboxed long as the key) * 2,951.6891ms - Me run 4 (Change entire line reading to be long based with bit operations to determine number) * 2,684.9823ms - Me run 5 (Use main thread as one of the processing threads) * 2,307.3038ms - Me run 6 (Remove use of math.min and math.max in favor of ternary operator (Reduces getStatic operation)) * 2,265.3521ms */ public class CalculateAverage_justplainlaake { // Constants private static final String FILE = "./measurements.txt"; private static final byte SEPERATOR_BYTE = ';'; private static final byte NEW_LINE_BYTE = '\n'; private static final DecimalFormat STATION_FORMAT = new DecimalFormat("#,##0.0"); private static final long[] OFFSET_CLEARS = { 0x0000000000000000L, // 8 Offset (Clear whole thing) 0x00000000000000FFL, 0x000000000000FFFFL, 0x0000000000FFFFFFL, 0x00000000FFFFFFFFL, 0x000000FFFFFFFFFFL, 0x0000FFFFFFFFFFFFL, 0x00FFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL,// 0 Offset (Clear nothing) }; private static final Unsafe UNSAFE; static { Unsafe _unsafe = null; try { Field unsafe = Unsafe.class.getDeclaredField("theUnsafe"); unsafe.setAccessible(true); _unsafe = (Unsafe) unsafe.get(Unsafe.class); } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); System.exit(1); } UNSAFE = _unsafe;// Just to get around "The blank final field UNSAFE may not have been initialized" } public static void main(String[] args) throws IOException { int processors = Runtime.getRuntime().availableProcessors(); ExecutorService e = null; List> futures = new ArrayList<>(); OpenMap mainMap = null; try (FileChannel channel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { long fileSize = channel.size(); if (fileSize < 10_000) {// File is smaller than 10,000 bytes, we will lose performance trying to multithread so just set processors to 1 which will skip the futures and only use main thread processors = 1; } else { e = Executors.newFixedThreadPool(processors);// Create a ThreadPool based executor using the count of processors available } long chunkSize = fileSize / processors;// Determine approximate size of each chunk based on amount of processors available long startAddress = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global())// Map the file channel into memory using the global arena (accessible by all threads) .address();// And get the starting address of mapped section long endAddress = startAddress + fileSize; long currentAddress = startAddress + chunkSize; long chunkStart = startAddress; for (int i = 0; i < processors; i++) {// We need to chunk the file for each processor/thread while (currentAddress < endAddress) {// While loop to locate the next new line character from the chunk we are in long match = UNSAFE.getLong(currentAddress);// Read the next 8 bytes as a long from the memory address short offset = getMaskOffset(match, NEW_LINE_BYTE);// find the byte in the long which equals 10 aka '\n', if it is not found this returns -1 if (offset != -1) {// We found the offset, so add it to the current adress and break the while loop currentAddress += offset; break; } currentAddress += 8;// No offset was found so advance 8 bytes, aka 1 long } long finalChunkStart = chunkStart, finalChunkEnd = Math.min(endAddress, currentAddress - 1);// Create final fields to pass to the thread call below, // Also Math.min doesn't matter here since its called x times where x = count of processors if (i == processors - 1) {// if on last processor use main thread to optimize threading, doing on last processor means the others are already processing while this runs mainMap = process(finalChunkStart, finalChunkEnd); } else { futures.add(e.submit(() -> process(finalChunkStart, finalChunkEnd))); } chunkStart = currentAddress + 1;// Advance the start of the next chunk to be the end of this chunk + 1 to move past the new line character currentAddress = Math.min(currentAddress + chunkSize, endAddress);// Advance the next chunks end to be the end of the mapped file or the end of the approximated chunk } } OpenMap merged = mainMap;// Set the main map created with the process called on main thread to make it effectively final if (processors > 1) {// If there is only one processor then we only used the main thread so no point in merging the futures // The merging of processing takes ~10ms for (Future f : futures) { try { OpenMap processed = f.get();// Waits until the process task is done but then returns the callable value from the process method // Simple way to merge both lists, tried doing it more inline inside the map and ended up taking a 10ms longer processed.forEach((i, s) -> { merged.merge(i, s); }); } catch (InterruptedException | ExecutionException e1) { e1.printStackTrace(); } } // Mark threadpool to be shutdown, call it here to let the threadpool finish out while the rest of the processing occurs e.shutdown(); } // Ordering and printing takes 50ms Station[] nameOrdered = merged.toArray();// Turn the merged map into an array to quickly sort it Arrays.sort(nameOrdered, (n1, n2) -> n1.name.compareTo(n2.name));// Sort based on name, this might be optimizable based on the longs of the name, but would likely only gain some ms?? // Print results to the sys out System.out.print("{"); for (int i = 0; i < nameOrdered.length; i++) { if (i != 0) { System.out.print(", "); } System.out.print(nameOrdered[i]); } System.out.print("}\n");// Need newline character to meet specs } // Core processing functionality, processes a chunk of memory private static OpenMap process(long fromAddress, long toAddress) { OpenMap stationsLookup = new OpenMap();// Create a new map for this specific chunk, this is also the returned value for the callable long blockStart = fromAddress; long currentAddress = fromAddress; while (currentAddress < toAddress) {// Just keep looping until we exhaust the chunk long read = 0l; short offset = -1; // The hash is a long hash based on the murmur3 algorithm. Look at the getMurmurHash3 method to find link long hash = 1; while ((offset = getMaskOffset(read = UNSAFE.getLong(currentAddress), SEPERATOR_BYTE)) == -1) {// Read and compute the hash until we locate the seperator byte 59 or ';' currentAddress += 8;// forwardscan hash = (997 * hash) ^ getMurmurHash3(991 * read); } // Compute the final hash based using the last read long but only the effective bits (anything before the byte 59 or ';'). // Using the OFFSET_CLEARS masks that are defined statically we can essentially segregate the important bits of the name based on the offset read above hash = (997 * hash) ^ getMurmurHash3(991 * (read & OFFSET_CLEARS[offset])); // Advance the current address/pointer to be 1 character past the end of the name Example: BillyJoel;29 would make the current address start at the '2' character currentAddress += offset + 1; Station station = stationsLookup.getOrCreate(hash, currentAddress, blockStart); /* * Possible combinations (x = number) -99.9 -> 99.9; ex: 54.4, -31.7, -4.5, 1.9 * x.x * xx.x * -x.x * -xx.x */ // Encoding is UTF8 however, since numbers in UTF8 are all single byte characters we can do some byte math to determin the number; 0=48 and 9=57, so character - 48 = number // And since - and . are also single byte characters we can make some assumptions, leading us with the primary one that no matter what the number will be 3 to 5 bytes (see above combinations) // Unfortunately since an integer is only 4 bytes we must read the long; Something to test would be to see if we could read an integer and then read an extra byte if it is the 5 character edge case read = UNSAFE.getLong(currentAddress); offset = 0;// reinitiate the offset to reuse the local address byte sign = (byte) ((read >> offset) ^ 45);// Check the first byte of the new long to see if it is 45 aka '-', if it is this byte will be 0 // The logic below is based on the fact that we are reading int num = sign == 0 ? (((byte) (read >> (offset += 8))) - 48) : (((byte) read) - 48);// Start the number reading, if it is a negative advance 8 bits in the long (8 bits = 1 byte) currentAddress += 4;// There will always be at least 3 digits to read and the newline digit (4 total) if ((byte) ((read >> (offset + 8)) ^ 46) != 0) {// There can only be one more possible number for cases of (XY.X | -XY.X) where Y is that other number num *= 10; num += ((byte) (read >> (offset += 8))) - 48; currentAddress++;// Add one digit read if temp is 3 digits } num *= 10; num += ((byte) (read >> (offset + 16))) - 48;// Read the decimal character (no matter what it is 16 bits past the offset here, since 8 bits is the last number and 8 bits is the decimal) if (sign == 0) { num *= -1; currentAddress++;// Add another digit read for the negative sign } // Assign the values, don't use Math.min or any special bit manipulation. Faster to just use ternary station.min = station.min < num ? station.min : num; station.max = station.max > num ? station.max : num; station.count++; station.sum += num; // And now set the next block to start at the current address blockStart = currentAddress; } return stationsLookup; } // Avalanche hashing function for longs: https://github.com/aappleby/smhasher/blob/master/README.md public final static long getMurmurHash3(long x) { x ^= x >>> 33; x *= 0xff51afd7ed558ccdL; x ^= x >>> 33; x *= 0xc4ceb9fe1a85ec53L; x ^= x >>> 33; return x; } // Simple way to identify if a byte is set in a long at any of the 8 spots, and also to get the offset of that byte. // On average this is fast but certain cases could make it slow (checking 500,000,000,000 longs that don't have the test byte at all...) private static short getMaskOffset(long value, byte test) { for (short i = 0; i < 8; i++) { if (((byte) value & 0xFF) == test) { return i; } value = value >> 8; } return -1; } private static class Station { private final long nameStart, nameEnd;// Store the starting and ending address of the name, to fill it later private final int nameLength; private int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE, count; private long sum; private String name; Station(long nameStart, long nameEnd) { this.nameStart = nameStart; this.nameEnd = nameEnd; this.nameLength = (int) (nameEnd - nameStart) + 1;// Add 1 to include seperator } protected void fillName() { byte[] nameBuffer = new byte[(int) (nameEnd - nameStart)]; UNSAFE.copyMemory(null, this.nameStart, nameBuffer, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameBuffer.length);// Quick memory copy, using null as src copies from the file we mapped earlier name = new String(nameBuffer, StandardCharsets.UTF_8); } @Override public String toString() {// Use decimal format to print numbers return name + "=" + STATION_FORMAT.format(Math.round(min) * 0.1) + "/" + STATION_FORMAT.format(Math.round(((double) sum) / count) * 0.1) + "/" + STATION_FORMAT.format(Math.round(max) * 0.1); } } public static class OpenMap { public static final float LOAD_FACTOR = 0.75f; public static final int EXPECTED_INITIAL_SIZE = 100_000; protected transient long[] keys;// Use unboxed long values as a key, faster than a doing new HashMap() as with generics it will box/unbox every action (can be costly in large quantities) protected transient Station[] values; protected transient int capacity; protected transient int maxFill; protected transient int mask; protected int size; public OpenMap() { // capacity = (int) getNextPowerOfTwo((long) Math.ceil(EXPECTED_INITIAL_SIZE / LOAD_FACTOR));// need to base the capacity on the next power of two for the mask to work properly // initial size of 100k gives 262,144 Capacity, since we know this and its way oversized for a max of 10k keys theres no need to recalculate capacity = 262_144; mask = capacity - 1; maxFill = (int) Math.ceil(capacity * 0.75f);// Only allow 75% of capacity before resizing keys = new long[capacity]; values = new Station[capacity]; } public void merge(long key, Station toMerge) { // Simple compute function, if exists pass existing, if it doesn't pass null int pos = (int) key & mask;// Key has already been hashed as we read, but cap it by mask while (values[pos] != null) { if (keys[pos] == key) { final Station oldValue = values[pos]; // If names are different size but key was same, then continue to next step as hash collided // Compare memory values to see if the name is same as well, prevents hash collision if (oldValue.nameLength == toMerge.nameLength && compareMemory(toMerge.nameStart, oldValue.nameStart, oldValue.nameLength)) { // Memory was the same, making these the same station oldValue.count += toMerge.count; oldValue.sum += toMerge.sum; oldValue.min = oldValue.min < toMerge.min ? oldValue.min : toMerge.min; oldValue.max = oldValue.max > toMerge.max ? oldValue.max : toMerge.max; return; } } pos = (pos + 1) & mask; } keys[pos] = key; values[pos] = toMerge; size++; } public Station getOrCreate(final long key, long currentAddress, long blockStart) { int pos = (int) key & mask;// Key has already been hashed as we read, but cap it by mask while (values[pos] != null) {// While position is set if (keys[pos] == key) {// Check if key is correct // If names are different size but key was same, then continue to next step as hash collided // Compare memory values to see if the name is same as well, prevents hash collision if (values[pos].nameLength == currentAddress - blockStart && compareMemory(blockStart, values[pos].nameStart, values[pos].nameLength)) { return values[pos]; } } pos = (pos + 1) & mask;// Since this is an open map we keep checking next masked key for an open spot (Faster than tree or linked list on a specific node) } keys[pos] = key; size++; return values[pos] = new Station(blockStart, currentAddress - 1);// Since current address contains the splitter (we will subtract by 1 here, better to do here since this is only called when it doesn't exist less math = performance) } // Simple iterator for each set value public void forEach(OpenConsumer consumer) { for (int i = 0; i < this.capacity; i++) { if (values[i] != null) { consumer.accept(keys[i], values[i]); } } } public Station[] toArray() { Station[] array = new Station[size]; int setter = 0; for (int i = 0; i < capacity; i++) { if (values[i] != null) { array[setter++] = values[i]; values[i].fillName(); } } return array; } // Bit function to get the next power of two on some number, used to determine best capacity based on initial size public long getNextPowerOfTwo(long length) { if (length-- == 0) return 1; length |= length >> 1; length |= length >> 2; length |= length >> 4; length |= length >> 8; length |= length >> 16; return (length | length >> 32) + 1; } private boolean compareMemory(long start1, long start2, int length) { while (length > 0) { if (length >= 8) { if (UNSAFE.getLong(start1) != UNSAFE.getLong(start2)) { return false; } } else { if ((UNSAFE.getLong(start1) & OFFSET_CLEARS[length]) != (UNSAFE.getLong(start2) & OFFSET_CLEARS[length])) { System.out.println("Found collision: " + start1 + ": " + start2); System.out.println("Found collision: " + UNSAFE.getLong(start1) + ": " + UNSAFE.getLong(start2)); System.out.println("Length: " + length); return false; } } length -= 8; start1 += 8; start2 += 8; } return true; } @FunctionalInterface public static interface OpenConsumer { void accept(long key, Station value); } @FunctionalInterface public static interface OpenFunction { Station action(long key, Station value); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_karthikeyan97.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import static java.util.stream.Collectors.*; import java.io.FileInputStream; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; import java.util.TreeMap; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Collectors; public class CalculateAverage_karthikeyan97 { private static final Unsafe UNSAFE = initUnsafe(); private static final String FILE = "./measurements.txt"; private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private record Measurement(modifiedbytearray station, double value) { } private record customPair(String stationName, MeasurementAggregator agg) { } private static class MeasurementAggregator { private long min = Long.MAX_VALUE; private long max = Long.MIN_VALUE; private long sum; private long count; public String toString() { return new StringBuffer(14) .append(round((1.0 * min))) .append("/") .append(round((1.0 * sum) / count)) .append("/") .append(round((1.0 * max))).toString(); } private double round(double value) { return Math.round(value) / 10.0; } } public static void main(String[] args) throws Exception { // long start = System.nanoTime(); // System.setSecurityManager(null); Collector, MeasurementAggregator, MeasurementAggregator> collector = Collector.of( MeasurementAggregator::new, (a, m) -> { MeasurementAggregator agg = m.getValue(); if (a.min >= agg.min) { a.min = agg.min; } if (a.max <= agg.max) { a.max = agg.max; } a.max = Math.max(a.max, m.getValue().max); a.sum += m.getValue().sum; a.count += m.getValue().count; }, (agg1, agg2) -> { if (agg1.min <= agg2.min) { agg2.min = agg1.min; } if (agg1.max >= agg2.max) { agg2.max = agg1.max; } agg2.sum = agg1.sum + agg2.sum; agg2.count = agg1.count + agg2.count; return agg2; }, agg -> agg); RandomAccessFile raf = new RandomAccessFile(FILE, "r"); FileChannel fileChannel = raf.getChannel(); final long mappedAddress = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, raf.length(), Arena.global()).address(); long length = raf.length(); final long endAddress = mappedAddress + length - 1; int cores = length > 1000 ? Runtime.getRuntime().availableProcessors() : 1; long boundary[][] = new long[cores][2]; long segments = length / (cores); long before = -1; for (int i = 0; i < cores - 1; i++) { boundary[i][0] = before + 1; if (before + segments - 107 > 0) { raf.seek(before + segments - 107); } else { raf.seek(0); } while (raf.read() != '\n') { } boundary[i][1] = raf.getChannel().position() - 1; before = boundary[i][1]; } boundary[cores - 1][0] = before + 1; boundary[cores - 1][1] = length - 1; int l3Size = (12 * 1024 * 1024);// unsafe.l3Size(); System.out.println(new TreeMap((Arrays.stream(boundary).parallel().map(i -> { try { int seglen = (int) (i[1] - i[0] + 1); HashMap resultmap = new HashMap<>(4000); long segstart = mappedAddress + i[0]; int bytesRemaining = seglen; long num = 0; boolean isNumber = false; byte bi; int sign = 1; modifiedbytearray stationName = null; int hascode = 5381; // System.out.println("start:" + System.nanoTime() / 1000000); while (bytesRemaining > 0) { int bytesptr = 0; // int bytesread = buffer.remaining() > l3Size ? l3Size : buffer.remaining(); // byte[] bufferArr = new byte[bytesread]; // buffer.get(bufferArr); int bbstart = 0; int readSize = bytesRemaining > l3Size ? l3Size : bytesRemaining; int actualReadSize = (segstart + readSize + 110 > endAddress || readSize + 110 > i[1]) ? readSize : readSize + 110; byte[] readArr = new byte[actualReadSize]; UNSAFE.copyMemory(null, segstart, readArr, UNSAFE.ARRAY_BYTE_BASE_OFFSET, actualReadSize); while (bytesptr < actualReadSize) { bi = readArr[bytesptr++];// UNSAFE.getByte(segstart + bytesReading++); if (!isNumber) { while (bi != 59) { hascode = (hascode << 5) + hascode ^ bi; bi = readArr[bytesptr++]; } isNumber = true; stationName = new modifiedbytearray(readArr, bbstart, bytesptr - 2, hascode & 0xFFFFFFFF); bbstart = 0; hascode = 5381; } else { while (bi != 10) { if (bi == 0x2D) { sign = -1; } else if (bi != 0x2E) { num = num * 10 + (bi - 0x30); } bi = readArr[bytesptr++]; } hascode = 5381; isNumber = false; bbstart = bytesptr; num *= sign; MeasurementAggregator agg = resultmap.get(stationName); if (agg == null) { agg = new MeasurementAggregator(); agg.min = num; agg.max = num; agg.sum = (long) (num); agg.count = 1; resultmap.put(stationName, agg); } else { if (agg.min >= num) { agg.min = num; } if (agg.max <= num) { agg.max = num; } agg.sum += (long) (num); agg.count++; } num = 0; sign = 1; if (bytesptr >= readSize) { break; } } } bytesRemaining -= bytesptr; segstart += bytesptr; } // System.out.println("end:" + System.nanoTime() / 1000000); /* * while (bytesReading < (i[1] - i[0] + 1) && buffer.position() < buffer.limit()) { * buffer.clear(); * bytesRead = fileChannel.read(buffer); * buffer.flip(); * while (bytesReading <= (i[1] - i[0]) && buffer.position() < buffer.limit()) { * bytesReading += 1; * bi = buffer.get(); * String s; * if (ctr > 0) { * hascode = 31 * hascode + bi; * ctr--; * } * else { * if (bi >= 240) { * ctr = 3; * } * else if (bi >= 224) { * ctr = 2; * } * else if (bi >= 192) { * ctr = 1; * } * else if (bi == 59) { * isNumber = true; * System.out.println(buffer); * stationName = new modifiedbytearray(bbstart, buffer.position() - 1, hascode, buffer); * hascode = 1; * bbstart = buffer.position(); * } * else if (bi == 10) { * hascode = 1; * isNumber = false; * MeasurementAggregator agg = resultmap.get(stationName); * if (agg == null) { * agg = new MeasurementAggregator(); * agg.min = num * sign; * agg.max = num * sign; * agg.sum = (long) (num * sign); * agg.count = 1; * resultmap.put(stationName, agg); * } * else { * agg.min = Math.min(agg.min, num * sign); * agg.max = Math.max(agg.max, num * sign); * agg.sum += (long) (num * sign); * agg.count++; * } * num = 1; * bbstart = buffer.position(); * } * else { * hascode = 31 * hascode + bi; * if (isNumber) { * switch (bi) { * case 0x2E: * break; * case 0x2D: * num = num * -1; * break; * default: * num = num * 10 + (bi - 0x30); * } * } * } * } * } * } */ return resultmap; } catch (Exception e) { e.printStackTrace(); } return null; }).flatMap(e -> e.entrySet().stream()).collect(groupingBy(e -> e.getKey(), collector)))) { @Override public Object put(Object key, Object value) { return super.put(((modifiedbytearray) key).getStationName(), value); } }); /* * .map(a -> { * return a.stream().parallel().collect(groupingBy(m -> m.station(), collector)); * }).flatMap(m -> m.entrySet() * .stream() */ // Get the FileChannel from the FileInputStream // System.out.println("time taken1:" + (System.nanoTime() - start) / 1000000); // System.out.println(measurements); } } class modifiedbytearray { private int length; private int start; private int end; private byte[] arr; public int hashcode; modifiedbytearray(byte[] arr, int start, int end, int hashcode) { this.arr = arr; this.length = end - start + 1; this.end = end; this.start = start; this.hashcode = hashcode; } public String getStationName() { return new String(this.getArr(), start, length, StandardCharsets.UTF_8); } public byte[] getArr() { return this.arr; } @Override public String toString() { return getStationName(); } @Override public boolean equals(Object obj) { modifiedbytearray b = (modifiedbytearray) obj; return Arrays.equals(this.getArr(), start, end, b.arr, b.start, b.end); } public int getHashcode() { return hashcode; } @Override public int hashCode() { return hashcode; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_kevinmcmurtrie.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Comparator; import java.util.Spliterator; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** * Kevin McMurtrie https://github.com/kevinmcmurtrie *

* Code challenge submission for https://github.com/gunnarmorling/1brc */ public class CalculateAverage_kevinmcmurtrie implements AutoCloseable { private static final String FILE = "./measurements.txt"; private static final Charset CHARSET = StandardCharsets.UTF_8; private static final int THREADS = Runtime.getRuntime().availableProcessors() + 2; // This is used for push-back when fitting buffers to the last line break private static final int MAX_LINE_LENGTH = 1024; private static final int READ_CHUNK_SIZE = 4 * 1024 * 1024; private static final int CHAR_CHUNK_SIZE = Math.max(MAX_LINE_LENGTH, 64 * 1024); // Internal array size for hashing cities private static final int HASH_BUCKETS = 1039; // Fixed-point number parameters private static final int DIGITS_AFTER_DECIMAL_POINT_INPUT = 1; private static final int DIGITS_AFTER_DECIMAL_POINT_OUTPUT = 1; // City and Temperature delimiter private static final char DELIMITER = ';'; private final LineAlignedInput in; // Must synchronize on this when multiple threads are reading. Use fillFromFile(). public static class Accumulator { private static final Comparator cityComparator = new Comparator<>() { @Override public int compare(Element a, Element b) { return Arrays.compare(a.line, b.line); } }; private final Element buckets[]; public Accumulator(final int bucketCount) { buckets = new Element[bucketCount]; } /** * Custom hash element. *

    *
  • Can avoid many expensive substring operations while parsing *
  • Combines the key and values into a single object *
*/ public static class Element { private final char[] line; private final int hashCode; private final Element collision; private long min = 0; private long max = 0; private long sum = 0; private long count = 0; public Element(final Element collision, final char[] line, final int hashCode, final long value) { this.collision = collision; this.line = line; this.hashCode = hashCode; min = value; max = value; sum = value; count = 1; } public Element(final Element collision, final Element src) { this.collision = collision; this.line = src.line; this.hashCode = src.hashCode; min = src.min; max = src.max; sum = src.sum; count = src.count; } void accumulate(final long value) { min = Math.min(value, min); max = Math.max(value, max); sum = Math.addExact(sum, value); count++; } private void merge(final Element a) { min = Math.min(a.min, min); max = Math.max(a.max, max); sum = Math.addExact(sum, a.sum); count += a.count; } @Override /** * City=min/avg/max */ public String toString() { return new String(line) + '=' + fixedToString(min) + "/" + fixedToString((double) sum / count) + "/" + fixedToString(max); } } /** * Hasher that operates on a buffer without generating a substring */ static private final int hasher(final char[] buf, final int start, final int end) { int hc = buf[start]; for (int i = start + 1; i < end; ++i) { hc = hc * 31 + buf[i]; } return hc & Integer.MAX_VALUE; } /** * Tests if a pre-calculated hash and buffer area match an Element */ static boolean matches(final Element o, final char[] str, final int start, final int end, final int hashCode) { return (hashCode == o.hashCode) && Arrays.equals(o.line, 0, o.line.length, str, start, end); } static boolean matches(final Element a, final Element b) { return (a.hashCode == b.hashCode) && Arrays.equals(a.line, b.line); } /** * Merge another Accumulator into this one * * @param src */ void merge(final Accumulator src) { for (final Element srcElementHead : src.buckets) { for (Element srcElem = srcElementHead; srcElem != null; srcElem = srcElem.collision) { final int idx = srcElem.hashCode % buckets.length; final Element elementHead = buckets[idx]; boolean found = false; for (Element e = elementHead; e != null; e = e.collision) { if (matches(e, srcElem)) { e.merge(srcElem); found = true; break; } } if (!found) { buckets[idx] = new Element(elementHead, srcElem); } } } } /** * Accumulate a weather string * * @param str City;temperature */ void accumulate(final char[] buf, final int delimiterPos, final int start, final int end) { final long value = readFixed(buf, delimiterPos + 1, end); final int hc = hasher(buf, start, delimiterPos); final int idx = hc % buckets.length; final Element elementHead = buckets[idx]; for (Element e = elementHead; e != null; e = e.collision) { if (matches(e, buf, start, delimiterPos, hc)) { e.accumulate(value); return; } } buckets[idx] = new Element(elementHead, Arrays.copyOfRange(buf, start, delimiterPos), hc, value); } /** * @return A stream of Element.toString() values. */ public Stream toStream() { final Spliterator sp = new Spliterator<>() { int idx = 0; Element elem = null; @Override public boolean tryAdvance(final Consumer action) { while ((elem == null) && (idx < buckets.length)) { elem = buckets[idx++]; } if (elem != null) { final Element result = elem; elem = result.collision; action.accept(result); return true; } return false; } @Override public Spliterator trySplit() { return null; } @Override public long estimateSize() { return buckets.length; } @Override public int characteristics() { return DISTINCT | NONNULL; } }; return StreamSupport.stream(sp, false); } /** * Converts rounds a higher precision fixed-point to a string. Not optimized. */ static String fixedToString(final double d) { return String.valueOf( Math.round(d / Math.pow(10, DIGITS_AFTER_DECIMAL_POINT_INPUT - DIGITS_AFTER_DECIMAL_POINT_OUTPUT)) / Math.pow(10, DIGITS_AFTER_DECIMAL_POINT_OUTPUT)); } /** * Read the suffix of a string as a fixed point number. * Doesn't allocate memory except for exceptions. */ static long readFixed(final char[] str, final int offset, final int end) { char c; int pos = offset; while ((c = str[pos]) == ' ') { pos++; } final boolean negate = c == '-'; if (negate) { pos++; } c = str[pos++]; if ((c < '0') || (c > '9')) { throw new IllegalArgumentException(new String(str, offset, end - offset)); } long v = c - '0'; for (; pos < end; ++pos) { c = str[pos]; if (c == '.') { pos++; break; } if ((c < '0') || (c > '9')) { throw new IllegalArgumentException(new String(str, offset, end - offset)); } v = v * 10 + c - '0'; } final int fractLimit = pos + DIGITS_AFTER_DECIMAL_POINT_INPUT; for (; (pos < end) && (pos < fractLimit); ++pos) { c = str[pos]; if ((c < '0') || (c > '9')) { throw new IllegalArgumentException(new String(str, offset, end - offset)); } v = v * 10 + c - '0'; } for (; (pos < fractLimit); ++pos) { v = v * 10; } return negate ? -v : v; } } public CalculateAverage_kevinmcmurtrie(final String path) throws IOException { in = new LineAlignedInput(new FileInputStream(path), MAX_LINE_LENGTH); } @Override public void close() throws IOException { in.close(); } /** * Fill a byte buffer with the end aligned to a CR or LF. * * @param b A byte array at least large enough to hold one full line. * @return Number of bytes filled, or zero for EOF * @throws IOException */ static class LineAlignedInput implements AutoCloseable { private final InputStream in; private final byte[] pushbackStack; private int pushedBackLen = 0; public LineAlignedInput(final InputStream in, final int maxLineLength) { this.in = in; pushbackStack = new byte[maxLineLength]; } public int fillChunk(final byte buf[]) throws IOException { int offset = 0; // Recover last pushback while ((pushedBackLen > 0) && (offset < buf.length)) { buf[offset++] = pushbackStack[--pushedBackLen]; } final int readSize = in.read(buf, offset, buf.length - offset); if (readSize <= 0) { return offset; } final int size = readSize + offset; // Roll back end of buffer to a line break so it's not truncated int rollbackPos = size - 1; if (rollbackPos > 0) { byte b; while (((b = buf[rollbackPos]) != '\n') && (b != '\r')) { pushbackStack[pushedBackLen++] = b; rollbackPos--; if (rollbackPos == 0) { return size; // Last entry. Return as-as. } } } return rollbackPos + 1; } @Override public void close() throws IOException { in.close(); } } /** * Fill a char buffer with the end aligned to a CR or LF. * * @param b A char array at least large enough to hold one full line. * @return Number of bytes filled, or zero for EOF * @throws IOException */ static class LineAlignedReader implements AutoCloseable { private final Reader in; private final char[] pushbackStack; private int pushedBackLen = 0; public LineAlignedReader(final Reader in, final int maxLineLength) { this.in = in; pushbackStack = new char[maxLineLength]; } public int fillChunk(final char buf[]) throws IOException { int offset = 0; // Recover last pushback while ((pushedBackLen > 0) && (offset < buf.length)) { buf[offset++] = pushbackStack[--pushedBackLen]; } final int readSize = in.read(buf, offset, buf.length - offset); if (readSize <= 0) { return offset; } final int size = readSize + offset; // Roll back end of buffer to a line break so it's not truncated int rollbackPos = size - 1; if (rollbackPos > 0) { char b; while (((b = buf[rollbackPos]) != '\n') && (b != '\r')) { pushbackStack[pushedBackLen++] = b; rollbackPos--; if (rollbackPos == 0) { return size; // Last entry. Return as-as. } } } return rollbackPos + 1; } @Override public void close() throws IOException { in.close(); } } private int fillFromFile(final byte buf[]) throws IOException { synchronized (in) { return in.fillChunk(buf); } } /** * Read as fast as possible and collect values. * There's some expensive charset and String work here so many of these may run in parallel. */ public Accumulator collect() throws IOException { final Accumulator accumulation = new Accumulator(HASH_BUCKETS); final byte buf[] = new byte[READ_CHUNK_SIZE]; final char cbuf[] = new char[CHAR_CHUNK_SIZE]; int blen; while ((blen = fillFromFile(buf)) > 0) { try (LineAlignedReader reader = new LineAlignedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, blen), CHARSET), CHAR_CHUNK_SIZE)) { int length; while ((length = reader.fillChunk(cbuf)) > 0) { int pos = 0; do { // Skip whitespace while ((pos < length) && Character.isWhitespace(cbuf[pos])) { pos++; } final int start = pos; if (start < length) { int lastDelimiterPos = -1; int c; while ((pos < length) && ((c = cbuf[pos]) != '\n') && (c != '\r')) { if (c == DELIMITER) { lastDelimiterPos = pos; } pos++; } if (pos > start) { if (lastDelimiterPos < 1) { throw new IllegalArgumentException("Malformed input: " + new String(cbuf, start, pos - start)); } accumulation.accumulate(cbuf, lastDelimiterPos, start, pos); } } } while (pos < length); } } } return accumulation; } /** * Run multiple collectors and merge the results. Performance * is optimized for reading many values but not for merging many results. * * @param threads How many threads to allocate * @return Accumulator * @throws InterruptedException * @throws ExecutionException */ public Accumulator collectParallel(final int threads) throws InterruptedException, ExecutionException { final Accumulator acc; // ForkJoinPool is somehow faster even without major work stealing. Class loading? try (final ExecutorService pool = new ForkJoinPool(threads)) { @SuppressWarnings("unchecked") final Future tasks[] = new Future[threads]; for (int i = 0; i < threads; ++i) { tasks[i] = pool.submit(this::collect); } acc = tasks[0].get(); for (int i = 1; i < threads; ++i) { acc.merge(tasks[i].get()); } } return acc; } public static void main(final String args[]) throws IOException, InterruptedException, ExecutionException { // final long startMillis = System.currentTimeMillis(); final String path = args.length > 0 ? args[0] : FILE; final Accumulator acc; try (CalculateAverage_kevinmcmurtrie c = new CalculateAverage_kevinmcmurtrie(path)) { acc = c.collectParallel(THREADS); } System.out.println(acc.toStream().sorted(Accumulator.cityComparator).map(String::valueOf).collect(Collectors.joining(", ", "{", "}"))); // System.out.println((System.currentTimeMillis() - startMillis) / 1000f); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_kgeri.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.io.UncheckedIOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.nio.channels.FileChannel; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ForkJoinPool; import java.util.stream.LongStream; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.charset.StandardCharsets.UTF_8; public class CalculateAverage_kgeri { private static final String FILE = "./measurements.txt"; private static class MeasurementAggregate { private double min = Double.POSITIVE_INFINITY; private double sum = 0d; private double max = Double.NEGATIVE_INFINITY; private long count; public void append(double measurement) { min = Math.min(min, measurement); max = Math.max(max, measurement); sum += measurement; count++; } public void merge(MeasurementAggregate other) { min = Math.min(min, other.min); max = Math.max(max, other.max); sum += other.sum; count += other.count; } @Override public String toString() { return STR."\{round(min)}/\{round(sum / count)}/\{round(max)}"; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } /** * This is to avoid instantiating `String`s during processing as much as possible. */ private static class StringSlice { protected final byte[] buf; protected int len; protected int hash; public StringSlice(StringSlice other) { buf = Arrays.copyOfRange(other.buf, 0, other.len); len = other.len; hash = other.hash; } public StringSlice(byte[] buf, int len) { this.buf = buf; this.len = len; calculateHash(); } public int length() { return len; } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { return obj instanceof StringSlice other && Arrays.equals(buf, 0, len, other.buf, 0, other.len); } @Override public String toString() { return new String(buf, 0, len, UTF_8); } protected void calculateHash() { hash = 1; for (int i = 0; i < len; i++) { hash = 31 * hash + buf[i]; } } } /** * A flyweight of {@link StringSlice}, to avoid instantiating that as well. */ private static class MutableStringSlice extends StringSlice { public MutableStringSlice(byte[] buf) { super(buf, 0); } public void updateLength(int length) { len = length; calculateHash(); } public void clear() { len = 0; hash = 0; } } private static Map readChunk(FileChannel channel, long from, long chunkSize) { MemorySegment data; long remaining; try { long offset = Math.max(0, from - 1); remaining = channel.size() - offset; data = channel.map(READ_ONLY, offset, Math.min(remaining, chunkSize + 256), Arena.ofConfined()); // +256 to allow for reading records that are split by the chunk } catch (IOException e) { throw new UncheckedIOException(e); } long start = 0; if (from > 0) { while (data.get(JAVA_BYTE, start++) != '\n') { } } Map results = new HashMap<>(1000); byte[] buf = new byte[256]; MutableStringSlice name = new MutableStringSlice(buf); long until = Math.min(remaining, chunkSize); // Records may end (slightly) after the chunk boundary, but must not start after it for (long pos = start; pos < until;) { name.clear(); int i = 0; for (;; i++) { if (pos + i >= data.byteSize()) { // Guard against malformed data (typically a missing newline at the end of the input) pos = chunkSize; break; } byte b = data.get(JAVA_BYTE, pos + i); buf[i] = b; if (b == ';') { name.updateLength(i); } else if (b == '\n') { pos = pos + i + 1; break; } } double measurement = parseDoubleFrom(buf, name.length() + 1, i - name.length() - 1); MeasurementAggregate aggr = results.get(name); if (aggr == null) { aggr = new MeasurementAggregate(); results.put(new StringSlice(name), aggr); } aggr.append(measurement); } return results; } // Note: based on java.lang.Integer.parseInt, surely missing some edge cases but avoids allocation private static double parseDoubleFrom(byte[] buf, int offset, int length) { boolean negative = false; int integer = 0; for (int i = 0; i < length; i++) { byte c = buf[offset + i]; if (c == '-') { negative = true; } else if (c != '.') { integer *= 10; integer -= c - '0'; } } return (negative ? integer : -integer) / 10.0; } public static void main(String[] args) throws IOException { Map measurements = new TreeMap<>(); try (RandomAccessFile raf = new RandomAccessFile(FILE, "r")) { long size = raf.length(); long threads = Math.min(ForkJoinPool.getCommonPoolParallelism(), Math.max(size / 1000000, 1)); long chunkSize = Math.ceilDiv(size, threads); System.err.printf("Processing size=%d, threads=%d, chunkSize=%d%n", size, threads, chunkSize); List> chunks = LongStream.range(0, threads) .parallel() .mapToObj(i -> readChunk(raf.getChannel(), i * chunkSize, chunkSize)) .toList(); for (Map chunk : chunks) { chunk.forEach((n, m) -> measurements.computeIfAbsent(n.toString(), x -> new MeasurementAggregate()).merge(m)); } } System.out.println(measurements); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_khmarbaise.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.DoubleSummaryStatistics; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.stream.Collectors.groupingBy; public class CalculateAverage_khmarbaise { private static final Path MEASUREMENT_FILES = Path.of("./measurements.txt"); private record MeasurementRecord(String city, Double measuredValue) { } private static final Function toMeasurementRecord = line -> { var posOf = line.indexOf(";"); var city = line.substring(0, posOf); var measuredValue = line.substring(posOf + 1); return new MeasurementRecord(city, Double.parseDouble(measuredValue)); }; private static final Function, String> MIN_AVG_MAX = s -> String.format("%s=%.1f/%.1f/%.1f", s.getKey(), s.getValue().getMin(), s.getValue().getAverage(), s.getValue().getMax()); public static void main(String[] args) throws IOException { try (Stream lines = Files.lines(MEASUREMENT_FILES)) { var collect = lines .parallel() .map(toMeasurementRecord) .collect(groupingBy(MeasurementRecord::city, Collectors.summarizingDouble(MeasurementRecord::measuredValue))) .entrySet() .stream() .sorted(Map.Entry.comparingByKey()) .map(MIN_AVG_MAX) .collect(Collectors.joining(", ")); System.out.println("{" + collect + "}"); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_kuduwa_keshavram.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.StandardOpenOption; import java.util.Iterator; import java.util.Spliterator; import java.util.Spliterators; import java.util.TreeMap; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; import sun.misc.Unsafe; public class CalculateAverage_kuduwa_keshavram { private static final String FILE = "./measurements.txt"; private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { try { final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws IOException, InterruptedException { TreeMap resultMap = getFileSegments(new File(FILE)) .flatMap( segment -> { Result result = new Result(); while (segment.start < segment.end) { byte[] city = new byte[100]; byte b; int hash = 0; int i = 0; while ((b = UNSAFE.getByte(segment.start++)) != 59) { hash = 31 * hash + b; city[i++] = b; } byte[] newCity = new byte[i]; System.arraycopy(city, 0, newCity, 0, i); int measurement = 0; boolean negative = false; while ((b = UNSAFE.getByte(segment.start++)) != 10) { if (b == 45) { negative = true; } else if (b == 46) { // skip } else { final int n = b - '0'; measurement = measurement * 10 + n; } } putOrMerge( result, new Measurement(hash, newCity, negative ? measurement * -1 : measurement)); } Iterator iterator = getMeasurementIterator(result); return StreamSupport.stream( Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), true); }) .collect( Collectors.toMap( measurement -> new String(measurement.city), Function.identity(), (m1, m2) -> { m1.merge(m2); return m1; }, TreeMap::new)); System.out.println(resultMap); } private static Iterator getMeasurementIterator(Result result) { return new Iterator<>() { final int uniqueIndex = result.uniqueIndex; final int[] indexArray = result.indexArray; final Measurement[][] measurements = result.measurements; int i = 0; int j = 0; @Override public boolean hasNext() { return i < uniqueIndex; } @Override public Measurement next() { Measurement measurement = measurements[indexArray[i]][j++]; if (measurements[indexArray[i]][j] == null) { i++; j = 0; } return measurement; } }; } static class Result { final Measurement[][] measurements = new Measurement[1024 * 128][3]; final int[] indexArray = new int[10_000]; int uniqueIndex = 0; } private static void putOrMerge(Result result, Measurement measurement) { int index = measurement.hash & (result.measurements.length - 1); Measurement[] existing = result.measurements[index]; for (int i = 0; i < existing.length; i++) { Measurement existingMeasurement = existing[i]; if (existingMeasurement == null) { result.measurements[index][i] = measurement; if (i == 0) { result.indexArray[result.uniqueIndex++] = index; } return; } if (equals(existingMeasurement.city, measurement.city)) { existingMeasurement.merge(measurement); return; } } } private static boolean equals(byte[] city1, byte[] city2) { for (int i = 0; i < city1.length; i++) { if (city1[i] != city2[i]) { return false; } } return true; } private static final class FileSegment { long start; long end; private FileSegment(long start, long end) { this.start = start; this.end = end; } } private static final class Measurement { private final int hash; private final byte[] city; int min; int max; int sum; int count; private Measurement(int hash, byte[] city, int temp) { this.hash = hash; this.city = city; this.min = this.max = this.sum = temp; this.count = 1; } private void merge(Measurement m2) { this.min = this.min < m2.min ? this.min : m2.min; this.max = this.max > m2.max ? this.max : m2.max; this.sum = this.sum + m2.sum; this.count = this.count + m2.count; } @Override public String toString() { return String.format( "%.1f/%.1f/%.1f", this.min / 10f, (this.sum / 10f) / this.count, this.max / 10f); } } private static Stream getFileSegments(final File file) throws IOException { final int numberOfSegments = Runtime.getRuntime().availableProcessors() * 4; final long[] chunks = new long[numberOfSegments + 1]; try (var fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ)) { final long fileSize = fileChannel.size(); final long segmentSize = (fileSize + numberOfSegments - 1) / numberOfSegments; final long mappedAddress = fileChannel.map(MapMode.READ_ONLY, 0, fileSize, Arena.global()).address(); chunks[0] = mappedAddress; final long endAddress = mappedAddress + fileSize; for (int i = 1; i < numberOfSegments; ++i) { long chunkAddress = mappedAddress + i * segmentSize; // Align to first row start. while (chunkAddress < endAddress && UNSAFE.getByte(chunkAddress++) != '\n') { // nop } chunks[i] = Math.min(chunkAddress, endAddress); } chunks[numberOfSegments] = endAddress; } return IntStream.range(0, chunks.length - 1) .mapToObj(chunkIndex -> new FileSegment(chunks[chunkIndex], chunks[chunkIndex + 1])) .parallel(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_kumarsaurav123.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collector; import java.util.stream.Collectors; import static java.util.stream.Collectors.groupingBy; public class CalculateAverage_kumarsaurav123 { private static final String FILE = "./measurements.txt"; private static AtomicInteger indexCount = new AtomicInteger(0); private static final ReentrantLock lock = new ReentrantLock(); private static final int MAX_UNIQUE_KEYS = 11000; private static Map indexMap; private static record Store(double[] min, double[] max, double[] sum, int[] count) { private double round(double value) { return Math.round(value * 10.0) / 10.0; } @Override public String toString() { return new TreeMap<>(indexMap.entrySet() .stream() .map(e -> Map.entry(e.getKey().toString(), round(min[e.getValue()]) + "/" + round((Math.round(sum[e.getValue()] * 10.0) / 10.0) / count[e.getValue()]) + "/" + round(max[e.getValue()]) )) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))).toString(); } } private static record Pair(long start, int size) { } public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { long start = System.currentTimeMillis(); System.out.println(run(FILE)); } public static String run(String filePath) throws IOException, InterruptedException, ExecutionException { indexCount = new AtomicInteger(0); indexMap = new HashMap<>(MAX_UNIQUE_KEYS); ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); CompletionService completionService = new ExecutorCompletionService<>(executorService); Map> leftOutsMap = new ConcurrentSkipListMap<>(); RandomAccessFile file = new RandomAccessFile(filePath, "r"); long filelength = file.length(); AtomicInteger kk = new AtomicInteger(); MemorySegment memorySegment = file.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, filelength, Arena.ofShared()); int nChunks = 1000; int pChunkSize = Math.min(Integer.MAX_VALUE, (int) (memorySegment.byteSize() / (1000))); if (pChunkSize < 100) { pChunkSize = (int) memorySegment.byteSize(); nChunks = 1; } ArrayList chunks = createStartAndEnd(pChunkSize, nChunks, memorySegment); chunks.stream() .parallel() .map(p -> { return createRunnable(memorySegment, p); }) .forEach(completionService::submit); executorService.shutdown(); int i = 0; double[] min = new double[MAX_UNIQUE_KEYS]; double[] max = new double[MAX_UNIQUE_KEYS]; double[] sum = new double[MAX_UNIQUE_KEYS]; int[] count = new int[MAX_UNIQUE_KEYS]; initArray(i, count, min, max, sum); i = 0; final Store cureentStore = new Store(min, max, sum, count); while (i < chunks.size()) { Store newStore = completionService.take().get(); Map reverseMap = indexMap.entrySet() .stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); reverseMap.forEach((key, value) -> { cureentStore.sum[key] += newStore.sum[key]; cureentStore.count[key] += newStore.count[key]; cureentStore.min[key] = Math.min(cureentStore.min[key], newStore.min[key]); cureentStore.max[key] = Math.max(cureentStore.max[key], newStore.max[key]); }); i++; } return cureentStore.toString(); } private static void initArray(int i, int[] count, double[] min, double[] max, double[] sum) { for (; i < count.length; i++) { min[i] = Double.POSITIVE_INFINITY; max[i] = Double.NEGATIVE_INFINITY; sum[i] = 0.0d; count[i] = 0; } } private static ArrayList createStartAndEnd(int chunksize, int nChunks, MemorySegment memorySegment) { ArrayList startSizePairs = new ArrayList<>(); byte eol = "\n".getBytes(StandardCharsets.UTF_8)[0]; long start = 0; long end = -1; if (nChunks == 1) { startSizePairs.add(new Pair(0, chunksize)); return startSizePairs; } else { while (start < memorySegment.byteSize()) { start = end + 1; end = Math.min(memorySegment.byteSize() - 1, start + chunksize - 1); while (memorySegment.get(ValueLayout.JAVA_BYTE, end) != eol) { end--; } startSizePairs.add(new Pair(start, (int) (end - start + 1))); } } return startSizePairs; } public static Callable createRunnable(MemorySegment memorySegment, Pair p) { return new Callable() { @Override public Store call() { try { double[] min = new double[MAX_UNIQUE_KEYS]; double[] max = new double[MAX_UNIQUE_KEYS]; double[] sum = new double[MAX_UNIQUE_KEYS]; int[] count = new int[MAX_UNIQUE_KEYS]; for (int i = 0; i < count.length; i++) { min[i] = Double.POSITIVE_INFINITY; max[i] = Double.NEGATIVE_INFINITY; sum[i] = 0.0d; count[i] = 0; } byte[] allBytes2 = memorySegment.asSlice(p.start, p.size).toArray(ValueLayout.JAVA_BYTE); byte[] eol = "\n".getBytes(StandardCharsets.UTF_8); byte[] sep = ";".getBytes(StandardCharsets.UTF_8); int st = 0; for (int i = 0; i < allBytes2.length; i++) { if (allBytes2[i] == eol[0]) { ; byte[] s2 = new byte[i - st]; System.arraycopy(allBytes2, st, s2, 0, s2.length); for (int j = 0; j < s2.length; j++) { if (s2[j] == sep[0]) { byte[] city = new byte[j]; byte[] value = new byte[s2.length - j - 1]; System.arraycopy(s2, 0, city, 0, city.length); System.arraycopy(s2, city.length + 1, value, 0, value.length); double d = getaDouble(value); StringHolder citys = new StringHolder(city); Integer index = indexMap.get(citys); if (Objects.isNull(index)) { lock.lock(); if (Objects.isNull(indexMap.get(citys))) { index = indexCount.getAndIncrement(); indexMap.putIfAbsent(citys, index); } index = indexMap.get(citys); lock.unlock(); } count[index] = count[index] + 1; max[index] = Math.max(max[index], d); min[index] = Math.min(min[index], d); sum[index] = Double.sum(sum[index], d); break; } } st = i + 1; } } // System.out.println("Task " + kk + "Completed in " + (System.nanoTime() - start)); return new Store(min, max, sum, count); } catch (Exception e) { // throw new RuntimeException(e); throw e; } } }; } private static double getaDouble(byte[] value) { double d = 0.0; int s = -1; for (int k = value.length - 1; k >= 0; k--) { if (value[k] == 45) { d = d * -1; } else if (value[k] == 46) { } else { d = d + (((int) value[k]) - 48) * Math.pow(10, s); s++; } } return d; } static class StringHolder implements Comparable { byte[] bytes; public StringHolder(byte[] bytes) { this.bytes = bytes; } @Override public String toString() { return new String(this.bytes); } @Override public int hashCode() { return Arrays.hashCode(this.bytes); } @Override public boolean equals(Object obj) { return Arrays.equals(this.bytes, ((StringHolder) obj).bytes); } @Override public int compareTo(StringHolder o) { return new String(this.bytes).compareTo(new String(o.bytes)); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_lawrey.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.LongStream; public class CalculateAverage_lawrey { // Path to the file containing temperature measurements. private static final String FILE = "./measurements.txt"; // Inner class representing a measurement with min, max, and average calculations. static class Measurement { double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; double sum = 0.0; long count = 0; // Default constructor for Measurement. public Measurement() { } // Adds a new temperature sample and updates min, max, and average. public void sample(double temp) { min = Math.min(min, temp); max = Math.max(max, temp); sum += temp; count++; } // Returns a formatted string representing min, average, and max. public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } // Helper method to round a double value to one decimal place. private double round(double value) { return Math.round(value * 10.0) / 10.0; } // Merges this Measurement with another Measurement. public Measurement merge(Measurement m2) { min = Math.min(min, m2.min); max = Math.max(max, m2.max); sum += m2.sum; count += m2.count; return this; } } // Inner class representing a key for measurements. static class Key { final byte[] data = new byte[32]; int hash = 0, length = 0; // Override of hashCode using the hash field. @Override public int hashCode() { return hash; } // Custom equals method to compare two Key objects. @Override public boolean equals(Object obj) { if (obj instanceof Key k) { if (length != k.length || hash != k.hash) return false; return Arrays.equals(data, 0, length, k.data, 0, length); } return false; } // Converts the key's data to a String. @Override public String toString() { return new String(data, 0, length, StandardCharsets.UTF_8); } // Appends a byte to the key and updates its hash. public void append(byte b) { data[length++] = b; hash = hash * 10191 + b; } // Resets the key to its initial state. public void clear() { length = hash = 0; } } public static void main(String[] args) throws IOException { // Open the file for reading. File file = new File(FILE); long length = file.length(); long chunk = 1 << 28; // Size of the chunk to be processed. RandomAccessFile raf = new RandomAccessFile(file, "r"); // Process the file in chunks and merge the results. Map allMeasurementsMap = LongStream.range(0, length / chunk + 1) .parallel() .mapToObj(i -> extractMeasurementsFromChunk(i, chunk, length, raf)) .reduce((a, b) -> mergeMeasurementMaps(a, b)) .orElseGet(Collections::emptyMap); // Sort the measurements and print them. Map sortedMeasurementsMap = new TreeMap<>(); allMeasurementsMap.forEach((k, m) -> sortedMeasurementsMap.put(k.toString(), m)); System.out.println(sortedMeasurementsMap); } // Merges two measurement maps. private static Map mergeMeasurementMaps(Map a, Map b) { a.forEach((k, m) -> { Measurement m2 = b.get(k); if (m2 != null) m.merge(m2); }); b.forEach(a::putIfAbsent); return a; } // Extracts measurements from a chunk of the file. private static Map extractMeasurementsFromChunk(long i, long chunk, long length, RandomAccessFile raf) { long start = i * chunk; long size = Math.min(length, start + chunk + 64 * 1024) - start; Map map = new HashMap<>(1024); try { MappedByteBuffer mbb = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, start, size); if (i > 0) skipToFirstLine(mbb); Key key = new Key(); while (mbb.remaining() > 0 && mbb.position() <= chunk) { key.clear(); readKey(mbb, key); int temp = readTemperatureFromBuffer(mbb); Measurement m = map.computeIfAbsent(key, n -> new Measurement()); if (m.count == 0) key = new Key(); m.sample(temp / 10.0); } } catch (IOException ioe) { throw new RuntimeException(ioe); } return map; } // Reads a temperature value from the buffer. private static int readTemperatureFromBuffer(MappedByteBuffer mbb) { int temp = 0; boolean negative = false; outer: while (mbb.remaining() > 0) { byte b = mbb.get(); switch (b) { case '-': negative = true; break; case '.': break; case '\r': case '\n': break outer; default: temp = 10 * temp + (b - '0'); break; } } if (mbb.remaining() > 0) { byte b = mbb.get(mbb.position()); if (b == '\n') mbb.get(); } if (negative) temp = -temp; return temp; } // Reads a key from the buffer. private static void readKey(MappedByteBuffer mbb, Key key) { while (mbb.remaining() > 0) { byte b = mbb.get(); if (b == ';' || b == '\r' || b == '\n') break; key.append(b); } } // Skips to the first line in the buffer, used for chunk processing. private static void skipToFirstLine(MappedByteBuffer mbb) { while ((mbb.get() & 0xFF) >= ' ') { // Skip bytes until reaching the start of a line. } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_maeda6uiui.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_maeda6uiui { record RecordCollectorResult( Map mins, Map maxes, Map sums, Map counts) { } static class RecordCollector implements Callable { private String inputFilepath; private long startByteIndex; private long numBytesToRead; private char delimiter; private int byteBufferSize; private int bisBufferSize; private Map mins; private Map maxes; private Map sums; private Map counts; public RecordCollector( String inputFilepath, long startByteIndex, long numBytesToRead, char delimiter, int byteBufferSize, int bisBufferSize) { this.inputFilepath = inputFilepath; this.startByteIndex = startByteIndex; this.numBytesToRead = numBytesToRead; this.delimiter = delimiter; this.byteBufferSize = byteBufferSize; this.bisBufferSize = bisBufferSize; mins = new HashMap<>(); maxes = new HashMap<>(); sums = new HashMap<>(); counts = new HashMap<>(); } private int byteToInt(byte b) { return switch (b) { case '0' -> 0; case '1' -> 1; case '2' -> 2; case '3' -> 3; case '4' -> 4; case '5' -> 5; case '6' -> 6; case '7' -> 7; case '8' -> 8; case '9' -> 9; default -> -1; }; } private double parseDouble(byte[] bs) { // Get the sign int valSign; if (bs[0] == '-') { valSign = -1; } else { valSign = 1; } // Get the dot position int dotPos = -1; for (int i = 0; i < bs.length; i++) { if (bs[i] == '.') { dotPos = i; break; } } if (dotPos == -1) { return Double.NaN; } // Get the integer part int valIntPart; int intPartStartIndex = (valSign == -1) ? 1 : 0; int intPartLength = dotPos - intPartStartIndex; // One-digit value if (intPartLength == 1) { valIntPart = this.byteToInt(bs[dotPos - 1]); } // Two-digit value else if (intPartLength == 2) { int valTens = this.byteToInt(bs[dotPos - 2]); int valOnes = this.byteToInt(bs[dotPos - 1]); valIntPart = valTens * 10 + valOnes; } else { return Double.NaN; } // Get the decimal part double valDecPart = this.byteToInt(bs[dotPos + 1]) * 0.1; return valSign * (valIntPart + valDecPart); } @Override public RecordCollectorResult call() { // Start and end indices are most likely pointing to the middle of a line // Therefore, actual start and end indices should be determined // before proceeding to actual reading of the file long actualStartByteIndex = -1; long actualEndByteIndex = -1; try (var bis = new BufferedInputStream(new FileInputStream(inputFilepath))) { int b; int readCount = 0; long firstLFPos; // If start index specified is 0, actual start index is also 0 if (startByteIndex == 0) { actualStartByteIndex = 0; } else { // Skip until the preceding byte of the start index specified bis.skipNBytes(startByteIndex - 1); // Get the preceding byte b = bis.read(); // If the preceding byte is LF, // actual start index is the start index specified // because it is the start of a new line if (b == '\n') { actualStartByteIndex = startByteIndex; } } if (actualStartByteIndex != -1) { // Skip until the end byte specified bis.skipNBytes(numBytesToRead); } // Start index specified is pointing to the middle of a line // In that case, actual start index is the one following the LF of that line // (Start index of the next line) else { firstLFPos = startByteIndex; while ((b = bis.read()) != -1) { readCount++; if (b == '\n') { break; } firstLFPos++; } actualStartByteIndex = firstLFPos + 1; // Skip until the end byte specified bis.skipNBytes(numBytesToRead - readCount); } // Actual end index is the first LF encountered readCount = 0; firstLFPos = startByteIndex + numBytesToRead; while ((b = bis.read()) != -1) { readCount++; if (b == '\n') { break; } firstLFPos++; } actualEndByteIndex = firstLFPos; } catch (IOException e) { System.err.println(e); return null; } // Get actual number of bytes to read long actualNumBytesToRead = actualEndByteIndex - actualStartByteIndex + 1; // Read bytes from the range obtained above try (var bis = new BufferedInputStream(new FileInputStream(inputFilepath), bisBufferSize)) { // Skip until the start byte bis.skipNBytes(actualStartByteIndex); final int EXTENSION_SIZE = 64; var buffer = new byte[byteBufferSize]; var extendedBuffer = new byte[byteBufferSize + EXTENSION_SIZE]; // Read bytes in chunk long numTotalBytesRead = 0; while (true) { int chunkSize; if (actualNumBytesToRead - numTotalBytesRead < byteBufferSize) { chunkSize = (int) (actualNumBytesToRead - numTotalBytesRead); } else { chunkSize = byteBufferSize; } if (chunkSize <= 0) { break; } Arrays.fill(buffer, (byte) 0); bis.read(buffer, 0, chunkSize); numTotalBytesRead += chunkSize; // Copy read content to another buffer Arrays.fill(extendedBuffer, (byte) 0); System.arraycopy(buffer, 0, extendedBuffer, 0, chunkSize); // Read until next LF is found // if end of buffer read above does not correspond to end of line for (int i = 0; i < EXTENSION_SIZE; i++) { int b = bis.read(); if (b == -1) { break; } else if (b == '\n') { extendedBuffer[chunkSize + i] = '\n'; numTotalBytesRead++; break; } extendedBuffer[chunkSize + i] = (byte) b; numTotalBytesRead++; } int currentDelimPos = -1; int currentLFPos = -1; int nextLineStartPos = 0; for (int i = 0; i < extendedBuffer.length; i++) { if (extendedBuffer[i] == 0) { break; } if (extendedBuffer[i] == delimiter) { currentDelimPos = i; } else if (extendedBuffer[i] == '\n') { currentLFPos = i; } if (currentLFPos != -1) { // Error if (currentDelimPos == -1) { System.err.printf( "Error near byte index %d\n", actualStartByteIndex + numTotalBytesRead); } else { String stationName = new String( Arrays.copyOfRange(extendedBuffer, nextLineStartPos, currentDelimPos)); // Parse string to double by myself // because Double.parseDouble() is slow... double temperature = this.parseDouble( Arrays.copyOfRange(extendedBuffer, currentDelimPos + 1, currentLFPos)); // Populate the maps if (!mins.containsKey(stationName)) { mins.put(stationName, temperature); maxes.put(stationName, temperature); sums.put(stationName, temperature); counts.put(stationName, 1); } else { double currentMin = mins.get(stationName); double currentMax = maxes.get(stationName); double currentSum = sums.get(stationName); int currentCount = counts.get(stationName); if (temperature < currentMin) { mins.put(stationName, temperature); } else if (temperature > currentMax) { maxes.put(stationName, temperature); } sums.put(stationName, currentSum + temperature); counts.put(stationName, currentCount + 1); } } nextLineStartPos = currentLFPos + 1; currentDelimPos = -1; currentLFPos = -1; } } } } catch (IOException e) { System.err.println(e); return null; } return new RecordCollectorResult(mins, maxes, sums, counts); } } private static double round(double d) { return Math.round(d * 10.0) / 10.0; } public static void main(String[] args) { final String INPUT_FILEPATH = "./measurements.txt"; final int DESIRED_NUM_THREADS = 20; final char DELIMITER = ';'; final int BIS_BUFFER_SIZE = 1024 * 1024; final int BYTE_BUFFER_SIZE = 1024; final int MULTI_THREAD_NUM_LINES_THRESHOLD = DESIRED_NUM_THREADS * 10; // First get the number of total bytes in the input file long numTotalBytes; try { numTotalBytes = Files.size(Paths.get(INPUT_FILEPATH)); } catch (IOException e) { e.printStackTrace(); return; } // Make sure the input file has enough lines // for this multithreading approach to work efficiently int actualNumThreads = 1; try (var br = new BufferedReader(new FileReader(INPUT_FILEPATH))) { int lineCount = 0; while (br.readLine() != null) { lineCount++; if (lineCount >= MULTI_THREAD_NUM_LINES_THRESHOLD) { actualNumThreads = DESIRED_NUM_THREADS; break; } } } catch (IOException e) { e.printStackTrace(); return; } // Calculate the number of bytes each thread has to process long numBytesToProcessPerThread = numTotalBytes / actualNumThreads; long remainingNumBytesToProcess = numTotalBytes % actualNumThreads; var exec = Executors.newFixedThreadPool(actualNumThreads); var futures = new ArrayList>(); for (int i = 0; i < actualNumThreads; i++) { RecordCollector recordCollector; if (i == actualNumThreads - 1) { recordCollector = new RecordCollector( INPUT_FILEPATH, i * numBytesToProcessPerThread, numBytesToProcessPerThread + remainingNumBytesToProcess, DELIMITER, BYTE_BUFFER_SIZE, BIS_BUFFER_SIZE); } else { recordCollector = new RecordCollector( INPUT_FILEPATH, i * numBytesToProcessPerThread, numBytesToProcessPerThread, DELIMITER, BYTE_BUFFER_SIZE, BIS_BUFFER_SIZE); } Future future = exec.submit(recordCollector); futures.add(future); } // Consolidate results of each thread var mins = new HashMap(); var maxes = new HashMap(); var sums = new HashMap(); var counts = new HashMap(); try { for (var future : futures) { RecordCollectorResult result = future.get(); result.mins.forEach((k, v) -> { if (!mins.containsKey(k)) { mins.put(k, v); } else { mins.put(k, Double.min(v, mins.get(k))); } }); result.maxes.forEach((k, v) -> { if (!maxes.containsKey(k)) { maxes.put(k, v); } else { maxes.put(k, Double.max(v, maxes.get(k))); } }); result.sums.forEach((k, v) -> { if (!sums.containsKey(k)) { sums.put(k, v); } else { sums.put(k, Double.sum(v, sums.get(k))); } }); result.counts.forEach((k, v) -> { if (!counts.containsKey(k)) { counts.put(k, v); } else { counts.put(k, Integer.sum(v, counts.get(k))); } }); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); return; } finally { exec.shutdown(); } // Calculate means var means = new HashMap(); sums.forEach((k, v) -> means.put(k, v / counts.get(k))); // Sort station names List sortedStationNames = means .keySet() .stream() .sorted() .toList(); // Create output string var sb = new StringBuilder(); sb.append("{"); sortedStationNames.forEach(stationName -> { sb .append(stationName) .append("=") .append(round(mins.get(stationName))) .append("/") .append(round(means.get(stationName))) .append("/") .append(round(maxes.get(stationName))) .append(", "); }); sb.delete(sb.length() - 2, sb.length()); sb.append("}"); // Print result string System.out.println(sb); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_mahadev_k.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; public class CalculateAverage_mahadev_k { private static final String FILE = "./measurements.txt"; private static Map stationMap = new ConcurrentSkipListMap<>(); private static double round(double value) { return Math.round(value * 10.0) / 10.0; } private static class MeasurementAggregator { double minima = Double.POSITIVE_INFINITY, maxima = Double.NEGATIVE_INFINITY, total = 0, count = 0; public synchronized void accept(double value) { if (minima > value) minima = value; if (maxima < value) maxima = value; total += value; count++; } public double min() { return round(minima); } public double max() { return round(maxima); } public double avg() { return round((Math.round(total * 10.0) / 10.0) / count); } } public static void main(String[] args) throws IOException { int chunkSize = args.length == 1 ? Integer.parseInt(args[0]) : 1_000_000; readAndProcess(chunkSize); print(); } public static void readAndProcess(int chunkSize) { final ThreadFactory factory = Thread.ofVirtual().name("routine-", 0).factory(); try (RandomAccessFile file = new RandomAccessFile(FILE, "r")) { try (var executor = Executors.newThreadPerTaskExecutor(factory)) { var channel = file.getChannel(); var size = channel.size(); long start = 0; while (start <= size) { long end = start + chunkSize; String letter = ""; do { end--; ByteBuffer buffer = ByteBuffer.allocate(1); channel.read(buffer, end); buffer.flip(); letter = StandardCharsets.UTF_8.decode(buffer).toString(); } while (!letter.equals("\n")); if (end < start) end = start + chunkSize; final long currentStart = start; final long currentEnd = end; executor.submit(() -> { ByteBuffer buffer = ByteBuffer.allocate((int) (currentEnd - currentStart + 1)); try { channel.read(buffer, currentStart); } catch (IOException e) { e.printStackTrace(); } buffer.flip(); String data = StandardCharsets.UTF_8.decode(buffer).toString(); processData(data); }); start = end + 1; } } } catch (IOException e) { e.printStackTrace(); } } public static void processData(String dataBlock) { StringTokenizer tokenizer = new StringTokenizer(dataBlock, "\n"); while (tokenizer.hasMoreElements()) { StringTokenizer tokens = new StringTokenizer(tokenizer.nextToken(), ";"); String station = tokens.nextToken(); double value = Double.parseDouble(tokens.nextToken()); processMinMaxMean(station, value); } } private static void processMinMaxMean(String station, double temp) { var values = stationMap.get(station); if (values == null) { values = new MeasurementAggregator(); stationMap.putIfAbsent(station, values); } values = stationMap.get(station); values.accept(temp); } public static void print() throws UnsupportedEncodingException { System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out), true, StandardCharsets.UTF_8)); System.out.print("{"); int i = stationMap.size(); for (var kv : stationMap.entrySet()) { System.out.printf("%s=%s/%s/%s", kv.getKey(), kv.getValue().min(), kv.getValue().avg(), kv.getValue().max()); if (i > 1) System.out.print(", "); i--; } System.out.println("}"); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_makohn.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Collection; import java.util.stream.Collectors; // // This implementation is partially inspired by // // - GavinRay97: 1BRC in Kotlin (memory mapping, chunking) | https://github.com/gunnarmorling/1brc/discussions/154 // - dannyvankooten: 1BRC in C (integer parsing, linear probing) | https://github.com/gunnarmorling/1brc/discussions/46 // public class CalculateAverage_makohn { private static final String FILE = "./measurements.txt"; private static class Measurement implements Comparable { final String city; int min; int max; int count = 1; int sum; Measurement(String city, int val) { this.city = city; this.min = val; this.max = val; this.sum = val; } @Override public String toString() { return STR."\{city}=\{round(min)}/\{round((1.0 * sum) / count)}/\{round(max)}"; } private double round(double value) { return Math.round(value) / 10.0; } @Override public int compareTo(Measurement other) { return this.city.compareTo(other.city); } } // Convert a given byte array of temperature data to an int value // Since the temperate values only have one decimal, we can use integer arithmetic until the end // // buffer: [..., '-', '1', '9', '.', '7', ...] // -------------> offset // ............ = s // // We initialize a "pointer" s with the offset. Depending on whether the first char is a '-' or not, we set the // sign and increment the pointer. // // Then we only have to distinguish between one-digit and two-digit numbers. // Depending on that, we set an index for the respective parts of the number. // private static int toInt(byte[] in, int offset) { int sign = 1; int s = offset; if (in[s] == '-') { sign = -1; s++; } if (in[s + 1] == '.') return sign * ((in[s] - '0') * 10 + (in[s + 2] - '0')); return sign * ((in[s] - '0') * 100 + (in[s + 1] - '0') * 10 + (in[s + 3] - '0')); } // 10_000 distinct station names as per specification // We use the next power of two (2^14 = 16384) to allow for bit-masking our hash (instead of using modulo) private static final int MAX_STATIONS = 2 << 14; // Twice as big as the maximum number of stations private static final int MAP_CAPACITY = MAX_STATIONS * 2; // We start at 1 to allow for checking our hash-index map for > 0 private static final int RES_FIRST_INDEX = 1; private static class ResultMap { final int[] map = new int[MAP_CAPACITY]; // hash -> index final Measurement[] measurements = new Measurement[MAX_STATIONS]; // index -> measurement private int lastIndex = 0; private void put(int hash, Measurement measurement) { lastIndex++; measurements[lastIndex] = measurement; map[hash] = lastIndex; } private boolean contains(int hash) { return map[hash] > 0; } private Measurement get(int hash) { return measurements[map[hash]]; } } // We use linear probing as our hash-collision strategy // // We use MAP_CAPACITY - 1 as a bitmask to force the hash to be lower than our capacity // Let's consider a hash 16390. If our capacity is 2^14 = 16384, the hash is out of bounds. // // 16390 : 100000000000110 // 16383 : 011111111111111 // ....... 000000000000110 = 3 private static int linearProbe(ResultMap res, String key) { var hash = key.hashCode() & (MAP_CAPACITY - 1); while (res.map[hash] > 0 && !(res.measurements[res.map[hash]].city.equals(key))) { hash = (hash + 1) & (MAP_CAPACITY - 1); } return hash; } // Custom Quicksort implementation, seems to be slightly faster than Arrays.sort private static void quickSort(Measurement[] arr, int begin, int end) { if (begin < end) { final var partitionIndex = partition(arr, begin, end); quickSort(arr, begin, partitionIndex - 1); quickSort(arr, partitionIndex + 1, end); } } private static int partition(Measurement[] arr, int begin, int end) { final var pivot = arr[end]; int i = (begin - 1); for (int j = begin; j < end; j++) { if (arr[j].compareTo(pivot) <= 0) { i++; final var tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } final var tmp = arr[i + 1]; arr[i + 1] = arr[end]; arr[end] = tmp; return i + 1; } private static Collection getChunks(MemorySegment memory, long chunkSize, long fileSize) { final var chunks = new ArrayList(); var chunkStart = 0L; var chunkEnd = 0L; while (chunkStart < fileSize) { chunkEnd = Math.min((chunkStart + chunkSize), fileSize); // starting from the calculated chunkEnd, seek the next newline to get the real chunkEnd while (chunkEnd < fileSize && (memory.getAtIndex(ValueLayout.JAVA_BYTE, chunkEnd) & 0xFF) != '\n') chunkEnd++; // we have found our chunk boundaries, add a slice of memory with these boundaries to our list of chunks if (chunkEnd < fileSize) chunks.add(memory.asSlice(chunkStart, chunkEnd - chunkStart + 1).asByteBuffer()); else // special case: we are at the end of the file chunks.add(memory.asSlice(chunkStart, chunkEnd - chunkStart).asByteBuffer()); // next chunk chunkStart = chunkEnd + 1; } return chunks; } // Station name: <= 100 bytes // Temperature: <= 5 bytes // // Semicolon and new line are ignored private static final int MAX_BYTES_PER_ROW = 105; private static ResultMap processChunk(ByteBuffer chunk) { final var map = new ResultMap(); final var buffer = new byte[MAX_BYTES_PER_ROW]; var i = 0; var delimiter = 0; // Process the chunk byte by byte and store each line in buffer while (chunk.hasRemaining()) { final var c = chunk.get(); // System.out.println((char) (c & 0xFF)); switch (c & 0xFF) { // Memorize the position of the semicolon, such that we can divide the buffer afterward case ';' -> delimiter = i; // If we encounter newline, we can do the actual calculations for the current line case '\n' -> { final var key = new String(buffer, 0, delimiter, StandardCharsets.UTF_8); final var value = toInt(buffer, delimiter); final var hash = linearProbe(map, key); if (map.contains(hash)) { final var current = map.get(hash); current.min = Math.min(current.min, value); current.max = Math.max(current.max, value); current.count++; current.sum += value; } else { map.put(hash, new Measurement(key, value)); } i = 0; delimiter = 0; } default -> { buffer[i] = c; i++; } } } return map; } // File size is approximately 13 GB, ByteBuffer has a 2 GB limit // Chunks should have a maximum size of approximately 13 GB / 8 = 1.625 GB private static final int MIN_NUMBER_THREADS = 8; public static void main(String[] args) throws Exception { final var numProcessors = Math.max(Runtime.getRuntime().availableProcessors(), MIN_NUMBER_THREADS); // memory-map the input file try (final var channel = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ)) { final var fileSize = channel.size(); final var chunkSize = (fileSize / numProcessors); final var mappedMemory = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); // process the mapped data concurrently in chunks. Each chunk is processed on a dedicated thread final var chunks = getChunks(mappedMemory, chunkSize, fileSize); final var processed = chunks .parallelStream() .map(CalculateAverage_makohn::processChunk) .collect(Collectors.toList()); // materialize and thus synchronize // merge the results, we can initialize with the first result, to avoid redundant probing final var first = processed.removeFirst(); final var res = processed .stream() .reduce(first, (acc, partial) -> { for (int i = RES_FIRST_INDEX; i <= partial.lastIndex; i++) { final var value = partial.measurements[i]; final var hash = linearProbe(acc, value.city); if (acc.contains(hash)) { final var cur = acc.get(hash); cur.min = Math.min(cur.min, value.min); cur.max = Math.max(cur.max, value.max); cur.count += value.count; cur.sum += value.sum; } else { acc.put(hash, value); } } return acc; }); quickSort(res.measurements, RES_FIRST_INDEX, res.lastIndex); final var sb = new StringBuilder("{"); for (int i = RES_FIRST_INDEX; i < res.lastIndex; i++) { sb.append(res.measurements[i]).append(',').append(' '); } sb.append(res.measurements[res.lastIndex]).append('}'); System.out.println(sb); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_manishgarg90.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; public class CalculateAverage_manishgarg90 { private static final String FILE = "./measurements.txt"; private static int nProcessors = Runtime.getRuntime().availableProcessors(); public static void main(String[] args) throws IOException { try (FileChannel channel = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ)) { long fileSize = channel.size(); long chunkSize = (fileSize + nProcessors - 1) / nProcessors; long pos = 0; List buffers = new ArrayList<>(nProcessors); for (int i = 0; i < nProcessors; i++) { long endPosition = getEndPosition(channel, pos + chunkSize); long size = endPosition - pos; MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, pos, size); pos = pos + size; buffers.add(buffer); } Map s = readBufferAndCalculateMeauremenst(buffers); Map tm = new TreeMap(s); System.out.println(tm); } catch (IOException e) { e.printStackTrace(); } } private static Map readBufferAndCalculateMeauremenst(List chunks) { return chunks.parallelStream().map(buffer -> { Map map = new HashMap<>(10_000, 1); int lineStart = 0; int doubleStart = 0; int length = buffer.limit(); String station = null; for (int i = 0; i < length; ++i) { byte b = buffer.get(i); if (b == ';') { byte[] stationBuffer = new byte[i - lineStart]; buffer.position(lineStart); buffer.get(stationBuffer); station = new String(stationBuffer, StandardCharsets.UTF_8); doubleStart = i + 1; } else if (b == '\n') { byte[] doubleBuffer = new byte[i - doubleStart]; buffer.position(doubleStart); buffer.get(doubleBuffer); Double temperature = Double.parseDouble(new String(doubleBuffer)); lineStart = i + 1; // I have station name and temp Stat s = map.get(station); if (s == null) { map.put(station, new Stat(temperature)); } else { s.update(temperature); } } } return map; }).reduce(new HashMap<>(), (map1, map2) -> { Stat s = new Stat(); s.merge(map1); s.merge(map2); return s.getResultMap(); }); } private static long getEndPosition(FileChannel channel, long position) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1); while (position < channel.size()) { channel.read(buffer, position); if (buffer.get(0) == '\n') { return position + 1; } position++; buffer.clear(); } return channel.size(); } private static final class Stat { private Double min = Double.MAX_VALUE; private Double max = Double.MIN_VALUE; private Double sum = 0d; private long count = 0L; private Map resultMap = null; public Stat() { this.resultMap = new HashMap<>(10_000, 1); } public Stat(Double value) { this.min = value; this.max = value; this.sum += value; this.count++; } private void update(Double value) { this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); this.sum = round(this.sum + value); this.count++; } private void merge(Map result) { result.forEach((city, resultRow) -> resultMap.merge(city, resultRow, (existing, incoming) -> { existing.min = Math.min(existing.min, incoming.min); existing.max = Math.max(existing.max, incoming.max); existing.sum += incoming.sum; existing.count += incoming.count; return existing; })); } public Map getResultMap() { return resultMap; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } @Override public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_martin2038.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class CalculateAverage_martin2038 { // private static final String FILE = "/Users/martin/Garden/blog/1BRC/1brc/./measurements.txt"; private static final String FILE = "./measurements.txt"; private static class MeasurementAggregator { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum; private int count; void update(int temp) { update(1, temp, temp, temp); } void update(int cnt, long sm, int min, int max) { sum += sm; count += cnt; if (this.min > min) { this.min = min; } if (this.max < max) { this.max = max; } } void merge(MeasurementAggregator it) { update(it.count, it.sum, it.min, it.max); } public String toString() { var mean = this.sum / 10.0 / this.count; return (min / 10f) + "/" + Math.round(mean * 10) / 10f + "/" + (max / 10f); } } public static void main(String[] args) throws IOException { var file = new RandomAccessFile(FILE, "r"); final int maxNameLength = 110; var fc = file.getChannel(); split(file).stream().parallel().map(ck -> { // StrFastHashKey 比string快500ms var map = new HashMap(200); // var pb = System.currentTimeMillis(); try { var mb = fc.map(MapMode.READ_ONLY, ck.start, ck.length); var buff = new byte[maxNameLength]; while (mb.hasRemaining()) { var name = readNextHashKey(buff, mb); // var name = readNextString(buff, mb);// .intern(); var temp = readNextInt10Times(buff, mb); add2map(map, name, temp); } // long end = ck.start + ck.length; // do { // var name = readNext(file, ';', 30).intern(); // var temp = Double.parseDouble(readNext(file, '\n', 6)); // var agg = map.computeIfAbsent(name,it->new MeasurementAggregator()); // agg.update(temp); // }while (file.getFilePointer() { var sb = new StringBuilder(map.size() * 100); sb.append('{'); map.entrySet().stream().sorted(Map.Entry.comparingByKey()) .forEachOrdered(kv -> sb.append(kv.getKey()).append('=').append(kv.getValue()).append(", ")); sb.deleteCharAt(sb.length() - 1); sb.setCharAt(sb.length() - 1, '}'); var resultStr = sb.toString(); System.out.println(resultStr); // System.out.println(resultStr.hashCode()); }); } static HashMap reduceMap(HashMap aMap, HashMap bMap) { aMap.forEach((k, v) -> { var b = bMap.get(k); if (null == b) { bMap.put(k, v); } else { b.merge(v); } }); return bMap; } static void add2map(Map map, Key name, int temp) { // 比computeIfAbsent 节约1秒 var agg = map.get(name); if (null == agg) { agg = new MeasurementAggregator(); map.put(name, agg); } // var agg = map.computeIfAbsent(name,it->new MeasurementAggregator()); agg.update(temp); } record FileChunk(long start, long length) { } static List split(RandomAccessFile file) throws IOException { long total = file.length(); var threadNum = Math.max((int) (total / Integer.MAX_VALUE + 1), Runtime.getRuntime().availableProcessors()); long avgChunkSize = total / threadNum; // System.out.println(avgChunkSize +" \t avgChunkSize : INT/MAX \t"+Integer.MAX_VALUE); // Exception in thread "main" java.lang.IllegalArgumentException: Size exceeds Integer.MAX_VALUE // at java.base/sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:1183) long lastStart = 0; var list = new ArrayList(threadNum); for (var i = 0; i < threadNum - 1; i++) { var length = avgChunkSize; file.seek(lastStart + length); while (file.readByte() != '\n') { // file.seek(lastStart+ ++length); ++length; } // include the '\n' length++; list.add(new FileChunk(lastStart, length)); lastStart += length; if (lastStart >= total) { return list; } } list.add(new FileChunk(lastStart, total - lastStart)); return list; } static StrFastHashKey readNextHashKey(byte[] buf, MappedByteBuffer mb) { int i = 1; mb.get(buf, 0, i); byte b; while ((b = mb.get()) != ';') { buf[i++] = b; } return new StrFastHashKey(buf, i); } static String readNextString(byte[] buf, MappedByteBuffer mb) { int i = 1; mb.get(buf, 0, i); byte b; while ((b = mb.get()) != ';') { buf[i++] = b; } return new String(buf, 0, i); } // copy from CalculateAverage_3j5a // 替换 Double.parse // 时间 38秒 -> 5418 ms static int readNextInt10Times(byte[] buf, MappedByteBuffer mb) { final int min_number_len = 3; int i = min_number_len; mb.get(buf, 0, i); byte b; while ((b = mb.get()) != '\n') { buf[i++] = b; } // -3.2 var zeroAscii = '0'; int temperature = buf[--i] - zeroAscii; i--; // skipping dot var base = 10; while (i > 0) { b = buf[--i]; if (b == '-') { temperature = -temperature; } else { temperature = base * (b - zeroAscii) + temperature; base *= base; } } return temperature; } // static String readNext(RandomAccessFile file, char endFlag,int initLength) throws IOException { // StringBuilder input = new StringBuilder(initLength); // int c = -1; // //boolean eol = false; // // while (true) { // c = file.read(); // if( c == endFlag || c == -1) { // break; // } // input.append((char)c); // } // // //if ((c == -1) && (input.length() == 0)) { // // return null; // //} // return input.toString(); // } static class StrFastHashKey implements Comparable { final byte[] name; final int hash; String nameStr; StrFastHashKey(byte[] buf, int size) { name = new byte[size]; System.arraycopy(buf, 0, name, 0, size); // hash = calculateHash(name, 0, size - 1); // FNV1a save 100+ms than calculateHash hash = hashFNV1a(name, size); } @Override public boolean equals(Object o) { // if (this == o) {return true;} // if (o == null || getClass() != o.getClass()) {return false;} StrFastHashKey that = (StrFastHashKey) o; return hash == that.hash && Arrays.equals(name, that.name); } @Override public int hashCode() { return hash; } @Override public String toString() { if (null == nameStr) { nameStr = new String(name); } return nameStr; } @Override public int compareTo(StrFastHashKey o) { return toString().compareTo(o.toString()); } } private static final VarHandle LONG_VIEW = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.nativeOrder()) .withInvokeExactBehavior(); private static final VarHandle INT_VIEW = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder()) .withInvokeExactBehavior(); /** * This is a prime number that gives pretty * good hash distributions * on the data in this challenge. */ private static final long RANDOM_PRIME = 0x7A646E4D; /** * The hash calculation is inspired by * QuestDB FastMap */ private static int calculateHash(byte[] buffer, int startPosition, int endPosition) { long hash = 0; int position = startPosition; for (; position + Long.BYTES <= endPosition; position += Long.BYTES) { long value = (long) LONG_VIEW.get(buffer, position); hash = hash * RANDOM_PRIME + value; } if (position + Integer.BYTES <= endPosition) { int value = (int) INT_VIEW.get(buffer, position); hash = hash * RANDOM_PRIME + value; position += Integer.BYTES; } for (; position <= endPosition; position++) { hash = hash * RANDOM_PRIME + buffer[position]; } hash = hash * RANDOM_PRIME; return (int) hash ^ (int) (hash >>> 32); } private static final int FNV1_32_INIT = 0x811c9dc5; private static final int FNV1_PRIME_32 = 16777619; /** * https://github.com/prasanthj/hasher/blob/master/src/main/java/hasher/FNV1a.java * * FNV1a 32 bit variant. * * @param data - input byte array * @param length - length of array * @return - hashcode */ public static int hashFNV1a(byte[] data, int length) { int hash = FNV1_32_INIT; for (int i = 0; i < length; i++) { hash ^= (data[i] & 0xff); hash *= FNV1_PRIME_32; } return hash; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_mattiz.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; public class CalculateAverage_mattiz { private static final int TWO_BYTE_TO_INT = 480 + 48; // 48 is the ASCII code for '0' private static final int THREE_BYTE_TO_INT = 4800 + 480 + 48; private static final String FILE = "./measurements.txt"; public static final int PARTS = 8; public static void main(String[] args) throws Exception { var result = new CalculateAverage_mattiz().calculate(FILE, PARTS); System.out.println(result); } StationList calculate(String file, int numParts) throws Exception { var buffers = createBuffers(Paths.get(file), numParts); return buffers .parallelStream() .map(this::aggregate) .reduce(StationList::merge) .orElseThrow(); } record BufferAndSize(ByteBuffer buffer, long size) { } List createBuffers(Path file, int numParts) throws IOException { FileChannel fileChannel = FileChannel.open(file, StandardOpenOption.READ); var fileSize = fileChannel.size(); if (fileSize < (1024 * 1024)) { // Only one core for small files numParts = 1; } var chunkSize = fileSize / numParts; var buffers = new ArrayList(); long filePointer = 0; for (int i = 0; i < numParts; i++) { if (i != numParts - 1) { // not last element var adjustedChunkSize = getBuffer(fileChannel, filePointer, chunkSize, true); buffers.add(adjustedChunkSize.buffer()); filePointer += adjustedChunkSize.size(); } else { var adjustedChunkSize = getBuffer(fileChannel, filePointer, fileSize - filePointer, false); buffers.add(adjustedChunkSize.buffer()); } } return buffers; } BufferAndSize getBuffer(FileChannel fileChannel, long start, long size, boolean adjust) throws IOException { MappedByteBuffer buffer = fileChannel.map(READ_ONLY, start, size); var actualSize = ((int) size); if (adjust) { while (buffer.get(actualSize - 1) != '\n') { actualSize--; } } buffer.limit(actualSize); return new BufferAndSize(buffer, actualSize); } private StationList aggregate(ByteBuffer buffer) { var measurements = new StationList(); while (buffer.hasRemaining()) { int startPos = buffer.position(); byte b; int hash = 0; while ((b = buffer.get()) != ';') { hash = ((hash << 5) - hash) + b; } if (hash < 0) { hash = -hash; } int length = buffer.position() - startPos - 1; byte[] station = new byte[length]; buffer.get(startPos, station); int value = readValue(buffer); measurements.update(station, length, hash, value); } return measurements; } /* * Read decimal number from ascii characters (copied from arjenw) * * Example: * If you have the decimal number 1.4, * then byte 1 contain 49 (ascii code for '1') * and byte 3 contain 52 (ascii code for '4') * Subtract 480 + 48 (48 is the ASCII code for '0') * to move number from ascii number to int * * 49 * 10 + 52 - 528 = 14 */ private static int readValue(ByteBuffer buffer) { int value; byte b1 = buffer.get(); byte b2 = buffer.get(); byte b3 = buffer.get(); byte b4 = buffer.get(); if (b2 == '.') {// value is n.n value = (b1 * 10 + b3 - TWO_BYTE_TO_INT); } else { if (b4 == '.') { // value is -nn.n value = -(b2 * 100 + b3 * 10 + buffer.get() - THREE_BYTE_TO_INT); } else if (b1 == '-') { // value is -n.n value = -(b2 * 10 + b4 - TWO_BYTE_TO_INT); } else { // value is nn.n value = (b1 * 100 + b2 * 10 + b4 - THREE_BYTE_TO_INT); } buffer.get(); // new line } return value; } } class CustomMap { private static final int SIZE = 1024 * 64; private final Station[] stationList = new Station[SIZE]; public void addOrUpdate(byte[] stationName, int length, int hash, int value) { int slot = hash & (SIZE - 1); var station = stationList[slot]; while (station != null && station.getHash() != hash && !Arrays.equals( station.getName(), 0, station.getName().length, stationName, 0, length)) { slot = (slot + 1) & (SIZE - 1); station = stationList[slot]; } if (station == null) { stationList[slot] = new Station(stationName, hash); } stationList[slot].add(value); } public Station get(byte[] stationName) { return stationList[findSlot(stationName)]; } public void put(byte[] stationName, Station newStation) { stationList[findSlot(stationName)] = newStation; } private int findSlot(byte[] stationName) { int hash = getHash(stationName); int slot = hash & (SIZE - 1); var station = stationList[slot]; while (station != null && station.getHash() != hash && !Arrays.equals(station.getName(), stationName)) { slot = (slot + 1) & (SIZE - 1); station = stationList[slot]; } return slot; } private int getHash(byte[] key) { int hash = 0; for (byte b : key) { hash = hash * 31 + b; } if (hash < 0) { hash = -hash; } return hash; } public Set> entrySet() { var sorted = new HashMap(); for (var s : stationList) { if (s != null) { sorted.put(s.getName(), s); } } return sorted.entrySet(); } public Map sorted() { var sorted = new TreeMap(); for (var s : stationList) { if (s != null) { sorted.put(new String(s.getName(), StandardCharsets.UTF_8), s); } } return sorted; } } class StationList { private final CustomMap stations = new CustomMap(); public void update(byte[] stationName, int length, int hash, int value) { stations.addOrUpdate(stationName, length, hash, value); } public StationList merge(StationList other) { for (var aggregator : other.stations.entrySet()) { var agg = stations.get(aggregator.getKey()); if (agg == null) { stations.put(aggregator.getKey(), aggregator.getValue()); } else { agg.merge(aggregator.getValue()); } } return this; } @Override public String toString() { return stations.sorted().toString(); } } class Station { private final byte[] name; private final int hash; private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private int sum; private int count; public Station(byte[] name, int hash) { this.name = name; this.hash = hash; } public void add(int max, int min, int sum, int count) { this.max = Math.max(this.max, max); this.min = Math.min(this.min, min); this.sum += sum; this.count += count; } public void add(int value) { this.max = Math.max(this.max, value); this.min = Math.min(this.min, value); this.sum += value; this.count++; } public void merge(Station other) { this.max = Math.max(this.max, other.max); this.min = Math.min(this.min, other.min); this.sum += other.sum; this.count += other.count; } public String toString() { return (min / 10.0) + "/" + (Math.round(((double) sum) / count)) / 10.0 + "/" + (max / 10.0); } public byte[] getName() { return name; } public int getHash() { return hash; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_maximz101.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CalculateAverage_maximz101 { private static final String FILE = "./measurements.txt"; private record Measurement(String station, double value) { } private record ResultRow(double min, double mean, double max) { public String toString() { return STR."\{round(min)}/\{round(mean)}/\{round(max)}"; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private static class MeasurementAggregator { private double min; private double max; private double sum; private long count; public MeasurementAggregator(double min, double max, double sum, long count) { this.min = min; this.max = max; this.sum = sum; this.count = count; } } record FileChunkRange(long start, long end) { } public static void main(String[] args) throws IOException { int parallelism = args.length == 1 ? Integer.parseInt(args[0]) : Runtime.getRuntime().availableProcessors(); Map measurements = new ConcurrentHashMap<>(); try (ExecutorService executor = Executors.newWorkStealingPool(parallelism)) { List chunks = getChunks(new File(FILE), parallelism, 1_048_576); List> completableFutureList = new ArrayList<>(); for (FileChunkRange chunk : chunks) { completableFutureList .add(CompletableFuture .supplyAsync(() -> computePartialAggregations(chunk), executor) .thenAccept(map -> updateResultMap(map, measurements))); } CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[0])) .thenAccept(_ -> System.out.println(new TreeMap<>(measurements))) .join(); } } private static void updateResultMap(Map map, Map measurements) { map.forEach((station, agg) -> measurements.merge(station, new ResultRow(agg.min, agg.sum / agg.count, agg.max), (r1, r2) -> new ResultRow(Math.min(r1.min, r2.min), (r1.mean + r2.mean) / 2, Math.max(r1.max, r2.max)))); } private static Map computePartialAggregations(FileChunkRange chunk) { try (FileChannel channel = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ)) { MappedByteBuffer buffer = channel.map(MapMode.READ_ONLY, chunk.start(), chunk.end() - chunk.start()); return process(buffer); } catch (IOException e) { throw new RuntimeException(e); } } private static List getChunks(File file, int chunksCount, int minChunkSize) { long fileSize = file.length(); long chunkSize = fileSize / chunksCount; if (chunkSize < minChunkSize || chunksCount == 1) { return List.of(new FileChunkRange(0, fileSize)); } int currentChunk = 1; long currentChunkStart = 0; long currentChunkEnd = chunkSize; var list = new ArrayList(chunksCount); try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { while (currentChunk <= chunksCount) { currentChunkEnd = findNextEOLFrom(raf, currentChunkEnd); list.add(new FileChunkRange(currentChunkStart, currentChunkEnd)); // next currentChunkStart = currentChunkEnd + 1; currentChunkEnd = currentChunkStart + chunkSize; if (currentChunkEnd >= fileSize) { list.add(new FileChunkRange(currentChunkStart, fileSize)); break; } currentChunk++; } } catch (IOException e) { throw new RuntimeException(e); } return list; } private static long findNextEOLFrom(RandomAccessFile raf, long currentChunkEnd) throws IOException { raf.seek(currentChunkEnd); while (currentChunkEnd < raf.length() && raf.read() != '\n') { currentChunkEnd++; } return currentChunkEnd; } private static Map process(MappedByteBuffer buffer) { var map = new HashMap(); byte[] lineBytes = new byte[107]; while (buffer.hasRemaining()) { int i = 0; lineBytes[i] = buffer.get(); int separatorIdx = -1; while (lineBytes[i] != '\n' && buffer.hasRemaining()) { lineBytes[++i] = buffer.get(); if (lineBytes[i] == ';') { separatorIdx = i; } } Measurement measurement = parseLine(lineBytes, separatorIdx, i); map.merge(measurement.station, new MeasurementAggregator(measurement.value, measurement.value, measurement.value, 1), (agg, m) -> { agg.min = Math.min(agg.min, m.min); agg.max = Math.max(agg.max, m.max); agg.sum += m.sum; agg.count++; return agg; }); } return map; } private static Measurement parseLine(byte[] lineBytes, int separatorIdx, int eolIdx) { return new Measurement( new String(lineBytes, 0, separatorIdx, StandardCharsets.UTF_8), bytesToDouble(lineBytes, separatorIdx + 1, eolIdx)); } private static double bytesToDouble(byte[] bytes, int startIdx, int endIdx) { double d = 0d; boolean negative = bytes[startIdx] == '-'; int numberStartIdx = negative ? 1 + startIdx : startIdx; boolean afterDot = false; int dots = 1; for (int i = numberStartIdx; i < endIdx; i++) { if (bytes[i] == '.') { afterDot = true; continue; } double n = bytes[i] - '0'; if (afterDot) { d = d + n / Math.pow(10, dots++); } else { d = d * Math.pow(10, i - numberStartIdx) + n; } } return negative ? -d : d; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_melgenek.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.*; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.TreeMap; import java.util.concurrent.*; /** * The implementation: * - reads a file with buffered IO * - uses VarHandles to get longs/ints from a byte array * - delimiter search is vectorized * - there is a custom hash function, that provides a low collision rate and short probe distances in hash tables * - has 2 custom open addressing hash tables: one for strings <=8 bytes in length, and one more for strings of any length */ public class CalculateAverage_melgenek { private static final VarHandle LONG_VIEW = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.nativeOrder()).withInvokeExactBehavior(); private static final VarHandle INT_VIEW = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder()).withInvokeExactBehavior(); private static final int CORES_COUNT = Runtime.getRuntime().availableProcessors(); private static final String FILE = "./measurements.txt"; /** * This is a prime number that gives pretty * good hash distributions * on the data in this challenge. */ private static final long RANDOM_PRIME = 0x7A646E4D; private static final int ZERO_CHAR_3_SUM = 100 * '0' + 10 * '0' + '0'; private static final int ZERO_CHAR_2_SUM = 10 * '0' + '0'; private static final byte NEWLINE = '\n'; private static final byte SEMICOLON = ';'; private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED; private static final int BYTE_SPECIES_BYTE_SIZE = BYTE_SPECIES.vectorByteSize(); private static final Vector NEWLINE_VECTOR = BYTE_SPECIES.broadcast(NEWLINE); private static final Vector SEMICOLON_VECTOR = BYTE_SPECIES.broadcast(SEMICOLON); private static final int MAX_LINE_LENGTH = 107; // 100 + len(";-11.1\n") = 100+7 public static void main(String[] args) throws Throwable { long totalSize = Files.size(Path.of(FILE)); long chunkSize = Math.max(MAX_LINE_LENGTH, totalSize / CORES_COUNT); var result = new TreeMap(); try (var executor = Executors.newFixedThreadPool(CORES_COUNT)) { var service = new ExecutorCompletionService(executor); int i = 0; for (; i * chunkSize < totalSize; i++) { long currentOffset = Math.max(0, i * chunkSize - 1); long maxOffset = Math.min((i + 1) * chunkSize, totalSize); service.submit(() -> processRange(currentOffset, maxOffset)); } for (; i > 0; i--) { service.take().get().addRows(result); } } System.out.println(printTree(result)); } private static String printTree(TreeMap result) { var sb = new StringBuilder(50 * result.size()); sb.append("{"); boolean first = true; for (var entry : result.entrySet()) { if (first) { first = false; } else { sb.append(", "); } sb.append(entry.getKey()); sb.append('='); entry.getValue().appendToStringBuffer(sb); } sb.append("}"); return sb.toString(); } private static CompositeTable processRange(long startOffset, long maxOffset) { final var table = new CompositeTable(); try (var file = new BufferedFile(startOffset, maxOffset)) { processChunk(file, table); } catch (Exception e) { throw new RuntimeException(e); } return table; } private static void processChunk(BufferedFile file, CompositeTable table) throws IOException { if (file.offset != 0) { file.refillBuffer(); int newlinePosition = findDelimiter(file, 0, NEWLINE_VECTOR, NEWLINE); file.bufferPosition = newlinePosition + 1; file.offset += file.bufferPosition; } while (file.offset < file.maxOffset) { file.refillBuffer(); int bytesProcessed = processOneRow(file, table); file.offset += bytesProcessed; } } private static int processOneRow(BufferedFile file, CompositeTable table) { int stringStart = file.bufferPosition; int stringEnd = findDelimiter(file, stringStart, SEMICOLON_VECTOR, SEMICOLON); file.bufferPosition = stringEnd + 1; short value = parseValue(file); table.add(file.buffer, stringStart, stringEnd, value); return file.bufferPosition - stringStart; } private static short parseValue(BufferedFile file) { byte firstDigit = file.buffer[file.bufferPosition]; int sign = 1; if (firstDigit == '-') { sign = -1; file.bufferPosition++; firstDigit = file.buffer[file.bufferPosition]; } byte secondDigit = file.buffer[file.bufferPosition + 1]; int result; if (secondDigit == '.') { result = firstDigit * 10 + file.buffer[file.bufferPosition + 2] - ZERO_CHAR_2_SUM; file.bufferPosition += 4; } else { result = firstDigit * 100 + secondDigit * 10 + file.buffer[file.bufferPosition + 3] - ZERO_CHAR_3_SUM; file.bufferPosition += 5; } return (short) (result * sign); } /** * Finds a delimiter in a byte array using vectorized comparisons. */ private static int findDelimiter(BufferedFile file, int startPosition, Vector repeatedDelimiter, byte delimiter) { int position = startPosition; int vectorLoopBound = startPosition + BYTE_SPECIES.loopBound(file.bufferLimit - startPosition); for (; position < vectorLoopBound; position += BYTE_SPECIES_BYTE_SIZE) { var vector = ByteVector.fromArray(BYTE_SPECIES, file.buffer, position); var comparisonResult = vector.compare(VectorOperators.EQ, repeatedDelimiter); if (comparisonResult.anyTrue()) { return position + comparisonResult.firstTrue(); } } while (file.buffer[position] != delimiter) { position++; } return position; } private static long keepLastBytes(long value, int numBytesToKeep) { // Number of bits to shift, so that the mask covers only `numBytesToKeep` least significant bits int bitShift = (Long.BYTES - numBytesToKeep) * Byte.SIZE; // Mask with the specified number of the least significant bits set to 1 long mask = -1L >>> bitShift; return value & mask; } /** * The function transforms a string with the length <=8 bytes to a java String. * The function assumes that the string is 0 terminated. */ private static String longToString(long value) { int strLength = Long.BYTES - Long.numberOfLeadingZeros(value) / Byte.SIZE; var bytes = new byte[strLength]; for (int i = 0; i < strLength; i++) { bytes[i] = (byte) (value >> (i * Byte.SIZE)); } return new String(bytes, StandardCharsets.UTF_8); } /** * Store measurements based on string lengths. * Stores strings of length <= 8 and other strings separately. * This table is a simplified implementation of strings hash table in ClickHouse. * The original parer that describes benefits of the approach is SAHA: A String Adaptive Hash Table for Analytical Databases. */ private static final class CompositeTable { private final LongTable longTable = new LongTable(); private final RegularTable regularTable = new RegularTable(); private void add(byte[] buffer, int stringStart, int stringEnd, short value) { int stringLength = stringEnd - stringStart; if (stringLength <= Long.BYTES) { long str = keepLastBytes((long) LONG_VIEW.get(buffer, stringStart), stringLength); this.longTable.add(str, value); } else { int hash = calculateHash(buffer, stringStart, stringEnd); this.regularTable.add(buffer, stringStart, stringLength, hash, value); } } public void addRows(TreeMap result) { this.longTable.addRows(result); this.regularTable.addRows(result); } } /** * The hash calculation is inspired by * QuestDB FastMap */ private static int calculateHash(byte[] buffer, int startPosition, int endPosition) { long hash = 0; int position = startPosition; for (; position + Long.BYTES < endPosition; position += Long.BYTES) { long value = (long) LONG_VIEW.get(buffer, position); hash = hash * RANDOM_PRIME + value; } if (position + Integer.BYTES < endPosition) { int value = (int) INT_VIEW.get(buffer, position); hash = hash * RANDOM_PRIME + value; position += Integer.BYTES; } for (; position < endPosition; position++) { hash = hash * RANDOM_PRIME + buffer[position]; } hash = hash * RANDOM_PRIME; return (int) hash ^ (int) (hash >>> 32); } private static int calculateLongHash(long str) { long hash = str * RANDOM_PRIME; return (int) hash ^ (int) (hash >>> 32); } /** * This tables stores strings of length <= 8 bytes. * Does not store hashes. */ private static final class LongTable { private static final int TABLE_CAPACITY = 32768; private static final int TABLE_CAPACITY_MASK = TABLE_CAPACITY - 1; /** * The buckets use 3 longs to store strings and measurements: * long 1) station name * long 2) sum of measurements * long 3) count (int) | min (short) | max (short) <-- packed into one long */ private final long[] buckets = new long[TABLE_CAPACITY * 3]; public void add(long str, short value) { int hash = calculateLongHash(str); int bucketIdx = hash & TABLE_CAPACITY_MASK; long bucketStr = buckets[bucketIdx * 3]; if (bucketStr == str) { updateBucket(bucketIdx, value); } else if (bucketStr == 0L) { createBucket(bucketIdx, str, value); } else { addWithProbing(str, value, (bucketIdx + 1) & TABLE_CAPACITY_MASK); } } private void addWithProbing(long str, short value, int bucketIdx) { int distance = 1; while (true) { long bucketStr = buckets[bucketIdx * 3]; if (bucketStr == str) { updateBucket(bucketIdx, value); break; } else if (bucketStr == 0L) { createBucket(bucketIdx, str, value); break; } else { distance++; // A new bucket index is calculated based on quadratic probing https://thenumb.at/Hashtables/#quadratic-probing // Quadratic probing decreases the number of collisions and max probing distance. // Linear: // - capacity 16k, 28.6M collisions, 14-17 max distance // - capacity 32k, 9.5M collisions, 5-7 max distance // Quadratic: // - capacity 16k 25M collisions, 8-10 max distance // - capacity 32k, 9.3M collisions, 4-7 max distance bucketIdx = (bucketIdx + distance) & TABLE_CAPACITY_MASK; } } } public void addRows(TreeMap result) { for (int bucketIdx = 0; bucketIdx < TABLE_CAPACITY; bucketIdx++) { int bucketOffset = bucketIdx * 3; long str = buckets[bucketOffset]; if (str != 0L) { long sum = buckets[bucketOffset + 1]; long countMinMax = buckets[bucketOffset + 2]; int count = (int) ((countMinMax >> 32)); short min = (short) ((countMinMax >> 16) & 0xFFFF); short max = (short) (countMinMax & 0xFFFF); result.compute(longToString(str), (k, resultRow) -> { if (resultRow == null) { return new ResultRow(sum, count, min, max); } else { resultRow.add(sum, count, min, max); return resultRow; } }); } } } private void createBucket(int bucketIdx, long str, short value) { int offset = bucketIdx * 3; buckets[offset] = str; buckets[offset + 1] = value; buckets[offset + 2] = (1L << 32) | ((long) (value & 0xFFFF) << 16) | (long) (value & 0xFFFF); } private void updateBucket(int bucketIdx, short value) { int offset = bucketIdx * 3; long sum = buckets[offset + 1]; buckets[offset + 1] = sum + value; long countMinMax = buckets[offset + 2]; int count = (int) ((countMinMax >> 32)); short min = (short) ((countMinMax >> 16) & 0xFFFF); short max = (short) (countMinMax & 0xFFFF); if (value < min) { min = value; } if (value > max) { max = value; } buckets[offset + 2] = ((long) (count + 1) << 32) | ((long) (min & 0xFFFF) << 16) | (long) (max & 0xFFFF); } } /** * An open addressing hash table that stores strings as byte arrays. * Stores hashes. */ private static final class RegularTable { private static final int TABLE_CAPACITY = 16384; private static final int TABLE_CAPACITY_MASK = TABLE_CAPACITY - 1; private final Bucket[] buckets = new Bucket[TABLE_CAPACITY]; public void add(byte[] data, int start, int stringLength, int hash, short value) { int bucketIdx = hash & TABLE_CAPACITY_MASK; var bucket = buckets[bucketIdx]; if (bucket == null) { buckets[bucketIdx] = new Bucket(data, start, stringLength, hash, value); } else if (hash == bucket.hash && bucket.isEqual(data, start, stringLength)) { bucket.update(value); } else { addWithProbing(data, start, stringLength, hash, value, (bucketIdx + 1) & TABLE_CAPACITY_MASK); } } private void addWithProbing(byte[] data, int start, int stringLength, int hash, short value, int bucketIdx) { int distance = 1; while (true) { var bucket = buckets[bucketIdx]; if (bucket == null) { buckets[bucketIdx] = new Bucket(data, start, stringLength, hash, value); break; } else if (hash == bucket.hash && bucket.isEqual(data, start, stringLength)) { bucket.update(value); break; } else { distance++; bucketIdx = (bucketIdx + distance) & TABLE_CAPACITY_MASK; } } } public void addRows(TreeMap result) { for (var bucket : buckets) { if (bucket != null) { result.compute(new String(bucket.str, StandardCharsets.UTF_8), (k, resultRow) -> { if (resultRow == null) { return new ResultRow(bucket.sum, bucket.count, bucket.min, bucket.max); } else { resultRow.add(bucket.sum, bucket.count, bucket.min, bucket.max); return resultRow; } }); } } } private static final class Bucket { int hash; byte[] str; long sum; int count; short max = Short.MIN_VALUE; short min = Short.MAX_VALUE; Bucket(byte[] data, int start, int stringLength, int hash, short value) { this.str = new byte[stringLength]; System.arraycopy(data, start, this.str, 0, stringLength); this.hash = hash; update(value); } public void update(short value) { this.sum += value; this.count++; if (max < value) max = value; if (min > value) min = value; } public boolean isEqual(byte[] data, int start, int length) { if (str.length != length) return false; int i = 0; int vectorLoopBound = BYTE_SPECIES.loopBound(str.length); for (; i < vectorLoopBound; i += BYTE_SPECIES_BYTE_SIZE) { var vector1 = ByteVector.fromArray(BYTE_SPECIES, str, i); var vector2 = ByteVector.fromArray(BYTE_SPECIES, data, start + i); var comparisonResult = vector1.compare(VectorOperators.NE, vector2); if (comparisonResult.anyTrue()) return false; } for (; i + Long.BYTES < str.length; i += Long.BYTES) { long value1 = (long) LONG_VIEW.get(str, i); long value2 = (long) LONG_VIEW.get(data, start + i); if (value1 != value2) return false; } if (i + Integer.BYTES < str.length) { int value1 = (int) INT_VIEW.get(str, i); int value2 = (int) INT_VIEW.get(data, start + i); if (value1 != value2) return false; i += Integer.BYTES; } for (; i < str.length; i++) { if (data[start + i] != str[i]) return false; } return true; } } } private static class ResultRow { long sum; int count; short min; short max; public ResultRow(long sum, int count, short min, short max) { this.sum = sum; this.count = count; this.min = min; this.max = max; } public void add(long anotherSum, int anotherCount, short anotherMin, short anotherMax) { sum += anotherSum; count += anotherCount; if (max < anotherMax) max = anotherMax; if (min > anotherMin) min = anotherMin; } public void appendToStringBuffer(StringBuilder sb) { sb.append(Math.round((double) min) / 10.0); sb.append('/'); sb.append(Math.round((double) sum / count) / 10.0); sb.append('/'); sb.append(Math.round((double) max) / 10.0); } } /** * A utility class that uses the RandomAccessFile to read at offset. * Keeps the in-memory buffer, as well as current offsets in the buffer and the file. */ private static final class BufferedFile implements AutoCloseable { private static final int BUFFER_SIZE = 512 * 1024; private final byte[] buffer = new byte[BUFFER_SIZE]; private int bufferLimit = 0; private int bufferPosition = 0; private final long maxOffset; private final RandomAccessFile file; private long offset; private BufferedFile(long startOffset, long maxOffset) throws IOException { this.offset = startOffset; this.maxOffset = maxOffset; this.file = new RandomAccessFile(FILE, "r"); } private void refillBuffer() throws IOException { int remainingBytes = bufferLimit - bufferPosition; if (remainingBytes < MAX_LINE_LENGTH) { bufferPosition = 0; file.seek(offset); int bytesRead = file.read(buffer, 0, BUFFER_SIZE); bufferLimit = Math.max(bytesRead, 0); } } @Override public void close() throws Exception { file.close(); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_merykitty.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Map; import java.util.TreeMap; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; public class CalculateAverage_merykitty { private static final String FILE = "./measurements.txt"; private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED.length() >= 32 ? ByteVector.SPECIES_256 : ByteVector.SPECIES_128; private static final ValueLayout.OfLong JAVA_LONG_LT = ValueLayout.JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN); private static final long KEY_MAX_SIZE = 100; private static class Aggregator { private int keySize; private long min = Integer.MAX_VALUE; private long max = Integer.MIN_VALUE; private long sum; private long count; public String toString() { return round(min / 10.) + "/" + round(sum / (double) (10 * count)) + "/" + round(max / 10.); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } // An open-address map that is specialized for this task private static class PoorManMap { // 100-byte key + 4-byte hash + 4-byte size + // 2-byte min + 2-byte max + 8-byte sum + 8-byte count private static final int KEY_SIZE = 128; // There is an assumption that map size <= 10000; private static final int CAPACITY = 1 << 17; private static final int BUCKET_MASK = CAPACITY - 1; byte[] keyData; Aggregator[] nodes; PoorManMap() { this.keyData = new byte[CAPACITY * KEY_SIZE]; this.nodes = new Aggregator[CAPACITY]; } void observe(Aggregator node, long value) { if (node.min > value) { node.min = value; } if (node.max < value) { node.max = value; } node.sum += value; node.count++; } Aggregator indexSimple(MemorySegment data, long offset, int size) { int x; int y; if (size >= Integer.BYTES) { x = data.get(ValueLayout.JAVA_INT_UNALIGNED, offset); y = data.get(ValueLayout.JAVA_INT_UNALIGNED, offset + size - Integer.BYTES); } else { x = data.get(ValueLayout.JAVA_BYTE, offset); y = data.get(ValueLayout.JAVA_BYTE, offset + size - Byte.BYTES); } int hash = hash(x, y); int bucket = hash & BUCKET_MASK; for (;; bucket = (bucket + 1) & BUCKET_MASK) { var node = this.nodes[bucket]; if (node == null) { return insertInto(bucket, data, offset, size); } else if (keyEqualScalar(bucket, data, offset, size)) { return node; } } } Aggregator insertInto(int bucket, MemorySegment data, long offset, int size) { var node = new Aggregator(); node.keySize = size; this.nodes[bucket] = node; MemorySegment.copy(data, offset, MemorySegment.ofArray(this.keyData), (long) bucket * KEY_SIZE, size + 1); return node; } void mergeInto(Map target) { for (int i = 0; i < CAPACITY; i++) { var node = this.nodes[i]; if (node == null) { continue; } String key = new String(this.keyData, i * KEY_SIZE, node.keySize, StandardCharsets.UTF_8); target.compute(key, (k, v) -> { if (v == null) { v = new Aggregator(); } v.min = Math.min(v.min, node.min); v.max = Math.max(v.max, node.max); v.sum += node.sum; v.count += node.count; return v; }); } } static int hash(int x, int y) { int seed = 0x9E3779B9; int rotate = 5; return (Integer.rotateLeft(x * seed, rotate) ^ y) * seed; // FxHash } private boolean keyEqualScalar(int bucket, MemorySegment data, long offset, int size) { if (this.nodes[bucket].keySize != size) { return false; } // Be simple for (int i = 0; i < size; i++) { int c1 = this.keyData[bucket * KEY_SIZE + i]; int c2 = data.get(ValueLayout.JAVA_BYTE, offset + i); if (c1 != c2) { return false; } } return true; } } // Parse a number that may/may not contain a minus sign followed by a decimal with // 1 - 2 digits to the left and 1 digits to the right of the separator to a // fix-precision format. It returns the offset of the next line (presumably followed // the final digit and a '\n') private static long parseDataPoint(PoorManMap aggrMap, Aggregator node, MemorySegment data, long offset) { long word = data.get(JAVA_LONG_LT, offset); // The 4th binary digit of the ascii of a digit is 1 while // that of the '.' is 0. This finds the decimal separator // The value can be 12, 20, 28 int decimalSepPos = Long.numberOfTrailingZeros(~word & 0x10101000); int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise long signed = (~word << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii code // to actual digit value in each byte long digits = ((word & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + // 0x00UU00TTHH000000 * 10 + // 0xUU00TTHH00000000 * 100 // Now TT * 100 has 2 trailing zeroes and HH * 100 + TT * 10 + UU < 0x400 // This results in our value lies in the bit 32 to 41 of this product // That was close :) long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; long value = (absValue ^ signed) - signed; aggrMap.observe(node, value); return offset + (decimalSepPos >>> 3) + 3; } // Tail processing version of the above, do not over-fetch and be simple private static long parseDataPointSimple(PoorManMap aggrMap, Aggregator node, MemorySegment data, long offset) { int value = 0; boolean negative = false; if (data.get(ValueLayout.JAVA_BYTE, offset) == '-') { negative = true; offset++; } for (;; offset++) { int c = data.get(ValueLayout.JAVA_BYTE, offset); if (c == '.') { c = data.get(ValueLayout.JAVA_BYTE, offset + 1); value = value * 10 + (c - '0'); offset += 3; break; } value = value * 10 + (c - '0'); } value = negative ? -value : value; aggrMap.observe(node, value); return offset; } // An iteration of the main parse loop, parse a line starting from offset. // This requires offset to be the start of the line and there is spare space so // that we have relative freedom in processing // It returns the offset of the next line that it needs processing private static long iterate(PoorManMap aggrMap, MemorySegment data, long offset) { var line = ByteVector.fromMemorySegment(BYTE_SPECIES, data, offset, ByteOrder.nativeOrder()); // Find the delimiter ';' long semicolons = line.compare(VectorOperators.EQ, ';').toLong(); // If we cannot find the delimiter in the vector, that means the key is // longer than the vector, fall back to scalar processing if (semicolons == 0) { int keySize = BYTE_SPECIES.length(); while (data.get(ValueLayout.JAVA_BYTE, offset + keySize) != ';') { keySize++; } var node = aggrMap.indexSimple(data, offset, keySize); return parseDataPoint(aggrMap, node, data, offset + 1 + keySize); } // We inline the searching of the value in the hash map int keySize = Long.numberOfTrailingZeros(semicolons); int x; int y; if (keySize >= Integer.BYTES) { x = data.get(ValueLayout.JAVA_INT_UNALIGNED, offset); y = data.get(ValueLayout.JAVA_INT_UNALIGNED, offset + keySize - Integer.BYTES); } else { x = data.get(ValueLayout.JAVA_BYTE, offset); y = data.get(ValueLayout.JAVA_BYTE, offset + keySize - Byte.BYTES); } int hash = PoorManMap.hash(x, y); int bucket = hash & PoorManMap.BUCKET_MASK; Aggregator node; for (;; bucket = (bucket + 1) & PoorManMap.BUCKET_MASK) { node = aggrMap.nodes[bucket]; if (node == null) { node = aggrMap.insertInto(bucket, data, offset, keySize); break; } if (node.keySize != keySize) { continue; } var nodeKey = ByteVector.fromArray(BYTE_SPECIES, aggrMap.keyData, bucket * PoorManMap.KEY_SIZE); long eqMask = line.compare(VectorOperators.EQ, nodeKey).toLong(); long validMask = semicolons ^ (semicolons - 1); if ((eqMask & validMask) == validMask) { break; } } return parseDataPoint(aggrMap, node, data, offset + keySize + 1); } private static long findOffset(MemorySegment data, long offset, long limit) { if (offset == 0) { return offset; } offset--; while (offset < limit) { if (data.get(ValueLayout.JAVA_BYTE, offset++) == '\n') { break; } } return offset; } // Process all lines that start in [offset, limit) private static PoorManMap processFile(MemorySegment data, long offset, long limit) { var aggrMap = new PoorManMap(); if (offset == limit) { return aggrMap; } int batches = 2; long batchSize = Math.ceilDiv(limit - offset, batches); long offset0 = offset; long offset1 = offset + batchSize; long limit0 = Math.min(offset1, limit); long limit1 = limit; // Find the start of a new line offset0 = findOffset(data, offset0, limit0); offset1 = findOffset(data, offset1, limit1); long mainLoopMinWidth = Math.max(BYTE_SPECIES.vectorByteSize(), KEY_MAX_SIZE + 1 + Long.BYTES); if (limit1 - offset1 < mainLoopMinWidth) { offset = findOffset(data, offset, limit); while (offset < limit - mainLoopMinWidth) { offset = iterate(aggrMap, data, offset); } } else { while (true) { boolean finish = false; if (offset0 < limit0) { offset0 = iterate(aggrMap, data, offset0); } else { finish = true; } if (offset1 < limit1 - mainLoopMinWidth) { offset1 = iterate(aggrMap, data, offset1); } else { if (finish) { break; } } } offset = offset1; } // Now we are at the tail, just be simple while (offset < limit) { int keySize = 0; while (data.get(ValueLayout.JAVA_BYTE, offset + keySize) != ';') { keySize++; } var node = aggrMap.indexSimple(data, offset, keySize); offset = parseDataPointSimple(aggrMap, node, data, offset + 1 + keySize); } return aggrMap; } public static void main(String[] args) throws InterruptedException, IOException { int processorCnt = Runtime.getRuntime().availableProcessors(); var res = new TreeMap(); try (var file = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); var arena = Arena.ofShared()) { var data = file.map(MapMode.READ_ONLY, 0, file.size(), arena); long chunkSize = Math.ceilDiv(data.byteSize(), processorCnt); var threadList = new Thread[processorCnt]; var resultList = new PoorManMap[processorCnt]; for (int i = 0; i < processorCnt; i++) { int index = i; long offset = i * chunkSize; long limit = Math.min((i + 1) * chunkSize, data.byteSize()); var thread = new Thread(() -> resultList[index] = processFile(data, offset, limit)); threadList[index] = thread; thread.start(); } for (var thread : threadList) { thread.join(); } // Collect the results for (var aggrMap : resultList) { aggrMap.mergeInto(res); } } System.out.println(res); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_merykittyunsafe.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Map; import java.util.TreeMap; public class CalculateAverage_merykittyunsafe { private static final String FILE = "./measurements.txt"; private static final Unsafe UNSAFE; static { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED.length() >= 32 ? ByteVector.SPECIES_256 : ByteVector.SPECIES_128; private static final long KEY_MAX_SIZE = 100; private static class Aggregator { private long min = Integer.MAX_VALUE; private long max = Integer.MIN_VALUE; private long sum; private long count; public String toString() { return round(min / 10.) + "/" + round(sum / (double) (10 * count)) + "/" + round(max / 10.); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } // An open-address map that is specialized for this task private static class PoorManMap { // 100-byte key + 4-byte hash + 4-byte size + // 2-byte min + 2-byte max + 8-byte sum + 8-byte count private static final int ENTRY_SIZE = 128; private static final int SIZE_OFFSET = 0; private static final int MIN_OFFSET = 4; private static final int MAX_OFFSET = 6; private static final int SUM_OFFSET = 8; private static final int COUNT_OFFSET = 16; private static final int KEY_OFFSET = 24; // There is an assumption that map size <= 10000; private static final int CAPACITY = 1 << 17; private static final int ENTRY_MASK = ENTRY_SIZE * CAPACITY - 1; final byte[] data; PoorManMap() { this.data = new byte[CAPACITY * ENTRY_SIZE]; } void observe(long entryOffset, long value) { long baseOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + entryOffset; if (UNSAFE.getShort(this.data, baseOffset + MIN_OFFSET) > value) { UNSAFE.putShort(this.data, baseOffset + MIN_OFFSET, (short) value); } if (UNSAFE.getShort(this.data, baseOffset + MAX_OFFSET) < value) { UNSAFE.putShort(this.data, baseOffset + MAX_OFFSET, (short) value); } UNSAFE.putLong(this.data, baseOffset + SUM_OFFSET, value + UNSAFE.getLong(this.data, baseOffset + SUM_OFFSET)); UNSAFE.putLong(this.data, baseOffset + COUNT_OFFSET, 1 + UNSAFE.getLong(this.data, baseOffset + COUNT_OFFSET)); } long indexSimple(long address, int size) { int x; int y; if (size >= Integer.BYTES) { x = UNSAFE.getInt(address); y = UNSAFE.getInt(address + size - Integer.BYTES); } else { x = UNSAFE.getByte(address); y = UNSAFE.getByte(address + size - Byte.BYTES); } int hash = hash(x, y); long entryOffset = (hash * ENTRY_SIZE) & ENTRY_MASK; for (;; entryOffset = (entryOffset + ENTRY_SIZE) & ENTRY_MASK) { int nodeSize = UNSAFE.getInt(this.data, Unsafe.ARRAY_BYTE_BASE_OFFSET + entryOffset + SIZE_OFFSET); if (nodeSize == 0) { insertInto(entryOffset, address, size); return entryOffset; } else if (keyEqualScalar(entryOffset, address, size)) { return entryOffset; } } } void insertInto(long entryOffset, long address, int size) { long baseOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + entryOffset; UNSAFE.putInt(this.data, baseOffset + SIZE_OFFSET, size); UNSAFE.putShort(this.data, baseOffset + MIN_OFFSET, Short.MAX_VALUE); UNSAFE.putShort(this.data, baseOffset + MAX_OFFSET, Short.MIN_VALUE); try (var arena = Arena.ofConfined()) { var segment = MemorySegment.ofAddress(address) .reinterpret(size + 1, arena, null); MemorySegment.copy(segment, 0, MemorySegment.ofArray(this.data), entryOffset + KEY_OFFSET, size + 1); } } void mergeInto(Map target) { for (int entryOffset = 0; entryOffset < data.length; entryOffset += ENTRY_SIZE) { long baseOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + entryOffset; int size = UNSAFE.getInt(this.data, baseOffset + SIZE_OFFSET); if (size == 0) { continue; } String key = new String(this.data, entryOffset + KEY_OFFSET, size, StandardCharsets.UTF_8); target.compute(key, (k, v) -> { if (v == null) { v = new Aggregator(); } v.min = Math.min(v.min, UNSAFE.getShort(this.data, baseOffset + MIN_OFFSET)); v.max = Math.max(v.max, UNSAFE.getShort(this.data, baseOffset + MAX_OFFSET)); v.sum += UNSAFE.getLong(this.data, baseOffset + SUM_OFFSET); v.count += UNSAFE.getLong(this.data, baseOffset + COUNT_OFFSET); return v; }); } } static int hash(int x, int y) { int seed = 0x9E3779B9; int rotate = 5; return (Integer.rotateLeft(x * seed, rotate) ^ y) * seed; // FxHash } private boolean keyEqualScalar(long entryOffset, long address, int size) { long baseOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + entryOffset; if (UNSAFE.getInt(this.data, baseOffset + SIZE_OFFSET) != size) { return false; } // Be simple for (long i = 0; i < size; i++) { int c1 = UNSAFE.getByte(this.data, baseOffset + KEY_OFFSET + i); int c2 = UNSAFE.getByte(address + i); if (c1 != c2) { return false; } } return true; } } // Parse a number that may/may not contain a minus sign followed by a decimal with // 1 - 2 digits to the left and 1 digits to the right of the separator to a // fix-precision format. It returns the offset of the next line (presumably followed // the final digit and a '\n') private static long parseDataPoint(PoorManMap aggrMap, long entryOffset, long address) { long word = UNSAFE.getLong(address); if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { word = Long.reverseBytes(word); } // The 4th binary digit of the ascii of a digit is 1 while // that of the '.' is 0. This finds the decimal separator // The value can be 12, 20, 28 int decimalSepPos = Long.numberOfTrailingZeros(~word & 0x10101000); int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise long signed = (~word << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii code // to actual digit value in each byte long digits = ((word & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + // 0x00UU00TTHH000000 * 10 + // 0xUU00TTHH00000000 * 100 // Now TT * 100 has 2 trailing zeroes and HH * 100 + TT * 10 + UU < 0x400 // This results in our value lies in the bit 32 to 41 of this product // That was close :) long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; long value = (absValue ^ signed) - signed; aggrMap.observe(entryOffset, value); return address + (decimalSepPos >>> 3) + 3; } // Tail processing version of the above, do not over-fetch and be simple private static long parseDataPointSimple(PoorManMap aggrMap, long entryOffset, long address) { int value = 0; boolean negative = false; if (UNSAFE.getByte(address) == '-') { negative = true; address++; } for (;; address++) { int c = UNSAFE.getByte(address); if (c == '.') { c = UNSAFE.getByte(address + 1); value = value * 10 + (c - '0'); address += 3; break; } value = value * 10 + (c - '0'); } value = negative ? -value : value; aggrMap.observe(entryOffset, value); return address; } // An iteration of the main parse loop, parse a line starting from offset. // This requires offset to be the start of the line and there is spare space so // that we have relative freedom in processing // It returns the offset of the next line that it needs processing private static long iterate(PoorManMap aggrMap, long address) { ByteVector line; try (var arena = Arena.ofConfined()) { var segment = MemorySegment.ofAddress(address) .reinterpret(BYTE_SPECIES.vectorByteSize(), arena, null); line = ByteVector.fromMemorySegment(BYTE_SPECIES, segment, 0, ByteOrder.nativeOrder()); } // Find the delimiter ';' long semicolons = line.compare(VectorOperators.EQ, ';').toLong(); // If we cannot find the delimiter in the vector, that means the key is // longer than the vector, fall back to scalar processing if (semicolons == 0) { int keySize = BYTE_SPECIES.length(); while (UNSAFE.getByte(address + keySize) != ';') { keySize++; } var node = aggrMap.indexSimple(address, keySize); return parseDataPoint(aggrMap, node, address + 1 + keySize); } // We inline the searching of the value in the hash map int keySize = Long.numberOfTrailingZeros(semicolons); int x; int y; if (keySize >= Integer.BYTES) { x = UNSAFE.getInt(address); y = UNSAFE.getInt(address + keySize - Integer.BYTES); } else { x = UNSAFE.getByte(address); y = UNSAFE.getByte(address + keySize - Byte.BYTES); } int hash = PoorManMap.hash(x, y); long entryOffset = (hash * PoorManMap.ENTRY_SIZE) & PoorManMap.ENTRY_MASK; for (;; entryOffset = (entryOffset + PoorManMap.ENTRY_SIZE) & PoorManMap.ENTRY_MASK) { var nodeSize = UNSAFE.getInt(aggrMap.data, Unsafe.ARRAY_BYTE_BASE_OFFSET + entryOffset + PoorManMap.SIZE_OFFSET); if (nodeSize == 0) { aggrMap.insertInto(entryOffset, address, keySize); break; } if (nodeSize != keySize) { continue; } var nodeKey = ByteVector.fromArray(BYTE_SPECIES, aggrMap.data, (int) (entryOffset + PoorManMap.KEY_OFFSET)); long eqMask = line.compare(VectorOperators.EQ, nodeKey).toLong(); long validMask = semicolons ^ (semicolons - 1); if ((eqMask & validMask) == validMask) { break; } } return parseDataPoint(aggrMap, entryOffset, address + keySize + 1); } private static long findOffset(long base, long offset, long limit) { if (offset == 0) { return offset; } offset--; while (offset < limit) { if (UNSAFE.getByte(base + (offset++)) == '\n') { break; } } return offset; } // Process all lines that start in [offset, limit) private static PoorManMap processFile(MemorySegment data, long offset, long limit) { var aggrMap = new PoorManMap(); if (offset == limit) { return aggrMap; } long base = data.address(); int batches = 2; long batchSize = Math.ceilDiv(limit - offset, batches); long offset0 = offset; long offset1 = offset + batchSize; long limit0 = Math.min(offset1, limit); long limit1 = limit; // Find the start of a new line offset0 = findOffset(base, offset0, limit0); offset1 = findOffset(base, offset1, limit1); long begin; long end = base + limit; long mainLoopMinWidth = Math.max(BYTE_SPECIES.vectorByteSize(), KEY_MAX_SIZE + 1 + Long.BYTES); if (limit1 - offset1 < mainLoopMinWidth) { begin = base + findOffset(base, offset, limit); while (begin < end - mainLoopMinWidth) { begin = iterate(aggrMap, begin); } } else { long begin0 = base + offset0; long begin1 = base + offset1; long end0 = base + limit0; long end1 = base + limit1; while (true) { boolean finish = false; if (begin0 < end0) { begin0 = iterate(aggrMap, begin0); } else { finish = true; } if (begin1 < end1 - mainLoopMinWidth) { begin1 = iterate(aggrMap, begin1); } else { if (finish) { break; } } } begin = begin1; } // Now we are at the tail, just be simple while (begin < end) { int keySize = 0; while (UNSAFE.getByte(begin + keySize) != ';') { keySize++; } long entryOffset = aggrMap.indexSimple(begin, keySize); begin = parseDataPointSimple(aggrMap, entryOffset, begin + 1 + keySize); } return aggrMap; } public static void main(String[] args) throws InterruptedException, IOException { int processorCnt = Runtime.getRuntime().availableProcessors(); var res = new TreeMap(); try (var file = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); var arena = Arena.ofShared()) { var data = file.map(MapMode.READ_ONLY, 0, file.size(), arena); long chunkSize = Math.ceilDiv(data.byteSize(), processorCnt); var threadList = new Thread[processorCnt]; var resultList = new PoorManMap[processorCnt]; for (int i = 0; i < processorCnt; i++) { int index = i; long offset = i * chunkSize; long limit = Math.min((i + 1) * chunkSize, data.byteSize()); var thread = new Thread(() -> resultList[index] = processFile(data, offset, limit)); threadList[index] = thread; thread.start(); } for (var thread : threadList) { thread.join(); } // Collect the results for (var aggrMap : resultList) { aggrMap.mergeInto(res); } } System.out.println(res); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_moysesb.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.BufferUnderflowException; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_moysesb { private static final String FILE = "./measurements.txt"; static class ByteArray { final byte[] value; final int hashCode; private ByteArray(byte[] val, int hashCode) { this.value = val; this.hashCode = hashCode; } @Override public boolean equals(Object o) { if (o instanceof ByteArray other) { return this == other || hashCode == other.hashCode && Arrays.equals(value, other.value); } return false; } @Override public int hashCode() { return hashCode; } } static final Map allResults = new HashMap<>(512); public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { int ncpus = Runtime.getRuntime().availableProcessors(); ExecutorCompletionService> exec = new ExecutorCompletionService<>(Executors.newFixedThreadPool(ncpus)); var file = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); File f = new File(FILE); long fileSize = f.length(); long split = 0; long chunkSize = Math.min(1 << 28, fileSize < 1 << 28 ? fileSize : (fileSize / ncpus)); List>> tasks = new ArrayList<>(); while (split < fileSize) { final long[] offset = {split}; var task = exec.submit(() -> { try { final Map results = new HashMap<>(512); file: for (; ; ) { long chunk = Math.min(fileSize - offset[0], chunkSize); if (chunk == 0) { return results; } MemorySegment mm = file.map(FileChannel.MapMode.READ_ONLY, offset[0], chunk, Arena.ofConfined()); int i = 0; if (offset[0] > 0) { while (mm.get(ValueLayout.JAVA_BYTE, i) != '\n') { i++; } i++; } while (i < chunk) { try { byte[] city = new byte[32]; byte[] valb = new byte[16]; byte[] target = city; double val = 0d; int dotOffset = 0; int l = 0; int nameHash = 0; for (; ; ) { byte b = mm.get(ValueLayout.OfByte.JAVA_BYTE, i++); if (b == ';') { target = valb; l = 0; } else if (b == '\n') { int integral = 0; int mult = 1; int frac = 0; for (int ii = 0; ii < dotOffset; ii++) { if (valb[ii] == '-') { mult = -1; } else { integral = integral * 10 + (valb[ii] - '0'); } } for (int ii = dotOffset+1; ii < l; ii++) { frac = frac * 10 + (valb[ii] - '0'); } val = integral; if (frac > 0) val += (frac/10d); val *= mult; var ba = new ByteArray(city, nameHash); var r = results.computeIfAbsent(ba, _s -> new double[]{1000, -1000, 0, 0}); r[0] = Math.min(r[0], val); r[1] = Math.max(r[1], val); r[2]++; r[3] += val; break; } else { if (target == city) { nameHash = nameHash * 31 + b; } else if (b == '.') { dotOffset = l; } target[l++] = b; } } } catch (BufferUnderflowException | IndexOutOfBoundsException e) { //happens on the last segment after EOF break file; } } offset[0] += i; } return results; } catch (IOException e) { throw new RuntimeException(e); } }); tasks.add(task); split += chunkSize; } int taken = 0; while (taken < tasks.size()) { Future> fut = exec.take(); var map = fut.get(); taken++; if (map == null) { continue; } for (Map.Entry e : map.entrySet()) { var ba = e.getKey(); var val = e.getValue(); allResults.merge(ba, val, (old, _new) -> { old[0] = Math.min(old[0], _new[0]); old[1] = Math.max(old[1], _new[1]); old[2] += _new[2]; old[3] += _new[3]; return old; }); } } SortedMap sorted = new TreeMap<>(); for (Map.Entry e : allResults.entrySet()) { byte[] utf8 = e.getKey().value; int strlen = 0; while (utf8[strlen] != '\0') strlen++; String city = new String(utf8, 0, strlen, StandardCharsets.UTF_8); double[] r = e.getValue(); String fmt = FormatProcessor.FMT."%.1f\{round(r[0])}/%.1f\{round(r[3]/r[2])}/%.1f\{round(r[1])}"; sorted.put(city, fmt); } System.out.println(sorted); System.exit(0); } private static double round(double d) { return Math.round(d * 10.0) / 10.0; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_mtopolnik.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.File; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import static java.lang.ProcessBuilder.Redirect.PIPE; import static java.util.Arrays.asList; public class CalculateAverage_mtopolnik { private static final Unsafe UNSAFE = unsafe(); private static final int MAX_NAME_LEN = 100; private static final int STATS_TABLE_SIZE = 1 << 16; private static final int TABLE_INDEX_MASK = STATS_TABLE_SIZE - 1; private static final String MEASUREMENTS_TXT = "measurements.txt"; private static Unsafe unsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws Exception { if (args.length >= 1 && args[0].equals("--worker")) { calculate(); System.out.close(); return; } var curProcInfo = ProcessHandle.current().info(); var cmdLine = new ArrayList(); cmdLine.add(curProcInfo.command().get()); cmdLine.addAll(asList(curProcInfo.arguments().get())); cmdLine.add("--worker"); var process = new ProcessBuilder() .command(cmdLine) .inheritIO().redirectOutput(PIPE) .start() .getInputStream().transferTo(System.out); } static void calculate() throws Exception { final File file = new File(MEASUREMENTS_TXT); final long length = file.length(); final int chunkCount = Runtime.getRuntime().availableProcessors(); final var results = new StationStats[chunkCount][]; final var chunkStartOffsets = new long[chunkCount]; try (var raf = new RandomAccessFile(file, "r")) { final var inputBase = raf.getChannel().map(MapMode.READ_ONLY, 0, length, Arena.global()).address(); for (int i = 1; i < chunkStartOffsets.length; i++) { var start = length * i / chunkStartOffsets.length; raf.seek(start); while (raf.read() != (byte) '\n') { } start = raf.getFilePointer(); chunkStartOffsets[i] = start; } var threads = new Thread[chunkCount]; for (int i = 0; i < chunkCount; i++) { final long chunkStart = chunkStartOffsets[i]; final long chunkLimit = (i + 1 < chunkCount) ? chunkStartOffsets[i + 1] : length; threads[i] = new Thread(new ChunkProcessor(inputBase + chunkStart, inputBase + chunkLimit, results, i)); } for (var thread : threads) { thread.start(); } for (var thread : threads) { thread.join(); } } mergeSortAndPrint(results); } private static class ChunkProcessor implements Runnable { private static final int CACHELINE_SIZE = 64; private final long inputBase; private final long inputSize; private final StationStats[][] results; private final int myIndex; private StatsAccessor stats; ChunkProcessor(long chunkStart, long chunkLimit, StationStats[][] results, int myIndex) { this.inputBase = chunkStart; this.inputSize = chunkLimit - chunkStart; this.results = results; this.myIndex = myIndex; } @Override public void run() { try (Arena confinedArena = Arena.ofConfined()) { long totalAllocated = 0; String threadName = Thread.currentThread().getName(); long statsByteSize = STATS_TABLE_SIZE * StatsAccessor.SIZEOF; var diagnosticString = String.format("Thread %s needs %,d bytes", threadName, statsByteSize); try { stats = new StatsAccessor(confinedArena.allocate(statsByteSize, CACHELINE_SIZE)); } catch (OutOfMemoryError e) { System.err.print(diagnosticString); throw e; } processChunk(); exportResults(); } } private void processChunk() { final long inputSize = this.inputSize; final long inputBase = this.inputBase; long cursor = 0; long lastNameWord; while (cursor < inputSize) { long nameStartAddress = inputBase + cursor; long nameWord0 = UNSAFE.getLong(nameStartAddress); long nameWord1 = 0; long matchBits = semicolonMatchBits(nameWord0); long hash; int nameLen; int temperature; if (matchBits != 0) { nameLen = nameLen(matchBits); nameWord0 = maskWord(nameWord0, matchBits); cursor += nameLen; long tempWord = UNSAFE.getLong(inputBase + cursor); int dotPos = dotPos(tempWord); temperature = parseTemperature(tempWord, dotPos); cursor += (dotPos >> 3) + 3; hash = hash(nameWord0); if (stats.gotoName0(hash, nameWord0)) { stats.observe(temperature); continue; } lastNameWord = nameWord0; } else { // nameLen > 8 hash = hash(nameWord0); nameWord1 = UNSAFE.getLong(nameStartAddress + Long.BYTES); matchBits = semicolonMatchBits(nameWord1); if (matchBits != 0) { nameLen = Long.BYTES + nameLen(matchBits); nameWord1 = maskWord(nameWord1, matchBits); cursor += nameLen; long tempWord = UNSAFE.getLong(inputBase + cursor); int dotPos = dotPos(tempWord); temperature = parseTemperature(tempWord, dotPos); cursor += (dotPos >> 3) + 3; if (stats.gotoName1(hash, nameWord0, nameWord1)) { stats.observe(temperature); continue; } lastNameWord = nameWord1; } else { // nameLen > 16 nameLen = 2 * Long.BYTES; while (true) { lastNameWord = UNSAFE.getLong(nameStartAddress + nameLen); matchBits = semicolonMatchBits(lastNameWord); if (matchBits != 0) { nameLen += nameLen(matchBits); lastNameWord = maskWord(lastNameWord, matchBits); cursor += nameLen; long tempWord = UNSAFE.getLong(inputBase + cursor); int dotPos = dotPos(tempWord); temperature = parseTemperature(tempWord, dotPos); cursor += (dotPos >> 3) + 3; break; } nameLen += Long.BYTES; } } } stats.gotoAndObserve(hash, nameStartAddress, nameLen, nameWord0, nameWord1, lastNameWord, temperature); } } private static final long BROADCAST_SEMICOLON = 0x3B3B3B3B3B3B3B3BL; private static final long BROADCAST_0x01 = 0x0101010101010101L; private static final long BROADCAST_0x80 = 0x8080808080808080L; private static long semicolonMatchBits(long word) { long diff = word ^ BROADCAST_SEMICOLON; return (diff - BROADCAST_0x01) & (~diff & BROADCAST_0x80); } // credit: artsiomkorzun private static long maskWord(long word, long matchBits) { long mask = matchBits ^ (matchBits - 1); return word & mask; } // credit: merykitty private static int dotPos(long word) { return Long.numberOfTrailingZeros(~word & 0x10101000); } // credit: merykitty private static int parseTemperature(long word, int dotPos) { final long signed = (~word << 59) >> 63; final long removeSignMask = ~(signed & 0xFF); final long digits = ((word & removeSignMask) << (28 - dotPos)) & 0x0F000F0F00L; final long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; return (int) ((absValue ^ signed) - signed); } private static int nameLen(long separator) { return (Long.numberOfTrailingZeros(separator) >>> 3) + 1; } private static long hash(long word) { return Long.rotateLeft(word * 0x51_7c_c1_b7_27_22_0a_95L, 17); } // Copies the results from native memory to Java heap and puts them into the results array. private void exportResults() { var exportedStats = new ArrayList(10_000); for (int i = 0; i < STATS_TABLE_SIZE; i++) { stats.gotoIndex(i); if (stats.nameLen() == 0) { continue; } var sum = stats.sum(); var count = stats.count(); var min = stats.min(); var max = stats.max(); var name = stats.exportNameString(); var stationStats = new StationStats(); stationStats.name = name; stationStats.sum = sum; stationStats.count = count; stationStats.min = min; stationStats.max = max; exportedStats.add(stationStats); } StationStats[] exported = exportedStats.toArray(new StationStats[0]); Arrays.sort(exported); results[myIndex] = exported; } } static class StatsAccessor { static final int NAME_SLOT_SIZE = 104; static final long HASH_OFFSET = 0; static final long NAMELEN_OFFSET = HASH_OFFSET + Long.BYTES; static final long SUM_OFFSET = NAMELEN_OFFSET + Integer.BYTES; static final long COUNT_OFFSET = SUM_OFFSET + Integer.BYTES; static final long MIN_OFFSET = COUNT_OFFSET + Integer.BYTES; static final long MAX_OFFSET = MIN_OFFSET + Short.BYTES; static final long NAME_OFFSET = MAX_OFFSET + Short.BYTES; static final long SIZEOF = (NAME_OFFSET + NAME_SLOT_SIZE - 1) / 8 * 8 + 8; static final int ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); private final long address; private long slotBase; StatsAccessor(MemorySegment memSeg) { memSeg.fill((byte) 0); this.address = memSeg.address(); } void gotoIndex(int index) { slotBase = address + index * SIZEOF; } private boolean gotoName0(long hash, long nameWord0) { gotoIndex((int) (hash & TABLE_INDEX_MASK)); return hash() == hash && nameWord0() == nameWord0; } private boolean gotoName1(long hash, long nameWord0, long nameWord1) { gotoIndex((int) (hash & TABLE_INDEX_MASK)); return hash() == hash && nameWord0() == nameWord0 && nameWord1() == nameWord1; } long hash() { return UNSAFE.getLong(slotBase + HASH_OFFSET); } int nameLen() { return UNSAFE.getInt(slotBase + NAMELEN_OFFSET); } int sum() { return UNSAFE.getInt(slotBase + SUM_OFFSET); } int count() { return UNSAFE.getInt(slotBase + COUNT_OFFSET); } short min() { return UNSAFE.getShort(slotBase + MIN_OFFSET); } short max() { return UNSAFE.getShort(slotBase + MAX_OFFSET); } long nameAddress() { return slotBase + NAME_OFFSET; } long nameWord0() { return UNSAFE.getLong(nameAddress()); } long nameWord1() { return UNSAFE.getLong(nameAddress() + Long.BYTES); } String exportNameString() { final var bytes = new byte[nameLen() - 1]; UNSAFE.copyMemory(null, nameAddress(), bytes, ARRAY_BASE_OFFSET, bytes.length); return new String(bytes, StandardCharsets.UTF_8); } void setHash(long hash) { UNSAFE.putLong(slotBase + HASH_OFFSET, hash); } void setNameLen(int nameLen) { UNSAFE.putInt(slotBase + NAMELEN_OFFSET, nameLen); } void setSum(int sum) { UNSAFE.putInt(slotBase + SUM_OFFSET, sum); } void setCount(int count) { UNSAFE.putInt(slotBase + COUNT_OFFSET, count); } void setMin(short min) { UNSAFE.putShort(slotBase + MIN_OFFSET, min); } void setMax(short max) { UNSAFE.putShort(slotBase + MAX_OFFSET, max); } void gotoAndObserve( long hash, long nameStartAddress, int nameLen, long nameWord0, long nameWord1, long lastNameWord, int temperature) { int tableIndex = (int) (hash & TABLE_INDEX_MASK); while (true) { gotoIndex(tableIndex); if (hash() == hash && nameLen() == nameLen && nameEquals( nameAddress(), nameStartAddress, nameLen, nameWord0, nameWord1, lastNameWord)) { observe(temperature); break; } if (nameLen() != 0) { tableIndex = (tableIndex + 1) & TABLE_INDEX_MASK; continue; } initialize(hash, nameLen, nameStartAddress, temperature); break; } } void initialize(long hash, long nameLen, long nameStartAddress, int temperature) { setHash(hash); setNameLen((int) nameLen); setSum(temperature); setCount(1); setMin((short) temperature); setMax((short) temperature); UNSAFE.copyMemory(nameStartAddress, nameAddress(), nameLen); } void observe(int temperature) { setSum(sum() + temperature); setCount(count() + 1); setMin((short) Integer.min(min(), temperature)); setMax((short) Integer.max(max(), temperature)); } private static boolean nameEquals( long statsAddr, long inputAddr, long len, long inputWord1, long inputWord2, long lastInputWord) { boolean mismatch1 = inputWord1 != UNSAFE.getLong(statsAddr); boolean mismatch2 = inputWord2 != UNSAFE.getLong(statsAddr + Long.BYTES); if (len <= 2 * Long.BYTES) { return !(mismatch1 | mismatch2); } int i = 2 * Long.BYTES; for (; i <= len - Long.BYTES; i += Long.BYTES) { if (UNSAFE.getLong(inputAddr + i) != UNSAFE.getLong(statsAddr + i)) { return false; } } return i == len || lastInputWord == UNSAFE.getLong(statsAddr + i); } } private static void mergeSortAndPrint(StationStats[][] results) { var onFirst = true; System.out.print('{'); var cursors = new int[results.length]; var indexOfMin = 0; StationStats curr = null; int exhaustedCount; while (true) { exhaustedCount = 0; StationStats min = null; for (int i = 0; i < cursors.length; i++) { if (cursors[i] == results[i].length) { exhaustedCount++; continue; } StationStats candidate = results[i][cursors[i]]; if (min == null || min.compareTo(candidate) > 0) { indexOfMin = i; min = candidate; } } if (exhaustedCount == cursors.length) { if (!onFirst) { System.out.print(", "); } System.out.print(curr); break; } cursors[indexOfMin]++; if (curr == null) { curr = min; } else if (min.equals(curr)) { curr.sum += min.sum; curr.count += min.count; curr.min = Integer.min(curr.min, min.min); curr.max = Integer.max(curr.max, min.max); } else { if (onFirst) { onFirst = false; } else { System.out.print(", "); } System.out.print(curr); curr = min; } } System.out.println('}'); } static class StationStats implements Comparable { String name; long sum; int count; int min; int max; @Override public String toString() { return String.format("%s=%.1f/%.1f/%.1f", name, min / 10.0, Math.round((double) sum / count) / 10.0, max / 10.0); } @Override public boolean equals(Object that) { return that.getClass() == StationStats.class && ((StationStats) that).name.equals(this.name); } @Override public int compareTo(StationStats that) { return name.compareTo(that.name); } } private static String longToString(long word) { final ByteBuffer buf = ByteBuffer.allocate(8).order(ByteOrder.nativeOrder()); buf.clear(); buf.putLong(word); return new String(buf.array(), StandardCharsets.UTF_8); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_muditsaxena.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; public class CalculateAverage_muditsaxena { private static final String FILE = "./measurements.txt"; private static record Measurement(String station, double value) { private Measurement(String[] parts) { this(parts[0], Double.parseDouble(parts[1])); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } static class TaskRunner implements Callable { List inputList; String input; TaskRunner(List taskList) { this.inputList = taskList; } TaskRunner(String input) { this.input = input; } String[] readInput(String inputTask) { StringBuilder stationName = new StringBuilder(); String[] values = new String[2]; int index = 0; char ch = inputTask.charAt(index); while (ch != ';') { stationName.append(ch); ch = inputTask.charAt(++index); } index++; values[0] = stationName.toString(); values[1] = inputTask.substring(index); return values; } @Override public V call() { for (String inputTask : inputList) { if (inputTask.isEmpty()) { continue; } String[] values = readInput(inputTask); double value = Double.parseDouble(values[1]); MeasurementAggregator measurementAggregator = map.getOrDefault(values[0], new MeasurementAggregator()); measurementAggregator.count += 1; measurementAggregator.sum += value; measurementAggregator.max = Math.max(measurementAggregator.max, value); measurementAggregator.min = Math.min(measurementAggregator.min, value); map.put(values[0], measurementAggregator); } inputList = null; return null; } } static ConcurrentMap map = new ConcurrentHashMap<>(); static final int TASK_LIST_CAPACITY = 10000; // 100000 - 1:23 // 10000 - 1:10 // 1000 - 1:21 // Optimising split // 10000 - 1:15, 1:12, 1:11 // 1000 - 1:18, 1:23 public static void main(String[] args) throws IOException { try (ExecutorService virtualThreadExecutors = Executors.newVirtualThreadPerTaskExecutor()) { List taskList = new ArrayList<>(TASK_LIST_CAPACITY); List> tasks = new ArrayList<>(); try (BufferedReader br = Files.newBufferedReader(Paths.get(FILE))) { String line = br.readLine(); while (line != null) { taskList.add(line); if (taskList.size() >= TASK_LIST_CAPACITY) { tasks.add(CompletableFuture.runAsync(new FutureTask<>(new TaskRunner<>(taskList)), virtualThreadExecutors)); taskList = null; taskList = new ArrayList<>(TASK_LIST_CAPACITY); } line = br.readLine(); } } if (!taskList.isEmpty()) { tasks.add(CompletableFuture.runAsync(new FutureTask<>(new TaskRunner<>(taskList)), virtualThreadExecutors)); } for (CompletableFuture task : tasks) { if (task != null) { task.join(); } } Map resultRowMap = new TreeMap<>(); map.forEach((key, value) -> resultRowMap.put(key, new ResultRow(value.min, value.sum / value.count, value.max))); System.out.println(resultRowMap); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_naive.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.concurrent.ConcurrentSkipListMap; import java.util.stream.Collectors; public class CalculateAverage_naive { record Result(double min, double max, double sum, long count) { } public static void main(String[] args) throws FileNotFoundException { long start = System.currentTimeMillis(); var results = new BufferedReader(new FileReader("./measurements.txt")) .lines() .map(l -> l.split(";")) .collect(Collectors.toMap( parts -> parts[0], parts -> { double temperature = Double.parseDouble(parts[1]); return new Result(temperature, temperature, temperature, 1); }, (oldResult, newResult) -> { double min = Math.min(oldResult.min, newResult.min); double max = Math.max(oldResult.max, newResult.max); double sum = oldResult.sum + newResult.sum; long count = oldResult.count + newResult.count; return new Result(min, max, sum, count); }, ConcurrentSkipListMap::new)); System.out.println(System.currentTimeMillis() - start); System.out.println(results); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_netrunnereve.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.util.Arrays; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.CountDownLatch; import java.lang.Math; public class CalculateAverage_netrunnereve { private static final String FILE = "./measurements.txt"; private static final int NUM_THREADS = 8; // test machine private static final int LEN_EXTEND = 200; // guarantees a newline private static final int HASHT_SIZE = 16384; // size of hash table, adjust tradeoff between colisions and cache utilization private static final int DJB2_INIT = 5831; private static class MeasurementAggregator { // min, max, sum stored as 0.1/unit private MeasurementAggregator next = null; // linked list of entries for handling hash colisions private byte[] station = null; private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum = 0; private int count = 0; } private static class ThreadCalcs { private MeasurementAggregator[] hashSpace = null; private String[] staArr = null; private int numStations = 0; } // djb2 hash private static int calc_hash(byte[] input, int len) { int hash = DJB2_INIT; for (int i = 0; i < len; i++) { hash = ((hash << 5) + hash) + Byte.toUnsignedInt(input[i]); } return Math.abs(hash % HASHT_SIZE); } private static class ThreadedParser extends Thread { private MappedByteBuffer mbuf; private int mbs; private ThreadCalcs[] threadOut; private int threadID; private CountDownLatch tpLatch; private ThreadedParser(MappedByteBuffer mbuf, int mbs, ThreadCalcs[] threadOut, int threadID, CountDownLatch tpLatch) { this.mbuf = mbuf; this.mbs = mbs; this.threadOut = threadOut; this.threadID = threadID; this.tpLatch = tpLatch; } public void run() { MeasurementAggregator[] hashSpace = new MeasurementAggregator[HASHT_SIZE]; // hash table byte[] scratch = new byte[100]; // <= 100 characters in station name String[] staArr = new String[10000]; // max 10000 station names MeasurementAggregator ma = null; int numStations = 0; int negMul = 1; int head = 0; int tempCnt = -1; // 0 if 1 digit measurement, 1 if 2 digit int hash = DJB2_INIT; // do calc_hash manually in loop int i = 0; // byte by byte iterator while (true) { byte cur = mbuf.get(i); if (cur == 59) { // ; hash = Math.abs(hash % HASHT_SIZE); // this is faster than filling scratch immediately after each byte is read int len = i - head; mbuf.position(head); mbuf.get(scratch, 0, len); ma = hashSpace[hash]; MeasurementAggregator prev = null; while (true) { if (ma == null) { ma = new MeasurementAggregator(); ma.station = Arrays.copyOfRange(scratch, 0, len); staArr[numStations] = new String(scratch, 0, len, StandardCharsets.UTF_8); if (prev != null) { prev.next = ma; } else { hashSpace[hash] = ma; } numStations++; break; } else if ((len != ma.station.length) || (Arrays.compare(scratch, 0, len, ma.station, 0, len) != 0)) { // hash collision prev = ma; ma = ma.next; } else { // hit break; } } i++; while (true) { cur = mbuf.get(i); if (cur == 46) { // . int tempa = (negMul) * ((10 + 90 * tempCnt) * (scratch[0] - 48) + (10 * tempCnt) * (scratch[1] - 48) + (mbuf.get(i + 1) - 48)); // branchless if (tempa < ma.min) { ma.min = tempa; } if (tempa > ma.max) { ma.max = tempa; } ma.sum += tempa; ma.count++; // this line is finished! i += 2; // newline char hash = DJB2_INIT; negMul = 1; head = i + 1; // start of next line tempCnt = -1; break; } else if (cur == 45) { // ascii - negMul = -1; } else { scratch[tempCnt + 1] = cur; tempCnt++; } i++; } if (head >= mbs) { break; } } else { hash = ((hash << 5) + hash) + Byte.toUnsignedInt(cur); } i++; } threadOut[threadID] = new ThreadCalcs(); threadOut[threadID].hashSpace = hashSpace; threadOut[threadID].staArr = staArr; threadOut[threadID].numStations = numStations; tpLatch.countDown(); } } public static void main(String[] args) { try { RandomAccessFile mraf = new RandomAccessFile(FILE, "r"); long fileSize = mraf.getChannel().size(); long threadNum = NUM_THREADS; long minThreads = (fileSize / Integer.MAX_VALUE) + 1; // minimum # of threads required due to MappedByteBuffer size limit if (threadNum < minThreads) { threadNum = minThreads; } long bufSize = fileSize / threadNum; // don't bother multithreading for small files if (bufSize < 1000000) { threadNum = 1; bufSize = Integer.MAX_VALUE; } ThreadCalcs[] threadOut = new ThreadCalcs[(int) threadNum]; CountDownLatch tpLatch = new CountDownLatch((int) threadNum); int threadID = 0; long h = 0; while (h < fileSize) { long length = bufSize; boolean finished = false; if ((h == 0) && (length + LEN_EXTEND < Integer.MAX_VALUE)) { // add a bit of extra bytes to first thread to avoid generating new thread for the remainder length += LEN_EXTEND; // arbitary bytes to guarantee a newline somewhere } if (h + length > fileSize) { // past the end length = fileSize - h; finished = true; } MappedByteBuffer mbuf = mraf.getChannel().map(FileChannel.MapMode.READ_ONLY, h, length); int mbs = mbuf.capacity(); // check for last newline and split there, anything after goes to next buffer if (!finished) { for (int i = mbs - 1; true; i--) { byte cur = mbuf.get(i - 1); if (cur == 10) { // \n mbs = i; break; } } } ThreadedParser tpThr = new ThreadedParser(mbuf, mbs, threadOut, threadID, tpLatch); tpThr.start(); h += mbs; threadID++; } try { tpLatch.await(); } catch (InterruptedException ex) { System.exit(1); } // use treemap to sort and uniquify Map staMap = new TreeMap<>(); for (int i = 0; i < threadID; i++) { for (int j = 0; j < threadOut[i].numStations; j++) { staMap.put(threadOut[i].staArr[j], false); } } boolean started = false; String out = "{"; for (String i : staMap.keySet()) { if (started) { out += ", "; } else { started = true; } byte[] strBuf = i.getBytes(StandardCharsets.UTF_8); int hash = calc_hash(strBuf, strBuf.length); MeasurementAggregator mSum = new MeasurementAggregator(); for (int j = 0; j < threadID; j++) { MeasurementAggregator ma = threadOut[j].hashSpace[hash]; while (true) { if ((strBuf.length != ma.station.length) || (Arrays.compare(strBuf, ma.station) != 0)) { // hash collision ma = ma.next; continue; } else { // hit if (ma.min < mSum.min) { mSum.min = ma.min; } if (ma.max > mSum.max) { mSum.max = ma.max; } mSum.sum += ma.sum; mSum.count += ma.count; break; } } } double min = Math.round(Double.valueOf(mSum.min)) / 10.0; double avg = Math.round(Double.valueOf(mSum.sum) / Double.valueOf(mSum.count)) / 10.0; double max = Math.round(Double.valueOf(mSum.max)) / 10.0; out += i + "=" + min + "/" + avg + "/" + max; } out += "}\n"; System.out.print(out); mraf.getChannel().close(); mraf.close(); } catch (IOException ex) { System.exit(1); } System.exit(0); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_obourgain.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; import java.util.function.Consumer; public class CalculateAverage_obourgain { private static final String FILE = "./measurements.txt"; private static final boolean USE_UNSAFE = true; static class ThreadLocalState { private final OpenAddressingMap resultMap = new OpenAddressingMap(); private final byte[] cityNameBuffer = new byte[128]; } private static final ThreadLocal THREAD_LOCAL_STATE = ThreadLocal.withInitial(ThreadLocalState::new); public static final int PER_THREAD_MAP_CAPACITY = 65536; public static final int MASK = PER_THREAD_MAP_CAPACITY - 1; // needed ony without unsafe // public static final int MOST_SIGNIFICANT_BIT_SET = 0x80808080; // public static final int SUBTRACT_0_FROM_EACH_BYTE_IN_INT = 0x30303030; // private static final ValueLayout.OfInt BIG_ENDIAN_INTEGER_UNALIGNED = ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN); private static final Unsafe UNSAFE; private static final int BYTE_ARRAY_OFFSET_BASE; // TODO support big endian archis public static final int MASK_3_BYTES = Integer.reverseBytes(16777215); public static final int MASK_2_BYTES = Integer.reverseBytes(65535); static { if (USE_UNSAFE) { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); UNSAFE = (Unsafe) field.get(null); BYTE_ARRAY_OFFSET_BASE = UNSAFE.arrayBaseOffset(byte[].class); } catch (Exception e) { throw new RuntimeException(e); } } else { UNSAFE = null; BYTE_ARRAY_OFFSET_BASE = -1; } } static class MeasurementAggregator { // deci-Celcius values private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum; private int count; public void appendTo(StringBuilder stringBuilder) { // micro optim, never saw the toString on a profile stringBuilder.append(round(min)).append("/").append(round(((double) sum) / count)).append("/").append(round(max)); } private double round(double value) { return Math.round(value) / 10.0; } public void reset() { min = Integer.MAX_VALUE; max = Integer.MIN_VALUE; sum = 0; count = 0; } void add(int measurementInDeciCelsius) { max = Math.max(max, measurementInDeciCelsius); min = Math.min(min, measurementInDeciCelsius); sum += measurementInDeciCelsius; count++; } } record PrintableMeasurement(String key, MeasurementAggregator measurementAggregator) { } public static void main(String[] args) throws Exception { // no close, leak everything and let the OS cleanup! var randomAccessFile = new RandomAccessFile(FILE, "r"); MemorySegment segment = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length(), Arena.global()); // can we do better to balance across cpu cores? int chunkSize = 20 * 1024 * 1024; var mergedResults = new ConcurrentHashMap(1024); // Fork join pool as a lock free queue, it should help putting all threads to work faster try (ExecutorService executor = Executors.newWorkStealingPool()) { // start processing chunks asap, no need for an intermediate list. Finding chunks limits takes about 5ms. Using ForkJoinPool would be fun, but unlikely to yield any measurable gain createChunks(segment, chunkSize, chunk -> { executor.execute(() -> { ThreadLocalState threadLocalState = THREAD_LOCAL_STATE.get(); processChunk(chunk, threadLocalState); merge(mergedResults, threadLocalState.resultMap); }); }); executor.shutdown(); boolean shutdownProperly = executor.awaitTermination(1, TimeUnit.MINUTES); if (!shutdownProperly) { throw new RuntimeException("did not complete on time"); } } // making it over complicated here for the sake of gaining a few milliseconds. In fact, it doesn't matter much and the stupid code would be enough here List entries = mergedResults.entrySet().stream() // we have to convert to String to have proper UTF-8 support for the sort .map(entry -> new PrintableMeasurement(new String(entry.getKey().key, StandardCharsets.UTF_8), entry.getValue())) .sorted(Comparator.comparing(PrintableMeasurement::key)) .toList(); // presize the StringBuilder to avoid any copy. With the worst case dataset (10k keys each 100 bytes long etc) it would resize, but I don't care much var sb = new StringBuilder(64 * 1024); sb.append('{'); for (int i = 0; i < entries.size(); i++) { printEntry(entries, i, sb); } sb.append('}'); System.out.println(sb); // System.out.println(COLLISIONS.get()); } private static void printEntry(List entries, int i, StringBuilder sb) { var entry = entries.get(i); sb.append(entry.key()); sb.append('='); entry.measurementAggregator().appendTo(sb); if (i != entries.size() - 1) { sb.append(',').append(' '); } } private static void processChunk(MemorySegment segment, ThreadLocalState threadLocalState) { // safe as our segments are in the tens of MB range int size = (int) segment.byteSize(); long position = 0; while (position < size - 1) { position = processLineInChunk(segment, position, threadLocalState); } } private static long processLineInChunk(MemorySegment segment, long position, ThreadLocalState threadLocalState) { // compute hashCode for the city name, copy the bytes to a buffer and search for the semicolon all at once, so we don' t visit the same byte twice // the packing is used to return two ints. The alternative is to return an int and add a mutable field to ThreadLocalState, but that's a bit slower long packed_cityNameLength_hashCode = getCityNameLength(segment, position, threadLocalState); int cityNameLength = (int) (packed_cityNameLength_hashCode >> 32); int hashCode = (int) packed_cityNameLength_hashCode; MeasurementAggregator perCityStats = threadLocalState.resultMap.getOrCreate(threadLocalState, cityNameLength, hashCode); // I tried packing for decodeDouble, but here it is slower than passing the MeasurementAggregator // + 1 for the semicolon position = decodeDouble(segment, position + cityNameLength + 1, perCityStats); return position; } static long getCityNameLength(MemorySegment segment, long position, ThreadLocalState threadLocalState) { long cityNameLength = 0; int cityNameHashCode = 0; byte[] cityNameBuffer = threadLocalState.cityNameBuffer; while (true) { // trick: we know that we will have a value after the semicolon which is at least 3 bytes, so we can unroll the loop // adding one for the semicolon, we know we can always read 4 bytes without worrying about reading out of bounds int i = readBigEndianInt(segment, position + cityNameLength); // if (USE_UNSAFE) { // put all four bytes at once, we'll use cityNameLength to not read past the actual end of the buffer UNSAFE.putInt(cityNameBuffer, BYTE_ARRAY_OFFSET_BASE + cityNameLength, Integer.reverseBytes(i)); // } byte b0 = (byte) (i >>> 24); if (b0 == ';') { break; } // if (!USE_UNSAFE) { // cityNameBuffer[cityNameLength] = b0; // } byte b1 = (byte) (i >>> 16); if (b1 == ';') { cityNameHashCode = cityNameHashCode * 31 + b0; cityNameLength += 1; break; } // if (!USE_UNSAFE) { // cityNameBuffer[cityNameLength + 1] = b1; // } byte b2 = (byte) (i >>> 8); if (b2 == ';') { int masked = i & MASK_2_BYTES; cityNameHashCode = cityNameHashCode * 31 + masked; cityNameLength += 2; break; } // if (!USE_UNSAFE) { // cityNameBuffer[cityNameLength + 2] = b2; // } byte b3 = (byte) i; if (b3 == ';') { int masked = i & MASK_3_BYTES; cityNameHashCode = cityNameHashCode * 31 + masked; cityNameLength += 3; break; } // if (!USE_UNSAFE) { // cityNameBuffer[cityNameLength + 3] = b3; // } cityNameHashCode = cityNameHashCode * 31 + i; cityNameLength += 4; } return (cityNameLength << 32) | (cityNameHashCode & 0xffffffffL); } private static long decodeDouble(MemorySegment segment, long position, MeasurementAggregator perCityStats) { // values are assumed to be: // * maybe with a minus sign // * an integer part in the range of 0 to 99 included, single digit possible // * always with a single decimal // that's between 3 and 5 bytes long offsetFromSign = 0; long offsetFromValue = 0; int signum = 1; // peak at the first byte to see if we have a minus sign byte maybeSign = readByte(segment, position); if (maybeSign == '-') { offsetFromSign++; signum = -1; } // as the value is at least 3 bytes then we have a line feed, we can safely read 4 bytes int i = readBigEndianInt(segment, position + offsetFromSign); // keep in deci-Celcius, so we avoid a division for each line, and keep it for the end int tempInDeciCelcius = (byte) (i >>> 24) - '0'; byte secondDigitOrDot = (byte) (i >>> 16); byte decimalDigit; if (secondDigitOrDot == '.') { decimalDigit = (byte) (i >>> 8); // +1 for the line feed offsetFromValue += 3 + 1; } else { tempInDeciCelcius = 10 * tempInDeciCelcius + (secondDigitOrDot - '0'); decimalDigit = (byte) i; // +1 for the line feed offsetFromValue += 4 + 1; } tempInDeciCelcius = 10 * tempInDeciCelcius + (decimalDigit - '0'); tempInDeciCelcius *= signum; perCityStats.add(tempInDeciCelcius); return position + offsetFromSign + offsetFromValue; } private static int readBigEndianInt(MemorySegment segment, long position) { // I had to comment the code as a static flag isn't enough for max perf, maybe because until the code is JIT-ed it is a lot slower to do the check in a hot loop // if (USE_UNSAFE) { // sadly, Unsafe is faster than reading via the MemorySegment API. For real production code, I would go with the safety of the bound checks, but here I need the boost // Actually, the MemorySegment is a great improvement over unsafe for the developer experience, kudos return Integer.reverseBytes(UNSAFE.getInt(segment.address() + position)); // } else { // return segment.get(BIG_ENDIAN_INTEGER_UNALIGNED, position); // } } private static byte readByte(MemorySegment segment, long position) { // if (USE_UNSAFE) { return UNSAFE.getByte(segment.address() + position); // } else { // return segment.get(ValueLayout.JAVA_BYTE, position); // } } static final class KeyWrapper implements Comparable { private final byte[] key; private final int keyHashCode; KeyWrapper(byte[] key, int keyHashCode) { this.key = key; this.keyHashCode = keyHashCode; } @Override public int hashCode() { return keyHashCode; } @Override public boolean equals(Object obj) { // I tried making the key field mutable and interning, but that only made performance more variable. Sometime faster, sometime slower var that = (KeyWrapper) obj; return Arrays.equals(this.key, that.key); } @Override public int compareTo(KeyWrapper o) { return Arrays.compare(this.key, o.key); } @Override public String toString() { return new String(key, StandardCharsets.UTF_8); } } private static void merge(ConcurrentHashMap mergedResults, OpenAddressingMap chunkResult) { chunkResult.forEach((k1, v1) -> { var keyWrapper = new KeyWrapper(k1, Arrays.hashCode(k1)); // compute is atomic, so we don't need to synchronize mergedResults.compute(keyWrapper, (k2, v2) -> { if (v2 == null) { v2 = new MeasurementAggregator(); } v2.min = Math.min(v2.min, v1.min); v2.max = Math.max(v2.max, v1.max); v2.sum += v1.sum; v2.count += v1.count; v1.reset(); return v2; }); }); } static void createChunks(MemorySegment segment, int chunkSize, Consumer onChunkCreated) { long endOfPreviousChunk = 0; while (endOfPreviousChunk < segment.byteSize()) { long chunkStart = endOfPreviousChunk; long tmpChunkEnd = Math.min(segment.byteSize() - 1, endOfPreviousChunk + chunkSize); long chunkEnd; if (segment.get(ValueLayout.JAVA_BYTE, tmpChunkEnd) == '\n') { // we got lucky and our chunk ends on a line break chunkEnd = tmpChunkEnd + 1; } else { // round the chunk to the next line break, included chunkEnd = findNextLineBreak(segment, tmpChunkEnd) + 1; } MemorySegment slice = segment.asSlice(chunkStart, chunkEnd - chunkStart); onChunkCreated.accept(slice); endOfPreviousChunk = chunkEnd; } } static long findNextLineBreak(MemorySegment segment, long start) { long limit = segment.byteSize(); for (long i = start; i < limit; i++) { byte b = segment.get(ValueLayout.JAVA_BYTE, i); if (b == '\n') { return i; } } return segment.byteSize(); } static class OpenAddressingMap { private final byte[][] keys; private final MeasurementAggregator[] values; private int size = 0; public OpenAddressingMap() { // must be power of 2 this.keys = new byte[PER_THREAD_MAP_CAPACITY][]; this.values = new MeasurementAggregator[PER_THREAD_MAP_CAPACITY]; } public void forEach(final BiConsumer consumer) { int remaining = size; for (int i = 1, length = values.length; remaining > 0 && i < length; i++) { MeasurementAggregator value = values[i]; if (null != value) { consumer.accept(keys[i], value); remaining--; } } } public MeasurementAggregator getOrCreate(ThreadLocalState threadLocalState, int cityNameLength, int cityNameHashCode) { // as I mask I lose some bits. Reinject those bit to avoid too many collisions. Maybe expert in hashing can help? cityNameHashCode = (cityNameHashCode >> 16) ^ cityNameHashCode; byte[] cityNameBuffer = threadLocalState.cityNameBuffer; int keyIndex = cityNameHashCode & MASK; MeasurementAggregator value; while (null != (value = values[keyIndex])) { byte[] existingKey = keys[keyIndex]; if (existingKey.length == cityNameLength && arrayEquals(existingKey, cityNameBuffer, (byte) cityNameLength)) { return value; } // } // COLLISIONS.incrementAndGet(); // go to next slot keyIndex = (keyIndex + 1) & MASK; } return create(cityNameLength, cityNameBuffer, keyIndex); } private MeasurementAggregator create(int cityNameLength, byte[] cityNameBuffer, int keyIndex) { byte[] key = Arrays.copyOf(cityNameBuffer, cityNameLength); keys[keyIndex] = key; MeasurementAggregator value = new MeasurementAggregator(); values[keyIndex] = value; size++; return value; } } static final AtomicLong COLLISIONS = new AtomicLong(); static boolean arrayEquals(byte[] existingKey, byte[] cityNameBuffer, byte length) { int i = 0; while (i != length) { if (length >= 8) { if (UNSAFE.getLong(existingKey, BYTE_ARRAY_OFFSET_BASE + i) != UNSAFE.getLong(cityNameBuffer, BYTE_ARRAY_OFFSET_BASE + i)) { return false; } else { i += 8; } } else if (length >= 4) { if (UNSAFE.getInt(existingKey, BYTE_ARRAY_OFFSET_BASE + i) != UNSAFE.getInt(cityNameBuffer, BYTE_ARRAY_OFFSET_BASE + i)) { return false; } else { i += 4; } } for (; i < (long) length; ++i) { if (UNSAFE.getByte(existingKey, BYTE_ARRAY_OFFSET_BASE + i) != UNSAFE.getByte(cityNameBuffer, BYTE_ARRAY_OFFSET_BASE + i)) { return false; } } } return true; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_omarchenko4j.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; public class CalculateAverage_omarchenko4j { private static final String FILE = "./measurements.txt"; private static final int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); private static final int MAX_LINE_SIZE = 128; public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { try (var file = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ)) { var fileSize = file.size(); try (var arena = Arena.ofShared()) { var segment = file.map(MapMode.READ_ONLY, 0, fileSize, arena); var tasks = sliceIntoTasks(segment); try (var executor = Executors.newFixedThreadPool(Math.min(tasks.size(), NUMBER_OF_CORES))) { var futures = executor.invokeAll(tasks); var measurements = new TreeMap(String::compareTo); for (var future : futures) { var result = future.get(); for (var entry : result.entrySet()) { var station = entry.getKey(); var a1 = entry.getValue(); var a2 = measurements.get(station); if (a2 != null) { a1.merge(a2); } measurements.put(station, a1); } } System.out.println(measurements); } } } } private static List sliceIntoTasks(MemorySegment segment) { var tasks = new ArrayList(NUMBER_OF_CORES); var segmentSize = segment.byteSize(); var chunkSize = segmentSize / NUMBER_OF_CORES; if (chunkSize < (long) NUMBER_OF_CORES * MAX_LINE_SIZE) { return List.of(new Task(segment, 0, segmentSize)); } long offsetStart = 0; for (int coreNumber = 1; coreNumber <= NUMBER_OF_CORES; coreNumber++) { long offsetEnd = chunkSize * coreNumber; while (offsetEnd < segmentSize) { var b = segment.get(ValueLayout.JAVA_BYTE, offsetEnd); offsetEnd++; if (b == '\n') { break; } } tasks.add(new Task(segment, offsetStart, offsetEnd)); offsetStart = offsetEnd; } return tasks; } private static class Task implements Callable> { private static final byte SEPARATOR = ';'; private static final byte END_LINE = '\n'; private final MemorySegment segment; private final long startOffset; private final long endOffset; private Task(MemorySegment segment, long startOffset, long endOffset) { this.segment = segment; this.startOffset = startOffset; this.endOffset = endOffset; } @Override public Map call() { var measurements = new HashMap(512); int startIndex = 0; int separatorIndex = 0; int endIndex = 0; var buffer = new byte[MAX_LINE_SIZE]; int bufferIndex = 0; for (long offset = startOffset; offset < endOffset; offset++) { var b = segment.get(ValueLayout.JAVA_BYTE, offset); buffer[bufferIndex] = b; bufferIndex++; if (b == SEPARATOR) { separatorIndex = bufferIndex - 1; continue; } if (b == END_LINE) { endIndex = bufferIndex - 1; var station = new String(buffer, startIndex, separatorIndex); var value = getDouble(buffer, separatorIndex + 1, endIndex - separatorIndex - 1); measurements.computeIfAbsent(station, ignored -> new Aggregator()).addValue(value); bufferIndex = 0; } } return measurements; } private static final byte MINUS = '-'; private static final byte ZERO = '0'; private double getDouble(byte[] buffer, int offset, int length) { if (length == 4) { if (buffer[offset] == MINUS) { int value; value = buffer[offset + 1] - ZERO; value = (value * 10) + (buffer[offset + 3] - ZERO); value = -value; return value * .1D; } int value; value = buffer[offset] - ZERO; value = (value * 10) + (buffer[offset + 1] - ZERO); value = (value * 10) + (buffer[offset + 3] - ZERO); return value * .1D; } if (length == 3) { int value; value = buffer[offset] - ZERO; value = (value * 10) + (buffer[offset + 2] - ZERO); return value * .1D; } int value; value = buffer[offset + 1] - ZERO; value = (value * 10) + (buffer[offset + 2] - ZERO); value = (value * 10) + (buffer[offset + 4] - ZERO); value = -value; return value * .1D; } } public static class Aggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; public void addValue(double value) { min = Math.min(min, value); max = Math.max(max, value); sum += value; count++; } public void merge(Aggregator aggregator) { if (aggregator.min < min) { min = aggregator.min; } if (aggregator.max > max) { max = aggregator.max; } sum += aggregator.sum; count += aggregator.count; } @Override public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_padreati.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.StructuredTaskScope; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; public class CalculateAverage_padreati { private static final VectorSpecies species = ByteVector.SPECIES_PREFERRED; private static final String FILE = "./measurements.txt"; private static final int CHUNK_SIZE = 1024 * 1024; private record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } private record MeasurementAggregator(double min, double max, double sum, long count) { public MeasurementAggregator(double seed) { this(seed, seed, seed, 1); } public MeasurementAggregator merge(MeasurementAggregator b) { return new MeasurementAggregator( Math.min(min, b.min), Math.max(max, b.max), sum + b.sum, count + b.count ); } public ResultRow toResultRow() { return new ResultRow(min, sum / count, max); } } public static void main(String[] args) throws IOException { new CalculateAverage_padreati().run(); } private void run() throws IOException { File file = new File(FILE); var splits = findFileSplits(); List>> subtasks = new ArrayList<>(); try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { for (int i = 0; i < splits.size(); i++) { long splitStart = splits.get(i); long splitEnd = i < splits.size() - 1 ? splits.get(i + 1) : file.length() + 1; subtasks.add(scope.fork(() -> chunkProcessor(file, splitStart, splitEnd))); } scope.join(); scope.throwIfFailed(); var resultList = subtasks.stream().map(StructuredTaskScope.Subtask::get).toList(); TreeMap measurements = collapseResults(resultList); System.out.println(measurements); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } } private List findFileSplits() throws IOException { var splits = new ArrayList(); splits.add(0L); File file = new File(FILE); long next = CHUNK_SIZE; while (true) { if (next >= file.length()) { break; } try (FileInputStream fis = new FileInputStream(file)) { long skip = fis.skip(next); if (skip != next) { throw new RuntimeException(); } // find first new line while (true) { int ch = fis.read(); if (ch != '\n') { next++; continue; } break; } // skip eventual \\r if (fis.read() == '\r') { next++; } splits.add(next + 1); next += CHUNK_SIZE; } } return splits; } public Map chunkProcessor(File source, long start, long end) throws IOException { var map = new HashMap(); byte[] buffer = new byte[(int) (end - start)]; int len; try (FileInputStream bis = new FileInputStream(source)) { bis.skip(start); len = bis.read(buffer, 0, buffer.length); } List nlIndexes = new ArrayList<>(); List commaIndexes = new ArrayList<>(); int loopBound = species.loopBound(len); int i = 0; for (; i < loopBound; i += species.length()) { ByteVector v = ByteVector.fromArray(species, buffer, i); var mask = v.compare(VectorOperators.EQ, '\n'); for (int j = 0; j < species.length(); j++) { if (mask.laneIsSet(j)) { nlIndexes.add(i + j); } } mask = v.compare(VectorOperators.EQ, ';'); for (int j = 0; j < species.length(); j++) { if (mask.laneIsSet(j)) { commaIndexes.add(i + j); } } } for (; i < len; i++) { if (buffer[i] == '\n') { nlIndexes.add(i); } if (buffer[i] == ';') { commaIndexes.add(i); } } int startLine = 0; for (int j = 0; j < nlIndexes.size(); j++) { int endLine = nlIndexes.get(j); int commaIndex = commaIndexes.get(j); String key = new String(buffer, startLine, commaIndex - startLine); double value = Double.parseDouble(new String(buffer, commaIndex + 1, endLine - commaIndex - 1)); map.merge(key, new MeasurementAggregator(value), MeasurementAggregator::merge); startLine = endLine + 1; } return map; } private TreeMap collapseResults(List> resultList) { HashMap aggregate = new HashMap<>(); for (var map : resultList) { for (var entry : map.entrySet()) { aggregate.merge(entry.getKey(), entry.getValue(), MeasurementAggregator::merge); } } TreeMap measurements = new TreeMap<>(); for (var entry : aggregate.entrySet()) { measurements.put(entry.getKey(), entry.getValue().toResultRow()); } return measurements; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_palmr.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; import java.util.stream.StreamSupport; public class CalculateAverage_palmr { private static final String FILE = "./measurements.txt"; private static final int CHUNK_SIZE = 1024 * 1024 * 10; // Trial and error showed ~10MB to be a good size on our machine private static final int STATION_NAME_BUFFER_SIZE = 128; private static final int THREAD_COUNT = Math.min(8, Runtime.getRuntime().availableProcessors()); private static final char SEPARATOR_CHAR = ';'; private static final char END_OF_RECORD = '\n'; private static final char MINUS_CHAR = '-'; private static final char DECIMAL_POINT_CHAR = '.'; public static void main(String[] args) throws IOException { final var file = new RandomAccessFile(FILE, "r"); final var channel = file.getChannel(); final TreeMap results = StreamSupport.stream(ThreadChunk.chunk(file, THREAD_COUNT), true) .map(chunk -> parseChunk(chunk, channel)) .flatMap(bakm -> bakm.getAsUnorderedList().stream()) .collect(Collectors.toMap(m -> new String(m.stationNameBytes, StandardCharsets.UTF_8), m -> m, MeasurementAggregator::merge, TreeMap::new)); System.out.println(results); } private record ThreadChunk(long startPoint, long endPoint, long size) { public static Spliterator chunk(final RandomAccessFile file, final int chunkCount) throws IOException { final var fileSize = file.length(); final var idealChunkSize = Math.max(CHUNK_SIZE, fileSize / THREAD_COUNT); final var chunks = new CalculateAverage_palmr.ThreadChunk[chunkCount]; var validChunks = 0; var startPoint = 0L; for (int i = 0; i < chunkCount; i++) { var endPoint = Math.min(startPoint + idealChunkSize, fileSize); if (startPoint + idealChunkSize < fileSize) { file.seek(endPoint); while (endPoint++ < fileSize && file.readByte() != END_OF_RECORD) { Thread.onSpinWait(); } } final var actualSize = endPoint - startPoint; if (actualSize > 1) { chunks[i] = new CalculateAverage_palmr.ThreadChunk(startPoint, endPoint, actualSize); startPoint += actualSize; validChunks++; } else { break; } } return Spliterators.spliterator(chunks, 0, validChunks, Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.NONNULL | Spliterator.IMMUTABLE | Spliterator.CONCURRENT ); } } private static ByteArrayKeyedMap parseChunk(ThreadChunk chunk, FileChannel channel) { final var state = new State(); var offset = chunk.startPoint; while (offset < chunk.endPoint) { parseData(channel, state, offset, Math.min(CHUNK_SIZE, chunk.endPoint - offset)); offset += CHUNK_SIZE; } return state.aggregators; } private static void parseData(final FileChannel channel, final State state, final long offset, final long bufferSize) { final ByteBuffer byteBuffer; try { byteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, offset, bufferSize); while (byteBuffer.hasRemaining()) { final var currentChar = byteBuffer.get(); if (currentChar == SEPARATOR_CHAR) { state.parsingValue = true; } else if (currentChar == END_OF_RECORD) { if (state.stationPointerEnd != 0) { final var value = state.measurementValue * state.exponent; MeasurementAggregator aggregator = state.aggregators.computeIfAbsent(state.stationBuffer, state.stationPointerEnd, state.signedHashCode); aggregator.count++; aggregator.min = Math.min(aggregator.min, value); aggregator.max = Math.max(aggregator.max, value); aggregator.sum += value; } // reset state.reset(); } else { if (!state.parsingValue) { state.stationBuffer[state.stationPointerEnd++] = currentChar; state.signedHashCode = 31 * state.signedHashCode + (currentChar & 0xff); } else { if (currentChar == MINUS_CHAR) { state.exponent = -0.1; } else if (currentChar != DECIMAL_POINT_CHAR) { state.measurementValue = state.measurementValue * 10 + (currentChar - '0'); } } } } } catch (IOException e) { throw new RuntimeException(e); } } private static final class State { ByteArrayKeyedMap aggregators = new ByteArrayKeyedMap(); boolean parsingValue = false; byte[] stationBuffer = new byte[STATION_NAME_BUFFER_SIZE]; int signedHashCode = 0; int stationPointerEnd = 0; double measurementValue = 0; double exponent = 0.1; public void reset() { parsingValue = false; signedHashCode = 0; stationPointerEnd = 0; measurementValue = 0; exponent = 0.1; } } private static class MeasurementAggregator { final byte[] stationNameBytes; final int stationNameHashCode; private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; public MeasurementAggregator(final byte[] stationNameBytes, final int stationNameHashCode) { this.stationNameBytes = stationNameBytes; this.stationNameHashCode = stationNameHashCode; } public String toString() { return STR."\{round(min)}/\{round(sum / count)}/\{round(max)}"; } private double round(final double value) { return Math.round(value * 10.0) / 10.0; } private MeasurementAggregator merge(final MeasurementAggregator b) { this.count += b.count; this.min = Math.min(this.min, b.min); this.max = Math.max(this.max, b.max); this.sum += b.sum; return this; } } /** * Very basic hash table implementation, only implementing computeIfAbsent since that's all the code needs. * It's sized to give minimal collisions with the example test set. this may not hold true if the stations list * changes, but it should still perform fairly well. * It uses Open Addressing, meaning it's just one array, rather Separate Chaining which is what the default java HashMap uses. * IT also uses Linear probing for collision resolution, which given the minimal collision count should hold up well. */ private static class ByteArrayKeyedMap { private final int BUCKET_COUNT = 0xFFFF; private final MeasurementAggregator[] buckets = new MeasurementAggregator[BUCKET_COUNT + 1]; private final List compactUnorderedBuckets = new ArrayList<>(413); public MeasurementAggregator computeIfAbsent(final byte[] key, final int keyLength, final int keyHashCode) { var index = keyHashCode & BUCKET_COUNT; while (true) { MeasurementAggregator maybe = buckets[index]; if (maybe != null) { if (Arrays.equals(key, 0, keyLength, maybe.stationNameBytes, 0, maybe.stationNameBytes.length)) { return maybe; } index++; index &= BUCKET_COUNT; } else { final var copiedKey = Arrays.copyOf(key, keyLength); MeasurementAggregator measurementAggregator = new MeasurementAggregator(copiedKey, keyHashCode); buckets[index] = measurementAggregator; compactUnorderedBuckets.add(measurementAggregator); return measurementAggregator; } } } public List getAsUnorderedList() { return compactUnorderedBuckets; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_parkertimmins.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.util.*; import java.util.concurrent.atomic.AtomicLong; public class CalculateAverage_parkertimmins { private static final String FILE = "./measurements.txt"; private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; static class OpenHashTable { static class Entry { // key always stored as multiple of 32 bytes byte[] key; byte keyLen; short min = Short.MAX_VALUE; short max = Short.MIN_VALUE; long sum = 0; long count = 0; void merge(Entry other) { min = (short) Math.min(min, other.min); max = (short) Math.max(max, other.max); sum += other.sum; count += other.count; } } static final int bits = 14; static final int tableSize = 1 << bits; // 16k static final int mask = tableSize - 1; final Entry[] entries = new Entry[tableSize]; void add(byte[] buf, int sLen, short val, int hash) { int idx = hash & mask; while (true) { Entry entry = entries[idx]; // key not present, so add it if (entry == null) { entry = entries[idx] = new Entry(); int rem = sLen % 32; int arrayLen = rem == 0 ? sLen : sLen + 32 - rem; entry.key = Arrays.copyOf(buf, arrayLen); Arrays.fill(entry.key, sLen, arrayLen, (byte) 0); entry.keyLen = (byte) sLen; entry.min = entry.max = val; entry.sum += val; entry.count++; break; } else { if (entry.keyLen == sLen && eq(buf, entry.key, entry.keyLen)) { entry.min = (short) Math.min(entry.min, val); entry.max = (short) Math.max(entry.max, val); entry.sum += val; entry.count++; break; } else { idx = (idx + 1) & mask; } } } } } static boolean eq(byte[] buf, byte[] entryKey, int sLen) { int needed = sLen; for (int offset = 0; offset <= 96; offset += 32) { var a = ByteVector.fromArray(ByteVector.SPECIES_256, buf, offset); var b = ByteVector.fromArray(ByteVector.SPECIES_256, entryKey, offset); int matches = a.eq(b).not().firstTrue(); if (needed <= 32) { return matches >= needed; } else if (matches < 32) { return false; } needed -= 32; } return false; } static long findNextEntryStart(MemorySegment ms, long offset) { long curr = offset; while (ms.get(ValueLayout.JAVA_BYTE, curr) != '\n') { curr++; } curr++; return curr; } static short[] digits2s = new short[256]; static short[] digits1s = new short[256]; static short[] digits0s = new short[256]; static { for (int i = 0; i < 10; ++i) { digits2s[i + ((int) '0')] = (short) (i * 100); digits1s[i + ((int) '0')] = (short) (i * 10); digits0s[i + ((int) '0')] = (short) i; } } static void processRangeScalar(MemorySegment ms, long start, long end, final OpenHashTable localAgg) { byte[] buf = new byte[128]; long curr = start; long limit = end; while (curr < limit) { int i = 0; byte val = ms.get(ValueLayout.JAVA_BYTE, curr); while (val != ';') { buf[i++] = val; curr++; val = ms.get(ValueLayout.JAVA_BYTE, curr); } int sLen = i; int hash = hash(buf, sLen); curr++; // skip semicolon long tempIdx = curr; boolean neg = ms.get(ValueLayout.JAVA_BYTE, tempIdx) == '-'; boolean twoDig = ms.get(ValueLayout.JAVA_BYTE, tempIdx + 1 + (neg ? 1 : 0)) == '.'; int len = 3 + (neg ? 1 : 0) + (twoDig ? 0 : 1); int d0 = ((char) ms.get(ValueLayout.JAVA_BYTE, tempIdx + len - 1)); int d1 = ((char) ms.get(ValueLayout.JAVA_BYTE, tempIdx + len - 3)); int d2 = ((char) ms.get(ValueLayout.JAVA_BYTE, tempIdx + len - 4)); // could be - or \n int base = digits0s[d0] + digits1s[d1] + digits2s[d2]; short temp = (short) (neg ? -base : base); localAgg.add(buf, sLen, temp, hash); curr = tempIdx + len + 1; } } static int hash(byte[] buf, int sLen) { int shift = Math.max(0, 8 - sLen) << 3; long mask = (~0L) >>> shift; long val = ((buf[7] & 0xffL) << 56) | ((buf[6] & 0xffL) << 48) | ((buf[5] & 0xffL) << 40) | ((buf[4] & 0xffL) << 32) | ((buf[3] & 0xffL) << 24) | ((buf[2] & 0xffL) << 16) | ((buf[1] & 0xFFL) << 8) | (buf[0] & 0xffL); val &= mask; // lemire: https://lemire.me/blog/2023/07/14/recognizing-string-prefixes-with-simd-instructions/ int hash = (int) (((((val >> 32) ^ val) & 0xffffffffL) * 3523216699L) >> 32); return hash; } static void processRangeSIMD(MemorySegment ms, boolean isFirst, boolean isLast, long start, long end, final OpenHashTable localAgg) { byte[] buf = new byte[128]; long curr = isFirst ? start : findNextEntryStart(ms, start); long limit = isLast ? end - padding : end; while (curr < limit) { int nl = 0; for (int offset = 0; offset < 128; offset += 32) { ByteVector section = ByteVector.fromMemorySegment(ByteVector.SPECIES_256, ms, curr + offset, ByteOrder.LITTLE_ENDIAN); section.intoArray(buf, offset); var idx = section.eq((byte) '\n').firstTrue(); if (idx != 32) { nl = offset + idx; break; } } int nl1 = buf[nl - 1]; int nl3 = buf[nl - 3]; int nl4 = buf[nl - 4]; int nl5 = buf[nl - 5]; int base = (nl1 - '0') + 10 * (nl3 - '0') + digits2s[nl4]; boolean neg = nl4 == '-' || (nl4 != ';' && nl5 == '-'); short temp = (short) (neg ? -base : base); int tempLen = 4 + (neg ? 1 : 0) + (base >= 100 ? 1 : 0); int semi = nl - tempLen; int hash = hash(buf, semi); localAgg.add(buf, semi, temp, hash); curr += (nl + 1); } // last batch is near end of file, process without SIMD to avoid out-of-bounds if (isLast) { processRangeScalar(ms, curr, end, localAgg); } } /** * Combine thread local values */ static HashMap mergeAggregations(ArrayList localAggs) { HashMap global = new HashMap<>(); for (var agg : localAggs) { for (OpenHashTable.Entry entry : agg.entries) { if (entry == null) { continue; } String station = new String(entry.key, 0, entry.keyLen, StandardCharsets.UTF_8); // for UTF-8 encoding var currentVal = global.get(station); if (currentVal != null) { currentVal.merge(entry); } else { global.put(station, entry); } } } return global; } static final int padding = 200; // max entry size is 107ish == 100 (station) + 1 (semicolon) + 5 (temp, eg -99.9) + 1 (newline) public static void main(String[] args) throws IOException, InterruptedException { RandomAccessFile file = new RandomAccessFile(FILE, "r"); FileChannel channel = file.getChannel(); int numThreads = Runtime.getRuntime().availableProcessors(); final long batchSize = 10_000_000; final long fileSize = channel.size(); // final long batchSize = fileSize / numThreads + 1; final MemorySegment ms = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); final ArrayList localAggs = new ArrayList<>(numThreads); Thread[] threads = new Thread[numThreads]; final AtomicLong progress = new AtomicLong(0); class Task implements Runnable { final int threadId; Task(int threadId) { this.threadId = threadId; } @Override public void run() { var localAgg = localAggs.get(threadId); while (true) { final long startBatch = progress.getAndAdd(batchSize); if (startBatch >= fileSize) { break; } final long endBatch = Math.min(startBatch + batchSize, fileSize); final boolean isFirstBatch = startBatch == 0; final boolean isLastBatch = endBatch == fileSize; processRangeSIMD(ms, isFirstBatch, isLastBatch, startBatch, endBatch, localAgg); } } } for (int t = 0; t < numThreads; t++) { localAggs.add(new OpenHashTable()); threads[t] = new Thread(new Task(t), "Thread-" + t); threads[t].start(); } for (var thread : threads) { thread.join(); } var globalAggs = mergeAggregations(localAggs); Map res = new TreeMap<>(); for (Map.Entry entry : globalAggs.entrySet()) { final var ma = entry.getValue(); res.put(entry.getKey(), new ResultRow(ma.min / 10.0, (ma.sum / 10.0) / ma.count, ma.max / 10.0)); } System.out.println(res); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_pedestrianlove.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.util.stream.Collectors.*; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentSkipListMap; import java.util.stream.Collector; public class CalculateAverage_pedestrianlove { private static final String FILE = "./measurements.txt"; private static record Measurement(String station, double value) { private Measurement(String[] parts) { this(parts[0], Double.parseDouble(parts[1])); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } public static void main(String[] args) throws IOException { Collector collector = Collector.of( MeasurementAggregator::new, (a, m) -> { a.min = Math.min(a.min, m.value); a.max = Math.max(a.max, m.value); a.sum += m.value; a.count++; }, (agg1, agg2) -> { var res = new MeasurementAggregator(); res.min = Math.min(agg1.min, agg2.min); res.max = Math.max(agg1.max, agg2.max); res.sum = agg1.sum + agg2.sum; res.count = agg1.count + agg2.count; return res; }, agg -> { return new ResultRow(agg.min, agg.sum / agg.count, agg.max); }); BufferedReader br = new BufferedReader(new FileReader(FILE)); Map measurements = new ConcurrentSkipListMap<>( br.lines().parallel() .map(l -> new Measurement(l.split(";"))) .collect(groupingBy(m -> m.station(), collector))); br.close(); System.out.println(measurements); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_phd3.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.util.stream.Collectors.*; import java.io.File; import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.IntStream; public class CalculateAverage_phd3 { private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors() * 2; private static final String FILE = "./measurements.txt"; private static final long FILE_SIZE = new File(FILE).length(); // A chunk is a unit for processing, the file will be divided in chunks of the following size private static final int CHUNK_SIZE = 65536 * 1024; // Read a little more data into the buffer to finish processing current line private static final int PADDING = 512; // Minor : Precompute powers to avoid recalculating while parsing doubles (temperatures) private static final double[] POWERS_OF_10 = IntStream.range(0, 6).mapToDouble(x -> Math.pow(10.0, x)).toArray(); /** * A Utility to print aggregated information in the desired format */ private record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; public static ResultRow resultRow(AggregationInfo aggregationInfo) { return new ResultRow(aggregationInfo.min, (Math.round(aggregationInfo.sum * 10.0) / 10.0) / (aggregationInfo.count), aggregationInfo.max); } public static void main(String[] args) throws Exception { long fileLength = new File(FILE).length(); int numChunks = (int) Math.ceil(fileLength * 1.0 / CHUNK_SIZE); ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS); BufferDataProvider provider = new RandomAccessBasedProvider(FILE, FILE_SIZE); List> futures = new ArrayList<>(); // Process chunks in parallel for (int chunkIndex = 0; chunkIndex < numChunks; chunkIndex++) { futures.add(executorService.submit(new Aggregator(chunkIndex, provider))); } executorService.shutdown(); executorService.awaitTermination(10, TimeUnit.MINUTES); Map info = futures.stream().map(f -> { try { return f.get(); } catch (ExecutionException | InterruptedException e) { throw new RuntimeException(e); } }) .map(LinearProbingHashMap::toMap) .flatMap(map -> map.entrySet().stream()) .sequential() .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, AggregationInfo::update)); Map measurements = new TreeMap<>(info.entrySet().stream() .collect(toMap(Map.Entry::getKey, e -> resultRow(e.getValue())))); System.out.println(measurements); } /** * Stores required running aggregation information to be able to compute min/max/average at the end */ private static class AggregationInfo { double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; double sum; long count; public AggregationInfo update(AggregationInfo update) { this.count += update.count; this.sum += update.sum; if (this.max < update.max) { this.max = update.max; } if (this.min > update.min) { this.min = update.min; } return this; } public AggregationInfo update(double value) { this.count++; this.sum += value; if (this.max < value) { this.max = value; } if (this.min > value) { this.min = value; } return this; } } private interface BufferDataProvider { int read(byte[] buffer, long offset) throws Exception; } /** * uses RandomAccessFile seek and read APIs to load data into a buffer. */ private static class RandomAccessBasedProvider implements BufferDataProvider { private final String filePath; RandomAccessBasedProvider(String filePath, long fileSize) { this.filePath = filePath; } @Override public int read(byte[] buffer, long offset) throws Exception { RandomAccessFile file = null; try { file = new RandomAccessFile(filePath, "r"); file.seek(offset); return file.read(buffer); } finally { if (file != null) { file.close(); } } } } /** * Task to processes a chunk of file and return a custom linear probing hashmap for performance */ private static class Aggregator implements Callable { private final long startByte; private final BufferDataProvider dataProvider; public Aggregator(long chunkIndex, BufferDataProvider dataProvider) { this.startByte = chunkIndex * CHUNK_SIZE; this.dataProvider = dataProvider; } @Override public LinearProbingHashMap call() { try { // offset for the last byte to be processed (excluded) long endByte = Math.min(startByte + CHUNK_SIZE, FILE_SIZE); // read a little more than needed to cover next entry if needed long bufferSize = endByte - startByte + ((endByte == FILE_SIZE) ? 0 : PADDING); byte[] buffer = new byte[(int) bufferSize]; int bytes = dataProvider.read(buffer, startByte); // Partial aggregation in a hashmap return processBuffer(buffer, startByte == 0, endByte - startByte); } catch (Throwable e) { throw new RuntimeException(e); } } private static LinearProbingHashMap processBuffer(byte[] buffer, boolean isFileStart, long nextChunkStart) { int start = 0; // Move to the next entry after '\n'. Don't do this if we're at the start of // the file to avoid missing first entry. if (!isFileStart) { while (buffer[start] != '\n') { start++; } start += 1; } LinearProbingHashMap chunkLocalMap = new LinearProbingHashMap(); while (true) { LineInfo lineInfo = getNextLine(buffer, start); byte[] keyBytes = new byte[lineInfo.semicolonIndex - start]; System.arraycopy(buffer, start, keyBytes, 0, keyBytes.length); double value = parseDouble(buffer, lineInfo.semicolonIndex + 1, lineInfo.nextStart - 1); // Update aggregated value for the given key with the new line AggregationInfo info = chunkLocalMap.get(keyBytes, lineInfo.keyHash); info.update(value); if ((lineInfo.nextStart > nextChunkStart) || (lineInfo.nextStart >= buffer.length)) { // we are already at a point where the next line will be processed in the next chunk, // so the job is done here break; } start = lineInfo.nextStart(); } return chunkLocalMap; } /** * Converts bytes to double value without intermediate string conversion, faster than Double.parseDouble. */ private static double parseDouble(byte[] bytes, int offset, int end) { boolean negative = (bytes[offset] == '-'); int current = negative ? offset + 1 : offset; int preFloat = 0; while (current < end && bytes[current] != '.') { preFloat = (preFloat * 10) + (bytes[current++] - '0'); } current++; int postFloatStart = current; int postFloat = 0; while (current < end) { postFloat = (postFloat * 10) + (bytes[current++] - '0'); } return (preFloat + ((postFloat) / POWERS_OF_10[end - postFloatStart])) * (negative ? -1 : 1); } /** * Identifies indexes of the next ';' and '\n', which will be used to get entry key and value from line. Also * computes the hash value for the key while iterating. */ private static LineInfo getNextLine(byte[] buffer, int start) { // caller guarantees that the access is in bounds, so no index check int hash = 0; while (buffer[start] != ';') { start++; hash = hash * 31 + buffer[start]; } // The following is just to further reduce the probability of collisions hash = hash ^ (hash << 16); int semicolonIndex = start; // caller guarantees that the access is in bounds, so no index check while (buffer[start] != '\n') { start++; } return new LineInfo(semicolonIndex, start + 1, hash); } } private record LineInfo(int semicolonIndex, int nextStart, int keyHash) { } /** * A simple map with pre-configured fixed bucket count. With 2^13 buckets and current hash function, seeing 4 * collisions which is not too bad. Every bucket is implemented with a linked list. The map is NOT thread safe. */ private static class LinearProbingHashMap { private final static int BUCKET_COUNT = 8191; private final Node[] buckets; LinearProbingHashMap() { this.buckets = new Node[BUCKET_COUNT]; } /** * Given a key, returns the current value of AggregationInfo. If not present, creates a new empty node at the * front of the bucket */ public AggregationInfo get(byte[] key, int keyHash) { // find bucket index through bitwise AND, works for bucketCount = (2^p - 1) int bucketIndex = BUCKET_COUNT & keyHash; Node current = buckets[bucketIndex]; while (current != null) { if (Arrays.equals(current.entry.key(), key)) { return current.entry.aggregationInfo(); } current = current.next; } // Entry does not exist, so add a new node in the linked list AggregationInfo newInfo = new AggregationInfo(); KeyValuePair pair = new KeyValuePair(key, keyHash, newInfo); Node newNode = new Node(pair, buckets[bucketIndex]); buckets[bucketIndex] = newNode; return newNode.entry.aggregationInfo(); } /** * A helper to convert to Java's hash map to build the final aggregation after partial aggregations */ private Map toMap() { Map map = new HashMap<>(); for (Node bucket : buckets) { while (bucket != null) { map.put(new String(bucket.entry.key, StandardCharsets.UTF_8), bucket.entry.aggregationInfo()); bucket = bucket.next; } } return map; } } /** * Linked List node to implement a bucket of custom hash map */ private static class Node { KeyValuePair entry; Node next; public Node(KeyValuePair entry, Node next) { this.entry = entry; this.next = next; } } /** * a wrapper class to store information needed for storing a measurement information in the hashmap */ private record KeyValuePair(byte[] key, int keyHash, AggregationInfo aggregationInfo) { } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_plbpietrz.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.io.UncheckedIOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CalculateAverage_plbpietrz { private static final String FILE = "./measurements.txt"; private static final int READ_SIZE = 1024; private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static class TemperatureStats { double min = 999, max = -999d; double accumulated; int count; public void update(double temp) { this.min = Math.min(this.min, temp); this.max = Math.max(this.max, temp); this.accumulated += temp; this.count++; } } private record FilePart(long pos, long size) { } private static class WeatherStation { private int length; private int nameHash; private byte[] nameBytes; private String string; public WeatherStation() { nameBytes = new byte[128]; } public WeatherStation(WeatherStation station) { this.nameBytes = Arrays.copyOf(station.nameBytes, station.length); this.length = station.length; this.nameHash = station.nameHash; } @Override public int hashCode() { return nameHash; } @Override public boolean equals(Object o) { if (this == o) return true; if (o instanceof WeatherStation s) { return this.nameHash == s.nameHash && Arrays.equals(this.nameBytes, 0, this.length, s.nameBytes, 0, s.length); } return false; } @Override public String toString() { if (string == null) string = new String(nameBytes, 0, length, Charset.defaultCharset()); return string; } public void appendByte(byte b) { string = null; nameBytes[length++] = b; nameHash = nameHash * 31 + b; } public void clear() { this.length = 0; this.nameHash = 0; this.string = null; } } public static void main(String[] args) throws IOException { Path inputFilePath = Path.of(FILE); Map results; try (RandomAccessFile inputFile = new RandomAccessFile(inputFilePath.toFile(), "r")) { var parsedBuffers = partitionInput(inputFile) .stream() .parallel() .map(fp -> getMappedByteBuffer(fp, inputFile)) .map(CalculateAverage_plbpietrz::parseBuffer); results = parsedBuffers.flatMap(m -> m.entrySet().stream()) .collect( Collectors.groupingBy( Map.Entry::getKey, Collectors.reducing( new TemperatureStats(), Map.Entry::getValue, CalculateAverage_plbpietrz::mergeTemperatureStats))); try (PrintWriter pw = new PrintWriter(new BufferedOutputStream(System.out))) { formatResults(pw, results); } } } private static List partitionInput(RandomAccessFile inputFile) throws IOException { List fileParts = new ArrayList<>(); long fileLength = inputFile.length(); long blockSize = Math.min(fileLength, Math.max(READ_SIZE, fileLength / CPU_COUNT)); for (long start = 0, end; start < fileLength; start = end) { end = findMinBlockOffset(inputFile, start, blockSize); fileParts.add(new FilePart(start, end - start)); } return fileParts; } private static long findMinBlockOffset(RandomAccessFile file, long startPosition, long minBlockSize) throws IOException { long length = file.length(); if (startPosition + minBlockSize < length) { file.seek(startPosition + minBlockSize); while (file.readByte() != '\n') { } return file.getFilePointer(); } else { return length; } } private static MappedByteBuffer getMappedByteBuffer(FilePart fp, RandomAccessFile inputFile) { try { return inputFile.getChannel().map(FileChannel.MapMode.READ_ONLY, fp.pos, fp.size); } catch (IOException e) { throw new UncheckedIOException(e); } } private static Map parseBuffer(MappedByteBuffer buffer) { byte[] readLong = new byte[READ_SIZE]; byte[] temperature = new byte[32]; int temperatureLineLenght = 0; int limit = buffer.limit(); boolean readingName = true; Map temperatures = new HashMap<>(); WeatherStation station = new WeatherStation(); int bytesToRead = Math.min(READ_SIZE, limit - buffer.position()); while (bytesToRead > 0) { if (bytesToRead == READ_SIZE) { buffer.get(readLong); } else { for (int j = 0; j < bytesToRead; ++j) readLong[j] = buffer.get(); } for (int i = 0; i < bytesToRead; ++i) { byte aChar = readLong[i]; if (readingName) { if (aChar != ';') { if (aChar != '\n') { station.appendByte(aChar); } } else { readingName = false; } } else { if (aChar != '\n') { temperature[temperatureLineLenght++] = aChar; } else { double temp = parseTemperature(temperature, temperatureLineLenght); if (!temperatures.containsKey(station)) { temperatures.put(new WeatherStation(station), new TemperatureStats()); } TemperatureStats weatherStats = temperatures.get(station); weatherStats.update(temp); station.clear(); temperatureLineLenght = 0; readingName = true; } } } bytesToRead = Math.min(READ_SIZE, limit - buffer.position()); } return temperatures; } private static double parseTemperature(byte[] temperature, int temperatureSize) { double sign = 1; double manitssa = 0; double exponent = 1; for (int i = 0; i < temperatureSize; ++i) { byte c = temperature[i]; switch (c) { case '-': sign = -1; break; case '.': for (int j = i; j < temperatureSize - 1; ++j) exponent *= 0.1; break; default: manitssa = manitssa * 10 + (c - 48); } } return sign * manitssa * exponent; } private static TemperatureStats mergeTemperatureStats(TemperatureStats v1, TemperatureStats v2) { TemperatureStats acc = new TemperatureStats(); acc.min = Math.min(v1.min, v2.min); acc.max = Math.max(v1.max, v2.max); acc.accumulated = v1.accumulated + v2.accumulated; acc.count = v1.count + v2.count; return acc; } private static void formatResults(PrintWriter pw, Map resultsMap) { pw.print('{'); var results = new ArrayList<>(resultsMap.entrySet()); results.sort(Comparator.comparing(e -> e.getKey().toString())); var iterator = results.iterator(); while (iterator.hasNext()) { var entry = iterator.next(); TemperatureStats stats = entry.getValue(); pw.printf("%s=%.1f/%.1f/%.1f", entry.getKey(), stats.min, stats.accumulated / stats.count, stats.max); if ((iterator.hasNext())) pw.print(", "); } pw.println('}'); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_plevart.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Comparator; import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; public class CalculateAverage_plevart { private static final Path FILE = Path.of("measurements.txt"); private static final int MAX_CITY_LEN = 100; // 100 (city name) + 1 (;) + 5 (-99.9) + 1 (NL) private static final int MAX_LINE_LEN = MAX_CITY_LEN + 7; private static final int INITIAL_TABLE_CAPACITY = 8192; public static void main(String[] args) throws IOException { System.setProperty("jdk.incubator.vector.VECTOR_ACCESS_OOB_CHECK", "0"); try ( var channel = (FileChannel) Files.newByteChannel(FILE, StandardOpenOption.READ); var arena = Arena.ofShared()) { var segment = channel.map(FileChannel.MapMode.READ_ONLY, 0, Files.size(FILE), arena); int regions = Runtime.getRuntime().availableProcessors(); IntStream .range(0, regions) .parallel() .mapToObj(r -> calculateRegion(segment, regions, r)) .reduce(StatsTable::reduce) .ifPresent(System.out::println); } } private static StatsTable calculateRegion(MemorySegment segment, int regions, int r) { long start = (segment.byteSize() * r) / regions; long end = (segment.byteSize() * (r + 1)) / regions; if (r > 0) { start = skipPastNl(segment, start); } if (r + 1 < regions) { end = skipPastNl(segment, end); } return calculateAdjustedRegion(segment, start, end); } private static long skipPastNl(MemorySegment segment, long i) { int skipped = 0; while (skipped++ < MAX_LINE_LEN && segment.get(ValueLayout.JAVA_BYTE, i++) != '\n') { } if (skipped > MAX_LINE_LEN) { throw new IllegalArgumentException( "Encountered line that exceeds " + MAX_LINE_LEN + " bytes at offset: " + i); } return i; } private static StatsTable calculateAdjustedRegion(MemorySegment segment, long start, long end) { var stats = new StatsTable(segment, INITIAL_TABLE_CAPACITY); var species = ByteVector.SPECIES_PREFERRED; long cityStart = start, numberStart = 0; int cityLen = 0; for (long i = start, j = i; i < end; j = i) { long semiNlSet; if (end - i >= species.vectorByteSize()) { var vec = ByteVector.fromMemorySegment(species, segment, i, ByteOrder.nativeOrder()); semiNlSet = vec.compare(VectorOperators.EQ, (byte) ';') .or(vec.compare(VectorOperators.EQ, (byte) '\n')) .toLong(); i += species.vectorByteSize(); } else { // tail, smaller than speciesByteSize semiNlSet = 0; long mask = 1; while (i < end && mask != 0) { int c = segment.get(ValueLayout.JAVA_BYTE, i++); if (c == '\n' || c == ';') { semiNlSet |= mask; } mask <<= 1; } } for (int step = Long.numberOfTrailingZeros(semiNlSet); step < 64; semiNlSet >>>= (step + 1), step = Long.numberOfTrailingZeros(semiNlSet)) { j += step; if (numberStart == 0) { // semi cityLen = (int) (j - cityStart); numberStart = ++j; } else { // nl int numberLen = (int) (j - numberStart); stats.calculateEntry(cityStart, cityLen, numberStart, numberLen); cityStart = ++j; numberStart = 0; } } } return stats; } final static class StatsTable { private static final int LOAD_FACTOR = 16; // offsets of fields private static final int _lenHash = 0, _off = 1, _count = 2, _sum = 3, _min = 4, _max = 5; private final MemorySegment segment; private int pow2cap, loadedSize; private long[] table; StatsTable(MemorySegment segment, int capacity) { this.segment = Objects.requireNonNull(segment); int pow2cap = Integer.highestOneBit(capacity); if (pow2cap < capacity) { pow2cap <<= 1; } this.pow2cap = pow2cap; this.table = new long[idx(pow2cap)]; } private StatsTable(StatsTable st) { this.segment = st.segment; this.pow2cap = st.pow2cap; this.loadedSize = st.loadedSize; this.table = st.table; } private static int idx(int i) { return i << 3; } private static long lenHash(int len, int hash) { return ((long) len << 32) | ((long) hash & 0x00000000FFFFFFFFL); } private static int len(long lenHash) { return (int) (lenHash >>> 32); } private static int hash(long lenHash) { return (int) (lenHash & 0x00000000FFFFFFFFL); } private static final long[] LEN_LONG_MASK; private static final int[] LEN_INT_MASK; static { LEN_LONG_MASK = new long[Long.BYTES + 1]; for (int len = 0; len <= Long.BYTES; len++) { LEN_LONG_MASK[len] = len == 0 ? 0L : ValueLayout.JAVA_LONG_UNALIGNED.order() == ByteOrder.LITTLE_ENDIAN ? -1L >>> ((Long.BYTES - len) * Byte.SIZE) : -1L << ((Long.BYTES - len) * Byte.SIZE); } LEN_INT_MASK = new int[Integer.BYTES + 1]; for (int len = 0; len <= Integer.BYTES; len++) { LEN_INT_MASK[len] = len == 0 ? 0 : ValueLayout.JAVA_LONG_UNALIGNED.order() == ByteOrder.LITTLE_ENDIAN ? -1 >>> ((Integer.BYTES - len) * Byte.SIZE) : -1 << ((Integer.BYTES - len) * Byte.SIZE); } } void calculateEntry(long cityStart, int cityLen, long numberStart, int numberLen) { int hash = hash(cityStart, cityLen); int number = parseNumber(numberStart, numberLen); aggregate(cityStart, cityLen, hash, 1, number, number, number); } int parseNumber(long off, int len) { int c0 = segment.get(ValueLayout.JAVA_BYTE, off); int d0; int sign; if (c0 == '-') { off++; len--; d0 = segment.get(ValueLayout.JAVA_BYTE, off) - '0'; sign = -1; } else { d0 = c0 - '0'; sign = 1; } return sign * switch (len) { case 1 -> d0 * 10; // 9 case 2 -> { int d1 = segment.get(ValueLayout.JAVA_BYTE, off + 1) - '0'; yield d0 * 100 + d1 * 10; // 99 } case 3 -> { int d2 = segment.get(ValueLayout.JAVA_BYTE, off + 2) - '0'; yield d0 * 10 + d2; // 9.9 } case 4 -> { int d1 = segment.get(ValueLayout.JAVA_BYTE, off + 1) - '0'; int d3 = segment.get(ValueLayout.JAVA_BYTE, off + 3) - '0'; yield d0 * 100 + d1 * 10 + d3; // 99.9 } default -> throw new IllegalArgumentException( "Invalid number: " + new String(segment.asSlice(off, len).toArray(ValueLayout.JAVA_BYTE), StandardCharsets.UTF_8) ); }; } int hash(long off, int len) { if (len > Integer.BYTES) { int head = segment.get(ValueLayout.JAVA_INT_UNALIGNED, off); int tail = segment.get(ValueLayout.JAVA_INT_UNALIGNED, off + len - Integer.BYTES); return (head * 31) ^ tail; } else { // assert len >= 0 && len <= 4; // each city name starts at least 4 bytes before segment end // assert off + Integer.BYTES <= segment.byteSize(); return segment.get(ValueLayout.JAVA_INT_UNALIGNED, off) & LEN_INT_MASK[len]; } } private static boolean bothLessThan(long a, long b, long threshold) { return (a < threshold) && (b < threshold); } boolean equals(long off1, long off2, int len) { while (len >= Long.BYTES) { if (segment.get(ValueLayout.JAVA_LONG_UNALIGNED, off1) != segment.get(ValueLayout.JAVA_LONG_UNALIGNED, off2)) { return false; } off1 += Long.BYTES; off2 += Long.BYTES; len -= Long.BYTES; } // still enough memory to compare two longs, but masked? if (bothLessThan(off1, off2, segment.byteSize() - Long.BYTES + 1)) { long mask = LEN_LONG_MASK[len]; return (segment.get(ValueLayout.JAVA_LONG_UNALIGNED, off1) & mask) == (segment.get(ValueLayout.JAVA_LONG_UNALIGNED, off2) & mask); } else { return equalsAtBorder(off1, off2, len); } } private boolean equalsAtBorder(long off1, long off2, int len) { if (len > Integer.BYTES) { if (segment.get(ValueLayout.JAVA_INT_UNALIGNED, off1) != segment.get(ValueLayout.JAVA_INT_UNALIGNED, off2)) { return false; } len -= Integer.BYTES; off1 += Integer.BYTES; off2 += Integer.BYTES; } // assert len >= 0 && len <= 4; // each city name starts at least 4 bytes before segment end // assert Math.max(off1, off2) + Integer.BYTES <= segment.byteSize(); int mask = LEN_INT_MASK[len]; return (segment.get(ValueLayout.JAVA_INT_UNALIGNED, off1) & mask) == (segment.get(ValueLayout.JAVA_INT_UNALIGNED, off2) & mask); } void aggregate( // key long off, int len, int hash, // value long count, long sum, int min, int max) { long lenHash = lenHash(len, hash); int mask = pow2cap - 1; for (int i = hash & mask, probe = 0; probe < pow2cap; i = (i + 1) & mask, probe++) { int idx = idx(i); long lenHash_i = table[idx + _lenHash]; if (lenHash_i == 0) { table[idx + _lenHash] = lenHash; table[idx + _off] = off; table[idx + _count] = count; table[idx + _sum] = sum; table[idx + _min] = min; table[idx + _max] = max; loadedSize += LOAD_FACTOR; if (loadedSize >= pow2cap) { grow(); } return; } if (lenHash_i == lenHash && equals(off, table[idx + _off], len)) { table[idx + _count] += count; table[idx + _sum] += sum; table[idx + _min] = Math.min(min, (int) table[idx + _min]); table[idx + _max] = Math.max(max, (int) table[idx + _max]); return; } } throw new OutOfMemoryError("StatsTable capacity exceeded due to poor hash"); } private void grow() { if (idx(pow2cap) >= 0x4000_0000) { throw new OutOfMemoryError("StatsTable capacity exceeded"); } else { var oldStats = new StatsTable(this); pow2cap <<= 1; table = new long[idx(pow2cap)]; loadedSize = 0; reduce(oldStats); } } StatsTable reduce(StatsTable other) { other .idxStream() .forEach( idx -> aggregate( other.table[idx + _off], len(other.table[idx + _lenHash]), hash(other.table[idx + _lenHash]), other.table[idx + _count], other.table[idx + _sum], (int) other.table[idx + _min], (int) other.table[idx + _max])); return this; } IntStream idxStream() { return IntStream .range(0, pow2cap) .map(StatsTable::idx) .filter(idx -> table[idx + _lenHash] != 0); } Stream stream() { return idxStream() .mapToObj( idx -> new Entry( new String( segment .asSlice(table[idx + _off], len(table[idx + _lenHash])) .toArray(ValueLayout.JAVA_BYTE), StandardCharsets.UTF_8), table[idx + _count], table[idx + _sum], table[idx + _min], table[idx + _max])); } @Override public String toString() { return stream() .sorted(Comparator.comparing(StatsTable.Entry::city)) .map(Entry::toString) .collect(Collectors.joining(", ", "{", "}")); } record Entry(String city, long count, long sum, long min, long max) { double average() { return count > 0L ? (double) sum / (double) count : 0d; } @Override public String toString() { return String.format( "%s=%.1f/%.1f/%.1f", city(), (double) min() / 10d, average() / 10d, (double) max() / 10d ); } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_raipc.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Comparator; import java.util.concurrent.RecursiveTask; public class CalculateAverage_raipc { private static final String FILE = "./measurements.txt"; private static final int BUFFER_SIZE = 16 * 1024; private static final class AggregatedMeasurement { final ByteArrayWrapper station; private String stationStringCached; private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private int count; private long sum; private AggregatedMeasurement(ByteArrayWrapper station) { this.station = station; } public String station() { String s = this.stationStringCached; if (s == null) { this.stationStringCached = s = station.toString(); } return s; } public AggregatedMeasurement merge(AggregatedMeasurement other) { this.min = Math.min(this.min, other.min); this.max = Math.max(this.max, other.max); this.sum += other.sum; this.count += other.count; return this; } public void add(int value) { this.min = Math.min(this.min, value); this.max = Math.max(this.max, value); this.sum += value; this.count++; } public void format(StringBuilder out) { out.append(station()).append('=') .append(min / 10.0).append('/') .append(Math.round(1.0 * sum / count) / 10.0).append('/') .append(max / 10.0); } } public static void main(String[] args) throws InterruptedException { File inputFile = new File(FILE); ParsingTask parsingTask = new ParsingTask(inputFile, 0, inputFile.length()); AggregatedMeasurement[] results = parsingTask.fork().join().toArray(); System.out.println(formatResult(results)); } private static String formatResult(AggregatedMeasurement[] result) { Arrays.sort(result, Comparator.comparing(AggregatedMeasurement::station)); StringBuilder out = new StringBuilder().append('{'); for (AggregatedMeasurement item : result) { item.format(out); out.append(", "); } if (out.length() > 2) { out.setLength(out.length() - 2); } return out.append('}').toString(); } private static class ParsingTask extends RecursiveTask { private static final int SPLIT_FACTOR = 4; private static final int MIN_TASK_SIZE = 256 * 1024; private final File file; private final long startPosition; private final long endPosition; private ParsingTask(File file, long startPosition, long endPosition) { this.file = file; this.startPosition = startPosition; this.endPosition = endPosition; } @Override protected MyHashMap compute() { long size = endPosition - startPosition; if (size <= MIN_TASK_SIZE || size < file.length() / Runtime.getRuntime().availableProcessors() / SPLIT_FACTOR) { return doCompute(); } var firstHalf = new ParsingTask(file, startPosition, (startPosition + endPosition) / 2).fork(); var secondHalf = new ParsingTask(file, (startPosition + endPosition) / 2, endPosition).fork(); var firstHalfResults = firstHalf.join(); var secondHalfResults = secondHalf.join(); firstHalfResults.merge(secondHalfResults); return firstHalfResults; } private MyHashMap doCompute() { try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { return new ParsingRoutine(startPosition, endPosition).parse(raf); } catch (IOException e) { throw new UncheckedIOException(e); } } } private static class ParsingRoutine { private final MyHashMap result = new MyHashMap(2048); private final byte[] partialContentBuff = new byte[128]; private final ByteArrayWrapper reusableWrapper = new ByteArrayWrapper(partialContentBuff, 0, 0, 0); private int partialSize; private final long startPosition; private final long endPosition; private ParsingRoutine(long startPosition, long endPosition) { this.startPosition = startPosition; this.endPosition = endPosition; } private boolean hasPartialContent() { return partialSize > 0; } MyHashMap parse(RandomAccessFile raf) throws IOException { byte[] buffer = new byte[BUFFER_SIZE]; long offset = findStartOffset(raf, buffer); boolean readMore = offset <= endPosition; while (readMore) { raf.seek(offset); int length = raf.read(buffer); if (length == -1) { break; } int bufStart = 0; if (hasPartialContent()) { int idxOfLf = indexOf(buffer, '\n', 0, length); if (idxOfLf >= 0) { bufStart = idxOfLf + 1; System.arraycopy(buffer, 0, partialContentBuff, partialSize, bufStart); int toProcess = partialSize + bufStart; partialSize = 0; processPart(offset - toProcess, partialContentBuff, 0, toProcess); } else { System.arraycopy(buffer, 0, partialContentBuff, partialSize, bufStart); offset += length; continue; } } readMore = processPart(offset, buffer, bufStart, length); offset += length; } return result; } long findStartOffset(RandomAccessFile raf, byte[] buffer) throws IOException { if (startPosition == 0) { return 0; } long offset = startPosition - 1; int length = 0; int idxOfLf = -1; while (offset < endPosition) { raf.seek(offset); length = raf.read(buffer); if (length == -1) { throw new IllegalStateException("No content read on position " + offset); } offset += length; idxOfLf = indexOf(buffer, '\n', 0, length); if (idxOfLf >= 0) { break; } } if (offset > startPosition) { int start = idxOfLf + 1; processPart(offset + start - length, buffer, start, length); } return offset; } boolean processPart(long position, byte[] buf, int start, int end) { ByteArrayWrapper key = reusableWrapper; key.content = buf; while (position <= endPosition) { int idxOfSemicolon = indexOf(buf, ';', start, end); if (idxOfSemicolon >= 0) { int idxOfLf = indexOf(buf, '\n', idxOfSemicolon + 2, Math.max(idxOfSemicolon + 2, end)); if (idxOfLf >= 0) { key.start = start; key.length = idxOfSemicolon - start; var aggregatedMeasurement = result.getOrCreate(key); aggregatedMeasurement.add(parseInt(buf, idxOfSemicolon + 1, idxOfLf - 1)); int prevStart = start; start = idxOfLf + 1; position += start - prevStart; continue; } } partialSize = end - start; if (partialSize > 0) { System.arraycopy(buf, start, partialContentBuff, 0, partialSize); } break; } return hasPartialContent() || position < endPosition; } private int parseInt(byte[] buf, int from, int toIncl) { int mul = 1; if (buf[from] == '-') { mul = -1; ++from; } int res = buf[toIncl] - '0'; int dec = 10; for (int i = toIncl - 2; i >= from; --i) { res += (buf[i] - '0') * dec; dec *= 10; } return mul * res; } } private static final MethodHandle indexOfMH; private static final MethodHandle vectorizedHashCodeMH; private static final MethodHandle mismatchMH; static { try { Class stringLatin1 = Class.forName("java.lang.StringLatin1"); var lookup = MethodHandles.privateLookupIn(stringLatin1, MethodHandles.lookup()); // int indexOf(byte[] value, int ch, int fromIndex, int toIndex) indexOfMH = lookup.findStatic(stringLatin1, "indexOf", MethodType.methodType(int.class, byte[].class, int.class, int.class, int.class)); Class arraysSupport = Class.forName("jdk.internal.util.ArraysSupport"); lookup = MethodHandles.privateLookupIn(arraysSupport, MethodHandles.lookup()); // int vectorizedHashCode(Object array, int fromIndex, int length, int initialValue, int basicType) vectorizedHashCodeMH = lookup.findStatic(arraysSupport, "vectorizedHashCode", MethodType.methodType(int.class, Object.class, int.class, int.class, int.class, int.class)); lookup = MethodHandles.privateLookupIn(arraysSupport, MethodHandles.lookup()); // int mismatch(byte[] a, int aFromIndex, byte[] b, int bFromIndex, int length) mismatchMH = lookup.findStatic(arraysSupport, "mismatch", MethodType.methodType(int.class, byte[].class, int.class, byte[].class, int.class, int.class)); } catch (Exception e) { throw new Error(e); } } static int indexOf(byte[] src, int ch, int fromIndex, int toIndex) { try { return (int) indexOfMH.invoke(src, ch, fromIndex, toIndex); } catch (Throwable e) { throw new Error(e); } } static int vectorizedHashCode(byte[] array, int fromIndex, int length) { try { return (int) vectorizedHashCodeMH.invoke((Object) array, fromIndex, length, 0, 8); } catch (Throwable e) { throw new Error(e); } } static boolean arraysEqual(byte[] a, int aFromIndex, byte[] b, int bFromIndex, int length) { try { return ((int) mismatchMH.invoke(a, aFromIndex, b, bFromIndex, length)) < 0; } catch (Throwable e) { throw new Error(e); } } private static class ByteArrayWrapper { private byte[] content; private int start; private int length; int hashCodeCached; ByteArrayWrapper(byte[] content, int start, int length, int hashCodeCached) { this.content = content; this.start = start; this.length = length; this.hashCodeCached = hashCodeCached; } int calculateHashCode() { return this.hashCodeCached = vectorizedHashCode(content, start, length); } @Override public boolean equals(Object obj) { return obj instanceof ByteArrayWrapper bw && isEqualTo(bw); } boolean isEqualTo(ByteArrayWrapper other) { return length == other.length && arraysEqual(content, start, other.content, other.start, length); } @Override public int hashCode() { return hashCodeCached; } @Override public String toString() { return new String(content, start, length, StandardCharsets.UTF_8); } ByteArrayWrapper copy() { byte[] copy = Arrays.copyOfRange(content, start, start + length); return new ByteArrayWrapper(copy, 0, copy.length, hashCodeCached); } } private static class MyHashMap { private static final int INVERSE_LOAD_FACTOR = 2; private AggregatedMeasurement[] data; private int size; MyHashMap(int initialCapacity) { this.data = new AggregatedMeasurement[initialCapacity]; } private void grow() { AggregatedMeasurement[] oldData = data; AggregatedMeasurement[] newData = new AggregatedMeasurement[oldData.length * 2]; for (AggregatedMeasurement measurement : oldData) { if (measurement != null) { put(measurement, newData); } } this.data = newData; } private void put(AggregatedMeasurement value, AggregatedMeasurement[] data) { int length = data.length; int hashCode = value.hashCode(); int idx = length & (hashCode ^ (hashCode >> 16)); for (int i = idx; i < length; ++i) { if (data[i] == null) { data[i] = value; return; } } for (int i = 0; i < idx; ++i) { if (data[i] == null) { data[i] = value; return; } } } AggregatedMeasurement getOrCreate(ByteArrayWrapper key) { AggregatedMeasurement[] data = this.data; int length = data.length; int hashCode = key.calculateHashCode(); int idx = (length - 1) & (hashCode ^ (hashCode >> 16)); AggregatedMeasurement result = doGetOrCreate(key, idx, length, data); return result != null ? result : doGetOrCreate(key, 0, idx, data); } private AggregatedMeasurement doGetOrCreate(ByteArrayWrapper key, int from, int to, AggregatedMeasurement[] data) { for (int i = from; i < to; ++i) { AggregatedMeasurement item = data[i]; if (item != null) { if (item.station.isEqualTo(key)) { return item; } } else { AggregatedMeasurement result = new AggregatedMeasurement(key.copy()); ++size; if (size * INVERSE_LOAD_FACTOR >= data.length) { grow(); put(result, this.data); } else { data[i] = result; } return result; } } return null; } void merge(MyHashMap other) { for (AggregatedMeasurement measurement : other.data) { if (measurement != null) { merge(measurement); } } } private boolean merge(AggregatedMeasurement value) { AggregatedMeasurement[] data = this.data; int length = data.length; ByteArrayWrapper key = value.station; int hashCode = key.hashCode(); int idx = (length - 1) & (hashCode ^ (hashCode >> 16)); return doMerge(key, value, idx, length, data) || doMerge(key, value, 0, idx, data); } private boolean doMerge(ByteArrayWrapper key, AggregatedMeasurement value, int from, int to, AggregatedMeasurement[] data) { for (int i = from; i < to; ++i) { AggregatedMeasurement item = data[i]; if (item != null) { if (item.station.isEqualTo(key)) { item.merge(value); return true; } } else { ++size; if (size * INVERSE_LOAD_FACTOR >= data.length) { grow(); put(value, this.data); } else { data[i] = value; } return true; } } return false; } AggregatedMeasurement[] toArray() { AggregatedMeasurement[] result = new AggregatedMeasurement[size]; int i = 0; for (AggregatedMeasurement measurement : data) { if (measurement != null) { result[i++] = measurement; } } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_rby.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.nio.*; import java.nio.channels.*; import java.nio.file.*; import java.util.*; import java.util.stream.*; public class CalculateAverage_rby { private static final String FILE = "./measurements.txt"; // private static final int CHUNK_SIZE = 8 * 1024 * 1024; private static final int CHUNK_SIZE = 32 << 20; /** * Computes good enough partitions which end on a newline */ static long[] cuts(Path p, int workers) throws IOException { var channel = (FileChannel) Files.newByteChannel(p, EnumSet.of(StandardOpenOption.READ)); final long size = channel.size(); if (size < 10000l) { return new long[]{ 0l, size }; } long chunk = size / workers; long position = size - chunk; long[] cuts = new long[workers + 1]; cuts[workers] = size; // 1024 should cover enough to catch a newline var buf = ByteBuffer.allocateDirect(1024); byte[] bytes = new byte[1024]; while (workers-- > 0) { var read = channel.read(buf, position); buf.flip(); buf.get(bytes, 0, read); var nextNL = position; while (read-- > 0) { if (bytes[read] == '\n') { nextNL += read; cuts[workers] = nextNL; break; } } position -= chunk; buf.rewind(); } cuts[0] = 0L; return cuts; } public static void main(String[] args) throws IOException { var p = Paths.get(FILE); var cpus = Runtime.getRuntime().availableProcessors(); final long[] cuts = cuts(p, cpus); var stats = IntStream.range(0, cuts.length - 1) .parallel() .mapToObj((i) -> stats(p, cuts[i], cuts[i + 1])) .reduce(Stats.IDENTITY, Stats::combine); stats.print(); } static record Stats(Map indexes, int nextIx, int[] stats) { private final static Stats IDENTITY = new Stats(new HashMap(), 0, new int[0]); // not much optimization needed here Stats combine(Stats other) { if (this == IDENTITY) return other; if (other == IDENTITY) return this; var myNextIx = nextIx; for(var e : other.indexes.entrySet()) { int ix; var ixi = indexes.get(e.getKey()); if ( ixi == null) { ix = myNextIx++ * 4; } else { ix = ixi.intValue() * 4; } var oix = e.getValue() * 4; stats[ix] = Math.min(stats[ix], other.stats[oix]); stats[ix + 1] = Math.max(stats[ix + 1], other.stats[oix + 1]); stats[ix + 2] += other.stats[oix + 2]; stats[ix + 3] += other.stats[oix + 3]; } return new Stats(indexes, myNextIx, stats); } // or here void print() { var iter = new TreeMap<>(indexes).entrySet().iterator(); System.out.print("{"); if (iter.hasNext()) { var e = iter.next(); var ix = e.getValue().intValue() * 4; var avg = Math.round(stats[ix + 2]/((double)stats[ix+3]))/10.0; System.out.print(e.getKey() + "=" + (stats[ix]/10.0) + "/" + avg + "/" + (stats[ix + 1]/10.0)); } while(iter.hasNext()) { var e = iter.next(); var ix = e.getValue().intValue() * 4; var avg = Math.round(stats[ix + 2]/((double)stats[ix+3]))/10.0; System.out.print(", " + e.getKey() + "=" + (stats[ix]/10.0) + "/" + avg + "/" + (stats[ix + 1]/10.0)) ; } System.out.println("}"); } } static final int MAX_CITIES = 1000; static final int ARRAY_SIZE = 1 << 20; static Stats stats(Path p, long start, long end) { int nextCityIx = 0; var cityIndexes = new HashMap(MAX_CITIES, 1.0f); int[] stats = new int[MAX_CITIES * 4]; for (int i = 0; i < MAX_CITIES; i++) { stats[i * 4] = Integer.MAX_VALUE; stats[i * 4 + 1] = Integer.MIN_VALUE; } try { final var channel = (FileChannel) Files.newByteChannel(p, EnumSet.of(StandardOpenOption.READ)); channel.position(start); var offset = start; final byte[] array = new byte[ARRAY_SIZE]; // the next expected char, the most simple stateMachine char nextChar = ';'; // good enough for a city name, or a double byte[] strbuff = new byte[128]; int strbuffIx = 0; int cityIndex = 0; final var buffer = ByteBuffer.allocateDirect(CHUNK_SIZE); while (offset < end) { final int limit = channel.read(buffer); if (limit <= 0) break; offset += limit; int totalRead = 0; buffer.flip(); while (totalRead < limit) { int read = Math.min(array.length, limit - totalRead); buffer.get(array, 0, read); totalRead += read; for (int i = 0; i < read; i++) { if (nextChar == '\n' && array[i] == '.') continue; strbuff[strbuffIx++] = array[i]; if (array[i] == nextChar) { var str = new String(strbuff, 0, strbuffIx - 1, "utf8"); strbuffIx = 0; switch (nextChar) { case ';': nextChar = '\n'; var mbCityIx = cityIndexes.get(str); if (mbCityIx == null) { cityIndex = nextCityIx; cityIndexes.put(str, nextCityIx++); if (nextCityIx * 4 >= stats.length) { var newStats = Arrays.copyOf(stats, stats.length * 2); for (int j = stats.length; j < newStats.length; j += 4) { newStats[j] = Integer.MAX_VALUE; newStats[j + 1] = Integer.MIN_VALUE; } stats = newStats; } } else { cityIndex = mbCityIx.intValue(); } break; case '\n': nextChar = ';'; int temp = Integer.parseInt(str); var ix = cityIndex * 4; if (temp < stats[ix]) stats[ix] = temp; if (temp > stats[ix + 1]) stats[ix + 1] = temp; stats[ix + 2] += temp; stats[ix + 3]++; break; default: } } } } buffer.rewind(); } return new Stats(cityIndexes, nextCityIx, stats); } catch (IOException err) { return null; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_rcasteltrione.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.time.Duration; import java.time.Instant; import java.util.*; import static java.util.stream.Collectors.toMap; //baseline: 266s public class CalculateAverage_rcasteltrione { private static final String FILE = "./measurements.txt"; // private static final String FILE = "./backup/measurements.txt"; public static void main(String[] args) throws IOException, InterruptedException { Path path = Paths.get(FILE); Instant start = Instant.now(); var segList = FileSegment.forFile(path, Runtime.getRuntime().availableProcessors()); var results = new ByteArrayToMeasurementMap[segList.size()]; var threads = new Thread[segList.size()]; try (var channel = FileChannel.open(path, StandardOpenOption.READ)) { for (int i = 0; i < segList.size(); i++) { int finalI = i; FileSegment fileSegment = segList.get(finalI); var t = Thread.ofPlatform().start(() -> results[finalI] = processSegment(channel, fileSegment)); threads[i] = t; } for (Thread thread : threads) { thread.join(); } } Map aggregatedMap = Arrays.stream(results) .flatMap(m -> m.entries().stream()) .collect(toMap( ByteArrayToMeasurementMap.Entry::key, ByteArrayToMeasurementMap.Entry::value, Measurement::merge, TreeMap::new)); System.out.println(aggregatedMap); // System.out.println(Duration.between(start, Instant.now()).toMillis()); } private static ByteArrayToMeasurementMap processSegment(FileChannel channel, FileSegment seg) { try { MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_ONLY, seg.start(), seg.size()); byte b; var result = new ByteArrayToMeasurementMap(); var lineBuffer = new byte[1 << 13]; var segmentPosition = mbb.position(); var limit = mbb.limit(); var lastLineOffset = 0; while (segmentPosition < mbb.limit()) { int remaining = limit - segmentPosition; int chunk = Math.min(remaining, lineBuffer.length); mbb.get(segmentPosition, lineBuffer, 0, chunk); for (int i = chunk - 1; i >= 0; i--) { if (lineBuffer[i] == '\n') { lastLineOffset = i; break; } } for (int lineBufferOffset = 0; lineBufferOffset < lastLineOffset;) { int nameHash = 0; int nameLength = 0; int nameStart = lineBufferOffset; while ((b = lineBuffer[lineBufferOffset++]) != ';') { nameHash = 31 * nameHash + b; nameLength++; } int temp; int negative = 1; // var s = new String(Arrays.copyOfRange(lineBuffer, nameStart, lineOffset - 1), StandardCharsets.UTF_8); if (lineBuffer[lineBufferOffset] == '-') { lineBufferOffset++; negative = -1; } // Temperature value: non-null double between -99.9 (inclusive) and 99.9 (inclusive), always with one fractional digit if (lineBuffer[lineBufferOffset + 1] == '.') { temp = (lineBuffer[lineBufferOffset] - '0') * 10 + (lineBuffer[lineBufferOffset + 2] - '0'); lineBufferOffset += 3; } else { temp = (lineBuffer[lineBufferOffset] - '0') * 100 + (lineBuffer[lineBufferOffset + 1] - '0') * 10 + (lineBuffer[lineBufferOffset + 3] - '0'); lineBufferOffset += 4; } if (lineBuffer[lineBufferOffset] == '\r') { lineBufferOffset++; } lineBufferOffset++; temp *= negative; result.mergeOrCreate(lineBuffer, nameStart, nameLength, nameHash, temp); // segmentPosition += lineOffset; // i += lineoffset; } segmentPosition += lastLineOffset + 1; } return result; } catch (IOException e) { throw new RuntimeException(e); } } record FileSegment(long start, long size) { public static List forFile(Path file, int desiredSegmentsCount) throws IOException { try (var raf = new RandomAccessFile(file.toFile(), "r")) { var segments = new ArrayList(); var fileSize = raf.length(); if (fileSize < 1000000) { return Collections.singletonList(new FileSegment(0, fileSize)); } var segmentSize = fileSize / desiredSegmentsCount; for (int segmentIdx = 0; segmentIdx < desiredSegmentsCount; segmentIdx++) { var segStart = segmentIdx * segmentSize; var segEnd = (segmentIdx == desiredSegmentsCount - 1) ? fileSize : segStart + segmentSize; segStart = findSegmentBoundary(raf, segmentIdx, 0, segStart, segEnd); segEnd = findSegmentBoundary(raf, segmentIdx, desiredSegmentsCount - 1, segEnd, fileSize); var segSize = segEnd - segStart; segments.add(new FileSegment(segStart, segSize)); } return segments; } } private static long findSegmentBoundary(RandomAccessFile raf, int i, int skipForSegment, long location, long fileSize) throws IOException { if (i == skipForSegment) return location; raf.seek(location); while (location < fileSize) { location++; if (raf.read() == '\n') break; } return location; } } static class Measurement { int min, max, n; long sum; private Measurement(int min, int max, long sum, int n) { this.min = min; this.max = max; this.sum = sum; this.n = n; } public Measurement(int temp) { this(temp, temp, temp, 1); } final Measurement merge(Measurement other) { this.min = Math.min(other.min, this.min); this.max = Math.max(other.max, this.max); this.sum += other.sum; this.n += other.n; return this; } @Override public String toString() { return STR."\{round(min)}/\{round(((double) sum / n))}/\{round(max)}"; } double round(double v) { return Math.round(v) / 10.0; } } static class ByteArrayToMeasurementMap { public static final int DEFAULT_CAPACITY = 1024; public static final float LOAD_FACTOR = 0.75f; MeasurementSlot[] slots = new MeasurementSlot[DEFAULT_CAPACITY]; int threshold = (int) (DEFAULT_CAPACITY * LOAD_FACTOR); int size = 0; private record MeasurementSlot(int hash, byte[] key, String city, Measurement measurement) { } public final void mergeOrCreate(byte[] line, int nameStart, int nameLength, int hash, int temperature) { int hashMask = slots.length - 1; for (int idx = hash & hashMask;; idx = (idx + 1) & hashMask) { MeasurementSlot slot = slots[idx]; if (slot == null) { size++; if (size > threshold) { idx = resize(hash); } byte[] nameBuffer = new byte[nameLength]; System.arraycopy(line, nameStart, nameBuffer, 0, nameLength); slots[idx] = new MeasurementSlot( hash, nameBuffer, new String(nameBuffer, StandardCharsets.UTF_8), new Measurement(temperature)); return; } if (slot.hash == hash && arrayEquals(slot.key, line, nameStart, nameLength)) { Measurement value = slots[idx].measurement; value.min = Math.min(value.min, temperature); value.max = Math.max(value.max, temperature); value.sum += temperature; value.n++; return; } } } private int resize(int hash) { var oldSlots = slots; var newSlots = new MeasurementSlot[oldSlots.length << 1]; var mask = newSlots.length - 1; for (MeasurementSlot oldSlot : oldSlots) { if (oldSlot == null) { continue; } int idx = oldSlot.hash & mask; while (newSlots[idx] != null) { idx = (idx + 1) & mask; } newSlots[idx] = oldSlot; } slots = newSlots; threshold = (int) (newSlots.length * LOAD_FACTOR); int hashMask = slots.length - 1; int idx; for (idx = hash & hashMask; slots[idx] != null; idx = (idx + 1) & hashMask) { } return idx; } private boolean arrayEquals(byte[] storedKey, byte[] line, int nameStart, int nameLength) { if (storedKey.length != nameLength) { return false; } for (int i = 0; i < storedKey.length; i++) { if (storedKey[i] != line[nameStart + i]) { return false; } } return true; } private static int hashCode(int h) { h ^= (h >>> 20) ^ (h >>> 12); h ^= (h >>> 7) ^ (h >>> 4); h += h << 7; return h; } public final List entries() { var result = new ArrayList(slots.length); for (MeasurementSlot slot : slots) { if (slot != null) { result.add(new Entry(slot.city, slot.measurement)); } } return result; } public record Entry(String key, Measurement value) { } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_ricardopieper.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.*; /* If I had to submit something without looking what others did, I am fairly certain I would be under 500MB/s on my machine on the dataset created by ./create_measurement.sh. Maybe it's cheating? But I learned stuff. Credits: - @royvanrijn for the idea of using bit twiddling without java.incubator.vector - @royvanrijn again for the parse int only and /10 in the very end - @flippingbits for the idea of not calling getLong on mmaped file directly, get a big chunk into memory instead - Maybe on linux it's actually faster? - OpenAI / ChatGPT for the actual bit twiddling idea for finding a character, it couldn't explain it so I did my best (Turns out @royvanrijn had the same idea) Note: - If someone needs maybe a new trick: in the hashmap, if you read a station name as a long and it fits under 8 bytes, use it directly on equality comparisons. Saves a lot of looping and gives a nice ~5% boost for me. Todo: - The final aggregation of results is taking around 1 whole second to complete. Improve it. */ @SuppressWarnings("unchecked") public class CalculateAverage_ricardopieper { private static final String FILE = "./measurements.txt"; public static final class StationMeasurements { public long min; public long max; public long sum; public int count; public StationMeasurements( long min, long max, long sum, int count ) { this.min = min; this.max = max; this.sum = sum; this.count = count; } @Override public String toString() { var min = String.format("%.1f", (double) this.min / 10.0); var avg = String.format("%.1f", ((double) this.sum / (double) this.count) / 10.0); var max = String.format("%.1f", (double) this.max / 10.0); return STR."\{min}/\{avg}/\{max}"; } } public static record FileChunk(Path path, long start, int size) { } public static List splitFile(Path path, int numChunks) throws IOException { try (var fileChannel = FileChannel.open(path, StandardOpenOption.READ)) { var chunks = new ArrayList(); long size = fileChannel.size(); if (numChunks == 1 && size <= Integer.MAX_VALUE) { chunks.add(new FileChunk(path, 0, (int) size)); return chunks; } long approxSizePerChunk = size / numChunks; var buffer = ByteBuffer.allocate(1024); // in practice records are max 20 chars long long curOffset = 0; for (int i = 0; i < numChunks; i++) { long targetOffset = curOffset + approxSizePerChunk; // advance to the target offset fileChannel.position(targetOffset); // advance until we find a newline buffer.clear(); fileChannel.read(buffer); int extraAdvance = 0; while (buffer.get(extraAdvance) != '\n') { extraAdvance++; } // +1 because we want to include the \n in the current chunk var end = Math.min(curOffset + approxSizePerChunk + extraAdvance + 1, size); long sizeOfChunk = end - curOffset; chunks.add(new FileChunk(path, curOffset, (int) sizeOfChunk)); curOffset = end; // because we found the \n but we want to leave at the next character if (curOffset >= size) { break; } } return chunks; } } // Ignores the dots because we do a division in the end public static int longBytesToInt(long vector, int len) { byte b0 = (byte) (vector >> 64 - 8), b1 = (byte) (vector >> 64 - 16), b2 = (byte) (vector >> 64 - 24), b3 = (byte) (vector >> 64 - 32), b4 = (byte) (vector >> 64 - 40); // check for windows newline byte newlineByte = (byte) (vector >> (64 - (len * 8))); if (newlineByte == '\r') { len--; } // len can be 3 4 or 5 // in this case we get bytes 0 and 2 if (len == 3) { return (b0 - '0') * 10 + (b2 - '0'); } boolean isNegative = b0 == '-'; // a number like -1.0 if (len == 4 && isNegative) { return -((b1 - '0') * 10 + (b3 - '0')); } // a number like 99.1 if (len == 4) { return (b0 - '0') * 100 + (b1 - '0') * 10 + (b3 - '0'); } // a number like -99.1 if (len == 5 && isNegative) { return -((b1 - '0') * 100 + (b2 - '0') * 10 + (b4 - '0')); } throw new RuntimeException("Shouldn't reach here: non-negative number with >5 characters"); } public static void main(String[] args) throws IOException, InterruptedException { var debugMode = false; var path = Paths.get(FILE); var averageSizePerChunk = 16 * 1024 * 1024; var fileSize = Files.size(path); var numChunks = Math.max(1, fileSize / averageSizePerChunk); var chunks = splitFile(path, (int) numChunks); TreeMap result = null; if (debugMode) { for (var chunk : chunks) { result = processFileChunk(chunk).asTreeMap(); System.out.println("Processed chunk " + chunk.start); } } else { result = chunks.parallelStream() .map(chunk -> { try { return processFileChunk(chunk); } catch (IOException e) { throw new RuntimeException(e); } }) // .peek(x -> x.printStats()) .map(MeasureMap::asTreeMap) .reduce((map1, map2) -> { for (var kv : map1.entrySet()) { var mergedEntry = map2.get(kv.getKey()); if (mergedEntry == null) { map2.put(kv.getKey(), kv.getValue()); } else { mergedEntry.min = Math.min(mergedEntry.min, kv.getValue().min); mergedEntry.max = Math.max(mergedEntry.max, kv.getValue().max); mergedEntry.count += kv.getValue().count; mergedEntry.sum += kv.getValue().sum; } } return map2; }).get(); } System.out.println(result); } private static final int findFirstOccurrence(long bytes, long pattern) { // This masked value will have a 00000000 at the byte where we have the separator, // and crucially, only at those places. long masked = bytes ^ pattern; // Now those 00000000 can be turned into a 11111111 (underflow). Why? // It will become apparent later. long underflowed = masked - 0x0101010101010101L; long clearHighBits = underflowed & ~masked; /* * A lot happened in that line of code above. Suppose this comment starts before the line executes. * To understand this, it's best if you take some actual values during runtime and plug them onto * bitwisecmd.com and see these things happening. * * Now that we have that full 0xFF byte at the position of the separators (or not, * maybe we didn't find a separator. Don't worry it still works the same), we can do a trick * where we look for the highest bit on each byte being set and isolating them. Unfortunately * it's possible that our masked value (and underflowed value) still have some unrelated * bytes with the high bit still set. * * When we negate the masked value, those high bits become 0. The AND will make those high bits in * underflowed switch to 0, because the negated mask high bit is zero. Notice that this is specific to our * mask value. * * How about our 0xFF? the ~masked turns the 0x00 at the byte position into 0xFF, so the AND * still leaves them 0xFF. * * Well, what if our mask has a high 1 though? like 0xBB? And the input had a mix of high bits * set and unset? * * In that case, masked would be 0x000000 at the byte position and would keep its high bits everywhere else, * and every input byte with high MSB the XOR would turn into a 0. In the next operations * it forwards a 0 into the calculations, and it doesn't matter what is in the other side of the AND, * it will result in zero. * * What if our mask is 0x3B as always but we have some bytes with MSB 1? * The XORed value will contain the high bits, but this same XORed value will be negated * later, making its high bits 0. When they get ANDed, the high bit 0 in the negated value * will clear that 1. * * * If the search finds nothing, the final value of clearHighBits will be zero at every MSB on every byte. */ // this 0x808080 is just 0b100000000|100000000, it selects the highest bit of every byte. long highBitOfSeparator = clearHighBits & 0x8080808080808080L; // If nothing is found, highBitOfSeparator will be zero, and this call returns 64, /8 becomes 8 which means // we can advance 8 positions forward. return Long.numberOfLeadingZeros(highBitOfSeparator) / 8; } private static MeasureMap processFileChunk(FileChunk chunk) throws IOException { var map = new MeasureMap(); long newlineMask = 0x0A0A0A0A0A0A0A0AL; long separatorMask = 0x3B3B3B3B3B3B3B3BL; // the state machine goes like this: // 1 - looking for the next ; to get name int nameStart = 0, nameEnd = 0; int tempStart = 0, tempEnd = 0; // this +8 helps to remove a conditional check when calling getLong when there's less than 8 bytes remaining ByteBuffer byteBuffer = ByteBuffer.allocate(chunk.size + 8); try (var channel = new RandomAccessFile(chunk.path.toString(), "r")) { channel.seek(chunk.start); channel.read(byteBuffer.array(), 0, chunk.size); } byteBuffer.position(0); while (byteBuffer.hasRemaining()) { long nameVector = byteBuffer.getLong(); int idx = findFirstOccurrence(nameVector, separatorMask); boolean foundSeparator = idx != 8; nameEnd += idx; if (foundSeparator) { // skip the separator tempStart = nameEnd + 1; } else { continue; } byteBuffer.position(tempStart); var tempVector = byteBuffer.getLong(); // looking for temperature // this one is guaranteed to run in one go because // the temperature is max 5 characters, 6 with the newline, // and we start at the number already. int newlineIdx = findFirstOccurrence(tempVector, newlineMask); tempEnd = tempStart + newlineIdx; // var tempStr = new String(tempBytes, 0, tempEnd - tempStart); var temp = longBytesToInt(tempVector, tempEnd - tempStart);// Float.parseFloat(tempStr); var measurements = map.getOrAdd(byteBuffer.array(), nameVector, nameStart, nameEnd - nameStart); measurements.min = Math.min(measurements.min, temp); measurements.max = Math.max(measurements.max, temp); measurements.sum += temp; measurements.count += 1; // System.out.println(STR."\{name} -> \{tempStr}"); nameStart = tempEnd + 1; nameEnd = nameStart; byteBuffer.position(nameStart); } return map; } // I tried using a trie but it wasn't cache efficient enough. // THe problem with a HashMap is that it doesnt allow passing a byte and length, // I also tried using a wrapper object that, when added, I called a .consolidate() method // to actually copy the underlying buffer, but the profiler showed it allocated // way too much memory. Performance was kind of the same though... public static class MeasureMap { public record Entry(byte[] bytes, long nameVector, StationMeasurements measurements) { } // Useful for debugging public static byte[] longToBytes(long vector) { byte b0 = (byte) (vector >> 64 - 8), b1 = (byte) (vector >> 64 - 16), b2 = (byte) (vector >> 64 - 24), b3 = (byte) (vector >> 64 - 32), b4 = (byte) (vector >> 64 - 40), b5 = (byte) (vector >> 64 - 48), b6 = (byte) (vector >> 64 - 56), b7 = (byte) (vector); return new byte[]{ b0, b1, b2, b3, b4, b5, b6, b7 }; } ArrayList[] buckets; public MeasureMap() { buckets = new ArrayList[32 * 1024]; for (int i = 0; i < 32 * 1024; i++) { var bucket = new ArrayList(); buckets[i] = bucket; } } // I tried using a vectorized hashCode using ByteVector, // but I think this gets vectorized anyway because the performance is // either better or the same. public static int hashCode(byte[] array, int offset, int length) { int result = 1; for (int i = offset; i < offset + length; i++) { result = 31 * result + array[i]; } return result; } public boolean equals(byte[] a1, int a1offset, int a1len, byte[] a2) { if (a1len != a2.length) { return false; } // I tried all sorts of trickery here, // like comparing first and last before the loop. // It's useless. Maybe slower. Because when strings are the same length // it's a pretty high chance they are the same... for (int i = 0; i < a1len; i++) { if (a1[a1offset + i] != a2[i]) { return false; } } return true; } public StationMeasurements getOrAdd(byte[] bytes, long nameVector, int offset, int length) { long shift = (64 - (length * 8L)); long actualNameVector = nameVector >> shift << shift; var hash = hashCode(bytes, offset, length); // This is a modulo operation because: // buckets.length is a power of 2 (like 01000000, only 1 bit is set) // -1 makes all the bits right to that 1 become 1 (like 00111111) // the & will select only those 1s, and due to math, it is the remainder // this has also the benefit of not needing a positive hash int bucketIndex = (buckets.length - 1) & hash; var bucket = buckets[bucketIndex]; for (int i = 0; i < bucket.size(); i++) { var item = bucket.get(i); // <= 7 means we found the name and separator in one read so it's // guaranteed the actualNameVector holds all the data boolean found = length <= 7 ? (actualNameVector == item.nameVector) : equals(bytes, offset, length, item.bytes); if (found) { return item.measurements; } } // new item, consolidate var newItemKey = Arrays.copyOfRange(bytes, offset, offset + length); var measurements = new StationMeasurements( Integer.MAX_VALUE, Integer.MIN_VALUE, 0, 0); bucket.add(new Entry( newItemKey, actualNameVector, measurements)); return measurements; } // Convenience method to merge maps later public TreeMap asTreeMap() { var result = new TreeMap(); for (var bucket : this.buckets) { for (var item : bucket) { var str = new String(item.bytes); result.put(str, item.measurements); } } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_richardstartin.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Consumer; public class CalculateAverage_richardstartin { private static final String FILE = "./measurements.txt"; private record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } record Slot(byte[] key, int[] aggregates) { private static final int WIDTH = 8; private static int[] newAggregates(int stripes) { var aggregates = new int[stripes * WIDTH]; for (int i = 0; i < aggregates.length; i += WIDTH) { aggregates[i] = Integer.MAX_VALUE; aggregates[i + 1] = Integer.MIN_VALUE; } return aggregates; } Slot(byte[] key, int stripes) { this(key, newAggregates(stripes)); } void update(int stripe, int value) { int i = stripe * WIDTH; aggregates[i] = Math.min(value, aggregates[i]); aggregates[i + 1] = Math.max(value, aggregates[i + 1]); aggregates[i + 2] += value; aggregates[i + 3]++; } public ResultRow toResultRow() { int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; int sum = 0; int count = 0; for (int i = 0; i < aggregates.length; i += WIDTH) { min = Math.min(min, aggregates[i]); max = Math.max(max, aggregates[i + 1]); sum += aggregates[i + 2]; count += aggregates[i + 3]; } return new ResultRow(min * 0.1, 0.1 * sum / count, max * 0.1); } public String toKey() { return new String(key, StandardCharsets.UTF_8); } } /** Maps text to an integer encoding. Adapted from async-profiler. */ public static class Dictionary { private static final int ROW_BITS = 11; private static final int ROWS = (1 << ROW_BITS); private static final int TABLE_CAPACITY = ROWS; private final Table table = new Table(this, nextBaseIndex()); private static final AtomicIntegerFieldUpdater BASE_INDEX_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Dictionary.class, "baseIndex"); volatile int baseIndex; private void forEach(Table table, Consumer consumer) { for (var row : table.rows) { var slot = row.slot; if (slot != null) { consumer.accept(slot); } if (row.next != null) { forEach(row.next, consumer); } } } public void forEach(Consumer consumer) { forEach(this.table, consumer); } public Slot lookup(int hash, byte[] key, int length, int stripes) { Table table = this.table; while (true) { int rowIndex = Math.abs(hash) % ROWS; Row row = table.rows[rowIndex]; var storedSlot = row.slot; if (storedSlot == null) { Slot slot = new Slot(Arrays.copyOf(key, length), stripes); if (row.compareAndSet(null, slot)) { return slot; } else { storedSlot = row.slot; if (Arrays.equals(key, 0, length, storedSlot.key, 0, storedSlot.key.length)) { return storedSlot; } } } else if (Arrays.equals(key, 0, length, storedSlot.key, 0, storedSlot.key.length)) { return storedSlot; } table = row.getOrCreateNextTable(); hash = Integer.rotateRight(hash, ROW_BITS); } } private int nextBaseIndex() { return BASE_INDEX_UPDATER.addAndGet(this, TABLE_CAPACITY); } private static final class Row { private static final AtomicReferenceFieldUpdater NEXT_TABLE_UPDATER = AtomicReferenceFieldUpdater.newUpdater(Row.class, Table.class, "next"); private static final AtomicReferenceFieldUpdater SLOT_UPDATER = AtomicReferenceFieldUpdater.newUpdater(Row.class, Slot.class, "slot"); private volatile Slot slot = null; private final Dictionary dictionary; volatile Table next; private Row(Dictionary dictionary) { this.dictionary = dictionary; } public Table getOrCreateNextTable() { Table next = this.next; if (next == null) { Table newTable = new Table(dictionary, dictionary.nextBaseIndex()); if (NEXT_TABLE_UPDATER.compareAndSet(this, null, newTable)) { next = newTable; } else { next = this.next; } } return next; } public boolean compareAndSet(Slot expected, Slot newSlot) { return SLOT_UPDATER.compareAndSet(this, expected, newSlot); } } private static final class Table { final Row[] rows; final int baseIndex; private Table(Dictionary dictionary, int baseIndex) { this.baseIndex = baseIndex; this.rows = new Row[ROWS]; Arrays.setAll(rows, i -> new Row(dictionary)); } } } private static long compilePattern(long repeat) { return 0x101010101010101L * repeat; } private static long compilePattern(byte delimiter) { return compilePattern(delimiter & 0xFFL); } private static final long DELIMITER = compilePattern((byte) ';'); private static int findLastNewLine(ByteBuffer buffer) { return findLastNewLine(buffer, buffer.limit() - 1); } private static int findLastNewLine(ByteBuffer buffer, int offset) { for (int i = offset; i >= 0; i--) { if (buffer.get(i) == '\n') { return i; } } return 0; } private static int findIndexOf(ByteBuffer buffer, int limit, int offset, long pattern) { int i = offset; for (; i < limit - Long.BYTES + 1; i += Long.BYTES) { long word = buffer.getLong(i); long input = word ^ pattern; long tmp = (input & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL; tmp |= input | 0x7F7F7F7F7F7F7F7FL; if (tmp != -1L) { return i + (Long.numberOfTrailingZeros(~tmp) >>> 3); } } byte b = (byte) (pattern & 0xFF); for (; i < limit; i++) { if (buffer.get(i) == b) { return i; } } return buffer.limit(); } private static int hash(byte[] bytes, int limit) { int hash = 1; for (int i = 0; i < limit; i++) { hash += hash * 129 + bytes[i]; } return hash; } private static class AggregationTask extends RecursiveAction { private final Dictionary dictionary; private final List slices; private final int min; private final int max; private AggregationTask(Dictionary dictionary, List slices) { this(dictionary, slices, 0, slices.size() - 1); } private AggregationTask(Dictionary dictionary, List slices, int min, int max) { this.dictionary = dictionary; this.slices = slices; this.min = min; this.max = max; } private void computeSlice(int stripe) { var slice = slices.get(stripe); int end = slice.limit(); byte[] tmp = new byte[128]; for (int offset = 0; offset < end;) { int delimiter = findIndexOf(slice, end, offset, DELIMITER); int value = 0; int sign = 1; byte b; int i = delimiter + 1; while (i != end && (b = slice.get(i++)) != '\n') { if (b != '.') { if (b == '-') { sign = -1; } else { value = 10 * value + (b - '0'); } } } value *= sign; int length = delimiter - offset; slice.get(offset, tmp, 0, length); dictionary.lookup(hash(tmp, length), tmp, length, slices.size()).update(stripe, value); offset = i; } } @Override protected void compute() { if (min == max) { computeSlice(min); } else { int mid = (min + max) / 2; var low = new AggregationTask(dictionary, slices, min, mid); var high = new AggregationTask(dictionary, slices, mid + 1, max); var fork = high.fork(); low.compute(); fork.join(); } } } public static void main(String[] args) throws IOException { int maxChunkSize = 10 << 20; // 10MiB try (var raf = new RandomAccessFile(FILE, "r"); var channel = raf.getChannel()) { long size = channel.size(); // make as few mmap calls as possible subject to the 2GiB limit per buffer List rawBuffers = new ArrayList<>(); for (long offset = 0; offset < size - 1;) { long end = Math.min(Integer.MAX_VALUE, size - offset); ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, offset, end) .order(ByteOrder.LITTLE_ENDIAN); boolean lastSlice = end != Integer.MAX_VALUE; int limit = lastSlice ? (int) end : findLastNewLine(buffer); rawBuffers.add(buffer.limit(limit)); offset += limit; } // now slice them up for parallel processing var slices = new ArrayList(); for (ByteBuffer rawBuffer : rawBuffers) { for (int offset = 0; offset < rawBuffer.limit();) { int chunkSize = Math.min(rawBuffer.limit() - offset, maxChunkSize); int target = offset + chunkSize; int limit = target >= rawBuffer.limit() ? rawBuffer.limit() : findLastNewLine(rawBuffer, target); int adjustment = rawBuffer.get(offset) == '\n' ? 1 : 0; var slice = rawBuffer.slice(offset + adjustment, limit - offset - adjustment).order(ByteOrder.LITTLE_ENDIAN); slices.add(slice); offset = limit; } } try (var fjp = new ForkJoinPool(Runtime.getRuntime().availableProcessors())) { Dictionary dictionary = new Dictionary(); fjp.submit(new AggregationTask(dictionary, slices)).join(); var map = new TreeMap(); dictionary.forEach(slot -> map.put(slot.toKey(), slot.toResultRow())); System.out.println(map); } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_roman_r_m.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.File; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.util.TreeMap; import java.util.stream.IntStream; public class CalculateAverage_roman_r_m { private static final String FILE = "./measurements.txt"; private static Unsafe UNSAFE; private static long broadcast(byte b) { return 0x101010101010101L * b; } private static final long SEMICOLON_MASK = broadcast((byte) ';'); private static final long LINE_END_MASK = broadcast((byte) '\n'); private static final long DOT_MASK = broadcast((byte) '.'); private static final long ZEROES_MASK = broadcast((byte) '0'); // from netty /** * Applies a compiled pattern to given word. * Returns a word where each byte that matches the pattern has the highest bit set. */ private static long applyPattern(final long word, final long pattern) { long input = word ^ pattern; long tmp = (input & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL; return ~(tmp | input | 0x7F7F7F7F7F7F7F7FL); } static long nextNewline(long from, MemorySegment ms) { long start = from; long i; long next = ms.get(ValueLayout.JAVA_LONG_UNALIGNED, start); while ((i = applyPattern(next, LINE_END_MASK)) == 0) { start += 8; next = ms.get(ValueLayout.JAVA_LONG_UNALIGNED, start); } return start + Long.numberOfTrailingZeros(i) / 8; } static int hashFull(long word) { return (int) (word ^ (word >>> 32)); } static int hashPartial(long word, int bytes) { long h = Long.reverseBytes(word) >>> (8 * (8 - bytes)); return (int) (h ^ (h >>> 32)); } public static void main(String[] args) throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); UNSAFE = (Unsafe) f.get(null); long fileSize = new File(FILE).length(); var channel = FileChannel.open(Paths.get(FILE)); MemorySegment ms = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.ofConfined()); int numThreads = fileSize > Integer.MAX_VALUE ? Runtime.getRuntime().availableProcessors() : 1; long chunk = fileSize / numThreads; var bounds = IntStream.range(0, numThreads).mapToLong(i -> { boolean lastChunk = i == numThreads - 1; return lastChunk ? fileSize : nextNewline((i + 1) * chunk, ms); }).toArray(); ms.unload(); var result = IntStream.range(0, numThreads) .parallel() .mapToObj(i -> { try { long segmentStart = i == 0 ? 0 : bounds[i - 1] + 1; long segmentEnd = bounds[i]; var segment = channel.map(FileChannel.MapMode.READ_ONLY, segmentStart, segmentEnd - segmentStart, Arena.ofConfined()); var resultStore = new ResultStore(); var station = new ByteString(segment); long offset = segment.address(); long end = offset + segment.byteSize(); long tailMask; while (offset < end) { // parsing station name long start = offset; long next = UNSAFE.getLong(offset); long pattern = applyPattern(next, SEMICOLON_MASK); int bytes; if (pattern == 0) { station.hash = hashFull(next); do { offset += 8; next = UNSAFE.getLong(offset); pattern = applyPattern(next, SEMICOLON_MASK); } while (pattern == 0); bytes = Long.numberOfTrailingZeros(pattern) / 8; offset += bytes; tailMask = ((1L << (8 * bytes)) - 1); } else { bytes = Long.numberOfTrailingZeros(pattern) / 8; offset += bytes; tailMask = ((1L << (8 * bytes)) - 1); station.hash = hashPartial(next, bytes); } int len = (int) (offset - start); station.offset = start; station.len = len; station.tail = next & tailMask; offset++; // parsing temperature // TODO next may contain temperature as well, maybe try using it if we know the full number is there // 8 - bytes >= 5 -> bytes <= 3 long val; if (end - offset >= 8) { long encodedVal = UNSAFE.getLong(offset); int neg = 1 - Integer.bitCount((int) (encodedVal & 0x10)); encodedVal >>>= 8 * neg; long numLen = applyPattern(encodedVal, DOT_MASK); numLen = Long.numberOfTrailingZeros(numLen) / 8; encodedVal ^= ZEROES_MASK; int intPart = (int) (encodedVal & ((1 << (8 * numLen)) - 1)); intPart <<= 8 * (2 - numLen); intPart *= (100 * 256 + 10); intPart = (intPart & 0x3FF80) >>> 8; int frac = (int) ((encodedVal >>> (8 * (numLen + 1))) & 0xFF); offset += neg + numLen + 3; // 1 for . + 1 for fractional part + 1 for new line char int sign = 1 - 2 * neg; val = sign * (intPart + frac); } else { int neg = 1 - Integer.bitCount(UNSAFE.getByte(offset) & 0x10); offset += neg; val = UNSAFE.getByte(offset++) - '0'; byte b; while ((b = UNSAFE.getByte(offset++)) != '.') { val = val * 10 + (b - '0'); } b = UNSAFE.getByte(offset); val = val * 10 + (b - '0'); offset += 2; val *= 1 - (2L * neg); } resultStore.update(station, (int) val); } segment.unload(); return resultStore.toMap(); } catch (Exception e) { throw new RuntimeException(e); } }).reduce((m1, m2) -> { m2.forEach((k, v) -> m1.merge(k, v, ResultRow::merge)); return m1; }); System.out.println(result.get()); } static final class ByteString { private final MemorySegment ms; private long offset; private int len = 0; private int hash = 0; private long tail = 0L; ByteString(MemorySegment ms) { this.ms = ms; } public String asString(byte[] reusable) { UNSAFE.copyMemory(null, offset, reusable, Unsafe.ARRAY_BYTE_BASE_OFFSET, len); return new String(reusable, 0, len); } public ByteString copy() { var copy = new ByteString(ms); copy.offset = this.offset; copy.len = this.len; copy.hash = this.hash; copy.tail = this.tail; return copy; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ByteString that = (ByteString) o; if (len != that.len) return false; for (int i = 0; i + 7 < len; i += 8) { long l1 = UNSAFE.getLong(offset + i); long l2 = UNSAFE.getLong(that.offset + i); if (l1 != l2) { return false; } } return this.tail == that.tail; } @Override public int hashCode() { return hash; } @Override public String toString() { byte[] buf = new byte[100]; return asString(buf); } } private static final class ResultRow { long min; long sum; long max; int count; public ResultRow(int[] values) { min = values[0]; max = values[1]; sum = values[2]; count = values[3]; } public String toString() { return round(min / 10.0) + "/" + round(sum / 10.0 / count) + "/" + round(max / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } public ResultRow merge(ResultRow other) { this.min = Math.min(this.min, other.min); this.max = Math.max(this.max, other.max); this.sum += other.sum; this.count += other.count; return this; } } static class ResultStore { private static final int SIZE = 16384; private final ByteString[] keys = new ByteString[SIZE]; private final int[][] values = new int[SIZE][]; void update(ByteString s, int value) { int h = s.hashCode(); int idx = (SIZE - 1) & h; var keys = this.keys; int idx0 = idx; int i = 0; while (true) { if (keys[idx] != null && keys[idx].equals(s)) { values[idx][0] = Math.min(values[idx][0], value); values[idx][1] = Math.max(values[idx][1], value); values[idx][2] += value; values[idx][3] += 1; return; } else if (keys[idx] == null) { keys[idx] = s.copy(); values[idx] = new int[4]; values[idx][0] = value; values[idx][1] = value; values[idx][2] = value; values[idx][3] = 1; return; } else { i++; idx = (idx0 + i * i) % SIZE; } } } TreeMap toMap() { byte[] buf = new byte[100]; var result = new TreeMap(); for (int i = 0; i < SIZE; i++) { if (keys[i] != null) { result.put(keys[i].asString(buf), new ResultRow(values[i])); } } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_royvanrijn.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import sun.misc.Unsafe; /** * Changelog: * * Initial submission: 62000 ms * Chunked reader: 16000 ms * Optimized parser: 13000 ms * Branchless methods: 11000 ms * Adding memory mapped files: 6500 ms (based on bjhara's submission) * Skipping string creation: 4700 ms * Custom hashmap... 4200 ms * Added SWAR token checks: 3900 ms * Skipped String creation: 3500 ms (idea from kgonia) * Improved String skip: 3250 ms * Segmenting files: 3150 ms (based on spullara's code) * Not using SWAR for EOL: 2850 ms * Inlining hash calculation: 2450 ms * Replacing branchless code: 2200 ms (sometimes we need to kill the things we love) * Added unsafe memory access: 1900 ms (keeping the long[] small and local) * Fixed bug, UNSAFE bytes String: 1850 ms * Separate hash from entries: 1550 ms * Various tweaks for Linux/cache 1550 ms (should/could make a difference on target machine) * Improved layout/predictability: 1400 ms * Delayed String creation again: 1350 ms * Remove writing to buffer: 1335 ms * Optimized collecting at the end: 1310 ms * Adding a lot of comments: priceless * Changed to flyweight byte[]: 1290 ms (adds even more Unsafe, was initially slower, now faster) * More LOC now parallel: 1260 ms (moved more to processMemoryArea, recombining in ConcurrentHashMap) * Storing only the address: 1240 ms (this is now faster, tried before, was slower) * Unrolling scan-loop: 1200 ms (seems to help, perhaps even more on target machine) * Adding more readable reader: 1300 ms (scores got worse on target machine anyway) * * Using old x86 MacBook and perf: 3500 ms (different machine for testing) * Decided to rewrite loop for 16 b: 3050 ms * Small changes, limited heap: 2950 ms * * I have some instructions that could be removed, but faster with... * * Big thanks to Francesco Nigro, Thomas Wuerthinger, Quan Anh Mai and many others for ideas. * * Follow me at: @royvanrijn */ public class CalculateAverage_royvanrijn { private static final String FILE = "./measurements.txt"; // private static final String FILE = "src/test/resources/samples/measurements-1.txt"; private static final Unsafe UNSAFE = initUnsafe(); // Twice the processors, smoothens things out. private static final int PROCESSORS = Runtime.getRuntime().availableProcessors(); /** * Flyweight entry in a byte[], max 128 bytes. *

* long: sum * int: min * int: max * int: count * byte: length * byte[]: cityname */ // ------------------------------------------------------------------------ private static final int ENTRY_LENGTH = (Unsafe.ARRAY_BYTE_BASE_OFFSET); private static final int ENTRY_SUM = (ENTRY_LENGTH + Byte.BYTES); private static final int ENTRY_MIN = (ENTRY_SUM + Long.BYTES); private static final int ENTRY_MAX = (ENTRY_MIN + Integer.BYTES); private static final int ENTRY_COUNT = (ENTRY_MAX + Integer.BYTES); private static final int ENTRY_NAME = (ENTRY_COUNT + Integer.BYTES); private static final int ENTRY_NAME_8 = ENTRY_NAME + 8; private static final int ENTRY_NAME_16 = ENTRY_NAME + 16; private static final int ENTRY_BASESIZE_WHITESPACE = ENTRY_NAME + 7; // with enough empty bytes to fill a long // ------------------------------------------------------------------------ private static final int PREMADE_MAX_SIZE = 1 << 5; // pre-initialize some entries in memory, keep them close private static final int PREMADE_ENTRIES = 512; // amount of pre-created entries we should use private static final int TABLE_SIZE = 1 << 19; // large enough for the contest. private static final int TABLE_MASK = (TABLE_SIZE - 1); // Idea of thomaswue, don't wait for slow unmap: private static void spawnWorker() throws IOException { ProcessHandle.Info info = ProcessHandle.current().info(); ArrayList workerCommand = new ArrayList<>(); info.command().ifPresent(workerCommand::add); info.arguments().ifPresent(args -> workerCommand.addAll(Arrays.asList(args))); workerCommand.add("--worker"); new ProcessBuilder() .command(workerCommand) .inheritIO() .redirectOutput(ProcessBuilder.Redirect.PIPE) .start() .getInputStream() .transferTo(System.out); } public static void main(String[] args) throws Exception { if (args.length == 0 || !("--worker".equals(args[0]))) { spawnWorker(); return; } // Calculate input segments. final FileChannel fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); final long fileSize = fileChannel.size(); final long segmentSize = (fileSize + PROCESSORS - 1) / PROCESSORS; final long mapAddress = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()).address(); final Thread[] parallelThreads = new Thread[PROCESSORS - 1]; // This is where the entries will land: final ConcurrentHashMap measurements = new ConcurrentHashMap(1 << 10); // We create separate threads for twice the amount of processors. long lastAddress = mapAddress; final long endOfFile = mapAddress + fileSize; for (int i = 0; i < PROCESSORS - 1; ++i) { final long fromAddress = lastAddress; final long toAddress = Math.min(endOfFile, fromAddress + segmentSize); final Thread thread = new Thread(() -> { // The actual work is done here: final byte[][] table = processMemoryArea(fromAddress, toAddress, fromAddress == mapAddress); for (byte[] entry : table) { if (entry != null) { measurements.merge(entryToName(entry), entry, CalculateAverage_royvanrijn::mergeEntry); } } }); thread.start(); // start a.s.a.p. parallelThreads[i] = thread; lastAddress = toAddress; } // Use the current thread for the part of memory: final byte[][] table = processMemoryArea(lastAddress, mapAddress + fileSize, false); for (byte[] entry : table) { if (entry != null) { measurements.merge(entryToName(entry), entry, CalculateAverage_royvanrijn::mergeEntry); } } // Wait for all threads to finish: for (Thread thread : parallelThreads) { // Can we implement work-stealing? Not sure how... thread.join(); } // If we don't reach start of file, System.out.print("{" + measurements.entrySet().stream().sorted(Map.Entry.comparingByKey()) .map(entry -> entry.getKey() + '=' + entryValuesToString(entry.getValue())) .collect(Collectors.joining(", "))); System.out.println("}"); System.out.close(); // close the stream to stop } private static byte[] fillEntry(final byte[] entry, final long fromAddress, final int entryLength, final int temp, final long readBuffer1, final long readBuffer2) { UNSAFE.putLong(entry, ENTRY_SUM, temp); UNSAFE.putInt(entry, ENTRY_MIN, temp); UNSAFE.putInt(entry, ENTRY_MAX, temp); UNSAFE.putInt(entry, ENTRY_COUNT, 1); UNSAFE.putByte(entry, ENTRY_LENGTH, (byte) entryLength); UNSAFE.copyMemory(null, fromAddress, entry, ENTRY_NAME, entryLength - 16); UNSAFE.putLong(entry, ENTRY_NAME + entryLength - 16, readBuffer1); UNSAFE.putLong(entry, ENTRY_NAME + entryLength - 8, readBuffer2); return entry; } private static byte[] fillEntry16(final byte[] entry, final int entryLength, final int temp, final long readBuffer1, final long readBuffer2) { UNSAFE.putLong(entry, ENTRY_SUM, temp); UNSAFE.putInt(entry, ENTRY_MIN, temp); UNSAFE.putInt(entry, ENTRY_MAX, temp); UNSAFE.putInt(entry, ENTRY_COUNT, 1); UNSAFE.putByte(entry, ENTRY_LENGTH, (byte) entryLength); UNSAFE.putLong(entry, ENTRY_NAME + entryLength - 16, readBuffer1); UNSAFE.putLong(entry, ENTRY_NAME + entryLength - 8, readBuffer2); return entry; } public static void updateEntry(final byte[] entry, final int temp) { int entryMin = UNSAFE.getInt(entry, ENTRY_MIN); int entryMax = UNSAFE.getInt(entry, ENTRY_MAX); long entrySum = UNSAFE.getLong(entry, ENTRY_SUM) + temp; int entryCount = UNSAFE.getInt(entry, ENTRY_COUNT) + 1; if (temp < entryMin) { UNSAFE.putInt(entry, ENTRY_MIN, temp); } else if (temp > entryMax) { UNSAFE.putInt(entry, ENTRY_MAX, temp); } UNSAFE.putInt(entry, ENTRY_COUNT, entryCount); UNSAFE.putLong(entry, ENTRY_SUM, entrySum); } public static byte[] mergeEntry(final byte[] entry, final byte[] merge) { long sum = UNSAFE.getLong(merge, ENTRY_SUM); final int mergeMin = UNSAFE.getInt(merge, ENTRY_MIN); final int mergeMax = UNSAFE.getInt(merge, ENTRY_MAX); int count = UNSAFE.getInt(merge, ENTRY_COUNT); sum += UNSAFE.getLong(entry, ENTRY_SUM); count += UNSAFE.getInt(entry, ENTRY_COUNT); int entryMin = UNSAFE.getInt(entry, ENTRY_MIN); int entryMax = UNSAFE.getInt(entry, ENTRY_MAX); entryMin = Math.min(entryMin, mergeMin); entryMax = Math.max(entryMax, mergeMax); UNSAFE.putInt(entry, ENTRY_MIN, entryMin); UNSAFE.putInt(entry, ENTRY_MAX, entryMax); UNSAFE.putLong(entry, ENTRY_SUM, sum); UNSAFE.putInt(entry, ENTRY_COUNT, count); return entry; } private static String entryToName(final byte[] entry) { // Get the length from memory: int length = UNSAFE.getByte(entry, ENTRY_LENGTH); byte[] name = new byte[length]; UNSAFE.copyMemory(entry, ENTRY_NAME, name, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); // Create a new String with the existing byte[]: return new String(name, StandardCharsets.UTF_8).trim(); } private static String entryValuesToString(final byte[] entry) { return (round(UNSAFE.getInt(entry, ENTRY_MIN)) + "/" + round((1.0 * UNSAFE.getLong(entry, ENTRY_SUM)) / UNSAFE.getInt(entry, ENTRY_COUNT)) + "/" + round(UNSAFE.getInt(entry, ENTRY_MAX))); } // Print a piece of memory: // For debug. private static String printMemory(final Object target, final long address, int length) { String result = ""; for (int i = 0; i < length; i++) { result += (char) UNSAFE.getByte(target, address + i); } return result; } // Print a piece of memory: // For debug. private static String printMemory(final long value, int length) { String result = ""; for (int i = 0; i < length; i++) { result += (char) ((value >> (i << 3)) & 0xFF); } return result; } private static double round(final double value) { return Math.round(value) / 10.0; } private static final class Reader { private long ptr; private long readBuffer1; private long readBuffer2; private long hash; private long entryStart; private int entryLength; // in bytes rounded to nearest 16 private final long endAddress; Reader(final long startAddress, final long endAddress, final boolean isFileStart) { this.ptr = startAddress; this.endAddress = endAddress; // Adjust start to next delimiter: if (!isFileStart) { ptr--; while (ptr < endAddress) { if (UNSAFE.getByte(ptr++) == '\n') { break; } } } } private void processStart() { hash = 0; entryStart = ptr; entryLength = 0; } private boolean hasNext() { return (ptr < endAddress); } private static final long DELIMITER_MASK = 0x3B3B3B3B3B3B3B3BL; private boolean readNext() { long lastRead = UNSAFE.getLong(ptr); entryLength += 16; // Find delimiter and create mask for long1 long comparisonResult1 = (lastRead ^ DELIMITER_MASK); long highBitMask1 = (comparisonResult1 - 0x0101010101010101L) & (~comparisonResult1 & 0x8080808080808080L); boolean noContent1 = highBitMask1 == 0; long mask1 = noContent1 ? 0 : ~((highBitMask1 >>> 7) - 1); int position1 = noContent1 ? 0 : 1 + (Long.numberOfTrailingZeros(highBitMask1) >> 3); readBuffer1 = lastRead & ~mask1; hash ^= readBuffer1; int delimiter1 = position1 == 0 ? 0 : position1; // not nnecessary, but faster? if (delimiter1 != 0) { hash ^= hash >> 32; readBuffer2 = 0; ptr += delimiter1; return false; } lastRead = UNSAFE.getLong(ptr + 8); // Repeat for long2 long comparisonResult2 = (lastRead ^ DELIMITER_MASK); long highBitMask2 = (comparisonResult2 - 0x0101010101010101L) & (~comparisonResult2 & 0x8080808080808080L); boolean noContent2 = highBitMask2 == 0; long mask2 = noContent2 ? 0 : ~((highBitMask2 >>> 7) - 1); int position2 = noContent2 ? 0 : 1 + (Long.numberOfTrailingZeros(highBitMask2) >> 3); // Apply masks readBuffer2 = lastRead & ~mask2; hash ^= readBuffer2; int delimiter2 = position2 == 0 ? 0 : position2 + 8; // not necessary, but faster? hash ^= hash >> 32; if (delimiter2 != 0) { ptr += delimiter2; return false; } ptr += 16; return true; } private int processEndAndGetTemperature() { finalizeHash(); return readTemperature(); } private void finalizeHash() { hash ^= hash >> 17; // extra entropy } private static final long DOT_BITS = 0x10101000; private static final long MAGIC_MULTIPLIER = (100 * 0x1000000 + 10 * 0x10000 + 1); // Awesome idea of merykitty: private int readTemperature() { // This is the number part: X.X, -X.X, XX.x or -XX.X final long numberBytes = UNSAFE.getLong(ptr); final long invNumberBytes = ~numberBytes; final int dotPosition = Long.numberOfTrailingZeros(invNumberBytes & DOT_BITS); // Calculates the sign final long signed = (invNumberBytes << 59) >> 63; final int min28 = (dotPosition ^ 0b11100); final long minusFilter = ~(signed & 0xFF); // Use the pre-calculated decimal position to adjust the values final long digits = ((numberBytes & minusFilter) << min28) & 0x0F000F0F00L; // Update the pointer here, bit awkward, but we have all the data ptr += (dotPosition >> 3) + 3; // Multiply by a magic (100 * 0x1000000 + 10 * 0x10000 + 1), to get the result final long absValue = ((digits * MAGIC_MULTIPLIER) >>> 32) & 0x3FF; // And perform abs() return (int) ((absValue + signed) ^ signed); // non-patented method of doing the same trick } private boolean matches(final byte[] entry) { int step = 0; for (; step < entryLength - 16;) { if (compare(null, entryStart + step, entry, ENTRY_NAME + step)) { return false; } step += 8; } if (compare(readBuffer1, entry, ENTRY_NAME + step)) { return false; } step += 8; if (compare(readBuffer2, entry, ENTRY_NAME + step)) { return false; } return true; } private boolean matches16(final byte[] entry) { if (compare(readBuffer1, entry, ENTRY_NAME)) { return false; } if (compare(readBuffer2, entry, ENTRY_NAME + 8)) { return false; } return true; } } private static byte[][] processMemoryArea(final long startAddress, final long endAddress, boolean isFileStart) { final byte[][] table = new byte[TABLE_SIZE][]; final byte[][] preConstructedEntries = new byte[PREMADE_ENTRIES][ENTRY_BASESIZE_WHITESPACE + PREMADE_MAX_SIZE]; final Reader reader = new Reader(startAddress, endAddress, isFileStart); byte[] entry; int entryCount = 0; // Find the correct starting position while (reader.hasNext()) { reader.processStart(); if (!reader.readNext()) { // First 16 bytes: int temperature = reader.processEndAndGetTemperature(); // Find or insert the entry: int index = (int) (reader.hash & TABLE_MASK); while (true) { entry = table[index]; if (entry == null) { byte[] entryBytes = (entryCount < PREMADE_ENTRIES) ? preConstructedEntries[entryCount++] : new byte[ENTRY_BASESIZE_WHITESPACE + 16]; // with enough room table[index] = fillEntry16(entryBytes, 16, temperature, reader.readBuffer1, reader.readBuffer2); break; } else if (reader.matches16(entry)) { updateEntry(entry, temperature); break; } else { // Move to the next index index = (index + 1) & TABLE_MASK; } } continue; } while (reader.readNext()) ; int temperature = reader.processEndAndGetTemperature(); // Find or insert the entry: int index = (int) (reader.hash & TABLE_MASK); while (true) { entry = table[index]; if (entry == null) { int length = reader.entryLength; byte[] entryBytes = (length < PREMADE_MAX_SIZE && entryCount < PREMADE_ENTRIES) ? preConstructedEntries[entryCount++] : new byte[ENTRY_BASESIZE_WHITESPACE + length]; // with enough room table[index] = fillEntry(entryBytes, reader.entryStart, length, temperature, reader.readBuffer1, reader.readBuffer2); break; } else if (reader.matches(entry)) { updateEntry(entry, temperature); break; } else { // Move to the next index index = (index + 1) & TABLE_MASK; } } } return table; } private static boolean compare(final Object object1, final long address1, final Object object2, final long address2) { return UNSAFE.getLong(object1, address1) != UNSAFE.getLong(object2, address2); } private static boolean compare(final long value1, final Object object2, final long address2) { return value1 != UNSAFE.getLong(object2, address2); } /* * `___` ___ ___ _ ___` ` ___ ` _ ` _ ` _` ___ * / ` \| _ \ __| \| \ \ / /_\ | | | | | | __| * | () | _ / __|| . |\ V / _ \| |_| |_| | ._| * \___/|_| |___|_|\_| \_/_/ \_\___|\___/|___| * ---------------- BETTER SOFTWARE, FASTER -- * * https://www.openvalue.eu/ */ private static Unsafe initUnsafe() { try { final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_rprabhu.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class CalculateAverage_rprabhu { private static final String FILE = "./measurements.txt"; private static record Measurement(String station, double value) { private Measurement(String[] parts) { this(parts[0], Double.parseDouble(parts[1])); } } private static record ResultRow(double min, double mean, double max) { public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } }; private static class MeasurementAggregator { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; } private static ConcurrentHashMap map = new ConcurrentHashMap<>(); private static final int TASK_CHUNK = 50000; private static class TaskExecutor implements Runnable { List list; TaskExecutor(List list) { this.list = list; } @Override public void run() { for (String str : list) { // System.out.println(str); // String[] values = str.split(";"); int index = str.indexOf(';'); String station = str.substring(0, index); double val = Double.parseDouble(str.substring(index + 1)); // double val = Double.parseDouble(values[1]); MeasurementAggregator aggr = map.getOrDefault(station, new MeasurementAggregator()); aggr.count += 1; aggr.sum += val; aggr.max = (val >= aggr.max) ? val : aggr.max; // Math.max(aggr.max, val); aggr.min = (val <= aggr.min) ? val : aggr.min; // Math.min(aggr.min, val); map.put(station, aggr); } } } private static class TaskScheduler { ExecutorService executor; List list = new ArrayList<>(TASK_CHUNK); TaskScheduler(ExecutorService executor) { this.executor = executor; } void push(String line) { // System.out.println("adding: " + line); list.add(line); if (list.size() >= TASK_CHUNK) { executor.submit(new TaskExecutor(list)); list = new ArrayList<>(TASK_CHUNK); } } void completeRemaining() { // System.out.println("Completing remaining: " + list.size()); if (!list.isEmpty()) { // System.out.println("Scheduling remaining"); executor.submit(new TaskExecutor(list)); } } } public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { // ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); // ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); ExecutorService executor = Executors.newCachedThreadPool(); TaskScheduler scheduler = new TaskScheduler(executor); Files.lines(Paths.get(FILE), StandardCharsets.UTF_8).forEach(line -> { scheduler.push(line); }); scheduler.completeRemaining(); executor.shutdown(); executor.awaitTermination(600, TimeUnit.SECONDS); Map sortedResult = new TreeMap<>(); map.forEach( (key, value) -> sortedResult.put(key, new ResultRow(value.min, value.sum / value.count, value.max))); System.out.println(sortedResult); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_santanu.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.DoubleSummaryStatistics; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import static java.lang.Double.parseDouble; import static java.lang.String.format; import static java.util.stream.Collectors.summarizingDouble; public class CalculateAverage_santanu { private static final String DATA_FILE = "./measurements.txt"; private static final String PRINT_FORMAT = "%s=%.1f/%.1f/%.1f"; public static void main(String[] args) throws IOException { Map summaryStatisticsMap = processMeasurements(); printResults(summaryStatisticsMap); } private static final Function stationMeasurements = row -> { int splitPosition = row.indexOf(";"); return new StationMeasurementPair(row.substring(0, splitPosition), parseDouble(row.substring(splitPosition + 1))); }; private static Map processMeasurements() throws IOException { return Files.lines(Path.of(DATA_FILE)).map(stationMeasurements).parallel() .collect(Collectors.groupingBy( StationMeasurementPair::station, summarizingDouble(StationMeasurementPair::measurement))); } private static void printResults(Map collect) { String result = collect.entrySet().parallelStream().sorted(Map.Entry.comparingByKey()).map(s -> format(PRINT_FORMAT, s.getKey(), s.getValue().getMin(), s.getValue().getAverage(), s.getValue().getMax())).collect(Collectors.joining(", ")); System.out.println("{" + result + "}"); } private record StationMeasurementPair(String station, Double measurement) { } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_seijikun.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import java.io.IOException; import java.io.PrintStream; import java.io.RandomAccessFile; import java.lang.foreign.MemorySegment; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.Arrays; import java.util.HashMap; import java.util.TreeMap; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class CalculateAverage_seijikun { private static final String FILE = "./measurements.txt"; private static class MeasurementAggregator { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; // final long startTs = System.currentTimeMillis(); private long sum = 0; private long count = 0; private double mean = 0; public void finish() { double sum = this.sum / 10.0; mean = sum / (double) count; } public void printInto(PrintStream out) { double min = (double) this.min / 10.0; double max = (double) this.max / 10.0; out.printf("%.1f/%.1f/%.1f", min, mean, max); } } public static class StationIdent { private final byte[] name; private final int nameHash; public StationIdent(byte[] name, int nameHash) { this.name = name; this.nameHash = nameHash; } @Override public int hashCode() { return nameHash; } @Override public boolean equals(Object obj) { var other = (StationIdent) obj; if (other.name.length != name.length) { return false; } return Arrays.equals(name, other.name); } } public static class ChunkReader implements Runnable { RandomAccessFile file; // Start offset of this chunk private final long startOffset; // end offset of this chunk private final long endOffset; // state private int chunkSize = 0; private MappedByteBuffer buffer = null; private MemorySegment memorySegment = null; private int ptr = 0; private HashMap workSet; public ChunkReader(RandomAccessFile file, long startOffset, long endOffset) { this.file = file; this.startOffset = startOffset; this.endOffset = endOffset; } // private StationIdent readStationName() { // int startPtr = ptr; // int hashCode = 0; // int hashBytePtr = 0; // byte c; // while ((c = buffer.get(ptr++)) != ';') { // hashCode ^= ((int) c) << (hashBytePtr * 8); // hashBytePtr = (hashBytePtr + 1) % 4; // } // byte[] stationNameBfr = new byte[ptr - startPtr - 1]; // buffer.get(startPtr, stationNameBfr); // return new StationIdent(stationNameBfr, hashCode); // } private StationIdent readStationName() { final var VECTOR_SPECIES = ByteVector.SPECIES_256; if (chunkSize - ptr - 100 < VECTOR_SPECIES.length()) { // fallback int startPtr = ptr; while (buffer.get(ptr++) != ';') { } byte[] stationNameBfr = new byte[ptr - startPtr - 1]; buffer.get(startPtr, stationNameBfr); return new StationIdent(stationNameBfr, Arrays.hashCode(stationNameBfr) ^ stationNameBfr.length); } else { // SIMD int sepIdx = 0; while (true) { ByteVector tmp = ByteVector.fromMemorySegment(VECTOR_SPECIES, memorySegment, ptr + sepIdx, ByteOrder.LITTLE_ENDIAN); final var cmpResult = tmp.compare(VectorOperators.EQ, ';'); if (cmpResult.anyTrue()) { sepIdx += cmpResult.firstTrue(); break; } else { sepIdx += tmp.length(); } } int endPtr = ptr + sepIdx; byte[] stationNameBfr = new byte[endPtr - ptr]; buffer.get(ptr, stationNameBfr); ptr = endPtr + 1; return new StationIdent(stationNameBfr, Arrays.hashCode(stationNameBfr) ^ stationNameBfr.length); } } private int readTemperature() { int ret = 0; byte c = buffer.get(ptr++); final boolean neg = (c == '-'); if (neg) { c = buffer.get(ptr++); } do { if (c != '.') { ret = ret * 10 + c - '0'; } } while ((c = buffer.get(ptr++)) != '\n'); if (neg) return -ret; return ret; } @Override public void run() { workSet = new HashMap<>(); if (endOffset - startOffset > Integer.MAX_VALUE) { throw new RuntimeException("Mapping a block larger than 2GB is not possible with Java! Welcome to 2024 :)"); } chunkSize = (int) (endOffset - startOffset); try { buffer = file.getChannel().map(FileChannel.MapMode.READ_ONLY, startOffset, chunkSize); memorySegment = MemorySegment.ofBuffer(buffer); while (ptr < chunkSize) { var station = readStationName(); int temp = readTemperature(); var stationWorkSet = workSet.get(station); if (stationWorkSet == null) { stationWorkSet = new MeasurementAggregator(); workSet.put(station, stationWorkSet); } stationWorkSet.min = Math.min(temp, stationWorkSet.min); stationWorkSet.max = Math.max(temp, stationWorkSet.max); stationWorkSet.sum += temp; stationWorkSet.count += 1; } } catch (Throwable e) { e.printStackTrace(); throw new RuntimeException(e); } } } private static void printWorkSet(TreeMap result, PrintStream out) { out.write('{'); final var iterator = result.entrySet().iterator(); while (iterator.hasNext()) { var entry = iterator.next(); out.print(entry.getKey()); out.write('='); entry.getValue().printInto(out); if (iterator.hasNext()) { out.print(", "); } } out.println('}'); } private static int createChunks(final RandomAccessFile file, final ChunkReader[] chunks) throws IOException { final long fileEndPtr = file.length(); final long chunkSize = Math.max(1, fileEndPtr / chunks.length); int jobCnt = 0; long chunkStartPtr = 0; final byte[] tmpBuffer = new byte[128]; while (chunkStartPtr < fileEndPtr) { long chunkEndPtr = Math.min(chunkStartPtr + chunkSize, fileEndPtr); // Seek into file at the calculated chunk end ptr, then extend it until the next // new-line or EOF if (chunkEndPtr < fileEndPtr) { file.seek(Math.max(0, chunkEndPtr - 1)); file.read(tmpBuffer); int offset = 0; while (tmpBuffer[offset] != '\n') { offset += 1; } chunkEndPtr += offset; } chunks[jobCnt] = new ChunkReader(file, chunkStartPtr, chunkEndPtr); jobCnt += 1; chunkStartPtr = chunkEndPtr; } return jobCnt; } public static void main(String[] args) throws IOException, InterruptedException { final RandomAccessFile file = new RandomAccessFile(FILE, "r"); int jobCnt = Runtime.getRuntime().availableProcessors(); final var chunks = new ChunkReader[jobCnt]; jobCnt = createChunks(file, chunks); try (final var executor = Executors.newFixedThreadPool(jobCnt)) { for (int i = 0; i < jobCnt; ++i) { executor.submit(chunks[i]); } executor.shutdown(); final var ignored = executor.awaitTermination(1, TimeUnit.DAYS); } // merge chunks final var result = new TreeMap(); for (int i = 0; i < jobCnt; ++i) { chunks[i].workSet.forEach((ident, otherStationWorkSet) -> { final var identStr = new String(ident.name); final var stationWorkSet = result.get(identStr); if (stationWorkSet == null) { result.put(identStr, otherStationWorkSet); } else { stationWorkSet.min = Math.min(stationWorkSet.min, otherStationWorkSet.min); stationWorkSet.max = Math.max(stationWorkSet.max, otherStationWorkSet.max); stationWorkSet.sum += otherStationWorkSet.sum; stationWorkSet.count += otherStationWorkSet.count; } }); } result.forEach((ignored, meas) -> meas.finish()); // print in required format printWorkSet(result, System.out); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_semotpan.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.IntStream; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.file.StandardOpenOption.READ; public class CalculateAverage_semotpan { private static final Path FILE = Path.of("./measurements.txt"); public static void main(String[] args) throws IOException, InterruptedException { var spliterator = new SegmentSpliterator(FILE); var summaryStations = IntStream.range(0, spliterator.segments) .parallel() .mapToObj(segment -> { var buff = spliterator.buffSegment(segment); return new SegmentReader(buff); }) .flatMap(segmentReader -> segmentReader.values().stream()) .collect(Collectors.groupingByConcurrent(StationSummary::station)); // Complexity: O(stations * segments) var output = new TreeMap(); summaryStations.forEach((station, summaries) -> { var summary = summaries.stream() .reduce(new StationSummary(), (identity, acc) -> { identity.min = Integer.min(identity.min, acc.min); identity.max = Integer.max(identity.max, acc.max); identity.sum += acc.sum; identity.count += acc.count; return identity; }); output.put(station, summary); }); System.out.println(output); } private static class StationSummary { public String station; public int min, max, sum, count; StationSummary() { min = Integer.MAX_VALUE; max = Integer.MIN_VALUE; } StationSummary(String station, int temperature) { this.station = station; this.min = this.max = this.sum = temperature; this.count = 1; } String station() { return station; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } @Override public String toString() { return new StringBuilder() .append(round((double) min * 0.1)).append('/') .append(round((double) sum * 0.1 / count)) .append('/').append(round((double) max * 0.1)) .toString(); } } static class SegmentSpliterator { private int segments; private final ByteBuffer[] buffSegments; SegmentSpliterator(Path file) throws IOException { this.segments = Runtime.getRuntime().availableProcessors(); var fileSize = Files.size(file); var segmentSize = fileSize / segments; if (segmentSize <= (1 << 8)) { // small segment: 1 is enough segments = 1; } buffSegments = new ByteBuffer[segments]; var pos = 0L; for (var s = 0; s < segments - 1; s++) { try (var channel = (FileChannel) Files.newByteChannel(FILE, READ)) { var buff = channel.map(READ_ONLY, pos, segmentSize); pos = normalize(buff, (int) segmentSize - 1, pos); buffSegments[s] = buff; } } // handle last segment try (var channel = (FileChannel) Files.newByteChannel(FILE, READ)) { var buff = channel.map(READ_ONLY, pos, fileSize - pos); buffSegments[segments - 1] = buff; } } private long normalize(ByteBuffer buff, int relativePos, long pos) { while (buff.get(relativePos) != '\n') { relativePos--; } buff.limit(relativePos + 1); return pos + (relativePos + 1); } ByteBuffer buffSegment(int index) { return buffSegments[index]; } } static class SegmentReader { private final Map accumulator; private final byte[] keyBuff = new byte[256]; SegmentReader(ByteBuffer byteBuffer) { accumulator = new HashMap<>(); read(byteBuffer); } private void read(ByteBuffer buff) { byte b; for (int pos = 0, limit = buff.limit(); buff.hasRemaining(); buff.position(pos)) { // parse station (key) int offset = 0; while ((b = buff.get(pos++)) != ';') { keyBuff[offset++] = b; } // parse temperature (value) as int (1.1 * 10 = 11) int temp = 0; int negative = 1; while (pos != limit && (b = buff.get(pos++)) != '\n') { switch (b) { case '-': negative = -1; case '.': continue; default: temp = 10 * temp + (b - '0'); } } temp *= negative; // compute station var station = new String(keyBuff, 0, offset); var finalTemp = temp; accumulator.compute(station, (key, summary) -> { if (summary == null) { return new StationSummary(station, finalTemp); } summary.min = Integer.min(summary.min, finalTemp); summary.max = Integer.max(summary.max, finalTemp); summary.sum += finalTemp; summary.count += 1; return summary; }); } } Collection values() { return accumulator.values(); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_serkan_ozal.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; import sun.misc.Unsafe; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author serkan-ozal */ public class CalculateAverage_serkan_ozal { private static final String FILE = System.getProperty("file.path", "./measurements.txt"); private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED.length() >= 16 // Since majority (99%) of the city names <= 16 bytes, according to my experiments, // 128 bit (16 byte) vectors perform better than 256 bit (32 byte) or 512 bit (64 byte) vectors // even though supported by platform. ? ByteVector.SPECIES_128 : ByteVector.SPECIES_64; private static final int BYTE_SPECIES_SIZE = BYTE_SPECIES.vectorByteSize(); private static final MemorySegment NULL = MemorySegment.NULL.reinterpret(Long.MAX_VALUE); private static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder(); private static final char NEW_LINE_SEPARATOR = '\n'; private static final char KEY_VALUE_SEPARATOR = ';'; private static final int MAX_LINE_LENGTH = 128; // Get configurations //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private static final boolean VERBOSE = false; // getBooleanConfig("VERBOSE", false); private static final int THREAD_COUNT = Runtime.getRuntime().availableProcessors(); // getIntegerConfig("THREAD_COUNT", Runtime.getRuntime().availableProcessors()); private static final boolean USE_VTHREADS = false; // getBooleanConfig("USE_VTHREADS", false); private static final int VTHREAD_COUNT = 1024; // getIntegerConfig("VTHREAD_COUNT", 1024); private static final int REGION_COUNT = 256; // getIntegerConfig("REGION_COUNT", -1); private static final boolean USE_SHARED_ARENA = true; // getBooleanConfig("USE_SHARED_ARENA", true); private static final boolean USE_SHARED_REGION = true; // getBooleanConfig("USE_SHARED_REGION", true); private static final int MAP_CAPACITY = 1 << 17; // getIntegerConfig("MAP_CAPACITY", 1 << 17); private static final boolean CLOSE_STDOUT_ON_RESULT = true; // getBooleanConfig("CLOSE_STDOUT_ON_RESULT", true); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // My dear old friend Unsafe private static final Unsafe U; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); U = (Unsafe) f.get(null); } catch (Exception e) { throw new IllegalStateException(e); } } public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); if (VERBOSE) { System.out.println("Processing started at " + start); System.out.println("Vector byte size: " + BYTE_SPECIES.vectorByteSize()); System.out.println("Use shared memory arena: " + USE_SHARED_ARENA); if (USE_VTHREADS) { System.out.println("Virtual thread count: " + VTHREAD_COUNT); } else { System.out.println("Thread count: " + THREAD_COUNT); } System.out.println("Map capacity: " + MAP_CAPACITY); } int concurrency = USE_VTHREADS ? VTHREAD_COUNT : THREAD_COUNT; int regionCount = REGION_COUNT > 0 ? REGION_COUNT : concurrency; ByteBuffer lineBuffer = getByteBuffer(MAX_LINE_LENGTH); Result result = new Result(); RandomAccessFile file = new RandomAccessFile(FILE, "r"); FileChannel fc = file.getChannel(); Arena arena = USE_SHARED_ARENA ? Arena.ofShared() : null; try { long fileSize = fc.size(); long regionSize = fileSize / regionCount; long startPos = 0; ExecutorService executor = USE_VTHREADS ? Executors.newVirtualThreadPerTaskExecutor() : Executors.newFixedThreadPool(concurrency, new RegionProcessorThreadFactory()); MemorySegment region = null; if (USE_SHARED_REGION) { arena = Arena.ofShared(); region = fc.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, arena); } List tasks = new ArrayList<>(regionCount); // Split whole file into regions and create tasks for each region List> futures = new ArrayList<>(regionCount); for (int i = 0; i < regionCount; i++) { long endPos = Math.min(fileSize, startPos + regionSize); // Lines might split into different regions. // If so, move back to the line starting at the end of previous region long closestLineEndPos = (i < regionCount - 1) ? findClosestLineEnd(fc, endPos, lineBuffer) : fileSize; Task task = new Task(fc, region, startPos, closestLineEndPos); tasks.add(task); startPos = closestLineEndPos; } Queue sharedTasks = new ConcurrentLinkedQueue<>(tasks); // Start region processors to process tasks for each region for (int i = 0; i < concurrency; i++) { Request request = new Request(arena, sharedTasks, result); RegionProcessor regionProcessor = createRegionProcessor(request); Future future = executor.submit(regionProcessor); futures.add(future); } // Wait processors to complete for (Future future : futures) { future.get(); } long finish = System.currentTimeMillis(); if (VERBOSE) { System.out.println("Processing completed at " + finish); System.out.println("Processing completed in " + (finish - start) + " milliseconds"); } // Print result to stdout result.print(); if (CLOSE_STDOUT_ON_RESULT) { // After printing result, close stdout. // So parent process can complete without waiting this process completed. // Saves a few hundred milliseconds caused by unmap. System.out.close(); } } finally { // Close memory arena if it is managed globally here (shared arena) if (arena != null) { arena.close(); } fc.close(); if (VERBOSE) { long finish = System.currentTimeMillis(); System.out.println("All completed at " + finish); System.out.println("All Completed in " + ((finish - start)) + " milliseconds"); } } } private static boolean getBooleanConfig(String envVarName, boolean defaultValue) { String envVarValue = System.getenv(envVarName); if (envVarValue == null) { return defaultValue; } else { return Boolean.parseBoolean(envVarValue); } } private static int getIntegerConfig(String envVarName, int defaultValue) { String envVarValue = System.getenv(envVarName); if (envVarValue == null) { return defaultValue; } else { return Integer.parseInt(envVarValue); } } private static ByteBuffer getByteBuffer(int size) { ByteBuffer bb = ByteBuffer.allocateDirect(size); bb.order(NATIVE_BYTE_ORDER); return bb; } private static long findClosestLineEnd(FileChannel fc, long endPos, ByteBuffer lineBuffer) throws IOException { long lineCheckStartPos = Math.max(0, endPos - MAX_LINE_LENGTH); lineBuffer.rewind(); fc.read(lineBuffer, lineCheckStartPos); int i = MAX_LINE_LENGTH; while (lineBuffer.get(i - 1) != NEW_LINE_SEPARATOR) { i--; } return lineCheckStartPos + i; } private static RegionProcessor createRegionProcessor(Request request) { return new RegionProcessor(request); } private static class RegionProcessorThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); t.setPriority(Thread.MAX_PRIORITY); return t; } } /** * Region processor */ private static class RegionProcessor implements Callable { private final Arena arena; private final Queue sharedTasks; private final Result result; private OpenMap map; private RegionProcessor(Request request) { this.arena = request.arena; this.sharedTasks = request.sharedTasks; this.result = request.result; } @Override public Response call() throws Exception { if (VERBOSE) { System.out.println("[Processor-" + Thread.currentThread().getName() + "] Processing started at " + System.currentTimeMillis()); } try { processRegion(); return new Response(map); } finally { if (VERBOSE) { System.out.println("[Processor-" + Thread.currentThread().getName() + "] Processing finished at " + System.currentTimeMillis()); } } } private void processRegion() throws Exception { // Create map in its own thread this.map = new OpenMap(); boolean arenaGiven = arena != null; // If no shared global memory arena is used, create and use its own local memory arena Arena a = arenaGiven ? arena : Arena.ofConfined(); try { for (Task task = sharedTasks.poll(); task != null; task = sharedTasks.poll()) { boolean regionGiven = task.region != null; MemorySegment r = regionGiven ? task.region : task.fileChannel.map(FileChannel.MapMode.READ_ONLY, task.start, task.size, a); long regionStart = regionGiven ? (r.address() + task.start) : r.address(); long regionEnd = regionStart + task.size; doProcessRegion(regionStart, regionEnd); } if (VERBOSE) { System.out.println("[Processor-" + Thread.currentThread().getName() + "] Region processed at " + System.currentTimeMillis()); } // Some threads/processors might finish slightly before others. // So, instead of releasing their cores idle, merge their own results here. // If there is no another processor merging its results now, merge now. // Otherwise (there is already another thread/processor got the lock of merging), // Close current processor's own local memory arena (if no shared global memory arena is used) now // and merge its own results after then. boolean merged = result.tryMergeInto(map); if (VERBOSE && merged) { System.out.println("[Processor-" + Thread.currentThread().getName() + "] Result merged at " + System.currentTimeMillis()); } if (!merged) { if (!arenaGiven) { a.close(); a = null; if (VERBOSE) { System.out.println("[Processor-" + Thread.currentThread().getName() + "] Arena closed at " + System.currentTimeMillis()); } } result.mergeInto(map); if (VERBOSE) { System.out.println("[Processor-" + Thread.currentThread().getName() + "] Result merged at " + System.currentTimeMillis()); } } } finally { // If local memory arena is managed here and not closed yet, close it here if (!arenaGiven && a != null) { a.close(); if (VERBOSE) { System.out.println("[Processor-" + Thread.currentThread().getName() + "] Arena closed at " + System.currentTimeMillis()); } } } } private long findClosestLineEnd(long endPos, long minPos) { int i = 0; int maxI = Math.min(MAX_LINE_LENGTH, (int) (endPos - minPos)); while (i <= maxI && U.getByte(endPos - i) != NEW_LINE_SEPARATOR) { i++; } return endPos - i + 1; } // Credits: merykitty private long extractValue(long regionPtr, long word, OpenMap map, int entryOffset) { // Parse and extract value // 1. level instruction set (no dependency between each other so can be run in parallel) long signed = (~word << 59) >> 63; int decimalSepPos = Long.numberOfTrailingZeros(~word & 0x10101000); // 2. level instruction set (no dependency between each other so can be run in parallel) long nextPtr = regionPtr + (decimalSepPos >>> 3) + 3; int shift = 28 - decimalSepPos; long designMask = ~(signed & 0xFF); long digits = ((word & designMask) << shift) & 0x0F000F0F00L; long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; int value = (int) ((absValue ^ signed) - signed); // Put extracted value into map map.putValue(entryOffset, value); // Return new position return nextPtr; } private void doProcessRegion(long regionStart, long regionEnd) { final long size = regionEnd - regionStart; final long segmentSize = size / 2; final long regionStart1 = regionStart; final long regionEnd1 = Math.max(regionStart1, findClosestLineEnd(regionStart1 + segmentSize, regionStart)); final long regionStart2 = regionEnd1; final long regionEnd2 = regionEnd; long regionPtr1, regionPtr2; // Read and process region - main // Inspired by: @jerrinot // - two lines at a time (according to my experiment, this is optimum value in terms of register spilling) // - most of the implementation is inlined // - so get the benefit of ILP (Instruction Level Parallelism) better for (regionPtr1 = regionStart1, regionPtr2 = regionStart2; regionPtr1 < regionEnd1 && regionPtr2 < regionEnd2;) { // Search key/value separators and find keys' start and end positions //////////////////////////////////////////////////////////////////////////////////////////////////////// long keyStartPtr1 = regionPtr1; long keyStartPtr2 = regionPtr2; ByteVector keyVector1 = ByteVector.fromMemorySegment(BYTE_SPECIES, NULL, regionPtr1, NATIVE_BYTE_ORDER); ByteVector keyVector2 = ByteVector.fromMemorySegment(BYTE_SPECIES, NULL, regionPtr2, NATIVE_BYTE_ORDER); int keyLength1 = keyVector1.compare(VectorOperators.EQ, KEY_VALUE_SEPARATOR).firstTrue(); int keyLength2 = keyVector2.compare(VectorOperators.EQ, KEY_VALUE_SEPARATOR).firstTrue(); if (keyLength1 != BYTE_SPECIES_SIZE && keyLength2 != BYTE_SPECIES_SIZE) { regionPtr1 += (keyLength1 + 1); regionPtr2 += (keyLength2 + 1); } else { if (keyLength1 != BYTE_SPECIES_SIZE) { regionPtr1 += (keyLength1 + 1); } else { regionPtr1 += BYTE_SPECIES_SIZE; for (; U.getByte(regionPtr1) != KEY_VALUE_SEPARATOR; regionPtr1++) ; keyLength1 = (int) (regionPtr1 - keyStartPtr1); regionPtr1++; } if (keyLength2 != BYTE_SPECIES_SIZE) { regionPtr2 += (keyLength2 + 1); } else { regionPtr2 += BYTE_SPECIES_SIZE; for (; U.getByte(regionPtr2) != KEY_VALUE_SEPARATOR; regionPtr2++) ; keyLength2 = (int) (regionPtr2 - keyStartPtr2); regionPtr2++; } } // Read first words as they will be used while extracting values later long word1 = U.getLong(regionPtr1); long word2 = U.getLong(regionPtr2); if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { word1 = Long.reverseBytes(word1); word2 = Long.reverseBytes(word2); } //////////////////////////////////////////////////////////////////////////////////////////////////////// // Calculate key hashes and find entry indexes //////////////////////////////////////////////////////////////////////////////////////////////////////// int x1, y1, x2, y2; if (keyLength1 > 3 && keyLength2 > 3) { x1 = U.getInt(keyStartPtr1); y1 = U.getInt(regionPtr1 - 5); x2 = U.getInt(keyStartPtr2); y2 = U.getInt(regionPtr2 - 5); } else { if (keyLength1 > 3) { x1 = U.getInt(keyStartPtr1); y1 = U.getInt(regionPtr1 - 5); } else { x1 = U.getByte(keyStartPtr1); y1 = U.getByte(regionPtr1 - 2); } if (keyLength2 > 3) { x2 = U.getInt(keyStartPtr2); y2 = U.getInt(regionPtr2 - 5); } else { x2 = U.getByte(keyStartPtr2); y2 = U.getByte(regionPtr2 - 2); } } int keyHash1 = (Integer.rotateLeft(x1 * OpenMap.HASH_SEED, OpenMap.HASH_ROTATE) ^ y1) * OpenMap.HASH_SEED; int keyHash2 = (Integer.rotateLeft(x2 * OpenMap.HASH_SEED, OpenMap.HASH_ROTATE) ^ y2) * OpenMap.HASH_SEED; int entryIdx1 = (keyHash1 & OpenMap.ENTRY_HASH_MASK) << OpenMap.ENTRY_SIZE_SHIFT; int entryIdx2 = (keyHash2 & OpenMap.ENTRY_HASH_MASK) << OpenMap.ENTRY_SIZE_SHIFT; //////////////////////////////////////////////////////////////////////////////////////////////////////// // Put keys and calculate entry offsets to put values //////////////////////////////////////////////////////////////////////////////////////////////////////// int entryOffset1 = map.putKey(keyVector1, keyStartPtr1, keyLength1, entryIdx1); int entryOffset2 = map.putKey(keyVector2, keyStartPtr2, keyLength2, entryIdx2); //////////////////////////////////////////////////////////////////////////////////////////////////////// // Extract values by parsing and put them into map //////////////////////////////////////////////////////////////////////////////////////////////////////// regionPtr1 = extractValue(regionPtr1, word1, map, entryOffset1); regionPtr2 = extractValue(regionPtr2, word2, map, entryOffset2); //////////////////////////////////////////////////////////////////////////////////////////////////////// } // Read and process region - tail doProcessTail(regionPtr1, regionEnd1, regionPtr2, regionEnd2); } private void doProcessTail(long regionPtr1, long regionEnd1, long regionPtr2, long regionEnd2) { while (regionPtr1 < regionEnd1) { long keyStartPtr1 = regionPtr1; ByteVector keyVector1 = ByteVector.fromMemorySegment(BYTE_SPECIES, NULL, regionPtr1, NATIVE_BYTE_ORDER); int keyLength1 = keyVector1.compare(VectorOperators.EQ, KEY_VALUE_SEPARATOR).firstTrue(); if (keyLength1 != BYTE_SPECIES_SIZE) { regionPtr1 += (keyLength1 + 1); } else { regionPtr1 += BYTE_SPECIES_SIZE; for (; U.getByte(regionPtr1) != KEY_VALUE_SEPARATOR; regionPtr1++) ; keyLength1 = (int) (regionPtr1 - keyStartPtr1); regionPtr1++; } int entryIdx1 = map.calculateEntryIndex(keyStartPtr1, keyLength1); int entryOffset1 = map.putKey(keyVector1, keyStartPtr1, keyLength1, entryIdx1); long word1 = U.getLong(regionPtr1); if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { word1 = Long.reverseBytes(word1); } regionPtr1 = extractValue(regionPtr1, word1, map, entryOffset1); } while (regionPtr2 < regionEnd2) { long keyStartPtr2 = regionPtr2; ByteVector keyVector2 = ByteVector.fromMemorySegment(BYTE_SPECIES, NULL, regionPtr2, NATIVE_BYTE_ORDER); int keyLength2 = keyVector2.compare(VectorOperators.EQ, KEY_VALUE_SEPARATOR).firstTrue(); if (keyLength2 != BYTE_SPECIES_SIZE) { regionPtr2 += (keyLength2 + 1); } else { regionPtr2 += BYTE_SPECIES_SIZE; for (; U.getByte(regionPtr2) != KEY_VALUE_SEPARATOR; regionPtr2++) ; keyLength2 = (int) (regionPtr2 - keyStartPtr2); regionPtr2++; } int entryIdx2 = map.calculateEntryIndex(keyStartPtr2, keyLength2); int entryOffset2 = map.putKey(keyVector2, keyStartPtr2, keyLength2, entryIdx2); long word2 = U.getLong(regionPtr2); if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { word2 = Long.reverseBytes(word2); } regionPtr2 = extractValue(regionPtr2, word2, map, entryOffset2); } } } /** * Region processor task */ private static final class Task { private final FileChannel fileChannel; private final MemorySegment region; private final long start; private final long end; private final long size; private Task(FileChannel fileChannel, MemorySegment region, long start, long end) { this.fileChannel = fileChannel; this.region = region; this.start = start; this.end = end; this.size = end - start; } } /** * Region processor request */ private static final class Request { private final Arena arena; private final Queue sharedTasks; private final Result result; private Request(Arena arena, Queue sharedTasks, Result result) { this.arena = arena; this.sharedTasks = sharedTasks; this.result = result; } } /** * Region processor response */ private static final class Response { private final OpenMap map; private Response(OpenMap map) { this.map = map; } } /** * Result of each key (city) */ private static final class KeyResult { private int count; private int minValue; private int maxValue; private long sum; private KeyResult(int count, int minValue, int maxValue, long sum) { this.count = count; this.minValue = minValue; this.maxValue = maxValue; this.sum = sum; } private void merge(KeyResult result) { count += result.count; minValue = Math.min(minValue, result.minValue); maxValue = Math.max(maxValue, result.maxValue); sum += result.sum; } @Override public String toString() { return (minValue / 10.0) + "/" + round(sum / (double) (count * 10)) + "/" + (maxValue / 10.0); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } /** * Global result */ private static final class Result { private final Lock lock = new ReentrantLock(); private final Map resultMap; private Result() { this.resultMap = new TreeMap<>(); } private boolean tryMergeInto(OpenMap map) { // Use lock (not "synchronized" block) to be virtual threads friendly if (!lock.tryLock()) { return false; } try { map.merge(this.resultMap); return true; } finally { lock.unlock(); } } private void mergeInto(OpenMap map) { // Use lock (not "synchronized" block) to be virtual threads friendly lock.lock(); try { map.merge(this.resultMap); } finally { lock.unlock(); } } private void print() { StringBuilder sb = new StringBuilder(1 << 14); boolean firstEntryAppended = false; sb.append("{"); for (Map.Entry e : resultMap.entrySet()) { if (firstEntryAppended) { sb.append(", "); } String key = e.getKey(); KeyResult value = e.getValue(); sb.append(key).append("=").append(value); firstEntryAppended = true; } sb.append('}'); System.out.println(sb); } } /** * Custom map implementation to store results */ private static final class OpenMap { // Layout // ================================ // 0 : 4 bytes - count // 4 : 2 bytes - min value // 6 : 2 bytes - max value // 8 : 8 bytes - value sum // 16 : 4 bytes - key size // 20 : 4 bytes - padding // 24 : 100 bytes - key // 124 : 4 bytes - padding // ================================ // 128 bytes - total private static final int ENTRY_SIZE = 128; private static final int ENTRY_SIZE_SHIFT = 7; private static final int COUNT_OFFSET = 0; private static final int MIN_VALUE_OFFSET = 4; private static final int MAX_VALUE_OFFSET = 6; private static final int VALUE_SUM_OFFSET = 8; private static final int KEY_SIZE_OFFSET = 16; private static final int KEY_OFFSET = 24; private static final int ENTRY_HASH_MASK = MAP_CAPACITY - 1; private static final int MAP_SIZE = ENTRY_SIZE * MAP_CAPACITY; private static final int ENTRY_MASK = MAP_SIZE - 1; private static final int KEY_ARRAY_OFFSET = KEY_OFFSET - Unsafe.ARRAY_BYTE_BASE_OFFSET; private static final int HASH_SEED = 0x9E3779B9; private static final int HASH_ROTATE = 5; private final byte[] data; private final int[] entryOffsets; private int entryOffsetIdx; private OpenMap() { this.data = new byte[MAP_SIZE]; // Max number of unique keys are 10K, so 1 << 14 (16384) is long enough to hold offsets for all of them this.entryOffsets = new int[1 << 14]; this.entryOffsetIdx = 0; } // Credits: merykitty private int calculateEntryIndex(long address, int keyLength) { int x, y; if (keyLength >= Integer.BYTES) { x = U.getInt(address); y = U.getInt(address + keyLength - Integer.BYTES); } else { x = U.getByte(address); y = U.getByte(address + keyLength - Byte.BYTES); } // Calculate key hash int keyHash = (Integer.rotateLeft(x * HASH_SEED, HASH_ROTATE) ^ y) * HASH_SEED; // Get the position of the entry in the linear map based on calculated hash return (keyHash & ENTRY_HASH_MASK) << ENTRY_SIZE_SHIFT; } private int putKey(ByteVector keyVector, long keyStartAddress, int keyLength, int entryIdx) { // Start searching from the calculated position // and continue until find an available slot in case of hash collision // TODO Prevent infinite loop if all the slots are in use for other keys for (int entryOffset = Unsafe.ARRAY_BYTE_BASE_OFFSET + entryIdx;; entryOffset = (entryOffset + ENTRY_SIZE) & ENTRY_MASK) { int keySize = U.getInt(data, entryOffset + KEY_SIZE_OFFSET); // Check whether current index is empty (no another key is inserted yet) if (keySize == 0) { // Initialize entry slot for new key U.putShort(data, entryOffset + MIN_VALUE_OFFSET, Short.MAX_VALUE); U.putShort(data, entryOffset + MAX_VALUE_OFFSET, Short.MIN_VALUE); U.putInt(data, entryOffset + KEY_SIZE_OFFSET, keyLength); U.copyMemory(null, keyStartAddress, data, entryOffset + KEY_OFFSET, keyLength); entryOffsets[entryOffsetIdx++] = entryOffset; return entryOffset; } // Check for hash collision (hashes are same, but keys are different). // If there is no collision (both hashes and keys are equals), return current slot's offset. // Otherwise, continue iterating until find an available slot. if (keySize == keyLength && keysEqual(keyVector, keyStartAddress, keyLength, entryOffset + KEY_ARRAY_OFFSET)) { return entryOffset; } } } private boolean keysEqual(ByteVector keyVector, long keyStartAddress, int keyLength, int keyStartArrayOffset) { // Use vectorized search for the comparison of keys. // Since majority of the city names >= 8 bytes and <= 16 bytes, // this way is more efficient (according to my experiments) than any other comparisons (byte by byte or 2 longs). ByteVector entryKeyVector = ByteVector.fromArray(BYTE_SPECIES, data, keyStartArrayOffset); int eqCount = keyVector.compare(VectorOperators.EQ, entryKeyVector).trueCount(); if (eqCount == keyLength) { return true; } else if (keyLength <= BYTE_SPECIES_SIZE) { return false; } // Compare remaining parts of the keys int normalizedKeyLength = keyLength; if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { normalizedKeyLength = Integer.reverseBytes(normalizedKeyLength); } long keyStartOffset = keyStartArrayOffset + Unsafe.ARRAY_BYTE_BASE_OFFSET; int alignedKeyLength = normalizedKeyLength & 0xFFFFFFF8; int i; for (i = BYTE_SPECIES_SIZE; i < alignedKeyLength; i += Long.BYTES) { if (U.getLong(keyStartAddress + i) != U.getLong(data, keyStartOffset + i)) { return false; } } long wordA = U.getLong(keyStartAddress + i); long wordB = U.getLong(data, keyStartOffset + i); if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { wordA = Long.reverseBytes(wordA); wordB = Long.reverseBytes(wordB); } int halfShift = (Long.BYTES - (normalizedKeyLength & 0x00000007)) << 2; long mask = (0xFFFFFFFFFFFFFFFFL >>> halfShift) >> halfShift; wordA = wordA & mask; // No need to mask "wordB" (word from key in the map), because it is already padded with 0s return wordA == wordB; } private void putValue(int entryOffset, int value) { int countOffset = entryOffset + COUNT_OFFSET; int minValueOffset = entryOffset + MIN_VALUE_OFFSET; int maxValueOffset = entryOffset + MAX_VALUE_OFFSET; int sumOffset = entryOffset + VALUE_SUM_OFFSET; U.putInt(data, countOffset, U.getInt(data, countOffset) + 1); if (value < U.getShort(data, minValueOffset)) { U.putShort(data, minValueOffset, (short) value); } if (value > U.getShort(data, maxValueOffset)) { U.putShort(data, maxValueOffset, (short) value); } U.putLong(data, sumOffset, U.getLong(data, sumOffset) + value); } private void merge(Map resultMap) { // Merge this local map into global result map Arrays.sort(entryOffsets, 0, entryOffsetIdx); for (int i = 0; i < entryOffsetIdx; i++) { int entryOffset = entryOffsets[i]; int keyLength = U.getInt(data, entryOffset + KEY_SIZE_OFFSET); if (keyLength == 0) { // No entry is available for this index, so continue iterating continue; } int entryArrayIdx = entryOffset + KEY_OFFSET - Unsafe.ARRAY_BYTE_BASE_OFFSET; String key = new String(data, entryArrayIdx, keyLength, StandardCharsets.UTF_8); int count = U.getInt(data, entryOffset + COUNT_OFFSET); short minValue = U.getShort(data, entryOffset + MIN_VALUE_OFFSET); short maxValue = U.getShort(data, entryOffset + MAX_VALUE_OFFSET); long sum = U.getLong(data, entryOffset + VALUE_SUM_OFFSET); KeyResult result = new KeyResult(count, minValue, maxValue, sum); KeyResult existingResult = resultMap.get(key); if (existingResult == null) { resultMap.put(key, result); } else { existingResult.merge(result); } } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_shipilev.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.reflect.InaccessibleObjectException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.concurrent.*; import java.util.function.Supplier; public class CalculateAverage_shipilev { // Detour: This implementation tries to balance the speed and readability. // // While the original contest suggests we pull off every trick in the // book to get the peak performance, here we set a more pragmatic goal: // how fast we can get without going too far into hacks. Or, putting it // in another way, what would be the reasonably fast implementation that // would *also* pass a code review in a reasonable project, would be usable // in production without waking people up in the middle of the night, and // would work through JDK updates, upgrades, and migrations. // // To that end, this implementation uses vanilla and standard Java as much // as possible, without relying on Unsafe tricks and preview features. // When any non-standard things are used, they are guarded by a feature flag, // which allows to cleanly turn them off when anything goes off the rails. // // For performance reasons, the implementation takes more care to be reliably // parallel to survive I/O stalls and scheduling oddities. This would not // show up in laboratory conditions, but it is a necessary thing for a reliable // code in production. It also tries not to miss simple optimizations without // going too far into the woods. // // Note that some of the magic to run this workload fast in evaluation // conditions is done separately in the invocation script. Most of that // is only needed for the short-running scenarios. In real life, this code // would likely run well without any of that. // // ========================= Tunables ========================= // Workload data file. private static final String FILE = "./measurements.txt"; // Max distance to search for line separator when scanning for line // boundaries. 100 bytes name should fit into this power-of-two buffer. // Should probably never change. private static final int MAX_LINE_LENGTH = 128; // Fixed size of the measurements map. Must be the power of two. Should // be large enough to accomodate all the station names. Rules say there are // 10K station names max, so anything more than 16K works well. private static final int MAP_SIZE = 1 << 15; // The largest mmap-ed chunk. This can be be Integer.MAX_VALUE, but // it is normally tuned down to seed the workers with smaller mmap regions // more efficiently. This also allows to incrementally unmap chunks as we // complete working on them. private static final int MMAP_CHUNK_SIZE = Integer.MAX_VALUE / 32; // The largest slice as unit of work, processed serially by a worker. // Set it too low and there would be more tasks and less batching, but // more parallelism. Set it too high, and the reverse would be true. // Something around a large page would likely hit the right balance. private static final int UNIT_SLICE_SIZE = 4 * 1024 * 1024; // Employ direct unmapping techniques to alleviate the cost of system // unmmapping on process termination. This matters for very short runs // on highly parallel machines. This unfortunately calls into private // methods of buffers themselves. If not available on target JVM, the // feature would automatically turn off. private static final boolean DIRECT_UNMMAPS = true; // ========================= Storage ========================= // Thread-local measurement maps, each thread gets one. // This allows workers to work nearly unimpeded without synchronization. // Even though crude, avoid lambdas here to alleviate startup costs. private static final ThreadLocal MAPS = ThreadLocal.withInitial(new Supplier<>() { @Override public MeasurementsMap get() { MeasurementsMap m = new MeasurementsMap(); ALL_MAPS.add(m); return m; } }); // After worker threads finish, the data is available here. The reporting // code would pull the maps from here, once all workers finish. private static final ConcurrentLinkedQueue ALL_MAPS = new ConcurrentLinkedQueue<>(); // Releasable mmaped buffers that workers are done with. These can be un-mapped // in background. Main thread would wait on this queue, until it gets the poison // pill from the root task. private static final LinkedBlockingQueue RELEASABLE_BUFFERS = new LinkedBlockingQueue<>(); private static final ByteBuffer RELEASABLE_BUFFER_POISON_PILL = ByteBuffer.allocate(1); // ========================= MEATY GRITTY PARTS: PARSE AND AGGREGATE ========================= public static final class Bucket { // Raw station name, encoded as two prefixes and the name tail, // its total length, and hash. public final byte[] nameTail; public final int len; public final int hash; public final int prefix1, prefix2; // Temperature values, in 10x scale. public long sum; public int count; public int min; public int max; public Bucket(ByteBuffer slice, int begin, int end, int hash, int temp) { len = end - begin; // Decode the station name. It is handy to have a few prefixes // available to simplify matches later. int tailStart = 0; if (len >= 8) { prefix1 = slice.getInt(begin + 0); prefix2 = slice.getInt(begin + 4); tailStart += 8; } else if (len >= 4) { prefix1 = slice.getInt(begin + 0); prefix2 = 0; tailStart += 4; } else { prefix1 = 0; prefix2 = 0; } // The rest goes to tail byte array. We are checking reading it on hot-path. // Therefore, it is convenient to keep allocation for names near the buckets. // One can avoid this by carefully recording the tail in a separate field, // like the prefixes above, but this is simple enough to gain enough perf. int tailLen = len - tailStart; nameTail = new byte[tailLen]; slice.get(begin + tailStart, nameTail, 0, tailLen); // Seed the bucket with initial value. this.hash = hash; this.sum = temp; this.count = 1; this.min = temp; this.max = temp; } // Little helper method to compare the array with given ByteBuffer range. public boolean matches(ByteBuffer cand, int begin, int end) { int origLen = len; int candLen = end - begin; if (origLen != candLen) { return false; } // Check the prefixes first, if we can. int tailStart = 0; if (origLen >= 8) { if (prefix1 != cand.getInt(begin)) { return false; } if (prefix2 != cand.getInt(begin + 4)) { return false; } tailStart += 8; } else if (origLen >= 4) { if (prefix1 != cand.getInt(begin)) { return false; } tailStart += 4; } // Check the rest. for (int i = 0; i < origLen - tailStart; i++) { if (nameTail[i] != cand.get(begin + tailStart + i)) { return false; } } return true; } // Check if current Bucket matches another. public boolean matches(Bucket other) { return len == other.len && prefix1 == other.prefix1 && prefix2 == other.prefix2 && Arrays.equals(nameTail, other.nameTail); } // Merge the temp value. Hot-path, should be fairly efficient. public void merge(int value) { sum += value; count++; // We rarely do the updates, so these branches are almost // never taken. Writing them as explicit branches instead of // Math.{min,max} improves performance a bit. if (value < min) { min = value; } if (value > max) { max = value; } } // Merge the buckets. Called during reporting, not a hot path. public void merge(Bucket s) { sum += s.sum; count += s.count; min = Math.min(min, s.min); max = Math.max(max, s.max); } public Row toRow() { // Reconstruct the name first. The prefixes and the tail were copied // from the little-endian slice, so we need to match the endianness here. ByteBuffer bb = ByteBuffer.allocate(len); bb.order(ByteOrder.LITTLE_ENDIAN); if (len >= 4) { bb.putInt(prefix1); } if (len >= 8) { bb.putInt(prefix2); } bb.put(nameTail); return new Row( new String(Arrays.copyOf(bb.array(), len)), Math.round((double) min) / 10.0, Math.round((double) sum / count) / 10.0, Math.round((double) max) / 10.0); } } // Quick and dirty linear-probing hash map. YOLO. public static final class MeasurementsMap { // Individual map buckets. Inlining these straight into map complicates // the implementation without much of the performance improvement. // The map is likely sparse, so whatever footprint loss we have due to // Bucket headers we gain by allocating the buckets lazily. The memory // dereference costs are still high in both cases. The additional benefit // for explicit fields in Bucket is that we only need to pay for a single // null-check on bucket instead of multiple range-checks on inlined array. private final Bucket[] buckets = new Bucket[MAP_SIZE]; // Fast path is inlined in seqCompute. This is a slow-path that is taken // rarely, usually when there is a hash collision. We normally do not enter here. private void updateSlow(ByteBuffer name, int begin, int end, int hash, int temp) { int idx = hash & (MAP_SIZE - 1); while (true) { Bucket cur = buckets[idx]; if (cur == null) { // No bucket yet, lucky us. Create the bucket and be done. buckets[idx] = new Bucket(name, begin, end, hash, temp); return; } else if ((cur.hash == hash) && cur.matches(name, begin, end)) { // Same as bucket fastpath. Check for collision by checking the full hash // first (since the index is truncated by map size), and then the exact name. cur.merge(temp); return; } else { // No dice. Keep searching. idx = (idx + 1) & (MAP_SIZE - 1); } } } // Same as update(), really, but for merging maps. See the comments there. public void merge(MeasurementsMap otherMap) { for (Bucket other : otherMap.buckets) { if (other == null) continue; int idx = other.hash & (MAP_SIZE - 1); while (true) { Bucket cur = buckets[idx]; if (cur == null) { buckets[idx] = other; break; } else if ((cur.hash == other.hash) && cur.matches(other)) { cur.merge(other); break; } else { idx = (idx + 1) & (MAP_SIZE - 1); } } } } // Convert from internal representation to the rows. This does several // major things: filters away null-s, instantates full Strings, and // computes the final rows. public int fill(Row[] rows) { int idx = 0; for (Bucket bucket : buckets) { if (bucket == null) continue; rows[idx++] = bucket.toRow(); } return idx; } } // The heavy-weight, where most of the magic happens. This is not a usual // RecursiveAction, but rather a CountedCompleter in order to be more robust // in presence of I/O stalls and other scheduling irregularities. public static final class ParsingTask extends CountedCompleter { private final MappedByteBuffer mappedBuf; private final ByteBuffer buf; // Entered from the root task, records the original mmap-ed slice // for later cleanup. public ParsingTask(CountedCompleter p, MappedByteBuffer mappedBuf) { super(p); this.mappedBuf = mappedBuf; this.buf = mappedBuf; } // Entered from the other parsing tasks. public ParsingTask(CountedCompleter p, ByteBuffer buf) { super(p); this.mappedBuf = null; this.buf = buf; } @Override public void compute() { try { internalCompute(); } catch (Exception e) { // Meh, YOLO. e.printStackTrace(); throw new IllegalStateException("Internal error", e); } } @Override public void onCompletion(CountedCompleter caller) { // FJP API: Would be called when this task completes. At that point, // we know the mmap-ed slice is not needed anymore, and can give it // out for unmmaps. We do not do unmmap here, let the main thread // handle it for us, as we go on doing other hot work. if (DIRECT_UNMMAPS && (mappedBuf != null)) { RELEASABLE_BUFFERS.offer(mappedBuf); } } private void internalCompute() throws Exception { int len = buf.limit(); if (len > UNIT_SLICE_SIZE) { // Still a large chunk, let's split it in half. int mid = len / 2; // Figure out the boundary that does not split the line. int w = mid + MAX_LINE_LENGTH; while (buf.get(w - 1) != '\n') { w--; } mid = w; // Fork out! The stack depth would be shallow enough for us to // execute one of the computations directly. // FJP API: Tell there is a pending task. setPendingCount(1); new ParsingTask(this, buf.slice(0, mid)).fork(); // The stack depth would be shallow enough for us to // execute one of the computations directly. new ParsingTask(this, buf.slice(mid, len - mid)).compute(); } else { // Small enough chunk, time to process it. // The call to seqCompute would normally be non-inlined. // Do setup stuff here to save inlining budget. MeasurementsMap map = MAPS.get(); // Force the order we need for bit extraction to work. This fits // most of the hardware very well without introducing platform // dependencies. Note that it would be wrong to use nativeOrder() // here, because we _need_ a particular byte ordering for our // computations to work. It just so happens that most hardware // we have is LE. buf.order(ByteOrder.LITTLE_ENDIAN); // Go! seqCompute(map, buf, len); // FJP API: Notify that this task have completed. tryComplete(); } } private void seqCompute(MeasurementsMap map, ByteBuffer origSlice, int length) throws IOException { Bucket[] buckets = map.buckets; // Slice up our slice! Pecular note here: this instantiates a full new buffer // object, which allows compiler to trust its fields more thoroughly. ByteBuffer slice = origSlice.slice(); // New slice lost the endianness setting, set it up as the original slice. slice.order(ByteOrder.LITTLE_ENDIAN); // Touch the buffer once to let the compiler eject the common checks // for this slice from the loop here. This is an odd, flaky, and sometimes // desperate, but a safe thing to do. slice.get(0); int idx = 0; while (idx < length) { // Parse out the name, computing the hash on the fly. // Reading with ints allows us to guarantee that read would always // be in bounds, since the temperature+EOL is at least 4 bytes // long themselves. This implementation prefers simplicity over // advanced tricks like SWAR. int nameBegin = idx; int nameHash = 0; outer: while (true) { int intName = slice.getInt(idx); for (int c = 0; c < 4; c++) { int b = (intName >> (c << 3)) & 0xFF; if (b == ';') { idx += c + 1; break outer; } nameHash ^= b * 82805; } idx += 4; } int nameEnd = idx - 1; // Parse out the temperature. The rules specify temperatures // are within -99.9..99.9. This means even in the shortest case of // "0.0", we are not out of bounds for the int-sized read. int intTemp = slice.getInt(idx); int neg = 1; if ((intTemp & 0xFF) == '-') { // Unlucky, there is a sign. Record it, shift one byte and read // the remaining digit again. Surprisingly, doing a second read // is not significantly worse than reading into long and trying // to do bit shifts on it. But it is significantly simpler. neg = -1; intTemp >>>= 8; intTemp |= slice.get(idx + 4) << 24; idx++; } // Since the sign is consumed, we are only left with two cases, // which means we can trivially extract the number from int. int temp = 0; if ((intTemp >>> 24) == '\n') { // Case 1: EOL-digitL-point-digitH temp = (((intTemp & 0xFF)) - '0') * 10 + ((intTemp >> 16) & 0xFF) - '0'; idx += 4; } else { // Case 2: digitL-point-digitH-digitHH temp = (((intTemp & 0xFF)) - '0') * 100 + (((intTemp >> 8) & 0xFF) - '0') * 10 + (((intTemp >>> 24)) - '0'); idx += 5; } // All done, just flip the sign, if needed. temp *= neg; // Time to update! Bucket bucket = buckets[nameHash & (MAP_SIZE - 1)]; if ((bucket != null) && (nameHash == bucket.hash) && bucket.matches(slice, nameBegin, nameEnd)) { // Lucky fast path: matching bucket hit. Most of the time we complete here. bucket.merge(temp); } else { // Unlucky, slow path. The method would not be inlined, it is useful // to give it the original slice, so that we keep current hot slice // metadata provably unmodified. map.updateSlow(origSlice, nameBegin, nameEnd, nameHash, temp); } } } } // Fork out the initial tasks. We would normally just fork out one large // task and let it split, but unfortunately buffer API does not allow us // "long" start-s and length-s. So we have to chunk at least by mmap-ed // size first. It is a CountedCompleter for the same reason ParsingTask is. // This also gives us a very nice opportunity to process mmap-ed chunks // one by one, thus allowing incremental unmmaps. public static final class RootTask extends CountedCompleter { public RootTask() { super(null); } @Override public void compute() { try { internalCompute(); } catch (Exception e) { // Meh, YOLO. e.printStackTrace(); throw new IllegalStateException("Internal error", e); } } private void internalCompute() throws Exception { ByteBuffer buf = ByteBuffer.allocateDirect(MAX_LINE_LENGTH); FileChannel fc = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); long start = 0; long size = fc.size(); while (start < size) { long end = Math.min(size, start + MMAP_CHUNK_SIZE); // Read a little chunk into a little buffer. long minEnd = Math.max(0, end - MAX_LINE_LENGTH); buf.rewind(); fc.read(buf, minEnd); // Figure out the boundary that does not split the line. int w = MAX_LINE_LENGTH; while (buf.get(w - 1) != '\n') { w--; } end = minEnd + w; // Fork out the large slice. long len = end - start; MappedByteBuffer slice = fc.map(FileChannel.MapMode.READ_ONLY, start, len); start += len; // FJP API: Announce we have a pending task before forking. addToPendingCount(1); // ...and fork it! new ParsingTask(this, slice).fork(); } // All mappings are up, can close the channel now. fc.close(); // FJP API: We have finished, try to complete the whole task tree. propagateCompletion(); } @Override public void onCompletion(CountedCompleter caller) { // FJP API: This would be called when root task completes along with // all subtasks. This means the processing is done, we can go and // tell main thread about that. try { RELEASABLE_BUFFERS.put(RELEASABLE_BUFFER_POISON_PILL); } catch (Exception e) { throw new IllegalStateException(e); } } } // ========================= Invocation ========================= public static void main(String[] args) throws Exception { // Instantiate a separate FJP to match the parallelism accurately, without // relying on common pool defaults. ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors()); // This little line carries the whole world pool.submit(new RootTask()); // While the root task is working, prepare what we need for the // end of the run. Go and try to report something to prepare the // reporting code for execution. This prepares classes, storage, // and some profiles for eventual execution. MeasurementsMap map = new MeasurementsMap(); Row[] rows = new Row[MAP_SIZE]; StringBuilder sb = new StringBuilder(16384); report(map, rows, sb); sb.setLength(0); // Nothing else is left to do preparation-wise. Now see if we can clean up // buffers that tasks do not need anymore. The root task would communicate // that it is done by giving us a poison pill. ByteBuffer buf; while ((buf = RELEASABLE_BUFFERS.take()) != RELEASABLE_BUFFER_POISON_PILL) { DirectUnmaps.invokeCleaner(buf); } // All done. Merge results from thread-local maps... for (MeasurementsMap m : ALL_MAPS) { map.merge(m); } // ...and truly report them System.out.println(report(map, rows, sb)); } private static String report(MeasurementsMap map, Row[] rows, StringBuilder sb) { int rowCount = map.fill(rows); Arrays.sort(rows, 0, rowCount); sb.append("{"); boolean first = true; for (int c = 0; c < rowCount; c++) { if (c != 0) { sb.append(", "); } rows[c].printTo(sb); } sb.append("}"); return sb.toString(); } // ========================= Reporting ========================= private static final class Row implements Comparable { private final String name; private final double min; private final double max; private final double avg; public Row(String name, double min, double avg, double max) { this.name = name; this.min = min; this.max = max; this.avg = avg; } @Override public int compareTo(Row o) { return name.compareTo(o.name); } public void printTo(StringBuilder sb) { sb.append(name); sb.append("="); sb.append(min); sb.append("/"); sb.append(avg); sb.append("/"); sb.append(max); } } // ========================= Utils ========================= // Tries to figure out if calling Cleaner directly on the DirectByteBuffer // is possible. If this fails, we still go on. public static class DirectUnmaps { private static final Method METHOD_GET_CLEANER; private static final Method METHOD_CLEANER_CLEAN; static Method getCleaner() { try { ByteBuffer dbb = ByteBuffer.allocateDirect(1); Method m = dbb.getClass().getMethod("cleaner"); m.setAccessible(true); return m; } catch (NoSuchMethodException | InaccessibleObjectException e) { return null; } } static Method getCleanerClean(Method methodGetCleaner) { try { ByteBuffer dbb = ByteBuffer.allocateDirect(1); Object cleaner = methodGetCleaner.invoke(dbb); Method m = cleaner.getClass().getMethod("clean"); m.setAccessible(true); m.invoke(cleaner); return m; } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InaccessibleObjectException e) { return null; } } static { METHOD_GET_CLEANER = getCleaner(); METHOD_CLEANER_CLEAN = (METHOD_GET_CLEANER != null) ? getCleanerClean(METHOD_GET_CLEANER) : null; } public static void invokeCleaner(ByteBuffer bb) { if (METHOD_GET_CLEANER == null || METHOD_CLEANER_CLEAN == null) { return; } try { METHOD_CLEANER_CLEAN.invoke(METHOD_GET_CLEANER.invoke(bb)); } catch (InvocationTargetException | IllegalAccessException e) { throw new IllegalStateException("Cannot happen at this point", e); } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_slovdahl.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; import java.nio.channels.FileChannel; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.StringJoiner; import java.util.TreeMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.reducing; public class CalculateAverage_slovdahl { private static final String FILE = "./measurements.txt"; private static final int SLICE_SIZE = 1_048_576; public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { int segments = Runtime.getRuntime().availableProcessors() - 1; try (Arena arena = Arena.ofShared(); FileChannel channel = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ); ExecutorService executor = Executors.newThreadPerTaskExecutor(Executors.defaultThreadFactory())) { long size = channel.size(); if (size < SLICE_SIZE) { segments = 1; } long idealSegmentSize = size / segments; MemorySegment mappedFile = channel.map(FileChannel.MapMode.READ_ONLY, 0, size, arena); var futures = new ArrayList>>(segments); long segmentStart = 0; for (int i = 1; i <= segments; i++) { long actualSegmentOffset = idealSegmentSize * i; while (actualSegmentOffset < size && mappedFile.get(ValueLayout.JAVA_BYTE, actualSegmentOffset) != (byte) '\n') { actualSegmentOffset++; } long end = actualSegmentOffset - segmentStart; if (segmentStart + actualSegmentOffset - segmentStart + 1 < size) { end += 1; } MemorySegment segment = mappedFile.asSlice(segmentStart, end); segmentStart = actualSegmentOffset + 1; futures.add(executor.submit(() -> { byte[] array = new byte[SLICE_SIZE]; MemorySegment bufferSegment = MemorySegment.ofArray(array); long position = 0; long segmentSize = segment.byteSize(); Map map = HashMap.newHashMap(10_000); while (position < segmentSize) { long thisSliceSize = Math.min(SLICE_SIZE, segmentSize - position); MemorySegment.copy( segment, ValueLayout.JAVA_BYTE, position, bufferSegment, ValueLayout.JAVA_BYTE, 0, thisSliceSize); if (thisSliceSize % 8 != 0) { bufferSegment .asSlice(thisSliceSize) .fill((byte) 0); } int newlinePosition = 0; int startOffset = 0; while (true) { int semicolonPosition = nextOccurrence(array, (byte) ';', startOffset); if (semicolonPosition < 0) { break; } int eolPosition = nextOccurrence(array, (byte) '\n', startOffset); if (eolPosition < 0) { if (semicolonPosition < segmentSize - 4) { break; } else { newlinePosition = (int) segmentSize; } } else { newlinePosition = eolPosition; } byte[] nameArray = new byte[semicolonPosition - startOffset]; System.arraycopy(array, startOffset, nameArray, 0, semicolonPosition - startOffset); Station station = new Station(nameArray); int temperatureStart = semicolonPosition + 1; int temperatureLength = newlinePosition - semicolonPosition - 1; int temperatureIntValue; if (array[temperatureStart] == '-') { if (temperatureLength == 4) { temperatureIntValue = -1 * ((array[temperatureStart + 1] - 48) * 10 + (array[temperatureStart + 3] - 48)); } else { temperatureIntValue = -1 * ((array[temperatureStart + 1] - 48) * 100 + (array[temperatureStart + 2] - 48) * 10 + (array[temperatureStart + 4] - 48)); } } else { if (temperatureLength == 3) { temperatureIntValue = (array[temperatureStart] - 48) * 10 + (array[temperatureStart + 2] - 48); } else { temperatureIntValue = (array[temperatureStart] - 48) * 100 + (array[temperatureStart + 1] - 48) * 10 + (array[temperatureStart + 3] - 48); } } MeasurementAggregator agg = map.get(station); if (agg == null) { agg = new MeasurementAggregator(); map.put(station, agg); } agg.min = Math.min(agg.min, temperatureIntValue); agg.max = Math.max(agg.max, temperatureIntValue); agg.sum += temperatureIntValue; agg.count++; // Make sure the next iteration won't find the same delimiters. array[semicolonPosition] = (byte) 0; array[newlinePosition] = (byte) 0; startOffset = newlinePosition + 1; } position += newlinePosition + 1; } return map; })); } TreeMap result = futures.stream() .map(f -> { try { return f.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }) .flatMap(m -> m.entrySet().stream()) .collect(groupingBy( e -> new String(e.getKey().name()), TreeMap::new, collectingAndThen( reducing( new MeasurementAggregator(), Map.Entry::getValue, (agg1, agg2) -> { MeasurementAggregator res = new MeasurementAggregator(); res.min = Math.min(agg1.min, agg2.min); res.max = Math.max(agg1.max, agg2.max); res.sum = agg1.sum + agg2.sum; res.count = agg1.count + agg2.count; return res; }), agg -> new ResultRow( agg.min / 10.0, (Math.round((agg.sum / 10.0) * 10.0) / 10.0) / agg.count, agg.max / 10.0)))); System.out.println(result); executor.shutdownNow(); } } private static int nextOccurrence(byte[] data, byte needle, int offset) { while (offset < data.length) { if (data[offset] == needle) { return offset; } offset++; } return -1; } private record Station(byte[] name, int hash) { private Station(byte[] name) { this(name, Arrays.hashCode(name)); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Station station = (Station) o; return Arrays.equals(name, station.name); } @Override public int hashCode() { return hash; } @Override public String toString() { return new StringJoiner(", ", Station.class.getSimpleName() + "[", "]") .add("name=" + new String(name)) .add("hash=" + hash) .toString(); } } private static class MeasurementAggregator { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum; private long count; } private record ResultRow(double min, double mean, double max) { @Override public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_spullara.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; public class CalculateAverage_spullara { private static final String FILE = "./measurements.txt"; /* * My results on this computer: * * CalculateAverage: 2m37.788s * CalculateAverage_royvanrijn: 0m29.639s * CalculateAverage_spullara: 0m2.013s * */ public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { long start = System.currentTimeMillis(); var filename = args.length == 0 ? FILE : args[0]; var file = new File(filename); var resultsMap = getFileSegments(file).stream().map(segment -> { var resultMap = new ByteArrayToResultMap(); long segmentEnd = segment.end(); try (var fileChannel = (FileChannel) Files.newByteChannel(Path.of(filename), StandardOpenOption.READ)) { var bb = fileChannel.map(FileChannel.MapMode.READ_ONLY, segment.start(), segmentEnd - segment.start()); // Up to 100 characters for a city name var buffer = new byte[100]; int startLine; int limit = bb.limit(); while ((startLine = bb.position()) < limit) { int currentPosition = startLine; byte b; int offset = 0; int hash = 0; while (currentPosition != segmentEnd && (b = bb.get(currentPosition++)) != ';') { buffer[offset++] = b; hash = 31 * hash + b; } int temp; int negative = 1; // Inspired by @yemreinci to unroll this even further if (bb.get(currentPosition) == '-') { negative = -1; currentPosition++; } if (bb.get(currentPosition + 1) == '.') { temp = negative * ((bb.get(currentPosition) - '0') * 10 + (bb.get(currentPosition + 2) - '0')); currentPosition += 3; } else { temp = negative * ((bb.get(currentPosition) - '0') * 100 + ((bb.get(currentPosition + 1) - '0') * 10 + (bb.get(currentPosition + 3) - '0'))); currentPosition += 4; } if (bb.get(currentPosition) == '\r') { currentPosition++; } currentPosition++; resultMap.putOrMerge(buffer, 0, offset, temp / 10.0, hash); bb.position(currentPosition); } return resultMap; } catch (IOException e) { throw new RuntimeException(e); } }).parallel().flatMap(partition -> partition.getAll().stream()) .collect(Collectors.toMap(e -> new String(e.key()), Entry::value, CalculateAverage_spullara::merge, TreeMap::new)); System.out.println(resultsMap); } private static List getFileSegments(File file) throws IOException { int numberOfSegments = Runtime.getRuntime().availableProcessors(); long fileSize = file.length(); long segmentSize = fileSize / numberOfSegments; List segments = new ArrayList<>(numberOfSegments); // Pointless to split small files if (segmentSize < 1_000_000) { segments.add(new FileSegment(0, fileSize)); return segments; } try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) { for (int i = 0; i < numberOfSegments; i++) { long segStart = i * segmentSize; long segEnd = (i == numberOfSegments - 1) ? fileSize : segStart + segmentSize; segStart = findSegment(i, 0, randomAccessFile, segStart, segEnd); segEnd = findSegment(i, numberOfSegments - 1, randomAccessFile, segEnd, fileSize); segments.add(new FileSegment(segStart, segEnd)); } } return segments; } private static Result merge(Result v, Result value) { return merge(v, value.min, value.max, value.sum, value.count); } private static Result merge(Result v, double value, double value1, double value2, long value3) { v.min = Math.min(v.min, value); v.max = Math.max(v.max, value1); v.sum += value2; v.count += value3; return v; } private static long findSegment(int i, int skipSegment, RandomAccessFile raf, long location, long fileSize) throws IOException { if (i != skipSegment) { raf.seek(location); while (location < fileSize) { location++; if (raf.read() == '\n') break; } } return location; } } class Result { double min, max, sum; long count; Result(double value) { min = max = sum = value; this.count = 1; } @Override public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } double round(double v) { return Math.round(v * 10.0) / 10.0; } } record Entry(byte[] key, Result value) { } record FileSegment(long start, long end) { } class ByteArrayToResultMap { public static final int MAPSIZE = 1024 * 128; Result[] slots = new Result[MAPSIZE]; byte[][] keys = new byte[MAPSIZE][]; public void putOrMerge(byte[] key, int offset, int size, double temp, int hash) { int slot = hash & (slots.length - 1); var slotValue = slots[slot]; // Linear probe for open slot while (slotValue != null && (keys[slot].length != size || !Arrays.equals(keys[slot], 0, size, key, offset, size))) { slot = (slot + 1) & (slots.length - 1); slotValue = slots[slot]; } Result value = slotValue; if (value == null) { slots[slot] = new Result(temp); byte[] bytes = new byte[size]; System.arraycopy(key, offset, bytes, 0, size); keys[slot] = bytes; } else { value.min = Math.min(value.min, temp); value.max = Math.max(value.max, temp); value.sum += temp; value.count += 1; } } // Get all pairs public List getAll() { List result = new ArrayList<>(slots.length); for (int i = 0; i < slots.length; i++) { Result slotValue = slots[i]; if (slotValue != null) { result.add(new Entry(keys[i], slotValue)); } } return result; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_stephenvonworley.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.*; import java.lang.foreign.*; import java.lang.reflect.Field; import java.nio.*; import java.nio.channels.*; import java.nio.file.*; import java.nio.charset.*; import java.util.*; import java.util.concurrent.*; import java.util.stream.*; import sun.misc.Unsafe; /* * Stephen Von Worley's (von@von.io) entry to Gunnar Morling's "One Billion Row Challenge": * https://www.morling.dev/blog/one-billion-row-challenge/ * * To compute the desired result, this program: * 1. Memory maps the input file. * 2. Partitions the file into a queue of Chunks, which delimit sections of the file. * 3. Spawns one thread per processor. Each thread: * a. Allocates a Table, which will accumulate names and tallies (min/max/total/count). * b. Get a Chunk from the queue. * c. Processes the Chunk using a parser that reads the Chunk simultaneously at three * different, evenly-spaced locations, using heavily-optimized scalar code. * d. Repeats steps b and c until there are no more Chunks. * 4. Aggregates the resulting Tables into a treemap of names to Tallies. * 5. Outputs the names and Tallies in ascending name order. * * Runs fastest as a natively-compiled, standalone binary, as might be produced by Graal's * `native-image` utility. Tested with Oracle Graal 21.0.2. * * Incorporates code authored by a number of submitters, including Thomas Wue, Quan Anh * Mai, and others. * * Thanks y'all, and Happy Rowing! * Steve * von@von.io * www.von.io */ public class CalculateAverage_stephenvonworley { private static final int NAME_LIMIT = 10000; private static final long CHUNK_SIZE = 5000000; private static final long CHUNK_PAD = 200; private static final long CHUNK_PARSE3_LIMIT = 1000; private static final long GOLDEN_LONG = 0x9e3779b97f4a7c15L; private static final long TALLY_BITS = 7; private static final long TALLY_SIZE = 1L << TALLY_BITS; private static final long HASH_BITS = 16; private static final long HASH_MASK = ((1L << HASH_BITS) - 1) << TALLY_BITS; private static final long TABLE_SIZE = 1L << (HASH_BITS + TALLY_BITS); private static final long OFFSET_MIN = 0; private static final long OFFSET_MAX = 2; private static final long OFFSET_COUNT = 4; private static final long OFFSET_TOTAL = 8; private static final long OFFSET_LEN = 16; private static final long OFFSET_NAME = 17; private static final Unsafe unsafe; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null); } catch (Exception e) { throw new RuntimeException("Exception initializing unsafe", e); } } public static void main(String[] args) throws IOException, InterruptedException { if (!List.of(args).contains("--worker")) { spawnWorker(); return; } MemorySegment in = map("./measurements.txt"); Queue chunks = partition(in); List tables = process(chunks, processorCount()); Map nameToTally = aggregate(tables); System.out.println(nameToTally); System.out.close(); } // credit: "Spawn worker" code by Thomas Wue private static void spawnWorker() throws IOException { ProcessHandle.Info info = ProcessHandle.current().info(); ArrayList workerCommand = new ArrayList<>(); info.command().ifPresent(workerCommand::add); info.arguments().ifPresent(args -> workerCommand.addAll(Arrays.asList(args))); workerCommand.add("--worker"); new ProcessBuilder().command(workerCommand).inheritIO().redirectOutput(ProcessBuilder.Redirect.PIPE) .start().getInputStream().transferTo(System.out); } private static int processorCount() { return Runtime.getRuntime().availableProcessors(); } private static MemorySegment map(String path) throws IOException { FileChannel file = FileChannel.open(Path.of(path), StandardOpenOption.READ); return file.map(FileChannel.MapMode.READ_ONLY, 0, file.size(), Arena.global()); } private static MemorySegment allocate(long len) { return Arena.global().allocate(len, 4096); } private static Queue partition(MemorySegment in) throws IOException { Queue chunks = new ConcurrentLinkedDeque<>(); long address = in.address(); long len = in.byteSize(); long start = address; while (start < address + len) { long end = start + CHUNK_SIZE; if (end >= address + len) { end = address + len; } else { end = afterNewline(end); } Chunk chunk; if (end + CHUNK_PAD < address + len) { chunk = new Chunk(start, end); } else { MemorySegment padded = allocate(end - start + CHUNK_PAD); MemorySegment.copy(in, start - address, padded, 0, end - start); chunk = new Chunk(padded.address(), padded.address() + (end - start)); } chunks.offer(chunk); start = end; } return chunks; } private static List
process(Queue chunks, int threadCount) throws InterruptedException { List
tables = Collections.synchronizedList(new ArrayList<>(threadCount)); List threads = new ArrayList<>(threadCount); for (int i = 0; i < threadCount; i++) { Thread thread = new Thread(() -> { Table t = new Table(); tables.add(t); Chunk chunk; while ((chunk = chunks.poll()) != null) { parse3(chunk.start(), chunk.end(), t); } }); threads.add(thread); thread.start(); } for (Thread thread : threads) { thread.join(); } return tables; } private static Map aggregate(List
tables) { Map nameToTally = new TreeMap<>(); tables.forEach(table -> aggregate(nameToTally, table)); return nameToTally; } private static void aggregate(Map nameToTally, Table table) { table.process((name, min, max, total, count) -> nameToTally.computeIfAbsent(name, _ -> new Tally()).add(min, max, total, count)); } private static void parse3(long start, long end, Table table) { if (end - start < CHUNK_PARSE3_LIMIT) { parse1(start, end, table); return; } final long tallies = table.tallies; long part = (end - start) / 3; long startA = start; long startB = afterNewline(start + part); long startC = afterNewline(start + 2 * part); long endA = startB; long endB = startC; long endC = end; while (true) { long N = min( remaining(startA, endA), remaining(startB, endB), remaining(startC, endC)); if (N <= 1) { break; } while (N > 0) { long semicolonA = semicolon(startA); long semicolonB = semicolon(startB); long semicolonC = semicolon(startC); long tallyA = locate(startA, semicolonA, tallies, table); long tallyB = locate(startB, semicolonB, tallies, table); long tallyC = locate(startC, semicolonC, tallies, table); long numberA = number(semicolonA); tally(tallyA, numberA); long numberB = number(semicolonB); tally(tallyB, numberB); long numberC = number(semicolonC); tally(tallyC, numberC); startA = next(semicolonA); startB = next(semicolonB); startC = next(semicolonC); N--; } } parse1(startA, endA, table); parse1(startB, endB, table); parse1(startC, endC, table); } private static void parse1(long start, long end, Table table) { final long tallies = table.tallies; while (start < end) { long semicolon = semicolon(start); long tally = locate(start, semicolon, tallies, table); long number = number(semicolon); tally(tally, number); start = next(semicolon); } } private static long remaining(long start, long end) { return (end - start) >> 7; } // credit: Adapted from code by Thomas Wue private static long semicolon(long start) { start++; long word = getLong(start); long input = word ^ 0x3B3B3B3B3B3B3B3BL; long tmp = (input - 0x0101010101010101L) & ~input & 0x8080808080808080L; if (tmp != 0) { return start + (Long.numberOfTrailingZeros(tmp) >>> 3); } while (true) { start += 8; long word2 = getLong(start); long input2 = word2 ^ 0x3B3B3B3B3B3B3B3BL; long tmp2 = (input2 - 0x0101010101010101L) & ~input2 & 0x8080808080808080L; if (tmp2 != 0) { return start + (Long.numberOfTrailingZeros(tmp2) >>> 3); } } } private static long trim(long value, long remove) { long shift = remove << 3; return ((value << shift) >>> shift); } // https://softwareengineering.stackexchange.com/questions/402542/where-do-magic-hashing-constants-like-0x9e3779b9-and-0x9e3779b1-come-from private static long locate(long start, long semicolon, long tallies, Table table) { long len = semicolon - start; long word = getLong(start); if (len <= 8) { word = trim(word, 8 - len); long hash = word * GOLDEN_LONG; long offset = (hash >>> (64 - HASH_BITS)) << TALLY_BITS; while (true) { long tally = tallies + offset; long tlen = getByte(tally + OFFSET_LEN); long tword = getLong(tally + OFFSET_NAME); if (len == tlen && word == tword) { return tally; } if (tword == 0) { init(tally, start, len, table); return tally; } offset = (offset + TALLY_SIZE) & HASH_MASK; } } else { long word2 = getLong(semicolon - 8); long hash = (word + word2) * GOLDEN_LONG; long offset = (hash >>> (64 - HASH_BITS)) << TALLY_BITS; while (true) { long tally = tallies + offset; long tword = getLong(tally + OFFSET_NAME); if (len <= 16) { long tlen = getByte(tally + OFFSET_LEN); long tword2 = getLong(tally + OFFSET_NAME + len - 8); if (len == tlen && word == tword && word2 == tword2) { return tally; } } else { if (match(tally, start, len)) { return tally; } } if (tword == 0) { init(tally, start, len, table); return tally; } offset = (offset + TALLY_SIZE) & HASH_MASK; } } } private static void init(long tally, long start, long len, Table t) { setShort(tally + OFFSET_MIN, Short.MAX_VALUE); setShort(tally + OFFSET_MAX, Short.MIN_VALUE); setByte(tally + OFFSET_LEN, (byte) len); copyMemory(start, tally + OFFSET_NAME, len); t.addresses[t.count++] = tally; } private static boolean match(long tally, long name, long len) { if (getByte(tally + OFFSET_LEN) != len) { return false; } long a = name; long b = tally + OFFSET_NAME; while (len > 7) { if (getLong(a) != getLong(b)) { return false; } a += 8; b += 8; len -= 8; } if (len > 0) { return (trim(getLong(a), 8 - len) == getLong(b)); } return true; } // credit: Wonderfully-fast number parsing implementation by Quan Anh Mai private static long number(long semicolon) { long numberWord = getLong(semicolon + 1); int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000); int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise long signed = (~numberWord << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii to digit value long digits = ((numberWord & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + 0x00UU00TTHH000000 * 10 + 0xUU00TTHH00000000 * 100 long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; return (absValue ^ signed) - signed; } private static void tally(long tally, long number) { short min = getShort(tally + OFFSET_MIN); short max = getShort(tally + OFFSET_MAX); int count = getInt(tally + OFFSET_COUNT); long total = getLong(tally + OFFSET_TOTAL); if (number < min) { setShort(tally + OFFSET_MIN, (short) number); } if (number > max) { setShort(tally + OFFSET_MAX, (short) number); } setInt(tally + OFFSET_COUNT, count + 1); setLong(tally + OFFSET_TOTAL, total + number); } private static long next(long semicolon) { long word = getLong(semicolon); semicolon += 7; semicolon -= (~word >>> (24 + 4)) & 1; semicolon -= (~word >>> (16 + 4 - 1)) & 2; return semicolon; } private static long afterNewline(long start) { while (getByte(start) != '\n') start++; return start + 1; } private static long min(long a, long b, long c) { return Math.min(a, Math.min(b, c)); } private static byte getByte(long addr) { return unsafe.getByte(addr); } private static short getShort(long addr) { return unsafe.getShort(addr); } private static int getInt(long addr) { return unsafe.getInt(addr); } private static long getLong(long addr) { return unsafe.getLong(addr); } private static void setByte(long addr, byte value) { unsafe.putByte(addr, value); } private static void setShort(long addr, short value) { unsafe.putShort(addr, value); } private static void setInt(long addr, int value) { unsafe.putInt(addr, value); } private static void setLong(long addr, long value) { unsafe.putLong(addr, value); } private static void copyMemory(long srcAddr, long dstAddr, long count) { unsafe.copyMemory(srcAddr, dstAddr, count); } private static record Chunk(long start, long end) { } private static class Table { public final long tallies; public final long[] addresses; public int count; public Table() { tallies = allocate(TABLE_SIZE).address(); addresses = new long[NAME_LIMIT]; count = 0; } public void process(Consumer consumer) { for (int i = 0; i < count; i++) { long address = addresses[i]; int len = getByte(address + OFFSET_LEN); byte[] bytes = new byte[len]; for (int j = 0; j < len; j++) { bytes[j] = getByte(address + OFFSET_NAME + j); } String name = new String(bytes, StandardCharsets.UTF_8); long min = getShort(address + OFFSET_MIN); long max = getShort(address + OFFSET_MAX); long total = getLong(address + OFFSET_TOTAL); long count = getInt(address + OFFSET_COUNT); consumer.consume(name, min, max, total, count); } } } private static interface Consumer { public void consume(String name, long min, long max, long total, long count); } private static class Tally { private long min; private long max; private long total; private long count; public Tally() { this.min = Short.MAX_VALUE; this.max = Short.MIN_VALUE; this.total = 0; this.count = 0; } public void add(long addMin, long addMax, long addTotal, long addCount) { min = Math.min(min, addMin); max = Math.max(max, addMax); total += addTotal; count += addCount; } public long getMin() { return min; } public long getMax() { return max; } public long getTotal() { return total; } public long getCount() { return count; } public String toString() { return String.format("%.1f/%.1f/%.1f", getMin() / 10.0, getTotal() / (10.0 * getCount()), getMax() / 10.0); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_sudhirtumati.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; public class CalculateAverage_sudhirtumati { private static final String FILE = "./measurements.txt"; private static final int bufferSize = 8192; private static final byte SEMICOLON = (byte) ';'; private static final byte NEW_LINE = (byte) '\n'; private static final int THREAD_COUNT = Runtime.getRuntime().availableProcessors(); private static final Semaphore PERMITS = new Semaphore(THREAD_COUNT); private static final MeasurementAggregator globalAggregator = new MeasurementAggregator(); private static final Semaphore AGGREGATOR_PERMITS = new Semaphore(1); private static final Map LOCATION_STORE = new ConcurrentHashMap<>(); public static void main(String[] args) throws IOException, InterruptedException { CalculateAverage_sudhirtumati instance = new CalculateAverage_sudhirtumati(); instance.chunkProcess(); } private void chunkProcess() throws IOException, InterruptedException { try (FileInputStream is = new FileInputStream(FILE); FileChannel fc = is.getChannel()) { for (int i = 0; i < THREAD_COUNT; i++) { PERMITS.acquire(); Thread t = new ChunkProcessingThread(i, fc); t.setName(STR."T\{i}"); t.start(); } do { Thread.sleep(100); } while (PERMITS.availablePermits() != THREAD_COUNT); } System.out.println(globalAggregator.getResult()); } static class ChunkProcessingThread extends Thread { private int index; private final FileChannel fc; private final MeasurementAggregator aggregator; ChunkProcessingThread(int index, FileChannel fc) { this.index = index; this.fc = fc; aggregator = new MeasurementAggregator(); } @Override public void run() { ByteBuffer buffer = ByteBuffer.allocate(index == 0 ? bufferSize : bufferSize + 50); long fcPosition = index == 0 ? 0 : (((long) index * bufferSize) - 50); try { while (fc.read(buffer, fcPosition) != -1) { buffer.flip(); if (index != 0 /* && fc.position() != bufferSize */) { seekStartPos(buffer); } processBuffer(buffer); index += THREAD_COUNT; fcPosition = ((long) index * bufferSize) - 50L; if (buffer.capacity() == 8192) { buffer = ByteBuffer.allocate(bufferSize + 50); } buffer.position(0); } AGGREGATOR_PERMITS.acquire(); globalAggregator.process(aggregator); AGGREGATOR_PERMITS.release(); } catch (IOException | InterruptedException e) { throw new RuntimeException(e); } PERMITS.release(); } private void processBuffer(ByteBuffer buffer) throws IOException { int mStartMark = buffer.position(); int tStartMark = -1; int count = buffer.position(); do { byte b = buffer.get(count); if (b == SEMICOLON) { tStartMark = count; } else if (b == NEW_LINE) { byte[] locArr = new byte[tStartMark - mStartMark]; byte[] tempArr = new byte[count - tStartMark]; buffer.get(mStartMark, locArr); buffer.get(mStartMark + locArr.length + 1, tempArr); aggregator.process(locArr, tempArr); mStartMark = count + 1; } count++; } while (count < buffer.limit()); } private void seekStartPos(ByteBuffer buffer) { int i = buffer.limit() > 50 ? 49 : buffer.limit() - 2; for (; i >= 0; i--) { if (buffer.get(i) == NEW_LINE) { buffer.position(i + 1); break; } } } } static final class MeasurementAggregator { private static final long MAX_VALUE_DIVIDE_10 = Long.MAX_VALUE / 10; private final Map store = new HashMap<>(); public void process(MeasurementAggregator other) { other.store.forEach((k, v) -> { Measurement m = store.get(k); if (m == null) { m = new Measurement(); store.put(k, m); } m.process(v); }); } public void process(byte[] location, byte[] temperature) throws IOException { Integer hashCode = Arrays.hashCode(location); LOCATION_STORE.computeIfAbsent(hashCode, _ -> new String(location)); // String loc = new String(location); Measurement measurement = store.get(hashCode); if (measurement == null) { measurement = new Measurement(); store.put(hashCode, measurement); } double tempD = parseDouble(temperature); measurement.process(tempD); } public double parseDouble(byte[] bytes) { long value = 0; int exp = 0; boolean negative = false; int decimalPlaces = Integer.MIN_VALUE; int index = 0; int ch = bytes[index]; if (ch == '-') { negative = true; ch = bytes[++index]; } while (index < bytes.length) { if (ch >= '0' && ch <= '9') { while (value >= MAX_VALUE_DIVIDE_10) { value >>>= 1; exp++; } value = value * 10 + (ch - '0'); decimalPlaces++; } else if (ch == '.') { decimalPlaces = 0; } if (index == bytes.length - 1) { break; } else { ch = bytes[++index]; } } return asDouble(value, exp, negative, decimalPlaces); } private static double asDouble(long value, int exp, boolean negative, int decimalPlaces) { if (decimalPlaces > 0 && value < Long.MAX_VALUE / 2) { if (value < Long.MAX_VALUE / (1L << 32)) { exp -= 32; value <<= 32; } if (value < Long.MAX_VALUE / (1L << 16)) { exp -= 16; value <<= 16; } if (value < Long.MAX_VALUE / (1L << 8)) { exp -= 8; value <<= 8; } if (value < Long.MAX_VALUE / (1L << 4)) { exp -= 4; value <<= 4; } if (value < Long.MAX_VALUE / (1L << 2)) { exp -= 2; value <<= 2; } if (value < Long.MAX_VALUE / (1L << 1)) { exp -= 1; value <<= 1; } } for (; decimalPlaces > 0; decimalPlaces--) { exp--; long mod = value % 5; value /= 5; int modDiv = 1; if (value < Long.MAX_VALUE / (1L << 4)) { exp -= 4; value <<= 4; modDiv <<= 4; } if (value < Long.MAX_VALUE / (1L << 2)) { exp -= 2; value <<= 2; modDiv <<= 2; } if (value < Long.MAX_VALUE / (1L << 1)) { exp -= 1; value <<= 1; modDiv <<= 1; } if (decimalPlaces > 1) value += modDiv * mod / 5; else value += (modDiv * mod + 4) / 5; } final double d = Math.scalb((double) value, exp); return negative ? -d : d; } public String getResult() { Map sortedMap = new TreeMap<>(); store.forEach((k, v) -> sortedMap.put(LOCATION_STORE.get(k), v)); return sortedMap.toString(); } } static final class Measurement { private double min = Double.POSITIVE_INFINITY; private double max = Double.NEGATIVE_INFINITY; private double sum; private long count; public void process(double value) { if (value < min) { min = value; } if (value > max) { max = value; } sum += value; count++; } public void process(Measurement other) { if (other.min < min) { this.min = other.min; } if (other.max > max) { this.max = other.max; } this.sum += other.sum; this.count += other.count; } public String toString() { ResultRow result = new ResultRow(min, sum, count, max); return result.toString(); } } private record ResultRow(double min, double sum, double count, double max) { public String toString() { return STR."\{round(min)}/\{round((Math.round(sum * 10.0) / 10.0) / count)}/\{round(max)}"; } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_thanhtrinity.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.Math.round; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardOpenOption.READ; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.TreeMap; import java.util.stream.Collectors; public class CalculateAverage_thanhtrinity { private static final String FILE = "./measurements.txt"; private static final int TOTAL_PROCESSOR = Runtime.getRuntime().availableProcessors(); public static void main(String[] args) throws IOException, InterruptedException { // System.out.println("Num Of Processor:" + TOTAL_PROCESSOR); var threads = new ArrayList(); var fileChannel = FileChannel.open(Path.of(FILE), READ); long fullSize = fileChannel.size(); // System.out.println("FullSize:" + fullSize); var standardChunkSize = fullSize / TOTAL_PROCESSOR; // System.out.println("StandardChunkSize:" + standardChunkSize); var chunkDataList = new ChunkData[TOTAL_PROCESSOR]; var start = 0L; var end = standardChunkSize; for (int index = 0; index < TOTAL_PROCESSOR; index++) { long newStart = start; end = adjustBreakLinePosition(start + standardChunkSize) + 1; end = end >= fullSize ? fullSize : end; var chunkSize = end - start; // Have checked with virtual thread but it slower than normal thread int taskIdx = index; var thread = new Thread(() -> { try (var file = new RandomAccessFile(FILE, "r"); var fc = file.getChannel()) { var buffer = fc.map(READ_ONLY, newStart, chunkSize); chunkDataList[taskIdx] = processBufferData(buffer, taskIdx + 1); } catch (IOException e) { e.printStackTrace(); } }); thread.start(); threads.add(thread); if (end == fullSize) { break; } start = end; } for (var thread : threads) { thread.join(); } consolidateData(chunkDataList); } private static long adjustBreakLinePosition(long position) throws IOException { try (var file = new RandomAccessFile(FILE, "r")) { file.seek(position); while (file.read() != '\n' && position < file.length()) { position++; } } return position; } private static ChunkData processBufferData(MappedByteBuffer buffer, long taskIdx) { int currentIdx = 0; int breakLineIndex = 0; final int capacity = 100000; var cities = new City[capacity]; var isProcessKey = true; var hashKey = 0; double result = 0; int integerPart = 0; double fractionalPart = 0; boolean isFractional = false; double divisorForFraction = 1; boolean isNegative = false; for (int i = 0; i < buffer.limit(); i++) { var b = buffer.get(); var position = i + 1; if (isProcessKey) { if (b == ';') { hashKey = hashKey % capacity; currentIdx = hashKey; var name = new byte[position - breakLineIndex]; buffer.get(breakLineIndex, name, 0, position - breakLineIndex - 1); if (cities[currentIdx] == null) { cities[currentIdx] = new City(name); } else if (!Arrays.equals(cities[currentIdx].getName(), name)) { while (cities[currentIdx] != null) { // Continue probing until empty slot currentIdx = (currentIdx + 1) % capacity; } cities[currentIdx] = new City(name); } hashKey = 0; isProcessKey = false; } else { hashKey = (31 * hashKey + b) & 0x7FFFFFFF; } } else { if (b == '\n') { fractionalPart /= divisorForFraction; result = integerPart + fractionalPart; if (isNegative) { result *= -1; } cities[currentIdx].updateTemp(result); breakLineIndex = position; // reset parameter hashKey = 0; result = 0; integerPart = 0; fractionalPart = 0; isFractional = false; divisorForFraction = 1; isNegative = false; isProcessKey = true; } else { switch (b) { case '-': isNegative = true; break; case '.': isFractional = true; break; default: if (!isFractional) { integerPart = integerPart * 10 + (b - '0'); } else { divisorForFraction *= 10; fractionalPart = fractionalPart * 10 + (b - '0'); } break; } } } } buffer = null; System.gc(); var citiesList = Arrays.stream(cities).filter(Objects::nonNull).toList(); return new ChunkData(citiesList); } private static void consolidateData(ChunkData[] citiesTempChunk) { var cities = Arrays.stream(citiesTempChunk).filter(Objects::nonNull) .flatMap(chunkData -> chunkData.cities().stream()) .collect( Collectors.toMap( City::getKey, city -> city, City::combine)); System.out.println(new TreeMap<>(cities)); } record ChunkData(List cities) { } } class City { private byte[] name; private String key; private double min = Double.MAX_VALUE; private double max = -Double.MAX_VALUE; private double sum = 0L; private int count = 0; public City() { } public City(byte[] name) { this.name = name; } public void updateTemp(double temp) { min = min(min, temp); max = max(max, temp); sum += temp; count++; } public String getKey() { int i = 0; while (i < name.length && name[i] != 0) { i++; } key = new String(name, 0, i, UTF_8); return key; } public static City combine(City t1, City t2) { City result = new City(); result.min = min(t1.min, t2.min); result.max = max(t1.max, t2.max); result.sum = t1.sum + t2.sum; result.count = t1.count + t2.count; return result; } @Override public String toString() { return roundNumber(min) + "/" + roundNumber(sum / count) + "/" + roundNumber(max); } public void setName(byte[] name) { this.name = name; } public byte[] getName() { return this.name; } private static double roundNumber(double value) { return round(value * 10.0) / 10.0; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_thomaswue.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.channels.FileChannel; import java.util.*; import java.util.concurrent.atomic.AtomicLong; /** * The solution starts a child worker process for the actual work such that clean up of the memory mapping can occur * while the main process already returns with the result. The worker then memory maps the input file, creates a worker * thread per available core, and then processes segments of size {@link #SEGMENT_SIZE} at a time. The segments are * split into 3 parts and cursors for each of those parts are processing the segment simultaneously in the same thread. * Results are accumulated into {@link Result} objects and a tree map is used to sequentially accumulate the results in * the end. * Runs in 0.31 on an Intel i9-13900K while the reference implementation takes 120.37s. * Credit: * Quan Anh Mai for branchless number parsing code * Alfonso² Peterssen for suggesting memory mapping with unsafe and the subprocess idea * Artsiom Korzun for showing the benefits of work stealing at 2MB segments instead of equal split between workers * Jaromir Hamala for showing that avoiding the branch misprediction between <8 and 8-16 cases is a big win even if * more work is performed * Van Phu DO for demonstrating the lookup tables based on masks instead of bit shifting */ public class CalculateAverage_thomaswue { private static final String FILE = "./measurements.txt"; private static final int MIN_TEMP = -999; private static final int MAX_TEMP = 999; private static final int MAX_NAME_LENGTH = 100; private static final int MAX_CITIES = 10000; private static final int SEGMENT_SIZE = 1 << 21; private static final int HASH_TABLE_SIZE = 1 << 17; public static void main(String[] args) throws IOException, InterruptedException { // Start worker subprocess if this process is not the worker. if (args.length == 0 || !("--worker".equals(args[0]))) { spawnWorker(); return; } int numberOfWorkers = Runtime.getRuntime().availableProcessors(); try (var fileChannel = FileChannel.open(java.nio.file.Path.of(FILE), java.nio.file.StandardOpenOption.READ)) { long fileSize = fileChannel.size(); final long fileStart = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, java.lang.foreign.Arena.global()).address(); final long fileEnd = fileStart + fileSize; final AtomicLong cursor = new AtomicLong(fileStart); // Parallel processing of segments. Thread[] threads = new Thread[numberOfWorkers]; List[] allResults = new List[numberOfWorkers]; for (int i = 0; i < threads.length; ++i) { final int index = i; threads[i] = new Thread(() -> { List results = new ArrayList<>(MAX_CITIES); parseLoop(cursor, fileEnd, fileStart, results); allResults[index] = results; }); threads[i].start(); } for (Thread thread : threads) { thread.join(); } // Final output. System.out.println(accumulateResults(allResults)); System.out.close(); } } private static void spawnWorker() throws IOException { ProcessHandle.Info info = ProcessHandle.current().info(); ArrayList workerCommand = new ArrayList<>(); info.command().ifPresent(workerCommand::add); info.arguments().ifPresent(args -> workerCommand.addAll(Arrays.asList(args))); workerCommand.add("--worker"); new ProcessBuilder().command(workerCommand).inheritIO().redirectOutput(ProcessBuilder.Redirect.PIPE) .start().getInputStream().transferTo(System.out); } private static TreeMap accumulateResults(List[] allResults) { TreeMap result = new TreeMap<>(); for (List resultArr : allResults) { for (Result r : resultArr) { Result current = result.putIfAbsent(r.calcName(), r); if (current != null) { current.accumulate(r); } } } return result; } private static void parseLoop(AtomicLong counter, long fileEnd, long fileStart, List collectedResults) { Result[] results = new Result[HASH_TABLE_SIZE]; while (true) { long current = counter.addAndGet(SEGMENT_SIZE) - SEGMENT_SIZE; if (current >= fileEnd) { return; } long segmentEnd = nextNewLine(Math.min(fileEnd - 1, current + SEGMENT_SIZE)); long segmentStart; if (current == fileStart) { segmentStart = current; } else { segmentStart = nextNewLine(current) + 1; } long dist = (segmentEnd - segmentStart) / 3; long midPoint1 = nextNewLine(segmentStart + dist); long midPoint2 = nextNewLine(segmentStart + dist + dist); Scanner scanner1 = new Scanner(segmentStart, midPoint1); Scanner scanner2 = new Scanner(midPoint1 + 1, midPoint2); Scanner scanner3 = new Scanner(midPoint2 + 1, segmentEnd); while (true) { if (!scanner1.hasNext()) { break; } if (!scanner2.hasNext()) { break; } if (!scanner3.hasNext()) { break; } long word1 = scanner1.getLong(); long word2 = scanner2.getLong(); long word3 = scanner3.getLong(); long delimiterMask1 = findDelimiter(word1); long delimiterMask2 = findDelimiter(word2); long delimiterMask3 = findDelimiter(word3); long word1b = scanner1.getLongAt(scanner1.pos() + 8); long word2b = scanner2.getLongAt(scanner2.pos() + 8); long word3b = scanner3.getLongAt(scanner3.pos() + 8); long delimiterMask1b = findDelimiter(word1b); long delimiterMask2b = findDelimiter(word2b); long delimiterMask3b = findDelimiter(word3b); Result existingResult1 = findResult(word1, delimiterMask1, word1b, delimiterMask1b, scanner1, results, collectedResults); Result existingResult2 = findResult(word2, delimiterMask2, word2b, delimiterMask2b, scanner2, results, collectedResults); Result existingResult3 = findResult(word3, delimiterMask3, word3b, delimiterMask3b, scanner3, results, collectedResults); long number1 = scanNumber(scanner1); long number2 = scanNumber(scanner2); long number3 = scanNumber(scanner3); record(existingResult1, number1); record(existingResult2, number2); record(existingResult3, number3); } while (scanner1.hasNext()) { long word = scanner1.getLong(); long pos = findDelimiter(word); long wordB = scanner1.getLongAt(scanner1.pos() + 8); long posB = findDelimiter(wordB); record(findResult(word, pos, wordB, posB, scanner1, results, collectedResults), scanNumber(scanner1)); } while (scanner2.hasNext()) { long word = scanner2.getLong(); long pos = findDelimiter(word); long wordB = scanner2.getLongAt(scanner2.pos() + 8); long posB = findDelimiter(wordB); record(findResult(word, pos, wordB, posB, scanner2, results, collectedResults), scanNumber(scanner2)); } while (scanner3.hasNext()) { long word = scanner3.getLong(); long pos = findDelimiter(word); long wordB = scanner3.getLongAt(scanner3.pos() + 8); long posB = findDelimiter(wordB); record(findResult(word, pos, wordB, posB, scanner3, results, collectedResults), scanNumber(scanner3)); } } } private static final long[] MASK1 = new long[]{ 0xFFL, 0xFFFFL, 0xFFFFFFL, 0xFFFFFFFFL, 0xFFFFFFFFFFL, 0xFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL }; private static final long[] MASK2 = new long[]{ 0x00L, 0x00L, 0x00L, 0x00L, 0x00L, 0x00L, 0x00L, 0x00L, 0xFFFFFFFFFFFFFFFFL }; private static Result findResult(long initialWord, long initialDelimiterMask, long wordB, long delimiterMaskB, Scanner scanner, Result[] results, List collectedResults) { Result existingResult; long word = initialWord; long delimiterMask = initialDelimiterMask; long hash; long nameAddress = scanner.pos(); long word2 = wordB; long delimiterMask2 = delimiterMaskB; if ((delimiterMask | delimiterMask2) != 0) { int letterCount1 = Long.numberOfTrailingZeros(delimiterMask) >>> 3; // value between 1 and 8 int letterCount2 = Long.numberOfTrailingZeros(delimiterMask2) >>> 3; // value between 0 and 8 long mask = MASK2[letterCount1]; word = word & MASK1[letterCount1]; word2 = mask & word2 & MASK1[letterCount2]; hash = word ^ word2; existingResult = results[hashToIndex(hash, results)]; scanner.add(letterCount1 + (letterCount2 & mask)); if (existingResult != null && existingResult.firstNameWord == word && existingResult.secondNameWord == word2) { return existingResult; } } else { // Slow-path for when the ';' could not be found in the first 16 bytes. hash = word ^ word2; scanner.add(16); while (true) { word = scanner.getLong(); delimiterMask = findDelimiter(word); if (delimiterMask != 0) { int trailingZeros = Long.numberOfTrailingZeros(delimiterMask); word = (word << (63 - trailingZeros)); scanner.add(trailingZeros >>> 3); hash ^= word; break; } else { scanner.add(8); hash ^= word; } } } // Save length of name for later. int nameLength = (int) (scanner.pos() - nameAddress); // Final calculation for index into hash table. int tableIndex = hashToIndex(hash, results); outer: while (true) { existingResult = results[tableIndex]; if (existingResult == null) { existingResult = newEntry(results, nameAddress, tableIndex, nameLength, scanner, collectedResults); } // Check for collision. int i = 0; for (; i < nameLength + 1 - 8; i += 8) { if (scanner.getLongAt(existingResult.nameAddress + i) != scanner.getLongAt(nameAddress + i)) { // Collision error, try next. tableIndex = (tableIndex + 31) & (results.length - 1); continue outer; } } int remainingShift = (64 - ((nameLength + 1 - i) << 3)); if (((scanner.getLongAt(existingResult.nameAddress + i) ^ (scanner.getLongAt(nameAddress + i))) << remainingShift) == 0) { break; } else { // Collision error, try next. tableIndex = (tableIndex + 31) & (results.length - 1); } } return existingResult; } private static long nextNewLine(long prev) { while (true) { long currentWord = Scanner.UNSAFE.getLong(prev); long input = currentWord ^ 0x0A0A0A0A0A0A0A0AL; long pos = (input - 0x0101010101010101L) & ~input & 0x8080808080808080L; if (pos != 0) { prev += Long.numberOfTrailingZeros(pos) >>> 3; break; } else { prev += 8; } } return prev; } private static long scanNumber(Scanner scanPtr) { long numberWord = scanPtr.getLongAt(scanPtr.pos() + 1); int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000L); long number = convertIntoNumber(decimalSepPos, numberWord); scanPtr.add((decimalSepPos >>> 3) + 4); return number; } private static void record(Result existingResult, long number) { if (number < existingResult.min) { existingResult.min = (short) number; } if (number > existingResult.max) { existingResult.max = (short) number; } existingResult.sum += number; existingResult.count++; } private static int hashToIndex(long hash, Result[] results) { long hashAsInt = hash ^ (hash >>> 33) ^ (hash >>> 15); return (int) (hashAsInt & (results.length - 1)); } // Special method to convert a number in the ascii number into an int without branches created by Quan Anh Mai. private static long convertIntoNumber(int decimalSepPos, long numberWord) { int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise long signed = (~numberWord << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii to digit value long digits = ((numberWord & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + 0x00UU00TTHH000000 * 10 + 0xUU00TTHH00000000 * 100 long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; return (absValue ^ signed) - signed; } private static long findDelimiter(long word) { long input = word ^ 0x3B3B3B3B3B3B3B3BL; return (input - 0x0101010101010101L) & ~input & 0x8080808080808080L; } private static Result newEntry(Result[] results, long nameAddress, int hash, int nameLength, Scanner scanner, List collectedResults) { Result r = new Result(); results[hash] = r; int totalLength = nameLength + 1; r.firstNameWord = scanner.getLongAt(nameAddress); r.secondNameWord = scanner.getLongAt(nameAddress + 8); if (totalLength <= 8) { r.firstNameWord = r.firstNameWord & MASK1[totalLength - 1]; r.secondNameWord = 0; } else if (totalLength < 16) { r.secondNameWord = r.secondNameWord & MASK1[totalLength - 9]; } r.nameAddress = nameAddress; collectedResults.add(r); return r; } private static final class Result { long firstNameWord, secondNameWord; short min, max; int count; long sum; long nameAddress; private Result() { this.min = MAX_TEMP; this.max = MIN_TEMP; } public String toString() { return round(((double) min) / 10.0) + "/" + round((((double) sum) / 10.0) / count) + "/" + round(((double) max) / 10.0); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } private void accumulate(Result other) { if (other.min < min) { min = other.min; } if (other.max > max) { max = other.max; } sum += other.sum; count += other.count; } public String calcName() { Scanner scanner = new Scanner(nameAddress, nameAddress + MAX_NAME_LENGTH + 1); int nameLength = 0; while (scanner.getByteAt(nameAddress + nameLength) != ';') { nameLength++; } byte[] array = new byte[nameLength]; for (int i = 0; i < nameLength; ++i) { array[i] = scanner.getByteAt(nameAddress + i); } return new String(array, java.nio.charset.StandardCharsets.UTF_8); } } private static final class Scanner { private static final sun.misc.Unsafe UNSAFE = initUnsafe(); private long pos; private final long end; private static sun.misc.Unsafe initUnsafe() { try { java.lang.reflect.Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (sun.misc.Unsafe) theUnsafe.get(sun.misc.Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public Scanner(long start, long end) { this.pos = start; this.end = end; } boolean hasNext() { return pos < end; } long pos() { return pos; } void add(long delta) { pos += delta; } long getLong() { return UNSAFE.getLong(pos); } long getLongAt(long pos) { return UNSAFE.getLong(pos); } byte getByteAt(long pos) { return UNSAFE.getByte(pos); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_tivrfoa.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * Solution based on thomaswue solution, commit: * commit d0a28599c293d3afe3291fc3cf169a7b25ae9ae6 * Author: Thomas Wuerthinger * Date: Sun Jan 21 20:13:48 2024 +0100 * * The goal here was to try to improve the runtime of his 10k * solution of: 00:04.516 * * With Thomas latest changes, his time is probably much better * already, and maybe even 1st place for the 10k too. * See: https://github.com/gunnarmorling/1brc/pull/606 * * As I was not able to make it faster ... so I'll make it slower, * because my current solution should *not* stay at the top, as it added * basically nothing. */ public class CalculateAverage_tivrfoa { private static final String FILE = "./measurements.txt"; private static final int MAX_CITIES = 10_000; private static final int BUCKETS_LEN = 1 << 17; private static final int LAST_BUCKET_ENTRY = BUCKETS_LEN - 1; private static final int NUM_CPUS = Runtime.getRuntime().availableProcessors(); private static final AtomicInteger chunkIdx = new AtomicInteger(); private static long[] chunks; private static int numChunks; // Holding the current result for a single city. private static class Result { long lastNameLong; long[] name; int count; short min, max; long sum; private Result(short number, long nameAddress, byte nameLength, Scanner scanner) { this.min = number; this.max = number; this.sum = number; this.count = 1; name = new long[(nameLength / Long.BYTES) + 1]; int pos = 0, i = 0; for (; i < nameLength + 1 - Long.BYTES; i += Long.BYTES) { name[pos++] = scanner.getLongAt(nameAddress + i); } int remainingShift = (64 - (nameLength + 1 - i) << 3); lastNameLong = (scanner.getLongAt(nameAddress + i) << remainingShift); name[pos] = lastNameLong >> remainingShift; } public String toString() { return round(((double) min) / 10.0) + "/" + round((((double) sum) / 10.0) / count) + "/" + round(((double) max) / 10.0); } private static double round(double value) { return Math.round(value * 10.0) / 10.0; } // Accumulate another result into this one. private void add(Result other) { if (other.min < min) { min = other.min; } if (other.max > max) { max = other.max; } sum += other.sum; count += other.count; } private void add(short number) { if (number < min) { min = number; } if (number > max) { max = number; } sum += number; count++; } public String calcName() { ByteBuffer bb = ByteBuffer.allocate(name.length * Long.BYTES).order(ByteOrder.nativeOrder()); bb.asLongBuffer().put(name); byte[] array = bb.array(); int i = 0; while (array[i++] != ';') ; return new String(array, 0, i - 1, StandardCharsets.UTF_8); } } /** * From: * https://github.com/OpenHFT/Zero-Allocation-Hashing/blob/ea/src/main/java/net/openhft/hashing/XXH3.java * * Less collisions, but it will make the code slower. xD * * One interesting thing about Thomas' solution that I * started to work with (d0a28599), is that it basically does not have * any collision for the small data set (sometimes none!), but it * has lots of collisions for the 10k, hence its poor performance. * */ private static long XXH3_avalanche(long h64) { h64 ^= h64 >>> 37; h64 *= 0x165667919E3779F9L; return h64 ^ (h64 >>> 32); } private static final class SolveChunk extends Thread { private int chunkStartIdx; private Result[] results = new Result[MAX_CITIES]; private Result[] buckets = new Result[BUCKETS_LEN]; private int resIdx = 0; public SolveChunk(int chunkStartIdx) { this.chunkStartIdx = chunkStartIdx; } @Override public void run() { for (; chunkStartIdx < numChunks; chunkStartIdx = chunkIdx.getAndIncrement()) { Scanner scanner = new Scanner(chunks[chunkStartIdx], chunks[chunkStartIdx + 1]); long word = scanner.getLong(); long pos = findDelimiter(word); while (scanner.hasNext()) { long nameAddress = scanner.pos(); long hash = 0; while (true) { if (pos != 0) { pos = Long.numberOfTrailingZeros(pos) >>> 3; scanner.add(pos); word = mask(word, pos); hash ^= XXH3_avalanche(word); break; } else { scanner.add(8); hash ^= XXH3_avalanche(word); } word = scanner.getLong(); pos = findDelimiter(word); } byte nameLength = (byte) (scanner.pos() - nameAddress); short number = scanNumber(scanner); int tableIndex = hashToIndex(hash); outer: while (true) { Result existingResult = buckets[tableIndex]; if (existingResult == null) { var newResult = new Result(number, nameAddress, nameLength, scanner); buckets[tableIndex] = newResult; results[resIdx++] = newResult; break; } int i = 0; int namePos = 0; for (; i < nameLength + 1 - 8; i += 8) { if (namePos >= existingResult.name.length || existingResult.name[namePos++] != scanner.getLongAt(nameAddress + i)) { tableIndex = (tableIndex + 31) & (LAST_BUCKET_ENTRY); continue outer; } } int remainingShift = (64 - (nameLength + 1 - i) << 3); if (((existingResult.lastNameLong ^ (scanner.getLongAt(nameAddress + i) << remainingShift)) == 0)) { existingResult.add(number); break; } else { tableIndex = (tableIndex + 31) & (LAST_BUCKET_ENTRY); } } word = scanner.getLong(); pos = findDelimiter(word); } } } } private static void mergeIntoFinalMap(TreeMap map, Result[] newResults) { for (var r : newResults) { if (r == null) return; Result current = map.putIfAbsent(r.calcName(), r); if (current != null) { current.add(r); } } } public static void main(String[] args) throws InterruptedException, IOException { chunks = getSegments(NUM_CPUS); numChunks = chunks.length - 1; final SolveChunk[] threads = new SolveChunk[NUM_CPUS]; chunkIdx.set(NUM_CPUS); for (int i = 0; i < NUM_CPUS; i++) { threads[i] = new SolveChunk(i); threads[i].start(); } System.out.println(getMap(threads)); System.out.close(); } private static TreeMap getMap(SolveChunk[] threads) throws InterruptedException { TreeMap map = new TreeMap<>(); threads[0].join(); for (var r : threads[0].results) { if (r == null) break; map.put(r.calcName(), r); } for (int i = 1; i < NUM_CPUS; ++i) { threads[i].join(); mergeIntoFinalMap(map, threads[i].results); } return map; } private static short scanNumber(Scanner scanPtr) { scanPtr.add(1); long numberWord = scanPtr.getLong(); int decimalSepPos = Long.numberOfTrailingZeros(~numberWord & 0x10101000); int number = convertIntoNumber(decimalSepPos, numberWord); scanPtr.add((decimalSepPos >>> 3) + 3); return (short) number; } private static int hashToIndex(long hash) { int hashAsInt = (int) (hash ^ (hash >>> 28)); int finalHash = (hashAsInt ^ (hashAsInt >>> 17)); return (finalHash & LAST_BUCKET_ENTRY); } private static long mask(long word, long pos) { return (word << ((7 - pos) << 3)); } // Special method to convert a number in the ascii number into an int without branches created by Quan Anh Mai. private static int convertIntoNumber(int decimalSepPos, long numberWord) { int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise long signed = (~numberWord << 59) >> 63; long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii to digit value long digits = ((numberWord & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + 0x00UU00TTHH000000 * 10 + 0xUU00TTHH00000000 * 100 long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; long value = (absValue ^ signed) - signed; return (int) value; } private static long findDelimiter(long word) { long input = word ^ 0x3B3B3B3B3B3B3B3BL; long tmp = (input - 0x0101010101010101L) & ~input & 0x8080808080808080L; return tmp; } /** * - Split 70% of the file in even chunks for all cpus; * - Create smaller chunks for the remainder of the file. */ private static long[] getSegments(int cpus) throws IOException { try (var fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { final long fileSize = fileChannel.size(); final long part1 = (long) (fileSize * 0.7); final long part2 = (long) (fileSize * 0.2); final long part3 = fileSize - part1 - part2; final long bigChunkSize = (part1 - 1) / cpus; final long smallChunkSize1 = (part2 - 1) / (cpus * 3); final long smallChunkSize2 = (part3 - 1) / (cpus * 3); final int numChunks = cpus + cpus * 3 + cpus * 3; final long[] sizes = new long[numChunks]; int l = 0, r = cpus; Arrays.fill(sizes, l, r, bigChunkSize); l = r; r = l + cpus * 3; Arrays.fill(sizes, l, r, smallChunkSize1); l = r; r = l + cpus * 3; Arrays.fill(sizes, l, r, smallChunkSize2); final long[] chunks = new long[sizes.length + 1]; final long mappedAddress = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, java.lang.foreign.Arena.global()).address(); chunks[0] = mappedAddress; final long endAddress = mappedAddress + fileSize; final Scanner s = new Scanner(mappedAddress, mappedAddress + fileSize); for (int i = 1, sizeIdx = 0; i < chunks.length - 1; ++i, sizeIdx = (sizeIdx + 1) % sizes.length) { long chunkAddress = chunks[i - 1] + sizes[sizeIdx]; // Align to first row start. while (chunkAddress < endAddress && (s.getLongAt(chunkAddress++) & 0xFF) != '\n') ; chunks[i] = Math.min(chunkAddress, endAddress); // System.err.printf("Chunk size %d\n", chunks[i] - chunks[i - 1]); } chunks[chunks.length - 1] = endAddress; // System.err.printf("Chunk size %d\n", chunks[chunks.length - 1] - chunks[chunks.length - 2]); return chunks; } } private static class Scanner { private static final sun.misc.Unsafe UNSAFE = initUnsafe(); private static sun.misc.Unsafe initUnsafe() { try { java.lang.reflect.Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (sun.misc.Unsafe) theUnsafe.get(sun.misc.Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } long pos, end; public Scanner(long start, long end) { this.pos = start; this.end = end; } boolean hasNext() { return pos < end; } long pos() { return pos; } void add(long delta) { pos += delta; } long getLong() { return UNSAFE.getLong(pos); } long getLongAt(long pos) { return UNSAFE.getLong(pos); } void setPos(long l) { this.pos = l; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_tkosachev.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CalculateAverage_tkosachev { private static final String FILE = "./measurements.txt"; public static int numThreads = Math.min(Runtime.getRuntime().availableProcessors(), 8); private record ResultRow(int min, double mean, int max) { public String toString() { return STR."\{round(min)}/\{round(mean)}/\{round(max)}"; } private double round(double value) { return Math.round(value) / 10.0; } } private static class MeasurementAggregator { private int min = Integer.MAX_VALUE; private int max = Integer.MIN_VALUE; private long sum = 0; private long count = 0; public void newValue(int m) { if (m < min) { min = m; } if (m > max) { max = m; } sum += m; count++; } public void mergeIn(MeasurementAggregator add) { if (add.min < min) { min = add.min; } if (add.max > max) { max = add.max; } sum += add.sum; count += add.count; } } public static void main(String[] args) { Path path = Paths.get(args.length == 0 ? FILE : args[0]); Map total; try (RandomAccessFile aFile = new RandomAccessFile(path.toFile(), "r"); ExecutorService executorService = Executors.newFixedThreadPool(numThreads)) { FileChannel inChannel = aFile.getChannel(); int numChunks = args.length > 1 ? Integer.parseInt(args[1]) : 100; if (inChannel.size() < 1024 * 1024 * 1024) { numThreads = 1; numChunks = 1; } List>> futures = new ArrayList<>(numThreads); int bufferSize = (int) (inChannel.size() / numChunks) + 100; for (int i = 0; i < numChunks; i++) { final int finalI = i; futures.add(executorService.submit(() -> processBuffer(inChannel, bufferSize, finalI))); } executorService.shutdown(); total = new HashMap<>(); for (Future> future : futures) { mergeIn(total, future.get()); } } catch (IOException | InterruptedException | ExecutionException e) { throw new RuntimeException(e); } printResults(total); } private static void mergeIn(Map total, Map result) { for (String name : result.keySet()) { MeasurementAggregator totalAggregator = total.computeIfAbsent(name, _ -> new MeasurementAggregator()); totalAggregator.mergeIn(result.get(name)); } } private static Map processBuffer(FileChannel channel, int bufferSize, int nr) throws IOException { HashMap aggregatorMap = new HashMap<>(); long start = ((long) nr) * bufferSize; long length = Math.min(bufferSize, channel.size() - start); ByteBuffer byteBuffer = channel.map( FileChannel.MapMode.READ_ONLY, start, length); int i = 0; int smcIndex = -1; byte[] buf = new byte[1024]; int count = 0; if (nr > 0) { do { i++; } while (byteBuffer.get() != '\n'); } while (i < length) { byte b = byteBuffer.get(); buf[count] = b; if (b == ';') { smcIndex = count; } count++; if (b == '\n') { String name = new String(buf, 0, smcIndex); int value = fastParse(buf, smcIndex + 1, count - smcIndex - 2); aggregatorMap.computeIfAbsent(name, _ -> new MeasurementAggregator()).newValue(value); count = 0; } i++; } return aggregatorMap; } private static void printResults(Map result) { Map measurements = new TreeMap<>(); for (Map.Entry entry : result.entrySet()) { MeasurementAggregator value = entry.getValue(); measurements.put(entry.getKey(), new ResultRow(value.min, ((double) value.sum / value.count), value.max)); } System.out.println(measurements); } public static int fastParse(byte[] buf, int start, int len) { int i = 0; int sign = 1; for (int index = start; index < start + len; index++) { byte b = buf[index]; if (b == '-') { sign = -1; } if (b >= '0' && b <= '9') { i = i * 10 + (b - '0'); } } return i * sign; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_tonivade.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.StructuredTaskScope; import java.util.concurrent.StructuredTaskScope.Subtask; public class CalculateAverage_tonivade { private static final String FILE = "./measurements.txt"; private static final int MIN_CHUNK_SIZE = 1024; private static final int MAX_NAME_LENGTH = 128; private static final int MAX_TEMP_LENGTH = 8; public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { System.out.println(readFile()); } private static Map readFile() throws IOException, InterruptedException, ExecutionException { Map result = new TreeMap<>(); try (var channel = FileChannel.open(Paths.get(FILE), StandardOpenOption.READ)) { long consumed = 0; long remaining = channel.size(); while (remaining > 0) { var buffer = channel.map( MapMode.READ_ONLY, consumed, Math.min(remaining, Integer.MAX_VALUE)); int chunks = Runtime.getRuntime().availableProcessors(); int chunkSize = buffer.remaining() / chunks; int leftover = buffer.remaining() % chunks; if (chunkSize < MIN_CHUNK_SIZE) { var partialResult = new Chunk(buffer, 0, buffer.remaining()).read(); consumed += partialResult.end(); remaining -= partialResult.end(); partialResult.merge(result); } else { try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { var tasks = new ArrayList>(chunks); for (int i = 0; i < chunks; i++) { int start = i * chunkSize; int length = chunkSize + (i < chunks ? leftover : 0); tasks.add(scope.fork(new Chunk(buffer, start, length)::read)); } scope.join(); scope.throwIfFailed(); for (var subtask : tasks) { subtask.get().merge(result); } consumed += tasks.getLast().get().end(); remaining -= tasks.getLast().get().end(); } } } } return result; } static final class Chunk { private static final int EOL = 10; private static final int MINUS = 45; private static final int SEMICOLON = 59; final ByteBuffer buffer; final int start; final int end; final byte[] name = new byte[MAX_NAME_LENGTH]; final byte[] temp = new byte[MAX_TEMP_LENGTH]; final Stations stations = new Stations(); int hash; Chunk(ByteBuffer buffer, int start, int length) { this.buffer = buffer; this.start = findStart(buffer, start); this.end = start + length; } private static int findStart(ByteBuffer buffer, int start) { if (start > 0 && buffer.get(start - 1) != EOL) { for (int i = start - 2; i > 0; i--) { byte b = buffer.get(i); if (b == EOL) { return i + 1; } } } return start; } PartialResult read() { int position = start; while (position < end) { int semicolon = readName(position, end - position); if (semicolon < 0) { break; } int endOfLine = readTemp(semicolon + 1, end - semicolon - 1); if (endOfLine < 0) { break; } stations.find(name, semicolon - position, hash) .add(parseTemp(temp, endOfLine - semicolon - 1)); // skip end of line position = endOfLine + 1; } return new PartialResult(position, stations.buckets); } private int readName(int offset, int length) { hash = 1; for (int i = 0; i < length; i++) { byte b = buffer.get(i + offset); if (b == SEMICOLON) { return i + offset; } name[i] = b; hash = 31 * hash + b; } return -1; } private int readTemp(int offset, int length) { for (int i = 0; i < length; i++) { byte b = buffer.get(i + offset); if (b == EOL) { return i + offset; } temp[i] = b; } return -1; } // non null double between -99.9 (inclusive) and 99.9 (inclusive), always with one fractional digit private static int parseTemp(byte[] value, int length) { int period = length - 2; if (value[0] == MINUS) { int left = parseLeft(value, 1, period - 1); int right = toInt(value[period + 1]); return -(left + right); } int left = parseLeft(value, 0, period); int right = toInt(value[period + 1]); return left + right; } private static int parseLeft(byte[] value, int start, int length) { if (length == 1) { return toInt(value[start]) * 10; } // two chars int a = toInt(value[start]) * 100; int b = toInt(value[start + 1]) * 10; return a + b; } private static int toInt(byte c) { return c - 48; } } static final class Stations { private static final int NUMBER_OF_BUCKETS = 1000; private static final int BUCKET_SIZE = 50; final Station[][] buckets = new Station[NUMBER_OF_BUCKETS][BUCKET_SIZE]; Station find(byte[] name, int length, int hash) { var bucket = buckets[Math.abs(hash % NUMBER_OF_BUCKETS)]; for (int i = 0; i < BUCKET_SIZE; i++) { if (bucket[i] == null) { bucket[i] = new Station(name, length, hash); return bucket[i]; } else if (bucket[i].sameName(length, hash)) { return bucket[i]; } } throw new IllegalStateException("no more space left"); } } static final class Station { private final byte[] name; private final int hash; private int min = 1000; private int max = -1000; private int sum; private long count; Station(byte[] source, int length, int hash) { name = new byte[length]; System.arraycopy(source, 0, name, 0, length); this.hash = hash; } String getName() { return new String(name, StandardCharsets.UTF_8); } void add(int value) { min = Math.min(min, value); max = Math.max(max, value); sum += value; count++; } Station merge(Station other) { min = Math.min(min, other.min); max = Math.max(max, other.max); sum += other.sum; count += other.count; return this; } @Override public String toString() { return toDouble(min) + "/" + round(mean()) + "/" + toDouble(max); } boolean sameName(int length, int hash) { return name.length == length && this.hash == hash; } private double mean() { return toDouble(sum) / count; } private double toDouble(int value) { return value / 10.; } private double round(double value) { return Math.round(value * 10.) / 10.; } } static record PartialResult(int end, Station[][] stations) { void merge(Map result) { for (Station[] bucket : stations) { for (Station station : bucket) { if (station != null) { result.merge(station.getName(), station, Station::merge); } } } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_truelive.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.*; import java.util.concurrent.atomic.DoubleAccumulator; import java.util.concurrent.atomic.LongAdder; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.StreamSupport; public class CalculateAverage_truelive { private static final String FILE = "./measurements.txt"; private static final long CHUNK_SIZE = 1024 * 1024 * 10L; private static double getDouble(final byte[] arr, int pos) { final int negative = ~(arr[pos] >> 4) & 1; int sig = 1; sig -= 2 * negative; pos += negative; final int digit1 = arr[pos] - '0'; pos++; if (arr[pos] == '.') { return sig * (digit1 + (arr[pos + 1] - '0') / 10.0); } else { return sig * (digit1 * 10 + (arr[pos] - '0') + (arr[pos + 2] - '0') / 10.0); } } private record Measurement(DoubleAccumulator min, DoubleAccumulator max, DoubleAccumulator sum, LongAdder count) { public static Measurement of(final Double initialMeasurement) { final Measurement measurement = new Measurement( new DoubleAccumulator(Math::min, initialMeasurement), new DoubleAccumulator(Math::max, initialMeasurement), new DoubleAccumulator(Double::sum, initialMeasurement), new LongAdder() ); measurement.count.increment(); return measurement; } public Measurement add(final double measurment) { min.accumulate(measurment); max.accumulate(measurment); sum.accumulate(measurment); count.increment(); return this; } public String toString() { return round(min.doubleValue()) + "/" + round(sum.doubleValue() / count.sum()) + "/" + round(max.doubleValue()); } private double round(final double value) { return Math.round(value * 10.0) / 10.0; } public static Measurement combineWith(final Measurement m1, final Measurement m2) { m1.min.accumulate(m2.min.doubleValue()); m1.max.accumulate(m2.max.doubleValue()); m1.sum.accumulate(m2.sum.doubleValue()); m1.count.add(m2.count.sum()); return new Measurement( m1.min, m1.max, m1.sum, m1.count ); } } public static void main(final String[] args) throws IOException { // long before = System.currentTimeMillis(); /** * Shoutout to bjhara */ final Iterator iterator = new Iterator<>() { final FileChannel in = new FileInputStream(FILE).getChannel(); final long total = in.size(); long start; @Override public boolean hasNext() { return start < total; } @Override public ByteBuffer next() { try { final MappedByteBuffer mbb = in.map( FileChannel.MapMode.READ_ONLY, start, Math.min(CHUNK_SIZE, total - start)); int realEnd = mbb.limit() - 1; while (mbb.get(realEnd) != '\n') { realEnd--; } realEnd++; mbb.limit(realEnd); start += realEnd; return mbb; } catch (final IOException e) { throw new RuntimeException(e); } } }; final Map reduce = StreamSupport.stream(Spliterators.spliteratorUnknownSize( iterator, Spliterator.IMMUTABLE), true) .map(CalculateAverage_truelive::parseBuffer) .flatMap(map -> map.entrySet().stream()) .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, Measurement::combineWith, TreeMap::new)); System.out.println(reduce); } private static Map parseBuffer(final ByteBuffer bug) { final Map resultMap = new HashMap<>(); bug.mark(); String name = null; final byte[] arr = new byte[128]; int cur = 0; while (bug.hasRemaining()) { char c = (char) bug.get(); arr[cur++] = (byte) c; while (c != ';') { c = (char) bug.get(); arr[cur++] = (byte) c; } name = new String(arr, 0, cur - 1); cur = 0; while (c != '\n') { c = (char) bug.get(); arr[cur++] = (byte) c; } final double temp = getDouble(arr, 0); resultMap.compute(name, (k, v) -> (v == null) ? Measurement.of(temp) : v.add(temp)); cur = 0; } return resultMap; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_twobiers.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import static java.util.stream.Collectors.groupingBy; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.TreeMap; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.StreamSupport; public class CalculateAverage_twobiers { private static final String FILE = "./measurements.txt"; private static final FastAveragingCollector FAST_AVERAGING_COLLECTOR = new FastAveragingCollector(); // private static final VectorSpecies SPECIES = IntVector.SPECIES_256; private static class FastAveragingCollector implements Collector { @Override public Supplier supplier() { // 0: current sum // 1: count // 2: current max // 3: current min return () -> new double[4]; } @Override public BiConsumer accumulator() { return (a, t) -> { double val = t.value(); a[0] += val; a[1]++; if (val > a[2] || a[1] == 1) { a[2] = val; } if (val < a[3] || a[1] == 1) { a[3] = val; } }; } @Override public BinaryOperator combiner() { return (a, b) -> { a[0] += b[0]; a[1] += b[1]; if (b[2] > a[2]) { a[2] = b[2]; } if (b[3] < a[3]) { a[3] = b[3]; } return a; }; } @Override public Function finisher() { return a -> { var mean = (a[1] == 0) ? 0.0d : Math.round((a[0] / a[1]) * 10.0) / 10.0; var max = a[2]; var min = a[3]; return min + "/" + mean + "/" + max; }; } @Override public Set characteristics() { return Collections.emptySet(); } } private static class FileChannelIterator implements Iterator { // File Size will be approx. 14G, 1MB Chunks sound kinda reasonable private static final long CHUNK_SIZE = (long) Math.pow(2, 20); private final FileChannel fileChannel; private final long size; private long bytesRead = 0; public FileChannelIterator(FileChannel fileChannel) { this.fileChannel = fileChannel; try { this.size = fileChannel.size(); } catch (IOException e) { throw new RuntimeException(e); } } @Override public boolean hasNext() { return bytesRead < size; } @Override public ByteBuffer next() { try { MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, bytesRead, Math.min(CHUNK_SIZE, size - bytesRead)); // Ensure the chunks will end on a newline int realEnd = mappedByteBuffer.limit() - 1; while (mappedByteBuffer.get(realEnd) != '\n') { realEnd--; } mappedByteBuffer.limit(++realEnd); bytesRead += realEnd; return mappedByteBuffer; } catch (IOException e) { throw new RuntimeException(e); } } } private record Measurement(String station, double value) { } public static void main(String[] args) throws IOException { try ( var file = new RandomAccessFile(FILE, "r"); var channel = file.getChannel();) { TreeMap measurements = StreamSupport.stream( Spliterators.spliteratorUnknownSize(new FileChannelIterator(channel), Spliterator.IMMUTABLE), true) .flatMap(a -> parseMeasurements(a).stream()) .collect( groupingBy( Measurement::station, TreeMap::new, FAST_AVERAGING_COLLECTOR)); System.out.println(measurements); // Simple test for my generated file assert measurements.toString().equals( "{Abha=-30.1/18.0/72.2, Abidjan=-23.4/26.0/79.9, Abéché=-17.3/29.4/80.5, Accra=-24.3/26.4/77.9, Addis Ababa=-33.6/16.0/65.9, Adelaide=-33.7/17.3/71.1, Aden=-21.2/29.1/81.2, Ahvaz=-30.2/25.4/80.2, Albuquerque=-39.1/14.0/60.9, Alexandra=-36.6/11.0/62.9, Alexandria=-30.4/20.0/69.8, Algiers=-33.1/18.2/68.4, Alice Springs=-27.2/21.0/72.1, Almaty=-40.5/10.0/58.5, Amsterdam=-38.9/10.2/58.9, Anadyr=-58.2/-6.9/39.9, Anchorage=-45.3/2.8/54.3, Andorra la Vella=-42.9/9.8/60.6, Ankara=-36.7/12.0/63.7, Antananarivo=-30.9/17.9/70.8, Antsiranana=-23.0/25.2/75.4, Arkhangelsk=-48.2/1.3/51.4, Ashgabat=-30.4/17.1/64.9, Asmara=-32.6/15.6/65.4, Assab=-21.2/30.5/78.7, Astana=-45.5/3.5/49.3, Athens=-28.5/19.2/68.0, Atlanta=-33.3/17.0/69.7, Auckland=-33.6/15.2/65.0, Austin=-32.1/20.7/68.5, Baghdad=-29.6/22.8/71.6, Baguio=-29.9/19.5/68.7, Baku=-32.1/15.1/66.8, Baltimore=-36.4/13.1/62.8, Bamako=-27.0/27.8/82.0, Bangkok=-20.5/28.6/79.0, Bangui=-24.2/26.0/73.8, Banjul=-27.0/26.0/75.4, Barcelona=-36.1/18.2/74.0, Bata=-25.9/25.1/73.7, Batumi=-36.8/14.0/65.7, Beijing=-34.2/12.9/65.9, Beirut=-28.9/20.9/68.5, Belgrade=-35.7/12.5/65.5, Belize City=-24.8/26.7/75.3, Benghazi=-28.6/19.9/68.9, Bergen=-48.8/7.7/58.5, Berlin=-48.1/10.3/62.5, Bilbao=-34.5/14.7/65.2, Birao=-22.7/26.5/77.9, Bishkek=-37.2/11.3/62.1, Bissau=-24.1/27.0/82.6, Blantyre=-29.2/22.2/68.9, Bloemfontein=-32.1/15.6/66.1, Boise=-36.6/11.4/62.2, Bordeaux=-34.2/14.2/63.0, Bosaso=-20.8/30.0/77.2, Boston=-39.8/10.9/58.4, Bouaké=-24.8/26.0/76.8, Bratislava=-40.5/10.5/62.6, Brazzaville=-29.3/25.0/74.6, Bridgetown=-25.0/27.0/78.4, Brisbane=-27.2/21.4/69.7, Brussels=-37.0/10.5/56.6, Bucharest=-48.7/10.8/60.8, Budapest=-39.6/11.3/61.6, Bujumbura=-26.3/23.8/73.8, Bulawayo=-32.8/18.9/68.5, Burnie=-35.3/13.1/64.4, Busan=-34.8/15.0/62.8, Cabo San Lucas=-25.0/23.9/71.6, Cairns=-23.1/25.0/74.3, Cairo=-27.6/21.4/68.6, Calgary=-46.9/4.4/56.2, Canberra=-36.9/13.1/63.4, Cape Town=-33.1/16.2/63.5, Changsha=-33.3/17.4/64.8, Charlotte=-33.7/16.1/63.5, Chiang Mai=-26.4/25.8/76.2, Chicago=-39.7/9.8/60.6, Chihuahua=-33.0/18.6/72.4, Chittagong=-20.8/25.9/76.1, Chișinău=-39.5/10.2/59.2, Chongqing=-30.8/18.6/71.4, Christchurch=-37.9/12.2/62.1, City of San Marino=-40.0/11.8/64.1, Colombo=-27.2/27.4/78.0, Columbus=-39.6/11.7/63.3, Conakry=-29.0/26.4/77.7, Copenhagen=-41.2/9.1/59.8, Cotonou=-24.6/27.2/80.1, Cracow=-39.9/9.3/63.2, Da Lat=-29.9/17.9/66.6, Da Nang=-23.4/25.8/74.5, Dakar=-27.8/24.0/74.8, Dallas=-33.8/19.0/69.2, Damascus=-33.5/17.0/68.5, Dampier=-25.0/26.4/75.8, Dar es Salaam=-22.6/25.8/78.6, Darwin=-22.0/27.6/75.4, Denpasar=-24.0/23.7/76.5, Denver=-38.2/10.4/60.4, Detroit=-38.0/10.0/60.0, Dhaka=-24.9/25.9/75.7, Dikson=-59.6/-11.1/39.0, Dili=-23.6/26.6/76.5, Djibouti=-18.9/29.9/77.8, Dodoma=-26.5/22.7/73.5, Dolisie=-24.4/24.0/71.9, Douala=-23.0/26.7/77.2, Dubai=-20.8/26.9/79.8, Dublin=-38.2/9.8/62.2, Dunedin=-39.3/11.1/61.3, Durban=-26.3/20.6/73.5, Dushanbe=-33.2/14.7/66.6, Edinburgh=-45.9/9.3/58.6, Edmonton=-41.7/4.2/56.0, El Paso=-29.6/18.1/66.5, Entebbe=-27.0/21.0/72.7, Erbil=-31.8/19.5/71.9, Erzurum=-47.0/5.1/55.4, Fairbanks=-48.9/-2.3/47.3, Fianarantsoa=-33.2/17.9/68.1, Flores, Petén=-20.9/26.4/74.6, Frankfurt=-39.5/10.6/58.5, Fresno=-31.8/17.9/65.9, Fukuoka=-33.1/17.0/70.7, Gaborone=-29.0/21.0/73.1, Gabès=-34.3/19.5/70.3, Gagnoa=-24.6/26.0/80.8, Gangtok=-36.3/15.2/67.1, Garissa=-21.1/29.3/80.8, Garoua=-23.0/28.3/79.0, George Town=-23.4/27.9/78.1, Ghanzi=-33.3/21.4/73.2, Gjoa Haven=-64.9/-14.4/36.4, Guadalajara=-27.8/20.9/72.2, Guangzhou=-27.1/22.4/71.1, Guatemala City=-28.9/20.4/77.1, Halifax=-43.4/7.5/57.7, Hamburg=-41.5/9.7/65.0, Hamilton=-35.1/13.8/69.3, Hanga Roa=-29.1/20.5/70.8, Hanoi=-25.8/23.6/75.2, Harare=-33.2/18.4/69.1, Harbin=-45.1/5.0/55.0, Hargeisa=-33.4/21.7/71.1, Hat Yai=-22.5/27.0/74.3, Havana=-24.9/25.2/74.1, Helsinki=-42.3/5.9/55.2, Heraklion=-30.4/18.9/67.2, Hiroshima=-34.0/16.3/67.0, Ho Chi Minh City=-18.5/27.4/77.0, Hobart=-36.9/12.7/59.6, Hong Kong=-25.9/23.3/79.9, Honiara=-21.5/26.5/75.6, Honolulu=-22.9/25.4/73.7, Houston=-29.9/20.8/69.7, Ifrane=-36.8/11.4/59.3, Indianapolis=-39.8/11.8/58.4, Iqaluit=-58.2/-9.3/40.9, Irkutsk=-46.4/1.0/52.9, Istanbul=-35.2/13.9/61.9, Jacksonville=-29.4/20.3/72.0, Jakarta=-25.8/26.7/78.1, Jayapura=-21.2/27.0/74.9, Jerusalem=-31.8/18.3/66.8, Johannesburg=-32.2/15.5/63.9, Jos=-28.2/22.8/71.2, Juba=-21.4/27.8/76.2, Kabul=-37.5/12.1/61.9, Kampala=-33.6/20.0/70.1, Kandi=-23.3/27.7/77.6, Kankan=-24.6/26.5/82.7, Kano=-25.7/26.4/78.4, Kansas City=-38.7/12.5/60.9, Karachi=-24.5/26.0/74.4, Karonga=-22.5/24.4/77.5, Kathmandu=-37.5/18.3/67.3, Khartoum=-19.5/29.9/83.9, Kingston=-30.9/27.4/76.1, Kinshasa=-26.0/25.3/78.9, Kolkata=-23.0/26.7/76.4, Kuala Lumpur=-22.1/27.3/78.0, Kumasi=-22.4/26.0/80.3, Kunming=-33.1/15.7/66.1, Kuopio=-50.4/3.4/53.6, Kuwait City=-27.0/25.7/78.7, Kyiv=-43.4/8.4/57.5, Kyoto=-33.9/15.8/65.8, La Ceiba=-24.1/26.2/77.2, La Paz=-26.5/23.7/76.0, Lagos=-24.4/26.8/75.9, Lahore=-25.8/24.3/71.6, Lake Havasu City=-25.3/23.7/72.4, Lake Tekapo=-41.3/8.7/58.3, Las Palmas de Gran Canaria=-39.1/21.2/71.0, Las Vegas=-28.4/20.3/76.1, Launceston=-34.4/13.1/66.0, Lhasa=-41.2/7.6/57.5, Libreville=-24.0/25.9/73.8, Lisbon=-36.4/17.5/74.4, Livingstone=-27.5/21.8/77.2, Ljubljana=-36.4/10.9/61.4, Lodwar=-21.2/29.3/77.3, Lomé=-24.0/26.9/78.7, London=-38.3/11.3/63.4, Los Angeles=-29.8/18.6/67.1, Louisville=-40.0/13.9/61.7, Luanda=-22.7/25.8/75.3, Lubumbashi=-28.9/20.8/72.4, Lusaka=-29.1/19.9/75.4, Luxembourg City=-42.5/9.3/61.9, Lviv=-42.0/7.8/57.5, Lyon=-36.6/12.5/61.3, Madrid=-41.5/15.0/65.8, Mahajanga=-21.3/26.3/76.7, Makassar=-24.0/26.7/78.5, Makurdi=-21.6/26.0/76.9, Malabo=-20.1/26.3/73.1, Malé=-24.4/28.0/79.3, Managua=-26.7/27.3/78.7, Manama=-27.2/26.5/82.0, Mandalay=-22.3/28.0/76.9, Mango=-19.7/28.1/77.3, Manila=-27.7/28.4/76.9, Maputo=-31.7/22.8/71.0, Marrakesh=-28.0/19.6/74.1, Marseille=-39.0/15.8/66.1, Maun=-28.2/22.4/69.5, Medan=-24.4/26.5/78.9, Mek'ele=-28.4/22.7/73.7, Melbourne=-32.9/15.1/71.6, Memphis=-34.9/17.2/70.0, Mexicali=-31.9/23.1/71.6, Mexico City=-31.1/17.5/69.5, Miami=-24.6/24.9/75.0, Milan=-37.9/13.0/64.9, Milwaukee=-42.9/8.9/58.9, Minneapolis=-46.9/7.8/58.7, Minsk=-46.3/6.7/56.7, Mogadishu=-20.7/27.1/80.1, Mombasa=-21.4/26.3/77.4, Monaco=-34.1/16.4/66.0, Moncton=-42.7/6.1/53.7, Monterrey=-25.2/22.3/72.4, Montreal=-43.2/6.8/56.2, Moscow=-43.0/5.8/58.1, Mumbai=-22.5/27.1/78.4, Murmansk=-49.8/0.6/49.2, Muscat=-23.5/28.0/79.8, Mzuzu=-34.0/17.7/71.2, N'Djamena=-22.5/28.3/81.0, Naha=-24.9/23.1/71.0, Nairobi=-31.3/17.8/68.2, Nakhon Ratchasima=-29.4/27.3/75.4, Napier=-37.5/14.6/66.0, Napoli=-30.2/15.9/66.8, Nashville=-33.2/15.4/61.3, Nassau=-23.5/24.6/73.9, Ndola=-29.0/20.3/72.7, New Delhi=-23.4/25.0/73.4, New Orleans=-27.6/20.7/72.3, New York City=-36.1/12.9/63.9, Ngaoundéré=-25.2/22.0/73.8, Niamey=-20.5/29.3/79.3, Nicosia=-34.4/19.7/68.1, Niigata=-40.0/13.9/70.6, Nouadhibou=-27.3/21.3/76.0, Nouakchott=-23.3/25.7/84.5, Novosibirsk=-49.9/1.7/53.6, Nuuk=-52.0/-1.4/48.4, Odesa=-38.3/10.7/60.8, Odienné=-25.6/26.0/77.7, Oklahoma City=-31.5/15.9/64.4, Omaha=-38.6/10.6/57.4, Oranjestad=-24.7/28.1/78.5, Oslo=-43.8/5.7/56.2, Ottawa=-46.2/6.6/60.6, Ouagadougou=-22.3/28.3/76.2, Ouahigouya=-28.9/28.6/76.5, Ouarzazate=-32.1/18.9/66.6, Oulu=-51.5/2.7/53.0, Palembang=-20.6/27.3/76.1, Palermo=-33.8/18.5/68.3, Palm Springs=-25.4/24.5/71.9, Palmerston North=-38.8/13.2/60.8, Panama City=-22.3/28.0/76.3, Parakou=-26.4/26.8/76.6, Paris=-37.9/12.3/62.5, Perth=-29.8/18.7/67.9, Petropavlovsk-Kamchatsky=-47.7/1.9/56.0, Philadelphia=-34.1/13.2/60.1, Phnom Penh=-23.1/28.3/79.2, Phoenix=-26.8/23.9/72.6, Pittsburgh=-41.3/10.8/61.2, Podgorica=-32.4/15.3/65.3, Pointe-Noire=-24.8/26.1/74.1, Pontianak=-22.9/27.7/76.3, Port Moresby=-23.2/26.9/77.7, Port Sudan=-20.3/28.4/84.2, Port Vila=-23.2/24.3/74.7, Port-Gentil=-22.9/26.0/77.3, Portland (OR)=-36.2/12.4/61.0, Porto=-30.6/15.7/69.7, Prague=-46.5/8.4/57.3, Praia=-22.2/24.4/80.3, Pretoria=-34.0/18.2/66.9, Pyongyang=-41.5/10.8/59.6, Rabat=-34.9/17.2/68.1, Rangpur=-27.2/24.4/74.9, Reggane=-22.9/28.3/77.8, Reykjavík=-45.4/4.3/55.7, Riga=-39.8/6.2/61.3, Riyadh=-23.5/26.0/74.9, Rome=-35.5/15.2/64.5, Roseau=-22.3/26.2/81.4, Rostov-on-Don=-36.8/9.9/61.2, Sacramento=-30.9/16.3/65.1, Saint Petersburg=-43.9/5.8/54.5, Saint-Pierre=-45.1/5.7/53.5, Salt Lake City=-40.6/11.6/67.5, San Antonio=-29.7/20.8/72.8, San Diego=-31.6/17.8/69.0, San Francisco=-36.6/14.6/66.4, San Jose=-30.3/16.4/64.0, San José=-32.8/22.6/73.9, San Juan=-24.9/27.2/76.5, San Salvador=-27.9/23.1/74.4, Sana'a=-31.2/20.0/71.0, Santo Domingo=-26.5/25.9/76.4, Sapporo=-41.4/8.9/60.3, Sarajevo=-43.1/10.1/61.1, Saskatoon=-46.7/3.3/55.8, Seattle=-40.2/11.3/60.1, Seoul=-40.7/12.5/62.3, Seville=-35.5/19.2/68.8, Shanghai=-34.9/16.7/64.6, Singapore=-21.8/27.0/76.5, Skopje=-40.1/12.4/62.3, Sochi=-33.6/14.2/62.6, Sofia=-36.6/10.6/63.1, Sokoto=-21.7/28.0/76.1, Split=-33.9/16.1/63.2, St. John's=-47.5/5.0/57.9, St. Louis=-35.4/13.9/64.0, Stockholm=-43.6/6.6/57.6, Surabaya=-25.2/27.1/77.0, Suva=-26.2/25.6/74.0, Suwałki=-46.4/7.2/57.1, Sydney=-33.9/17.7/71.3, Ségou=-21.3/28.0/75.1, Tabora=-23.8/23.0/73.8, Tabriz=-40.5/12.6/63.5, Taipei=-23.5/23.0/72.8, Tallinn=-48.3/6.4/54.1, Tamale=-24.6/27.9/80.0, Tamanrasset=-26.1/21.7/70.3, Tampa=-28.4/22.9/74.7, Tashkent=-33.6/14.8/64.1, Tauranga=-37.1/14.8/69.5, Tbilisi=-40.9/12.9/63.9, Tegucigalpa=-28.9/21.7/69.4, Tehran=-36.2/17.0/70.8, Tel Aviv=-34.0/20.0/70.2, Thessaloniki=-35.1/16.0/66.3, Thiès=-23.8/24.0/70.9, Tijuana=-32.8/17.8/71.3, Timbuktu=-19.7/28.0/83.9, Tirana=-39.9/15.2/64.4, Toamasina=-30.3/23.4/72.8, Tokyo=-32.7/15.4/65.4, Toliara=-26.2/24.1/75.0, Toluca=-37.1/12.4/59.1, Toronto=-41.2/9.4/59.0, Tripoli=-32.2/20.0/66.5, Tromsø=-47.6/2.9/60.0, Tucson=-33.5/20.9/69.9, Tunis=-30.9/18.4/68.2, Ulaanbaatar=-54.1/-0.4/49.7, Upington=-27.2/20.4/78.8, Vaduz=-41.9/10.1/60.8, Valencia=-31.7/18.3/65.8, Valletta=-32.4/18.8/71.4, Vancouver=-41.1/10.4/60.9, Veracruz=-25.9/25.4/74.6, Vienna=-44.2/10.4/63.4, Vientiane=-23.2/25.9/83.4, Villahermosa=-20.0/27.1/76.7, Vilnius=-44.9/6.0/56.9, Virginia Beach=-35.9/15.8/64.7, Vladivostok=-46.3/4.9/60.2, Warsaw=-41.0/8.5/59.9, Washington, D.C.=-36.3/14.6/65.5, Wau=-20.5/27.8/79.2, Wellington=-33.0/12.9/64.0, Whitehorse=-47.5/-0.1/50.1, Wichita=-35.2/13.9/60.6, Willemstad=-21.7/28.0/77.8, Winnipeg=-51.2/3.0/53.5, Wrocław=-41.4/9.6/60.5, Xi'an=-39.7/14.1/73.7, Yakutsk=-57.8/-8.8/46.7, Yangon=-24.3/27.5/78.7, Yaoundé=-24.8/23.8/71.4, Yellowknife=-51.9/-4.3/50.0, Yerevan=-45.6/12.4/60.0, Yinchuan=-39.0/9.0/63.5, Zagreb=-41.6/10.7/58.5, Zanzibar City=-25.4/26.0/75.7, Zürich=-40.6/9.3/55.4, Ürümqi=-43.5/7.4/58.3, İzmir=-34.7/17.9/68.3}"); } } private static List parseMeasurements(ByteBuffer byteBuffer) { // Most of the code here is derived from @bjhara's implementation // https://github.com/gunnarmorling/1brc/pull/10 var measurements = new ArrayList(100_000); final int limit = byteBuffer.limit(); final byte[] buffer = new byte[128]; while (byteBuffer.position() < limit) { final int start = byteBuffer.position(); int separatorPosition = start; while (separatorPosition != limit && byteBuffer.get(separatorPosition) != ';') { separatorPosition++; } int endOfLinePosition = separatorPosition; // must be after the separator while (endOfLinePosition != limit && byteBuffer.get(endOfLinePosition) != '\n') { endOfLinePosition++; } int nameOffset = separatorPosition - start; byteBuffer.get(buffer, 0, nameOffset); String key = new String(buffer, 0, nameOffset); byteBuffer.get(); // Skip separator int valueLength = endOfLinePosition - separatorPosition - 1; byteBuffer.get(buffer, 0, valueLength); double value = fastParseDouble(buffer, valueLength); byteBuffer.get(); // Skip newline measurements.add(new Measurement(key, value)); } return measurements; } private static double fastParseDouble(byte[] bytes, int length) { long value = 0; int exp = 0; boolean negative = false; int decimalPlaces = Integer.MIN_VALUE; for (int i = 0; i < length; i++) { byte ch = bytes[i]; if (ch >= '0' && ch <= '9') { value = value * 10 + (ch - '0'); decimalPlaces++; } else if (ch == '-') { negative = true; } else if (ch == '.') { decimalPlaces = 0; } } return asDouble(value, exp, negative, decimalPlaces); } private static double asDouble(long value, int exp, boolean negative, int decimalPlaces) { if (decimalPlaces > 0 && value < Long.MAX_VALUE / 2) { if (value < Long.MAX_VALUE / (1L << 32)) { exp -= 32; value <<= 32; } if (value < Long.MAX_VALUE / (1L << 16)) { exp -= 16; value <<= 16; } if (value < Long.MAX_VALUE / (1L << 8)) { exp -= 8; value <<= 8; } if (value < Long.MAX_VALUE / (1L << 4)) { exp -= 4; value <<= 4; } if (value < Long.MAX_VALUE / (1L << 2)) { exp -= 2; value <<= 2; } if (value < Long.MAX_VALUE / (1L << 1)) { exp -= 1; value <<= 1; } } for (; decimalPlaces > 0; decimalPlaces--) { exp--; long mod = value % 5; value /= 5; int modDiv = 1; if (value < Long.MAX_VALUE / (1L << 4)) { exp -= 4; value <<= 4; modDiv <<= 4; } if (value < Long.MAX_VALUE / (1L << 2)) { exp -= 2; value <<= 2; modDiv <<= 2; } if (value < Long.MAX_VALUE / (1L << 1)) { exp -= 1; value <<= 1; modDiv <<= 1; } if (decimalPlaces > 1) { value += modDiv * mod / 5; } else { value += (modDiv * mod + 4) / 5; } } final double d = Math.scalb((double) value, exp); return negative ? -d : d; } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_unbounded.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.*; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ArrayBlockingQueue; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; import static java.lang.foreign.ValueLayout.*; import static java.nio.ByteOrder.BIG_ENDIAN; public class CalculateAverage_unbounded { private static final Path FILE = Path.of("./measurements.txt"); private static final int MAX_STATION_NAME_LEN = 100; private static final int MAX_UNIQUE_STATIONS = 10000; // this is *really* expensive private static final OfInt BIG_ENDIAN_INT = JAVA_INT_UNALIGNED.withOrder(BIG_ENDIAN); private static final VectorSpecies LINE_SCAN_SPECIES = ByteVector.SPECIES_256; private static final int LINE_SCAN_LEN = LINE_SCAN_SPECIES.length(); private static final VectorSpecies NAME_HASH_SPECIES = IntVector.SPECIES_256; private static final VectorSpecies HASH_LOOKUP_SPECIES = ShortVector.SPECIES_256; private static final VectorSpecies ACCUMULATOR_SPECIES = LongVector.SPECIES_256; private static final int CHUNK_SIZE = 16 * 1024 * 1024; // Arbitrarily chosen primes private static final int[] HASH_PRIMES = { 661, 1663, 2293, 3581, 5449, 5953, 6311, 6841, 7573, 7669, 7703, 7789, 7901, 8887, 8581, 8831 }; private static final byte[] PREFIX_MASK = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; private static final int[] DIGIT_MULTIPLIERS = { 0, 10, 1, 1, 100, 10, 1, 1, 0, -10, 1, -1, -100, -10, 1, -1, }; private static final int[] DIGIT_MASK = { 0x000fff0f, 0x0f0fff0f, 0x000fff0f, 0x0f0fff0f, }; private static final int[] DIGIT_FLIPS = { 0, 0, -1, -1 }; record Segment(long start, int len) { } static class StationStat { long count; long totalTemp; int min; int max; StationStat(long count, long totalTemp, int min, int max) { this.count = count; this.totalTemp = totalTemp; this.min = min; this.max = max; } StationStat merge(StationStat other) { this.count += other.count; this.totalTemp += other.totalTemp; this.min = Math.min(this.min, other.min); this.max = Math.max(this.max, other.max); return this; } @Override public String toString() { return STR."\{min/10.0}/\{Math.round(1.0 * totalTemp / count)/10.0}/\{max/10.0}"; } } public static void main(String[] args) throws IOException, InterruptedException { long fileSize = Files.size(FILE); int lastChunkSize = (int) Math.min(200, fileSize); int numSegments = (int) (fileSize / CHUNK_SIZE + 10); var segments = new ArrayBlockingQueue((int) (fileSize / CHUNK_SIZE + 10)); for (long i = 0; i < fileSize - lastChunkSize; i += CHUNK_SIZE) { segments.put(new Segment(i, (int) Math.min(CHUNK_SIZE, fileSize - i - lastChunkSize))); } int numThreads = Runtime.getRuntime().availableProcessors(); var results = new ArrayBlockingQueue>(numThreads); var toMerge = new ArrayList>(numThreads + 1); try (var ch = FileChannel.open(FILE, StandardOpenOption.READ); var arena = Arena.ofConfined()) { var threads = IntStream.range(0, numThreads).mapToObj((ignored) -> new ProcessorThread(segments, ch, results::add)).toList(); threads.forEach(Thread::start); // Process last piece without OOB int margin = lastChunkSize < fileSize ? 1 : 0; var mem = ch.map(FileChannel.MapMode.READ_ONLY, fileSize - lastChunkSize - margin, lastChunkSize + margin, arena); slowProcessChunk(mem, margin, lastChunkSize, toMerge::add); for (var thread : threads) { thread.join(); } } results.drainTo(toMerge); var merged = toMerge.stream().reduce((a, b) -> { b.forEach((k, v) -> a.merge(k, v, StationStat::merge)); return a; }).get(); printResult(merged); } // Simple implementation for the end - so we don't need to worry about reading past the end of the file private static void slowProcessChunk(MemorySegment mem, int startPos, int endPos, Consumer> report) { int index = scanForStartPos(mem, startPos); byte[] nameBuf = new byte[MAX_STATION_NAME_LEN]; while (index < endPos) { int nameLen = 0; while (mem.get(JAVA_BYTE, index) != ';') { nameBuf[nameLen++] = mem.get(JAVA_BYTE, index); index++; } var name = new String(nameBuf, 0, nameLen); index++; StringBuilder numStr = new StringBuilder(5); while (mem.get(JAVA_BYTE, index) != '\n') { if (mem.get(JAVA_BYTE, index) != '.') { numStr.append((char) mem.get(JAVA_BYTE, index)); } index++; } index++; int num = Integer.parseInt(numStr.toString()); var entry = new HashMap(1); entry.put(name, new StationStat(1, num, num, num)); report.accept(entry); } } static class ProcessorThread extends Thread { static final int NUM_BUCKETS = 1024; static final int BUCKET_MASK = 0x3ff; static final int BUCKET_SIZE = 16; // n-way hash table state // 16 buckets, then 16 name pointers private final short[] hashTable = new short[2 * BUCKET_SIZE * NUM_BUCKETS]; // storage of station name keys for hash collision check private final byte[] nameTable = new byte[MAX_UNIQUE_STATIONS * (MAX_STATION_NAME_LEN + 1)]; // values for the hash key stable private final short[] stationIndexes = new short[BUCKET_SIZE * NUM_BUCKETS]; private final int[] nextNamePos = { 0 }; private final int[] nextStationIndex = { 0 }; // Accumulator for (10s, 1s, (count*-2), .1s) per station private final long[] accumulators = new long[4 * MAX_UNIQUE_STATIONS]; // min and max per station private final int[] minMax = new int[2 * MAX_UNIQUE_STATIONS]; private final Queue segments; private final FileChannel channel; private final Consumer> report; ProcessorThread(Queue segments, FileChannel channel, Consumer> report) { this.segments = segments; this.channel = channel; this.report = report; for (int i = 0; i < minMax.length; i += 2) { minMax[i] = Integer.MAX_VALUE; minMax[i + 1] = Integer.MIN_VALUE; } } @Override public void run() { try { while (true) { var segment = segments.poll(); if (segment == null) { break; } int startMargin = segment.start == 0 ? 0 : 1; int endMargin = 64; try (var arena = Arena.ofConfined()) { var mem = channel.map(FileChannel.MapMode.READ_ONLY, segment.start - startMargin, segment.len + endMargin + startMargin, arena); processChunk(mem, startMargin, segment.len + startMargin, hashTable, nameTable, stationIndexes, minMax, accumulators, nextNamePos, nextStationIndex); } } report.accept(decodeResult(hashTable, nameTable, stationIndexes, accumulators, minMax)); } catch (IOException e) { System.err.println(STR."I/O Exception: \{e}"); throw new RuntimeException(e); } } private static void processChunk(MemorySegment mem, int startPos, int endPos, short[] hashTable, byte[] nameTable, short[] stationIndexes, int[] minMax, long[] accumulators, int[] nextNamePos, int[] nextStationIndex) { int index = scanForStartPos(mem, startPos); var primeVec = IntVector.fromArray(NAME_HASH_SPECIES, HASH_PRIMES, 0); while (index < endPos) { var lineVec = ByteVector.fromMemorySegment(LINE_SCAN_SPECIES, mem, index, ByteOrder.LITTLE_ENDIAN); int numPos = lineVec.eq((byte) ';').firstTrue() + 1; int nlPos = 0; int stationIndex; if (numPos != LINE_SCAN_LEN + 1) { // Fast path, station name fits in one SIMD register nlPos = lineVec.eq((byte) '\n').firstTrue(); if (nlPos == LINE_SCAN_LEN) { while (mem.get(JAVA_BYTE, index + nlPos) != '\n') { nlPos++; } } var nameVec = lineVec.and(ByteVector.fromArray(LINE_SCAN_SPECIES, PREFIX_MASK, 33 - numPos)); int nameHash = nameVec.reinterpretAsInts().mul(primeVec).reduceLanes(VectorOperators.ADD); stationIndex = fastLookupHash(nameHash, nameVec, hashTable, nameTable, stationIndexes, nextNamePos, nextStationIndex); } else { // Slow path, station name larger than SIMD register while (mem.get(JAVA_BYTE, index + numPos - 1) != ';') numPos++; while (mem.get(JAVA_BYTE, index + nlPos) != '\n') nlPos++; int nameHash = lineVec.reinterpretAsInts().mul(primeVec).reduceLanes(VectorOperators.ADD); for (int i = LINE_SCAN_LEN; i < numPos - 1; i++) { nameHash = nameHash * 33 + mem.get(JAVA_BYTE, index + i); } stationIndex = lookupHash(nameHash, mem.asSlice(index, numPos - 1), hashTable, nameTable, stationIndexes, nextNamePos, nextStationIndex); } boolean isNegative = mem.get(JAVA_BYTE, index + numPos) == '-'; // format; 0: 9.9, 1: 99.9, 2: -9.9, 3: -99.9 int numFormat = nlPos - numPos - 3 + (isNegative ? 1 : 0); // accumulate sums for mean var numPartsVec = ByteVector.fromMemorySegment(ByteVector.SPECIES_128, mem, index + nlPos - 4, ByteOrder.LITTLE_ENDIAN) .sub((byte) '0') .convert(VectorOperators.B2I, 0); var multiplyVec = IntVector.fromArray(IntVector.SPECIES_128, DIGIT_MULTIPLIERS, 4 * numFormat); var toAdd = numPartsVec.mul(multiplyVec).castShape(ACCUMULATOR_SPECIES, 0); var acc = LongVector.fromArray(ACCUMULATOR_SPECIES, accumulators, 4 * stationIndex); acc.add(toAdd).intoArray(accumulators, 4 * stationIndex); // record min/max // encode ASCII value to sortable format without parsing int encoded = (mem.get(BIG_ENDIAN_INT, index + nlPos - 4) & DIGIT_MASK[numFormat]) ^ DIGIT_FLIPS[numFormat]; minMax[2 * stationIndex] = Math.min(minMax[2 * stationIndex], encoded); minMax[2 * stationIndex + 1] = Math.max(minMax[2 * stationIndex + 1], encoded); index += nlPos + 1; } } // Look up name that fits in a vector private static int fastLookupHash(int nameHash, ByteVector nameVec, short[] hashTable, byte[] nameTable, short[] stationIndexes, int[] nextNamePos, int[] nextStationIndex) { int bucketIdx = nameHash & BUCKET_MASK; short shortHash = (short) (0x8000 | (nameHash >> 16)); // Look up the station name to find the index while (true) { var bucketVec = ShortVector.fromArray(HASH_LOOKUP_SPECIES, hashTable, 2 * BUCKET_SIZE * bucketIdx); var bucketPos = bucketVec.eq(shortHash).firstTrue(); if (bucketPos != HASH_LOOKUP_SPECIES.length()) { int slotNamePos = 32 * Short.toUnsignedInt(hashTable[2 * BUCKET_SIZE * bucketIdx + BUCKET_SIZE + bucketPos]); var slotNameVec = ByteVector.fromArray(LINE_SCAN_SPECIES, nameTable, slotNamePos); if (nameVec.eq(slotNameVec).allTrue()) { // Hit return stationIndexes[BUCKET_SIZE * bucketIdx + bucketPos]; } else { bucketPos = handleHashCollision(shortHash, bucketIdx, MemorySegment.ofArray(nameVec.toArray()), hashTable, nameTable); if (bucketPos != -1) { return stationIndexes[BUCKET_SIZE * bucketIdx + bucketPos]; } } } var emptyPos = bucketVec.eq((short) 0).firstTrue(); if (emptyPos != HASH_LOOKUP_SPECIES.length()) { // Miss, insert int stationIndex = nextStationIndex[0]++; nameVec.intoArray(nameTable, nextNamePos[0]); hashTable[2 * BUCKET_SIZE * bucketIdx + emptyPos] = shortHash; hashTable[2 * BUCKET_SIZE * bucketIdx + BUCKET_SIZE + emptyPos] = (short) (nextNamePos[0] / 32); stationIndexes[BUCKET_SIZE * bucketIdx + emptyPos] = (short) stationIndex; nextNamePos[0] += nameVec.length(); return stationIndex; } // Try next bucket bucketIdx = (bucketIdx + 1) & BUCKET_MASK; } } // Look up long name private static int lookupHash(int nameHash, MemorySegment nameSeg, short[] hashTable, byte[] nameTable, short[] stationIndexes, int[] nextNamePos, int[] nextStationIndex) { int bucketIdx = nameHash & BUCKET_MASK; short shortHash = (short) (0x8000 | (nameHash >> 16)); // Look up the station name to find the index while (true) { var bucketVec = ShortVector.fromArray(HASH_LOOKUP_SPECIES, hashTable, 2 * BUCKET_SIZE * bucketIdx); var bucketPos = bucketVec.eq(shortHash).firstTrue(); if (bucketPos != HASH_LOOKUP_SPECIES.length()) { int slotNamePos = 32 * Short.toUnsignedInt(hashTable[2 * BUCKET_SIZE * bucketIdx + BUCKET_SIZE + bucketPos]); boolean match = true; for (int i = 0; i < nameSeg.byteSize(); i++) { if (nameSeg.get(JAVA_BYTE, i) != nameTable[slotNamePos + i]) { match = false; } } match = match && nameTable[slotNamePos + (int) nameSeg.byteSize()] == '\0'; if (match) { // Hit return stationIndexes[BUCKET_SIZE * bucketIdx + bucketPos]; } else { bucketPos = handleHashCollision(shortHash, bucketIdx, nameSeg, hashTable, nameTable); if (bucketPos != -1) { return stationIndexes[BUCKET_SIZE * bucketIdx + bucketPos]; } } } var emptyPos = bucketVec.eq((short) 0).firstTrue(); if (emptyPos != HASH_LOOKUP_SPECIES.length()) { // Miss, insert int stationIndex = nextStationIndex[0]++; hashTable[2 * BUCKET_SIZE * bucketIdx + emptyPos] = shortHash; hashTable[2 * BUCKET_SIZE * bucketIdx + BUCKET_SIZE + emptyPos] = (short) (nextNamePos[0] / 32); stationIndexes[BUCKET_SIZE * bucketIdx + emptyPos] = (short) stationIndex; for (int i = 0; i < nameSeg.byteSize(); i++) { nameTable[nextNamePos[0]++] = nameSeg.get(JAVA_BYTE, i); } nameTable[nextNamePos[0]++] = '\0'; while (nextNamePos[0] % 32 != 0) nextNamePos[0]++; return stationIndex; } // Try next bucket bucketIdx = (bucketIdx + 1) & BUCKET_MASK; } } private static int handleHashCollision(short shortHash, int bucketIdx, MemorySegment nameSeg, short[] hashTable, byte[] nameTable) { for (int i = 0; i < BUCKET_SIZE; i++) { if (hashTable[2 * BUCKET_SIZE * bucketIdx + i] == shortHash) { int namePos = 32 * Short.toUnsignedInt(hashTable[2 * BUCKET_SIZE * bucketIdx + BUCKET_SIZE + i]); if (Arrays.equals(nameSeg.toArray(JAVA_BYTE), Arrays.copyOfRange(nameTable, namePos, namePos + (int) nameSeg.byteSize())) && nameTable[namePos + (int) nameSeg.byteSize()] == '\0') { return i; } } } return -1; } } // Find next record private static int scanForStartPos(MemorySegment mem, int startPos) { if (startPos == 0) { return startPos; } while (mem.get(JAVA_BYTE, startPos - 1) != '\n') { startPos++; } return startPos; } // Decode the accumulator values to StationStats private static Map decodeResult(short[] hashTable, byte[] nameTable, short[] stationIndexes, long[] accumulators, int[] minMax) { var result = new HashMap(MAX_UNIQUE_STATIONS); for (int i = 0; i < hashTable.length; i += 32) { for (int j = 0; j < 16; j++) { if (hashTable[i + j] != 0) { int namePos = 32 * Short.toUnsignedInt(hashTable[i + j + 16]); int nameLen = 1; while (nameTable[namePos + nameLen] != '\0') { nameLen++; } int stationIdx = stationIndexes[i / 2 + j]; // Number of '-2' valued dots seen long count = accumulators[4 * stationIdx + 2] / -2; long total = accumulators[4 * stationIdx]; total += accumulators[4 * stationIdx + 1]; total += accumulators[4 * stationIdx + 3]; int min = decodeInteger(minMax[2 * stationIdx]); int max = decodeInteger(minMax[2 * stationIdx + 1]); result.put(new String(nameTable, namePos, nameLen), new StationStat(count, total, min, max)); } } } return result; } private static int decodeInteger(int encoded) { int mask = encoded >> 31; int orig = (encoded ^ mask) & 0x7fffffff; int val = (orig & 0xff) + ((orig >> 16) & 0xff) * 10 + ((orig >> 24) & 0xff) * 100; return val * (mask | 1); } private static void printResult(Map stats) { System.out.print("{"); System.out.print( stats.keySet().stream().sorted() .map(key -> { var s = stats.get(key); return STR."\{key}=\{s}"; }) .collect(Collectors.joining(", ")) ); System.out.println("}"); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_vaidhy.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.Function; import java.util.function.Supplier; public class CalculateAverage_vaidhy { private static final class HashEntry { private long startAddress; private long keyLength; private long suffix; private int next; IntSummaryStatistics value; } private static class PrimitiveHashMap { private final HashEntry[] entries; private final long[] hashes; private final int twoPow; private int next = -1; PrimitiveHashMap(int twoPow) { this.twoPow = twoPow; this.entries = new HashEntry[1 << twoPow]; this.hashes = new long[1 << twoPow]; for (int i = 0; i < entries.length; i++) { this.entries[i] = new HashEntry(); } } public IntSummaryStatistics find(long startAddress, long endAddress, long hash, long suffix) { int len = entries.length; int h = Long.hashCode(hash); int initialIndex = (h ^ (h >> twoPow)) & (len - 1); int i = initialIndex; long lookupLength = endAddress - startAddress; long hashEntry = hashes[i]; if (hashEntry == hash) { HashEntry entry = entries[i]; if (lookupLength <= 7) { // This works because // hash = suffix , when simpleHash is just xor. // Since length is not 8, suffix will have a 0 at the end. // Since utf-8 strings can't have 0 in middle of a string this means // we can stop here. return entry.value; } boolean found = (entry.suffix == suffix && compareEntryKeys(startAddress, endAddress, entry.startAddress)); if (found) { return entry.value; } } if (hashEntry == 0) { HashEntry entry = entries[i]; entry.startAddress = startAddress; entry.keyLength = lookupLength; hashes[i] = hash; entry.suffix = suffix; entry.next = next; this.next = i; entry.value = new IntSummaryStatistics(); return entry.value; } i++; if (i == len) { i = 0; } if (i == initialIndex) { return null; } do { hashEntry = hashes[i]; if (hashEntry == hash) { HashEntry entry = entries[i]; if (lookupLength <= 7) { return entry.value; } boolean found = (entry.suffix == suffix && compareEntryKeys(startAddress, endAddress, entry.startAddress)); if (found) { return entry.value; } } if (hashEntry == 0) { HashEntry entry = entries[i]; entry.startAddress = startAddress; entry.keyLength = lookupLength; hashes[i] = hash; entry.suffix = suffix; entry.next = next; this.next = i; entry.value = new IntSummaryStatistics(); return entry.value; } i++; if (i == len) { i = 0; } } while (i != initialIndex); return null; } private static boolean compareEntryKeys(long startAddress, long endAddress, long entryStartAddress) { long entryIndex = entryStartAddress; long lookupIndex = startAddress; long endAddressStop = endAddress - 7; for (; lookupIndex < endAddressStop; lookupIndex += 8) { if (UNSAFE.getLong(entryIndex) != UNSAFE.getLong(lookupIndex)) { return false; } entryIndex += 8; } return true; } public Iterable entrySet() { return () -> new Iterator<>() { int scan = next; @Override public boolean hasNext() { return scan != -1; } @Override public HashEntry next() { HashEntry entry = entries[scan]; scan = entry.next; return entry; } }; } } private static final String FILE = "./measurements.txt"; private static long simpleHash(long hash, long nextData) { return hash ^ nextData; // return (hash ^ Long.rotateLeft((nextData * C1), R1)) * C2; } private static Unsafe initUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(Unsafe.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } private static final Unsafe UNSAFE = initUnsafe(); private static int parseDouble(long startAddress, long endAddress) { int normalized; int length = (int) (endAddress - startAddress); if (length == 5) { normalized = (UNSAFE.getByte(startAddress + 1) ^ 0x30); normalized = (normalized << 3) + (normalized << 1) + (UNSAFE.getByte(startAddress + 2) ^ 0x30); normalized = (normalized << 3) + (normalized << 1) + (UNSAFE.getByte(startAddress + 4) ^ 0x30); normalized = -normalized; return normalized; } if (length == 3) { normalized = (UNSAFE.getByte(startAddress) ^ 0x30); normalized = (normalized << 3) + (normalized << 1) + (UNSAFE.getByte(startAddress + 2) ^ 0x30); return normalized; } if (UNSAFE.getByte(startAddress) == '-') { normalized = (UNSAFE.getByte(startAddress + 1) ^ 0x30); normalized = (normalized << 3) + (normalized << 1) + (UNSAFE.getByte(startAddress + 3) ^ 0x30); normalized = -normalized; return normalized; } else { normalized = (UNSAFE.getByte(startAddress) ^ 0x30); normalized = (normalized << 3) + (normalized << 1) + (UNSAFE.getByte(startAddress + 1) ^ 0x30); normalized = (normalized << 3) + (normalized << 1) + (UNSAFE.getByte(startAddress + 3) ^ 0x30); return normalized; } } interface MapReduce { void process(long keyStartAddress, long keyEndAddress, long hash, long suffix, int temperature); I result(); } private final FileService fileService; private final Supplier> chunkProcessCreator; private final Function, T> reducer; interface FileService { long length(); long address(); } CalculateAverage_vaidhy(FileService fileService, Supplier> mapReduce, Function, T> reducer) { this.fileService = fileService; this.chunkProcessCreator = mapReduce; this.reducer = reducer; } static class LineStream { private final long fileEnd; private final long chunkEnd; private long position; private long hash; private long suffix; private final ByteBuffer buf = ByteBuffer .allocate(8) .order(ByteOrder.LITTLE_ENDIAN); public LineStream(FileService fileService, long offset, long chunkSize) { long fileStart = fileService.address(); this.fileEnd = fileStart + fileService.length(); this.chunkEnd = fileStart + offset + chunkSize; this.position = fileStart + offset; this.hash = 0; } public boolean hasNext() { return position <= chunkEnd; } public long findSemi() { long h = 0; buf.rewind(); for (long i = position; i < fileEnd; i++) { byte ch = UNSAFE.getByte(i); if (ch == ';') { int discard = buf.remaining(); buf.rewind(); long nextData = (buf.getLong() << discard) >>> discard; this.suffix = nextData; this.hash = simpleHash(h, nextData); position = i + 1; return i; } if (buf.hasRemaining()) { buf.put(ch); } else { buf.flip(); long nextData = buf.getLong(); h = simpleHash(h, nextData); buf.rewind(); } } this.hash = h; this.suffix = buf.getLong(); position = fileEnd; return fileEnd; } public long skipLine() { for (long i = position; i < fileEnd; i++) { byte ch = UNSAFE.getByte(i); if (ch == 0x0a) { position = i + 1; return i; } } position = fileEnd; return fileEnd; } public long findTemperature() { position += 3; for (long i = position; i < fileEnd; i++) { byte ch = UNSAFE.getByte(i); if (ch == 0x0a) { position = i + 1; return i; } } position = fileEnd; return fileEnd; } } private static final long START_BYTE_INDICATOR = 0x0101_0101_0101_0101L; private static final long END_BYTE_INDICATOR = START_BYTE_INDICATOR << 7; private static final long NEW_LINE_DETECTION = START_BYTE_INDICATOR * '\n'; private static final long SEMI_DETECTION = START_BYTE_INDICATOR * ';'; private static final long ALL_ONES = 0xffff_ffff_ffff_ffffL; private long findByteOctet(long data, long pattern) { long match = data ^ pattern; return (match - START_BYTE_INDICATOR) & ((~match) & END_BYTE_INDICATOR); } private void bigWorker(long offset, long chunkSize, MapReduce lineConsumer) { long chunkStart = offset + fileService.address(); long chunkEnd = chunkStart + chunkSize; long fileEnd = fileService.address() + fileService.length(); long stopPoint = Math.min(chunkEnd + 1, fileEnd); boolean skip = offset != 0; for (long position = chunkStart; position < stopPoint;) { if (skip) { long data = UNSAFE.getLong(position); long newLineMask = findByteOctet(data, NEW_LINE_DETECTION); if (newLineMask != 0) { int newLinePosition = Long.numberOfTrailingZeros(newLineMask) >>> 3; skip = false; position = position + newLinePosition + 1; } else { position = position + 8; } continue; } long stationStart = position; long stationEnd = -1; long hash = 0; long suffix = 0; do { long data = UNSAFE.getLong(position); long semiMask = findByteOctet(data, SEMI_DETECTION); if (semiMask != 0) { int semiPosition = Long.numberOfTrailingZeros(semiMask) >>> 3; stationEnd = position + semiPosition; position = stationEnd + 1; if (semiPosition != 0) { suffix = data & (ALL_ONES >>> (64 - (semiPosition << 3))); } else { suffix = UNSAFE.getLong(position - 8); } hash = simpleHash(hash, suffix); break; } else { hash = simpleHash(hash, data); position = position + 8; } } while (true); int temperature = 0; { byte ch = UNSAFE.getByte(position++); boolean negative = false; if (ch == '-') { negative = true; ch = UNSAFE.getByte(position++); } do { if (ch != '.') { temperature *= 10; temperature += (ch ^ '0'); } ch = UNSAFE.getByte(position++); } while (ch != '\n'); if (negative) { temperature = -temperature; } } lineConsumer.process(stationStart, stationEnd, hash, suffix, temperature); } } private void smallWorker(long offset, long chunkSize, MapReduce lineConsumer) { LineStream lineStream = new LineStream(fileService, offset, chunkSize); if (offset != 0) { if (lineStream.hasNext()) { // Skip the first line. lineStream.skipLine(); } else { // No lines then do nothing. return; } } while (lineStream.hasNext()) { long keyStartAddress = lineStream.position; long keyEndAddress = lineStream.findSemi(); long keyHash = lineStream.hash; long suffix = lineStream.suffix; long valueStartAddress = lineStream.position; long valueEndAddress = lineStream.findTemperature(); int temperature = parseDouble(valueStartAddress, valueEndAddress); // System.out.println("Small worker!"); lineConsumer.process(keyStartAddress, keyEndAddress, keyHash, suffix, temperature); } } // file size = 7 // (0,0) (0,0) small chunk= (0,7) // a;0.1\n public T master(int shards, ExecutorService executor) { List> summaries = new ArrayList<>(); long len = fileService.length(); if (len > 128) { long bigChunk = Math.floorDiv(len, shards); long bigChunkReAlign = bigChunk & 0xffff_ffff_ffff_fff8L; long smallChunkStart = bigChunkReAlign * shards; long smallChunkSize = len - smallChunkStart; for (long offset = 0; offset < smallChunkStart; offset += bigChunkReAlign) { MapReduce mr = chunkProcessCreator.get(); final long transferOffset = offset; Future task = executor.submit(() -> { bigWorker(transferOffset, bigChunkReAlign, mr); return mr.result(); }); summaries.add(task); } MapReduce mrLast = chunkProcessCreator.get(); Future lastTask = executor.submit(() -> { smallWorker(smallChunkStart, smallChunkSize - 1, mrLast); return mrLast.result(); }); summaries.add(lastTask); } else { MapReduce mrLast = chunkProcessCreator.get(); Future lastTask = executor.submit(() -> { smallWorker(0, len - 1, mrLast); return mrLast.result(); }); summaries.add(lastTask); } List summariesDone = summaries.stream() .map(task -> { try { return task.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }) .toList(); return reducer.apply(summariesDone); } static class DiskFileService implements FileService { private final long fileSize; private final long mappedAddress; DiskFileService(String fileName) throws IOException { FileChannel fileChannel = FileChannel.open(Path.of(fileName), StandardOpenOption.READ); this.fileSize = fileChannel.size(); this.mappedAddress = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()).address(); } @Override public long length() { return fileSize; } @Override public long address() { return mappedAddress; } } private static class ChunkProcessorImpl implements MapReduce { // 1 << 14 > 10,000 so it works private final PrimitiveHashMap statistics = new PrimitiveHashMap(15); @Override public void process(long keyStartAddress, long keyEndAddress, long hash, long suffix, int temperature) { IntSummaryStatistics stats = statistics.find(keyStartAddress, keyEndAddress, hash, suffix); stats.accept(temperature); } @Override public PrimitiveHashMap result() { return statistics; } } public static void main(String[] args) throws IOException { DiskFileService diskFileService = new DiskFileService(FILE); CalculateAverage_vaidhy> calculateAverageVaidhy = new CalculateAverage_vaidhy<>( diskFileService, ChunkProcessorImpl::new, CalculateAverage_vaidhy::combineOutputs); int proc = Runtime.getRuntime().availableProcessors(); ExecutorService executor = Executors.newFixedThreadPool(proc); Map output = calculateAverageVaidhy.master(2 * proc, executor); executor.shutdown(); Map outputStr = toPrintMap(output); System.out.println(outputStr); } private static Map toPrintMap(Map output) { Map outputStr = new TreeMap<>(); for (Map.Entry entry : output.entrySet()) { IntSummaryStatistics stat = entry.getValue(); outputStr.put(entry.getKey(), STR."\{stat.getMin() / 10.0}/\{Math.round(stat.getAverage()) / 10.0}/\{stat.getMax() / 10.0}"); } return outputStr; } private static Map combineOutputs( List list) { Map output = HashMap.newHashMap(10000); for (PrimitiveHashMap map : list) { for (HashEntry entry : map.entrySet()) { if (entry.value != null) { String keyStr = unsafeToString(entry.startAddress, entry.startAddress + entry.keyLength); output.compute(keyStr, (ignore, val) -> { if (val == null) { return entry.value; } else { val.combine(entry.value); return val; } }); } } } return output; } private static String unsafeToString(long startAddress, long endAddress) { byte[] keyBytes = new byte[(int) (endAddress - startAddress)]; for (int i = 0; i < keyBytes.length; i++) { keyBytes[i] = UNSAFE.getByte(startAddress + i); } return new String(keyBytes, StandardCharsets.UTF_8); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_vemana.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; /** * This submission focuses on exploiting the non-SIMD parallelism that is inherent in OOO * super-scalar CPUs and avoids using Unsafe, SWAR and other such fine techniques. The hope is to * remain readable for a majority of SWEs. At a high level, the approach relies on a few principles * listed herein. * *

[Exploit Parallelism] Distribute the work into Shards. Separate threads (one per core) process * Shards and follow it up by merging the results. parallelStream() is appealing but carries * potential run-time variance (i.e. std. deviation) penalties based on informal testing. Variance * is not ideal when trying to minimize the maximum worker latency. * *

[Understand that unmapping is serial and runs in exit()]. This is very much about exploiting * parallelism. After adding tracing (plain old printfs), it was clear that the JVM was taking 400ms * (out of 1500ms) just to exit. Turns out that the kernel tries to unmap all the mappings as part * of the exit() call. Even strace wouldn't report this because the unmapping is running as part of * the exit() call. perf stat barely hinted at it, but we had more insights by actually running a * couple of experiments: reduce touched pages --> JVM shutdown latency went down; manually run * unmap() call to free up the ByteBuffers --> parallel execution doesn't help at all. From this it * was conclusive that unmap() executes serially and the 400ms was being spent purely unmapping. * Now, the challenge is to both (1) unmap a MappedByteBuffer (no such methods exposed) from code * rather than via exit() syscall and (2) do it in parallel without causing lock contention. For 1, * use Reflection and (2) is an interesting math problem with a provably optimal solution. * Parallelism in munmap() is achieved by using a fast lock that prevents two threads from * simultaneously cleaning (i.e. munmap()) the ByteBuffer. * *

[Use ByteBuffers over MemorySegment] Each Shard is further divided in Chunks. This would've * been unnecessary except that Shards are too big to be backed by ByteBuffers. Besides, * MemorySegment appears slower than ByteBuffers. So, to use ByteBuffers, we have to use smaller * chunks. * *

[Straggler freedom] The optimization function here is to minimize the maximal worker thread * completion. Law of large number averages means that all the threads will end up with similar * amounts of work and similar completion times; but, however ever so often there could be a bad * sharding and more importantly, Cores are not created equal; some will be throttled more than * others. So, we have a shared {@code LazyShardQueue} that aims to distribute work to minimize the * latest completion time. * *

[Work Assignment with LazyShardQueue] The queue provides each thread with its next big-chunk * until X% of the work remains. Big-chunks belong to the thread and will not be provided to another * thread. Then, it switches to providing small-chunk sizes. Small-chunks comprise the last X% of * work and every thread can participate in completing the chunk. Even though the queue is shared * across threads, there's no communication across thread during the big-chunk phases. The queue is * effectively a per-thread queue while processing big-chunks. The small-chunk phase uses an * AtomicLong to coordinate chunk allocation across threads. * *

[Chunk processing] Chunk processing is typical. Process line by line. Find a hash function * (polynomial hash fns are slow, but will work fine), hash the city name, resolve conflicts using * linear probing and then accumulate the temperature into the appropriate hash slot. The key * element then is how fast can you identify the hash slot, read the temperature and update the new * temperature in the slot (i.e. min, max, count). * *

[Cache friendliness] 7502P and my machine (7950X) offer 4MB L3 cache/core. This means we can * hope to fit all our datastructures in L3 cache. Since SMT is turned on, the Runtime's available * processors will show twice the number of actual cores and so we get 2MB L3 cache/thread. To be * safe, we try to stay within 1.8 MB/thread and size our hashtable appropriately. * *

[Native ByteOrder is MUCH better] There was almost a 10% lift by reading ints from bytebuffers * using native byteorder . It so happens that both the eval machine (7502P) and my machine 7950X * use native LITTLE_ENDIAN order, which again apparently is because X86[-64] is little-endian. But, * by default, ByteBuffers use BIG_ENDIAN order, which appears to be a somewhat strange default from * Java. * *

[Allocation] Since MemorySegment seemed slower than ByteBuffers, backing Chunks by bytebuffers * was the logical option. Creating one ByteBuffer per chunk was no bueno because the system doesn't * like it (JVM runs out of mapped file handle quota). Other than that, allocation in the hot path * was avoided. * *

[General approach to fast hashing and temperature reading] Here, it helps to understand the * various bottlenecks in execution. One particular thing that I kept coming back to was to * understand the relative costs of instructions: See * https://www.agner.org/optimize/instruction_tables.pdf It is helpful to think of hardware as a * smart parallel execution machine that can do several operations in one cycle if only you can feed * it. So, the idea is to reduce data-dependency chains in the bottleneck path. The other major idea * is to just avoid unnecessary work. For example, copying the city name into a byte array just for * the purpose of looking it up was costing a noticeable amount. Instead, encoding it as * (bytebuffer, start, len) was helpful. Spotting unnecessary work was non-trivial.So, them pesky * range checks? see if you can avoid them. For example, sometimes you can eliminate a "nextPos < * endPos" in a tight loop by breaking it into two pieces: one piece where the check will not be * needed and a tail piece where it will be needed. * *

[Understand What Cores like]. Cores like to go straight and loop back. Despite good branch * prediction, performance sucks with mispredicted branches. * *

[JIT] Java performance requires understanding the JIT. It is helpful to understand what the * JIT likes though it is still somewhat of a mystery to me. In general, it inlines small methods * very well and after constant folding, it can optimize quite well across a reasonably deep call * chain. My experience with the JIT was that everything I tried to tune it made it worse except for * one parameter. I have a new-found respect for JIT - it likes and understands typical Java idioms. * *

[Tuning] Nothing was more insightful than actually playing with various tuning parameters. I * can have all the theories but the hardware and JIT are giant blackboxes. I used a bunch of tools * to optimize: (1) Command line parameters to tune big and small chunk sizes etc. This was also * very helpful in forming a mental model of the JIT. Sometimes, it would compile some methods and * sometimes it would just run them interpreted since the compilation threshold wouldn't be reached * for intermediate methods. (2) AsyncProfiler - this was the first line tool to understand cache * misses and cpu time to figure where to aim the next optimization effort. (3) JitWatch - * invaluable for forming a mental model and attempting to tune the JIT. * *

[Things that didn't work]. This is a looong list and the hit rate is quite low. In general, * removing unnecessary work had a high hit rate and my pet theories on how things work in hardware * had a low hit rate. Java Vector API lacked a good gather API to load from arbitrary memory * addresses; this prevented performing real SIMD on the entire dataset, one where you load 64 bytes * from 64 different line starting positions (just after a previous line end) and then step through * one byte at a time. This to me is the most natural SIMD approach to the 1BRC problem but I * couldn't use it. I tried other local uses of the vector API and it was always slower, and mostly * much slower. In other words, Java Vector API needs a problem for which it is suited (duh) but, * unless I am overlooking something, the API still lacks gather from arbitrary memory addresses. * *

[My general takeaways]. Write simple, idiomatic java code and get 70-80% of the max * performance of an optimally hand-tuned code. Focus any optimization efforts on being friendly to * the JIT *before* thinking about tuning for the hardware. There's a real cost to EXTREME * performance tuning: a loss of abstraction and maintainability, but being JIT friendly is probably * much more achievable without sacrificing abstraction. */ public class CalculateAverage_vemana { public static void main(String[] args) throws Exception { Tracing.recordAppStart(); Runtime.getRuntime() .addShutdownHook( new Thread( () -> { Tracing.recordEvent("In Shutdown hook"); })); // First process in large chunks without coordination among threads // Use chunkSizeBits for the large-chunk size int chunkSizeBits = 20; // For the last commonChunkFraction fraction of total work, use smaller chunk sizes double commonChunkFraction = 0.03; // Use commonChunkSizeBits for the small-chunk size int commonChunkSizeBits = 18; // Size of the hashtable (attempt to fit in L3) int hashtableSizeBits = 14; int minReservedBytesAtFileTail = 9; int nThreads = -1; String inputFile = "measurements.txt"; double munmapFraction = 0.03; boolean fakeAdvance = false; for (String arg : args) { String key = arg.substring(0, arg.indexOf('=')).trim(); String value = arg.substring(key.length() + 1).trim(); switch (key) { case "chunkSizeBits": chunkSizeBits = Integer.parseInt(value); break; case "commonChunkFraction": commonChunkFraction = Double.parseDouble(value); break; case "commonChunkSizeBits": commonChunkSizeBits = Integer.parseInt(value); break; case "hashtableSizeBits": hashtableSizeBits = Integer.parseInt(value); break; case "inputfile": inputFile = value; break; case "munmapFraction": munmapFraction = Double.parseDouble(value); break; case "fakeAdvance": fakeAdvance = Boolean.parseBoolean(value); break; case "nThreads": nThreads = Integer.parseInt(value); break; default: throw new IllegalArgumentException("Unknown argument: " + arg); } } // System.err.println(STR.""" // Using the following parameters: // - chunkSizeBits = \{chunkSizeBits} // - commonChunkFraction = \{commonChunkFraction} // - commonChunkSizeBits = \{commonChunkSizeBits} // - hashtableSizeBits = \{hashtableSizeBits} // """); System.out.println( new Runner( Path.of(inputFile), nThreads, chunkSizeBits, commonChunkFraction, commonChunkSizeBits, hashtableSizeBits, minReservedBytesAtFileTail, munmapFraction, fakeAdvance) .getSummaryStatistics()); Tracing.recordEvent("Final result printed"); } public record AggregateResult(Map tempStats) { @Override public String toString() { return this.tempStats().entrySet().stream() .sorted(Entry.comparingByKey()) .map(entry -> "%s=%s".formatted(entry.getKey(), entry.getValue())) .collect(Collectors.joining(", ", "{", "}")); } } // Mutable to avoid allocation public static class ByteRange { private static final int BUF_SIZE = 1 << 30; private final long fileSize; private final long maxEndPos; // Treat as if the file ends here private final RandomAccessFile raf; private final List unclosedBuffers = new ArrayList<>(); // ***************** What this is doing and why ***************** // Reading from ByteBuffer appears faster from MemorySegment, but ByteBuffer can only be // Integer.MAX_VALUE long; Creating one byteBuffer per chunk kills native memory quota // and JVM crashes without futher parameters. // // So, in this solution, create a sliding window of bytebuffers: // - Create a large bytebuffer that spans the chunk // - If the next chunk falls outside the byteBuffer, create another byteBuffer that spans the // chunk. Because chunks are allocated serially, a single large (1<<30) byteBuffer spans // many successive chunks. // - In fact, for serial chunk allocation (which is friendly to page faulting anyway), // the number of created ByteBuffers doesn't exceed [size of shard/(1<<30)] which is less than // 100/thread and is comfortably below what the JVM can handle (65K) without further param // tuning // - This enables (relatively) allocation free chunking implementation. Our chunking impl uses // fine grained chunking for the last say X% of work to avoid being hostage to stragglers // The PUBLIC API public MappedByteBuffer byteBuffer; public int endInBuf; // where the chunk ends inside the buffer public int startInBuf; // where the chunk starts inside the buffer // Private State private long bufferEnd; // byteBuffer's ending coordinate private long bufferStart; // byteBuffer's begin coordinate // Uninitialized; for mutability public ByteRange(RandomAccessFile raf, long maxEndPos) { this.raf = raf; this.maxEndPos = maxEndPos; try { this.fileSize = raf.length(); } catch (IOException e) { throw new RuntimeException(e); } bufferEnd = bufferStart = -1; } public void close(String closerId, int shardIdx) { Tracing.recordWorkStart(closerId, shardIdx); if (byteBuffer != null) { unclosedBuffers.add(byteBuffer); } for (MappedByteBuffer buf : unclosedBuffers) { close(buf); } unclosedBuffers.clear(); bufferEnd = bufferStart = -1; byteBuffer = null; Tracing.recordWorkEnd(closerId, shardIdx); } public void setRange(long rangeStart, long rangeEnd) { if (rangeEnd + 1024 > bufferEnd || rangeStart < bufferStart) { bufferStart = rangeStart; bufferEnd = Math.min(bufferStart + BUF_SIZE, fileSize); setByteBufferToRange(bufferStart, bufferEnd); } if (rangeStart > 0) { rangeStart = 1 + nextNewLine(rangeStart); } else { rangeStart = 0; } if (rangeEnd < maxEndPos) { rangeEnd = 1 + nextNewLine(rangeEnd); } else { rangeEnd = maxEndPos; } startInBuf = (int) (rangeStart - bufferStart); endInBuf = (int) (rangeEnd - bufferStart); } @Override public String toString() { return STR.""" ByteRange { bufferStart = \{bufferStart} bufferEnd = \{bufferEnd} startInBuf = \{startInBuf} endInBuf = \{endInBuf} } """; } private void close(MappedByteBuffer buffer) { Method cleanerMethod = Reflection.findMethodNamed(buffer, "cleaner"); cleanerMethod.setAccessible(true); Object cleaner = Reflection.invoke(buffer, cleanerMethod); Method cleanMethod = Reflection.findMethodNamed(cleaner, "clean"); cleanMethod.setAccessible(true); Reflection.invoke(cleaner, cleanMethod); } private long nextNewLine(long pos) { int nextPos = (int) (pos - bufferStart); while (byteBuffer.get(nextPos) != '\n') { nextPos++; } return nextPos + bufferStart; } private void setByteBufferToRange(long start, long end) { if (byteBuffer != null) { unclosedBuffers.add(byteBuffer); } try { byteBuffer = raf.getChannel().map(MapMode.READ_ONLY, start, end - start); byteBuffer.order(ByteOrder.nativeOrder()); } catch (IOException e) { throw new RuntimeException(e); } } } public static final class Checks { public static void checkArg(boolean condition) { if (!condition) { throw new IllegalArgumentException(); } } private Checks() { } } public interface LazyShardQueue { void close(String closerId, int shardIdx); Optional fileTailEndWork(int idx); ByteRange take(int shardIdx); } static final class Reflection { static Method findMethodNamed(Object object, String name, Class... paramTypes) { try { return object.getClass().getMethod(name, paramTypes); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } static Object invoke(Object receiver, Method method, Object... params) { try { return method.invoke(receiver, params); } catch (Exception e) { throw new RuntimeException(e); } } } public static class Runner { private final double commonChunkFraction; private final int commonChunkSizeBits; private final boolean fakeAdvance; private final int hashtableSizeBits; private final Path inputFile; private final int minReservedBytesAtFileTail; private final double munmapFraction; private final int nThreads; private final int shardSizeBits; public Runner( Path inputFile, int nThreads, int chunkSizeBits, double commonChunkFraction, int commonChunkSizeBits, int hashtableSizeBits, int minReservedBytesAtFileTail, double munmapFraction, boolean fakeAdvance) { this.inputFile = inputFile; this.nThreads = nThreads; this.shardSizeBits = chunkSizeBits; this.commonChunkFraction = commonChunkFraction; this.commonChunkSizeBits = commonChunkSizeBits; this.hashtableSizeBits = hashtableSizeBits; this.minReservedBytesAtFileTail = minReservedBytesAtFileTail; this.munmapFraction = munmapFraction; this.fakeAdvance = fakeAdvance; } AggregateResult getSummaryStatistics() throws Exception { int nThreads = this.nThreads < 0 ? Runtime.getRuntime().availableProcessors() : this.nThreads; LazyShardQueue shardQueue = new SerialLazyShardQueue( 1L << shardSizeBits, inputFile, nThreads, commonChunkFraction, commonChunkSizeBits, minReservedBytesAtFileTail, munmapFraction, fakeAdvance); ExecutorService executorService = Executors.newFixedThreadPool( nThreads, runnable -> { Thread thread = new Thread(runnable); thread.setDaemon(true); return thread; }); List> results = new ArrayList<>(); for (int i = 0; i < nThreads; i++) { final int shardIdx = i; final Callable callable = () -> { Tracing.recordWorkStart("Shard", shardIdx); AggregateResult result = new ShardProcessor(shardQueue, hashtableSizeBits, shardIdx).processShard(); Tracing.recordWorkEnd("Shard", shardIdx); return result; }; results.add(executorService.submit(callable)); } Tracing.recordEvent("Basic push time"); // This particular sequence of Futures is so that both merge and munmap() can work as shards // finish their computation without blocking on the entire set of shards to complete. In // particular, munmap() doesn't need to wait on merge. // First, submit a task to merge the results and then submit a task to cleanup bytebuffers // from completed shards. Future resultFutures = executorService.submit(() -> merge(results)); // Note that munmap() is serial and not parallel and hence we use just one thread. executorService.submit(() -> closeByteBuffers(results, shardQueue)); AggregateResult result = resultFutures.get(); Tracing.recordEvent("Merge results received"); Tracing.recordEvent("About to shutdown executor and wait"); executorService.shutdown(); executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); Tracing.recordEvent("Executor terminated"); Tracing.analyzeWorkThreads(nThreads); return result; } private void closeByteBuffers( List> results, LazyShardQueue shardQueue) { int n = results.size(); boolean[] isDone = new boolean[n]; int remaining = results.size(); while (remaining > 0) { for (int i = 0; i < n; i++) { if (!isDone[i] && results.get(i).isDone()) { remaining--; isDone[i] = true; shardQueue.close("Ending Cleaner", i); } } } } private AggregateResult merge(List> results) throws ExecutionException, InterruptedException { Tracing.recordEvent("Merge start time"); Map output = null; boolean[] isDone = new boolean[results.size()]; int remaining = results.size(); // Let's be naughty and spin in a busy loop while (remaining > 0) { for (int i = 0; i < results.size(); i++) { if (!isDone[i] && results.get(i).isDone()) { isDone[i] = true; remaining--; if (output == null) { output = new TreeMap<>(results.get(i).get().tempStats()); } else { for (Entry entry : results.get(i).get().tempStats().entrySet()) { output.compute( entry.getKey(), (key, value) -> value == null ? entry.getValue() : Stat.merge(value, entry.getValue())); } } } } } Tracing.recordEvent("Merge end time"); return new AggregateResult(output); } } public static class SerialLazyShardQueue implements LazyShardQueue { private static long roundToNearestLowerMultipleOf(long divisor, long value) { return value / divisor * divisor; } private final ByteRange[] byteRanges; private final long chunkSize; private final long commonChunkSize; private final AtomicLong commonPool; private final long effectiveFileSize; private final boolean fakeAdvance; private final long fileSize; private final long[] perThreadData; private final RandomAccessFile raf; private final SeqLock seqLock; public SerialLazyShardQueue( long chunkSize, Path filePath, int shards, double commonChunkFraction, int commonChunkSizeBits, int fileTailReservedBytes, double munmapFraction, boolean fakeAdvance) throws IOException { this.fakeAdvance = fakeAdvance; Checks.checkArg(commonChunkFraction < 0.9 && commonChunkFraction >= 0); Checks.checkArg(fileTailReservedBytes >= 0); this.raf = new RandomAccessFile(filePath.toFile(), "r"); this.fileSize = raf.length(); fileTailReservedBytes = fileTailReservedBytes == 0 ? 0 : consumeToPreviousNewLineExclusive(raf, fileTailReservedBytes); this.effectiveFileSize = fileSize - fileTailReservedBytes; // Common pool long commonPoolStart = Math.min( roundToNearestLowerMultipleOf( chunkSize, (long) (effectiveFileSize * (1 - commonChunkFraction))), effectiveFileSize); this.commonPool = new AtomicLong(commonPoolStart); this.commonChunkSize = 1L << commonChunkSizeBits; // Distribute chunks to shards this.perThreadData = new long[shards << 4]; // thread idx -> 16*idx to avoid cache line conflict for (long i = 0, currentStart = 0, remainingChunks = (commonPoolStart + chunkSize - 1) / chunkSize; i < shards; i++) { long remainingShards = shards - i; long currentChunks = (remainingChunks + remainingShards - 1) / remainingShards; // Shard i handles: [currentStart, currentStart + currentChunks * chunkSize) int pos = (int) i << 4; perThreadData[pos] = currentStart; // next chunk begin perThreadData[pos + 1] = currentStart + currentChunks * chunkSize; // shard end perThreadData[pos + 2] = currentChunks; // active chunks remaining // threshold below which need to shrink // 0.03 is a practical number but the optimal strategy is this: // Shard number N (1-based) should unmap as soon as it completes (R/(R+1))^N fraction of // its work, where R = relative speed of unmap compared to the computation. // For our problem, R ~ 75 because unmap unmaps 30GB/sec (but, it is serial) while // cores go through data at the rate of 400MB/sec. perThreadData[pos + 3] = (long) (currentChunks * (munmapFraction * (shards - i))); perThreadData[pos + 4] = 1; // true iff munmap() hasn't been triggered yet currentStart += currentChunks * chunkSize; remainingChunks -= currentChunks; } this.chunkSize = chunkSize; this.byteRanges = new ByteRange[shards << 4]; for (int i = 0; i < shards; i++) { byteRanges[i << 4] = new ByteRange(raf, effectiveFileSize); } this.seqLock = new SeqLock(); } @Override public void close(String closerId, int shardIdx) { byteRanges[shardIdx << 4].close(closerId, shardIdx); } @Override public Optional fileTailEndWork(int idx) { if (idx == 0 && effectiveFileSize < fileSize) { ByteRange chunk = new ByteRange(raf, fileSize); chunk.setRange( effectiveFileSize == 0 ? 0 : effectiveFileSize - 1 /* will consume newline at eFS-1 */, fileSize); return Optional.of(chunk); } return Optional.empty(); } @Override public ByteRange take(int shardIdx) { // Try for thread local range final int pos = shardIdx << 4; final long rangeStart; final long rangeEnd; if (perThreadData[pos + 2] >= 1) { rangeStart = perThreadData[pos]; rangeEnd = rangeStart + chunkSize; // Don't do this in the if-check; it causes negative values that trigger intermediate // cleanup perThreadData[pos + 2]--; if (!fakeAdvance) { perThreadData[pos] = rangeEnd; } } else { rangeStart = commonPool.getAndAdd(commonChunkSize); // If that's exhausted too, nothing remains! if (rangeStart >= effectiveFileSize) { return null; } rangeEnd = rangeStart + commonChunkSize; } if (perThreadData[pos + 2] < perThreadData[pos + 3] && perThreadData[pos + 4] > 0) { if (attemptIntermediateClose(shardIdx)) { perThreadData[pos + 4]--; } } ByteRange chunk = byteRanges[pos]; chunk.setRange(rangeStart, rangeEnd); return chunk; } private boolean attemptIntermediateClose(int shardIdx) { if (seqLock.acquire()) { close("Intermediate Cleaner", shardIdx); seqLock.release(); return true; } return false; } private int consumeToPreviousNewLineExclusive(RandomAccessFile raf, int minReservedBytes) { try { long pos = Math.max(raf.length() - minReservedBytes - 1, -1); if (pos < 0) { return (int) raf.length(); } long start = Math.max(pos - 512, 0); ByteBuffer buf = raf.getChannel().map(MapMode.READ_ONLY, start, pos + 1 - start); while (pos >= 0 && buf.get((int) (pos - start)) != '\n') { pos--; } pos++; return (int) (raf.length() - pos); } catch (Exception e) { throw new RuntimeException(e); } } } /** A low-traffic non-blocking lock. */ static class SeqLock { private final AtomicBoolean isOccupied = new AtomicBoolean(false); boolean acquire() { return !isOccupied.get() && isOccupied.compareAndSet(false, true); } void release() { isOccupied.set(false); } } public static class ShardProcessor { private final int shardIdx; private final LazyShardQueue shardQueue; private final ShardProcessorState state; public ShardProcessor(LazyShardQueue shardQueue, int hashtableSizeBits, int shardIdx) { this.shardQueue = shardQueue; this.shardIdx = shardIdx; this.state = new ShardProcessorState(hashtableSizeBits); } public AggregateResult processShard() { return processShardReal(); } public AggregateResult processShardReal() { // First process the file tail work to give ourselves freedom to go past ranges in parsing shardQueue.fileTailEndWork(shardIdx).ifPresent(this::processRangeSlow); ByteRange range; while ((range = shardQueue.take(shardIdx)) != null) { processRange(range); } return result(); } private void processRange(ByteRange range) { MappedByteBuffer mmb = range.byteBuffer; int nextPos = range.startInBuf; int end = range.endInBuf; while (nextPos < end) { nextPos = state.processLine(mmb, nextPos); } } private void processRangeSlow(ByteRange range) { int nextPos = range.startInBuf; while (nextPos < range.endInBuf) { nextPos = state.processLineSlow(range.byteBuffer, nextPos); } } private AggregateResult result() { return state.result(); } } public static class ShardProcessorState { public static final long ONE_MASK = 0x0101010101010101L; private static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder(); private static final long SEMICOLON_MASK = 0x3b3b3b3b3b3b3b3bL; private final byte[][] cityNames; private final int slotsMask; private final Stat[] stats; public ShardProcessorState(int slotsBits) { this.stats = new Stat[1 << slotsBits]; this.cityNames = new byte[1 << slotsBits][]; this.slotsMask = (1 << slotsBits) - 1; } public int processLine(MappedByteBuffer mmb, int nextPos) { int originalPos = nextPos; byte nextByte; int hash = 0; while (true) { int x = mmb.getInt(nextPos); if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { x = Integer.reverseBytes(x); } byte a = (byte) (x >>> 0); if (a == ';') { nextPos += 1; break; } byte b = (byte) (x >>> 8); if (b == ';') { nextPos += 2; hash = hash * 31 + (0xFF & x); break; } byte c = (byte) (x >>> 16); if (c == ';') { nextPos += 3; hash = hash * 31 + (0xFFFF & x); break; } byte d = (byte) (x >>> 24); if (d == ';') { nextPos += 4; hash = hash * 31 + (0xFFFFFF & x); break; } hash = hash * 31 + x; nextPos += 4; } int cityLen = nextPos - 1 - originalPos; // Read temperature int temperature = 0; boolean negative = (nextByte = mmb.get(nextPos++)) == '-'; if (!negative) { temperature = nextByte - '0'; } else { temperature = mmb.get(nextPos++) - '0'; } while (true) { nextByte = mmb.get(nextPos++); if (nextByte != '.') { temperature = temperature * 10 + (nextByte - '0'); } else { temperature = temperature * 10 + (mmb.get(nextPos++) - '0'); nextPos++; break; } } linearProbe( cityLen, hash & slotsMask, negative ? -temperature : temperature, mmb, originalPos); return nextPos; } /** A slow version which is used only for the tail part of the file. */ public int processLineSlow(MappedByteBuffer mmb, int nextPos) { int originalPos = nextPos; byte nextByte; int hash = 0; outer: while (true) { int accumulated = 0; for (int i = 0; i < 4; i++) { nextByte = mmb.get(nextPos++); if (nextByte == ';') { if (i > 0) { hash = hash * 31 + accumulated; } break outer; } else { accumulated |= ((int) nextByte << (8 * i)); } } hash = hash * 31 + accumulated; } int cityLen = nextPos - 1 - originalPos; int temperature = 0; boolean negative = mmb.get(nextPos) == '-'; while ((nextByte = mmb.get(nextPos++)) != '\n') { if (nextByte != '-' && nextByte != '.') { temperature = temperature * 10 + (nextByte - '0'); } } linearProbe( cityLen, hash & slotsMask, negative ? -temperature : temperature, mmb, originalPos); return nextPos; } public AggregateResult result() { int N = stats.length; Map map = new LinkedHashMap<>(5_000); for (int i = 0; i < N; i++) { if (stats[i] != null) { map.put(new String(cityNames[i]), stats[i]); } } return new AggregateResult(map); } private byte[] copyFrom(MappedByteBuffer mmb, int offsetInMmb, int len) { byte[] out = new byte[len]; for (int i = 0; i < len; i++) { out[i] = mmb.get(offsetInMmb + i); } return out; } private boolean equals(byte[] left, MappedByteBuffer right, int offsetInMmb, int len) { for (int i = 0; i < len; i++) { if (left[i] != right.get(offsetInMmb + i)) { return false; } } return true; } private boolean hasSemicolonByte(long value) { long a = value ^ SEMICOLON_MASK; return (((a - ONE_MASK) & ~a) & (0x8080808080808080L)) != 0; } private void linearProbe(int len, int hash, int temp, MappedByteBuffer mmb, int offsetInMmb) { for (int i = hash;; i = (i + 1) & slotsMask) { var curBytes = cityNames[i]; if (curBytes == null) { cityNames[i] = copyFrom(mmb, offsetInMmb, len); stats[i] = Stat.firstReading(temp); return; } else { if (len == curBytes.length && equals(curBytes, mmb, offsetInMmb, len)) { stats[i].mergeReading(temp); return; } } } } } /** Represents aggregate stats. */ public static class Stat { public static Stat firstReading(int temp) { return new Stat(temp, temp, temp, 1); } public static Stat merge(Stat left, Stat right) { return new Stat( Math.min(left.min, right.min), Math.max(left.max, right.max), left.sum + right.sum, left.count + right.count); } private long count, sum; private int min, max; public Stat(int min, int max, long sum, long count) { this.min = min; this.max = max; this.sum = sum; this.count = count; } // Caution: Mutates public void mergeReading(int curTemp) { // Can this be improved furhter? // Assuming random values for curTemp, // min (&max) gets updated roughly log(N)/N fraction of the time (a small number) // In the worst case, there will be at-most one branch misprediction. if (curTemp > min) { // Mostly passes. On branch misprediction, just update min. if (curTemp > max) { // Mostly fails. On branch misprediction, just update max. max = curTemp; } } else { min = curTemp; } sum += curTemp; count++; } @Override public String toString() { return "%.1f/%.1f/%.1f".formatted(min / 10.0, sum / 10.0 / count, max / 10.0); } } static class Tracing { private static final Map knownWorkThreadEvents; private static long startTime; static { // Maintain the ordering to be chronological in execution // Map.of(..) screws up ordering knownWorkThreadEvents = new LinkedHashMap<>(); for (String id : List.of("Shard", "Intermediate Cleaner", "Ending Cleaner")) { knownWorkThreadEvents.put(id, new ThreadTimingsArray(id, 1 << 6 << 1)); } } static void analyzeWorkThreads(int nThreads) { for (ThreadTimingsArray array : knownWorkThreadEvents.values()) { errPrint(array.analyze(nThreads)); } } static void recordAppStart() { startTime = System.nanoTime(); } static void recordEvent(String event) { printEvent(event, System.nanoTime()); } static void recordWorkEnd(String id, int threadId) { knownWorkThreadEvents.get(id).recordEnd(threadId); } static void recordWorkStart(String id, int threadId) { knownWorkThreadEvents.get(id).recordStart(threadId); } ///////////////////////////////////////////////////////////////////////////////////////////////// private static void errPrint(String message) { System.err.println(message); } private static void printEvent(String message, long nanoTime) { errPrint(STR."\{message} = \{(nanoTime - startTime) / 1_000_000}ms"); } public static class ThreadTimingsArray { private static String toString(long[] array) { return Arrays.stream(array) .map(x -> x < 0 ? -1 : x) .mapToObj(x -> String.format("%6d", x)) .collect(Collectors.joining(", ", "[ ", " ]")); } private final String id; private final long[] timestamps; private boolean hasData = false; public ThreadTimingsArray(String id, int maxSize) { this.timestamps = new long[maxSize]; this.id = id; } public String analyze(int nThreads) { if (!hasData) { return "%s has no thread timings data".formatted(id); } Checks.checkArg(nThreads <= timestamps.length); long minDuration = Long.MAX_VALUE, maxDuration = Long.MIN_VALUE; long minBegin = Long.MAX_VALUE, maxCompletion = Long.MIN_VALUE; long maxBegin = Long.MIN_VALUE, minCompletion = Long.MAX_VALUE; long[] durationsMs = new long[nThreads]; long[] completionsMs = new long[nThreads]; long[] beginMs = new long[nThreads]; for (int i = 0; i < nThreads; i++) { long durationNs = timestamps[2 * i + 1] - timestamps[2 * i]; durationsMs[i] = durationNs / 1_000_000; completionsMs[i] = (timestamps[2 * i + 1] - startTime) / 1_000_000; beginMs[i] = (timestamps[2 * i] - startTime) / 1_000_000; minDuration = Math.min(minDuration, durationNs); maxDuration = Math.max(maxDuration, durationNs); minBegin = Math.min(minBegin, timestamps[2 * i] - startTime); maxBegin = Math.max(maxBegin, timestamps[2 * i] - startTime); maxCompletion = Math.max(maxCompletion, timestamps[2 * i + 1] - startTime); minCompletion = Math.min(minCompletion, timestamps[2 * i + 1] - startTime); } return STR.""" ------------------------------------------------------------------------------------------- \{id} Stats ------------------------------------------------------------------------------------------- Max duration = \{maxDuration / 1_000_000} ms Min duration = \{minDuration / 1_000_000} ms Timespan[max(end)-min(start)] = \{(maxCompletion - minBegin) / 1_000_000} ms [\{maxCompletion / 1_000_000} - \{minBegin / 1_000_000} ] Completion Timespan[max(end)-min(end)] = \{(maxCompletion - minCompletion) / 1_000_000} ms Begin Timespan[max(begin)-min(begin)] = \{(maxBegin - minBegin) / 1_000_000} ms Average Duration = \{Arrays.stream(durationsMs) .average() .getAsDouble()} ms Durations = \{toString(durationsMs)} ms Begin Timestamps = \{toString(beginMs)} ms Completion Timestamps = \{toString(completionsMs)} ms """; } public void recordEnd(int idx) { timestamps[2 * idx + 1] = System.nanoTime(); hasData = true; } public void recordStart(int idx) { timestamps[2 * idx] = System.nanoTime(); hasData = true; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_vemanaNonIdiomatic.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import sun.misc.Unsafe; /** * Unlike its sister submission {@code CalculateAverage_vemana}, this submission employs non * idiomatic methods such as SWAR and Unsafe. * *

For details on how this solution works, check the documentation on the sister submission. */ public class CalculateAverage_vemanaNonIdiomatic { public static void main(String[] args) throws Exception { String className = MethodHandles.lookup().lookupClass().getSimpleName(); System.err.println( STR.""" ------------------------------------------------ Running \{className} ------------------------------------------------- """); Tracing.recordAppStart(); Runtime.getRuntime() .addShutdownHook( new Thread( () -> { Tracing.recordEvent("In Shutdown hook"); })); // First process in large chunks without coordination among threads // Use chunkSizeBits for the large-chunk size int chunkSizeBits = 20; // For the last commonChunkFraction fraction of total work, use smaller chunk sizes double commonChunkFraction = 0.03; // Use commonChunkSizeBits for the small-chunk size int commonChunkSizeBits = 18; // Size of the hashtable (attempt to fit in L2 of 512KB of eval machine) int hashtableSizeBits = className.toLowerCase().contains("nonidiomatic") ? 13 : 16; // Reserve some number of lines at the end to give us freedom in reading LONGs past ranges int minReservedBytesAtFileTail = 9; // Number of threads int nThreads = -1; String inputFile = "measurements.txt"; // Parallelize unmap. Thread #n (n=1,2,..N) unmaps its bytebuffer when // munmapFraction * n work remains. double munmapFraction = 0.03; boolean fakeAdvance = false; for (String arg : args) { String key = arg.substring(0, arg.indexOf('=')).trim(); String value = arg.substring(key.length() + 1).trim(); switch (key) { case "chunkSizeBits": chunkSizeBits = Integer.parseInt(value); break; case "commonChunkFraction": commonChunkFraction = Double.parseDouble(value); break; case "commonChunkSizeBits": commonChunkSizeBits = Integer.parseInt(value); break; case "hashtableSizeBits": hashtableSizeBits = Integer.parseInt(value); break; case "inputFile": inputFile = value; break; case "munmapFraction": munmapFraction = Double.parseDouble(value); break; case "fakeAdvance": fakeAdvance = Boolean.parseBoolean(value); break; case "nThreads": nThreads = Integer.parseInt(value); break; default: throw new IllegalArgumentException("Unknown argument: " + arg); } } System.out.println( new Runner( Path.of(inputFile), nThreads, chunkSizeBits, commonChunkFraction, commonChunkSizeBits, hashtableSizeBits, minReservedBytesAtFileTail, munmapFraction, fakeAdvance) .getSummaryStatistics()); Tracing.recordEvent("Final result printed"); } public record AggregateResult(Map tempStats) { @Override public String toString() { return this.tempStats().entrySet().stream() .sorted(Map.Entry.comparingByKey()) .map(entry -> "%s=%s".formatted(entry.getKey(), entry.getValue())) .collect(Collectors.joining(", ", "{", "}")); } } // Mutable to avoid allocation public static class ByteRange { private static final int BUF_SIZE = 1 << 28; private final long fileSize; private final long maxEndPos; // Treat as if the file ends here private final RandomAccessFile raf; private final int shardIdx; private final List unclosedBuffers = new ArrayList<>(); // ***************** What this is doing and why ***************** // Reading from ByteBuffer appears faster from MemorySegment, but ByteBuffer can only be // Integer.MAX_VALUE long; Creating one byteBuffer per chunk kills native memory quota // and JVM crashes without futher parameters. // // So, in this solution, create a sliding window of bytebuffers: // - Create a large bytebuffer that spans the chunk // - If the next chunk falls outside the byteBuffer, create another byteBuffer that spans the // chunk. Because chunks are allocated serially, a single large (1<<30) byteBuffer spans // many successive chunks. // - In fact, for serial chunk allocation (which is friendly to page faulting anyway), // the number of created ByteBuffers doesn't exceed [size of shard/(1<<30)] which is less than // 100/thread and is comfortably below what the JVM can handle (65K) without further param // tuning // - This enables (relatively) allocation free chunking implementation. Our chunking impl uses // fine grained chunking for the last say X% of work to avoid being hostage to stragglers ///////////// The PUBLIC API public MappedByteBuffer byteBuffer; public long endAddress; // the virtual memory address corresponding to 'endInBuf' public int endInBuf; // where the chunk ends inside the buffer public long startAddress; // the virtual memory address corresponding to 'startInBuf' public int startInBuf; // where the chunk starts inside the buffer ///////////// Private State long bufferBaseAddr; // buffer's base virtual memory address long extentEnd; // byteBuffer's ending coordinate long extentStart; // byteBuffer's begin coordinate // Uninitialized; for mutability public ByteRange(RandomAccessFile raf, long maxEndPos, int shardIdx) { this.raf = raf; this.maxEndPos = maxEndPos; this.shardIdx = shardIdx; try { this.fileSize = raf.length(); } catch (IOException e) { throw new RuntimeException(e); } bufferCleanSlate(); } public void close(String closerId) { Tracing.recordWorkStart(closerId, shardIdx); bufferCleanSlate(); for (MappedByteBuffer buf : unclosedBuffers) { close(buf); } unclosedBuffers.clear(); Tracing.recordWorkEnd(closerId, shardIdx); } public void setRange(long rangeStart, long rangeEnd) { if (rangeEnd + 1024 > extentEnd || rangeStart < extentStart) { setByteBufferExtent(rangeStart, Math.min(rangeStart + BUF_SIZE, fileSize)); } if (rangeStart > 0) { rangeStart = 1 + nextNewLine(rangeStart); } else { rangeStart = 0; } if (rangeEnd < maxEndPos) { // rangeEnd = 1 + nextNewLine(rangeEnd); // not needed rangeEnd = 1 + rangeEnd; } else { rangeEnd = maxEndPos; } startInBuf = (int) (rangeStart - extentStart); endInBuf = (int) (rangeEnd - extentStart); startAddress = bufferBaseAddr + startInBuf; endAddress = bufferBaseAddr + endInBuf; } @Override public String toString() { return STR.""" ByteRange { shard = \{shardIdx} extentStart = \{extentStart} extentEnd = \{extentEnd} startInBuf = \{startInBuf} endInBuf = \{endInBuf} startAddress = \{startAddress} endAddress = \{endAddress} } """; } private void bufferCleanSlate() { if (byteBuffer != null) { unclosedBuffers.add(byteBuffer); byteBuffer = null; } extentEnd = extentStart = bufferBaseAddr = startAddress = endAddress = -1; } private void close(MappedByteBuffer buffer) { Method cleanerMethod = Reflection.findMethodNamed(buffer, "cleaner"); cleanerMethod.setAccessible(true); Object cleaner = Reflection.invoke(buffer, cleanerMethod); Method cleanMethod = Reflection.findMethodNamed(cleaner, "clean"); cleanMethod.setAccessible(true); Reflection.invoke(cleaner, cleanMethod); } private long getBaseAddr(MappedByteBuffer buffer) { Method addressMethod = Reflection.findMethodNamed(buffer, "address"); addressMethod.setAccessible(true); return (long) Reflection.invoke(buffer, addressMethod); } private long nextNewLine(long pos) { int nextPos = (int) (pos - extentStart); while (byteBuffer.get(nextPos) != '\n') { nextPos++; } return nextPos + extentStart; } /** * Extent different from Range. Range is what needs to be processed. Extent is what the byte * buffer can read without failing. */ private void setByteBufferExtent(long start, long end) { bufferCleanSlate(); try { byteBuffer = raf.getChannel().map(MapMode.READ_ONLY, start, end - start); byteBuffer.order(ByteOrder.nativeOrder()); } catch (IOException e) { throw new RuntimeException(e); } extentStart = start; extentEnd = end; bufferBaseAddr = getBaseAddr(byteBuffer); } } public static final class Checks { public static void checkArg(boolean condition) { if (!condition) { throw new IllegalArgumentException(); } } private Checks() { } } /* * ENTRY SHAPE * Ensure alignment boundaries. 4 bytes on 4 byte, 2 bytes on 2 byte etc. * 32 bytes per entry. * 96 KB L1 cache. 2048 entries should fully fit * ------------------- * str: 14 bytes [Defined by constant STR_FIELD_LEN] * hash: 2 bytes * cityNameOffset: 3 bytes // Index in city names array if len > STR_FIELD_LEN bytes * len: 1 byte // Length of string, in bytes * sum: 4 bytes * count: 4 bytes * max: 2 bytes * min: 2 bytes */ static class EntryData { public static final int ENTRY_SIZE_BITS = 5; /////////// OFFSETS /////////////// private static final int OFFSET_STR = 0; private static final int STR_FIELD_LEN = 14; private static final int OFFSET_HASH = OFFSET_STR + STR_FIELD_LEN; private static final int OFFSET_CITY_NAME_EXTRA = OFFSET_HASH + 2; private static final int OFFSET_LEN = OFFSET_CITY_NAME_EXTRA + 3; private static final int OFFSET_SUM = OFFSET_LEN + 1; private static final int OFFSET_COUNT = OFFSET_SUM + 4; private static final int OFFSET_MAX = OFFSET_COUNT + 4; private static final int OFFSET_MIN = OFFSET_MAX + 2; public static int strFieldLen() { return STR_FIELD_LEN; } private final EntryMeta entryMeta; private long baseAddress; public EntryData(EntryMeta entryMeta) { this.entryMeta = entryMeta; } public long baseAddress() { return baseAddress; } public String cityNameString() { int len = len(); byte[] zeBytes = new byte[len]; for (int i = 0; i < Math.min(len, strFieldLen()); i++) { zeBytes[i] = Unsafely.readByte(baseAddress + i); } if (len > strFieldLen()) { int rem = len - strFieldLen(); long ptr = entryMeta.cityNamesAddress(cityNamesOffset()); for (int i = 0; i < rem; i++) { zeBytes[strFieldLen() + i] = Unsafely.readByte(ptr + i); } } return new String(zeBytes); } public int cityNamesOffset() { return Unsafely.readInt(baseAddress + OFFSET_CITY_NAME_EXTRA) & 0xFFFFFF; } public int count() { return Unsafely.readInt(baseAddress + OFFSET_COUNT); } public short hash16() { return Unsafely.readShort(baseAddress + OFFSET_HASH); } public int index() { return (int) ((baseAddress() - entryMeta.baseAddress(0)) >> ENTRY_SIZE_BITS); } public void init(long srcAddr, int len, short hash16, short temperature) { // Copy the string Unsafely.copyMemory(srcAddr, strAddress(), Math.min(len, EntryData.strFieldLen())); if (len > EntryData.strFieldLen()) { int remaining = len - EntryData.strFieldLen(); int cityNamesOffset = entryMeta.getAndIncrementCityNames(remaining); Unsafely.copyMemory( srcAddr + EntryData.strFieldLen(), entryMeta.cityNamesAddress(cityNamesOffset), remaining); setCityNameOffset(cityNamesOffset, len); } else { setLen((byte) len); } // and then update the others setHash16(hash16); setSum(temperature); setCount(1); setMax(temperature); setMin(temperature); } public boolean isPresent() { return len() > 0; } public int len() { return Unsafely.readByte(baseAddress + OFFSET_LEN); } public short max() { return Unsafely.readShort(baseAddress + OFFSET_MAX); } public short min() { return Unsafely.readShort(baseAddress + OFFSET_MIN); } public void setBaseAddress(long baseAddress) { this.baseAddress = baseAddress; } public void setCityNameOffset(int cityNamesOffset, int len) { // The 24 here is 3 bytes for Cityname extra index + 1 byte for actual len // that writes 4 bytes in one shot. It is not an offset. Unsafely.setInt(baseAddress + OFFSET_CITY_NAME_EXTRA, cityNamesOffset | (len << 24)); } public void setCount(int value) { Unsafely.setInt(baseAddress + OFFSET_COUNT, value); } public void setHash16(short value) { Unsafely.setShort(baseAddress + OFFSET_HASH, value); } public void setIndex(int index) { setBaseAddress(entryMeta.baseAddress(index)); } public void setLen(byte value) { Unsafely.setByte(baseAddress + OFFSET_LEN, value); } public void setMax(short value) { Unsafely.setShort(baseAddress + OFFSET_MAX, value); } public void setMin(short value) { Unsafely.setShort(baseAddress + OFFSET_MIN, value); } public void setSum(int value) { Unsafely.setInt(baseAddress + OFFSET_SUM, value); } public Stat stat() { return new Stat(min(), max(), sum(), count()); } public long strAddress() { return baseAddress + OFFSET_STR; } public int sum() { return Unsafely.readInt(baseAddress + OFFSET_SUM); } public String toString() { return STR.""" min = \{min()} max = \{max()} count = \{count()} sum = \{sum()} """; } public void update(short temperature) { setMin((short) Math.min(min(), temperature)); setMax((short) Math.max(max(), temperature)); setCount(count() + 1); setSum(sum() + temperature); } public boolean updateOnMatch( EntryMeta entryMeta, long srcAddr, int len, short hash16, short temperature) { // Quick paths if (len() != len) { return false; } if (hash16() != hash16) { return false; } // Actual string comparison if (len <= STR_FIELD_LEN) { if (!Unsafely.matches(srcAddr, strAddress(), len)) { return false; } } else { if (!Unsafely.matches(srcAddr, strAddress(), STR_FIELD_LEN)) { return false; } if (!Unsafely.matches( srcAddr + STR_FIELD_LEN, entryMeta.cityNamesAddress(cityNamesOffset()), len - STR_FIELD_LEN)) { return false; } } update(temperature); return true; } } /** Metadata for the collection of entries */ static class EntryMeta { static int toIntFromUnsignedShort(short x) { int ret = x; if (ret < 0) { ret += (1 << 16); } return ret; } private final long baseAddress; private final long cityNamesBaseAddress; // For city names that overflow Entry.STR_FIELD_LEN private final int hashMask; private final int n_entries; private final int n_entriesBits; private long cityNamesEndAddress; // [cityNamesBaseAddress, cityNamesEndAddress) EntryMeta(int n_entriesBits, EntryMeta oldEntryMeta) { this.n_entries = 1 << n_entriesBits; this.hashMask = (1 << n_entriesBits) - 1; this.n_entriesBits = n_entriesBits; this.baseAddress = Unsafely.allocateZeroedCacheLineAligned(this.n_entries << EntryData.ENTRY_SIZE_BITS); if (oldEntryMeta == null) { this.cityNamesBaseAddress = Unsafely.allocateZeroedCacheLineAligned(1 << 17); this.cityNamesEndAddress = cityNamesBaseAddress; } else { this.cityNamesBaseAddress = oldEntryMeta.cityNamesBaseAddress; this.cityNamesEndAddress = oldEntryMeta.cityNamesEndAddress; } } public long cityNamesAddress(int extraLenOffset) { return cityNamesBaseAddress + extraLenOffset; } public int indexFromHash16(short hash16) { return indexFromHash32(toIntFromUnsignedShort(hash16)); } public int nEntriesBits() { return n_entriesBits; } // Base Address of nth entry long baseAddress(int n) { return baseAddress + ((long) n << EntryData.ENTRY_SIZE_BITS); } // Size of each entry int entrySizeInBytes() { return 1 << EntryData.ENTRY_SIZE_BITS; } int getAndIncrementCityNames(int len) { long ret = cityNamesEndAddress; cityNamesEndAddress += ((len + 7) >> 3) << 3; // use aligned 8 bytes return (int) (ret - cityNamesBaseAddress); } // Index of an entry with given hash32 int indexFromHash32(int hash32) { return hash32 & hashMask; } // Number of entries int nEntries() { return n_entries; } int nextIndex(int index) { return (index + 1) & hashMask; } } static class Hashtable { // State int n_filledEntries; // A single Entry to avoid local allocation private EntryData entry; private EntryMeta entryMeta; // Invariants // hash16 = (short) hash32 // index = hash16 & hashMask private int hashHits = 0, hashMisses = 0; Hashtable(int slotsBits) { entryMeta = new EntryMeta(slotsBits, null); this.entry = new EntryData(entryMeta); } public void addDataPoint(long srcAddr, int len, int hash32, short temperature) { // hashHits++; for (int index = entryMeta.indexFromHash32(hash32);; index = entryMeta.nextIndex(index)) { entry.setIndex(index); if (!entry.isPresent()) { entry.init(srcAddr, len, (short) hash32, temperature); onNewEntry(); return; } if (entry.updateOnMatch(entryMeta, srcAddr, len, (short) hash32, temperature)) { return; } // hashMisses++; } } public AggregateResult result() { Map map = new LinkedHashMap<>(5_000); for (int i = 0; i < entryMeta.nEntries(); i++) { entry.setIndex(i); if (entry.isPresent()) { map.put(entry.cityNameString(), entry.stat()); } } System.err.println( STR.""" HashHits = \{hashHits} HashMisses = \{hashMisses} (\{hashMisses * 100.0 / hashHits}) """); return new AggregateResult(map); } private EntryData getNewEntry(EntryData oldEntry, EntryMeta newEntryMeta) { EntryData newEntry = new EntryData(newEntryMeta); for (int index = newEntryMeta.indexFromHash16(oldEntry.hash16());; index = newEntryMeta.nextIndex(index)) { newEntry.setIndex(index); if (!newEntry.isPresent()) { return newEntry; } } } private void onNewEntry() { if (++n_filledEntries == 450) { reHash(16); } } private void reHash(int new_N_EntriesBits) { EntryMeta oldEntryMeta = this.entryMeta; EntryData oldEntry = new EntryData(oldEntryMeta); Checks.checkArg(new_N_EntriesBits <= 16); Checks.checkArg(new_N_EntriesBits > oldEntryMeta.nEntriesBits()); EntryMeta newEntryMeta = new EntryMeta(new_N_EntriesBits, oldEntryMeta); for (int i = 0; i < oldEntryMeta.nEntries(); i++) { oldEntry.setIndex(i); if (oldEntry.isPresent()) { Unsafely.copyMemory( oldEntry.baseAddress(), getNewEntry(oldEntry, newEntryMeta).baseAddress(), oldEntryMeta.entrySizeInBytes()); } } this.entryMeta = newEntryMeta; this.entry = new EntryData(this.entryMeta); } } public interface LazyShardQueue { void close(String closerId, int shardIdx); Optional fileTailEndWork(int idx); ByteRange take(int shardIdx); } static final class Reflection { static Method findMethodNamed(Object object, String name, Class... paramTypes) { try { return object.getClass().getMethod(name, paramTypes); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } static Object invoke(Object receiver, Method method, Object... params) { try { return method.invoke(receiver, params); } catch (Exception e) { throw new RuntimeException(e); } } } public static class Runner { private final double commonChunkFraction; private final int commonChunkSizeBits; private final boolean fakeAdvance; private final int hashtableSizeBits; private final Path inputFile; private final int minReservedBytesAtFileTail; private final double munmapFraction; private final int nThreads; private final int shardSizeBits; public Runner( Path inputFile, int nThreads, int chunkSizeBits, double commonChunkFraction, int commonChunkSizeBits, int hashtableSizeBits, int minReservedBytesAtFileTail, double munmapFraction, boolean fakeAdvance) { this.inputFile = inputFile; this.nThreads = nThreads; this.shardSizeBits = chunkSizeBits; this.commonChunkFraction = commonChunkFraction; this.commonChunkSizeBits = commonChunkSizeBits; this.hashtableSizeBits = hashtableSizeBits; this.minReservedBytesAtFileTail = minReservedBytesAtFileTail; this.munmapFraction = munmapFraction; this.fakeAdvance = fakeAdvance; } AggregateResult getSummaryStatistics() throws Exception { int nThreads = this.nThreads < 0 ? Runtime.getRuntime().availableProcessors() : this.nThreads; LazyShardQueue shardQueue = new SerialLazyShardQueue( 1L << shardSizeBits, inputFile, nThreads, commonChunkFraction, commonChunkSizeBits, minReservedBytesAtFileTail, munmapFraction, fakeAdvance); ExecutorService executorService = Executors.newFixedThreadPool( nThreads, runnable -> { Thread thread = new Thread(runnable); thread.setDaemon(true); return thread; }); List> results = new ArrayList<>(); for (int i = 0; i < nThreads; i++) { final int shardIdx = i; final Callable callable = () -> { Tracing.recordWorkStart("Shard", shardIdx); AggregateResult result = new ShardProcessor(shardQueue, hashtableSizeBits, shardIdx).processShard(); Tracing.recordWorkEnd("Shard", shardIdx); return result; }; results.add(executorService.submit(callable)); } Tracing.recordEvent("Basic push time"); // This particular sequence of Futures is so that both merge and munmap() can work as shards // finish their computation without blocking on the entire set of shards to complete. In // particular, munmap() doesn't need to wait on merge. // First, submit a task to merge the results and then submit a task to cleanup bytebuffers // from completed shards. Future resultFutures = executorService.submit(() -> merge(results)); // Note that munmap() is serial and not parallel and hence we use just one thread. executorService.submit(() -> closeByteBuffers(results, shardQueue)); AggregateResult result = resultFutures.get(); Tracing.recordEvent("Merge results received"); Tracing.recordEvent("About to shutdown executor and wait"); executorService.shutdown(); executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); Tracing.recordEvent("Executor terminated"); Tracing.analyzeWorkThreads(nThreads); return result; } private void closeByteBuffers( List> results, LazyShardQueue shardQueue) { int n = results.size(); boolean[] isDone = new boolean[n]; int remaining = results.size(); while (remaining > 0) { for (int i = 0; i < n; i++) { if (!isDone[i] && results.get(i).isDone()) { remaining--; isDone[i] = true; shardQueue.close("Ending Cleaner", i); } } } } private AggregateResult merge(List> results) throws ExecutionException, InterruptedException { Tracing.recordEvent("Merge start time"); Map output = null; boolean[] isDone = new boolean[results.size()]; int remaining = results.size(); // Let's be naughty and spin in a busy loop while (remaining > 0) { for (int i = 0; i < results.size(); i++) { if (!isDone[i] && results.get(i).isDone()) { isDone[i] = true; remaining--; if (output == null) { output = new TreeMap<>(results.get(i).get().tempStats()); } else { for (Entry entry : results.get(i).get().tempStats().entrySet()) { output.compute( entry.getKey(), (key, value) -> value == null ? entry.getValue() : Stat.merge(value, entry.getValue())); } } } } } Tracing.recordEvent("Merge end time"); return new AggregateResult(output); } } public static class SerialLazyShardQueue implements LazyShardQueue { private static long roundToNearestLowerMultipleOf(long divisor, long value) { return value / divisor * divisor; } private final ByteRange[] byteRanges; private final long chunkSize; private final long commonChunkSize; private final AtomicLong commonPool; private final long effectiveFileSize; private final boolean fakeAdvance; private final long fileSize; private final long[] perThreadData; private final RandomAccessFile raf; private final SeqLock seqLock; public SerialLazyShardQueue( long chunkSize, Path filePath, int shards, double commonChunkFraction, int commonChunkSizeBits, int fileTailReservedBytes, double munmapFraction, boolean fakeAdvance) throws IOException { this.fakeAdvance = fakeAdvance; Checks.checkArg(commonChunkFraction < 0.9 && commonChunkFraction >= 0); Checks.checkArg(fileTailReservedBytes >= 0); this.raf = new RandomAccessFile(filePath.toFile(), "r"); this.fileSize = raf.length(); fileTailReservedBytes = fileTailReservedBytes == 0 ? 0 : consumeToPreviousNewLineExclusive(raf, fileTailReservedBytes); this.effectiveFileSize = fileSize - fileTailReservedBytes; // Common pool long commonPoolStart = Math.min( roundToNearestLowerMultipleOf( chunkSize, (long) (effectiveFileSize * (1 - commonChunkFraction))), effectiveFileSize); this.commonPool = new AtomicLong(commonPoolStart); this.commonChunkSize = 1L << commonChunkSizeBits; // Distribute chunks to shards this.perThreadData = new long[shards << 4]; // thread idx -> 16*idx to avoid cache line conflict for (long i = 0, currentStart = 0, remainingChunks = (commonPoolStart + chunkSize - 1) / chunkSize; i < shards; i++) { long remainingShards = shards - i; long currentChunks = (remainingChunks + remainingShards - 1) / remainingShards; // Shard i handles: [currentStart, currentStart + currentChunks * chunkSize) int pos = (int) i << 4; perThreadData[pos] = currentStart; // next chunk begin perThreadData[pos + 1] = currentStart + currentChunks * chunkSize; // shard end perThreadData[pos + 2] = currentChunks; // active chunks remaining // threshold below which need to shrink // 0.03 is a practical number but the optimal strategy is this: // Shard number N (1-based) should unmap as soon as it completes (R/(R+1))^N fraction of // its work, where R = relative speed of unmap compared to the computation. // For our problem, R ~ 75 because unmap unmaps 30GB/sec (but, it is serial) while // cores go through data at the rate of 400MB/sec. perThreadData[pos + 3] = (long) (currentChunks * (munmapFraction * (shards - i))); perThreadData[pos + 4] = 1; // true iff munmap() hasn't been triggered yet currentStart += currentChunks * chunkSize; remainingChunks -= currentChunks; } this.chunkSize = chunkSize; this.byteRanges = new ByteRange[shards << 4]; for (int i = 0; i < shards; i++) { byteRanges[i << 4] = new ByteRange(raf, effectiveFileSize, i); } this.seqLock = new SeqLock(); } @Override public void close(String closerId, int shardIdx) { byteRanges[shardIdx << 4].close(closerId); } @Override public Optional fileTailEndWork(int idx) { if (idx == 0 && effectiveFileSize < fileSize) { ByteRange chunk = new ByteRange(raf, fileSize, 0); chunk.setRange( effectiveFileSize == 0 ? 0 : effectiveFileSize - 1 /* will consume newline at eFS-1 */, fileSize); return Optional.of(chunk); } return Optional.empty(); } @Override public ByteRange take(int shardIdx) { // Try for thread local range final int pos = shardIdx << 4; final long rangeStart; final long rangeEnd; if (perThreadData[pos + 2] >= 1) { rangeStart = perThreadData[pos]; rangeEnd = rangeStart + chunkSize; // Don't do this in the if-check; it causes negative values that trigger intermediate // cleanup perThreadData[pos + 2]--; if (!fakeAdvance) { perThreadData[pos] = rangeEnd; } } else { rangeStart = commonPool.getAndAdd(commonChunkSize); // If that's exhausted too, nothing remains! if (rangeStart >= effectiveFileSize) { return null; } rangeEnd = rangeStart + commonChunkSize; } if (perThreadData[pos + 2] < perThreadData[pos + 3] && perThreadData[pos + 4] > 0) { if (attemptIntermediateClose(shardIdx)) { perThreadData[pos + 4]--; } } ByteRange chunk = byteRanges[pos]; chunk.setRange(rangeStart, rangeEnd); return chunk; } private boolean attemptIntermediateClose(int shardIdx) { if (seqLock.acquire()) { close("Intermediate Cleaner", shardIdx); seqLock.release(); return true; } return false; } private int consumeToPreviousNewLineExclusive(RandomAccessFile raf, int minReservedBytes) { try { long pos = Math.max(raf.length() - minReservedBytes - 1, -1); if (pos < 0) { return (int) raf.length(); } long start = Math.max(pos - 512, 0); ByteBuffer buf = raf.getChannel().map(MapMode.READ_ONLY, start, pos + 1 - start); while (pos >= 0 && buf.get((int) (pos - start)) != '\n') { pos--; } pos++; return (int) (raf.length() - pos); } catch (Exception e) { throw new RuntimeException(e); } } } /** A low-traffic non-blocking lock. */ static class SeqLock { private final AtomicBoolean isOccupied = new AtomicBoolean(false); boolean acquire() { return !isOccupied.get() && isOccupied.compareAndSet(false, true); } void release() { isOccupied.set(false); } } public static class ShardProcessor { private final int shardIdx; private final LazyShardQueue shardQueue; private final FastShardProcessorState state; public ShardProcessor(LazyShardQueue shardQueue, int hashtableSizeBits, int shardIdx) { this.shardQueue = shardQueue; this.shardIdx = shardIdx; this.state = new FastShardProcessorState(hashtableSizeBits); } public AggregateResult processShard() { return processShardReal(); } private void processRange(ByteRange range) { long nextPos = range.startAddress; while (nextPos < range.endAddress) { nextPos = state.processLine(nextPos); } } private void processRangeSlow(ByteRange range) { long nextPos = range.startAddress; while (nextPos < range.endAddress) { nextPos = state.processLineSlow(nextPos); } } private AggregateResult processShardReal() { // First process the file tail work to give ourselves freedom to go past ranges in parsing shardQueue.fileTailEndWork(shardIdx).ifPresent(this::processRangeSlow); ByteRange range; while ((range = shardQueue.take(shardIdx)) != null) { processRange(range); } return result(); } private AggregateResult result() { return state.result(); } } public static class FastShardProcessorState { private static final long LEADING_ONE_BIT_MASK = 0x8080808080808080L; private static final long ONE_MASK = 0x0101010101010101L; private static final long SEMICOLON_MASK = 0x3b3b3b3b3b3b3b3bL; private final Hashtable hashtable; private final Map slowProcessStats = new HashMap<>(); public FastShardProcessorState(int slotsBits) { this.hashtable = new Hashtable(slotsBits); Checks.checkArg(slotsBits <= 16); // since this.hashes is 'short' } public long processLine(long nextPos) { final long origPos = nextPos; // Trying to extract this into a function made it slower.. so, leaving it at inlining. // It's a pity since the extracted version was more elegant to read long firstLong; int hash = 0; // Don't run Long.numberOfTrailingZeros in hasSemiColon; it is not needed to establish // whether there's a semicolon; only needed for pin-pointing length of the tail. long s = hasSemicolon(firstLong = Unsafely.readLong(nextPos)); final int trailingZeroes; if (s == 0) { hash = doHash(firstLong); do { nextPos += 8; s = hasSemicolon(Unsafely.readLong(nextPos)); } while (s == 0); trailingZeroes = Long.numberOfTrailingZeros(s) + 1; // 8, 16, 24, .. # past ; } else { trailingZeroes = Long.numberOfTrailingZeros(s) + 1; // 8, 16, 24, .. # past ; hash = doHash(firstLong & maskOf(trailingZeroes - 8)); } // Sometimes we do mix a tail of length 0.. nextPos += (trailingZeroes >> 3); final int temp = readTemperature(nextPos); hashtable.addDataPoint(origPos, (int) (nextPos - 1 - origPos), hash, (short) (temp >> 3)); return nextPos + (temp & 7); } /** * A slow version which is used only for the tail part of the file. Maintaining hashcode sync * between this and the fast version is a pain for experimentation. So, we'll simply use a naive * approach. */ public long processLineSlow(long nextPos) { byte nextByte; ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((nextByte = Unsafely.readByte(nextPos++)) != ';') { baos.write(nextByte); } int temperature = 0; boolean negative = Unsafely.readByte(nextPos) == '-'; while ((nextByte = Unsafely.readByte(nextPos++)) != '\n') { if (nextByte != '-' && nextByte != '.') { temperature = temperature * 10 + (nextByte - '0'); } } if (negative) { temperature = -temperature; } updateStat(slowProcessStats, baos.toString(), Stat.firstReading(temperature)); return nextPos; } public AggregateResult result() { AggregateResult result = hashtable.result(); if (!slowProcessStats.isEmpty()) { // bah.. just mutate the arg of the record... for (Entry entry : slowProcessStats.entrySet()) { updateStat(result.tempStats(), entry.getKey(), entry.getValue()); } } return result; } int readTemperature(long nextPos) { // This Dependency chain // read -> shift -> xor -> compare -> 2 in parallel [ shift -> read ] -> add -> shift // Chain latency: 2 reads + 2 add + 4 logical [assuming compare = add] // vs // Prior Dependency chain (slightly optimized by hand) // read -> compare to '-' -> read -> compare to '.' -> 3 in parallel [read -> imul] -> add // Chain latency: 3 reads + 3 add + 1 mul [assuming compare = add] long data = Unsafely.readLong(nextPos); long d = data ^ (data >> 4); if ((data & 0xFF) == '-') { return TemperatureLookup.firstNeg(d >> 8) + TemperatureLookup.secondNeg(d >> 24); } else { return TemperatureLookup.firstPos(d >> 0) + TemperatureLookup.secondPos(d >> 16); } } private int doHash(long value) { long hash = 31L * (int) value + (int) (value >> 32); return (int) (hash ^ (hash >> 17) ^ (hash >> 28)); } private long hasSemicolon(long x) { long a = (x ^ SEMICOLON_MASK); return (a - ONE_MASK) & (~a) & LEADING_ONE_BIT_MASK; } private long maskOf(int bits) { return ~(-1L << bits); } private void updateStat(Map map, String key, Stat curValue) { map.compute(key, (_, value) -> value == null ? curValue : Stat.merge(value, curValue)); } } /** Represents aggregate stats. */ public static class Stat { public static Stat firstReading(int temp) { return new Stat(temp, temp, temp, 1); } public static Stat merge(Stat left, Stat right) { return new Stat( Math.min(left.min, right.min), Math.max(left.max, right.max), left.sum + right.sum, left.count + right.count); } long count, sum; int min, max; public Stat(int min, int max, long sum, long count) { this.min = min; this.max = max; this.sum = sum; this.count = count; } // Caution: Mutates public Stat mergeReading(int curTemp) { // Can this be improved furhter? // Assuming random values for curTemp, // min (&max) gets updated roughly log(N)/N fraction of the time (a small number) // In the worst case, there will be at-most one branch misprediction. if (curTemp > min) { // Mostly passes. On branch misprediction, just update min. if (curTemp > max) { // Mostly fails. On branch misprediction, just update max. max = curTemp; } } else { min = curTemp; } sum += curTemp; count++; return this; } @Override public String toString() { return "%.1f/%.1f/%.1f".formatted(min / 10.0, sum / 10.0 / count, max / 10.0); } } /** * Lookup table for temperature parsing. * *

     * 0       0011-0000
     * 9       0011-1001
     * .       0010-1110
     * \n      0000-1010
     *
     * Notice that there's no overlap in the last 4 bits. This means, if we are given two successive
     * bytes X, Y all of which belong to the above characters, we can REVERSIBLY hash it to
     * a single byte by doing 8-bit-hash = (last 4 bits of X) concat (last 4 bits of Y).
     *
     * Such a hash requires a few more operations than ideal. A more practical hash is:
     * (X>>4) ^ Y ^ (Y >> 4). This means if you read 4 bytes after the '-',
     * L = X Y Z W, where each of X Y Z W is a byte, then,
     * L ^ (L >> 4) = D hash(X, Y) hash(Y, Z) hash(Z, W) where D = don't care. In other words, we
     * can SWAR the hash.
     *
     * This has potential for minor conflicts; for e.g. (3, NewLine) collides with (0, 9). But, we
     * don't have any collisions between two digits. That is (x, y) will never collide with (a, b)
     * where x, y, a, b are digits (proof left as an exercise, lol). Along with a couple of other
     * such no-conflict observations, it suffices for our purposes.
     *
     * If we just precompute some values like
     * - BigTable[hash(X,Y)] = 100*X + 10*Y
     * - SmallTable[hash(Z,W)] = 10*Z + W
     *
     * where potentially X, Y, Z, W can be '.' or '\n', (and the arithmetic adjusted), we can lookup
     * the temperature pieces from BigTable and SmallTable and add them together.
     * 
* *

This class is an implementation of the above idea. The lookup tables being 256 ints long * will always be resident in L1 cache. What remains then is to also add the information on how * much input is to be consumed; i.e. count the - and newlines too. That can be piggy backed on * top of the values. * *

FWIW, this lookup appears to have reduced the temperature reading overhead substantially on * a Ryzen 7950X machine. But, it wasn't done systematically; so, YMMV. */ public static class TemperatureLookup { // Second is the smaller (units place) // First is the larger (100 & 10) // _NEG tables simply negate the value so that call-site can always simply add the values from // the first and second units. Call-sites adding-up First and Second units adds up the // amount of input to consume. // Here, 2 is the amount of bytes consumed. This informs how much the reading pointer // should move. // For pattern XY value = ((-100*X -10*Y) << 3) + 2 [2 = 1 for X, 1 for Y] // For pattern Y. value = ((-10*Y) << 3) + 2 [2 = 1 for Y, 1 for .] private static final int[] FIRST_NEG = make(true, true); // For pattern XY value = ((100*X + 10*Y) << 3) + 2 // For pattern Y. value = ((10*Y) << 3) + 2 private static final int[] FIRST_POS = make(true, false); // We count newline and any initial '-' as part of SECOND // For pattern .Z value = (-Z << 3) + 2 + 2 [1 each for . and Z, 1 for newline, 1 for minus] // For pattern Zn value = (-Z << 3) + 1 + 2 [1 for Z, 1 for newline, 1 for minus] private static final int[] SECOND_NEG = make(false, true); // For pattern .Z value = (Z << 3) + 2 + 1 [1 each for . and Z, 1 for newline] // For pattern Zn value = (Z << 3) + 1 + 1 [1 for Z, 1 for newline] private static final int[] SECOND_POS = make(false, false); public static int firstNeg(long b) { return FIRST_NEG[(int) (b & 255)]; } public static int firstPos(long b) { return FIRST_POS[(int) (b & 255)]; } public static int secondNeg(long b) { return SECOND_NEG[(int) (b & 255)]; } public static int secondPos(long b) { return SECOND_POS[(int) (b & 255)]; } private static byte[] allDigits() { byte[] out = new byte[10]; for (byte a = '0'; a <= '9'; a++) { out[a - '0'] = a; } return out; } private static int hash(byte msb, byte lsb) { // If K = [D msb lsb], then (K ^ (K>>4)) & 255 == hash(msb, lsb). D = don't care return (msb << 4) ^ lsb ^ (lsb >> 4); } private static int[] make(boolean isFirst, boolean isNegative) { int[] ret = new int[256]; boolean[] done = new boolean[256]; // Conventions: X = 100s place, Y = 10s place, Z = 1s place, n = new line // All the cases to handle // X Y . Z // Y . Z n // In little-endian order it becomes (byte-wise), shown in place value notation // Z . Y X // n Z . Y // First = YX or .Y // Second = Z. or nZ // Pattern 'YX' for (byte x : allDigits()) { for (byte y : allDigits()) { int index = hash(y, x); // Shouldn't occur in Second int value = isFirst ? (y - '0') * 10 + (x - '0') * 100 : 12345; int delta = isFirst ? 2 : 12345; update(index, isNegative ? -value : value, delta, ret, done); } } // Pattern 'Z.' for (byte z : allDigits()) { int index = hash(z, (byte) '.'); // shouldn't occur in First int value = isFirst ? 12345 : (z - '0'); int delta = isFirst ? 12345 : 2; update(index, isNegative ? -value : value, delta, ret, done); } // Pattern '.Y' for (byte y : allDigits()) { int index = hash((byte) '.', y); // Shouldn't occur in Second int value = isFirst ? 10 * (y - '0') : 12345; int delta = isFirst ? 2 : 12345; update(index, isNegative ? -value : value, delta, ret, done); } // Pattern 'nZ' for (byte z : allDigits()) { int index = hash((byte) '\n', z); // shouldn't occur in First int value = isFirst ? 12345 : (z - '0'); int delta = isFirst ? 12345 : 1; update(index, isNegative ? -value : value, delta, ret, done); } if (!isFirst) { // Adjust the deltas to reflect how much input needs to be consumed // need to consume the newline and any - sign in front for (int i = 0; i < ret.length; i++) { ret[i] += (isNegative ? 1 : 0) /* for - sign */ + 1 /* for new line */; } } return ret; } private static void update(int index, int value, int delta, int[] ret, boolean[] done) { index &= 255; Checks.checkArg(!done[index]); // just a sanity check that our hashing is indeed reversible ret[index] = (value << 3) | delta; done[index] = true; } } static class Tracing { private static final Map knownWorkThreadEvents; private static long startTime; static { // Maintain the ordering to be chronological in execution // Map.of(..) screws up ordering knownWorkThreadEvents = new LinkedHashMap<>(); for (String id : List.of("Shard", "Intermediate Cleaner", "Ending Cleaner", "Buffer Creation")) { knownWorkThreadEvents.put(id, new ThreadTimingsArray(id, 1 << 10)); } } static void analyzeWorkThreads(int nThreads) { for (ThreadTimingsArray array : knownWorkThreadEvents.values()) { errPrint(array.analyze(nThreads)); } } static void recordAppStart() { startTime = System.nanoTime(); printEvent("Start time", startTime); } static void recordEvent(String event) { printEvent(event, System.nanoTime()); } static void recordWorkEnd(String id, int threadId) { knownWorkThreadEvents.get(id).recordEnd(threadId); } static void recordWorkStart(String id, int threadId) { knownWorkThreadEvents.get(id).recordStart(threadId); } ///////////////////////////////////////////////////////////////////////////////////////////////// private static void errPrint(String message) { System.err.println(message); } private static void printEvent(String message, long nanoTime) { errPrint(STR."\{message} = \{(nanoTime - startTime) / 1_000_000}ms"); } public static class ThreadTimingsArray { private static String toString(long[] array) { return Arrays.stream(array) .map(x -> x < 0 ? -1 : x) .mapToObj(x -> String.format("%6d", x)) .collect(Collectors.joining(", ", "[ ", " ]")); } private final String id; private final long[] timestamps; private boolean hasData = false; public ThreadTimingsArray(String id, int maxSize) { this.timestamps = new long[maxSize]; this.id = id; } public String analyze(int nThreads) { if (!hasData) { return "%s has no thread timings data".formatted(id); } Checks.checkArg(nThreads <= timestamps.length); long minDuration = Long.MAX_VALUE, maxDuration = Long.MIN_VALUE; long minBegin = Long.MAX_VALUE, maxCompletion = Long.MIN_VALUE; long maxBegin = Long.MIN_VALUE, minCompletion = Long.MAX_VALUE; long[] durationsMs = new long[nThreads]; long[] completionsMs = new long[nThreads]; long[] beginMs = new long[nThreads]; for (int i = 0; i < nThreads; i++) { long durationNs = timestamps[2 * i + 1] - timestamps[2 * i]; durationsMs[i] = durationNs / 1_000_000; completionsMs[i] = (timestamps[2 * i + 1] - startTime) / 1_000_000; beginMs[i] = (timestamps[2 * i] - startTime) / 1_000_000; minDuration = Math.min(minDuration, durationNs); maxDuration = Math.max(maxDuration, durationNs); minBegin = Math.min(minBegin, timestamps[2 * i] - startTime); maxBegin = Math.max(maxBegin, timestamps[2 * i] - startTime); maxCompletion = Math.max(maxCompletion, timestamps[2 * i + 1] - startTime); minCompletion = Math.min(minCompletion, timestamps[2 * i + 1] - startTime); } return STR.""" ------------------------------------------------------------------------------------------- \{id} Stats ------------------------------------------------------------------------------------------- Max duration = \{maxDuration / 1_000_000} ms Min duration = \{minDuration / 1_000_000} ms Timespan[max(end)-min(start)] = \{(maxCompletion - minBegin) / 1_000_000} ms [\{maxCompletion / 1_000_000} - \{minBegin / 1_000_000} ] Completion Timespan[max(end)-min(end)] = \{(maxCompletion - minCompletion) / 1_000_000} ms Begin Timespan[max(begin)-min(begin)] = \{(maxBegin - minBegin) / 1_000_000} ms Average Duration = \{Arrays.stream(durationsMs) .average() .getAsDouble()} ms Durations = \{toString(durationsMs)} ms Begin Timestamps = \{toString(beginMs)} ms Completion Timestamps = \{toString(completionsMs)} ms """; } public void recordEnd(int idx) { timestamps[2 * idx + 1] = System.nanoTime(); hasData = true; } public void recordStart(int idx) { timestamps[2 * idx] = System.nanoTime(); hasData = true; } } } static class Unsafely { private static final Unsafe unsafe = getUnsafe(); public static long allocateZeroedCacheLineAligned(int size) { long address = unsafe.allocateMemory(size + 63); unsafe.setMemory(address, size + 63, (byte) 0); return (address + 63) & ~63; } public static void copyMemory(long srcAddress, long destAddress, long byteCount) { unsafe.copyMemory(srcAddress, destAddress, byteCount); } public static boolean matches(long srcAddr, long destAddress, int len) { if (len < 8) { return (readLong(srcAddr) & ~(-1L << (len << 3))) == (readLong(destAddress) & ~(-1L << (len << 3))); } if (readLong(srcAddr) != readLong(destAddress)) { return false; } len -= 8; if (len < 8) { return (readLong(srcAddr + 8) & ~(-1L << (len << 3))) == (readLong(destAddress + 8) & ~(-1L << (len << 3))); } if (readLong(srcAddr + 8) != readLong(destAddress + 8)) { return false; } len -= 8; srcAddr += 16; destAddress += 16; int idx = 0; for (; idx < (len & ~7); idx += 8) { if (Unsafely.readLong(srcAddr + idx) != Unsafely.readLong(destAddress + idx)) { return false; } } if (idx < (len & ~3)) { if (Unsafely.readInt(srcAddr + idx) != Unsafely.readInt(destAddress + idx)) { return false; } idx += 4; } if (idx < (len & ~1)) { if (Unsafely.readShort(srcAddr + idx) != Unsafely.readShort(destAddress + idx)) { return false; } idx += 2; } return idx >= len || Unsafely.readByte(srcAddr + idx) == Unsafely.readByte(destAddress + idx); } public static byte readByte(long address) { return unsafe.getByte(address); } public static int readInt(long address) { return unsafe.getInt(address); } public static long readLong(long address) { return unsafe.getLong(address); } public static short readShort(long address) { return unsafe.getShort(address); } public static void setByte(long address, byte len) { unsafe.putByte(address, len); } public static void setInt(long address, int value) { unsafe.putInt(address, value); } public static void setShort(long address, short len) { unsafe.putShort(address, len); } private static Unsafe getUnsafe() { try { Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); return (Unsafe) unsafeField.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_xpmatteo.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.TreeMap; import java.util.stream.Collectors; @SuppressWarnings({ "ReassignedVariable", "StatementWithEmptyBody" }) public class CalculateAverage_xpmatteo { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException, InterruptedException { var fileName = dataFileName(args); try ( var file = new RandomAccessFile(new File(fileName), "r"); var channel = file.getChannel()) { var numCpus = Runtime.getRuntime().availableProcessors(); var threads = split(channel, numCpus).stream() .map(Worker::new) .toList(); threads.forEach(Thread::start); for (Worker thread : threads) { thread.join(); } var results = threads.stream().map(Worker::getResults) .reduce(CalculateAverage_xpmatteo::merge) .orElseThrow(); printCities(results); } } public static class Worker extends Thread { private final ByteBuffer buffer; private Results results; public Worker(ByteBuffer buffer) { this.buffer = buffer; } @Override public void run() { this.results = parseData(this.buffer); } public Results getResults() { return results; } } protected static List split(FileChannel channel, int numCpus) throws IOException { if (channel.size() < 10_000) { return List.of(channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size())); } long[] increments = new long[numCpus + 1]; for (int i = 0; i < numCpus; i++) { increments[i] = i * channel.size() / numCpus; // adjust the increments so that they start on the beginning of a city while (increments[i] > 0 && byteAt(channel, increments[i] - 1) != '\n') { increments[i]--; } } increments[numCpus] = channel.size(); List result = new ArrayList<>(numCpus); for (int i = 0; i < numCpus; i++) { long from = increments[i]; long to = increments[i + 1]; result.add(channel.map(FileChannel.MapMode.READ_ONLY, from, to - from)); } return result; } private static byte byteAt(FileChannel channel, long offset) throws IOException { ByteBuffer buf = ByteBuffer.allocate(1); channel.position(offset); channel.read(buf); buf.flip(); var bytes = new byte[1]; buf.get(bytes); return bytes[0]; } public static String dataFileName(String[] args) { if (args.length == 1) { return args[0]; } return FILE; } protected static byte[] readAllData(String fileName) throws IOException { return Files.readAllBytes(Path.of(fileName)); } protected static ByteBuffer memoryMap(String fileName) throws IOException { try (RandomAccessFile file = new RandomAccessFile(new File(fileName), "r")) { // Get file channel in read-only mode FileChannel fileChannel = file.getChannel(); return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); } } protected enum State { PARSING_CITY_NAME, SKIPPING_SEMICOLON, PARSING_TEMPERATURE } protected static Results parseData(ByteBuffer data) { var results = new Results(); var state = State.PARSING_CITY_NAME; int cityStartOffset = 0, cityEndOffset = 0; int temp = 0, sign = 0; for (int i = 0; i < data.limit(); i++) { byte currentChar = data.get(); if (state == State.PARSING_CITY_NAME && currentChar == ';') { state = State.SKIPPING_SEMICOLON; cityEndOffset = i; } else if (state == State.PARSING_CITY_NAME) { // do nothing } else if (state == State.SKIPPING_SEMICOLON && currentChar == '-') { state = State.PARSING_TEMPERATURE; temp = 0; sign = -1; } else if (state == State.SKIPPING_SEMICOLON && currentChar >= '0' && currentChar <= '9') { state = State.PARSING_TEMPERATURE; temp = currentChar - '0'; sign = 1; } else if (state == State.PARSING_TEMPERATURE && currentChar >= '0' && currentChar <= '9') { temp = temp * 10 + currentChar - '0'; } else if (state == State.PARSING_TEMPERATURE && currentChar == '.') { // do nothing } else if (state == State.PARSING_TEMPERATURE && currentChar == '\n') { byte[] bytes = new byte[cityEndOffset - cityStartOffset]; data.get(cityStartOffset, bytes); var cityName = new String(bytes); accumulate(results, cityName, temp * sign); state = State.PARSING_CITY_NAME; cityStartOffset = i + 1; } } return results; } private static void accumulate(Results results, String cityName, int tempTimesTen) { var existing = results.get(cityName); if (existing == null) { results.put(cityName, new CityData(tempTimesTen, tempTimesTen, tempTimesTen, 1)); } else { existing.min = Math.min(existing.min, tempTimesTen); existing.sum = existing.sum + tempTimesTen; existing.max = Math.max(existing.max, tempTimesTen); existing.count++; } } protected static Results merge(Results a, Results b) { for (var entry : b.entrySet()) { CityData valueInA = a.get(entry.getKey()); if (null == valueInA) { a.put(entry.getKey(), entry.getValue()); } else { var valueInB = entry.getValue(); valueInA.min = Math.min(valueInA.min, valueInB.min); valueInA.sum += valueInB.sum; valueInA.max = Math.max(valueInA.max, valueInB.max); valueInA.count += valueInB.count; } } return a; } protected static class Results extends TreeMap { } protected static class CityData { int min, sum, max, count; public CityData(int min, int sum, int max, int count) { this.min = min; this.sum = sum; this.max = max; this.count = count; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CityData cityData = (CityData) o; return min == cityData.min && sum == cityData.sum && max == cityData.max && count == cityData.count; } @Override public int hashCode() { return Objects.hash(min, sum, max, count); } @Override public String toString() { return STR."CityData{min=\{min}, sum=\{sum}, max=\{max}, count=\{count}\{'}'}"; } } protected static void printCities(Results cities) { System.out.print("{"); for (String city : cities.keySet()) { CityData data = cities.get(city); var min = data.min / 10.0; var mean = (data.sum * 10.0 / data.count) / 100.0; var max = data.max / 10.0; System.out.printf( "%s=%.1f/%.1f/%.1f, ", city, min, mean, max); } System.out.print("}"); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_yavuztas.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.function.Consumer; public class CalculateAverage_yavuztas { private static final Path FILE = Path.of("./measurements.txt"); private static final Unsafe UNSAFE = unsafe(); // I compared all three: MappedByteBuffer, MemorySegment and Unsafe. // Accessing the memory using Unsafe is still the fastest in my experience. // However, I would never use it in production, single programming error will crash your app. private static Unsafe unsafe() { try { final Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); } catch (Exception e) { throw new RuntimeException(e); } } /** * Extract bytes from a long */ private static long partial(long word, int length) { final long mask = (~0L) << (length << 3); return word & (~mask); } // Only one object, both for measurements and keys, less object creation in hotpots is always faster private static final class Record { private final long start; // memory address of the underlying data private final int length; private final long word1; private final long word2; private final long wordLast; private final int hash; private Record next; // linked list to resolve hash collisions private int min; // calculations over int is faster than double, we convert to double in the end only once private int max; private long sum; private int count; public Record(long start, int length, long word1, long word2, long wordLast, int hash, int temp) { this.start = start; this.length = length; this.word1 = word1; this.word2 = word2; this.wordLast = wordLast; this.hash = hash; this.min = temp; this.max = temp; this.sum = temp; this.count = 1; } @Override public boolean equals(Object o) { final Record record = (Record) o; return equals(record.start, record.word1, record.word2, record.wordLast, record.length); } private static boolean notEquals(long address1, long address2, int step) { return UNSAFE.getLong(address1 + step) != UNSAFE.getLong(address2 + step); } private static boolean equalsComparingLongs(long start1, long start2, int length) { // first shortcuts if (length < 24) return true; if (length < 32) return !notEquals(start1, start2, 16); int step = 24; // starting from 3rd long length -= step; while (length >= 8) { // scan longs if (notEquals(start1, start2, step)) { return false; } length -= 8; step += 8; // 8 bytes } return true; } private boolean equals(long start, long word1, long word2, long last, int length) { if (this.word1 != word1) return false; if (this.word2 != word2) return false; // equals check is done by comparing longs instead of byte by byte check, this is faster return equalsComparingLongs(this.start, start, length) && this.wordLast == last; } @Override public String toString() { final byte[] bytes = new byte[this.length]; UNSAFE.copyMemory(null, this.start, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, this.length); return new String(bytes, StandardCharsets.UTF_8); } private void collect(int temp) { if (temp < this.min) this.min = temp; if (temp > this.max) this.max = temp; this.sum += temp; this.count++; } private void merge(Record that) { if (that.min < this.min) this.min = that.min; if (that.max > this.max) this.max = that.max; this.sum += that.sum; this.count += that.count; } private String measurements() { // here is only executed once for each unique key, so StringBuilder creation doesn't harm final StringBuilder sb = new StringBuilder(14); sb.append(round(this.min)).append("/"); sb.append(round(1.0 * this.sum / this.count)).append("/"); sb.append(round(this.max)); return sb.toString(); } } // Inspired by @spullara - customized hashmap on purpose // The main difference is we hold only one array instead of two, fewer objects is faster private static final class RecordMap { // Bigger bucket size less collisions, but you have to find a sweet spot otherwise it is becoming slower. // Also works good enough for 10K stations private static final int SIZE = 1 << 14; // 16kb - enough for 10K private static final int BITMASK = SIZE - 1; private final Record[] keys = new Record[SIZE]; // int collision; private boolean hasNoRecord(int index) { return this.keys[index] == null; } private Record getRecord(int index) { return this.keys[index]; } private static int hashBucket(int hash) { hash = hash ^ (hash >>> 16); // naive bit spreading but surprisingly decreases collision :) return hash & BITMASK; // fast modulo, to find bucket } private void putAndCollect(int hash, int temp, long start, int length, long word1, long word2, long wordLast) { final int bucket = hashBucket(hash); if (hasNoRecord(bucket)) { this.keys[bucket] = new Record(start, length, word1, word2, wordLast, hash, temp); return; } Record existing = getRecord(bucket); if (existing.equals(start, word1, word2, wordLast, length)) { existing.collect(temp); return; } // collision++; // find possible slot by scanning the slot linked list while (existing.next != null) { if (existing.next.equals(start, word1, word2, wordLast, length)) { existing.next.collect(temp); return; } existing = existing.next; // go on to next // collision++; } existing.next = new Record(start, length, word1, word2, wordLast, hash, temp); } private void putOrMerge(Record key) { final int bucket = hashBucket(key.hash); if (hasNoRecord(bucket)) { key.next = null; this.keys[bucket] = key; return; } Record existing = getRecord(bucket); if (existing.equals(key)) { existing.merge(key); return; } // collision++; // find possible slot by scanning the slot linked list while (existing.next != null) { if (existing.next.equals(key)) { existing.next.merge(key); return; } existing = existing.next; // go on to next // collision++; } key.next = null; existing.next = key; } private void forEach(Consumer consumer) { int pos = 0; Record key; while (pos < SIZE) { if ((key = this.keys[pos++]) == null) { continue; } Record next = key.next; consumer.accept(key); while (next != null) { // also traverse the records in the collision list final Record tmp = next.next; consumer.accept(next); next = tmp; } } } private void merge(RecordMap other) { other.forEach(this::putOrMerge); } } // One actor for one thread, no synchronization private static final class RegionActor extends Thread { private final long startPos; // start of region memory address private final int size; private final RecordMap map = new RecordMap(); public RegionActor(long startPos, int size) { this.startPos = startPos; this.size = size; } private static long getWord(long address) { return UNSAFE.getLong(address); } // hasvalue & haszero // adapted from https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord private static long hasSemicolon(long word) { // semicolon pattern final long hasVal = word ^ 0x3B3B3B3B3B3B3B3BL; // hasvalue return ((hasVal - 0x0101010101010101L) & ~hasVal & 0x8080808080808080L); // haszero } private static int semicolonPos(long hasVal) { return Long.numberOfTrailingZeros(hasVal) >>> 3; } private static int decimalPos(long numberWord) { return Long.numberOfTrailingZeros(~numberWord & 0x10101000); } private static final int MAX_INNER_LOOP_SIZE = 11; @Override public void run() { long pointer = this.startPos; final long size = pointer + this.size; while (pointer < size) { // line start long hash = 0; // reset hash long s; // semicolon check word final int pos; // semicolon position long word1 = getWord(pointer); if ((s = hasSemicolon(word1)) != 0) { pos = semicolonPos(s); // read temparature final long numberWord = getWord(pointer + pos + 1); final int decimalPos = decimalPos(numberWord); final int temp = convertIntoNumber(decimalPos, numberWord); word1 = partial(word1, pos); // last word this.map.putAndCollect(completeHash(hash, word1), temp, pointer, pos, word1, 0, 0); pointer += pos + (decimalPos >>> 3) + 4; } else { long word2 = getWord(pointer + 8); if ((s = hasSemicolon(word2)) != 0) { pos = semicolonPos(s); // read temparature final int length = pos + 8; final long numberWord = getWord(pointer + length + 1); final int decimalPos = decimalPos(numberWord); final int temp = convertIntoNumber(decimalPos, numberWord); word2 = partial(word2, pos); // last word this.map.putAndCollect(completeHash(hash, word1, word2), temp, pointer, length, word1, word2, 0); pointer += length + (decimalPos >>> 3) + 4; // seek to the line end } else { long word = 0; int length = 16; hash = appendHash(hash, word1, word2); // Let the compiler know the loop size ahead // Then it's automatically unrolled // Max key length is 13 longs, 2 we've read before, 11 left for (int i = 0; i < MAX_INNER_LOOP_SIZE; i++) { if ((s = hasSemicolon((word = getWord(pointer + length)))) != 0) { break; } hash = appendHash(hash, word); length += 8; } pos = semicolonPos(s); length += pos; // read temparature final long numberWord = getWord(pointer + length + 1); final int decimalPos = decimalPos(numberWord); final int temp = convertIntoNumber(decimalPos, numberWord); word = partial(word, pos); // last word this.map.putAndCollect(completeHash(hash, word), temp, pointer, length, word1, word2, word); pointer += length + (decimalPos >>> 3) + 4; // seek to the line end } } } } // Hashes are calculated by a Mersenne Prime (1 << 7) -1 // This is faster than multiplication in some machines private static long appendHash(long hash, long word) { return (hash << 7) - hash + word; } private static long appendHash(long hash, long word1, long word2) { hash = (hash << 7) - hash + word1; return (hash << 7) - hash + word2; } private static int completeHash(long hash, long partial) { hash = (hash << 7) - hash + partial; return (int) (hash ^ (hash >>> 25)); } private static int completeHash(long hash, long word1, long word2) { hash = (hash << 7) - hash + word1; hash = (hash << 7) - hash + word2; return (int) hash ^ (int) (hash >>> 25); } // Credits to @merrykitty. Magical solution to parse temparature values branchless! // Taken as without modification, comments belong to @merrykitty private static int convertIntoNumber(int decimalSepPos, long numberWord) { final int shift = 28 - decimalSepPos; // signed is -1 if negative, 0 otherwise final long signed = (~numberWord << 59) >> 63; final long designMask = ~(signed & 0xFF); // Align the number to a specific position and transform the ascii code // to actual digit value in each byte final long digits = ((numberWord & designMask) << shift) & 0x0F000F0F00L; // Now digits is in the form 0xUU00TTHH00 (UU: units digit, TT: tens digit, HH: hundreds digit) // 0xUU00TTHH00 * (100 * 0x1000000 + 10 * 0x10000 + 1) = // 0x000000UU00TTHH00 + // 0x00UU00TTHH000000 * 10 + // 0xUU00TTHH00000000 * 100 // Now TT * 100 has 2 trailing zeroes and HH * 100 + TT * 10 + UU < 0x400 // This results in our value lies in the bit 32 to 41 of this product // That was close :) final long absValue = ((digits * 0x640a0001) >>> 32) & 0x3FF; final long value = (absValue ^ signed) - signed; return (int) value; } /** * blocks until the map is fully collected */ private RecordMap get() throws InterruptedException { join(); return this.map; } } private static double round(double value) { return Math.round(value) / 10.0; } /** * Scans the given buffer to the left */ private static long findClosestLineEnd(long start, int size) { long position = start + size; while (UNSAFE.getByte(--position) != '\n') { // read until a linebreak size--; } return size; } private static boolean isWorkerProcess(String[] args) { return Arrays.asList(args).contains("--worker"); } private static void runAsWorker() throws Exception { final ProcessHandle.Info info = ProcessHandle.current().info(); final List commands = new ArrayList<>(); info.command().ifPresent(commands::add); info.arguments().ifPresent(args -> commands.addAll(Arrays.asList(args))); commands.add("--worker"); new ProcessBuilder() .command(commands) .start() .getInputStream() .transferTo(System.out); } public static void main(String[] args) throws Exception { // Dased on @thomaswue's idea, to cut unmapping delay. // Strangely, unmapping delay doesn't occur on macOS/M1 however in Linux/AMD it's substantial - ~200ms if (!isWorkerProcess(args)) { runAsWorker(); return; } var concurrency = 2 * Runtime.getRuntime().availableProcessors(); final long fileSize = Files.size(FILE); long regionSize = fileSize / concurrency; // handling extreme cases while (regionSize > Integer.MAX_VALUE) { concurrency *= 2; regionSize /= 2; } if (fileSize <= 1 << 20) { // small file (1mb), no need concurrency concurrency = 1; regionSize = fileSize; } long startPos = 0; final FileChannel channel = (FileChannel) Files.newByteChannel(FILE, StandardOpenOption.READ); // get the memory address, this is the only thing we need for Unsafe final long memoryAddress = channel.map(FileChannel.MapMode.READ_ONLY, startPos, fileSize, Arena.global()).address(); final RegionActor[] actors = new RegionActor[concurrency]; for (int i = 0; i < concurrency; i++) { // calculate boundaries long maxSize = (startPos + regionSize > fileSize) ? fileSize - startPos : regionSize; // shift position to back until we find a linebreak maxSize = findClosestLineEnd(memoryAddress + startPos, (int) maxSize); final RegionActor region = (actors[i] = new RegionActor(memoryAddress + startPos, (int) maxSize)); region.start(); // start processing startPos += maxSize; } final RecordMap output = new RecordMap(); // output to merge all records for (RegionActor actor : actors) { final RecordMap partial = actor.get(); // blocks until get the result output.merge(partial); // System.out.println("collisions: " + partial.collision); } // sort and print the result final TreeMap sorted = new TreeMap<>(); output.forEach(key -> { sorted.put(key.toString(), key.measurements()); }); System.out.println(sorted); System.out.close(); // closing the stream will trigger the main process to pick up the output early } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_yehwankim23.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.OutputStreamWriter; import java.util.HashMap; import java.util.TreeMap; public class CalculateAverage_yehwankim23 { public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new FileReader("./measurements.txt")); HashMap hm = new HashMap<>(); char[] buffer = new char[107]; int offset = 0; while (true) { br.read(buffer, offset, 107 - offset); int semicolon = 0; while (buffer[semicolon] != ';') { semicolon++; } String stationName = new String(buffer, 0, semicolon++); int newline = semicolon; while (buffer[newline] != '\n') { newline++; } if (hm.containsKey(stationName)) { hm.get(stationName).update(Double.parseDouble( new String(buffer, semicolon, newline - semicolon))); } else { hm.put(stationName, new Measurement( Double.parseDouble(new String(buffer, semicolon, newline - semicolon)))); } if (buffer[++newline] == 0) { break; } offset = 107 - newline; char[] temp = new char[107]; System.arraycopy(buffer, newline, temp, 0, offset); buffer = temp; } br.close(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); bw.write(new TreeMap<>(hm).toString()); bw.newLine(); bw.flush(); bw.close(); } private static class Measurement { public double min; public double sum; public int count; public double max; public Measurement(double measurement) { min = measurement; sum = measurement; count = 1; max = measurement; } public void update(double measurement) { if (measurement < min) { min = measurement; } sum += measurement; count++; if (max < measurement) { max = measurement; } } @Override public String toString() { return min + "/" + Math.round(sum / count * 10) / 10.0 + "/" + max; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_yemreinci.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.TreeMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; public class CalculateAverage_yemreinci { private static final String FILE = "./measurements.txt"; public static void main(String[] args) throws IOException, ExecutionException, InterruptedException { var filename = args.length == 0 ? FILE : args[0]; var file = new File(filename); long start = System.currentTimeMillis(); var totalLines = new AtomicInteger(); var results = getFileSegments(file).stream().map(segment -> { var resultMap = new ByteArrayToResultMap(); long segmentEnd = segment.end(); try (var fileChannel = (FileChannel) Files.newByteChannel(Path.of(filename), StandardOpenOption.READ)) { var bb = fileChannel.map(FileChannel.MapMode.READ_ONLY, segment.start(), segmentEnd - segment.start()); var buffer = new byte[64]; int lines = 0; int startLine; int limit = bb.limit(); while ((startLine = bb.position()) < limit) { int currentPosition = startLine; byte b; int offset = 0; int hash = 0; while ((b = bb.get(currentPosition++)) != ';') { buffer[offset++] = b; hash = (hash << 5) - hash + b; } boolean negative = false; if ((b = bb.get(currentPosition)) == '-') { negative = true; currentPosition++; } double temp; if ((b = bb.get(currentPosition + 1)) == '.') { // temperature is in either XX.X or X.X form temp = (bb.get(currentPosition) - '0') + (bb.get(currentPosition + 2) - '0') / 10.0; currentPosition += 4; } else { temp = (bb.get(currentPosition) - '0') * 10 + (b - '0') + (bb.get(currentPosition + 3) - '0') / 10.0; currentPosition += 5; } double finalTemp = (negative ? -temp : temp); resultMap.putOrMerge(hash, buffer, 0, offset, () -> new Result(temp), measurement -> merge(measurement, finalTemp, finalTemp, finalTemp, 1)); lines++; bb.position(currentPosition); } totalLines.addAndGet(lines); return resultMap; } catch (IOException e) { throw new RuntimeException(e); } }).parallel().toList(); var resultMap = results.stream() .flatMap(partition -> partition.getAll().stream()) .collect(Collectors.toMap(e -> new String(e.key()), Entry::value, CalculateAverage_yemreinci::merge, TreeMap::new)); System.out.println("Time: " + (System.currentTimeMillis() - start) + "ms"); System.out.println("Lines processed: " + totalLines); System.out.println(resultMap); } private static List getFileSegments(File file) throws IOException { int numberOfSegments = Runtime.getRuntime().availableProcessors(); long fileSize = file.length(); long segmentSize = fileSize / numberOfSegments; List segments = new ArrayList<>(); try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) { for (int i = 0; i < numberOfSegments; i++) { long segStart = i * segmentSize; long segEnd = (i == numberOfSegments - 1) ? fileSize : segStart + segmentSize; segStart = findSegment(i, 0, randomAccessFile, segStart, segEnd); segEnd = findSegment(i, numberOfSegments - 1, randomAccessFile, segEnd, fileSize); segments.add(new FileSegment(segStart, segEnd)); } } return segments; } private static Result merge(Result v, Result value) { return merge(v, value.min, value.max, value.sum, value.count); } private static Result merge(Result v, double value, double value1, double value2, long value3) { v.min = Math.min(v.min, value); v.max = Math.max(v.max, value1); v.sum += value2; v.count += value3; return v; } private static long findSegment(int i, int skipSegment, RandomAccessFile raf, long location, long fileSize) throws IOException { if (i != skipSegment) { raf.seek(location); while (location < fileSize) { location++; if (raf.read() == '\n') break; } } return location; } static class Result { double min, max, sum; long count; Result(double value) { min = max = sum = value; this.count = 1; } @Override public String toString() { return round(min) + "/" + round(sum / count) + "/" + round(max); } double round(double v) { return Math.round(v * 10.0) / 10.0; } } static record Pair(int slot, Result slotValue) { } static record Entry(byte[] key, Result value) { } static record FileSegment(long start, long end) { } static class ByteArrayToResultMap { public static final int MAPSIZE = 1024 * 128; Result[] slots = new Result[MAPSIZE]; byte[][] keys = new byte[MAPSIZE][]; private Pair getPair(int hash, byte[] key, int offset, int size) { int slot = hash & (slots.length - 1); var slotValue = slots[slot]; // Linear probe for open slot while (slotValue != null && (keys[slot].length != size || !Arrays.equals(keys[slot], 0, size, key, offset, size))) { slot = (slot + 1) & (slots.length - 1); slotValue = slots[slot]; } return new Pair(slot, slotValue); } public void putOrMerge(int hash, byte[] key, int offset, int size, Supplier supplier, Consumer merge) { Pair result = getPair(hash, key, offset, size); Result value = result.slotValue(); if (value == null) { int slot = result.slot(); slots[slot] = supplier.get(); byte[] bytes = new byte[size]; System.arraycopy(key, offset, bytes, 0, size); keys[slot] = bytes; } else { merge.accept(value); } } // Get all pairs public List getAll() { List result = new ArrayList<>(); for (int i = 0; i < slots.length; i++) { Result slotValue = slots[i]; if (slotValue != null) { result.add(new Entry(keys[i], slotValue)); } } return result; } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_yonatang.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Map; import java.util.TreeMap; public class CalculateAverage_yonatang { private static final String FILE = "./measurements.txt"; private static final int DICT_OFFSET_STATION = 2; private static final int DICT_OFFSET_SUM = 1; private static final int DICT_SIZE = 15000; private static final int DICT_STATION_RECORD_SIZE = 13; private static final int DICT_RECORD_SIZE = DICT_OFFSET_STATION + DICT_STATION_RECORD_SIZE; private static final int DICT_SIZE_BYTES = DICT_SIZE * DICT_RECORD_SIZE; private static final long[] DICT_ZERO_RECORD = new long[DICT_RECORD_SIZE]; private static final long DICT_BASELINE_MEASURES = ((long) Short.MAX_VALUE & 0xFFFF) | (((long) Short.MIN_VALUE & 0xFFFF) << 16); public static class HashTable { // Continuous array of [key, min, max, count, sum], which will be more CPU cache friendly. private final long[] data = new long[DICT_SIZE_BYTES]; public HashTable() { for (int i = 0; i < DICT_SIZE_BYTES; i += DICT_RECORD_SIZE) { data[i] = DICT_BASELINE_MEASURES; } } private int getIndex(long[] station) { long key = 0; short len = (short) (station[0] & 0xFF); int longs = ((len + 1) / 8) + 1; for (int i = 0; i < longs; i++) { key = key ^ station[i]; } int idx = Math.abs((int) (key % DICT_SIZE)) * DICT_RECORD_SIZE; while (true) { if (data[idx] == DICT_BASELINE_MEASURES) { break; } if (Arrays.equals(station, 0, longs, data, idx + DICT_OFFSET_STATION, idx + DICT_OFFSET_STATION + longs)) { break; } idx += DICT_RECORD_SIZE; if (idx >= DICT_SIZE_BYTES) { idx = 0; } } return idx; } private void addRawMeasurementAgg(long[] title, long measurements, long sum) { int idx = getIndex(title); short currentMin = (short) (data[idx] & 0xFFFF); short currentMax = (short) ((data[idx] >> 16) & 0xFFFF); int currentCount = (int) (data[idx] >> 32); short thisMin = (short) (measurements & 0xFFFF); short thisMax = (short) ((measurements >> 16) & 0xFFFF); int thisCount = (int) (measurements >> 32); thisMin = (short) Math.min(thisMin, currentMin); thisMax = (short) Math.max(thisMax, currentMax); thisCount += currentCount; data[idx] = ((long) thisMin & 0xFFFF) | (((long) thisMax & 0xFFFF) << 16) | (((long) thisCount) << 32); data[idx + DICT_OFFSET_SUM] += sum; System.arraycopy(title, 0, data, idx + DICT_OFFSET_STATION, DICT_STATION_RECORD_SIZE); } public TreeMap toMap() { TreeMap finalMap = new TreeMap<>(); byte[] bytes = new byte[128]; ByteBuffer bb = ByteBuffer.allocate(136); bb.order(ByteOrder.nativeOrder()); for (int i = 0; i < DICT_SIZE_BYTES; i += DICT_RECORD_SIZE) { if (data[i] == DICT_BASELINE_MEASURES) continue; short min = (short) (data[i] & 0xFFFF); short max = (short) ((data[i] >> 16) & 0xFFFF); int count = (int) (data[i] >> 32); long sum = data[i + DICT_OFFSET_SUM]; for (int j = 0; j < DICT_STATION_RECORD_SIZE; j++) { bb.putLong(data[i + DICT_OFFSET_STATION + j]); } bb.flip(); byte len = bb.get(); bb.get(1, bytes, 0, len); bb.clear(); String station = new String(bytes, 0, len, Charset.defaultCharset()); finalMap.put(station, new ResultRow(min / 10.0, (sum / 10.0) / count, max / 10.0)); } return finalMap; } public void addMeasurement(long[] title, short temp) { int idx = getIndex(title); short min = (short) (data[idx] & 0xFFFF); short max = (short) ((data[idx] >> 16) & 0xFFFF); int count = (int) (data[idx] >> 32); min = (short) Math.min(min, temp); max = (short) Math.max(max, temp); count += 1; data[idx] = ((long) min & 0xFFFF) | (((long) max & 0xFFFF) << 16) | (((long) count) << 32); data[idx + DICT_OFFSET_SUM] += temp; System.arraycopy(title, 0, data, idx + DICT_OFFSET_STATION, DICT_STATION_RECORD_SIZE); } public void mergeInto(HashTable other) { long[] title = new long[DICT_STATION_RECORD_SIZE]; for (int i = 0; i < DICT_SIZE_BYTES; i += DICT_RECORD_SIZE) { if (data[i] == DICT_BASELINE_MEASURES) continue; System.arraycopy(data, i + DICT_OFFSET_STATION, title, 0, DICT_STATION_RECORD_SIZE); other.addRawMeasurementAgg(title, data[i], data[i + DICT_OFFSET_SUM]); } } } private static class ResultRow { final double min; final double mean; final double max; ResultRow(double min, double mean, double max) { this.min = min; this.mean = mean; this.max = max; } public String toString() { return round(min) + "/" + round(mean) + "/" + round(max); } private double round(double value) { return Math.round(value * 10.0) / 10.0; } } public static boolean parseStation(MappedByteBuffer byteBuffer, ByteBuffer tempBb, long[] station) { System.arraycopy(DICT_ZERO_RECORD, 0, station, 0, DICT_STATION_RECORD_SIZE); byte len = 1; boolean valid = false; tempBb.clear(); tempBb.put((byte) 0); while (byteBuffer.hasRemaining()) { byte ch = byteBuffer.get(); if (ch == '\n') { continue; } if (ch == ';') { valid = true; break; } tempBb.put(ch); // long theNew = ((long) ch) << (len * 8); // stationId[0] = stationId[0] ^ theNew; // int arrIdx = len / 8; // station[arrIdx] = station[arrIdx] ^ theNew; len++; } tempBb.put(0, (byte) (len - 1)); if (!valid) { return false; } tempBb.position(0); tempBb.asLongBuffer().get(station); int pivotIdx = (len) / 8; long pivotBits = (len % 8) * 8; long pivotMask = (1L << pivotBits) - 1; station[pivotIdx] = station[pivotIdx] & pivotMask; return true; } public static short parseShort(MappedByteBuffer byteBuffer) { boolean valid = false; boolean negative = false; int num = 0; while (byteBuffer.hasRemaining()) { byte ch = byteBuffer.get(); if (ch == '\n') { valid = true; break; } if (ch == '-') { negative = true; } else if (ch == '.') { // noop } else { num = (num * 10 + (ch - '0')); } } if (!valid) { return Short.MIN_VALUE; } return (short) (negative ? -num : num); } private static final int MARGIN = 130; private static void processChunk(FileChannel fc, int j, long chunkSize, HashTable[] maps, boolean isLast) { try { HashTable agg = new HashTable(); maps[j] = agg; long[] station = new long[DICT_STATION_RECORD_SIZE]; ByteBuffer tempBb = ByteBuffer.allocate((DICT_STATION_RECORD_SIZE + 1) * Long.BYTES); tempBb.order(ByteOrder.nativeOrder()); long startIdx = Math.max(j * chunkSize - MARGIN, 0); int padding; if (isLast) { chunkSize = fc.size() - startIdx; padding = 0; } else { padding = j == 0 ? 0 : MARGIN; } if (chunkSize == 0) { return; } MappedByteBuffer byteBuffer = fc.map(FileChannel.MapMode.READ_ONLY, startIdx, chunkSize + padding); // search back for the actual start line, at \n if (startIdx > 0) { int i = MARGIN; while (i > 0) { byte ch = byteBuffer.get(i); if (ch == '\n') { break; } i--; } byteBuffer.position(i); } while (byteBuffer.hasRemaining()) { if (!parseStation(byteBuffer, tempBb, station)) { continue; } short value = parseShort(byteBuffer); if (value == Short.MIN_VALUE) { continue; } agg.addMeasurement(station, value); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { // long start = System.nanoTime(); File f = new File(FILE); try (RandomAccessFile raf = new RandomAccessFile(f, "r"); FileChannel fc = raf.getChannel()) { int chunks = f.length() < 1_048_576 ? 1 : (Runtime.getRuntime().availableProcessors()); long chunkSize = f.length() / chunks; Thread[] threads = new Thread[chunks]; HashTable totalAgg = new HashTable(); HashTable[] maps = new HashTable[chunks]; for (int i = 0; i < chunks; i++) { final int j = i; Thread thread = new Thread(() -> processChunk(fc, j, chunkSize, maps, j == chunks - 1)); threads[i] = thread; thread.start(); } for (int i = 0; i < chunks; i++) { threads[i].join(); maps[i].mergeInto(totalAgg); } Map finalMap = totalAgg.toMap(); // long end = System.nanoTime(); System.out.println(finalMap); // System.err.println("Total time: " + java.time.Duration.ofNanos(end - start).toMillis() + "ms"); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_yourwass.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.util.TreeMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.nio.charset.StandardCharsets; import java.nio.ByteOrder; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorOperators; import jdk.incubator.vector.VectorSpecies; import sun.misc.Unsafe; public class CalculateAverage_yourwass { static final class Record { private long cityAddr; private long cityLength; private int min; private int max; private int count; private long sum; Record(final long cityAddr, final long cityLength) { this.cityAddr = cityAddr; this.cityLength = cityLength; this.min = 1000; this.max = -1000; this.sum = 0; this.count = 0; } private Record merge(Record r) { if (r.min < this.min) this.min = r.min; if (r.max > this.max) this.max = r.max; this.sum += r.sum; this.count += r.count; return this; } } private final static Lock _mutex = new ReentrantLock(true); private final static TreeMap aggregateResults = new TreeMap<>(); private static short lookupDecimal[]; private static byte lookupFraction[]; private static byte lookupDotPositive[]; private static byte lookupDotNegative[]; private static MemorySegment VAS; private static final VectorSpecies SPECIES = ByteVector.SPECIES_PREFERRED; private static final int MAXINDEX = (1 << 16) + 10000; // short hash + max allowed cities for collisions at the end :p private static final String FILE = "measurements.txt"; private static long unsafeResults; private static int RECORDSIZE = 36; private static final Unsafe UNSAFE = getUnsafe(); private static Unsafe getUnsafe() { try { final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null); return unsafe; } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws IOException, Throwable { // prepare lookup tables // the parsing reads two shorts after possible '-' // first short, the Decimal part, can be N. or NN with N:[0..9] // second short, the Fraction part, can be N\n or .N lookupDecimal = new short[('9' << 8) + '9' + 1]; lookupFraction = new byte[('9' << 8) + '.' + 1]; lookupDotPositive = new byte[('9' << 8) + '.' + 1]; lookupDotNegative = new byte[('9' << 8) + '.' + 1]; for (short i = 0; i < 10; i++) { final int ones = i * 10; final int ix256 = i << 8; // case N. i.e. single digit decimals: skip to 11824 = ('.'<<8)+'0' lookupDecimal[11824 + i] = (short) ones; for (short j = 1; j < 10; j++) { // case NN i.e double digits decimals: skip to 12236 = ('0'<<8)+'0' lookupDecimal[12336 + ix256 + j] = (short) (j * 100 + ones); } // case N\n skip to 2608 = ('\n'<<8)+'0' lookupFraction[2608 + i] = (byte) i; lookupDotPositive[2608 + i] = 4; lookupDotNegative[2608 + i] = 5; // case .N skip to 12334 = ('0'<<8)+'.' lookupFraction[12334 + ix256] = (byte) i; lookupDotPositive[12334 + ix256] = 5; lookupDotNegative[12334 + ix256] = 6; } // open file final FileChannel fileChannel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); final long fileSize = fileChannel.size(); final long mmapAddr = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()).address(); // VAS: Virtual Address Space, as a MemorySegment upto and including the mmaped file. // If the mmaped MemorySegment is used for Vector creation as is, then there are two problems: // 1) fromMemorySegment takes an offset and not an address, so we have to do arithmetic // this is solved by creating a MemorySegment from Address=0 // 2) fromMemorySegment checks bounds for memory segment's size - Vector size // this is solved by adding SPECIES.length() to the size of the segment, but // XXX there lies the possibility for an out of bounds read at the end of file, which is not handled here. VAS = MemorySegment.ofAddress(0).reinterpret(mmapAddr + fileSize + SPECIES.length()); // allocate memory for results final int nThreads = Runtime.getRuntime().availableProcessors(); unsafeResults = UNSAFE.allocateMemory(RECORDSIZE * MAXINDEX * nThreads); UNSAFE.setMemory(unsafeResults, RECORDSIZE * MAXINDEX * nThreads, (byte) 0); // start and wait for threads to finish Thread[] threadList = new Thread[nThreads]; final long chunkSize = fileSize / nThreads; for (int i = 0; i < nThreads; i++) { final int threadIndex = i; final long startAddr = mmapAddr + i * chunkSize; final long endAddr = (i == nThreads - 1) ? mmapAddr + fileSize : mmapAddr + (i + 1) * chunkSize; threadList[i] = new Thread(() -> threadMain(threadIndex, startAddr, endAddr, nThreads)); threadList[i].start(); } for (int i = 0; i < nThreads; i++) threadList[i].join(); // prepare string and print StringBuilder sb = new StringBuilder(); sb.append("{"); for (var entry : aggregateResults.entrySet()) { Record record = entry.getValue(); float min = record.min; min /= 10.f; float max = record.max; max /= 10.f; double avg = Math.round((record.sum * 1.0) / record.count) / 10.; sb.append(entry.getKey()).append("=").append(min).append("/").append(avg).append("/").append(max).append(", "); } int stringLength = sb.length(); sb.setCharAt(stringLength - 2, '}'); sb.setCharAt(stringLength - 1, '\n'); System.out.print(sb.toString()); System.out.close(); } private static final boolean citiesDiffer(final long a, final long b, final long len) { int part = 0; for (; part < (len - 1) >> 3; part++) if (UNSAFE.getLong(a + (part << 3)) != UNSAFE.getLong(b + (part << 3))) return true; if (((UNSAFE.getLong(a + (part << 3)) ^ (UNSAFE.getLong(b + (part << 3)))) << ((8 - (len & 7)) << 3)) != 0) return true; return false; } private static void threadMain(int id, long startAddr, long endAddr, long nThreads) { // snap to newlines if (id != 0) while (UNSAFE.getByte(startAddr++) != '\n') ; if (id != nThreads - 1) while (UNSAFE.getByte(endAddr++) != '\n') ; final long threadResults = unsafeResults + id * MAXINDEX * RECORDSIZE; final Record[] results = new Record[MAXINDEX]; final long VECTORBYTESIZE = SPECIES.length(); final ByteOrder BYTEORDER = ByteOrder.nativeOrder(); final ByteVector delim = ByteVector.broadcast(SPECIES, ';'); long cityAddr = startAddr; long ptr = 0; while (cityAddr < endAddr) { // parse city ByteVector parsed = ByteVector.fromMemorySegment(SPECIES, VAS, cityAddr, BYTEORDER); long mask = parsed.compare(VectorOperators.EQ, delim).toLong(); while (mask == 0) { ptr += VECTORBYTESIZE; mask = ByteVector.fromMemorySegment(SPECIES, VAS, cityAddr + ptr, BYTEORDER).compare(VectorOperators.EQ, delim).toLong(); } final long cityLength = ptr + Long.numberOfTrailingZeros(mask); final long tempAddr = cityAddr + cityLength + 1; ptr = 0; // compute hash table index int index; if (cityLength > 1) index = (UNSAFE.getByte(cityAddr) // mix the first, ^ (UNSAFE.getByte(cityAddr + 2) << 4) // the third (even if it is the delimiter ';') ^ (UNSAFE.getByte(tempAddr - 2) << 8) // and the last two bytes of each city's name ^ (UNSAFE.getByte(tempAddr - 3) << 12)) & 0xFFFF; else index = (UNSAFE.getByte(cityAddr) << 8) & 0xFF00; // resolve collisions with linear probing // use vector api here also, but only if city name fits in one vector length, for faster default case long record = threadResults + index * RECORDSIZE; long recordCityLength = UNSAFE.getLong(record); if (cityLength <= VECTORBYTESIZE) { while (recordCityLength > 0) { if (cityLength == recordCityLength) { long sameMask = ByteVector.fromMemorySegment(SPECIES, VAS, UNSAFE.getLong(record + 8), BYTEORDER) .compare(VectorOperators.EQ, parsed).toLong(); if (Long.numberOfTrailingZeros(~sameMask) >= cityLength) break; } index++; record = threadResults + index * RECORDSIZE; recordCityLength = UNSAFE.getLong(record); } } else { // slower normal case for city names with length > VECTORBYTESIZE while (recordCityLength > 0 && (cityLength != recordCityLength || citiesDiffer(UNSAFE.getLong(record + 8), cityAddr, cityLength))) { index++; record = threadResults + index * RECORDSIZE; recordCityLength = UNSAFE.getLong(record); } } // add record for new key if (recordCityLength == 0) { UNSAFE.putLong(record, cityLength); UNSAFE.putLong(record + 8, cityAddr); UNSAFE.putInt(record + 16, 1000); UNSAFE.putInt(record + 20, -1000); } // parse temp with lookup tables int temp; if (UNSAFE.getByte(tempAddr) == '-') { temp = -lookupDecimal[UNSAFE.getShort(tempAddr + 1)] - lookupFraction[UNSAFE.getShort(tempAddr + 3)]; cityAddr = tempAddr + lookupDotNegative[UNSAFE.getShort(tempAddr + 3)]; } else { temp = lookupDecimal[UNSAFE.getShort(tempAddr)] + lookupFraction[UNSAFE.getShort(tempAddr + 2)]; cityAddr = tempAddr + lookupDotPositive[UNSAFE.getShort(tempAddr + 2)]; } // merge if (temp < UNSAFE.getInt(record + 16)) UNSAFE.putInt(record + 16, temp); if (temp > UNSAFE.getInt(record + 20)) UNSAFE.putInt(record + 20, temp); UNSAFE.putLong(record + 24, UNSAFE.getLong(record + 24) + temp); UNSAFE.putInt(record + 32, UNSAFE.getInt(record + 32) + 1); } // create strings from raw data // and aggregate results onto TreeMap int idx = 0; byte b[] = new byte[100]; _mutex.lock(); for (int i = 0; i < MAXINDEX; i++) { if (UNSAFE.getLong(threadResults + i * RECORDSIZE) == 0) continue; final long recordAddress = threadResults + i * RECORDSIZE; results[idx] = new Record(UNSAFE.getLong(recordAddress + 8), UNSAFE.getLong(recordAddress)); results[idx].min = UNSAFE.getInt(recordAddress + 16); results[idx].max = UNSAFE.getInt(recordAddress + 20); results[idx].sum = UNSAFE.getLong(recordAddress + 24); results[idx].count = UNSAFE.getInt(recordAddress + 32); UNSAFE.copyMemory(null, UNSAFE.getLong(recordAddress + 8), b, Unsafe.ARRAY_BYTE_BASE_OFFSET, UNSAFE.getLong(recordAddress)); final Record record = results[idx]; aggregateResults.compute(new String(b, 0, (int) results[idx].cityLength, StandardCharsets.UTF_8), (k, v) -> (v == null) ? record : v.merge(record)); idx++; } _mutex.unlock(); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CalculateAverage_zerninv.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import sun.misc.Unsafe; import java.io.BufferedOutputStream; import java.io.IOException; import java.lang.foreign.Arena; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.*; public class CalculateAverage_zerninv { private static final String FILE = "./measurements.txt"; private static final int CORES = Runtime.getRuntime().availableProcessors(); private static final int CHUNK_SIZE = 1024 * 1024 * 32; private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { try { Field unsafe = Unsafe.class.getDeclaredField("theUnsafe"); unsafe.setAccessible(true); return (Unsafe) unsafe.get(Unsafe.class); } catch (IllegalAccessException | NoSuchFieldException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws IOException, InterruptedException { try (var channel = FileChannel.open(Path.of(FILE), StandardOpenOption.READ)) { var fileSize = channel.size(); var minChunkSize = Math.min(fileSize, CHUNK_SIZE); var segment = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize, Arena.global()); var tasks = new TaskThread[CORES]; for (int i = 0; i < tasks.length; i++) { tasks[i] = new TaskThread((int) (fileSize / minChunkSize / CORES + 1)); } var chunks = splitByChunks(segment.address(), segment.address() + fileSize, minChunkSize); for (int i = 0; i < chunks.size() - 1; i++) { var task = tasks[i % tasks.length]; task.addChunk(chunks.get(i), chunks.get(i + 1)); } for (var task : tasks) { task.start(); } var results = new HashMap(); for (var task : tasks) { task.join(); task.collectTo(results); } var bos = new BufferedOutputStream(System.out); bos.write(new TreeMap<>(results).toString().getBytes(StandardCharsets.UTF_8)); bos.write('\n'); bos.flush(); } } private static List splitByChunks(long address, long end, long minChunkSize) { // split by chunks List result = new ArrayList<>((int) ((end - address) / minChunkSize + 1)); result.add(address); while (address < end) { address += Math.min(end - address, minChunkSize); while (address < end && UNSAFE.getByte(address++) != '\n') { } result.add(address); } return result; } private static final class TemperatureAggregation { private long sum; private int count; private short min; private short max; public TemperatureAggregation(long sum, int count, short min, short max) { this.sum = sum; this.count = count; this.min = min; this.max = max; } public void merge(long sum, int count, short min, short max) { this.sum += sum; this.count += count; this.min = this.min < min ? this.min : min; this.max = this.max > max ? this.max : max; } @Override public String toString() { return min / 10d + "/" + Math.round(sum / 1d / count) / 10d + "/" + max / 10d; } } private static final class MeasurementContainer { private static final int SIZE = 1 << 17; private static final int ENTRY_SIZE = 4 + 4 + 8 + 1 + 8 + 8 + 2 + 2; private static final int COUNT_OFFSET = 0; private static final int HASH_OFFSET = 4; private static final int LAST_BYTES_OFFSET = 8; private static final int SIZE_OFFSET = 16; private static final int ADDRESS_OFFSET = 17; private static final int SUM_OFFSET = 25; private static final int MIN_OFFSET = 33; private static final int MAX_OFFSET = 35; private final long address; private MeasurementContainer() { address = UNSAFE.allocateMemory(ENTRY_SIZE * SIZE); UNSAFE.setMemory(address, ENTRY_SIZE * SIZE, (byte) 0); } public void put(long address, byte size, int hash, long lastBytes, short value) { int idx = Math.abs(hash % SIZE); long ptr = this.address + idx * ENTRY_SIZE; int count; boolean fastEqual; while ((count = UNSAFE.getInt(ptr + COUNT_OFFSET)) != 0) { fastEqual = UNSAFE.getInt(ptr + HASH_OFFSET) == hash && UNSAFE.getLong(ptr + LAST_BYTES_OFFSET) == lastBytes; if (fastEqual && UNSAFE.getByte(ptr + SIZE_OFFSET) == size && isEqual(UNSAFE.getLong(ptr + ADDRESS_OFFSET), address, size - 8)) { UNSAFE.putInt(ptr + COUNT_OFFSET, count + 1); UNSAFE.putLong(ptr + ADDRESS_OFFSET, address); UNSAFE.putLong(ptr + SUM_OFFSET, UNSAFE.getLong(ptr + SUM_OFFSET) + value); if (value < UNSAFE.getShort(ptr + MIN_OFFSET)) { UNSAFE.putShort(ptr + MIN_OFFSET, value); } if (value > UNSAFE.getShort(ptr + MAX_OFFSET)) { UNSAFE.putShort(ptr + MAX_OFFSET, value); } return; } idx = (idx + 1) % SIZE; ptr = this.address + idx * ENTRY_SIZE; } UNSAFE.putInt(ptr + COUNT_OFFSET, 1); UNSAFE.putInt(ptr + HASH_OFFSET, hash); UNSAFE.putLong(ptr + LAST_BYTES_OFFSET, lastBytes); UNSAFE.putByte(ptr + SIZE_OFFSET, size); UNSAFE.putLong(ptr + ADDRESS_OFFSET, address); UNSAFE.putLong(ptr + SUM_OFFSET, value); UNSAFE.putShort(ptr + MIN_OFFSET, value); UNSAFE.putShort(ptr + MAX_OFFSET, value); } public void collectTo(Map results) { int count; for (int i = 0; i < SIZE; i++) { long ptr = this.address + i * ENTRY_SIZE; count = UNSAFE.getInt(ptr + COUNT_OFFSET); if (count != 0) { var station = createString(UNSAFE.getLong(ptr + ADDRESS_OFFSET), UNSAFE.getByte(ptr + SIZE_OFFSET)); var result = results.get(station); if (result == null) { results.put(station, new TemperatureAggregation( UNSAFE.getLong(ptr + SUM_OFFSET), count, UNSAFE.getShort(ptr + MIN_OFFSET), UNSAFE.getShort(ptr + MAX_OFFSET))); } else { result.merge(UNSAFE.getLong(ptr + SUM_OFFSET), count, UNSAFE.getShort(ptr + MIN_OFFSET), UNSAFE.getShort(ptr + MAX_OFFSET)); } } } } private boolean isEqual(long address, long address2, int size) { for (int i = 0; i < size; i += 8) { if (UNSAFE.getLong(address + i) != UNSAFE.getLong(address2 + i)) { return false; } } return true; } private String createString(long address, byte size) { byte[] arr = new byte[size]; for (int i = 0; i < size; i++) { arr[i] = UNSAFE.getByte(address + i); } return new String(arr); } } private static class TaskThread extends Thread { // #.## private static final int THREE_DIGITS_MASK = 0x2e0000; // #.# private static final int TWO_DIGITS_MASK = 0x2e00; // #.#- private static final int TWO_NEGATIVE_DIGITS_MASK = 0x2e002d; private static final int BYTE_MASK = 0xff; private static final int ZERO = '0'; private static final long DELIMITER_MASK = 0x3b3b3b3b3b3b3b3bL; private static final long[] SIGNIFICANT_BYTES_MASK = { 0, 0xff, 0xffff, 0xffffff, 0xffffffffL, 0xffffffffffL, 0xffffffffffffL, 0xffffffffffffffL, 0xffffffffffffffffL }; private final MeasurementContainer container; private final List begins; private final List ends; private TaskThread(int chunks) { this.container = new MeasurementContainer(); this.begins = new ArrayList<>(chunks); this.ends = new ArrayList<>(chunks); } public void addChunk(long begin, long end) { begins.add(begin); ends.add(end); } @Override public void run() { for (int i = 0; i < begins.size(); i++) { var begin = begins.get(i); var end = ends.get(i) - 1; while (end > begin && UNSAFE.getByte(end - 1) != '\n') { end--; } calcForChunk(begin, end); calcLastLine(end); } } private void calcLastLine(long offset) { long cityOffset = offset; long lastBytes = 0; int hashCode = 0; byte cityNameSize = 0; byte b; while ((b = UNSAFE.getByte(offset++)) != ';') { lastBytes = (lastBytes << 8) | b; hashCode = hashCode * 31 + b; cityNameSize++; } int temperature; int word = UNSAFE.getInt(offset); offset += 4; if ((word & TWO_NEGATIVE_DIGITS_MASK) == TWO_NEGATIVE_DIGITS_MASK) { word >>>= 8; temperature = ZERO * 11 - ((word & BYTE_MASK) * 10 + ((word >>> 16) & BYTE_MASK)); } else if ((word & THREE_DIGITS_MASK) == THREE_DIGITS_MASK) { temperature = (word & BYTE_MASK) * 100 + ((word >>> 8) & BYTE_MASK) * 10 + ((word >>> 24) & BYTE_MASK) - ZERO * 111; } else if ((word & TWO_DIGITS_MASK) == TWO_DIGITS_MASK) { temperature = (word & BYTE_MASK) * 10 + ((word >>> 16) & BYTE_MASK) - ZERO * 11; } else { // #.##- word = (word >>> 8) | (UNSAFE.getByte(offset) << 24); temperature = ZERO * 111 - ((word & BYTE_MASK) * 100 + ((word >>> 8) & BYTE_MASK) * 10 + ((word >>> 24) & BYTE_MASK)); } container.put(cityOffset, cityNameSize, hashCode, lastBytes, (short) temperature); } private void calcForChunk(long offset, long end) { long cityOffset, lastBytes, city, masked, hashCode; int temperature, word, delimiterIdx; byte cityNameSize; while (offset < end) { cityOffset = offset; lastBytes = 0; hashCode = 0; delimiterIdx = 8; while (delimiterIdx == 8) { city = UNSAFE.getLong(offset); masked = city ^ DELIMITER_MASK; masked = (masked - 0x0101010101010101L) & ~masked & 0x8080808080808080L; delimiterIdx = Long.numberOfTrailingZeros(masked) >>> 3; if (delimiterIdx == 0) { break; } offset += delimiterIdx; lastBytes = city & SIGNIFICANT_BYTES_MASK[delimiterIdx]; hashCode = ((hashCode >>> 5) ^ lastBytes) * 0x517cc1b727220a95L; } cityNameSize = (byte) (offset - cityOffset); word = UNSAFE.getInt(++offset); offset += 4; if ((word & TWO_NEGATIVE_DIGITS_MASK) == TWO_NEGATIVE_DIGITS_MASK) { word >>>= 8; temperature = ZERO * 11 - ((word & BYTE_MASK) * 10 + ((word >>> 16) & BYTE_MASK)); } else if ((word & THREE_DIGITS_MASK) == THREE_DIGITS_MASK) { temperature = (word & BYTE_MASK) * 100 + ((word >>> 8) & BYTE_MASK) * 10 + ((word >>> 24) & BYTE_MASK) - ZERO * 111; } else if ((word & TWO_DIGITS_MASK) == TWO_DIGITS_MASK) { temperature = (word & BYTE_MASK) * 10 + ((word >>> 16) & BYTE_MASK) - ZERO * 11; offset--; } else { // #.##- word = (word >>> 8) | (UNSAFE.getByte(offset++) << 24); temperature = ZERO * 111 - ((word & BYTE_MASK) * 100 + ((word >>> 8) & BYTE_MASK) * 10 + ((word >>> 24) & BYTE_MASK)); } offset++; container.put(cityOffset, cityNameSize, Long.hashCode(hashCode), lastBytes, (short) temperature); } } public void collectTo(Map results) { container.collectTo(results); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CreateMeasurements.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedWriter; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.concurrent.ThreadLocalRandom; public class CreateMeasurements { private static final Path MEASUREMENT_FILE = Path.of("./measurements.txt"); private record WeatherStation(String id, double meanTemperature) { double measurement() { double m = ThreadLocalRandom.current().nextGaussian(meanTemperature, 10); return Math.round(m * 10.0) / 10.0; } } public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); if (args.length != 1) { System.out.println("Usage: create_measurements.sh "); System.exit(1); } int size = 0; try { size = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.out.println("Invalid value for "); System.out.println("Usage: CreateMeasurements "); System.exit(1); } // @formatter:off // data from https://en.wikipedia.org/wiki/List_of_cities_by_average_temperature; // converted using https://wikitable2csv.ggor.de/ // brought to form using DuckDB: // D copy ( // select City, regexp_extract(Year,'(.*)\n.*', 1) as AverageTemp // from ( // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_1.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_2.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_3.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_4.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_5.csv', header = true) // ) // ) TO 'output.csv' (HEADER, DELIMITER ','); // @formatter:on List stations = List.of( new WeatherStation("Abha", 18.0), new WeatherStation("Abidjan", 26.0), new WeatherStation("Abéché", 29.4), new WeatherStation("Accra", 26.4), new WeatherStation("Addis Ababa", 16.0), new WeatherStation("Adelaide", 17.3), new WeatherStation("Aden", 29.1), new WeatherStation("Ahvaz", 25.4), new WeatherStation("Albuquerque", 14.0), new WeatherStation("Alexandra", 11.0), new WeatherStation("Alexandria", 20.0), new WeatherStation("Algiers", 18.2), new WeatherStation("Alice Springs", 21.0), new WeatherStation("Almaty", 10.0), new WeatherStation("Amsterdam", 10.2), new WeatherStation("Anadyr", -6.9), new WeatherStation("Anchorage", 2.8), new WeatherStation("Andorra la Vella", 9.8), new WeatherStation("Ankara", 12.0), new WeatherStation("Antananarivo", 17.9), new WeatherStation("Antsiranana", 25.2), new WeatherStation("Arkhangelsk", 1.3), new WeatherStation("Ashgabat", 17.1), new WeatherStation("Asmara", 15.6), new WeatherStation("Assab", 30.5), new WeatherStation("Astana", 3.5), new WeatherStation("Athens", 19.2), new WeatherStation("Atlanta", 17.0), new WeatherStation("Auckland", 15.2), new WeatherStation("Austin", 20.7), new WeatherStation("Baghdad", 22.77), new WeatherStation("Baguio", 19.5), new WeatherStation("Baku", 15.1), new WeatherStation("Baltimore", 13.1), new WeatherStation("Bamako", 27.8), new WeatherStation("Bangkok", 28.6), new WeatherStation("Bangui", 26.0), new WeatherStation("Banjul", 26.0), new WeatherStation("Barcelona", 18.2), new WeatherStation("Bata", 25.1), new WeatherStation("Batumi", 14.0), new WeatherStation("Beijing", 12.9), new WeatherStation("Beirut", 20.9), new WeatherStation("Belgrade", 12.5), new WeatherStation("Belize City", 26.7), new WeatherStation("Benghazi", 19.9), new WeatherStation("Bergen", 7.7), new WeatherStation("Berlin", 10.3), new WeatherStation("Bilbao", 14.7), new WeatherStation("Birao", 26.5), new WeatherStation("Bishkek", 11.3), new WeatherStation("Bissau", 27.0), new WeatherStation("Blantyre", 22.2), new WeatherStation("Bloemfontein", 15.6), new WeatherStation("Boise", 11.4), new WeatherStation("Bordeaux", 14.2), new WeatherStation("Bosaso", 30.0), new WeatherStation("Boston", 10.9), new WeatherStation("Bouaké", 26.0), new WeatherStation("Bratislava", 10.5), new WeatherStation("Brazzaville", 25.0), new WeatherStation("Bridgetown", 27.0), new WeatherStation("Brisbane", 21.4), new WeatherStation("Brussels", 10.5), new WeatherStation("Bucharest", 10.8), new WeatherStation("Budapest", 11.3), new WeatherStation("Bujumbura", 23.8), new WeatherStation("Bulawayo", 18.9), new WeatherStation("Burnie", 13.1), new WeatherStation("Busan", 15.0), new WeatherStation("Cabo San Lucas", 23.9), new WeatherStation("Cairns", 25.0), new WeatherStation("Cairo", 21.4), new WeatherStation("Calgary", 4.4), new WeatherStation("Canberra", 13.1), new WeatherStation("Cape Town", 16.2), new WeatherStation("Changsha", 17.4), new WeatherStation("Charlotte", 16.1), new WeatherStation("Chiang Mai", 25.8), new WeatherStation("Chicago", 9.8), new WeatherStation("Chihuahua", 18.6), new WeatherStation("Chișinău", 10.2), new WeatherStation("Chittagong", 25.9), new WeatherStation("Chongqing", 18.6), new WeatherStation("Christchurch", 12.2), new WeatherStation("City of San Marino", 11.8), new WeatherStation("Colombo", 27.4), new WeatherStation("Columbus", 11.7), new WeatherStation("Conakry", 26.4), new WeatherStation("Copenhagen", 9.1), new WeatherStation("Cotonou", 27.2), new WeatherStation("Cracow", 9.3), new WeatherStation("Da Lat", 17.9), new WeatherStation("Da Nang", 25.8), new WeatherStation("Dakar", 24.0), new WeatherStation("Dallas", 19.0), new WeatherStation("Damascus", 17.0), new WeatherStation("Dampier", 26.4), new WeatherStation("Dar es Salaam", 25.8), new WeatherStation("Darwin", 27.6), new WeatherStation("Denpasar", 23.7), new WeatherStation("Denver", 10.4), new WeatherStation("Detroit", 10.0), new WeatherStation("Dhaka", 25.9), new WeatherStation("Dikson", -11.1), new WeatherStation("Dili", 26.6), new WeatherStation("Djibouti", 29.9), new WeatherStation("Dodoma", 22.7), new WeatherStation("Dolisie", 24.0), new WeatherStation("Douala", 26.7), new WeatherStation("Dubai", 26.9), new WeatherStation("Dublin", 9.8), new WeatherStation("Dunedin", 11.1), new WeatherStation("Durban", 20.6), new WeatherStation("Dushanbe", 14.7), new WeatherStation("Edinburgh", 9.3), new WeatherStation("Edmonton", 4.2), new WeatherStation("El Paso", 18.1), new WeatherStation("Entebbe", 21.0), new WeatherStation("Erbil", 19.5), new WeatherStation("Erzurum", 5.1), new WeatherStation("Fairbanks", -2.3), new WeatherStation("Fianarantsoa", 17.9), new WeatherStation("Flores, Petén", 26.4), new WeatherStation("Frankfurt", 10.6), new WeatherStation("Fresno", 17.9), new WeatherStation("Fukuoka", 17.0), new WeatherStation("Gabès", 19.5), new WeatherStation("Gaborone", 21.0), new WeatherStation("Gagnoa", 26.0), new WeatherStation("Gangtok", 15.2), new WeatherStation("Garissa", 29.3), new WeatherStation("Garoua", 28.3), new WeatherStation("George Town", 27.9), new WeatherStation("Ghanzi", 21.4), new WeatherStation("Gjoa Haven", -14.4), new WeatherStation("Guadalajara", 20.9), new WeatherStation("Guangzhou", 22.4), new WeatherStation("Guatemala City", 20.4), new WeatherStation("Halifax", 7.5), new WeatherStation("Hamburg", 9.7), new WeatherStation("Hamilton", 13.8), new WeatherStation("Hanga Roa", 20.5), new WeatherStation("Hanoi", 23.6), new WeatherStation("Harare", 18.4), new WeatherStation("Harbin", 5.0), new WeatherStation("Hargeisa", 21.7), new WeatherStation("Hat Yai", 27.0), new WeatherStation("Havana", 25.2), new WeatherStation("Helsinki", 5.9), new WeatherStation("Heraklion", 18.9), new WeatherStation("Hiroshima", 16.3), new WeatherStation("Ho Chi Minh City", 27.4), new WeatherStation("Hobart", 12.7), new WeatherStation("Hong Kong", 23.3), new WeatherStation("Honiara", 26.5), new WeatherStation("Honolulu", 25.4), new WeatherStation("Houston", 20.8), new WeatherStation("Ifrane", 11.4), new WeatherStation("Indianapolis", 11.8), new WeatherStation("Iqaluit", -9.3), new WeatherStation("Irkutsk", 1.0), new WeatherStation("Istanbul", 13.9), new WeatherStation("İzmir", 17.9), new WeatherStation("Jacksonville", 20.3), new WeatherStation("Jakarta", 26.7), new WeatherStation("Jayapura", 27.0), new WeatherStation("Jerusalem", 18.3), new WeatherStation("Johannesburg", 15.5), new WeatherStation("Jos", 22.8), new WeatherStation("Juba", 27.8), new WeatherStation("Kabul", 12.1), new WeatherStation("Kampala", 20.0), new WeatherStation("Kandi", 27.7), new WeatherStation("Kankan", 26.5), new WeatherStation("Kano", 26.4), new WeatherStation("Kansas City", 12.5), new WeatherStation("Karachi", 26.0), new WeatherStation("Karonga", 24.4), new WeatherStation("Kathmandu", 18.3), new WeatherStation("Khartoum", 29.9), new WeatherStation("Kingston", 27.4), new WeatherStation("Kinshasa", 25.3), new WeatherStation("Kolkata", 26.7), new WeatherStation("Kuala Lumpur", 27.3), new WeatherStation("Kumasi", 26.0), new WeatherStation("Kunming", 15.7), new WeatherStation("Kuopio", 3.4), new WeatherStation("Kuwait City", 25.7), new WeatherStation("Kyiv", 8.4), new WeatherStation("Kyoto", 15.8), new WeatherStation("La Ceiba", 26.2), new WeatherStation("La Paz", 23.7), new WeatherStation("Lagos", 26.8), new WeatherStation("Lahore", 24.3), new WeatherStation("Lake Havasu City", 23.7), new WeatherStation("Lake Tekapo", 8.7), new WeatherStation("Las Palmas de Gran Canaria", 21.2), new WeatherStation("Las Vegas", 20.3), new WeatherStation("Launceston", 13.1), new WeatherStation("Lhasa", 7.6), new WeatherStation("Libreville", 25.9), new WeatherStation("Lisbon", 17.5), new WeatherStation("Livingstone", 21.8), new WeatherStation("Ljubljana", 10.9), new WeatherStation("Lodwar", 29.3), new WeatherStation("Lomé", 26.9), new WeatherStation("London", 11.3), new WeatherStation("Los Angeles", 18.6), new WeatherStation("Louisville", 13.9), new WeatherStation("Luanda", 25.8), new WeatherStation("Lubumbashi", 20.8), new WeatherStation("Lusaka", 19.9), new WeatherStation("Luxembourg City", 9.3), new WeatherStation("Lviv", 7.8), new WeatherStation("Lyon", 12.5), new WeatherStation("Madrid", 15.0), new WeatherStation("Mahajanga", 26.3), new WeatherStation("Makassar", 26.7), new WeatherStation("Makurdi", 26.0), new WeatherStation("Malabo", 26.3), new WeatherStation("Malé", 28.0), new WeatherStation("Managua", 27.3), new WeatherStation("Manama", 26.5), new WeatherStation("Mandalay", 28.0), new WeatherStation("Mango", 28.1), new WeatherStation("Manila", 28.4), new WeatherStation("Maputo", 22.8), new WeatherStation("Marrakesh", 19.6), new WeatherStation("Marseille", 15.8), new WeatherStation("Maun", 22.4), new WeatherStation("Medan", 26.5), new WeatherStation("Mek'ele", 22.7), new WeatherStation("Melbourne", 15.1), new WeatherStation("Memphis", 17.2), new WeatherStation("Mexicali", 23.1), new WeatherStation("Mexico City", 17.5), new WeatherStation("Miami", 24.9), new WeatherStation("Milan", 13.0), new WeatherStation("Milwaukee", 8.9), new WeatherStation("Minneapolis", 7.8), new WeatherStation("Minsk", 6.7), new WeatherStation("Mogadishu", 27.1), new WeatherStation("Mombasa", 26.3), new WeatherStation("Monaco", 16.4), new WeatherStation("Moncton", 6.1), new WeatherStation("Monterrey", 22.3), new WeatherStation("Montreal", 6.8), new WeatherStation("Moscow", 5.8), new WeatherStation("Mumbai", 27.1), new WeatherStation("Murmansk", 0.6), new WeatherStation("Muscat", 28.0), new WeatherStation("Mzuzu", 17.7), new WeatherStation("N'Djamena", 28.3), new WeatherStation("Naha", 23.1), new WeatherStation("Nairobi", 17.8), new WeatherStation("Nakhon Ratchasima", 27.3), new WeatherStation("Napier", 14.6), new WeatherStation("Napoli", 15.9), new WeatherStation("Nashville", 15.4), new WeatherStation("Nassau", 24.6), new WeatherStation("Ndola", 20.3), new WeatherStation("New Delhi", 25.0), new WeatherStation("New Orleans", 20.7), new WeatherStation("New York City", 12.9), new WeatherStation("Ngaoundéré", 22.0), new WeatherStation("Niamey", 29.3), new WeatherStation("Nicosia", 19.7), new WeatherStation("Niigata", 13.9), new WeatherStation("Nouadhibou", 21.3), new WeatherStation("Nouakchott", 25.7), new WeatherStation("Novosibirsk", 1.7), new WeatherStation("Nuuk", -1.4), new WeatherStation("Odesa", 10.7), new WeatherStation("Odienné", 26.0), new WeatherStation("Oklahoma City", 15.9), new WeatherStation("Omaha", 10.6), new WeatherStation("Oranjestad", 28.1), new WeatherStation("Oslo", 5.7), new WeatherStation("Ottawa", 6.6), new WeatherStation("Ouagadougou", 28.3), new WeatherStation("Ouahigouya", 28.6), new WeatherStation("Ouarzazate", 18.9), new WeatherStation("Oulu", 2.7), new WeatherStation("Palembang", 27.3), new WeatherStation("Palermo", 18.5), new WeatherStation("Palm Springs", 24.5), new WeatherStation("Palmerston North", 13.2), new WeatherStation("Panama City", 28.0), new WeatherStation("Parakou", 26.8), new WeatherStation("Paris", 12.3), new WeatherStation("Perth", 18.7), new WeatherStation("Petropavlovsk-Kamchatsky", 1.9), new WeatherStation("Philadelphia", 13.2), new WeatherStation("Phnom Penh", 28.3), new WeatherStation("Phoenix", 23.9), new WeatherStation("Pittsburgh", 10.8), new WeatherStation("Podgorica", 15.3), new WeatherStation("Pointe-Noire", 26.1), new WeatherStation("Pontianak", 27.7), new WeatherStation("Port Moresby", 26.9), new WeatherStation("Port Sudan", 28.4), new WeatherStation("Port Vila", 24.3), new WeatherStation("Port-Gentil", 26.0), new WeatherStation("Portland (OR)", 12.4), new WeatherStation("Porto", 15.7), new WeatherStation("Prague", 8.4), new WeatherStation("Praia", 24.4), new WeatherStation("Pretoria", 18.2), new WeatherStation("Pyongyang", 10.8), new WeatherStation("Rabat", 17.2), new WeatherStation("Rangpur", 24.4), new WeatherStation("Reggane", 28.3), new WeatherStation("Reykjavík", 4.3), new WeatherStation("Riga", 6.2), new WeatherStation("Riyadh", 26.0), new WeatherStation("Rome", 15.2), new WeatherStation("Roseau", 26.2), new WeatherStation("Rostov-on-Don", 9.9), new WeatherStation("Sacramento", 16.3), new WeatherStation("Saint Petersburg", 5.8), new WeatherStation("Saint-Pierre", 5.7), new WeatherStation("Salt Lake City", 11.6), new WeatherStation("San Antonio", 20.8), new WeatherStation("San Diego", 17.8), new WeatherStation("San Francisco", 14.6), new WeatherStation("San Jose", 16.4), new WeatherStation("San José", 22.6), new WeatherStation("San Juan", 27.2), new WeatherStation("San Salvador", 23.1), new WeatherStation("Sana'a", 20.0), new WeatherStation("Santo Domingo", 25.9), new WeatherStation("Sapporo", 8.9), new WeatherStation("Sarajevo", 10.1), new WeatherStation("Saskatoon", 3.3), new WeatherStation("Seattle", 11.3), new WeatherStation("Ségou", 28.0), new WeatherStation("Seoul", 12.5), new WeatherStation("Seville", 19.2), new WeatherStation("Shanghai", 16.7), new WeatherStation("Singapore", 27.0), new WeatherStation("Skopje", 12.4), new WeatherStation("Sochi", 14.2), new WeatherStation("Sofia", 10.6), new WeatherStation("Sokoto", 28.0), new WeatherStation("Split", 16.1), new WeatherStation("St. John's", 5.0), new WeatherStation("St. Louis", 13.9), new WeatherStation("Stockholm", 6.6), new WeatherStation("Surabaya", 27.1), new WeatherStation("Suva", 25.6), new WeatherStation("Suwałki", 7.2), new WeatherStation("Sydney", 17.7), new WeatherStation("Tabora", 23.0), new WeatherStation("Tabriz", 12.6), new WeatherStation("Taipei", 23.0), new WeatherStation("Tallinn", 6.4), new WeatherStation("Tamale", 27.9), new WeatherStation("Tamanrasset", 21.7), new WeatherStation("Tampa", 22.9), new WeatherStation("Tashkent", 14.8), new WeatherStation("Tauranga", 14.8), new WeatherStation("Tbilisi", 12.9), new WeatherStation("Tegucigalpa", 21.7), new WeatherStation("Tehran", 17.0), new WeatherStation("Tel Aviv", 20.0), new WeatherStation("Thessaloniki", 16.0), new WeatherStation("Thiès", 24.0), new WeatherStation("Tijuana", 17.8), new WeatherStation("Timbuktu", 28.0), new WeatherStation("Tirana", 15.2), new WeatherStation("Toamasina", 23.4), new WeatherStation("Tokyo", 15.4), new WeatherStation("Toliara", 24.1), new WeatherStation("Toluca", 12.4), new WeatherStation("Toronto", 9.4), new WeatherStation("Tripoli", 20.0), new WeatherStation("Tromsø", 2.9), new WeatherStation("Tucson", 20.9), new WeatherStation("Tunis", 18.4), new WeatherStation("Ulaanbaatar", -0.4), new WeatherStation("Upington", 20.4), new WeatherStation("Ürümqi", 7.4), new WeatherStation("Vaduz", 10.1), new WeatherStation("Valencia", 18.3), new WeatherStation("Valletta", 18.8), new WeatherStation("Vancouver", 10.4), new WeatherStation("Veracruz", 25.4), new WeatherStation("Vienna", 10.4), new WeatherStation("Vientiane", 25.9), new WeatherStation("Villahermosa", 27.1), new WeatherStation("Vilnius", 6.0), new WeatherStation("Virginia Beach", 15.8), new WeatherStation("Vladivostok", 4.9), new WeatherStation("Warsaw", 8.5), new WeatherStation("Washington, D.C.", 14.6), new WeatherStation("Wau", 27.8), new WeatherStation("Wellington", 12.9), new WeatherStation("Whitehorse", -0.1), new WeatherStation("Wichita", 13.9), new WeatherStation("Willemstad", 28.0), new WeatherStation("Winnipeg", 3.0), new WeatherStation("Wrocław", 9.6), new WeatherStation("Xi'an", 14.1), new WeatherStation("Yakutsk", -8.8), new WeatherStation("Yangon", 27.5), new WeatherStation("Yaoundé", 23.8), new WeatherStation("Yellowknife", -4.3), new WeatherStation("Yerevan", 12.4), new WeatherStation("Yinchuan", 9.0), new WeatherStation("Zagreb", 10.7), new WeatherStation("Zanzibar City", 26.0), new WeatherStation("Zürich", 9.3)); try (BufferedWriter bw = Files.newBufferedWriter(MEASUREMENT_FILE)) { for (int i = 0; i < size; i++) { if (i > 0 && i % 50_000_000 == 0) { System.out.printf("Wrote %,d measurements in %s ms%n", i, System.currentTimeMillis() - start); } WeatherStation station = stations.get(ThreadLocalRandom.current().nextInt(stations.size())); bw.write(station.id()); bw.write(";" + station.measurement()); bw.write('\n'); } } System.out.printf("Created file with %,d measurements in %s ms%n", size, System.currentTimeMillis() - start); } } ================================================ FILE: src/main/java/dev/morling/onebrc/CreateMeasurements2.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import org.rschwietzke.CheaperCharBuffer; import org.rschwietzke.FastRandom; /** * Faster version with some data faking instead of a real Gaussian distribution * Good enough for our purppose I guess. */ public class CreateMeasurements2 { private static final String FILE = "./measurements2.txt"; static class WeatherStation { final static char[] NUMBERS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; final String id; final int meanTemperature; final char[] firstPart; final FastRandom r = new FastRandom(ThreadLocalRandom.current().nextLong()); WeatherStation(String id, double meanTemperature) { this.id = id; this.meanTemperature = (int) meanTemperature; // make it directly copyable this.firstPart = (id + ";").toCharArray(); } /** * We write out data into the buffer to avoid string conversion * We also no longer use double and gaussian, because for our * purpose, the fake numbers here will do it. Less * * @param buffer the buffer to append to */ void measurement(final CheaperCharBuffer buffer) { // fake -10.9 to +10.9 variance without double operations and rounding // gives us -10 to +10 int m = meanTemperature + (r.nextInt(21) - 10); // gives us a decimal digit 0 to 9 as char char d = NUMBERS[r.nextInt(10)]; // just append, only one number has to be converted and we can do // better... if we watn buffer.append(firstPart, 0, firstPart.length) .append(String.valueOf(m)).append('.').append(d) .append('\n'); } } public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); if (args.length != 1) { System.out.println("Usage: create_measurements.sh "); System.exit(1); } int size = 0; try { size = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.out.println("Invalid value for "); System.out.println("Usage: CreateMeasurements "); System.exit(1); } // @formatter:off // data from https://en.wikipedia.org/wiki/List_of_cities_by_average_temperature; // converted using https://wikitable2csv.ggor.de/ // brought to form using DuckDB: // D copy ( // select City, regexp_extract(Year,'(.*)\n.*', 1) as AverageTemp // from ( // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_1.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_2.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_3.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_4.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_5.csv', header = true) // ) // ) TO 'output.csv' (HEADER, DELIMITER ','); // @formatter:on final List stations = Arrays.asList( new WeatherStation("Abha", 18.0), new WeatherStation("Abidjan", 26.0), new WeatherStation("Abéché", 29.4), new WeatherStation("Accra", 26.4), new WeatherStation("Addis Ababa", 16.0), new WeatherStation("Adelaide", 17.3), new WeatherStation("Aden", 29.1), new WeatherStation("Ahvaz", 25.4), new WeatherStation("Albuquerque", 14.0), new WeatherStation("Alexandra", 11.0), new WeatherStation("Alexandria", 20.0), new WeatherStation("Algiers", 18.2), new WeatherStation("Alice Springs", 21.0), new WeatherStation("Almaty", 10.0), new WeatherStation("Amsterdam", 10.2), new WeatherStation("Anadyr", -6.9), new WeatherStation("Anchorage", 2.8), new WeatherStation("Andorra la Vella", 9.8), new WeatherStation("Ankara", 12.0), new WeatherStation("Antananarivo", 17.9), new WeatherStation("Antsiranana", 25.2), new WeatherStation("Arkhangelsk", 1.3), new WeatherStation("Ashgabat", 17.1), new WeatherStation("Asmara", 15.6), new WeatherStation("Assab", 30.5), new WeatherStation("Astana", 3.5), new WeatherStation("Athens", 19.2), new WeatherStation("Atlanta", 17.0), new WeatherStation("Auckland", 15.2), new WeatherStation("Austin", 20.7), new WeatherStation("Baghdad", 22.77), new WeatherStation("Baguio", 19.5), new WeatherStation("Baku", 15.1), new WeatherStation("Baltimore", 13.1), new WeatherStation("Bamako", 27.8), new WeatherStation("Bangkok", 28.6), new WeatherStation("Bangui", 26.0), new WeatherStation("Banjul", 26.0), new WeatherStation("Barcelona", 18.2), new WeatherStation("Bata", 25.1), new WeatherStation("Batumi", 14.0), new WeatherStation("Beijing", 12.9), new WeatherStation("Beirut", 20.9), new WeatherStation("Belgrade", 12.5), new WeatherStation("Belize City", 26.7), new WeatherStation("Benghazi", 19.9), new WeatherStation("Bergen", 7.7), new WeatherStation("Berlin", 10.3), new WeatherStation("Bilbao", 14.7), new WeatherStation("Birao", 26.5), new WeatherStation("Bishkek", 11.3), new WeatherStation("Bissau", 27.0), new WeatherStation("Blantyre", 22.2), new WeatherStation("Bloemfontein", 15.6), new WeatherStation("Boise", 11.4), new WeatherStation("Bordeaux", 14.2), new WeatherStation("Bosaso", 30.0), new WeatherStation("Boston", 10.9), new WeatherStation("Bouaké", 26.0), new WeatherStation("Bratislava", 10.5), new WeatherStation("Brazzaville", 25.0), new WeatherStation("Bridgetown", 27.0), new WeatherStation("Brisbane", 21.4), new WeatherStation("Brussels", 10.5), new WeatherStation("Bucharest", 10.8), new WeatherStation("Budapest", 11.3), new WeatherStation("Bujumbura", 23.8), new WeatherStation("Bulawayo", 18.9), new WeatherStation("Burnie", 13.1), new WeatherStation("Busan", 15.0), new WeatherStation("Cabo San Lucas", 23.9), new WeatherStation("Cairns", 25.0), new WeatherStation("Cairo", 21.4), new WeatherStation("Calgary", 4.4), new WeatherStation("Canberra", 13.1), new WeatherStation("Cape Town", 16.2), new WeatherStation("Changsha", 17.4), new WeatherStation("Charlotte", 16.1), new WeatherStation("Chiang Mai", 25.8), new WeatherStation("Chicago", 9.8), new WeatherStation("Chihuahua", 18.6), new WeatherStation("Chișinău", 10.2), new WeatherStation("Chittagong", 25.9), new WeatherStation("Chongqing", 18.6), new WeatherStation("Christchurch", 12.2), new WeatherStation("City of San Marino", 11.8), new WeatherStation("Colombo", 27.4), new WeatherStation("Columbus", 11.7), new WeatherStation("Conakry", 26.4), new WeatherStation("Copenhagen", 9.1), new WeatherStation("Cotonou", 27.2), new WeatherStation("Cracow", 9.3), new WeatherStation("Da Lat", 17.9), new WeatherStation("Da Nang", 25.8), new WeatherStation("Dakar", 24.0), new WeatherStation("Dallas", 19.0), new WeatherStation("Damascus", 17.0), new WeatherStation("Dampier", 26.4), new WeatherStation("Dar es Salaam", 25.8), new WeatherStation("Darwin", 27.6), new WeatherStation("Denpasar", 23.7), new WeatherStation("Denver", 10.4), new WeatherStation("Detroit", 10.0), new WeatherStation("Dhaka", 25.9), new WeatherStation("Dikson", -11.1), new WeatherStation("Dili", 26.6), new WeatherStation("Djibouti", 29.9), new WeatherStation("Dodoma", 22.7), new WeatherStation("Dolisie", 24.0), new WeatherStation("Douala", 26.7), new WeatherStation("Dubai", 26.9), new WeatherStation("Dublin", 9.8), new WeatherStation("Dunedin", 11.1), new WeatherStation("Durban", 20.6), new WeatherStation("Dushanbe", 14.7), new WeatherStation("Edinburgh", 9.3), new WeatherStation("Edmonton", 4.2), new WeatherStation("El Paso", 18.1), new WeatherStation("Entebbe", 21.0), new WeatherStation("Erbil", 19.5), new WeatherStation("Erzurum", 5.1), new WeatherStation("Fairbanks", -2.3), new WeatherStation("Fianarantsoa", 17.9), new WeatherStation("Flores, Petén", 26.4), new WeatherStation("Frankfurt", 10.6), new WeatherStation("Fresno", 17.9), new WeatherStation("Fukuoka", 17.0), new WeatherStation("Gabès", 19.5), new WeatherStation("Gaborone", 21.0), new WeatherStation("Gagnoa", 26.0), new WeatherStation("Gangtok", 15.2), new WeatherStation("Garissa", 29.3), new WeatherStation("Garoua", 28.3), new WeatherStation("George Town", 27.9), new WeatherStation("Ghanzi", 21.4), new WeatherStation("Gjoa Haven", -14.4), new WeatherStation("Guadalajara", 20.9), new WeatherStation("Guangzhou", 22.4), new WeatherStation("Guatemala City", 20.4), new WeatherStation("Halifax", 7.5), new WeatherStation("Hamburg", 9.7), new WeatherStation("Hamilton", 13.8), new WeatherStation("Hanga Roa", 20.5), new WeatherStation("Hanoi", 23.6), new WeatherStation("Harare", 18.4), new WeatherStation("Harbin", 5.0), new WeatherStation("Hargeisa", 21.7), new WeatherStation("Hat Yai", 27.0), new WeatherStation("Havana", 25.2), new WeatherStation("Helsinki", 5.9), new WeatherStation("Heraklion", 18.9), new WeatherStation("Hiroshima", 16.3), new WeatherStation("Ho Chi Minh City", 27.4), new WeatherStation("Hobart", 12.7), new WeatherStation("Hong Kong", 23.3), new WeatherStation("Honiara", 26.5), new WeatherStation("Honolulu", 25.4), new WeatherStation("Houston", 20.8), new WeatherStation("Ifrane", 11.4), new WeatherStation("Indianapolis", 11.8), new WeatherStation("Iqaluit", -9.3), new WeatherStation("Irkutsk", 1.0), new WeatherStation("Istanbul", 13.9), new WeatherStation("İzmir", 17.9), new WeatherStation("Jacksonville", 20.3), new WeatherStation("Jakarta", 26.7), new WeatherStation("Jayapura", 27.0), new WeatherStation("Jerusalem", 18.3), new WeatherStation("Johannesburg", 15.5), new WeatherStation("Jos", 22.8), new WeatherStation("Juba", 27.8), new WeatherStation("Kabul", 12.1), new WeatherStation("Kampala", 20.0), new WeatherStation("Kandi", 27.7), new WeatherStation("Kankan", 26.5), new WeatherStation("Kano", 26.4), new WeatherStation("Kansas City", 12.5), new WeatherStation("Karachi", 26.0), new WeatherStation("Karonga", 24.4), new WeatherStation("Kathmandu", 18.3), new WeatherStation("Khartoum", 29.9), new WeatherStation("Kingston", 27.4), new WeatherStation("Kinshasa", 25.3), new WeatherStation("Kolkata", 26.7), new WeatherStation("Kuala Lumpur", 27.3), new WeatherStation("Kumasi", 26.0), new WeatherStation("Kunming", 15.7), new WeatherStation("Kuopio", 3.4), new WeatherStation("Kuwait City", 25.7), new WeatherStation("Kyiv", 8.4), new WeatherStation("Kyoto", 15.8), new WeatherStation("La Ceiba", 26.2), new WeatherStation("La Paz", 23.7), new WeatherStation("Lagos", 26.8), new WeatherStation("Lahore", 24.3), new WeatherStation("Lake Havasu City", 23.7), new WeatherStation("Lake Tekapo", 8.7), new WeatherStation("Las Palmas de Gran Canaria", 21.2), new WeatherStation("Las Vegas", 20.3), new WeatherStation("Launceston", 13.1), new WeatherStation("Lhasa", 7.6), new WeatherStation("Libreville", 25.9), new WeatherStation("Lisbon", 17.5), new WeatherStation("Livingstone", 21.8), new WeatherStation("Ljubljana", 10.9), new WeatherStation("Lodwar", 29.3), new WeatherStation("Lomé", 26.9), new WeatherStation("London", 11.3), new WeatherStation("Los Angeles", 18.6), new WeatherStation("Louisville", 13.9), new WeatherStation("Luanda", 25.8), new WeatherStation("Lubumbashi", 20.8), new WeatherStation("Lusaka", 19.9), new WeatherStation("Luxembourg City", 9.3), new WeatherStation("Lviv", 7.8), new WeatherStation("Lyon", 12.5), new WeatherStation("Madrid", 15.0), new WeatherStation("Mahajanga", 26.3), new WeatherStation("Makassar", 26.7), new WeatherStation("Makurdi", 26.0), new WeatherStation("Malabo", 26.3), new WeatherStation("Malé", 28.0), new WeatherStation("Managua", 27.3), new WeatherStation("Manama", 26.5), new WeatherStation("Mandalay", 28.0), new WeatherStation("Mango", 28.1), new WeatherStation("Manila", 28.4), new WeatherStation("Maputo", 22.8), new WeatherStation("Marrakesh", 19.6), new WeatherStation("Marseille", 15.8), new WeatherStation("Maun", 22.4), new WeatherStation("Medan", 26.5), new WeatherStation("Mek'ele", 22.7), new WeatherStation("Melbourne", 15.1), new WeatherStation("Memphis", 17.2), new WeatherStation("Mexicali", 23.1), new WeatherStation("Mexico City", 17.5), new WeatherStation("Miami", 24.9), new WeatherStation("Milan", 13.0), new WeatherStation("Milwaukee", 8.9), new WeatherStation("Minneapolis", 7.8), new WeatherStation("Minsk", 6.7), new WeatherStation("Mogadishu", 27.1), new WeatherStation("Mombasa", 26.3), new WeatherStation("Monaco", 16.4), new WeatherStation("Moncton", 6.1), new WeatherStation("Monterrey", 22.3), new WeatherStation("Montreal", 6.8), new WeatherStation("Moscow", 5.8), new WeatherStation("Mumbai", 27.1), new WeatherStation("Murmansk", 0.6), new WeatherStation("Muscat", 28.0), new WeatherStation("Mzuzu", 17.7), new WeatherStation("N'Djamena", 28.3), new WeatherStation("Naha", 23.1), new WeatherStation("Nairobi", 17.8), new WeatherStation("Nakhon Ratchasima", 27.3), new WeatherStation("Napier", 14.6), new WeatherStation("Napoli", 15.9), new WeatherStation("Nashville", 15.4), new WeatherStation("Nassau", 24.6), new WeatherStation("Ndola", 20.3), new WeatherStation("New Delhi", 25.0), new WeatherStation("New Orleans", 20.7), new WeatherStation("New York City", 12.9), new WeatherStation("Ngaoundéré", 22.0), new WeatherStation("Niamey", 29.3), new WeatherStation("Nicosia", 19.7), new WeatherStation("Niigata", 13.9), new WeatherStation("Nouadhibou", 21.3), new WeatherStation("Nouakchott", 25.7), new WeatherStation("Novosibirsk", 1.7), new WeatherStation("Nuuk", -1.4), new WeatherStation("Odesa", 10.7), new WeatherStation("Odienné", 26.0), new WeatherStation("Oklahoma City", 15.9), new WeatherStation("Omaha", 10.6), new WeatherStation("Oranjestad", 28.1), new WeatherStation("Oslo", 5.7), new WeatherStation("Ottawa", 6.6), new WeatherStation("Ouagadougou", 28.3), new WeatherStation("Ouahigouya", 28.6), new WeatherStation("Ouarzazate", 18.9), new WeatherStation("Oulu", 2.7), new WeatherStation("Palembang", 27.3), new WeatherStation("Palermo", 18.5), new WeatherStation("Palm Springs", 24.5), new WeatherStation("Palmerston North", 13.2), new WeatherStation("Panama City", 28.0), new WeatherStation("Parakou", 26.8), new WeatherStation("Paris", 12.3), new WeatherStation("Perth", 18.7), new WeatherStation("Petropavlovsk-Kamchatsky", 1.9), new WeatherStation("Philadelphia", 13.2), new WeatherStation("Phnom Penh", 28.3), new WeatherStation("Phoenix", 23.9), new WeatherStation("Pittsburgh", 10.8), new WeatherStation("Podgorica", 15.3), new WeatherStation("Pointe-Noire", 26.1), new WeatherStation("Pontianak", 27.7), new WeatherStation("Port Moresby", 26.9), new WeatherStation("Port Sudan", 28.4), new WeatherStation("Port Vila", 24.3), new WeatherStation("Port-Gentil", 26.0), new WeatherStation("Portland (OR)", 12.4), new WeatherStation("Porto", 15.7), new WeatherStation("Prague", 8.4), new WeatherStation("Praia", 24.4), new WeatherStation("Pretoria", 18.2), new WeatherStation("Pyongyang", 10.8), new WeatherStation("Rabat", 17.2), new WeatherStation("Rangpur", 24.4), new WeatherStation("Reggane", 28.3), new WeatherStation("Reykjavík", 4.3), new WeatherStation("Riga", 6.2), new WeatherStation("Riyadh", 26.0), new WeatherStation("Rome", 15.2), new WeatherStation("Roseau", 26.2), new WeatherStation("Rostov-on-Don", 9.9), new WeatherStation("Sacramento", 16.3), new WeatherStation("Saint Petersburg", 5.8), new WeatherStation("Saint-Pierre", 5.7), new WeatherStation("Salt Lake City", 11.6), new WeatherStation("San Antonio", 20.8), new WeatherStation("San Diego", 17.8), new WeatherStation("San Francisco", 14.6), new WeatherStation("San Jose", 16.4), new WeatherStation("San José", 22.6), new WeatherStation("San Juan", 27.2), new WeatherStation("San Salvador", 23.1), new WeatherStation("Sana'a", 20.0), new WeatherStation("Santo Domingo", 25.9), new WeatherStation("Sapporo", 8.9), new WeatherStation("Sarajevo", 10.1), new WeatherStation("Saskatoon", 3.3), new WeatherStation("Seattle", 11.3), new WeatherStation("Ségou", 28.0), new WeatherStation("Seoul", 12.5), new WeatherStation("Seville", 19.2), new WeatherStation("Shanghai", 16.7), new WeatherStation("Singapore", 27.0), new WeatherStation("Skopje", 12.4), new WeatherStation("Sochi", 14.2), new WeatherStation("Sofia", 10.6), new WeatherStation("Sokoto", 28.0), new WeatherStation("Split", 16.1), new WeatherStation("St. John's", 5.0), new WeatherStation("St. Louis", 13.9), new WeatherStation("Stockholm", 6.6), new WeatherStation("Surabaya", 27.1), new WeatherStation("Suva", 25.6), new WeatherStation("Suwałki", 7.2), new WeatherStation("Sydney", 17.7), new WeatherStation("Tabora", 23.0), new WeatherStation("Tabriz", 12.6), new WeatherStation("Taipei", 23.0), new WeatherStation("Tallinn", 6.4), new WeatherStation("Tamale", 27.9), new WeatherStation("Tamanrasset", 21.7), new WeatherStation("Tampa", 22.9), new WeatherStation("Tashkent", 14.8), new WeatherStation("Tauranga", 14.8), new WeatherStation("Tbilisi", 12.9), new WeatherStation("Tegucigalpa", 21.7), new WeatherStation("Tehran", 17.0), new WeatherStation("Tel Aviv", 20.0), new WeatherStation("Thessaloniki", 16.0), new WeatherStation("Thiès", 24.0), new WeatherStation("Tijuana", 17.8), new WeatherStation("Timbuktu", 28.0), new WeatherStation("Tirana", 15.2), new WeatherStation("Toamasina", 23.4), new WeatherStation("Tokyo", 15.4), new WeatherStation("Toliara", 24.1), new WeatherStation("Toluca", 12.4), new WeatherStation("Toronto", 9.4), new WeatherStation("Tripoli", 20.0), new WeatherStation("Tromsø", 2.9), new WeatherStation("Tucson", 20.9), new WeatherStation("Tunis", 18.4), new WeatherStation("Ulaanbaatar", -0.4), new WeatherStation("Upington", 20.4), new WeatherStation("Ürümqi", 7.4), new WeatherStation("Vaduz", 10.1), new WeatherStation("Valencia", 18.3), new WeatherStation("Valletta", 18.8), new WeatherStation("Vancouver", 10.4), new WeatherStation("Veracruz", 25.4), new WeatherStation("Vienna", 10.4), new WeatherStation("Vientiane", 25.9), new WeatherStation("Villahermosa", 27.1), new WeatherStation("Vilnius", 6.0), new WeatherStation("Virginia Beach", 15.8), new WeatherStation("Vladivostok", 4.9), new WeatherStation("Warsaw", 8.5), new WeatherStation("Washington, D.C.", 14.6), new WeatherStation("Wau", 27.8), new WeatherStation("Wellington", 12.9), new WeatherStation("Whitehorse", -0.1), new WeatherStation("Wichita", 13.9), new WeatherStation("Willemstad", 28.0), new WeatherStation("Winnipeg", 3.0), new WeatherStation("Wrocław", 9.6), new WeatherStation("Xi'an", 14.1), new WeatherStation("Yakutsk", -8.8), new WeatherStation("Yangon", 27.5), new WeatherStation("Yaoundé", 23.8), new WeatherStation("Yellowknife", -4.3), new WeatherStation("Yerevan", 12.4), new WeatherStation("Yinchuan", 9.0), new WeatherStation("Zagreb", 10.7), new WeatherStation("Zanzibar City", 26.0), new WeatherStation("Zürich", 9.3)); File file = new File(FILE); // break the loop and unroll it manually int strideSize = 50_000_000; int outer = size / strideSize; int remainder = size - (outer * strideSize); try (final BufferedWriter bw = new BufferedWriter(new FileWriter(file))) { for (int i = 0; i < outer; i++) { produce(bw, stations, strideSize); // we avoid a modulo if here and use the stride size to print and update System.out.println("Wrote %,d measurements in %s ms".formatted((i + 1) * strideSize, System.currentTimeMillis() - start)); } // there might be a rest produce(bw, stations, remainder); // write fully before taking measurements bw.flush(); System.out.println("Created file with %,d measurements in %s ms".formatted(size, System.currentTimeMillis() - start)); } } private static void produce(BufferedWriter bw, List stations, int count) throws IOException { final int stationCount = stations.size(); final int rest = count % 8; // use a fast ranodm impl without atomics to be able to utilize the cpu better // and avoid sideeffects, FastRandom is very fake random and does not have a state final FastRandom r1 = new FastRandom(ThreadLocalRandom.current().nextLong()); final FastRandom r2 = new FastRandom(ThreadLocalRandom.current().nextLong()); final FastRandom r3 = new FastRandom(ThreadLocalRandom.current().nextLong()); final FastRandom r4 = new FastRandom(ThreadLocalRandom.current().nextLong()); // write to a fix buffer first, don't create strings ever // reuse buffer final CheaperCharBuffer sb = new CheaperCharBuffer(200); // manual loop unroll for less jumps for (int i = 0; i < count; i = i + 8) { { // try to fill teh cpu pipeline as much as possible with // independent operations int s1 = r1.nextInt(stationCount); int s2 = r2.nextInt(stationCount); int s3 = r3.nextInt(stationCount); int s4 = r4.nextInt(stationCount); // get us the ojects one after the other to have the array // in our L1 cache and not push it out with other data var w1 = stations.get(s1); var w2 = stations.get(s2); var w3 = stations.get(s3); var w4 = stations.get(s4); // write our data to our buffer w1.measurement(sb); w2.measurement(sb); w3.measurement(sb); w4.measurement(sb); } { int s1 = r1.nextInt(stationCount); int s2 = r2.nextInt(stationCount); int s3 = r3.nextInt(stationCount); int s4 = r4.nextInt(stationCount); var w1 = stations.get(s1); var w2 = stations.get(s2); var w3 = stations.get(s3); var w4 = stations.get(s4); w1.measurement(sb); w2.measurement(sb); w3.measurement(sb); w4.measurement(sb); } // write the buffer directly, no intermediate string copy bw.write(sb.data_, 0, sb.length_); // reuse buffer, reset only, no cleaning sb.clear(); } // there might be a rest to write for (int i = 0; i < rest; i++) { sb.clear(); int s = r1.nextInt(stationCount); var w = stations.get(s); w.measurement(sb); bw.write(sb.data_, 0, sb.length_); } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CreateMeasurements3.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashSet; import java.util.concurrent.ThreadLocalRandom; public class CreateMeasurements3 { public static final int MAX_NAME_LEN = 100; public static final int KEYSET_SIZE = 10_000; public static void main(String[] args) throws Exception { if (args.length != 1) { System.out.println("Usage: create_measurements3.sh "); System.exit(1); } int size = 0; try { size = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.out.println("Invalid value for "); System.out.println("Usage: create_measurements3.sh "); System.exit(1); } final var weatherStations = generateWeatherStations(); final var start = System.currentTimeMillis(); final var rnd = ThreadLocalRandom.current(); try (var out = new BufferedWriter(new FileWriter("measurements3.txt"))) { for (int i = 1; i <= size; i++) { var station = weatherStations.get(rnd.nextInt(KEYSET_SIZE)); double temp = rnd.nextGaussian(station.avgTemp, 7.0); out.write(station.name); out.write(';'); out.write(Double.toString(Math.round(temp * 10.0) / 10.0)); out.write('\n'); if (i % 50_000_000 == 0) { System.out.printf("Wrote %,d measurements in %,d ms%n", i, System.currentTimeMillis() - start); } } } } record WeatherStation(String name, float avgTemp) { } private static ArrayList generateWeatherStations() throws Exception { // Use a public list of city names and concatenate them all into a long string, // which we'll use as a "source of city name randomness" var bigName = new StringBuilder(1 << 20); try (var rows = new BufferedReader(new FileReader("data/weather_stations.csv"));) { skipComments(rows); while (true) { var row = rows.readLine(); if (row == null) { break; } bigName.append(row, 0, row.indexOf(';')); } } final var weatherStations = new ArrayList(); final var names = new HashSet(); var minLen = Integer.MAX_VALUE; var maxLen = Integer.MIN_VALUE; try (var rows = new BufferedReader(new FileReader("data/weather_stations.csv"))) { skipComments(rows); final var nameSource = new StringReader(bigName.toString()); final var buf = new char[MAX_NAME_LEN]; final var rnd = ThreadLocalRandom.current(); final double yOffset = 4; final double factor = 2500; final double xOffset = 0.372; final double power = 7; for (int i = 0; i < KEYSET_SIZE; i++) { var row = rows.readLine(); if (row == null) { break; } // Use a 7th-order curve to simulate the name length distribution. // It gives us mostly short names, but with large outliers. var nameLen = (int) (yOffset + factor * Math.pow(rnd.nextDouble() - xOffset, power)); var count = nameSource.read(buf, 0, nameLen); if (count == -1) { throw new Exception("Name source exhausted"); } var nameBuf = new StringBuilder(nameLen); nameBuf.append(buf, 0, nameLen); if (Character.isWhitespace(nameBuf.charAt(0))) { nameBuf.setCharAt(0, readNonSpace(nameSource)); } if (Character.isWhitespace(nameBuf.charAt(nameBuf.length() - 1))) { nameBuf.setCharAt(nameBuf.length() - 1, readNonSpace(nameSource)); } var name = nameBuf.toString(); while (names.contains(name)) { nameBuf.setCharAt(rnd.nextInt(nameBuf.length()), readNonSpace(nameSource)); name = nameBuf.toString(); } int actualLen; while (true) { actualLen = name.getBytes(StandardCharsets.UTF_8).length; if (actualLen <= 100) { break; } nameBuf.deleteCharAt(nameBuf.length() - 1); if (Character.isWhitespace(nameBuf.charAt(nameBuf.length() - 1))) { nameBuf.setCharAt(nameBuf.length() - 1, readNonSpace(nameSource)); } name = nameBuf.toString(); } if (name.indexOf(';') != -1) { throw new Exception("Station name contains a semicolon!"); } names.add(name); minLen = Integer.min(minLen, actualLen); maxLen = Integer.max(maxLen, actualLen); var lat = Float.parseFloat(row.substring(row.indexOf(';') + 1)); // Guesstimate mean temperature using cosine of latitude var avgTemp = (float) (30 * Math.cos(Math.toRadians(lat))) - 10; weatherStations.add(new WeatherStation(name, avgTemp)); } } System.out.format("Generated %,d station names with length from %,d to %,d%n", KEYSET_SIZE, minLen, maxLen); return weatherStations; } private static void skipComments(BufferedReader rows) throws IOException { while (rows.readLine().startsWith("#")) { } } private static char readNonSpace(StringReader nameSource) throws IOException { while (true) { var n = nameSource.read(); if (n == -1) { throw new IOException("Name source exhausted"); } var ch = (char) n; if (ch != ' ') { return ch; } } } } ================================================ FILE: src/main/java/dev/morling/onebrc/CreateMeasurementsFast.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ThreadLocalRandom; public class CreateMeasurementsFast { private static final Path MEASUREMENT_FILE = Path.of("./measurements.txt"); static final Executor EXECUTOR_SERVICE = Executors.newWorkStealingPool(); private record WeatherStation(String id, double meanTemperature) { double measurement() { double m = ThreadLocalRandom.current().nextGaussian(meanTemperature, 10); return Math.round(m * 10.0) / 10.0; } } public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); if (args.length != 1) { System.out.println("Usage: create_measurements.sh "); System.exit(1); } int size = 0; try { size = Integer.parseInt(args[0]); } catch (NumberFormatException e) { System.out.println("Invalid value for "); System.out.println("Usage: CreateMeasurements "); System.exit(1); } try { Files.deleteIfExists(MEASUREMENT_FILE); Files.createFile(MEASUREMENT_FILE); } catch (Exception e) { // ignore } // @formatter:off // data from https://en.wikipedia.org/wiki/List_of_cities_by_average_temperature; // converted using https://wikitable2csv.ggor.de/ // brought to form using DuckDB: // D copy ( // select City, regexp_extract(Year,'(.*)\n.*', 1) as AverageTemp // from ( // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_1.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_2.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_3.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_4.csv', header = true) // union // select City,Year // from read_csv_auto('List_of_cities_by_average_temperature_5.csv', header = true) // ) // ) TO 'output.csv' (HEADER, DELIMITER ','); // @formatter:on List stations = List.of( new WeatherStation("Abha", 18.0), new WeatherStation("Abidjan", 26.0), new WeatherStation("Abéché", 29.4), new WeatherStation("Accra", 26.4), new WeatherStation("Addis Ababa", 16.0), new WeatherStation("Adelaide", 17.3), new WeatherStation("Aden", 29.1), new WeatherStation("Ahvaz", 25.4), new WeatherStation("Albuquerque", 14.0), new WeatherStation("Alexandra", 11.0), new WeatherStation("Alexandria", 20.0), new WeatherStation("Algiers", 18.2), new WeatherStation("Alice Springs", 21.0), new WeatherStation("Almaty", 10.0), new WeatherStation("Amsterdam", 10.2), new WeatherStation("Anadyr", -6.9), new WeatherStation("Anchorage", 2.8), new WeatherStation("Andorra la Vella", 9.8), new WeatherStation("Ankara", 12.0), new WeatherStation("Antananarivo", 17.9), new WeatherStation("Antsiranana", 25.2), new WeatherStation("Arkhangelsk", 1.3), new WeatherStation("Ashgabat", 17.1), new WeatherStation("Asmara", 15.6), new WeatherStation("Assab", 30.5), new WeatherStation("Astana", 3.5), new WeatherStation("Athens", 19.2), new WeatherStation("Atlanta", 17.0), new WeatherStation("Auckland", 15.2), new WeatherStation("Austin", 20.7), new WeatherStation("Baghdad", 22.77), new WeatherStation("Baguio", 19.5), new WeatherStation("Baku", 15.1), new WeatherStation("Baltimore", 13.1), new WeatherStation("Bamako", 27.8), new WeatherStation("Bangkok", 28.6), new WeatherStation("Bangui", 26.0), new WeatherStation("Banjul", 26.0), new WeatherStation("Barcelona", 18.2), new WeatherStation("Bata", 25.1), new WeatherStation("Batumi", 14.0), new WeatherStation("Beijing", 12.9), new WeatherStation("Beirut", 20.9), new WeatherStation("Belgrade", 12.5), new WeatherStation("Belize City", 26.7), new WeatherStation("Benghazi", 19.9), new WeatherStation("Bergen", 7.7), new WeatherStation("Berlin", 10.3), new WeatherStation("Bilbao", 14.7), new WeatherStation("Birao", 26.5), new WeatherStation("Bishkek", 11.3), new WeatherStation("Bissau", 27.0), new WeatherStation("Blantyre", 22.2), new WeatherStation("Bloemfontein", 15.6), new WeatherStation("Boise", 11.4), new WeatherStation("Bordeaux", 14.2), new WeatherStation("Bosaso", 30.0), new WeatherStation("Boston", 10.9), new WeatherStation("Bouaké", 26.0), new WeatherStation("Bratislava", 10.5), new WeatherStation("Brazzaville", 25.0), new WeatherStation("Bridgetown", 27.0), new WeatherStation("Brisbane", 21.4), new WeatherStation("Brussels", 10.5), new WeatherStation("Bucharest", 10.8), new WeatherStation("Budapest", 11.3), new WeatherStation("Bujumbura", 23.8), new WeatherStation("Bulawayo", 18.9), new WeatherStation("Burnie", 13.1), new WeatherStation("Busan", 15.0), new WeatherStation("Cabo San Lucas", 23.9), new WeatherStation("Cairns", 25.0), new WeatherStation("Cairo", 21.4), new WeatherStation("Calgary", 4.4), new WeatherStation("Canberra", 13.1), new WeatherStation("Cape Town", 16.2), new WeatherStation("Changsha", 17.4), new WeatherStation("Charlotte", 16.1), new WeatherStation("Chiang Mai", 25.8), new WeatherStation("Chicago", 9.8), new WeatherStation("Chihuahua", 18.6), new WeatherStation("Chișinău", 10.2), new WeatherStation("Chittagong", 25.9), new WeatherStation("Chongqing", 18.6), new WeatherStation("Christchurch", 12.2), new WeatherStation("City of San Marino", 11.8), new WeatherStation("Colombo", 27.4), new WeatherStation("Columbus", 11.7), new WeatherStation("Conakry", 26.4), new WeatherStation("Copenhagen", 9.1), new WeatherStation("Cotonou", 27.2), new WeatherStation("Cracow", 9.3), new WeatherStation("Da Lat", 17.9), new WeatherStation("Da Nang", 25.8), new WeatherStation("Dakar", 24.0), new WeatherStation("Dallas", 19.0), new WeatherStation("Damascus", 17.0), new WeatherStation("Dampier", 26.4), new WeatherStation("Dar es Salaam", 25.8), new WeatherStation("Darwin", 27.6), new WeatherStation("Denpasar", 23.7), new WeatherStation("Denver", 10.4), new WeatherStation("Detroit", 10.0), new WeatherStation("Dhaka", 25.9), new WeatherStation("Dikson", -11.1), new WeatherStation("Dili", 26.6), new WeatherStation("Djibouti", 29.9), new WeatherStation("Dodoma", 22.7), new WeatherStation("Dolisie", 24.0), new WeatherStation("Douala", 26.7), new WeatherStation("Dubai", 26.9), new WeatherStation("Dublin", 9.8), new WeatherStation("Dunedin", 11.1), new WeatherStation("Durban", 20.6), new WeatherStation("Dushanbe", 14.7), new WeatherStation("Edinburgh", 9.3), new WeatherStation("Edmonton", 4.2), new WeatherStation("El Paso", 18.1), new WeatherStation("Entebbe", 21.0), new WeatherStation("Erbil", 19.5), new WeatherStation("Erzurum", 5.1), new WeatherStation("Fairbanks", -2.3), new WeatherStation("Fianarantsoa", 17.9), new WeatherStation("Flores, Petén", 26.4), new WeatherStation("Frankfurt", 10.6), new WeatherStation("Fresno", 17.9), new WeatherStation("Fukuoka", 17.0), new WeatherStation("Gabès", 19.5), new WeatherStation("Gaborone", 21.0), new WeatherStation("Gagnoa", 26.0), new WeatherStation("Gangtok", 15.2), new WeatherStation("Garissa", 29.3), new WeatherStation("Garoua", 28.3), new WeatherStation("George Town", 27.9), new WeatherStation("Ghanzi", 21.4), new WeatherStation("Gjoa Haven", -14.4), new WeatherStation("Guadalajara", 20.9), new WeatherStation("Guangzhou", 22.4), new WeatherStation("Guatemala City", 20.4), new WeatherStation("Halifax", 7.5), new WeatherStation("Hamburg", 9.7), new WeatherStation("Hamilton", 13.8), new WeatherStation("Hanga Roa", 20.5), new WeatherStation("Hanoi", 23.6), new WeatherStation("Harare", 18.4), new WeatherStation("Harbin", 5.0), new WeatherStation("Hargeisa", 21.7), new WeatherStation("Hat Yai", 27.0), new WeatherStation("Havana", 25.2), new WeatherStation("Helsinki", 5.9), new WeatherStation("Heraklion", 18.9), new WeatherStation("Hiroshima", 16.3), new WeatherStation("Ho Chi Minh City", 27.4), new WeatherStation("Hobart", 12.7), new WeatherStation("Hong Kong", 23.3), new WeatherStation("Honiara", 26.5), new WeatherStation("Honolulu", 25.4), new WeatherStation("Houston", 20.8), new WeatherStation("Ifrane", 11.4), new WeatherStation("Indianapolis", 11.8), new WeatherStation("Iqaluit", -9.3), new WeatherStation("Irkutsk", 1.0), new WeatherStation("Istanbul", 13.9), new WeatherStation("İzmir", 17.9), new WeatherStation("Jacksonville", 20.3), new WeatherStation("Jakarta", 26.7), new WeatherStation("Jayapura", 27.0), new WeatherStation("Jerusalem", 18.3), new WeatherStation("Johannesburg", 15.5), new WeatherStation("Jos", 22.8), new WeatherStation("Juba", 27.8), new WeatherStation("Kabul", 12.1), new WeatherStation("Kampala", 20.0), new WeatherStation("Kandi", 27.7), new WeatherStation("Kankan", 26.5), new WeatherStation("Kano", 26.4), new WeatherStation("Kansas City", 12.5), new WeatherStation("Karachi", 26.0), new WeatherStation("Karonga", 24.4), new WeatherStation("Kathmandu", 18.3), new WeatherStation("Khartoum", 29.9), new WeatherStation("Kingston", 27.4), new WeatherStation("Kinshasa", 25.3), new WeatherStation("Kolkata", 26.7), new WeatherStation("Kuala Lumpur", 27.3), new WeatherStation("Kumasi", 26.0), new WeatherStation("Kunming", 15.7), new WeatherStation("Kuopio", 3.4), new WeatherStation("Kuwait City", 25.7), new WeatherStation("Kyiv", 8.4), new WeatherStation("Kyoto", 15.8), new WeatherStation("La Ceiba", 26.2), new WeatherStation("La Paz", 23.7), new WeatherStation("Lagos", 26.8), new WeatherStation("Lahore", 24.3), new WeatherStation("Lake Havasu City", 23.7), new WeatherStation("Lake Tekapo", 8.7), new WeatherStation("Las Palmas de Gran Canaria", 21.2), new WeatherStation("Las Vegas", 20.3), new WeatherStation("Launceston", 13.1), new WeatherStation("Lhasa", 7.6), new WeatherStation("Libreville", 25.9), new WeatherStation("Lisbon", 17.5), new WeatherStation("Livingstone", 21.8), new WeatherStation("Ljubljana", 10.9), new WeatherStation("Lodwar", 29.3), new WeatherStation("Lomé", 26.9), new WeatherStation("London", 11.3), new WeatherStation("Los Angeles", 18.6), new WeatherStation("Louisville", 13.9), new WeatherStation("Luanda", 25.8), new WeatherStation("Lubumbashi", 20.8), new WeatherStation("Lusaka", 19.9), new WeatherStation("Luxembourg City", 9.3), new WeatherStation("Lviv", 7.8), new WeatherStation("Lyon", 12.5), new WeatherStation("Madrid", 15.0), new WeatherStation("Mahajanga", 26.3), new WeatherStation("Makassar", 26.7), new WeatherStation("Makurdi", 26.0), new WeatherStation("Malabo", 26.3), new WeatherStation("Malé", 28.0), new WeatherStation("Managua", 27.3), new WeatherStation("Manama", 26.5), new WeatherStation("Mandalay", 28.0), new WeatherStation("Mango", 28.1), new WeatherStation("Manila", 28.4), new WeatherStation("Maputo", 22.8), new WeatherStation("Marrakesh", 19.6), new WeatherStation("Marseille", 15.8), new WeatherStation("Maun", 22.4), new WeatherStation("Medan", 26.5), new WeatherStation("Mek'ele", 22.7), new WeatherStation("Melbourne", 15.1), new WeatherStation("Memphis", 17.2), new WeatherStation("Mexicali", 23.1), new WeatherStation("Mexico City", 17.5), new WeatherStation("Miami", 24.9), new WeatherStation("Milan", 13.0), new WeatherStation("Milwaukee", 8.9), new WeatherStation("Minneapolis", 7.8), new WeatherStation("Minsk", 6.7), new WeatherStation("Mogadishu", 27.1), new WeatherStation("Mombasa", 26.3), new WeatherStation("Monaco", 16.4), new WeatherStation("Moncton", 6.1), new WeatherStation("Monterrey", 22.3), new WeatherStation("Montreal", 6.8), new WeatherStation("Moscow", 5.8), new WeatherStation("Mumbai", 27.1), new WeatherStation("Murmansk", 0.6), new WeatherStation("Muscat", 28.0), new WeatherStation("Mzuzu", 17.7), new WeatherStation("N'Djamena", 28.3), new WeatherStation("Naha", 23.1), new WeatherStation("Nairobi", 17.8), new WeatherStation("Nakhon Ratchasima", 27.3), new WeatherStation("Napier", 14.6), new WeatherStation("Napoli", 15.9), new WeatherStation("Nashville", 15.4), new WeatherStation("Nassau", 24.6), new WeatherStation("Ndola", 20.3), new WeatherStation("New Delhi", 25.0), new WeatherStation("New Orleans", 20.7), new WeatherStation("New York City", 12.9), new WeatherStation("Ngaoundéré", 22.0), new WeatherStation("Niamey", 29.3), new WeatherStation("Nicosia", 19.7), new WeatherStation("Niigata", 13.9), new WeatherStation("Nouadhibou", 21.3), new WeatherStation("Nouakchott", 25.7), new WeatherStation("Novosibirsk", 1.7), new WeatherStation("Nuuk", -1.4), new WeatherStation("Odesa", 10.7), new WeatherStation("Odienné", 26.0), new WeatherStation("Oklahoma City", 15.9), new WeatherStation("Omaha", 10.6), new WeatherStation("Oranjestad", 28.1), new WeatherStation("Oslo", 5.7), new WeatherStation("Ottawa", 6.6), new WeatherStation("Ouagadougou", 28.3), new WeatherStation("Ouahigouya", 28.6), new WeatherStation("Ouarzazate", 18.9), new WeatherStation("Oulu", 2.7), new WeatherStation("Palembang", 27.3), new WeatherStation("Palermo", 18.5), new WeatherStation("Palm Springs", 24.5), new WeatherStation("Palmerston North", 13.2), new WeatherStation("Panama City", 28.0), new WeatherStation("Parakou", 26.8), new WeatherStation("Paris", 12.3), new WeatherStation("Perth", 18.7), new WeatherStation("Petropavlovsk-Kamchatsky", 1.9), new WeatherStation("Philadelphia", 13.2), new WeatherStation("Phnom Penh", 28.3), new WeatherStation("Phoenix", 23.9), new WeatherStation("Pittsburgh", 10.8), new WeatherStation("Podgorica", 15.3), new WeatherStation("Pointe-Noire", 26.1), new WeatherStation("Pontianak", 27.7), new WeatherStation("Port Moresby", 26.9), new WeatherStation("Port Sudan", 28.4), new WeatherStation("Port Vila", 24.3), new WeatherStation("Port-Gentil", 26.0), new WeatherStation("Portland (OR)", 12.4), new WeatherStation("Porto", 15.7), new WeatherStation("Prague", 8.4), new WeatherStation("Praia", 24.4), new WeatherStation("Pretoria", 18.2), new WeatherStation("Pyongyang", 10.8), new WeatherStation("Rabat", 17.2), new WeatherStation("Rangpur", 24.4), new WeatherStation("Reggane", 28.3), new WeatherStation("Reykjavík", 4.3), new WeatherStation("Riga", 6.2), new WeatherStation("Riyadh", 26.0), new WeatherStation("Rome", 15.2), new WeatherStation("Roseau", 26.2), new WeatherStation("Rostov-on-Don", 9.9), new WeatherStation("Sacramento", 16.3), new WeatherStation("Saint Petersburg", 5.8), new WeatherStation("Saint-Pierre", 5.7), new WeatherStation("Salt Lake City", 11.6), new WeatherStation("San Antonio", 20.8), new WeatherStation("San Diego", 17.8), new WeatherStation("San Francisco", 14.6), new WeatherStation("San Jose", 16.4), new WeatherStation("San José", 22.6), new WeatherStation("San Juan", 27.2), new WeatherStation("San Salvador", 23.1), new WeatherStation("Sana'a", 20.0), new WeatherStation("Santo Domingo", 25.9), new WeatherStation("Sapporo", 8.9), new WeatherStation("Sarajevo", 10.1), new WeatherStation("Saskatoon", 3.3), new WeatherStation("Seattle", 11.3), new WeatherStation("Ségou", 28.0), new WeatherStation("Seoul", 12.5), new WeatherStation("Seville", 19.2), new WeatherStation("Shanghai", 16.7), new WeatherStation("Singapore", 27.0), new WeatherStation("Skopje", 12.4), new WeatherStation("Sochi", 14.2), new WeatherStation("Sofia", 10.6), new WeatherStation("Sokoto", 28.0), new WeatherStation("Split", 16.1), new WeatherStation("St. John's", 5.0), new WeatherStation("St. Louis", 13.9), new WeatherStation("Stockholm", 6.6), new WeatherStation("Surabaya", 27.1), new WeatherStation("Suva", 25.6), new WeatherStation("Suwałki", 7.2), new WeatherStation("Sydney", 17.7), new WeatherStation("Tabora", 23.0), new WeatherStation("Tabriz", 12.6), new WeatherStation("Taipei", 23.0), new WeatherStation("Tallinn", 6.4), new WeatherStation("Tamale", 27.9), new WeatherStation("Tamanrasset", 21.7), new WeatherStation("Tampa", 22.9), new WeatherStation("Tashkent", 14.8), new WeatherStation("Tauranga", 14.8), new WeatherStation("Tbilisi", 12.9), new WeatherStation("Tegucigalpa", 21.7), new WeatherStation("Tehran", 17.0), new WeatherStation("Tel Aviv", 20.0), new WeatherStation("Thessaloniki", 16.0), new WeatherStation("Thiès", 24.0), new WeatherStation("Tijuana", 17.8), new WeatherStation("Timbuktu", 28.0), new WeatherStation("Tirana", 15.2), new WeatherStation("Toamasina", 23.4), new WeatherStation("Tokyo", 15.4), new WeatherStation("Toliara", 24.1), new WeatherStation("Toluca", 12.4), new WeatherStation("Toronto", 9.4), new WeatherStation("Tripoli", 20.0), new WeatherStation("Tromsø", 2.9), new WeatherStation("Tucson", 20.9), new WeatherStation("Tunis", 18.4), new WeatherStation("Ulaanbaatar", -0.4), new WeatherStation("Upington", 20.4), new WeatherStation("Ürümqi", 7.4), new WeatherStation("Vaduz", 10.1), new WeatherStation("Valencia", 18.3), new WeatherStation("Valletta", 18.8), new WeatherStation("Vancouver", 10.4), new WeatherStation("Veracruz", 25.4), new WeatherStation("Vienna", 10.4), new WeatherStation("Vientiane", 25.9), new WeatherStation("Villahermosa", 27.1), new WeatherStation("Vilnius", 6.0), new WeatherStation("Virginia Beach", 15.8), new WeatherStation("Vladivostok", 4.9), new WeatherStation("Warsaw", 8.5), new WeatherStation("Washington, D.C.", 14.6), new WeatherStation("Wau", 27.8), new WeatherStation("Wellington", 12.9), new WeatherStation("Whitehorse", -0.1), new WeatherStation("Wichita", 13.9), new WeatherStation("Willemstad", 28.0), new WeatherStation("Winnipeg", 3.0), new WeatherStation("Wrocław", 9.6), new WeatherStation("Xi'an", 14.1), new WeatherStation("Yakutsk", -8.8), new WeatherStation("Yangon", 27.5), new WeatherStation("Yaoundé", 23.8), new WeatherStation("Yellowknife", -4.3), new WeatherStation("Yerevan", 12.4), new WeatherStation("Yinchuan", 9.0), new WeatherStation("Zagreb", 10.7), new WeatherStation("Zanzibar City", 26.0), new WeatherStation("Zürich", 9.3)); int chunkSize = (size / 10_000_000) == 0 ? size : 10_000_000; int numberOfFutures = size / chunkSize; if (numberOfFutures == 0) { numberOfFutures = 1; } CompletableFuture[] futures = new CompletableFuture[numberOfFutures]; for (int n = 0; n < numberOfFutures; n++) { int finalN = n; futures[n] = CompletableFuture.runAsync(() -> { StringBuilder builder = new StringBuilder(); for (int i = finalN * chunkSize; i <= (finalN + 1) * chunkSize - 1; i++) { WeatherStation station = stations.get(ThreadLocalRandom.current().nextInt(stations.size())); builder.append(station.id()) .append(";") .append(station.measurement()) .append('\n'); } try (BufferedWriter bw = Files.newBufferedWriter(MEASUREMENT_FILE, StandardOpenOption.APPEND)) { bw.write(builder.toString()); } catch (IOException e) { throw new RuntimeException(e); } }, EXECUTOR_SERVICE); } CompletableFuture.allOf(futures).join(); System.out.printf("Created file with %,d measurements in %s ms%n", size, System.currentTimeMillis() - start); } } ================================================ FILE: src/main/java/dev/morling/onebrc/PerfectHashSearch_hundredwatt.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.BitSet; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.random.RandomGenerator; import java.util.random.RandomGeneratorFactory; /** Offline script used to find the perfect hash seed for CalculateAverage_hundredwatt. */ public class PerfectHashSearch_hundredwatt { public static final int DESIRED_SLOTS = 5003; public static final int N_THREADS = Runtime.getRuntime().availableProcessors() - 1; public static void main(String[] args) throws IOException, InterruptedException { AtomicLong magicSeed = new AtomicLong(0); AtomicLong totalAttempts = new AtomicLong(0); AtomicLong maxCardinality = new AtomicLong(0); long start = System.currentTimeMillis(); System.out.println("Searching for perfect hash seed for " + DESIRED_SLOTS + " slots"); // Figure out encoding for all possible temperature values (1999 total) Map decodeTemperatureMap = new HashMap<>(); for (short i = -999; i <= 999; i++) { long word = 0; int shift = 0; if (i < 0) { word |= ((long) '-') << shift; shift += 8; } if (Math.abs(i) >= 100) { int hh = Math.abs(i) / 100; int tt = (Math.abs(i) - hh * 100) / 10; word |= ((long) (hh + '0')) << shift; shift += 8; word |= ((long) (tt + '0')) << shift; } else { int tt = Math.abs(i) / 10; // convert to ascii word |= ((long) (tt + '0')) << shift; } shift += 8; word |= ((long) '.') << shift; shift += 8; int uu = Math.abs(i) % 10; word |= ((long) (uu + '0')) << shift; // 31302e3000000000 decodeTemperatureMap.put(word, i); } ExecutorService executor = Executors.newFixedThreadPool(N_THREADS); RandomGeneratorFactory factory = RandomGeneratorFactory.of("L64X256MixRandom"); Runnable search = () -> { // Brute force to find seed: // generate a cryptographically secure random seed RandomGenerator rand; try { byte[] seed = new byte[16]; SecureRandom.getInstanceStrong().nextBytes(seed); rand = factory.create(ByteBuffer.wrap(seed).getLong()); System.out.println(Thread.currentThread().getName() + " | Using seed: " + rand.nextLong()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } int max = 0; int attempts = 0; while (true) { BitSet bs = new BitSet(DESIRED_SLOTS); var seed = rand.nextLong(); seed |= 0b1; // make sure it's odd for (var word : decodeTemperatureMap.keySet()) { var h = (word * seed) & ~(1L << 63); var pos = (int) (h % DESIRED_SLOTS); bs.set(pos); } var c = bs.cardinality(); if (c == decodeTemperatureMap.size()) { System.out.println("FOUND seed: " + seed + " cardinality: " + c + " max cardinality: " + max); magicSeed.set(seed); return; } max = Math.max(max, c); if (attempts % 100_000 == 0) { if (magicSeed.get() != 0) return; int finalMax = max; long currentMaxCardinality = maxCardinality.updateAndGet(currentMax -> Math.max(currentMax, finalMax)); long currentTotalAttempts = totalAttempts.addAndGet(100_000); if (Thread.currentThread().getName().endsWith("-1")) System.out.println(Thread.currentThread().getName() + " | max cardinality: " + currentMaxCardinality + " attempts: " + String.format("%,d", currentTotalAttempts)); } attempts++; } }; for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) { executor.submit(search); } // Wait for the search to complete executor.shutdown(); executor.awaitTermination(1, TimeUnit.DAYS); short[] TEMPERATURES = new short[DESIRED_SLOTS]; long seed = magicSeed.get(); decodeTemperatureMap.entrySet().stream().forEach(e -> { var word = e.getKey(); var h = (word * seed) & ~(1L << 63); var pos = (int) (h % DESIRED_SLOTS); if (TEMPERATURES[pos] != 0) throw new RuntimeException("collision at " + pos); TEMPERATURES[pos] = e.getValue(); }); System.out.println("SUCCESS seed: " + seed + " total attempts: " + totalAttempts.get()); try { File file = new File("seeds.txt"); file.delete(); file.createNewFile(); // Write the seed to seeds.txt FileWriter myWriter = new FileWriter("seeds.txt"); myWriter.write(Long.toString(seed)); myWriter.write("\n"); myWriter.close(); } catch (IOException e) { throw new RuntimeException(e); } System.out.println("Search took " + ((System.currentTimeMillis() - start) / 1000) + "s"); } } ================================================ FILE: src/main/java/org/rschwietzke/CheaperCharBuffer.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package org.rschwietzke; import java.util.Arrays; /** *

This class is meant to replaces the old {@link CheaperCharBuffer} in all areas * where performance and memory-efficency is key. XMLString compatibility * remains in place in case one has used that in their own code. * *

This buffer is mutable and when you use it, make sure you work with * it responsibly. In many cases, we will reuse the buffer to avoid fresh * memory allocations, hence you have to pay attention to its usage pattern. * It is not meant to be a general String replacement. * *

This class avoids many of the standard runtime checks that will result * in a runtime or array exception anyway. Why check twice and raise the * same exception? * * @author René Schwietzke * @since 3.10.0 */ public class CheaperCharBuffer implements CharSequence { // our data, can grow - that is not safe and has be altered from the original code // to allow speed public char[] data_; // the current size of the string data public int length_; // the current size of the string data private final int growBy_; // how much do we grow if needed, half a cache line public static final int CAPACITY_GROWTH = 64 / 2; // what is our start size? // a cache line is 64 byte mostly, the overhead is mostly 24 bytes // a char is two bytes, let's use one cache lines public static final int INITIAL_CAPACITY = (64 - 24) / 2; // static empty version; DON'T MODIFY IT public static final CheaperCharBuffer EMPTY = new CheaperCharBuffer(0); // the � character private static final char REPLACEMENT_CHARACTER = '\uFFFD'; /** * Constructs an XMLCharBuffer with a default size. */ public CheaperCharBuffer() { this.data_ = new char[INITIAL_CAPACITY]; this.length_ = 0; this.growBy_ = CAPACITY_GROWTH; } /** * Constructs an XMLCharBuffer with a desired size. * * @param startSize the size of the buffer to start with */ public CheaperCharBuffer(final int startSize) { this(startSize, CAPACITY_GROWTH); } /** * Constructs an XMLCharBuffer with a desired size. * * @param startSize the size of the buffer to start with * @param growBy by how much do we want to grow when needed */ public CheaperCharBuffer(final int startSize, final int growBy) { this.data_ = new char[startSize]; this.length_ = 0; this.growBy_ = Math.max(1, growBy); } /** * Constructs an XMLCharBuffer from another buffer. Copies the data * over. The new buffer capacity matches the length of the source. * * @param src the source buffer to copy from */ public CheaperCharBuffer(final CheaperCharBuffer src) { this(src, 0); } /** * Constructs an XMLCharBuffer from another buffer. Copies the data * over. You can add more capacity on top of the source length. If * you specify 0, the capacity will match the src length. * * @param src the source buffer to copy from * @param addCapacity how much capacity to add to origin length */ public CheaperCharBuffer(final CheaperCharBuffer src, final int addCapacity) { this.data_ = Arrays.copyOf(src.data_, src.length_ + Math.max(0, addCapacity)); this.length_ = src.length(); this.growBy_ = Math.max(1, CAPACITY_GROWTH); } /** * Constructs an XMLCharBuffer from a string. To avoid * too much allocation, we just take the string array as is and * don't allocate extra space in the first place. * * @param src the string to copy from */ public CheaperCharBuffer(final String src) { this.data_ = src.toCharArray(); this.length_ = src.length(); this.growBy_ = CAPACITY_GROWTH; } /** * Constructs an XMLString structure preset with the specified values. * There will not be any room to grow, if you need that, construct an * empty one and append. * *

There are not range checks performed. Make sure your data is correct. * * @param ch The character array, must not be null * @param offset The offset into the character array. * @param length The length of characters from the offset. */ public CheaperCharBuffer(final char[] ch, final int offset, final int length) { // just as big as we need it this(length); append(ch, offset, length); } /** * Check capacity and grow if needed automatically * * @param minimumCapacity how much space do we need at least */ private void ensureCapacity(final int minimumCapacity) { if (minimumCapacity > this.data_.length) { final int newSize = Math.max(minimumCapacity + this.growBy_, (this.data_.length << 1) + 2); this.data_ = Arrays.copyOf(this.data_, newSize); } } /** * Returns the current max capacity without growth. Does not * indicate how much capacity is already in use. Use {@link #length()} * for that. * * @return the current capacity, not taken any usage into account */ public int capacity() { return this.data_.length; } /** * Appends a single character to the buffer. * * @param c the character to append * @return this instance */ public CheaperCharBuffer append(final char c) { final int oldLength = this.length_++; // ensureCapacity is not inlined by the compiler, so put that here for the most // called method of all appends. Duplicate code, but for a reason. if (oldLength == this.data_.length) { final int newSize = Math.max(oldLength + this.growBy_, (this.data_.length << 1) + 2); this.data_ = Arrays.copyOf(this.data_, newSize); } this.data_[oldLength] = c; return this; } /** * Append a string to this buffer without copying the string first. * * @param src the string to append * @return this instance */ public CheaperCharBuffer append(final String src) { final int start = this.length_; this.length_ = this.length_ + src.length(); ensureCapacity(this.length_); // copy char by char because we don't get a copy for free // from a string yet, this might change when immutable arrays // make it into Java, but that will not be very soon for (int i = 0; i < src.length(); i++) { this.data_[start + i] = src.charAt(i); } return this; } /** * Add another buffer to this one. * * @param src the buffer to append * @return this instance */ public CheaperCharBuffer append(final CheaperCharBuffer src) { final int start = this.length_; this.length_ = this.length_ + src.length(); ensureCapacity(this.length_); System.arraycopy(src.data_, 0, this.data_, start, src.length_); return this; } /** * Add data from a char array to this buffer with the ability to specify * a range to copy from * * @param src the source char array * @param offset the pos to start to copy from * @param length the length of the data to copy * * @return this instance */ public CheaperCharBuffer append(final char[] src, final int offset, final int length) { final int start = this.length_; this.length_ = start + length; ensureCapacity(this.length_); System.arraycopy(src, offset, this.data_, start, length); return this; } /** * Returns the current length * * @return the length of the charbuffer data */ public int length() { return length_; } /** * Tell us how much the capacity grows if needed * * @return the value that determines how much we grow the backing * array in case we have to */ public int getGrowBy() { return this.growBy_; } /** * Resets the buffer to 0 length. It won't resize it to avoid memory * churn. * * @return this instance for fluid programming */ public CheaperCharBuffer clear() { this.length_ = 0; return this; } /** * Resets the buffer to 0 length and sets the new data. This * is a little cheaper than clear().append(c) depending on * the where and the inlining decisions. * * @param c the char to set * @return this instance for fluid programming */ public CheaperCharBuffer clearAndAppend(final char c) { this.length_ = 0; if (this.data_.length > 0) { this.data_[this.length_] = c; this.length_++; } else { // the rare case when we don't have any buffer at hand append(c); } return this; } /** * Does this buffer end with this string? If we check for * the empty string, we get true. If we would support JDK 11, we could * use Arrays.mismatch and be way faster. * * @param s the string to check the end against * @return true of the end matches the buffer, false otherwise */ public boolean endsWith(final String s) { // length does not match, cannot be the end if (this.length_ < s.length()) { return false; } // check the string by each char, avoids a copy of the string final int start = this.length_ - s.length(); // change this to Arrays.mismatch when going JDK 11 or higher for (int i = 0; i < s.length(); i++) { if (this.data_[i + start] != s.charAt(i)) { return false; } } return true; } /** * Reduces the buffer to the content between start and end marker when * only whitespaces are found before the startMarker as well as after the end marker. * If both strings overlap due to identical characters such as "foo" and "oof" * and the buffer is " foof ", we don't do anything. * *

If a marker is empty, it behaves like {@link java.lang.String#trim()} on that side. * * @param startMarker the start string to find, must not be null * @param endMarker the end string to find, must not be null * @return this instance * * @deprecated Use the new method {@link #trimToContent(String, String)} instead. */ public CheaperCharBuffer reduceToContent(final String startMarker, final String endMarker) { return trimToContent(startMarker, endMarker); } /** * Reduces the buffer to the content between start and end marker when * only whitespaces are found before the startMarker as well as after the end marker. * If both strings overlap due to identical characters such as "foo" and "oof" * and the buffer is " foof ", we don't do anything. * *

If a marker is empty, it behaves like {@link java.lang.String#trim()} on that side. * * @param startMarker the start string to find, must not be null * @param endMarker the end string to find, must not be null * @return this instance */ public CheaperCharBuffer trimToContent(final String startMarker, final String endMarker) { // if both are longer or same length than content, don't do anything final int markerLength = startMarker.length() + endMarker.length(); if (markerLength >= this.length_) { return this; } // run over starting whitespaces int sPos = 0; for (; sPos < this.length_ - markerLength; sPos++) { if (!Character.isWhitespace(this.data_[sPos])) { break; } } // run over ending whitespaces int ePos = this.length_ - 1; for (; ePos > sPos - markerLength; ePos--) { if (!Character.isWhitespace(this.data_[ePos])) { break; } } // if we have less content than marker length, give up // this also helps when markers overlap such as // and the string is " " if (ePos - sPos + 1 < markerLength) { return this; } // check the start for (int i = 0; i < startMarker.length(); i++) { if (startMarker.charAt(i) != this.data_[i + sPos]) { // no start match, stop and don't do anything return this; } } // check the end, ePos is when the first good char // occurred final int endStartCheckPos = ePos - endMarker.length() + 1; for (int i = 0; i < endMarker.length(); i++) { if (endMarker.charAt(i) != this.data_[endStartCheckPos + i]) { // no start match, stop and don't do anything return this; } } // shift left and cut length final int newLength = ePos - sPos + 1 - markerLength; System.arraycopy(this.data_, sPos + startMarker.length(), this.data_, 0, newLength); this.length_ = newLength; return this; } /** * Check if we have only whitespaces * * @return true if we have only whitespace, false otherwise */ public boolean isWhitespace() { for (int i = 0; i < this.length_; i++) { if (!Character.isWhitespace(this.data_[i])) { return false; } } return true; } /** * Trims the string similar to {@link java.lang.String#trim()} * * @return a string with removed whitespace at the beginning and the end */ public CheaperCharBuffer trim() { // clean the end first, because it is cheap return trimTrailing().trimLeading(); } /** * Removes all whitespace before the first non-whitespace char. * If all are whitespaces, we get an empty buffer * * @return this instance */ public CheaperCharBuffer trimLeading() { // run over starting whitespace int sPos = 0; for (; sPos < this.length_; sPos++) { if (!Character.isWhitespace(this.data_[sPos])) { break; } } if (sPos == 0) { // nothing to do return this; } else if (sPos == this.length_) { // only whitespace this.length_ = 0; return this; } // shift left final int newLength = this.length_ - sPos; System.arraycopy(this.data_, sPos, this.data_, 0, newLength); this.length_ = newLength; return this; } /** * Removes all whitespace at the end. * If all are whitespace, we get an empty buffer * * @return this instance * * @deprecated Use {@link #trimTrailing()} instead. */ public CheaperCharBuffer trimWhitespaceAtEnd() { return trimTrailing(); } /** * Removes all whitespace at the end. * If all are whitespace, we get an empty buffer * * @return this instance */ public CheaperCharBuffer trimTrailing() { // run over ending whitespaces int ePos = this.length_ - 1; for (; ePos >= 0; ePos--) { if (!Character.isWhitespace(this.data_[ePos])) { break; } } this.length_ = ePos + 1; return this; } /** * Shortens the buffer by that many positions. If the count is * larger than the length, we get just an empty buffer. If you pass in negative * values, we are failing, likely often silently. It is all about performance and * not a general all-purpose API. * * @param count a positive number, no runtime checks, if count is larger than * length, we get length = 0 * @return this instance */ public CheaperCharBuffer shortenBy(final int count) { final int newLength = this.length_ - count; this.length_ = newLength < 0 ? 0 : newLength; return this; } /** * Get the characters as char array, this will be a copy! * * @return a copy of the underlying char darta */ public char[] getChars() { return Arrays.copyOf(this.data_, this.length_); } /** * Returns a string representation of this buffer. This will be a copy * operation. If the buffer is emoty, we get a constant empty String back * to avoid any overhead. * * @return a string of the content of this buffer */ @Override public String toString() { if (this.length_ > 0) { return new String(this.data_, 0, this.length_); } else { return ""; } } /** * Returns the char a the given position. Will complain if * we try to read outside the range. We do a range check here * because we might not notice when we are within the buffer * but outside the current length. * * @param index the position to read from * @return the char at the position * @throws IndexOutOfBoundsException * in case one tries to read outside of valid buffer range */ @Override public char charAt(final int index) { if (index > this.length_ - 1 || index < 0) { throw new IndexOutOfBoundsException( "Tried to read outside of the valid buffer data"); } return this.data_[index]; } /** * Returns the char at the given position. No checks are * performed. It is up to the caller to make sure we * read correctly. Reading outside of the array will * cause an {@link IndexOutOfBoundsException} but using an * incorrect position in the array (such as beyond length) * might stay unnoticed! This is a performance method, * use at your own risk. * * @param index the position to read from * @return the char at the position */ public char unsafeCharAt(final int index) { return this.data_[index]; } /** * Returns a content copy of this buffer * * @return a copy of this buffer, the capacity might differ */ @Override public CheaperCharBuffer clone() { return new CheaperCharBuffer(this); } /** * Returns a CharSequence that is a subsequence of this sequence. * The subsequence starts with the char value at the specified index and * ends with the char value at index end - 1. The length * (in chars) of the * returned sequence is end - start, so if start == end * then an empty sequence is returned. * * @param start the start index, inclusive * @param end the end index, exclusive * * @return the specified subsequence * * @throws IndexOutOfBoundsException * if start or end are negative, * if end is greater than length(), * or if start is greater than end * * @return a charsequence of this buffer */ @Override public CharSequence subSequence(final int start, final int end) { if (start < 0) { throw new StringIndexOutOfBoundsException(start); } if (end > this.length_) { throw new StringIndexOutOfBoundsException(end); } final int l = end - start; if (l < 0) { throw new StringIndexOutOfBoundsException(l); } return new String(this.data_, start, l); } /** * Two buffers are identical when the length and * the content of the backing array (only for the * data in view) are identical. * * @param o the object to compare with * @return true if length and array content match, false otherwise */ @Override public boolean equals(final Object o) { if (o instanceof CharSequence) { final CharSequence ob = (CharSequence) o; if (ob.length() != this.length_) { return false; } // ok, in JDK 11 or up, we could use an // Arrays.mismatch, but we cannot do that // due to JDK 8 compatibility for (int i = 0; i < this.length_; i++) { if (ob.charAt(i) != this.data_[i]) { return false; } } // length and content match, be happy return true; } return false; } /** * We don't cache the hashcode because we mutate often. Don't use this in * hashmaps as key. But you can use that to look up in a hashmap against * a string using the CharSequence interface. * * @return the hashcode, similar to what a normal string would deliver */ @Override public int hashCode() { int h = 0; for (int i = 0; i < this.length_; i++) { h = ((h << 5) - h) + this.data_[i]; } return h; } /** * Append a character to an XMLCharBuffer. The character is an int value, and * can either be a single UTF-16 character or a supplementary character * represented by two UTF-16 code points. * * @param value The character value. * @return this instance for fluid programming * * @throws IllegalArgumentException if the specified * {@code codePoint} is not a valid Unicode code point. */ public CheaperCharBuffer appendCodePoint(final int value) { if (value <= Character.MAX_VALUE) { return this.append((char) value); } else { try { final char[] chars = Character.toChars(value); return this.append(chars, 0, chars.length); } catch (final IllegalArgumentException e) { // when value is not valid as UTF-16 this.append(REPLACEMENT_CHARACTER); throw e; } } } } ================================================ FILE: src/main/java/org/rschwietzke/FastRandom.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package org.rschwietzke; /** * Ultra-fast pseudo random generator that is not synchronized! * Don't use anything from Random by inheritance, this will inherit * a volatile! Not my idea, copyied in parts some demo random * generator lessons. * * @author rschwietzke * */ public class FastRandom { private long seed; public FastRandom() { this.seed = System.currentTimeMillis(); } public FastRandom(long seed) { this.seed = seed; } protected int next(int nbits) { // N.B. Not thread-safe! long x = this.seed; x ^= (x << 21); x ^= (x >>> 35); x ^= (x << 4); this.seed = x; x &= ((1L << nbits) - 1); return (int) x; } /** * Borrowed from the JDK * * @param bound * @return */ public int nextInt(int bound) { int r = next(31); int m = bound - 1; if ((bound & m) == 0) // i.e., bound is a power of 2 r = (int) ((bound * (long) r) >> 31); else { for (int u = r; u - (r = u % bound) + m < 0; u = next(31)) ; } return r; } /** * Borrowed from the JDK * @return */ public int nextInt() { return next(32); } } ================================================ FILE: src/main/java-22/dev/morling/onebrc/CalculateAverage_linl33.java ================================================ /* * Copyright 2023 The original authors * * 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. */ package dev.morling.onebrc; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorSpecies; import sun.misc.Unsafe; import java.io.IOException; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.stream.IntStream; public class CalculateAverage_linl33 { private static final String FILE_PATH_PROPERTY = "dev.morling.onebrc.CalculateAverage_linl33.measurementsPath"; private static final int WEATHER_STATION_LENGTH_MAX = 100; private static final long WEATHER_STATION_DISTINCT_MAX = 10_000L; private static final int N_THREADS = Runtime.getRuntime().availableProcessors(); private static final MemorySegment ALL = MemorySegment.NULL.reinterpret(Long.MAX_VALUE); private static final VectorSpecies BYTE_SPECIES = ByteVector.SPECIES_PREFERRED; private static final Thread.Builder THREAD_BUILDER = Thread .ofPlatform() .name("1brc-CalculateAverage-", 0) .inheritInheritableThreadLocals(false); private static final Unsafe UNSAFE; static { if (ByteOrder.nativeOrder() != ByteOrder.LITTLE_ENDIAN) { throw new UnsupportedOperationException("Error: BE JVMs are not supported"); } if ((BYTE_SPECIES.vectorByteSize() & (BYTE_SPECIES.vectorByteSize() - 1)) != 0) { throw new UnsupportedOperationException(STR."Unsupported vectorByteSize \{BYTE_SPECIES.vectorByteSize()}"); } try { var f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); UNSAFE = (Unsafe) f.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } public static void main() throws InterruptedException, IOException { final var filePath = Paths.get(System.getProperty(FILE_PATH_PROPERTY, "./measurements.txt")); try (final var channel = FileChannel.open(filePath)) { final var inputMapped = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size(), Arena.global()); final var chunkBounds = calcChunkBounds(inputMapped.address(), inputMapped.byteSize()); final var maps = new HashTable[N_THREADS]; try (final var threadPool = Executors.newFixedThreadPool(N_THREADS, THREAD_BUILDER.factory()); final var singleThreadExecutor = Executors.newSingleThreadExecutor(Thread.ofVirtual().factory())) { final var rootTask = CompletableFuture.runAsync(new CalculateAverageTask(maps, chunkBounds, 0), threadPool); final var futures = IntStream .range(1, N_THREADS) .mapToObj(t -> CompletableFuture .runAsync(new CalculateAverageTask(maps, chunkBounds, t), threadPool) .runAfterBothAsync(rootTask, () -> maps[0].merge(maps[t]), singleThreadExecutor)) .toArray(CompletableFuture[]::new); CompletableFuture.allOf(futures).join(); } printSorted(maps[0]); } } private static long[] calcChunkBounds(final long mappedAddr, final long fileSizeBytes) { final var chunkBounds = new long[N_THREADS + 1]; chunkBounds[0] = mappedAddr; chunkBounds[chunkBounds.length - 1] = mappedAddr + fileSizeBytes; final var chunkSize = (fileSizeBytes / N_THREADS) & -CalculateAverageTask.BATCH_SIZE_BYTES; for (int i = 1; i < chunkBounds.length - 1; i++) { chunkBounds[i] = chunkBounds[i - 1] + chunkSize; } return chunkBounds; } private static void printSorted(final HashTable temperatureMeasurements) { final var weatherStations = new AggregatedMeasurement[(int) temperatureMeasurements.size]; final var nameBuffer = new byte[WEATHER_STATION_LENGTH_MAX]; for (int i = 0; i < weatherStations.length; i++) { final var offset = temperatureMeasurements.getOffset(i); final var nameAddr = UNSAFE.getLong(offset); final var nameLength = UNSAFE.getInt(offset + Integer.BYTES * 7); MemorySegment.copy(ALL, ValueLayout.JAVA_BYTE, nameAddr, nameBuffer, 0, nameLength); final var nameStr = new String(nameBuffer, 0, nameLength, StandardCharsets.UTF_8); weatherStations[i] = new AggregatedMeasurement(nameStr, i); } Arrays.sort(weatherStations); System.out.print('{'); for (int i = 0; i < weatherStations.length - 1; i++) { printAggMeasurement(weatherStations[i], temperatureMeasurements); System.out.print(','); System.out.print(' '); } printAggMeasurement(weatherStations[weatherStations.length - 1], temperatureMeasurements); System.out.println('}'); } private static void printAggMeasurement(final AggregatedMeasurement aggMeasurement, final HashTable temperatureMeasurements) { final var offset = temperatureMeasurements.getOffset(aggMeasurement.id()); // name System.out.print(aggMeasurement.name()); System.out.print('='); // min printAsDouble(offset + Integer.BYTES * 5); System.out.print('/'); // mean final double total = UNSAFE.getLong(offset + Integer.BYTES * 2); final var count = UNSAFE.getInt(offset + Integer.BYTES * 4); System.out.print(round(total / count / 10d)); System.out.print('/'); // max printAsDouble(offset + Integer.BYTES * 6); } private static void printAsDouble(final long addr) { final var val = (double) UNSAFE.getInt(addr); System.out.print(val / 10d); } private static double round(final double d) { return Math.round(d * 10d) / 10d; } private static class CalculateAverageTask implements Runnable { public static final int BATCH_SIZE_BYTES = BYTE_SPECIES.vectorByteSize(); private final HashTable[] maps; private final long[] chunkBounds; private final long chunkStart; private final long chunkEnd; private final int t; private HashTable map; public CalculateAverageTask(HashTable[] maps, long[] chunkBounds, int t) { this.maps = maps; this.chunkBounds = chunkBounds; this.chunkStart = chunkBounds[t]; this.chunkEnd = chunkBounds[t + 1]; this.t = t; } @Override public void run() { this.maps[this.t] = new HashTable(); this.map = this.maps[this.t]; var lineStart = this.chunkBounds[0]; // walk back to find the previous '\n' and use it as lineStart for (long i = this.chunkStart - 1; i > this.chunkBounds[0]; i--) { if (UNSAFE.getByte(i) == (byte) '\n') { lineStart = i + 1L; break; } } final var vectorLimit = this.chunkStart + ((this.chunkEnd - this.chunkStart) & -BATCH_SIZE_BYTES); for (long i = this.chunkStart; i < vectorLimit; i += BATCH_SIZE_BYTES) { var lfMask = ByteVector.fromMemorySegment(BYTE_SPECIES, ALL, i, ByteOrder.nativeOrder()) .eq((byte) '\n') .toLong(); final var lfCount = Long.bitCount(lfMask); for (int j = 0; j < lfCount; j++) { final var lfPosRelative = Long.numberOfTrailingZeros(lfMask); final var lfAddress = i + lfPosRelative; processLine(lineStart, lfAddress); lineStart = lfAddress + 1L; // unset the lowest set bit, should compile to BLSR lfMask &= lfMask - 1L; } } if (vectorLimit != this.chunkEnd) { processTrailingBytes(lineStart, vectorLimit, this.chunkEnd); } } private void processTrailingBytes(long lineStart, final long start, final long end) { for (long i = start; i < end; i++) { final var b = UNSAFE.getByte(i); if (b != (byte) '\n') { continue; } processLine(lineStart, i); lineStart = i + 1; } } private void processLine(final long lineStart, final long lfAddress) { // read 5 bytes before '\n' // the temperature is formatted to 1 decimal place // therefore the shortest temperature value is 0.0 // so there are always at least 5 bytes between the location name and '\n' final var trailing5Bytes = UNSAFE.getLong(lfAddress - 5); final int trailingDWordRaw = (int) (trailing5Bytes >>> 8); // select the low nibble for each byte, '0'-'9' -> 0-9, ';' -> 11, '-' -> 13 final var trailingDWordLowNibble = trailingDWordRaw & 0x0f_0f_0f_0f; // parse the 2 digits around the decimal point (note that these 2 digits must be present) final var trailingDigitsParsed = (trailingDWordLowNibble * 0x00_0a_00_01) >>> 24; // this byte must be ('-' & 0xf), (';' & 0xf), or a valid digit (0-9) final var secondHighestByte = trailingDWordLowNibble & 0xf; var temperature = trailingDigitsParsed; var lineLength = lfAddress - lineStart - 4; if (secondHighestByte > 9) { if (secondHighestByte == ('-' & 0xf)) { lineLength--; temperature = -temperature; } } else { lineLength--; temperature += secondHighestByte * 100; final var isNegative = (trailing5Bytes & 0xffL) == '-'; if (isNegative) { lineLength--; temperature = -temperature; } } this.map.putEntry(lineStart, (int) lineLength, temperature); } } /** * Open addressing, linear probing hash map backed by off-heap memory */ private static class HashTable { private static final int TRUNCATED_HASH_BITS = 26; // max # of unique keys private static final long DENSE_SIZE = WEATHER_STATION_DISTINCT_MAX; // max hash code (exclusive) private static final long SPARSE_SIZE = 1L << (TRUNCATED_HASH_BITS + 1); public static final long SPARSE_SCALE = 32; public static final long DENSE_SCALE = 8; public final long sparseAddress; public final long denseAddress; public long size; public HashTable() { var arena = new MallocArena(Arena.global()); var callocArena = new CallocArena(Arena.global()); final var sparse = callocArena.allocate(ValueLayout.JAVA_BYTE, SPARSE_SIZE * SPARSE_SCALE); this.sparseAddress = (sparse.address() + MallocArena.MAX_ALIGN) & -MallocArena.MAX_ALIGN; final var dense = arena.allocate(ValueLayout.JAVA_BYTE, DENSE_SIZE * DENSE_SCALE); this.denseAddress = (dense.address() + MallocArena.MAX_ALIGN) & -MallocArena.MAX_ALIGN; } public long getOffset(final long index) { return UNSAFE.getLong(this.denseAddress + index * DENSE_SCALE); } public void putEntry(final long keyAddress, final int keyLength, final int value) { final var hash = hash(keyAddress, keyLength); this.putEntryInternal(hash, keyAddress, keyLength, value, 1, value, value); } private void putEntryInternal(final long hash, final long keyAddress, final int keyLength, final long temperature, final int count, final int temperatureMin, final int temperatureMax) { final var sparseOffset = this.sparseAddress + truncateHash(hash) * SPARSE_SCALE; for (long n = 0, sparseLinearOffset = sparseOffset; n < WEATHER_STATION_DISTINCT_MAX; n++, sparseLinearOffset += SPARSE_SCALE) { final var entryKeyAddress = UNSAFE.getLong(sparseLinearOffset); if (entryKeyAddress == 0L) { this.add(sparseLinearOffset, keyAddress, keyLength, temperature, count, temperatureMin, temperatureMax); this.size++; return; } if (mismatch(keyAddress, entryKeyAddress, keyLength)) { continue; } final var currMin = UNSAFE.getInt(sparseLinearOffset + Integer.BYTES * 5); final var currMax = UNSAFE.getInt(sparseLinearOffset + Integer.BYTES * 6); final var currTotal = UNSAFE.getLong(sparseLinearOffset + Integer.BYTES * 2); final var currCount = UNSAFE.getInt(sparseLinearOffset + Integer.BYTES * 4); UNSAFE.putLong(sparseLinearOffset + Integer.BYTES * 2, currTotal + temperature); UNSAFE.putInt(sparseLinearOffset + Integer.BYTES * 4, currCount + count); if (temperatureMin < currMin) { UNSAFE.putInt(sparseLinearOffset + Integer.BYTES * 5, temperatureMin); } if (temperatureMax > currMax) { UNSAFE.putInt(sparseLinearOffset + Integer.BYTES * 6, temperatureMax); } return; } } public void merge(final HashTable other) { final var otherSize = other.size; for (long i = 0; i < otherSize; i++) { final var offset = other.getOffset(i); final var keyAddress = UNSAFE.getLong(offset); final var keyLength = UNSAFE.getInt(offset + Integer.BYTES * 7); final var hash = hash(keyAddress, keyLength); this.putEntryInternal( hash, keyAddress, keyLength, UNSAFE.getLong(offset + Integer.BYTES * 2), UNSAFE.getInt(offset + Integer.BYTES * 4), UNSAFE.getInt(offset + Integer.BYTES * 5), UNSAFE.getInt(offset + Integer.BYTES * 6)); } } private void add(final long sparseOffset, final long keyAddress, final int keyLength, final long temperature, final int count, final int temperatureMin, final int temperatureMax) { // new entry, initialize sparse and dense final var denseOffset = this.denseAddress + this.size * DENSE_SCALE; UNSAFE.putLong(denseOffset, sparseOffset); UNSAFE.putLong(sparseOffset, keyAddress); UNSAFE.putLong(sparseOffset + Integer.BYTES * 2, temperature); UNSAFE.putInt(sparseOffset + Integer.BYTES * 4, count); UNSAFE.putInt(sparseOffset + Integer.BYTES * 5, temperatureMin); UNSAFE.putInt(sparseOffset + Integer.BYTES * 6, temperatureMax); UNSAFE.putInt(sparseOffset + Integer.BYTES * 7, keyLength); } private static boolean mismatch(final long leftAddr, final long rightAddr, final int length) { // key length compare is unnecessary // strings compared through delimiter byte ';' final var loopBound = length >= (BYTE_SPECIES.vectorByteSize() - 1) ? ((length + 1) & -BYTE_SPECIES.vectorByteSize()) : 0; for (long i = 0; i < loopBound; i += BYTE_SPECIES.vectorByteSize()) { final var l = ByteVector.fromMemorySegment(BYTE_SPECIES, ALL, leftAddr + i, ByteOrder.nativeOrder()); final var r = ByteVector.fromMemorySegment(BYTE_SPECIES, ALL, rightAddr + i, ByteOrder.nativeOrder()); if (!l.eq(r).allTrue()) { return true; } } final var l = ByteVector.fromMemorySegment(BYTE_SPECIES, ALL, leftAddr + loopBound, ByteOrder.nativeOrder()); final var r = ByteVector.fromMemorySegment(BYTE_SPECIES, ALL, rightAddr + loopBound, ByteOrder.nativeOrder()); final var eqMask = l.eq(r).toLong(); return Long.numberOfTrailingZeros(~eqMask) < ((length + 1) & (BYTE_SPECIES.vectorByteSize() - 1)); // to support platforms without TZCNT, the check can be replaced with // a comparison to lowestZero = ~eqMask & (eqMask + 1) } // Use the leading and trailing few bytes as hash // this performs better than computing a good hash private static long hash(final long keyAddress, final int keyLength) { final var leadingQWord = UNSAFE.getLong(keyAddress); // the constant is the 64 bit FNV-1 offset basis final var hash = -3750763034362895579L ^ leadingQWord; if (keyLength < Integer.BYTES) { // the key is at least 2 bytes (if you count the delimiter) return hash & 0xffffL; } else { final var trailingDWord = UNSAFE.getLong(keyAddress + keyLength - Integer.BYTES) & 0xffffffffL; // only the lower dword in hash is guaranteed to exist so shift left 32 return (hash << Integer.SIZE) ^ trailingDWord; } } private static long truncateHash(final long hash) { return ((hash >>> TRUNCATED_HASH_BITS) ^ hash) & ((1L << TRUNCATED_HASH_BITS) - 1L); } } private static class MallocArena implements Arena { public static final long MAX_ALIGN = 1L << 21; protected static final Linker LINKER = Linker.nativeLinker(); protected static final AddressLayout C_POINTER = (AddressLayout) LINKER.canonicalLayouts().get("void*"); protected static final ValueLayout C_SIZE_T = (ValueLayout) LINKER.canonicalLayouts().get("size_t"); private static final MethodHandle MALLOC = LINKER.downcallHandle( LINKER.defaultLookup().find("malloc").orElseThrow(), FunctionDescriptor.of(C_POINTER, C_SIZE_T), Linker.Option.critical(false)); private static final MethodHandle FREE = LINKER.downcallHandle( LINKER.defaultLookup().find("free").orElseThrow(), FunctionDescriptor.ofVoid(C_POINTER), Linker.Option.critical(false)); protected static final MethodHandle CALLOC = LINKER.downcallHandle( LINKER.defaultLookup().find("calloc").orElseThrow(), FunctionDescriptor.of(C_POINTER, C_SIZE_T, C_SIZE_T), Linker.Option.critical(false)); private final Arena arena; public MallocArena(Arena arena) { this.arena = arena; } @Override public MemorySegment allocate(final long byteSize, final long byteAlignment) { return malloc(byteSize + MAX_ALIGN).reinterpret(this, MallocArena::free); } @Override public MemorySegment.Scope scope() { return arena.scope(); } @Override public void close() { arena.close(); } private static MemorySegment malloc(final long byteSize) { try { return ((MemorySegment) MALLOC.invokeExact(byteSize)).reinterpret(byteSize); } catch (Throwable e) { throw new RuntimeException(e); } } protected static void free(final MemorySegment address) { try { FREE.invokeExact(address); } catch (Throwable e) { throw new RuntimeException(e); } } } private static class CallocArena extends MallocArena { public CallocArena(Arena arena) { super(arena); } @Override public MemorySegment allocate(final long byteSize, final long byteAlignment) { return calloc(byteSize + MAX_ALIGN).reinterpret(this, MallocArena::free); } private static MemorySegment calloc(final long byteSize) { try { return ((MemorySegment) MallocArena.CALLOC.invokeExact(1L, byteSize)).reinterpret(byteSize); } catch (Throwable e) { throw new RuntimeException(e); } } } private record AggregatedMeasurement(String name, long id) implements Comparable { @Override public int compareTo(final AggregatedMeasurement other) { return name.compareTo(other.name); } }} ================================================ FILE: src/main/python/create_measurements.py ================================================ #!/usr/bin/env python # # Copyright 2023 The original authors # # 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. # # Based on https://github.com/gunnarmorling/1brc/blob/main/src/main/java/dev/morling/onebrc/CreateMeasurements.java import os import sys import random import time def check_args(file_args): """ Sanity checks out input and prints out usage if input is not a positive integer """ try: if len(file_args) != 2 or int(file_args[1]) <= 0: raise Exception() except: print("Usage: create_measurements.sh ") print(" You can use underscore notation for large number of records.") print(" For example: 1_000_000_000 for one billion") exit() def build_weather_station_name_list(): """ Grabs the weather station names from example data provided in repo and dedups """ station_names = [] with open('../../../data/weather_stations.csv', 'r') as file: file_contents = file.read() for station in file_contents.splitlines(): if "#" in station: next else: station_names.append(station.split(';')[0]) return list(set(station_names)) def convert_bytes(num): """ Convert bytes to a human-readable format (e.g., KiB, MiB, GiB) """ for x in ['bytes', 'KiB', 'MiB', 'GiB']: if num < 1024.0: return "%3.1f %s" % (num, x) num /= 1024.0 def format_elapsed_time(seconds): """ Format elapsed time in a human-readable format """ if seconds < 60: return f"{seconds:.3f} seconds" elif seconds < 3600: minutes, seconds = divmod(seconds, 60) return f"{int(minutes)} minutes {int(seconds)} seconds" else: hours, remainder = divmod(seconds, 3600) minutes, seconds = divmod(remainder, 60) if minutes == 0: return f"{int(hours)} hours {int(seconds)} seconds" else: return f"{int(hours)} hours {int(minutes)} minutes {int(seconds)} seconds" def estimate_file_size(weather_station_names, num_rows_to_create): """ Tries to estimate how large a file the test data will be """ total_name_bytes = sum(len(s.encode("utf-8")) for s in weather_station_names) avg_name_bytes = total_name_bytes / float(len(weather_station_names)) # avg_temp_bytes = sum(len(str(n / 10.0)) for n in range(-999, 1000)) / 1999 avg_temp_bytes = 4.400200100050025 # add 2 for separator and newline avg_line_length = avg_name_bytes + avg_temp_bytes + 2 human_file_size = convert_bytes(num_rows_to_create * avg_line_length) return f"Estimated max file size is: {human_file_size}." def build_test_data(weather_station_names, num_rows_to_create): """ Generates and writes to file the requested length of test data """ start_time = time.time() coldest_temp = -99.9 hottest_temp = 99.9 station_names_10k_max = random.choices(weather_station_names, k=10_000) batch_size = 10000 # instead of writing line by line to file, process a batch of stations and put it to disk chunks = num_rows_to_create // batch_size print('Building test data...') try: with open("../../../data/measurements.txt", 'w') as file: progress = 0 for chunk in range(chunks): batch = random.choices(station_names_10k_max, k=batch_size) prepped_deviated_batch = '\n'.join([f"{station};{random.uniform(coldest_temp, hottest_temp):.1f}" for station in batch]) # :.1f should quicker than round on a large scale, because round utilizes mathematical operation file.write(prepped_deviated_batch + '\n') # Update progress bar every 1% if (chunk + 1) * 100 // chunks != progress: progress = (chunk + 1) * 100 // chunks bars = '=' * (progress // 2) sys.stdout.write(f"\r[{bars:<50}] {progress}%") sys.stdout.flush() sys.stdout.write('\n') except Exception as e: print("Something went wrong. Printing error info and exiting...") print(e) exit() end_time = time.time() elapsed_time = end_time - start_time file_size = os.path.getsize("../../../data/measurements.txt") human_file_size = convert_bytes(file_size) print("Test data successfully written to 1brc/data/measurements.txt") print(f"Actual file size: {human_file_size}") print(f"Elapsed time: {format_elapsed_time(elapsed_time)}") def main(): """ main program function """ check_args(sys.argv) num_rows_to_create = int(sys.argv[1]) weather_station_names = [] weather_station_names = build_weather_station_name_list() print(estimate_file_size(weather_station_names, num_rows_to_create)) build_test_data(weather_station_names, num_rows_to_create) print("Test data build complete.") if __name__ == "__main__": main() exit() ================================================ FILE: src/main/resources/.dontdelete ================================================ ================================================ FILE: src/test/resources/.dontdelete ================================================ ================================================ FILE: src/test/resources/samples/measurements-1.out ================================================ {Kunming=19.8/19.8/19.8} ================================================ FILE: src/test/resources/samples/measurements-1.txt ================================================ Kunming;19.8 ================================================ FILE: src/test/resources/samples/measurements-10.out ================================================ {Adelaide=15.0/15.0/15.0, Cabo San Lucas=14.9/14.9/14.9, Dodoma=22.2/22.2/22.2, Halifax=12.9/12.9/12.9, Karachi=15.4/15.4/15.4, Pittsburgh=9.7/9.7/9.7, Ségou=25.7/25.7/25.7, Tauranga=38.2/38.2/38.2, Xi'an=24.2/24.2/24.2, Zagreb=12.2/12.2/12.2} ================================================ FILE: src/test/resources/samples/measurements-10.txt ================================================ Halifax;12.9 Zagreb;12.2 Cabo San Lucas;14.9 Adelaide;15.0 Ségou;25.7 Pittsburgh;9.7 Karachi;15.4 Xi'an;24.2 Dodoma;22.2 Tauranga;38.2 ================================================ FILE: src/test/resources/samples/measurements-10000-unique-keys.out ================================================ {id1=1.0/1.0/1.0, id10=1.0/1.0/1.0, id100=1.0/1.0/1.0, id1000=1.0/1.0/1.0, id10000=1.0/1.0/1.0, id1001=1.0/1.0/1.0, id1002=1.0/1.0/1.0, id1003=1.0/1.0/1.0, id1004=1.0/1.0/1.0, id1005=1.0/1.0/1.0, id1006=1.0/1.0/1.0, id1007=1.0/1.0/1.0, id1008=1.0/1.0/1.0, id1009=1.0/1.0/1.0, id101=1.0/1.0/1.0, id1010=1.0/1.0/1.0, id1011=1.0/1.0/1.0, id1012=1.0/1.0/1.0, id1013=1.0/1.0/1.0, id1014=1.0/1.0/1.0, id1015=1.0/1.0/1.0, id1016=1.0/1.0/1.0, id1017=1.0/1.0/1.0, id1018=1.0/1.0/1.0, id1019=1.0/1.0/1.0, id102=1.0/1.0/1.0, id1020=1.0/1.0/1.0, id1021=1.0/1.0/1.0, id1022=1.0/1.0/1.0, id1023=1.0/1.0/1.0, id1024=1.0/1.0/1.0, id1025=1.0/1.0/1.0, id1026=1.0/1.0/1.0, id1027=1.0/1.0/1.0, id1028=1.0/1.0/1.0, id1029=1.0/1.0/1.0, id103=1.0/1.0/1.0, id1030=1.0/1.0/1.0, id1031=1.0/1.0/1.0, id1032=1.0/1.0/1.0, id1033=1.0/1.0/1.0, id1034=1.0/1.0/1.0, id1035=1.0/1.0/1.0, id1036=1.0/1.0/1.0, id1037=1.0/1.0/1.0, id1038=1.0/1.0/1.0, id1039=1.0/1.0/1.0, id104=1.0/1.0/1.0, id1040=1.0/1.0/1.0, id1041=1.0/1.0/1.0, id1042=1.0/1.0/1.0, id1043=1.0/1.0/1.0, id1044=1.0/1.0/1.0, id1045=1.0/1.0/1.0, id1046=1.0/1.0/1.0, id1047=1.0/1.0/1.0, id1048=1.0/1.0/1.0, id1049=1.0/1.0/1.0, id105=1.0/1.0/1.0, id1050=1.0/1.0/1.0, id1051=1.0/1.0/1.0, id1052=1.0/1.0/1.0, id1053=1.0/1.0/1.0, id1054=1.0/1.0/1.0, id1055=1.0/1.0/1.0, id1056=1.0/1.0/1.0, id1057=1.0/1.0/1.0, id1058=1.0/1.0/1.0, id1059=1.0/1.0/1.0, id106=1.0/1.0/1.0, id1060=1.0/1.0/1.0, id1061=1.0/1.0/1.0, id1062=1.0/1.0/1.0, id1063=1.0/1.0/1.0, id1064=1.0/1.0/1.0, id1065=1.0/1.0/1.0, id1066=1.0/1.0/1.0, id1067=1.0/1.0/1.0, id1068=1.0/1.0/1.0, id1069=1.0/1.0/1.0, id107=1.0/1.0/1.0, id1070=1.0/1.0/1.0, id1071=1.0/1.0/1.0, id1072=1.0/1.0/1.0, id1073=1.0/1.0/1.0, id1074=1.0/1.0/1.0, id1075=1.0/1.0/1.0, id1076=1.0/1.0/1.0, id1077=1.0/1.0/1.0, id1078=1.0/1.0/1.0, id1079=1.0/1.0/1.0, id108=1.0/1.0/1.0, id1080=1.0/1.0/1.0, id1081=1.0/1.0/1.0, id1082=1.0/1.0/1.0, id1083=1.0/1.0/1.0, id1084=1.0/1.0/1.0, id1085=1.0/1.0/1.0, id1086=1.0/1.0/1.0, id1087=1.0/1.0/1.0, id1088=1.0/1.0/1.0, id1089=1.0/1.0/1.0, id109=1.0/1.0/1.0, id1090=1.0/1.0/1.0, id1091=1.0/1.0/1.0, id1092=1.0/1.0/1.0, id1093=1.0/1.0/1.0, id1094=1.0/1.0/1.0, id1095=1.0/1.0/1.0, id1096=1.0/1.0/1.0, id1097=1.0/1.0/1.0, id1098=1.0/1.0/1.0, id1099=1.0/1.0/1.0, id11=1.0/1.0/1.0, id110=1.0/1.0/1.0, id1100=1.0/1.0/1.0, id1101=1.0/1.0/1.0, id1102=1.0/1.0/1.0, id1103=1.0/1.0/1.0, id1104=1.0/1.0/1.0, id1105=1.0/1.0/1.0, id1106=1.0/1.0/1.0, id1107=1.0/1.0/1.0, id1108=1.0/1.0/1.0, id1109=1.0/1.0/1.0, id111=1.0/1.0/1.0, id1110=1.0/1.0/1.0, id1111=1.0/1.0/1.0, id1112=1.0/1.0/1.0, id1113=1.0/1.0/1.0, id1114=1.0/1.0/1.0, id1115=1.0/1.0/1.0, id1116=1.0/1.0/1.0, id1117=1.0/1.0/1.0, id1118=1.0/1.0/1.0, id1119=1.0/1.0/1.0, id112=1.0/1.0/1.0, id1120=1.0/1.0/1.0, id1121=1.0/1.0/1.0, id1122=1.0/1.0/1.0, id1123=1.0/1.0/1.0, id1124=1.0/1.0/1.0, id1125=1.0/1.0/1.0, id1126=1.0/1.0/1.0, id1127=1.0/1.0/1.0, id1128=1.0/1.0/1.0, id1129=1.0/1.0/1.0, id113=1.0/1.0/1.0, id1130=1.0/1.0/1.0, id1131=1.0/1.0/1.0, id1132=1.0/1.0/1.0, id1133=1.0/1.0/1.0, id1134=1.0/1.0/1.0, id1135=1.0/1.0/1.0, id1136=1.0/1.0/1.0, id1137=1.0/1.0/1.0, id1138=1.0/1.0/1.0, id1139=1.0/1.0/1.0, id114=1.0/1.0/1.0, id1140=1.0/1.0/1.0, id1141=1.0/1.0/1.0, id1142=1.0/1.0/1.0, id1143=1.0/1.0/1.0, id1144=1.0/1.0/1.0, id1145=1.0/1.0/1.0, id1146=1.0/1.0/1.0, id1147=1.0/1.0/1.0, id1148=1.0/1.0/1.0, id1149=1.0/1.0/1.0, id115=1.0/1.0/1.0, id1150=1.0/1.0/1.0, id1151=1.0/1.0/1.0, id1152=1.0/1.0/1.0, id1153=1.0/1.0/1.0, id1154=1.0/1.0/1.0, id1155=1.0/1.0/1.0, id1156=1.0/1.0/1.0, id1157=1.0/1.0/1.0, id1158=1.0/1.0/1.0, id1159=1.0/1.0/1.0, id116=1.0/1.0/1.0, id1160=1.0/1.0/1.0, id1161=1.0/1.0/1.0, id1162=1.0/1.0/1.0, id1163=1.0/1.0/1.0, id1164=1.0/1.0/1.0, id1165=1.0/1.0/1.0, id1166=1.0/1.0/1.0, id1167=1.0/1.0/1.0, id1168=1.0/1.0/1.0, id1169=1.0/1.0/1.0, id117=1.0/1.0/1.0, id1170=1.0/1.0/1.0, id1171=1.0/1.0/1.0, id1172=1.0/1.0/1.0, id1173=1.0/1.0/1.0, id1174=1.0/1.0/1.0, id1175=1.0/1.0/1.0, id1176=1.0/1.0/1.0, id1177=1.0/1.0/1.0, id1178=1.0/1.0/1.0, id1179=1.0/1.0/1.0, id118=1.0/1.0/1.0, id1180=1.0/1.0/1.0, id1181=1.0/1.0/1.0, id1182=1.0/1.0/1.0, id1183=1.0/1.0/1.0, id1184=1.0/1.0/1.0, id1185=1.0/1.0/1.0, id1186=1.0/1.0/1.0, id1187=1.0/1.0/1.0, id1188=1.0/1.0/1.0, id1189=1.0/1.0/1.0, id119=1.0/1.0/1.0, id1190=1.0/1.0/1.0, id1191=1.0/1.0/1.0, id1192=1.0/1.0/1.0, id1193=1.0/1.0/1.0, id1194=1.0/1.0/1.0, id1195=1.0/1.0/1.0, id1196=1.0/1.0/1.0, id1197=1.0/1.0/1.0, id1198=1.0/1.0/1.0, id1199=1.0/1.0/1.0, id12=1.0/1.0/1.0, id120=1.0/1.0/1.0, id1200=1.0/1.0/1.0, id1201=1.0/1.0/1.0, id1202=1.0/1.0/1.0, id1203=1.0/1.0/1.0, id1204=1.0/1.0/1.0, id1205=1.0/1.0/1.0, id1206=1.0/1.0/1.0, id1207=1.0/1.0/1.0, id1208=1.0/1.0/1.0, id1209=1.0/1.0/1.0, id121=1.0/1.0/1.0, id1210=1.0/1.0/1.0, id1211=1.0/1.0/1.0, id1212=1.0/1.0/1.0, id1213=1.0/1.0/1.0, id1214=1.0/1.0/1.0, id1215=1.0/1.0/1.0, id1216=1.0/1.0/1.0, id1217=1.0/1.0/1.0, id1218=1.0/1.0/1.0, id1219=1.0/1.0/1.0, id122=1.0/1.0/1.0, id1220=1.0/1.0/1.0, id1221=1.0/1.0/1.0, id1222=1.0/1.0/1.0, id1223=1.0/1.0/1.0, id1224=1.0/1.0/1.0, id1225=1.0/1.0/1.0, id1226=1.0/1.0/1.0, id1227=1.0/1.0/1.0, id1228=1.0/1.0/1.0, id1229=1.0/1.0/1.0, id123=1.0/1.0/1.0, id1230=1.0/1.0/1.0, id1231=1.0/1.0/1.0, id1232=1.0/1.0/1.0, id1233=1.0/1.0/1.0, id1234=1.0/1.0/1.0, id1235=1.0/1.0/1.0, id1236=1.0/1.0/1.0, id1237=1.0/1.0/1.0, id1238=1.0/1.0/1.0, id1239=1.0/1.0/1.0, id124=1.0/1.0/1.0, id1240=1.0/1.0/1.0, id1241=1.0/1.0/1.0, id1242=1.0/1.0/1.0, id1243=1.0/1.0/1.0, id1244=1.0/1.0/1.0, id1245=1.0/1.0/1.0, id1246=1.0/1.0/1.0, id1247=1.0/1.0/1.0, id1248=1.0/1.0/1.0, id1249=1.0/1.0/1.0, id125=1.0/1.0/1.0, id1250=1.0/1.0/1.0, id1251=1.0/1.0/1.0, id1252=1.0/1.0/1.0, id1253=1.0/1.0/1.0, id1254=1.0/1.0/1.0, id1255=1.0/1.0/1.0, id1256=1.0/1.0/1.0, id1257=1.0/1.0/1.0, id1258=1.0/1.0/1.0, id1259=1.0/1.0/1.0, id126=1.0/1.0/1.0, id1260=1.0/1.0/1.0, id1261=1.0/1.0/1.0, id1262=1.0/1.0/1.0, id1263=1.0/1.0/1.0, id1264=1.0/1.0/1.0, id1265=1.0/1.0/1.0, id1266=1.0/1.0/1.0, id1267=1.0/1.0/1.0, id1268=1.0/1.0/1.0, id1269=1.0/1.0/1.0, id127=1.0/1.0/1.0, id1270=1.0/1.0/1.0, id1271=1.0/1.0/1.0, id1272=1.0/1.0/1.0, id1273=1.0/1.0/1.0, id1274=1.0/1.0/1.0, id1275=1.0/1.0/1.0, id1276=1.0/1.0/1.0, id1277=1.0/1.0/1.0, id1278=1.0/1.0/1.0, id1279=1.0/1.0/1.0, id128=1.0/1.0/1.0, id1280=1.0/1.0/1.0, id1281=1.0/1.0/1.0, id1282=1.0/1.0/1.0, id1283=1.0/1.0/1.0, id1284=1.0/1.0/1.0, id1285=1.0/1.0/1.0, id1286=1.0/1.0/1.0, id1287=1.0/1.0/1.0, id1288=1.0/1.0/1.0, id1289=1.0/1.0/1.0, id129=1.0/1.0/1.0, id1290=1.0/1.0/1.0, id1291=1.0/1.0/1.0, id1292=1.0/1.0/1.0, id1293=1.0/1.0/1.0, id1294=1.0/1.0/1.0, id1295=1.0/1.0/1.0, id1296=1.0/1.0/1.0, id1297=1.0/1.0/1.0, id1298=1.0/1.0/1.0, id1299=1.0/1.0/1.0, id13=1.0/1.0/1.0, id130=1.0/1.0/1.0, id1300=1.0/1.0/1.0, id1301=1.0/1.0/1.0, id1302=1.0/1.0/1.0, id1303=1.0/1.0/1.0, id1304=1.0/1.0/1.0, id1305=1.0/1.0/1.0, id1306=1.0/1.0/1.0, id1307=1.0/1.0/1.0, id1308=1.0/1.0/1.0, id1309=1.0/1.0/1.0, id131=1.0/1.0/1.0, id1310=1.0/1.0/1.0, id1311=1.0/1.0/1.0, id1312=1.0/1.0/1.0, id1313=1.0/1.0/1.0, id1314=1.0/1.0/1.0, id1315=1.0/1.0/1.0, id1316=1.0/1.0/1.0, id1317=1.0/1.0/1.0, id1318=1.0/1.0/1.0, id1319=1.0/1.0/1.0, id132=1.0/1.0/1.0, id1320=1.0/1.0/1.0, id1321=1.0/1.0/1.0, id1322=1.0/1.0/1.0, id1323=1.0/1.0/1.0, id1324=1.0/1.0/1.0, id1325=1.0/1.0/1.0, id1326=1.0/1.0/1.0, id1327=1.0/1.0/1.0, id1328=1.0/1.0/1.0, id1329=1.0/1.0/1.0, id133=1.0/1.0/1.0, id1330=1.0/1.0/1.0, id1331=1.0/1.0/1.0, id1332=1.0/1.0/1.0, id1333=1.0/1.0/1.0, id1334=1.0/1.0/1.0, id1335=1.0/1.0/1.0, id1336=1.0/1.0/1.0, id1337=1.0/1.0/1.0, id1338=1.0/1.0/1.0, id1339=1.0/1.0/1.0, id134=1.0/1.0/1.0, id1340=1.0/1.0/1.0, id1341=1.0/1.0/1.0, id1342=1.0/1.0/1.0, id1343=1.0/1.0/1.0, id1344=1.0/1.0/1.0, id1345=1.0/1.0/1.0, id1346=1.0/1.0/1.0, id1347=1.0/1.0/1.0, id1348=1.0/1.0/1.0, id1349=1.0/1.0/1.0, id135=1.0/1.0/1.0, id1350=1.0/1.0/1.0, id1351=1.0/1.0/1.0, id1352=1.0/1.0/1.0, id1353=1.0/1.0/1.0, id1354=1.0/1.0/1.0, id1355=1.0/1.0/1.0, id1356=1.0/1.0/1.0, id1357=1.0/1.0/1.0, id1358=1.0/1.0/1.0, id1359=1.0/1.0/1.0, id136=1.0/1.0/1.0, id1360=1.0/1.0/1.0, id1361=1.0/1.0/1.0, id1362=1.0/1.0/1.0, id1363=1.0/1.0/1.0, id1364=1.0/1.0/1.0, id1365=1.0/1.0/1.0, id1366=1.0/1.0/1.0, id1367=1.0/1.0/1.0, id1368=1.0/1.0/1.0, id1369=1.0/1.0/1.0, id137=1.0/1.0/1.0, id1370=1.0/1.0/1.0, id1371=1.0/1.0/1.0, id1372=1.0/1.0/1.0, id1373=1.0/1.0/1.0, id1374=1.0/1.0/1.0, id1375=1.0/1.0/1.0, id1376=1.0/1.0/1.0, id1377=1.0/1.0/1.0, id1378=1.0/1.0/1.0, id1379=1.0/1.0/1.0, id138=1.0/1.0/1.0, id1380=1.0/1.0/1.0, id1381=1.0/1.0/1.0, id1382=1.0/1.0/1.0, id1383=1.0/1.0/1.0, id1384=1.0/1.0/1.0, id1385=1.0/1.0/1.0, id1386=1.0/1.0/1.0, id1387=1.0/1.0/1.0, id1388=1.0/1.0/1.0, id1389=1.0/1.0/1.0, id139=1.0/1.0/1.0, id1390=1.0/1.0/1.0, id1391=1.0/1.0/1.0, id1392=1.0/1.0/1.0, id1393=1.0/1.0/1.0, id1394=1.0/1.0/1.0, id1395=1.0/1.0/1.0, id1396=1.0/1.0/1.0, id1397=1.0/1.0/1.0, id1398=1.0/1.0/1.0, id1399=1.0/1.0/1.0, id14=1.0/1.0/1.0, id140=1.0/1.0/1.0, id1400=1.0/1.0/1.0, id1401=1.0/1.0/1.0, id1402=1.0/1.0/1.0, id1403=1.0/1.0/1.0, id1404=1.0/1.0/1.0, id1405=1.0/1.0/1.0, id1406=1.0/1.0/1.0, id1407=1.0/1.0/1.0, id1408=1.0/1.0/1.0, id1409=1.0/1.0/1.0, id141=1.0/1.0/1.0, id1410=1.0/1.0/1.0, id1411=1.0/1.0/1.0, id1412=1.0/1.0/1.0, id1413=1.0/1.0/1.0, id1414=1.0/1.0/1.0, id1415=1.0/1.0/1.0, id1416=1.0/1.0/1.0, id1417=1.0/1.0/1.0, id1418=1.0/1.0/1.0, id1419=1.0/1.0/1.0, id142=1.0/1.0/1.0, id1420=1.0/1.0/1.0, id1421=1.0/1.0/1.0, id1422=1.0/1.0/1.0, id1423=1.0/1.0/1.0, id1424=1.0/1.0/1.0, id1425=1.0/1.0/1.0, id1426=1.0/1.0/1.0, id1427=1.0/1.0/1.0, id1428=1.0/1.0/1.0, id1429=1.0/1.0/1.0, id143=1.0/1.0/1.0, id1430=1.0/1.0/1.0, id1431=1.0/1.0/1.0, id1432=1.0/1.0/1.0, id1433=1.0/1.0/1.0, id1434=1.0/1.0/1.0, id1435=1.0/1.0/1.0, id1436=1.0/1.0/1.0, id1437=1.0/1.0/1.0, id1438=1.0/1.0/1.0, id1439=1.0/1.0/1.0, id144=1.0/1.0/1.0, id1440=1.0/1.0/1.0, id1441=1.0/1.0/1.0, id1442=1.0/1.0/1.0, id1443=1.0/1.0/1.0, id1444=1.0/1.0/1.0, id1445=1.0/1.0/1.0, id1446=1.0/1.0/1.0, id1447=1.0/1.0/1.0, id1448=1.0/1.0/1.0, id1449=1.0/1.0/1.0, id145=1.0/1.0/1.0, id1450=1.0/1.0/1.0, id1451=1.0/1.0/1.0, id1452=1.0/1.0/1.0, id1453=1.0/1.0/1.0, id1454=1.0/1.0/1.0, id1455=1.0/1.0/1.0, id1456=1.0/1.0/1.0, id1457=1.0/1.0/1.0, id1458=1.0/1.0/1.0, id1459=1.0/1.0/1.0, id146=1.0/1.0/1.0, id1460=1.0/1.0/1.0, id1461=1.0/1.0/1.0, id1462=1.0/1.0/1.0, id1463=1.0/1.0/1.0, id1464=1.0/1.0/1.0, id1465=1.0/1.0/1.0, id1466=1.0/1.0/1.0, id1467=1.0/1.0/1.0, id1468=1.0/1.0/1.0, id1469=1.0/1.0/1.0, id147=1.0/1.0/1.0, id1470=1.0/1.0/1.0, id1471=1.0/1.0/1.0, id1472=1.0/1.0/1.0, id1473=1.0/1.0/1.0, id1474=1.0/1.0/1.0, id1475=1.0/1.0/1.0, id1476=1.0/1.0/1.0, id1477=1.0/1.0/1.0, id1478=1.0/1.0/1.0, id1479=1.0/1.0/1.0, id148=1.0/1.0/1.0, id1480=1.0/1.0/1.0, id1481=1.0/1.0/1.0, id1482=1.0/1.0/1.0, id1483=1.0/1.0/1.0, id1484=1.0/1.0/1.0, id1485=1.0/1.0/1.0, id1486=1.0/1.0/1.0, id1487=1.0/1.0/1.0, id1488=1.0/1.0/1.0, id1489=1.0/1.0/1.0, id149=1.0/1.0/1.0, id1490=1.0/1.0/1.0, id1491=1.0/1.0/1.0, id1492=1.0/1.0/1.0, id1493=1.0/1.0/1.0, id1494=1.0/1.0/1.0, id1495=1.0/1.0/1.0, id1496=1.0/1.0/1.0, id1497=1.0/1.0/1.0, id1498=1.0/1.0/1.0, id1499=1.0/1.0/1.0, id15=1.0/1.0/1.0, id150=1.0/1.0/1.0, id1500=1.0/1.0/1.0, id1501=1.0/1.0/1.0, id1502=1.0/1.0/1.0, id1503=1.0/1.0/1.0, id1504=1.0/1.0/1.0, id1505=1.0/1.0/1.0, id1506=1.0/1.0/1.0, id1507=1.0/1.0/1.0, id1508=1.0/1.0/1.0, id1509=1.0/1.0/1.0, id151=1.0/1.0/1.0, id1510=1.0/1.0/1.0, id1511=1.0/1.0/1.0, id1512=1.0/1.0/1.0, id1513=1.0/1.0/1.0, id1514=1.0/1.0/1.0, id1515=1.0/1.0/1.0, id1516=1.0/1.0/1.0, id1517=1.0/1.0/1.0, id1518=1.0/1.0/1.0, id1519=1.0/1.0/1.0, id152=1.0/1.0/1.0, id1520=1.0/1.0/1.0, id1521=1.0/1.0/1.0, id1522=1.0/1.0/1.0, id1523=1.0/1.0/1.0, id1524=1.0/1.0/1.0, id1525=1.0/1.0/1.0, id1526=1.0/1.0/1.0, id1527=1.0/1.0/1.0, id1528=1.0/1.0/1.0, id1529=1.0/1.0/1.0, id153=1.0/1.0/1.0, id1530=1.0/1.0/1.0, id1531=1.0/1.0/1.0, id1532=1.0/1.0/1.0, id1533=1.0/1.0/1.0, id1534=1.0/1.0/1.0, id1535=1.0/1.0/1.0, id1536=1.0/1.0/1.0, id1537=1.0/1.0/1.0, id1538=1.0/1.0/1.0, id1539=1.0/1.0/1.0, id154=1.0/1.0/1.0, id1540=1.0/1.0/1.0, id1541=1.0/1.0/1.0, id1542=1.0/1.0/1.0, id1543=1.0/1.0/1.0, id1544=1.0/1.0/1.0, id1545=1.0/1.0/1.0, id1546=1.0/1.0/1.0, id1547=1.0/1.0/1.0, id1548=1.0/1.0/1.0, id1549=1.0/1.0/1.0, id155=1.0/1.0/1.0, id1550=1.0/1.0/1.0, id1551=1.0/1.0/1.0, id1552=1.0/1.0/1.0, id1553=1.0/1.0/1.0, id1554=1.0/1.0/1.0, id1555=1.0/1.0/1.0, id1556=1.0/1.0/1.0, id1557=1.0/1.0/1.0, id1558=1.0/1.0/1.0, id1559=1.0/1.0/1.0, id156=1.0/1.0/1.0, id1560=1.0/1.0/1.0, id1561=1.0/1.0/1.0, id1562=1.0/1.0/1.0, id1563=1.0/1.0/1.0, id1564=1.0/1.0/1.0, id1565=1.0/1.0/1.0, id1566=1.0/1.0/1.0, id1567=1.0/1.0/1.0, id1568=1.0/1.0/1.0, id1569=1.0/1.0/1.0, id157=1.0/1.0/1.0, id1570=1.0/1.0/1.0, id1571=1.0/1.0/1.0, id1572=1.0/1.0/1.0, id1573=1.0/1.0/1.0, id1574=1.0/1.0/1.0, id1575=1.0/1.0/1.0, id1576=1.0/1.0/1.0, id1577=1.0/1.0/1.0, id1578=1.0/1.0/1.0, id1579=1.0/1.0/1.0, id158=1.0/1.0/1.0, id1580=1.0/1.0/1.0, id1581=1.0/1.0/1.0, id1582=1.0/1.0/1.0, id1583=1.0/1.0/1.0, id1584=1.0/1.0/1.0, id1585=1.0/1.0/1.0, id1586=1.0/1.0/1.0, id1587=1.0/1.0/1.0, id1588=1.0/1.0/1.0, id1589=1.0/1.0/1.0, id159=1.0/1.0/1.0, id1590=1.0/1.0/1.0, id1591=1.0/1.0/1.0, id1592=1.0/1.0/1.0, id1593=1.0/1.0/1.0, id1594=1.0/1.0/1.0, id1595=1.0/1.0/1.0, id1596=1.0/1.0/1.0, id1597=1.0/1.0/1.0, id1598=1.0/1.0/1.0, id1599=1.0/1.0/1.0, id16=1.0/1.0/1.0, id160=1.0/1.0/1.0, id1600=1.0/1.0/1.0, id1601=1.0/1.0/1.0, id1602=1.0/1.0/1.0, id1603=1.0/1.0/1.0, id1604=1.0/1.0/1.0, id1605=1.0/1.0/1.0, id1606=1.0/1.0/1.0, id1607=1.0/1.0/1.0, id1608=1.0/1.0/1.0, id1609=1.0/1.0/1.0, id161=1.0/1.0/1.0, id1610=1.0/1.0/1.0, id1611=1.0/1.0/1.0, id1612=1.0/1.0/1.0, id1613=1.0/1.0/1.0, id1614=1.0/1.0/1.0, id1615=1.0/1.0/1.0, id1616=1.0/1.0/1.0, id1617=1.0/1.0/1.0, id1618=1.0/1.0/1.0, id1619=1.0/1.0/1.0, id162=1.0/1.0/1.0, id1620=1.0/1.0/1.0, id1621=1.0/1.0/1.0, id1622=1.0/1.0/1.0, id1623=1.0/1.0/1.0, id1624=1.0/1.0/1.0, id1625=1.0/1.0/1.0, id1626=1.0/1.0/1.0, id1627=1.0/1.0/1.0, id1628=1.0/1.0/1.0, id1629=1.0/1.0/1.0, id163=1.0/1.0/1.0, id1630=1.0/1.0/1.0, id1631=1.0/1.0/1.0, id1632=1.0/1.0/1.0, id1633=1.0/1.0/1.0, id1634=1.0/1.0/1.0, id1635=1.0/1.0/1.0, id1636=1.0/1.0/1.0, id1637=1.0/1.0/1.0, id1638=1.0/1.0/1.0, id1639=1.0/1.0/1.0, id164=1.0/1.0/1.0, id1640=1.0/1.0/1.0, id1641=1.0/1.0/1.0, id1642=1.0/1.0/1.0, id1643=1.0/1.0/1.0, id1644=1.0/1.0/1.0, id1645=1.0/1.0/1.0, id1646=1.0/1.0/1.0, id1647=1.0/1.0/1.0, id1648=1.0/1.0/1.0, id1649=1.0/1.0/1.0, id165=1.0/1.0/1.0, id1650=1.0/1.0/1.0, id1651=1.0/1.0/1.0, id1652=1.0/1.0/1.0, id1653=1.0/1.0/1.0, id1654=1.0/1.0/1.0, id1655=1.0/1.0/1.0, id1656=1.0/1.0/1.0, id1657=1.0/1.0/1.0, id1658=1.0/1.0/1.0, id1659=1.0/1.0/1.0, id166=1.0/1.0/1.0, id1660=1.0/1.0/1.0, id1661=1.0/1.0/1.0, id1662=1.0/1.0/1.0, id1663=1.0/1.0/1.0, id1664=1.0/1.0/1.0, id1665=1.0/1.0/1.0, id1666=1.0/1.0/1.0, id1667=1.0/1.0/1.0, id1668=1.0/1.0/1.0, id1669=1.0/1.0/1.0, id167=1.0/1.0/1.0, id1670=1.0/1.0/1.0, id1671=1.0/1.0/1.0, id1672=1.0/1.0/1.0, id1673=1.0/1.0/1.0, id1674=1.0/1.0/1.0, id1675=1.0/1.0/1.0, id1676=1.0/1.0/1.0, id1677=1.0/1.0/1.0, id1678=1.0/1.0/1.0, id1679=1.0/1.0/1.0, id168=1.0/1.0/1.0, id1680=1.0/1.0/1.0, id1681=1.0/1.0/1.0, id1682=1.0/1.0/1.0, id1683=1.0/1.0/1.0, id1684=1.0/1.0/1.0, id1685=1.0/1.0/1.0, id1686=1.0/1.0/1.0, id1687=1.0/1.0/1.0, id1688=1.0/1.0/1.0, id1689=1.0/1.0/1.0, id169=1.0/1.0/1.0, id1690=1.0/1.0/1.0, id1691=1.0/1.0/1.0, id1692=1.0/1.0/1.0, id1693=1.0/1.0/1.0, id1694=1.0/1.0/1.0, id1695=1.0/1.0/1.0, id1696=1.0/1.0/1.0, id1697=1.0/1.0/1.0, id1698=1.0/1.0/1.0, id1699=1.0/1.0/1.0, id17=1.0/1.0/1.0, id170=1.0/1.0/1.0, id1700=1.0/1.0/1.0, id1701=1.0/1.0/1.0, id1702=1.0/1.0/1.0, id1703=1.0/1.0/1.0, id1704=1.0/1.0/1.0, id1705=1.0/1.0/1.0, id1706=1.0/1.0/1.0, id1707=1.0/1.0/1.0, id1708=1.0/1.0/1.0, id1709=1.0/1.0/1.0, id171=1.0/1.0/1.0, id1710=1.0/1.0/1.0, id1711=1.0/1.0/1.0, id1712=1.0/1.0/1.0, id1713=1.0/1.0/1.0, id1714=1.0/1.0/1.0, id1715=1.0/1.0/1.0, id1716=1.0/1.0/1.0, id1717=1.0/1.0/1.0, id1718=1.0/1.0/1.0, id1719=1.0/1.0/1.0, id172=1.0/1.0/1.0, id1720=1.0/1.0/1.0, id1721=1.0/1.0/1.0, id1722=1.0/1.0/1.0, id1723=1.0/1.0/1.0, id1724=1.0/1.0/1.0, id1725=1.0/1.0/1.0, id1726=1.0/1.0/1.0, id1727=1.0/1.0/1.0, id1728=1.0/1.0/1.0, id1729=1.0/1.0/1.0, id173=1.0/1.0/1.0, id1730=1.0/1.0/1.0, id1731=1.0/1.0/1.0, id1732=1.0/1.0/1.0, id1733=1.0/1.0/1.0, id1734=1.0/1.0/1.0, id1735=1.0/1.0/1.0, id1736=1.0/1.0/1.0, id1737=1.0/1.0/1.0, id1738=1.0/1.0/1.0, id1739=1.0/1.0/1.0, id174=1.0/1.0/1.0, id1740=1.0/1.0/1.0, id1741=1.0/1.0/1.0, id1742=1.0/1.0/1.0, id1743=1.0/1.0/1.0, id1744=1.0/1.0/1.0, id1745=1.0/1.0/1.0, id1746=1.0/1.0/1.0, id1747=1.0/1.0/1.0, id1748=1.0/1.0/1.0, id1749=1.0/1.0/1.0, id175=1.0/1.0/1.0, id1750=1.0/1.0/1.0, id1751=1.0/1.0/1.0, id1752=1.0/1.0/1.0, id1753=1.0/1.0/1.0, id1754=1.0/1.0/1.0, id1755=1.0/1.0/1.0, id1756=1.0/1.0/1.0, id1757=1.0/1.0/1.0, id1758=1.0/1.0/1.0, id1759=1.0/1.0/1.0, id176=1.0/1.0/1.0, id1760=1.0/1.0/1.0, id1761=1.0/1.0/1.0, id1762=1.0/1.0/1.0, id1763=1.0/1.0/1.0, id1764=1.0/1.0/1.0, id1765=1.0/1.0/1.0, id1766=1.0/1.0/1.0, id1767=1.0/1.0/1.0, id1768=1.0/1.0/1.0, id1769=1.0/1.0/1.0, id177=1.0/1.0/1.0, id1770=1.0/1.0/1.0, id1771=1.0/1.0/1.0, id1772=1.0/1.0/1.0, id1773=1.0/1.0/1.0, id1774=1.0/1.0/1.0, id1775=1.0/1.0/1.0, id1776=1.0/1.0/1.0, id1777=1.0/1.0/1.0, id1778=1.0/1.0/1.0, id1779=1.0/1.0/1.0, id178=1.0/1.0/1.0, id1780=1.0/1.0/1.0, id1781=1.0/1.0/1.0, id1782=1.0/1.0/1.0, id1783=1.0/1.0/1.0, id1784=1.0/1.0/1.0, id1785=1.0/1.0/1.0, id1786=1.0/1.0/1.0, id1787=1.0/1.0/1.0, id1788=1.0/1.0/1.0, id1789=1.0/1.0/1.0, id179=1.0/1.0/1.0, id1790=1.0/1.0/1.0, id1791=1.0/1.0/1.0, id1792=1.0/1.0/1.0, id1793=1.0/1.0/1.0, id1794=1.0/1.0/1.0, id1795=1.0/1.0/1.0, id1796=1.0/1.0/1.0, id1797=1.0/1.0/1.0, id1798=1.0/1.0/1.0, id1799=1.0/1.0/1.0, id18=1.0/1.0/1.0, id180=1.0/1.0/1.0, id1800=1.0/1.0/1.0, id1801=1.0/1.0/1.0, id1802=1.0/1.0/1.0, id1803=1.0/1.0/1.0, id1804=1.0/1.0/1.0, id1805=1.0/1.0/1.0, id1806=1.0/1.0/1.0, id1807=1.0/1.0/1.0, id1808=1.0/1.0/1.0, id1809=1.0/1.0/1.0, id181=1.0/1.0/1.0, id1810=1.0/1.0/1.0, id1811=1.0/1.0/1.0, id1812=1.0/1.0/1.0, id1813=1.0/1.0/1.0, id1814=1.0/1.0/1.0, id1815=1.0/1.0/1.0, id1816=1.0/1.0/1.0, id1817=1.0/1.0/1.0, id1818=1.0/1.0/1.0, id1819=1.0/1.0/1.0, id182=1.0/1.0/1.0, id1820=1.0/1.0/1.0, id1821=1.0/1.0/1.0, id1822=1.0/1.0/1.0, id1823=1.0/1.0/1.0, id1824=1.0/1.0/1.0, id1825=1.0/1.0/1.0, id1826=1.0/1.0/1.0, id1827=1.0/1.0/1.0, id1828=1.0/1.0/1.0, id1829=1.0/1.0/1.0, id183=1.0/1.0/1.0, id1830=1.0/1.0/1.0, id1831=1.0/1.0/1.0, id1832=1.0/1.0/1.0, id1833=1.0/1.0/1.0, id1834=1.0/1.0/1.0, id1835=1.0/1.0/1.0, id1836=1.0/1.0/1.0, id1837=1.0/1.0/1.0, id1838=1.0/1.0/1.0, id1839=1.0/1.0/1.0, id184=1.0/1.0/1.0, id1840=1.0/1.0/1.0, id1841=1.0/1.0/1.0, id1842=1.0/1.0/1.0, id1843=1.0/1.0/1.0, id1844=1.0/1.0/1.0, id1845=1.0/1.0/1.0, id1846=1.0/1.0/1.0, id1847=1.0/1.0/1.0, id1848=1.0/1.0/1.0, id1849=1.0/1.0/1.0, id185=1.0/1.0/1.0, id1850=1.0/1.0/1.0, id1851=1.0/1.0/1.0, id1852=1.0/1.0/1.0, id1853=1.0/1.0/1.0, id1854=1.0/1.0/1.0, id1855=1.0/1.0/1.0, id1856=1.0/1.0/1.0, id1857=1.0/1.0/1.0, id1858=1.0/1.0/1.0, id1859=1.0/1.0/1.0, id186=1.0/1.0/1.0, id1860=1.0/1.0/1.0, id1861=1.0/1.0/1.0, id1862=1.0/1.0/1.0, id1863=1.0/1.0/1.0, id1864=1.0/1.0/1.0, id1865=1.0/1.0/1.0, id1866=1.0/1.0/1.0, id1867=1.0/1.0/1.0, id1868=1.0/1.0/1.0, id1869=1.0/1.0/1.0, id187=1.0/1.0/1.0, id1870=1.0/1.0/1.0, id1871=1.0/1.0/1.0, id1872=1.0/1.0/1.0, id1873=1.0/1.0/1.0, id1874=1.0/1.0/1.0, id1875=1.0/1.0/1.0, id1876=1.0/1.0/1.0, id1877=1.0/1.0/1.0, id1878=1.0/1.0/1.0, id1879=1.0/1.0/1.0, id188=1.0/1.0/1.0, id1880=1.0/1.0/1.0, id1881=1.0/1.0/1.0, id1882=1.0/1.0/1.0, id1883=1.0/1.0/1.0, id1884=1.0/1.0/1.0, id1885=1.0/1.0/1.0, id1886=1.0/1.0/1.0, id1887=1.0/1.0/1.0, id1888=1.0/1.0/1.0, id1889=1.0/1.0/1.0, id189=1.0/1.0/1.0, id1890=1.0/1.0/1.0, id1891=1.0/1.0/1.0, id1892=1.0/1.0/1.0, id1893=1.0/1.0/1.0, id1894=1.0/1.0/1.0, id1895=1.0/1.0/1.0, id1896=1.0/1.0/1.0, id1897=1.0/1.0/1.0, id1898=1.0/1.0/1.0, id1899=1.0/1.0/1.0, id19=1.0/1.0/1.0, id190=1.0/1.0/1.0, id1900=1.0/1.0/1.0, id1901=1.0/1.0/1.0, id1902=1.0/1.0/1.0, id1903=1.0/1.0/1.0, id1904=1.0/1.0/1.0, id1905=1.0/1.0/1.0, id1906=1.0/1.0/1.0, id1907=1.0/1.0/1.0, id1908=1.0/1.0/1.0, id1909=1.0/1.0/1.0, id191=1.0/1.0/1.0, id1910=1.0/1.0/1.0, id1911=1.0/1.0/1.0, id1912=1.0/1.0/1.0, id1913=1.0/1.0/1.0, id1914=1.0/1.0/1.0, id1915=1.0/1.0/1.0, id1916=1.0/1.0/1.0, id1917=1.0/1.0/1.0, id1918=1.0/1.0/1.0, id1919=1.0/1.0/1.0, id192=1.0/1.0/1.0, id1920=1.0/1.0/1.0, id1921=1.0/1.0/1.0, id1922=1.0/1.0/1.0, id1923=1.0/1.0/1.0, id1924=1.0/1.0/1.0, id1925=1.0/1.0/1.0, id1926=1.0/1.0/1.0, id1927=1.0/1.0/1.0, id1928=1.0/1.0/1.0, id1929=1.0/1.0/1.0, id193=1.0/1.0/1.0, id1930=1.0/1.0/1.0, id1931=1.0/1.0/1.0, id1932=1.0/1.0/1.0, id1933=1.0/1.0/1.0, id1934=1.0/1.0/1.0, id1935=1.0/1.0/1.0, id1936=1.0/1.0/1.0, id1937=1.0/1.0/1.0, id1938=1.0/1.0/1.0, id1939=1.0/1.0/1.0, id194=1.0/1.0/1.0, id1940=1.0/1.0/1.0, id1941=1.0/1.0/1.0, id1942=1.0/1.0/1.0, id1943=1.0/1.0/1.0, id1944=1.0/1.0/1.0, id1945=1.0/1.0/1.0, id1946=1.0/1.0/1.0, id1947=1.0/1.0/1.0, id1948=1.0/1.0/1.0, id1949=1.0/1.0/1.0, id195=1.0/1.0/1.0, id1950=1.0/1.0/1.0, id1951=1.0/1.0/1.0, id1952=1.0/1.0/1.0, id1953=1.0/1.0/1.0, id1954=1.0/1.0/1.0, id1955=1.0/1.0/1.0, id1956=1.0/1.0/1.0, id1957=1.0/1.0/1.0, id1958=1.0/1.0/1.0, id1959=1.0/1.0/1.0, id196=1.0/1.0/1.0, id1960=1.0/1.0/1.0, id1961=1.0/1.0/1.0, id1962=1.0/1.0/1.0, id1963=1.0/1.0/1.0, id1964=1.0/1.0/1.0, id1965=1.0/1.0/1.0, id1966=1.0/1.0/1.0, id1967=1.0/1.0/1.0, id1968=1.0/1.0/1.0, id1969=1.0/1.0/1.0, id197=1.0/1.0/1.0, id1970=1.0/1.0/1.0, id1971=1.0/1.0/1.0, id1972=1.0/1.0/1.0, id1973=1.0/1.0/1.0, id1974=1.0/1.0/1.0, id1975=1.0/1.0/1.0, id1976=1.0/1.0/1.0, id1977=1.0/1.0/1.0, id1978=1.0/1.0/1.0, id1979=1.0/1.0/1.0, id198=1.0/1.0/1.0, id1980=1.0/1.0/1.0, id1981=1.0/1.0/1.0, id1982=1.0/1.0/1.0, id1983=1.0/1.0/1.0, id1984=1.0/1.0/1.0, id1985=1.0/1.0/1.0, id1986=1.0/1.0/1.0, id1987=1.0/1.0/1.0, id1988=1.0/1.0/1.0, id1989=1.0/1.0/1.0, id199=1.0/1.0/1.0, id1990=1.0/1.0/1.0, id1991=1.0/1.0/1.0, id1992=1.0/1.0/1.0, id1993=1.0/1.0/1.0, id1994=1.0/1.0/1.0, id1995=1.0/1.0/1.0, id1996=1.0/1.0/1.0, id1997=1.0/1.0/1.0, id1998=1.0/1.0/1.0, id1999=1.0/1.0/1.0, id2=1.0/1.0/1.0, id20=1.0/1.0/1.0, id200=1.0/1.0/1.0, id2000=1.0/1.0/1.0, id2001=1.0/1.0/1.0, id2002=1.0/1.0/1.0, id2003=1.0/1.0/1.0, id2004=1.0/1.0/1.0, id2005=1.0/1.0/1.0, id2006=1.0/1.0/1.0, id2007=1.0/1.0/1.0, id2008=1.0/1.0/1.0, id2009=1.0/1.0/1.0, id201=1.0/1.0/1.0, id2010=1.0/1.0/1.0, id2011=1.0/1.0/1.0, id2012=1.0/1.0/1.0, id2013=1.0/1.0/1.0, id2014=1.0/1.0/1.0, id2015=1.0/1.0/1.0, id2016=1.0/1.0/1.0, id2017=1.0/1.0/1.0, id2018=1.0/1.0/1.0, id2019=1.0/1.0/1.0, id202=1.0/1.0/1.0, id2020=1.0/1.0/1.0, id2021=1.0/1.0/1.0, id2022=1.0/1.0/1.0, id2023=1.0/1.0/1.0, id2024=1.0/1.0/1.0, id2025=1.0/1.0/1.0, id2026=1.0/1.0/1.0, id2027=1.0/1.0/1.0, id2028=1.0/1.0/1.0, id2029=1.0/1.0/1.0, id203=1.0/1.0/1.0, id2030=1.0/1.0/1.0, id2031=1.0/1.0/1.0, id2032=1.0/1.0/1.0, id2033=1.0/1.0/1.0, id2034=1.0/1.0/1.0, id2035=1.0/1.0/1.0, id2036=1.0/1.0/1.0, id2037=1.0/1.0/1.0, id2038=1.0/1.0/1.0, id2039=1.0/1.0/1.0, id204=1.0/1.0/1.0, id2040=1.0/1.0/1.0, id2041=1.0/1.0/1.0, id2042=1.0/1.0/1.0, id2043=1.0/1.0/1.0, id2044=1.0/1.0/1.0, id2045=1.0/1.0/1.0, id2046=1.0/1.0/1.0, id2047=1.0/1.0/1.0, id2048=1.0/1.0/1.0, id2049=1.0/1.0/1.0, id205=1.0/1.0/1.0, id2050=1.0/1.0/1.0, id2051=1.0/1.0/1.0, id2052=1.0/1.0/1.0, id2053=1.0/1.0/1.0, id2054=1.0/1.0/1.0, id2055=1.0/1.0/1.0, id2056=1.0/1.0/1.0, id2057=1.0/1.0/1.0, id2058=1.0/1.0/1.0, id2059=1.0/1.0/1.0, id206=1.0/1.0/1.0, id2060=1.0/1.0/1.0, id2061=1.0/1.0/1.0, id2062=1.0/1.0/1.0, id2063=1.0/1.0/1.0, id2064=1.0/1.0/1.0, id2065=1.0/1.0/1.0, id2066=1.0/1.0/1.0, id2067=1.0/1.0/1.0, id2068=1.0/1.0/1.0, id2069=1.0/1.0/1.0, id207=1.0/1.0/1.0, id2070=1.0/1.0/1.0, id2071=1.0/1.0/1.0, id2072=1.0/1.0/1.0, id2073=1.0/1.0/1.0, id2074=1.0/1.0/1.0, id2075=1.0/1.0/1.0, id2076=1.0/1.0/1.0, id2077=1.0/1.0/1.0, id2078=1.0/1.0/1.0, id2079=1.0/1.0/1.0, id208=1.0/1.0/1.0, id2080=1.0/1.0/1.0, id2081=1.0/1.0/1.0, id2082=1.0/1.0/1.0, id2083=1.0/1.0/1.0, id2084=1.0/1.0/1.0, id2085=1.0/1.0/1.0, id2086=1.0/1.0/1.0, id2087=1.0/1.0/1.0, id2088=1.0/1.0/1.0, id2089=1.0/1.0/1.0, id209=1.0/1.0/1.0, id2090=1.0/1.0/1.0, id2091=1.0/1.0/1.0, id2092=1.0/1.0/1.0, id2093=1.0/1.0/1.0, id2094=1.0/1.0/1.0, id2095=1.0/1.0/1.0, id2096=1.0/1.0/1.0, id2097=1.0/1.0/1.0, id2098=1.0/1.0/1.0, id2099=1.0/1.0/1.0, id21=1.0/1.0/1.0, id210=1.0/1.0/1.0, id2100=1.0/1.0/1.0, id2101=1.0/1.0/1.0, id2102=1.0/1.0/1.0, id2103=1.0/1.0/1.0, id2104=1.0/1.0/1.0, id2105=1.0/1.0/1.0, id2106=1.0/1.0/1.0, id2107=1.0/1.0/1.0, id2108=1.0/1.0/1.0, id2109=1.0/1.0/1.0, id211=1.0/1.0/1.0, id2110=1.0/1.0/1.0, id2111=1.0/1.0/1.0, id2112=1.0/1.0/1.0, id2113=1.0/1.0/1.0, id2114=1.0/1.0/1.0, id2115=1.0/1.0/1.0, id2116=1.0/1.0/1.0, id2117=1.0/1.0/1.0, id2118=1.0/1.0/1.0, id2119=1.0/1.0/1.0, id212=1.0/1.0/1.0, id2120=1.0/1.0/1.0, id2121=1.0/1.0/1.0, id2122=1.0/1.0/1.0, id2123=1.0/1.0/1.0, id2124=1.0/1.0/1.0, id2125=1.0/1.0/1.0, id2126=1.0/1.0/1.0, id2127=1.0/1.0/1.0, id2128=1.0/1.0/1.0, id2129=1.0/1.0/1.0, id213=1.0/1.0/1.0, id2130=1.0/1.0/1.0, id2131=1.0/1.0/1.0, id2132=1.0/1.0/1.0, id2133=1.0/1.0/1.0, id2134=1.0/1.0/1.0, id2135=1.0/1.0/1.0, id2136=1.0/1.0/1.0, id2137=1.0/1.0/1.0, id2138=1.0/1.0/1.0, id2139=1.0/1.0/1.0, id214=1.0/1.0/1.0, id2140=1.0/1.0/1.0, id2141=1.0/1.0/1.0, id2142=1.0/1.0/1.0, id2143=1.0/1.0/1.0, id2144=1.0/1.0/1.0, id2145=1.0/1.0/1.0, id2146=1.0/1.0/1.0, id2147=1.0/1.0/1.0, id2148=1.0/1.0/1.0, id2149=1.0/1.0/1.0, id215=1.0/1.0/1.0, id2150=1.0/1.0/1.0, id2151=1.0/1.0/1.0, id2152=1.0/1.0/1.0, id2153=1.0/1.0/1.0, id2154=1.0/1.0/1.0, id2155=1.0/1.0/1.0, id2156=1.0/1.0/1.0, id2157=1.0/1.0/1.0, id2158=1.0/1.0/1.0, id2159=1.0/1.0/1.0, id216=1.0/1.0/1.0, id2160=1.0/1.0/1.0, id2161=1.0/1.0/1.0, id2162=1.0/1.0/1.0, id2163=1.0/1.0/1.0, id2164=1.0/1.0/1.0, id2165=1.0/1.0/1.0, id2166=1.0/1.0/1.0, id2167=1.0/1.0/1.0, id2168=1.0/1.0/1.0, id2169=1.0/1.0/1.0, id217=1.0/1.0/1.0, id2170=1.0/1.0/1.0, id2171=1.0/1.0/1.0, id2172=1.0/1.0/1.0, id2173=1.0/1.0/1.0, id2174=1.0/1.0/1.0, id2175=1.0/1.0/1.0, id2176=1.0/1.0/1.0, id2177=1.0/1.0/1.0, id2178=1.0/1.0/1.0, id2179=1.0/1.0/1.0, id218=1.0/1.0/1.0, id2180=1.0/1.0/1.0, id2181=1.0/1.0/1.0, id2182=1.0/1.0/1.0, id2183=1.0/1.0/1.0, id2184=1.0/1.0/1.0, id2185=1.0/1.0/1.0, id2186=1.0/1.0/1.0, id2187=1.0/1.0/1.0, id2188=1.0/1.0/1.0, id2189=1.0/1.0/1.0, id219=1.0/1.0/1.0, id2190=1.0/1.0/1.0, id2191=1.0/1.0/1.0, id2192=1.0/1.0/1.0, id2193=1.0/1.0/1.0, id2194=1.0/1.0/1.0, id2195=1.0/1.0/1.0, id2196=1.0/1.0/1.0, id2197=1.0/1.0/1.0, id2198=1.0/1.0/1.0, id2199=1.0/1.0/1.0, id22=1.0/1.0/1.0, id220=1.0/1.0/1.0, id2200=1.0/1.0/1.0, id2201=1.0/1.0/1.0, id2202=1.0/1.0/1.0, id2203=1.0/1.0/1.0, id2204=1.0/1.0/1.0, id2205=1.0/1.0/1.0, id2206=1.0/1.0/1.0, id2207=1.0/1.0/1.0, id2208=1.0/1.0/1.0, id2209=1.0/1.0/1.0, id221=1.0/1.0/1.0, id2210=1.0/1.0/1.0, id2211=1.0/1.0/1.0, id2212=1.0/1.0/1.0, id2213=1.0/1.0/1.0, id2214=1.0/1.0/1.0, id2215=1.0/1.0/1.0, id2216=1.0/1.0/1.0, id2217=1.0/1.0/1.0, id2218=1.0/1.0/1.0, id2219=1.0/1.0/1.0, id222=1.0/1.0/1.0, id2220=1.0/1.0/1.0, id2221=1.0/1.0/1.0, id2222=1.0/1.0/1.0, id2223=1.0/1.0/1.0, id2224=1.0/1.0/1.0, id2225=1.0/1.0/1.0, id2226=1.0/1.0/1.0, id2227=1.0/1.0/1.0, id2228=1.0/1.0/1.0, id2229=1.0/1.0/1.0, id223=1.0/1.0/1.0, id2230=1.0/1.0/1.0, id2231=1.0/1.0/1.0, id2232=1.0/1.0/1.0, id2233=1.0/1.0/1.0, id2234=1.0/1.0/1.0, id2235=1.0/1.0/1.0, id2236=1.0/1.0/1.0, id2237=1.0/1.0/1.0, id2238=1.0/1.0/1.0, id2239=1.0/1.0/1.0, id224=1.0/1.0/1.0, id2240=1.0/1.0/1.0, id2241=1.0/1.0/1.0, id2242=1.0/1.0/1.0, id2243=1.0/1.0/1.0, id2244=1.0/1.0/1.0, id2245=1.0/1.0/1.0, id2246=1.0/1.0/1.0, id2247=1.0/1.0/1.0, id2248=1.0/1.0/1.0, id2249=1.0/1.0/1.0, id225=1.0/1.0/1.0, id2250=1.0/1.0/1.0, id2251=1.0/1.0/1.0, id2252=1.0/1.0/1.0, id2253=1.0/1.0/1.0, id2254=1.0/1.0/1.0, id2255=1.0/1.0/1.0, id2256=1.0/1.0/1.0, id2257=1.0/1.0/1.0, id2258=1.0/1.0/1.0, id2259=1.0/1.0/1.0, id226=1.0/1.0/1.0, id2260=1.0/1.0/1.0, id2261=1.0/1.0/1.0, id2262=1.0/1.0/1.0, id2263=1.0/1.0/1.0, id2264=1.0/1.0/1.0, id2265=1.0/1.0/1.0, id2266=1.0/1.0/1.0, id2267=1.0/1.0/1.0, id2268=1.0/1.0/1.0, id2269=1.0/1.0/1.0, id227=1.0/1.0/1.0, id2270=1.0/1.0/1.0, id2271=1.0/1.0/1.0, id2272=1.0/1.0/1.0, id2273=1.0/1.0/1.0, id2274=1.0/1.0/1.0, id2275=1.0/1.0/1.0, id2276=1.0/1.0/1.0, id2277=1.0/1.0/1.0, id2278=1.0/1.0/1.0, id2279=1.0/1.0/1.0, id228=1.0/1.0/1.0, id2280=1.0/1.0/1.0, id2281=1.0/1.0/1.0, id2282=1.0/1.0/1.0, id2283=1.0/1.0/1.0, id2284=1.0/1.0/1.0, id2285=1.0/1.0/1.0, id2286=1.0/1.0/1.0, id2287=1.0/1.0/1.0, id2288=1.0/1.0/1.0, id2289=1.0/1.0/1.0, id229=1.0/1.0/1.0, id2290=1.0/1.0/1.0, id2291=1.0/1.0/1.0, id2292=1.0/1.0/1.0, id2293=1.0/1.0/1.0, id2294=1.0/1.0/1.0, id2295=1.0/1.0/1.0, id2296=1.0/1.0/1.0, id2297=1.0/1.0/1.0, id2298=1.0/1.0/1.0, id2299=1.0/1.0/1.0, id23=1.0/1.0/1.0, id230=1.0/1.0/1.0, id2300=1.0/1.0/1.0, id2301=1.0/1.0/1.0, id2302=1.0/1.0/1.0, id2303=1.0/1.0/1.0, id2304=1.0/1.0/1.0, id2305=1.0/1.0/1.0, id2306=1.0/1.0/1.0, id2307=1.0/1.0/1.0, id2308=1.0/1.0/1.0, id2309=1.0/1.0/1.0, id231=1.0/1.0/1.0, id2310=1.0/1.0/1.0, id2311=1.0/1.0/1.0, id2312=1.0/1.0/1.0, id2313=1.0/1.0/1.0, id2314=1.0/1.0/1.0, id2315=1.0/1.0/1.0, id2316=1.0/1.0/1.0, id2317=1.0/1.0/1.0, id2318=1.0/1.0/1.0, id2319=1.0/1.0/1.0, id232=1.0/1.0/1.0, id2320=1.0/1.0/1.0, id2321=1.0/1.0/1.0, id2322=1.0/1.0/1.0, id2323=1.0/1.0/1.0, id2324=1.0/1.0/1.0, id2325=1.0/1.0/1.0, id2326=1.0/1.0/1.0, id2327=1.0/1.0/1.0, id2328=1.0/1.0/1.0, id2329=1.0/1.0/1.0, id233=1.0/1.0/1.0, id2330=1.0/1.0/1.0, id2331=1.0/1.0/1.0, id2332=1.0/1.0/1.0, id2333=1.0/1.0/1.0, id2334=1.0/1.0/1.0, id2335=1.0/1.0/1.0, id2336=1.0/1.0/1.0, id2337=1.0/1.0/1.0, id2338=1.0/1.0/1.0, id2339=1.0/1.0/1.0, id234=1.0/1.0/1.0, id2340=1.0/1.0/1.0, id2341=1.0/1.0/1.0, id2342=1.0/1.0/1.0, id2343=1.0/1.0/1.0, id2344=1.0/1.0/1.0, id2345=1.0/1.0/1.0, id2346=1.0/1.0/1.0, id2347=1.0/1.0/1.0, id2348=1.0/1.0/1.0, id2349=1.0/1.0/1.0, id235=1.0/1.0/1.0, id2350=1.0/1.0/1.0, id2351=1.0/1.0/1.0, id2352=1.0/1.0/1.0, id2353=1.0/1.0/1.0, id2354=1.0/1.0/1.0, id2355=1.0/1.0/1.0, id2356=1.0/1.0/1.0, id2357=1.0/1.0/1.0, id2358=1.0/1.0/1.0, id2359=1.0/1.0/1.0, id236=1.0/1.0/1.0, id2360=1.0/1.0/1.0, id2361=1.0/1.0/1.0, id2362=1.0/1.0/1.0, id2363=1.0/1.0/1.0, id2364=1.0/1.0/1.0, id2365=1.0/1.0/1.0, id2366=1.0/1.0/1.0, id2367=1.0/1.0/1.0, id2368=1.0/1.0/1.0, id2369=1.0/1.0/1.0, id237=1.0/1.0/1.0, id2370=1.0/1.0/1.0, id2371=1.0/1.0/1.0, id2372=1.0/1.0/1.0, id2373=1.0/1.0/1.0, id2374=1.0/1.0/1.0, id2375=1.0/1.0/1.0, id2376=1.0/1.0/1.0, id2377=1.0/1.0/1.0, id2378=1.0/1.0/1.0, id2379=1.0/1.0/1.0, id238=1.0/1.0/1.0, id2380=1.0/1.0/1.0, id2381=1.0/1.0/1.0, id2382=1.0/1.0/1.0, id2383=1.0/1.0/1.0, id2384=1.0/1.0/1.0, id2385=1.0/1.0/1.0, id2386=1.0/1.0/1.0, id2387=1.0/1.0/1.0, id2388=1.0/1.0/1.0, id2389=1.0/1.0/1.0, id239=1.0/1.0/1.0, id2390=1.0/1.0/1.0, id2391=1.0/1.0/1.0, id2392=1.0/1.0/1.0, id2393=1.0/1.0/1.0, id2394=1.0/1.0/1.0, id2395=1.0/1.0/1.0, id2396=1.0/1.0/1.0, id2397=1.0/1.0/1.0, id2398=1.0/1.0/1.0, id2399=1.0/1.0/1.0, id24=1.0/1.0/1.0, id240=1.0/1.0/1.0, id2400=1.0/1.0/1.0, id2401=1.0/1.0/1.0, id2402=1.0/1.0/1.0, id2403=1.0/1.0/1.0, id2404=1.0/1.0/1.0, id2405=1.0/1.0/1.0, id2406=1.0/1.0/1.0, id2407=1.0/1.0/1.0, id2408=1.0/1.0/1.0, id2409=1.0/1.0/1.0, id241=1.0/1.0/1.0, id2410=1.0/1.0/1.0, id2411=1.0/1.0/1.0, id2412=1.0/1.0/1.0, id2413=1.0/1.0/1.0, id2414=1.0/1.0/1.0, id2415=1.0/1.0/1.0, id2416=1.0/1.0/1.0, id2417=1.0/1.0/1.0, id2418=1.0/1.0/1.0, id2419=1.0/1.0/1.0, id242=1.0/1.0/1.0, id2420=1.0/1.0/1.0, id2421=1.0/1.0/1.0, id2422=1.0/1.0/1.0, id2423=1.0/1.0/1.0, id2424=1.0/1.0/1.0, id2425=1.0/1.0/1.0, id2426=1.0/1.0/1.0, id2427=1.0/1.0/1.0, id2428=1.0/1.0/1.0, id2429=1.0/1.0/1.0, id243=1.0/1.0/1.0, id2430=1.0/1.0/1.0, id2431=1.0/1.0/1.0, id2432=1.0/1.0/1.0, id2433=1.0/1.0/1.0, id2434=1.0/1.0/1.0, id2435=1.0/1.0/1.0, id2436=1.0/1.0/1.0, id2437=1.0/1.0/1.0, id2438=1.0/1.0/1.0, id2439=1.0/1.0/1.0, id244=1.0/1.0/1.0, id2440=1.0/1.0/1.0, id2441=1.0/1.0/1.0, id2442=1.0/1.0/1.0, id2443=1.0/1.0/1.0, id2444=1.0/1.0/1.0, id2445=1.0/1.0/1.0, id2446=1.0/1.0/1.0, id2447=1.0/1.0/1.0, id2448=1.0/1.0/1.0, id2449=1.0/1.0/1.0, id245=1.0/1.0/1.0, id2450=1.0/1.0/1.0, id2451=1.0/1.0/1.0, id2452=1.0/1.0/1.0, id2453=1.0/1.0/1.0, id2454=1.0/1.0/1.0, id2455=1.0/1.0/1.0, id2456=1.0/1.0/1.0, id2457=1.0/1.0/1.0, id2458=1.0/1.0/1.0, id2459=1.0/1.0/1.0, id246=1.0/1.0/1.0, id2460=1.0/1.0/1.0, id2461=1.0/1.0/1.0, id2462=1.0/1.0/1.0, id2463=1.0/1.0/1.0, id2464=1.0/1.0/1.0, id2465=1.0/1.0/1.0, id2466=1.0/1.0/1.0, id2467=1.0/1.0/1.0, id2468=1.0/1.0/1.0, id2469=1.0/1.0/1.0, id247=1.0/1.0/1.0, id2470=1.0/1.0/1.0, id2471=1.0/1.0/1.0, id2472=1.0/1.0/1.0, id2473=1.0/1.0/1.0, id2474=1.0/1.0/1.0, id2475=1.0/1.0/1.0, id2476=1.0/1.0/1.0, id2477=1.0/1.0/1.0, id2478=1.0/1.0/1.0, id2479=1.0/1.0/1.0, id248=1.0/1.0/1.0, id2480=1.0/1.0/1.0, id2481=1.0/1.0/1.0, id2482=1.0/1.0/1.0, id2483=1.0/1.0/1.0, id2484=1.0/1.0/1.0, id2485=1.0/1.0/1.0, id2486=1.0/1.0/1.0, id2487=1.0/1.0/1.0, id2488=1.0/1.0/1.0, id2489=1.0/1.0/1.0, id249=1.0/1.0/1.0, id2490=1.0/1.0/1.0, id2491=1.0/1.0/1.0, id2492=1.0/1.0/1.0, id2493=1.0/1.0/1.0, id2494=1.0/1.0/1.0, id2495=1.0/1.0/1.0, id2496=1.0/1.0/1.0, id2497=1.0/1.0/1.0, id2498=1.0/1.0/1.0, id2499=1.0/1.0/1.0, id25=1.0/1.0/1.0, id250=1.0/1.0/1.0, id2500=1.0/1.0/1.0, id2501=1.0/1.0/1.0, id2502=1.0/1.0/1.0, id2503=1.0/1.0/1.0, id2504=1.0/1.0/1.0, id2505=1.0/1.0/1.0, id2506=1.0/1.0/1.0, id2507=1.0/1.0/1.0, id2508=1.0/1.0/1.0, id2509=1.0/1.0/1.0, id251=1.0/1.0/1.0, id2510=1.0/1.0/1.0, id2511=1.0/1.0/1.0, id2512=1.0/1.0/1.0, id2513=1.0/1.0/1.0, id2514=1.0/1.0/1.0, id2515=1.0/1.0/1.0, id2516=1.0/1.0/1.0, id2517=1.0/1.0/1.0, id2518=1.0/1.0/1.0, id2519=1.0/1.0/1.0, id252=1.0/1.0/1.0, id2520=1.0/1.0/1.0, id2521=1.0/1.0/1.0, id2522=1.0/1.0/1.0, id2523=1.0/1.0/1.0, id2524=1.0/1.0/1.0, id2525=1.0/1.0/1.0, id2526=1.0/1.0/1.0, id2527=1.0/1.0/1.0, id2528=1.0/1.0/1.0, id2529=1.0/1.0/1.0, id253=1.0/1.0/1.0, id2530=1.0/1.0/1.0, id2531=1.0/1.0/1.0, id2532=1.0/1.0/1.0, id2533=1.0/1.0/1.0, id2534=1.0/1.0/1.0, id2535=1.0/1.0/1.0, id2536=1.0/1.0/1.0, id2537=1.0/1.0/1.0, id2538=1.0/1.0/1.0, id2539=1.0/1.0/1.0, id254=1.0/1.0/1.0, id2540=1.0/1.0/1.0, id2541=1.0/1.0/1.0, id2542=1.0/1.0/1.0, id2543=1.0/1.0/1.0, id2544=1.0/1.0/1.0, id2545=1.0/1.0/1.0, id2546=1.0/1.0/1.0, id2547=1.0/1.0/1.0, id2548=1.0/1.0/1.0, id2549=1.0/1.0/1.0, id255=1.0/1.0/1.0, id2550=1.0/1.0/1.0, id2551=1.0/1.0/1.0, id2552=1.0/1.0/1.0, id2553=1.0/1.0/1.0, id2554=1.0/1.0/1.0, id2555=1.0/1.0/1.0, id2556=1.0/1.0/1.0, id2557=1.0/1.0/1.0, id2558=1.0/1.0/1.0, id2559=1.0/1.0/1.0, id256=1.0/1.0/1.0, id2560=1.0/1.0/1.0, id2561=1.0/1.0/1.0, id2562=1.0/1.0/1.0, id2563=1.0/1.0/1.0, id2564=1.0/1.0/1.0, id2565=1.0/1.0/1.0, id2566=1.0/1.0/1.0, id2567=1.0/1.0/1.0, id2568=1.0/1.0/1.0, id2569=1.0/1.0/1.0, id257=1.0/1.0/1.0, id2570=1.0/1.0/1.0, id2571=1.0/1.0/1.0, id2572=1.0/1.0/1.0, id2573=1.0/1.0/1.0, id2574=1.0/1.0/1.0, id2575=1.0/1.0/1.0, id2576=1.0/1.0/1.0, id2577=1.0/1.0/1.0, id2578=1.0/1.0/1.0, id2579=1.0/1.0/1.0, id258=1.0/1.0/1.0, id2580=1.0/1.0/1.0, id2581=1.0/1.0/1.0, id2582=1.0/1.0/1.0, id2583=1.0/1.0/1.0, id2584=1.0/1.0/1.0, id2585=1.0/1.0/1.0, id2586=1.0/1.0/1.0, id2587=1.0/1.0/1.0, id2588=1.0/1.0/1.0, id2589=1.0/1.0/1.0, id259=1.0/1.0/1.0, id2590=1.0/1.0/1.0, id2591=1.0/1.0/1.0, id2592=1.0/1.0/1.0, id2593=1.0/1.0/1.0, id2594=1.0/1.0/1.0, id2595=1.0/1.0/1.0, id2596=1.0/1.0/1.0, id2597=1.0/1.0/1.0, id2598=1.0/1.0/1.0, id2599=1.0/1.0/1.0, id26=1.0/1.0/1.0, id260=1.0/1.0/1.0, id2600=1.0/1.0/1.0, id2601=1.0/1.0/1.0, id2602=1.0/1.0/1.0, id2603=1.0/1.0/1.0, id2604=1.0/1.0/1.0, id2605=1.0/1.0/1.0, id2606=1.0/1.0/1.0, id2607=1.0/1.0/1.0, id2608=1.0/1.0/1.0, id2609=1.0/1.0/1.0, id261=1.0/1.0/1.0, id2610=1.0/1.0/1.0, id2611=1.0/1.0/1.0, id2612=1.0/1.0/1.0, id2613=1.0/1.0/1.0, id2614=1.0/1.0/1.0, id2615=1.0/1.0/1.0, id2616=1.0/1.0/1.0, id2617=1.0/1.0/1.0, id2618=1.0/1.0/1.0, id2619=1.0/1.0/1.0, id262=1.0/1.0/1.0, id2620=1.0/1.0/1.0, id2621=1.0/1.0/1.0, id2622=1.0/1.0/1.0, id2623=1.0/1.0/1.0, id2624=1.0/1.0/1.0, id2625=1.0/1.0/1.0, id2626=1.0/1.0/1.0, id2627=1.0/1.0/1.0, id2628=1.0/1.0/1.0, id2629=1.0/1.0/1.0, id263=1.0/1.0/1.0, id2630=1.0/1.0/1.0, id2631=1.0/1.0/1.0, id2632=1.0/1.0/1.0, id2633=1.0/1.0/1.0, id2634=1.0/1.0/1.0, id2635=1.0/1.0/1.0, id2636=1.0/1.0/1.0, id2637=1.0/1.0/1.0, id2638=1.0/1.0/1.0, id2639=1.0/1.0/1.0, id264=1.0/1.0/1.0, id2640=1.0/1.0/1.0, id2641=1.0/1.0/1.0, id2642=1.0/1.0/1.0, id2643=1.0/1.0/1.0, id2644=1.0/1.0/1.0, id2645=1.0/1.0/1.0, id2646=1.0/1.0/1.0, id2647=1.0/1.0/1.0, id2648=1.0/1.0/1.0, id2649=1.0/1.0/1.0, id265=1.0/1.0/1.0, id2650=1.0/1.0/1.0, id2651=1.0/1.0/1.0, id2652=1.0/1.0/1.0, id2653=1.0/1.0/1.0, id2654=1.0/1.0/1.0, id2655=1.0/1.0/1.0, id2656=1.0/1.0/1.0, id2657=1.0/1.0/1.0, id2658=1.0/1.0/1.0, id2659=1.0/1.0/1.0, id266=1.0/1.0/1.0, id2660=1.0/1.0/1.0, id2661=1.0/1.0/1.0, id2662=1.0/1.0/1.0, id2663=1.0/1.0/1.0, id2664=1.0/1.0/1.0, id2665=1.0/1.0/1.0, id2666=1.0/1.0/1.0, id2667=1.0/1.0/1.0, id2668=1.0/1.0/1.0, id2669=1.0/1.0/1.0, id267=1.0/1.0/1.0, id2670=1.0/1.0/1.0, id2671=1.0/1.0/1.0, id2672=1.0/1.0/1.0, id2673=1.0/1.0/1.0, id2674=1.0/1.0/1.0, id2675=1.0/1.0/1.0, id2676=1.0/1.0/1.0, id2677=1.0/1.0/1.0, id2678=1.0/1.0/1.0, id2679=1.0/1.0/1.0, id268=1.0/1.0/1.0, id2680=1.0/1.0/1.0, id2681=1.0/1.0/1.0, id2682=1.0/1.0/1.0, id2683=1.0/1.0/1.0, id2684=1.0/1.0/1.0, id2685=1.0/1.0/1.0, id2686=1.0/1.0/1.0, id2687=1.0/1.0/1.0, id2688=1.0/1.0/1.0, id2689=1.0/1.0/1.0, id269=1.0/1.0/1.0, id2690=1.0/1.0/1.0, id2691=1.0/1.0/1.0, id2692=1.0/1.0/1.0, id2693=1.0/1.0/1.0, id2694=1.0/1.0/1.0, id2695=1.0/1.0/1.0, id2696=1.0/1.0/1.0, id2697=1.0/1.0/1.0, id2698=1.0/1.0/1.0, id2699=1.0/1.0/1.0, id27=1.0/1.0/1.0, id270=1.0/1.0/1.0, id2700=1.0/1.0/1.0, id2701=1.0/1.0/1.0, id2702=1.0/1.0/1.0, id2703=1.0/1.0/1.0, id2704=1.0/1.0/1.0, id2705=1.0/1.0/1.0, id2706=1.0/1.0/1.0, id2707=1.0/1.0/1.0, id2708=1.0/1.0/1.0, id2709=1.0/1.0/1.0, id271=1.0/1.0/1.0, id2710=1.0/1.0/1.0, id2711=1.0/1.0/1.0, id2712=1.0/1.0/1.0, id2713=1.0/1.0/1.0, id2714=1.0/1.0/1.0, id2715=1.0/1.0/1.0, id2716=1.0/1.0/1.0, id2717=1.0/1.0/1.0, id2718=1.0/1.0/1.0, id2719=1.0/1.0/1.0, id272=1.0/1.0/1.0, id2720=1.0/1.0/1.0, id2721=1.0/1.0/1.0, id2722=1.0/1.0/1.0, id2723=1.0/1.0/1.0, id2724=1.0/1.0/1.0, id2725=1.0/1.0/1.0, id2726=1.0/1.0/1.0, id2727=1.0/1.0/1.0, id2728=1.0/1.0/1.0, id2729=1.0/1.0/1.0, id273=1.0/1.0/1.0, id2730=1.0/1.0/1.0, id2731=1.0/1.0/1.0, id2732=1.0/1.0/1.0, id2733=1.0/1.0/1.0, id2734=1.0/1.0/1.0, id2735=1.0/1.0/1.0, id2736=1.0/1.0/1.0, id2737=1.0/1.0/1.0, id2738=1.0/1.0/1.0, id2739=1.0/1.0/1.0, id274=1.0/1.0/1.0, id2740=1.0/1.0/1.0, id2741=1.0/1.0/1.0, id2742=1.0/1.0/1.0, id2743=1.0/1.0/1.0, id2744=1.0/1.0/1.0, id2745=1.0/1.0/1.0, id2746=1.0/1.0/1.0, id2747=1.0/1.0/1.0, id2748=1.0/1.0/1.0, id2749=1.0/1.0/1.0, id275=1.0/1.0/1.0, id2750=1.0/1.0/1.0, id2751=1.0/1.0/1.0, id2752=1.0/1.0/1.0, id2753=1.0/1.0/1.0, id2754=1.0/1.0/1.0, id2755=1.0/1.0/1.0, id2756=1.0/1.0/1.0, id2757=1.0/1.0/1.0, id2758=1.0/1.0/1.0, id2759=1.0/1.0/1.0, id276=1.0/1.0/1.0, id2760=1.0/1.0/1.0, id2761=1.0/1.0/1.0, id2762=1.0/1.0/1.0, id2763=1.0/1.0/1.0, id2764=1.0/1.0/1.0, id2765=1.0/1.0/1.0, id2766=1.0/1.0/1.0, id2767=1.0/1.0/1.0, id2768=1.0/1.0/1.0, id2769=1.0/1.0/1.0, id277=1.0/1.0/1.0, id2770=1.0/1.0/1.0, id2771=1.0/1.0/1.0, id2772=1.0/1.0/1.0, id2773=1.0/1.0/1.0, id2774=1.0/1.0/1.0, id2775=1.0/1.0/1.0, id2776=1.0/1.0/1.0, id2777=1.0/1.0/1.0, id2778=1.0/1.0/1.0, id2779=1.0/1.0/1.0, id278=1.0/1.0/1.0, id2780=1.0/1.0/1.0, id2781=1.0/1.0/1.0, id2782=1.0/1.0/1.0, id2783=1.0/1.0/1.0, id2784=1.0/1.0/1.0, id2785=1.0/1.0/1.0, id2786=1.0/1.0/1.0, id2787=1.0/1.0/1.0, id2788=1.0/1.0/1.0, id2789=1.0/1.0/1.0, id279=1.0/1.0/1.0, id2790=1.0/1.0/1.0, id2791=1.0/1.0/1.0, id2792=1.0/1.0/1.0, id2793=1.0/1.0/1.0, id2794=1.0/1.0/1.0, id2795=1.0/1.0/1.0, id2796=1.0/1.0/1.0, id2797=1.0/1.0/1.0, id2798=1.0/1.0/1.0, id2799=1.0/1.0/1.0, id28=1.0/1.0/1.0, id280=1.0/1.0/1.0, id2800=1.0/1.0/1.0, id2801=1.0/1.0/1.0, id2802=1.0/1.0/1.0, id2803=1.0/1.0/1.0, id2804=1.0/1.0/1.0, id2805=1.0/1.0/1.0, id2806=1.0/1.0/1.0, id2807=1.0/1.0/1.0, id2808=1.0/1.0/1.0, id2809=1.0/1.0/1.0, id281=1.0/1.0/1.0, id2810=1.0/1.0/1.0, id2811=1.0/1.0/1.0, id2812=1.0/1.0/1.0, id2813=1.0/1.0/1.0, id2814=1.0/1.0/1.0, id2815=1.0/1.0/1.0, id2816=1.0/1.0/1.0, id2817=1.0/1.0/1.0, id2818=1.0/1.0/1.0, id2819=1.0/1.0/1.0, id282=1.0/1.0/1.0, id2820=1.0/1.0/1.0, id2821=1.0/1.0/1.0, id2822=1.0/1.0/1.0, id2823=1.0/1.0/1.0, id2824=1.0/1.0/1.0, id2825=1.0/1.0/1.0, id2826=1.0/1.0/1.0, id2827=1.0/1.0/1.0, id2828=1.0/1.0/1.0, id2829=1.0/1.0/1.0, id283=1.0/1.0/1.0, id2830=1.0/1.0/1.0, id2831=1.0/1.0/1.0, id2832=1.0/1.0/1.0, id2833=1.0/1.0/1.0, id2834=1.0/1.0/1.0, id2835=1.0/1.0/1.0, id2836=1.0/1.0/1.0, id2837=1.0/1.0/1.0, id2838=1.0/1.0/1.0, id2839=1.0/1.0/1.0, id284=1.0/1.0/1.0, id2840=1.0/1.0/1.0, id2841=1.0/1.0/1.0, id2842=1.0/1.0/1.0, id2843=1.0/1.0/1.0, id2844=1.0/1.0/1.0, id2845=1.0/1.0/1.0, id2846=1.0/1.0/1.0, id2847=1.0/1.0/1.0, id2848=1.0/1.0/1.0, id2849=1.0/1.0/1.0, id285=1.0/1.0/1.0, id2850=1.0/1.0/1.0, id2851=1.0/1.0/1.0, id2852=1.0/1.0/1.0, id2853=1.0/1.0/1.0, id2854=1.0/1.0/1.0, id2855=1.0/1.0/1.0, id2856=1.0/1.0/1.0, id2857=1.0/1.0/1.0, id2858=1.0/1.0/1.0, id2859=1.0/1.0/1.0, id286=1.0/1.0/1.0, id2860=1.0/1.0/1.0, id2861=1.0/1.0/1.0, id2862=1.0/1.0/1.0, id2863=1.0/1.0/1.0, id2864=1.0/1.0/1.0, id2865=1.0/1.0/1.0, id2866=1.0/1.0/1.0, id2867=1.0/1.0/1.0, id2868=1.0/1.0/1.0, id2869=1.0/1.0/1.0, id287=1.0/1.0/1.0, id2870=1.0/1.0/1.0, id2871=1.0/1.0/1.0, id2872=1.0/1.0/1.0, id2873=1.0/1.0/1.0, id2874=1.0/1.0/1.0, id2875=1.0/1.0/1.0, id2876=1.0/1.0/1.0, id2877=1.0/1.0/1.0, id2878=1.0/1.0/1.0, id2879=1.0/1.0/1.0, id288=1.0/1.0/1.0, id2880=1.0/1.0/1.0, id2881=1.0/1.0/1.0, id2882=1.0/1.0/1.0, id2883=1.0/1.0/1.0, id2884=1.0/1.0/1.0, id2885=1.0/1.0/1.0, id2886=1.0/1.0/1.0, id2887=1.0/1.0/1.0, id2888=1.0/1.0/1.0, id2889=1.0/1.0/1.0, id289=1.0/1.0/1.0, id2890=1.0/1.0/1.0, id2891=1.0/1.0/1.0, id2892=1.0/1.0/1.0, id2893=1.0/1.0/1.0, id2894=1.0/1.0/1.0, id2895=1.0/1.0/1.0, id2896=1.0/1.0/1.0, id2897=1.0/1.0/1.0, id2898=1.0/1.0/1.0, id2899=1.0/1.0/1.0, id29=1.0/1.0/1.0, id290=1.0/1.0/1.0, id2900=1.0/1.0/1.0, id2901=1.0/1.0/1.0, id2902=1.0/1.0/1.0, id2903=1.0/1.0/1.0, id2904=1.0/1.0/1.0, id2905=1.0/1.0/1.0, id2906=1.0/1.0/1.0, id2907=1.0/1.0/1.0, id2908=1.0/1.0/1.0, id2909=1.0/1.0/1.0, id291=1.0/1.0/1.0, id2910=1.0/1.0/1.0, id2911=1.0/1.0/1.0, id2912=1.0/1.0/1.0, id2913=1.0/1.0/1.0, id2914=1.0/1.0/1.0, id2915=1.0/1.0/1.0, id2916=1.0/1.0/1.0, id2917=1.0/1.0/1.0, id2918=1.0/1.0/1.0, id2919=1.0/1.0/1.0, id292=1.0/1.0/1.0, id2920=1.0/1.0/1.0, id2921=1.0/1.0/1.0, id2922=1.0/1.0/1.0, id2923=1.0/1.0/1.0, id2924=1.0/1.0/1.0, id2925=1.0/1.0/1.0, id2926=1.0/1.0/1.0, id2927=1.0/1.0/1.0, id2928=1.0/1.0/1.0, id2929=1.0/1.0/1.0, id293=1.0/1.0/1.0, id2930=1.0/1.0/1.0, id2931=1.0/1.0/1.0, id2932=1.0/1.0/1.0, id2933=1.0/1.0/1.0, id2934=1.0/1.0/1.0, id2935=1.0/1.0/1.0, id2936=1.0/1.0/1.0, id2937=1.0/1.0/1.0, id2938=1.0/1.0/1.0, id2939=1.0/1.0/1.0, id294=1.0/1.0/1.0, id2940=1.0/1.0/1.0, id2941=1.0/1.0/1.0, id2942=1.0/1.0/1.0, id2943=1.0/1.0/1.0, id2944=1.0/1.0/1.0, id2945=1.0/1.0/1.0, id2946=1.0/1.0/1.0, id2947=1.0/1.0/1.0, id2948=1.0/1.0/1.0, id2949=1.0/1.0/1.0, id295=1.0/1.0/1.0, id2950=1.0/1.0/1.0, id2951=1.0/1.0/1.0, id2952=1.0/1.0/1.0, id2953=1.0/1.0/1.0, id2954=1.0/1.0/1.0, id2955=1.0/1.0/1.0, id2956=1.0/1.0/1.0, id2957=1.0/1.0/1.0, id2958=1.0/1.0/1.0, id2959=1.0/1.0/1.0, id296=1.0/1.0/1.0, id2960=1.0/1.0/1.0, id2961=1.0/1.0/1.0, id2962=1.0/1.0/1.0, id2963=1.0/1.0/1.0, id2964=1.0/1.0/1.0, id2965=1.0/1.0/1.0, id2966=1.0/1.0/1.0, id2967=1.0/1.0/1.0, id2968=1.0/1.0/1.0, id2969=1.0/1.0/1.0, id297=1.0/1.0/1.0, id2970=1.0/1.0/1.0, id2971=1.0/1.0/1.0, id2972=1.0/1.0/1.0, id2973=1.0/1.0/1.0, id2974=1.0/1.0/1.0, id2975=1.0/1.0/1.0, id2976=1.0/1.0/1.0, id2977=1.0/1.0/1.0, id2978=1.0/1.0/1.0, id2979=1.0/1.0/1.0, id298=1.0/1.0/1.0, id2980=1.0/1.0/1.0, id2981=1.0/1.0/1.0, id2982=1.0/1.0/1.0, id2983=1.0/1.0/1.0, id2984=1.0/1.0/1.0, id2985=1.0/1.0/1.0, id2986=1.0/1.0/1.0, id2987=1.0/1.0/1.0, id2988=1.0/1.0/1.0, id2989=1.0/1.0/1.0, id299=1.0/1.0/1.0, id2990=1.0/1.0/1.0, id2991=1.0/1.0/1.0, id2992=1.0/1.0/1.0, id2993=1.0/1.0/1.0, id2994=1.0/1.0/1.0, id2995=1.0/1.0/1.0, id2996=1.0/1.0/1.0, id2997=1.0/1.0/1.0, id2998=1.0/1.0/1.0, id2999=1.0/1.0/1.0, id3=1.0/1.0/1.0, id30=1.0/1.0/1.0, id300=1.0/1.0/1.0, id3000=1.0/1.0/1.0, id3001=1.0/1.0/1.0, id3002=1.0/1.0/1.0, id3003=1.0/1.0/1.0, id3004=1.0/1.0/1.0, id3005=1.0/1.0/1.0, id3006=1.0/1.0/1.0, id3007=1.0/1.0/1.0, id3008=1.0/1.0/1.0, id3009=1.0/1.0/1.0, id301=1.0/1.0/1.0, id3010=1.0/1.0/1.0, id3011=1.0/1.0/1.0, id3012=1.0/1.0/1.0, id3013=1.0/1.0/1.0, id3014=1.0/1.0/1.0, id3015=1.0/1.0/1.0, id3016=1.0/1.0/1.0, id3017=1.0/1.0/1.0, id3018=1.0/1.0/1.0, id3019=1.0/1.0/1.0, id302=1.0/1.0/1.0, id3020=1.0/1.0/1.0, id3021=1.0/1.0/1.0, id3022=1.0/1.0/1.0, id3023=1.0/1.0/1.0, id3024=1.0/1.0/1.0, id3025=1.0/1.0/1.0, id3026=1.0/1.0/1.0, id3027=1.0/1.0/1.0, id3028=1.0/1.0/1.0, id3029=1.0/1.0/1.0, id303=1.0/1.0/1.0, id3030=1.0/1.0/1.0, id3031=1.0/1.0/1.0, id3032=1.0/1.0/1.0, id3033=1.0/1.0/1.0, id3034=1.0/1.0/1.0, id3035=1.0/1.0/1.0, id3036=1.0/1.0/1.0, id3037=1.0/1.0/1.0, id3038=1.0/1.0/1.0, id3039=1.0/1.0/1.0, id304=1.0/1.0/1.0, id3040=1.0/1.0/1.0, id3041=1.0/1.0/1.0, id3042=1.0/1.0/1.0, id3043=1.0/1.0/1.0, id3044=1.0/1.0/1.0, id3045=1.0/1.0/1.0, id3046=1.0/1.0/1.0, id3047=1.0/1.0/1.0, id3048=1.0/1.0/1.0, id3049=1.0/1.0/1.0, id305=1.0/1.0/1.0, id3050=1.0/1.0/1.0, id3051=1.0/1.0/1.0, id3052=1.0/1.0/1.0, id3053=1.0/1.0/1.0, id3054=1.0/1.0/1.0, id3055=1.0/1.0/1.0, id3056=1.0/1.0/1.0, id3057=1.0/1.0/1.0, id3058=1.0/1.0/1.0, id3059=1.0/1.0/1.0, id306=1.0/1.0/1.0, id3060=1.0/1.0/1.0, id3061=1.0/1.0/1.0, id3062=1.0/1.0/1.0, id3063=1.0/1.0/1.0, id3064=1.0/1.0/1.0, id3065=1.0/1.0/1.0, id3066=1.0/1.0/1.0, id3067=1.0/1.0/1.0, id3068=1.0/1.0/1.0, id3069=1.0/1.0/1.0, id307=1.0/1.0/1.0, id3070=1.0/1.0/1.0, id3071=1.0/1.0/1.0, id3072=1.0/1.0/1.0, id3073=1.0/1.0/1.0, id3074=1.0/1.0/1.0, id3075=1.0/1.0/1.0, id3076=1.0/1.0/1.0, id3077=1.0/1.0/1.0, id3078=1.0/1.0/1.0, id3079=1.0/1.0/1.0, id308=1.0/1.0/1.0, id3080=1.0/1.0/1.0, id3081=1.0/1.0/1.0, id3082=1.0/1.0/1.0, id3083=1.0/1.0/1.0, id3084=1.0/1.0/1.0, id3085=1.0/1.0/1.0, id3086=1.0/1.0/1.0, id3087=1.0/1.0/1.0, id3088=1.0/1.0/1.0, id3089=1.0/1.0/1.0, id309=1.0/1.0/1.0, id3090=1.0/1.0/1.0, id3091=1.0/1.0/1.0, id3092=1.0/1.0/1.0, id3093=1.0/1.0/1.0, id3094=1.0/1.0/1.0, id3095=1.0/1.0/1.0, id3096=1.0/1.0/1.0, id3097=1.0/1.0/1.0, id3098=1.0/1.0/1.0, id3099=1.0/1.0/1.0, id31=1.0/1.0/1.0, id310=1.0/1.0/1.0, id3100=1.0/1.0/1.0, id3101=1.0/1.0/1.0, id3102=1.0/1.0/1.0, id3103=1.0/1.0/1.0, id3104=1.0/1.0/1.0, id3105=1.0/1.0/1.0, id3106=1.0/1.0/1.0, id3107=1.0/1.0/1.0, id3108=1.0/1.0/1.0, id3109=1.0/1.0/1.0, id311=1.0/1.0/1.0, id3110=1.0/1.0/1.0, id3111=1.0/1.0/1.0, id3112=1.0/1.0/1.0, id3113=1.0/1.0/1.0, id3114=1.0/1.0/1.0, id3115=1.0/1.0/1.0, id3116=1.0/1.0/1.0, id3117=1.0/1.0/1.0, id3118=1.0/1.0/1.0, id3119=1.0/1.0/1.0, id312=1.0/1.0/1.0, id3120=1.0/1.0/1.0, id3121=1.0/1.0/1.0, id3122=1.0/1.0/1.0, id3123=1.0/1.0/1.0, id3124=1.0/1.0/1.0, id3125=1.0/1.0/1.0, id3126=1.0/1.0/1.0, id3127=1.0/1.0/1.0, id3128=1.0/1.0/1.0, id3129=1.0/1.0/1.0, id313=1.0/1.0/1.0, id3130=1.0/1.0/1.0, id3131=1.0/1.0/1.0, id3132=1.0/1.0/1.0, id3133=1.0/1.0/1.0, id3134=1.0/1.0/1.0, id3135=1.0/1.0/1.0, id3136=1.0/1.0/1.0, id3137=1.0/1.0/1.0, id3138=1.0/1.0/1.0, id3139=1.0/1.0/1.0, id314=1.0/1.0/1.0, id3140=1.0/1.0/1.0, id3141=1.0/1.0/1.0, id3142=1.0/1.0/1.0, id3143=1.0/1.0/1.0, id3144=1.0/1.0/1.0, id3145=1.0/1.0/1.0, id3146=1.0/1.0/1.0, id3147=1.0/1.0/1.0, id3148=1.0/1.0/1.0, id3149=1.0/1.0/1.0, id315=1.0/1.0/1.0, id3150=1.0/1.0/1.0, id3151=1.0/1.0/1.0, id3152=1.0/1.0/1.0, id3153=1.0/1.0/1.0, id3154=1.0/1.0/1.0, id3155=1.0/1.0/1.0, id3156=1.0/1.0/1.0, id3157=1.0/1.0/1.0, id3158=1.0/1.0/1.0, id3159=1.0/1.0/1.0, id316=1.0/1.0/1.0, id3160=1.0/1.0/1.0, id3161=1.0/1.0/1.0, id3162=1.0/1.0/1.0, id3163=1.0/1.0/1.0, id3164=1.0/1.0/1.0, id3165=1.0/1.0/1.0, id3166=1.0/1.0/1.0, id3167=1.0/1.0/1.0, id3168=1.0/1.0/1.0, id3169=1.0/1.0/1.0, id317=1.0/1.0/1.0, id3170=1.0/1.0/1.0, id3171=1.0/1.0/1.0, id3172=1.0/1.0/1.0, id3173=1.0/1.0/1.0, id3174=1.0/1.0/1.0, id3175=1.0/1.0/1.0, id3176=1.0/1.0/1.0, id3177=1.0/1.0/1.0, id3178=1.0/1.0/1.0, id3179=1.0/1.0/1.0, id318=1.0/1.0/1.0, id3180=1.0/1.0/1.0, id3181=1.0/1.0/1.0, id3182=1.0/1.0/1.0, id3183=1.0/1.0/1.0, id3184=1.0/1.0/1.0, id3185=1.0/1.0/1.0, id3186=1.0/1.0/1.0, id3187=1.0/1.0/1.0, id3188=1.0/1.0/1.0, id3189=1.0/1.0/1.0, id319=1.0/1.0/1.0, id3190=1.0/1.0/1.0, id3191=1.0/1.0/1.0, id3192=1.0/1.0/1.0, id3193=1.0/1.0/1.0, id3194=1.0/1.0/1.0, id3195=1.0/1.0/1.0, id3196=1.0/1.0/1.0, id3197=1.0/1.0/1.0, id3198=1.0/1.0/1.0, id3199=1.0/1.0/1.0, id32=1.0/1.0/1.0, id320=1.0/1.0/1.0, id3200=1.0/1.0/1.0, id3201=1.0/1.0/1.0, id3202=1.0/1.0/1.0, id3203=1.0/1.0/1.0, id3204=1.0/1.0/1.0, id3205=1.0/1.0/1.0, id3206=1.0/1.0/1.0, id3207=1.0/1.0/1.0, id3208=1.0/1.0/1.0, id3209=1.0/1.0/1.0, id321=1.0/1.0/1.0, id3210=1.0/1.0/1.0, id3211=1.0/1.0/1.0, id3212=1.0/1.0/1.0, id3213=1.0/1.0/1.0, id3214=1.0/1.0/1.0, id3215=1.0/1.0/1.0, id3216=1.0/1.0/1.0, id3217=1.0/1.0/1.0, id3218=1.0/1.0/1.0, id3219=1.0/1.0/1.0, id322=1.0/1.0/1.0, id3220=1.0/1.0/1.0, id3221=1.0/1.0/1.0, id3222=1.0/1.0/1.0, id3223=1.0/1.0/1.0, id3224=1.0/1.0/1.0, id3225=1.0/1.0/1.0, id3226=1.0/1.0/1.0, id3227=1.0/1.0/1.0, id3228=1.0/1.0/1.0, id3229=1.0/1.0/1.0, id323=1.0/1.0/1.0, id3230=1.0/1.0/1.0, id3231=1.0/1.0/1.0, id3232=1.0/1.0/1.0, id3233=1.0/1.0/1.0, id3234=1.0/1.0/1.0, id3235=1.0/1.0/1.0, id3236=1.0/1.0/1.0, id3237=1.0/1.0/1.0, id3238=1.0/1.0/1.0, id3239=1.0/1.0/1.0, id324=1.0/1.0/1.0, id3240=1.0/1.0/1.0, id3241=1.0/1.0/1.0, id3242=1.0/1.0/1.0, id3243=1.0/1.0/1.0, id3244=1.0/1.0/1.0, id3245=1.0/1.0/1.0, id3246=1.0/1.0/1.0, id3247=1.0/1.0/1.0, id3248=1.0/1.0/1.0, id3249=1.0/1.0/1.0, id325=1.0/1.0/1.0, id3250=1.0/1.0/1.0, id3251=1.0/1.0/1.0, id3252=1.0/1.0/1.0, id3253=1.0/1.0/1.0, id3254=1.0/1.0/1.0, id3255=1.0/1.0/1.0, id3256=1.0/1.0/1.0, id3257=1.0/1.0/1.0, id3258=1.0/1.0/1.0, id3259=1.0/1.0/1.0, id326=1.0/1.0/1.0, id3260=1.0/1.0/1.0, id3261=1.0/1.0/1.0, id3262=1.0/1.0/1.0, id3263=1.0/1.0/1.0, id3264=1.0/1.0/1.0, id3265=1.0/1.0/1.0, id3266=1.0/1.0/1.0, id3267=1.0/1.0/1.0, id3268=1.0/1.0/1.0, id3269=1.0/1.0/1.0, id327=1.0/1.0/1.0, id3270=1.0/1.0/1.0, id3271=1.0/1.0/1.0, id3272=1.0/1.0/1.0, id3273=1.0/1.0/1.0, id3274=1.0/1.0/1.0, id3275=1.0/1.0/1.0, id3276=1.0/1.0/1.0, id3277=1.0/1.0/1.0, id3278=1.0/1.0/1.0, id3279=1.0/1.0/1.0, id328=1.0/1.0/1.0, id3280=1.0/1.0/1.0, id3281=1.0/1.0/1.0, id3282=1.0/1.0/1.0, id3283=1.0/1.0/1.0, id3284=1.0/1.0/1.0, id3285=1.0/1.0/1.0, id3286=1.0/1.0/1.0, id3287=1.0/1.0/1.0, id3288=1.0/1.0/1.0, id3289=1.0/1.0/1.0, id329=1.0/1.0/1.0, id3290=1.0/1.0/1.0, id3291=1.0/1.0/1.0, id3292=1.0/1.0/1.0, id3293=1.0/1.0/1.0, id3294=1.0/1.0/1.0, id3295=1.0/1.0/1.0, id3296=1.0/1.0/1.0, id3297=1.0/1.0/1.0, id3298=1.0/1.0/1.0, id3299=1.0/1.0/1.0, id33=1.0/1.0/1.0, id330=1.0/1.0/1.0, id3300=1.0/1.0/1.0, id3301=1.0/1.0/1.0, id3302=1.0/1.0/1.0, id3303=1.0/1.0/1.0, id3304=1.0/1.0/1.0, id3305=1.0/1.0/1.0, id3306=1.0/1.0/1.0, id3307=1.0/1.0/1.0, id3308=1.0/1.0/1.0, id3309=1.0/1.0/1.0, id331=1.0/1.0/1.0, id3310=1.0/1.0/1.0, id3311=1.0/1.0/1.0, id3312=1.0/1.0/1.0, id3313=1.0/1.0/1.0, id3314=1.0/1.0/1.0, id3315=1.0/1.0/1.0, id3316=1.0/1.0/1.0, id3317=1.0/1.0/1.0, id3318=1.0/1.0/1.0, id3319=1.0/1.0/1.0, id332=1.0/1.0/1.0, id3320=1.0/1.0/1.0, id3321=1.0/1.0/1.0, id3322=1.0/1.0/1.0, id3323=1.0/1.0/1.0, id3324=1.0/1.0/1.0, id3325=1.0/1.0/1.0, id3326=1.0/1.0/1.0, id3327=1.0/1.0/1.0, id3328=1.0/1.0/1.0, id3329=1.0/1.0/1.0, id333=1.0/1.0/1.0, id3330=1.0/1.0/1.0, id3331=1.0/1.0/1.0, id3332=1.0/1.0/1.0, id3333=1.0/1.0/1.0, id3334=1.0/1.0/1.0, id3335=1.0/1.0/1.0, id3336=1.0/1.0/1.0, id3337=1.0/1.0/1.0, id3338=1.0/1.0/1.0, id3339=1.0/1.0/1.0, id334=1.0/1.0/1.0, id3340=1.0/1.0/1.0, id3341=1.0/1.0/1.0, id3342=1.0/1.0/1.0, id3343=1.0/1.0/1.0, id3344=1.0/1.0/1.0, id3345=1.0/1.0/1.0, id3346=1.0/1.0/1.0, id3347=1.0/1.0/1.0, id3348=1.0/1.0/1.0, id3349=1.0/1.0/1.0, id335=1.0/1.0/1.0, id3350=1.0/1.0/1.0, id3351=1.0/1.0/1.0, id3352=1.0/1.0/1.0, id3353=1.0/1.0/1.0, id3354=1.0/1.0/1.0, id3355=1.0/1.0/1.0, id3356=1.0/1.0/1.0, id3357=1.0/1.0/1.0, id3358=1.0/1.0/1.0, id3359=1.0/1.0/1.0, id336=1.0/1.0/1.0, id3360=1.0/1.0/1.0, id3361=1.0/1.0/1.0, id3362=1.0/1.0/1.0, id3363=1.0/1.0/1.0, id3364=1.0/1.0/1.0, id3365=1.0/1.0/1.0, id3366=1.0/1.0/1.0, id3367=1.0/1.0/1.0, id3368=1.0/1.0/1.0, id3369=1.0/1.0/1.0, id337=1.0/1.0/1.0, id3370=1.0/1.0/1.0, id3371=1.0/1.0/1.0, id3372=1.0/1.0/1.0, id3373=1.0/1.0/1.0, id3374=1.0/1.0/1.0, id3375=1.0/1.0/1.0, id3376=1.0/1.0/1.0, id3377=1.0/1.0/1.0, id3378=1.0/1.0/1.0, id3379=1.0/1.0/1.0, id338=1.0/1.0/1.0, id3380=1.0/1.0/1.0, id3381=1.0/1.0/1.0, id3382=1.0/1.0/1.0, id3383=1.0/1.0/1.0, id3384=1.0/1.0/1.0, id3385=1.0/1.0/1.0, id3386=1.0/1.0/1.0, id3387=1.0/1.0/1.0, id3388=1.0/1.0/1.0, id3389=1.0/1.0/1.0, id339=1.0/1.0/1.0, id3390=1.0/1.0/1.0, id3391=1.0/1.0/1.0, id3392=1.0/1.0/1.0, id3393=1.0/1.0/1.0, id3394=1.0/1.0/1.0, id3395=1.0/1.0/1.0, id3396=1.0/1.0/1.0, id3397=1.0/1.0/1.0, id3398=1.0/1.0/1.0, id3399=1.0/1.0/1.0, id34=1.0/1.0/1.0, id340=1.0/1.0/1.0, id3400=1.0/1.0/1.0, id3401=1.0/1.0/1.0, id3402=1.0/1.0/1.0, id3403=1.0/1.0/1.0, id3404=1.0/1.0/1.0, id3405=1.0/1.0/1.0, id3406=1.0/1.0/1.0, id3407=1.0/1.0/1.0, id3408=1.0/1.0/1.0, id3409=1.0/1.0/1.0, id341=1.0/1.0/1.0, id3410=1.0/1.0/1.0, id3411=1.0/1.0/1.0, id3412=1.0/1.0/1.0, id3413=1.0/1.0/1.0, id3414=1.0/1.0/1.0, id3415=1.0/1.0/1.0, id3416=1.0/1.0/1.0, id3417=1.0/1.0/1.0, id3418=1.0/1.0/1.0, id3419=1.0/1.0/1.0, id342=1.0/1.0/1.0, id3420=1.0/1.0/1.0, id3421=1.0/1.0/1.0, id3422=1.0/1.0/1.0, id3423=1.0/1.0/1.0, id3424=1.0/1.0/1.0, id3425=1.0/1.0/1.0, id3426=1.0/1.0/1.0, id3427=1.0/1.0/1.0, id3428=1.0/1.0/1.0, id3429=1.0/1.0/1.0, id343=1.0/1.0/1.0, id3430=1.0/1.0/1.0, id3431=1.0/1.0/1.0, id3432=1.0/1.0/1.0, id3433=1.0/1.0/1.0, id3434=1.0/1.0/1.0, id3435=1.0/1.0/1.0, id3436=1.0/1.0/1.0, id3437=1.0/1.0/1.0, id3438=1.0/1.0/1.0, id3439=1.0/1.0/1.0, id344=1.0/1.0/1.0, id3440=1.0/1.0/1.0, id3441=1.0/1.0/1.0, id3442=1.0/1.0/1.0, id3443=1.0/1.0/1.0, id3444=1.0/1.0/1.0, id3445=1.0/1.0/1.0, id3446=1.0/1.0/1.0, id3447=1.0/1.0/1.0, id3448=1.0/1.0/1.0, id3449=1.0/1.0/1.0, id345=1.0/1.0/1.0, id3450=1.0/1.0/1.0, id3451=1.0/1.0/1.0, id3452=1.0/1.0/1.0, id3453=1.0/1.0/1.0, id3454=1.0/1.0/1.0, id3455=1.0/1.0/1.0, id3456=1.0/1.0/1.0, id3457=1.0/1.0/1.0, id3458=1.0/1.0/1.0, id3459=1.0/1.0/1.0, id346=1.0/1.0/1.0, id3460=1.0/1.0/1.0, id3461=1.0/1.0/1.0, id3462=1.0/1.0/1.0, id3463=1.0/1.0/1.0, id3464=1.0/1.0/1.0, id3465=1.0/1.0/1.0, id3466=1.0/1.0/1.0, id3467=1.0/1.0/1.0, id3468=1.0/1.0/1.0, id3469=1.0/1.0/1.0, id347=1.0/1.0/1.0, id3470=1.0/1.0/1.0, id3471=1.0/1.0/1.0, id3472=1.0/1.0/1.0, id3473=1.0/1.0/1.0, id3474=1.0/1.0/1.0, id3475=1.0/1.0/1.0, id3476=1.0/1.0/1.0, id3477=1.0/1.0/1.0, id3478=1.0/1.0/1.0, id3479=1.0/1.0/1.0, id348=1.0/1.0/1.0, id3480=1.0/1.0/1.0, id3481=1.0/1.0/1.0, id3482=1.0/1.0/1.0, id3483=1.0/1.0/1.0, id3484=1.0/1.0/1.0, id3485=1.0/1.0/1.0, id3486=1.0/1.0/1.0, id3487=1.0/1.0/1.0, id3488=1.0/1.0/1.0, id3489=1.0/1.0/1.0, id349=1.0/1.0/1.0, id3490=1.0/1.0/1.0, id3491=1.0/1.0/1.0, id3492=1.0/1.0/1.0, id3493=1.0/1.0/1.0, id3494=1.0/1.0/1.0, id3495=1.0/1.0/1.0, id3496=1.0/1.0/1.0, id3497=1.0/1.0/1.0, id3498=1.0/1.0/1.0, id3499=1.0/1.0/1.0, id35=1.0/1.0/1.0, id350=1.0/1.0/1.0, id3500=1.0/1.0/1.0, id3501=1.0/1.0/1.0, id3502=1.0/1.0/1.0, id3503=1.0/1.0/1.0, id3504=1.0/1.0/1.0, id3505=1.0/1.0/1.0, id3506=1.0/1.0/1.0, id3507=1.0/1.0/1.0, id3508=1.0/1.0/1.0, id3509=1.0/1.0/1.0, id351=1.0/1.0/1.0, id3510=1.0/1.0/1.0, id3511=1.0/1.0/1.0, id3512=1.0/1.0/1.0, id3513=1.0/1.0/1.0, id3514=1.0/1.0/1.0, id3515=1.0/1.0/1.0, id3516=1.0/1.0/1.0, id3517=1.0/1.0/1.0, id3518=1.0/1.0/1.0, id3519=1.0/1.0/1.0, id352=1.0/1.0/1.0, id3520=1.0/1.0/1.0, id3521=1.0/1.0/1.0, id3522=1.0/1.0/1.0, id3523=1.0/1.0/1.0, id3524=1.0/1.0/1.0, id3525=1.0/1.0/1.0, id3526=1.0/1.0/1.0, id3527=1.0/1.0/1.0, id3528=1.0/1.0/1.0, id3529=1.0/1.0/1.0, id353=1.0/1.0/1.0, id3530=1.0/1.0/1.0, id3531=1.0/1.0/1.0, id3532=1.0/1.0/1.0, id3533=1.0/1.0/1.0, id3534=1.0/1.0/1.0, id3535=1.0/1.0/1.0, id3536=1.0/1.0/1.0, id3537=1.0/1.0/1.0, id3538=1.0/1.0/1.0, id3539=1.0/1.0/1.0, id354=1.0/1.0/1.0, id3540=1.0/1.0/1.0, id3541=1.0/1.0/1.0, id3542=1.0/1.0/1.0, id3543=1.0/1.0/1.0, id3544=1.0/1.0/1.0, id3545=1.0/1.0/1.0, id3546=1.0/1.0/1.0, id3547=1.0/1.0/1.0, id3548=1.0/1.0/1.0, id3549=1.0/1.0/1.0, id355=1.0/1.0/1.0, id3550=1.0/1.0/1.0, id3551=1.0/1.0/1.0, id3552=1.0/1.0/1.0, id3553=1.0/1.0/1.0, id3554=1.0/1.0/1.0, id3555=1.0/1.0/1.0, id3556=1.0/1.0/1.0, id3557=1.0/1.0/1.0, id3558=1.0/1.0/1.0, id3559=1.0/1.0/1.0, id356=1.0/1.0/1.0, id3560=1.0/1.0/1.0, id3561=1.0/1.0/1.0, id3562=1.0/1.0/1.0, id3563=1.0/1.0/1.0, id3564=1.0/1.0/1.0, id3565=1.0/1.0/1.0, id3566=1.0/1.0/1.0, id3567=1.0/1.0/1.0, id3568=1.0/1.0/1.0, id3569=1.0/1.0/1.0, id357=1.0/1.0/1.0, id3570=1.0/1.0/1.0, id3571=1.0/1.0/1.0, id3572=1.0/1.0/1.0, id3573=1.0/1.0/1.0, id3574=1.0/1.0/1.0, id3575=1.0/1.0/1.0, id3576=1.0/1.0/1.0, id3577=1.0/1.0/1.0, id3578=1.0/1.0/1.0, id3579=1.0/1.0/1.0, id358=1.0/1.0/1.0, id3580=1.0/1.0/1.0, id3581=1.0/1.0/1.0, id3582=1.0/1.0/1.0, id3583=1.0/1.0/1.0, id3584=1.0/1.0/1.0, id3585=1.0/1.0/1.0, id3586=1.0/1.0/1.0, id3587=1.0/1.0/1.0, id3588=1.0/1.0/1.0, id3589=1.0/1.0/1.0, id359=1.0/1.0/1.0, id3590=1.0/1.0/1.0, id3591=1.0/1.0/1.0, id3592=1.0/1.0/1.0, id3593=1.0/1.0/1.0, id3594=1.0/1.0/1.0, id3595=1.0/1.0/1.0, id3596=1.0/1.0/1.0, id3597=1.0/1.0/1.0, id3598=1.0/1.0/1.0, id3599=1.0/1.0/1.0, id36=1.0/1.0/1.0, id360=1.0/1.0/1.0, id3600=1.0/1.0/1.0, id3601=1.0/1.0/1.0, id3602=1.0/1.0/1.0, id3603=1.0/1.0/1.0, id3604=1.0/1.0/1.0, id3605=1.0/1.0/1.0, id3606=1.0/1.0/1.0, id3607=1.0/1.0/1.0, id3608=1.0/1.0/1.0, id3609=1.0/1.0/1.0, id361=1.0/1.0/1.0, id3610=1.0/1.0/1.0, id3611=1.0/1.0/1.0, id3612=1.0/1.0/1.0, id3613=1.0/1.0/1.0, id3614=1.0/1.0/1.0, id3615=1.0/1.0/1.0, id3616=1.0/1.0/1.0, id3617=1.0/1.0/1.0, id3618=1.0/1.0/1.0, id3619=1.0/1.0/1.0, id362=1.0/1.0/1.0, id3620=1.0/1.0/1.0, id3621=1.0/1.0/1.0, id3622=1.0/1.0/1.0, id3623=1.0/1.0/1.0, id3624=1.0/1.0/1.0, id3625=1.0/1.0/1.0, id3626=1.0/1.0/1.0, id3627=1.0/1.0/1.0, id3628=1.0/1.0/1.0, id3629=1.0/1.0/1.0, id363=1.0/1.0/1.0, id3630=1.0/1.0/1.0, id3631=1.0/1.0/1.0, id3632=1.0/1.0/1.0, id3633=1.0/1.0/1.0, id3634=1.0/1.0/1.0, id3635=1.0/1.0/1.0, id3636=1.0/1.0/1.0, id3637=1.0/1.0/1.0, id3638=1.0/1.0/1.0, id3639=1.0/1.0/1.0, id364=1.0/1.0/1.0, id3640=1.0/1.0/1.0, id3641=1.0/1.0/1.0, id3642=1.0/1.0/1.0, id3643=1.0/1.0/1.0, id3644=1.0/1.0/1.0, id3645=1.0/1.0/1.0, id3646=1.0/1.0/1.0, id3647=1.0/1.0/1.0, id3648=1.0/1.0/1.0, id3649=1.0/1.0/1.0, id365=1.0/1.0/1.0, id3650=1.0/1.0/1.0, id3651=1.0/1.0/1.0, id3652=1.0/1.0/1.0, id3653=1.0/1.0/1.0, id3654=1.0/1.0/1.0, id3655=1.0/1.0/1.0, id3656=1.0/1.0/1.0, id3657=1.0/1.0/1.0, id3658=1.0/1.0/1.0, id3659=1.0/1.0/1.0, id366=1.0/1.0/1.0, id3660=1.0/1.0/1.0, id3661=1.0/1.0/1.0, id3662=1.0/1.0/1.0, id3663=1.0/1.0/1.0, id3664=1.0/1.0/1.0, id3665=1.0/1.0/1.0, id3666=1.0/1.0/1.0, id3667=1.0/1.0/1.0, id3668=1.0/1.0/1.0, id3669=1.0/1.0/1.0, id367=1.0/1.0/1.0, id3670=1.0/1.0/1.0, id3671=1.0/1.0/1.0, id3672=1.0/1.0/1.0, id3673=1.0/1.0/1.0, id3674=1.0/1.0/1.0, id3675=1.0/1.0/1.0, id3676=1.0/1.0/1.0, id3677=1.0/1.0/1.0, id3678=1.0/1.0/1.0, id3679=1.0/1.0/1.0, id368=1.0/1.0/1.0, id3680=1.0/1.0/1.0, id3681=1.0/1.0/1.0, id3682=1.0/1.0/1.0, id3683=1.0/1.0/1.0, id3684=1.0/1.0/1.0, id3685=1.0/1.0/1.0, id3686=1.0/1.0/1.0, id3687=1.0/1.0/1.0, id3688=1.0/1.0/1.0, id3689=1.0/1.0/1.0, id369=1.0/1.0/1.0, id3690=1.0/1.0/1.0, id3691=1.0/1.0/1.0, id3692=1.0/1.0/1.0, id3693=1.0/1.0/1.0, id3694=1.0/1.0/1.0, id3695=1.0/1.0/1.0, id3696=1.0/1.0/1.0, id3697=1.0/1.0/1.0, id3698=1.0/1.0/1.0, id3699=1.0/1.0/1.0, id37=1.0/1.0/1.0, id370=1.0/1.0/1.0, id3700=1.0/1.0/1.0, id3701=1.0/1.0/1.0, id3702=1.0/1.0/1.0, id3703=1.0/1.0/1.0, id3704=1.0/1.0/1.0, id3705=1.0/1.0/1.0, id3706=1.0/1.0/1.0, id3707=1.0/1.0/1.0, id3708=1.0/1.0/1.0, id3709=1.0/1.0/1.0, id371=1.0/1.0/1.0, id3710=1.0/1.0/1.0, id3711=1.0/1.0/1.0, id3712=1.0/1.0/1.0, id3713=1.0/1.0/1.0, id3714=1.0/1.0/1.0, id3715=1.0/1.0/1.0, id3716=1.0/1.0/1.0, id3717=1.0/1.0/1.0, id3718=1.0/1.0/1.0, id3719=1.0/1.0/1.0, id372=1.0/1.0/1.0, id3720=1.0/1.0/1.0, id3721=1.0/1.0/1.0, id3722=1.0/1.0/1.0, id3723=1.0/1.0/1.0, id3724=1.0/1.0/1.0, id3725=1.0/1.0/1.0, id3726=1.0/1.0/1.0, id3727=1.0/1.0/1.0, id3728=1.0/1.0/1.0, id3729=1.0/1.0/1.0, id373=1.0/1.0/1.0, id3730=1.0/1.0/1.0, id3731=1.0/1.0/1.0, id3732=1.0/1.0/1.0, id3733=1.0/1.0/1.0, id3734=1.0/1.0/1.0, id3735=1.0/1.0/1.0, id3736=1.0/1.0/1.0, id3737=1.0/1.0/1.0, id3738=1.0/1.0/1.0, id3739=1.0/1.0/1.0, id374=1.0/1.0/1.0, id3740=1.0/1.0/1.0, id3741=1.0/1.0/1.0, id3742=1.0/1.0/1.0, id3743=1.0/1.0/1.0, id3744=1.0/1.0/1.0, id3745=1.0/1.0/1.0, id3746=1.0/1.0/1.0, id3747=1.0/1.0/1.0, id3748=1.0/1.0/1.0, id3749=1.0/1.0/1.0, id375=1.0/1.0/1.0, id3750=1.0/1.0/1.0, id3751=1.0/1.0/1.0, id3752=1.0/1.0/1.0, id3753=1.0/1.0/1.0, id3754=1.0/1.0/1.0, id3755=1.0/1.0/1.0, id3756=1.0/1.0/1.0, id3757=1.0/1.0/1.0, id3758=1.0/1.0/1.0, id3759=1.0/1.0/1.0, id376=1.0/1.0/1.0, id3760=1.0/1.0/1.0, id3761=1.0/1.0/1.0, id3762=1.0/1.0/1.0, id3763=1.0/1.0/1.0, id3764=1.0/1.0/1.0, id3765=1.0/1.0/1.0, id3766=1.0/1.0/1.0, id3767=1.0/1.0/1.0, id3768=1.0/1.0/1.0, id3769=1.0/1.0/1.0, id377=1.0/1.0/1.0, id3770=1.0/1.0/1.0, id3771=1.0/1.0/1.0, id3772=1.0/1.0/1.0, id3773=1.0/1.0/1.0, id3774=1.0/1.0/1.0, id3775=1.0/1.0/1.0, id3776=1.0/1.0/1.0, id3777=1.0/1.0/1.0, id3778=1.0/1.0/1.0, id3779=1.0/1.0/1.0, id378=1.0/1.0/1.0, id3780=1.0/1.0/1.0, id3781=1.0/1.0/1.0, id3782=1.0/1.0/1.0, id3783=1.0/1.0/1.0, id3784=1.0/1.0/1.0, id3785=1.0/1.0/1.0, id3786=1.0/1.0/1.0, id3787=1.0/1.0/1.0, id3788=1.0/1.0/1.0, id3789=1.0/1.0/1.0, id379=1.0/1.0/1.0, id3790=1.0/1.0/1.0, id3791=1.0/1.0/1.0, id3792=1.0/1.0/1.0, id3793=1.0/1.0/1.0, id3794=1.0/1.0/1.0, id3795=1.0/1.0/1.0, id3796=1.0/1.0/1.0, id3797=1.0/1.0/1.0, id3798=1.0/1.0/1.0, id3799=1.0/1.0/1.0, id38=1.0/1.0/1.0, id380=1.0/1.0/1.0, id3800=1.0/1.0/1.0, id3801=1.0/1.0/1.0, id3802=1.0/1.0/1.0, id3803=1.0/1.0/1.0, id3804=1.0/1.0/1.0, id3805=1.0/1.0/1.0, id3806=1.0/1.0/1.0, id3807=1.0/1.0/1.0, id3808=1.0/1.0/1.0, id3809=1.0/1.0/1.0, id381=1.0/1.0/1.0, id3810=1.0/1.0/1.0, id3811=1.0/1.0/1.0, id3812=1.0/1.0/1.0, id3813=1.0/1.0/1.0, id3814=1.0/1.0/1.0, id3815=1.0/1.0/1.0, id3816=1.0/1.0/1.0, id3817=1.0/1.0/1.0, id3818=1.0/1.0/1.0, id3819=1.0/1.0/1.0, id382=1.0/1.0/1.0, id3820=1.0/1.0/1.0, id3821=1.0/1.0/1.0, id3822=1.0/1.0/1.0, id3823=1.0/1.0/1.0, id3824=1.0/1.0/1.0, id3825=1.0/1.0/1.0, id3826=1.0/1.0/1.0, id3827=1.0/1.0/1.0, id3828=1.0/1.0/1.0, id3829=1.0/1.0/1.0, id383=1.0/1.0/1.0, id3830=1.0/1.0/1.0, id3831=1.0/1.0/1.0, id3832=1.0/1.0/1.0, id3833=1.0/1.0/1.0, id3834=1.0/1.0/1.0, id3835=1.0/1.0/1.0, id3836=1.0/1.0/1.0, id3837=1.0/1.0/1.0, id3838=1.0/1.0/1.0, id3839=1.0/1.0/1.0, id384=1.0/1.0/1.0, id3840=1.0/1.0/1.0, id3841=1.0/1.0/1.0, id3842=1.0/1.0/1.0, id3843=1.0/1.0/1.0, id3844=1.0/1.0/1.0, id3845=1.0/1.0/1.0, id3846=1.0/1.0/1.0, id3847=1.0/1.0/1.0, id3848=1.0/1.0/1.0, id3849=1.0/1.0/1.0, id385=1.0/1.0/1.0, id3850=1.0/1.0/1.0, id3851=1.0/1.0/1.0, id3852=1.0/1.0/1.0, id3853=1.0/1.0/1.0, id3854=1.0/1.0/1.0, id3855=1.0/1.0/1.0, id3856=1.0/1.0/1.0, id3857=1.0/1.0/1.0, id3858=1.0/1.0/1.0, id3859=1.0/1.0/1.0, id386=1.0/1.0/1.0, id3860=1.0/1.0/1.0, id3861=1.0/1.0/1.0, id3862=1.0/1.0/1.0, id3863=1.0/1.0/1.0, id3864=1.0/1.0/1.0, id3865=1.0/1.0/1.0, id3866=1.0/1.0/1.0, id3867=1.0/1.0/1.0, id3868=1.0/1.0/1.0, id3869=1.0/1.0/1.0, id387=1.0/1.0/1.0, id3870=1.0/1.0/1.0, id3871=1.0/1.0/1.0, id3872=1.0/1.0/1.0, id3873=1.0/1.0/1.0, id3874=1.0/1.0/1.0, id3875=1.0/1.0/1.0, id3876=1.0/1.0/1.0, id3877=1.0/1.0/1.0, id3878=1.0/1.0/1.0, id3879=1.0/1.0/1.0, id388=1.0/1.0/1.0, id3880=1.0/1.0/1.0, id3881=1.0/1.0/1.0, id3882=1.0/1.0/1.0, id3883=1.0/1.0/1.0, id3884=1.0/1.0/1.0, id3885=1.0/1.0/1.0, id3886=1.0/1.0/1.0, id3887=1.0/1.0/1.0, id3888=1.0/1.0/1.0, id3889=1.0/1.0/1.0, id389=1.0/1.0/1.0, id3890=1.0/1.0/1.0, id3891=1.0/1.0/1.0, id3892=1.0/1.0/1.0, id3893=1.0/1.0/1.0, id3894=1.0/1.0/1.0, id3895=1.0/1.0/1.0, id3896=1.0/1.0/1.0, id3897=1.0/1.0/1.0, id3898=1.0/1.0/1.0, id3899=1.0/1.0/1.0, id39=1.0/1.0/1.0, id390=1.0/1.0/1.0, id3900=1.0/1.0/1.0, id3901=1.0/1.0/1.0, id3902=1.0/1.0/1.0, id3903=1.0/1.0/1.0, id3904=1.0/1.0/1.0, id3905=1.0/1.0/1.0, id3906=1.0/1.0/1.0, id3907=1.0/1.0/1.0, id3908=1.0/1.0/1.0, id3909=1.0/1.0/1.0, id391=1.0/1.0/1.0, id3910=1.0/1.0/1.0, id3911=1.0/1.0/1.0, id3912=1.0/1.0/1.0, id3913=1.0/1.0/1.0, id3914=1.0/1.0/1.0, id3915=1.0/1.0/1.0, id3916=1.0/1.0/1.0, id3917=1.0/1.0/1.0, id3918=1.0/1.0/1.0, id3919=1.0/1.0/1.0, id392=1.0/1.0/1.0, id3920=1.0/1.0/1.0, id3921=1.0/1.0/1.0, id3922=1.0/1.0/1.0, id3923=1.0/1.0/1.0, id3924=1.0/1.0/1.0, id3925=1.0/1.0/1.0, id3926=1.0/1.0/1.0, id3927=1.0/1.0/1.0, id3928=1.0/1.0/1.0, id3929=1.0/1.0/1.0, id393=1.0/1.0/1.0, id3930=1.0/1.0/1.0, id3931=1.0/1.0/1.0, id3932=1.0/1.0/1.0, id3933=1.0/1.0/1.0, id3934=1.0/1.0/1.0, id3935=1.0/1.0/1.0, id3936=1.0/1.0/1.0, id3937=1.0/1.0/1.0, id3938=1.0/1.0/1.0, id3939=1.0/1.0/1.0, id394=1.0/1.0/1.0, id3940=1.0/1.0/1.0, id3941=1.0/1.0/1.0, id3942=1.0/1.0/1.0, id3943=1.0/1.0/1.0, id3944=1.0/1.0/1.0, id3945=1.0/1.0/1.0, id3946=1.0/1.0/1.0, id3947=1.0/1.0/1.0, id3948=1.0/1.0/1.0, id3949=1.0/1.0/1.0, id395=1.0/1.0/1.0, id3950=1.0/1.0/1.0, id3951=1.0/1.0/1.0, id3952=1.0/1.0/1.0, id3953=1.0/1.0/1.0, id3954=1.0/1.0/1.0, id3955=1.0/1.0/1.0, id3956=1.0/1.0/1.0, id3957=1.0/1.0/1.0, id3958=1.0/1.0/1.0, id3959=1.0/1.0/1.0, id396=1.0/1.0/1.0, id3960=1.0/1.0/1.0, id3961=1.0/1.0/1.0, id3962=1.0/1.0/1.0, id3963=1.0/1.0/1.0, id3964=1.0/1.0/1.0, id3965=1.0/1.0/1.0, id3966=1.0/1.0/1.0, id3967=1.0/1.0/1.0, id3968=1.0/1.0/1.0, id3969=1.0/1.0/1.0, id397=1.0/1.0/1.0, id3970=1.0/1.0/1.0, id3971=1.0/1.0/1.0, id3972=1.0/1.0/1.0, id3973=1.0/1.0/1.0, id3974=1.0/1.0/1.0, id3975=1.0/1.0/1.0, id3976=1.0/1.0/1.0, id3977=1.0/1.0/1.0, id3978=1.0/1.0/1.0, id3979=1.0/1.0/1.0, id398=1.0/1.0/1.0, id3980=1.0/1.0/1.0, id3981=1.0/1.0/1.0, id3982=1.0/1.0/1.0, id3983=1.0/1.0/1.0, id3984=1.0/1.0/1.0, id3985=1.0/1.0/1.0, id3986=1.0/1.0/1.0, id3987=1.0/1.0/1.0, id3988=1.0/1.0/1.0, id3989=1.0/1.0/1.0, id399=1.0/1.0/1.0, id3990=1.0/1.0/1.0, id3991=1.0/1.0/1.0, id3992=1.0/1.0/1.0, id3993=1.0/1.0/1.0, id3994=1.0/1.0/1.0, id3995=1.0/1.0/1.0, id3996=1.0/1.0/1.0, id3997=1.0/1.0/1.0, id3998=1.0/1.0/1.0, id3999=1.0/1.0/1.0, id4=1.0/1.0/1.0, id40=1.0/1.0/1.0, id400=1.0/1.0/1.0, id4000=1.0/1.0/1.0, id4001=1.0/1.0/1.0, id4002=1.0/1.0/1.0, id4003=1.0/1.0/1.0, id4004=1.0/1.0/1.0, id4005=1.0/1.0/1.0, id4006=1.0/1.0/1.0, id4007=1.0/1.0/1.0, id4008=1.0/1.0/1.0, id4009=1.0/1.0/1.0, id401=1.0/1.0/1.0, id4010=1.0/1.0/1.0, id4011=1.0/1.0/1.0, id4012=1.0/1.0/1.0, id4013=1.0/1.0/1.0, id4014=1.0/1.0/1.0, id4015=1.0/1.0/1.0, id4016=1.0/1.0/1.0, id4017=1.0/1.0/1.0, id4018=1.0/1.0/1.0, id4019=1.0/1.0/1.0, id402=1.0/1.0/1.0, id4020=1.0/1.0/1.0, id4021=1.0/1.0/1.0, id4022=1.0/1.0/1.0, id4023=1.0/1.0/1.0, id4024=1.0/1.0/1.0, id4025=1.0/1.0/1.0, id4026=1.0/1.0/1.0, id4027=1.0/1.0/1.0, id4028=1.0/1.0/1.0, id4029=1.0/1.0/1.0, id403=1.0/1.0/1.0, id4030=1.0/1.0/1.0, id4031=1.0/1.0/1.0, id4032=1.0/1.0/1.0, id4033=1.0/1.0/1.0, id4034=1.0/1.0/1.0, id4035=1.0/1.0/1.0, id4036=1.0/1.0/1.0, id4037=1.0/1.0/1.0, id4038=1.0/1.0/1.0, id4039=1.0/1.0/1.0, id404=1.0/1.0/1.0, id4040=1.0/1.0/1.0, id4041=1.0/1.0/1.0, id4042=1.0/1.0/1.0, id4043=1.0/1.0/1.0, id4044=1.0/1.0/1.0, id4045=1.0/1.0/1.0, id4046=1.0/1.0/1.0, id4047=1.0/1.0/1.0, id4048=1.0/1.0/1.0, id4049=1.0/1.0/1.0, id405=1.0/1.0/1.0, id4050=1.0/1.0/1.0, id4051=1.0/1.0/1.0, id4052=1.0/1.0/1.0, id4053=1.0/1.0/1.0, id4054=1.0/1.0/1.0, id4055=1.0/1.0/1.0, id4056=1.0/1.0/1.0, id4057=1.0/1.0/1.0, id4058=1.0/1.0/1.0, id4059=1.0/1.0/1.0, id406=1.0/1.0/1.0, id4060=1.0/1.0/1.0, id4061=1.0/1.0/1.0, id4062=1.0/1.0/1.0, id4063=1.0/1.0/1.0, id4064=1.0/1.0/1.0, id4065=1.0/1.0/1.0, id4066=1.0/1.0/1.0, id4067=1.0/1.0/1.0, id4068=1.0/1.0/1.0, id4069=1.0/1.0/1.0, id407=1.0/1.0/1.0, id4070=1.0/1.0/1.0, id4071=1.0/1.0/1.0, id4072=1.0/1.0/1.0, id4073=1.0/1.0/1.0, id4074=1.0/1.0/1.0, id4075=1.0/1.0/1.0, id4076=1.0/1.0/1.0, id4077=1.0/1.0/1.0, id4078=1.0/1.0/1.0, id4079=1.0/1.0/1.0, id408=1.0/1.0/1.0, id4080=1.0/1.0/1.0, id4081=1.0/1.0/1.0, id4082=1.0/1.0/1.0, id4083=1.0/1.0/1.0, id4084=1.0/1.0/1.0, id4085=1.0/1.0/1.0, id4086=1.0/1.0/1.0, id4087=1.0/1.0/1.0, id4088=1.0/1.0/1.0, id4089=1.0/1.0/1.0, id409=1.0/1.0/1.0, id4090=1.0/1.0/1.0, id4091=1.0/1.0/1.0, id4092=1.0/1.0/1.0, id4093=1.0/1.0/1.0, id4094=1.0/1.0/1.0, id4095=1.0/1.0/1.0, id4096=1.0/1.0/1.0, id4097=1.0/1.0/1.0, id4098=1.0/1.0/1.0, id4099=1.0/1.0/1.0, id41=1.0/1.0/1.0, id410=1.0/1.0/1.0, id4100=1.0/1.0/1.0, id4101=1.0/1.0/1.0, id4102=1.0/1.0/1.0, id4103=1.0/1.0/1.0, id4104=1.0/1.0/1.0, id4105=1.0/1.0/1.0, id4106=1.0/1.0/1.0, id4107=1.0/1.0/1.0, id4108=1.0/1.0/1.0, id4109=1.0/1.0/1.0, id411=1.0/1.0/1.0, id4110=1.0/1.0/1.0, id4111=1.0/1.0/1.0, id4112=1.0/1.0/1.0, id4113=1.0/1.0/1.0, id4114=1.0/1.0/1.0, id4115=1.0/1.0/1.0, id4116=1.0/1.0/1.0, id4117=1.0/1.0/1.0, id4118=1.0/1.0/1.0, id4119=1.0/1.0/1.0, id412=1.0/1.0/1.0, id4120=1.0/1.0/1.0, id4121=1.0/1.0/1.0, id4122=1.0/1.0/1.0, id4123=1.0/1.0/1.0, id4124=1.0/1.0/1.0, id4125=1.0/1.0/1.0, id4126=1.0/1.0/1.0, id4127=1.0/1.0/1.0, id4128=1.0/1.0/1.0, id4129=1.0/1.0/1.0, id413=1.0/1.0/1.0, id4130=1.0/1.0/1.0, id4131=1.0/1.0/1.0, id4132=1.0/1.0/1.0, id4133=1.0/1.0/1.0, id4134=1.0/1.0/1.0, id4135=1.0/1.0/1.0, id4136=1.0/1.0/1.0, id4137=1.0/1.0/1.0, id4138=1.0/1.0/1.0, id4139=1.0/1.0/1.0, id414=1.0/1.0/1.0, id4140=1.0/1.0/1.0, id4141=1.0/1.0/1.0, id4142=1.0/1.0/1.0, id4143=1.0/1.0/1.0, id4144=1.0/1.0/1.0, id4145=1.0/1.0/1.0, id4146=1.0/1.0/1.0, id4147=1.0/1.0/1.0, id4148=1.0/1.0/1.0, id4149=1.0/1.0/1.0, id415=1.0/1.0/1.0, id4150=1.0/1.0/1.0, id4151=1.0/1.0/1.0, id4152=1.0/1.0/1.0, id4153=1.0/1.0/1.0, id4154=1.0/1.0/1.0, id4155=1.0/1.0/1.0, id4156=1.0/1.0/1.0, id4157=1.0/1.0/1.0, id4158=1.0/1.0/1.0, id4159=1.0/1.0/1.0, id416=1.0/1.0/1.0, id4160=1.0/1.0/1.0, id4161=1.0/1.0/1.0, id4162=1.0/1.0/1.0, id4163=1.0/1.0/1.0, id4164=1.0/1.0/1.0, id4165=1.0/1.0/1.0, id4166=1.0/1.0/1.0, id4167=1.0/1.0/1.0, id4168=1.0/1.0/1.0, id4169=1.0/1.0/1.0, id417=1.0/1.0/1.0, id4170=1.0/1.0/1.0, id4171=1.0/1.0/1.0, id4172=1.0/1.0/1.0, id4173=1.0/1.0/1.0, id4174=1.0/1.0/1.0, id4175=1.0/1.0/1.0, id4176=1.0/1.0/1.0, id4177=1.0/1.0/1.0, id4178=1.0/1.0/1.0, id4179=1.0/1.0/1.0, id418=1.0/1.0/1.0, id4180=1.0/1.0/1.0, id4181=1.0/1.0/1.0, id4182=1.0/1.0/1.0, id4183=1.0/1.0/1.0, id4184=1.0/1.0/1.0, id4185=1.0/1.0/1.0, id4186=1.0/1.0/1.0, id4187=1.0/1.0/1.0, id4188=1.0/1.0/1.0, id4189=1.0/1.0/1.0, id419=1.0/1.0/1.0, id4190=1.0/1.0/1.0, id4191=1.0/1.0/1.0, id4192=1.0/1.0/1.0, id4193=1.0/1.0/1.0, id4194=1.0/1.0/1.0, id4195=1.0/1.0/1.0, id4196=1.0/1.0/1.0, id4197=1.0/1.0/1.0, id4198=1.0/1.0/1.0, id4199=1.0/1.0/1.0, id42=1.0/1.0/1.0, id420=1.0/1.0/1.0, id4200=1.0/1.0/1.0, id4201=1.0/1.0/1.0, id4202=1.0/1.0/1.0, id4203=1.0/1.0/1.0, id4204=1.0/1.0/1.0, id4205=1.0/1.0/1.0, id4206=1.0/1.0/1.0, id4207=1.0/1.0/1.0, id4208=1.0/1.0/1.0, id4209=1.0/1.0/1.0, id421=1.0/1.0/1.0, id4210=1.0/1.0/1.0, id4211=1.0/1.0/1.0, id4212=1.0/1.0/1.0, id4213=1.0/1.0/1.0, id4214=1.0/1.0/1.0, id4215=1.0/1.0/1.0, id4216=1.0/1.0/1.0, id4217=1.0/1.0/1.0, id4218=1.0/1.0/1.0, id4219=1.0/1.0/1.0, id422=1.0/1.0/1.0, id4220=1.0/1.0/1.0, id4221=1.0/1.0/1.0, id4222=1.0/1.0/1.0, id4223=1.0/1.0/1.0, id4224=1.0/1.0/1.0, id4225=1.0/1.0/1.0, id4226=1.0/1.0/1.0, id4227=1.0/1.0/1.0, id4228=1.0/1.0/1.0, id4229=1.0/1.0/1.0, id423=1.0/1.0/1.0, id4230=1.0/1.0/1.0, id4231=1.0/1.0/1.0, id4232=1.0/1.0/1.0, id4233=1.0/1.0/1.0, id4234=1.0/1.0/1.0, id4235=1.0/1.0/1.0, id4236=1.0/1.0/1.0, id4237=1.0/1.0/1.0, id4238=1.0/1.0/1.0, id4239=1.0/1.0/1.0, id424=1.0/1.0/1.0, id4240=1.0/1.0/1.0, id4241=1.0/1.0/1.0, id4242=1.0/1.0/1.0, id4243=1.0/1.0/1.0, id4244=1.0/1.0/1.0, id4245=1.0/1.0/1.0, id4246=1.0/1.0/1.0, id4247=1.0/1.0/1.0, id4248=1.0/1.0/1.0, id4249=1.0/1.0/1.0, id425=1.0/1.0/1.0, id4250=1.0/1.0/1.0, id4251=1.0/1.0/1.0, id4252=1.0/1.0/1.0, id4253=1.0/1.0/1.0, id4254=1.0/1.0/1.0, id4255=1.0/1.0/1.0, id4256=1.0/1.0/1.0, id4257=1.0/1.0/1.0, id4258=1.0/1.0/1.0, id4259=1.0/1.0/1.0, id426=1.0/1.0/1.0, id4260=1.0/1.0/1.0, id4261=1.0/1.0/1.0, id4262=1.0/1.0/1.0, id4263=1.0/1.0/1.0, id4264=1.0/1.0/1.0, id4265=1.0/1.0/1.0, id4266=1.0/1.0/1.0, id4267=1.0/1.0/1.0, id4268=1.0/1.0/1.0, id4269=1.0/1.0/1.0, id427=1.0/1.0/1.0, id4270=1.0/1.0/1.0, id4271=1.0/1.0/1.0, id4272=1.0/1.0/1.0, id4273=1.0/1.0/1.0, id4274=1.0/1.0/1.0, id4275=1.0/1.0/1.0, id4276=1.0/1.0/1.0, id4277=1.0/1.0/1.0, id4278=1.0/1.0/1.0, id4279=1.0/1.0/1.0, id428=1.0/1.0/1.0, id4280=1.0/1.0/1.0, id4281=1.0/1.0/1.0, id4282=1.0/1.0/1.0, id4283=1.0/1.0/1.0, id4284=1.0/1.0/1.0, id4285=1.0/1.0/1.0, id4286=1.0/1.0/1.0, id4287=1.0/1.0/1.0, id4288=1.0/1.0/1.0, id4289=1.0/1.0/1.0, id429=1.0/1.0/1.0, id4290=1.0/1.0/1.0, id4291=1.0/1.0/1.0, id4292=1.0/1.0/1.0, id4293=1.0/1.0/1.0, id4294=1.0/1.0/1.0, id4295=1.0/1.0/1.0, id4296=1.0/1.0/1.0, id4297=1.0/1.0/1.0, id4298=1.0/1.0/1.0, id4299=1.0/1.0/1.0, id43=1.0/1.0/1.0, id430=1.0/1.0/1.0, id4300=1.0/1.0/1.0, id4301=1.0/1.0/1.0, id4302=1.0/1.0/1.0, id4303=1.0/1.0/1.0, id4304=1.0/1.0/1.0, id4305=1.0/1.0/1.0, id4306=1.0/1.0/1.0, id4307=1.0/1.0/1.0, id4308=1.0/1.0/1.0, id4309=1.0/1.0/1.0, id431=1.0/1.0/1.0, id4310=1.0/1.0/1.0, id4311=1.0/1.0/1.0, id4312=1.0/1.0/1.0, id4313=1.0/1.0/1.0, id4314=1.0/1.0/1.0, id4315=1.0/1.0/1.0, id4316=1.0/1.0/1.0, id4317=1.0/1.0/1.0, id4318=1.0/1.0/1.0, id4319=1.0/1.0/1.0, id432=1.0/1.0/1.0, id4320=1.0/1.0/1.0, id4321=1.0/1.0/1.0, id4322=1.0/1.0/1.0, id4323=1.0/1.0/1.0, id4324=1.0/1.0/1.0, id4325=1.0/1.0/1.0, id4326=1.0/1.0/1.0, id4327=1.0/1.0/1.0, id4328=1.0/1.0/1.0, id4329=1.0/1.0/1.0, id433=1.0/1.0/1.0, id4330=1.0/1.0/1.0, id4331=1.0/1.0/1.0, id4332=1.0/1.0/1.0, id4333=1.0/1.0/1.0, id4334=1.0/1.0/1.0, id4335=1.0/1.0/1.0, id4336=1.0/1.0/1.0, id4337=1.0/1.0/1.0, id4338=1.0/1.0/1.0, id4339=1.0/1.0/1.0, id434=1.0/1.0/1.0, id4340=1.0/1.0/1.0, id4341=1.0/1.0/1.0, id4342=1.0/1.0/1.0, id4343=1.0/1.0/1.0, id4344=1.0/1.0/1.0, id4345=1.0/1.0/1.0, id4346=1.0/1.0/1.0, id4347=1.0/1.0/1.0, id4348=1.0/1.0/1.0, id4349=1.0/1.0/1.0, id435=1.0/1.0/1.0, id4350=1.0/1.0/1.0, id4351=1.0/1.0/1.0, id4352=1.0/1.0/1.0, id4353=1.0/1.0/1.0, id4354=1.0/1.0/1.0, id4355=1.0/1.0/1.0, id4356=1.0/1.0/1.0, id4357=1.0/1.0/1.0, id4358=1.0/1.0/1.0, id4359=1.0/1.0/1.0, id436=1.0/1.0/1.0, id4360=1.0/1.0/1.0, id4361=1.0/1.0/1.0, id4362=1.0/1.0/1.0, id4363=1.0/1.0/1.0, id4364=1.0/1.0/1.0, id4365=1.0/1.0/1.0, id4366=1.0/1.0/1.0, id4367=1.0/1.0/1.0, id4368=1.0/1.0/1.0, id4369=1.0/1.0/1.0, id437=1.0/1.0/1.0, id4370=1.0/1.0/1.0, id4371=1.0/1.0/1.0, id4372=1.0/1.0/1.0, id4373=1.0/1.0/1.0, id4374=1.0/1.0/1.0, id4375=1.0/1.0/1.0, id4376=1.0/1.0/1.0, id4377=1.0/1.0/1.0, id4378=1.0/1.0/1.0, id4379=1.0/1.0/1.0, id438=1.0/1.0/1.0, id4380=1.0/1.0/1.0, id4381=1.0/1.0/1.0, id4382=1.0/1.0/1.0, id4383=1.0/1.0/1.0, id4384=1.0/1.0/1.0, id4385=1.0/1.0/1.0, id4386=1.0/1.0/1.0, id4387=1.0/1.0/1.0, id4388=1.0/1.0/1.0, id4389=1.0/1.0/1.0, id439=1.0/1.0/1.0, id4390=1.0/1.0/1.0, id4391=1.0/1.0/1.0, id4392=1.0/1.0/1.0, id4393=1.0/1.0/1.0, id4394=1.0/1.0/1.0, id4395=1.0/1.0/1.0, id4396=1.0/1.0/1.0, id4397=1.0/1.0/1.0, id4398=1.0/1.0/1.0, id4399=1.0/1.0/1.0, id44=1.0/1.0/1.0, id440=1.0/1.0/1.0, id4400=1.0/1.0/1.0, id4401=1.0/1.0/1.0, id4402=1.0/1.0/1.0, id4403=1.0/1.0/1.0, id4404=1.0/1.0/1.0, id4405=1.0/1.0/1.0, id4406=1.0/1.0/1.0, id4407=1.0/1.0/1.0, id4408=1.0/1.0/1.0, id4409=1.0/1.0/1.0, id441=1.0/1.0/1.0, id4410=1.0/1.0/1.0, id4411=1.0/1.0/1.0, id4412=1.0/1.0/1.0, id4413=1.0/1.0/1.0, id4414=1.0/1.0/1.0, id4415=1.0/1.0/1.0, id4416=1.0/1.0/1.0, id4417=1.0/1.0/1.0, id4418=1.0/1.0/1.0, id4419=1.0/1.0/1.0, id442=1.0/1.0/1.0, id4420=1.0/1.0/1.0, id4421=1.0/1.0/1.0, id4422=1.0/1.0/1.0, id4423=1.0/1.0/1.0, id4424=1.0/1.0/1.0, id4425=1.0/1.0/1.0, id4426=1.0/1.0/1.0, id4427=1.0/1.0/1.0, id4428=1.0/1.0/1.0, id4429=1.0/1.0/1.0, id443=1.0/1.0/1.0, id4430=1.0/1.0/1.0, id4431=1.0/1.0/1.0, id4432=1.0/1.0/1.0, id4433=1.0/1.0/1.0, id4434=1.0/1.0/1.0, id4435=1.0/1.0/1.0, id4436=1.0/1.0/1.0, id4437=1.0/1.0/1.0, id4438=1.0/1.0/1.0, id4439=1.0/1.0/1.0, id444=1.0/1.0/1.0, id4440=1.0/1.0/1.0, id4441=1.0/1.0/1.0, id4442=1.0/1.0/1.0, id4443=1.0/1.0/1.0, id4444=1.0/1.0/1.0, id4445=1.0/1.0/1.0, id4446=1.0/1.0/1.0, id4447=1.0/1.0/1.0, id4448=1.0/1.0/1.0, id4449=1.0/1.0/1.0, id445=1.0/1.0/1.0, id4450=1.0/1.0/1.0, id4451=1.0/1.0/1.0, id4452=1.0/1.0/1.0, id4453=1.0/1.0/1.0, id4454=1.0/1.0/1.0, id4455=1.0/1.0/1.0, id4456=1.0/1.0/1.0, id4457=1.0/1.0/1.0, id4458=1.0/1.0/1.0, id4459=1.0/1.0/1.0, id446=1.0/1.0/1.0, id4460=1.0/1.0/1.0, id4461=1.0/1.0/1.0, id4462=1.0/1.0/1.0, id4463=1.0/1.0/1.0, id4464=1.0/1.0/1.0, id4465=1.0/1.0/1.0, id4466=1.0/1.0/1.0, id4467=1.0/1.0/1.0, id4468=1.0/1.0/1.0, id4469=1.0/1.0/1.0, id447=1.0/1.0/1.0, id4470=1.0/1.0/1.0, id4471=1.0/1.0/1.0, id4472=1.0/1.0/1.0, id4473=1.0/1.0/1.0, id4474=1.0/1.0/1.0, id4475=1.0/1.0/1.0, id4476=1.0/1.0/1.0, id4477=1.0/1.0/1.0, id4478=1.0/1.0/1.0, id4479=1.0/1.0/1.0, id448=1.0/1.0/1.0, id4480=1.0/1.0/1.0, id4481=1.0/1.0/1.0, id4482=1.0/1.0/1.0, id4483=1.0/1.0/1.0, id4484=1.0/1.0/1.0, id4485=1.0/1.0/1.0, id4486=1.0/1.0/1.0, id4487=1.0/1.0/1.0, id4488=1.0/1.0/1.0, id4489=1.0/1.0/1.0, id449=1.0/1.0/1.0, id4490=1.0/1.0/1.0, id4491=1.0/1.0/1.0, id4492=1.0/1.0/1.0, id4493=1.0/1.0/1.0, id4494=1.0/1.0/1.0, id4495=1.0/1.0/1.0, id4496=1.0/1.0/1.0, id4497=1.0/1.0/1.0, id4498=1.0/1.0/1.0, id4499=1.0/1.0/1.0, id45=1.0/1.0/1.0, id450=1.0/1.0/1.0, id4500=1.0/1.0/1.0, id4501=1.0/1.0/1.0, id4502=1.0/1.0/1.0, id4503=1.0/1.0/1.0, id4504=1.0/1.0/1.0, id4505=1.0/1.0/1.0, id4506=1.0/1.0/1.0, id4507=1.0/1.0/1.0, id4508=1.0/1.0/1.0, id4509=1.0/1.0/1.0, id451=1.0/1.0/1.0, id4510=1.0/1.0/1.0, id4511=1.0/1.0/1.0, id4512=1.0/1.0/1.0, id4513=1.0/1.0/1.0, id4514=1.0/1.0/1.0, id4515=1.0/1.0/1.0, id4516=1.0/1.0/1.0, id4517=1.0/1.0/1.0, id4518=1.0/1.0/1.0, id4519=1.0/1.0/1.0, id452=1.0/1.0/1.0, id4520=1.0/1.0/1.0, id4521=1.0/1.0/1.0, id4522=1.0/1.0/1.0, id4523=1.0/1.0/1.0, id4524=1.0/1.0/1.0, id4525=1.0/1.0/1.0, id4526=1.0/1.0/1.0, id4527=1.0/1.0/1.0, id4528=1.0/1.0/1.0, id4529=1.0/1.0/1.0, id453=1.0/1.0/1.0, id4530=1.0/1.0/1.0, id4531=1.0/1.0/1.0, id4532=1.0/1.0/1.0, id4533=1.0/1.0/1.0, id4534=1.0/1.0/1.0, id4535=1.0/1.0/1.0, id4536=1.0/1.0/1.0, id4537=1.0/1.0/1.0, id4538=1.0/1.0/1.0, id4539=1.0/1.0/1.0, id454=1.0/1.0/1.0, id4540=1.0/1.0/1.0, id4541=1.0/1.0/1.0, id4542=1.0/1.0/1.0, id4543=1.0/1.0/1.0, id4544=1.0/1.0/1.0, id4545=1.0/1.0/1.0, id4546=1.0/1.0/1.0, id4547=1.0/1.0/1.0, id4548=1.0/1.0/1.0, id4549=1.0/1.0/1.0, id455=1.0/1.0/1.0, id4550=1.0/1.0/1.0, id4551=1.0/1.0/1.0, id4552=1.0/1.0/1.0, id4553=1.0/1.0/1.0, id4554=1.0/1.0/1.0, id4555=1.0/1.0/1.0, id4556=1.0/1.0/1.0, id4557=1.0/1.0/1.0, id4558=1.0/1.0/1.0, id4559=1.0/1.0/1.0, id456=1.0/1.0/1.0, id4560=1.0/1.0/1.0, id4561=1.0/1.0/1.0, id4562=1.0/1.0/1.0, id4563=1.0/1.0/1.0, id4564=1.0/1.0/1.0, id4565=1.0/1.0/1.0, id4566=1.0/1.0/1.0, id4567=1.0/1.0/1.0, id4568=1.0/1.0/1.0, id4569=1.0/1.0/1.0, id457=1.0/1.0/1.0, id4570=1.0/1.0/1.0, id4571=1.0/1.0/1.0, id4572=1.0/1.0/1.0, id4573=1.0/1.0/1.0, id4574=1.0/1.0/1.0, id4575=1.0/1.0/1.0, id4576=1.0/1.0/1.0, id4577=1.0/1.0/1.0, id4578=1.0/1.0/1.0, id4579=1.0/1.0/1.0, id458=1.0/1.0/1.0, id4580=1.0/1.0/1.0, id4581=1.0/1.0/1.0, id4582=1.0/1.0/1.0, id4583=1.0/1.0/1.0, id4584=1.0/1.0/1.0, id4585=1.0/1.0/1.0, id4586=1.0/1.0/1.0, id4587=1.0/1.0/1.0, id4588=1.0/1.0/1.0, id4589=1.0/1.0/1.0, id459=1.0/1.0/1.0, id4590=1.0/1.0/1.0, id4591=1.0/1.0/1.0, id4592=1.0/1.0/1.0, id4593=1.0/1.0/1.0, id4594=1.0/1.0/1.0, id4595=1.0/1.0/1.0, id4596=1.0/1.0/1.0, id4597=1.0/1.0/1.0, id4598=1.0/1.0/1.0, id4599=1.0/1.0/1.0, id46=1.0/1.0/1.0, id460=1.0/1.0/1.0, id4600=1.0/1.0/1.0, id4601=1.0/1.0/1.0, id4602=1.0/1.0/1.0, id4603=1.0/1.0/1.0, id4604=1.0/1.0/1.0, id4605=1.0/1.0/1.0, id4606=1.0/1.0/1.0, id4607=1.0/1.0/1.0, id4608=1.0/1.0/1.0, id4609=1.0/1.0/1.0, id461=1.0/1.0/1.0, id4610=1.0/1.0/1.0, id4611=1.0/1.0/1.0, id4612=1.0/1.0/1.0, id4613=1.0/1.0/1.0, id4614=1.0/1.0/1.0, id4615=1.0/1.0/1.0, id4616=1.0/1.0/1.0, id4617=1.0/1.0/1.0, id4618=1.0/1.0/1.0, id4619=1.0/1.0/1.0, id462=1.0/1.0/1.0, id4620=1.0/1.0/1.0, id4621=1.0/1.0/1.0, id4622=1.0/1.0/1.0, id4623=1.0/1.0/1.0, id4624=1.0/1.0/1.0, id4625=1.0/1.0/1.0, id4626=1.0/1.0/1.0, id4627=1.0/1.0/1.0, id4628=1.0/1.0/1.0, id4629=1.0/1.0/1.0, id463=1.0/1.0/1.0, id4630=1.0/1.0/1.0, id4631=1.0/1.0/1.0, id4632=1.0/1.0/1.0, id4633=1.0/1.0/1.0, id4634=1.0/1.0/1.0, id4635=1.0/1.0/1.0, id4636=1.0/1.0/1.0, id4637=1.0/1.0/1.0, id4638=1.0/1.0/1.0, id4639=1.0/1.0/1.0, id464=1.0/1.0/1.0, id4640=1.0/1.0/1.0, id4641=1.0/1.0/1.0, id4642=1.0/1.0/1.0, id4643=1.0/1.0/1.0, id4644=1.0/1.0/1.0, id4645=1.0/1.0/1.0, id4646=1.0/1.0/1.0, id4647=1.0/1.0/1.0, id4648=1.0/1.0/1.0, id4649=1.0/1.0/1.0, id465=1.0/1.0/1.0, id4650=1.0/1.0/1.0, id4651=1.0/1.0/1.0, id4652=1.0/1.0/1.0, id4653=1.0/1.0/1.0, id4654=1.0/1.0/1.0, id4655=1.0/1.0/1.0, id4656=1.0/1.0/1.0, id4657=1.0/1.0/1.0, id4658=1.0/1.0/1.0, id4659=1.0/1.0/1.0, id466=1.0/1.0/1.0, id4660=1.0/1.0/1.0, id4661=1.0/1.0/1.0, id4662=1.0/1.0/1.0, id4663=1.0/1.0/1.0, id4664=1.0/1.0/1.0, id4665=1.0/1.0/1.0, id4666=1.0/1.0/1.0, id4667=1.0/1.0/1.0, id4668=1.0/1.0/1.0, id4669=1.0/1.0/1.0, id467=1.0/1.0/1.0, id4670=1.0/1.0/1.0, id4671=1.0/1.0/1.0, id4672=1.0/1.0/1.0, id4673=1.0/1.0/1.0, id4674=1.0/1.0/1.0, id4675=1.0/1.0/1.0, id4676=1.0/1.0/1.0, id4677=1.0/1.0/1.0, id4678=1.0/1.0/1.0, id4679=1.0/1.0/1.0, id468=1.0/1.0/1.0, id4680=1.0/1.0/1.0, id4681=1.0/1.0/1.0, id4682=1.0/1.0/1.0, id4683=1.0/1.0/1.0, id4684=1.0/1.0/1.0, id4685=1.0/1.0/1.0, id4686=1.0/1.0/1.0, id4687=1.0/1.0/1.0, id4688=1.0/1.0/1.0, id4689=1.0/1.0/1.0, id469=1.0/1.0/1.0, id4690=1.0/1.0/1.0, id4691=1.0/1.0/1.0, id4692=1.0/1.0/1.0, id4693=1.0/1.0/1.0, id4694=1.0/1.0/1.0, id4695=1.0/1.0/1.0, id4696=1.0/1.0/1.0, id4697=1.0/1.0/1.0, id4698=1.0/1.0/1.0, id4699=1.0/1.0/1.0, id47=1.0/1.0/1.0, id470=1.0/1.0/1.0, id4700=1.0/1.0/1.0, id4701=1.0/1.0/1.0, id4702=1.0/1.0/1.0, id4703=1.0/1.0/1.0, id4704=1.0/1.0/1.0, id4705=1.0/1.0/1.0, id4706=1.0/1.0/1.0, id4707=1.0/1.0/1.0, id4708=1.0/1.0/1.0, id4709=1.0/1.0/1.0, id471=1.0/1.0/1.0, id4710=1.0/1.0/1.0, id4711=1.0/1.0/1.0, id4712=1.0/1.0/1.0, id4713=1.0/1.0/1.0, id4714=1.0/1.0/1.0, id4715=1.0/1.0/1.0, id4716=1.0/1.0/1.0, id4717=1.0/1.0/1.0, id4718=1.0/1.0/1.0, id4719=1.0/1.0/1.0, id472=1.0/1.0/1.0, id4720=1.0/1.0/1.0, id4721=1.0/1.0/1.0, id4722=1.0/1.0/1.0, id4723=1.0/1.0/1.0, id4724=1.0/1.0/1.0, id4725=1.0/1.0/1.0, id4726=1.0/1.0/1.0, id4727=1.0/1.0/1.0, id4728=1.0/1.0/1.0, id4729=1.0/1.0/1.0, id473=1.0/1.0/1.0, id4730=1.0/1.0/1.0, id4731=1.0/1.0/1.0, id4732=1.0/1.0/1.0, id4733=1.0/1.0/1.0, id4734=1.0/1.0/1.0, id4735=1.0/1.0/1.0, id4736=1.0/1.0/1.0, id4737=1.0/1.0/1.0, id4738=1.0/1.0/1.0, id4739=1.0/1.0/1.0, id474=1.0/1.0/1.0, id4740=1.0/1.0/1.0, id4741=1.0/1.0/1.0, id4742=1.0/1.0/1.0, id4743=1.0/1.0/1.0, id4744=1.0/1.0/1.0, id4745=1.0/1.0/1.0, id4746=1.0/1.0/1.0, id4747=1.0/1.0/1.0, id4748=1.0/1.0/1.0, id4749=1.0/1.0/1.0, id475=1.0/1.0/1.0, id4750=1.0/1.0/1.0, id4751=1.0/1.0/1.0, id4752=1.0/1.0/1.0, id4753=1.0/1.0/1.0, id4754=1.0/1.0/1.0, id4755=1.0/1.0/1.0, id4756=1.0/1.0/1.0, id4757=1.0/1.0/1.0, id4758=1.0/1.0/1.0, id4759=1.0/1.0/1.0, id476=1.0/1.0/1.0, id4760=1.0/1.0/1.0, id4761=1.0/1.0/1.0, id4762=1.0/1.0/1.0, id4763=1.0/1.0/1.0, id4764=1.0/1.0/1.0, id4765=1.0/1.0/1.0, id4766=1.0/1.0/1.0, id4767=1.0/1.0/1.0, id4768=1.0/1.0/1.0, id4769=1.0/1.0/1.0, id477=1.0/1.0/1.0, id4770=1.0/1.0/1.0, id4771=1.0/1.0/1.0, id4772=1.0/1.0/1.0, id4773=1.0/1.0/1.0, id4774=1.0/1.0/1.0, id4775=1.0/1.0/1.0, id4776=1.0/1.0/1.0, id4777=1.0/1.0/1.0, id4778=1.0/1.0/1.0, id4779=1.0/1.0/1.0, id478=1.0/1.0/1.0, id4780=1.0/1.0/1.0, id4781=1.0/1.0/1.0, id4782=1.0/1.0/1.0, id4783=1.0/1.0/1.0, id4784=1.0/1.0/1.0, id4785=1.0/1.0/1.0, id4786=1.0/1.0/1.0, id4787=1.0/1.0/1.0, id4788=1.0/1.0/1.0, id4789=1.0/1.0/1.0, id479=1.0/1.0/1.0, id4790=1.0/1.0/1.0, id4791=1.0/1.0/1.0, id4792=1.0/1.0/1.0, id4793=1.0/1.0/1.0, id4794=1.0/1.0/1.0, id4795=1.0/1.0/1.0, id4796=1.0/1.0/1.0, id4797=1.0/1.0/1.0, id4798=1.0/1.0/1.0, id4799=1.0/1.0/1.0, id48=1.0/1.0/1.0, id480=1.0/1.0/1.0, id4800=1.0/1.0/1.0, id4801=1.0/1.0/1.0, id4802=1.0/1.0/1.0, id4803=1.0/1.0/1.0, id4804=1.0/1.0/1.0, id4805=1.0/1.0/1.0, id4806=1.0/1.0/1.0, id4807=1.0/1.0/1.0, id4808=1.0/1.0/1.0, id4809=1.0/1.0/1.0, id481=1.0/1.0/1.0, id4810=1.0/1.0/1.0, id4811=1.0/1.0/1.0, id4812=1.0/1.0/1.0, id4813=1.0/1.0/1.0, id4814=1.0/1.0/1.0, id4815=1.0/1.0/1.0, id4816=1.0/1.0/1.0, id4817=1.0/1.0/1.0, id4818=1.0/1.0/1.0, id4819=1.0/1.0/1.0, id482=1.0/1.0/1.0, id4820=1.0/1.0/1.0, id4821=1.0/1.0/1.0, id4822=1.0/1.0/1.0, id4823=1.0/1.0/1.0, id4824=1.0/1.0/1.0, id4825=1.0/1.0/1.0, id4826=1.0/1.0/1.0, id4827=1.0/1.0/1.0, id4828=1.0/1.0/1.0, id4829=1.0/1.0/1.0, id483=1.0/1.0/1.0, id4830=1.0/1.0/1.0, id4831=1.0/1.0/1.0, id4832=1.0/1.0/1.0, id4833=1.0/1.0/1.0, id4834=1.0/1.0/1.0, id4835=1.0/1.0/1.0, id4836=1.0/1.0/1.0, id4837=1.0/1.0/1.0, id4838=1.0/1.0/1.0, id4839=1.0/1.0/1.0, id484=1.0/1.0/1.0, id4840=1.0/1.0/1.0, id4841=1.0/1.0/1.0, id4842=1.0/1.0/1.0, id4843=1.0/1.0/1.0, id4844=1.0/1.0/1.0, id4845=1.0/1.0/1.0, id4846=1.0/1.0/1.0, id4847=1.0/1.0/1.0, id4848=1.0/1.0/1.0, id4849=1.0/1.0/1.0, id485=1.0/1.0/1.0, id4850=1.0/1.0/1.0, id4851=1.0/1.0/1.0, id4852=1.0/1.0/1.0, id4853=1.0/1.0/1.0, id4854=1.0/1.0/1.0, id4855=1.0/1.0/1.0, id4856=1.0/1.0/1.0, id4857=1.0/1.0/1.0, id4858=1.0/1.0/1.0, id4859=1.0/1.0/1.0, id486=1.0/1.0/1.0, id4860=1.0/1.0/1.0, id4861=1.0/1.0/1.0, id4862=1.0/1.0/1.0, id4863=1.0/1.0/1.0, id4864=1.0/1.0/1.0, id4865=1.0/1.0/1.0, id4866=1.0/1.0/1.0, id4867=1.0/1.0/1.0, id4868=1.0/1.0/1.0, id4869=1.0/1.0/1.0, id487=1.0/1.0/1.0, id4870=1.0/1.0/1.0, id4871=1.0/1.0/1.0, id4872=1.0/1.0/1.0, id4873=1.0/1.0/1.0, id4874=1.0/1.0/1.0, id4875=1.0/1.0/1.0, id4876=1.0/1.0/1.0, id4877=1.0/1.0/1.0, id4878=1.0/1.0/1.0, id4879=1.0/1.0/1.0, id488=1.0/1.0/1.0, id4880=1.0/1.0/1.0, id4881=1.0/1.0/1.0, id4882=1.0/1.0/1.0, id4883=1.0/1.0/1.0, id4884=1.0/1.0/1.0, id4885=1.0/1.0/1.0, id4886=1.0/1.0/1.0, id4887=1.0/1.0/1.0, id4888=1.0/1.0/1.0, id4889=1.0/1.0/1.0, id489=1.0/1.0/1.0, id4890=1.0/1.0/1.0, id4891=1.0/1.0/1.0, id4892=1.0/1.0/1.0, id4893=1.0/1.0/1.0, id4894=1.0/1.0/1.0, id4895=1.0/1.0/1.0, id4896=1.0/1.0/1.0, id4897=1.0/1.0/1.0, id4898=1.0/1.0/1.0, id4899=1.0/1.0/1.0, id49=1.0/1.0/1.0, id490=1.0/1.0/1.0, id4900=1.0/1.0/1.0, id4901=1.0/1.0/1.0, id4902=1.0/1.0/1.0, id4903=1.0/1.0/1.0, id4904=1.0/1.0/1.0, id4905=1.0/1.0/1.0, id4906=1.0/1.0/1.0, id4907=1.0/1.0/1.0, id4908=1.0/1.0/1.0, id4909=1.0/1.0/1.0, id491=1.0/1.0/1.0, id4910=1.0/1.0/1.0, id4911=1.0/1.0/1.0, id4912=1.0/1.0/1.0, id4913=1.0/1.0/1.0, id4914=1.0/1.0/1.0, id4915=1.0/1.0/1.0, id4916=1.0/1.0/1.0, id4917=1.0/1.0/1.0, id4918=1.0/1.0/1.0, id4919=1.0/1.0/1.0, id492=1.0/1.0/1.0, id4920=1.0/1.0/1.0, id4921=1.0/1.0/1.0, id4922=1.0/1.0/1.0, id4923=1.0/1.0/1.0, id4924=1.0/1.0/1.0, id4925=1.0/1.0/1.0, id4926=1.0/1.0/1.0, id4927=1.0/1.0/1.0, id4928=1.0/1.0/1.0, id4929=1.0/1.0/1.0, id493=1.0/1.0/1.0, id4930=1.0/1.0/1.0, id4931=1.0/1.0/1.0, id4932=1.0/1.0/1.0, id4933=1.0/1.0/1.0, id4934=1.0/1.0/1.0, id4935=1.0/1.0/1.0, id4936=1.0/1.0/1.0, id4937=1.0/1.0/1.0, id4938=1.0/1.0/1.0, id4939=1.0/1.0/1.0, id494=1.0/1.0/1.0, id4940=1.0/1.0/1.0, id4941=1.0/1.0/1.0, id4942=1.0/1.0/1.0, id4943=1.0/1.0/1.0, id4944=1.0/1.0/1.0, id4945=1.0/1.0/1.0, id4946=1.0/1.0/1.0, id4947=1.0/1.0/1.0, id4948=1.0/1.0/1.0, id4949=1.0/1.0/1.0, id495=1.0/1.0/1.0, id4950=1.0/1.0/1.0, id4951=1.0/1.0/1.0, id4952=1.0/1.0/1.0, id4953=1.0/1.0/1.0, id4954=1.0/1.0/1.0, id4955=1.0/1.0/1.0, id4956=1.0/1.0/1.0, id4957=1.0/1.0/1.0, id4958=1.0/1.0/1.0, id4959=1.0/1.0/1.0, id496=1.0/1.0/1.0, id4960=1.0/1.0/1.0, id4961=1.0/1.0/1.0, id4962=1.0/1.0/1.0, id4963=1.0/1.0/1.0, id4964=1.0/1.0/1.0, id4965=1.0/1.0/1.0, id4966=1.0/1.0/1.0, id4967=1.0/1.0/1.0, id4968=1.0/1.0/1.0, id4969=1.0/1.0/1.0, id497=1.0/1.0/1.0, id4970=1.0/1.0/1.0, id4971=1.0/1.0/1.0, id4972=1.0/1.0/1.0, id4973=1.0/1.0/1.0, id4974=1.0/1.0/1.0, id4975=1.0/1.0/1.0, id4976=1.0/1.0/1.0, id4977=1.0/1.0/1.0, id4978=1.0/1.0/1.0, id4979=1.0/1.0/1.0, id498=1.0/1.0/1.0, id4980=1.0/1.0/1.0, id4981=1.0/1.0/1.0, id4982=1.0/1.0/1.0, id4983=1.0/1.0/1.0, id4984=1.0/1.0/1.0, id4985=1.0/1.0/1.0, id4986=1.0/1.0/1.0, id4987=1.0/1.0/1.0, id4988=1.0/1.0/1.0, id4989=1.0/1.0/1.0, id499=1.0/1.0/1.0, id4990=1.0/1.0/1.0, id4991=1.0/1.0/1.0, id4992=1.0/1.0/1.0, id4993=1.0/1.0/1.0, id4994=1.0/1.0/1.0, id4995=1.0/1.0/1.0, id4996=1.0/1.0/1.0, id4997=1.0/1.0/1.0, id4998=1.0/1.0/1.0, id4999=1.0/1.0/1.0, id5=1.0/1.0/1.0, id50=1.0/1.0/1.0, id500=1.0/1.0/1.0, id5000=1.0/1.0/1.0, id5001=1.0/1.0/1.0, id5002=1.0/1.0/1.0, id5003=1.0/1.0/1.0, id5004=1.0/1.0/1.0, id5005=1.0/1.0/1.0, id5006=1.0/1.0/1.0, id5007=1.0/1.0/1.0, id5008=1.0/1.0/1.0, id5009=1.0/1.0/1.0, id501=1.0/1.0/1.0, id5010=1.0/1.0/1.0, id5011=1.0/1.0/1.0, id5012=1.0/1.0/1.0, id5013=1.0/1.0/1.0, id5014=1.0/1.0/1.0, id5015=1.0/1.0/1.0, id5016=1.0/1.0/1.0, id5017=1.0/1.0/1.0, id5018=1.0/1.0/1.0, id5019=1.0/1.0/1.0, id502=1.0/1.0/1.0, id5020=1.0/1.0/1.0, id5021=1.0/1.0/1.0, id5022=1.0/1.0/1.0, id5023=1.0/1.0/1.0, id5024=1.0/1.0/1.0, id5025=1.0/1.0/1.0, id5026=1.0/1.0/1.0, id5027=1.0/1.0/1.0, id5028=1.0/1.0/1.0, id5029=1.0/1.0/1.0, id503=1.0/1.0/1.0, id5030=1.0/1.0/1.0, id5031=1.0/1.0/1.0, id5032=1.0/1.0/1.0, id5033=1.0/1.0/1.0, id5034=1.0/1.0/1.0, id5035=1.0/1.0/1.0, id5036=1.0/1.0/1.0, id5037=1.0/1.0/1.0, id5038=1.0/1.0/1.0, id5039=1.0/1.0/1.0, id504=1.0/1.0/1.0, id5040=1.0/1.0/1.0, id5041=1.0/1.0/1.0, id5042=1.0/1.0/1.0, id5043=1.0/1.0/1.0, id5044=1.0/1.0/1.0, id5045=1.0/1.0/1.0, id5046=1.0/1.0/1.0, id5047=1.0/1.0/1.0, id5048=1.0/1.0/1.0, id5049=1.0/1.0/1.0, id505=1.0/1.0/1.0, id5050=1.0/1.0/1.0, id5051=1.0/1.0/1.0, id5052=1.0/1.0/1.0, id5053=1.0/1.0/1.0, id5054=1.0/1.0/1.0, id5055=1.0/1.0/1.0, id5056=1.0/1.0/1.0, id5057=1.0/1.0/1.0, id5058=1.0/1.0/1.0, id5059=1.0/1.0/1.0, id506=1.0/1.0/1.0, id5060=1.0/1.0/1.0, id5061=1.0/1.0/1.0, id5062=1.0/1.0/1.0, id5063=1.0/1.0/1.0, id5064=1.0/1.0/1.0, id5065=1.0/1.0/1.0, id5066=1.0/1.0/1.0, id5067=1.0/1.0/1.0, id5068=1.0/1.0/1.0, id5069=1.0/1.0/1.0, id507=1.0/1.0/1.0, id5070=1.0/1.0/1.0, id5071=1.0/1.0/1.0, id5072=1.0/1.0/1.0, id5073=1.0/1.0/1.0, id5074=1.0/1.0/1.0, id5075=1.0/1.0/1.0, id5076=1.0/1.0/1.0, id5077=1.0/1.0/1.0, id5078=1.0/1.0/1.0, id5079=1.0/1.0/1.0, id508=1.0/1.0/1.0, id5080=1.0/1.0/1.0, id5081=1.0/1.0/1.0, id5082=1.0/1.0/1.0, id5083=1.0/1.0/1.0, id5084=1.0/1.0/1.0, id5085=1.0/1.0/1.0, id5086=1.0/1.0/1.0, id5087=1.0/1.0/1.0, id5088=1.0/1.0/1.0, id5089=1.0/1.0/1.0, id509=1.0/1.0/1.0, id5090=1.0/1.0/1.0, id5091=1.0/1.0/1.0, id5092=1.0/1.0/1.0, id5093=1.0/1.0/1.0, id5094=1.0/1.0/1.0, id5095=1.0/1.0/1.0, id5096=1.0/1.0/1.0, id5097=1.0/1.0/1.0, id5098=1.0/1.0/1.0, id5099=1.0/1.0/1.0, id51=1.0/1.0/1.0, id510=1.0/1.0/1.0, id5100=1.0/1.0/1.0, id5101=1.0/1.0/1.0, id5102=1.0/1.0/1.0, id5103=1.0/1.0/1.0, id5104=1.0/1.0/1.0, id5105=1.0/1.0/1.0, id5106=1.0/1.0/1.0, id5107=1.0/1.0/1.0, id5108=1.0/1.0/1.0, id5109=1.0/1.0/1.0, id511=1.0/1.0/1.0, id5110=1.0/1.0/1.0, id5111=1.0/1.0/1.0, id5112=1.0/1.0/1.0, id5113=1.0/1.0/1.0, id5114=1.0/1.0/1.0, id5115=1.0/1.0/1.0, id5116=1.0/1.0/1.0, id5117=1.0/1.0/1.0, id5118=1.0/1.0/1.0, id5119=1.0/1.0/1.0, id512=1.0/1.0/1.0, id5120=1.0/1.0/1.0, id5121=1.0/1.0/1.0, id5122=1.0/1.0/1.0, id5123=1.0/1.0/1.0, id5124=1.0/1.0/1.0, id5125=1.0/1.0/1.0, id5126=1.0/1.0/1.0, id5127=1.0/1.0/1.0, id5128=1.0/1.0/1.0, id5129=1.0/1.0/1.0, id513=1.0/1.0/1.0, id5130=1.0/1.0/1.0, id5131=1.0/1.0/1.0, id5132=1.0/1.0/1.0, id5133=1.0/1.0/1.0, id5134=1.0/1.0/1.0, id5135=1.0/1.0/1.0, id5136=1.0/1.0/1.0, id5137=1.0/1.0/1.0, id5138=1.0/1.0/1.0, id5139=1.0/1.0/1.0, id514=1.0/1.0/1.0, id5140=1.0/1.0/1.0, id5141=1.0/1.0/1.0, id5142=1.0/1.0/1.0, id5143=1.0/1.0/1.0, id5144=1.0/1.0/1.0, id5145=1.0/1.0/1.0, id5146=1.0/1.0/1.0, id5147=1.0/1.0/1.0, id5148=1.0/1.0/1.0, id5149=1.0/1.0/1.0, id515=1.0/1.0/1.0, id5150=1.0/1.0/1.0, id5151=1.0/1.0/1.0, id5152=1.0/1.0/1.0, id5153=1.0/1.0/1.0, id5154=1.0/1.0/1.0, id5155=1.0/1.0/1.0, id5156=1.0/1.0/1.0, id5157=1.0/1.0/1.0, id5158=1.0/1.0/1.0, id5159=1.0/1.0/1.0, id516=1.0/1.0/1.0, id5160=1.0/1.0/1.0, id5161=1.0/1.0/1.0, id5162=1.0/1.0/1.0, id5163=1.0/1.0/1.0, id5164=1.0/1.0/1.0, id5165=1.0/1.0/1.0, id5166=1.0/1.0/1.0, id5167=1.0/1.0/1.0, id5168=1.0/1.0/1.0, id5169=1.0/1.0/1.0, id517=1.0/1.0/1.0, id5170=1.0/1.0/1.0, id5171=1.0/1.0/1.0, id5172=1.0/1.0/1.0, id5173=1.0/1.0/1.0, id5174=1.0/1.0/1.0, id5175=1.0/1.0/1.0, id5176=1.0/1.0/1.0, id5177=1.0/1.0/1.0, id5178=1.0/1.0/1.0, id5179=1.0/1.0/1.0, id518=1.0/1.0/1.0, id5180=1.0/1.0/1.0, id5181=1.0/1.0/1.0, id5182=1.0/1.0/1.0, id5183=1.0/1.0/1.0, id5184=1.0/1.0/1.0, id5185=1.0/1.0/1.0, id5186=1.0/1.0/1.0, id5187=1.0/1.0/1.0, id5188=1.0/1.0/1.0, id5189=1.0/1.0/1.0, id519=1.0/1.0/1.0, id5190=1.0/1.0/1.0, id5191=1.0/1.0/1.0, id5192=1.0/1.0/1.0, id5193=1.0/1.0/1.0, id5194=1.0/1.0/1.0, id5195=1.0/1.0/1.0, id5196=1.0/1.0/1.0, id5197=1.0/1.0/1.0, id5198=1.0/1.0/1.0, id5199=1.0/1.0/1.0, id52=1.0/1.0/1.0, id520=1.0/1.0/1.0, id5200=1.0/1.0/1.0, id5201=1.0/1.0/1.0, id5202=1.0/1.0/1.0, id5203=1.0/1.0/1.0, id5204=1.0/1.0/1.0, id5205=1.0/1.0/1.0, id5206=1.0/1.0/1.0, id5207=1.0/1.0/1.0, id5208=1.0/1.0/1.0, id5209=1.0/1.0/1.0, id521=1.0/1.0/1.0, id5210=1.0/1.0/1.0, id5211=1.0/1.0/1.0, id5212=1.0/1.0/1.0, id5213=1.0/1.0/1.0, id5214=1.0/1.0/1.0, id5215=1.0/1.0/1.0, id5216=1.0/1.0/1.0, id5217=1.0/1.0/1.0, id5218=1.0/1.0/1.0, id5219=1.0/1.0/1.0, id522=1.0/1.0/1.0, id5220=1.0/1.0/1.0, id5221=1.0/1.0/1.0, id5222=1.0/1.0/1.0, id5223=1.0/1.0/1.0, id5224=1.0/1.0/1.0, id5225=1.0/1.0/1.0, id5226=1.0/1.0/1.0, id5227=1.0/1.0/1.0, id5228=1.0/1.0/1.0, id5229=1.0/1.0/1.0, id523=1.0/1.0/1.0, id5230=1.0/1.0/1.0, id5231=1.0/1.0/1.0, id5232=1.0/1.0/1.0, id5233=1.0/1.0/1.0, id5234=1.0/1.0/1.0, id5235=1.0/1.0/1.0, id5236=1.0/1.0/1.0, id5237=1.0/1.0/1.0, id5238=1.0/1.0/1.0, id5239=1.0/1.0/1.0, id524=1.0/1.0/1.0, id5240=1.0/1.0/1.0, id5241=1.0/1.0/1.0, id5242=1.0/1.0/1.0, id5243=1.0/1.0/1.0, id5244=1.0/1.0/1.0, id5245=1.0/1.0/1.0, id5246=1.0/1.0/1.0, id5247=1.0/1.0/1.0, id5248=1.0/1.0/1.0, id5249=1.0/1.0/1.0, id525=1.0/1.0/1.0, id5250=1.0/1.0/1.0, id5251=1.0/1.0/1.0, id5252=1.0/1.0/1.0, id5253=1.0/1.0/1.0, id5254=1.0/1.0/1.0, id5255=1.0/1.0/1.0, id5256=1.0/1.0/1.0, id5257=1.0/1.0/1.0, id5258=1.0/1.0/1.0, id5259=1.0/1.0/1.0, id526=1.0/1.0/1.0, id5260=1.0/1.0/1.0, id5261=1.0/1.0/1.0, id5262=1.0/1.0/1.0, id5263=1.0/1.0/1.0, id5264=1.0/1.0/1.0, id5265=1.0/1.0/1.0, id5266=1.0/1.0/1.0, id5267=1.0/1.0/1.0, id5268=1.0/1.0/1.0, id5269=1.0/1.0/1.0, id527=1.0/1.0/1.0, id5270=1.0/1.0/1.0, id5271=1.0/1.0/1.0, id5272=1.0/1.0/1.0, id5273=1.0/1.0/1.0, id5274=1.0/1.0/1.0, id5275=1.0/1.0/1.0, id5276=1.0/1.0/1.0, id5277=1.0/1.0/1.0, id5278=1.0/1.0/1.0, id5279=1.0/1.0/1.0, id528=1.0/1.0/1.0, id5280=1.0/1.0/1.0, id5281=1.0/1.0/1.0, id5282=1.0/1.0/1.0, id5283=1.0/1.0/1.0, id5284=1.0/1.0/1.0, id5285=1.0/1.0/1.0, id5286=1.0/1.0/1.0, id5287=1.0/1.0/1.0, id5288=1.0/1.0/1.0, id5289=1.0/1.0/1.0, id529=1.0/1.0/1.0, id5290=1.0/1.0/1.0, id5291=1.0/1.0/1.0, id5292=1.0/1.0/1.0, id5293=1.0/1.0/1.0, id5294=1.0/1.0/1.0, id5295=1.0/1.0/1.0, id5296=1.0/1.0/1.0, id5297=1.0/1.0/1.0, id5298=1.0/1.0/1.0, id5299=1.0/1.0/1.0, id53=1.0/1.0/1.0, id530=1.0/1.0/1.0, id5300=1.0/1.0/1.0, id5301=1.0/1.0/1.0, id5302=1.0/1.0/1.0, id5303=1.0/1.0/1.0, id5304=1.0/1.0/1.0, id5305=1.0/1.0/1.0, id5306=1.0/1.0/1.0, id5307=1.0/1.0/1.0, id5308=1.0/1.0/1.0, id5309=1.0/1.0/1.0, id531=1.0/1.0/1.0, id5310=1.0/1.0/1.0, id5311=1.0/1.0/1.0, id5312=1.0/1.0/1.0, id5313=1.0/1.0/1.0, id5314=1.0/1.0/1.0, id5315=1.0/1.0/1.0, id5316=1.0/1.0/1.0, id5317=1.0/1.0/1.0, id5318=1.0/1.0/1.0, id5319=1.0/1.0/1.0, id532=1.0/1.0/1.0, id5320=1.0/1.0/1.0, id5321=1.0/1.0/1.0, id5322=1.0/1.0/1.0, id5323=1.0/1.0/1.0, id5324=1.0/1.0/1.0, id5325=1.0/1.0/1.0, id5326=1.0/1.0/1.0, id5327=1.0/1.0/1.0, id5328=1.0/1.0/1.0, id5329=1.0/1.0/1.0, id533=1.0/1.0/1.0, id5330=1.0/1.0/1.0, id5331=1.0/1.0/1.0, id5332=1.0/1.0/1.0, id5333=1.0/1.0/1.0, id5334=1.0/1.0/1.0, id5335=1.0/1.0/1.0, id5336=1.0/1.0/1.0, id5337=1.0/1.0/1.0, id5338=1.0/1.0/1.0, id5339=1.0/1.0/1.0, id534=1.0/1.0/1.0, id5340=1.0/1.0/1.0, id5341=1.0/1.0/1.0, id5342=1.0/1.0/1.0, id5343=1.0/1.0/1.0, id5344=1.0/1.0/1.0, id5345=1.0/1.0/1.0, id5346=1.0/1.0/1.0, id5347=1.0/1.0/1.0, id5348=1.0/1.0/1.0, id5349=1.0/1.0/1.0, id535=1.0/1.0/1.0, id5350=1.0/1.0/1.0, id5351=1.0/1.0/1.0, id5352=1.0/1.0/1.0, id5353=1.0/1.0/1.0, id5354=1.0/1.0/1.0, id5355=1.0/1.0/1.0, id5356=1.0/1.0/1.0, id5357=1.0/1.0/1.0, id5358=1.0/1.0/1.0, id5359=1.0/1.0/1.0, id536=1.0/1.0/1.0, id5360=1.0/1.0/1.0, id5361=1.0/1.0/1.0, id5362=1.0/1.0/1.0, id5363=1.0/1.0/1.0, id5364=1.0/1.0/1.0, id5365=1.0/1.0/1.0, id5366=1.0/1.0/1.0, id5367=1.0/1.0/1.0, id5368=1.0/1.0/1.0, id5369=1.0/1.0/1.0, id537=1.0/1.0/1.0, id5370=1.0/1.0/1.0, id5371=1.0/1.0/1.0, id5372=1.0/1.0/1.0, id5373=1.0/1.0/1.0, id5374=1.0/1.0/1.0, id5375=1.0/1.0/1.0, id5376=1.0/1.0/1.0, id5377=1.0/1.0/1.0, id5378=1.0/1.0/1.0, id5379=1.0/1.0/1.0, id538=1.0/1.0/1.0, id5380=1.0/1.0/1.0, id5381=1.0/1.0/1.0, id5382=1.0/1.0/1.0, id5383=1.0/1.0/1.0, id5384=1.0/1.0/1.0, id5385=1.0/1.0/1.0, id5386=1.0/1.0/1.0, id5387=1.0/1.0/1.0, id5388=1.0/1.0/1.0, id5389=1.0/1.0/1.0, id539=1.0/1.0/1.0, id5390=1.0/1.0/1.0, id5391=1.0/1.0/1.0, id5392=1.0/1.0/1.0, id5393=1.0/1.0/1.0, id5394=1.0/1.0/1.0, id5395=1.0/1.0/1.0, id5396=1.0/1.0/1.0, id5397=1.0/1.0/1.0, id5398=1.0/1.0/1.0, id5399=1.0/1.0/1.0, id54=1.0/1.0/1.0, id540=1.0/1.0/1.0, id5400=1.0/1.0/1.0, id5401=1.0/1.0/1.0, id5402=1.0/1.0/1.0, id5403=1.0/1.0/1.0, id5404=1.0/1.0/1.0, id5405=1.0/1.0/1.0, id5406=1.0/1.0/1.0, id5407=1.0/1.0/1.0, id5408=1.0/1.0/1.0, id5409=1.0/1.0/1.0, id541=1.0/1.0/1.0, id5410=1.0/1.0/1.0, id5411=1.0/1.0/1.0, id5412=1.0/1.0/1.0, id5413=1.0/1.0/1.0, id5414=1.0/1.0/1.0, id5415=1.0/1.0/1.0, id5416=1.0/1.0/1.0, id5417=1.0/1.0/1.0, id5418=1.0/1.0/1.0, id5419=1.0/1.0/1.0, id542=1.0/1.0/1.0, id5420=1.0/1.0/1.0, id5421=1.0/1.0/1.0, id5422=1.0/1.0/1.0, id5423=1.0/1.0/1.0, id5424=1.0/1.0/1.0, id5425=1.0/1.0/1.0, id5426=1.0/1.0/1.0, id5427=1.0/1.0/1.0, id5428=1.0/1.0/1.0, id5429=1.0/1.0/1.0, id543=1.0/1.0/1.0, id5430=1.0/1.0/1.0, id5431=1.0/1.0/1.0, id5432=1.0/1.0/1.0, id5433=1.0/1.0/1.0, id5434=1.0/1.0/1.0, id5435=1.0/1.0/1.0, id5436=1.0/1.0/1.0, id5437=1.0/1.0/1.0, id5438=1.0/1.0/1.0, id5439=1.0/1.0/1.0, id544=1.0/1.0/1.0, id5440=1.0/1.0/1.0, id5441=1.0/1.0/1.0, id5442=1.0/1.0/1.0, id5443=1.0/1.0/1.0, id5444=1.0/1.0/1.0, id5445=1.0/1.0/1.0, id5446=1.0/1.0/1.0, id5447=1.0/1.0/1.0, id5448=1.0/1.0/1.0, id5449=1.0/1.0/1.0, id545=1.0/1.0/1.0, id5450=1.0/1.0/1.0, id5451=1.0/1.0/1.0, id5452=1.0/1.0/1.0, id5453=1.0/1.0/1.0, id5454=1.0/1.0/1.0, id5455=1.0/1.0/1.0, id5456=1.0/1.0/1.0, id5457=1.0/1.0/1.0, id5458=1.0/1.0/1.0, id5459=1.0/1.0/1.0, id546=1.0/1.0/1.0, id5460=1.0/1.0/1.0, id5461=1.0/1.0/1.0, id5462=1.0/1.0/1.0, id5463=1.0/1.0/1.0, id5464=1.0/1.0/1.0, id5465=1.0/1.0/1.0, id5466=1.0/1.0/1.0, id5467=1.0/1.0/1.0, id5468=1.0/1.0/1.0, id5469=1.0/1.0/1.0, id547=1.0/1.0/1.0, id5470=1.0/1.0/1.0, id5471=1.0/1.0/1.0, id5472=1.0/1.0/1.0, id5473=1.0/1.0/1.0, id5474=1.0/1.0/1.0, id5475=1.0/1.0/1.0, id5476=1.0/1.0/1.0, id5477=1.0/1.0/1.0, id5478=1.0/1.0/1.0, id5479=1.0/1.0/1.0, id548=1.0/1.0/1.0, id5480=1.0/1.0/1.0, id5481=1.0/1.0/1.0, id5482=1.0/1.0/1.0, id5483=1.0/1.0/1.0, id5484=1.0/1.0/1.0, id5485=1.0/1.0/1.0, id5486=1.0/1.0/1.0, id5487=1.0/1.0/1.0, id5488=1.0/1.0/1.0, id5489=1.0/1.0/1.0, id549=1.0/1.0/1.0, id5490=1.0/1.0/1.0, id5491=1.0/1.0/1.0, id5492=1.0/1.0/1.0, id5493=1.0/1.0/1.0, id5494=1.0/1.0/1.0, id5495=1.0/1.0/1.0, id5496=1.0/1.0/1.0, id5497=1.0/1.0/1.0, id5498=1.0/1.0/1.0, id5499=1.0/1.0/1.0, id55=1.0/1.0/1.0, id550=1.0/1.0/1.0, id5500=1.0/1.0/1.0, id5501=1.0/1.0/1.0, id5502=1.0/1.0/1.0, id5503=1.0/1.0/1.0, id5504=1.0/1.0/1.0, id5505=1.0/1.0/1.0, id5506=1.0/1.0/1.0, id5507=1.0/1.0/1.0, id5508=1.0/1.0/1.0, id5509=1.0/1.0/1.0, id551=1.0/1.0/1.0, id5510=1.0/1.0/1.0, id5511=1.0/1.0/1.0, id5512=1.0/1.0/1.0, id5513=1.0/1.0/1.0, id5514=1.0/1.0/1.0, id5515=1.0/1.0/1.0, id5516=1.0/1.0/1.0, id5517=1.0/1.0/1.0, id5518=1.0/1.0/1.0, id5519=1.0/1.0/1.0, id552=1.0/1.0/1.0, id5520=1.0/1.0/1.0, id5521=1.0/1.0/1.0, id5522=1.0/1.0/1.0, id5523=1.0/1.0/1.0, id5524=1.0/1.0/1.0, id5525=1.0/1.0/1.0, id5526=1.0/1.0/1.0, id5527=1.0/1.0/1.0, id5528=1.0/1.0/1.0, id5529=1.0/1.0/1.0, id553=1.0/1.0/1.0, id5530=1.0/1.0/1.0, id5531=1.0/1.0/1.0, id5532=1.0/1.0/1.0, id5533=1.0/1.0/1.0, id5534=1.0/1.0/1.0, id5535=1.0/1.0/1.0, id5536=1.0/1.0/1.0, id5537=1.0/1.0/1.0, id5538=1.0/1.0/1.0, id5539=1.0/1.0/1.0, id554=1.0/1.0/1.0, id5540=1.0/1.0/1.0, id5541=1.0/1.0/1.0, id5542=1.0/1.0/1.0, id5543=1.0/1.0/1.0, id5544=1.0/1.0/1.0, id5545=1.0/1.0/1.0, id5546=1.0/1.0/1.0, id5547=1.0/1.0/1.0, id5548=1.0/1.0/1.0, id5549=1.0/1.0/1.0, id555=1.0/1.0/1.0, id5550=1.0/1.0/1.0, id5551=1.0/1.0/1.0, id5552=1.0/1.0/1.0, id5553=1.0/1.0/1.0, id5554=1.0/1.0/1.0, id5555=1.0/1.0/1.0, id5556=1.0/1.0/1.0, id5557=1.0/1.0/1.0, id5558=1.0/1.0/1.0, id5559=1.0/1.0/1.0, id556=1.0/1.0/1.0, id5560=1.0/1.0/1.0, id5561=1.0/1.0/1.0, id5562=1.0/1.0/1.0, id5563=1.0/1.0/1.0, id5564=1.0/1.0/1.0, id5565=1.0/1.0/1.0, id5566=1.0/1.0/1.0, id5567=1.0/1.0/1.0, id5568=1.0/1.0/1.0, id5569=1.0/1.0/1.0, id557=1.0/1.0/1.0, id5570=1.0/1.0/1.0, id5571=1.0/1.0/1.0, id5572=1.0/1.0/1.0, id5573=1.0/1.0/1.0, id5574=1.0/1.0/1.0, id5575=1.0/1.0/1.0, id5576=1.0/1.0/1.0, id5577=1.0/1.0/1.0, id5578=1.0/1.0/1.0, id5579=1.0/1.0/1.0, id558=1.0/1.0/1.0, id5580=1.0/1.0/1.0, id5581=1.0/1.0/1.0, id5582=1.0/1.0/1.0, id5583=1.0/1.0/1.0, id5584=1.0/1.0/1.0, id5585=1.0/1.0/1.0, id5586=1.0/1.0/1.0, id5587=1.0/1.0/1.0, id5588=1.0/1.0/1.0, id5589=1.0/1.0/1.0, id559=1.0/1.0/1.0, id5590=1.0/1.0/1.0, id5591=1.0/1.0/1.0, id5592=1.0/1.0/1.0, id5593=1.0/1.0/1.0, id5594=1.0/1.0/1.0, id5595=1.0/1.0/1.0, id5596=1.0/1.0/1.0, id5597=1.0/1.0/1.0, id5598=1.0/1.0/1.0, id5599=1.0/1.0/1.0, id56=1.0/1.0/1.0, id560=1.0/1.0/1.0, id5600=1.0/1.0/1.0, id5601=1.0/1.0/1.0, id5602=1.0/1.0/1.0, id5603=1.0/1.0/1.0, id5604=1.0/1.0/1.0, id5605=1.0/1.0/1.0, id5606=1.0/1.0/1.0, id5607=1.0/1.0/1.0, id5608=1.0/1.0/1.0, id5609=1.0/1.0/1.0, id561=1.0/1.0/1.0, id5610=1.0/1.0/1.0, id5611=1.0/1.0/1.0, id5612=1.0/1.0/1.0, id5613=1.0/1.0/1.0, id5614=1.0/1.0/1.0, id5615=1.0/1.0/1.0, id5616=1.0/1.0/1.0, id5617=1.0/1.0/1.0, id5618=1.0/1.0/1.0, id5619=1.0/1.0/1.0, id562=1.0/1.0/1.0, id5620=1.0/1.0/1.0, id5621=1.0/1.0/1.0, id5622=1.0/1.0/1.0, id5623=1.0/1.0/1.0, id5624=1.0/1.0/1.0, id5625=1.0/1.0/1.0, id5626=1.0/1.0/1.0, id5627=1.0/1.0/1.0, id5628=1.0/1.0/1.0, id5629=1.0/1.0/1.0, id563=1.0/1.0/1.0, id5630=1.0/1.0/1.0, id5631=1.0/1.0/1.0, id5632=1.0/1.0/1.0, id5633=1.0/1.0/1.0, id5634=1.0/1.0/1.0, id5635=1.0/1.0/1.0, id5636=1.0/1.0/1.0, id5637=1.0/1.0/1.0, id5638=1.0/1.0/1.0, id5639=1.0/1.0/1.0, id564=1.0/1.0/1.0, id5640=1.0/1.0/1.0, id5641=1.0/1.0/1.0, id5642=1.0/1.0/1.0, id5643=1.0/1.0/1.0, id5644=1.0/1.0/1.0, id5645=1.0/1.0/1.0, id5646=1.0/1.0/1.0, id5647=1.0/1.0/1.0, id5648=1.0/1.0/1.0, id5649=1.0/1.0/1.0, id565=1.0/1.0/1.0, id5650=1.0/1.0/1.0, id5651=1.0/1.0/1.0, id5652=1.0/1.0/1.0, id5653=1.0/1.0/1.0, id5654=1.0/1.0/1.0, id5655=1.0/1.0/1.0, id5656=1.0/1.0/1.0, id5657=1.0/1.0/1.0, id5658=1.0/1.0/1.0, id5659=1.0/1.0/1.0, id566=1.0/1.0/1.0, id5660=1.0/1.0/1.0, id5661=1.0/1.0/1.0, id5662=1.0/1.0/1.0, id5663=1.0/1.0/1.0, id5664=1.0/1.0/1.0, id5665=1.0/1.0/1.0, id5666=1.0/1.0/1.0, id5667=1.0/1.0/1.0, id5668=1.0/1.0/1.0, id5669=1.0/1.0/1.0, id567=1.0/1.0/1.0, id5670=1.0/1.0/1.0, id5671=1.0/1.0/1.0, id5672=1.0/1.0/1.0, id5673=1.0/1.0/1.0, id5674=1.0/1.0/1.0, id5675=1.0/1.0/1.0, id5676=1.0/1.0/1.0, id5677=1.0/1.0/1.0, id5678=1.0/1.0/1.0, id5679=1.0/1.0/1.0, id568=1.0/1.0/1.0, id5680=1.0/1.0/1.0, id5681=1.0/1.0/1.0, id5682=1.0/1.0/1.0, id5683=1.0/1.0/1.0, id5684=1.0/1.0/1.0, id5685=1.0/1.0/1.0, id5686=1.0/1.0/1.0, id5687=1.0/1.0/1.0, id5688=1.0/1.0/1.0, id5689=1.0/1.0/1.0, id569=1.0/1.0/1.0, id5690=1.0/1.0/1.0, id5691=1.0/1.0/1.0, id5692=1.0/1.0/1.0, id5693=1.0/1.0/1.0, id5694=1.0/1.0/1.0, id5695=1.0/1.0/1.0, id5696=1.0/1.0/1.0, id5697=1.0/1.0/1.0, id5698=1.0/1.0/1.0, id5699=1.0/1.0/1.0, id57=1.0/1.0/1.0, id570=1.0/1.0/1.0, id5700=1.0/1.0/1.0, id5701=1.0/1.0/1.0, id5702=1.0/1.0/1.0, id5703=1.0/1.0/1.0, id5704=1.0/1.0/1.0, id5705=1.0/1.0/1.0, id5706=1.0/1.0/1.0, id5707=1.0/1.0/1.0, id5708=1.0/1.0/1.0, id5709=1.0/1.0/1.0, id571=1.0/1.0/1.0, id5710=1.0/1.0/1.0, id5711=1.0/1.0/1.0, id5712=1.0/1.0/1.0, id5713=1.0/1.0/1.0, id5714=1.0/1.0/1.0, id5715=1.0/1.0/1.0, id5716=1.0/1.0/1.0, id5717=1.0/1.0/1.0, id5718=1.0/1.0/1.0, id5719=1.0/1.0/1.0, id572=1.0/1.0/1.0, id5720=1.0/1.0/1.0, id5721=1.0/1.0/1.0, id5722=1.0/1.0/1.0, id5723=1.0/1.0/1.0, id5724=1.0/1.0/1.0, id5725=1.0/1.0/1.0, id5726=1.0/1.0/1.0, id5727=1.0/1.0/1.0, id5728=1.0/1.0/1.0, id5729=1.0/1.0/1.0, id573=1.0/1.0/1.0, id5730=1.0/1.0/1.0, id5731=1.0/1.0/1.0, id5732=1.0/1.0/1.0, id5733=1.0/1.0/1.0, id5734=1.0/1.0/1.0, id5735=1.0/1.0/1.0, id5736=1.0/1.0/1.0, id5737=1.0/1.0/1.0, id5738=1.0/1.0/1.0, id5739=1.0/1.0/1.0, id574=1.0/1.0/1.0, id5740=1.0/1.0/1.0, id5741=1.0/1.0/1.0, id5742=1.0/1.0/1.0, id5743=1.0/1.0/1.0, id5744=1.0/1.0/1.0, id5745=1.0/1.0/1.0, id5746=1.0/1.0/1.0, id5747=1.0/1.0/1.0, id5748=1.0/1.0/1.0, id5749=1.0/1.0/1.0, id575=1.0/1.0/1.0, id5750=1.0/1.0/1.0, id5751=1.0/1.0/1.0, id5752=1.0/1.0/1.0, id5753=1.0/1.0/1.0, id5754=1.0/1.0/1.0, id5755=1.0/1.0/1.0, id5756=1.0/1.0/1.0, id5757=1.0/1.0/1.0, id5758=1.0/1.0/1.0, id5759=1.0/1.0/1.0, id576=1.0/1.0/1.0, id5760=1.0/1.0/1.0, id5761=1.0/1.0/1.0, id5762=1.0/1.0/1.0, id5763=1.0/1.0/1.0, id5764=1.0/1.0/1.0, id5765=1.0/1.0/1.0, id5766=1.0/1.0/1.0, id5767=1.0/1.0/1.0, id5768=1.0/1.0/1.0, id5769=1.0/1.0/1.0, id577=1.0/1.0/1.0, id5770=1.0/1.0/1.0, id5771=1.0/1.0/1.0, id5772=1.0/1.0/1.0, id5773=1.0/1.0/1.0, id5774=1.0/1.0/1.0, id5775=1.0/1.0/1.0, id5776=1.0/1.0/1.0, id5777=1.0/1.0/1.0, id5778=1.0/1.0/1.0, id5779=1.0/1.0/1.0, id578=1.0/1.0/1.0, id5780=1.0/1.0/1.0, id5781=1.0/1.0/1.0, id5782=1.0/1.0/1.0, id5783=1.0/1.0/1.0, id5784=1.0/1.0/1.0, id5785=1.0/1.0/1.0, id5786=1.0/1.0/1.0, id5787=1.0/1.0/1.0, id5788=1.0/1.0/1.0, id5789=1.0/1.0/1.0, id579=1.0/1.0/1.0, id5790=1.0/1.0/1.0, id5791=1.0/1.0/1.0, id5792=1.0/1.0/1.0, id5793=1.0/1.0/1.0, id5794=1.0/1.0/1.0, id5795=1.0/1.0/1.0, id5796=1.0/1.0/1.0, id5797=1.0/1.0/1.0, id5798=1.0/1.0/1.0, id5799=1.0/1.0/1.0, id58=1.0/1.0/1.0, id580=1.0/1.0/1.0, id5800=1.0/1.0/1.0, id5801=1.0/1.0/1.0, id5802=1.0/1.0/1.0, id5803=1.0/1.0/1.0, id5804=1.0/1.0/1.0, id5805=1.0/1.0/1.0, id5806=1.0/1.0/1.0, id5807=1.0/1.0/1.0, id5808=1.0/1.0/1.0, id5809=1.0/1.0/1.0, id581=1.0/1.0/1.0, id5810=1.0/1.0/1.0, id5811=1.0/1.0/1.0, id5812=1.0/1.0/1.0, id5813=1.0/1.0/1.0, id5814=1.0/1.0/1.0, id5815=1.0/1.0/1.0, id5816=1.0/1.0/1.0, id5817=1.0/1.0/1.0, id5818=1.0/1.0/1.0, id5819=1.0/1.0/1.0, id582=1.0/1.0/1.0, id5820=1.0/1.0/1.0, id5821=1.0/1.0/1.0, id5822=1.0/1.0/1.0, id5823=1.0/1.0/1.0, id5824=1.0/1.0/1.0, id5825=1.0/1.0/1.0, id5826=1.0/1.0/1.0, id5827=1.0/1.0/1.0, id5828=1.0/1.0/1.0, id5829=1.0/1.0/1.0, id583=1.0/1.0/1.0, id5830=1.0/1.0/1.0, id5831=1.0/1.0/1.0, id5832=1.0/1.0/1.0, id5833=1.0/1.0/1.0, id5834=1.0/1.0/1.0, id5835=1.0/1.0/1.0, id5836=1.0/1.0/1.0, id5837=1.0/1.0/1.0, id5838=1.0/1.0/1.0, id5839=1.0/1.0/1.0, id584=1.0/1.0/1.0, id5840=1.0/1.0/1.0, id5841=1.0/1.0/1.0, id5842=1.0/1.0/1.0, id5843=1.0/1.0/1.0, id5844=1.0/1.0/1.0, id5845=1.0/1.0/1.0, id5846=1.0/1.0/1.0, id5847=1.0/1.0/1.0, id5848=1.0/1.0/1.0, id5849=1.0/1.0/1.0, id585=1.0/1.0/1.0, id5850=1.0/1.0/1.0, id5851=1.0/1.0/1.0, id5852=1.0/1.0/1.0, id5853=1.0/1.0/1.0, id5854=1.0/1.0/1.0, id5855=1.0/1.0/1.0, id5856=1.0/1.0/1.0, id5857=1.0/1.0/1.0, id5858=1.0/1.0/1.0, id5859=1.0/1.0/1.0, id586=1.0/1.0/1.0, id5860=1.0/1.0/1.0, id5861=1.0/1.0/1.0, id5862=1.0/1.0/1.0, id5863=1.0/1.0/1.0, id5864=1.0/1.0/1.0, id5865=1.0/1.0/1.0, id5866=1.0/1.0/1.0, id5867=1.0/1.0/1.0, id5868=1.0/1.0/1.0, id5869=1.0/1.0/1.0, id587=1.0/1.0/1.0, id5870=1.0/1.0/1.0, id5871=1.0/1.0/1.0, id5872=1.0/1.0/1.0, id5873=1.0/1.0/1.0, id5874=1.0/1.0/1.0, id5875=1.0/1.0/1.0, id5876=1.0/1.0/1.0, id5877=1.0/1.0/1.0, id5878=1.0/1.0/1.0, id5879=1.0/1.0/1.0, id588=1.0/1.0/1.0, id5880=1.0/1.0/1.0, id5881=1.0/1.0/1.0, id5882=1.0/1.0/1.0, id5883=1.0/1.0/1.0, id5884=1.0/1.0/1.0, id5885=1.0/1.0/1.0, id5886=1.0/1.0/1.0, id5887=1.0/1.0/1.0, id5888=1.0/1.0/1.0, id5889=1.0/1.0/1.0, id589=1.0/1.0/1.0, id5890=1.0/1.0/1.0, id5891=1.0/1.0/1.0, id5892=1.0/1.0/1.0, id5893=1.0/1.0/1.0, id5894=1.0/1.0/1.0, id5895=1.0/1.0/1.0, id5896=1.0/1.0/1.0, id5897=1.0/1.0/1.0, id5898=1.0/1.0/1.0, id5899=1.0/1.0/1.0, id59=1.0/1.0/1.0, id590=1.0/1.0/1.0, id5900=1.0/1.0/1.0, id5901=1.0/1.0/1.0, id5902=1.0/1.0/1.0, id5903=1.0/1.0/1.0, id5904=1.0/1.0/1.0, id5905=1.0/1.0/1.0, id5906=1.0/1.0/1.0, id5907=1.0/1.0/1.0, id5908=1.0/1.0/1.0, id5909=1.0/1.0/1.0, id591=1.0/1.0/1.0, id5910=1.0/1.0/1.0, id5911=1.0/1.0/1.0, id5912=1.0/1.0/1.0, id5913=1.0/1.0/1.0, id5914=1.0/1.0/1.0, id5915=1.0/1.0/1.0, id5916=1.0/1.0/1.0, id5917=1.0/1.0/1.0, id5918=1.0/1.0/1.0, id5919=1.0/1.0/1.0, id592=1.0/1.0/1.0, id5920=1.0/1.0/1.0, id5921=1.0/1.0/1.0, id5922=1.0/1.0/1.0, id5923=1.0/1.0/1.0, id5924=1.0/1.0/1.0, id5925=1.0/1.0/1.0, id5926=1.0/1.0/1.0, id5927=1.0/1.0/1.0, id5928=1.0/1.0/1.0, id5929=1.0/1.0/1.0, id593=1.0/1.0/1.0, id5930=1.0/1.0/1.0, id5931=1.0/1.0/1.0, id5932=1.0/1.0/1.0, id5933=1.0/1.0/1.0, id5934=1.0/1.0/1.0, id5935=1.0/1.0/1.0, id5936=1.0/1.0/1.0, id5937=1.0/1.0/1.0, id5938=1.0/1.0/1.0, id5939=1.0/1.0/1.0, id594=1.0/1.0/1.0, id5940=1.0/1.0/1.0, id5941=1.0/1.0/1.0, id5942=1.0/1.0/1.0, id5943=1.0/1.0/1.0, id5944=1.0/1.0/1.0, id5945=1.0/1.0/1.0, id5946=1.0/1.0/1.0, id5947=1.0/1.0/1.0, id5948=1.0/1.0/1.0, id5949=1.0/1.0/1.0, id595=1.0/1.0/1.0, id5950=1.0/1.0/1.0, id5951=1.0/1.0/1.0, id5952=1.0/1.0/1.0, id5953=1.0/1.0/1.0, id5954=1.0/1.0/1.0, id5955=1.0/1.0/1.0, id5956=1.0/1.0/1.0, id5957=1.0/1.0/1.0, id5958=1.0/1.0/1.0, id5959=1.0/1.0/1.0, id596=1.0/1.0/1.0, id5960=1.0/1.0/1.0, id5961=1.0/1.0/1.0, id5962=1.0/1.0/1.0, id5963=1.0/1.0/1.0, id5964=1.0/1.0/1.0, id5965=1.0/1.0/1.0, id5966=1.0/1.0/1.0, id5967=1.0/1.0/1.0, id5968=1.0/1.0/1.0, id5969=1.0/1.0/1.0, id597=1.0/1.0/1.0, id5970=1.0/1.0/1.0, id5971=1.0/1.0/1.0, id5972=1.0/1.0/1.0, id5973=1.0/1.0/1.0, id5974=1.0/1.0/1.0, id5975=1.0/1.0/1.0, id5976=1.0/1.0/1.0, id5977=1.0/1.0/1.0, id5978=1.0/1.0/1.0, id5979=1.0/1.0/1.0, id598=1.0/1.0/1.0, id5980=1.0/1.0/1.0, id5981=1.0/1.0/1.0, id5982=1.0/1.0/1.0, id5983=1.0/1.0/1.0, id5984=1.0/1.0/1.0, id5985=1.0/1.0/1.0, id5986=1.0/1.0/1.0, id5987=1.0/1.0/1.0, id5988=1.0/1.0/1.0, id5989=1.0/1.0/1.0, id599=1.0/1.0/1.0, id5990=1.0/1.0/1.0, id5991=1.0/1.0/1.0, id5992=1.0/1.0/1.0, id5993=1.0/1.0/1.0, id5994=1.0/1.0/1.0, id5995=1.0/1.0/1.0, id5996=1.0/1.0/1.0, id5997=1.0/1.0/1.0, id5998=1.0/1.0/1.0, id5999=1.0/1.0/1.0, id6=1.0/1.0/1.0, id60=1.0/1.0/1.0, id600=1.0/1.0/1.0, id6000=1.0/1.0/1.0, id6001=1.0/1.0/1.0, id6002=1.0/1.0/1.0, id6003=1.0/1.0/1.0, id6004=1.0/1.0/1.0, id6005=1.0/1.0/1.0, id6006=1.0/1.0/1.0, id6007=1.0/1.0/1.0, id6008=1.0/1.0/1.0, id6009=1.0/1.0/1.0, id601=1.0/1.0/1.0, id6010=1.0/1.0/1.0, id6011=1.0/1.0/1.0, id6012=1.0/1.0/1.0, id6013=1.0/1.0/1.0, id6014=1.0/1.0/1.0, id6015=1.0/1.0/1.0, id6016=1.0/1.0/1.0, id6017=1.0/1.0/1.0, id6018=1.0/1.0/1.0, id6019=1.0/1.0/1.0, id602=1.0/1.0/1.0, id6020=1.0/1.0/1.0, id6021=1.0/1.0/1.0, id6022=1.0/1.0/1.0, id6023=1.0/1.0/1.0, id6024=1.0/1.0/1.0, id6025=1.0/1.0/1.0, id6026=1.0/1.0/1.0, id6027=1.0/1.0/1.0, id6028=1.0/1.0/1.0, id6029=1.0/1.0/1.0, id603=1.0/1.0/1.0, id6030=1.0/1.0/1.0, id6031=1.0/1.0/1.0, id6032=1.0/1.0/1.0, id6033=1.0/1.0/1.0, id6034=1.0/1.0/1.0, id6035=1.0/1.0/1.0, id6036=1.0/1.0/1.0, id6037=1.0/1.0/1.0, id6038=1.0/1.0/1.0, id6039=1.0/1.0/1.0, id604=1.0/1.0/1.0, id6040=1.0/1.0/1.0, id6041=1.0/1.0/1.0, id6042=1.0/1.0/1.0, id6043=1.0/1.0/1.0, id6044=1.0/1.0/1.0, id6045=1.0/1.0/1.0, id6046=1.0/1.0/1.0, id6047=1.0/1.0/1.0, id6048=1.0/1.0/1.0, id6049=1.0/1.0/1.0, id605=1.0/1.0/1.0, id6050=1.0/1.0/1.0, id6051=1.0/1.0/1.0, id6052=1.0/1.0/1.0, id6053=1.0/1.0/1.0, id6054=1.0/1.0/1.0, id6055=1.0/1.0/1.0, id6056=1.0/1.0/1.0, id6057=1.0/1.0/1.0, id6058=1.0/1.0/1.0, id6059=1.0/1.0/1.0, id606=1.0/1.0/1.0, id6060=1.0/1.0/1.0, id6061=1.0/1.0/1.0, id6062=1.0/1.0/1.0, id6063=1.0/1.0/1.0, id6064=1.0/1.0/1.0, id6065=1.0/1.0/1.0, id6066=1.0/1.0/1.0, id6067=1.0/1.0/1.0, id6068=1.0/1.0/1.0, id6069=1.0/1.0/1.0, id607=1.0/1.0/1.0, id6070=1.0/1.0/1.0, id6071=1.0/1.0/1.0, id6072=1.0/1.0/1.0, id6073=1.0/1.0/1.0, id6074=1.0/1.0/1.0, id6075=1.0/1.0/1.0, id6076=1.0/1.0/1.0, id6077=1.0/1.0/1.0, id6078=1.0/1.0/1.0, id6079=1.0/1.0/1.0, id608=1.0/1.0/1.0, id6080=1.0/1.0/1.0, id6081=1.0/1.0/1.0, id6082=1.0/1.0/1.0, id6083=1.0/1.0/1.0, id6084=1.0/1.0/1.0, id6085=1.0/1.0/1.0, id6086=1.0/1.0/1.0, id6087=1.0/1.0/1.0, id6088=1.0/1.0/1.0, id6089=1.0/1.0/1.0, id609=1.0/1.0/1.0, id6090=1.0/1.0/1.0, id6091=1.0/1.0/1.0, id6092=1.0/1.0/1.0, id6093=1.0/1.0/1.0, id6094=1.0/1.0/1.0, id6095=1.0/1.0/1.0, id6096=1.0/1.0/1.0, id6097=1.0/1.0/1.0, id6098=1.0/1.0/1.0, id6099=1.0/1.0/1.0, id61=1.0/1.0/1.0, id610=1.0/1.0/1.0, id6100=1.0/1.0/1.0, id6101=1.0/1.0/1.0, id6102=1.0/1.0/1.0, id6103=1.0/1.0/1.0, id6104=1.0/1.0/1.0, id6105=1.0/1.0/1.0, id6106=1.0/1.0/1.0, id6107=1.0/1.0/1.0, id6108=1.0/1.0/1.0, id6109=1.0/1.0/1.0, id611=1.0/1.0/1.0, id6110=1.0/1.0/1.0, id6111=1.0/1.0/1.0, id6112=1.0/1.0/1.0, id6113=1.0/1.0/1.0, id6114=1.0/1.0/1.0, id6115=1.0/1.0/1.0, id6116=1.0/1.0/1.0, id6117=1.0/1.0/1.0, id6118=1.0/1.0/1.0, id6119=1.0/1.0/1.0, id612=1.0/1.0/1.0, id6120=1.0/1.0/1.0, id6121=1.0/1.0/1.0, id6122=1.0/1.0/1.0, id6123=1.0/1.0/1.0, id6124=1.0/1.0/1.0, id6125=1.0/1.0/1.0, id6126=1.0/1.0/1.0, id6127=1.0/1.0/1.0, id6128=1.0/1.0/1.0, id6129=1.0/1.0/1.0, id613=1.0/1.0/1.0, id6130=1.0/1.0/1.0, id6131=1.0/1.0/1.0, id6132=1.0/1.0/1.0, id6133=1.0/1.0/1.0, id6134=1.0/1.0/1.0, id6135=1.0/1.0/1.0, id6136=1.0/1.0/1.0, id6137=1.0/1.0/1.0, id6138=1.0/1.0/1.0, id6139=1.0/1.0/1.0, id614=1.0/1.0/1.0, id6140=1.0/1.0/1.0, id6141=1.0/1.0/1.0, id6142=1.0/1.0/1.0, id6143=1.0/1.0/1.0, id6144=1.0/1.0/1.0, id6145=1.0/1.0/1.0, id6146=1.0/1.0/1.0, id6147=1.0/1.0/1.0, id6148=1.0/1.0/1.0, id6149=1.0/1.0/1.0, id615=1.0/1.0/1.0, id6150=1.0/1.0/1.0, id6151=1.0/1.0/1.0, id6152=1.0/1.0/1.0, id6153=1.0/1.0/1.0, id6154=1.0/1.0/1.0, id6155=1.0/1.0/1.0, id6156=1.0/1.0/1.0, id6157=1.0/1.0/1.0, id6158=1.0/1.0/1.0, id6159=1.0/1.0/1.0, id616=1.0/1.0/1.0, id6160=1.0/1.0/1.0, id6161=1.0/1.0/1.0, id6162=1.0/1.0/1.0, id6163=1.0/1.0/1.0, id6164=1.0/1.0/1.0, id6165=1.0/1.0/1.0, id6166=1.0/1.0/1.0, id6167=1.0/1.0/1.0, id6168=1.0/1.0/1.0, id6169=1.0/1.0/1.0, id617=1.0/1.0/1.0, id6170=1.0/1.0/1.0, id6171=1.0/1.0/1.0, id6172=1.0/1.0/1.0, id6173=1.0/1.0/1.0, id6174=1.0/1.0/1.0, id6175=1.0/1.0/1.0, id6176=1.0/1.0/1.0, id6177=1.0/1.0/1.0, id6178=1.0/1.0/1.0, id6179=1.0/1.0/1.0, id618=1.0/1.0/1.0, id6180=1.0/1.0/1.0, id6181=1.0/1.0/1.0, id6182=1.0/1.0/1.0, id6183=1.0/1.0/1.0, id6184=1.0/1.0/1.0, id6185=1.0/1.0/1.0, id6186=1.0/1.0/1.0, id6187=1.0/1.0/1.0, id6188=1.0/1.0/1.0, id6189=1.0/1.0/1.0, id619=1.0/1.0/1.0, id6190=1.0/1.0/1.0, id6191=1.0/1.0/1.0, id6192=1.0/1.0/1.0, id6193=1.0/1.0/1.0, id6194=1.0/1.0/1.0, id6195=1.0/1.0/1.0, id6196=1.0/1.0/1.0, id6197=1.0/1.0/1.0, id6198=1.0/1.0/1.0, id6199=1.0/1.0/1.0, id62=1.0/1.0/1.0, id620=1.0/1.0/1.0, id6200=1.0/1.0/1.0, id6201=1.0/1.0/1.0, id6202=1.0/1.0/1.0, id6203=1.0/1.0/1.0, id6204=1.0/1.0/1.0, id6205=1.0/1.0/1.0, id6206=1.0/1.0/1.0, id6207=1.0/1.0/1.0, id6208=1.0/1.0/1.0, id6209=1.0/1.0/1.0, id621=1.0/1.0/1.0, id6210=1.0/1.0/1.0, id6211=1.0/1.0/1.0, id6212=1.0/1.0/1.0, id6213=1.0/1.0/1.0, id6214=1.0/1.0/1.0, id6215=1.0/1.0/1.0, id6216=1.0/1.0/1.0, id6217=1.0/1.0/1.0, id6218=1.0/1.0/1.0, id6219=1.0/1.0/1.0, id622=1.0/1.0/1.0, id6220=1.0/1.0/1.0, id6221=1.0/1.0/1.0, id6222=1.0/1.0/1.0, id6223=1.0/1.0/1.0, id6224=1.0/1.0/1.0, id6225=1.0/1.0/1.0, id6226=1.0/1.0/1.0, id6227=1.0/1.0/1.0, id6228=1.0/1.0/1.0, id6229=1.0/1.0/1.0, id623=1.0/1.0/1.0, id6230=1.0/1.0/1.0, id6231=1.0/1.0/1.0, id6232=1.0/1.0/1.0, id6233=1.0/1.0/1.0, id6234=1.0/1.0/1.0, id6235=1.0/1.0/1.0, id6236=1.0/1.0/1.0, id6237=1.0/1.0/1.0, id6238=1.0/1.0/1.0, id6239=1.0/1.0/1.0, id624=1.0/1.0/1.0, id6240=1.0/1.0/1.0, id6241=1.0/1.0/1.0, id6242=1.0/1.0/1.0, id6243=1.0/1.0/1.0, id6244=1.0/1.0/1.0, id6245=1.0/1.0/1.0, id6246=1.0/1.0/1.0, id6247=1.0/1.0/1.0, id6248=1.0/1.0/1.0, id6249=1.0/1.0/1.0, id625=1.0/1.0/1.0, id6250=1.0/1.0/1.0, id6251=1.0/1.0/1.0, id6252=1.0/1.0/1.0, id6253=1.0/1.0/1.0, id6254=1.0/1.0/1.0, id6255=1.0/1.0/1.0, id6256=1.0/1.0/1.0, id6257=1.0/1.0/1.0, id6258=1.0/1.0/1.0, id6259=1.0/1.0/1.0, id626=1.0/1.0/1.0, id6260=1.0/1.0/1.0, id6261=1.0/1.0/1.0, id6262=1.0/1.0/1.0, id6263=1.0/1.0/1.0, id6264=1.0/1.0/1.0, id6265=1.0/1.0/1.0, id6266=1.0/1.0/1.0, id6267=1.0/1.0/1.0, id6268=1.0/1.0/1.0, id6269=1.0/1.0/1.0, id627=1.0/1.0/1.0, id6270=1.0/1.0/1.0, id6271=1.0/1.0/1.0, id6272=1.0/1.0/1.0, id6273=1.0/1.0/1.0, id6274=1.0/1.0/1.0, id6275=1.0/1.0/1.0, id6276=1.0/1.0/1.0, id6277=1.0/1.0/1.0, id6278=1.0/1.0/1.0, id6279=1.0/1.0/1.0, id628=1.0/1.0/1.0, id6280=1.0/1.0/1.0, id6281=1.0/1.0/1.0, id6282=1.0/1.0/1.0, id6283=1.0/1.0/1.0, id6284=1.0/1.0/1.0, id6285=1.0/1.0/1.0, id6286=1.0/1.0/1.0, id6287=1.0/1.0/1.0, id6288=1.0/1.0/1.0, id6289=1.0/1.0/1.0, id629=1.0/1.0/1.0, id6290=1.0/1.0/1.0, id6291=1.0/1.0/1.0, id6292=1.0/1.0/1.0, id6293=1.0/1.0/1.0, id6294=1.0/1.0/1.0, id6295=1.0/1.0/1.0, id6296=1.0/1.0/1.0, id6297=1.0/1.0/1.0, id6298=1.0/1.0/1.0, id6299=1.0/1.0/1.0, id63=1.0/1.0/1.0, id630=1.0/1.0/1.0, id6300=1.0/1.0/1.0, id6301=1.0/1.0/1.0, id6302=1.0/1.0/1.0, id6303=1.0/1.0/1.0, id6304=1.0/1.0/1.0, id6305=1.0/1.0/1.0, id6306=1.0/1.0/1.0, id6307=1.0/1.0/1.0, id6308=1.0/1.0/1.0, id6309=1.0/1.0/1.0, id631=1.0/1.0/1.0, id6310=1.0/1.0/1.0, id6311=1.0/1.0/1.0, id6312=1.0/1.0/1.0, id6313=1.0/1.0/1.0, id6314=1.0/1.0/1.0, id6315=1.0/1.0/1.0, id6316=1.0/1.0/1.0, id6317=1.0/1.0/1.0, id6318=1.0/1.0/1.0, id6319=1.0/1.0/1.0, id632=1.0/1.0/1.0, id6320=1.0/1.0/1.0, id6321=1.0/1.0/1.0, id6322=1.0/1.0/1.0, id6323=1.0/1.0/1.0, id6324=1.0/1.0/1.0, id6325=1.0/1.0/1.0, id6326=1.0/1.0/1.0, id6327=1.0/1.0/1.0, id6328=1.0/1.0/1.0, id6329=1.0/1.0/1.0, id633=1.0/1.0/1.0, id6330=1.0/1.0/1.0, id6331=1.0/1.0/1.0, id6332=1.0/1.0/1.0, id6333=1.0/1.0/1.0, id6334=1.0/1.0/1.0, id6335=1.0/1.0/1.0, id6336=1.0/1.0/1.0, id6337=1.0/1.0/1.0, id6338=1.0/1.0/1.0, id6339=1.0/1.0/1.0, id634=1.0/1.0/1.0, id6340=1.0/1.0/1.0, id6341=1.0/1.0/1.0, id6342=1.0/1.0/1.0, id6343=1.0/1.0/1.0, id6344=1.0/1.0/1.0, id6345=1.0/1.0/1.0, id6346=1.0/1.0/1.0, id6347=1.0/1.0/1.0, id6348=1.0/1.0/1.0, id6349=1.0/1.0/1.0, id635=1.0/1.0/1.0, id6350=1.0/1.0/1.0, id6351=1.0/1.0/1.0, id6352=1.0/1.0/1.0, id6353=1.0/1.0/1.0, id6354=1.0/1.0/1.0, id6355=1.0/1.0/1.0, id6356=1.0/1.0/1.0, id6357=1.0/1.0/1.0, id6358=1.0/1.0/1.0, id6359=1.0/1.0/1.0, id636=1.0/1.0/1.0, id6360=1.0/1.0/1.0, id6361=1.0/1.0/1.0, id6362=1.0/1.0/1.0, id6363=1.0/1.0/1.0, id6364=1.0/1.0/1.0, id6365=1.0/1.0/1.0, id6366=1.0/1.0/1.0, id6367=1.0/1.0/1.0, id6368=1.0/1.0/1.0, id6369=1.0/1.0/1.0, id637=1.0/1.0/1.0, id6370=1.0/1.0/1.0, id6371=1.0/1.0/1.0, id6372=1.0/1.0/1.0, id6373=1.0/1.0/1.0, id6374=1.0/1.0/1.0, id6375=1.0/1.0/1.0, id6376=1.0/1.0/1.0, id6377=1.0/1.0/1.0, id6378=1.0/1.0/1.0, id6379=1.0/1.0/1.0, id638=1.0/1.0/1.0, id6380=1.0/1.0/1.0, id6381=1.0/1.0/1.0, id6382=1.0/1.0/1.0, id6383=1.0/1.0/1.0, id6384=1.0/1.0/1.0, id6385=1.0/1.0/1.0, id6386=1.0/1.0/1.0, id6387=1.0/1.0/1.0, id6388=1.0/1.0/1.0, id6389=1.0/1.0/1.0, id639=1.0/1.0/1.0, id6390=1.0/1.0/1.0, id6391=1.0/1.0/1.0, id6392=1.0/1.0/1.0, id6393=1.0/1.0/1.0, id6394=1.0/1.0/1.0, id6395=1.0/1.0/1.0, id6396=1.0/1.0/1.0, id6397=1.0/1.0/1.0, id6398=1.0/1.0/1.0, id6399=1.0/1.0/1.0, id64=1.0/1.0/1.0, id640=1.0/1.0/1.0, id6400=1.0/1.0/1.0, id6401=1.0/1.0/1.0, id6402=1.0/1.0/1.0, id6403=1.0/1.0/1.0, id6404=1.0/1.0/1.0, id6405=1.0/1.0/1.0, id6406=1.0/1.0/1.0, id6407=1.0/1.0/1.0, id6408=1.0/1.0/1.0, id6409=1.0/1.0/1.0, id641=1.0/1.0/1.0, id6410=1.0/1.0/1.0, id6411=1.0/1.0/1.0, id6412=1.0/1.0/1.0, id6413=1.0/1.0/1.0, id6414=1.0/1.0/1.0, id6415=1.0/1.0/1.0, id6416=1.0/1.0/1.0, id6417=1.0/1.0/1.0, id6418=1.0/1.0/1.0, id6419=1.0/1.0/1.0, id642=1.0/1.0/1.0, id6420=1.0/1.0/1.0, id6421=1.0/1.0/1.0, id6422=1.0/1.0/1.0, id6423=1.0/1.0/1.0, id6424=1.0/1.0/1.0, id6425=1.0/1.0/1.0, id6426=1.0/1.0/1.0, id6427=1.0/1.0/1.0, id6428=1.0/1.0/1.0, id6429=1.0/1.0/1.0, id643=1.0/1.0/1.0, id6430=1.0/1.0/1.0, id6431=1.0/1.0/1.0, id6432=1.0/1.0/1.0, id6433=1.0/1.0/1.0, id6434=1.0/1.0/1.0, id6435=1.0/1.0/1.0, id6436=1.0/1.0/1.0, id6437=1.0/1.0/1.0, id6438=1.0/1.0/1.0, id6439=1.0/1.0/1.0, id644=1.0/1.0/1.0, id6440=1.0/1.0/1.0, id6441=1.0/1.0/1.0, id6442=1.0/1.0/1.0, id6443=1.0/1.0/1.0, id6444=1.0/1.0/1.0, id6445=1.0/1.0/1.0, id6446=1.0/1.0/1.0, id6447=1.0/1.0/1.0, id6448=1.0/1.0/1.0, id6449=1.0/1.0/1.0, id645=1.0/1.0/1.0, id6450=1.0/1.0/1.0, id6451=1.0/1.0/1.0, id6452=1.0/1.0/1.0, id6453=1.0/1.0/1.0, id6454=1.0/1.0/1.0, id6455=1.0/1.0/1.0, id6456=1.0/1.0/1.0, id6457=1.0/1.0/1.0, id6458=1.0/1.0/1.0, id6459=1.0/1.0/1.0, id646=1.0/1.0/1.0, id6460=1.0/1.0/1.0, id6461=1.0/1.0/1.0, id6462=1.0/1.0/1.0, id6463=1.0/1.0/1.0, id6464=1.0/1.0/1.0, id6465=1.0/1.0/1.0, id6466=1.0/1.0/1.0, id6467=1.0/1.0/1.0, id6468=1.0/1.0/1.0, id6469=1.0/1.0/1.0, id647=1.0/1.0/1.0, id6470=1.0/1.0/1.0, id6471=1.0/1.0/1.0, id6472=1.0/1.0/1.0, id6473=1.0/1.0/1.0, id6474=1.0/1.0/1.0, id6475=1.0/1.0/1.0, id6476=1.0/1.0/1.0, id6477=1.0/1.0/1.0, id6478=1.0/1.0/1.0, id6479=1.0/1.0/1.0, id648=1.0/1.0/1.0, id6480=1.0/1.0/1.0, id6481=1.0/1.0/1.0, id6482=1.0/1.0/1.0, id6483=1.0/1.0/1.0, id6484=1.0/1.0/1.0, id6485=1.0/1.0/1.0, id6486=1.0/1.0/1.0, id6487=1.0/1.0/1.0, id6488=1.0/1.0/1.0, id6489=1.0/1.0/1.0, id649=1.0/1.0/1.0, id6490=1.0/1.0/1.0, id6491=1.0/1.0/1.0, id6492=1.0/1.0/1.0, id6493=1.0/1.0/1.0, id6494=1.0/1.0/1.0, id6495=1.0/1.0/1.0, id6496=1.0/1.0/1.0, id6497=1.0/1.0/1.0, id6498=1.0/1.0/1.0, id6499=1.0/1.0/1.0, id65=1.0/1.0/1.0, id650=1.0/1.0/1.0, id6500=1.0/1.0/1.0, id6501=1.0/1.0/1.0, id6502=1.0/1.0/1.0, id6503=1.0/1.0/1.0, id6504=1.0/1.0/1.0, id6505=1.0/1.0/1.0, id6506=1.0/1.0/1.0, id6507=1.0/1.0/1.0, id6508=1.0/1.0/1.0, id6509=1.0/1.0/1.0, id651=1.0/1.0/1.0, id6510=1.0/1.0/1.0, id6511=1.0/1.0/1.0, id6512=1.0/1.0/1.0, id6513=1.0/1.0/1.0, id6514=1.0/1.0/1.0, id6515=1.0/1.0/1.0, id6516=1.0/1.0/1.0, id6517=1.0/1.0/1.0, id6518=1.0/1.0/1.0, id6519=1.0/1.0/1.0, id652=1.0/1.0/1.0, id6520=1.0/1.0/1.0, id6521=1.0/1.0/1.0, id6522=1.0/1.0/1.0, id6523=1.0/1.0/1.0, id6524=1.0/1.0/1.0, id6525=1.0/1.0/1.0, id6526=1.0/1.0/1.0, id6527=1.0/1.0/1.0, id6528=1.0/1.0/1.0, id6529=1.0/1.0/1.0, id653=1.0/1.0/1.0, id6530=1.0/1.0/1.0, id6531=1.0/1.0/1.0, id6532=1.0/1.0/1.0, id6533=1.0/1.0/1.0, id6534=1.0/1.0/1.0, id6535=1.0/1.0/1.0, id6536=1.0/1.0/1.0, id6537=1.0/1.0/1.0, id6538=1.0/1.0/1.0, id6539=1.0/1.0/1.0, id654=1.0/1.0/1.0, id6540=1.0/1.0/1.0, id6541=1.0/1.0/1.0, id6542=1.0/1.0/1.0, id6543=1.0/1.0/1.0, id6544=1.0/1.0/1.0, id6545=1.0/1.0/1.0, id6546=1.0/1.0/1.0, id6547=1.0/1.0/1.0, id6548=1.0/1.0/1.0, id6549=1.0/1.0/1.0, id655=1.0/1.0/1.0, id6550=1.0/1.0/1.0, id6551=1.0/1.0/1.0, id6552=1.0/1.0/1.0, id6553=1.0/1.0/1.0, id6554=1.0/1.0/1.0, id6555=1.0/1.0/1.0, id6556=1.0/1.0/1.0, id6557=1.0/1.0/1.0, id6558=1.0/1.0/1.0, id6559=1.0/1.0/1.0, id656=1.0/1.0/1.0, id6560=1.0/1.0/1.0, id6561=1.0/1.0/1.0, id6562=1.0/1.0/1.0, id6563=1.0/1.0/1.0, id6564=1.0/1.0/1.0, id6565=1.0/1.0/1.0, id6566=1.0/1.0/1.0, id6567=1.0/1.0/1.0, id6568=1.0/1.0/1.0, id6569=1.0/1.0/1.0, id657=1.0/1.0/1.0, id6570=1.0/1.0/1.0, id6571=1.0/1.0/1.0, id6572=1.0/1.0/1.0, id6573=1.0/1.0/1.0, id6574=1.0/1.0/1.0, id6575=1.0/1.0/1.0, id6576=1.0/1.0/1.0, id6577=1.0/1.0/1.0, id6578=1.0/1.0/1.0, id6579=1.0/1.0/1.0, id658=1.0/1.0/1.0, id6580=1.0/1.0/1.0, id6581=1.0/1.0/1.0, id6582=1.0/1.0/1.0, id6583=1.0/1.0/1.0, id6584=1.0/1.0/1.0, id6585=1.0/1.0/1.0, id6586=1.0/1.0/1.0, id6587=1.0/1.0/1.0, id6588=1.0/1.0/1.0, id6589=1.0/1.0/1.0, id659=1.0/1.0/1.0, id6590=1.0/1.0/1.0, id6591=1.0/1.0/1.0, id6592=1.0/1.0/1.0, id6593=1.0/1.0/1.0, id6594=1.0/1.0/1.0, id6595=1.0/1.0/1.0, id6596=1.0/1.0/1.0, id6597=1.0/1.0/1.0, id6598=1.0/1.0/1.0, id6599=1.0/1.0/1.0, id66=1.0/1.0/1.0, id660=1.0/1.0/1.0, id6600=1.0/1.0/1.0, id6601=1.0/1.0/1.0, id6602=1.0/1.0/1.0, id6603=1.0/1.0/1.0, id6604=1.0/1.0/1.0, id6605=1.0/1.0/1.0, id6606=1.0/1.0/1.0, id6607=1.0/1.0/1.0, id6608=1.0/1.0/1.0, id6609=1.0/1.0/1.0, id661=1.0/1.0/1.0, id6610=1.0/1.0/1.0, id6611=1.0/1.0/1.0, id6612=1.0/1.0/1.0, id6613=1.0/1.0/1.0, id6614=1.0/1.0/1.0, id6615=1.0/1.0/1.0, id6616=1.0/1.0/1.0, id6617=1.0/1.0/1.0, id6618=1.0/1.0/1.0, id6619=1.0/1.0/1.0, id662=1.0/1.0/1.0, id6620=1.0/1.0/1.0, id6621=1.0/1.0/1.0, id6622=1.0/1.0/1.0, id6623=1.0/1.0/1.0, id6624=1.0/1.0/1.0, id6625=1.0/1.0/1.0, id6626=1.0/1.0/1.0, id6627=1.0/1.0/1.0, id6628=1.0/1.0/1.0, id6629=1.0/1.0/1.0, id663=1.0/1.0/1.0, id6630=1.0/1.0/1.0, id6631=1.0/1.0/1.0, id6632=1.0/1.0/1.0, id6633=1.0/1.0/1.0, id6634=1.0/1.0/1.0, id6635=1.0/1.0/1.0, id6636=1.0/1.0/1.0, id6637=1.0/1.0/1.0, id6638=1.0/1.0/1.0, id6639=1.0/1.0/1.0, id664=1.0/1.0/1.0, id6640=1.0/1.0/1.0, id6641=1.0/1.0/1.0, id6642=1.0/1.0/1.0, id6643=1.0/1.0/1.0, id6644=1.0/1.0/1.0, id6645=1.0/1.0/1.0, id6646=1.0/1.0/1.0, id6647=1.0/1.0/1.0, id6648=1.0/1.0/1.0, id6649=1.0/1.0/1.0, id665=1.0/1.0/1.0, id6650=1.0/1.0/1.0, id6651=1.0/1.0/1.0, id6652=1.0/1.0/1.0, id6653=1.0/1.0/1.0, id6654=1.0/1.0/1.0, id6655=1.0/1.0/1.0, id6656=1.0/1.0/1.0, id6657=1.0/1.0/1.0, id6658=1.0/1.0/1.0, id6659=1.0/1.0/1.0, id666=1.0/1.0/1.0, id6660=1.0/1.0/1.0, id6661=1.0/1.0/1.0, id6662=1.0/1.0/1.0, id6663=1.0/1.0/1.0, id6664=1.0/1.0/1.0, id6665=1.0/1.0/1.0, id6666=1.0/1.0/1.0, id6667=1.0/1.0/1.0, id6668=1.0/1.0/1.0, id6669=1.0/1.0/1.0, id667=1.0/1.0/1.0, id6670=1.0/1.0/1.0, id6671=1.0/1.0/1.0, id6672=1.0/1.0/1.0, id6673=1.0/1.0/1.0, id6674=1.0/1.0/1.0, id6675=1.0/1.0/1.0, id6676=1.0/1.0/1.0, id6677=1.0/1.0/1.0, id6678=1.0/1.0/1.0, id6679=1.0/1.0/1.0, id668=1.0/1.0/1.0, id6680=1.0/1.0/1.0, id6681=1.0/1.0/1.0, id6682=1.0/1.0/1.0, id6683=1.0/1.0/1.0, id6684=1.0/1.0/1.0, id6685=1.0/1.0/1.0, id6686=1.0/1.0/1.0, id6687=1.0/1.0/1.0, id6688=1.0/1.0/1.0, id6689=1.0/1.0/1.0, id669=1.0/1.0/1.0, id6690=1.0/1.0/1.0, id6691=1.0/1.0/1.0, id6692=1.0/1.0/1.0, id6693=1.0/1.0/1.0, id6694=1.0/1.0/1.0, id6695=1.0/1.0/1.0, id6696=1.0/1.0/1.0, id6697=1.0/1.0/1.0, id6698=1.0/1.0/1.0, id6699=1.0/1.0/1.0, id67=1.0/1.0/1.0, id670=1.0/1.0/1.0, id6700=1.0/1.0/1.0, id6701=1.0/1.0/1.0, id6702=1.0/1.0/1.0, id6703=1.0/1.0/1.0, id6704=1.0/1.0/1.0, id6705=1.0/1.0/1.0, id6706=1.0/1.0/1.0, id6707=1.0/1.0/1.0, id6708=1.0/1.0/1.0, id6709=1.0/1.0/1.0, id671=1.0/1.0/1.0, id6710=1.0/1.0/1.0, id6711=1.0/1.0/1.0, id6712=1.0/1.0/1.0, id6713=1.0/1.0/1.0, id6714=1.0/1.0/1.0, id6715=1.0/1.0/1.0, id6716=1.0/1.0/1.0, id6717=1.0/1.0/1.0, id6718=1.0/1.0/1.0, id6719=1.0/1.0/1.0, id672=1.0/1.0/1.0, id6720=1.0/1.0/1.0, id6721=1.0/1.0/1.0, id6722=1.0/1.0/1.0, id6723=1.0/1.0/1.0, id6724=1.0/1.0/1.0, id6725=1.0/1.0/1.0, id6726=1.0/1.0/1.0, id6727=1.0/1.0/1.0, id6728=1.0/1.0/1.0, id6729=1.0/1.0/1.0, id673=1.0/1.0/1.0, id6730=1.0/1.0/1.0, id6731=1.0/1.0/1.0, id6732=1.0/1.0/1.0, id6733=1.0/1.0/1.0, id6734=1.0/1.0/1.0, id6735=1.0/1.0/1.0, id6736=1.0/1.0/1.0, id6737=1.0/1.0/1.0, id6738=1.0/1.0/1.0, id6739=1.0/1.0/1.0, id674=1.0/1.0/1.0, id6740=1.0/1.0/1.0, id6741=1.0/1.0/1.0, id6742=1.0/1.0/1.0, id6743=1.0/1.0/1.0, id6744=1.0/1.0/1.0, id6745=1.0/1.0/1.0, id6746=1.0/1.0/1.0, id6747=1.0/1.0/1.0, id6748=1.0/1.0/1.0, id6749=1.0/1.0/1.0, id675=1.0/1.0/1.0, id6750=1.0/1.0/1.0, id6751=1.0/1.0/1.0, id6752=1.0/1.0/1.0, id6753=1.0/1.0/1.0, id6754=1.0/1.0/1.0, id6755=1.0/1.0/1.0, id6756=1.0/1.0/1.0, id6757=1.0/1.0/1.0, id6758=1.0/1.0/1.0, id6759=1.0/1.0/1.0, id676=1.0/1.0/1.0, id6760=1.0/1.0/1.0, id6761=1.0/1.0/1.0, id6762=1.0/1.0/1.0, id6763=1.0/1.0/1.0, id6764=1.0/1.0/1.0, id6765=1.0/1.0/1.0, id6766=1.0/1.0/1.0, id6767=1.0/1.0/1.0, id6768=1.0/1.0/1.0, id6769=1.0/1.0/1.0, id677=1.0/1.0/1.0, id6770=1.0/1.0/1.0, id6771=1.0/1.0/1.0, id6772=1.0/1.0/1.0, id6773=1.0/1.0/1.0, id6774=1.0/1.0/1.0, id6775=1.0/1.0/1.0, id6776=1.0/1.0/1.0, id6777=1.0/1.0/1.0, id6778=1.0/1.0/1.0, id6779=1.0/1.0/1.0, id678=1.0/1.0/1.0, id6780=1.0/1.0/1.0, id6781=1.0/1.0/1.0, id6782=1.0/1.0/1.0, id6783=1.0/1.0/1.0, id6784=1.0/1.0/1.0, id6785=1.0/1.0/1.0, id6786=1.0/1.0/1.0, id6787=1.0/1.0/1.0, id6788=1.0/1.0/1.0, id6789=1.0/1.0/1.0, id679=1.0/1.0/1.0, id6790=1.0/1.0/1.0, id6791=1.0/1.0/1.0, id6792=1.0/1.0/1.0, id6793=1.0/1.0/1.0, id6794=1.0/1.0/1.0, id6795=1.0/1.0/1.0, id6796=1.0/1.0/1.0, id6797=1.0/1.0/1.0, id6798=1.0/1.0/1.0, id6799=1.0/1.0/1.0, id68=1.0/1.0/1.0, id680=1.0/1.0/1.0, id6800=1.0/1.0/1.0, id6801=1.0/1.0/1.0, id6802=1.0/1.0/1.0, id6803=1.0/1.0/1.0, id6804=1.0/1.0/1.0, id6805=1.0/1.0/1.0, id6806=1.0/1.0/1.0, id6807=1.0/1.0/1.0, id6808=1.0/1.0/1.0, id6809=1.0/1.0/1.0, id681=1.0/1.0/1.0, id6810=1.0/1.0/1.0, id6811=1.0/1.0/1.0, id6812=1.0/1.0/1.0, id6813=1.0/1.0/1.0, id6814=1.0/1.0/1.0, id6815=1.0/1.0/1.0, id6816=1.0/1.0/1.0, id6817=1.0/1.0/1.0, id6818=1.0/1.0/1.0, id6819=1.0/1.0/1.0, id682=1.0/1.0/1.0, id6820=1.0/1.0/1.0, id6821=1.0/1.0/1.0, id6822=1.0/1.0/1.0, id6823=1.0/1.0/1.0, id6824=1.0/1.0/1.0, id6825=1.0/1.0/1.0, id6826=1.0/1.0/1.0, id6827=1.0/1.0/1.0, id6828=1.0/1.0/1.0, id6829=1.0/1.0/1.0, id683=1.0/1.0/1.0, id6830=1.0/1.0/1.0, id6831=1.0/1.0/1.0, id6832=1.0/1.0/1.0, id6833=1.0/1.0/1.0, id6834=1.0/1.0/1.0, id6835=1.0/1.0/1.0, id6836=1.0/1.0/1.0, id6837=1.0/1.0/1.0, id6838=1.0/1.0/1.0, id6839=1.0/1.0/1.0, id684=1.0/1.0/1.0, id6840=1.0/1.0/1.0, id6841=1.0/1.0/1.0, id6842=1.0/1.0/1.0, id6843=1.0/1.0/1.0, id6844=1.0/1.0/1.0, id6845=1.0/1.0/1.0, id6846=1.0/1.0/1.0, id6847=1.0/1.0/1.0, id6848=1.0/1.0/1.0, id6849=1.0/1.0/1.0, id685=1.0/1.0/1.0, id6850=1.0/1.0/1.0, id6851=1.0/1.0/1.0, id6852=1.0/1.0/1.0, id6853=1.0/1.0/1.0, id6854=1.0/1.0/1.0, id6855=1.0/1.0/1.0, id6856=1.0/1.0/1.0, id6857=1.0/1.0/1.0, id6858=1.0/1.0/1.0, id6859=1.0/1.0/1.0, id686=1.0/1.0/1.0, id6860=1.0/1.0/1.0, id6861=1.0/1.0/1.0, id6862=1.0/1.0/1.0, id6863=1.0/1.0/1.0, id6864=1.0/1.0/1.0, id6865=1.0/1.0/1.0, id6866=1.0/1.0/1.0, id6867=1.0/1.0/1.0, id6868=1.0/1.0/1.0, id6869=1.0/1.0/1.0, id687=1.0/1.0/1.0, id6870=1.0/1.0/1.0, id6871=1.0/1.0/1.0, id6872=1.0/1.0/1.0, id6873=1.0/1.0/1.0, id6874=1.0/1.0/1.0, id6875=1.0/1.0/1.0, id6876=1.0/1.0/1.0, id6877=1.0/1.0/1.0, id6878=1.0/1.0/1.0, id6879=1.0/1.0/1.0, id688=1.0/1.0/1.0, id6880=1.0/1.0/1.0, id6881=1.0/1.0/1.0, id6882=1.0/1.0/1.0, id6883=1.0/1.0/1.0, id6884=1.0/1.0/1.0, id6885=1.0/1.0/1.0, id6886=1.0/1.0/1.0, id6887=1.0/1.0/1.0, id6888=1.0/1.0/1.0, id6889=1.0/1.0/1.0, id689=1.0/1.0/1.0, id6890=1.0/1.0/1.0, id6891=1.0/1.0/1.0, id6892=1.0/1.0/1.0, id6893=1.0/1.0/1.0, id6894=1.0/1.0/1.0, id6895=1.0/1.0/1.0, id6896=1.0/1.0/1.0, id6897=1.0/1.0/1.0, id6898=1.0/1.0/1.0, id6899=1.0/1.0/1.0, id69=1.0/1.0/1.0, id690=1.0/1.0/1.0, id6900=1.0/1.0/1.0, id6901=1.0/1.0/1.0, id6902=1.0/1.0/1.0, id6903=1.0/1.0/1.0, id6904=1.0/1.0/1.0, id6905=1.0/1.0/1.0, id6906=1.0/1.0/1.0, id6907=1.0/1.0/1.0, id6908=1.0/1.0/1.0, id6909=1.0/1.0/1.0, id691=1.0/1.0/1.0, id6910=1.0/1.0/1.0, id6911=1.0/1.0/1.0, id6912=1.0/1.0/1.0, id6913=1.0/1.0/1.0, id6914=1.0/1.0/1.0, id6915=1.0/1.0/1.0, id6916=1.0/1.0/1.0, id6917=1.0/1.0/1.0, id6918=1.0/1.0/1.0, id6919=1.0/1.0/1.0, id692=1.0/1.0/1.0, id6920=1.0/1.0/1.0, id6921=1.0/1.0/1.0, id6922=1.0/1.0/1.0, id6923=1.0/1.0/1.0, id6924=1.0/1.0/1.0, id6925=1.0/1.0/1.0, id6926=1.0/1.0/1.0, id6927=1.0/1.0/1.0, id6928=1.0/1.0/1.0, id6929=1.0/1.0/1.0, id693=1.0/1.0/1.0, id6930=1.0/1.0/1.0, id6931=1.0/1.0/1.0, id6932=1.0/1.0/1.0, id6933=1.0/1.0/1.0, id6934=1.0/1.0/1.0, id6935=1.0/1.0/1.0, id6936=1.0/1.0/1.0, id6937=1.0/1.0/1.0, id6938=1.0/1.0/1.0, id6939=1.0/1.0/1.0, id694=1.0/1.0/1.0, id6940=1.0/1.0/1.0, id6941=1.0/1.0/1.0, id6942=1.0/1.0/1.0, id6943=1.0/1.0/1.0, id6944=1.0/1.0/1.0, id6945=1.0/1.0/1.0, id6946=1.0/1.0/1.0, id6947=1.0/1.0/1.0, id6948=1.0/1.0/1.0, id6949=1.0/1.0/1.0, id695=1.0/1.0/1.0, id6950=1.0/1.0/1.0, id6951=1.0/1.0/1.0, id6952=1.0/1.0/1.0, id6953=1.0/1.0/1.0, id6954=1.0/1.0/1.0, id6955=1.0/1.0/1.0, id6956=1.0/1.0/1.0, id6957=1.0/1.0/1.0, id6958=1.0/1.0/1.0, id6959=1.0/1.0/1.0, id696=1.0/1.0/1.0, id6960=1.0/1.0/1.0, id6961=1.0/1.0/1.0, id6962=1.0/1.0/1.0, id6963=1.0/1.0/1.0, id6964=1.0/1.0/1.0, id6965=1.0/1.0/1.0, id6966=1.0/1.0/1.0, id6967=1.0/1.0/1.0, id6968=1.0/1.0/1.0, id6969=1.0/1.0/1.0, id697=1.0/1.0/1.0, id6970=1.0/1.0/1.0, id6971=1.0/1.0/1.0, id6972=1.0/1.0/1.0, id6973=1.0/1.0/1.0, id6974=1.0/1.0/1.0, id6975=1.0/1.0/1.0, id6976=1.0/1.0/1.0, id6977=1.0/1.0/1.0, id6978=1.0/1.0/1.0, id6979=1.0/1.0/1.0, id698=1.0/1.0/1.0, id6980=1.0/1.0/1.0, id6981=1.0/1.0/1.0, id6982=1.0/1.0/1.0, id6983=1.0/1.0/1.0, id6984=1.0/1.0/1.0, id6985=1.0/1.0/1.0, id6986=1.0/1.0/1.0, id6987=1.0/1.0/1.0, id6988=1.0/1.0/1.0, id6989=1.0/1.0/1.0, id699=1.0/1.0/1.0, id6990=1.0/1.0/1.0, id6991=1.0/1.0/1.0, id6992=1.0/1.0/1.0, id6993=1.0/1.0/1.0, id6994=1.0/1.0/1.0, id6995=1.0/1.0/1.0, id6996=1.0/1.0/1.0, id6997=1.0/1.0/1.0, id6998=1.0/1.0/1.0, id6999=1.0/1.0/1.0, id7=1.0/1.0/1.0, id70=1.0/1.0/1.0, id700=1.0/1.0/1.0, id7000=1.0/1.0/1.0, id7001=1.0/1.0/1.0, id7002=1.0/1.0/1.0, id7003=1.0/1.0/1.0, id7004=1.0/1.0/1.0, id7005=1.0/1.0/1.0, id7006=1.0/1.0/1.0, id7007=1.0/1.0/1.0, id7008=1.0/1.0/1.0, id7009=1.0/1.0/1.0, id701=1.0/1.0/1.0, id7010=1.0/1.0/1.0, id7011=1.0/1.0/1.0, id7012=1.0/1.0/1.0, id7013=1.0/1.0/1.0, id7014=1.0/1.0/1.0, id7015=1.0/1.0/1.0, id7016=1.0/1.0/1.0, id7017=1.0/1.0/1.0, id7018=1.0/1.0/1.0, id7019=1.0/1.0/1.0, id702=1.0/1.0/1.0, id7020=1.0/1.0/1.0, id7021=1.0/1.0/1.0, id7022=1.0/1.0/1.0, id7023=1.0/1.0/1.0, id7024=1.0/1.0/1.0, id7025=1.0/1.0/1.0, id7026=1.0/1.0/1.0, id7027=1.0/1.0/1.0, id7028=1.0/1.0/1.0, id7029=1.0/1.0/1.0, id703=1.0/1.0/1.0, id7030=1.0/1.0/1.0, id7031=1.0/1.0/1.0, id7032=1.0/1.0/1.0, id7033=1.0/1.0/1.0, id7034=1.0/1.0/1.0, id7035=1.0/1.0/1.0, id7036=1.0/1.0/1.0, id7037=1.0/1.0/1.0, id7038=1.0/1.0/1.0, id7039=1.0/1.0/1.0, id704=1.0/1.0/1.0, id7040=1.0/1.0/1.0, id7041=1.0/1.0/1.0, id7042=1.0/1.0/1.0, id7043=1.0/1.0/1.0, id7044=1.0/1.0/1.0, id7045=1.0/1.0/1.0, id7046=1.0/1.0/1.0, id7047=1.0/1.0/1.0, id7048=1.0/1.0/1.0, id7049=1.0/1.0/1.0, id705=1.0/1.0/1.0, id7050=1.0/1.0/1.0, id7051=1.0/1.0/1.0, id7052=1.0/1.0/1.0, id7053=1.0/1.0/1.0, id7054=1.0/1.0/1.0, id7055=1.0/1.0/1.0, id7056=1.0/1.0/1.0, id7057=1.0/1.0/1.0, id7058=1.0/1.0/1.0, id7059=1.0/1.0/1.0, id706=1.0/1.0/1.0, id7060=1.0/1.0/1.0, id7061=1.0/1.0/1.0, id7062=1.0/1.0/1.0, id7063=1.0/1.0/1.0, id7064=1.0/1.0/1.0, id7065=1.0/1.0/1.0, id7066=1.0/1.0/1.0, id7067=1.0/1.0/1.0, id7068=1.0/1.0/1.0, id7069=1.0/1.0/1.0, id707=1.0/1.0/1.0, id7070=1.0/1.0/1.0, id7071=1.0/1.0/1.0, id7072=1.0/1.0/1.0, id7073=1.0/1.0/1.0, id7074=1.0/1.0/1.0, id7075=1.0/1.0/1.0, id7076=1.0/1.0/1.0, id7077=1.0/1.0/1.0, id7078=1.0/1.0/1.0, id7079=1.0/1.0/1.0, id708=1.0/1.0/1.0, id7080=1.0/1.0/1.0, id7081=1.0/1.0/1.0, id7082=1.0/1.0/1.0, id7083=1.0/1.0/1.0, id7084=1.0/1.0/1.0, id7085=1.0/1.0/1.0, id7086=1.0/1.0/1.0, id7087=1.0/1.0/1.0, id7088=1.0/1.0/1.0, id7089=1.0/1.0/1.0, id709=1.0/1.0/1.0, id7090=1.0/1.0/1.0, id7091=1.0/1.0/1.0, id7092=1.0/1.0/1.0, id7093=1.0/1.0/1.0, id7094=1.0/1.0/1.0, id7095=1.0/1.0/1.0, id7096=1.0/1.0/1.0, id7097=1.0/1.0/1.0, id7098=1.0/1.0/1.0, id7099=1.0/1.0/1.0, id71=1.0/1.0/1.0, id710=1.0/1.0/1.0, id7100=1.0/1.0/1.0, id7101=1.0/1.0/1.0, id7102=1.0/1.0/1.0, id7103=1.0/1.0/1.0, id7104=1.0/1.0/1.0, id7105=1.0/1.0/1.0, id7106=1.0/1.0/1.0, id7107=1.0/1.0/1.0, id7108=1.0/1.0/1.0, id7109=1.0/1.0/1.0, id711=1.0/1.0/1.0, id7110=1.0/1.0/1.0, id7111=1.0/1.0/1.0, id7112=1.0/1.0/1.0, id7113=1.0/1.0/1.0, id7114=1.0/1.0/1.0, id7115=1.0/1.0/1.0, id7116=1.0/1.0/1.0, id7117=1.0/1.0/1.0, id7118=1.0/1.0/1.0, id7119=1.0/1.0/1.0, id712=1.0/1.0/1.0, id7120=1.0/1.0/1.0, id7121=1.0/1.0/1.0, id7122=1.0/1.0/1.0, id7123=1.0/1.0/1.0, id7124=1.0/1.0/1.0, id7125=1.0/1.0/1.0, id7126=1.0/1.0/1.0, id7127=1.0/1.0/1.0, id7128=1.0/1.0/1.0, id7129=1.0/1.0/1.0, id713=1.0/1.0/1.0, id7130=1.0/1.0/1.0, id7131=1.0/1.0/1.0, id7132=1.0/1.0/1.0, id7133=1.0/1.0/1.0, id7134=1.0/1.0/1.0, id7135=1.0/1.0/1.0, id7136=1.0/1.0/1.0, id7137=1.0/1.0/1.0, id7138=1.0/1.0/1.0, id7139=1.0/1.0/1.0, id714=1.0/1.0/1.0, id7140=1.0/1.0/1.0, id7141=1.0/1.0/1.0, id7142=1.0/1.0/1.0, id7143=1.0/1.0/1.0, id7144=1.0/1.0/1.0, id7145=1.0/1.0/1.0, id7146=1.0/1.0/1.0, id7147=1.0/1.0/1.0, id7148=1.0/1.0/1.0, id7149=1.0/1.0/1.0, id715=1.0/1.0/1.0, id7150=1.0/1.0/1.0, id7151=1.0/1.0/1.0, id7152=1.0/1.0/1.0, id7153=1.0/1.0/1.0, id7154=1.0/1.0/1.0, id7155=1.0/1.0/1.0, id7156=1.0/1.0/1.0, id7157=1.0/1.0/1.0, id7158=1.0/1.0/1.0, id7159=1.0/1.0/1.0, id716=1.0/1.0/1.0, id7160=1.0/1.0/1.0, id7161=1.0/1.0/1.0, id7162=1.0/1.0/1.0, id7163=1.0/1.0/1.0, id7164=1.0/1.0/1.0, id7165=1.0/1.0/1.0, id7166=1.0/1.0/1.0, id7167=1.0/1.0/1.0, id7168=1.0/1.0/1.0, id7169=1.0/1.0/1.0, id717=1.0/1.0/1.0, id7170=1.0/1.0/1.0, id7171=1.0/1.0/1.0, id7172=1.0/1.0/1.0, id7173=1.0/1.0/1.0, id7174=1.0/1.0/1.0, id7175=1.0/1.0/1.0, id7176=1.0/1.0/1.0, id7177=1.0/1.0/1.0, id7178=1.0/1.0/1.0, id7179=1.0/1.0/1.0, id718=1.0/1.0/1.0, id7180=1.0/1.0/1.0, id7181=1.0/1.0/1.0, id7182=1.0/1.0/1.0, id7183=1.0/1.0/1.0, id7184=1.0/1.0/1.0, id7185=1.0/1.0/1.0, id7186=1.0/1.0/1.0, id7187=1.0/1.0/1.0, id7188=1.0/1.0/1.0, id7189=1.0/1.0/1.0, id719=1.0/1.0/1.0, id7190=1.0/1.0/1.0, id7191=1.0/1.0/1.0, id7192=1.0/1.0/1.0, id7193=1.0/1.0/1.0, id7194=1.0/1.0/1.0, id7195=1.0/1.0/1.0, id7196=1.0/1.0/1.0, id7197=1.0/1.0/1.0, id7198=1.0/1.0/1.0, id7199=1.0/1.0/1.0, id72=1.0/1.0/1.0, id720=1.0/1.0/1.0, id7200=1.0/1.0/1.0, id7201=1.0/1.0/1.0, id7202=1.0/1.0/1.0, id7203=1.0/1.0/1.0, id7204=1.0/1.0/1.0, id7205=1.0/1.0/1.0, id7206=1.0/1.0/1.0, id7207=1.0/1.0/1.0, id7208=1.0/1.0/1.0, id7209=1.0/1.0/1.0, id721=1.0/1.0/1.0, id7210=1.0/1.0/1.0, id7211=1.0/1.0/1.0, id7212=1.0/1.0/1.0, id7213=1.0/1.0/1.0, id7214=1.0/1.0/1.0, id7215=1.0/1.0/1.0, id7216=1.0/1.0/1.0, id7217=1.0/1.0/1.0, id7218=1.0/1.0/1.0, id7219=1.0/1.0/1.0, id722=1.0/1.0/1.0, id7220=1.0/1.0/1.0, id7221=1.0/1.0/1.0, id7222=1.0/1.0/1.0, id7223=1.0/1.0/1.0, id7224=1.0/1.0/1.0, id7225=1.0/1.0/1.0, id7226=1.0/1.0/1.0, id7227=1.0/1.0/1.0, id7228=1.0/1.0/1.0, id7229=1.0/1.0/1.0, id723=1.0/1.0/1.0, id7230=1.0/1.0/1.0, id7231=1.0/1.0/1.0, id7232=1.0/1.0/1.0, id7233=1.0/1.0/1.0, id7234=1.0/1.0/1.0, id7235=1.0/1.0/1.0, id7236=1.0/1.0/1.0, id7237=1.0/1.0/1.0, id7238=1.0/1.0/1.0, id7239=1.0/1.0/1.0, id724=1.0/1.0/1.0, id7240=1.0/1.0/1.0, id7241=1.0/1.0/1.0, id7242=1.0/1.0/1.0, id7243=1.0/1.0/1.0, id7244=1.0/1.0/1.0, id7245=1.0/1.0/1.0, id7246=1.0/1.0/1.0, id7247=1.0/1.0/1.0, id7248=1.0/1.0/1.0, id7249=1.0/1.0/1.0, id725=1.0/1.0/1.0, id7250=1.0/1.0/1.0, id7251=1.0/1.0/1.0, id7252=1.0/1.0/1.0, id7253=1.0/1.0/1.0, id7254=1.0/1.0/1.0, id7255=1.0/1.0/1.0, id7256=1.0/1.0/1.0, id7257=1.0/1.0/1.0, id7258=1.0/1.0/1.0, id7259=1.0/1.0/1.0, id726=1.0/1.0/1.0, id7260=1.0/1.0/1.0, id7261=1.0/1.0/1.0, id7262=1.0/1.0/1.0, id7263=1.0/1.0/1.0, id7264=1.0/1.0/1.0, id7265=1.0/1.0/1.0, id7266=1.0/1.0/1.0, id7267=1.0/1.0/1.0, id7268=1.0/1.0/1.0, id7269=1.0/1.0/1.0, id727=1.0/1.0/1.0, id7270=1.0/1.0/1.0, id7271=1.0/1.0/1.0, id7272=1.0/1.0/1.0, id7273=1.0/1.0/1.0, id7274=1.0/1.0/1.0, id7275=1.0/1.0/1.0, id7276=1.0/1.0/1.0, id7277=1.0/1.0/1.0, id7278=1.0/1.0/1.0, id7279=1.0/1.0/1.0, id728=1.0/1.0/1.0, id7280=1.0/1.0/1.0, id7281=1.0/1.0/1.0, id7282=1.0/1.0/1.0, id7283=1.0/1.0/1.0, id7284=1.0/1.0/1.0, id7285=1.0/1.0/1.0, id7286=1.0/1.0/1.0, id7287=1.0/1.0/1.0, id7288=1.0/1.0/1.0, id7289=1.0/1.0/1.0, id729=1.0/1.0/1.0, id7290=1.0/1.0/1.0, id7291=1.0/1.0/1.0, id7292=1.0/1.0/1.0, id7293=1.0/1.0/1.0, id7294=1.0/1.0/1.0, id7295=1.0/1.0/1.0, id7296=1.0/1.0/1.0, id7297=1.0/1.0/1.0, id7298=1.0/1.0/1.0, id7299=1.0/1.0/1.0, id73=1.0/1.0/1.0, id730=1.0/1.0/1.0, id7300=1.0/1.0/1.0, id7301=1.0/1.0/1.0, id7302=1.0/1.0/1.0, id7303=1.0/1.0/1.0, id7304=1.0/1.0/1.0, id7305=1.0/1.0/1.0, id7306=1.0/1.0/1.0, id7307=1.0/1.0/1.0, id7308=1.0/1.0/1.0, id7309=1.0/1.0/1.0, id731=1.0/1.0/1.0, id7310=1.0/1.0/1.0, id7311=1.0/1.0/1.0, id7312=1.0/1.0/1.0, id7313=1.0/1.0/1.0, id7314=1.0/1.0/1.0, id7315=1.0/1.0/1.0, id7316=1.0/1.0/1.0, id7317=1.0/1.0/1.0, id7318=1.0/1.0/1.0, id7319=1.0/1.0/1.0, id732=1.0/1.0/1.0, id7320=1.0/1.0/1.0, id7321=1.0/1.0/1.0, id7322=1.0/1.0/1.0, id7323=1.0/1.0/1.0, id7324=1.0/1.0/1.0, id7325=1.0/1.0/1.0, id7326=1.0/1.0/1.0, id7327=1.0/1.0/1.0, id7328=1.0/1.0/1.0, id7329=1.0/1.0/1.0, id733=1.0/1.0/1.0, id7330=1.0/1.0/1.0, id7331=1.0/1.0/1.0, id7332=1.0/1.0/1.0, id7333=1.0/1.0/1.0, id7334=1.0/1.0/1.0, id7335=1.0/1.0/1.0, id7336=1.0/1.0/1.0, id7337=1.0/1.0/1.0, id7338=1.0/1.0/1.0, id7339=1.0/1.0/1.0, id734=1.0/1.0/1.0, id7340=1.0/1.0/1.0, id7341=1.0/1.0/1.0, id7342=1.0/1.0/1.0, id7343=1.0/1.0/1.0, id7344=1.0/1.0/1.0, id7345=1.0/1.0/1.0, id7346=1.0/1.0/1.0, id7347=1.0/1.0/1.0, id7348=1.0/1.0/1.0, id7349=1.0/1.0/1.0, id735=1.0/1.0/1.0, id7350=1.0/1.0/1.0, id7351=1.0/1.0/1.0, id7352=1.0/1.0/1.0, id7353=1.0/1.0/1.0, id7354=1.0/1.0/1.0, id7355=1.0/1.0/1.0, id7356=1.0/1.0/1.0, id7357=1.0/1.0/1.0, id7358=1.0/1.0/1.0, id7359=1.0/1.0/1.0, id736=1.0/1.0/1.0, id7360=1.0/1.0/1.0, id7361=1.0/1.0/1.0, id7362=1.0/1.0/1.0, id7363=1.0/1.0/1.0, id7364=1.0/1.0/1.0, id7365=1.0/1.0/1.0, id7366=1.0/1.0/1.0, id7367=1.0/1.0/1.0, id7368=1.0/1.0/1.0, id7369=1.0/1.0/1.0, id737=1.0/1.0/1.0, id7370=1.0/1.0/1.0, id7371=1.0/1.0/1.0, id7372=1.0/1.0/1.0, id7373=1.0/1.0/1.0, id7374=1.0/1.0/1.0, id7375=1.0/1.0/1.0, id7376=1.0/1.0/1.0, id7377=1.0/1.0/1.0, id7378=1.0/1.0/1.0, id7379=1.0/1.0/1.0, id738=1.0/1.0/1.0, id7380=1.0/1.0/1.0, id7381=1.0/1.0/1.0, id7382=1.0/1.0/1.0, id7383=1.0/1.0/1.0, id7384=1.0/1.0/1.0, id7385=1.0/1.0/1.0, id7386=1.0/1.0/1.0, id7387=1.0/1.0/1.0, id7388=1.0/1.0/1.0, id7389=1.0/1.0/1.0, id739=1.0/1.0/1.0, id7390=1.0/1.0/1.0, id7391=1.0/1.0/1.0, id7392=1.0/1.0/1.0, id7393=1.0/1.0/1.0, id7394=1.0/1.0/1.0, id7395=1.0/1.0/1.0, id7396=1.0/1.0/1.0, id7397=1.0/1.0/1.0, id7398=1.0/1.0/1.0, id7399=1.0/1.0/1.0, id74=1.0/1.0/1.0, id740=1.0/1.0/1.0, id7400=1.0/1.0/1.0, id7401=1.0/1.0/1.0, id7402=1.0/1.0/1.0, id7403=1.0/1.0/1.0, id7404=1.0/1.0/1.0, id7405=1.0/1.0/1.0, id7406=1.0/1.0/1.0, id7407=1.0/1.0/1.0, id7408=1.0/1.0/1.0, id7409=1.0/1.0/1.0, id741=1.0/1.0/1.0, id7410=1.0/1.0/1.0, id7411=1.0/1.0/1.0, id7412=1.0/1.0/1.0, id7413=1.0/1.0/1.0, id7414=1.0/1.0/1.0, id7415=1.0/1.0/1.0, id7416=1.0/1.0/1.0, id7417=1.0/1.0/1.0, id7418=1.0/1.0/1.0, id7419=1.0/1.0/1.0, id742=1.0/1.0/1.0, id7420=1.0/1.0/1.0, id7421=1.0/1.0/1.0, id7422=1.0/1.0/1.0, id7423=1.0/1.0/1.0, id7424=1.0/1.0/1.0, id7425=1.0/1.0/1.0, id7426=1.0/1.0/1.0, id7427=1.0/1.0/1.0, id7428=1.0/1.0/1.0, id7429=1.0/1.0/1.0, id743=1.0/1.0/1.0, id7430=1.0/1.0/1.0, id7431=1.0/1.0/1.0, id7432=1.0/1.0/1.0, id7433=1.0/1.0/1.0, id7434=1.0/1.0/1.0, id7435=1.0/1.0/1.0, id7436=1.0/1.0/1.0, id7437=1.0/1.0/1.0, id7438=1.0/1.0/1.0, id7439=1.0/1.0/1.0, id744=1.0/1.0/1.0, id7440=1.0/1.0/1.0, id7441=1.0/1.0/1.0, id7442=1.0/1.0/1.0, id7443=1.0/1.0/1.0, id7444=1.0/1.0/1.0, id7445=1.0/1.0/1.0, id7446=1.0/1.0/1.0, id7447=1.0/1.0/1.0, id7448=1.0/1.0/1.0, id7449=1.0/1.0/1.0, id745=1.0/1.0/1.0, id7450=1.0/1.0/1.0, id7451=1.0/1.0/1.0, id7452=1.0/1.0/1.0, id7453=1.0/1.0/1.0, id7454=1.0/1.0/1.0, id7455=1.0/1.0/1.0, id7456=1.0/1.0/1.0, id7457=1.0/1.0/1.0, id7458=1.0/1.0/1.0, id7459=1.0/1.0/1.0, id746=1.0/1.0/1.0, id7460=1.0/1.0/1.0, id7461=1.0/1.0/1.0, id7462=1.0/1.0/1.0, id7463=1.0/1.0/1.0, id7464=1.0/1.0/1.0, id7465=1.0/1.0/1.0, id7466=1.0/1.0/1.0, id7467=1.0/1.0/1.0, id7468=1.0/1.0/1.0, id7469=1.0/1.0/1.0, id747=1.0/1.0/1.0, id7470=1.0/1.0/1.0, id7471=1.0/1.0/1.0, id7472=1.0/1.0/1.0, id7473=1.0/1.0/1.0, id7474=1.0/1.0/1.0, id7475=1.0/1.0/1.0, id7476=1.0/1.0/1.0, id7477=1.0/1.0/1.0, id7478=1.0/1.0/1.0, id7479=1.0/1.0/1.0, id748=1.0/1.0/1.0, id7480=1.0/1.0/1.0, id7481=1.0/1.0/1.0, id7482=1.0/1.0/1.0, id7483=1.0/1.0/1.0, id7484=1.0/1.0/1.0, id7485=1.0/1.0/1.0, id7486=1.0/1.0/1.0, id7487=1.0/1.0/1.0, id7488=1.0/1.0/1.0, id7489=1.0/1.0/1.0, id749=1.0/1.0/1.0, id7490=1.0/1.0/1.0, id7491=1.0/1.0/1.0, id7492=1.0/1.0/1.0, id7493=1.0/1.0/1.0, id7494=1.0/1.0/1.0, id7495=1.0/1.0/1.0, id7496=1.0/1.0/1.0, id7497=1.0/1.0/1.0, id7498=1.0/1.0/1.0, id7499=1.0/1.0/1.0, id75=1.0/1.0/1.0, id750=1.0/1.0/1.0, id7500=1.0/1.0/1.0, id7501=1.0/1.0/1.0, id7502=1.0/1.0/1.0, id7503=1.0/1.0/1.0, id7504=1.0/1.0/1.0, id7505=1.0/1.0/1.0, id7506=1.0/1.0/1.0, id7507=1.0/1.0/1.0, id7508=1.0/1.0/1.0, id7509=1.0/1.0/1.0, id751=1.0/1.0/1.0, id7510=1.0/1.0/1.0, id7511=1.0/1.0/1.0, id7512=1.0/1.0/1.0, id7513=1.0/1.0/1.0, id7514=1.0/1.0/1.0, id7515=1.0/1.0/1.0, id7516=1.0/1.0/1.0, id7517=1.0/1.0/1.0, id7518=1.0/1.0/1.0, id7519=1.0/1.0/1.0, id752=1.0/1.0/1.0, id7520=1.0/1.0/1.0, id7521=1.0/1.0/1.0, id7522=1.0/1.0/1.0, id7523=1.0/1.0/1.0, id7524=1.0/1.0/1.0, id7525=1.0/1.0/1.0, id7526=1.0/1.0/1.0, id7527=1.0/1.0/1.0, id7528=1.0/1.0/1.0, id7529=1.0/1.0/1.0, id753=1.0/1.0/1.0, id7530=1.0/1.0/1.0, id7531=1.0/1.0/1.0, id7532=1.0/1.0/1.0, id7533=1.0/1.0/1.0, id7534=1.0/1.0/1.0, id7535=1.0/1.0/1.0, id7536=1.0/1.0/1.0, id7537=1.0/1.0/1.0, id7538=1.0/1.0/1.0, id7539=1.0/1.0/1.0, id754=1.0/1.0/1.0, id7540=1.0/1.0/1.0, id7541=1.0/1.0/1.0, id7542=1.0/1.0/1.0, id7543=1.0/1.0/1.0, id7544=1.0/1.0/1.0, id7545=1.0/1.0/1.0, id7546=1.0/1.0/1.0, id7547=1.0/1.0/1.0, id7548=1.0/1.0/1.0, id7549=1.0/1.0/1.0, id755=1.0/1.0/1.0, id7550=1.0/1.0/1.0, id7551=1.0/1.0/1.0, id7552=1.0/1.0/1.0, id7553=1.0/1.0/1.0, id7554=1.0/1.0/1.0, id7555=1.0/1.0/1.0, id7556=1.0/1.0/1.0, id7557=1.0/1.0/1.0, id7558=1.0/1.0/1.0, id7559=1.0/1.0/1.0, id756=1.0/1.0/1.0, id7560=1.0/1.0/1.0, id7561=1.0/1.0/1.0, id7562=1.0/1.0/1.0, id7563=1.0/1.0/1.0, id7564=1.0/1.0/1.0, id7565=1.0/1.0/1.0, id7566=1.0/1.0/1.0, id7567=1.0/1.0/1.0, id7568=1.0/1.0/1.0, id7569=1.0/1.0/1.0, id757=1.0/1.0/1.0, id7570=1.0/1.0/1.0, id7571=1.0/1.0/1.0, id7572=1.0/1.0/1.0, id7573=1.0/1.0/1.0, id7574=1.0/1.0/1.0, id7575=1.0/1.0/1.0, id7576=1.0/1.0/1.0, id7577=1.0/1.0/1.0, id7578=1.0/1.0/1.0, id7579=1.0/1.0/1.0, id758=1.0/1.0/1.0, id7580=1.0/1.0/1.0, id7581=1.0/1.0/1.0, id7582=1.0/1.0/1.0, id7583=1.0/1.0/1.0, id7584=1.0/1.0/1.0, id7585=1.0/1.0/1.0, id7586=1.0/1.0/1.0, id7587=1.0/1.0/1.0, id7588=1.0/1.0/1.0, id7589=1.0/1.0/1.0, id759=1.0/1.0/1.0, id7590=1.0/1.0/1.0, id7591=1.0/1.0/1.0, id7592=1.0/1.0/1.0, id7593=1.0/1.0/1.0, id7594=1.0/1.0/1.0, id7595=1.0/1.0/1.0, id7596=1.0/1.0/1.0, id7597=1.0/1.0/1.0, id7598=1.0/1.0/1.0, id7599=1.0/1.0/1.0, id76=1.0/1.0/1.0, id760=1.0/1.0/1.0, id7600=1.0/1.0/1.0, id7601=1.0/1.0/1.0, id7602=1.0/1.0/1.0, id7603=1.0/1.0/1.0, id7604=1.0/1.0/1.0, id7605=1.0/1.0/1.0, id7606=1.0/1.0/1.0, id7607=1.0/1.0/1.0, id7608=1.0/1.0/1.0, id7609=1.0/1.0/1.0, id761=1.0/1.0/1.0, id7610=1.0/1.0/1.0, id7611=1.0/1.0/1.0, id7612=1.0/1.0/1.0, id7613=1.0/1.0/1.0, id7614=1.0/1.0/1.0, id7615=1.0/1.0/1.0, id7616=1.0/1.0/1.0, id7617=1.0/1.0/1.0, id7618=1.0/1.0/1.0, id7619=1.0/1.0/1.0, id762=1.0/1.0/1.0, id7620=1.0/1.0/1.0, id7621=1.0/1.0/1.0, id7622=1.0/1.0/1.0, id7623=1.0/1.0/1.0, id7624=1.0/1.0/1.0, id7625=1.0/1.0/1.0, id7626=1.0/1.0/1.0, id7627=1.0/1.0/1.0, id7628=1.0/1.0/1.0, id7629=1.0/1.0/1.0, id763=1.0/1.0/1.0, id7630=1.0/1.0/1.0, id7631=1.0/1.0/1.0, id7632=1.0/1.0/1.0, id7633=1.0/1.0/1.0, id7634=1.0/1.0/1.0, id7635=1.0/1.0/1.0, id7636=1.0/1.0/1.0, id7637=1.0/1.0/1.0, id7638=1.0/1.0/1.0, id7639=1.0/1.0/1.0, id764=1.0/1.0/1.0, id7640=1.0/1.0/1.0, id7641=1.0/1.0/1.0, id7642=1.0/1.0/1.0, id7643=1.0/1.0/1.0, id7644=1.0/1.0/1.0, id7645=1.0/1.0/1.0, id7646=1.0/1.0/1.0, id7647=1.0/1.0/1.0, id7648=1.0/1.0/1.0, id7649=1.0/1.0/1.0, id765=1.0/1.0/1.0, id7650=1.0/1.0/1.0, id7651=1.0/1.0/1.0, id7652=1.0/1.0/1.0, id7653=1.0/1.0/1.0, id7654=1.0/1.0/1.0, id7655=1.0/1.0/1.0, id7656=1.0/1.0/1.0, id7657=1.0/1.0/1.0, id7658=1.0/1.0/1.0, id7659=1.0/1.0/1.0, id766=1.0/1.0/1.0, id7660=1.0/1.0/1.0, id7661=1.0/1.0/1.0, id7662=1.0/1.0/1.0, id7663=1.0/1.0/1.0, id7664=1.0/1.0/1.0, id7665=1.0/1.0/1.0, id7666=1.0/1.0/1.0, id7667=1.0/1.0/1.0, id7668=1.0/1.0/1.0, id7669=1.0/1.0/1.0, id767=1.0/1.0/1.0, id7670=1.0/1.0/1.0, id7671=1.0/1.0/1.0, id7672=1.0/1.0/1.0, id7673=1.0/1.0/1.0, id7674=1.0/1.0/1.0, id7675=1.0/1.0/1.0, id7676=1.0/1.0/1.0, id7677=1.0/1.0/1.0, id7678=1.0/1.0/1.0, id7679=1.0/1.0/1.0, id768=1.0/1.0/1.0, id7680=1.0/1.0/1.0, id7681=1.0/1.0/1.0, id7682=1.0/1.0/1.0, id7683=1.0/1.0/1.0, id7684=1.0/1.0/1.0, id7685=1.0/1.0/1.0, id7686=1.0/1.0/1.0, id7687=1.0/1.0/1.0, id7688=1.0/1.0/1.0, id7689=1.0/1.0/1.0, id769=1.0/1.0/1.0, id7690=1.0/1.0/1.0, id7691=1.0/1.0/1.0, id7692=1.0/1.0/1.0, id7693=1.0/1.0/1.0, id7694=1.0/1.0/1.0, id7695=1.0/1.0/1.0, id7696=1.0/1.0/1.0, id7697=1.0/1.0/1.0, id7698=1.0/1.0/1.0, id7699=1.0/1.0/1.0, id77=1.0/1.0/1.0, id770=1.0/1.0/1.0, id7700=1.0/1.0/1.0, id7701=1.0/1.0/1.0, id7702=1.0/1.0/1.0, id7703=1.0/1.0/1.0, id7704=1.0/1.0/1.0, id7705=1.0/1.0/1.0, id7706=1.0/1.0/1.0, id7707=1.0/1.0/1.0, id7708=1.0/1.0/1.0, id7709=1.0/1.0/1.0, id771=1.0/1.0/1.0, id7710=1.0/1.0/1.0, id7711=1.0/1.0/1.0, id7712=1.0/1.0/1.0, id7713=1.0/1.0/1.0, id7714=1.0/1.0/1.0, id7715=1.0/1.0/1.0, id7716=1.0/1.0/1.0, id7717=1.0/1.0/1.0, id7718=1.0/1.0/1.0, id7719=1.0/1.0/1.0, id772=1.0/1.0/1.0, id7720=1.0/1.0/1.0, id7721=1.0/1.0/1.0, id7722=1.0/1.0/1.0, id7723=1.0/1.0/1.0, id7724=1.0/1.0/1.0, id7725=1.0/1.0/1.0, id7726=1.0/1.0/1.0, id7727=1.0/1.0/1.0, id7728=1.0/1.0/1.0, id7729=1.0/1.0/1.0, id773=1.0/1.0/1.0, id7730=1.0/1.0/1.0, id7731=1.0/1.0/1.0, id7732=1.0/1.0/1.0, id7733=1.0/1.0/1.0, id7734=1.0/1.0/1.0, id7735=1.0/1.0/1.0, id7736=1.0/1.0/1.0, id7737=1.0/1.0/1.0, id7738=1.0/1.0/1.0, id7739=1.0/1.0/1.0, id774=1.0/1.0/1.0, id7740=1.0/1.0/1.0, id7741=1.0/1.0/1.0, id7742=1.0/1.0/1.0, id7743=1.0/1.0/1.0, id7744=1.0/1.0/1.0, id7745=1.0/1.0/1.0, id7746=1.0/1.0/1.0, id7747=1.0/1.0/1.0, id7748=1.0/1.0/1.0, id7749=1.0/1.0/1.0, id775=1.0/1.0/1.0, id7750=1.0/1.0/1.0, id7751=1.0/1.0/1.0, id7752=1.0/1.0/1.0, id7753=1.0/1.0/1.0, id7754=1.0/1.0/1.0, id7755=1.0/1.0/1.0, id7756=1.0/1.0/1.0, id7757=1.0/1.0/1.0, id7758=1.0/1.0/1.0, id7759=1.0/1.0/1.0, id776=1.0/1.0/1.0, id7760=1.0/1.0/1.0, id7761=1.0/1.0/1.0, id7762=1.0/1.0/1.0, id7763=1.0/1.0/1.0, id7764=1.0/1.0/1.0, id7765=1.0/1.0/1.0, id7766=1.0/1.0/1.0, id7767=1.0/1.0/1.0, id7768=1.0/1.0/1.0, id7769=1.0/1.0/1.0, id777=1.0/1.0/1.0, id7770=1.0/1.0/1.0, id7771=1.0/1.0/1.0, id7772=1.0/1.0/1.0, id7773=1.0/1.0/1.0, id7774=1.0/1.0/1.0, id7775=1.0/1.0/1.0, id7776=1.0/1.0/1.0, id7777=1.0/1.0/1.0, id7778=1.0/1.0/1.0, id7779=1.0/1.0/1.0, id778=1.0/1.0/1.0, id7780=1.0/1.0/1.0, id7781=1.0/1.0/1.0, id7782=1.0/1.0/1.0, id7783=1.0/1.0/1.0, id7784=1.0/1.0/1.0, id7785=1.0/1.0/1.0, id7786=1.0/1.0/1.0, id7787=1.0/1.0/1.0, id7788=1.0/1.0/1.0, id7789=1.0/1.0/1.0, id779=1.0/1.0/1.0, id7790=1.0/1.0/1.0, id7791=1.0/1.0/1.0, id7792=1.0/1.0/1.0, id7793=1.0/1.0/1.0, id7794=1.0/1.0/1.0, id7795=1.0/1.0/1.0, id7796=1.0/1.0/1.0, id7797=1.0/1.0/1.0, id7798=1.0/1.0/1.0, id7799=1.0/1.0/1.0, id78=1.0/1.0/1.0, id780=1.0/1.0/1.0, id7800=1.0/1.0/1.0, id7801=1.0/1.0/1.0, id7802=1.0/1.0/1.0, id7803=1.0/1.0/1.0, id7804=1.0/1.0/1.0, id7805=1.0/1.0/1.0, id7806=1.0/1.0/1.0, id7807=1.0/1.0/1.0, id7808=1.0/1.0/1.0, id7809=1.0/1.0/1.0, id781=1.0/1.0/1.0, id7810=1.0/1.0/1.0, id7811=1.0/1.0/1.0, id7812=1.0/1.0/1.0, id7813=1.0/1.0/1.0, id7814=1.0/1.0/1.0, id7815=1.0/1.0/1.0, id7816=1.0/1.0/1.0, id7817=1.0/1.0/1.0, id7818=1.0/1.0/1.0, id7819=1.0/1.0/1.0, id782=1.0/1.0/1.0, id7820=1.0/1.0/1.0, id7821=1.0/1.0/1.0, id7822=1.0/1.0/1.0, id7823=1.0/1.0/1.0, id7824=1.0/1.0/1.0, id7825=1.0/1.0/1.0, id7826=1.0/1.0/1.0, id7827=1.0/1.0/1.0, id7828=1.0/1.0/1.0, id7829=1.0/1.0/1.0, id783=1.0/1.0/1.0, id7830=1.0/1.0/1.0, id7831=1.0/1.0/1.0, id7832=1.0/1.0/1.0, id7833=1.0/1.0/1.0, id7834=1.0/1.0/1.0, id7835=1.0/1.0/1.0, id7836=1.0/1.0/1.0, id7837=1.0/1.0/1.0, id7838=1.0/1.0/1.0, id7839=1.0/1.0/1.0, id784=1.0/1.0/1.0, id7840=1.0/1.0/1.0, id7841=1.0/1.0/1.0, id7842=1.0/1.0/1.0, id7843=1.0/1.0/1.0, id7844=1.0/1.0/1.0, id7845=1.0/1.0/1.0, id7846=1.0/1.0/1.0, id7847=1.0/1.0/1.0, id7848=1.0/1.0/1.0, id7849=1.0/1.0/1.0, id785=1.0/1.0/1.0, id7850=1.0/1.0/1.0, id7851=1.0/1.0/1.0, id7852=1.0/1.0/1.0, id7853=1.0/1.0/1.0, id7854=1.0/1.0/1.0, id7855=1.0/1.0/1.0, id7856=1.0/1.0/1.0, id7857=1.0/1.0/1.0, id7858=1.0/1.0/1.0, id7859=1.0/1.0/1.0, id786=1.0/1.0/1.0, id7860=1.0/1.0/1.0, id7861=1.0/1.0/1.0, id7862=1.0/1.0/1.0, id7863=1.0/1.0/1.0, id7864=1.0/1.0/1.0, id7865=1.0/1.0/1.0, id7866=1.0/1.0/1.0, id7867=1.0/1.0/1.0, id7868=1.0/1.0/1.0, id7869=1.0/1.0/1.0, id787=1.0/1.0/1.0, id7870=1.0/1.0/1.0, id7871=1.0/1.0/1.0, id7872=1.0/1.0/1.0, id7873=1.0/1.0/1.0, id7874=1.0/1.0/1.0, id7875=1.0/1.0/1.0, id7876=1.0/1.0/1.0, id7877=1.0/1.0/1.0, id7878=1.0/1.0/1.0, id7879=1.0/1.0/1.0, id788=1.0/1.0/1.0, id7880=1.0/1.0/1.0, id7881=1.0/1.0/1.0, id7882=1.0/1.0/1.0, id7883=1.0/1.0/1.0, id7884=1.0/1.0/1.0, id7885=1.0/1.0/1.0, id7886=1.0/1.0/1.0, id7887=1.0/1.0/1.0, id7888=1.0/1.0/1.0, id7889=1.0/1.0/1.0, id789=1.0/1.0/1.0, id7890=1.0/1.0/1.0, id7891=1.0/1.0/1.0, id7892=1.0/1.0/1.0, id7893=1.0/1.0/1.0, id7894=1.0/1.0/1.0, id7895=1.0/1.0/1.0, id7896=1.0/1.0/1.0, id7897=1.0/1.0/1.0, id7898=1.0/1.0/1.0, id7899=1.0/1.0/1.0, id79=1.0/1.0/1.0, id790=1.0/1.0/1.0, id7900=1.0/1.0/1.0, id7901=1.0/1.0/1.0, id7902=1.0/1.0/1.0, id7903=1.0/1.0/1.0, id7904=1.0/1.0/1.0, id7905=1.0/1.0/1.0, id7906=1.0/1.0/1.0, id7907=1.0/1.0/1.0, id7908=1.0/1.0/1.0, id7909=1.0/1.0/1.0, id791=1.0/1.0/1.0, id7910=1.0/1.0/1.0, id7911=1.0/1.0/1.0, id7912=1.0/1.0/1.0, id7913=1.0/1.0/1.0, id7914=1.0/1.0/1.0, id7915=1.0/1.0/1.0, id7916=1.0/1.0/1.0, id7917=1.0/1.0/1.0, id7918=1.0/1.0/1.0, id7919=1.0/1.0/1.0, id792=1.0/1.0/1.0, id7920=1.0/1.0/1.0, id7921=1.0/1.0/1.0, id7922=1.0/1.0/1.0, id7923=1.0/1.0/1.0, id7924=1.0/1.0/1.0, id7925=1.0/1.0/1.0, id7926=1.0/1.0/1.0, id7927=1.0/1.0/1.0, id7928=1.0/1.0/1.0, id7929=1.0/1.0/1.0, id793=1.0/1.0/1.0, id7930=1.0/1.0/1.0, id7931=1.0/1.0/1.0, id7932=1.0/1.0/1.0, id7933=1.0/1.0/1.0, id7934=1.0/1.0/1.0, id7935=1.0/1.0/1.0, id7936=1.0/1.0/1.0, id7937=1.0/1.0/1.0, id7938=1.0/1.0/1.0, id7939=1.0/1.0/1.0, id794=1.0/1.0/1.0, id7940=1.0/1.0/1.0, id7941=1.0/1.0/1.0, id7942=1.0/1.0/1.0, id7943=1.0/1.0/1.0, id7944=1.0/1.0/1.0, id7945=1.0/1.0/1.0, id7946=1.0/1.0/1.0, id7947=1.0/1.0/1.0, id7948=1.0/1.0/1.0, id7949=1.0/1.0/1.0, id795=1.0/1.0/1.0, id7950=1.0/1.0/1.0, id7951=1.0/1.0/1.0, id7952=1.0/1.0/1.0, id7953=1.0/1.0/1.0, id7954=1.0/1.0/1.0, id7955=1.0/1.0/1.0, id7956=1.0/1.0/1.0, id7957=1.0/1.0/1.0, id7958=1.0/1.0/1.0, id7959=1.0/1.0/1.0, id796=1.0/1.0/1.0, id7960=1.0/1.0/1.0, id7961=1.0/1.0/1.0, id7962=1.0/1.0/1.0, id7963=1.0/1.0/1.0, id7964=1.0/1.0/1.0, id7965=1.0/1.0/1.0, id7966=1.0/1.0/1.0, id7967=1.0/1.0/1.0, id7968=1.0/1.0/1.0, id7969=1.0/1.0/1.0, id797=1.0/1.0/1.0, id7970=1.0/1.0/1.0, id7971=1.0/1.0/1.0, id7972=1.0/1.0/1.0, id7973=1.0/1.0/1.0, id7974=1.0/1.0/1.0, id7975=1.0/1.0/1.0, id7976=1.0/1.0/1.0, id7977=1.0/1.0/1.0, id7978=1.0/1.0/1.0, id7979=1.0/1.0/1.0, id798=1.0/1.0/1.0, id7980=1.0/1.0/1.0, id7981=1.0/1.0/1.0, id7982=1.0/1.0/1.0, id7983=1.0/1.0/1.0, id7984=1.0/1.0/1.0, id7985=1.0/1.0/1.0, id7986=1.0/1.0/1.0, id7987=1.0/1.0/1.0, id7988=1.0/1.0/1.0, id7989=1.0/1.0/1.0, id799=1.0/1.0/1.0, id7990=1.0/1.0/1.0, id7991=1.0/1.0/1.0, id7992=1.0/1.0/1.0, id7993=1.0/1.0/1.0, id7994=1.0/1.0/1.0, id7995=1.0/1.0/1.0, id7996=1.0/1.0/1.0, id7997=1.0/1.0/1.0, id7998=1.0/1.0/1.0, id7999=1.0/1.0/1.0, id8=1.0/1.0/1.0, id80=1.0/1.0/1.0, id800=1.0/1.0/1.0, id8000=1.0/1.0/1.0, id8001=1.0/1.0/1.0, id8002=1.0/1.0/1.0, id8003=1.0/1.0/1.0, id8004=1.0/1.0/1.0, id8005=1.0/1.0/1.0, id8006=1.0/1.0/1.0, id8007=1.0/1.0/1.0, id8008=1.0/1.0/1.0, id8009=1.0/1.0/1.0, id801=1.0/1.0/1.0, id8010=1.0/1.0/1.0, id8011=1.0/1.0/1.0, id8012=1.0/1.0/1.0, id8013=1.0/1.0/1.0, id8014=1.0/1.0/1.0, id8015=1.0/1.0/1.0, id8016=1.0/1.0/1.0, id8017=1.0/1.0/1.0, id8018=1.0/1.0/1.0, id8019=1.0/1.0/1.0, id802=1.0/1.0/1.0, id8020=1.0/1.0/1.0, id8021=1.0/1.0/1.0, id8022=1.0/1.0/1.0, id8023=1.0/1.0/1.0, id8024=1.0/1.0/1.0, id8025=1.0/1.0/1.0, id8026=1.0/1.0/1.0, id8027=1.0/1.0/1.0, id8028=1.0/1.0/1.0, id8029=1.0/1.0/1.0, id803=1.0/1.0/1.0, id8030=1.0/1.0/1.0, id8031=1.0/1.0/1.0, id8032=1.0/1.0/1.0, id8033=1.0/1.0/1.0, id8034=1.0/1.0/1.0, id8035=1.0/1.0/1.0, id8036=1.0/1.0/1.0, id8037=1.0/1.0/1.0, id8038=1.0/1.0/1.0, id8039=1.0/1.0/1.0, id804=1.0/1.0/1.0, id8040=1.0/1.0/1.0, id8041=1.0/1.0/1.0, id8042=1.0/1.0/1.0, id8043=1.0/1.0/1.0, id8044=1.0/1.0/1.0, id8045=1.0/1.0/1.0, id8046=1.0/1.0/1.0, id8047=1.0/1.0/1.0, id8048=1.0/1.0/1.0, id8049=1.0/1.0/1.0, id805=1.0/1.0/1.0, id8050=1.0/1.0/1.0, id8051=1.0/1.0/1.0, id8052=1.0/1.0/1.0, id8053=1.0/1.0/1.0, id8054=1.0/1.0/1.0, id8055=1.0/1.0/1.0, id8056=1.0/1.0/1.0, id8057=1.0/1.0/1.0, id8058=1.0/1.0/1.0, id8059=1.0/1.0/1.0, id806=1.0/1.0/1.0, id8060=1.0/1.0/1.0, id8061=1.0/1.0/1.0, id8062=1.0/1.0/1.0, id8063=1.0/1.0/1.0, id8064=1.0/1.0/1.0, id8065=1.0/1.0/1.0, id8066=1.0/1.0/1.0, id8067=1.0/1.0/1.0, id8068=1.0/1.0/1.0, id8069=1.0/1.0/1.0, id807=1.0/1.0/1.0, id8070=1.0/1.0/1.0, id8071=1.0/1.0/1.0, id8072=1.0/1.0/1.0, id8073=1.0/1.0/1.0, id8074=1.0/1.0/1.0, id8075=1.0/1.0/1.0, id8076=1.0/1.0/1.0, id8077=1.0/1.0/1.0, id8078=1.0/1.0/1.0, id8079=1.0/1.0/1.0, id808=1.0/1.0/1.0, id8080=1.0/1.0/1.0, id8081=1.0/1.0/1.0, id8082=1.0/1.0/1.0, id8083=1.0/1.0/1.0, id8084=1.0/1.0/1.0, id8085=1.0/1.0/1.0, id8086=1.0/1.0/1.0, id8087=1.0/1.0/1.0, id8088=1.0/1.0/1.0, id8089=1.0/1.0/1.0, id809=1.0/1.0/1.0, id8090=1.0/1.0/1.0, id8091=1.0/1.0/1.0, id8092=1.0/1.0/1.0, id8093=1.0/1.0/1.0, id8094=1.0/1.0/1.0, id8095=1.0/1.0/1.0, id8096=1.0/1.0/1.0, id8097=1.0/1.0/1.0, id8098=1.0/1.0/1.0, id8099=1.0/1.0/1.0, id81=1.0/1.0/1.0, id810=1.0/1.0/1.0, id8100=1.0/1.0/1.0, id8101=1.0/1.0/1.0, id8102=1.0/1.0/1.0, id8103=1.0/1.0/1.0, id8104=1.0/1.0/1.0, id8105=1.0/1.0/1.0, id8106=1.0/1.0/1.0, id8107=1.0/1.0/1.0, id8108=1.0/1.0/1.0, id8109=1.0/1.0/1.0, id811=1.0/1.0/1.0, id8110=1.0/1.0/1.0, id8111=1.0/1.0/1.0, id8112=1.0/1.0/1.0, id8113=1.0/1.0/1.0, id8114=1.0/1.0/1.0, id8115=1.0/1.0/1.0, id8116=1.0/1.0/1.0, id8117=1.0/1.0/1.0, id8118=1.0/1.0/1.0, id8119=1.0/1.0/1.0, id812=1.0/1.0/1.0, id8120=1.0/1.0/1.0, id8121=1.0/1.0/1.0, id8122=1.0/1.0/1.0, id8123=1.0/1.0/1.0, id8124=1.0/1.0/1.0, id8125=1.0/1.0/1.0, id8126=1.0/1.0/1.0, id8127=1.0/1.0/1.0, id8128=1.0/1.0/1.0, id8129=1.0/1.0/1.0, id813=1.0/1.0/1.0, id8130=1.0/1.0/1.0, id8131=1.0/1.0/1.0, id8132=1.0/1.0/1.0, id8133=1.0/1.0/1.0, id8134=1.0/1.0/1.0, id8135=1.0/1.0/1.0, id8136=1.0/1.0/1.0, id8137=1.0/1.0/1.0, id8138=1.0/1.0/1.0, id8139=1.0/1.0/1.0, id814=1.0/1.0/1.0, id8140=1.0/1.0/1.0, id8141=1.0/1.0/1.0, id8142=1.0/1.0/1.0, id8143=1.0/1.0/1.0, id8144=1.0/1.0/1.0, id8145=1.0/1.0/1.0, id8146=1.0/1.0/1.0, id8147=1.0/1.0/1.0, id8148=1.0/1.0/1.0, id8149=1.0/1.0/1.0, id815=1.0/1.0/1.0, id8150=1.0/1.0/1.0, id8151=1.0/1.0/1.0, id8152=1.0/1.0/1.0, id8153=1.0/1.0/1.0, id8154=1.0/1.0/1.0, id8155=1.0/1.0/1.0, id8156=1.0/1.0/1.0, id8157=1.0/1.0/1.0, id8158=1.0/1.0/1.0, id8159=1.0/1.0/1.0, id816=1.0/1.0/1.0, id8160=1.0/1.0/1.0, id8161=1.0/1.0/1.0, id8162=1.0/1.0/1.0, id8163=1.0/1.0/1.0, id8164=1.0/1.0/1.0, id8165=1.0/1.0/1.0, id8166=1.0/1.0/1.0, id8167=1.0/1.0/1.0, id8168=1.0/1.0/1.0, id8169=1.0/1.0/1.0, id817=1.0/1.0/1.0, id8170=1.0/1.0/1.0, id8171=1.0/1.0/1.0, id8172=1.0/1.0/1.0, id8173=1.0/1.0/1.0, id8174=1.0/1.0/1.0, id8175=1.0/1.0/1.0, id8176=1.0/1.0/1.0, id8177=1.0/1.0/1.0, id8178=1.0/1.0/1.0, id8179=1.0/1.0/1.0, id818=1.0/1.0/1.0, id8180=1.0/1.0/1.0, id8181=1.0/1.0/1.0, id8182=1.0/1.0/1.0, id8183=1.0/1.0/1.0, id8184=1.0/1.0/1.0, id8185=1.0/1.0/1.0, id8186=1.0/1.0/1.0, id8187=1.0/1.0/1.0, id8188=1.0/1.0/1.0, id8189=1.0/1.0/1.0, id819=1.0/1.0/1.0, id8190=1.0/1.0/1.0, id8191=1.0/1.0/1.0, id8192=1.0/1.0/1.0, id8193=1.0/1.0/1.0, id8194=1.0/1.0/1.0, id8195=1.0/1.0/1.0, id8196=1.0/1.0/1.0, id8197=1.0/1.0/1.0, id8198=1.0/1.0/1.0, id8199=1.0/1.0/1.0, id82=1.0/1.0/1.0, id820=1.0/1.0/1.0, id8200=1.0/1.0/1.0, id8201=1.0/1.0/1.0, id8202=1.0/1.0/1.0, id8203=1.0/1.0/1.0, id8204=1.0/1.0/1.0, id8205=1.0/1.0/1.0, id8206=1.0/1.0/1.0, id8207=1.0/1.0/1.0, id8208=1.0/1.0/1.0, id8209=1.0/1.0/1.0, id821=1.0/1.0/1.0, id8210=1.0/1.0/1.0, id8211=1.0/1.0/1.0, id8212=1.0/1.0/1.0, id8213=1.0/1.0/1.0, id8214=1.0/1.0/1.0, id8215=1.0/1.0/1.0, id8216=1.0/1.0/1.0, id8217=1.0/1.0/1.0, id8218=1.0/1.0/1.0, id8219=1.0/1.0/1.0, id822=1.0/1.0/1.0, id8220=1.0/1.0/1.0, id8221=1.0/1.0/1.0, id8222=1.0/1.0/1.0, id8223=1.0/1.0/1.0, id8224=1.0/1.0/1.0, id8225=1.0/1.0/1.0, id8226=1.0/1.0/1.0, id8227=1.0/1.0/1.0, id8228=1.0/1.0/1.0, id8229=1.0/1.0/1.0, id823=1.0/1.0/1.0, id8230=1.0/1.0/1.0, id8231=1.0/1.0/1.0, id8232=1.0/1.0/1.0, id8233=1.0/1.0/1.0, id8234=1.0/1.0/1.0, id8235=1.0/1.0/1.0, id8236=1.0/1.0/1.0, id8237=1.0/1.0/1.0, id8238=1.0/1.0/1.0, id8239=1.0/1.0/1.0, id824=1.0/1.0/1.0, id8240=1.0/1.0/1.0, id8241=1.0/1.0/1.0, id8242=1.0/1.0/1.0, id8243=1.0/1.0/1.0, id8244=1.0/1.0/1.0, id8245=1.0/1.0/1.0, id8246=1.0/1.0/1.0, id8247=1.0/1.0/1.0, id8248=1.0/1.0/1.0, id8249=1.0/1.0/1.0, id825=1.0/1.0/1.0, id8250=1.0/1.0/1.0, id8251=1.0/1.0/1.0, id8252=1.0/1.0/1.0, id8253=1.0/1.0/1.0, id8254=1.0/1.0/1.0, id8255=1.0/1.0/1.0, id8256=1.0/1.0/1.0, id8257=1.0/1.0/1.0, id8258=1.0/1.0/1.0, id8259=1.0/1.0/1.0, id826=1.0/1.0/1.0, id8260=1.0/1.0/1.0, id8261=1.0/1.0/1.0, id8262=1.0/1.0/1.0, id8263=1.0/1.0/1.0, id8264=1.0/1.0/1.0, id8265=1.0/1.0/1.0, id8266=1.0/1.0/1.0, id8267=1.0/1.0/1.0, id8268=1.0/1.0/1.0, id8269=1.0/1.0/1.0, id827=1.0/1.0/1.0, id8270=1.0/1.0/1.0, id8271=1.0/1.0/1.0, id8272=1.0/1.0/1.0, id8273=1.0/1.0/1.0, id8274=1.0/1.0/1.0, id8275=1.0/1.0/1.0, id8276=1.0/1.0/1.0, id8277=1.0/1.0/1.0, id8278=1.0/1.0/1.0, id8279=1.0/1.0/1.0, id828=1.0/1.0/1.0, id8280=1.0/1.0/1.0, id8281=1.0/1.0/1.0, id8282=1.0/1.0/1.0, id8283=1.0/1.0/1.0, id8284=1.0/1.0/1.0, id8285=1.0/1.0/1.0, id8286=1.0/1.0/1.0, id8287=1.0/1.0/1.0, id8288=1.0/1.0/1.0, id8289=1.0/1.0/1.0, id829=1.0/1.0/1.0, id8290=1.0/1.0/1.0, id8291=1.0/1.0/1.0, id8292=1.0/1.0/1.0, id8293=1.0/1.0/1.0, id8294=1.0/1.0/1.0, id8295=1.0/1.0/1.0, id8296=1.0/1.0/1.0, id8297=1.0/1.0/1.0, id8298=1.0/1.0/1.0, id8299=1.0/1.0/1.0, id83=1.0/1.0/1.0, id830=1.0/1.0/1.0, id8300=1.0/1.0/1.0, id8301=1.0/1.0/1.0, id8302=1.0/1.0/1.0, id8303=1.0/1.0/1.0, id8304=1.0/1.0/1.0, id8305=1.0/1.0/1.0, id8306=1.0/1.0/1.0, id8307=1.0/1.0/1.0, id8308=1.0/1.0/1.0, id8309=1.0/1.0/1.0, id831=1.0/1.0/1.0, id8310=1.0/1.0/1.0, id8311=1.0/1.0/1.0, id8312=1.0/1.0/1.0, id8313=1.0/1.0/1.0, id8314=1.0/1.0/1.0, id8315=1.0/1.0/1.0, id8316=1.0/1.0/1.0, id8317=1.0/1.0/1.0, id8318=1.0/1.0/1.0, id8319=1.0/1.0/1.0, id832=1.0/1.0/1.0, id8320=1.0/1.0/1.0, id8321=1.0/1.0/1.0, id8322=1.0/1.0/1.0, id8323=1.0/1.0/1.0, id8324=1.0/1.0/1.0, id8325=1.0/1.0/1.0, id8326=1.0/1.0/1.0, id8327=1.0/1.0/1.0, id8328=1.0/1.0/1.0, id8329=1.0/1.0/1.0, id833=1.0/1.0/1.0, id8330=1.0/1.0/1.0, id8331=1.0/1.0/1.0, id8332=1.0/1.0/1.0, id8333=1.0/1.0/1.0, id8334=1.0/1.0/1.0, id8335=1.0/1.0/1.0, id8336=1.0/1.0/1.0, id8337=1.0/1.0/1.0, id8338=1.0/1.0/1.0, id8339=1.0/1.0/1.0, id834=1.0/1.0/1.0, id8340=1.0/1.0/1.0, id8341=1.0/1.0/1.0, id8342=1.0/1.0/1.0, id8343=1.0/1.0/1.0, id8344=1.0/1.0/1.0, id8345=1.0/1.0/1.0, id8346=1.0/1.0/1.0, id8347=1.0/1.0/1.0, id8348=1.0/1.0/1.0, id8349=1.0/1.0/1.0, id835=1.0/1.0/1.0, id8350=1.0/1.0/1.0, id8351=1.0/1.0/1.0, id8352=1.0/1.0/1.0, id8353=1.0/1.0/1.0, id8354=1.0/1.0/1.0, id8355=1.0/1.0/1.0, id8356=1.0/1.0/1.0, id8357=1.0/1.0/1.0, id8358=1.0/1.0/1.0, id8359=1.0/1.0/1.0, id836=1.0/1.0/1.0, id8360=1.0/1.0/1.0, id8361=1.0/1.0/1.0, id8362=1.0/1.0/1.0, id8363=1.0/1.0/1.0, id8364=1.0/1.0/1.0, id8365=1.0/1.0/1.0, id8366=1.0/1.0/1.0, id8367=1.0/1.0/1.0, id8368=1.0/1.0/1.0, id8369=1.0/1.0/1.0, id837=1.0/1.0/1.0, id8370=1.0/1.0/1.0, id8371=1.0/1.0/1.0, id8372=1.0/1.0/1.0, id8373=1.0/1.0/1.0, id8374=1.0/1.0/1.0, id8375=1.0/1.0/1.0, id8376=1.0/1.0/1.0, id8377=1.0/1.0/1.0, id8378=1.0/1.0/1.0, id8379=1.0/1.0/1.0, id838=1.0/1.0/1.0, id8380=1.0/1.0/1.0, id8381=1.0/1.0/1.0, id8382=1.0/1.0/1.0, id8383=1.0/1.0/1.0, id8384=1.0/1.0/1.0, id8385=1.0/1.0/1.0, id8386=1.0/1.0/1.0, id8387=1.0/1.0/1.0, id8388=1.0/1.0/1.0, id8389=1.0/1.0/1.0, id839=1.0/1.0/1.0, id8390=1.0/1.0/1.0, id8391=1.0/1.0/1.0, id8392=1.0/1.0/1.0, id8393=1.0/1.0/1.0, id8394=1.0/1.0/1.0, id8395=1.0/1.0/1.0, id8396=1.0/1.0/1.0, id8397=1.0/1.0/1.0, id8398=1.0/1.0/1.0, id8399=1.0/1.0/1.0, id84=1.0/1.0/1.0, id840=1.0/1.0/1.0, id8400=1.0/1.0/1.0, id8401=1.0/1.0/1.0, id8402=1.0/1.0/1.0, id8403=1.0/1.0/1.0, id8404=1.0/1.0/1.0, id8405=1.0/1.0/1.0, id8406=1.0/1.0/1.0, id8407=1.0/1.0/1.0, id8408=1.0/1.0/1.0, id8409=1.0/1.0/1.0, id841=1.0/1.0/1.0, id8410=1.0/1.0/1.0, id8411=1.0/1.0/1.0, id8412=1.0/1.0/1.0, id8413=1.0/1.0/1.0, id8414=1.0/1.0/1.0, id8415=1.0/1.0/1.0, id8416=1.0/1.0/1.0, id8417=1.0/1.0/1.0, id8418=1.0/1.0/1.0, id8419=1.0/1.0/1.0, id842=1.0/1.0/1.0, id8420=1.0/1.0/1.0, id8421=1.0/1.0/1.0, id8422=1.0/1.0/1.0, id8423=1.0/1.0/1.0, id8424=1.0/1.0/1.0, id8425=1.0/1.0/1.0, id8426=1.0/1.0/1.0, id8427=1.0/1.0/1.0, id8428=1.0/1.0/1.0, id8429=1.0/1.0/1.0, id843=1.0/1.0/1.0, id8430=1.0/1.0/1.0, id8431=1.0/1.0/1.0, id8432=1.0/1.0/1.0, id8433=1.0/1.0/1.0, id8434=1.0/1.0/1.0, id8435=1.0/1.0/1.0, id8436=1.0/1.0/1.0, id8437=1.0/1.0/1.0, id8438=1.0/1.0/1.0, id8439=1.0/1.0/1.0, id844=1.0/1.0/1.0, id8440=1.0/1.0/1.0, id8441=1.0/1.0/1.0, id8442=1.0/1.0/1.0, id8443=1.0/1.0/1.0, id8444=1.0/1.0/1.0, id8445=1.0/1.0/1.0, id8446=1.0/1.0/1.0, id8447=1.0/1.0/1.0, id8448=1.0/1.0/1.0, id8449=1.0/1.0/1.0, id845=1.0/1.0/1.0, id8450=1.0/1.0/1.0, id8451=1.0/1.0/1.0, id8452=1.0/1.0/1.0, id8453=1.0/1.0/1.0, id8454=1.0/1.0/1.0, id8455=1.0/1.0/1.0, id8456=1.0/1.0/1.0, id8457=1.0/1.0/1.0, id8458=1.0/1.0/1.0, id8459=1.0/1.0/1.0, id846=1.0/1.0/1.0, id8460=1.0/1.0/1.0, id8461=1.0/1.0/1.0, id8462=1.0/1.0/1.0, id8463=1.0/1.0/1.0, id8464=1.0/1.0/1.0, id8465=1.0/1.0/1.0, id8466=1.0/1.0/1.0, id8467=1.0/1.0/1.0, id8468=1.0/1.0/1.0, id8469=1.0/1.0/1.0, id847=1.0/1.0/1.0, id8470=1.0/1.0/1.0, id8471=1.0/1.0/1.0, id8472=1.0/1.0/1.0, id8473=1.0/1.0/1.0, id8474=1.0/1.0/1.0, id8475=1.0/1.0/1.0, id8476=1.0/1.0/1.0, id8477=1.0/1.0/1.0, id8478=1.0/1.0/1.0, id8479=1.0/1.0/1.0, id848=1.0/1.0/1.0, id8480=1.0/1.0/1.0, id8481=1.0/1.0/1.0, id8482=1.0/1.0/1.0, id8483=1.0/1.0/1.0, id8484=1.0/1.0/1.0, id8485=1.0/1.0/1.0, id8486=1.0/1.0/1.0, id8487=1.0/1.0/1.0, id8488=1.0/1.0/1.0, id8489=1.0/1.0/1.0, id849=1.0/1.0/1.0, id8490=1.0/1.0/1.0, id8491=1.0/1.0/1.0, id8492=1.0/1.0/1.0, id8493=1.0/1.0/1.0, id8494=1.0/1.0/1.0, id8495=1.0/1.0/1.0, id8496=1.0/1.0/1.0, id8497=1.0/1.0/1.0, id8498=1.0/1.0/1.0, id8499=1.0/1.0/1.0, id85=1.0/1.0/1.0, id850=1.0/1.0/1.0, id8500=1.0/1.0/1.0, id8501=1.0/1.0/1.0, id8502=1.0/1.0/1.0, id8503=1.0/1.0/1.0, id8504=1.0/1.0/1.0, id8505=1.0/1.0/1.0, id8506=1.0/1.0/1.0, id8507=1.0/1.0/1.0, id8508=1.0/1.0/1.0, id8509=1.0/1.0/1.0, id851=1.0/1.0/1.0, id8510=1.0/1.0/1.0, id8511=1.0/1.0/1.0, id8512=1.0/1.0/1.0, id8513=1.0/1.0/1.0, id8514=1.0/1.0/1.0, id8515=1.0/1.0/1.0, id8516=1.0/1.0/1.0, id8517=1.0/1.0/1.0, id8518=1.0/1.0/1.0, id8519=1.0/1.0/1.0, id852=1.0/1.0/1.0, id8520=1.0/1.0/1.0, id8521=1.0/1.0/1.0, id8522=1.0/1.0/1.0, id8523=1.0/1.0/1.0, id8524=1.0/1.0/1.0, id8525=1.0/1.0/1.0, id8526=1.0/1.0/1.0, id8527=1.0/1.0/1.0, id8528=1.0/1.0/1.0, id8529=1.0/1.0/1.0, id853=1.0/1.0/1.0, id8530=1.0/1.0/1.0, id8531=1.0/1.0/1.0, id8532=1.0/1.0/1.0, id8533=1.0/1.0/1.0, id8534=1.0/1.0/1.0, id8535=1.0/1.0/1.0, id8536=1.0/1.0/1.0, id8537=1.0/1.0/1.0, id8538=1.0/1.0/1.0, id8539=1.0/1.0/1.0, id854=1.0/1.0/1.0, id8540=1.0/1.0/1.0, id8541=1.0/1.0/1.0, id8542=1.0/1.0/1.0, id8543=1.0/1.0/1.0, id8544=1.0/1.0/1.0, id8545=1.0/1.0/1.0, id8546=1.0/1.0/1.0, id8547=1.0/1.0/1.0, id8548=1.0/1.0/1.0, id8549=1.0/1.0/1.0, id855=1.0/1.0/1.0, id8550=1.0/1.0/1.0, id8551=1.0/1.0/1.0, id8552=1.0/1.0/1.0, id8553=1.0/1.0/1.0, id8554=1.0/1.0/1.0, id8555=1.0/1.0/1.0, id8556=1.0/1.0/1.0, id8557=1.0/1.0/1.0, id8558=1.0/1.0/1.0, id8559=1.0/1.0/1.0, id856=1.0/1.0/1.0, id8560=1.0/1.0/1.0, id8561=1.0/1.0/1.0, id8562=1.0/1.0/1.0, id8563=1.0/1.0/1.0, id8564=1.0/1.0/1.0, id8565=1.0/1.0/1.0, id8566=1.0/1.0/1.0, id8567=1.0/1.0/1.0, id8568=1.0/1.0/1.0, id8569=1.0/1.0/1.0, id857=1.0/1.0/1.0, id8570=1.0/1.0/1.0, id8571=1.0/1.0/1.0, id8572=1.0/1.0/1.0, id8573=1.0/1.0/1.0, id8574=1.0/1.0/1.0, id8575=1.0/1.0/1.0, id8576=1.0/1.0/1.0, id8577=1.0/1.0/1.0, id8578=1.0/1.0/1.0, id8579=1.0/1.0/1.0, id858=1.0/1.0/1.0, id8580=1.0/1.0/1.0, id8581=1.0/1.0/1.0, id8582=1.0/1.0/1.0, id8583=1.0/1.0/1.0, id8584=1.0/1.0/1.0, id8585=1.0/1.0/1.0, id8586=1.0/1.0/1.0, id8587=1.0/1.0/1.0, id8588=1.0/1.0/1.0, id8589=1.0/1.0/1.0, id859=1.0/1.0/1.0, id8590=1.0/1.0/1.0, id8591=1.0/1.0/1.0, id8592=1.0/1.0/1.0, id8593=1.0/1.0/1.0, id8594=1.0/1.0/1.0, id8595=1.0/1.0/1.0, id8596=1.0/1.0/1.0, id8597=1.0/1.0/1.0, id8598=1.0/1.0/1.0, id8599=1.0/1.0/1.0, id86=1.0/1.0/1.0, id860=1.0/1.0/1.0, id8600=1.0/1.0/1.0, id8601=1.0/1.0/1.0, id8602=1.0/1.0/1.0, id8603=1.0/1.0/1.0, id8604=1.0/1.0/1.0, id8605=1.0/1.0/1.0, id8606=1.0/1.0/1.0, id8607=1.0/1.0/1.0, id8608=1.0/1.0/1.0, id8609=1.0/1.0/1.0, id861=1.0/1.0/1.0, id8610=1.0/1.0/1.0, id8611=1.0/1.0/1.0, id8612=1.0/1.0/1.0, id8613=1.0/1.0/1.0, id8614=1.0/1.0/1.0, id8615=1.0/1.0/1.0, id8616=1.0/1.0/1.0, id8617=1.0/1.0/1.0, id8618=1.0/1.0/1.0, id8619=1.0/1.0/1.0, id862=1.0/1.0/1.0, id8620=1.0/1.0/1.0, id8621=1.0/1.0/1.0, id8622=1.0/1.0/1.0, id8623=1.0/1.0/1.0, id8624=1.0/1.0/1.0, id8625=1.0/1.0/1.0, id8626=1.0/1.0/1.0, id8627=1.0/1.0/1.0, id8628=1.0/1.0/1.0, id8629=1.0/1.0/1.0, id863=1.0/1.0/1.0, id8630=1.0/1.0/1.0, id8631=1.0/1.0/1.0, id8632=1.0/1.0/1.0, id8633=1.0/1.0/1.0, id8634=1.0/1.0/1.0, id8635=1.0/1.0/1.0, id8636=1.0/1.0/1.0, id8637=1.0/1.0/1.0, id8638=1.0/1.0/1.0, id8639=1.0/1.0/1.0, id864=1.0/1.0/1.0, id8640=1.0/1.0/1.0, id8641=1.0/1.0/1.0, id8642=1.0/1.0/1.0, id8643=1.0/1.0/1.0, id8644=1.0/1.0/1.0, id8645=1.0/1.0/1.0, id8646=1.0/1.0/1.0, id8647=1.0/1.0/1.0, id8648=1.0/1.0/1.0, id8649=1.0/1.0/1.0, id865=1.0/1.0/1.0, id8650=1.0/1.0/1.0, id8651=1.0/1.0/1.0, id8652=1.0/1.0/1.0, id8653=1.0/1.0/1.0, id8654=1.0/1.0/1.0, id8655=1.0/1.0/1.0, id8656=1.0/1.0/1.0, id8657=1.0/1.0/1.0, id8658=1.0/1.0/1.0, id8659=1.0/1.0/1.0, id866=1.0/1.0/1.0, id8660=1.0/1.0/1.0, id8661=1.0/1.0/1.0, id8662=1.0/1.0/1.0, id8663=1.0/1.0/1.0, id8664=1.0/1.0/1.0, id8665=1.0/1.0/1.0, id8666=1.0/1.0/1.0, id8667=1.0/1.0/1.0, id8668=1.0/1.0/1.0, id8669=1.0/1.0/1.0, id867=1.0/1.0/1.0, id8670=1.0/1.0/1.0, id8671=1.0/1.0/1.0, id8672=1.0/1.0/1.0, id8673=1.0/1.0/1.0, id8674=1.0/1.0/1.0, id8675=1.0/1.0/1.0, id8676=1.0/1.0/1.0, id8677=1.0/1.0/1.0, id8678=1.0/1.0/1.0, id8679=1.0/1.0/1.0, id868=1.0/1.0/1.0, id8680=1.0/1.0/1.0, id8681=1.0/1.0/1.0, id8682=1.0/1.0/1.0, id8683=1.0/1.0/1.0, id8684=1.0/1.0/1.0, id8685=1.0/1.0/1.0, id8686=1.0/1.0/1.0, id8687=1.0/1.0/1.0, id8688=1.0/1.0/1.0, id8689=1.0/1.0/1.0, id869=1.0/1.0/1.0, id8690=1.0/1.0/1.0, id8691=1.0/1.0/1.0, id8692=1.0/1.0/1.0, id8693=1.0/1.0/1.0, id8694=1.0/1.0/1.0, id8695=1.0/1.0/1.0, id8696=1.0/1.0/1.0, id8697=1.0/1.0/1.0, id8698=1.0/1.0/1.0, id8699=1.0/1.0/1.0, id87=1.0/1.0/1.0, id870=1.0/1.0/1.0, id8700=1.0/1.0/1.0, id8701=1.0/1.0/1.0, id8702=1.0/1.0/1.0, id8703=1.0/1.0/1.0, id8704=1.0/1.0/1.0, id8705=1.0/1.0/1.0, id8706=1.0/1.0/1.0, id8707=1.0/1.0/1.0, id8708=1.0/1.0/1.0, id8709=1.0/1.0/1.0, id871=1.0/1.0/1.0, id8710=1.0/1.0/1.0, id8711=1.0/1.0/1.0, id8712=1.0/1.0/1.0, id8713=1.0/1.0/1.0, id8714=1.0/1.0/1.0, id8715=1.0/1.0/1.0, id8716=1.0/1.0/1.0, id8717=1.0/1.0/1.0, id8718=1.0/1.0/1.0, id8719=1.0/1.0/1.0, id872=1.0/1.0/1.0, id8720=1.0/1.0/1.0, id8721=1.0/1.0/1.0, id8722=1.0/1.0/1.0, id8723=1.0/1.0/1.0, id8724=1.0/1.0/1.0, id8725=1.0/1.0/1.0, id8726=1.0/1.0/1.0, id8727=1.0/1.0/1.0, id8728=1.0/1.0/1.0, id8729=1.0/1.0/1.0, id873=1.0/1.0/1.0, id8730=1.0/1.0/1.0, id8731=1.0/1.0/1.0, id8732=1.0/1.0/1.0, id8733=1.0/1.0/1.0, id8734=1.0/1.0/1.0, id8735=1.0/1.0/1.0, id8736=1.0/1.0/1.0, id8737=1.0/1.0/1.0, id8738=1.0/1.0/1.0, id8739=1.0/1.0/1.0, id874=1.0/1.0/1.0, id8740=1.0/1.0/1.0, id8741=1.0/1.0/1.0, id8742=1.0/1.0/1.0, id8743=1.0/1.0/1.0, id8744=1.0/1.0/1.0, id8745=1.0/1.0/1.0, id8746=1.0/1.0/1.0, id8747=1.0/1.0/1.0, id8748=1.0/1.0/1.0, id8749=1.0/1.0/1.0, id875=1.0/1.0/1.0, id8750=1.0/1.0/1.0, id8751=1.0/1.0/1.0, id8752=1.0/1.0/1.0, id8753=1.0/1.0/1.0, id8754=1.0/1.0/1.0, id8755=1.0/1.0/1.0, id8756=1.0/1.0/1.0, id8757=1.0/1.0/1.0, id8758=1.0/1.0/1.0, id8759=1.0/1.0/1.0, id876=1.0/1.0/1.0, id8760=1.0/1.0/1.0, id8761=1.0/1.0/1.0, id8762=1.0/1.0/1.0, id8763=1.0/1.0/1.0, id8764=1.0/1.0/1.0, id8765=1.0/1.0/1.0, id8766=1.0/1.0/1.0, id8767=1.0/1.0/1.0, id8768=1.0/1.0/1.0, id8769=1.0/1.0/1.0, id877=1.0/1.0/1.0, id8770=1.0/1.0/1.0, id8771=1.0/1.0/1.0, id8772=1.0/1.0/1.0, id8773=1.0/1.0/1.0, id8774=1.0/1.0/1.0, id8775=1.0/1.0/1.0, id8776=1.0/1.0/1.0, id8777=1.0/1.0/1.0, id8778=1.0/1.0/1.0, id8779=1.0/1.0/1.0, id878=1.0/1.0/1.0, id8780=1.0/1.0/1.0, id8781=1.0/1.0/1.0, id8782=1.0/1.0/1.0, id8783=1.0/1.0/1.0, id8784=1.0/1.0/1.0, id8785=1.0/1.0/1.0, id8786=1.0/1.0/1.0, id8787=1.0/1.0/1.0, id8788=1.0/1.0/1.0, id8789=1.0/1.0/1.0, id879=1.0/1.0/1.0, id8790=1.0/1.0/1.0, id8791=1.0/1.0/1.0, id8792=1.0/1.0/1.0, id8793=1.0/1.0/1.0, id8794=1.0/1.0/1.0, id8795=1.0/1.0/1.0, id8796=1.0/1.0/1.0, id8797=1.0/1.0/1.0, id8798=1.0/1.0/1.0, id8799=1.0/1.0/1.0, id88=1.0/1.0/1.0, id880=1.0/1.0/1.0, id8800=1.0/1.0/1.0, id8801=1.0/1.0/1.0, id8802=1.0/1.0/1.0, id8803=1.0/1.0/1.0, id8804=1.0/1.0/1.0, id8805=1.0/1.0/1.0, id8806=1.0/1.0/1.0, id8807=1.0/1.0/1.0, id8808=1.0/1.0/1.0, id8809=1.0/1.0/1.0, id881=1.0/1.0/1.0, id8810=1.0/1.0/1.0, id8811=1.0/1.0/1.0, id8812=1.0/1.0/1.0, id8813=1.0/1.0/1.0, id8814=1.0/1.0/1.0, id8815=1.0/1.0/1.0, id8816=1.0/1.0/1.0, id8817=1.0/1.0/1.0, id8818=1.0/1.0/1.0, id8819=1.0/1.0/1.0, id882=1.0/1.0/1.0, id8820=1.0/1.0/1.0, id8821=1.0/1.0/1.0, id8822=1.0/1.0/1.0, id8823=1.0/1.0/1.0, id8824=1.0/1.0/1.0, id8825=1.0/1.0/1.0, id8826=1.0/1.0/1.0, id8827=1.0/1.0/1.0, id8828=1.0/1.0/1.0, id8829=1.0/1.0/1.0, id883=1.0/1.0/1.0, id8830=1.0/1.0/1.0, id8831=1.0/1.0/1.0, id8832=1.0/1.0/1.0, id8833=1.0/1.0/1.0, id8834=1.0/1.0/1.0, id8835=1.0/1.0/1.0, id8836=1.0/1.0/1.0, id8837=1.0/1.0/1.0, id8838=1.0/1.0/1.0, id8839=1.0/1.0/1.0, id884=1.0/1.0/1.0, id8840=1.0/1.0/1.0, id8841=1.0/1.0/1.0, id8842=1.0/1.0/1.0, id8843=1.0/1.0/1.0, id8844=1.0/1.0/1.0, id8845=1.0/1.0/1.0, id8846=1.0/1.0/1.0, id8847=1.0/1.0/1.0, id8848=1.0/1.0/1.0, id8849=1.0/1.0/1.0, id885=1.0/1.0/1.0, id8850=1.0/1.0/1.0, id8851=1.0/1.0/1.0, id8852=1.0/1.0/1.0, id8853=1.0/1.0/1.0, id8854=1.0/1.0/1.0, id8855=1.0/1.0/1.0, id8856=1.0/1.0/1.0, id8857=1.0/1.0/1.0, id8858=1.0/1.0/1.0, id8859=1.0/1.0/1.0, id886=1.0/1.0/1.0, id8860=1.0/1.0/1.0, id8861=1.0/1.0/1.0, id8862=1.0/1.0/1.0, id8863=1.0/1.0/1.0, id8864=1.0/1.0/1.0, id8865=1.0/1.0/1.0, id8866=1.0/1.0/1.0, id8867=1.0/1.0/1.0, id8868=1.0/1.0/1.0, id8869=1.0/1.0/1.0, id887=1.0/1.0/1.0, id8870=1.0/1.0/1.0, id8871=1.0/1.0/1.0, id8872=1.0/1.0/1.0, id8873=1.0/1.0/1.0, id8874=1.0/1.0/1.0, id8875=1.0/1.0/1.0, id8876=1.0/1.0/1.0, id8877=1.0/1.0/1.0, id8878=1.0/1.0/1.0, id8879=1.0/1.0/1.0, id888=1.0/1.0/1.0, id8880=1.0/1.0/1.0, id8881=1.0/1.0/1.0, id8882=1.0/1.0/1.0, id8883=1.0/1.0/1.0, id8884=1.0/1.0/1.0, id8885=1.0/1.0/1.0, id8886=1.0/1.0/1.0, id8887=1.0/1.0/1.0, id8888=1.0/1.0/1.0, id8889=1.0/1.0/1.0, id889=1.0/1.0/1.0, id8890=1.0/1.0/1.0, id8891=1.0/1.0/1.0, id8892=1.0/1.0/1.0, id8893=1.0/1.0/1.0, id8894=1.0/1.0/1.0, id8895=1.0/1.0/1.0, id8896=1.0/1.0/1.0, id8897=1.0/1.0/1.0, id8898=1.0/1.0/1.0, id8899=1.0/1.0/1.0, id89=1.0/1.0/1.0, id890=1.0/1.0/1.0, id8900=1.0/1.0/1.0, id8901=1.0/1.0/1.0, id8902=1.0/1.0/1.0, id8903=1.0/1.0/1.0, id8904=1.0/1.0/1.0, id8905=1.0/1.0/1.0, id8906=1.0/1.0/1.0, id8907=1.0/1.0/1.0, id8908=1.0/1.0/1.0, id8909=1.0/1.0/1.0, id891=1.0/1.0/1.0, id8910=1.0/1.0/1.0, id8911=1.0/1.0/1.0, id8912=1.0/1.0/1.0, id8913=1.0/1.0/1.0, id8914=1.0/1.0/1.0, id8915=1.0/1.0/1.0, id8916=1.0/1.0/1.0, id8917=1.0/1.0/1.0, id8918=1.0/1.0/1.0, id8919=1.0/1.0/1.0, id892=1.0/1.0/1.0, id8920=1.0/1.0/1.0, id8921=1.0/1.0/1.0, id8922=1.0/1.0/1.0, id8923=1.0/1.0/1.0, id8924=1.0/1.0/1.0, id8925=1.0/1.0/1.0, id8926=1.0/1.0/1.0, id8927=1.0/1.0/1.0, id8928=1.0/1.0/1.0, id8929=1.0/1.0/1.0, id893=1.0/1.0/1.0, id8930=1.0/1.0/1.0, id8931=1.0/1.0/1.0, id8932=1.0/1.0/1.0, id8933=1.0/1.0/1.0, id8934=1.0/1.0/1.0, id8935=1.0/1.0/1.0, id8936=1.0/1.0/1.0, id8937=1.0/1.0/1.0, id8938=1.0/1.0/1.0, id8939=1.0/1.0/1.0, id894=1.0/1.0/1.0, id8940=1.0/1.0/1.0, id8941=1.0/1.0/1.0, id8942=1.0/1.0/1.0, id8943=1.0/1.0/1.0, id8944=1.0/1.0/1.0, id8945=1.0/1.0/1.0, id8946=1.0/1.0/1.0, id8947=1.0/1.0/1.0, id8948=1.0/1.0/1.0, id8949=1.0/1.0/1.0, id895=1.0/1.0/1.0, id8950=1.0/1.0/1.0, id8951=1.0/1.0/1.0, id8952=1.0/1.0/1.0, id8953=1.0/1.0/1.0, id8954=1.0/1.0/1.0, id8955=1.0/1.0/1.0, id8956=1.0/1.0/1.0, id8957=1.0/1.0/1.0, id8958=1.0/1.0/1.0, id8959=1.0/1.0/1.0, id896=1.0/1.0/1.0, id8960=1.0/1.0/1.0, id8961=1.0/1.0/1.0, id8962=1.0/1.0/1.0, id8963=1.0/1.0/1.0, id8964=1.0/1.0/1.0, id8965=1.0/1.0/1.0, id8966=1.0/1.0/1.0, id8967=1.0/1.0/1.0, id8968=1.0/1.0/1.0, id8969=1.0/1.0/1.0, id897=1.0/1.0/1.0, id8970=1.0/1.0/1.0, id8971=1.0/1.0/1.0, id8972=1.0/1.0/1.0, id8973=1.0/1.0/1.0, id8974=1.0/1.0/1.0, id8975=1.0/1.0/1.0, id8976=1.0/1.0/1.0, id8977=1.0/1.0/1.0, id8978=1.0/1.0/1.0, id8979=1.0/1.0/1.0, id898=1.0/1.0/1.0, id8980=1.0/1.0/1.0, id8981=1.0/1.0/1.0, id8982=1.0/1.0/1.0, id8983=1.0/1.0/1.0, id8984=1.0/1.0/1.0, id8985=1.0/1.0/1.0, id8986=1.0/1.0/1.0, id8987=1.0/1.0/1.0, id8988=1.0/1.0/1.0, id8989=1.0/1.0/1.0, id899=1.0/1.0/1.0, id8990=1.0/1.0/1.0, id8991=1.0/1.0/1.0, id8992=1.0/1.0/1.0, id8993=1.0/1.0/1.0, id8994=1.0/1.0/1.0, id8995=1.0/1.0/1.0, id8996=1.0/1.0/1.0, id8997=1.0/1.0/1.0, id8998=1.0/1.0/1.0, id8999=1.0/1.0/1.0, id9=1.0/1.0/1.0, id90=1.0/1.0/1.0, id900=1.0/1.0/1.0, id9000=1.0/1.0/1.0, id9001=1.0/1.0/1.0, id9002=1.0/1.0/1.0, id9003=1.0/1.0/1.0, id9004=1.0/1.0/1.0, id9005=1.0/1.0/1.0, id9006=1.0/1.0/1.0, id9007=1.0/1.0/1.0, id9008=1.0/1.0/1.0, id9009=1.0/1.0/1.0, id901=1.0/1.0/1.0, id9010=1.0/1.0/1.0, id9011=1.0/1.0/1.0, id9012=1.0/1.0/1.0, id9013=1.0/1.0/1.0, id9014=1.0/1.0/1.0, id9015=1.0/1.0/1.0, id9016=1.0/1.0/1.0, id9017=1.0/1.0/1.0, id9018=1.0/1.0/1.0, id9019=1.0/1.0/1.0, id902=1.0/1.0/1.0, id9020=1.0/1.0/1.0, id9021=1.0/1.0/1.0, id9022=1.0/1.0/1.0, id9023=1.0/1.0/1.0, id9024=1.0/1.0/1.0, id9025=1.0/1.0/1.0, id9026=1.0/1.0/1.0, id9027=1.0/1.0/1.0, id9028=1.0/1.0/1.0, id9029=1.0/1.0/1.0, id903=1.0/1.0/1.0, id9030=1.0/1.0/1.0, id9031=1.0/1.0/1.0, id9032=1.0/1.0/1.0, id9033=1.0/1.0/1.0, id9034=1.0/1.0/1.0, id9035=1.0/1.0/1.0, id9036=1.0/1.0/1.0, id9037=1.0/1.0/1.0, id9038=1.0/1.0/1.0, id9039=1.0/1.0/1.0, id904=1.0/1.0/1.0, id9040=1.0/1.0/1.0, id9041=1.0/1.0/1.0, id9042=1.0/1.0/1.0, id9043=1.0/1.0/1.0, id9044=1.0/1.0/1.0, id9045=1.0/1.0/1.0, id9046=1.0/1.0/1.0, id9047=1.0/1.0/1.0, id9048=1.0/1.0/1.0, id9049=1.0/1.0/1.0, id905=1.0/1.0/1.0, id9050=1.0/1.0/1.0, id9051=1.0/1.0/1.0, id9052=1.0/1.0/1.0, id9053=1.0/1.0/1.0, id9054=1.0/1.0/1.0, id9055=1.0/1.0/1.0, id9056=1.0/1.0/1.0, id9057=1.0/1.0/1.0, id9058=1.0/1.0/1.0, id9059=1.0/1.0/1.0, id906=1.0/1.0/1.0, id9060=1.0/1.0/1.0, id9061=1.0/1.0/1.0, id9062=1.0/1.0/1.0, id9063=1.0/1.0/1.0, id9064=1.0/1.0/1.0, id9065=1.0/1.0/1.0, id9066=1.0/1.0/1.0, id9067=1.0/1.0/1.0, id9068=1.0/1.0/1.0, id9069=1.0/1.0/1.0, id907=1.0/1.0/1.0, id9070=1.0/1.0/1.0, id9071=1.0/1.0/1.0, id9072=1.0/1.0/1.0, id9073=1.0/1.0/1.0, id9074=1.0/1.0/1.0, id9075=1.0/1.0/1.0, id9076=1.0/1.0/1.0, id9077=1.0/1.0/1.0, id9078=1.0/1.0/1.0, id9079=1.0/1.0/1.0, id908=1.0/1.0/1.0, id9080=1.0/1.0/1.0, id9081=1.0/1.0/1.0, id9082=1.0/1.0/1.0, id9083=1.0/1.0/1.0, id9084=1.0/1.0/1.0, id9085=1.0/1.0/1.0, id9086=1.0/1.0/1.0, id9087=1.0/1.0/1.0, id9088=1.0/1.0/1.0, id9089=1.0/1.0/1.0, id909=1.0/1.0/1.0, id9090=1.0/1.0/1.0, id9091=1.0/1.0/1.0, id9092=1.0/1.0/1.0, id9093=1.0/1.0/1.0, id9094=1.0/1.0/1.0, id9095=1.0/1.0/1.0, id9096=1.0/1.0/1.0, id9097=1.0/1.0/1.0, id9098=1.0/1.0/1.0, id9099=1.0/1.0/1.0, id91=1.0/1.0/1.0, id910=1.0/1.0/1.0, id9100=1.0/1.0/1.0, id9101=1.0/1.0/1.0, id9102=1.0/1.0/1.0, id9103=1.0/1.0/1.0, id9104=1.0/1.0/1.0, id9105=1.0/1.0/1.0, id9106=1.0/1.0/1.0, id9107=1.0/1.0/1.0, id9108=1.0/1.0/1.0, id9109=1.0/1.0/1.0, id911=1.0/1.0/1.0, id9110=1.0/1.0/1.0, id9111=1.0/1.0/1.0, id9112=1.0/1.0/1.0, id9113=1.0/1.0/1.0, id9114=1.0/1.0/1.0, id9115=1.0/1.0/1.0, id9116=1.0/1.0/1.0, id9117=1.0/1.0/1.0, id9118=1.0/1.0/1.0, id9119=1.0/1.0/1.0, id912=1.0/1.0/1.0, id9120=1.0/1.0/1.0, id9121=1.0/1.0/1.0, id9122=1.0/1.0/1.0, id9123=1.0/1.0/1.0, id9124=1.0/1.0/1.0, id9125=1.0/1.0/1.0, id9126=1.0/1.0/1.0, id9127=1.0/1.0/1.0, id9128=1.0/1.0/1.0, id9129=1.0/1.0/1.0, id913=1.0/1.0/1.0, id9130=1.0/1.0/1.0, id9131=1.0/1.0/1.0, id9132=1.0/1.0/1.0, id9133=1.0/1.0/1.0, id9134=1.0/1.0/1.0, id9135=1.0/1.0/1.0, id9136=1.0/1.0/1.0, id9137=1.0/1.0/1.0, id9138=1.0/1.0/1.0, id9139=1.0/1.0/1.0, id914=1.0/1.0/1.0, id9140=1.0/1.0/1.0, id9141=1.0/1.0/1.0, id9142=1.0/1.0/1.0, id9143=1.0/1.0/1.0, id9144=1.0/1.0/1.0, id9145=1.0/1.0/1.0, id9146=1.0/1.0/1.0, id9147=1.0/1.0/1.0, id9148=1.0/1.0/1.0, id9149=1.0/1.0/1.0, id915=1.0/1.0/1.0, id9150=1.0/1.0/1.0, id9151=1.0/1.0/1.0, id9152=1.0/1.0/1.0, id9153=1.0/1.0/1.0, id9154=1.0/1.0/1.0, id9155=1.0/1.0/1.0, id9156=1.0/1.0/1.0, id9157=1.0/1.0/1.0, id9158=1.0/1.0/1.0, id9159=1.0/1.0/1.0, id916=1.0/1.0/1.0, id9160=1.0/1.0/1.0, id9161=1.0/1.0/1.0, id9162=1.0/1.0/1.0, id9163=1.0/1.0/1.0, id9164=1.0/1.0/1.0, id9165=1.0/1.0/1.0, id9166=1.0/1.0/1.0, id9167=1.0/1.0/1.0, id9168=1.0/1.0/1.0, id9169=1.0/1.0/1.0, id917=1.0/1.0/1.0, id9170=1.0/1.0/1.0, id9171=1.0/1.0/1.0, id9172=1.0/1.0/1.0, id9173=1.0/1.0/1.0, id9174=1.0/1.0/1.0, id9175=1.0/1.0/1.0, id9176=1.0/1.0/1.0, id9177=1.0/1.0/1.0, id9178=1.0/1.0/1.0, id9179=1.0/1.0/1.0, id918=1.0/1.0/1.0, id9180=1.0/1.0/1.0, id9181=1.0/1.0/1.0, id9182=1.0/1.0/1.0, id9183=1.0/1.0/1.0, id9184=1.0/1.0/1.0, id9185=1.0/1.0/1.0, id9186=1.0/1.0/1.0, id9187=1.0/1.0/1.0, id9188=1.0/1.0/1.0, id9189=1.0/1.0/1.0, id919=1.0/1.0/1.0, id9190=1.0/1.0/1.0, id9191=1.0/1.0/1.0, id9192=1.0/1.0/1.0, id9193=1.0/1.0/1.0, id9194=1.0/1.0/1.0, id9195=1.0/1.0/1.0, id9196=1.0/1.0/1.0, id9197=1.0/1.0/1.0, id9198=1.0/1.0/1.0, id9199=1.0/1.0/1.0, id92=1.0/1.0/1.0, id920=1.0/1.0/1.0, id9200=1.0/1.0/1.0, id9201=1.0/1.0/1.0, id9202=1.0/1.0/1.0, id9203=1.0/1.0/1.0, id9204=1.0/1.0/1.0, id9205=1.0/1.0/1.0, id9206=1.0/1.0/1.0, id9207=1.0/1.0/1.0, id9208=1.0/1.0/1.0, id9209=1.0/1.0/1.0, id921=1.0/1.0/1.0, id9210=1.0/1.0/1.0, id9211=1.0/1.0/1.0, id9212=1.0/1.0/1.0, id9213=1.0/1.0/1.0, id9214=1.0/1.0/1.0, id9215=1.0/1.0/1.0, id9216=1.0/1.0/1.0, id9217=1.0/1.0/1.0, id9218=1.0/1.0/1.0, id9219=1.0/1.0/1.0, id922=1.0/1.0/1.0, id9220=1.0/1.0/1.0, id9221=1.0/1.0/1.0, id9222=1.0/1.0/1.0, id9223=1.0/1.0/1.0, id9224=1.0/1.0/1.0, id9225=1.0/1.0/1.0, id9226=1.0/1.0/1.0, id9227=1.0/1.0/1.0, id9228=1.0/1.0/1.0, id9229=1.0/1.0/1.0, id923=1.0/1.0/1.0, id9230=1.0/1.0/1.0, id9231=1.0/1.0/1.0, id9232=1.0/1.0/1.0, id9233=1.0/1.0/1.0, id9234=1.0/1.0/1.0, id9235=1.0/1.0/1.0, id9236=1.0/1.0/1.0, id9237=1.0/1.0/1.0, id9238=1.0/1.0/1.0, id9239=1.0/1.0/1.0, id924=1.0/1.0/1.0, id9240=1.0/1.0/1.0, id9241=1.0/1.0/1.0, id9242=1.0/1.0/1.0, id9243=1.0/1.0/1.0, id9244=1.0/1.0/1.0, id9245=1.0/1.0/1.0, id9246=1.0/1.0/1.0, id9247=1.0/1.0/1.0, id9248=1.0/1.0/1.0, id9249=1.0/1.0/1.0, id925=1.0/1.0/1.0, id9250=1.0/1.0/1.0, id9251=1.0/1.0/1.0, id9252=1.0/1.0/1.0, id9253=1.0/1.0/1.0, id9254=1.0/1.0/1.0, id9255=1.0/1.0/1.0, id9256=1.0/1.0/1.0, id9257=1.0/1.0/1.0, id9258=1.0/1.0/1.0, id9259=1.0/1.0/1.0, id926=1.0/1.0/1.0, id9260=1.0/1.0/1.0, id9261=1.0/1.0/1.0, id9262=1.0/1.0/1.0, id9263=1.0/1.0/1.0, id9264=1.0/1.0/1.0, id9265=1.0/1.0/1.0, id9266=1.0/1.0/1.0, id9267=1.0/1.0/1.0, id9268=1.0/1.0/1.0, id9269=1.0/1.0/1.0, id927=1.0/1.0/1.0, id9270=1.0/1.0/1.0, id9271=1.0/1.0/1.0, id9272=1.0/1.0/1.0, id9273=1.0/1.0/1.0, id9274=1.0/1.0/1.0, id9275=1.0/1.0/1.0, id9276=1.0/1.0/1.0, id9277=1.0/1.0/1.0, id9278=1.0/1.0/1.0, id9279=1.0/1.0/1.0, id928=1.0/1.0/1.0, id9280=1.0/1.0/1.0, id9281=1.0/1.0/1.0, id9282=1.0/1.0/1.0, id9283=1.0/1.0/1.0, id9284=1.0/1.0/1.0, id9285=1.0/1.0/1.0, id9286=1.0/1.0/1.0, id9287=1.0/1.0/1.0, id9288=1.0/1.0/1.0, id9289=1.0/1.0/1.0, id929=1.0/1.0/1.0, id9290=1.0/1.0/1.0, id9291=1.0/1.0/1.0, id9292=1.0/1.0/1.0, id9293=1.0/1.0/1.0, id9294=1.0/1.0/1.0, id9295=1.0/1.0/1.0, id9296=1.0/1.0/1.0, id9297=1.0/1.0/1.0, id9298=1.0/1.0/1.0, id9299=1.0/1.0/1.0, id93=1.0/1.0/1.0, id930=1.0/1.0/1.0, id9300=1.0/1.0/1.0, id9301=1.0/1.0/1.0, id9302=1.0/1.0/1.0, id9303=1.0/1.0/1.0, id9304=1.0/1.0/1.0, id9305=1.0/1.0/1.0, id9306=1.0/1.0/1.0, id9307=1.0/1.0/1.0, id9308=1.0/1.0/1.0, id9309=1.0/1.0/1.0, id931=1.0/1.0/1.0, id9310=1.0/1.0/1.0, id9311=1.0/1.0/1.0, id9312=1.0/1.0/1.0, id9313=1.0/1.0/1.0, id9314=1.0/1.0/1.0, id9315=1.0/1.0/1.0, id9316=1.0/1.0/1.0, id9317=1.0/1.0/1.0, id9318=1.0/1.0/1.0, id9319=1.0/1.0/1.0, id932=1.0/1.0/1.0, id9320=1.0/1.0/1.0, id9321=1.0/1.0/1.0, id9322=1.0/1.0/1.0, id9323=1.0/1.0/1.0, id9324=1.0/1.0/1.0, id9325=1.0/1.0/1.0, id9326=1.0/1.0/1.0, id9327=1.0/1.0/1.0, id9328=1.0/1.0/1.0, id9329=1.0/1.0/1.0, id933=1.0/1.0/1.0, id9330=1.0/1.0/1.0, id9331=1.0/1.0/1.0, id9332=1.0/1.0/1.0, id9333=1.0/1.0/1.0, id9334=1.0/1.0/1.0, id9335=1.0/1.0/1.0, id9336=1.0/1.0/1.0, id9337=1.0/1.0/1.0, id9338=1.0/1.0/1.0, id9339=1.0/1.0/1.0, id934=1.0/1.0/1.0, id9340=1.0/1.0/1.0, id9341=1.0/1.0/1.0, id9342=1.0/1.0/1.0, id9343=1.0/1.0/1.0, id9344=1.0/1.0/1.0, id9345=1.0/1.0/1.0, id9346=1.0/1.0/1.0, id9347=1.0/1.0/1.0, id9348=1.0/1.0/1.0, id9349=1.0/1.0/1.0, id935=1.0/1.0/1.0, id9350=1.0/1.0/1.0, id9351=1.0/1.0/1.0, id9352=1.0/1.0/1.0, id9353=1.0/1.0/1.0, id9354=1.0/1.0/1.0, id9355=1.0/1.0/1.0, id9356=1.0/1.0/1.0, id9357=1.0/1.0/1.0, id9358=1.0/1.0/1.0, id9359=1.0/1.0/1.0, id936=1.0/1.0/1.0, id9360=1.0/1.0/1.0, id9361=1.0/1.0/1.0, id9362=1.0/1.0/1.0, id9363=1.0/1.0/1.0, id9364=1.0/1.0/1.0, id9365=1.0/1.0/1.0, id9366=1.0/1.0/1.0, id9367=1.0/1.0/1.0, id9368=1.0/1.0/1.0, id9369=1.0/1.0/1.0, id937=1.0/1.0/1.0, id9370=1.0/1.0/1.0, id9371=1.0/1.0/1.0, id9372=1.0/1.0/1.0, id9373=1.0/1.0/1.0, id9374=1.0/1.0/1.0, id9375=1.0/1.0/1.0, id9376=1.0/1.0/1.0, id9377=1.0/1.0/1.0, id9378=1.0/1.0/1.0, id9379=1.0/1.0/1.0, id938=1.0/1.0/1.0, id9380=1.0/1.0/1.0, id9381=1.0/1.0/1.0, id9382=1.0/1.0/1.0, id9383=1.0/1.0/1.0, id9384=1.0/1.0/1.0, id9385=1.0/1.0/1.0, id9386=1.0/1.0/1.0, id9387=1.0/1.0/1.0, id9388=1.0/1.0/1.0, id9389=1.0/1.0/1.0, id939=1.0/1.0/1.0, id9390=1.0/1.0/1.0, id9391=1.0/1.0/1.0, id9392=1.0/1.0/1.0, id9393=1.0/1.0/1.0, id9394=1.0/1.0/1.0, id9395=1.0/1.0/1.0, id9396=1.0/1.0/1.0, id9397=1.0/1.0/1.0, id9398=1.0/1.0/1.0, id9399=1.0/1.0/1.0, id94=1.0/1.0/1.0, id940=1.0/1.0/1.0, id9400=1.0/1.0/1.0, id9401=1.0/1.0/1.0, id9402=1.0/1.0/1.0, id9403=1.0/1.0/1.0, id9404=1.0/1.0/1.0, id9405=1.0/1.0/1.0, id9406=1.0/1.0/1.0, id9407=1.0/1.0/1.0, id9408=1.0/1.0/1.0, id9409=1.0/1.0/1.0, id941=1.0/1.0/1.0, id9410=1.0/1.0/1.0, id9411=1.0/1.0/1.0, id9412=1.0/1.0/1.0, id9413=1.0/1.0/1.0, id9414=1.0/1.0/1.0, id9415=1.0/1.0/1.0, id9416=1.0/1.0/1.0, id9417=1.0/1.0/1.0, id9418=1.0/1.0/1.0, id9419=1.0/1.0/1.0, id942=1.0/1.0/1.0, id9420=1.0/1.0/1.0, id9421=1.0/1.0/1.0, id9422=1.0/1.0/1.0, id9423=1.0/1.0/1.0, id9424=1.0/1.0/1.0, id9425=1.0/1.0/1.0, id9426=1.0/1.0/1.0, id9427=1.0/1.0/1.0, id9428=1.0/1.0/1.0, id9429=1.0/1.0/1.0, id943=1.0/1.0/1.0, id9430=1.0/1.0/1.0, id9431=1.0/1.0/1.0, id9432=1.0/1.0/1.0, id9433=1.0/1.0/1.0, id9434=1.0/1.0/1.0, id9435=1.0/1.0/1.0, id9436=1.0/1.0/1.0, id9437=1.0/1.0/1.0, id9438=1.0/1.0/1.0, id9439=1.0/1.0/1.0, id944=1.0/1.0/1.0, id9440=1.0/1.0/1.0, id9441=1.0/1.0/1.0, id9442=1.0/1.0/1.0, id9443=1.0/1.0/1.0, id9444=1.0/1.0/1.0, id9445=1.0/1.0/1.0, id9446=1.0/1.0/1.0, id9447=1.0/1.0/1.0, id9448=1.0/1.0/1.0, id9449=1.0/1.0/1.0, id945=1.0/1.0/1.0, id9450=1.0/1.0/1.0, id9451=1.0/1.0/1.0, id9452=1.0/1.0/1.0, id9453=1.0/1.0/1.0, id9454=1.0/1.0/1.0, id9455=1.0/1.0/1.0, id9456=1.0/1.0/1.0, id9457=1.0/1.0/1.0, id9458=1.0/1.0/1.0, id9459=1.0/1.0/1.0, id946=1.0/1.0/1.0, id9460=1.0/1.0/1.0, id9461=1.0/1.0/1.0, id9462=1.0/1.0/1.0, id9463=1.0/1.0/1.0, id9464=1.0/1.0/1.0, id9465=1.0/1.0/1.0, id9466=1.0/1.0/1.0, id9467=1.0/1.0/1.0, id9468=1.0/1.0/1.0, id9469=1.0/1.0/1.0, id947=1.0/1.0/1.0, id9470=1.0/1.0/1.0, id9471=1.0/1.0/1.0, id9472=1.0/1.0/1.0, id9473=1.0/1.0/1.0, id9474=1.0/1.0/1.0, id9475=1.0/1.0/1.0, id9476=1.0/1.0/1.0, id9477=1.0/1.0/1.0, id9478=1.0/1.0/1.0, id9479=1.0/1.0/1.0, id948=1.0/1.0/1.0, id9480=1.0/1.0/1.0, id9481=1.0/1.0/1.0, id9482=1.0/1.0/1.0, id9483=1.0/1.0/1.0, id9484=1.0/1.0/1.0, id9485=1.0/1.0/1.0, id9486=1.0/1.0/1.0, id9487=1.0/1.0/1.0, id9488=1.0/1.0/1.0, id9489=1.0/1.0/1.0, id949=1.0/1.0/1.0, id9490=1.0/1.0/1.0, id9491=1.0/1.0/1.0, id9492=1.0/1.0/1.0, id9493=1.0/1.0/1.0, id9494=1.0/1.0/1.0, id9495=1.0/1.0/1.0, id9496=1.0/1.0/1.0, id9497=1.0/1.0/1.0, id9498=1.0/1.0/1.0, id9499=1.0/1.0/1.0, id95=1.0/1.0/1.0, id950=1.0/1.0/1.0, id9500=1.0/1.0/1.0, id9501=1.0/1.0/1.0, id9502=1.0/1.0/1.0, id9503=1.0/1.0/1.0, id9504=1.0/1.0/1.0, id9505=1.0/1.0/1.0, id9506=1.0/1.0/1.0, id9507=1.0/1.0/1.0, id9508=1.0/1.0/1.0, id9509=1.0/1.0/1.0, id951=1.0/1.0/1.0, id9510=1.0/1.0/1.0, id9511=1.0/1.0/1.0, id9512=1.0/1.0/1.0, id9513=1.0/1.0/1.0, id9514=1.0/1.0/1.0, id9515=1.0/1.0/1.0, id9516=1.0/1.0/1.0, id9517=1.0/1.0/1.0, id9518=1.0/1.0/1.0, id9519=1.0/1.0/1.0, id952=1.0/1.0/1.0, id9520=1.0/1.0/1.0, id9521=1.0/1.0/1.0, id9522=1.0/1.0/1.0, id9523=1.0/1.0/1.0, id9524=1.0/1.0/1.0, id9525=1.0/1.0/1.0, id9526=1.0/1.0/1.0, id9527=1.0/1.0/1.0, id9528=1.0/1.0/1.0, id9529=1.0/1.0/1.0, id953=1.0/1.0/1.0, id9530=1.0/1.0/1.0, id9531=1.0/1.0/1.0, id9532=1.0/1.0/1.0, id9533=1.0/1.0/1.0, id9534=1.0/1.0/1.0, id9535=1.0/1.0/1.0, id9536=1.0/1.0/1.0, id9537=1.0/1.0/1.0, id9538=1.0/1.0/1.0, id9539=1.0/1.0/1.0, id954=1.0/1.0/1.0, id9540=1.0/1.0/1.0, id9541=1.0/1.0/1.0, id9542=1.0/1.0/1.0, id9543=1.0/1.0/1.0, id9544=1.0/1.0/1.0, id9545=1.0/1.0/1.0, id9546=1.0/1.0/1.0, id9547=1.0/1.0/1.0, id9548=1.0/1.0/1.0, id9549=1.0/1.0/1.0, id955=1.0/1.0/1.0, id9550=1.0/1.0/1.0, id9551=1.0/1.0/1.0, id9552=1.0/1.0/1.0, id9553=1.0/1.0/1.0, id9554=1.0/1.0/1.0, id9555=1.0/1.0/1.0, id9556=1.0/1.0/1.0, id9557=1.0/1.0/1.0, id9558=1.0/1.0/1.0, id9559=1.0/1.0/1.0, id956=1.0/1.0/1.0, id9560=1.0/1.0/1.0, id9561=1.0/1.0/1.0, id9562=1.0/1.0/1.0, id9563=1.0/1.0/1.0, id9564=1.0/1.0/1.0, id9565=1.0/1.0/1.0, id9566=1.0/1.0/1.0, id9567=1.0/1.0/1.0, id9568=1.0/1.0/1.0, id9569=1.0/1.0/1.0, id957=1.0/1.0/1.0, id9570=1.0/1.0/1.0, id9571=1.0/1.0/1.0, id9572=1.0/1.0/1.0, id9573=1.0/1.0/1.0, id9574=1.0/1.0/1.0, id9575=1.0/1.0/1.0, id9576=1.0/1.0/1.0, id9577=1.0/1.0/1.0, id9578=1.0/1.0/1.0, id9579=1.0/1.0/1.0, id958=1.0/1.0/1.0, id9580=1.0/1.0/1.0, id9581=1.0/1.0/1.0, id9582=1.0/1.0/1.0, id9583=1.0/1.0/1.0, id9584=1.0/1.0/1.0, id9585=1.0/1.0/1.0, id9586=1.0/1.0/1.0, id9587=1.0/1.0/1.0, id9588=1.0/1.0/1.0, id9589=1.0/1.0/1.0, id959=1.0/1.0/1.0, id9590=1.0/1.0/1.0, id9591=1.0/1.0/1.0, id9592=1.0/1.0/1.0, id9593=1.0/1.0/1.0, id9594=1.0/1.0/1.0, id9595=1.0/1.0/1.0, id9596=1.0/1.0/1.0, id9597=1.0/1.0/1.0, id9598=1.0/1.0/1.0, id9599=1.0/1.0/1.0, id96=1.0/1.0/1.0, id960=1.0/1.0/1.0, id9600=1.0/1.0/1.0, id9601=1.0/1.0/1.0, id9602=1.0/1.0/1.0, id9603=1.0/1.0/1.0, id9604=1.0/1.0/1.0, id9605=1.0/1.0/1.0, id9606=1.0/1.0/1.0, id9607=1.0/1.0/1.0, id9608=1.0/1.0/1.0, id9609=1.0/1.0/1.0, id961=1.0/1.0/1.0, id9610=1.0/1.0/1.0, id9611=1.0/1.0/1.0, id9612=1.0/1.0/1.0, id9613=1.0/1.0/1.0, id9614=1.0/1.0/1.0, id9615=1.0/1.0/1.0, id9616=1.0/1.0/1.0, id9617=1.0/1.0/1.0, id9618=1.0/1.0/1.0, id9619=1.0/1.0/1.0, id962=1.0/1.0/1.0, id9620=1.0/1.0/1.0, id9621=1.0/1.0/1.0, id9622=1.0/1.0/1.0, id9623=1.0/1.0/1.0, id9624=1.0/1.0/1.0, id9625=1.0/1.0/1.0, id9626=1.0/1.0/1.0, id9627=1.0/1.0/1.0, id9628=1.0/1.0/1.0, id9629=1.0/1.0/1.0, id963=1.0/1.0/1.0, id9630=1.0/1.0/1.0, id9631=1.0/1.0/1.0, id9632=1.0/1.0/1.0, id9633=1.0/1.0/1.0, id9634=1.0/1.0/1.0, id9635=1.0/1.0/1.0, id9636=1.0/1.0/1.0, id9637=1.0/1.0/1.0, id9638=1.0/1.0/1.0, id9639=1.0/1.0/1.0, id964=1.0/1.0/1.0, id9640=1.0/1.0/1.0, id9641=1.0/1.0/1.0, id9642=1.0/1.0/1.0, id9643=1.0/1.0/1.0, id9644=1.0/1.0/1.0, id9645=1.0/1.0/1.0, id9646=1.0/1.0/1.0, id9647=1.0/1.0/1.0, id9648=1.0/1.0/1.0, id9649=1.0/1.0/1.0, id965=1.0/1.0/1.0, id9650=1.0/1.0/1.0, id9651=1.0/1.0/1.0, id9652=1.0/1.0/1.0, id9653=1.0/1.0/1.0, id9654=1.0/1.0/1.0, id9655=1.0/1.0/1.0, id9656=1.0/1.0/1.0, id9657=1.0/1.0/1.0, id9658=1.0/1.0/1.0, id9659=1.0/1.0/1.0, id966=1.0/1.0/1.0, id9660=1.0/1.0/1.0, id9661=1.0/1.0/1.0, id9662=1.0/1.0/1.0, id9663=1.0/1.0/1.0, id9664=1.0/1.0/1.0, id9665=1.0/1.0/1.0, id9666=1.0/1.0/1.0, id9667=1.0/1.0/1.0, id9668=1.0/1.0/1.0, id9669=1.0/1.0/1.0, id967=1.0/1.0/1.0, id9670=1.0/1.0/1.0, id9671=1.0/1.0/1.0, id9672=1.0/1.0/1.0, id9673=1.0/1.0/1.0, id9674=1.0/1.0/1.0, id9675=1.0/1.0/1.0, id9676=1.0/1.0/1.0, id9677=1.0/1.0/1.0, id9678=1.0/1.0/1.0, id9679=1.0/1.0/1.0, id968=1.0/1.0/1.0, id9680=1.0/1.0/1.0, id9681=1.0/1.0/1.0, id9682=1.0/1.0/1.0, id9683=1.0/1.0/1.0, id9684=1.0/1.0/1.0, id9685=1.0/1.0/1.0, id9686=1.0/1.0/1.0, id9687=1.0/1.0/1.0, id9688=1.0/1.0/1.0, id9689=1.0/1.0/1.0, id969=1.0/1.0/1.0, id9690=1.0/1.0/1.0, id9691=1.0/1.0/1.0, id9692=1.0/1.0/1.0, id9693=1.0/1.0/1.0, id9694=1.0/1.0/1.0, id9695=1.0/1.0/1.0, id9696=1.0/1.0/1.0, id9697=1.0/1.0/1.0, id9698=1.0/1.0/1.0, id9699=1.0/1.0/1.0, id97=1.0/1.0/1.0, id970=1.0/1.0/1.0, id9700=1.0/1.0/1.0, id9701=1.0/1.0/1.0, id9702=1.0/1.0/1.0, id9703=1.0/1.0/1.0, id9704=1.0/1.0/1.0, id9705=1.0/1.0/1.0, id9706=1.0/1.0/1.0, id9707=1.0/1.0/1.0, id9708=1.0/1.0/1.0, id9709=1.0/1.0/1.0, id971=1.0/1.0/1.0, id9710=1.0/1.0/1.0, id9711=1.0/1.0/1.0, id9712=1.0/1.0/1.0, id9713=1.0/1.0/1.0, id9714=1.0/1.0/1.0, id9715=1.0/1.0/1.0, id9716=1.0/1.0/1.0, id9717=1.0/1.0/1.0, id9718=1.0/1.0/1.0, id9719=1.0/1.0/1.0, id972=1.0/1.0/1.0, id9720=1.0/1.0/1.0, id9721=1.0/1.0/1.0, id9722=1.0/1.0/1.0, id9723=1.0/1.0/1.0, id9724=1.0/1.0/1.0, id9725=1.0/1.0/1.0, id9726=1.0/1.0/1.0, id9727=1.0/1.0/1.0, id9728=1.0/1.0/1.0, id9729=1.0/1.0/1.0, id973=1.0/1.0/1.0, id9730=1.0/1.0/1.0, id9731=1.0/1.0/1.0, id9732=1.0/1.0/1.0, id9733=1.0/1.0/1.0, id9734=1.0/1.0/1.0, id9735=1.0/1.0/1.0, id9736=1.0/1.0/1.0, id9737=1.0/1.0/1.0, id9738=1.0/1.0/1.0, id9739=1.0/1.0/1.0, id974=1.0/1.0/1.0, id9740=1.0/1.0/1.0, id9741=1.0/1.0/1.0, id9742=1.0/1.0/1.0, id9743=1.0/1.0/1.0, id9744=1.0/1.0/1.0, id9745=1.0/1.0/1.0, id9746=1.0/1.0/1.0, id9747=1.0/1.0/1.0, id9748=1.0/1.0/1.0, id9749=1.0/1.0/1.0, id975=1.0/1.0/1.0, id9750=1.0/1.0/1.0, id9751=1.0/1.0/1.0, id9752=1.0/1.0/1.0, id9753=1.0/1.0/1.0, id9754=1.0/1.0/1.0, id9755=1.0/1.0/1.0, id9756=1.0/1.0/1.0, id9757=1.0/1.0/1.0, id9758=1.0/1.0/1.0, id9759=1.0/1.0/1.0, id976=1.0/1.0/1.0, id9760=1.0/1.0/1.0, id9761=1.0/1.0/1.0, id9762=1.0/1.0/1.0, id9763=1.0/1.0/1.0, id9764=1.0/1.0/1.0, id9765=1.0/1.0/1.0, id9766=1.0/1.0/1.0, id9767=1.0/1.0/1.0, id9768=1.0/1.0/1.0, id9769=1.0/1.0/1.0, id977=1.0/1.0/1.0, id9770=1.0/1.0/1.0, id9771=1.0/1.0/1.0, id9772=1.0/1.0/1.0, id9773=1.0/1.0/1.0, id9774=1.0/1.0/1.0, id9775=1.0/1.0/1.0, id9776=1.0/1.0/1.0, id9777=1.0/1.0/1.0, id9778=1.0/1.0/1.0, id9779=1.0/1.0/1.0, id978=1.0/1.0/1.0, id9780=1.0/1.0/1.0, id9781=1.0/1.0/1.0, id9782=1.0/1.0/1.0, id9783=1.0/1.0/1.0, id9784=1.0/1.0/1.0, id9785=1.0/1.0/1.0, id9786=1.0/1.0/1.0, id9787=1.0/1.0/1.0, id9788=1.0/1.0/1.0, id9789=1.0/1.0/1.0, id979=1.0/1.0/1.0, id9790=1.0/1.0/1.0, id9791=1.0/1.0/1.0, id9792=1.0/1.0/1.0, id9793=1.0/1.0/1.0, id9794=1.0/1.0/1.0, id9795=1.0/1.0/1.0, id9796=1.0/1.0/1.0, id9797=1.0/1.0/1.0, id9798=1.0/1.0/1.0, id9799=1.0/1.0/1.0, id98=1.0/1.0/1.0, id980=1.0/1.0/1.0, id9800=1.0/1.0/1.0, id9801=1.0/1.0/1.0, id9802=1.0/1.0/1.0, id9803=1.0/1.0/1.0, id9804=1.0/1.0/1.0, id9805=1.0/1.0/1.0, id9806=1.0/1.0/1.0, id9807=1.0/1.0/1.0, id9808=1.0/1.0/1.0, id9809=1.0/1.0/1.0, id981=1.0/1.0/1.0, id9810=1.0/1.0/1.0, id9811=1.0/1.0/1.0, id9812=1.0/1.0/1.0, id9813=1.0/1.0/1.0, id9814=1.0/1.0/1.0, id9815=1.0/1.0/1.0, id9816=1.0/1.0/1.0, id9817=1.0/1.0/1.0, id9818=1.0/1.0/1.0, id9819=1.0/1.0/1.0, id982=1.0/1.0/1.0, id9820=1.0/1.0/1.0, id9821=1.0/1.0/1.0, id9822=1.0/1.0/1.0, id9823=1.0/1.0/1.0, id9824=1.0/1.0/1.0, id9825=1.0/1.0/1.0, id9826=1.0/1.0/1.0, id9827=1.0/1.0/1.0, id9828=1.0/1.0/1.0, id9829=1.0/1.0/1.0, id983=1.0/1.0/1.0, id9830=1.0/1.0/1.0, id9831=1.0/1.0/1.0, id9832=1.0/1.0/1.0, id9833=1.0/1.0/1.0, id9834=1.0/1.0/1.0, id9835=1.0/1.0/1.0, id9836=1.0/1.0/1.0, id9837=1.0/1.0/1.0, id9838=1.0/1.0/1.0, id9839=1.0/1.0/1.0, id984=1.0/1.0/1.0, id9840=1.0/1.0/1.0, id9841=1.0/1.0/1.0, id9842=1.0/1.0/1.0, id9843=1.0/1.0/1.0, id9844=1.0/1.0/1.0, id9845=1.0/1.0/1.0, id9846=1.0/1.0/1.0, id9847=1.0/1.0/1.0, id9848=1.0/1.0/1.0, id9849=1.0/1.0/1.0, id985=1.0/1.0/1.0, id9850=1.0/1.0/1.0, id9851=1.0/1.0/1.0, id9852=1.0/1.0/1.0, id9853=1.0/1.0/1.0, id9854=1.0/1.0/1.0, id9855=1.0/1.0/1.0, id9856=1.0/1.0/1.0, id9857=1.0/1.0/1.0, id9858=1.0/1.0/1.0, id9859=1.0/1.0/1.0, id986=1.0/1.0/1.0, id9860=1.0/1.0/1.0, id9861=1.0/1.0/1.0, id9862=1.0/1.0/1.0, id9863=1.0/1.0/1.0, id9864=1.0/1.0/1.0, id9865=1.0/1.0/1.0, id9866=1.0/1.0/1.0, id9867=1.0/1.0/1.0, id9868=1.0/1.0/1.0, id9869=1.0/1.0/1.0, id987=1.0/1.0/1.0, id9870=1.0/1.0/1.0, id9871=1.0/1.0/1.0, id9872=1.0/1.0/1.0, id9873=1.0/1.0/1.0, id9874=1.0/1.0/1.0, id9875=1.0/1.0/1.0, id9876=1.0/1.0/1.0, id9877=1.0/1.0/1.0, id9878=1.0/1.0/1.0, id9879=1.0/1.0/1.0, id988=1.0/1.0/1.0, id9880=1.0/1.0/1.0, id9881=1.0/1.0/1.0, id9882=1.0/1.0/1.0, id9883=1.0/1.0/1.0, id9884=1.0/1.0/1.0, id9885=1.0/1.0/1.0, id9886=1.0/1.0/1.0, id9887=1.0/1.0/1.0, id9888=1.0/1.0/1.0, id9889=1.0/1.0/1.0, id989=1.0/1.0/1.0, id9890=1.0/1.0/1.0, id9891=1.0/1.0/1.0, id9892=1.0/1.0/1.0, id9893=1.0/1.0/1.0, id9894=1.0/1.0/1.0, id9895=1.0/1.0/1.0, id9896=1.0/1.0/1.0, id9897=1.0/1.0/1.0, id9898=1.0/1.0/1.0, id9899=1.0/1.0/1.0, id99=1.0/1.0/1.0, id990=1.0/1.0/1.0, id9900=1.0/1.0/1.0, id9901=1.0/1.0/1.0, id9902=1.0/1.0/1.0, id9903=1.0/1.0/1.0, id9904=1.0/1.0/1.0, id9905=1.0/1.0/1.0, id9906=1.0/1.0/1.0, id9907=1.0/1.0/1.0, id9908=1.0/1.0/1.0, id9909=1.0/1.0/1.0, id991=1.0/1.0/1.0, id9910=1.0/1.0/1.0, id9911=1.0/1.0/1.0, id9912=1.0/1.0/1.0, id9913=1.0/1.0/1.0, id9914=1.0/1.0/1.0, id9915=1.0/1.0/1.0, id9916=1.0/1.0/1.0, id9917=1.0/1.0/1.0, id9918=1.0/1.0/1.0, id9919=1.0/1.0/1.0, id992=1.0/1.0/1.0, id9920=1.0/1.0/1.0, id9921=1.0/1.0/1.0, id9922=1.0/1.0/1.0, id9923=1.0/1.0/1.0, id9924=1.0/1.0/1.0, id9925=1.0/1.0/1.0, id9926=1.0/1.0/1.0, id9927=1.0/1.0/1.0, id9928=1.0/1.0/1.0, id9929=1.0/1.0/1.0, id993=1.0/1.0/1.0, id9930=1.0/1.0/1.0, id9931=1.0/1.0/1.0, id9932=1.0/1.0/1.0, id9933=1.0/1.0/1.0, id9934=1.0/1.0/1.0, id9935=1.0/1.0/1.0, id9936=1.0/1.0/1.0, id9937=1.0/1.0/1.0, id9938=1.0/1.0/1.0, id9939=1.0/1.0/1.0, id994=1.0/1.0/1.0, id9940=1.0/1.0/1.0, id9941=1.0/1.0/1.0, id9942=1.0/1.0/1.0, id9943=1.0/1.0/1.0, id9944=1.0/1.0/1.0, id9945=1.0/1.0/1.0, id9946=1.0/1.0/1.0, id9947=1.0/1.0/1.0, id9948=1.0/1.0/1.0, id9949=1.0/1.0/1.0, id995=1.0/1.0/1.0, id9950=1.0/1.0/1.0, id9951=1.0/1.0/1.0, id9952=1.0/1.0/1.0, id9953=1.0/1.0/1.0, id9954=1.0/1.0/1.0, id9955=1.0/1.0/1.0, id9956=1.0/1.0/1.0, id9957=1.0/1.0/1.0, id9958=1.0/1.0/1.0, id9959=1.0/1.0/1.0, id996=1.0/1.0/1.0, id9960=1.0/1.0/1.0, id9961=1.0/1.0/1.0, id9962=1.0/1.0/1.0, id9963=1.0/1.0/1.0, id9964=1.0/1.0/1.0, id9965=1.0/1.0/1.0, id9966=1.0/1.0/1.0, id9967=1.0/1.0/1.0, id9968=1.0/1.0/1.0, id9969=1.0/1.0/1.0, id997=1.0/1.0/1.0, id9970=1.0/1.0/1.0, id9971=1.0/1.0/1.0, id9972=1.0/1.0/1.0, id9973=1.0/1.0/1.0, id9974=1.0/1.0/1.0, id9975=1.0/1.0/1.0, id9976=1.0/1.0/1.0, id9977=1.0/1.0/1.0, id9978=1.0/1.0/1.0, id9979=1.0/1.0/1.0, id998=1.0/1.0/1.0, id9980=1.0/1.0/1.0, id9981=1.0/1.0/1.0, id9982=1.0/1.0/1.0, id9983=1.0/1.0/1.0, id9984=1.0/1.0/1.0, id9985=1.0/1.0/1.0, id9986=1.0/1.0/1.0, id9987=1.0/1.0/1.0, id9988=1.0/1.0/1.0, id9989=1.0/1.0/1.0, id999=1.0/1.0/1.0, id9990=1.0/1.0/1.0, id9991=1.0/1.0/1.0, id9992=1.0/1.0/1.0, id9993=1.0/1.0/1.0, id9994=1.0/1.0/1.0, id9995=1.0/1.0/1.0, id9996=1.0/1.0/1.0, id9997=1.0/1.0/1.0, id9998=1.0/1.0/1.0, id9999=1.0/1.0/1.0} ================================================ FILE: src/test/resources/samples/measurements-10000-unique-keys.txt ================================================ id1;1.0 id2;1.0 id3;1.0 id4;1.0 id5;1.0 id6;1.0 id7;1.0 id8;1.0 id9;1.0 id10;1.0 id11;1.0 id12;1.0 id13;1.0 id14;1.0 id15;1.0 id16;1.0 id17;1.0 id18;1.0 id19;1.0 id20;1.0 id21;1.0 id22;1.0 id23;1.0 id24;1.0 id25;1.0 id26;1.0 id27;1.0 id28;1.0 id29;1.0 id30;1.0 id31;1.0 id32;1.0 id33;1.0 id34;1.0 id35;1.0 id36;1.0 id37;1.0 id38;1.0 id39;1.0 id40;1.0 id41;1.0 id42;1.0 id43;1.0 id44;1.0 id45;1.0 id46;1.0 id47;1.0 id48;1.0 id49;1.0 id50;1.0 id51;1.0 id52;1.0 id53;1.0 id54;1.0 id55;1.0 id56;1.0 id57;1.0 id58;1.0 id59;1.0 id60;1.0 id61;1.0 id62;1.0 id63;1.0 id64;1.0 id65;1.0 id66;1.0 id67;1.0 id68;1.0 id69;1.0 id70;1.0 id71;1.0 id72;1.0 id73;1.0 id74;1.0 id75;1.0 id76;1.0 id77;1.0 id78;1.0 id79;1.0 id80;1.0 id81;1.0 id82;1.0 id83;1.0 id84;1.0 id85;1.0 id86;1.0 id87;1.0 id88;1.0 id89;1.0 id90;1.0 id91;1.0 id92;1.0 id93;1.0 id94;1.0 id95;1.0 id96;1.0 id97;1.0 id98;1.0 id99;1.0 id100;1.0 id101;1.0 id102;1.0 id103;1.0 id104;1.0 id105;1.0 id106;1.0 id107;1.0 id108;1.0 id109;1.0 id110;1.0 id111;1.0 id112;1.0 id113;1.0 id114;1.0 id115;1.0 id116;1.0 id117;1.0 id118;1.0 id119;1.0 id120;1.0 id121;1.0 id122;1.0 id123;1.0 id124;1.0 id125;1.0 id126;1.0 id127;1.0 id128;1.0 id129;1.0 id130;1.0 id131;1.0 id132;1.0 id133;1.0 id134;1.0 id135;1.0 id136;1.0 id137;1.0 id138;1.0 id139;1.0 id140;1.0 id141;1.0 id142;1.0 id143;1.0 id144;1.0 id145;1.0 id146;1.0 id147;1.0 id148;1.0 id149;1.0 id150;1.0 id151;1.0 id152;1.0 id153;1.0 id154;1.0 id155;1.0 id156;1.0 id157;1.0 id158;1.0 id159;1.0 id160;1.0 id161;1.0 id162;1.0 id163;1.0 id164;1.0 id165;1.0 id166;1.0 id167;1.0 id168;1.0 id169;1.0 id170;1.0 id171;1.0 id172;1.0 id173;1.0 id174;1.0 id175;1.0 id176;1.0 id177;1.0 id178;1.0 id179;1.0 id180;1.0 id181;1.0 id182;1.0 id183;1.0 id184;1.0 id185;1.0 id186;1.0 id187;1.0 id188;1.0 id189;1.0 id190;1.0 id191;1.0 id192;1.0 id193;1.0 id194;1.0 id195;1.0 id196;1.0 id197;1.0 id198;1.0 id199;1.0 id200;1.0 id201;1.0 id202;1.0 id203;1.0 id204;1.0 id205;1.0 id206;1.0 id207;1.0 id208;1.0 id209;1.0 id210;1.0 id211;1.0 id212;1.0 id213;1.0 id214;1.0 id215;1.0 id216;1.0 id217;1.0 id218;1.0 id219;1.0 id220;1.0 id221;1.0 id222;1.0 id223;1.0 id224;1.0 id225;1.0 id226;1.0 id227;1.0 id228;1.0 id229;1.0 id230;1.0 id231;1.0 id232;1.0 id233;1.0 id234;1.0 id235;1.0 id236;1.0 id237;1.0 id238;1.0 id239;1.0 id240;1.0 id241;1.0 id242;1.0 id243;1.0 id244;1.0 id245;1.0 id246;1.0 id247;1.0 id248;1.0 id249;1.0 id250;1.0 id251;1.0 id252;1.0 id253;1.0 id254;1.0 id255;1.0 id256;1.0 id257;1.0 id258;1.0 id259;1.0 id260;1.0 id261;1.0 id262;1.0 id263;1.0 id264;1.0 id265;1.0 id266;1.0 id267;1.0 id268;1.0 id269;1.0 id270;1.0 id271;1.0 id272;1.0 id273;1.0 id274;1.0 id275;1.0 id276;1.0 id277;1.0 id278;1.0 id279;1.0 id280;1.0 id281;1.0 id282;1.0 id283;1.0 id284;1.0 id285;1.0 id286;1.0 id287;1.0 id288;1.0 id289;1.0 id290;1.0 id291;1.0 id292;1.0 id293;1.0 id294;1.0 id295;1.0 id296;1.0 id297;1.0 id298;1.0 id299;1.0 id300;1.0 id301;1.0 id302;1.0 id303;1.0 id304;1.0 id305;1.0 id306;1.0 id307;1.0 id308;1.0 id309;1.0 id310;1.0 id311;1.0 id312;1.0 id313;1.0 id314;1.0 id315;1.0 id316;1.0 id317;1.0 id318;1.0 id319;1.0 id320;1.0 id321;1.0 id322;1.0 id323;1.0 id324;1.0 id325;1.0 id326;1.0 id327;1.0 id328;1.0 id329;1.0 id330;1.0 id331;1.0 id332;1.0 id333;1.0 id334;1.0 id335;1.0 id336;1.0 id337;1.0 id338;1.0 id339;1.0 id340;1.0 id341;1.0 id342;1.0 id343;1.0 id344;1.0 id345;1.0 id346;1.0 id347;1.0 id348;1.0 id349;1.0 id350;1.0 id351;1.0 id352;1.0 id353;1.0 id354;1.0 id355;1.0 id356;1.0 id357;1.0 id358;1.0 id359;1.0 id360;1.0 id361;1.0 id362;1.0 id363;1.0 id364;1.0 id365;1.0 id366;1.0 id367;1.0 id368;1.0 id369;1.0 id370;1.0 id371;1.0 id372;1.0 id373;1.0 id374;1.0 id375;1.0 id376;1.0 id377;1.0 id378;1.0 id379;1.0 id380;1.0 id381;1.0 id382;1.0 id383;1.0 id384;1.0 id385;1.0 id386;1.0 id387;1.0 id388;1.0 id389;1.0 id390;1.0 id391;1.0 id392;1.0 id393;1.0 id394;1.0 id395;1.0 id396;1.0 id397;1.0 id398;1.0 id399;1.0 id400;1.0 id401;1.0 id402;1.0 id403;1.0 id404;1.0 id405;1.0 id406;1.0 id407;1.0 id408;1.0 id409;1.0 id410;1.0 id411;1.0 id412;1.0 id413;1.0 id414;1.0 id415;1.0 id416;1.0 id417;1.0 id418;1.0 id419;1.0 id420;1.0 id421;1.0 id422;1.0 id423;1.0 id424;1.0 id425;1.0 id426;1.0 id427;1.0 id428;1.0 id429;1.0 id430;1.0 id431;1.0 id432;1.0 id433;1.0 id434;1.0 id435;1.0 id436;1.0 id437;1.0 id438;1.0 id439;1.0 id440;1.0 id441;1.0 id442;1.0 id443;1.0 id444;1.0 id445;1.0 id446;1.0 id447;1.0 id448;1.0 id449;1.0 id450;1.0 id451;1.0 id452;1.0 id453;1.0 id454;1.0 id455;1.0 id456;1.0 id457;1.0 id458;1.0 id459;1.0 id460;1.0 id461;1.0 id462;1.0 id463;1.0 id464;1.0 id465;1.0 id466;1.0 id467;1.0 id468;1.0 id469;1.0 id470;1.0 id471;1.0 id472;1.0 id473;1.0 id474;1.0 id475;1.0 id476;1.0 id477;1.0 id478;1.0 id479;1.0 id480;1.0 id481;1.0 id482;1.0 id483;1.0 id484;1.0 id485;1.0 id486;1.0 id487;1.0 id488;1.0 id489;1.0 id490;1.0 id491;1.0 id492;1.0 id493;1.0 id494;1.0 id495;1.0 id496;1.0 id497;1.0 id498;1.0 id499;1.0 id500;1.0 id501;1.0 id502;1.0 id503;1.0 id504;1.0 id505;1.0 id506;1.0 id507;1.0 id508;1.0 id509;1.0 id510;1.0 id511;1.0 id512;1.0 id513;1.0 id514;1.0 id515;1.0 id516;1.0 id517;1.0 id518;1.0 id519;1.0 id520;1.0 id521;1.0 id522;1.0 id523;1.0 id524;1.0 id525;1.0 id526;1.0 id527;1.0 id528;1.0 id529;1.0 id530;1.0 id531;1.0 id532;1.0 id533;1.0 id534;1.0 id535;1.0 id536;1.0 id537;1.0 id538;1.0 id539;1.0 id540;1.0 id541;1.0 id542;1.0 id543;1.0 id544;1.0 id545;1.0 id546;1.0 id547;1.0 id548;1.0 id549;1.0 id550;1.0 id551;1.0 id552;1.0 id553;1.0 id554;1.0 id555;1.0 id556;1.0 id557;1.0 id558;1.0 id559;1.0 id560;1.0 id561;1.0 id562;1.0 id563;1.0 id564;1.0 id565;1.0 id566;1.0 id567;1.0 id568;1.0 id569;1.0 id570;1.0 id571;1.0 id572;1.0 id573;1.0 id574;1.0 id575;1.0 id576;1.0 id577;1.0 id578;1.0 id579;1.0 id580;1.0 id581;1.0 id582;1.0 id583;1.0 id584;1.0 id585;1.0 id586;1.0 id587;1.0 id588;1.0 id589;1.0 id590;1.0 id591;1.0 id592;1.0 id593;1.0 id594;1.0 id595;1.0 id596;1.0 id597;1.0 id598;1.0 id599;1.0 id600;1.0 id601;1.0 id602;1.0 id603;1.0 id604;1.0 id605;1.0 id606;1.0 id607;1.0 id608;1.0 id609;1.0 id610;1.0 id611;1.0 id612;1.0 id613;1.0 id614;1.0 id615;1.0 id616;1.0 id617;1.0 id618;1.0 id619;1.0 id620;1.0 id621;1.0 id622;1.0 id623;1.0 id624;1.0 id625;1.0 id626;1.0 id627;1.0 id628;1.0 id629;1.0 id630;1.0 id631;1.0 id632;1.0 id633;1.0 id634;1.0 id635;1.0 id636;1.0 id637;1.0 id638;1.0 id639;1.0 id640;1.0 id641;1.0 id642;1.0 id643;1.0 id644;1.0 id645;1.0 id646;1.0 id647;1.0 id648;1.0 id649;1.0 id650;1.0 id651;1.0 id652;1.0 id653;1.0 id654;1.0 id655;1.0 id656;1.0 id657;1.0 id658;1.0 id659;1.0 id660;1.0 id661;1.0 id662;1.0 id663;1.0 id664;1.0 id665;1.0 id666;1.0 id667;1.0 id668;1.0 id669;1.0 id670;1.0 id671;1.0 id672;1.0 id673;1.0 id674;1.0 id675;1.0 id676;1.0 id677;1.0 id678;1.0 id679;1.0 id680;1.0 id681;1.0 id682;1.0 id683;1.0 id684;1.0 id685;1.0 id686;1.0 id687;1.0 id688;1.0 id689;1.0 id690;1.0 id691;1.0 id692;1.0 id693;1.0 id694;1.0 id695;1.0 id696;1.0 id697;1.0 id698;1.0 id699;1.0 id700;1.0 id701;1.0 id702;1.0 id703;1.0 id704;1.0 id705;1.0 id706;1.0 id707;1.0 id708;1.0 id709;1.0 id710;1.0 id711;1.0 id712;1.0 id713;1.0 id714;1.0 id715;1.0 id716;1.0 id717;1.0 id718;1.0 id719;1.0 id720;1.0 id721;1.0 id722;1.0 id723;1.0 id724;1.0 id725;1.0 id726;1.0 id727;1.0 id728;1.0 id729;1.0 id730;1.0 id731;1.0 id732;1.0 id733;1.0 id734;1.0 id735;1.0 id736;1.0 id737;1.0 id738;1.0 id739;1.0 id740;1.0 id741;1.0 id742;1.0 id743;1.0 id744;1.0 id745;1.0 id746;1.0 id747;1.0 id748;1.0 id749;1.0 id750;1.0 id751;1.0 id752;1.0 id753;1.0 id754;1.0 id755;1.0 id756;1.0 id757;1.0 id758;1.0 id759;1.0 id760;1.0 id761;1.0 id762;1.0 id763;1.0 id764;1.0 id765;1.0 id766;1.0 id767;1.0 id768;1.0 id769;1.0 id770;1.0 id771;1.0 id772;1.0 id773;1.0 id774;1.0 id775;1.0 id776;1.0 id777;1.0 id778;1.0 id779;1.0 id780;1.0 id781;1.0 id782;1.0 id783;1.0 id784;1.0 id785;1.0 id786;1.0 id787;1.0 id788;1.0 id789;1.0 id790;1.0 id791;1.0 id792;1.0 id793;1.0 id794;1.0 id795;1.0 id796;1.0 id797;1.0 id798;1.0 id799;1.0 id800;1.0 id801;1.0 id802;1.0 id803;1.0 id804;1.0 id805;1.0 id806;1.0 id807;1.0 id808;1.0 id809;1.0 id810;1.0 id811;1.0 id812;1.0 id813;1.0 id814;1.0 id815;1.0 id816;1.0 id817;1.0 id818;1.0 id819;1.0 id820;1.0 id821;1.0 id822;1.0 id823;1.0 id824;1.0 id825;1.0 id826;1.0 id827;1.0 id828;1.0 id829;1.0 id830;1.0 id831;1.0 id832;1.0 id833;1.0 id834;1.0 id835;1.0 id836;1.0 id837;1.0 id838;1.0 id839;1.0 id840;1.0 id841;1.0 id842;1.0 id843;1.0 id844;1.0 id845;1.0 id846;1.0 id847;1.0 id848;1.0 id849;1.0 id850;1.0 id851;1.0 id852;1.0 id853;1.0 id854;1.0 id855;1.0 id856;1.0 id857;1.0 id858;1.0 id859;1.0 id860;1.0 id861;1.0 id862;1.0 id863;1.0 id864;1.0 id865;1.0 id866;1.0 id867;1.0 id868;1.0 id869;1.0 id870;1.0 id871;1.0 id872;1.0 id873;1.0 id874;1.0 id875;1.0 id876;1.0 id877;1.0 id878;1.0 id879;1.0 id880;1.0 id881;1.0 id882;1.0 id883;1.0 id884;1.0 id885;1.0 id886;1.0 id887;1.0 id888;1.0 id889;1.0 id890;1.0 id891;1.0 id892;1.0 id893;1.0 id894;1.0 id895;1.0 id896;1.0 id897;1.0 id898;1.0 id899;1.0 id900;1.0 id901;1.0 id902;1.0 id903;1.0 id904;1.0 id905;1.0 id906;1.0 id907;1.0 id908;1.0 id909;1.0 id910;1.0 id911;1.0 id912;1.0 id913;1.0 id914;1.0 id915;1.0 id916;1.0 id917;1.0 id918;1.0 id919;1.0 id920;1.0 id921;1.0 id922;1.0 id923;1.0 id924;1.0 id925;1.0 id926;1.0 id927;1.0 id928;1.0 id929;1.0 id930;1.0 id931;1.0 id932;1.0 id933;1.0 id934;1.0 id935;1.0 id936;1.0 id937;1.0 id938;1.0 id939;1.0 id940;1.0 id941;1.0 id942;1.0 id943;1.0 id944;1.0 id945;1.0 id946;1.0 id947;1.0 id948;1.0 id949;1.0 id950;1.0 id951;1.0 id952;1.0 id953;1.0 id954;1.0 id955;1.0 id956;1.0 id957;1.0 id958;1.0 id959;1.0 id960;1.0 id961;1.0 id962;1.0 id963;1.0 id964;1.0 id965;1.0 id966;1.0 id967;1.0 id968;1.0 id969;1.0 id970;1.0 id971;1.0 id972;1.0 id973;1.0 id974;1.0 id975;1.0 id976;1.0 id977;1.0 id978;1.0 id979;1.0 id980;1.0 id981;1.0 id982;1.0 id983;1.0 id984;1.0 id985;1.0 id986;1.0 id987;1.0 id988;1.0 id989;1.0 id990;1.0 id991;1.0 id992;1.0 id993;1.0 id994;1.0 id995;1.0 id996;1.0 id997;1.0 id998;1.0 id999;1.0 id1000;1.0 id1001;1.0 id1002;1.0 id1003;1.0 id1004;1.0 id1005;1.0 id1006;1.0 id1007;1.0 id1008;1.0 id1009;1.0 id1010;1.0 id1011;1.0 id1012;1.0 id1013;1.0 id1014;1.0 id1015;1.0 id1016;1.0 id1017;1.0 id1018;1.0 id1019;1.0 id1020;1.0 id1021;1.0 id1022;1.0 id1023;1.0 id1024;1.0 id1025;1.0 id1026;1.0 id1027;1.0 id1028;1.0 id1029;1.0 id1030;1.0 id1031;1.0 id1032;1.0 id1033;1.0 id1034;1.0 id1035;1.0 id1036;1.0 id1037;1.0 id1038;1.0 id1039;1.0 id1040;1.0 id1041;1.0 id1042;1.0 id1043;1.0 id1044;1.0 id1045;1.0 id1046;1.0 id1047;1.0 id1048;1.0 id1049;1.0 id1050;1.0 id1051;1.0 id1052;1.0 id1053;1.0 id1054;1.0 id1055;1.0 id1056;1.0 id1057;1.0 id1058;1.0 id1059;1.0 id1060;1.0 id1061;1.0 id1062;1.0 id1063;1.0 id1064;1.0 id1065;1.0 id1066;1.0 id1067;1.0 id1068;1.0 id1069;1.0 id1070;1.0 id1071;1.0 id1072;1.0 id1073;1.0 id1074;1.0 id1075;1.0 id1076;1.0 id1077;1.0 id1078;1.0 id1079;1.0 id1080;1.0 id1081;1.0 id1082;1.0 id1083;1.0 id1084;1.0 id1085;1.0 id1086;1.0 id1087;1.0 id1088;1.0 id1089;1.0 id1090;1.0 id1091;1.0 id1092;1.0 id1093;1.0 id1094;1.0 id1095;1.0 id1096;1.0 id1097;1.0 id1098;1.0 id1099;1.0 id1100;1.0 id1101;1.0 id1102;1.0 id1103;1.0 id1104;1.0 id1105;1.0 id1106;1.0 id1107;1.0 id1108;1.0 id1109;1.0 id1110;1.0 id1111;1.0 id1112;1.0 id1113;1.0 id1114;1.0 id1115;1.0 id1116;1.0 id1117;1.0 id1118;1.0 id1119;1.0 id1120;1.0 id1121;1.0 id1122;1.0 id1123;1.0 id1124;1.0 id1125;1.0 id1126;1.0 id1127;1.0 id1128;1.0 id1129;1.0 id1130;1.0 id1131;1.0 id1132;1.0 id1133;1.0 id1134;1.0 id1135;1.0 id1136;1.0 id1137;1.0 id1138;1.0 id1139;1.0 id1140;1.0 id1141;1.0 id1142;1.0 id1143;1.0 id1144;1.0 id1145;1.0 id1146;1.0 id1147;1.0 id1148;1.0 id1149;1.0 id1150;1.0 id1151;1.0 id1152;1.0 id1153;1.0 id1154;1.0 id1155;1.0 id1156;1.0 id1157;1.0 id1158;1.0 id1159;1.0 id1160;1.0 id1161;1.0 id1162;1.0 id1163;1.0 id1164;1.0 id1165;1.0 id1166;1.0 id1167;1.0 id1168;1.0 id1169;1.0 id1170;1.0 id1171;1.0 id1172;1.0 id1173;1.0 id1174;1.0 id1175;1.0 id1176;1.0 id1177;1.0 id1178;1.0 id1179;1.0 id1180;1.0 id1181;1.0 id1182;1.0 id1183;1.0 id1184;1.0 id1185;1.0 id1186;1.0 id1187;1.0 id1188;1.0 id1189;1.0 id1190;1.0 id1191;1.0 id1192;1.0 id1193;1.0 id1194;1.0 id1195;1.0 id1196;1.0 id1197;1.0 id1198;1.0 id1199;1.0 id1200;1.0 id1201;1.0 id1202;1.0 id1203;1.0 id1204;1.0 id1205;1.0 id1206;1.0 id1207;1.0 id1208;1.0 id1209;1.0 id1210;1.0 id1211;1.0 id1212;1.0 id1213;1.0 id1214;1.0 id1215;1.0 id1216;1.0 id1217;1.0 id1218;1.0 id1219;1.0 id1220;1.0 id1221;1.0 id1222;1.0 id1223;1.0 id1224;1.0 id1225;1.0 id1226;1.0 id1227;1.0 id1228;1.0 id1229;1.0 id1230;1.0 id1231;1.0 id1232;1.0 id1233;1.0 id1234;1.0 id1235;1.0 id1236;1.0 id1237;1.0 id1238;1.0 id1239;1.0 id1240;1.0 id1241;1.0 id1242;1.0 id1243;1.0 id1244;1.0 id1245;1.0 id1246;1.0 id1247;1.0 id1248;1.0 id1249;1.0 id1250;1.0 id1251;1.0 id1252;1.0 id1253;1.0 id1254;1.0 id1255;1.0 id1256;1.0 id1257;1.0 id1258;1.0 id1259;1.0 id1260;1.0 id1261;1.0 id1262;1.0 id1263;1.0 id1264;1.0 id1265;1.0 id1266;1.0 id1267;1.0 id1268;1.0 id1269;1.0 id1270;1.0 id1271;1.0 id1272;1.0 id1273;1.0 id1274;1.0 id1275;1.0 id1276;1.0 id1277;1.0 id1278;1.0 id1279;1.0 id1280;1.0 id1281;1.0 id1282;1.0 id1283;1.0 id1284;1.0 id1285;1.0 id1286;1.0 id1287;1.0 id1288;1.0 id1289;1.0 id1290;1.0 id1291;1.0 id1292;1.0 id1293;1.0 id1294;1.0 id1295;1.0 id1296;1.0 id1297;1.0 id1298;1.0 id1299;1.0 id1300;1.0 id1301;1.0 id1302;1.0 id1303;1.0 id1304;1.0 id1305;1.0 id1306;1.0 id1307;1.0 id1308;1.0 id1309;1.0 id1310;1.0 id1311;1.0 id1312;1.0 id1313;1.0 id1314;1.0 id1315;1.0 id1316;1.0 id1317;1.0 id1318;1.0 id1319;1.0 id1320;1.0 id1321;1.0 id1322;1.0 id1323;1.0 id1324;1.0 id1325;1.0 id1326;1.0 id1327;1.0 id1328;1.0 id1329;1.0 id1330;1.0 id1331;1.0 id1332;1.0 id1333;1.0 id1334;1.0 id1335;1.0 id1336;1.0 id1337;1.0 id1338;1.0 id1339;1.0 id1340;1.0 id1341;1.0 id1342;1.0 id1343;1.0 id1344;1.0 id1345;1.0 id1346;1.0 id1347;1.0 id1348;1.0 id1349;1.0 id1350;1.0 id1351;1.0 id1352;1.0 id1353;1.0 id1354;1.0 id1355;1.0 id1356;1.0 id1357;1.0 id1358;1.0 id1359;1.0 id1360;1.0 id1361;1.0 id1362;1.0 id1363;1.0 id1364;1.0 id1365;1.0 id1366;1.0 id1367;1.0 id1368;1.0 id1369;1.0 id1370;1.0 id1371;1.0 id1372;1.0 id1373;1.0 id1374;1.0 id1375;1.0 id1376;1.0 id1377;1.0 id1378;1.0 id1379;1.0 id1380;1.0 id1381;1.0 id1382;1.0 id1383;1.0 id1384;1.0 id1385;1.0 id1386;1.0 id1387;1.0 id1388;1.0 id1389;1.0 id1390;1.0 id1391;1.0 id1392;1.0 id1393;1.0 id1394;1.0 id1395;1.0 id1396;1.0 id1397;1.0 id1398;1.0 id1399;1.0 id1400;1.0 id1401;1.0 id1402;1.0 id1403;1.0 id1404;1.0 id1405;1.0 id1406;1.0 id1407;1.0 id1408;1.0 id1409;1.0 id1410;1.0 id1411;1.0 id1412;1.0 id1413;1.0 id1414;1.0 id1415;1.0 id1416;1.0 id1417;1.0 id1418;1.0 id1419;1.0 id1420;1.0 id1421;1.0 id1422;1.0 id1423;1.0 id1424;1.0 id1425;1.0 id1426;1.0 id1427;1.0 id1428;1.0 id1429;1.0 id1430;1.0 id1431;1.0 id1432;1.0 id1433;1.0 id1434;1.0 id1435;1.0 id1436;1.0 id1437;1.0 id1438;1.0 id1439;1.0 id1440;1.0 id1441;1.0 id1442;1.0 id1443;1.0 id1444;1.0 id1445;1.0 id1446;1.0 id1447;1.0 id1448;1.0 id1449;1.0 id1450;1.0 id1451;1.0 id1452;1.0 id1453;1.0 id1454;1.0 id1455;1.0 id1456;1.0 id1457;1.0 id1458;1.0 id1459;1.0 id1460;1.0 id1461;1.0 id1462;1.0 id1463;1.0 id1464;1.0 id1465;1.0 id1466;1.0 id1467;1.0 id1468;1.0 id1469;1.0 id1470;1.0 id1471;1.0 id1472;1.0 id1473;1.0 id1474;1.0 id1475;1.0 id1476;1.0 id1477;1.0 id1478;1.0 id1479;1.0 id1480;1.0 id1481;1.0 id1482;1.0 id1483;1.0 id1484;1.0 id1485;1.0 id1486;1.0 id1487;1.0 id1488;1.0 id1489;1.0 id1490;1.0 id1491;1.0 id1492;1.0 id1493;1.0 id1494;1.0 id1495;1.0 id1496;1.0 id1497;1.0 id1498;1.0 id1499;1.0 id1500;1.0 id1501;1.0 id1502;1.0 id1503;1.0 id1504;1.0 id1505;1.0 id1506;1.0 id1507;1.0 id1508;1.0 id1509;1.0 id1510;1.0 id1511;1.0 id1512;1.0 id1513;1.0 id1514;1.0 id1515;1.0 id1516;1.0 id1517;1.0 id1518;1.0 id1519;1.0 id1520;1.0 id1521;1.0 id1522;1.0 id1523;1.0 id1524;1.0 id1525;1.0 id1526;1.0 id1527;1.0 id1528;1.0 id1529;1.0 id1530;1.0 id1531;1.0 id1532;1.0 id1533;1.0 id1534;1.0 id1535;1.0 id1536;1.0 id1537;1.0 id1538;1.0 id1539;1.0 id1540;1.0 id1541;1.0 id1542;1.0 id1543;1.0 id1544;1.0 id1545;1.0 id1546;1.0 id1547;1.0 id1548;1.0 id1549;1.0 id1550;1.0 id1551;1.0 id1552;1.0 id1553;1.0 id1554;1.0 id1555;1.0 id1556;1.0 id1557;1.0 id1558;1.0 id1559;1.0 id1560;1.0 id1561;1.0 id1562;1.0 id1563;1.0 id1564;1.0 id1565;1.0 id1566;1.0 id1567;1.0 id1568;1.0 id1569;1.0 id1570;1.0 id1571;1.0 id1572;1.0 id1573;1.0 id1574;1.0 id1575;1.0 id1576;1.0 id1577;1.0 id1578;1.0 id1579;1.0 id1580;1.0 id1581;1.0 id1582;1.0 id1583;1.0 id1584;1.0 id1585;1.0 id1586;1.0 id1587;1.0 id1588;1.0 id1589;1.0 id1590;1.0 id1591;1.0 id1592;1.0 id1593;1.0 id1594;1.0 id1595;1.0 id1596;1.0 id1597;1.0 id1598;1.0 id1599;1.0 id1600;1.0 id1601;1.0 id1602;1.0 id1603;1.0 id1604;1.0 id1605;1.0 id1606;1.0 id1607;1.0 id1608;1.0 id1609;1.0 id1610;1.0 id1611;1.0 id1612;1.0 id1613;1.0 id1614;1.0 id1615;1.0 id1616;1.0 id1617;1.0 id1618;1.0 id1619;1.0 id1620;1.0 id1621;1.0 id1622;1.0 id1623;1.0 id1624;1.0 id1625;1.0 id1626;1.0 id1627;1.0 id1628;1.0 id1629;1.0 id1630;1.0 id1631;1.0 id1632;1.0 id1633;1.0 id1634;1.0 id1635;1.0 id1636;1.0 id1637;1.0 id1638;1.0 id1639;1.0 id1640;1.0 id1641;1.0 id1642;1.0 id1643;1.0 id1644;1.0 id1645;1.0 id1646;1.0 id1647;1.0 id1648;1.0 id1649;1.0 id1650;1.0 id1651;1.0 id1652;1.0 id1653;1.0 id1654;1.0 id1655;1.0 id1656;1.0 id1657;1.0 id1658;1.0 id1659;1.0 id1660;1.0 id1661;1.0 id1662;1.0 id1663;1.0 id1664;1.0 id1665;1.0 id1666;1.0 id1667;1.0 id1668;1.0 id1669;1.0 id1670;1.0 id1671;1.0 id1672;1.0 id1673;1.0 id1674;1.0 id1675;1.0 id1676;1.0 id1677;1.0 id1678;1.0 id1679;1.0 id1680;1.0 id1681;1.0 id1682;1.0 id1683;1.0 id1684;1.0 id1685;1.0 id1686;1.0 id1687;1.0 id1688;1.0 id1689;1.0 id1690;1.0 id1691;1.0 id1692;1.0 id1693;1.0 id1694;1.0 id1695;1.0 id1696;1.0 id1697;1.0 id1698;1.0 id1699;1.0 id1700;1.0 id1701;1.0 id1702;1.0 id1703;1.0 id1704;1.0 id1705;1.0 id1706;1.0 id1707;1.0 id1708;1.0 id1709;1.0 id1710;1.0 id1711;1.0 id1712;1.0 id1713;1.0 id1714;1.0 id1715;1.0 id1716;1.0 id1717;1.0 id1718;1.0 id1719;1.0 id1720;1.0 id1721;1.0 id1722;1.0 id1723;1.0 id1724;1.0 id1725;1.0 id1726;1.0 id1727;1.0 id1728;1.0 id1729;1.0 id1730;1.0 id1731;1.0 id1732;1.0 id1733;1.0 id1734;1.0 id1735;1.0 id1736;1.0 id1737;1.0 id1738;1.0 id1739;1.0 id1740;1.0 id1741;1.0 id1742;1.0 id1743;1.0 id1744;1.0 id1745;1.0 id1746;1.0 id1747;1.0 id1748;1.0 id1749;1.0 id1750;1.0 id1751;1.0 id1752;1.0 id1753;1.0 id1754;1.0 id1755;1.0 id1756;1.0 id1757;1.0 id1758;1.0 id1759;1.0 id1760;1.0 id1761;1.0 id1762;1.0 id1763;1.0 id1764;1.0 id1765;1.0 id1766;1.0 id1767;1.0 id1768;1.0 id1769;1.0 id1770;1.0 id1771;1.0 id1772;1.0 id1773;1.0 id1774;1.0 id1775;1.0 id1776;1.0 id1777;1.0 id1778;1.0 id1779;1.0 id1780;1.0 id1781;1.0 id1782;1.0 id1783;1.0 id1784;1.0 id1785;1.0 id1786;1.0 id1787;1.0 id1788;1.0 id1789;1.0 id1790;1.0 id1791;1.0 id1792;1.0 id1793;1.0 id1794;1.0 id1795;1.0 id1796;1.0 id1797;1.0 id1798;1.0 id1799;1.0 id1800;1.0 id1801;1.0 id1802;1.0 id1803;1.0 id1804;1.0 id1805;1.0 id1806;1.0 id1807;1.0 id1808;1.0 id1809;1.0 id1810;1.0 id1811;1.0 id1812;1.0 id1813;1.0 id1814;1.0 id1815;1.0 id1816;1.0 id1817;1.0 id1818;1.0 id1819;1.0 id1820;1.0 id1821;1.0 id1822;1.0 id1823;1.0 id1824;1.0 id1825;1.0 id1826;1.0 id1827;1.0 id1828;1.0 id1829;1.0 id1830;1.0 id1831;1.0 id1832;1.0 id1833;1.0 id1834;1.0 id1835;1.0 id1836;1.0 id1837;1.0 id1838;1.0 id1839;1.0 id1840;1.0 id1841;1.0 id1842;1.0 id1843;1.0 id1844;1.0 id1845;1.0 id1846;1.0 id1847;1.0 id1848;1.0 id1849;1.0 id1850;1.0 id1851;1.0 id1852;1.0 id1853;1.0 id1854;1.0 id1855;1.0 id1856;1.0 id1857;1.0 id1858;1.0 id1859;1.0 id1860;1.0 id1861;1.0 id1862;1.0 id1863;1.0 id1864;1.0 id1865;1.0 id1866;1.0 id1867;1.0 id1868;1.0 id1869;1.0 id1870;1.0 id1871;1.0 id1872;1.0 id1873;1.0 id1874;1.0 id1875;1.0 id1876;1.0 id1877;1.0 id1878;1.0 id1879;1.0 id1880;1.0 id1881;1.0 id1882;1.0 id1883;1.0 id1884;1.0 id1885;1.0 id1886;1.0 id1887;1.0 id1888;1.0 id1889;1.0 id1890;1.0 id1891;1.0 id1892;1.0 id1893;1.0 id1894;1.0 id1895;1.0 id1896;1.0 id1897;1.0 id1898;1.0 id1899;1.0 id1900;1.0 id1901;1.0 id1902;1.0 id1903;1.0 id1904;1.0 id1905;1.0 id1906;1.0 id1907;1.0 id1908;1.0 id1909;1.0 id1910;1.0 id1911;1.0 id1912;1.0 id1913;1.0 id1914;1.0 id1915;1.0 id1916;1.0 id1917;1.0 id1918;1.0 id1919;1.0 id1920;1.0 id1921;1.0 id1922;1.0 id1923;1.0 id1924;1.0 id1925;1.0 id1926;1.0 id1927;1.0 id1928;1.0 id1929;1.0 id1930;1.0 id1931;1.0 id1932;1.0 id1933;1.0 id1934;1.0 id1935;1.0 id1936;1.0 id1937;1.0 id1938;1.0 id1939;1.0 id1940;1.0 id1941;1.0 id1942;1.0 id1943;1.0 id1944;1.0 id1945;1.0 id1946;1.0 id1947;1.0 id1948;1.0 id1949;1.0 id1950;1.0 id1951;1.0 id1952;1.0 id1953;1.0 id1954;1.0 id1955;1.0 id1956;1.0 id1957;1.0 id1958;1.0 id1959;1.0 id1960;1.0 id1961;1.0 id1962;1.0 id1963;1.0 id1964;1.0 id1965;1.0 id1966;1.0 id1967;1.0 id1968;1.0 id1969;1.0 id1970;1.0 id1971;1.0 id1972;1.0 id1973;1.0 id1974;1.0 id1975;1.0 id1976;1.0 id1977;1.0 id1978;1.0 id1979;1.0 id1980;1.0 id1981;1.0 id1982;1.0 id1983;1.0 id1984;1.0 id1985;1.0 id1986;1.0 id1987;1.0 id1988;1.0 id1989;1.0 id1990;1.0 id1991;1.0 id1992;1.0 id1993;1.0 id1994;1.0 id1995;1.0 id1996;1.0 id1997;1.0 id1998;1.0 id1999;1.0 id2000;1.0 id2001;1.0 id2002;1.0 id2003;1.0 id2004;1.0 id2005;1.0 id2006;1.0 id2007;1.0 id2008;1.0 id2009;1.0 id2010;1.0 id2011;1.0 id2012;1.0 id2013;1.0 id2014;1.0 id2015;1.0 id2016;1.0 id2017;1.0 id2018;1.0 id2019;1.0 id2020;1.0 id2021;1.0 id2022;1.0 id2023;1.0 id2024;1.0 id2025;1.0 id2026;1.0 id2027;1.0 id2028;1.0 id2029;1.0 id2030;1.0 id2031;1.0 id2032;1.0 id2033;1.0 id2034;1.0 id2035;1.0 id2036;1.0 id2037;1.0 id2038;1.0 id2039;1.0 id2040;1.0 id2041;1.0 id2042;1.0 id2043;1.0 id2044;1.0 id2045;1.0 id2046;1.0 id2047;1.0 id2048;1.0 id2049;1.0 id2050;1.0 id2051;1.0 id2052;1.0 id2053;1.0 id2054;1.0 id2055;1.0 id2056;1.0 id2057;1.0 id2058;1.0 id2059;1.0 id2060;1.0 id2061;1.0 id2062;1.0 id2063;1.0 id2064;1.0 id2065;1.0 id2066;1.0 id2067;1.0 id2068;1.0 id2069;1.0 id2070;1.0 id2071;1.0 id2072;1.0 id2073;1.0 id2074;1.0 id2075;1.0 id2076;1.0 id2077;1.0 id2078;1.0 id2079;1.0 id2080;1.0 id2081;1.0 id2082;1.0 id2083;1.0 id2084;1.0 id2085;1.0 id2086;1.0 id2087;1.0 id2088;1.0 id2089;1.0 id2090;1.0 id2091;1.0 id2092;1.0 id2093;1.0 id2094;1.0 id2095;1.0 id2096;1.0 id2097;1.0 id2098;1.0 id2099;1.0 id2100;1.0 id2101;1.0 id2102;1.0 id2103;1.0 id2104;1.0 id2105;1.0 id2106;1.0 id2107;1.0 id2108;1.0 id2109;1.0 id2110;1.0 id2111;1.0 id2112;1.0 id2113;1.0 id2114;1.0 id2115;1.0 id2116;1.0 id2117;1.0 id2118;1.0 id2119;1.0 id2120;1.0 id2121;1.0 id2122;1.0 id2123;1.0 id2124;1.0 id2125;1.0 id2126;1.0 id2127;1.0 id2128;1.0 id2129;1.0 id2130;1.0 id2131;1.0 id2132;1.0 id2133;1.0 id2134;1.0 id2135;1.0 id2136;1.0 id2137;1.0 id2138;1.0 id2139;1.0 id2140;1.0 id2141;1.0 id2142;1.0 id2143;1.0 id2144;1.0 id2145;1.0 id2146;1.0 id2147;1.0 id2148;1.0 id2149;1.0 id2150;1.0 id2151;1.0 id2152;1.0 id2153;1.0 id2154;1.0 id2155;1.0 id2156;1.0 id2157;1.0 id2158;1.0 id2159;1.0 id2160;1.0 id2161;1.0 id2162;1.0 id2163;1.0 id2164;1.0 id2165;1.0 id2166;1.0 id2167;1.0 id2168;1.0 id2169;1.0 id2170;1.0 id2171;1.0 id2172;1.0 id2173;1.0 id2174;1.0 id2175;1.0 id2176;1.0 id2177;1.0 id2178;1.0 id2179;1.0 id2180;1.0 id2181;1.0 id2182;1.0 id2183;1.0 id2184;1.0 id2185;1.0 id2186;1.0 id2187;1.0 id2188;1.0 id2189;1.0 id2190;1.0 id2191;1.0 id2192;1.0 id2193;1.0 id2194;1.0 id2195;1.0 id2196;1.0 id2197;1.0 id2198;1.0 id2199;1.0 id2200;1.0 id2201;1.0 id2202;1.0 id2203;1.0 id2204;1.0 id2205;1.0 id2206;1.0 id2207;1.0 id2208;1.0 id2209;1.0 id2210;1.0 id2211;1.0 id2212;1.0 id2213;1.0 id2214;1.0 id2215;1.0 id2216;1.0 id2217;1.0 id2218;1.0 id2219;1.0 id2220;1.0 id2221;1.0 id2222;1.0 id2223;1.0 id2224;1.0 id2225;1.0 id2226;1.0 id2227;1.0 id2228;1.0 id2229;1.0 id2230;1.0 id2231;1.0 id2232;1.0 id2233;1.0 id2234;1.0 id2235;1.0 id2236;1.0 id2237;1.0 id2238;1.0 id2239;1.0 id2240;1.0 id2241;1.0 id2242;1.0 id2243;1.0 id2244;1.0 id2245;1.0 id2246;1.0 id2247;1.0 id2248;1.0 id2249;1.0 id2250;1.0 id2251;1.0 id2252;1.0 id2253;1.0 id2254;1.0 id2255;1.0 id2256;1.0 id2257;1.0 id2258;1.0 id2259;1.0 id2260;1.0 id2261;1.0 id2262;1.0 id2263;1.0 id2264;1.0 id2265;1.0 id2266;1.0 id2267;1.0 id2268;1.0 id2269;1.0 id2270;1.0 id2271;1.0 id2272;1.0 id2273;1.0 id2274;1.0 id2275;1.0 id2276;1.0 id2277;1.0 id2278;1.0 id2279;1.0 id2280;1.0 id2281;1.0 id2282;1.0 id2283;1.0 id2284;1.0 id2285;1.0 id2286;1.0 id2287;1.0 id2288;1.0 id2289;1.0 id2290;1.0 id2291;1.0 id2292;1.0 id2293;1.0 id2294;1.0 id2295;1.0 id2296;1.0 id2297;1.0 id2298;1.0 id2299;1.0 id2300;1.0 id2301;1.0 id2302;1.0 id2303;1.0 id2304;1.0 id2305;1.0 id2306;1.0 id2307;1.0 id2308;1.0 id2309;1.0 id2310;1.0 id2311;1.0 id2312;1.0 id2313;1.0 id2314;1.0 id2315;1.0 id2316;1.0 id2317;1.0 id2318;1.0 id2319;1.0 id2320;1.0 id2321;1.0 id2322;1.0 id2323;1.0 id2324;1.0 id2325;1.0 id2326;1.0 id2327;1.0 id2328;1.0 id2329;1.0 id2330;1.0 id2331;1.0 id2332;1.0 id2333;1.0 id2334;1.0 id2335;1.0 id2336;1.0 id2337;1.0 id2338;1.0 id2339;1.0 id2340;1.0 id2341;1.0 id2342;1.0 id2343;1.0 id2344;1.0 id2345;1.0 id2346;1.0 id2347;1.0 id2348;1.0 id2349;1.0 id2350;1.0 id2351;1.0 id2352;1.0 id2353;1.0 id2354;1.0 id2355;1.0 id2356;1.0 id2357;1.0 id2358;1.0 id2359;1.0 id2360;1.0 id2361;1.0 id2362;1.0 id2363;1.0 id2364;1.0 id2365;1.0 id2366;1.0 id2367;1.0 id2368;1.0 id2369;1.0 id2370;1.0 id2371;1.0 id2372;1.0 id2373;1.0 id2374;1.0 id2375;1.0 id2376;1.0 id2377;1.0 id2378;1.0 id2379;1.0 id2380;1.0 id2381;1.0 id2382;1.0 id2383;1.0 id2384;1.0 id2385;1.0 id2386;1.0 id2387;1.0 id2388;1.0 id2389;1.0 id2390;1.0 id2391;1.0 id2392;1.0 id2393;1.0 id2394;1.0 id2395;1.0 id2396;1.0 id2397;1.0 id2398;1.0 id2399;1.0 id2400;1.0 id2401;1.0 id2402;1.0 id2403;1.0 id2404;1.0 id2405;1.0 id2406;1.0 id2407;1.0 id2408;1.0 id2409;1.0 id2410;1.0 id2411;1.0 id2412;1.0 id2413;1.0 id2414;1.0 id2415;1.0 id2416;1.0 id2417;1.0 id2418;1.0 id2419;1.0 id2420;1.0 id2421;1.0 id2422;1.0 id2423;1.0 id2424;1.0 id2425;1.0 id2426;1.0 id2427;1.0 id2428;1.0 id2429;1.0 id2430;1.0 id2431;1.0 id2432;1.0 id2433;1.0 id2434;1.0 id2435;1.0 id2436;1.0 id2437;1.0 id2438;1.0 id2439;1.0 id2440;1.0 id2441;1.0 id2442;1.0 id2443;1.0 id2444;1.0 id2445;1.0 id2446;1.0 id2447;1.0 id2448;1.0 id2449;1.0 id2450;1.0 id2451;1.0 id2452;1.0 id2453;1.0 id2454;1.0 id2455;1.0 id2456;1.0 id2457;1.0 id2458;1.0 id2459;1.0 id2460;1.0 id2461;1.0 id2462;1.0 id2463;1.0 id2464;1.0 id2465;1.0 id2466;1.0 id2467;1.0 id2468;1.0 id2469;1.0 id2470;1.0 id2471;1.0 id2472;1.0 id2473;1.0 id2474;1.0 id2475;1.0 id2476;1.0 id2477;1.0 id2478;1.0 id2479;1.0 id2480;1.0 id2481;1.0 id2482;1.0 id2483;1.0 id2484;1.0 id2485;1.0 id2486;1.0 id2487;1.0 id2488;1.0 id2489;1.0 id2490;1.0 id2491;1.0 id2492;1.0 id2493;1.0 id2494;1.0 id2495;1.0 id2496;1.0 id2497;1.0 id2498;1.0 id2499;1.0 id2500;1.0 id2501;1.0 id2502;1.0 id2503;1.0 id2504;1.0 id2505;1.0 id2506;1.0 id2507;1.0 id2508;1.0 id2509;1.0 id2510;1.0 id2511;1.0 id2512;1.0 id2513;1.0 id2514;1.0 id2515;1.0 id2516;1.0 id2517;1.0 id2518;1.0 id2519;1.0 id2520;1.0 id2521;1.0 id2522;1.0 id2523;1.0 id2524;1.0 id2525;1.0 id2526;1.0 id2527;1.0 id2528;1.0 id2529;1.0 id2530;1.0 id2531;1.0 id2532;1.0 id2533;1.0 id2534;1.0 id2535;1.0 id2536;1.0 id2537;1.0 id2538;1.0 id2539;1.0 id2540;1.0 id2541;1.0 id2542;1.0 id2543;1.0 id2544;1.0 id2545;1.0 id2546;1.0 id2547;1.0 id2548;1.0 id2549;1.0 id2550;1.0 id2551;1.0 id2552;1.0 id2553;1.0 id2554;1.0 id2555;1.0 id2556;1.0 id2557;1.0 id2558;1.0 id2559;1.0 id2560;1.0 id2561;1.0 id2562;1.0 id2563;1.0 id2564;1.0 id2565;1.0 id2566;1.0 id2567;1.0 id2568;1.0 id2569;1.0 id2570;1.0 id2571;1.0 id2572;1.0 id2573;1.0 id2574;1.0 id2575;1.0 id2576;1.0 id2577;1.0 id2578;1.0 id2579;1.0 id2580;1.0 id2581;1.0 id2582;1.0 id2583;1.0 id2584;1.0 id2585;1.0 id2586;1.0 id2587;1.0 id2588;1.0 id2589;1.0 id2590;1.0 id2591;1.0 id2592;1.0 id2593;1.0 id2594;1.0 id2595;1.0 id2596;1.0 id2597;1.0 id2598;1.0 id2599;1.0 id2600;1.0 id2601;1.0 id2602;1.0 id2603;1.0 id2604;1.0 id2605;1.0 id2606;1.0 id2607;1.0 id2608;1.0 id2609;1.0 id2610;1.0 id2611;1.0 id2612;1.0 id2613;1.0 id2614;1.0 id2615;1.0 id2616;1.0 id2617;1.0 id2618;1.0 id2619;1.0 id2620;1.0 id2621;1.0 id2622;1.0 id2623;1.0 id2624;1.0 id2625;1.0 id2626;1.0 id2627;1.0 id2628;1.0 id2629;1.0 id2630;1.0 id2631;1.0 id2632;1.0 id2633;1.0 id2634;1.0 id2635;1.0 id2636;1.0 id2637;1.0 id2638;1.0 id2639;1.0 id2640;1.0 id2641;1.0 id2642;1.0 id2643;1.0 id2644;1.0 id2645;1.0 id2646;1.0 id2647;1.0 id2648;1.0 id2649;1.0 id2650;1.0 id2651;1.0 id2652;1.0 id2653;1.0 id2654;1.0 id2655;1.0 id2656;1.0 id2657;1.0 id2658;1.0 id2659;1.0 id2660;1.0 id2661;1.0 id2662;1.0 id2663;1.0 id2664;1.0 id2665;1.0 id2666;1.0 id2667;1.0 id2668;1.0 id2669;1.0 id2670;1.0 id2671;1.0 id2672;1.0 id2673;1.0 id2674;1.0 id2675;1.0 id2676;1.0 id2677;1.0 id2678;1.0 id2679;1.0 id2680;1.0 id2681;1.0 id2682;1.0 id2683;1.0 id2684;1.0 id2685;1.0 id2686;1.0 id2687;1.0 id2688;1.0 id2689;1.0 id2690;1.0 id2691;1.0 id2692;1.0 id2693;1.0 id2694;1.0 id2695;1.0 id2696;1.0 id2697;1.0 id2698;1.0 id2699;1.0 id2700;1.0 id2701;1.0 id2702;1.0 id2703;1.0 id2704;1.0 id2705;1.0 id2706;1.0 id2707;1.0 id2708;1.0 id2709;1.0 id2710;1.0 id2711;1.0 id2712;1.0 id2713;1.0 id2714;1.0 id2715;1.0 id2716;1.0 id2717;1.0 id2718;1.0 id2719;1.0 id2720;1.0 id2721;1.0 id2722;1.0 id2723;1.0 id2724;1.0 id2725;1.0 id2726;1.0 id2727;1.0 id2728;1.0 id2729;1.0 id2730;1.0 id2731;1.0 id2732;1.0 id2733;1.0 id2734;1.0 id2735;1.0 id2736;1.0 id2737;1.0 id2738;1.0 id2739;1.0 id2740;1.0 id2741;1.0 id2742;1.0 id2743;1.0 id2744;1.0 id2745;1.0 id2746;1.0 id2747;1.0 id2748;1.0 id2749;1.0 id2750;1.0 id2751;1.0 id2752;1.0 id2753;1.0 id2754;1.0 id2755;1.0 id2756;1.0 id2757;1.0 id2758;1.0 id2759;1.0 id2760;1.0 id2761;1.0 id2762;1.0 id2763;1.0 id2764;1.0 id2765;1.0 id2766;1.0 id2767;1.0 id2768;1.0 id2769;1.0 id2770;1.0 id2771;1.0 id2772;1.0 id2773;1.0 id2774;1.0 id2775;1.0 id2776;1.0 id2777;1.0 id2778;1.0 id2779;1.0 id2780;1.0 id2781;1.0 id2782;1.0 id2783;1.0 id2784;1.0 id2785;1.0 id2786;1.0 id2787;1.0 id2788;1.0 id2789;1.0 id2790;1.0 id2791;1.0 id2792;1.0 id2793;1.0 id2794;1.0 id2795;1.0 id2796;1.0 id2797;1.0 id2798;1.0 id2799;1.0 id2800;1.0 id2801;1.0 id2802;1.0 id2803;1.0 id2804;1.0 id2805;1.0 id2806;1.0 id2807;1.0 id2808;1.0 id2809;1.0 id2810;1.0 id2811;1.0 id2812;1.0 id2813;1.0 id2814;1.0 id2815;1.0 id2816;1.0 id2817;1.0 id2818;1.0 id2819;1.0 id2820;1.0 id2821;1.0 id2822;1.0 id2823;1.0 id2824;1.0 id2825;1.0 id2826;1.0 id2827;1.0 id2828;1.0 id2829;1.0 id2830;1.0 id2831;1.0 id2832;1.0 id2833;1.0 id2834;1.0 id2835;1.0 id2836;1.0 id2837;1.0 id2838;1.0 id2839;1.0 id2840;1.0 id2841;1.0 id2842;1.0 id2843;1.0 id2844;1.0 id2845;1.0 id2846;1.0 id2847;1.0 id2848;1.0 id2849;1.0 id2850;1.0 id2851;1.0 id2852;1.0 id2853;1.0 id2854;1.0 id2855;1.0 id2856;1.0 id2857;1.0 id2858;1.0 id2859;1.0 id2860;1.0 id2861;1.0 id2862;1.0 id2863;1.0 id2864;1.0 id2865;1.0 id2866;1.0 id2867;1.0 id2868;1.0 id2869;1.0 id2870;1.0 id2871;1.0 id2872;1.0 id2873;1.0 id2874;1.0 id2875;1.0 id2876;1.0 id2877;1.0 id2878;1.0 id2879;1.0 id2880;1.0 id2881;1.0 id2882;1.0 id2883;1.0 id2884;1.0 id2885;1.0 id2886;1.0 id2887;1.0 id2888;1.0 id2889;1.0 id2890;1.0 id2891;1.0 id2892;1.0 id2893;1.0 id2894;1.0 id2895;1.0 id2896;1.0 id2897;1.0 id2898;1.0 id2899;1.0 id2900;1.0 id2901;1.0 id2902;1.0 id2903;1.0 id2904;1.0 id2905;1.0 id2906;1.0 id2907;1.0 id2908;1.0 id2909;1.0 id2910;1.0 id2911;1.0 id2912;1.0 id2913;1.0 id2914;1.0 id2915;1.0 id2916;1.0 id2917;1.0 id2918;1.0 id2919;1.0 id2920;1.0 id2921;1.0 id2922;1.0 id2923;1.0 id2924;1.0 id2925;1.0 id2926;1.0 id2927;1.0 id2928;1.0 id2929;1.0 id2930;1.0 id2931;1.0 id2932;1.0 id2933;1.0 id2934;1.0 id2935;1.0 id2936;1.0 id2937;1.0 id2938;1.0 id2939;1.0 id2940;1.0 id2941;1.0 id2942;1.0 id2943;1.0 id2944;1.0 id2945;1.0 id2946;1.0 id2947;1.0 id2948;1.0 id2949;1.0 id2950;1.0 id2951;1.0 id2952;1.0 id2953;1.0 id2954;1.0 id2955;1.0 id2956;1.0 id2957;1.0 id2958;1.0 id2959;1.0 id2960;1.0 id2961;1.0 id2962;1.0 id2963;1.0 id2964;1.0 id2965;1.0 id2966;1.0 id2967;1.0 id2968;1.0 id2969;1.0 id2970;1.0 id2971;1.0 id2972;1.0 id2973;1.0 id2974;1.0 id2975;1.0 id2976;1.0 id2977;1.0 id2978;1.0 id2979;1.0 id2980;1.0 id2981;1.0 id2982;1.0 id2983;1.0 id2984;1.0 id2985;1.0 id2986;1.0 id2987;1.0 id2988;1.0 id2989;1.0 id2990;1.0 id2991;1.0 id2992;1.0 id2993;1.0 id2994;1.0 id2995;1.0 id2996;1.0 id2997;1.0 id2998;1.0 id2999;1.0 id3000;1.0 id3001;1.0 id3002;1.0 id3003;1.0 id3004;1.0 id3005;1.0 id3006;1.0 id3007;1.0 id3008;1.0 id3009;1.0 id3010;1.0 id3011;1.0 id3012;1.0 id3013;1.0 id3014;1.0 id3015;1.0 id3016;1.0 id3017;1.0 id3018;1.0 id3019;1.0 id3020;1.0 id3021;1.0 id3022;1.0 id3023;1.0 id3024;1.0 id3025;1.0 id3026;1.0 id3027;1.0 id3028;1.0 id3029;1.0 id3030;1.0 id3031;1.0 id3032;1.0 id3033;1.0 id3034;1.0 id3035;1.0 id3036;1.0 id3037;1.0 id3038;1.0 id3039;1.0 id3040;1.0 id3041;1.0 id3042;1.0 id3043;1.0 id3044;1.0 id3045;1.0 id3046;1.0 id3047;1.0 id3048;1.0 id3049;1.0 id3050;1.0 id3051;1.0 id3052;1.0 id3053;1.0 id3054;1.0 id3055;1.0 id3056;1.0 id3057;1.0 id3058;1.0 id3059;1.0 id3060;1.0 id3061;1.0 id3062;1.0 id3063;1.0 id3064;1.0 id3065;1.0 id3066;1.0 id3067;1.0 id3068;1.0 id3069;1.0 id3070;1.0 id3071;1.0 id3072;1.0 id3073;1.0 id3074;1.0 id3075;1.0 id3076;1.0 id3077;1.0 id3078;1.0 id3079;1.0 id3080;1.0 id3081;1.0 id3082;1.0 id3083;1.0 id3084;1.0 id3085;1.0 id3086;1.0 id3087;1.0 id3088;1.0 id3089;1.0 id3090;1.0 id3091;1.0 id3092;1.0 id3093;1.0 id3094;1.0 id3095;1.0 id3096;1.0 id3097;1.0 id3098;1.0 id3099;1.0 id3100;1.0 id3101;1.0 id3102;1.0 id3103;1.0 id3104;1.0 id3105;1.0 id3106;1.0 id3107;1.0 id3108;1.0 id3109;1.0 id3110;1.0 id3111;1.0 id3112;1.0 id3113;1.0 id3114;1.0 id3115;1.0 id3116;1.0 id3117;1.0 id3118;1.0 id3119;1.0 id3120;1.0 id3121;1.0 id3122;1.0 id3123;1.0 id3124;1.0 id3125;1.0 id3126;1.0 id3127;1.0 id3128;1.0 id3129;1.0 id3130;1.0 id3131;1.0 id3132;1.0 id3133;1.0 id3134;1.0 id3135;1.0 id3136;1.0 id3137;1.0 id3138;1.0 id3139;1.0 id3140;1.0 id3141;1.0 id3142;1.0 id3143;1.0 id3144;1.0 id3145;1.0 id3146;1.0 id3147;1.0 id3148;1.0 id3149;1.0 id3150;1.0 id3151;1.0 id3152;1.0 id3153;1.0 id3154;1.0 id3155;1.0 id3156;1.0 id3157;1.0 id3158;1.0 id3159;1.0 id3160;1.0 id3161;1.0 id3162;1.0 id3163;1.0 id3164;1.0 id3165;1.0 id3166;1.0 id3167;1.0 id3168;1.0 id3169;1.0 id3170;1.0 id3171;1.0 id3172;1.0 id3173;1.0 id3174;1.0 id3175;1.0 id3176;1.0 id3177;1.0 id3178;1.0 id3179;1.0 id3180;1.0 id3181;1.0 id3182;1.0 id3183;1.0 id3184;1.0 id3185;1.0 id3186;1.0 id3187;1.0 id3188;1.0 id3189;1.0 id3190;1.0 id3191;1.0 id3192;1.0 id3193;1.0 id3194;1.0 id3195;1.0 id3196;1.0 id3197;1.0 id3198;1.0 id3199;1.0 id3200;1.0 id3201;1.0 id3202;1.0 id3203;1.0 id3204;1.0 id3205;1.0 id3206;1.0 id3207;1.0 id3208;1.0 id3209;1.0 id3210;1.0 id3211;1.0 id3212;1.0 id3213;1.0 id3214;1.0 id3215;1.0 id3216;1.0 id3217;1.0 id3218;1.0 id3219;1.0 id3220;1.0 id3221;1.0 id3222;1.0 id3223;1.0 id3224;1.0 id3225;1.0 id3226;1.0 id3227;1.0 id3228;1.0 id3229;1.0 id3230;1.0 id3231;1.0 id3232;1.0 id3233;1.0 id3234;1.0 id3235;1.0 id3236;1.0 id3237;1.0 id3238;1.0 id3239;1.0 id3240;1.0 id3241;1.0 id3242;1.0 id3243;1.0 id3244;1.0 id3245;1.0 id3246;1.0 id3247;1.0 id3248;1.0 id3249;1.0 id3250;1.0 id3251;1.0 id3252;1.0 id3253;1.0 id3254;1.0 id3255;1.0 id3256;1.0 id3257;1.0 id3258;1.0 id3259;1.0 id3260;1.0 id3261;1.0 id3262;1.0 id3263;1.0 id3264;1.0 id3265;1.0 id3266;1.0 id3267;1.0 id3268;1.0 id3269;1.0 id3270;1.0 id3271;1.0 id3272;1.0 id3273;1.0 id3274;1.0 id3275;1.0 id3276;1.0 id3277;1.0 id3278;1.0 id3279;1.0 id3280;1.0 id3281;1.0 id3282;1.0 id3283;1.0 id3284;1.0 id3285;1.0 id3286;1.0 id3287;1.0 id3288;1.0 id3289;1.0 id3290;1.0 id3291;1.0 id3292;1.0 id3293;1.0 id3294;1.0 id3295;1.0 id3296;1.0 id3297;1.0 id3298;1.0 id3299;1.0 id3300;1.0 id3301;1.0 id3302;1.0 id3303;1.0 id3304;1.0 id3305;1.0 id3306;1.0 id3307;1.0 id3308;1.0 id3309;1.0 id3310;1.0 id3311;1.0 id3312;1.0 id3313;1.0 id3314;1.0 id3315;1.0 id3316;1.0 id3317;1.0 id3318;1.0 id3319;1.0 id3320;1.0 id3321;1.0 id3322;1.0 id3323;1.0 id3324;1.0 id3325;1.0 id3326;1.0 id3327;1.0 id3328;1.0 id3329;1.0 id3330;1.0 id3331;1.0 id3332;1.0 id3333;1.0 id3334;1.0 id3335;1.0 id3336;1.0 id3337;1.0 id3338;1.0 id3339;1.0 id3340;1.0 id3341;1.0 id3342;1.0 id3343;1.0 id3344;1.0 id3345;1.0 id3346;1.0 id3347;1.0 id3348;1.0 id3349;1.0 id3350;1.0 id3351;1.0 id3352;1.0 id3353;1.0 id3354;1.0 id3355;1.0 id3356;1.0 id3357;1.0 id3358;1.0 id3359;1.0 id3360;1.0 id3361;1.0 id3362;1.0 id3363;1.0 id3364;1.0 id3365;1.0 id3366;1.0 id3367;1.0 id3368;1.0 id3369;1.0 id3370;1.0 id3371;1.0 id3372;1.0 id3373;1.0 id3374;1.0 id3375;1.0 id3376;1.0 id3377;1.0 id3378;1.0 id3379;1.0 id3380;1.0 id3381;1.0 id3382;1.0 id3383;1.0 id3384;1.0 id3385;1.0 id3386;1.0 id3387;1.0 id3388;1.0 id3389;1.0 id3390;1.0 id3391;1.0 id3392;1.0 id3393;1.0 id3394;1.0 id3395;1.0 id3396;1.0 id3397;1.0 id3398;1.0 id3399;1.0 id3400;1.0 id3401;1.0 id3402;1.0 id3403;1.0 id3404;1.0 id3405;1.0 id3406;1.0 id3407;1.0 id3408;1.0 id3409;1.0 id3410;1.0 id3411;1.0 id3412;1.0 id3413;1.0 id3414;1.0 id3415;1.0 id3416;1.0 id3417;1.0 id3418;1.0 id3419;1.0 id3420;1.0 id3421;1.0 id3422;1.0 id3423;1.0 id3424;1.0 id3425;1.0 id3426;1.0 id3427;1.0 id3428;1.0 id3429;1.0 id3430;1.0 id3431;1.0 id3432;1.0 id3433;1.0 id3434;1.0 id3435;1.0 id3436;1.0 id3437;1.0 id3438;1.0 id3439;1.0 id3440;1.0 id3441;1.0 id3442;1.0 id3443;1.0 id3444;1.0 id3445;1.0 id3446;1.0 id3447;1.0 id3448;1.0 id3449;1.0 id3450;1.0 id3451;1.0 id3452;1.0 id3453;1.0 id3454;1.0 id3455;1.0 id3456;1.0 id3457;1.0 id3458;1.0 id3459;1.0 id3460;1.0 id3461;1.0 id3462;1.0 id3463;1.0 id3464;1.0 id3465;1.0 id3466;1.0 id3467;1.0 id3468;1.0 id3469;1.0 id3470;1.0 id3471;1.0 id3472;1.0 id3473;1.0 id3474;1.0 id3475;1.0 id3476;1.0 id3477;1.0 id3478;1.0 id3479;1.0 id3480;1.0 id3481;1.0 id3482;1.0 id3483;1.0 id3484;1.0 id3485;1.0 id3486;1.0 id3487;1.0 id3488;1.0 id3489;1.0 id3490;1.0 id3491;1.0 id3492;1.0 id3493;1.0 id3494;1.0 id3495;1.0 id3496;1.0 id3497;1.0 id3498;1.0 id3499;1.0 id3500;1.0 id3501;1.0 id3502;1.0 id3503;1.0 id3504;1.0 id3505;1.0 id3506;1.0 id3507;1.0 id3508;1.0 id3509;1.0 id3510;1.0 id3511;1.0 id3512;1.0 id3513;1.0 id3514;1.0 id3515;1.0 id3516;1.0 id3517;1.0 id3518;1.0 id3519;1.0 id3520;1.0 id3521;1.0 id3522;1.0 id3523;1.0 id3524;1.0 id3525;1.0 id3526;1.0 id3527;1.0 id3528;1.0 id3529;1.0 id3530;1.0 id3531;1.0 id3532;1.0 id3533;1.0 id3534;1.0 id3535;1.0 id3536;1.0 id3537;1.0 id3538;1.0 id3539;1.0 id3540;1.0 id3541;1.0 id3542;1.0 id3543;1.0 id3544;1.0 id3545;1.0 id3546;1.0 id3547;1.0 id3548;1.0 id3549;1.0 id3550;1.0 id3551;1.0 id3552;1.0 id3553;1.0 id3554;1.0 id3555;1.0 id3556;1.0 id3557;1.0 id3558;1.0 id3559;1.0 id3560;1.0 id3561;1.0 id3562;1.0 id3563;1.0 id3564;1.0 id3565;1.0 id3566;1.0 id3567;1.0 id3568;1.0 id3569;1.0 id3570;1.0 id3571;1.0 id3572;1.0 id3573;1.0 id3574;1.0 id3575;1.0 id3576;1.0 id3577;1.0 id3578;1.0 id3579;1.0 id3580;1.0 id3581;1.0 id3582;1.0 id3583;1.0 id3584;1.0 id3585;1.0 id3586;1.0 id3587;1.0 id3588;1.0 id3589;1.0 id3590;1.0 id3591;1.0 id3592;1.0 id3593;1.0 id3594;1.0 id3595;1.0 id3596;1.0 id3597;1.0 id3598;1.0 id3599;1.0 id3600;1.0 id3601;1.0 id3602;1.0 id3603;1.0 id3604;1.0 id3605;1.0 id3606;1.0 id3607;1.0 id3608;1.0 id3609;1.0 id3610;1.0 id3611;1.0 id3612;1.0 id3613;1.0 id3614;1.0 id3615;1.0 id3616;1.0 id3617;1.0 id3618;1.0 id3619;1.0 id3620;1.0 id3621;1.0 id3622;1.0 id3623;1.0 id3624;1.0 id3625;1.0 id3626;1.0 id3627;1.0 id3628;1.0 id3629;1.0 id3630;1.0 id3631;1.0 id3632;1.0 id3633;1.0 id3634;1.0 id3635;1.0 id3636;1.0 id3637;1.0 id3638;1.0 id3639;1.0 id3640;1.0 id3641;1.0 id3642;1.0 id3643;1.0 id3644;1.0 id3645;1.0 id3646;1.0 id3647;1.0 id3648;1.0 id3649;1.0 id3650;1.0 id3651;1.0 id3652;1.0 id3653;1.0 id3654;1.0 id3655;1.0 id3656;1.0 id3657;1.0 id3658;1.0 id3659;1.0 id3660;1.0 id3661;1.0 id3662;1.0 id3663;1.0 id3664;1.0 id3665;1.0 id3666;1.0 id3667;1.0 id3668;1.0 id3669;1.0 id3670;1.0 id3671;1.0 id3672;1.0 id3673;1.0 id3674;1.0 id3675;1.0 id3676;1.0 id3677;1.0 id3678;1.0 id3679;1.0 id3680;1.0 id3681;1.0 id3682;1.0 id3683;1.0 id3684;1.0 id3685;1.0 id3686;1.0 id3687;1.0 id3688;1.0 id3689;1.0 id3690;1.0 id3691;1.0 id3692;1.0 id3693;1.0 id3694;1.0 id3695;1.0 id3696;1.0 id3697;1.0 id3698;1.0 id3699;1.0 id3700;1.0 id3701;1.0 id3702;1.0 id3703;1.0 id3704;1.0 id3705;1.0 id3706;1.0 id3707;1.0 id3708;1.0 id3709;1.0 id3710;1.0 id3711;1.0 id3712;1.0 id3713;1.0 id3714;1.0 id3715;1.0 id3716;1.0 id3717;1.0 id3718;1.0 id3719;1.0 id3720;1.0 id3721;1.0 id3722;1.0 id3723;1.0 id3724;1.0 id3725;1.0 id3726;1.0 id3727;1.0 id3728;1.0 id3729;1.0 id3730;1.0 id3731;1.0 id3732;1.0 id3733;1.0 id3734;1.0 id3735;1.0 id3736;1.0 id3737;1.0 id3738;1.0 id3739;1.0 id3740;1.0 id3741;1.0 id3742;1.0 id3743;1.0 id3744;1.0 id3745;1.0 id3746;1.0 id3747;1.0 id3748;1.0 id3749;1.0 id3750;1.0 id3751;1.0 id3752;1.0 id3753;1.0 id3754;1.0 id3755;1.0 id3756;1.0 id3757;1.0 id3758;1.0 id3759;1.0 id3760;1.0 id3761;1.0 id3762;1.0 id3763;1.0 id3764;1.0 id3765;1.0 id3766;1.0 id3767;1.0 id3768;1.0 id3769;1.0 id3770;1.0 id3771;1.0 id3772;1.0 id3773;1.0 id3774;1.0 id3775;1.0 id3776;1.0 id3777;1.0 id3778;1.0 id3779;1.0 id3780;1.0 id3781;1.0 id3782;1.0 id3783;1.0 id3784;1.0 id3785;1.0 id3786;1.0 id3787;1.0 id3788;1.0 id3789;1.0 id3790;1.0 id3791;1.0 id3792;1.0 id3793;1.0 id3794;1.0 id3795;1.0 id3796;1.0 id3797;1.0 id3798;1.0 id3799;1.0 id3800;1.0 id3801;1.0 id3802;1.0 id3803;1.0 id3804;1.0 id3805;1.0 id3806;1.0 id3807;1.0 id3808;1.0 id3809;1.0 id3810;1.0 id3811;1.0 id3812;1.0 id3813;1.0 id3814;1.0 id3815;1.0 id3816;1.0 id3817;1.0 id3818;1.0 id3819;1.0 id3820;1.0 id3821;1.0 id3822;1.0 id3823;1.0 id3824;1.0 id3825;1.0 id3826;1.0 id3827;1.0 id3828;1.0 id3829;1.0 id3830;1.0 id3831;1.0 id3832;1.0 id3833;1.0 id3834;1.0 id3835;1.0 id3836;1.0 id3837;1.0 id3838;1.0 id3839;1.0 id3840;1.0 id3841;1.0 id3842;1.0 id3843;1.0 id3844;1.0 id3845;1.0 id3846;1.0 id3847;1.0 id3848;1.0 id3849;1.0 id3850;1.0 id3851;1.0 id3852;1.0 id3853;1.0 id3854;1.0 id3855;1.0 id3856;1.0 id3857;1.0 id3858;1.0 id3859;1.0 id3860;1.0 id3861;1.0 id3862;1.0 id3863;1.0 id3864;1.0 id3865;1.0 id3866;1.0 id3867;1.0 id3868;1.0 id3869;1.0 id3870;1.0 id3871;1.0 id3872;1.0 id3873;1.0 id3874;1.0 id3875;1.0 id3876;1.0 id3877;1.0 id3878;1.0 id3879;1.0 id3880;1.0 id3881;1.0 id3882;1.0 id3883;1.0 id3884;1.0 id3885;1.0 id3886;1.0 id3887;1.0 id3888;1.0 id3889;1.0 id3890;1.0 id3891;1.0 id3892;1.0 id3893;1.0 id3894;1.0 id3895;1.0 id3896;1.0 id3897;1.0 id3898;1.0 id3899;1.0 id3900;1.0 id3901;1.0 id3902;1.0 id3903;1.0 id3904;1.0 id3905;1.0 id3906;1.0 id3907;1.0 id3908;1.0 id3909;1.0 id3910;1.0 id3911;1.0 id3912;1.0 id3913;1.0 id3914;1.0 id3915;1.0 id3916;1.0 id3917;1.0 id3918;1.0 id3919;1.0 id3920;1.0 id3921;1.0 id3922;1.0 id3923;1.0 id3924;1.0 id3925;1.0 id3926;1.0 id3927;1.0 id3928;1.0 id3929;1.0 id3930;1.0 id3931;1.0 id3932;1.0 id3933;1.0 id3934;1.0 id3935;1.0 id3936;1.0 id3937;1.0 id3938;1.0 id3939;1.0 id3940;1.0 id3941;1.0 id3942;1.0 id3943;1.0 id3944;1.0 id3945;1.0 id3946;1.0 id3947;1.0 id3948;1.0 id3949;1.0 id3950;1.0 id3951;1.0 id3952;1.0 id3953;1.0 id3954;1.0 id3955;1.0 id3956;1.0 id3957;1.0 id3958;1.0 id3959;1.0 id3960;1.0 id3961;1.0 id3962;1.0 id3963;1.0 id3964;1.0 id3965;1.0 id3966;1.0 id3967;1.0 id3968;1.0 id3969;1.0 id3970;1.0 id3971;1.0 id3972;1.0 id3973;1.0 id3974;1.0 id3975;1.0 id3976;1.0 id3977;1.0 id3978;1.0 id3979;1.0 id3980;1.0 id3981;1.0 id3982;1.0 id3983;1.0 id3984;1.0 id3985;1.0 id3986;1.0 id3987;1.0 id3988;1.0 id3989;1.0 id3990;1.0 id3991;1.0 id3992;1.0 id3993;1.0 id3994;1.0 id3995;1.0 id3996;1.0 id3997;1.0 id3998;1.0 id3999;1.0 id4000;1.0 id4001;1.0 id4002;1.0 id4003;1.0 id4004;1.0 id4005;1.0 id4006;1.0 id4007;1.0 id4008;1.0 id4009;1.0 id4010;1.0 id4011;1.0 id4012;1.0 id4013;1.0 id4014;1.0 id4015;1.0 id4016;1.0 id4017;1.0 id4018;1.0 id4019;1.0 id4020;1.0 id4021;1.0 id4022;1.0 id4023;1.0 id4024;1.0 id4025;1.0 id4026;1.0 id4027;1.0 id4028;1.0 id4029;1.0 id4030;1.0 id4031;1.0 id4032;1.0 id4033;1.0 id4034;1.0 id4035;1.0 id4036;1.0 id4037;1.0 id4038;1.0 id4039;1.0 id4040;1.0 id4041;1.0 id4042;1.0 id4043;1.0 id4044;1.0 id4045;1.0 id4046;1.0 id4047;1.0 id4048;1.0 id4049;1.0 id4050;1.0 id4051;1.0 id4052;1.0 id4053;1.0 id4054;1.0 id4055;1.0 id4056;1.0 id4057;1.0 id4058;1.0 id4059;1.0 id4060;1.0 id4061;1.0 id4062;1.0 id4063;1.0 id4064;1.0 id4065;1.0 id4066;1.0 id4067;1.0 id4068;1.0 id4069;1.0 id4070;1.0 id4071;1.0 id4072;1.0 id4073;1.0 id4074;1.0 id4075;1.0 id4076;1.0 id4077;1.0 id4078;1.0 id4079;1.0 id4080;1.0 id4081;1.0 id4082;1.0 id4083;1.0 id4084;1.0 id4085;1.0 id4086;1.0 id4087;1.0 id4088;1.0 id4089;1.0 id4090;1.0 id4091;1.0 id4092;1.0 id4093;1.0 id4094;1.0 id4095;1.0 id4096;1.0 id4097;1.0 id4098;1.0 id4099;1.0 id4100;1.0 id4101;1.0 id4102;1.0 id4103;1.0 id4104;1.0 id4105;1.0 id4106;1.0 id4107;1.0 id4108;1.0 id4109;1.0 id4110;1.0 id4111;1.0 id4112;1.0 id4113;1.0 id4114;1.0 id4115;1.0 id4116;1.0 id4117;1.0 id4118;1.0 id4119;1.0 id4120;1.0 id4121;1.0 id4122;1.0 id4123;1.0 id4124;1.0 id4125;1.0 id4126;1.0 id4127;1.0 id4128;1.0 id4129;1.0 id4130;1.0 id4131;1.0 id4132;1.0 id4133;1.0 id4134;1.0 id4135;1.0 id4136;1.0 id4137;1.0 id4138;1.0 id4139;1.0 id4140;1.0 id4141;1.0 id4142;1.0 id4143;1.0 id4144;1.0 id4145;1.0 id4146;1.0 id4147;1.0 id4148;1.0 id4149;1.0 id4150;1.0 id4151;1.0 id4152;1.0 id4153;1.0 id4154;1.0 id4155;1.0 id4156;1.0 id4157;1.0 id4158;1.0 id4159;1.0 id4160;1.0 id4161;1.0 id4162;1.0 id4163;1.0 id4164;1.0 id4165;1.0 id4166;1.0 id4167;1.0 id4168;1.0 id4169;1.0 id4170;1.0 id4171;1.0 id4172;1.0 id4173;1.0 id4174;1.0 id4175;1.0 id4176;1.0 id4177;1.0 id4178;1.0 id4179;1.0 id4180;1.0 id4181;1.0 id4182;1.0 id4183;1.0 id4184;1.0 id4185;1.0 id4186;1.0 id4187;1.0 id4188;1.0 id4189;1.0 id4190;1.0 id4191;1.0 id4192;1.0 id4193;1.0 id4194;1.0 id4195;1.0 id4196;1.0 id4197;1.0 id4198;1.0 id4199;1.0 id4200;1.0 id4201;1.0 id4202;1.0 id4203;1.0 id4204;1.0 id4205;1.0 id4206;1.0 id4207;1.0 id4208;1.0 id4209;1.0 id4210;1.0 id4211;1.0 id4212;1.0 id4213;1.0 id4214;1.0 id4215;1.0 id4216;1.0 id4217;1.0 id4218;1.0 id4219;1.0 id4220;1.0 id4221;1.0 id4222;1.0 id4223;1.0 id4224;1.0 id4225;1.0 id4226;1.0 id4227;1.0 id4228;1.0 id4229;1.0 id4230;1.0 id4231;1.0 id4232;1.0 id4233;1.0 id4234;1.0 id4235;1.0 id4236;1.0 id4237;1.0 id4238;1.0 id4239;1.0 id4240;1.0 id4241;1.0 id4242;1.0 id4243;1.0 id4244;1.0 id4245;1.0 id4246;1.0 id4247;1.0 id4248;1.0 id4249;1.0 id4250;1.0 id4251;1.0 id4252;1.0 id4253;1.0 id4254;1.0 id4255;1.0 id4256;1.0 id4257;1.0 id4258;1.0 id4259;1.0 id4260;1.0 id4261;1.0 id4262;1.0 id4263;1.0 id4264;1.0 id4265;1.0 id4266;1.0 id4267;1.0 id4268;1.0 id4269;1.0 id4270;1.0 id4271;1.0 id4272;1.0 id4273;1.0 id4274;1.0 id4275;1.0 id4276;1.0 id4277;1.0 id4278;1.0 id4279;1.0 id4280;1.0 id4281;1.0 id4282;1.0 id4283;1.0 id4284;1.0 id4285;1.0 id4286;1.0 id4287;1.0 id4288;1.0 id4289;1.0 id4290;1.0 id4291;1.0 id4292;1.0 id4293;1.0 id4294;1.0 id4295;1.0 id4296;1.0 id4297;1.0 id4298;1.0 id4299;1.0 id4300;1.0 id4301;1.0 id4302;1.0 id4303;1.0 id4304;1.0 id4305;1.0 id4306;1.0 id4307;1.0 id4308;1.0 id4309;1.0 id4310;1.0 id4311;1.0 id4312;1.0 id4313;1.0 id4314;1.0 id4315;1.0 id4316;1.0 id4317;1.0 id4318;1.0 id4319;1.0 id4320;1.0 id4321;1.0 id4322;1.0 id4323;1.0 id4324;1.0 id4325;1.0 id4326;1.0 id4327;1.0 id4328;1.0 id4329;1.0 id4330;1.0 id4331;1.0 id4332;1.0 id4333;1.0 id4334;1.0 id4335;1.0 id4336;1.0 id4337;1.0 id4338;1.0 id4339;1.0 id4340;1.0 id4341;1.0 id4342;1.0 id4343;1.0 id4344;1.0 id4345;1.0 id4346;1.0 id4347;1.0 id4348;1.0 id4349;1.0 id4350;1.0 id4351;1.0 id4352;1.0 id4353;1.0 id4354;1.0 id4355;1.0 id4356;1.0 id4357;1.0 id4358;1.0 id4359;1.0 id4360;1.0 id4361;1.0 id4362;1.0 id4363;1.0 id4364;1.0 id4365;1.0 id4366;1.0 id4367;1.0 id4368;1.0 id4369;1.0 id4370;1.0 id4371;1.0 id4372;1.0 id4373;1.0 id4374;1.0 id4375;1.0 id4376;1.0 id4377;1.0 id4378;1.0 id4379;1.0 id4380;1.0 id4381;1.0 id4382;1.0 id4383;1.0 id4384;1.0 id4385;1.0 id4386;1.0 id4387;1.0 id4388;1.0 id4389;1.0 id4390;1.0 id4391;1.0 id4392;1.0 id4393;1.0 id4394;1.0 id4395;1.0 id4396;1.0 id4397;1.0 id4398;1.0 id4399;1.0 id4400;1.0 id4401;1.0 id4402;1.0 id4403;1.0 id4404;1.0 id4405;1.0 id4406;1.0 id4407;1.0 id4408;1.0 id4409;1.0 id4410;1.0 id4411;1.0 id4412;1.0 id4413;1.0 id4414;1.0 id4415;1.0 id4416;1.0 id4417;1.0 id4418;1.0 id4419;1.0 id4420;1.0 id4421;1.0 id4422;1.0 id4423;1.0 id4424;1.0 id4425;1.0 id4426;1.0 id4427;1.0 id4428;1.0 id4429;1.0 id4430;1.0 id4431;1.0 id4432;1.0 id4433;1.0 id4434;1.0 id4435;1.0 id4436;1.0 id4437;1.0 id4438;1.0 id4439;1.0 id4440;1.0 id4441;1.0 id4442;1.0 id4443;1.0 id4444;1.0 id4445;1.0 id4446;1.0 id4447;1.0 id4448;1.0 id4449;1.0 id4450;1.0 id4451;1.0 id4452;1.0 id4453;1.0 id4454;1.0 id4455;1.0 id4456;1.0 id4457;1.0 id4458;1.0 id4459;1.0 id4460;1.0 id4461;1.0 id4462;1.0 id4463;1.0 id4464;1.0 id4465;1.0 id4466;1.0 id4467;1.0 id4468;1.0 id4469;1.0 id4470;1.0 id4471;1.0 id4472;1.0 id4473;1.0 id4474;1.0 id4475;1.0 id4476;1.0 id4477;1.0 id4478;1.0 id4479;1.0 id4480;1.0 id4481;1.0 id4482;1.0 id4483;1.0 id4484;1.0 id4485;1.0 id4486;1.0 id4487;1.0 id4488;1.0 id4489;1.0 id4490;1.0 id4491;1.0 id4492;1.0 id4493;1.0 id4494;1.0 id4495;1.0 id4496;1.0 id4497;1.0 id4498;1.0 id4499;1.0 id4500;1.0 id4501;1.0 id4502;1.0 id4503;1.0 id4504;1.0 id4505;1.0 id4506;1.0 id4507;1.0 id4508;1.0 id4509;1.0 id4510;1.0 id4511;1.0 id4512;1.0 id4513;1.0 id4514;1.0 id4515;1.0 id4516;1.0 id4517;1.0 id4518;1.0 id4519;1.0 id4520;1.0 id4521;1.0 id4522;1.0 id4523;1.0 id4524;1.0 id4525;1.0 id4526;1.0 id4527;1.0 id4528;1.0 id4529;1.0 id4530;1.0 id4531;1.0 id4532;1.0 id4533;1.0 id4534;1.0 id4535;1.0 id4536;1.0 id4537;1.0 id4538;1.0 id4539;1.0 id4540;1.0 id4541;1.0 id4542;1.0 id4543;1.0 id4544;1.0 id4545;1.0 id4546;1.0 id4547;1.0 id4548;1.0 id4549;1.0 id4550;1.0 id4551;1.0 id4552;1.0 id4553;1.0 id4554;1.0 id4555;1.0 id4556;1.0 id4557;1.0 id4558;1.0 id4559;1.0 id4560;1.0 id4561;1.0 id4562;1.0 id4563;1.0 id4564;1.0 id4565;1.0 id4566;1.0 id4567;1.0 id4568;1.0 id4569;1.0 id4570;1.0 id4571;1.0 id4572;1.0 id4573;1.0 id4574;1.0 id4575;1.0 id4576;1.0 id4577;1.0 id4578;1.0 id4579;1.0 id4580;1.0 id4581;1.0 id4582;1.0 id4583;1.0 id4584;1.0 id4585;1.0 id4586;1.0 id4587;1.0 id4588;1.0 id4589;1.0 id4590;1.0 id4591;1.0 id4592;1.0 id4593;1.0 id4594;1.0 id4595;1.0 id4596;1.0 id4597;1.0 id4598;1.0 id4599;1.0 id4600;1.0 id4601;1.0 id4602;1.0 id4603;1.0 id4604;1.0 id4605;1.0 id4606;1.0 id4607;1.0 id4608;1.0 id4609;1.0 id4610;1.0 id4611;1.0 id4612;1.0 id4613;1.0 id4614;1.0 id4615;1.0 id4616;1.0 id4617;1.0 id4618;1.0 id4619;1.0 id4620;1.0 id4621;1.0 id4622;1.0 id4623;1.0 id4624;1.0 id4625;1.0 id4626;1.0 id4627;1.0 id4628;1.0 id4629;1.0 id4630;1.0 id4631;1.0 id4632;1.0 id4633;1.0 id4634;1.0 id4635;1.0 id4636;1.0 id4637;1.0 id4638;1.0 id4639;1.0 id4640;1.0 id4641;1.0 id4642;1.0 id4643;1.0 id4644;1.0 id4645;1.0 id4646;1.0 id4647;1.0 id4648;1.0 id4649;1.0 id4650;1.0 id4651;1.0 id4652;1.0 id4653;1.0 id4654;1.0 id4655;1.0 id4656;1.0 id4657;1.0 id4658;1.0 id4659;1.0 id4660;1.0 id4661;1.0 id4662;1.0 id4663;1.0 id4664;1.0 id4665;1.0 id4666;1.0 id4667;1.0 id4668;1.0 id4669;1.0 id4670;1.0 id4671;1.0 id4672;1.0 id4673;1.0 id4674;1.0 id4675;1.0 id4676;1.0 id4677;1.0 id4678;1.0 id4679;1.0 id4680;1.0 id4681;1.0 id4682;1.0 id4683;1.0 id4684;1.0 id4685;1.0 id4686;1.0 id4687;1.0 id4688;1.0 id4689;1.0 id4690;1.0 id4691;1.0 id4692;1.0 id4693;1.0 id4694;1.0 id4695;1.0 id4696;1.0 id4697;1.0 id4698;1.0 id4699;1.0 id4700;1.0 id4701;1.0 id4702;1.0 id4703;1.0 id4704;1.0 id4705;1.0 id4706;1.0 id4707;1.0 id4708;1.0 id4709;1.0 id4710;1.0 id4711;1.0 id4712;1.0 id4713;1.0 id4714;1.0 id4715;1.0 id4716;1.0 id4717;1.0 id4718;1.0 id4719;1.0 id4720;1.0 id4721;1.0 id4722;1.0 id4723;1.0 id4724;1.0 id4725;1.0 id4726;1.0 id4727;1.0 id4728;1.0 id4729;1.0 id4730;1.0 id4731;1.0 id4732;1.0 id4733;1.0 id4734;1.0 id4735;1.0 id4736;1.0 id4737;1.0 id4738;1.0 id4739;1.0 id4740;1.0 id4741;1.0 id4742;1.0 id4743;1.0 id4744;1.0 id4745;1.0 id4746;1.0 id4747;1.0 id4748;1.0 id4749;1.0 id4750;1.0 id4751;1.0 id4752;1.0 id4753;1.0 id4754;1.0 id4755;1.0 id4756;1.0 id4757;1.0 id4758;1.0 id4759;1.0 id4760;1.0 id4761;1.0 id4762;1.0 id4763;1.0 id4764;1.0 id4765;1.0 id4766;1.0 id4767;1.0 id4768;1.0 id4769;1.0 id4770;1.0 id4771;1.0 id4772;1.0 id4773;1.0 id4774;1.0 id4775;1.0 id4776;1.0 id4777;1.0 id4778;1.0 id4779;1.0 id4780;1.0 id4781;1.0 id4782;1.0 id4783;1.0 id4784;1.0 id4785;1.0 id4786;1.0 id4787;1.0 id4788;1.0 id4789;1.0 id4790;1.0 id4791;1.0 id4792;1.0 id4793;1.0 id4794;1.0 id4795;1.0 id4796;1.0 id4797;1.0 id4798;1.0 id4799;1.0 id4800;1.0 id4801;1.0 id4802;1.0 id4803;1.0 id4804;1.0 id4805;1.0 id4806;1.0 id4807;1.0 id4808;1.0 id4809;1.0 id4810;1.0 id4811;1.0 id4812;1.0 id4813;1.0 id4814;1.0 id4815;1.0 id4816;1.0 id4817;1.0 id4818;1.0 id4819;1.0 id4820;1.0 id4821;1.0 id4822;1.0 id4823;1.0 id4824;1.0 id4825;1.0 id4826;1.0 id4827;1.0 id4828;1.0 id4829;1.0 id4830;1.0 id4831;1.0 id4832;1.0 id4833;1.0 id4834;1.0 id4835;1.0 id4836;1.0 id4837;1.0 id4838;1.0 id4839;1.0 id4840;1.0 id4841;1.0 id4842;1.0 id4843;1.0 id4844;1.0 id4845;1.0 id4846;1.0 id4847;1.0 id4848;1.0 id4849;1.0 id4850;1.0 id4851;1.0 id4852;1.0 id4853;1.0 id4854;1.0 id4855;1.0 id4856;1.0 id4857;1.0 id4858;1.0 id4859;1.0 id4860;1.0 id4861;1.0 id4862;1.0 id4863;1.0 id4864;1.0 id4865;1.0 id4866;1.0 id4867;1.0 id4868;1.0 id4869;1.0 id4870;1.0 id4871;1.0 id4872;1.0 id4873;1.0 id4874;1.0 id4875;1.0 id4876;1.0 id4877;1.0 id4878;1.0 id4879;1.0 id4880;1.0 id4881;1.0 id4882;1.0 id4883;1.0 id4884;1.0 id4885;1.0 id4886;1.0 id4887;1.0 id4888;1.0 id4889;1.0 id4890;1.0 id4891;1.0 id4892;1.0 id4893;1.0 id4894;1.0 id4895;1.0 id4896;1.0 id4897;1.0 id4898;1.0 id4899;1.0 id4900;1.0 id4901;1.0 id4902;1.0 id4903;1.0 id4904;1.0 id4905;1.0 id4906;1.0 id4907;1.0 id4908;1.0 id4909;1.0 id4910;1.0 id4911;1.0 id4912;1.0 id4913;1.0 id4914;1.0 id4915;1.0 id4916;1.0 id4917;1.0 id4918;1.0 id4919;1.0 id4920;1.0 id4921;1.0 id4922;1.0 id4923;1.0 id4924;1.0 id4925;1.0 id4926;1.0 id4927;1.0 id4928;1.0 id4929;1.0 id4930;1.0 id4931;1.0 id4932;1.0 id4933;1.0 id4934;1.0 id4935;1.0 id4936;1.0 id4937;1.0 id4938;1.0 id4939;1.0 id4940;1.0 id4941;1.0 id4942;1.0 id4943;1.0 id4944;1.0 id4945;1.0 id4946;1.0 id4947;1.0 id4948;1.0 id4949;1.0 id4950;1.0 id4951;1.0 id4952;1.0 id4953;1.0 id4954;1.0 id4955;1.0 id4956;1.0 id4957;1.0 id4958;1.0 id4959;1.0 id4960;1.0 id4961;1.0 id4962;1.0 id4963;1.0 id4964;1.0 id4965;1.0 id4966;1.0 id4967;1.0 id4968;1.0 id4969;1.0 id4970;1.0 id4971;1.0 id4972;1.0 id4973;1.0 id4974;1.0 id4975;1.0 id4976;1.0 id4977;1.0 id4978;1.0 id4979;1.0 id4980;1.0 id4981;1.0 id4982;1.0 id4983;1.0 id4984;1.0 id4985;1.0 id4986;1.0 id4987;1.0 id4988;1.0 id4989;1.0 id4990;1.0 id4991;1.0 id4992;1.0 id4993;1.0 id4994;1.0 id4995;1.0 id4996;1.0 id4997;1.0 id4998;1.0 id4999;1.0 id5000;1.0 id5001;1.0 id5002;1.0 id5003;1.0 id5004;1.0 id5005;1.0 id5006;1.0 id5007;1.0 id5008;1.0 id5009;1.0 id5010;1.0 id5011;1.0 id5012;1.0 id5013;1.0 id5014;1.0 id5015;1.0 id5016;1.0 id5017;1.0 id5018;1.0 id5019;1.0 id5020;1.0 id5021;1.0 id5022;1.0 id5023;1.0 id5024;1.0 id5025;1.0 id5026;1.0 id5027;1.0 id5028;1.0 id5029;1.0 id5030;1.0 id5031;1.0 id5032;1.0 id5033;1.0 id5034;1.0 id5035;1.0 id5036;1.0 id5037;1.0 id5038;1.0 id5039;1.0 id5040;1.0 id5041;1.0 id5042;1.0 id5043;1.0 id5044;1.0 id5045;1.0 id5046;1.0 id5047;1.0 id5048;1.0 id5049;1.0 id5050;1.0 id5051;1.0 id5052;1.0 id5053;1.0 id5054;1.0 id5055;1.0 id5056;1.0 id5057;1.0 id5058;1.0 id5059;1.0 id5060;1.0 id5061;1.0 id5062;1.0 id5063;1.0 id5064;1.0 id5065;1.0 id5066;1.0 id5067;1.0 id5068;1.0 id5069;1.0 id5070;1.0 id5071;1.0 id5072;1.0 id5073;1.0 id5074;1.0 id5075;1.0 id5076;1.0 id5077;1.0 id5078;1.0 id5079;1.0 id5080;1.0 id5081;1.0 id5082;1.0 id5083;1.0 id5084;1.0 id5085;1.0 id5086;1.0 id5087;1.0 id5088;1.0 id5089;1.0 id5090;1.0 id5091;1.0 id5092;1.0 id5093;1.0 id5094;1.0 id5095;1.0 id5096;1.0 id5097;1.0 id5098;1.0 id5099;1.0 id5100;1.0 id5101;1.0 id5102;1.0 id5103;1.0 id5104;1.0 id5105;1.0 id5106;1.0 id5107;1.0 id5108;1.0 id5109;1.0 id5110;1.0 id5111;1.0 id5112;1.0 id5113;1.0 id5114;1.0 id5115;1.0 id5116;1.0 id5117;1.0 id5118;1.0 id5119;1.0 id5120;1.0 id5121;1.0 id5122;1.0 id5123;1.0 id5124;1.0 id5125;1.0 id5126;1.0 id5127;1.0 id5128;1.0 id5129;1.0 id5130;1.0 id5131;1.0 id5132;1.0 id5133;1.0 id5134;1.0 id5135;1.0 id5136;1.0 id5137;1.0 id5138;1.0 id5139;1.0 id5140;1.0 id5141;1.0 id5142;1.0 id5143;1.0 id5144;1.0 id5145;1.0 id5146;1.0 id5147;1.0 id5148;1.0 id5149;1.0 id5150;1.0 id5151;1.0 id5152;1.0 id5153;1.0 id5154;1.0 id5155;1.0 id5156;1.0 id5157;1.0 id5158;1.0 id5159;1.0 id5160;1.0 id5161;1.0 id5162;1.0 id5163;1.0 id5164;1.0 id5165;1.0 id5166;1.0 id5167;1.0 id5168;1.0 id5169;1.0 id5170;1.0 id5171;1.0 id5172;1.0 id5173;1.0 id5174;1.0 id5175;1.0 id5176;1.0 id5177;1.0 id5178;1.0 id5179;1.0 id5180;1.0 id5181;1.0 id5182;1.0 id5183;1.0 id5184;1.0 id5185;1.0 id5186;1.0 id5187;1.0 id5188;1.0 id5189;1.0 id5190;1.0 id5191;1.0 id5192;1.0 id5193;1.0 id5194;1.0 id5195;1.0 id5196;1.0 id5197;1.0 id5198;1.0 id5199;1.0 id5200;1.0 id5201;1.0 id5202;1.0 id5203;1.0 id5204;1.0 id5205;1.0 id5206;1.0 id5207;1.0 id5208;1.0 id5209;1.0 id5210;1.0 id5211;1.0 id5212;1.0 id5213;1.0 id5214;1.0 id5215;1.0 id5216;1.0 id5217;1.0 id5218;1.0 id5219;1.0 id5220;1.0 id5221;1.0 id5222;1.0 id5223;1.0 id5224;1.0 id5225;1.0 id5226;1.0 id5227;1.0 id5228;1.0 id5229;1.0 id5230;1.0 id5231;1.0 id5232;1.0 id5233;1.0 id5234;1.0 id5235;1.0 id5236;1.0 id5237;1.0 id5238;1.0 id5239;1.0 id5240;1.0 id5241;1.0 id5242;1.0 id5243;1.0 id5244;1.0 id5245;1.0 id5246;1.0 id5247;1.0 id5248;1.0 id5249;1.0 id5250;1.0 id5251;1.0 id5252;1.0 id5253;1.0 id5254;1.0 id5255;1.0 id5256;1.0 id5257;1.0 id5258;1.0 id5259;1.0 id5260;1.0 id5261;1.0 id5262;1.0 id5263;1.0 id5264;1.0 id5265;1.0 id5266;1.0 id5267;1.0 id5268;1.0 id5269;1.0 id5270;1.0 id5271;1.0 id5272;1.0 id5273;1.0 id5274;1.0 id5275;1.0 id5276;1.0 id5277;1.0 id5278;1.0 id5279;1.0 id5280;1.0 id5281;1.0 id5282;1.0 id5283;1.0 id5284;1.0 id5285;1.0 id5286;1.0 id5287;1.0 id5288;1.0 id5289;1.0 id5290;1.0 id5291;1.0 id5292;1.0 id5293;1.0 id5294;1.0 id5295;1.0 id5296;1.0 id5297;1.0 id5298;1.0 id5299;1.0 id5300;1.0 id5301;1.0 id5302;1.0 id5303;1.0 id5304;1.0 id5305;1.0 id5306;1.0 id5307;1.0 id5308;1.0 id5309;1.0 id5310;1.0 id5311;1.0 id5312;1.0 id5313;1.0 id5314;1.0 id5315;1.0 id5316;1.0 id5317;1.0 id5318;1.0 id5319;1.0 id5320;1.0 id5321;1.0 id5322;1.0 id5323;1.0 id5324;1.0 id5325;1.0 id5326;1.0 id5327;1.0 id5328;1.0 id5329;1.0 id5330;1.0 id5331;1.0 id5332;1.0 id5333;1.0 id5334;1.0 id5335;1.0 id5336;1.0 id5337;1.0 id5338;1.0 id5339;1.0 id5340;1.0 id5341;1.0 id5342;1.0 id5343;1.0 id5344;1.0 id5345;1.0 id5346;1.0 id5347;1.0 id5348;1.0 id5349;1.0 id5350;1.0 id5351;1.0 id5352;1.0 id5353;1.0 id5354;1.0 id5355;1.0 id5356;1.0 id5357;1.0 id5358;1.0 id5359;1.0 id5360;1.0 id5361;1.0 id5362;1.0 id5363;1.0 id5364;1.0 id5365;1.0 id5366;1.0 id5367;1.0 id5368;1.0 id5369;1.0 id5370;1.0 id5371;1.0 id5372;1.0 id5373;1.0 id5374;1.0 id5375;1.0 id5376;1.0 id5377;1.0 id5378;1.0 id5379;1.0 id5380;1.0 id5381;1.0 id5382;1.0 id5383;1.0 id5384;1.0 id5385;1.0 id5386;1.0 id5387;1.0 id5388;1.0 id5389;1.0 id5390;1.0 id5391;1.0 id5392;1.0 id5393;1.0 id5394;1.0 id5395;1.0 id5396;1.0 id5397;1.0 id5398;1.0 id5399;1.0 id5400;1.0 id5401;1.0 id5402;1.0 id5403;1.0 id5404;1.0 id5405;1.0 id5406;1.0 id5407;1.0 id5408;1.0 id5409;1.0 id5410;1.0 id5411;1.0 id5412;1.0 id5413;1.0 id5414;1.0 id5415;1.0 id5416;1.0 id5417;1.0 id5418;1.0 id5419;1.0 id5420;1.0 id5421;1.0 id5422;1.0 id5423;1.0 id5424;1.0 id5425;1.0 id5426;1.0 id5427;1.0 id5428;1.0 id5429;1.0 id5430;1.0 id5431;1.0 id5432;1.0 id5433;1.0 id5434;1.0 id5435;1.0 id5436;1.0 id5437;1.0 id5438;1.0 id5439;1.0 id5440;1.0 id5441;1.0 id5442;1.0 id5443;1.0 id5444;1.0 id5445;1.0 id5446;1.0 id5447;1.0 id5448;1.0 id5449;1.0 id5450;1.0 id5451;1.0 id5452;1.0 id5453;1.0 id5454;1.0 id5455;1.0 id5456;1.0 id5457;1.0 id5458;1.0 id5459;1.0 id5460;1.0 id5461;1.0 id5462;1.0 id5463;1.0 id5464;1.0 id5465;1.0 id5466;1.0 id5467;1.0 id5468;1.0 id5469;1.0 id5470;1.0 id5471;1.0 id5472;1.0 id5473;1.0 id5474;1.0 id5475;1.0 id5476;1.0 id5477;1.0 id5478;1.0 id5479;1.0 id5480;1.0 id5481;1.0 id5482;1.0 id5483;1.0 id5484;1.0 id5485;1.0 id5486;1.0 id5487;1.0 id5488;1.0 id5489;1.0 id5490;1.0 id5491;1.0 id5492;1.0 id5493;1.0 id5494;1.0 id5495;1.0 id5496;1.0 id5497;1.0 id5498;1.0 id5499;1.0 id5500;1.0 id5501;1.0 id5502;1.0 id5503;1.0 id5504;1.0 id5505;1.0 id5506;1.0 id5507;1.0 id5508;1.0 id5509;1.0 id5510;1.0 id5511;1.0 id5512;1.0 id5513;1.0 id5514;1.0 id5515;1.0 id5516;1.0 id5517;1.0 id5518;1.0 id5519;1.0 id5520;1.0 id5521;1.0 id5522;1.0 id5523;1.0 id5524;1.0 id5525;1.0 id5526;1.0 id5527;1.0 id5528;1.0 id5529;1.0 id5530;1.0 id5531;1.0 id5532;1.0 id5533;1.0 id5534;1.0 id5535;1.0 id5536;1.0 id5537;1.0 id5538;1.0 id5539;1.0 id5540;1.0 id5541;1.0 id5542;1.0 id5543;1.0 id5544;1.0 id5545;1.0 id5546;1.0 id5547;1.0 id5548;1.0 id5549;1.0 id5550;1.0 id5551;1.0 id5552;1.0 id5553;1.0 id5554;1.0 id5555;1.0 id5556;1.0 id5557;1.0 id5558;1.0 id5559;1.0 id5560;1.0 id5561;1.0 id5562;1.0 id5563;1.0 id5564;1.0 id5565;1.0 id5566;1.0 id5567;1.0 id5568;1.0 id5569;1.0 id5570;1.0 id5571;1.0 id5572;1.0 id5573;1.0 id5574;1.0 id5575;1.0 id5576;1.0 id5577;1.0 id5578;1.0 id5579;1.0 id5580;1.0 id5581;1.0 id5582;1.0 id5583;1.0 id5584;1.0 id5585;1.0 id5586;1.0 id5587;1.0 id5588;1.0 id5589;1.0 id5590;1.0 id5591;1.0 id5592;1.0 id5593;1.0 id5594;1.0 id5595;1.0 id5596;1.0 id5597;1.0 id5598;1.0 id5599;1.0 id5600;1.0 id5601;1.0 id5602;1.0 id5603;1.0 id5604;1.0 id5605;1.0 id5606;1.0 id5607;1.0 id5608;1.0 id5609;1.0 id5610;1.0 id5611;1.0 id5612;1.0 id5613;1.0 id5614;1.0 id5615;1.0 id5616;1.0 id5617;1.0 id5618;1.0 id5619;1.0 id5620;1.0 id5621;1.0 id5622;1.0 id5623;1.0 id5624;1.0 id5625;1.0 id5626;1.0 id5627;1.0 id5628;1.0 id5629;1.0 id5630;1.0 id5631;1.0 id5632;1.0 id5633;1.0 id5634;1.0 id5635;1.0 id5636;1.0 id5637;1.0 id5638;1.0 id5639;1.0 id5640;1.0 id5641;1.0 id5642;1.0 id5643;1.0 id5644;1.0 id5645;1.0 id5646;1.0 id5647;1.0 id5648;1.0 id5649;1.0 id5650;1.0 id5651;1.0 id5652;1.0 id5653;1.0 id5654;1.0 id5655;1.0 id5656;1.0 id5657;1.0 id5658;1.0 id5659;1.0 id5660;1.0 id5661;1.0 id5662;1.0 id5663;1.0 id5664;1.0 id5665;1.0 id5666;1.0 id5667;1.0 id5668;1.0 id5669;1.0 id5670;1.0 id5671;1.0 id5672;1.0 id5673;1.0 id5674;1.0 id5675;1.0 id5676;1.0 id5677;1.0 id5678;1.0 id5679;1.0 id5680;1.0 id5681;1.0 id5682;1.0 id5683;1.0 id5684;1.0 id5685;1.0 id5686;1.0 id5687;1.0 id5688;1.0 id5689;1.0 id5690;1.0 id5691;1.0 id5692;1.0 id5693;1.0 id5694;1.0 id5695;1.0 id5696;1.0 id5697;1.0 id5698;1.0 id5699;1.0 id5700;1.0 id5701;1.0 id5702;1.0 id5703;1.0 id5704;1.0 id5705;1.0 id5706;1.0 id5707;1.0 id5708;1.0 id5709;1.0 id5710;1.0 id5711;1.0 id5712;1.0 id5713;1.0 id5714;1.0 id5715;1.0 id5716;1.0 id5717;1.0 id5718;1.0 id5719;1.0 id5720;1.0 id5721;1.0 id5722;1.0 id5723;1.0 id5724;1.0 id5725;1.0 id5726;1.0 id5727;1.0 id5728;1.0 id5729;1.0 id5730;1.0 id5731;1.0 id5732;1.0 id5733;1.0 id5734;1.0 id5735;1.0 id5736;1.0 id5737;1.0 id5738;1.0 id5739;1.0 id5740;1.0 id5741;1.0 id5742;1.0 id5743;1.0 id5744;1.0 id5745;1.0 id5746;1.0 id5747;1.0 id5748;1.0 id5749;1.0 id5750;1.0 id5751;1.0 id5752;1.0 id5753;1.0 id5754;1.0 id5755;1.0 id5756;1.0 id5757;1.0 id5758;1.0 id5759;1.0 id5760;1.0 id5761;1.0 id5762;1.0 id5763;1.0 id5764;1.0 id5765;1.0 id5766;1.0 id5767;1.0 id5768;1.0 id5769;1.0 id5770;1.0 id5771;1.0 id5772;1.0 id5773;1.0 id5774;1.0 id5775;1.0 id5776;1.0 id5777;1.0 id5778;1.0 id5779;1.0 id5780;1.0 id5781;1.0 id5782;1.0 id5783;1.0 id5784;1.0 id5785;1.0 id5786;1.0 id5787;1.0 id5788;1.0 id5789;1.0 id5790;1.0 id5791;1.0 id5792;1.0 id5793;1.0 id5794;1.0 id5795;1.0 id5796;1.0 id5797;1.0 id5798;1.0 id5799;1.0 id5800;1.0 id5801;1.0 id5802;1.0 id5803;1.0 id5804;1.0 id5805;1.0 id5806;1.0 id5807;1.0 id5808;1.0 id5809;1.0 id5810;1.0 id5811;1.0 id5812;1.0 id5813;1.0 id5814;1.0 id5815;1.0 id5816;1.0 id5817;1.0 id5818;1.0 id5819;1.0 id5820;1.0 id5821;1.0 id5822;1.0 id5823;1.0 id5824;1.0 id5825;1.0 id5826;1.0 id5827;1.0 id5828;1.0 id5829;1.0 id5830;1.0 id5831;1.0 id5832;1.0 id5833;1.0 id5834;1.0 id5835;1.0 id5836;1.0 id5837;1.0 id5838;1.0 id5839;1.0 id5840;1.0 id5841;1.0 id5842;1.0 id5843;1.0 id5844;1.0 id5845;1.0 id5846;1.0 id5847;1.0 id5848;1.0 id5849;1.0 id5850;1.0 id5851;1.0 id5852;1.0 id5853;1.0 id5854;1.0 id5855;1.0 id5856;1.0 id5857;1.0 id5858;1.0 id5859;1.0 id5860;1.0 id5861;1.0 id5862;1.0 id5863;1.0 id5864;1.0 id5865;1.0 id5866;1.0 id5867;1.0 id5868;1.0 id5869;1.0 id5870;1.0 id5871;1.0 id5872;1.0 id5873;1.0 id5874;1.0 id5875;1.0 id5876;1.0 id5877;1.0 id5878;1.0 id5879;1.0 id5880;1.0 id5881;1.0 id5882;1.0 id5883;1.0 id5884;1.0 id5885;1.0 id5886;1.0 id5887;1.0 id5888;1.0 id5889;1.0 id5890;1.0 id5891;1.0 id5892;1.0 id5893;1.0 id5894;1.0 id5895;1.0 id5896;1.0 id5897;1.0 id5898;1.0 id5899;1.0 id5900;1.0 id5901;1.0 id5902;1.0 id5903;1.0 id5904;1.0 id5905;1.0 id5906;1.0 id5907;1.0 id5908;1.0 id5909;1.0 id5910;1.0 id5911;1.0 id5912;1.0 id5913;1.0 id5914;1.0 id5915;1.0 id5916;1.0 id5917;1.0 id5918;1.0 id5919;1.0 id5920;1.0 id5921;1.0 id5922;1.0 id5923;1.0 id5924;1.0 id5925;1.0 id5926;1.0 id5927;1.0 id5928;1.0 id5929;1.0 id5930;1.0 id5931;1.0 id5932;1.0 id5933;1.0 id5934;1.0 id5935;1.0 id5936;1.0 id5937;1.0 id5938;1.0 id5939;1.0 id5940;1.0 id5941;1.0 id5942;1.0 id5943;1.0 id5944;1.0 id5945;1.0 id5946;1.0 id5947;1.0 id5948;1.0 id5949;1.0 id5950;1.0 id5951;1.0 id5952;1.0 id5953;1.0 id5954;1.0 id5955;1.0 id5956;1.0 id5957;1.0 id5958;1.0 id5959;1.0 id5960;1.0 id5961;1.0 id5962;1.0 id5963;1.0 id5964;1.0 id5965;1.0 id5966;1.0 id5967;1.0 id5968;1.0 id5969;1.0 id5970;1.0 id5971;1.0 id5972;1.0 id5973;1.0 id5974;1.0 id5975;1.0 id5976;1.0 id5977;1.0 id5978;1.0 id5979;1.0 id5980;1.0 id5981;1.0 id5982;1.0 id5983;1.0 id5984;1.0 id5985;1.0 id5986;1.0 id5987;1.0 id5988;1.0 id5989;1.0 id5990;1.0 id5991;1.0 id5992;1.0 id5993;1.0 id5994;1.0 id5995;1.0 id5996;1.0 id5997;1.0 id5998;1.0 id5999;1.0 id6000;1.0 id6001;1.0 id6002;1.0 id6003;1.0 id6004;1.0 id6005;1.0 id6006;1.0 id6007;1.0 id6008;1.0 id6009;1.0 id6010;1.0 id6011;1.0 id6012;1.0 id6013;1.0 id6014;1.0 id6015;1.0 id6016;1.0 id6017;1.0 id6018;1.0 id6019;1.0 id6020;1.0 id6021;1.0 id6022;1.0 id6023;1.0 id6024;1.0 id6025;1.0 id6026;1.0 id6027;1.0 id6028;1.0 id6029;1.0 id6030;1.0 id6031;1.0 id6032;1.0 id6033;1.0 id6034;1.0 id6035;1.0 id6036;1.0 id6037;1.0 id6038;1.0 id6039;1.0 id6040;1.0 id6041;1.0 id6042;1.0 id6043;1.0 id6044;1.0 id6045;1.0 id6046;1.0 id6047;1.0 id6048;1.0 id6049;1.0 id6050;1.0 id6051;1.0 id6052;1.0 id6053;1.0 id6054;1.0 id6055;1.0 id6056;1.0 id6057;1.0 id6058;1.0 id6059;1.0 id6060;1.0 id6061;1.0 id6062;1.0 id6063;1.0 id6064;1.0 id6065;1.0 id6066;1.0 id6067;1.0 id6068;1.0 id6069;1.0 id6070;1.0 id6071;1.0 id6072;1.0 id6073;1.0 id6074;1.0 id6075;1.0 id6076;1.0 id6077;1.0 id6078;1.0 id6079;1.0 id6080;1.0 id6081;1.0 id6082;1.0 id6083;1.0 id6084;1.0 id6085;1.0 id6086;1.0 id6087;1.0 id6088;1.0 id6089;1.0 id6090;1.0 id6091;1.0 id6092;1.0 id6093;1.0 id6094;1.0 id6095;1.0 id6096;1.0 id6097;1.0 id6098;1.0 id6099;1.0 id6100;1.0 id6101;1.0 id6102;1.0 id6103;1.0 id6104;1.0 id6105;1.0 id6106;1.0 id6107;1.0 id6108;1.0 id6109;1.0 id6110;1.0 id6111;1.0 id6112;1.0 id6113;1.0 id6114;1.0 id6115;1.0 id6116;1.0 id6117;1.0 id6118;1.0 id6119;1.0 id6120;1.0 id6121;1.0 id6122;1.0 id6123;1.0 id6124;1.0 id6125;1.0 id6126;1.0 id6127;1.0 id6128;1.0 id6129;1.0 id6130;1.0 id6131;1.0 id6132;1.0 id6133;1.0 id6134;1.0 id6135;1.0 id6136;1.0 id6137;1.0 id6138;1.0 id6139;1.0 id6140;1.0 id6141;1.0 id6142;1.0 id6143;1.0 id6144;1.0 id6145;1.0 id6146;1.0 id6147;1.0 id6148;1.0 id6149;1.0 id6150;1.0 id6151;1.0 id6152;1.0 id6153;1.0 id6154;1.0 id6155;1.0 id6156;1.0 id6157;1.0 id6158;1.0 id6159;1.0 id6160;1.0 id6161;1.0 id6162;1.0 id6163;1.0 id6164;1.0 id6165;1.0 id6166;1.0 id6167;1.0 id6168;1.0 id6169;1.0 id6170;1.0 id6171;1.0 id6172;1.0 id6173;1.0 id6174;1.0 id6175;1.0 id6176;1.0 id6177;1.0 id6178;1.0 id6179;1.0 id6180;1.0 id6181;1.0 id6182;1.0 id6183;1.0 id6184;1.0 id6185;1.0 id6186;1.0 id6187;1.0 id6188;1.0 id6189;1.0 id6190;1.0 id6191;1.0 id6192;1.0 id6193;1.0 id6194;1.0 id6195;1.0 id6196;1.0 id6197;1.0 id6198;1.0 id6199;1.0 id6200;1.0 id6201;1.0 id6202;1.0 id6203;1.0 id6204;1.0 id6205;1.0 id6206;1.0 id6207;1.0 id6208;1.0 id6209;1.0 id6210;1.0 id6211;1.0 id6212;1.0 id6213;1.0 id6214;1.0 id6215;1.0 id6216;1.0 id6217;1.0 id6218;1.0 id6219;1.0 id6220;1.0 id6221;1.0 id6222;1.0 id6223;1.0 id6224;1.0 id6225;1.0 id6226;1.0 id6227;1.0 id6228;1.0 id6229;1.0 id6230;1.0 id6231;1.0 id6232;1.0 id6233;1.0 id6234;1.0 id6235;1.0 id6236;1.0 id6237;1.0 id6238;1.0 id6239;1.0 id6240;1.0 id6241;1.0 id6242;1.0 id6243;1.0 id6244;1.0 id6245;1.0 id6246;1.0 id6247;1.0 id6248;1.0 id6249;1.0 id6250;1.0 id6251;1.0 id6252;1.0 id6253;1.0 id6254;1.0 id6255;1.0 id6256;1.0 id6257;1.0 id6258;1.0 id6259;1.0 id6260;1.0 id6261;1.0 id6262;1.0 id6263;1.0 id6264;1.0 id6265;1.0 id6266;1.0 id6267;1.0 id6268;1.0 id6269;1.0 id6270;1.0 id6271;1.0 id6272;1.0 id6273;1.0 id6274;1.0 id6275;1.0 id6276;1.0 id6277;1.0 id6278;1.0 id6279;1.0 id6280;1.0 id6281;1.0 id6282;1.0 id6283;1.0 id6284;1.0 id6285;1.0 id6286;1.0 id6287;1.0 id6288;1.0 id6289;1.0 id6290;1.0 id6291;1.0 id6292;1.0 id6293;1.0 id6294;1.0 id6295;1.0 id6296;1.0 id6297;1.0 id6298;1.0 id6299;1.0 id6300;1.0 id6301;1.0 id6302;1.0 id6303;1.0 id6304;1.0 id6305;1.0 id6306;1.0 id6307;1.0 id6308;1.0 id6309;1.0 id6310;1.0 id6311;1.0 id6312;1.0 id6313;1.0 id6314;1.0 id6315;1.0 id6316;1.0 id6317;1.0 id6318;1.0 id6319;1.0 id6320;1.0 id6321;1.0 id6322;1.0 id6323;1.0 id6324;1.0 id6325;1.0 id6326;1.0 id6327;1.0 id6328;1.0 id6329;1.0 id6330;1.0 id6331;1.0 id6332;1.0 id6333;1.0 id6334;1.0 id6335;1.0 id6336;1.0 id6337;1.0 id6338;1.0 id6339;1.0 id6340;1.0 id6341;1.0 id6342;1.0 id6343;1.0 id6344;1.0 id6345;1.0 id6346;1.0 id6347;1.0 id6348;1.0 id6349;1.0 id6350;1.0 id6351;1.0 id6352;1.0 id6353;1.0 id6354;1.0 id6355;1.0 id6356;1.0 id6357;1.0 id6358;1.0 id6359;1.0 id6360;1.0 id6361;1.0 id6362;1.0 id6363;1.0 id6364;1.0 id6365;1.0 id6366;1.0 id6367;1.0 id6368;1.0 id6369;1.0 id6370;1.0 id6371;1.0 id6372;1.0 id6373;1.0 id6374;1.0 id6375;1.0 id6376;1.0 id6377;1.0 id6378;1.0 id6379;1.0 id6380;1.0 id6381;1.0 id6382;1.0 id6383;1.0 id6384;1.0 id6385;1.0 id6386;1.0 id6387;1.0 id6388;1.0 id6389;1.0 id6390;1.0 id6391;1.0 id6392;1.0 id6393;1.0 id6394;1.0 id6395;1.0 id6396;1.0 id6397;1.0 id6398;1.0 id6399;1.0 id6400;1.0 id6401;1.0 id6402;1.0 id6403;1.0 id6404;1.0 id6405;1.0 id6406;1.0 id6407;1.0 id6408;1.0 id6409;1.0 id6410;1.0 id6411;1.0 id6412;1.0 id6413;1.0 id6414;1.0 id6415;1.0 id6416;1.0 id6417;1.0 id6418;1.0 id6419;1.0 id6420;1.0 id6421;1.0 id6422;1.0 id6423;1.0 id6424;1.0 id6425;1.0 id6426;1.0 id6427;1.0 id6428;1.0 id6429;1.0 id6430;1.0 id6431;1.0 id6432;1.0 id6433;1.0 id6434;1.0 id6435;1.0 id6436;1.0 id6437;1.0 id6438;1.0 id6439;1.0 id6440;1.0 id6441;1.0 id6442;1.0 id6443;1.0 id6444;1.0 id6445;1.0 id6446;1.0 id6447;1.0 id6448;1.0 id6449;1.0 id6450;1.0 id6451;1.0 id6452;1.0 id6453;1.0 id6454;1.0 id6455;1.0 id6456;1.0 id6457;1.0 id6458;1.0 id6459;1.0 id6460;1.0 id6461;1.0 id6462;1.0 id6463;1.0 id6464;1.0 id6465;1.0 id6466;1.0 id6467;1.0 id6468;1.0 id6469;1.0 id6470;1.0 id6471;1.0 id6472;1.0 id6473;1.0 id6474;1.0 id6475;1.0 id6476;1.0 id6477;1.0 id6478;1.0 id6479;1.0 id6480;1.0 id6481;1.0 id6482;1.0 id6483;1.0 id6484;1.0 id6485;1.0 id6486;1.0 id6487;1.0 id6488;1.0 id6489;1.0 id6490;1.0 id6491;1.0 id6492;1.0 id6493;1.0 id6494;1.0 id6495;1.0 id6496;1.0 id6497;1.0 id6498;1.0 id6499;1.0 id6500;1.0 id6501;1.0 id6502;1.0 id6503;1.0 id6504;1.0 id6505;1.0 id6506;1.0 id6507;1.0 id6508;1.0 id6509;1.0 id6510;1.0 id6511;1.0 id6512;1.0 id6513;1.0 id6514;1.0 id6515;1.0 id6516;1.0 id6517;1.0 id6518;1.0 id6519;1.0 id6520;1.0 id6521;1.0 id6522;1.0 id6523;1.0 id6524;1.0 id6525;1.0 id6526;1.0 id6527;1.0 id6528;1.0 id6529;1.0 id6530;1.0 id6531;1.0 id6532;1.0 id6533;1.0 id6534;1.0 id6535;1.0 id6536;1.0 id6537;1.0 id6538;1.0 id6539;1.0 id6540;1.0 id6541;1.0 id6542;1.0 id6543;1.0 id6544;1.0 id6545;1.0 id6546;1.0 id6547;1.0 id6548;1.0 id6549;1.0 id6550;1.0 id6551;1.0 id6552;1.0 id6553;1.0 id6554;1.0 id6555;1.0 id6556;1.0 id6557;1.0 id6558;1.0 id6559;1.0 id6560;1.0 id6561;1.0 id6562;1.0 id6563;1.0 id6564;1.0 id6565;1.0 id6566;1.0 id6567;1.0 id6568;1.0 id6569;1.0 id6570;1.0 id6571;1.0 id6572;1.0 id6573;1.0 id6574;1.0 id6575;1.0 id6576;1.0 id6577;1.0 id6578;1.0 id6579;1.0 id6580;1.0 id6581;1.0 id6582;1.0 id6583;1.0 id6584;1.0 id6585;1.0 id6586;1.0 id6587;1.0 id6588;1.0 id6589;1.0 id6590;1.0 id6591;1.0 id6592;1.0 id6593;1.0 id6594;1.0 id6595;1.0 id6596;1.0 id6597;1.0 id6598;1.0 id6599;1.0 id6600;1.0 id6601;1.0 id6602;1.0 id6603;1.0 id6604;1.0 id6605;1.0 id6606;1.0 id6607;1.0 id6608;1.0 id6609;1.0 id6610;1.0 id6611;1.0 id6612;1.0 id6613;1.0 id6614;1.0 id6615;1.0 id6616;1.0 id6617;1.0 id6618;1.0 id6619;1.0 id6620;1.0 id6621;1.0 id6622;1.0 id6623;1.0 id6624;1.0 id6625;1.0 id6626;1.0 id6627;1.0 id6628;1.0 id6629;1.0 id6630;1.0 id6631;1.0 id6632;1.0 id6633;1.0 id6634;1.0 id6635;1.0 id6636;1.0 id6637;1.0 id6638;1.0 id6639;1.0 id6640;1.0 id6641;1.0 id6642;1.0 id6643;1.0 id6644;1.0 id6645;1.0 id6646;1.0 id6647;1.0 id6648;1.0 id6649;1.0 id6650;1.0 id6651;1.0 id6652;1.0 id6653;1.0 id6654;1.0 id6655;1.0 id6656;1.0 id6657;1.0 id6658;1.0 id6659;1.0 id6660;1.0 id6661;1.0 id6662;1.0 id6663;1.0 id6664;1.0 id6665;1.0 id6666;1.0 id6667;1.0 id6668;1.0 id6669;1.0 id6670;1.0 id6671;1.0 id6672;1.0 id6673;1.0 id6674;1.0 id6675;1.0 id6676;1.0 id6677;1.0 id6678;1.0 id6679;1.0 id6680;1.0 id6681;1.0 id6682;1.0 id6683;1.0 id6684;1.0 id6685;1.0 id6686;1.0 id6687;1.0 id6688;1.0 id6689;1.0 id6690;1.0 id6691;1.0 id6692;1.0 id6693;1.0 id6694;1.0 id6695;1.0 id6696;1.0 id6697;1.0 id6698;1.0 id6699;1.0 id6700;1.0 id6701;1.0 id6702;1.0 id6703;1.0 id6704;1.0 id6705;1.0 id6706;1.0 id6707;1.0 id6708;1.0 id6709;1.0 id6710;1.0 id6711;1.0 id6712;1.0 id6713;1.0 id6714;1.0 id6715;1.0 id6716;1.0 id6717;1.0 id6718;1.0 id6719;1.0 id6720;1.0 id6721;1.0 id6722;1.0 id6723;1.0 id6724;1.0 id6725;1.0 id6726;1.0 id6727;1.0 id6728;1.0 id6729;1.0 id6730;1.0 id6731;1.0 id6732;1.0 id6733;1.0 id6734;1.0 id6735;1.0 id6736;1.0 id6737;1.0 id6738;1.0 id6739;1.0 id6740;1.0 id6741;1.0 id6742;1.0 id6743;1.0 id6744;1.0 id6745;1.0 id6746;1.0 id6747;1.0 id6748;1.0 id6749;1.0 id6750;1.0 id6751;1.0 id6752;1.0 id6753;1.0 id6754;1.0 id6755;1.0 id6756;1.0 id6757;1.0 id6758;1.0 id6759;1.0 id6760;1.0 id6761;1.0 id6762;1.0 id6763;1.0 id6764;1.0 id6765;1.0 id6766;1.0 id6767;1.0 id6768;1.0 id6769;1.0 id6770;1.0 id6771;1.0 id6772;1.0 id6773;1.0 id6774;1.0 id6775;1.0 id6776;1.0 id6777;1.0 id6778;1.0 id6779;1.0 id6780;1.0 id6781;1.0 id6782;1.0 id6783;1.0 id6784;1.0 id6785;1.0 id6786;1.0 id6787;1.0 id6788;1.0 id6789;1.0 id6790;1.0 id6791;1.0 id6792;1.0 id6793;1.0 id6794;1.0 id6795;1.0 id6796;1.0 id6797;1.0 id6798;1.0 id6799;1.0 id6800;1.0 id6801;1.0 id6802;1.0 id6803;1.0 id6804;1.0 id6805;1.0 id6806;1.0 id6807;1.0 id6808;1.0 id6809;1.0 id6810;1.0 id6811;1.0 id6812;1.0 id6813;1.0 id6814;1.0 id6815;1.0 id6816;1.0 id6817;1.0 id6818;1.0 id6819;1.0 id6820;1.0 id6821;1.0 id6822;1.0 id6823;1.0 id6824;1.0 id6825;1.0 id6826;1.0 id6827;1.0 id6828;1.0 id6829;1.0 id6830;1.0 id6831;1.0 id6832;1.0 id6833;1.0 id6834;1.0 id6835;1.0 id6836;1.0 id6837;1.0 id6838;1.0 id6839;1.0 id6840;1.0 id6841;1.0 id6842;1.0 id6843;1.0 id6844;1.0 id6845;1.0 id6846;1.0 id6847;1.0 id6848;1.0 id6849;1.0 id6850;1.0 id6851;1.0 id6852;1.0 id6853;1.0 id6854;1.0 id6855;1.0 id6856;1.0 id6857;1.0 id6858;1.0 id6859;1.0 id6860;1.0 id6861;1.0 id6862;1.0 id6863;1.0 id6864;1.0 id6865;1.0 id6866;1.0 id6867;1.0 id6868;1.0 id6869;1.0 id6870;1.0 id6871;1.0 id6872;1.0 id6873;1.0 id6874;1.0 id6875;1.0 id6876;1.0 id6877;1.0 id6878;1.0 id6879;1.0 id6880;1.0 id6881;1.0 id6882;1.0 id6883;1.0 id6884;1.0 id6885;1.0 id6886;1.0 id6887;1.0 id6888;1.0 id6889;1.0 id6890;1.0 id6891;1.0 id6892;1.0 id6893;1.0 id6894;1.0 id6895;1.0 id6896;1.0 id6897;1.0 id6898;1.0 id6899;1.0 id6900;1.0 id6901;1.0 id6902;1.0 id6903;1.0 id6904;1.0 id6905;1.0 id6906;1.0 id6907;1.0 id6908;1.0 id6909;1.0 id6910;1.0 id6911;1.0 id6912;1.0 id6913;1.0 id6914;1.0 id6915;1.0 id6916;1.0 id6917;1.0 id6918;1.0 id6919;1.0 id6920;1.0 id6921;1.0 id6922;1.0 id6923;1.0 id6924;1.0 id6925;1.0 id6926;1.0 id6927;1.0 id6928;1.0 id6929;1.0 id6930;1.0 id6931;1.0 id6932;1.0 id6933;1.0 id6934;1.0 id6935;1.0 id6936;1.0 id6937;1.0 id6938;1.0 id6939;1.0 id6940;1.0 id6941;1.0 id6942;1.0 id6943;1.0 id6944;1.0 id6945;1.0 id6946;1.0 id6947;1.0 id6948;1.0 id6949;1.0 id6950;1.0 id6951;1.0 id6952;1.0 id6953;1.0 id6954;1.0 id6955;1.0 id6956;1.0 id6957;1.0 id6958;1.0 id6959;1.0 id6960;1.0 id6961;1.0 id6962;1.0 id6963;1.0 id6964;1.0 id6965;1.0 id6966;1.0 id6967;1.0 id6968;1.0 id6969;1.0 id6970;1.0 id6971;1.0 id6972;1.0 id6973;1.0 id6974;1.0 id6975;1.0 id6976;1.0 id6977;1.0 id6978;1.0 id6979;1.0 id6980;1.0 id6981;1.0 id6982;1.0 id6983;1.0 id6984;1.0 id6985;1.0 id6986;1.0 id6987;1.0 id6988;1.0 id6989;1.0 id6990;1.0 id6991;1.0 id6992;1.0 id6993;1.0 id6994;1.0 id6995;1.0 id6996;1.0 id6997;1.0 id6998;1.0 id6999;1.0 id7000;1.0 id7001;1.0 id7002;1.0 id7003;1.0 id7004;1.0 id7005;1.0 id7006;1.0 id7007;1.0 id7008;1.0 id7009;1.0 id7010;1.0 id7011;1.0 id7012;1.0 id7013;1.0 id7014;1.0 id7015;1.0 id7016;1.0 id7017;1.0 id7018;1.0 id7019;1.0 id7020;1.0 id7021;1.0 id7022;1.0 id7023;1.0 id7024;1.0 id7025;1.0 id7026;1.0 id7027;1.0 id7028;1.0 id7029;1.0 id7030;1.0 id7031;1.0 id7032;1.0 id7033;1.0 id7034;1.0 id7035;1.0 id7036;1.0 id7037;1.0 id7038;1.0 id7039;1.0 id7040;1.0 id7041;1.0 id7042;1.0 id7043;1.0 id7044;1.0 id7045;1.0 id7046;1.0 id7047;1.0 id7048;1.0 id7049;1.0 id7050;1.0 id7051;1.0 id7052;1.0 id7053;1.0 id7054;1.0 id7055;1.0 id7056;1.0 id7057;1.0 id7058;1.0 id7059;1.0 id7060;1.0 id7061;1.0 id7062;1.0 id7063;1.0 id7064;1.0 id7065;1.0 id7066;1.0 id7067;1.0 id7068;1.0 id7069;1.0 id7070;1.0 id7071;1.0 id7072;1.0 id7073;1.0 id7074;1.0 id7075;1.0 id7076;1.0 id7077;1.0 id7078;1.0 id7079;1.0 id7080;1.0 id7081;1.0 id7082;1.0 id7083;1.0 id7084;1.0 id7085;1.0 id7086;1.0 id7087;1.0 id7088;1.0 id7089;1.0 id7090;1.0 id7091;1.0 id7092;1.0 id7093;1.0 id7094;1.0 id7095;1.0 id7096;1.0 id7097;1.0 id7098;1.0 id7099;1.0 id7100;1.0 id7101;1.0 id7102;1.0 id7103;1.0 id7104;1.0 id7105;1.0 id7106;1.0 id7107;1.0 id7108;1.0 id7109;1.0 id7110;1.0 id7111;1.0 id7112;1.0 id7113;1.0 id7114;1.0 id7115;1.0 id7116;1.0 id7117;1.0 id7118;1.0 id7119;1.0 id7120;1.0 id7121;1.0 id7122;1.0 id7123;1.0 id7124;1.0 id7125;1.0 id7126;1.0 id7127;1.0 id7128;1.0 id7129;1.0 id7130;1.0 id7131;1.0 id7132;1.0 id7133;1.0 id7134;1.0 id7135;1.0 id7136;1.0 id7137;1.0 id7138;1.0 id7139;1.0 id7140;1.0 id7141;1.0 id7142;1.0 id7143;1.0 id7144;1.0 id7145;1.0 id7146;1.0 id7147;1.0 id7148;1.0 id7149;1.0 id7150;1.0 id7151;1.0 id7152;1.0 id7153;1.0 id7154;1.0 id7155;1.0 id7156;1.0 id7157;1.0 id7158;1.0 id7159;1.0 id7160;1.0 id7161;1.0 id7162;1.0 id7163;1.0 id7164;1.0 id7165;1.0 id7166;1.0 id7167;1.0 id7168;1.0 id7169;1.0 id7170;1.0 id7171;1.0 id7172;1.0 id7173;1.0 id7174;1.0 id7175;1.0 id7176;1.0 id7177;1.0 id7178;1.0 id7179;1.0 id7180;1.0 id7181;1.0 id7182;1.0 id7183;1.0 id7184;1.0 id7185;1.0 id7186;1.0 id7187;1.0 id7188;1.0 id7189;1.0 id7190;1.0 id7191;1.0 id7192;1.0 id7193;1.0 id7194;1.0 id7195;1.0 id7196;1.0 id7197;1.0 id7198;1.0 id7199;1.0 id7200;1.0 id7201;1.0 id7202;1.0 id7203;1.0 id7204;1.0 id7205;1.0 id7206;1.0 id7207;1.0 id7208;1.0 id7209;1.0 id7210;1.0 id7211;1.0 id7212;1.0 id7213;1.0 id7214;1.0 id7215;1.0 id7216;1.0 id7217;1.0 id7218;1.0 id7219;1.0 id7220;1.0 id7221;1.0 id7222;1.0 id7223;1.0 id7224;1.0 id7225;1.0 id7226;1.0 id7227;1.0 id7228;1.0 id7229;1.0 id7230;1.0 id7231;1.0 id7232;1.0 id7233;1.0 id7234;1.0 id7235;1.0 id7236;1.0 id7237;1.0 id7238;1.0 id7239;1.0 id7240;1.0 id7241;1.0 id7242;1.0 id7243;1.0 id7244;1.0 id7245;1.0 id7246;1.0 id7247;1.0 id7248;1.0 id7249;1.0 id7250;1.0 id7251;1.0 id7252;1.0 id7253;1.0 id7254;1.0 id7255;1.0 id7256;1.0 id7257;1.0 id7258;1.0 id7259;1.0 id7260;1.0 id7261;1.0 id7262;1.0 id7263;1.0 id7264;1.0 id7265;1.0 id7266;1.0 id7267;1.0 id7268;1.0 id7269;1.0 id7270;1.0 id7271;1.0 id7272;1.0 id7273;1.0 id7274;1.0 id7275;1.0 id7276;1.0 id7277;1.0 id7278;1.0 id7279;1.0 id7280;1.0 id7281;1.0 id7282;1.0 id7283;1.0 id7284;1.0 id7285;1.0 id7286;1.0 id7287;1.0 id7288;1.0 id7289;1.0 id7290;1.0 id7291;1.0 id7292;1.0 id7293;1.0 id7294;1.0 id7295;1.0 id7296;1.0 id7297;1.0 id7298;1.0 id7299;1.0 id7300;1.0 id7301;1.0 id7302;1.0 id7303;1.0 id7304;1.0 id7305;1.0 id7306;1.0 id7307;1.0 id7308;1.0 id7309;1.0 id7310;1.0 id7311;1.0 id7312;1.0 id7313;1.0 id7314;1.0 id7315;1.0 id7316;1.0 id7317;1.0 id7318;1.0 id7319;1.0 id7320;1.0 id7321;1.0 id7322;1.0 id7323;1.0 id7324;1.0 id7325;1.0 id7326;1.0 id7327;1.0 id7328;1.0 id7329;1.0 id7330;1.0 id7331;1.0 id7332;1.0 id7333;1.0 id7334;1.0 id7335;1.0 id7336;1.0 id7337;1.0 id7338;1.0 id7339;1.0 id7340;1.0 id7341;1.0 id7342;1.0 id7343;1.0 id7344;1.0 id7345;1.0 id7346;1.0 id7347;1.0 id7348;1.0 id7349;1.0 id7350;1.0 id7351;1.0 id7352;1.0 id7353;1.0 id7354;1.0 id7355;1.0 id7356;1.0 id7357;1.0 id7358;1.0 id7359;1.0 id7360;1.0 id7361;1.0 id7362;1.0 id7363;1.0 id7364;1.0 id7365;1.0 id7366;1.0 id7367;1.0 id7368;1.0 id7369;1.0 id7370;1.0 id7371;1.0 id7372;1.0 id7373;1.0 id7374;1.0 id7375;1.0 id7376;1.0 id7377;1.0 id7378;1.0 id7379;1.0 id7380;1.0 id7381;1.0 id7382;1.0 id7383;1.0 id7384;1.0 id7385;1.0 id7386;1.0 id7387;1.0 id7388;1.0 id7389;1.0 id7390;1.0 id7391;1.0 id7392;1.0 id7393;1.0 id7394;1.0 id7395;1.0 id7396;1.0 id7397;1.0 id7398;1.0 id7399;1.0 id7400;1.0 id7401;1.0 id7402;1.0 id7403;1.0 id7404;1.0 id7405;1.0 id7406;1.0 id7407;1.0 id7408;1.0 id7409;1.0 id7410;1.0 id7411;1.0 id7412;1.0 id7413;1.0 id7414;1.0 id7415;1.0 id7416;1.0 id7417;1.0 id7418;1.0 id7419;1.0 id7420;1.0 id7421;1.0 id7422;1.0 id7423;1.0 id7424;1.0 id7425;1.0 id7426;1.0 id7427;1.0 id7428;1.0 id7429;1.0 id7430;1.0 id7431;1.0 id7432;1.0 id7433;1.0 id7434;1.0 id7435;1.0 id7436;1.0 id7437;1.0 id7438;1.0 id7439;1.0 id7440;1.0 id7441;1.0 id7442;1.0 id7443;1.0 id7444;1.0 id7445;1.0 id7446;1.0 id7447;1.0 id7448;1.0 id7449;1.0 id7450;1.0 id7451;1.0 id7452;1.0 id7453;1.0 id7454;1.0 id7455;1.0 id7456;1.0 id7457;1.0 id7458;1.0 id7459;1.0 id7460;1.0 id7461;1.0 id7462;1.0 id7463;1.0 id7464;1.0 id7465;1.0 id7466;1.0 id7467;1.0 id7468;1.0 id7469;1.0 id7470;1.0 id7471;1.0 id7472;1.0 id7473;1.0 id7474;1.0 id7475;1.0 id7476;1.0 id7477;1.0 id7478;1.0 id7479;1.0 id7480;1.0 id7481;1.0 id7482;1.0 id7483;1.0 id7484;1.0 id7485;1.0 id7486;1.0 id7487;1.0 id7488;1.0 id7489;1.0 id7490;1.0 id7491;1.0 id7492;1.0 id7493;1.0 id7494;1.0 id7495;1.0 id7496;1.0 id7497;1.0 id7498;1.0 id7499;1.0 id7500;1.0 id7501;1.0 id7502;1.0 id7503;1.0 id7504;1.0 id7505;1.0 id7506;1.0 id7507;1.0 id7508;1.0 id7509;1.0 id7510;1.0 id7511;1.0 id7512;1.0 id7513;1.0 id7514;1.0 id7515;1.0 id7516;1.0 id7517;1.0 id7518;1.0 id7519;1.0 id7520;1.0 id7521;1.0 id7522;1.0 id7523;1.0 id7524;1.0 id7525;1.0 id7526;1.0 id7527;1.0 id7528;1.0 id7529;1.0 id7530;1.0 id7531;1.0 id7532;1.0 id7533;1.0 id7534;1.0 id7535;1.0 id7536;1.0 id7537;1.0 id7538;1.0 id7539;1.0 id7540;1.0 id7541;1.0 id7542;1.0 id7543;1.0 id7544;1.0 id7545;1.0 id7546;1.0 id7547;1.0 id7548;1.0 id7549;1.0 id7550;1.0 id7551;1.0 id7552;1.0 id7553;1.0 id7554;1.0 id7555;1.0 id7556;1.0 id7557;1.0 id7558;1.0 id7559;1.0 id7560;1.0 id7561;1.0 id7562;1.0 id7563;1.0 id7564;1.0 id7565;1.0 id7566;1.0 id7567;1.0 id7568;1.0 id7569;1.0 id7570;1.0 id7571;1.0 id7572;1.0 id7573;1.0 id7574;1.0 id7575;1.0 id7576;1.0 id7577;1.0 id7578;1.0 id7579;1.0 id7580;1.0 id7581;1.0 id7582;1.0 id7583;1.0 id7584;1.0 id7585;1.0 id7586;1.0 id7587;1.0 id7588;1.0 id7589;1.0 id7590;1.0 id7591;1.0 id7592;1.0 id7593;1.0 id7594;1.0 id7595;1.0 id7596;1.0 id7597;1.0 id7598;1.0 id7599;1.0 id7600;1.0 id7601;1.0 id7602;1.0 id7603;1.0 id7604;1.0 id7605;1.0 id7606;1.0 id7607;1.0 id7608;1.0 id7609;1.0 id7610;1.0 id7611;1.0 id7612;1.0 id7613;1.0 id7614;1.0 id7615;1.0 id7616;1.0 id7617;1.0 id7618;1.0 id7619;1.0 id7620;1.0 id7621;1.0 id7622;1.0 id7623;1.0 id7624;1.0 id7625;1.0 id7626;1.0 id7627;1.0 id7628;1.0 id7629;1.0 id7630;1.0 id7631;1.0 id7632;1.0 id7633;1.0 id7634;1.0 id7635;1.0 id7636;1.0 id7637;1.0 id7638;1.0 id7639;1.0 id7640;1.0 id7641;1.0 id7642;1.0 id7643;1.0 id7644;1.0 id7645;1.0 id7646;1.0 id7647;1.0 id7648;1.0 id7649;1.0 id7650;1.0 id7651;1.0 id7652;1.0 id7653;1.0 id7654;1.0 id7655;1.0 id7656;1.0 id7657;1.0 id7658;1.0 id7659;1.0 id7660;1.0 id7661;1.0 id7662;1.0 id7663;1.0 id7664;1.0 id7665;1.0 id7666;1.0 id7667;1.0 id7668;1.0 id7669;1.0 id7670;1.0 id7671;1.0 id7672;1.0 id7673;1.0 id7674;1.0 id7675;1.0 id7676;1.0 id7677;1.0 id7678;1.0 id7679;1.0 id7680;1.0 id7681;1.0 id7682;1.0 id7683;1.0 id7684;1.0 id7685;1.0 id7686;1.0 id7687;1.0 id7688;1.0 id7689;1.0 id7690;1.0 id7691;1.0 id7692;1.0 id7693;1.0 id7694;1.0 id7695;1.0 id7696;1.0 id7697;1.0 id7698;1.0 id7699;1.0 id7700;1.0 id7701;1.0 id7702;1.0 id7703;1.0 id7704;1.0 id7705;1.0 id7706;1.0 id7707;1.0 id7708;1.0 id7709;1.0 id7710;1.0 id7711;1.0 id7712;1.0 id7713;1.0 id7714;1.0 id7715;1.0 id7716;1.0 id7717;1.0 id7718;1.0 id7719;1.0 id7720;1.0 id7721;1.0 id7722;1.0 id7723;1.0 id7724;1.0 id7725;1.0 id7726;1.0 id7727;1.0 id7728;1.0 id7729;1.0 id7730;1.0 id7731;1.0 id7732;1.0 id7733;1.0 id7734;1.0 id7735;1.0 id7736;1.0 id7737;1.0 id7738;1.0 id7739;1.0 id7740;1.0 id7741;1.0 id7742;1.0 id7743;1.0 id7744;1.0 id7745;1.0 id7746;1.0 id7747;1.0 id7748;1.0 id7749;1.0 id7750;1.0 id7751;1.0 id7752;1.0 id7753;1.0 id7754;1.0 id7755;1.0 id7756;1.0 id7757;1.0 id7758;1.0 id7759;1.0 id7760;1.0 id7761;1.0 id7762;1.0 id7763;1.0 id7764;1.0 id7765;1.0 id7766;1.0 id7767;1.0 id7768;1.0 id7769;1.0 id7770;1.0 id7771;1.0 id7772;1.0 id7773;1.0 id7774;1.0 id7775;1.0 id7776;1.0 id7777;1.0 id7778;1.0 id7779;1.0 id7780;1.0 id7781;1.0 id7782;1.0 id7783;1.0 id7784;1.0 id7785;1.0 id7786;1.0 id7787;1.0 id7788;1.0 id7789;1.0 id7790;1.0 id7791;1.0 id7792;1.0 id7793;1.0 id7794;1.0 id7795;1.0 id7796;1.0 id7797;1.0 id7798;1.0 id7799;1.0 id7800;1.0 id7801;1.0 id7802;1.0 id7803;1.0 id7804;1.0 id7805;1.0 id7806;1.0 id7807;1.0 id7808;1.0 id7809;1.0 id7810;1.0 id7811;1.0 id7812;1.0 id7813;1.0 id7814;1.0 id7815;1.0 id7816;1.0 id7817;1.0 id7818;1.0 id7819;1.0 id7820;1.0 id7821;1.0 id7822;1.0 id7823;1.0 id7824;1.0 id7825;1.0 id7826;1.0 id7827;1.0 id7828;1.0 id7829;1.0 id7830;1.0 id7831;1.0 id7832;1.0 id7833;1.0 id7834;1.0 id7835;1.0 id7836;1.0 id7837;1.0 id7838;1.0 id7839;1.0 id7840;1.0 id7841;1.0 id7842;1.0 id7843;1.0 id7844;1.0 id7845;1.0 id7846;1.0 id7847;1.0 id7848;1.0 id7849;1.0 id7850;1.0 id7851;1.0 id7852;1.0 id7853;1.0 id7854;1.0 id7855;1.0 id7856;1.0 id7857;1.0 id7858;1.0 id7859;1.0 id7860;1.0 id7861;1.0 id7862;1.0 id7863;1.0 id7864;1.0 id7865;1.0 id7866;1.0 id7867;1.0 id7868;1.0 id7869;1.0 id7870;1.0 id7871;1.0 id7872;1.0 id7873;1.0 id7874;1.0 id7875;1.0 id7876;1.0 id7877;1.0 id7878;1.0 id7879;1.0 id7880;1.0 id7881;1.0 id7882;1.0 id7883;1.0 id7884;1.0 id7885;1.0 id7886;1.0 id7887;1.0 id7888;1.0 id7889;1.0 id7890;1.0 id7891;1.0 id7892;1.0 id7893;1.0 id7894;1.0 id7895;1.0 id7896;1.0 id7897;1.0 id7898;1.0 id7899;1.0 id7900;1.0 id7901;1.0 id7902;1.0 id7903;1.0 id7904;1.0 id7905;1.0 id7906;1.0 id7907;1.0 id7908;1.0 id7909;1.0 id7910;1.0 id7911;1.0 id7912;1.0 id7913;1.0 id7914;1.0 id7915;1.0 id7916;1.0 id7917;1.0 id7918;1.0 id7919;1.0 id7920;1.0 id7921;1.0 id7922;1.0 id7923;1.0 id7924;1.0 id7925;1.0 id7926;1.0 id7927;1.0 id7928;1.0 id7929;1.0 id7930;1.0 id7931;1.0 id7932;1.0 id7933;1.0 id7934;1.0 id7935;1.0 id7936;1.0 id7937;1.0 id7938;1.0 id7939;1.0 id7940;1.0 id7941;1.0 id7942;1.0 id7943;1.0 id7944;1.0 id7945;1.0 id7946;1.0 id7947;1.0 id7948;1.0 id7949;1.0 id7950;1.0 id7951;1.0 id7952;1.0 id7953;1.0 id7954;1.0 id7955;1.0 id7956;1.0 id7957;1.0 id7958;1.0 id7959;1.0 id7960;1.0 id7961;1.0 id7962;1.0 id7963;1.0 id7964;1.0 id7965;1.0 id7966;1.0 id7967;1.0 id7968;1.0 id7969;1.0 id7970;1.0 id7971;1.0 id7972;1.0 id7973;1.0 id7974;1.0 id7975;1.0 id7976;1.0 id7977;1.0 id7978;1.0 id7979;1.0 id7980;1.0 id7981;1.0 id7982;1.0 id7983;1.0 id7984;1.0 id7985;1.0 id7986;1.0 id7987;1.0 id7988;1.0 id7989;1.0 id7990;1.0 id7991;1.0 id7992;1.0 id7993;1.0 id7994;1.0 id7995;1.0 id7996;1.0 id7997;1.0 id7998;1.0 id7999;1.0 id8000;1.0 id8001;1.0 id8002;1.0 id8003;1.0 id8004;1.0 id8005;1.0 id8006;1.0 id8007;1.0 id8008;1.0 id8009;1.0 id8010;1.0 id8011;1.0 id8012;1.0 id8013;1.0 id8014;1.0 id8015;1.0 id8016;1.0 id8017;1.0 id8018;1.0 id8019;1.0 id8020;1.0 id8021;1.0 id8022;1.0 id8023;1.0 id8024;1.0 id8025;1.0 id8026;1.0 id8027;1.0 id8028;1.0 id8029;1.0 id8030;1.0 id8031;1.0 id8032;1.0 id8033;1.0 id8034;1.0 id8035;1.0 id8036;1.0 id8037;1.0 id8038;1.0 id8039;1.0 id8040;1.0 id8041;1.0 id8042;1.0 id8043;1.0 id8044;1.0 id8045;1.0 id8046;1.0 id8047;1.0 id8048;1.0 id8049;1.0 id8050;1.0 id8051;1.0 id8052;1.0 id8053;1.0 id8054;1.0 id8055;1.0 id8056;1.0 id8057;1.0 id8058;1.0 id8059;1.0 id8060;1.0 id8061;1.0 id8062;1.0 id8063;1.0 id8064;1.0 id8065;1.0 id8066;1.0 id8067;1.0 id8068;1.0 id8069;1.0 id8070;1.0 id8071;1.0 id8072;1.0 id8073;1.0 id8074;1.0 id8075;1.0 id8076;1.0 id8077;1.0 id8078;1.0 id8079;1.0 id8080;1.0 id8081;1.0 id8082;1.0 id8083;1.0 id8084;1.0 id8085;1.0 id8086;1.0 id8087;1.0 id8088;1.0 id8089;1.0 id8090;1.0 id8091;1.0 id8092;1.0 id8093;1.0 id8094;1.0 id8095;1.0 id8096;1.0 id8097;1.0 id8098;1.0 id8099;1.0 id8100;1.0 id8101;1.0 id8102;1.0 id8103;1.0 id8104;1.0 id8105;1.0 id8106;1.0 id8107;1.0 id8108;1.0 id8109;1.0 id8110;1.0 id8111;1.0 id8112;1.0 id8113;1.0 id8114;1.0 id8115;1.0 id8116;1.0 id8117;1.0 id8118;1.0 id8119;1.0 id8120;1.0 id8121;1.0 id8122;1.0 id8123;1.0 id8124;1.0 id8125;1.0 id8126;1.0 id8127;1.0 id8128;1.0 id8129;1.0 id8130;1.0 id8131;1.0 id8132;1.0 id8133;1.0 id8134;1.0 id8135;1.0 id8136;1.0 id8137;1.0 id8138;1.0 id8139;1.0 id8140;1.0 id8141;1.0 id8142;1.0 id8143;1.0 id8144;1.0 id8145;1.0 id8146;1.0 id8147;1.0 id8148;1.0 id8149;1.0 id8150;1.0 id8151;1.0 id8152;1.0 id8153;1.0 id8154;1.0 id8155;1.0 id8156;1.0 id8157;1.0 id8158;1.0 id8159;1.0 id8160;1.0 id8161;1.0 id8162;1.0 id8163;1.0 id8164;1.0 id8165;1.0 id8166;1.0 id8167;1.0 id8168;1.0 id8169;1.0 id8170;1.0 id8171;1.0 id8172;1.0 id8173;1.0 id8174;1.0 id8175;1.0 id8176;1.0 id8177;1.0 id8178;1.0 id8179;1.0 id8180;1.0 id8181;1.0 id8182;1.0 id8183;1.0 id8184;1.0 id8185;1.0 id8186;1.0 id8187;1.0 id8188;1.0 id8189;1.0 id8190;1.0 id8191;1.0 id8192;1.0 id8193;1.0 id8194;1.0 id8195;1.0 id8196;1.0 id8197;1.0 id8198;1.0 id8199;1.0 id8200;1.0 id8201;1.0 id8202;1.0 id8203;1.0 id8204;1.0 id8205;1.0 id8206;1.0 id8207;1.0 id8208;1.0 id8209;1.0 id8210;1.0 id8211;1.0 id8212;1.0 id8213;1.0 id8214;1.0 id8215;1.0 id8216;1.0 id8217;1.0 id8218;1.0 id8219;1.0 id8220;1.0 id8221;1.0 id8222;1.0 id8223;1.0 id8224;1.0 id8225;1.0 id8226;1.0 id8227;1.0 id8228;1.0 id8229;1.0 id8230;1.0 id8231;1.0 id8232;1.0 id8233;1.0 id8234;1.0 id8235;1.0 id8236;1.0 id8237;1.0 id8238;1.0 id8239;1.0 id8240;1.0 id8241;1.0 id8242;1.0 id8243;1.0 id8244;1.0 id8245;1.0 id8246;1.0 id8247;1.0 id8248;1.0 id8249;1.0 id8250;1.0 id8251;1.0 id8252;1.0 id8253;1.0 id8254;1.0 id8255;1.0 id8256;1.0 id8257;1.0 id8258;1.0 id8259;1.0 id8260;1.0 id8261;1.0 id8262;1.0 id8263;1.0 id8264;1.0 id8265;1.0 id8266;1.0 id8267;1.0 id8268;1.0 id8269;1.0 id8270;1.0 id8271;1.0 id8272;1.0 id8273;1.0 id8274;1.0 id8275;1.0 id8276;1.0 id8277;1.0 id8278;1.0 id8279;1.0 id8280;1.0 id8281;1.0 id8282;1.0 id8283;1.0 id8284;1.0 id8285;1.0 id8286;1.0 id8287;1.0 id8288;1.0 id8289;1.0 id8290;1.0 id8291;1.0 id8292;1.0 id8293;1.0 id8294;1.0 id8295;1.0 id8296;1.0 id8297;1.0 id8298;1.0 id8299;1.0 id8300;1.0 id8301;1.0 id8302;1.0 id8303;1.0 id8304;1.0 id8305;1.0 id8306;1.0 id8307;1.0 id8308;1.0 id8309;1.0 id8310;1.0 id8311;1.0 id8312;1.0 id8313;1.0 id8314;1.0 id8315;1.0 id8316;1.0 id8317;1.0 id8318;1.0 id8319;1.0 id8320;1.0 id8321;1.0 id8322;1.0 id8323;1.0 id8324;1.0 id8325;1.0 id8326;1.0 id8327;1.0 id8328;1.0 id8329;1.0 id8330;1.0 id8331;1.0 id8332;1.0 id8333;1.0 id8334;1.0 id8335;1.0 id8336;1.0 id8337;1.0 id8338;1.0 id8339;1.0 id8340;1.0 id8341;1.0 id8342;1.0 id8343;1.0 id8344;1.0 id8345;1.0 id8346;1.0 id8347;1.0 id8348;1.0 id8349;1.0 id8350;1.0 id8351;1.0 id8352;1.0 id8353;1.0 id8354;1.0 id8355;1.0 id8356;1.0 id8357;1.0 id8358;1.0 id8359;1.0 id8360;1.0 id8361;1.0 id8362;1.0 id8363;1.0 id8364;1.0 id8365;1.0 id8366;1.0 id8367;1.0 id8368;1.0 id8369;1.0 id8370;1.0 id8371;1.0 id8372;1.0 id8373;1.0 id8374;1.0 id8375;1.0 id8376;1.0 id8377;1.0 id8378;1.0 id8379;1.0 id8380;1.0 id8381;1.0 id8382;1.0 id8383;1.0 id8384;1.0 id8385;1.0 id8386;1.0 id8387;1.0 id8388;1.0 id8389;1.0 id8390;1.0 id8391;1.0 id8392;1.0 id8393;1.0 id8394;1.0 id8395;1.0 id8396;1.0 id8397;1.0 id8398;1.0 id8399;1.0 id8400;1.0 id8401;1.0 id8402;1.0 id8403;1.0 id8404;1.0 id8405;1.0 id8406;1.0 id8407;1.0 id8408;1.0 id8409;1.0 id8410;1.0 id8411;1.0 id8412;1.0 id8413;1.0 id8414;1.0 id8415;1.0 id8416;1.0 id8417;1.0 id8418;1.0 id8419;1.0 id8420;1.0 id8421;1.0 id8422;1.0 id8423;1.0 id8424;1.0 id8425;1.0 id8426;1.0 id8427;1.0 id8428;1.0 id8429;1.0 id8430;1.0 id8431;1.0 id8432;1.0 id8433;1.0 id8434;1.0 id8435;1.0 id8436;1.0 id8437;1.0 id8438;1.0 id8439;1.0 id8440;1.0 id8441;1.0 id8442;1.0 id8443;1.0 id8444;1.0 id8445;1.0 id8446;1.0 id8447;1.0 id8448;1.0 id8449;1.0 id8450;1.0 id8451;1.0 id8452;1.0 id8453;1.0 id8454;1.0 id8455;1.0 id8456;1.0 id8457;1.0 id8458;1.0 id8459;1.0 id8460;1.0 id8461;1.0 id8462;1.0 id8463;1.0 id8464;1.0 id8465;1.0 id8466;1.0 id8467;1.0 id8468;1.0 id8469;1.0 id8470;1.0 id8471;1.0 id8472;1.0 id8473;1.0 id8474;1.0 id8475;1.0 id8476;1.0 id8477;1.0 id8478;1.0 id8479;1.0 id8480;1.0 id8481;1.0 id8482;1.0 id8483;1.0 id8484;1.0 id8485;1.0 id8486;1.0 id8487;1.0 id8488;1.0 id8489;1.0 id8490;1.0 id8491;1.0 id8492;1.0 id8493;1.0 id8494;1.0 id8495;1.0 id8496;1.0 id8497;1.0 id8498;1.0 id8499;1.0 id8500;1.0 id8501;1.0 id8502;1.0 id8503;1.0 id8504;1.0 id8505;1.0 id8506;1.0 id8507;1.0 id8508;1.0 id8509;1.0 id8510;1.0 id8511;1.0 id8512;1.0 id8513;1.0 id8514;1.0 id8515;1.0 id8516;1.0 id8517;1.0 id8518;1.0 id8519;1.0 id8520;1.0 id8521;1.0 id8522;1.0 id8523;1.0 id8524;1.0 id8525;1.0 id8526;1.0 id8527;1.0 id8528;1.0 id8529;1.0 id8530;1.0 id8531;1.0 id8532;1.0 id8533;1.0 id8534;1.0 id8535;1.0 id8536;1.0 id8537;1.0 id8538;1.0 id8539;1.0 id8540;1.0 id8541;1.0 id8542;1.0 id8543;1.0 id8544;1.0 id8545;1.0 id8546;1.0 id8547;1.0 id8548;1.0 id8549;1.0 id8550;1.0 id8551;1.0 id8552;1.0 id8553;1.0 id8554;1.0 id8555;1.0 id8556;1.0 id8557;1.0 id8558;1.0 id8559;1.0 id8560;1.0 id8561;1.0 id8562;1.0 id8563;1.0 id8564;1.0 id8565;1.0 id8566;1.0 id8567;1.0 id8568;1.0 id8569;1.0 id8570;1.0 id8571;1.0 id8572;1.0 id8573;1.0 id8574;1.0 id8575;1.0 id8576;1.0 id8577;1.0 id8578;1.0 id8579;1.0 id8580;1.0 id8581;1.0 id8582;1.0 id8583;1.0 id8584;1.0 id8585;1.0 id8586;1.0 id8587;1.0 id8588;1.0 id8589;1.0 id8590;1.0 id8591;1.0 id8592;1.0 id8593;1.0 id8594;1.0 id8595;1.0 id8596;1.0 id8597;1.0 id8598;1.0 id8599;1.0 id8600;1.0 id8601;1.0 id8602;1.0 id8603;1.0 id8604;1.0 id8605;1.0 id8606;1.0 id8607;1.0 id8608;1.0 id8609;1.0 id8610;1.0 id8611;1.0 id8612;1.0 id8613;1.0 id8614;1.0 id8615;1.0 id8616;1.0 id8617;1.0 id8618;1.0 id8619;1.0 id8620;1.0 id8621;1.0 id8622;1.0 id8623;1.0 id8624;1.0 id8625;1.0 id8626;1.0 id8627;1.0 id8628;1.0 id8629;1.0 id8630;1.0 id8631;1.0 id8632;1.0 id8633;1.0 id8634;1.0 id8635;1.0 id8636;1.0 id8637;1.0 id8638;1.0 id8639;1.0 id8640;1.0 id8641;1.0 id8642;1.0 id8643;1.0 id8644;1.0 id8645;1.0 id8646;1.0 id8647;1.0 id8648;1.0 id8649;1.0 id8650;1.0 id8651;1.0 id8652;1.0 id8653;1.0 id8654;1.0 id8655;1.0 id8656;1.0 id8657;1.0 id8658;1.0 id8659;1.0 id8660;1.0 id8661;1.0 id8662;1.0 id8663;1.0 id8664;1.0 id8665;1.0 id8666;1.0 id8667;1.0 id8668;1.0 id8669;1.0 id8670;1.0 id8671;1.0 id8672;1.0 id8673;1.0 id8674;1.0 id8675;1.0 id8676;1.0 id8677;1.0 id8678;1.0 id8679;1.0 id8680;1.0 id8681;1.0 id8682;1.0 id8683;1.0 id8684;1.0 id8685;1.0 id8686;1.0 id8687;1.0 id8688;1.0 id8689;1.0 id8690;1.0 id8691;1.0 id8692;1.0 id8693;1.0 id8694;1.0 id8695;1.0 id8696;1.0 id8697;1.0 id8698;1.0 id8699;1.0 id8700;1.0 id8701;1.0 id8702;1.0 id8703;1.0 id8704;1.0 id8705;1.0 id8706;1.0 id8707;1.0 id8708;1.0 id8709;1.0 id8710;1.0 id8711;1.0 id8712;1.0 id8713;1.0 id8714;1.0 id8715;1.0 id8716;1.0 id8717;1.0 id8718;1.0 id8719;1.0 id8720;1.0 id8721;1.0 id8722;1.0 id8723;1.0 id8724;1.0 id8725;1.0 id8726;1.0 id8727;1.0 id8728;1.0 id8729;1.0 id8730;1.0 id8731;1.0 id8732;1.0 id8733;1.0 id8734;1.0 id8735;1.0 id8736;1.0 id8737;1.0 id8738;1.0 id8739;1.0 id8740;1.0 id8741;1.0 id8742;1.0 id8743;1.0 id8744;1.0 id8745;1.0 id8746;1.0 id8747;1.0 id8748;1.0 id8749;1.0 id8750;1.0 id8751;1.0 id8752;1.0 id8753;1.0 id8754;1.0 id8755;1.0 id8756;1.0 id8757;1.0 id8758;1.0 id8759;1.0 id8760;1.0 id8761;1.0 id8762;1.0 id8763;1.0 id8764;1.0 id8765;1.0 id8766;1.0 id8767;1.0 id8768;1.0 id8769;1.0 id8770;1.0 id8771;1.0 id8772;1.0 id8773;1.0 id8774;1.0 id8775;1.0 id8776;1.0 id8777;1.0 id8778;1.0 id8779;1.0 id8780;1.0 id8781;1.0 id8782;1.0 id8783;1.0 id8784;1.0 id8785;1.0 id8786;1.0 id8787;1.0 id8788;1.0 id8789;1.0 id8790;1.0 id8791;1.0 id8792;1.0 id8793;1.0 id8794;1.0 id8795;1.0 id8796;1.0 id8797;1.0 id8798;1.0 id8799;1.0 id8800;1.0 id8801;1.0 id8802;1.0 id8803;1.0 id8804;1.0 id8805;1.0 id8806;1.0 id8807;1.0 id8808;1.0 id8809;1.0 id8810;1.0 id8811;1.0 id8812;1.0 id8813;1.0 id8814;1.0 id8815;1.0 id8816;1.0 id8817;1.0 id8818;1.0 id8819;1.0 id8820;1.0 id8821;1.0 id8822;1.0 id8823;1.0 id8824;1.0 id8825;1.0 id8826;1.0 id8827;1.0 id8828;1.0 id8829;1.0 id8830;1.0 id8831;1.0 id8832;1.0 id8833;1.0 id8834;1.0 id8835;1.0 id8836;1.0 id8837;1.0 id8838;1.0 id8839;1.0 id8840;1.0 id8841;1.0 id8842;1.0 id8843;1.0 id8844;1.0 id8845;1.0 id8846;1.0 id8847;1.0 id8848;1.0 id8849;1.0 id8850;1.0 id8851;1.0 id8852;1.0 id8853;1.0 id8854;1.0 id8855;1.0 id8856;1.0 id8857;1.0 id8858;1.0 id8859;1.0 id8860;1.0 id8861;1.0 id8862;1.0 id8863;1.0 id8864;1.0 id8865;1.0 id8866;1.0 id8867;1.0 id8868;1.0 id8869;1.0 id8870;1.0 id8871;1.0 id8872;1.0 id8873;1.0 id8874;1.0 id8875;1.0 id8876;1.0 id8877;1.0 id8878;1.0 id8879;1.0 id8880;1.0 id8881;1.0 id8882;1.0 id8883;1.0 id8884;1.0 id8885;1.0 id8886;1.0 id8887;1.0 id8888;1.0 id8889;1.0 id8890;1.0 id8891;1.0 id8892;1.0 id8893;1.0 id8894;1.0 id8895;1.0 id8896;1.0 id8897;1.0 id8898;1.0 id8899;1.0 id8900;1.0 id8901;1.0 id8902;1.0 id8903;1.0 id8904;1.0 id8905;1.0 id8906;1.0 id8907;1.0 id8908;1.0 id8909;1.0 id8910;1.0 id8911;1.0 id8912;1.0 id8913;1.0 id8914;1.0 id8915;1.0 id8916;1.0 id8917;1.0 id8918;1.0 id8919;1.0 id8920;1.0 id8921;1.0 id8922;1.0 id8923;1.0 id8924;1.0 id8925;1.0 id8926;1.0 id8927;1.0 id8928;1.0 id8929;1.0 id8930;1.0 id8931;1.0 id8932;1.0 id8933;1.0 id8934;1.0 id8935;1.0 id8936;1.0 id8937;1.0 id8938;1.0 id8939;1.0 id8940;1.0 id8941;1.0 id8942;1.0 id8943;1.0 id8944;1.0 id8945;1.0 id8946;1.0 id8947;1.0 id8948;1.0 id8949;1.0 id8950;1.0 id8951;1.0 id8952;1.0 id8953;1.0 id8954;1.0 id8955;1.0 id8956;1.0 id8957;1.0 id8958;1.0 id8959;1.0 id8960;1.0 id8961;1.0 id8962;1.0 id8963;1.0 id8964;1.0 id8965;1.0 id8966;1.0 id8967;1.0 id8968;1.0 id8969;1.0 id8970;1.0 id8971;1.0 id8972;1.0 id8973;1.0 id8974;1.0 id8975;1.0 id8976;1.0 id8977;1.0 id8978;1.0 id8979;1.0 id8980;1.0 id8981;1.0 id8982;1.0 id8983;1.0 id8984;1.0 id8985;1.0 id8986;1.0 id8987;1.0 id8988;1.0 id8989;1.0 id8990;1.0 id8991;1.0 id8992;1.0 id8993;1.0 id8994;1.0 id8995;1.0 id8996;1.0 id8997;1.0 id8998;1.0 id8999;1.0 id9000;1.0 id9001;1.0 id9002;1.0 id9003;1.0 id9004;1.0 id9005;1.0 id9006;1.0 id9007;1.0 id9008;1.0 id9009;1.0 id9010;1.0 id9011;1.0 id9012;1.0 id9013;1.0 id9014;1.0 id9015;1.0 id9016;1.0 id9017;1.0 id9018;1.0 id9019;1.0 id9020;1.0 id9021;1.0 id9022;1.0 id9023;1.0 id9024;1.0 id9025;1.0 id9026;1.0 id9027;1.0 id9028;1.0 id9029;1.0 id9030;1.0 id9031;1.0 id9032;1.0 id9033;1.0 id9034;1.0 id9035;1.0 id9036;1.0 id9037;1.0 id9038;1.0 id9039;1.0 id9040;1.0 id9041;1.0 id9042;1.0 id9043;1.0 id9044;1.0 id9045;1.0 id9046;1.0 id9047;1.0 id9048;1.0 id9049;1.0 id9050;1.0 id9051;1.0 id9052;1.0 id9053;1.0 id9054;1.0 id9055;1.0 id9056;1.0 id9057;1.0 id9058;1.0 id9059;1.0 id9060;1.0 id9061;1.0 id9062;1.0 id9063;1.0 id9064;1.0 id9065;1.0 id9066;1.0 id9067;1.0 id9068;1.0 id9069;1.0 id9070;1.0 id9071;1.0 id9072;1.0 id9073;1.0 id9074;1.0 id9075;1.0 id9076;1.0 id9077;1.0 id9078;1.0 id9079;1.0 id9080;1.0 id9081;1.0 id9082;1.0 id9083;1.0 id9084;1.0 id9085;1.0 id9086;1.0 id9087;1.0 id9088;1.0 id9089;1.0 id9090;1.0 id9091;1.0 id9092;1.0 id9093;1.0 id9094;1.0 id9095;1.0 id9096;1.0 id9097;1.0 id9098;1.0 id9099;1.0 id9100;1.0 id9101;1.0 id9102;1.0 id9103;1.0 id9104;1.0 id9105;1.0 id9106;1.0 id9107;1.0 id9108;1.0 id9109;1.0 id9110;1.0 id9111;1.0 id9112;1.0 id9113;1.0 id9114;1.0 id9115;1.0 id9116;1.0 id9117;1.0 id9118;1.0 id9119;1.0 id9120;1.0 id9121;1.0 id9122;1.0 id9123;1.0 id9124;1.0 id9125;1.0 id9126;1.0 id9127;1.0 id9128;1.0 id9129;1.0 id9130;1.0 id9131;1.0 id9132;1.0 id9133;1.0 id9134;1.0 id9135;1.0 id9136;1.0 id9137;1.0 id9138;1.0 id9139;1.0 id9140;1.0 id9141;1.0 id9142;1.0 id9143;1.0 id9144;1.0 id9145;1.0 id9146;1.0 id9147;1.0 id9148;1.0 id9149;1.0 id9150;1.0 id9151;1.0 id9152;1.0 id9153;1.0 id9154;1.0 id9155;1.0 id9156;1.0 id9157;1.0 id9158;1.0 id9159;1.0 id9160;1.0 id9161;1.0 id9162;1.0 id9163;1.0 id9164;1.0 id9165;1.0 id9166;1.0 id9167;1.0 id9168;1.0 id9169;1.0 id9170;1.0 id9171;1.0 id9172;1.0 id9173;1.0 id9174;1.0 id9175;1.0 id9176;1.0 id9177;1.0 id9178;1.0 id9179;1.0 id9180;1.0 id9181;1.0 id9182;1.0 id9183;1.0 id9184;1.0 id9185;1.0 id9186;1.0 id9187;1.0 id9188;1.0 id9189;1.0 id9190;1.0 id9191;1.0 id9192;1.0 id9193;1.0 id9194;1.0 id9195;1.0 id9196;1.0 id9197;1.0 id9198;1.0 id9199;1.0 id9200;1.0 id9201;1.0 id9202;1.0 id9203;1.0 id9204;1.0 id9205;1.0 id9206;1.0 id9207;1.0 id9208;1.0 id9209;1.0 id9210;1.0 id9211;1.0 id9212;1.0 id9213;1.0 id9214;1.0 id9215;1.0 id9216;1.0 id9217;1.0 id9218;1.0 id9219;1.0 id9220;1.0 id9221;1.0 id9222;1.0 id9223;1.0 id9224;1.0 id9225;1.0 id9226;1.0 id9227;1.0 id9228;1.0 id9229;1.0 id9230;1.0 id9231;1.0 id9232;1.0 id9233;1.0 id9234;1.0 id9235;1.0 id9236;1.0 id9237;1.0 id9238;1.0 id9239;1.0 id9240;1.0 id9241;1.0 id9242;1.0 id9243;1.0 id9244;1.0 id9245;1.0 id9246;1.0 id9247;1.0 id9248;1.0 id9249;1.0 id9250;1.0 id9251;1.0 id9252;1.0 id9253;1.0 id9254;1.0 id9255;1.0 id9256;1.0 id9257;1.0 id9258;1.0 id9259;1.0 id9260;1.0 id9261;1.0 id9262;1.0 id9263;1.0 id9264;1.0 id9265;1.0 id9266;1.0 id9267;1.0 id9268;1.0 id9269;1.0 id9270;1.0 id9271;1.0 id9272;1.0 id9273;1.0 id9274;1.0 id9275;1.0 id9276;1.0 id9277;1.0 id9278;1.0 id9279;1.0 id9280;1.0 id9281;1.0 id9282;1.0 id9283;1.0 id9284;1.0 id9285;1.0 id9286;1.0 id9287;1.0 id9288;1.0 id9289;1.0 id9290;1.0 id9291;1.0 id9292;1.0 id9293;1.0 id9294;1.0 id9295;1.0 id9296;1.0 id9297;1.0 id9298;1.0 id9299;1.0 id9300;1.0 id9301;1.0 id9302;1.0 id9303;1.0 id9304;1.0 id9305;1.0 id9306;1.0 id9307;1.0 id9308;1.0 id9309;1.0 id9310;1.0 id9311;1.0 id9312;1.0 id9313;1.0 id9314;1.0 id9315;1.0 id9316;1.0 id9317;1.0 id9318;1.0 id9319;1.0 id9320;1.0 id9321;1.0 id9322;1.0 id9323;1.0 id9324;1.0 id9325;1.0 id9326;1.0 id9327;1.0 id9328;1.0 id9329;1.0 id9330;1.0 id9331;1.0 id9332;1.0 id9333;1.0 id9334;1.0 id9335;1.0 id9336;1.0 id9337;1.0 id9338;1.0 id9339;1.0 id9340;1.0 id9341;1.0 id9342;1.0 id9343;1.0 id9344;1.0 id9345;1.0 id9346;1.0 id9347;1.0 id9348;1.0 id9349;1.0 id9350;1.0 id9351;1.0 id9352;1.0 id9353;1.0 id9354;1.0 id9355;1.0 id9356;1.0 id9357;1.0 id9358;1.0 id9359;1.0 id9360;1.0 id9361;1.0 id9362;1.0 id9363;1.0 id9364;1.0 id9365;1.0 id9366;1.0 id9367;1.0 id9368;1.0 id9369;1.0 id9370;1.0 id9371;1.0 id9372;1.0 id9373;1.0 id9374;1.0 id9375;1.0 id9376;1.0 id9377;1.0 id9378;1.0 id9379;1.0 id9380;1.0 id9381;1.0 id9382;1.0 id9383;1.0 id9384;1.0 id9385;1.0 id9386;1.0 id9387;1.0 id9388;1.0 id9389;1.0 id9390;1.0 id9391;1.0 id9392;1.0 id9393;1.0 id9394;1.0 id9395;1.0 id9396;1.0 id9397;1.0 id9398;1.0 id9399;1.0 id9400;1.0 id9401;1.0 id9402;1.0 id9403;1.0 id9404;1.0 id9405;1.0 id9406;1.0 id9407;1.0 id9408;1.0 id9409;1.0 id9410;1.0 id9411;1.0 id9412;1.0 id9413;1.0 id9414;1.0 id9415;1.0 id9416;1.0 id9417;1.0 id9418;1.0 id9419;1.0 id9420;1.0 id9421;1.0 id9422;1.0 id9423;1.0 id9424;1.0 id9425;1.0 id9426;1.0 id9427;1.0 id9428;1.0 id9429;1.0 id9430;1.0 id9431;1.0 id9432;1.0 id9433;1.0 id9434;1.0 id9435;1.0 id9436;1.0 id9437;1.0 id9438;1.0 id9439;1.0 id9440;1.0 id9441;1.0 id9442;1.0 id9443;1.0 id9444;1.0 id9445;1.0 id9446;1.0 id9447;1.0 id9448;1.0 id9449;1.0 id9450;1.0 id9451;1.0 id9452;1.0 id9453;1.0 id9454;1.0 id9455;1.0 id9456;1.0 id9457;1.0 id9458;1.0 id9459;1.0 id9460;1.0 id9461;1.0 id9462;1.0 id9463;1.0 id9464;1.0 id9465;1.0 id9466;1.0 id9467;1.0 id9468;1.0 id9469;1.0 id9470;1.0 id9471;1.0 id9472;1.0 id9473;1.0 id9474;1.0 id9475;1.0 id9476;1.0 id9477;1.0 id9478;1.0 id9479;1.0 id9480;1.0 id9481;1.0 id9482;1.0 id9483;1.0 id9484;1.0 id9485;1.0 id9486;1.0 id9487;1.0 id9488;1.0 id9489;1.0 id9490;1.0 id9491;1.0 id9492;1.0 id9493;1.0 id9494;1.0 id9495;1.0 id9496;1.0 id9497;1.0 id9498;1.0 id9499;1.0 id9500;1.0 id9501;1.0 id9502;1.0 id9503;1.0 id9504;1.0 id9505;1.0 id9506;1.0 id9507;1.0 id9508;1.0 id9509;1.0 id9510;1.0 id9511;1.0 id9512;1.0 id9513;1.0 id9514;1.0 id9515;1.0 id9516;1.0 id9517;1.0 id9518;1.0 id9519;1.0 id9520;1.0 id9521;1.0 id9522;1.0 id9523;1.0 id9524;1.0 id9525;1.0 id9526;1.0 id9527;1.0 id9528;1.0 id9529;1.0 id9530;1.0 id9531;1.0 id9532;1.0 id9533;1.0 id9534;1.0 id9535;1.0 id9536;1.0 id9537;1.0 id9538;1.0 id9539;1.0 id9540;1.0 id9541;1.0 id9542;1.0 id9543;1.0 id9544;1.0 id9545;1.0 id9546;1.0 id9547;1.0 id9548;1.0 id9549;1.0 id9550;1.0 id9551;1.0 id9552;1.0 id9553;1.0 id9554;1.0 id9555;1.0 id9556;1.0 id9557;1.0 id9558;1.0 id9559;1.0 id9560;1.0 id9561;1.0 id9562;1.0 id9563;1.0 id9564;1.0 id9565;1.0 id9566;1.0 id9567;1.0 id9568;1.0 id9569;1.0 id9570;1.0 id9571;1.0 id9572;1.0 id9573;1.0 id9574;1.0 id9575;1.0 id9576;1.0 id9577;1.0 id9578;1.0 id9579;1.0 id9580;1.0 id9581;1.0 id9582;1.0 id9583;1.0 id9584;1.0 id9585;1.0 id9586;1.0 id9587;1.0 id9588;1.0 id9589;1.0 id9590;1.0 id9591;1.0 id9592;1.0 id9593;1.0 id9594;1.0 id9595;1.0 id9596;1.0 id9597;1.0 id9598;1.0 id9599;1.0 id9600;1.0 id9601;1.0 id9602;1.0 id9603;1.0 id9604;1.0 id9605;1.0 id9606;1.0 id9607;1.0 id9608;1.0 id9609;1.0 id9610;1.0 id9611;1.0 id9612;1.0 id9613;1.0 id9614;1.0 id9615;1.0 id9616;1.0 id9617;1.0 id9618;1.0 id9619;1.0 id9620;1.0 id9621;1.0 id9622;1.0 id9623;1.0 id9624;1.0 id9625;1.0 id9626;1.0 id9627;1.0 id9628;1.0 id9629;1.0 id9630;1.0 id9631;1.0 id9632;1.0 id9633;1.0 id9634;1.0 id9635;1.0 id9636;1.0 id9637;1.0 id9638;1.0 id9639;1.0 id9640;1.0 id9641;1.0 id9642;1.0 id9643;1.0 id9644;1.0 id9645;1.0 id9646;1.0 id9647;1.0 id9648;1.0 id9649;1.0 id9650;1.0 id9651;1.0 id9652;1.0 id9653;1.0 id9654;1.0 id9655;1.0 id9656;1.0 id9657;1.0 id9658;1.0 id9659;1.0 id9660;1.0 id9661;1.0 id9662;1.0 id9663;1.0 id9664;1.0 id9665;1.0 id9666;1.0 id9667;1.0 id9668;1.0 id9669;1.0 id9670;1.0 id9671;1.0 id9672;1.0 id9673;1.0 id9674;1.0 id9675;1.0 id9676;1.0 id9677;1.0 id9678;1.0 id9679;1.0 id9680;1.0 id9681;1.0 id9682;1.0 id9683;1.0 id9684;1.0 id9685;1.0 id9686;1.0 id9687;1.0 id9688;1.0 id9689;1.0 id9690;1.0 id9691;1.0 id9692;1.0 id9693;1.0 id9694;1.0 id9695;1.0 id9696;1.0 id9697;1.0 id9698;1.0 id9699;1.0 id9700;1.0 id9701;1.0 id9702;1.0 id9703;1.0 id9704;1.0 id9705;1.0 id9706;1.0 id9707;1.0 id9708;1.0 id9709;1.0 id9710;1.0 id9711;1.0 id9712;1.0 id9713;1.0 id9714;1.0 id9715;1.0 id9716;1.0 id9717;1.0 id9718;1.0 id9719;1.0 id9720;1.0 id9721;1.0 id9722;1.0 id9723;1.0 id9724;1.0 id9725;1.0 id9726;1.0 id9727;1.0 id9728;1.0 id9729;1.0 id9730;1.0 id9731;1.0 id9732;1.0 id9733;1.0 id9734;1.0 id9735;1.0 id9736;1.0 id9737;1.0 id9738;1.0 id9739;1.0 id9740;1.0 id9741;1.0 id9742;1.0 id9743;1.0 id9744;1.0 id9745;1.0 id9746;1.0 id9747;1.0 id9748;1.0 id9749;1.0 id9750;1.0 id9751;1.0 id9752;1.0 id9753;1.0 id9754;1.0 id9755;1.0 id9756;1.0 id9757;1.0 id9758;1.0 id9759;1.0 id9760;1.0 id9761;1.0 id9762;1.0 id9763;1.0 id9764;1.0 id9765;1.0 id9766;1.0 id9767;1.0 id9768;1.0 id9769;1.0 id9770;1.0 id9771;1.0 id9772;1.0 id9773;1.0 id9774;1.0 id9775;1.0 id9776;1.0 id9777;1.0 id9778;1.0 id9779;1.0 id9780;1.0 id9781;1.0 id9782;1.0 id9783;1.0 id9784;1.0 id9785;1.0 id9786;1.0 id9787;1.0 id9788;1.0 id9789;1.0 id9790;1.0 id9791;1.0 id9792;1.0 id9793;1.0 id9794;1.0 id9795;1.0 id9796;1.0 id9797;1.0 id9798;1.0 id9799;1.0 id9800;1.0 id9801;1.0 id9802;1.0 id9803;1.0 id9804;1.0 id9805;1.0 id9806;1.0 id9807;1.0 id9808;1.0 id9809;1.0 id9810;1.0 id9811;1.0 id9812;1.0 id9813;1.0 id9814;1.0 id9815;1.0 id9816;1.0 id9817;1.0 id9818;1.0 id9819;1.0 id9820;1.0 id9821;1.0 id9822;1.0 id9823;1.0 id9824;1.0 id9825;1.0 id9826;1.0 id9827;1.0 id9828;1.0 id9829;1.0 id9830;1.0 id9831;1.0 id9832;1.0 id9833;1.0 id9834;1.0 id9835;1.0 id9836;1.0 id9837;1.0 id9838;1.0 id9839;1.0 id9840;1.0 id9841;1.0 id9842;1.0 id9843;1.0 id9844;1.0 id9845;1.0 id9846;1.0 id9847;1.0 id9848;1.0 id9849;1.0 id9850;1.0 id9851;1.0 id9852;1.0 id9853;1.0 id9854;1.0 id9855;1.0 id9856;1.0 id9857;1.0 id9858;1.0 id9859;1.0 id9860;1.0 id9861;1.0 id9862;1.0 id9863;1.0 id9864;1.0 id9865;1.0 id9866;1.0 id9867;1.0 id9868;1.0 id9869;1.0 id9870;1.0 id9871;1.0 id9872;1.0 id9873;1.0 id9874;1.0 id9875;1.0 id9876;1.0 id9877;1.0 id9878;1.0 id9879;1.0 id9880;1.0 id9881;1.0 id9882;1.0 id9883;1.0 id9884;1.0 id9885;1.0 id9886;1.0 id9887;1.0 id9888;1.0 id9889;1.0 id9890;1.0 id9891;1.0 id9892;1.0 id9893;1.0 id9894;1.0 id9895;1.0 id9896;1.0 id9897;1.0 id9898;1.0 id9899;1.0 id9900;1.0 id9901;1.0 id9902;1.0 id9903;1.0 id9904;1.0 id9905;1.0 id9906;1.0 id9907;1.0 id9908;1.0 id9909;1.0 id9910;1.0 id9911;1.0 id9912;1.0 id9913;1.0 id9914;1.0 id9915;1.0 id9916;1.0 id9917;1.0 id9918;1.0 id9919;1.0 id9920;1.0 id9921;1.0 id9922;1.0 id9923;1.0 id9924;1.0 id9925;1.0 id9926;1.0 id9927;1.0 id9928;1.0 id9929;1.0 id9930;1.0 id9931;1.0 id9932;1.0 id9933;1.0 id9934;1.0 id9935;1.0 id9936;1.0 id9937;1.0 id9938;1.0 id9939;1.0 id9940;1.0 id9941;1.0 id9942;1.0 id9943;1.0 id9944;1.0 id9945;1.0 id9946;1.0 id9947;1.0 id9948;1.0 id9949;1.0 id9950;1.0 id9951;1.0 id9952;1.0 id9953;1.0 id9954;1.0 id9955;1.0 id9956;1.0 id9957;1.0 id9958;1.0 id9959;1.0 id9960;1.0 id9961;1.0 id9962;1.0 id9963;1.0 id9964;1.0 id9965;1.0 id9966;1.0 id9967;1.0 id9968;1.0 id9969;1.0 id9970;1.0 id9971;1.0 id9972;1.0 id9973;1.0 id9974;1.0 id9975;1.0 id9976;1.0 id9977;1.0 id9978;1.0 id9979;1.0 id9980;1.0 id9981;1.0 id9982;1.0 id9983;1.0 id9984;1.0 id9985;1.0 id9986;1.0 id9987;1.0 id9988;1.0 id9989;1.0 id9990;1.0 id9991;1.0 id9992;1.0 id9993;1.0 id9994;1.0 id9995;1.0 id9996;1.0 id9997;1.0 id9998;1.0 id9999;1.0 id10000;1.0 ================================================ FILE: src/test/resources/samples/measurements-2.out ================================================ {Bosaso=19.2/19.2/19.2, Petropavlovsk-Kamchatsky=9.5/9.5/9.5} ================================================ FILE: src/test/resources/samples/measurements-2.txt ================================================ Bosaso;19.2 Petropavlovsk-Kamchatsky;9.5 ================================================ FILE: src/test/resources/samples/measurements-20.out ================================================ {Abéché1️⃣🐝🏎️=27.3/27.3/27.3, Almaty1️⃣🐝🏎️=15.3/15.3/15.3, Baghdad1️⃣🐝🏎️=26.0/26.0/26.0, Bangkok1️⃣🐝🏎️=25.6/25.6/25.6, Berlin1️⃣🐝🏎️=-0.3/-0.3/-0.3, Birao1️⃣🐝🏎️=33.5/33.5/33.5, Canberra1️⃣🐝🏎️=5.2/5.2/5.2, Chittagong1️⃣🐝🏎️=12.6/12.6/12.6, Da Nang1️⃣🐝🏎️=33.7/33.7/33.7, Edinburgh1️⃣🐝🏎️=19.8/19.8/19.8, Irkutsk1️⃣🐝🏎️=9.9/9.9/9.9, Lhasa1️⃣🐝🏎️=13.4/13.4/13.4, Lyon1️⃣🐝🏎️=1.8/1.8/1.8, Mogadishu1️⃣🐝🏎️=11.5/11.5/11.5, Nashville1️⃣🐝🏎️=-4.9/-4.9/-4.9, Odesa1️⃣🐝🏎️=6.5/6.5/6.5, Parakou1️⃣🐝🏎️=36.3/36.3/36.3, Tamanrasset1️⃣🐝🏎️=17.9/17.9/17.9, Tirana1️⃣🐝🏎️=27.7/27.7/27.7, Xi'an1️⃣🐝🏎️=17.5/17.5/17.5} ================================================ FILE: src/test/resources/samples/measurements-20.txt ================================================ Odesa1️⃣🐝🏎️;6.5 Canberra1️⃣🐝🏎️;5.2 Lhasa1️⃣🐝🏎️;13.4 Edinburgh1️⃣🐝🏎️;19.8 Da Nang1️⃣🐝🏎️;33.7 Xi'an1️⃣🐝🏎️;17.5 Berlin1️⃣🐝🏎️;-0.3 Tamanrasset1️⃣🐝🏎️;17.9 Abéché1️⃣🐝🏎️;27.3 Baghdad1️⃣🐝🏎️;26.0 Lyon1️⃣🐝🏎️;1.8 Mogadishu1️⃣🐝🏎️;11.5 Bangkok1️⃣🐝🏎️;25.6 Irkutsk1️⃣🐝🏎️;9.9 Parakou1️⃣🐝🏎️;36.3 Almaty1️⃣🐝🏎️;15.3 Birao1️⃣🐝🏎️;33.5 Chittagong1️⃣🐝🏎️;12.6 Tirana1️⃣🐝🏎️;27.7 Nashville1️⃣🐝🏎️;-4.9 ================================================ FILE: src/test/resources/samples/measurements-3.out ================================================ {Bosaso=-15.0/1.3/20.0, Petropavlovsk-Kamchatsky=-9.5/0.0/9.5} ================================================ FILE: src/test/resources/samples/measurements-3.txt ================================================ Bosaso;5.0 Bosaso;20.0 Bosaso;-5.0 Bosaso;-15.0 Petropavlovsk-Kamchatsky;9.5 Petropavlovsk-Kamchatsky;-9.5 ================================================ FILE: src/test/resources/samples/measurements-boundaries.out ================================================ {Bosaso=-99.9/-99.9/-99.9, Petropavlovsk-Kamchatsky=99.9/99.9/99.9} ================================================ FILE: src/test/resources/samples/measurements-boundaries.txt ================================================ Bosaso;-99.9 Petropavlovsk-Kamchatsky;99.9 ================================================ FILE: src/test/resources/samples/measurements-complex-utf8.out ================================================ {B=8.9/8.9/8.9, C=38.9/38.9/38.9, CabindaKermānZunhuaRochesterValenzuelaOrūmīyehWugangShuangqiaoTshikapa=3.0/3.0/3.0, ChesterLobnyaSan LeandroHemeiSolweziGrand BourgKaliboS=23.4/23.4/23.4, MirnaPehčevoRopažiGus=16.7/16.7/16.7, PototanSahuayo de MorelosBambergMosigkauFrancisco BeltrãoJelenia GóraTelêmaco Borb=17.5/17.5/17.5, TanjungpinangKasselHaldiaLuxorLạng SơnAt TājīTaraka=10.6/10.6/10.6, aniCartagoEṭ ṬīraTemerinCormeilles-en-ParisisZawyat ech CheïkhS=25.4/25.4/25.4, burgazAl ḨawīyahSalamancaMbanza KongoNchelengeZhangaözenTurbatMatiMangghystaūMalak=21.5/21.5/21.5, cotánSan Ramón de la Nueva OránWausauGbaweTailaiRochester HillsVilla ElisaToba TekS=11.2/11.2/11.2, eLafayetteAsh Shaţ=14.2/14.2/14.2, en IslandKota BharuCiudad López MateosCelayaVinhDuyunLos Mochis‘AjmānNyalaLarkanaWichitaNishi=11.9/11.9/11.9, epé=28.2/28.2/28.2, hanVarkkallaiPort LokoD=10.9/10.9/10.9, iCoahuitlánRabatJahāngīrpur SālkhaniCamUniversity of California-Santa BarbaraSerravalleTelkathuM=13.4/13.4/13.4, igButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkomunGornji PetrovciRibnicaKon TumŠavnikPoul=22.5/22.5/22.5, igButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkopunGornji PetrovciRibnicaKon TumŠavnikPodl=11.5/11.5/11.5, igButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkopunGornji PetrovciRibnicaKon TumŠavnikPoul=18.5/18.5/18.5, inhoSökeDordrechtPoáLaloG=13.1/13.1/13.1, iudad Melchor MúzquizQuinhámelDa=40.5/40.5/40.5, ixButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkomunGornji PetrovciRibnicaKon TumŠavnikPoul=0.1/0.1/0.1, l ‘=14.6/14.6/14.6, lhuleuTacurongNavapolatskPiscoDera Ismail KhanLabéAltamiraCavite CityYevpatoriiaTait=22.8/22.8/22.8, liLoretoPlacentiaAliso ViejoChomaPen-y-Bont ar OgwrCojutepeque=12.4/12.4/12.4, lioúpoliBarahonaHoPhuketLe BardoBuena ParkKayesChampigny-sur-MarneHaskovoChathamBatleyEsteioRe=22.5/22.5/22.5, m el Bo=14.6/14.6/14.6, mazunchaleZrenjaninFouchanaSurtPanč=6.7/6.7/6.7, ngoDübendorfC=11.7/11.7/11.7, nt-A=9.2/9.2/9.2, ntington StationKampong SpeuKakataMoschátoBressoVentspilsSaint-CloudTamboSidi Smai’ilDandenon=14.6/14.6/14.6, oCanagatanHelsinkiJabalpurProvidenceRuchengNizhniy NovgorodAhvāzJeparaShaoyangComayagüe=17.3/17.3/17.3, oGumlāSamā’=14.9/14.9/14.9, os Reyes de SalgadoCinisello BalsamoKashibaH=20.0/20.0/20.0, picuíbaJhang CityTepicJayapuraRio BrancoToyamaFangtingSanandajDelhi CantonmentLinghaiShorāpurToy=13.0/13.0/13.0, raKielSibuYatoParanáSanta ClaraYamagataKatihārBeykozImperat=13.5/13.5/13.5, rhamDera Ghazi KhanMiyazakiBhātpār=21.3/21.3/21.3, rugarhVerāvalAlagoinhasEdremitBandırmaSalavatGandajikaLucapaLeesburgTamaRas Tan=10.9/10.9/10.9, skişeh=12.9/12.9/12.9, venGaopingDunhuaAz Zarqā’SylhetKaihuaCaerdyddJāmnagarFuyuanGayaFlorianópolisC=1.9/1.9/1.9, y-le-MoutierSant’ArpinoPljevljaRo=0.8/0.8/0.8, ça PaulistaDarmstadtZhengdingPindamonhangabaEnschedeGirónUttarpāraHeidelbergK=6.0/6.0/6.0, üSosnowiecTanauanMya=18.4/18.4/18.4, ālSongnimSanto TomasKoiduHoshangābādOpoleNovocheboksarskArarasKhannaPunoKoforiduaAhmadpur E=19.4/19.4/19.4, āng=15.7/15.7/15.7, ġFis=9.6/9.6/9.6, ‘AqabahPembaNowgongQu=12.9/12.9/12.9} ================================================ FILE: src/test/resources/samples/measurements-complex-utf8.txt ================================================ aniCartagoEṭ ṬīraTemerinCormeilles-en-ParisisZawyat ech CheïkhS;25.4 picuíbaJhang CityTepicJayapuraRio BrancoToyamaFangtingSanandajDelhi CantonmentLinghaiShorāpurToy;13.0 lhuleuTacurongNavapolatskPiscoDera Ismail KhanLabéAltamiraCavite CityYevpatoriiaTait;22.8 āng;15.7 hanVarkkallaiPort LokoD;10.9 eLafayetteAsh Shaţ;14.2 ‘AqabahPembaNowgongQu;12.9 inhoSökeDordrechtPoáLaloG;13.1 skişeh;12.9 rhamDera Ghazi KhanMiyazakiBhātpār;21.3 igButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkopunGornji PetrovciRibnicaKon TumŠavnikPodl;11.5 igButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkopunGornji PetrovciRibnicaKon TumŠavnikPoul;18.5 igButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkomunGornji PetrovciRibnicaKon TumŠavnikPoul;22.5 ixButeboJuršinciKoaniImdinaNova VasDestrnikVarvarinSkomunGornji PetrovciRibnicaKon TumŠavnikPoul;0.1 B;8.9 C;38.9 nt-A;9.2 y-le-MoutierSant’ArpinoPljevljaRo;0.8 oGumlāSamā’;14.9 os Reyes de SalgadoCinisello BalsamoKashibaH;20.0 m el Bo;14.6 mazunchaleZrenjaninFouchanaSurtPanč;6.7 ġFis;9.6 epé;28.2 ālSongnimSanto TomasKoiduHoshangābādOpoleNovocheboksarskArarasKhannaPunoKoforiduaAhmadpur E;19.4 iudad Melchor MúzquizQuinhámelDa;40.5 ChesterLobnyaSan LeandroHemeiSolweziGrand BourgKaliboS;23.4 cotánSan Ramón de la Nueva OránWausauGbaweTailaiRochester HillsVilla ElisaToba TekS;11.2 raKielSibuYatoParanáSanta ClaraYamagataKatihārBeykozImperat;13.5 l ‘;14.6 TanjungpinangKasselHaldiaLuxorLạng SơnAt TājīTaraka;10.6 MirnaPehčevoRopažiGus;16.7 üSosnowiecTanauanMya;18.4 ngoDübendorfC;11.7 liLoretoPlacentiaAliso ViejoChomaPen-y-Bont ar OgwrCojutepeque;12.4 burgazAl ḨawīyahSalamancaMbanza KongoNchelengeZhangaözenTurbatMatiMangghystaūMalak;21.5 iCoahuitlánRabatJahāngīrpur SālkhaniCamUniversity of California-Santa BarbaraSerravalleTelkathuM;13.4 lioúpoliBarahonaHoPhuketLe BardoBuena ParkKayesChampigny-sur-MarneHaskovoChathamBatleyEsteioRe;22.5 PototanSahuayo de MorelosBambergMosigkauFrancisco BeltrãoJelenia GóraTelêmaco Borb;17.5 CabindaKermānZunhuaRochesterValenzuelaOrūmīyehWugangShuangqiaoTshikapa;3.0 venGaopingDunhuaAz Zarqā’SylhetKaihuaCaerdyddJāmnagarFuyuanGayaFlorianópolisC;1.9 ntington StationKampong SpeuKakataMoschátoBressoVentspilsSaint-CloudTamboSidi Smai’ilDandenon;14.6 rugarhVerāvalAlagoinhasEdremitBandırmaSalavatGandajikaLucapaLeesburgTamaRas Tan;10.9 oCanagatanHelsinkiJabalpurProvidenceRuchengNizhniy NovgorodAhvāzJeparaShaoyangComayagüe;17.3 ça PaulistaDarmstadtZhengdingPindamonhangabaEnschedeGirónUttarpāraHeidelbergK;6.0 en IslandKota BharuCiudad López MateosCelayaVinhDuyunLos Mochis‘AjmānNyalaLarkanaWichitaNishi;11.9 ================================================ FILE: src/test/resources/samples/measurements-dot.out ================================================ {-=1.0/1.5/2.0, .=1.0/1.0/1.0} ================================================ FILE: src/test/resources/samples/measurements-dot.txt ================================================ .;1.0 -;1.0 -;2.0 ================================================ FILE: src/test/resources/samples/measurements-rounding.out ================================================ {ham=14.6/25.5/33.6, jel=-9.0/18.0/46.5} ================================================ FILE: src/test/resources/samples/measurements-rounding.txt ================================================ ham;33.6 ham;31.7 ham;21.9 ham;14.6 jel;18.6 jel;12.8 jel;20.7 jel;13.8 jel;26.7 jel;17.7 jel;24.7 jel;18.4 jel;10.7 jel;21.9 jel;35.6 jel;19.5 jel;15.9 jel;18.6 jel;24.7 jel;11.0 jel;26.4 jel;16.6 jel;24.3 jel;18.2 jel;15.7 jel;23.7 jel;20.1 jel;22.4 jel;1.7 jel;23.8 jel;9.0 jel;18.9 jel;1.0 jel;9.5 jel;25.6 jel;19.6 jel;-0.9 jel;13.0 jel;19.3 jel;12.0 jel;9.3 jel;24.8 jel;26.7 jel;15.3 jel;20.0 jel;25.8 jel;12.9 jel;23.4 jel;15.0 jel;16.9 jel;27.0 jel;17.7 jel;23.4 jel;16.1 jel;15.2 jel;28.0 jel;18.5 jel;30.1 jel;11.7 jel;19.9 jel;32.0 jel;20.3 jel;17.1 jel;16.9 jel;13.1 jel;28.4 jel;16.0 jel;26.1 jel;24.4 jel;16.1 jel;18.1 jel;6.6 jel;11.5 jel;20.5 jel;18.2 jel;22.3 jel;10.4 jel;0.3 jel;15.6 jel;19.7 jel;28.7 jel;10.8 jel;32.0 jel;31.8 jel;20.8 jel;14.0 jel;19.1 jel;26.3 jel;10.3 jel;6.8 jel;16.3 jel;18.8 jel;13.9 jel;27.9 jel;14.9 jel;25.8 jel;21.0 jel;15.4 jel;19.5 jel;16.7 jel;17.1 jel;18.6 jel;8.5 jel;27.4 jel;21.2 jel;33.4 jel;19.8 jel;19.6 jel;24.2 jel;15.0 jel;27.2 jel;7.8 jel;17.4 jel;16.1 jel;23.2 jel;16.3 jel;11.1 jel;20.4 jel;14.3 jel;18.8 jel;26.1 jel;13.8 jel;14.8 jel;11.9 jel;22.0 jel;13.1 jel;18.1 jel;17.5 jel;25.1 jel;26.4 jel;16.1 jel;6.6 jel;18.5 jel;22.6 jel;15.7 jel;20.6 jel;26.6 jel;25.5 jel;7.5 jel;25.4 jel;14.7 jel;19.8 jel;24.6 jel;27.7 jel;20.8 jel;11.2 jel;34.3 jel;19.0 jel;13.4 jel;20.0 jel;11.6 jel;16.3 jel;19.8 jel;22.0 jel;19.4 jel;10.6 jel;24.0 jel;13.5 jel;20.7 jel;23.5 jel;16.0 jel;18.8 jel;11.5 jel;11.8 jel;15.8 jel;9.7 jel;13.8 jel;23.9 jel;13.1 jel;23.9 jel;16.0 jel;11.1 jel;14.6 jel;12.3 jel;14.6 jel;12.8 jel;19.9 jel;10.7 jel;19.5 jel;14.0 jel;16.4 jel;25.8 jel;22.7 jel;20.9 jel;26.5 jel;20.3 jel;24.6 jel;31.9 jel;26.8 jel;21.0 jel;20.9 jel;19.3 jel;25.0 jel;23.1 jel;15.3 jel;13.4 jel;24.1 jel;15.6 jel;16.5 jel;14.6 jel;33.5 jel;17.3 jel;15.2 jel;23.9 jel;20.5 jel;16.2 jel;15.8 jel;27.7 jel;13.6 jel;35.0 jel;24.1 jel;21.6 jel;6.8 jel;6.6 jel;16.5 jel;34.1 jel;8.6 jel;14.6 jel;20.0 jel;28.4 jel;19.4 jel;22.5 jel;18.9 jel;9.8 jel;-0.5 jel;7.0 jel;11.4 jel;18.9 jel;16.5 jel;27.0 jel;1.8 jel;19.7 jel;12.5 jel;1.3 jel;8.8 jel;26.9 jel;24.5 jel;19.5 jel;22.9 jel;9.4 jel;18.1 jel;6.0 jel;10.9 jel;24.3 jel;16.6 jel;22.5 jel;18.6 jel;13.0 jel;16.3 jel;19.1 jel;19.9 jel;17.1 jel;23.5 jel;15.5 jel;15.1 jel;12.9 jel;22.2 jel;24.6 jel;10.4 jel;11.4 jel;10.2 jel;26.3 jel;31.0 jel;17.2 jel;12.0 jel;28.4 jel;24.2 jel;21.3 jel;7.3 jel;18.7 jel;12.8 jel;21.2 jel;7.7 jel;19.1 jel;18.6 jel;23.0 jel;13.3 jel;14.4 jel;17.6 jel;4.4 jel;16.1 jel;7.9 jel;20.9 jel;20.3 jel;25.8 jel;13.0 jel;14.4 jel;20.8 jel;30.4 jel;24.5 jel;20.1 jel;28.4 jel;15.7 jel;32.5 jel;30.0 jel;13.0 jel;17.8 jel;22.0 jel;24.3 jel;22.2 jel;23.8 jel;19.6 jel;15.7 jel;20.9 jel;9.8 jel;14.1 jel;15.0 jel;7.7 jel;22.2 jel;15.0 jel;25.9 jel;10.3 jel;13.8 jel;10.8 jel;14.8 jel;36.4 jel;22.4 jel;19.4 jel;18.6 jel;10.4 jel;17.6 jel;22.8 jel;10.0 jel;18.9 jel;23.0 jel;16.3 jel;22.6 jel;13.2 jel;19.4 jel;9.1 jel;6.3 jel;16.5 jel;13.2 jel;5.5 jel;19.3 jel;23.2 jel;22.8 jel;7.3 jel;16.4 jel;5.9 jel;20.9 jel;-3.0 jel;24.0 jel;25.7 jel;14.0 jel;13.5 jel;16.3 jel;23.2 jel;28.5 jel;20.0 jel;16.1 jel;21.7 jel;34.9 jel;17.3 jel;10.8 jel;17.3 jel;23.5 jel;14.1 jel;4.2 jel;14.4 jel;17.2 jel;21.1 jel;17.1 jel;6.5 jel;13.9 jel;13.5 jel;22.8 jel;29.1 jel;16.5 jel;10.2 jel;26.4 jel;30.4 jel;11.9 jel;21.5 jel;16.6 jel;14.9 jel;13.3 jel;11.6 jel;14.2 jel;11.3 jel;24.9 jel;27.7 jel;14.6 jel;18.1 jel;11.1 jel;9.2 jel;27.9 jel;13.2 jel;11.3 jel;11.9 jel;17.5 jel;15.9 jel;22.8 jel;8.1 jel;30.4 jel;24.3 jel;19.4 jel;15.0 jel;23.7 jel;18.0 jel;29.0 jel;15.7 jel;12.7 jel;18.9 jel;19.2 jel;28.6 jel;20.6 jel;21.0 jel;18.3 jel;9.3 jel;20.9 jel;20.7 jel;5.4 jel;18.0 jel;7.8 jel;7.4 jel;19.9 jel;17.0 jel;12.6 jel;36.4 jel;25.0 jel;21.9 jel;24.2 jel;18.5 jel;17.9 jel;14.0 jel;13.6 jel;21.9 jel;11.2 jel;28.0 jel;19.0 jel;11.7 jel;31.9 jel;6.3 jel;23.4 jel;19.9 jel;23.2 jel;13.3 jel;19.7 jel;23.1 jel;16.0 jel;28.9 jel;18.3 jel;19.6 jel;26.0 jel;20.2 jel;14.5 jel;27.5 jel;15.1 jel;27.3 jel;15.6 jel;16.9 jel;7.6 jel;8.0 jel;16.8 jel;4.5 jel;21.2 jel;17.6 jel;20.1 jel;23.1 jel;12.6 jel;35.7 jel;14.8 jel;24.3 jel;21.4 jel;23.7 jel;3.9 jel;30.7 jel;27.1 jel;8.5 jel;15.2 jel;15.3 jel;28.2 jel;22.4 jel;13.6 jel;20.1 jel;9.4 jel;16.0 jel;8.4 jel;18.3 jel;9.9 jel;24.8 jel;11.8 jel;25.5 jel;15.2 jel;23.2 jel;20.4 jel;11.6 jel;9.3 jel;16.5 jel;13.2 jel;16.4 jel;21.3 jel;18.3 jel;8.8 jel;22.8 jel;7.0 jel;18.3 jel;32.8 jel;4.3 jel;12.6 jel;24.3 jel;9.9 jel;19.6 jel;18.8 jel;21.1 jel;28.8 jel;25.3 jel;4.1 jel;13.0 jel;17.6 jel;30.9 jel;17.2 jel;24.7 jel;8.8 jel;16.3 jel;19.1 jel;8.9 jel;24.6 jel;21.8 jel;24.7 jel;15.1 jel;13.5 jel;19.6 jel;22.8 jel;15.1 jel;24.0 jel;21.1 jel;26.3 jel;13.3 jel;10.7 jel;18.2 jel;17.3 jel;17.2 jel;17.5 jel;14.5 jel;22.1 jel;26.8 jel;19.2 jel;21.4 jel;12.5 jel;31.8 jel;18.9 jel;13.3 jel;18.2 jel;6.5 jel;9.5 jel;15.6 jel;32.9 jel;22.8 jel;22.1 jel;15.4 jel;17.2 jel;14.4 jel;6.2 jel;21.5 jel;-2.9 jel;20.9 jel;21.5 jel;14.7 jel;29.7 jel;12.9 jel;29.7 jel;7.5 jel;6.2 jel;16.8 jel;15.4 jel;20.2 jel;16.7 jel;18.4 jel;17.5 jel;14.8 jel;10.8 jel;13.4 jel;18.6 jel;28.0 jel;4.5 jel;8.0 jel;16.5 jel;21.3 jel;15.8 jel;22.7 jel;16.0 jel;21.7 jel;22.0 jel;20.5 jel;13.4 jel;3.0 jel;20.7 jel;6.5 jel;36.7 jel;18.7 jel;22.6 jel;19.9 jel;30.8 jel;24.5 jel;18.1 jel;21.3 jel;27.3 jel;13.0 jel;34.4 jel;10.3 jel;13.8 jel;26.1 jel;14.1 jel;18.2 jel;21.1 jel;15.1 jel;13.1 jel;24.1 jel;13.0 jel;17.9 jel;22.0 jel;12.3 jel;38.0 jel;20.9 jel;12.0 jel;19.9 jel;29.7 jel;16.2 jel;17.9 jel;15.9 jel;16.7 jel;10.7 jel;0.1 jel;20.3 jel;13.4 jel;13.9 jel;15.7 jel;22.5 jel;11.0 jel;17.6 jel;22.1 jel;16.6 jel;19.9 jel;21.3 jel;8.8 jel;22.3 jel;21.4 jel;12.8 jel;20.1 jel;8.5 jel;19.3 jel;22.0 jel;18.9 jel;23.5 jel;4.2 jel;33.4 jel;34.5 jel;15.1 jel;7.7 jel;22.1 jel;14.8 jel;20.8 jel;18.9 jel;13.6 jel;10.7 jel;20.2 jel;18.3 jel;13.3 jel;20.6 jel;13.7 jel;23.1 jel;12.8 jel;28.1 jel;19.6 jel;13.5 jel;21.0 jel;15.6 jel;20.5 jel;19.6 jel;18.8 jel;19.2 jel;24.3 jel;29.8 jel;15.4 jel;16.9 jel;20.6 jel;20.5 jel;30.6 jel;13.9 jel;16.0 jel;13.7 jel;16.3 jel;30.6 jel;9.3 jel;27.7 jel;21.0 jel;31.5 jel;23.7 jel;26.9 jel;14.1 jel;14.5 jel;19.1 jel;24.9 jel;1.8 jel;35.5 jel;23.2 jel;18.6 jel;17.8 jel;28.8 jel;9.6 jel;20.7 jel;17.7 jel;14.5 jel;7.1 jel;18.1 jel;20.2 jel;10.6 jel;21.5 jel;7.6 jel;29.7 jel;14.6 jel;22.2 jel;16.0 jel;23.3 jel;21.6 jel;14.8 jel;19.7 jel;19.4 jel;20.5 jel;16.3 jel;8.7 jel;2.3 jel;8.1 jel;19.1 jel;24.7 jel;12.1 jel;8.7 jel;25.8 jel;24.6 jel;15.7 jel;20.4 jel;12.4 jel;29.7 jel;10.1 jel;20.5 jel;20.5 jel;30.2 jel;22.3 jel;19.4 jel;33.9 jel;18.9 jel;23.8 jel;34.0 jel;20.5 jel;13.4 jel;15.8 jel;13.6 jel;14.9 jel;20.5 jel;13.3 jel;22.6 jel;29.3 jel;26.3 jel;21.2 jel;20.8 jel;22.4 jel;18.3 jel;10.5 jel;29.6 jel;22.5 jel;28.5 jel;10.4 jel;10.3 jel;27.9 jel;14.4 jel;21.5 jel;14.0 jel;20.7 jel;20.5 jel;8.8 jel;24.3 jel;29.6 jel;18.9 jel;14.3 jel;14.1 jel;7.4 jel;19.4 jel;11.9 jel;15.2 jel;7.5 jel;12.2 jel;19.3 jel;18.6 jel;3.7 jel;8.0 jel;31.5 jel;24.3 jel;28.5 jel;18.6 jel;9.2 jel;5.6 jel;3.4 jel;26.2 jel;20.9 jel;16.6 jel;26.0 jel;11.2 jel;20.0 jel;5.1 jel;21.2 jel;22.8 jel;31.3 jel;20.8 jel;13.2 jel;35.8 jel;26.7 jel;17.9 jel;-8.0 jel;8.4 jel;12.6 jel;21.4 jel;27.0 jel;27.1 jel;28.0 jel;19.5 jel;17.5 jel;17.0 jel;19.9 jel;25.9 jel;19.9 jel;11.1 jel;20.7 jel;29.2 jel;23.6 jel;-2.8 jel;25.0 jel;19.0 jel;20.7 jel;17.0 jel;20.3 jel;21.3 jel;13.4 jel;14.5 jel;13.2 jel;6.0 jel;17.9 jel;29.2 jel;7.6 jel;32.5 jel;32.5 jel;21.0 jel;23.7 jel;22.7 jel;23.4 jel;17.2 jel;13.5 jel;26.9 jel;26.2 jel;17.0 jel;2.4 jel;26.4 jel;14.1 jel;22.4 jel;11.0 jel;8.1 jel;2.5 jel;10.6 jel;12.5 jel;13.5 jel;13.6 jel;24.4 jel;20.4 jel;10.4 jel;20.7 jel;21.6 jel;20.7 jel;14.4 jel;16.8 jel;22.3 jel;0.6 jel;18.5 jel;16.8 jel;17.1 jel;19.5 jel;11.0 jel;16.6 jel;19.7 jel;24.3 jel;17.8 jel;21.5 jel;9.3 jel;30.8 jel;13.4 jel;13.1 jel;14.9 jel;22.1 jel;-6.0 jel;8.3 jel;15.5 jel;17.2 jel;12.8 jel;20.1 jel;8.6 jel;22.3 jel;20.3 jel;17.4 jel;19.2 jel;28.8 jel;25.2 jel;21.4 jel;20.3 jel;18.2 jel;13.3 jel;23.9 jel;28.2 jel;17.5 jel;28.3 jel;12.0 jel;14.1 jel;16.0 jel;15.8 jel;11.2 jel;14.4 jel;19.4 jel;26.9 jel;14.5 jel;9.0 jel;16.0 jel;25.3 jel;19.7 jel;0.1 jel;24.7 jel;20.1 jel;16.1 jel;27.0 jel;17.7 jel;15.7 jel;11.8 jel;5.9 jel;15.5 jel;16.9 jel;16.1 jel;25.1 jel;9.6 jel;11.4 jel;20.9 jel;13.1 jel;18.9 jel;11.2 jel;5.8 jel;19.2 jel;23.3 jel;11.7 jel;37.7 jel;23.5 jel;6.9 jel;24.3 jel;21.4 jel;12.4 jel;8.7 jel;18.2 jel;15.7 jel;14.1 jel;12.8 jel;14.4 jel;12.4 jel;31.4 jel;22.6 jel;21.6 jel;26.9 jel;21.3 jel;17.4 jel;19.2 jel;22.1 jel;26.4 jel;23.0 jel;31.1 jel;16.2 jel;27.3 jel;18.6 jel;10.5 jel;33.6 jel;19.1 jel;14.1 jel;21.1 jel;35.8 jel;19.7 jel;10.5 jel;30.0 jel;19.0 jel;27.6 jel;22.2 jel;13.5 jel;15.5 jel;11.6 jel;11.2 jel;8.0 jel;24.8 jel;29.0 jel;29.1 jel;22.4 jel;23.2 jel;11.2 jel;25.7 jel;18.3 jel;19.1 jel;15.0 jel;12.3 jel;36.9 jel;10.5 jel;24.2 jel;14.4 jel;18.1 jel;8.4 jel;22.5 jel;11.8 jel;16.9 jel;19.3 jel;16.0 jel;18.6 jel;16.2 jel;30.3 jel;8.3 jel;17.7 jel;23.0 jel;29.8 jel;17.7 jel;8.8 jel;17.3 jel;3.6 jel;15.3 jel;20.1 jel;11.3 jel;13.4 jel;20.4 jel;22.0 jel;16.5 jel;4.7 jel;22.7 jel;16.0 jel;22.7 jel;15.3 jel;28.6 jel;17.0 jel;16.6 jel;13.3 jel;21.1 jel;-0.3 jel;19.7 jel;22.9 jel;25.4 jel;19.5 jel;25.4 jel;22.4 jel;19.1 jel;7.1 jel;27.7 jel;14.5 jel;20.7 jel;17.0 jel;24.6 jel;9.4 jel;27.7 jel;14.6 jel;10.3 jel;30.5 jel;24.9 jel;15.1 jel;10.0 jel;19.1 jel;22.4 jel;23.0 jel;13.0 jel;17.8 jel;10.3 jel;25.9 jel;17.1 jel;9.7 jel;22.5 jel;3.5 jel;25.1 jel;26.8 jel;18.9 jel;15.5 jel;12.8 jel;28.4 jel;23.8 jel;19.5 jel;21.0 jel;20.2 jel;24.9 jel;24.1 jel;18.5 jel;22.4 jel;11.6 jel;15.4 jel;21.6 jel;14.0 jel;23.8 jel;9.2 jel;18.2 jel;22.2 jel;10.7 jel;22.1 jel;19.2 jel;25.5 jel;12.0 jel;11.5 jel;16.1 jel;25.8 jel;22.1 jel;24.0 jel;20.6 jel;22.9 jel;10.9 jel;20.1 jel;29.6 jel;7.7 jel;18.8 jel;20.1 jel;22.8 jel;21.3 jel;14.1 jel;13.8 jel;9.8 jel;12.5 jel;17.4 jel;9.4 jel;17.2 jel;21.1 jel;14.1 jel;11.1 jel;11.9 jel;0.1 jel;10.8 jel;15.0 jel;22.4 jel;27.8 jel;24.7 jel;23.3 jel;25.5 jel;18.6 jel;22.9 jel;21.7 jel;24.1 jel;22.9 jel;16.7 jel;21.9 jel;14.2 jel;14.2 jel;32.9 jel;22.4 jel;25.8 jel;19.6 jel;17.2 jel;23.4 jel;10.6 jel;21.2 jel;22.5 jel;10.1 jel;14.0 jel;16.4 jel;10.9 jel;37.3 jel;16.4 jel;4.8 jel;11.6 jel;9.9 jel;10.3 jel;23.7 jel;17.5 jel;19.8 jel;13.5 jel;19.8 jel;16.1 jel;20.2 jel;12.6 jel;29.5 jel;11.5 jel;24.2 jel;27.0 jel;20.0 jel;17.6 jel;16.3 jel;10.6 jel;18.0 jel;14.1 jel;11.8 jel;22.0 jel;22.9 jel;18.4 jel;14.4 jel;22.1 jel;27.9 jel;13.5 jel;9.5 jel;16.5 jel;21.9 jel;12.4 jel;23.9 jel;13.3 jel;12.2 jel;26.2 jel;13.4 jel;6.7 jel;20.8 jel;17.8 jel;24.0 jel;17.5 jel;26.6 jel;22.8 jel;27.7 jel;18.5 jel;6.6 jel;20.2 jel;6.9 jel;21.3 jel;20.0 jel;18.7 jel;18.4 jel;18.8 jel;14.9 jel;8.5 jel;23.2 jel;4.6 jel;22.2 jel;14.6 jel;34.2 jel;11.6 jel;16.0 jel;14.2 jel;9.8 jel;15.7 jel;14.5 jel;22.2 jel;10.6 jel;20.2 jel;12.0 jel;20.8 jel;26.2 jel;20.8 jel;12.7 jel;16.7 jel;9.4 jel;20.9 jel;25.6 jel;19.4 jel;18.4 jel;17.6 jel;22.0 jel;26.9 jel;26.9 jel;21.5 jel;22.1 jel;21.2 jel;16.6 jel;34.0 jel;15.8 jel;18.0 jel;14.7 jel;19.8 jel;8.5 jel;16.5 jel;22.1 jel;25.8 jel;19.2 jel;16.0 jel;4.4 jel;9.5 jel;12.2 jel;6.8 jel;16.3 jel;23.1 jel;13.9 jel;21.9 jel;17.3 jel;15.7 jel;19.4 jel;12.2 jel;24.1 jel;16.4 jel;23.2 jel;17.0 jel;15.8 jel;15.8 jel;14.4 jel;12.7 jel;17.3 jel;22.4 jel;29.6 jel;23.7 jel;11.9 jel;7.3 jel;23.1 jel;7.7 jel;8.1 jel;6.2 jel;10.4 jel;15.7 jel;14.5 jel;22.5 jel;13.0 jel;10.9 jel;22.3 jel;31.4 jel;12.0 jel;16.0 jel;27.0 jel;18.8 jel;15.7 jel;32.3 jel;23.3 jel;16.6 jel;19.7 jel;19.7 jel;21.9 jel;18.7 jel;19.2 jel;6.1 jel;25.5 jel;9.2 jel;26.8 jel;20.0 jel;24.3 jel;26.1 jel;17.0 jel;16.6 jel;14.0 jel;13.1 jel;23.1 jel;14.9 jel;16.2 jel;18.2 jel;20.0 jel;21.5 jel;23.0 jel;8.6 jel;9.7 jel;27.1 jel;23.7 jel;21.0 jel;14.4 jel;21.7 jel;17.2 jel;21.9 jel;8.9 jel;20.9 jel;20.1 jel;19.1 jel;19.3 jel;18.4 jel;15.8 jel;15.9 jel;14.9 jel;13.9 jel;20.9 jel;19.9 jel;17.6 jel;29.0 jel;24.2 jel;8.8 jel;17.5 jel;20.8 jel;14.9 jel;24.5 jel;29.9 jel;19.7 jel;14.6 jel;11.6 jel;14.7 jel;9.0 jel;24.1 jel;37.5 jel;2.2 jel;16.9 jel;15.9 jel;9.1 jel;19.6 jel;27.9 jel;11.5 jel;18.9 jel;23.9 jel;20.4 jel;27.0 jel;23.2 jel;22.5 jel;20.1 jel;33.2 jel;12.6 jel;21.8 jel;18.1 jel;20.0 jel;3.5 jel;10.1 jel;14.2 jel;14.5 jel;12.0 jel;22.7 jel;22.6 jel;28.5 jel;13.6 jel;13.0 jel;14.7 jel;13.6 jel;33.6 jel;21.9 jel;33.2 jel;18.3 jel;13.6 jel;17.6 jel;3.5 jel;3.2 jel;12.6 jel;30.7 jel;24.4 jel;19.4 jel;20.9 jel;29.4 jel;30.3 jel;23.8 jel;11.8 jel;20.6 jel;26.2 jel;10.4 jel;14.5 jel;5.2 jel;10.8 jel;20.2 jel;12.4 jel;20.5 jel;18.1 jel;3.8 jel;12.1 jel;15.7 jel;4.8 jel;21.5 jel;15.1 jel;9.2 jel;28.1 jel;28.1 jel;23.8 jel;26.8 jel;4.3 jel;8.2 jel;30.6 jel;10.7 jel;7.1 jel;16.5 jel;13.3 jel;8.4 jel;30.5 jel;13.5 jel;13.9 jel;-2.5 jel;25.3 jel;16.4 jel;15.6 jel;20.1 jel;16.6 jel;16.9 jel;23.5 jel;19.4 jel;21.1 jel;19.7 jel;10.7 jel;2.1 jel;25.8 jel;21.3 jel;16.5 jel;14.8 jel;14.2 jel;21.3 jel;11.3 jel;18.8 jel;6.7 jel;30.5 jel;31.8 jel;28.9 jel;12.1 jel;23.8 jel;4.0 jel;20.8 jel;24.6 jel;9.4 jel;7.3 jel;24.9 jel;34.1 jel;16.2 jel;25.3 jel;6.8 jel;21.8 jel;21.4 jel;28.9 jel;14.4 jel;18.8 jel;28.6 jel;21.3 jel;12.6 jel;18.0 jel;15.1 jel;6.8 jel;9.6 jel;20.5 jel;23.2 jel;14.4 jel;23.3 jel;19.6 jel;21.0 jel;6.5 jel;17.5 jel;25.6 jel;22.8 jel;24.6 jel;17.9 jel;20.4 jel;22.5 jel;24.2 jel;17.1 jel;13.0 jel;27.4 jel;18.8 jel;15.3 jel;24.0 jel;25.8 jel;17.8 jel;14.3 jel;24.9 jel;24.9 jel;6.5 jel;32.1 jel;25.2 jel;35.4 jel;25.6 jel;27.9 jel;15.8 jel;19.8 jel;15.5 jel;19.1 jel;17.6 jel;13.7 jel;27.4 jel;15.4 jel;18.2 jel;9.3 jel;32.5 jel;18.2 jel;19.6 jel;18.2 jel;12.7 jel;15.2 jel;26.1 jel;20.2 jel;16.0 jel;17.1 jel;29.2 jel;14.3 jel;6.8 jel;13.5 jel;15.9 jel;13.2 jel;12.3 jel;19.1 jel;5.6 jel;7.5 jel;20.7 jel;20.9 jel;14.5 jel;11.1 jel;11.2 jel;17.4 jel;13.1 jel;26.8 jel;17.4 jel;23.3 jel;11.9 jel;17.7 jel;19.3 jel;17.6 jel;22.3 jel;18.6 jel;30.4 jel;13.2 jel;15.9 jel;17.8 jel;7.4 jel;16.0 jel;5.2 jel;17.5 jel;12.1 jel;12.3 jel;24.8 jel;7.3 jel;20.1 jel;18.7 jel;17.9 jel;17.4 jel;8.7 jel;29.8 jel;21.1 jel;22.6 jel;17.3 jel;15.9 jel;20.8 jel;14.8 jel;27.2 jel;22.0 jel;12.8 jel;9.8 jel;17.3 jel;18.9 jel;26.6 jel;15.5 jel;17.5 jel;15.1 jel;14.6 jel;20.4 jel;12.9 jel;18.5 jel;16.3 jel;12.2 jel;31.7 jel;13.7 jel;5.4 jel;17.8 jel;28.5 jel;17.3 jel;10.0 jel;14.5 jel;12.7 jel;15.8 jel;8.6 jel;21.0 jel;16.2 jel;31.6 jel;18.3 jel;18.6 jel;32.7 jel;15.4 jel;9.1 jel;15.6 jel;11.9 jel;15.2 jel;21.8 jel;8.3 jel;30.0 jel;32.4 jel;25.0 jel;14.2 jel;15.3 jel;11.2 jel;22.9 jel;19.9 jel;22.5 jel;31.8 jel;20.3 jel;26.0 jel;21.2 jel;13.1 jel;19.7 jel;21.3 jel;13.7 jel;32.5 jel;11.8 jel;16.5 jel;21.3 jel;8.7 jel;24.1 jel;14.6 jel;25.9 jel;28.0 jel;19.8 jel;9.5 jel;10.0 jel;12.7 jel;14.4 jel;9.2 jel;8.6 jel;17.2 jel;32.1 jel;3.9 jel;20.2 jel;23.4 jel;21.2 jel;20.9 jel;7.0 jel;20.7 jel;13.6 jel;13.6 jel;29.8 jel;25.6 jel;22.3 jel;13.1 jel;24.5 jel;14.5 jel;5.2 jel;17.2 jel;14.7 jel;23.9 jel;15.2 jel;32.5 jel;24.6 jel;9.0 jel;30.0 jel;15.9 jel;20.9 jel;5.0 jel;14.5 jel;16.1 jel;6.8 jel;20.6 jel;24.6 jel;15.5 jel;20.6 jel;19.2 jel;12.4 jel;31.8 jel;20.3 jel;18.3 jel;12.2 jel;19.1 jel;15.3 jel;-0.5 jel;20.3 jel;20.7 jel;30.0 jel;11.7 jel;13.6 jel;15.9 jel;19.6 jel;6.9 jel;20.6 jel;18.0 jel;10.0 jel;8.4 jel;18.5 jel;10.1 jel;16.5 jel;18.9 jel;17.6 jel;10.2 jel;23.7 jel;4.4 jel;16.9 jel;16.4 jel;14.2 jel;32.1 jel;19.4 jel;8.2 jel;23.2 jel;14.4 jel;15.1 jel;15.4 jel;15.0 jel;30.1 jel;19.9 jel;19.2 jel;25.7 jel;6.0 jel;20.4 jel;17.8 jel;5.0 jel;23.3 jel;6.2 jel;8.2 jel;29.3 jel;20.5 jel;17.4 jel;17.4 jel;27.3 jel;17.7 jel;8.8 jel;14.0 jel;10.3 jel;12.6 jel;7.3 jel;18.7 jel;18.9 jel;11.2 jel;17.5 jel;24.2 jel;17.8 jel;12.7 jel;9.5 jel;24.2 jel;11.6 jel;20.4 jel;35.6 jel;12.2 jel;25.7 jel;6.1 jel;7.6 jel;18.3 jel;31.1 jel;12.2 jel;12.5 jel;11.5 jel;15.7 jel;15.1 jel;18.5 jel;17.5 jel;18.4 jel;16.9 jel;18.4 jel;13.3 jel;13.8 jel;18.9 jel;15.4 jel;4.1 jel;18.4 jel;15.9 jel;10.2 jel;21.5 jel;18.0 jel;30.9 jel;20.6 jel;21.1 jel;15.0 jel;15.2 jel;23.1 jel;28.9 jel;18.9 jel;20.6 jel;17.2 jel;20.3 jel;14.7 jel;32.5 jel;22.5 jel;22.8 jel;26.1 jel;23.4 jel;25.6 jel;8.6 jel;28.5 jel;12.7 jel;12.5 jel;8.6 jel;6.1 jel;17.2 jel;25.9 jel;12.4 jel;18.5 jel;33.5 jel;16.4 jel;19.8 jel;10.6 jel;20.2 jel;16.6 jel;27.5 jel;22.0 jel;23.1 jel;18.6 jel;12.2 jel;17.0 jel;20.3 jel;22.7 jel;21.7 jel;18.7 jel;25.2 jel;16.3 jel;24.3 jel;23.1 jel;23.1 jel;27.2 jel;14.6 jel;19.8 jel;2.5 jel;17.9 jel;19.1 jel;21.1 jel;10.8 jel;19.2 jel;18.2 jel;19.7 jel;10.9 jel;26.7 jel;26.7 jel;16.7 jel;-1.2 jel;1.5 jel;20.7 jel;10.3 jel;18.9 jel;9.7 jel;23.9 jel;21.3 jel;13.7 jel;13.5 jel;19.0 jel;16.4 jel;13.4 jel;19.9 jel;3.5 jel;6.9 jel;18.6 jel;8.0 jel;25.1 jel;13.9 jel;13.4 jel;21.1 jel;14.5 jel;15.2 jel;13.6 jel;26.0 jel;18.0 jel;24.0 jel;28.1 jel;19.2 jel;20.3 jel;22.5 jel;17.8 jel;16.5 jel;20.4 jel;19.5 jel;17.7 jel;8.4 jel;18.9 jel;24.8 jel;11.8 jel;12.3 jel;18.9 jel;27.4 jel;21.0 jel;5.7 jel;21.2 jel;23.9 jel;15.7 jel;16.7 jel;15.6 jel;6.1 jel;8.9 jel;16.6 jel;18.9 jel;23.9 jel;21.0 jel;26.7 jel;20.0 jel;15.7 jel;17.9 jel;15.1 jel;13.1 jel;17.0 jel;24.0 jel;18.3 jel;14.0 jel;12.6 jel;15.8 jel;9.8 jel;26.7 jel;18.4 jel;11.9 jel;24.8 jel;19.1 jel;24.8 jel;22.3 jel;21.3 jel;25.3 jel;27.4 jel;21.3 jel;16.3 jel;29.9 jel;21.9 jel;27.6 jel;13.2 jel;14.6 jel;10.3 jel;29.1 jel;35.0 jel;23.8 jel;19.9 jel;2.8 jel;10.2 jel;24.8 jel;15.6 jel;17.9 jel;19.9 jel;21.6 jel;7.4 jel;14.7 jel;10.2 jel;21.7 jel;11.7 jel;19.5 jel;13.1 jel;15.8 jel;13.1 jel;11.3 jel;13.7 jel;18.7 jel;14.4 jel;10.9 jel;34.2 jel;22.3 jel;17.9 jel;25.6 jel;21.2 jel;20.4 jel;9.5 jel;17.8 jel;19.6 jel;18.0 jel;19.4 jel;-1.8 jel;18.9 jel;24.6 jel;13.4 jel;4.9 jel;18.2 jel;16.8 jel;17.9 jel;9.9 jel;13.3 jel;18.1 jel;24.2 jel;24.4 jel;26.9 jel;20.7 jel;17.0 jel;21.0 jel;19.8 jel;26.8 jel;31.2 jel;20.2 jel;20.0 jel;15.4 jel;1.7 jel;23.9 jel;12.5 jel;26.7 jel;13.5 jel;21.4 jel;24.0 jel;12.8 jel;28.5 jel;18.8 jel;17.5 jel;17.9 jel;16.6 jel;13.2 jel;17.3 jel;-9.0 jel;18.6 jel;18.6 jel;14.4 jel;19.8 jel;31.6 jel;13.0 jel;26.2 jel;8.7 jel;14.3 jel;12.8 jel;19.8 jel;11.7 jel;29.8 jel;20.9 jel;26.5 jel;23.7 jel;24.0 jel;27.4 jel;18.0 jel;19.4 jel;30.3 jel;22.5 jel;15.5 jel;18.2 jel;18.3 jel;22.2 jel;16.1 jel;14.5 jel;-3.7 jel;12.1 jel;14.6 jel;20.5 jel;8.2 jel;14.5 jel;19.4 jel;32.5 jel;14.0 jel;19.1 jel;13.3 jel;28.0 jel;23.5 jel;33.8 jel;22.0 jel;18.3 jel;22.7 jel;7.3 jel;4.3 jel;18.3 jel;18.7 jel;15.0 jel;9.3 jel;20.2 jel;19.3 jel;18.4 jel;12.1 jel;29.3 jel;19.9 jel;6.8 jel;30.7 jel;12.6 jel;16.2 jel;19.7 jel;25.9 jel;0.6 jel;17.7 jel;27.2 jel;13.5 jel;18.3 jel;24.2 jel;24.4 jel;11.1 jel;17.0 jel;-0.7 jel;18.5 jel;23.6 jel;8.3 jel;18.6 jel;0.0 jel;13.1 jel;13.0 jel;14.7 jel;30.4 jel;18.8 jel;14.4 jel;29.0 jel;14.0 jel;3.4 jel;13.2 jel;16.1 jel;18.6 jel;24.5 jel;7.4 jel;21.9 jel;16.9 jel;13.1 jel;29.3 jel;8.5 jel;17.2 jel;18.0 jel;20.6 jel;21.7 jel;27.5 jel;14.6 jel;20.3 jel;33.6 jel;18.4 jel;18.4 jel;20.7 jel;8.5 jel;3.1 jel;26.7 jel;10.4 jel;14.5 jel;2.8 jel;14.2 jel;7.4 jel;17.4 jel;15.6 jel;14.8 jel;24.5 jel;12.1 jel;22.5 jel;10.5 jel;25.1 jel;22.2 jel;16.2 jel;15.5 jel;17.5 jel;16.1 jel;10.4 jel;17.2 jel;11.8 jel;13.6 jel;-6.9 jel;17.2 jel;17.6 jel;28.0 jel;10.7 jel;25.3 jel;10.7 jel;17.3 jel;22.9 jel;26.1 jel;33.9 jel;9.8 jel;14.0 jel;18.5 jel;29.9 jel;12.4 jel;16.1 jel;15.1 jel;17.0 jel;20.9 jel;7.8 jel;13.9 jel;16.3 jel;18.1 jel;20.7 jel;23.4 jel;21.0 jel;12.3 jel;15.3 jel;24.0 jel;23.5 jel;16.3 jel;16.1 jel;24.0 jel;19.4 jel;23.3 jel;18.5 jel;11.6 jel;29.6 jel;11.3 jel;19.6 jel;21.7 jel;20.7 jel;17.6 jel;11.9 jel;17.3 jel;25.7 jel;11.9 jel;20.1 jel;22.9 jel;7.2 jel;17.3 jel;33.4 jel;14.3 jel;31.4 jel;21.1 jel;20.8 jel;14.6 jel;21.4 jel;1.9 jel;26.5 jel;28.5 jel;23.8 jel;25.3 jel;18.6 jel;29.1 jel;5.6 jel;19.7 jel;20.5 jel;9.0 jel;22.7 jel;12.8 jel;21.5 jel;22.6 jel;10.2 jel;16.7 jel;13.0 jel;10.8 jel;17.6 jel;18.3 jel;21.6 jel;11.5 jel;22.0 jel;15.5 jel;21.8 jel;32.2 jel;12.1 jel;25.8 jel;2.3 jel;17.1 jel;8.2 jel;17.6 jel;24.4 jel;15.8 jel;-0.5 jel;10.7 jel;30.3 jel;17.0 jel;25.1 jel;11.0 jel;23.2 jel;20.5 jel;16.9 jel;20.1 jel;24.0 jel;15.7 jel;7.3 jel;22.7 jel;20.7 jel;21.5 jel;12.9 jel;13.0 jel;12.3 jel;14.6 jel;9.0 jel;16.0 jel;20.1 jel;9.2 jel;25.3 jel;26.0 jel;19.0 jel;21.2 jel;27.8 jel;25.4 jel;19.1 jel;26.7 jel;2.0 jel;11.9 jel;9.9 jel;29.5 jel;10.9 jel;4.1 jel;26.1 jel;29.0 jel;24.0 jel;5.4 jel;9.5 jel;5.8 jel;20.1 jel;24.3 jel;35.4 jel;26.3 jel;15.1 jel;26.8 jel;11.8 jel;16.1 jel;17.5 jel;15.3 jel;10.0 jel;20.7 jel;18.6 jel;29.0 jel;12.9 jel;12.0 jel;18.0 jel;14.4 jel;18.8 jel;26.4 jel;12.7 jel;20.7 jel;23.6 jel;13.1 jel;24.4 jel;17.6 jel;16.6 jel;12.5 jel;20.1 jel;26.2 jel;11.8 jel;21.6 jel;10.5 jel;23.8 jel;9.4 jel;19.4 jel;25.0 jel;17.1 jel;4.9 jel;29.1 jel;16.5 jel;17.2 jel;20.9 jel;11.4 jel;26.4 jel;16.6 jel;18.2 jel;23.0 jel;18.2 jel;20.5 jel;19.8 jel;20.0 jel;25.7 jel;23.0 jel;5.8 jel;21.0 jel;18.4 jel;23.0 jel;9.5 jel;-0.8 jel;9.7 jel;13.6 jel;22.5 jel;20.1 jel;14.7 jel;23.4 jel;15.8 jel;18.3 jel;30.7 jel;18.9 jel;31.5 jel;27.5 jel;19.6 jel;29.8 jel;10.1 jel;26.8 jel;17.5 jel;17.0 jel;20.5 jel;4.5 jel;24.3 jel;13.7 jel;15.7 jel;8.5 jel;16.1 jel;14.7 jel;31.1 jel;12.9 jel;13.4 jel;23.2 jel;15.7 jel;25.2 jel;22.1 jel;23.6 jel;17.1 jel;22.4 jel;5.7 jel;6.9 jel;12.0 jel;17.3 jel;15.9 jel;19.3 jel;27.5 jel;19.5 jel;21.2 jel;20.3 jel;27.3 jel;17.1 jel;11.8 jel;29.0 jel;19.8 jel;27.3 jel;16.5 jel;29.5 jel;27.1 jel;30.5 jel;25.6 jel;17.9 jel;7.2 jel;10.0 jel;23.1 jel;10.0 jel;31.1 jel;23.6 jel;13.8 jel;12.7 jel;26.5 jel;20.3 jel;17.0 jel;9.9 jel;15.8 jel;9.8 jel;21.6 jel;21.3 jel;7.1 jel;10.6 jel;20.5 jel;22.4 jel;20.4 jel;16.8 jel;21.6 jel;15.1 jel;13.4 jel;27.3 jel;16.8 jel;25.9 jel;10.4 jel;29.5 jel;9.3 jel;29.1 jel;10.6 jel;17.9 jel;19.1 jel;23.8 jel;14.5 jel;22.9 jel;28.0 jel;23.4 jel;26.0 jel;6.6 jel;16.1 jel;21.9 jel;15.1 jel;11.3 jel;16.6 jel;20.6 jel;17.0 jel;20.3 jel;11.2 jel;19.6 jel;22.7 jel;18.0 jel;9.4 jel;21.7 jel;14.5 jel;18.0 jel;28.1 jel;13.4 jel;26.0 jel;9.0 jel;18.3 jel;12.8 jel;30.2 jel;19.9 jel;16.4 jel;13.9 jel;19.9 jel;32.2 jel;18.9 jel;3.1 jel;17.4 jel;19.3 jel;12.5 jel;19.2 jel;20.0 jel;14.7 jel;13.9 jel;17.1 jel;27.8 jel;20.8 jel;15.0 jel;8.8 jel;13.6 jel;22.1 jel;18.4 jel;10.0 jel;9.6 jel;26.7 jel;23.2 jel;3.7 jel;19.8 jel;14.6 jel;26.4 jel;14.0 jel;24.0 jel;17.2 jel;26.8 jel;33.4 jel;23.4 jel;11.9 jel;13.7 jel;19.1 jel;9.2 jel;32.7 jel;16.0 jel;26.1 jel;16.0 jel;18.9 jel;16.6 jel;33.1 jel;15.6 jel;26.2 jel;19.3 jel;20.7 jel;21.4 jel;9.2 jel;15.4 jel;29.9 jel;10.1 jel;8.9 jel;17.9 jel;14.1 jel;22.9 jel;3.4 jel;14.9 jel;27.0 jel;21.2 jel;18.8 jel;8.7 jel;21.7 jel;16.3 jel;9.2 jel;19.1 jel;10.3 jel;16.3 jel;20.5 jel;10.6 jel;22.4 jel;19.1 jel;18.8 jel;8.8 jel;22.4 jel;10.2 jel;22.4 jel;19.6 jel;12.0 jel;19.6 jel;19.2 jel;13.3 jel;13.1 jel;13.3 jel;16.9 jel;9.0 jel;17.7 jel;24.9 jel;6.5 jel;18.0 jel;13.9 jel;16.6 jel;23.2 jel;22.7 jel;26.2 jel;24.6 jel;11.7 jel;16.8 jel;20.6 jel;20.8 jel;19.5 jel;13.7 jel;14.3 jel;19.9 jel;15.7 jel;12.0 jel;7.7 jel;22.7 jel;26.2 jel;23.5 jel;15.6 jel;29.1 jel;21.0 jel;6.3 jel;11.2 jel;21.7 jel;26.7 jel;5.9 jel;15.8 jel;15.4 jel;24.1 jel;15.8 jel;16.2 jel;23.8 jel;16.4 jel;20.4 jel;22.4 jel;21.8 jel;26.6 jel;10.6 jel;19.5 jel;10.7 jel;11.4 jel;26.1 jel;12.3 jel;15.0 jel;11.9 jel;11.7 jel;19.8 jel;12.4 jel;12.0 jel;40.7 jel;17.3 jel;13.0 jel;21.9 jel;29.6 jel;27.1 jel;20.9 jel;17.4 jel;18.3 jel;18.8 jel;25.1 jel;21.6 jel;15.4 jel;20.9 jel;0.2 jel;25.8 jel;32.0 jel;11.1 jel;8.1 jel;9.4 jel;10.5 jel;6.3 jel;11.1 jel;17.2 jel;9.8 jel;26.0 jel;21.2 jel;15.1 jel;15.8 jel;3.2 jel;17.9 jel;12.4 jel;25.9 jel;19.0 jel;18.4 jel;14.8 jel;18.1 jel;19.1 jel;8.3 jel;13.1 jel;14.0 jel;10.0 jel;33.7 jel;18.0 jel;20.2 jel;17.3 jel;15.7 jel;18.8 jel;28.5 jel;28.9 jel;28.2 jel;40.9 jel;32.1 jel;14.8 jel;19.3 jel;23.3 jel;30.8 jel;21.5 jel;13.2 jel;21.7 jel;23.6 jel;8.8 jel;21.0 jel;24.7 jel;22.6 jel;21.6 jel;8.3 jel;20.4 jel;6.8 jel;22.4 jel;26.5 jel;3.7 jel;21.9 jel;27.7 jel;19.9 jel;24.8 jel;14.2 jel;37.5 jel;15.1 jel;11.6 jel;11.8 jel;18.1 jel;26.6 jel;11.2 jel;19.6 jel;22.6 jel;14.5 jel;14.7 jel;24.7 jel;25.1 jel;15.6 jel;24.9 jel;20.2 jel;16.4 jel;20.0 jel;26.9 jel;24.8 jel;12.4 jel;20.5 jel;18.4 jel;12.0 jel;18.1 jel;21.0 jel;32.7 jel;19.7 jel;10.1 jel;23.3 jel;6.6 jel;20.5 jel;24.5 jel;19.3 jel;18.9 jel;17.0 jel;11.1 jel;26.5 jel;12.1 jel;10.9 jel;15.4 jel;11.3 jel;21.1 jel;1.7 jel;21.6 jel;4.1 jel;14.3 jel;17.1 jel;21.3 jel;14.6 jel;14.9 jel;21.7 jel;33.3 jel;22.7 jel;14.6 jel;22.7 jel;16.0 jel;22.7 jel;18.6 jel;21.3 jel;23.2 jel;21.2 jel;15.5 jel;17.0 jel;10.3 jel;14.7 jel;14.0 jel;16.9 jel;21.3 jel;2.8 jel;25.9 jel;23.9 jel;16.8 jel;29.5 jel;25.8 jel;10.4 jel;23.0 jel;24.7 jel;17.0 jel;13.3 jel;1.3 jel;9.8 jel;21.6 jel;22.3 jel;4.7 jel;18.7 jel;19.5 jel;29.4 jel;13.7 jel;24.1 jel;13.1 jel;27.7 jel;17.0 jel;19.2 jel;17.3 jel;28.2 jel;7.6 jel;8.5 jel;20.4 jel;16.0 jel;17.9 jel;17.6 jel;24.2 jel;11.0 jel;16.6 jel;7.7 jel;18.1 jel;19.4 jel;22.4 jel;21.5 jel;24.8 jel;14.0 jel;20.0 jel;20.1 jel;8.8 jel;20.6 jel;18.1 jel;20.5 jel;19.9 jel;18.9 jel;13.5 jel;7.7 jel;21.4 jel;23.1 jel;20.8 jel;18.6 jel;25.6 jel;12.8 jel;21.3 jel;19.0 jel;10.9 jel;35.0 jel;5.9 jel;19.6 jel;9.2 jel;12.0 jel;21.6 jel;15.6 jel;20.4 jel;13.9 jel;13.4 jel;3.5 jel;34.4 jel;12.7 jel;8.2 jel;15.6 jel;20.7 jel;16.5 jel;18.0 jel;21.4 jel;17.9 jel;19.6 jel;11.7 jel;18.9 jel;8.5 jel;23.7 jel;25.3 jel;20.6 jel;17.4 jel;15.8 jel;19.4 jel;14.9 jel;25.1 jel;18.9 jel;8.0 jel;11.4 jel;14.9 jel;17.6 jel;20.8 jel;21.3 jel;17.9 jel;9.1 jel;5.5 jel;15.7 jel;20.6 jel;17.0 jel;27.1 jel;5.1 jel;10.0 jel;21.5 jel;10.6 jel;19.7 jel;25.7 jel;2.2 jel;26.6 jel;10.3 jel;21.2 jel;17.3 jel;23.0 jel;23.2 jel;15.4 jel;6.9 jel;22.5 jel;13.2 jel;19.6 jel;8.9 jel;17.9 jel;25.1 jel;20.5 jel;25.5 jel;18.3 jel;21.6 jel;16.6 jel;23.8 jel;23.2 jel;14.2 jel;15.9 jel;19.3 jel;11.8 jel;28.4 jel;16.4 jel;11.1 jel;17.6 jel;21.7 jel;18.6 jel;10.8 jel;15.2 jel;13.6 jel;6.3 jel;21.3 jel;23.7 jel;4.8 jel;13.5 jel;13.0 jel;9.4 jel;14.0 jel;26.8 jel;7.5 jel;24.3 jel;27.2 jel;24.5 jel;14.5 jel;16.1 jel;14.1 jel;12.9 jel;25.8 jel;21.8 jel;25.3 jel;12.7 jel;11.9 jel;21.0 jel;21.9 jel;28.0 jel;29.0 jel;17.6 jel;25.7 jel;11.9 jel;23.6 jel;13.6 jel;9.2 jel;9.6 jel;16.7 jel;22.2 jel;19.5 jel;29.1 jel;21.7 jel;12.2 jel;14.2 jel;20.7 jel;1.4 jel;29.0 jel;29.6 jel;30.2 jel;24.5 jel;32.6 jel;7.3 jel;15.0 jel;21.5 jel;8.1 jel;16.0 jel;17.4 jel;6.9 jel;17.8 jel;27.5 jel;9.6 jel;33.7 jel;18.8 jel;21.3 jel;33.8 jel;16.1 jel;24.0 jel;15.2 jel;11.8 jel;9.7 jel;25.5 jel;22.0 jel;26.5 jel;19.8 jel;10.0 jel;14.5 jel;13.9 jel;22.6 jel;19.0 jel;24.1 jel;12.1 jel;15.3 jel;18.4 jel;20.3 jel;33.4 jel;22.2 jel;22.5 jel;15.9 jel;17.9 jel;28.3 jel;3.4 jel;22.2 jel;13.0 jel;19.8 jel;20.3 jel;21.4 jel;19.9 jel;19.6 jel;14.8 jel;18.6 jel;21.5 jel;15.4 jel;10.8 jel;27.6 jel;17.8 jel;36.0 jel;13.5 jel;9.8 jel;14.2 jel;8.8 jel;21.3 jel;22.1 jel;5.4 jel;10.3 jel;13.8 jel;21.0 jel;19.3 jel;23.1 jel;28.2 jel;16.3 jel;19.2 jel;8.9 jel;18.6 jel;21.5 jel;24.4 jel;9.8 jel;26.2 jel;31.4 jel;28.3 jel;18.5 jel;2.0 jel;21.2 jel;15.9 jel;27.9 jel;23.2 jel;17.3 jel;12.9 jel;18.7 jel;5.0 jel;16.8 jel;13.1 jel;26.6 jel;17.8 jel;16.7 jel;18.5 jel;12.7 jel;28.9 jel;21.9 jel;20.2 jel;15.3 jel;25.3 jel;32.4 jel;29.1 jel;19.4 jel;14.7 jel;22.7 jel;26.8 jel;9.3 jel;19.3 jel;25.9 jel;24.7 jel;25.4 jel;28.9 jel;11.9 jel;15.8 jel;22.4 jel;16.4 jel;28.1 jel;20.1 jel;10.7 jel;12.4 jel;16.3 jel;6.4 jel;22.5 jel;6.4 jel;27.1 jel;22.9 jel;23.2 jel;26.6 jel;25.3 jel;15.5 jel;20.9 jel;21.9 jel;13.1 jel;21.6 jel;12.4 jel;29.8 jel;10.5 jel;15.4 jel;23.9 jel;6.4 jel;20.4 jel;17.3 jel;24.7 jel;18.0 jel;20.5 jel;9.3 jel;14.0 jel;27.2 jel;27.6 jel;20.6 jel;13.5 jel;21.5 jel;14.1 jel;23.4 jel;12.8 jel;16.5 jel;18.0 jel;18.8 jel;25.2 jel;17.7 jel;7.0 jel;27.8 jel;20.0 jel;4.9 jel;15.6 jel;14.7 jel;21.1 jel;12.4 jel;32.0 jel;24.0 jel;19.2 jel;20.0 jel;25.6 jel;22.2 jel;27.1 jel;19.2 jel;12.1 jel;27.4 jel;11.9 jel;20.2 jel;13.9 jel;26.3 jel;15.8 jel;13.6 jel;7.8 jel;12.4 jel;11.7 jel;19.4 jel;19.5 jel;18.5 jel;22.3 jel;15.0 jel;13.4 jel;0.8 jel;12.0 jel;9.5 jel;16.1 jel;21.2 jel;14.5 jel;26.4 jel;21.3 jel;19.0 jel;10.4 jel;17.7 jel;14.2 jel;26.1 jel;19.1 jel;25.3 jel;20.0 jel;1.1 jel;22.1 jel;26.9 jel;15.0 jel;23.2 jel;20.0 jel;14.0 jel;23.0 jel;13.4 jel;19.2 jel;31.2 jel;11.5 jel;20.9 jel;20.1 jel;-0.5 jel;18.1 jel;15.9 jel;9.3 jel;17.6 jel;15.3 jel;17.2 jel;20.3 jel;14.4 jel;14.4 jel;29.0 jel;14.9 jel;25.5 jel;14.1 jel;12.8 jel;14.4 jel;16.7 jel;13.3 jel;11.9 jel;8.8 jel;12.1 jel;16.1 jel;7.5 jel;17.0 jel;18.7 jel;21.7 jel;22.1 jel;12.1 jel;26.0 jel;4.1 jel;15.4 jel;2.3 jel;12.8 jel;15.8 jel;16.2 jel;22.2 jel;21.2 jel;18.2 jel;29.1 jel;16.2 jel;5.5 jel;18.4 jel;21.1 jel;24.4 jel;20.9 jel;14.8 jel;24.2 jel;31.4 jel;20.6 jel;13.3 jel;15.7 jel;10.2 jel;25.7 jel;17.1 jel;11.9 jel;13.7 jel;20.6 jel;9.4 jel;20.5 jel;22.1 jel;19.8 jel;12.1 jel;13.3 jel;14.7 jel;37.3 jel;14.5 jel;23.4 jel;15.8 jel;17.1 jel;12.2 jel;20.6 jel;26.0 jel;17.5 jel;19.6 jel;4.2 jel;23.1 jel;21.3 jel;22.1 jel;16.1 jel;3.6 jel;23.7 jel;21.9 jel;19.5 jel;15.2 jel;23.7 jel;6.1 jel;24.5 jel;22.4 jel;27.6 jel;16.7 jel;10.4 jel;30.0 jel;12.7 jel;33.5 jel;16.1 jel;19.7 jel;32.0 jel;15.4 jel;17.2 jel;26.4 jel;13.5 jel;16.5 jel;23.8 jel;11.9 jel;10.9 jel;15.7 jel;4.0 jel;18.9 jel;8.4 jel;6.0 jel;-4.5 jel;18.5 jel;15.3 jel;0.2 jel;28.9 jel;10.5 jel;14.4 jel;8.6 jel;19.3 jel;14.0 jel;25.7 jel;16.3 jel;38.6 jel;27.1 jel;24.2 jel;33.2 jel;7.4 jel;21.0 jel;14.5 jel;22.9 jel;11.6 jel;26.0 jel;7.4 jel;10.6 jel;25.1 jel;16.9 jel;16.9 jel;14.0 jel;14.8 jel;22.6 jel;24.4 jel;23.9 jel;24.1 jel;15.6 jel;29.0 jel;8.5 jel;14.4 jel;17.7 jel;6.8 jel;7.8 jel;13.5 jel;20.3 jel;19.9 jel;9.7 jel;21.2 jel;14.4 jel;13.9 jel;20.9 jel;28.9 jel;23.4 jel;17.5 jel;17.0 jel;15.9 jel;25.4 jel;11.3 jel;12.4 jel;24.1 jel;8.7 jel;29.6 jel;13.0 jel;24.1 jel;10.7 jel;10.3 jel;26.9 jel;10.9 jel;9.9 jel;14.1 jel;24.4 jel;11.9 jel;22.0 jel;7.9 jel;16.2 jel;14.4 jel;20.2 jel;16.5 jel;18.1 jel;15.4 jel;7.1 jel;21.8 jel;12.5 jel;15.5 jel;19.8 jel;12.6 jel;18.4 jel;16.1 jel;34.1 jel;15.3 jel;6.2 jel;6.6 jel;17.5 jel;25.1 jel;31.3 jel;26.7 jel;21.2 jel;17.7 jel;22.9 jel;20.6 jel;26.3 jel;17.4 jel;15.9 jel;7.1 jel;22.5 jel;21.3 jel;15.3 jel;17.2 jel;23.6 jel;9.2 jel;16.7 jel;23.6 jel;21.2 jel;18.3 jel;9.5 jel;10.2 jel;17.8 jel;7.7 jel;29.9 jel;16.5 jel;15.1 jel;18.4 jel;29.4 jel;25.7 jel;19.2 jel;14.9 jel;27.2 jel;22.8 jel;17.5 jel;4.5 jel;31.9 jel;19.7 jel;14.4 jel;26.0 jel;24.6 jel;15.5 jel;18.2 jel;15.5 jel;10.6 jel;22.3 jel;16.5 jel;22.8 jel;8.0 jel;18.0 jel;20.3 jel;22.9 jel;19.7 jel;17.6 jel;15.5 jel;9.7 jel;14.5 jel;13.9 jel;15.0 jel;23.6 jel;25.6 jel;15.4 jel;16.5 jel;31.1 jel;5.2 jel;19.7 jel;6.4 jel;22.4 jel;3.7 jel;24.3 jel;30.1 jel;14.1 jel;12.8 jel;16.4 jel;14.7 jel;27.2 jel;21.7 jel;13.9 jel;16.7 jel;14.4 jel;18.1 jel;27.0 jel;17.0 jel;8.6 jel;23.7 jel;14.0 jel;5.5 jel;7.0 jel;18.8 jel;21.2 jel;31.5 jel;20.7 jel;32.5 jel;13.6 jel;25.4 jel;12.4 jel;23.1 jel;26.9 jel;32.7 jel;23.9 jel;19.7 jel;9.5 jel;8.8 jel;17.6 jel;18.2 jel;19.7 jel;8.5 jel;17.8 jel;25.6 jel;23.4 jel;16.7 jel;5.3 jel;11.2 jel;8.5 jel;15.1 jel;23.4 jel;20.6 jel;21.5 jel;16.4 jel;22.1 jel;24.3 jel;18.5 jel;11.6 jel;18.0 jel;18.0 jel;15.1 jel;19.0 jel;20.3 jel;10.5 jel;21.6 jel;23.9 jel;16.0 jel;20.6 jel;14.4 jel;14.6 jel;14.7 jel;22.9 jel;13.3 jel;18.5 jel;17.6 jel;16.3 jel;10.5 jel;28.0 jel;18.4 jel;-5.1 jel;20.1 jel;21.6 jel;17.4 jel;11.7 jel;3.8 jel;14.8 jel;23.0 jel;24.3 jel;15.1 jel;26.5 jel;10.4 jel;17.6 jel;31.0 jel;16.7 jel;17.2 jel;-2.0 jel;16.1 jel;19.7 jel;17.2 jel;16.9 jel;17.5 jel;11.6 jel;21.0 jel;13.6 jel;25.5 jel;29.7 jel;22.2 jel;15.4 jel;17.8 jel;24.0 jel;15.8 jel;23.9 jel;23.6 jel;17.1 jel;21.1 jel;19.7 jel;15.1 jel;20.6 jel;31.1 jel;17.6 jel;26.9 jel;13.9 jel;26.0 jel;21.3 jel;22.5 jel;21.9 jel;22.5 jel;27.3 jel;18.9 jel;15.0 jel;16.5 jel;19.7 jel;16.1 jel;13.1 jel;25.3 jel;25.6 jel;17.5 jel;19.4 jel;20.5 jel;23.0 jel;15.5 jel;22.5 jel;17.8 jel;25.5 jel;10.6 jel;14.2 jel;23.6 jel;21.7 jel;23.9 jel;17.9 jel;22.0 jel;11.7 jel;16.3 jel;5.5 jel;11.1 jel;20.5 jel;29.4 jel;24.5 jel;23.0 jel;13.7 jel;30.3 jel;23.8 jel;17.4 jel;7.2 jel;26.1 jel;6.5 jel;19.7 jel;23.4 jel;22.2 jel;18.1 jel;26.0 jel;12.3 jel;17.9 jel;16.7 jel;12.0 jel;23.7 jel;16.1 jel;22.3 jel;16.8 jel;25.3 jel;12.9 jel;13.5 jel;21.8 jel;23.6 jel;23.3 jel;5.1 jel;9.5 jel;11.1 jel;17.9 jel;17.0 jel;26.8 jel;27.4 jel;4.1 jel;10.7 jel;14.8 jel;19.5 jel;12.6 jel;30.2 jel;20.6 jel;10.1 jel;13.7 jel;18.4 jel;4.3 jel;23.1 jel;17.6 jel;14.3 jel;17.7 jel;23.7 jel;11.9 jel;13.7 jel;7.8 jel;14.3 jel;17.2 jel;5.1 jel;21.8 jel;5.7 jel;22.5 jel;22.6 jel;13.8 jel;26.9 jel;12.6 jel;19.8 jel;16.7 jel;17.0 jel;23.7 jel;11.4 jel;3.7 jel;11.1 jel;14.7 jel;20.7 jel;18.2 jel;9.6 jel;21.9 jel;11.5 jel;13.1 jel;16.2 jel;31.9 jel;9.4 jel;16.1 jel;21.7 jel;11.9 jel;19.1 jel;25.7 jel;21.7 jel;18.5 jel;5.5 jel;12.7 jel;11.5 jel;7.8 jel;21.6 jel;7.1 jel;12.4 jel;5.5 jel;25.8 jel;15.2 jel;17.6 jel;25.8 jel;7.5 jel;26.5 jel;30.3 jel;25.5 jel;20.0 jel;16.0 jel;21.2 jel;7.2 jel;7.6 jel;19.1 jel;26.4 jel;11.1 jel;10.2 jel;4.5 jel;27.6 jel;15.7 jel;-2.5 jel;18.7 jel;16.4 jel;14.9 jel;18.1 jel;23.0 jel;9.0 jel;14.5 jel;29.6 jel;5.6 jel;12.6 jel;12.7 jel;20.6 jel;26.2 jel;30.1 jel;26.4 jel;6.8 jel;8.9 jel;25.5 jel;34.0 jel;12.9 jel;7.5 jel;20.2 jel;34.0 jel;16.5 jel;21.3 jel;33.0 jel;26.8 jel;17.0 jel;8.9 jel;7.9 jel;2.3 jel;25.7 jel;25.2 jel;29.4 jel;9.1 jel;8.0 jel;20.5 jel;16.1 jel;14.2 jel;20.5 jel;15.6 jel;8.9 jel;11.2 jel;5.2 jel;12.3 jel;19.1 jel;18.3 jel;21.3 jel;25.9 jel;15.9 jel;11.5 jel;19.9 jel;30.8 jel;18.5 jel;29.4 jel;13.2 jel;20.1 jel;29.3 jel;26.0 jel;8.8 jel;32.9 jel;14.9 jel;15.7 jel;28.5 jel;7.7 jel;14.6 jel;15.4 jel;14.4 jel;17.8 jel;17.3 jel;8.9 jel;19.0 jel;22.8 jel;17.0 jel;21.6 jel;5.8 jel;22.7 jel;9.4 jel;17.6 jel;12.2 jel;19.8 jel;32.0 jel;22.2 jel;16.8 jel;21.4 jel;18.5 jel;19.7 jel;18.9 jel;9.3 jel;19.0 jel;14.9 jel;16.8 jel;17.5 jel;16.2 jel;22.6 jel;14.5 jel;7.5 jel;24.1 jel;23.4 jel;29.7 jel;20.5 jel;14.6 jel;29.1 jel;17.9 jel;15.1 jel;8.6 jel;24.6 jel;24.9 jel;15.0 jel;24.2 jel;15.7 jel;19.8 jel;19.3 jel;23.5 jel;6.8 jel;30.0 jel;10.0 jel;23.6 jel;25.0 jel;16.3 jel;24.3 jel;24.4 jel;18.2 jel;16.5 jel;15.3 jel;23.9 jel;14.4 jel;15.1 jel;27.2 jel;27.8 jel;24.9 jel;20.6 jel;39.4 jel;19.0 jel;14.9 jel;-3.7 jel;17.6 jel;21.8 jel;14.3 jel;25.8 jel;16.0 jel;32.1 jel;13.9 jel;24.2 jel;17.5 jel;12.4 jel;13.7 jel;32.9 jel;16.5 jel;7.3 jel;18.1 jel;-4.4 jel;13.2 jel;28.9 jel;32.6 jel;11.3 jel;11.4 jel;31.5 jel;24.8 jel;7.5 jel;12.5 jel;19.9 jel;10.1 jel;17.8 jel;13.0 jel;13.8 jel;15.6 jel;25.0 jel;12.4 jel;14.5 jel;10.2 jel;15.7 jel;24.1 jel;15.8 jel;16.1 jel;19.3 jel;12.7 jel;22.9 jel;14.9 jel;-0.3 jel;23.0 jel;7.6 jel;10.2 jel;-0.8 jel;17.0 jel;17.1 jel;16.0 jel;12.9 jel;24.4 jel;22.9 jel;14.3 jel;15.6 jel;16.8 jel;31.3 jel;28.3 jel;8.5 jel;23.8 jel;25.0 jel;21.5 jel;23.4 jel;4.5 jel;14.8 jel;24.1 jel;10.1 jel;9.9 jel;17.4 jel;26.8 jel;26.7 jel;14.7 jel;16.7 jel;15.4 jel;4.7 jel;19.2 jel;19.3 jel;24.9 jel;21.4 jel;21.2 jel;22.4 jel;21.7 jel;19.7 jel;19.1 jel;22.7 jel;21.0 jel;4.5 jel;28.8 jel;23.4 jel;18.4 jel;32.0 jel;27.9 jel;8.9 jel;11.9 jel;25.0 jel;12.0 jel;19.3 jel;19.0 jel;16.1 jel;14.0 jel;14.3 jel;25.0 jel;24.6 jel;13.7 jel;19.0 jel;10.3 jel;25.8 jel;14.9 jel;8.0 jel;12.1 jel;15.0 jel;28.7 jel;28.7 jel;12.9 jel;25.4 jel;11.0 jel;37.8 jel;9.2 jel;23.9 jel;28.4 jel;13.7 jel;15.8 jel;20.8 jel;19.4 jel;21.0 jel;8.9 jel;14.8 jel;27.0 jel;25.2 jel;32.1 jel;28.1 jel;5.7 jel;16.6 jel;22.7 jel;13.6 jel;9.9 jel;28.8 jel;14.0 jel;16.9 jel;14.0 jel;20.9 jel;23.1 jel;30.9 jel;21.9 jel;-1.5 jel;7.7 jel;27.8 jel;26.4 jel;21.8 jel;20.7 jel;28.7 jel;12.9 jel;5.3 jel;20.2 jel;24.8 jel;18.2 jel;25.1 jel;9.0 jel;7.3 jel;25.3 jel;11.2 jel;14.9 jel;9.3 jel;12.9 jel;23.6 jel;26.3 jel;5.2 jel;18.2 jel;35.8 jel;23.9 jel;30.0 jel;22.0 jel;20.1 jel;18.4 jel;15.6 jel;14.7 jel;27.1 jel;12.1 jel;12.6 jel;6.8 jel;13.9 jel;14.7 jel;15.9 jel;27.5 jel;25.7 jel;16.9 jel;22.8 jel;8.3 jel;20.2 jel;30.4 jel;17.9 jel;22.7 jel;20.8 jel;30.2 jel;3.9 jel;14.5 jel;12.4 jel;24.8 jel;26.5 jel;10.2 jel;13.6 jel;11.3 jel;16.2 jel;12.9 jel;23.7 jel;12.9 jel;22.8 jel;27.8 jel;28.3 jel;21.4 jel;10.0 jel;13.4 jel;11.1 jel;13.0 jel;17.4 jel;26.2 jel;12.8 jel;24.4 jel;23.5 jel;23.8 jel;15.0 jel;21.1 jel;15.6 jel;11.0 jel;22.4 jel;24.7 jel;24.8 jel;16.7 jel;-2.5 jel;5.7 jel;26.9 jel;24.3 jel;26.0 jel;16.8 jel;17.9 jel;18.2 jel;22.1 jel;12.1 jel;18.4 jel;29.0 jel;10.8 jel;26.7 jel;15.4 jel;17.8 jel;18.6 jel;6.7 jel;18.0 jel;19.7 jel;22.2 jel;17.3 jel;14.7 jel;6.5 jel;13.6 jel;11.0 jel;26.0 jel;20.8 jel;30.1 jel;7.0 jel;29.0 jel;2.9 jel;10.2 jel;10.6 jel;22.3 jel;16.3 jel;21.5 jel;8.4 jel;17.4 jel;24.5 jel;21.0 jel;19.6 jel;18.6 jel;32.5 jel;13.1 jel;6.1 jel;36.1 jel;14.4 jel;18.2 jel;14.0 jel;20.9 jel;21.5 jel;24.1 jel;9.3 jel;21.5 jel;14.9 jel;18.1 jel;18.3 jel;25.4 jel;15.0 jel;32.4 jel;12.9 jel;19.6 jel;28.2 jel;21.8 jel;7.0 jel;16.6 jel;17.8 jel;18.1 jel;5.8 jel;13.1 jel;22.8 jel;15.3 jel;34.1 jel;12.5 jel;14.8 jel;19.9 jel;15.4 jel;8.4 jel;7.0 jel;19.0 jel;29.3 jel;8.5 jel;11.4 jel;38.0 jel;8.1 jel;15.6 jel;26.4 jel;25.3 jel;27.3 jel;17.2 jel;13.7 jel;22.8 jel;33.8 jel;21.8 jel;17.6 jel;6.7 jel;10.4 jel;17.4 jel;18.2 jel;23.4 jel;22.6 jel;22.6 jel;24.1 jel;20.9 jel;20.4 jel;24.7 jel;22.1 jel;16.5 jel;20.9 jel;19.3 jel;17.4 jel;22.9 jel;18.8 jel;6.0 jel;7.3 jel;2.8 jel;18.9 jel;10.4 jel;21.9 jel;12.1 jel;7.0 jel;9.1 jel;10.6 jel;26.0 jel;8.2 jel;32.1 jel;24.4 jel;24.4 jel;12.3 jel;13.7 jel;8.7 jel;27.2 jel;28.1 jel;17.7 jel;14.4 jel;28.4 jel;19.8 jel;8.1 jel;29.9 jel;19.4 jel;26.1 jel;13.9 jel;20.5 jel;23.2 jel;16.7 jel;18.7 jel;19.9 jel;14.7 jel;12.8 jel;28.1 jel;16.8 jel;15.8 jel;15.6 jel;13.9 jel;33.5 jel;9.1 jel;18.7 jel;15.4 jel;9.1 jel;21.4 jel;21.3 jel;14.9 jel;24.6 jel;14.0 jel;22.7 jel;15.6 jel;14.9 jel;15.9 jel;15.0 jel;19.1 jel;11.0 jel;17.5 jel;9.7 jel;5.6 jel;17.0 jel;15.3 jel;18.5 jel;22.6 jel;22.2 jel;26.5 jel;22.4 jel;7.5 jel;17.3 jel;26.0 jel;17.3 jel;16.6 jel;11.3 jel;1.1 jel;16.5 jel;14.9 jel;14.9 jel;23.3 jel;5.7 jel;17.7 jel;19.4 jel;12.5 jel;2.4 jel;21.2 jel;21.0 jel;19.2 jel;28.3 jel;14.2 jel;23.6 jel;10.0 jel;18.7 jel;28.4 jel;10.6 jel;23.0 jel;17.6 jel;27.6 jel;16.8 jel;12.9 jel;26.0 jel;34.2 jel;22.3 jel;15.6 jel;25.0 jel;12.9 jel;10.3 jel;17.6 jel;21.2 jel;29.0 jel;26.8 jel;14.9 jel;14.0 jel;20.8 jel;26.4 jel;20.5 jel;21.1 jel;11.8 jel;28.6 jel;4.7 jel;15.6 jel;22.2 jel;19.9 jel;14.4 jel;16.9 jel;27.7 jel;10.3 jel;18.4 jel;29.3 jel;20.5 jel;15.7 jel;29.1 jel;20.0 jel;16.4 jel;19.0 jel;20.9 jel;11.9 jel;13.5 jel;24.2 jel;25.2 jel;11.5 jel;21.5 jel;6.0 jel;18.8 jel;22.8 jel;26.0 jel;14.8 jel;8.5 jel;6.4 jel;20.3 jel;10.3 jel;15.0 jel;2.9 jel;19.1 jel;10.0 jel;2.2 jel;14.3 jel;17.9 jel;10.2 jel;11.0 jel;21.6 jel;13.9 jel;11.9 jel;14.6 jel;15.4 jel;18.5 jel;11.4 jel;8.2 jel;9.4 jel;28.1 jel;6.0 jel;17.9 jel;19.7 jel;14.4 jel;31.6 jel;23.7 jel;32.6 jel;11.4 jel;9.9 jel;20.5 jel;18.4 jel;21.3 jel;10.4 jel;5.4 jel;13.7 jel;8.7 jel;24.2 jel;16.2 jel;20.9 jel;7.7 jel;22.3 jel;10.2 jel;23.6 jel;28.1 jel;9.7 jel;5.2 jel;24.5 jel;6.7 jel;16.6 jel;15.9 jel;12.6 jel;24.8 jel;7.0 jel;20.6 jel;29.7 jel;18.0 jel;22.4 jel;23.7 jel;19.9 jel;7.4 jel;6.1 jel;17.3 jel;20.6 jel;23.2 jel;14.9 jel;16.8 jel;22.9 jel;28.6 jel;23.9 jel;31.4 jel;28.4 jel;30.2 jel;24.7 jel;16.8 jel;7.2 jel;18.3 jel;15.4 jel;14.6 jel;16.6 jel;15.3 jel;22.6 jel;13.1 jel;23.5 jel;25.5 jel;18.7 jel;21.3 jel;17.1 jel;20.9 jel;28.0 jel;19.8 jel;11.4 jel;25.5 jel;19.5 jel;4.8 jel;24.8 jel;23.3 jel;26.7 jel;9.7 jel;16.2 jel;26.6 jel;24.3 jel;10.9 jel;26.6 jel;9.9 jel;22.8 jel;15.7 jel;20.0 jel;13.7 jel;18.5 jel;26.7 jel;15.1 jel;23.2 jel;20.8 jel;14.0 jel;20.2 jel;10.7 jel;19.5 jel;17.0 jel;18.5 jel;35.1 jel;10.3 jel;28.6 jel;18.0 jel;21.5 jel;13.8 jel;1.1 jel;14.7 jel;21.4 jel;17.8 jel;39.2 jel;10.4 jel;23.4 jel;28.2 jel;12.9 jel;29.3 jel;21.2 jel;14.6 jel;18.0 jel;12.9 jel;19.5 jel;14.4 jel;30.7 jel;40.9 jel;22.2 jel;7.4 jel;24.7 jel;17.1 jel;19.3 jel;13.7 jel;25.9 jel;24.2 jel;16.8 jel;25.3 jel;11.4 jel;10.5 jel;27.9 jel;15.9 jel;32.5 jel;-2.9 jel;19.3 jel;26.8 jel;15.6 jel;21.9 jel;19.1 jel;4.9 jel;17.3 jel;25.9 jel;19.5 jel;2.7 jel;20.2 jel;7.6 jel;15.6 jel;15.2 jel;26.3 jel;31.0 jel;22.6 jel;21.3 jel;10.5 jel;18.0 jel;16.4 jel;15.3 jel;19.6 jel;25.9 jel;29.2 jel;19.0 jel;21.4 jel;17.5 jel;2.2 jel;26.0 jel;18.8 jel;16.5 jel;10.6 jel;27.7 jel;28.4 jel;5.8 jel;22.1 jel;12.2 jel;19.8 jel;20.3 jel;36.5 jel;23.8 jel;10.7 jel;20.8 jel;31.0 jel;22.8 jel;12.6 jel;21.7 jel;16.8 jel;18.6 jel;20.1 jel;11.3 jel;18.3 jel;12.8 jel;12.2 jel;21.6 jel;11.0 jel;-0.5 jel;27.0 jel;27.4 jel;27.6 jel;8.4 jel;12.3 jel;21.6 jel;21.4 jel;19.4 jel;23.8 jel;19.5 jel;28.9 jel;29.1 jel;15.3 jel;14.4 jel;4.6 jel;17.2 jel;25.9 jel;22.1 jel;10.1 jel;19.8 jel;24.6 jel;7.0 jel;13.5 jel;19.9 jel;17.1 jel;16.6 jel;25.0 jel;30.1 jel;14.9 jel;7.0 jel;17.7 jel;15.0 jel;30.7 jel;10.9 jel;18.5 jel;20.9 jel;4.0 jel;28.6 jel;17.6 jel;25.8 jel;18.9 jel;14.1 jel;29.4 jel;25.5 jel;13.2 jel;14.3 jel;20.6 jel;23.6 jel;24.0 jel;16.8 jel;5.9 jel;16.2 jel;39.0 jel;22.5 jel;28.0 jel;8.8 jel;21.0 jel;8.7 jel;4.2 jel;22.1 jel;8.9 jel;22.9 jel;17.4 jel;12.7 jel;9.8 jel;10.8 jel;9.3 jel;25.0 jel;17.1 jel;16.2 jel;16.2 jel;27.5 jel;16.5 jel;26.8 jel;8.1 jel;13.6 jel;25.1 jel;15.5 jel;19.3 jel;8.6 jel;18.8 jel;6.7 jel;21.1 jel;13.6 jel;12.4 jel;29.4 jel;20.6 jel;24.9 jel;11.9 jel;27.9 jel;25.3 jel;8.3 jel;23.3 jel;20.1 jel;12.0 jel;32.2 jel;15.7 jel;14.1 jel;22.8 jel;21.1 jel;9.7 jel;-1.7 jel;16.7 jel;25.6 jel;14.9 jel;16.8 jel;10.7 jel;8.3 jel;16.1 jel;17.1 jel;10.7 jel;2.3 jel;15.0 jel;10.1 jel;17.4 jel;25.6 jel;10.7 jel;23.4 jel;18.1 jel;26.6 jel;9.1 jel;31.0 jel;31.2 jel;19.4 jel;11.0 jel;16.3 jel;18.3 jel;29.3 jel;18.8 jel;14.8 jel;19.2 jel;16.5 jel;29.5 jel;20.7 jel;23.6 jel;17.9 jel;8.2 jel;20.2 jel;17.9 jel;37.7 jel;19.4 jel;12.8 jel;13.3 jel;18.4 jel;1.7 jel;13.7 jel;7.8 jel;16.1 jel;9.4 jel;19.4 jel;18.7 jel;15.9 jel;22.7 jel;20.3 jel;15.9 jel;-4.7 jel;24.7 jel;20.1 jel;20.5 jel;16.3 jel;14.2 jel;29.5 jel;15.3 jel;1.4 jel;15.5 jel;21.2 jel;15.5 jel;26.0 jel;15.0 jel;23.3 jel;20.2 jel;13.7 jel;30.1 jel;28.0 jel;24.0 jel;17.0 jel;27.9 jel;12.4 jel;5.3 jel;27.9 jel;14.8 jel;23.3 jel;8.3 jel;8.8 jel;16.1 jel;22.2 jel;10.6 jel;12.1 jel;24.0 jel;20.8 jel;14.7 jel;17.0 jel;17.7 jel;19.0 jel;11.2 jel;27.0 jel;25.0 jel;20.3 jel;18.4 jel;18.1 jel;28.4 jel;28.7 jel;18.0 jel;19.9 jel;18.1 jel;24.5 jel;19.4 jel;19.6 jel;20.6 jel;7.3 jel;24.3 jel;19.8 jel;17.8 jel;12.1 jel;24.8 jel;16.5 jel;27.4 jel;16.4 jel;22.2 jel;20.7 jel;13.4 jel;15.3 jel;15.3 jel;23.5 jel;15.4 jel;15.8 jel;22.3 jel;14.3 jel;14.9 jel;11.6 jel;16.5 jel;14.5 jel;14.0 jel;17.6 jel;10.5 jel;9.8 jel;23.5 jel;20.0 jel;21.6 jel;9.6 jel;20.9 jel;16.4 jel;22.3 jel;4.2 jel;30.4 jel;28.2 jel;17.7 jel;15.9 jel;16.6 jel;33.5 jel;13.9 jel;14.7 jel;5.5 jel;22.1 jel;24.0 jel;25.7 jel;17.2 jel;15.9 jel;14.8 jel;22.2 jel;12.5 jel;25.5 jel;21.4 jel;24.1 jel;16.5 jel;20.4 jel;11.8 jel;27.0 jel;19.8 jel;21.2 jel;20.6 jel;29.1 jel;9.7 jel;14.1 jel;17.2 jel;18.7 jel;10.6 jel;13.9 jel;15.3 jel;31.0 jel;22.1 jel;26.6 jel;20.0 jel;3.8 jel;12.7 jel;20.3 jel;16.4 jel;9.3 jel;11.8 jel;23.1 jel;13.1 jel;15.1 jel;20.9 jel;2.6 jel;18.2 jel;19.4 jel;16.2 jel;17.8 jel;26.2 jel;14.4 jel;13.9 jel;20.7 jel;7.4 jel;19.5 jel;15.9 jel;13.4 jel;18.8 jel;23.6 jel;16.3 jel;19.6 jel;14.5 jel;17.6 jel;20.0 jel;20.6 jel;22.1 jel;11.3 jel;10.6 jel;33.0 jel;24.9 jel;28.2 jel;12.7 jel;7.0 jel;12.6 jel;20.2 jel;20.2 jel;14.0 jel;17.4 jel;26.8 jel;17.9 jel;15.0 jel;24.2 jel;5.5 jel;18.7 jel;15.9 jel;20.1 jel;26.8 jel;15.5 jel;16.0 jel;14.9 jel;9.9 jel;12.3 jel;13.0 jel;28.0 jel;19.6 jel;14.5 jel;18.9 jel;15.8 jel;19.2 jel;19.4 jel;17.6 jel;25.9 jel;20.8 jel;10.7 jel;15.8 jel;6.5 jel;14.8 jel;18.5 jel;18.8 jel;7.8 jel;21.2 jel;8.4 jel;3.0 jel;8.6 jel;14.3 jel;29.4 jel;25.0 jel;17.5 jel;27.5 jel;32.5 jel;34.3 jel;23.2 jel;18.8 jel;19.3 jel;11.1 jel;19.5 jel;22.0 jel;21.1 jel;23.3 jel;10.5 jel;20.1 jel;27.6 jel;21.9 jel;17.5 jel;23.4 jel;24.3 jel;27.1 jel;19.6 jel;21.6 jel;17.7 jel;16.3 jel;24.1 jel;10.9 jel;3.6 jel;7.8 jel;22.8 jel;9.5 jel;20.6 jel;20.6 jel;20.0 jel;10.6 jel;23.2 jel;14.1 jel;27.8 jel;12.6 jel;1.3 jel;17.7 jel;20.3 jel;31.1 jel;22.0 jel;23.4 jel;26.6 jel;21.3 jel;22.9 jel;8.3 jel;18.2 jel;15.2 jel;15.5 jel;9.1 jel;16.6 jel;10.8 jel;17.9 jel;21.7 jel;21.1 jel;20.8 jel;26.8 jel;23.7 jel;14.4 jel;19.3 jel;10.1 jel;8.9 jel;12.6 jel;19.2 jel;26.0 jel;24.6 jel;26.7 jel;21.5 jel;23.0 jel;23.6 jel;20.0 jel;21.1 jel;10.6 jel;19.4 jel;10.1 jel;14.8 jel;21.8 jel;18.1 jel;20.2 jel;15.6 jel;20.0 jel;11.6 jel;16.4 jel;11.4 jel;19.4 jel;24.6 jel;10.2 jel;19.6 jel;8.5 jel;24.8 jel;18.2 jel;26.1 jel;24.0 jel;13.8 jel;19.0 jel;25.6 jel;10.4 jel;21.3 jel;23.0 jel;22.6 jel;26.7 jel;9.5 jel;0.3 jel;13.0 jel;19.0 jel;16.7 jel;16.7 jel;8.1 jel;19.7 jel;20.0 jel;20.1 jel;24.1 jel;23.4 jel;25.8 jel;13.7 jel;12.1 jel;9.3 jel;8.0 jel;27.6 jel;19.8 jel;8.6 jel;28.2 jel;9.5 jel;11.3 jel;12.7 jel;21.8 jel;14.5 jel;27.4 jel;6.3 jel;3.8 jel;5.9 jel;16.4 jel;22.5 jel;21.6 jel;22.9 jel;12.8 jel;12.1 jel;21.3 jel;22.0 jel;20.2 jel;25.1 jel;8.0 jel;18.8 jel;19.9 jel;16.6 jel;20.3 jel;30.1 jel;18.5 jel;28.6 jel;17.4 jel;17.0 jel;2.9 jel;5.4 jel;21.8 jel;25.3 jel;7.3 jel;29.9 jel;24.8 jel;19.0 jel;25.3 jel;17.0 jel;17.6 jel;14.1 jel;22.6 jel;18.6 jel;25.8 jel;18.7 jel;16.9 jel;29.7 jel;11.2 jel;15.0 jel;17.8 jel;13.0 jel;12.2 jel;13.8 jel;16.3 jel;17.0 jel;14.3 jel;17.8 jel;27.5 jel;9.6 jel;10.2 jel;37.0 jel;24.4 jel;18.6 jel;-1.7 jel;35.8 jel;18.9 jel;15.2 jel;4.2 jel;27.9 jel;17.8 jel;16.2 jel;16.0 jel;22.2 jel;20.2 jel;15.1 jel;13.9 jel;11.8 jel;24.3 jel;12.7 jel;13.6 jel;12.7 jel;27.5 jel;18.0 jel;19.1 jel;20.3 jel;21.3 jel;22.4 jel;3.7 jel;9.1 jel;10.7 jel;14.7 jel;11.8 jel;10.0 jel;8.3 jel;12.5 jel;16.1 jel;29.7 jel;32.4 jel;15.1 jel;15.3 jel;25.7 jel;27.2 jel;23.1 jel;22.5 jel;17.4 jel;25.5 jel;21.4 jel;21.9 jel;12.2 jel;17.7 jel;17.8 jel;3.6 jel;17.9 jel;23.0 jel;17.2 jel;14.9 jel;7.7 jel;20.8 jel;19.3 jel;14.3 jel;13.3 jel;22.7 jel;17.2 jel;30.3 jel;19.2 jel;8.2 jel;19.0 jel;5.2 jel;13.8 jel;28.1 jel;11.7 jel;11.4 jel;22.4 jel;17.0 jel;20.2 jel;15.9 jel;27.8 jel;23.2 jel;17.9 jel;8.1 jel;16.5 jel;11.8 jel;21.5 jel;22.1 jel;31.5 jel;14.6 jel;19.5 jel;34.6 jel;19.2 jel;17.6 jel;25.1 jel;24.3 jel;21.1 jel;7.0 jel;17.5 jel;27.8 jel;16.8 jel;23.5 jel;23.3 jel;25.9 jel;13.0 jel;12.9 jel;21.3 jel;14.3 jel;22.3 jel;9.5 jel;11.0 jel;28.0 jel;28.0 jel;23.7 jel;17.8 jel;15.9 jel;26.3 jel;20.2 jel;17.3 jel;14.9 jel;25.1 jel;26.3 jel;16.5 jel;17.1 jel;23.8 jel;18.9 jel;6.5 jel;15.1 jel;4.2 jel;18.3 jel;22.8 jel;12.8 jel;16.4 jel;20.0 jel;31.1 jel;9.3 jel;13.5 jel;20.6 jel;24.8 jel;18.0 jel;17.4 jel;14.3 jel;17.5 jel;16.9 jel;18.6 jel;19.2 jel;15.5 jel;15.0 jel;14.3 jel;20.2 jel;20.1 jel;21.9 jel;20.1 jel;21.6 jel;21.1 jel;28.5 jel;3.3 jel;14.9 jel;14.4 jel;28.0 jel;14.8 jel;5.0 jel;15.2 jel;14.6 jel;23.0 jel;20.2 jel;8.6 jel;15.4 jel;15.5 jel;16.9 jel;34.3 jel;9.5 jel;27.8 jel;20.4 jel;29.8 jel;18.1 jel;15.4 jel;12.7 jel;19.1 jel;20.6 jel;26.9 jel;9.5 jel;5.9 jel;22.9 jel;18.0 jel;22.7 jel;23.3 jel;15.4 jel;25.7 jel;30.1 jel;23.2 jel;28.8 jel;24.8 jel;19.2 jel;14.2 jel;4.8 jel;19.7 jel;16.3 jel;27.0 jel;6.7 jel;23.3 jel;28.6 jel;27.8 jel;39.6 jel;9.2 jel;25.0 jel;15.6 jel;19.0 jel;20.6 jel;23.6 jel;27.7 jel;17.9 jel;12.7 jel;15.8 jel;12.5 jel;22.2 jel;20.3 jel;13.3 jel;26.5 jel;31.5 jel;23.3 jel;21.2 jel;15.5 jel;18.2 jel;5.8 jel;35.2 jel;18.9 jel;12.2 jel;11.3 jel;9.4 jel;31.9 jel;14.0 jel;18.3 jel;20.3 jel;14.4 jel;26.9 jel;28.3 jel;16.7 jel;20.5 jel;22.4 jel;15.0 jel;12.3 jel;25.4 jel;5.6 jel;8.1 jel;11.0 jel;6.3 jel;33.4 jel;22.5 jel;18.5 jel;32.1 jel;22.0 jel;19.8 jel;19.2 jel;20.5 jel;24.4 jel;6.2 jel;10.4 jel;22.5 jel;10.1 jel;17.8 jel;24.6 jel;18.7 jel;6.8 jel;26.0 jel;23.1 jel;0.7 jel;29.8 jel;31.3 jel;22.6 jel;22.8 jel;15.8 jel;25.7 jel;16.5 jel;10.9 jel;18.6 jel;27.8 jel;27.4 jel;10.4 jel;20.8 jel;23.6 jel;8.6 jel;12.5 jel;22.7 jel;22.2 jel;27.6 jel;13.0 jel;16.1 jel;10.1 jel;19.8 jel;18.2 jel;13.9 jel;29.0 jel;25.2 jel;12.0 jel;9.3 jel;23.5 jel;18.4 jel;22.3 jel;19.9 jel;13.5 jel;5.1 jel;14.2 jel;17.7 jel;12.5 jel;23.7 jel;12.6 jel;20.0 jel;16.1 jel;22.7 jel;11.9 jel;11.3 jel;15.1 jel;28.9 jel;15.2 jel;16.5 jel;13.4 jel;25.5 jel;30.3 jel;8.6 jel;24.7 jel;18.1 jel;24.2 jel;19.9 jel;23.0 jel;18.7 jel;19.6 jel;16.7 jel;25.4 jel;14.3 jel;9.0 jel;8.3 jel;20.0 jel;4.9 jel;21.2 jel;23.7 jel;17.9 jel;38.3 jel;16.5 jel;7.3 jel;10.2 jel;15.5 jel;28.1 jel;5.7 jel;27.5 jel;20.0 jel;16.2 jel;15.6 jel;13.8 jel;8.2 jel;11.6 jel;14.4 jel;18.3 jel;34.0 jel;24.0 jel;15.1 jel;23.6 jel;12.4 jel;16.5 jel;19.6 jel;11.1 jel;12.7 jel;13.4 jel;11.9 jel;18.4 jel;19.9 jel;20.3 jel;12.3 jel;7.3 jel;23.4 jel;20.6 jel;32.0 jel;14.8 jel;14.6 jel;10.9 jel;9.6 jel;19.7 jel;9.6 jel;17.3 jel;16.5 jel;18.0 jel;16.3 jel;12.7 jel;23.0 jel;17.5 jel;13.0 jel;7.1 jel;9.0 jel;7.8 jel;19.9 jel;24.7 jel;-0.1 jel;28.2 jel;15.5 jel;23.2 jel;11.9 jel;7.9 jel;20.2 jel;19.3 jel;28.5 jel;9.8 jel;2.6 jel;11.8 jel;33.3 jel;4.5 jel;17.7 jel;12.9 jel;9.3 jel;16.0 jel;16.8 jel;17.7 jel;15.0 jel;9.5 jel;15.6 jel;15.9 jel;5.0 jel;10.7 jel;28.4 jel;11.0 jel;8.6 jel;22.4 jel;17.6 jel;12.9 jel;18.3 jel;16.1 jel;24.3 jel;20.9 jel;19.4 jel;1.8 jel;29.2 jel;16.0 jel;18.2 jel;20.5 jel;11.7 jel;15.4 jel;15.2 jel;19.7 jel;23.7 jel;15.8 jel;23.7 jel;20.4 jel;14.7 jel;22.8 jel;13.7 jel;33.1 jel;15.7 jel;16.4 jel;21.9 jel;16.4 jel;22.5 jel;19.9 jel;4.0 jel;29.2 jel;17.7 jel;22.9 jel;18.8 jel;18.5 jel;21.8 jel;13.3 jel;11.1 jel;8.4 jel;17.7 jel;21.6 jel;28.6 jel;21.7 jel;12.0 jel;14.1 jel;9.5 jel;16.1 jel;20.3 jel;19.8 jel;10.7 jel;14.4 jel;8.5 jel;35.7 jel;16.4 jel;8.7 jel;23.1 jel;7.7 jel;24.7 jel;11.9 jel;9.6 jel;7.6 jel;27.4 jel;24.2 jel;14.3 jel;18.8 jel;13.0 jel;24.0 jel;14.5 jel;24.1 jel;20.9 jel;19.9 jel;19.1 jel;20.1 jel;16.2 jel;14.8 jel;15.8 jel;10.4 jel;23.6 jel;11.9 jel;23.6 jel;15.2 jel;10.7 jel;30.1 jel;27.7 jel;23.5 jel;29.1 jel;15.0 jel;15.6 jel;15.9 jel;12.8 jel;11.6 jel;17.6 jel;23.4 jel;18.5 jel;2.7 jel;10.5 jel;10.1 jel;14.8 jel;15.9 jel;20.7 jel;8.4 jel;16.4 jel;26.6 jel;8.9 jel;24.2 jel;25.5 jel;12.6 jel;21.7 jel;14.0 jel;20.5 jel;12.0 jel;16.2 jel;17.7 jel;18.1 jel;20.6 jel;9.7 jel;7.5 jel;17.3 jel;19.1 jel;24.8 jel;21.6 jel;18.4 jel;21.8 jel;17.2 jel;8.0 jel;32.5 jel;25.0 jel;17.1 jel;18.5 jel;23.3 jel;26.1 jel;24.1 jel;28.4 jel;13.1 jel;21.0 jel;16.4 jel;8.5 jel;12.3 jel;28.5 jel;22.4 jel;21.4 jel;9.3 jel;20.9 jel;17.1 jel;19.6 jel;23.0 jel;12.1 jel;15.9 jel;28.6 jel;10.1 jel;18.0 jel;14.7 jel;21.1 jel;28.2 jel;13.4 jel;15.9 jel;12.2 jel;16.1 jel;22.0 jel;32.2 jel;19.7 jel;20.6 jel;17.1 jel;14.9 jel;15.7 jel;14.5 jel;27.8 jel;13.3 jel;10.2 jel;8.4 jel;9.1 jel;13.0 jel;23.8 jel;26.1 jel;23.1 jel;2.4 jel;7.1 jel;14.0 jel;27.6 jel;24.7 jel;20.0 jel;3.7 jel;16.0 jel;26.3 jel;19.3 jel;33.2 jel;19.7 jel;20.2 jel;39.0 jel;9.7 jel;10.3 jel;27.9 jel;8.1 jel;17.0 jel;20.3 jel;19.6 jel;19.3 jel;12.4 jel;14.7 jel;18.3 jel;11.8 jel;11.0 jel;7.7 jel;19.5 jel;33.6 jel;18.5 jel;16.8 jel;26.8 jel;20.2 jel;20.7 jel;23.8 jel;16.5 jel;28.7 jel;24.4 jel;10.9 jel;30.1 jel;11.0 jel;5.8 jel;10.5 jel;26.2 jel;18.6 jel;15.2 jel;17.7 jel;1.1 jel;15.1 jel;21.2 jel;26.0 jel;28.2 jel;21.1 jel;31.8 jel;15.6 jel;14.9 jel;24.9 jel;30.3 jel;18.6 jel;16.6 jel;24.4 jel;6.7 jel;17.2 jel;20.7 jel;14.6 jel;11.9 jel;17.6 jel;22.0 jel;13.6 jel;7.8 jel;11.9 jel;22.2 jel;23.8 jel;16.4 jel;21.6 jel;13.6 jel;26.0 jel;16.4 jel;15.8 jel;25.6 jel;6.5 jel;26.1 jel;12.9 jel;19.5 jel;21.0 jel;11.3 jel;11.8 jel;24.6 jel;10.5 jel;0.1 jel;27.9 jel;15.8 jel;15.8 jel;23.4 jel;21.1 jel;7.7 jel;7.0 jel;19.7 jel;15.9 jel;22.6 jel;15.0 jel;11.9 jel;15.2 jel;13.5 jel;4.4 jel;24.9 jel;31.3 jel;28.2 jel;16.4 jel;14.4 jel;17.8 jel;9.6 jel;22.1 jel;18.0 jel;28.2 jel;18.2 jel;10.1 jel;17.1 jel;30.4 jel;12.3 jel;14.4 jel;10.7 jel;28.4 jel;14.5 jel;30.2 jel;14.0 jel;12.9 jel;18.3 jel;14.6 jel;11.3 jel;16.6 jel;8.1 jel;18.0 jel;21.7 jel;7.9 jel;15.5 jel;11.2 jel;18.9 jel;22.0 jel;19.7 jel;16.8 jel;10.0 jel;13.2 jel;20.5 jel;25.2 jel;18.2 jel;19.9 jel;21.9 jel;18.2 jel;20.5 jel;24.0 jel;31.1 jel;20.7 jel;19.0 jel;23.0 jel;11.9 jel;16.3 jel;29.4 jel;18.8 jel;11.9 jel;21.1 jel;10.9 jel;14.4 jel;11.8 jel;12.3 jel;21.0 jel;22.9 jel;18.5 jel;22.0 jel;12.3 jel;12.5 jel;23.3 jel;26.0 jel;26.6 jel;11.7 jel;18.4 jel;15.0 jel;7.5 jel;21.5 jel;9.4 jel;21.2 jel;24.5 jel;29.3 jel;-2.4 jel;15.8 jel;10.7 jel;13.1 jel;25.5 jel;20.7 jel;16.1 jel;14.9 jel;15.2 jel;25.4 jel;15.2 jel;11.9 jel;13.9 jel;19.6 jel;17.9 jel;10.0 jel;16.2 jel;16.7 jel;16.7 jel;16.1 jel;30.7 jel;17.7 jel;5.6 jel;17.4 jel;5.1 jel;27.0 jel;15.8 jel;22.7 jel;4.4 jel;25.8 jel;22.8 jel;17.5 jel;11.0 jel;29.7 jel;21.3 jel;30.5 jel;11.6 jel;8.6 jel;15.2 jel;13.7 jel;11.8 jel;22.4 jel;18.2 jel;14.2 jel;1.0 jel;20.5 jel;19.0 jel;14.1 jel;20.8 jel;18.1 jel;11.9 jel;21.8 jel;22.7 jel;12.8 jel;35.2 jel;23.7 jel;24.2 jel;11.2 jel;14.1 jel;22.8 jel;15.3 jel;20.7 jel;26.5 jel;26.3 jel;26.0 jel;18.2 jel;25.6 jel;12.8 jel;14.4 jel;14.3 jel;18.9 jel;4.6 jel;24.3 jel;20.3 jel;8.0 jel;18.4 jel;22.1 jel;25.3 jel;17.6 jel;19.6 jel;12.1 jel;27.8 jel;25.9 jel;14.5 jel;13.3 jel;17.9 jel;21.0 jel;14.9 jel;19.0 jel;20.1 jel;9.6 jel;13.2 jel;27.8 jel;12.5 jel;20.7 jel;8.0 jel;11.9 jel;22.7 jel;22.5 jel;17.4 jel;13.3 jel;6.6 jel;8.2 jel;16.1 jel;15.5 jel;22.4 jel;13.8 jel;27.4 jel;8.1 jel;11.1 jel;31.7 jel;19.7 jel;27.9 jel;9.9 jel;11.8 jel;21.4 jel;22.7 jel;24.9 jel;21.7 jel;11.0 jel;13.5 jel;11.3 jel;15.0 jel;29.0 jel;23.8 jel;16.6 jel;13.4 jel;15.4 jel;22.2 jel;22.2 jel;9.9 jel;22.3 jel;14.7 jel;19.5 jel;18.4 jel;23.7 jel;12.5 jel;13.8 jel;14.4 jel;13.5 jel;12.0 jel;28.9 jel;16.2 jel;19.1 jel;10.4 jel;11.6 jel;18.1 jel;27.4 jel;17.1 jel;15.0 jel;27.8 jel;24.3 jel;21.2 jel;14.5 jel;9.1 jel;12.0 jel;15.5 jel;23.6 jel;16.5 jel;9.4 jel;24.9 jel;12.5 jel;8.3 jel;24.0 jel;8.3 jel;15.9 jel;13.8 jel;19.1 jel;34.2 jel;18.6 jel;15.4 jel;16.2 jel;21.4 jel;23.4 jel;23.7 jel;30.1 jel;15.0 jel;24.1 jel;22.1 jel;29.6 jel;9.1 jel;15.5 jel;1.2 jel;10.3 jel;25.4 jel;20.4 jel;25.7 jel;29.0 jel;27.6 jel;14.3 jel;24.9 jel;20.6 jel;21.9 jel;24.6 jel;9.4 jel;13.2 jel;23.3 jel;21.8 jel;19.0 jel;18.5 jel;23.9 jel;19.5 jel;20.8 jel;19.6 jel;27.6 jel;15.5 jel;15.4 jel;14.2 jel;9.7 jel;15.9 jel;30.8 jel;11.4 jel;20.3 jel;7.0 jel;8.8 jel;23.7 jel;30.3 jel;14.5 jel;17.7 jel;15.7 jel;17.5 jel;12.1 jel;23.6 jel;16.9 jel;27.7 jel;0.5 jel;13.6 jel;10.4 jel;17.2 jel;17.1 jel;32.1 jel;10.7 jel;20.6 jel;12.1 jel;13.7 jel;21.5 jel;15.0 jel;18.4 jel;25.8 jel;19.6 jel;19.3 jel;25.3 jel;18.1 jel;20.0 jel;20.2 jel;12.4 jel;22.2 jel;33.3 jel;13.0 jel;17.2 jel;31.8 jel;8.6 jel;20.1 jel;24.5 jel;10.9 jel;17.8 jel;15.4 jel;8.1 jel;2.4 jel;14.2 jel;22.0 jel;14.2 jel;19.9 jel;18.6 jel;18.7 jel;21.6 jel;25.8 jel;20.2 jel;20.4 jel;20.5 jel;23.0 jel;19.3 jel;25.6 jel;9.0 jel;14.0 jel;23.2 jel;12.6 jel;12.3 jel;15.5 jel;23.6 jel;0.8 jel;11.3 jel;24.0 jel;24.9 jel;12.5 jel;17.1 jel;29.9 jel;22.9 jel;18.5 jel;13.8 jel;10.4 jel;20.4 jel;25.4 jel;19.7 jel;17.0 jel;25.3 jel;21.6 jel;19.2 jel;16.4 jel;19.2 jel;11.2 jel;17.2 jel;14.9 jel;21.1 jel;21.1 jel;17.4 jel;16.0 jel;30.5 jel;15.5 jel;18.2 jel;12.5 jel;20.9 jel;19.1 jel;19.2 jel;19.8 jel;27.2 jel;18.7 jel;19.7 jel;18.5 jel;18.7 jel;27.7 jel;27.0 jel;17.1 jel;20.8 jel;12.0 jel;10.9 jel;20.8 jel;23.0 jel;20.5 jel;26.8 jel;13.2 jel;15.2 jel;24.0 jel;28.5 jel;15.1 jel;25.9 jel;16.9 jel;21.1 jel;21.2 jel;13.7 jel;12.9 jel;28.9 jel;29.6 jel;19.0 jel;25.1 jel;15.1 jel;15.0 jel;25.0 jel;19.2 jel;27.2 jel;17.1 jel;28.3 jel;22.3 jel;27.2 jel;17.8 jel;28.0 jel;17.2 jel;13.8 jel;20.8 jel;10.7 jel;25.9 jel;20.5 jel;17.1 jel;14.9 jel;12.7 jel;7.9 jel;10.4 jel;27.9 jel;19.3 jel;19.5 jel;20.9 jel;15.5 jel;24.4 jel;17.3 jel;8.2 jel;18.8 jel;37.6 jel;22.3 jel;12.6 jel;16.3 jel;14.8 jel;23.6 jel;16.8 jel;11.6 jel;1.3 jel;8.6 jel;28.2 jel;11.4 jel;13.9 jel;21.6 jel;18.1 jel;24.3 jel;22.5 jel;5.9 jel;22.0 jel;15.7 jel;27.1 jel;17.7 jel;26.0 jel;5.5 jel;11.5 jel;16.4 jel;22.0 jel;13.4 jel;21.9 jel;24.7 jel;11.5 jel;9.0 jel;21.4 jel;16.6 jel;5.4 jel;15.4 jel;17.5 jel;21.2 jel;19.4 jel;12.9 jel;19.6 jel;21.6 jel;15.4 jel;11.0 jel;18.8 jel;8.0 jel;18.3 jel;17.1 jel;17.1 jel;15.1 jel;13.2 jel;21.8 jel;25.6 jel;21.0 jel;18.8 jel;28.9 jel;12.0 jel;19.8 jel;16.8 jel;20.2 jel;14.3 jel;18.0 jel;28.8 jel;11.8 jel;12.8 jel;19.8 jel;11.5 jel;30.8 jel;7.7 jel;17.3 jel;25.4 jel;15.0 jel;29.4 jel;6.8 jel;21.5 jel;4.3 jel;19.9 jel;22.5 jel;31.8 jel;12.8 jel;6.2 jel;9.9 jel;11.8 jel;13.8 jel;26.1 jel;15.3 jel;5.4 jel;6.4 jel;12.6 jel;13.5 jel;31.0 jel;25.4 jel;24.2 jel;11.5 jel;6.2 jel;16.5 jel;28.4 jel;23.5 jel;23.0 jel;17.4 jel;18.6 jel;16.1 jel;13.2 jel;18.1 jel;17.6 jel;36.6 jel;17.0 jel;21.8 jel;29.1 jel;21.1 jel;18.0 jel;20.4 jel;17.1 jel;5.5 jel;21.7 jel;12.8 jel;15.5 jel;20.3 jel;20.4 jel;22.1 jel;25.0 jel;25.0 jel;13.8 jel;17.6 jel;25.3 jel;27.0 jel;18.9 jel;14.5 jel;10.3 jel;1.9 jel;20.1 jel;2.3 jel;20.4 jel;30.2 jel;20.1 jel;32.0 jel;10.8 jel;21.3 jel;22.5 jel;25.8 jel;12.4 jel;10.3 jel;13.3 jel;9.8 jel;27.2 jel;22.4 jel;16.2 jel;12.1 jel;16.9 jel;31.3 jel;14.4 jel;23.9 jel;23.3 jel;32.1 jel;7.9 jel;26.9 jel;14.5 jel;8.2 jel;14.7 jel;23.3 jel;15.2 jel;28.0 jel;21.6 jel;26.6 jel;20.0 jel;17.1 jel;2.0 jel;9.7 jel;12.3 jel;17.8 jel;16.6 jel;22.2 jel;16.1 jel;16.1 jel;33.5 jel;18.6 jel;10.5 jel;17.9 jel;27.0 jel;8.3 jel;32.1 jel;27.4 jel;13.9 jel;25.2 jel;23.5 jel;25.1 jel;9.0 jel;0.9 jel;25.4 jel;17.6 jel;0.2 jel;31.8 jel;16.0 jel;11.8 jel;7.1 jel;16.3 jel;18.3 jel;19.4 jel;2.4 jel;15.3 jel;13.5 jel;24.5 jel;12.6 jel;23.9 jel;6.4 jel;18.2 jel;24.5 jel;11.8 jel;16.5 jel;24.4 jel;20.8 jel;25.2 jel;14.7 jel;20.8 jel;7.9 jel;18.8 jel;3.8 jel;27.0 jel;20.2 jel;13.8 jel;8.0 jel;19.1 jel;24.2 jel;29.6 jel;24.4 jel;20.5 jel;22.1 jel;26.2 jel;9.0 jel;31.9 jel;18.4 jel;10.9 jel;18.0 jel;14.7 jel;27.1 jel;19.9 jel;15.3 jel;8.0 jel;23.5 jel;22.6 jel;18.4 jel;14.9 jel;29.2 jel;23.8 jel;19.3 jel;26.0 jel;16.5 jel;26.0 jel;15.3 jel;16.5 jel;16.9 jel;5.1 jel;2.8 jel;22.5 jel;26.8 jel;16.0 jel;13.9 jel;15.8 jel;17.5 jel;9.1 jel;26.7 jel;21.1 jel;17.1 jel;26.5 jel;27.1 jel;13.8 jel;23.3 jel;27.9 jel;17.7 jel;16.0 jel;26.9 jel;22.7 jel;18.0 jel;27.7 jel;13.7 jel;30.5 jel;22.5 jel;15.7 jel;20.0 jel;29.5 jel;16.3 jel;27.1 jel;25.9 jel;16.8 jel;14.0 jel;27.3 jel;22.0 jel;18.0 jel;27.2 jel;21.5 jel;29.4 jel;15.3 jel;13.9 jel;17.5 jel;9.5 jel;17.9 jel;14.4 jel;12.0 jel;8.3 jel;20.0 jel;14.4 jel;16.2 jel;-2.3 jel;8.1 jel;21.5 jel;5.4 jel;15.8 jel;32.8 jel;1.2 jel;10.5 jel;17.2 jel;22.7 jel;21.8 jel;26.3 jel;14.5 jel;21.0 jel;36.1 jel;20.1 jel;19.4 jel;27.9 jel;26.1 jel;14.6 jel;21.1 jel;6.7 jel;12.2 jel;21.6 jel;14.7 jel;17.4 jel;14.2 jel;19.0 jel;18.4 jel;14.8 jel;22.2 jel;10.4 jel;28.9 jel;21.4 jel;28.9 jel;9.8 jel;20.7 jel;22.5 jel;32.7 jel;21.8 jel;27.6 jel;20.1 jel;26.7 jel;11.2 jel;20.0 jel;15.8 jel;20.2 jel;24.1 jel;23.5 jel;24.0 jel;40.0 jel;27.9 jel;19.1 jel;16.1 jel;10.3 jel;7.6 jel;16.0 jel;11.1 jel;20.0 jel;4.8 jel;10.3 jel;16.8 jel;10.1 jel;18.1 jel;10.1 jel;13.7 jel;25.7 jel;17.5 jel;24.1 jel;15.6 jel;17.2 jel;13.3 jel;21.4 jel;20.4 jel;9.9 jel;11.5 jel;20.8 jel;11.9 jel;22.0 jel;20.0 jel;1.8 jel;23.2 jel;23.3 jel;13.8 jel;16.1 jel;38.0 jel;6.6 jel;36.8 jel;0.8 jel;16.6 jel;22.6 jel;11.1 jel;31.6 jel;14.7 jel;32.5 jel;13.3 jel;9.2 jel;19.5 jel;29.0 jel;10.7 jel;11.7 jel;9.8 jel;13.7 jel;23.4 jel;18.5 jel;9.0 jel;18.3 jel;19.3 jel;21.3 jel;24.8 jel;37.4 jel;19.4 jel;22.9 jel;20.5 jel;24.0 jel;12.7 jel;18.8 jel;26.1 jel;18.5 jel;20.5 jel;17.5 jel;17.2 jel;9.4 jel;19.0 jel;29.8 jel;10.9 jel;25.0 jel;31.8 jel;24.0 jel;11.1 jel;22.6 jel;20.4 jel;5.0 jel;12.7 jel;24.1 jel;10.1 jel;16.2 jel;11.1 jel;17.9 jel;14.1 jel;7.0 jel;18.0 jel;19.9 jel;14.7 jel;17.3 jel;32.1 jel;18.8 jel;6.2 jel;22.1 jel;7.4 jel;19.6 jel;14.4 jel;10.4 jel;29.4 jel;17.7 jel;22.9 jel;9.7 jel;17.5 jel;14.8 jel;17.7 jel;16.0 jel;15.1 jel;22.5 jel;40.9 jel;34.7 jel;9.1 jel;7.9 jel;23.3 jel;10.1 jel;12.6 jel;22.1 jel;22.7 jel;13.9 jel;8.3 jel;17.9 jel;2.5 jel;19.0 jel;21.5 jel;16.9 jel;13.6 jel;20.7 jel;13.9 jel;6.8 jel;18.7 jel;13.3 jel;9.7 jel;21.1 jel;24.5 jel;24.5 jel;21.2 jel;15.2 jel;13.0 jel;24.3 jel;26.2 jel;30.1 jel;14.7 jel;25.3 jel;10.8 jel;11.3 jel;19.6 jel;24.2 jel;18.4 jel;11.1 jel;32.9 jel;15.1 jel;21.3 jel;33.5 jel;34.1 jel;10.6 jel;24.8 jel;22.4 jel;23.5 jel;22.2 jel;13.5 jel;18.4 jel;18.0 jel;29.0 jel;14.3 jel;19.5 jel;9.8 jel;20.7 jel;32.1 jel;8.5 jel;11.5 jel;24.4 jel;27.8 jel;18.6 jel;11.0 jel;13.8 jel;10.8 jel;25.0 jel;13.7 jel;19.8 jel;15.8 jel;16.1 jel;20.0 jel;13.2 jel;17.7 jel;11.1 jel;7.1 jel;31.9 jel;20.4 jel;19.3 jel;17.0 jel;20.7 jel;11.9 jel;13.9 jel;23.7 jel;26.4 jel;12.1 jel;25.7 jel;13.1 jel;22.3 jel;3.3 jel;25.7 jel;10.4 jel;27.1 jel;20.9 jel;22.9 jel;17.2 jel;27.2 jel;10.4 jel;21.0 jel;13.0 jel;24.2 jel;17.7 jel;18.4 jel;14.0 jel;28.0 jel;13.0 jel;23.4 jel;7.4 jel;27.2 jel;24.5 jel;14.3 jel;12.4 jel;10.6 jel;8.1 jel;16.9 jel;33.4 jel;20.5 jel;19.9 jel;11.3 jel;3.9 jel;-1.1 jel;9.6 jel;14.1 jel;18.0 jel;15.2 jel;27.4 jel;7.0 jel;19.1 jel;18.0 jel;17.2 jel;19.3 jel;19.8 jel;17.1 jel;26.2 jel;15.0 jel;37.3 jel;18.7 jel;2.4 jel;28.1 jel;15.7 jel;26.5 jel;21.2 jel;20.5 jel;10.1 jel;16.5 jel;22.3 jel;20.7 jel;20.7 jel;30.0 jel;31.9 jel;18.4 jel;13.5 jel;9.9 jel;11.3 jel;13.1 jel;19.0 jel;3.5 jel;22.6 jel;18.3 jel;14.8 jel;0.9 jel;15.1 jel;15.7 jel;11.2 jel;23.4 jel;18.0 jel;12.1 jel;12.3 jel;12.6 jel;30.3 jel;8.2 jel;20.7 jel;21.6 jel;15.1 jel;24.5 jel;20.5 jel;14.3 jel;21.7 jel;24.8 jel;26.5 jel;23.5 jel;13.6 jel;8.2 jel;15.4 jel;20.2 jel;25.7 jel;8.9 jel;17.9 jel;17.4 jel;13.4 jel;13.3 jel;4.5 jel;29.4 jel;22.8 jel;21.5 jel;2.8 jel;25.1 jel;10.2 jel;20.1 jel;16.7 jel;20.7 jel;22.2 jel;19.5 jel;26.7 jel;24.6 jel;34.4 jel;19.5 jel;27.7 jel;28.6 jel;7.4 jel;5.1 jel;16.4 jel;23.9 jel;17.8 jel;17.2 jel;16.4 jel;27.1 jel;12.5 jel;15.9 jel;20.2 jel;12.8 jel;13.2 jel;26.8 jel;5.9 jel;16.6 jel;17.3 jel;25.3 jel;1.8 jel;7.8 jel;19.7 jel;11.5 jel;29.9 jel;19.5 jel;19.8 jel;11.5 jel;4.9 jel;23.4 jel;16.6 jel;6.0 jel;24.5 jel;17.0 jel;22.1 jel;29.3 jel;16.7 jel;21.3 jel;6.2 jel;27.2 jel;10.6 jel;7.6 jel;19.5 jel;7.2 jel;12.1 jel;24.1 jel;22.8 jel;15.4 jel;13.3 jel;21.4 jel;11.8 jel;18.7 jel;19.1 jel;15.3 jel;14.2 jel;2.4 jel;17.3 jel;31.8 jel;13.0 jel;29.6 jel;15.9 jel;17.0 jel;17.5 jel;38.7 jel;14.6 jel;13.3 jel;13.0 jel;11.5 jel;21.6 jel;5.9 jel;19.9 jel;12.9 jel;13.8 jel;9.3 jel;24.6 jel;22.7 jel;32.6 jel;13.8 jel;21.0 jel;14.4 jel;22.3 jel;11.9 jel;28.7 jel;26.5 jel;16.3 jel;10.9 jel;25.5 jel;19.2 jel;14.9 jel;11.1 jel;8.2 jel;19.1 jel;17.4 jel;17.7 jel;24.1 jel;27.0 jel;4.2 jel;19.3 jel;20.8 jel;15.2 jel;30.7 jel;7.7 jel;17.1 jel;14.0 jel;18.1 jel;20.5 jel;13.2 jel;25.9 jel;15.5 jel;30.0 jel;14.2 jel;7.6 jel;28.5 jel;16.9 jel;10.5 jel;24.3 jel;13.6 jel;20.2 jel;22.9 jel;13.8 jel;15.2 jel;23.2 jel;27.0 jel;18.6 jel;24.5 jel;26.3 jel;17.4 jel;10.0 jel;19.1 jel;12.4 jel;21.0 jel;18.1 jel;18.5 jel;32.3 jel;14.6 jel;27.4 jel;23.3 jel;17.9 jel;18.7 jel;18.1 jel;15.4 jel;26.9 jel;21.1 jel;22.5 jel;19.6 jel;17.2 jel;15.5 jel;22.6 jel;29.3 jel;27.9 jel;21.5 jel;31.9 jel;18.0 jel;10.7 jel;5.1 jel;19.0 jel;23.8 jel;14.5 jel;15.0 jel;17.6 jel;7.4 jel;18.8 jel;21.6 jel;6.7 jel;19.6 jel;18.5 jel;26.5 jel;11.6 jel;28.8 jel;20.1 jel;21.4 jel;19.2 jel;6.6 jel;9.6 jel;14.3 jel;23.8 jel;26.7 jel;32.5 jel;17.8 jel;27.0 jel;21.5 jel;20.8 jel;1.8 jel;15.9 jel;14.0 jel;6.9 jel;17.2 jel;14.5 jel;12.5 jel;8.9 jel;21.6 jel;20.1 jel;10.9 jel;7.9 jel;6.7 jel;16.2 jel;28.6 jel;11.4 jel;14.7 jel;23.4 jel;15.1 jel;-1.4 jel;16.2 jel;9.5 jel;21.1 jel;24.1 jel;14.6 jel;27.9 jel;17.9 jel;26.9 jel;10.8 jel;10.5 jel;11.5 jel;34.0 jel;33.4 jel;20.9 jel;15.4 jel;3.7 jel;22.6 jel;18.4 jel;13.1 jel;6.7 jel;26.5 jel;19.6 jel;21.5 jel;20.7 jel;20.9 jel;27.0 jel;12.1 jel;9.0 jel;20.7 jel;17.5 jel;18.1 jel;8.9 jel;3.0 jel;15.6 jel;13.4 jel;9.9 jel;23.9 jel;8.9 jel;17.3 jel;26.7 jel;24.6 jel;20.4 jel;9.8 jel;17.3 jel;12.8 jel;17.0 jel;16.8 jel;13.1 jel;11.5 jel;20.1 jel;10.3 jel;22.1 jel;2.3 jel;24.1 jel;5.9 jel;13.9 jel;16.6 jel;21.1 jel;7.8 jel;15.3 jel;17.2 jel;27.1 jel;29.8 jel;17.1 jel;19.4 jel;24.9 jel;22.2 jel;15.8 jel;13.2 jel;5.2 jel;13.2 jel;19.1 jel;19.5 jel;17.3 jel;20.0 jel;17.9 jel;15.4 jel;12.5 jel;18.2 jel;21.6 jel;15.5 jel;18.3 jel;11.1 jel;29.1 jel;21.0 jel;13.5 jel;17.7 jel;8.0 jel;20.6 jel;26.0 jel;5.0 jel;19.4 jel;28.4 jel;12.6 jel;18.9 jel;15.4 jel;7.7 jel;15.0 jel;8.4 jel;22.2 jel;5.3 jel;20.6 jel;34.6 jel;14.9 jel;6.5 jel;9.3 jel;15.1 jel;23.3 jel;10.1 jel;22.8 jel;12.8 jel;31.1 jel;26.8 jel;14.0 jel;13.7 jel;13.4 jel;14.9 jel;21.2 jel;19.1 jel;22.1 jel;25.4 jel;25.4 jel;25.9 jel;27.1 jel;16.8 jel;7.3 jel;18.8 jel;13.1 jel;5.1 jel;6.2 jel;19.1 jel;10.4 jel;22.2 jel;19.0 jel;16.7 jel;27.4 jel;17.8 jel;28.6 jel;23.7 jel;21.0 jel;21.3 jel;24.6 jel;22.3 jel;19.6 jel;16.2 jel;33.2 jel;14.6 jel;23.5 jel;16.1 jel;32.4 jel;20.4 jel;9.2 jel;20.4 jel;12.5 jel;21.8 jel;15.9 jel;20.6 jel;20.8 jel;18.1 jel;16.0 jel;13.5 jel;15.5 jel;23.1 jel;9.6 jel;40.7 jel;13.2 jel;22.7 jel;27.7 jel;23.5 jel;17.9 jel;12.4 jel;21.9 jel;22.3 jel;12.0 jel;16.3 jel;20.5 jel;28.2 jel;18.4 jel;12.3 jel;14.9 jel;14.7 jel;17.6 jel;14.4 jel;4.7 jel;19.4 jel;13.8 jel;24.8 jel;12.0 jel;19.7 jel;21.1 jel;28.3 jel;22.6 jel;8.3 jel;19.2 jel;22.8 jel;9.6 jel;11.6 jel;27.2 jel;16.6 jel;8.6 jel;19.0 jel;18.4 jel;5.3 jel;12.9 jel;10.1 jel;25.2 jel;27.3 jel;13.4 jel;18.6 jel;7.6 jel;15.5 jel;8.5 jel;18.1 jel;24.9 jel;15.8 jel;33.0 jel;11.5 jel;13.3 jel;22.3 jel;16.2 jel;18.8 jel;13.6 jel;20.1 jel;7.6 jel;22.4 jel;15.2 jel;13.7 jel;22.9 jel;18.7 jel;19.0 jel;26.4 jel;21.1 jel;19.5 jel;14.0 jel;22.3 jel;12.3 jel;22.2 jel;7.3 jel;23.1 jel;23.2 jel;30.4 jel;16.7 jel;17.7 jel;15.6 jel;23.5 jel;29.8 jel;16.6 jel;15.1 jel;25.9 jel;22.5 jel;16.3 jel;21.0 jel;21.7 jel;21.3 jel;14.2 jel;14.8 jel;20.1 jel;19.8 jel;12.8 jel;6.7 jel;20.4 jel;31.5 jel;14.1 jel;14.4 jel;11.7 jel;-1.8 jel;7.3 jel;22.0 jel;17.5 jel;19.7 jel;12.9 jel;31.2 jel;25.5 jel;14.1 jel;12.6 jel;25.2 jel;16.0 jel;6.2 jel;12.2 jel;16.6 jel;6.9 jel;3.3 jel;9.6 jel;25.5 jel;25.7 jel;16.5 jel;27.0 jel;20.0 jel;6.1 jel;25.4 jel;16.5 jel;22.9 jel;24.7 jel;2.0 jel;10.2 jel;21.1 jel;25.6 jel;24.2 jel;21.5 jel;14.1 jel;16.5 jel;24.1 jel;25.1 jel;17.3 jel;20.5 jel;28.1 jel;29.3 jel;16.9 jel;29.3 jel;24.8 jel;18.6 jel;15.0 jel;6.5 jel;15.7 jel;7.7 jel;13.9 jel;22.6 jel;31.9 jel;17.1 jel;28.9 jel;12.4 jel;4.5 jel;21.0 jel;21.5 jel;23.7 jel;10.5 jel;9.5 jel;21.8 jel;15.7 jel;10.5 jel;17.4 jel;14.0 jel;17.0 jel;8.2 jel;12.2 jel;17.6 jel;29.0 jel;8.1 jel;8.6 jel;13.4 jel;21.5 jel;10.6 jel;10.1 jel;34.4 jel;23.1 jel;26.0 jel;16.3 jel;22.1 jel;8.2 jel;22.3 jel;14.3 jel;18.1 jel;7.0 jel;-0.5 jel;7.5 jel;19.6 jel;7.3 jel;19.6 jel;28.4 jel;29.2 jel;17.4 jel;17.4 jel;9.6 jel;23.4 jel;15.2 jel;20.7 jel;18.0 jel;21.0 jel;14.8 jel;3.4 jel;22.3 jel;16.7 jel;25.6 jel;4.3 jel;11.8 jel;4.2 jel;11.6 jel;23.2 jel;4.8 jel;20.6 jel;15.2 jel;15.7 jel;29.5 jel;12.9 jel;20.8 jel;7.2 jel;11.9 jel;16.4 jel;19.0 jel;29.0 jel;19.5 jel;27.5 jel;-0.9 jel;19.2 jel;25.8 jel;21.1 jel;18.3 jel;12.7 jel;13.2 jel;23.2 jel;21.9 jel;19.7 jel;31.3 jel;14.9 jel;27.3 jel;15.3 jel;13.5 jel;19.9 jel;15.2 jel;27.7 jel;23.4 jel;14.3 jel;12.6 jel;24.0 jel;23.1 jel;25.6 jel;16.0 jel;22.3 jel;8.5 jel;24.2 jel;15.0 jel;26.5 jel;11.8 jel;16.2 jel;18.9 jel;18.7 jel;13.7 jel;12.1 jel;24.5 jel;25.0 jel;20.2 jel;15.2 jel;17.8 jel;18.2 jel;16.7 jel;12.9 jel;19.9 jel;28.2 jel;13.6 jel;26.1 jel;15.6 jel;21.4 jel;20.9 jel;18.6 jel;12.4 jel;14.8 jel;9.9 jel;22.8 jel;11.1 jel;12.4 jel;17.4 jel;11.0 jel;24.6 jel;26.3 jel;22.3 jel;20.7 jel;21.8 jel;28.5 jel;16.7 jel;4.5 jel;7.8 jel;15.1 jel;28.8 jel;25.8 jel;15.8 jel;32.8 jel;25.9 jel;5.6 jel;9.3 jel;11.7 jel;4.7 jel;20.4 jel;24.0 jel;16.8 jel;5.0 jel;19.0 jel;33.9 jel;28.0 jel;21.5 jel;8.8 jel;15.8 jel;21.9 jel;10.9 jel;24.9 jel;11.7 jel;29.9 jel;22.7 jel;20.6 jel;27.5 jel;18.4 jel;15.7 jel;16.9 jel;14.8 jel;-0.6 jel;18.4 jel;27.4 jel;6.2 jel;27.6 jel;17.1 jel;11.3 jel;16.7 jel;21.3 jel;8.2 jel;17.7 jel;19.8 jel;21.1 jel;16.9 jel;18.3 jel;12.0 jel;16.1 jel;28.9 jel;11.3 jel;25.4 jel;9.5 jel;28.7 jel;27.1 jel;20.5 jel;5.8 jel;13.1 jel;9.2 jel;29.7 jel;22.9 jel;10.1 jel;9.5 jel;16.6 jel;14.1 jel;16.5 jel;12.9 jel;22.6 jel;7.6 jel;17.6 jel;28.5 jel;17.3 jel;23.2 jel;17.3 jel;7.2 jel;27.0 jel;8.3 jel;25.4 jel;24.5 jel;23.3 jel;15.3 jel;17.5 jel;21.8 jel;21.7 jel;19.0 jel;17.2 jel;29.5 jel;16.3 jel;20.8 jel;9.9 jel;14.7 jel;17.3 jel;15.8 jel;18.0 jel;27.9 jel;25.5 jel;22.5 jel;10.2 jel;26.5 jel;18.1 jel;6.8 jel;30.4 jel;17.7 jel;15.3 jel;22.4 jel;18.3 jel;12.8 jel;3.0 jel;12.5 jel;12.9 jel;8.2 jel;12.5 jel;19.8 jel;22.4 jel;14.4 jel;17.4 jel;15.2 jel;25.8 jel;12.4 jel;19.0 jel;10.6 jel;26.2 jel;27.4 jel;5.0 jel;15.9 jel;14.3 jel;27.8 jel;8.5 jel;17.2 jel;30.5 jel;21.6 jel;17.6 jel;12.2 jel;27.1 jel;23.6 jel;16.7 jel;13.7 jel;15.1 jel;18.1 jel;16.8 jel;28.0 jel;18.7 jel;17.1 jel;14.9 jel;28.1 jel;6.2 jel;18.8 jel;22.2 jel;20.3 jel;31.7 jel;13.6 jel;20.4 jel;15.5 jel;28.0 jel;6.5 jel;2.2 jel;8.8 jel;15.0 jel;12.4 jel;25.4 jel;22.9 jel;15.1 jel;25.9 jel;16.1 jel;11.7 jel;1.5 jel;20.3 jel;21.5 jel;15.1 jel;21.2 jel;21.1 jel;18.0 jel;17.6 jel;22.0 jel;27.1 jel;12.5 jel;15.8 jel;19.9 jel;28.1 jel;16.3 jel;9.5 jel;22.2 jel;21.2 jel;17.1 jel;12.6 jel;15.6 jel;9.3 jel;20.7 jel;13.3 jel;4.3 jel;34.1 jel;27.3 jel;26.9 jel;7.3 jel;15.6 jel;13.2 jel;20.4 jel;4.6 jel;9.7 jel;26.7 jel;19.9 jel;12.1 jel;16.6 jel;19.0 jel;25.5 jel;29.7 jel;14.8 jel;22.6 jel;15.2 jel;17.9 jel;8.9 jel;27.2 jel;15.9 jel;19.6 jel;18.7 jel;29.7 jel;24.0 jel;24.4 jel;18.1 jel;12.4 jel;27.7 jel;21.2 jel;15.9 jel;17.4 jel;16.1 jel;13.9 jel;34.2 jel;20.0 jel;19.5 jel;25.8 jel;16.4 jel;15.5 jel;26.6 jel;21.9 jel;25.5 jel;15.5 jel;5.2 jel;17.3 jel;30.4 jel;15.4 jel;37.9 jel;10.3 jel;21.4 jel;27.5 jel;14.5 jel;21.6 jel;15.9 jel;20.5 jel;0.3 jel;14.8 jel;16.0 jel;18.2 jel;10.5 jel;25.0 jel;28.3 jel;24.5 jel;19.2 jel;9.2 jel;29.3 jel;19.6 jel;14.3 jel;25.6 jel;17.4 jel;25.6 jel;24.8 jel;21.2 jel;19.2 jel;16.2 jel;10.0 jel;7.6 jel;11.9 jel;19.4 jel;17.7 jel;13.8 jel;19.7 jel;13.9 jel;24.1 jel;17.8 jel;26.1 jel;4.7 jel;18.7 jel;18.1 jel;8.2 jel;18.1 jel;27.1 jel;12.2 jel;22.2 jel;30.1 jel;27.3 jel;24.6 jel;20.8 jel;4.5 jel;-0.5 jel;23.9 jel;17.2 jel;17.9 jel;24.1 jel;28.7 jel;20.9 jel;13.9 jel;18.7 jel;19.5 jel;12.0 jel;13.3 jel;10.5 jel;27.8 jel;20.8 jel;17.2 jel;6.6 jel;18.4 jel;8.5 jel;9.6 jel;5.3 jel;21.7 jel;27.0 jel;25.8 jel;24.4 jel;8.9 jel;17.1 jel;24.9 jel;21.5 jel;20.7 jel;21.7 jel;16.9 jel;19.2 jel;8.4 jel;21.1 jel;13.9 jel;23.8 jel;16.7 jel;12.5 jel;14.8 jel;23.1 jel;21.1 jel;15.8 jel;28.7 jel;11.9 jel;27.3 jel;16.7 jel;18.8 jel;0.4 jel;25.2 jel;11.2 jel;19.9 jel;35.0 jel;20.3 jel;10.9 jel;23.5 jel;13.7 jel;17.4 jel;25.2 jel;30.8 jel;29.5 jel;17.9 jel;20.4 jel;19.6 jel;16.7 jel;10.8 jel;25.8 jel;18.4 jel;15.1 jel;7.3 jel;16.5 jel;20.9 jel;26.5 jel;30.8 jel;11.2 jel;15.8 jel;21.1 jel;23.1 jel;26.5 jel;13.4 jel;19.4 jel;14.6 jel;18.0 jel;13.1 jel;2.5 jel;18.0 jel;18.7 jel;18.1 jel;28.3 jel;19.8 jel;27.2 jel;17.4 jel;25.7 jel;18.3 jel;16.9 jel;15.8 jel;32.4 jel;22.1 jel;25.5 jel;22.1 jel;22.4 jel;24.3 jel;21.5 jel;13.0 jel;23.1 jel;25.5 jel;12.9 jel;18.3 jel;17.4 jel;26.0 jel;15.8 jel;22.4 jel;29.2 jel;9.3 jel;26.0 jel;45.4 jel;15.9 jel;11.7 jel;17.9 jel;17.2 jel;18.9 jel;12.6 jel;28.1 jel;25.8 jel;19.6 jel;15.6 jel;14.2 jel;16.0 jel;17.8 jel;17.0 jel;20.6 jel;21.0 jel;15.3 jel;16.3 jel;7.9 jel;30.2 jel;21.5 jel;29.0 jel;23.2 jel;4.9 jel;15.7 jel;41.9 jel;6.6 jel;8.9 jel;14.7 jel;23.6 jel;21.2 jel;8.7 jel;27.6 jel;22.8 jel;14.7 jel;10.1 jel;10.8 jel;23.4 jel;22.1 jel;18.5 jel;14.2 jel;10.9 jel;15.5 jel;16.8 jel;13.6 jel;21.8 jel;10.0 jel;15.0 jel;25.2 jel;9.6 jel;12.6 jel;22.8 jel;21.6 jel;23.5 jel;17.8 jel;6.3 jel;3.9 jel;8.2 jel;18.4 jel;23.3 jel;20.5 jel;26.9 jel;12.7 jel;21.5 jel;24.6 jel;24.7 jel;21.8 jel;28.5 jel;15.5 jel;17.8 jel;24.0 jel;21.1 jel;10.9 jel;9.3 jel;27.6 jel;28.9 jel;15.7 jel;23.2 jel;11.1 jel;15.7 jel;37.8 jel;27.1 jel;32.4 jel;22.5 jel;11.5 jel;24.8 jel;22.3 jel;24.0 jel;11.4 jel;23.0 jel;-0.9 jel;18.8 jel;22.9 jel;16.5 jel;30.2 jel;15.1 jel;6.5 jel;24.0 jel;13.9 jel;32.6 jel;21.6 jel;21.9 jel;20.0 jel;4.4 jel;31.6 jel;14.7 jel;24.5 jel;15.1 jel;14.9 jel;20.1 jel;18.4 jel;9.8 jel;17.9 jel;23.3 jel;15.9 jel;19.5 jel;19.5 jel;20.9 jel;28.0 jel;19.5 jel;19.8 jel;14.8 jel;17.3 jel;25.0 jel;20.2 jel;14.8 jel;14.7 jel;30.6 jel;10.3 jel;20.5 jel;17.3 jel;5.7 jel;16.8 jel;21.8 jel;31.0 jel;13.8 jel;31.8 jel;20.9 jel;17.8 jel;15.8 jel;32.8 jel;31.7 jel;13.0 jel;19.9 jel;25.7 jel;15.9 jel;26.2 jel;13.9 jel;28.1 jel;15.5 jel;10.8 jel;14.7 jel;21.2 jel;11.2 jel;22.5 jel;17.6 jel;15.1 jel;13.7 jel;30.9 jel;17.2 jel;22.0 jel;9.0 jel;11.4 jel;10.2 jel;17.8 jel;19.5 jel;15.6 jel;18.1 jel;27.9 jel;22.9 jel;25.3 jel;22.6 jel;7.0 jel;22.4 jel;21.9 jel;25.3 jel;18.3 jel;15.5 jel;20.0 jel;22.4 jel;22.0 jel;8.5 jel;14.8 jel;6.9 jel;34.5 jel;23.7 jel;23.4 jel;13.3 jel;9.6 jel;16.0 jel;10.2 jel;21.6 jel;22.6 jel;12.0 jel;16.2 jel;26.7 jel;27.4 jel;-1.0 jel;14.0 jel;10.2 jel;14.7 jel;17.3 jel;13.2 jel;11.0 jel;20.9 jel;13.8 jel;14.4 jel;25.3 jel;18.2 jel;8.4 jel;8.7 jel;12.4 jel;31.2 jel;30.8 jel;22.7 jel;17.1 jel;25.5 jel;11.7 jel;34.4 jel;22.3 jel;5.5 jel;13.7 jel;13.9 jel;12.2 jel;18.6 jel;25.5 jel;33.1 jel;19.3 jel;15.2 jel;14.1 jel;17.1 jel;15.0 jel;5.4 jel;15.8 jel;23.6 jel;13.6 jel;24.1 jel;29.3 jel;16.5 jel;28.3 jel;4.6 jel;23.9 jel;19.7 jel;22.4 jel;15.2 jel;7.2 jel;23.1 jel;11.8 jel;27.6 jel;20.4 jel;23.3 jel;17.3 jel;25.9 jel;21.6 jel;9.3 jel;18.9 jel;18.2 jel;35.6 jel;22.9 jel;5.1 jel;14.3 jel;10.7 jel;12.5 jel;25.8 jel;13.4 jel;20.7 jel;-1.1 jel;21.3 jel;21.9 jel;11.9 jel;26.0 jel;18.5 jel;10.0 jel;23.6 jel;22.5 jel;21.4 jel;15.7 jel;26.9 jel;15.1 jel;23.0 jel;22.4 jel;15.7 jel;23.2 jel;4.0 jel;18.0 jel;23.9 jel;30.7 jel;13.5 jel;11.4 jel;19.3 jel;9.6 jel;7.2 jel;19.3 jel;23.5 jel;11.6 jel;24.8 jel;14.0 jel;14.1 jel;30.0 jel;13.4 jel;14.2 jel;17.0 jel;31.3 jel;5.6 jel;22.6 jel;32.5 jel;21.9 jel;11.1 jel;15.5 jel;23.7 jel;12.0 jel;13.7 jel;13.2 jel;13.7 jel;22.5 jel;22.2 jel;29.0 jel;5.2 jel;6.2 jel;14.5 jel;22.5 jel;21.5 jel;22.8 jel;16.8 jel;10.2 jel;13.5 jel;15.0 jel;9.9 jel;12.1 jel;23.2 jel;20.7 jel;17.2 jel;21.8 jel;26.1 jel;22.2 jel;26.2 jel;16.5 jel;25.1 jel;21.6 jel;12.5 jel;12.6 jel;23.4 jel;14.4 jel;11.6 jel;23.8 jel;24.2 jel;15.5 jel;26.3 jel;28.6 jel;21.8 jel;20.8 jel;33.4 jel;29.8 jel;29.5 jel;38.3 jel;10.7 jel;21.0 jel;6.5 jel;11.1 jel;19.8 jel;19.2 jel;16.9 jel;9.6 jel;16.9 jel;20.7 jel;27.6 jel;12.3 jel;11.1 jel;14.1 jel;28.3 jel;21.7 jel;28.6 jel;23.9 jel;14.9 jel;27.9 jel;19.6 jel;7.7 jel;17.2 jel;15.2 jel;15.0 jel;10.2 jel;12.6 jel;24.4 jel;20.5 jel;25.9 jel;17.5 jel;21.5 jel;33.0 jel;29.7 jel;26.6 jel;12.0 jel;15.2 jel;12.7 jel;12.6 jel;20.6 jel;22.7 jel;15.6 jel;20.4 jel;25.4 jel;8.8 jel;18.7 jel;18.2 jel;27.3 jel;7.7 jel;25.2 jel;11.4 jel;20.2 jel;15.1 jel;14.4 jel;19.6 jel;22.8 jel;17.7 jel;25.6 jel;9.3 jel;8.8 jel;9.6 jel;18.0 jel;19.7 jel;13.5 jel;17.4 jel;30.0 jel;31.2 jel;17.9 jel;4.8 jel;20.1 jel;17.3 jel;2.2 jel;19.6 jel;28.0 jel;7.7 jel;15.9 jel;28.0 jel;15.1 jel;14.0 jel;12.7 jel;15.0 jel;30.8 jel;7.7 jel;11.5 jel;10.1 jel;15.9 jel;17.7 jel;32.2 jel;12.7 jel;22.4 jel;26.8 jel;18.2 jel;4.7 jel;14.0 jel;18.5 jel;15.2 jel;9.6 jel;12.3 jel;14.7 jel;24.5 jel;14.3 jel;11.8 jel;33.9 jel;25.5 jel;6.6 jel;33.3 jel;14.1 jel;14.1 jel;21.2 jel;23.0 jel;28.7 jel;7.2 jel;10.8 jel;15.4 jel;20.7 jel;21.4 jel;15.4 jel;13.5 jel;12.4 jel;16.3 jel;10.1 jel;15.8 jel;22.2 jel;6.5 jel;14.7 jel;17.1 jel;23.5 jel;21.3 jel;28.3 jel;33.9 jel;21.3 jel;15.6 jel;19.5 jel;17.6 jel;16.6 jel;12.1 jel;15.1 jel;15.0 jel;19.2 jel;12.8 jel;12.8 jel;18.2 jel;16.2 jel;13.7 jel;8.5 jel;20.2 jel;20.7 jel;24.1 jel;16.8 jel;22.0 jel;19.2 jel;19.7 jel;30.5 jel;12.6 jel;20.5 jel;12.6 jel;13.7 jel;16.0 jel;25.4 jel;6.0 jel;19.8 jel;29.1 jel;23.2 jel;23.0 jel;19.0 jel;19.7 jel;17.0 jel;22.1 jel;26.6 jel;10.4 jel;11.3 jel;3.9 jel;25.1 jel;19.0 jel;20.5 jel;21.9 jel;13.7 jel;25.9 jel;21.5 jel;19.6 jel;26.0 jel;14.7 jel;30.2 jel;26.0 jel;15.6 jel;13.0 jel;4.8 jel;17.0 jel;7.3 jel;-0.8 jel;11.7 jel;26.0 jel;13.4 jel;22.0 jel;7.4 jel;11.4 jel;15.7 jel;23.7 jel;7.6 jel;19.2 jel;5.0 jel;15.7 jel;19.9 jel;16.8 jel;9.2 jel;11.6 jel;16.1 jel;9.4 jel;4.7 jel;16.3 jel;8.7 jel;14.2 jel;19.2 jel;26.5 jel;26.0 jel;28.5 jel;7.9 jel;20.0 jel;15.1 jel;20.9 jel;20.7 jel;20.0 jel;22.3 jel;19.3 jel;10.6 jel;35.2 jel;8.2 jel;13.7 jel;28.2 jel;13.9 jel;9.0 jel;26.3 jel;21.7 jel;9.1 jel;28.5 jel;17.2 jel;7.9 jel;10.0 jel;26.2 jel;15.2 jel;17.7 jel;27.4 jel;13.7 jel;28.5 jel;22.1 jel;22.9 jel;12.7 jel;27.1 jel;20.6 jel;20.9 jel;6.6 jel;4.9 jel;6.8 jel;23.4 jel;20.0 jel;27.4 jel;19.7 jel;19.4 jel;14.5 jel;9.9 jel;22.1 jel;10.9 jel;12.1 jel;14.2 jel;14.7 jel;16.0 jel;25.0 jel;18.2 jel;21.6 jel;24.6 jel;25.1 jel;17.0 jel;23.6 jel;28.5 jel;22.0 jel;10.8 jel;10.3 jel;21.6 jel;16.9 jel;18.5 jel;12.3 jel;32.3 jel;6.8 jel;31.3 jel;22.8 jel;23.1 jel;27.5 jel;30.2 jel;20.7 jel;35.5 jel;6.9 jel;7.6 jel;19.7 jel;18.8 jel;23.6 jel;4.7 jel;26.3 jel;19.5 jel;20.4 jel;12.7 jel;19.7 jel;25.4 jel;16.7 jel;19.0 jel;19.9 jel;24.3 jel;13.3 jel;20.7 jel;19.8 jel;30.2 jel;16.2 jel;29.4 jel;17.4 jel;18.8 jel;20.0 jel;6.5 jel;19.3 jel;26.7 jel;7.6 jel;11.4 jel;15.9 jel;7.3 jel;15.3 jel;12.2 jel;26.1 jel;17.5 jel;15.4 jel;21.2 jel;16.6 jel;33.3 jel;15.4 jel;8.4 jel;16.0 jel;24.0 jel;26.7 jel;11.6 jel;13.7 jel;12.0 jel;16.4 jel;13.1 jel;7.5 jel;10.1 jel;18.8 jel;9.0 jel;21.0 jel;18.5 jel;14.5 jel;10.1 jel;11.6 jel;37.4 jel;20.3 jel;-1.2 jel;11.8 jel;11.4 jel;16.9 jel;21.2 jel;17.0 jel;16.5 jel;19.9 jel;22.6 jel;23.8 jel;10.4 jel;14.7 jel;9.8 jel;21.5 jel;17.7 jel;20.7 jel;13.2 jel;16.1 jel;24.6 jel;13.8 jel;27.3 jel;16.6 jel;24.3 jel;12.6 jel;13.1 jel;17.4 jel;12.1 jel;13.9 jel;16.6 jel;19.5 jel;16.8 jel;9.3 jel;17.7 jel;20.5 jel;22.5 jel;13.9 jel;15.7 jel;4.3 jel;35.6 jel;8.3 jel;9.4 jel;9.1 jel;22.1 jel;19.2 jel;24.5 jel;13.7 jel;18.5 jel;21.2 jel;16.8 jel;34.7 jel;4.1 jel;19.7 jel;23.1 jel;15.0 jel;14.9 jel;7.3 jel;5.4 jel;16.8 jel;16.2 jel;11.8 jel;23.3 jel;11.6 jel;18.4 jel;10.1 jel;16.2 jel;16.4 jel;25.7 jel;18.8 jel;9.2 jel;8.6 jel;31.7 jel;17.9 jel;16.0 jel;18.3 jel;4.1 jel;32.3 jel;7.8 jel;11.0 jel;17.0 jel;0.5 jel;11.8 jel;31.7 jel;25.2 jel;26.4 jel;26.2 jel;18.7 jel;25.3 jel;18.8 jel;19.9 jel;19.7 jel;20.0 jel;24.2 jel;7.8 jel;18.0 jel;25.6 jel;29.3 jel;22.0 jel;15.7 jel;13.3 jel;18.9 jel;17.2 jel;17.6 jel;13.4 jel;23.4 jel;19.7 jel;11.3 jel;19.2 jel;20.8 jel;10.7 jel;18.7 jel;21.8 jel;22.9 jel;21.6 jel;5.0 jel;13.4 jel;25.6 jel;20.2 jel;18.1 jel;18.3 jel;14.0 jel;24.0 jel;18.3 jel;24.7 jel;22.9 jel;24.4 jel;17.2 jel;13.5 jel;29.1 jel;9.9 jel;14.3 jel;24.2 jel;13.8 jel;15.1 jel;8.5 jel;12.3 jel;27.4 jel;3.4 jel;4.4 jel;20.1 jel;34.2 jel;25.3 jel;13.3 jel;29.7 jel;17.8 jel;9.7 jel;30.1 jel;6.1 jel;28.0 jel;22.9 jel;19.3 jel;23.0 jel;15.1 jel;21.2 jel;12.7 jel;17.5 jel;19.4 jel;29.4 jel;17.6 jel;16.1 jel;5.9 jel;14.4 jel;23.7 jel;27.7 jel;18.1 jel;28.7 jel;22.3 jel;9.9 jel;22.6 jel;24.1 jel;23.4 jel;10.9 jel;3.6 jel;19.7 jel;22.6 jel;24.0 jel;18.9 jel;34.4 jel;20.5 jel;18.9 jel;9.8 jel;21.3 jel;13.1 jel;24.0 jel;20.3 jel;20.2 jel;18.7 jel;18.8 jel;28.7 jel;24.8 jel;20.5 jel;25.8 jel;18.4 jel;15.3 jel;16.3 jel;33.1 jel;5.7 jel;13.4 jel;23.8 jel;20.2 jel;24.7 jel;31.3 jel;18.0 jel;18.4 jel;16.1 jel;40.0 jel;17.6 jel;23.6 jel;20.1 jel;4.9 jel;14.9 jel;27.4 jel;23.4 jel;18.5 jel;16.7 jel;20.2 jel;26.2 jel;25.9 jel;29.6 jel;15.9 jel;28.9 jel;34.1 jel;15.3 jel;18.8 jel;17.2 jel;25.9 jel;25.1 jel;11.5 jel;12.2 jel;5.3 jel;6.9 jel;9.5 jel;14.4 jel;24.5 jel;5.3 jel;32.9 jel;26.3 jel;10.3 jel;16.1 jel;9.2 jel;19.0 jel;22.2 jel;16.0 jel;25.3 jel;3.8 jel;17.2 jel;25.2 jel;23.5 jel;17.0 jel;12.5 jel;27.5 jel;19.1 jel;12.0 jel;14.1 jel;21.5 jel;37.5 jel;23.6 jel;23.1 jel;33.7 jel;16.8 jel;6.0 jel;8.2 jel;22.6 jel;14.4 jel;18.6 jel;21.8 jel;8.7 jel;7.3 jel;13.5 jel;14.3 jel;27.1 jel;29.9 jel;21.2 jel;9.0 jel;21.4 jel;8.7 jel;19.8 jel;28.7 jel;25.3 jel;27.2 jel;15.9 jel;26.9 jel;22.8 jel;5.5 jel;22.7 jel;24.4 jel;27.1 jel;25.3 jel;24.9 jel;19.6 jel;18.4 jel;18.4 jel;13.5 jel;20.8 jel;21.0 jel;17.2 jel;17.0 jel;16.4 jel;15.5 jel;16.9 jel;14.8 jel;6.5 jel;16.0 jel;18.9 jel;22.9 jel;25.6 jel;8.1 jel;32.9 jel;18.5 jel;15.3 jel;23.5 jel;24.0 jel;24.3 jel;20.7 jel;18.0 jel;15.6 jel;16.4 jel;13.3 jel;20.2 jel;8.1 jel;19.6 jel;12.4 jel;12.2 jel;24.1 jel;15.5 jel;15.9 jel;22.3 jel;17.4 jel;21.9 jel;6.0 jel;36.0 jel;10.3 jel;19.0 jel;19.4 jel;25.6 jel;23.4 jel;19.5 jel;9.5 jel;27.8 jel;23.5 jel;24.1 jel;19.2 jel;12.6 jel;26.9 jel;16.2 jel;12.7 jel;13.8 jel;18.8 jel;13.7 jel;14.1 jel;19.6 jel;28.4 jel;31.7 jel;19.5 jel;23.1 jel;14.9 jel;26.2 jel;6.7 jel;23.6 jel;25.1 jel;23.3 jel;14.2 jel;13.1 jel;17.1 jel;17.3 jel;13.8 jel;20.1 jel;8.9 jel;16.7 jel;20.0 jel;10.5 jel;5.8 jel;14.9 jel;11.0 jel;14.4 jel;11.6 jel;24.1 jel;26.0 jel;19.1 jel;16.1 jel;20.9 jel;22.8 jel;19.4 jel;23.4 jel;16.6 jel;21.7 jel;13.4 jel;15.9 jel;29.5 jel;21.4 jel;9.5 jel;19.6 jel;13.4 jel;19.8 jel;16.2 jel;20.1 jel;16.6 jel;11.6 jel;24.0 jel;16.5 jel;6.3 jel;17.0 jel;-1.6 jel;26.4 jel;10.8 jel;17.4 jel;27.3 jel;13.3 jel;30.8 jel;24.0 jel;21.4 jel;19.3 jel;24.7 jel;29.4 jel;24.1 jel;10.6 jel;7.0 jel;23.6 jel;25.0 jel;23.3 jel;25.3 jel;30.4 jel;15.1 jel;21.1 jel;22.7 jel;14.0 jel;24.2 jel;1.5 jel;15.0 jel;17.0 jel;14.7 jel;16.5 jel;12.9 jel;9.4 jel;8.3 jel;3.8 jel;23.4 jel;22.8 jel;15.5 jel;20.7 jel;14.7 jel;9.5 jel;16.4 jel;23.2 jel;12.2 jel;14.3 jel;16.5 jel;20.1 jel;15.7 jel;12.1 jel;10.4 jel;22.8 jel;28.0 jel;39.9 jel;13.3 jel;17.3 jel;23.2 jel;13.1 jel;8.6 jel;21.5 jel;12.4 jel;18.6 jel;11.0 jel;8.8 jel;5.2 jel;27.8 jel;16.6 jel;10.8 jel;16.6 jel;18.1 jel;24.2 jel;24.6 jel;17.2 jel;11.8 jel;25.8 jel;27.1 jel;16.6 jel;24.7 jel;12.1 jel;27.5 jel;14.4 jel;31.6 jel;14.5 jel;32.3 jel;25.0 jel;6.6 jel;10.0 jel;19.0 jel;8.2 jel;9.6 jel;33.1 jel;21.5 jel;17.9 jel;15.8 jel;20.9 jel;17.2 jel;8.5 jel;19.5 jel;31.2 jel;11.2 jel;22.4 jel;26.5 jel;9.1 jel;19.8 jel;17.4 jel;16.8 jel;12.4 jel;31.1 jel;13.9 jel;21.4 jel;18.0 jel;16.3 jel;14.4 jel;15.5 jel;17.1 jel;26.5 jel;27.0 jel;20.0 jel;10.1 jel;11.9 jel;20.8 jel;12.7 jel;13.8 jel;23.6 jel;19.2 jel;19.2 jel;18.3 jel;28.0 jel;24.1 jel;20.3 jel;16.6 jel;16.1 jel;11.6 jel;25.8 jel;10.3 jel;11.4 jel;31.7 jel;19.8 jel;10.2 jel;10.1 jel;18.9 jel;17.2 jel;19.9 jel;16.5 jel;24.0 jel;14.9 jel;6.9 jel;10.8 jel;26.0 jel;17.7 jel;25.3 jel;17.8 jel;29.2 jel;25.2 jel;23.6 jel;18.9 jel;18.2 jel;21.9 jel;11.9 jel;16.8 jel;8.3 jel;17.0 jel;0.3 jel;20.2 jel;18.8 jel;18.5 jel;23.1 jel;10.5 jel;12.0 jel;13.8 jel;23.3 jel;18.7 jel;18.0 jel;16.0 jel;20.4 jel;21.2 jel;5.6 jel;23.3 jel;20.5 jel;20.9 jel;13.5 jel;15.5 jel;23.1 jel;24.1 jel;11.0 jel;19.6 jel;11.3 jel;17.0 jel;19.9 jel;28.4 jel;24.8 jel;12.7 jel;20.7 jel;31.9 jel;15.4 jel;20.7 jel;25.2 jel;17.1 jel;25.1 jel;10.8 jel;20.2 jel;1.1 jel;14.4 jel;17.0 jel;21.8 jel;11.6 jel;9.1 jel;20.9 jel;22.2 jel;3.3 jel;7.0 jel;9.1 jel;9.3 jel;21.2 jel;13.9 jel;13.3 jel;28.4 jel;26.7 jel;11.7 jel;4.1 jel;10.1 jel;20.2 jel;17.3 jel;25.6 jel;22.4 jel;21.7 jel;25.7 jel;16.1 jel;8.5 jel;12.0 jel;16.2 jel;25.6 jel;22.0 jel;25.5 jel;29.1 jel;10.2 jel;20.8 jel;20.1 jel;15.6 jel;5.0 jel;23.3 jel;15.2 jel;6.1 jel;15.9 jel;19.9 jel;19.6 jel;9.9 jel;11.6 jel;18.0 jel;28.0 jel;32.1 jel;22.5 jel;24.8 jel;22.4 jel;23.4 jel;18.1 jel;15.2 jel;-2.6 jel;17.5 jel;23.4 jel;11.2 jel;28.8 jel;19.4 jel;8.1 jel;19.5 jel;17.2 jel;25.5 jel;15.1 jel;24.3 jel;24.4 jel;11.3 jel;17.3 jel;13.3 jel;19.8 jel;9.9 jel;21.3 jel;17.0 jel;28.2 jel;7.5 jel;18.4 jel;16.1 jel;29.0 jel;19.9 jel;13.0 jel;16.2 jel;20.8 jel;21.1 jel;20.6 jel;11.5 jel;35.7 jel;24.5 jel;16.2 jel;23.7 jel;21.7 jel;12.7 jel;22.2 jel;18.0 jel;18.9 jel;18.1 jel;4.6 jel;20.7 jel;20.0 jel;18.8 jel;13.1 jel;12.6 jel;19.4 jel;15.8 jel;31.6 jel;22.0 jel;2.6 jel;20.6 jel;18.7 jel;23.7 jel;24.8 jel;13.8 jel;16.7 jel;27.9 jel;35.6 jel;21.8 jel;19.3 jel;13.4 jel;31.4 jel;9.0 jel;27.1 jel;13.3 jel;16.2 jel;26.0 jel;22.4 jel;25.6 jel;32.0 jel;26.4 jel;11.7 jel;19.8 jel;33.1 jel;28.6 jel;22.1 jel;13.2 jel;25.2 jel;19.2 jel;13.9 jel;19.2 jel;16.6 jel;22.1 jel;28.4 jel;17.2 jel;16.2 jel;20.6 jel;22.4 jel;16.4 jel;13.1 jel;9.3 jel;24.9 jel;10.4 jel;37.9 jel;14.5 jel;5.8 jel;18.7 jel;1.9 jel;27.0 jel;14.8 jel;15.1 jel;12.5 jel;22.3 jel;19.2 jel;13.3 jel;14.7 jel;20.7 jel;23.1 jel;-1.6 jel;22.1 jel;10.9 jel;5.7 jel;27.5 jel;21.7 jel;6.2 jel;25.5 jel;20.9 jel;17.3 jel;10.2 jel;7.7 jel;30.9 jel;3.3 jel;21.4 jel;25.7 jel;20.5 jel;9.2 jel;4.0 jel;14.9 jel;17.8 jel;16.7 jel;15.3 jel;21.6 jel;22.4 jel;21.5 jel;25.6 jel;19.4 jel;23.3 jel;18.9 jel;24.4 jel;10.7 jel;19.0 jel;8.0 jel;6.8 jel;9.1 jel;20.6 jel;18.9 jel;9.6 jel;15.6 jel;22.3 jel;18.0 jel;25.2 jel;17.8 jel;24.4 jel;23.2 jel;22.1 jel;20.5 jel;29.7 jel;18.2 jel;14.2 jel;18.8 jel;15.5 jel;20.9 jel;32.2 jel;20.8 jel;22.4 jel;7.6 jel;18.0 jel;19.7 jel;12.7 jel;15.6 jel;15.8 jel;18.4 jel;14.7 jel;14.4 jel;16.8 jel;13.3 jel;16.1 jel;19.9 jel;26.0 jel;14.0 jel;19.6 jel;15.0 jel;6.5 jel;24.5 jel;17.7 jel;17.5 jel;14.5 jel;12.8 jel;18.4 jel;29.7 jel;18.1 jel;22.0 jel;6.7 jel;18.8 jel;10.1 jel;9.5 jel;18.9 jel;18.0 jel;24.2 jel;17.3 jel;7.2 jel;27.9 jel;26.0 jel;4.6 jel;17.2 jel;20.3 jel;10.5 jel;17.6 jel;16.2 jel;22.5 jel;15.9 jel;13.6 jel;14.9 jel;22.9 jel;25.1 jel;18.0 jel;15.5 jel;9.1 jel;17.2 jel;21.6 jel;27.7 jel;18.6 jel;14.9 jel;10.9 jel;11.7 jel;22.7 jel;7.8 jel;23.8 jel;16.7 jel;22.8 jel;17.8 jel;21.7 jel;13.6 jel;8.7 jel;21.4 jel;23.7 jel;14.5 jel;17.3 jel;18.0 jel;13.7 jel;24.6 jel;28.8 jel;16.3 jel;10.2 jel;22.5 jel;27.1 jel;14.9 jel;16.3 jel;15.1 jel;24.2 jel;13.4 jel;14.6 jel;28.4 jel;16.2 jel;22.5 jel;18.5 jel;22.8 jel;15.1 jel;33.6 jel;13.1 jel;21.9 jel;12.7 jel;10.6 jel;28.4 jel;17.4 jel;15.9 jel;35.2 jel;15.9 jel;19.6 jel;19.4 jel;25.7 jel;0.0 jel;6.6 jel;9.5 jel;22.8 jel;20.5 jel;16.7 jel;29.7 jel;9.5 jel;10.9 jel;20.0 jel;23.3 jel;22.7 jel;19.9 jel;11.2 jel;7.2 jel;18.2 jel;10.0 jel;16.5 jel;14.5 jel;18.5 jel;17.1 jel;17.8 jel;17.6 jel;13.6 jel;9.5 jel;15.0 jel;14.9 jel;4.8 jel;24.2 jel;26.9 jel;8.1 jel;23.8 jel;26.6 jel;12.2 jel;8.3 jel;20.8 jel;16.7 jel;24.4 jel;15.3 jel;26.6 jel;20.6 jel;17.6 jel;10.4 jel;25.3 jel;28.4 jel;16.1 jel;23.2 jel;9.1 jel;12.4 jel;14.6 jel;25.2 jel;17.4 jel;23.6 jel;21.5 jel;31.6 jel;-0.7 jel;15.0 jel;7.5 jel;12.6 jel;21.0 jel;24.2 jel;17.6 jel;21.6 jel;18.1 jel;17.3 jel;11.0 jel;19.9 jel;19.2 jel;8.3 jel;18.8 jel;15.1 jel;28.9 jel;17.7 jel;17.5 jel;27.7 jel;12.2 jel;21.3 jel;13.2 jel;18.8 jel;25.9 jel;13.6 jel;14.5 jel;21.8 jel;7.6 jel;31.2 jel;11.7 jel;14.4 jel;19.6 jel;10.7 jel;28.5 jel;17.3 jel;10.6 jel;7.5 jel;19.5 jel;13.0 jel;10.3 jel;12.7 jel;11.1 jel;30.0 jel;24.9 jel;16.7 jel;23.1 jel;11.3 jel;6.9 jel;6.8 jel;24.1 jel;26.6 jel;14.3 jel;22.9 jel;26.3 jel;25.4 jel;20.0 jel;18.0 jel;9.8 jel;15.9 jel;26.7 jel;16.7 jel;19.3 jel;14.2 jel;14.2 jel;13.7 jel;15.4 jel;19.4 jel;24.2 jel;19.0 jel;10.3 jel;12.6 jel;15.0 jel;20.8 jel;14.3 jel;16.0 jel;12.4 jel;29.0 jel;21.0 jel;20.1 jel;24.9 jel;29.6 jel;16.1 jel;2.2 jel;14.8 jel;28.2 jel;10.8 jel;6.0 jel;11.0 jel;11.1 jel;26.7 jel;18.2 jel;19.1 jel;13.0 jel;17.0 jel;30.7 jel;14.3 jel;18.9 jel;24.4 jel;19.4 jel;13.1 jel;10.3 jel;27.7 jel;20.3 jel;12.6 jel;9.6 jel;8.3 jel;14.7 jel;11.2 jel;14.3 jel;31.8 jel;20.5 jel;21.5 jel;17.9 jel;8.8 jel;19.5 jel;28.0 jel;28.1 jel;11.9 jel;14.8 jel;18.1 jel;13.4 jel;18.6 jel;15.2 jel;11.6 jel;17.5 jel;22.2 jel;9.0 jel;13.9 jel;20.5 jel;14.6 jel;34.0 jel;21.3 jel;13.2 jel;23.3 jel;28.1 jel;22.6 jel;18.8 jel;4.7 jel;14.4 jel;26.8 jel;16.7 jel;22.9 jel;9.2 jel;13.9 jel;22.6 jel;36.3 jel;19.6 jel;17.0 jel;11.4 jel;18.7 jel;15.7 jel;25.0 jel;27.6 jel;16.6 jel;25.4 jel;15.2 jel;15.9 jel;17.2 jel;21.3 jel;16.8 jel;21.1 jel;11.3 jel;21.6 jel;12.5 jel;12.5 jel;10.3 jel;23.9 jel;32.3 jel;28.9 jel;20.1 jel;18.1 jel;16.1 jel;14.3 jel;9.8 jel;24.1 jel;3.4 jel;19.8 jel;15.3 jel;18.5 jel;13.9 jel;27.2 jel;18.6 jel;21.3 jel;13.3 jel;21.5 jel;18.7 jel;22.1 jel;9.2 jel;8.4 jel;9.4 jel;30.9 jel;24.9 jel;27.6 jel;10.7 jel;15.5 jel;28.6 jel;23.8 jel;12.0 jel;14.4 jel;17.9 jel;28.9 jel;18.1 jel;18.7 jel;19.2 jel;24.0 jel;23.0 jel;19.9 jel;16.2 jel;5.1 jel;19.4 jel;21.5 jel;24.2 jel;16.5 jel;19.4 jel;11.0 jel;21.1 jel;15.0 jel;2.0 jel;18.2 jel;21.3 jel;10.2 jel;21.4 jel;12.7 jel;27.9 jel;17.2 jel;17.0 jel;14.2 jel;33.8 jel;17.7 jel;13.2 jel;24.1 jel;21.0 jel;16.2 jel;18.2 jel;16.8 jel;12.4 jel;14.1 jel;17.8 jel;7.0 jel;15.7 jel;28.4 jel;13.5 jel;21.1 jel;10.7 jel;12.0 jel;33.2 jel;16.7 jel;19.2 jel;23.0 jel;12.7 jel;22.2 jel;20.9 jel;26.2 jel;9.5 jel;25.6 jel;26.5 jel;16.9 jel;19.8 jel;25.5 jel;18.6 jel;16.6 jel;9.3 jel;17.5 jel;22.0 jel;23.3 jel;9.8 jel;0.4 jel;17.4 jel;11.9 jel;22.2 jel;19.4 jel;20.2 jel;21.8 jel;17.2 jel;14.8 jel;25.5 jel;20.1 jel;13.5 jel;18.3 jel;22.6 jel;21.2 jel;28.0 jel;18.4 jel;15.4 jel;8.5 jel;23.8 jel;1.2 jel;28.9 jel;15.3 jel;14.4 jel;29.2 jel;30.5 jel;17.1 jel;18.2 jel;15.9 jel;23.1 jel;35.7 jel;13.9 jel;13.8 jel;8.6 jel;12.9 jel;9.4 jel;10.9 jel;15.7 jel;9.4 jel;23.7 jel;14.9 jel;21.1 jel;23.0 jel;16.4 jel;20.5 jel;19.1 jel;27.0 jel;16.1 jel;27.1 jel;7.6 jel;18.5 jel;9.9 jel;7.8 jel;16.4 jel;26.6 jel;9.0 jel;14.1 jel;21.7 jel;17.0 jel;19.1 jel;14.9 jel;14.6 jel;16.8 jel;23.0 jel;30.0 jel;33.8 jel;26.4 jel;16.5 jel;12.8 jel;28.7 jel;12.3 jel;21.5 jel;17.8 jel;8.2 jel;27.5 jel;17.7 jel;22.5 jel;14.4 jel;17.8 jel;11.5 jel;14.9 jel;19.9 jel;23.0 jel;-0.2 jel;15.1 jel;28.2 jel;21.2 jel;25.9 jel;27.0 jel;18.9 jel;15.9 jel;12.1 jel;10.1 jel;10.6 jel;17.9 jel;13.8 jel;16.3 jel;21.0 jel;23.4 jel;20.9 jel;15.8 jel;21.1 jel;3.9 jel;11.6 jel;22.5 jel;24.1 jel;-0.4 jel;22.3 jel;4.2 jel;18.4 jel;15.0 jel;17.2 jel;12.2 jel;25.4 jel;8.6 jel;25.8 jel;14.1 jel;18.8 jel;9.4 jel;14.7 jel;25.4 jel;23.6 jel;29.6 jel;22.1 jel;24.7 jel;14.0 jel;24.6 jel;20.1 jel;19.7 jel;22.1 jel;19.4 jel;17.4 jel;25.6 jel;7.2 jel;4.8 jel;25.0 jel;18.3 jel;10.8 jel;22.9 jel;13.8 jel;22.7 jel;21.4 jel;17.4 jel;6.3 jel;24.0 jel;17.1 jel;23.5 jel;32.8 jel;34.5 jel;7.1 jel;26.5 jel;11.1 jel;31.3 jel;9.8 jel;14.2 jel;7.4 jel;17.8 jel;15.4 jel;21.4 jel;21.4 jel;28.3 jel;20.0 jel;21.6 jel;37.9 jel;11.9 jel;15.7 jel;17.5 jel;28.8 jel;20.8 jel;13.4 jel;14.8 jel;31.6 jel;27.3 jel;1.4 jel;31.3 jel;24.4 jel;15.7 jel;25.7 jel;18.9 jel;15.2 jel;17.4 jel;16.4 jel;12.0 jel;21.5 jel;17.2 jel;24.2 jel;20.7 jel;19.5 jel;23.1 jel;24.6 jel;16.4 jel;19.3 jel;14.6 jel;22.3 jel;17.1 jel;19.4 jel;16.9 jel;5.0 jel;15.0 jel;17.6 jel;9.7 jel;18.3 jel;20.9 jel;28.9 jel;26.4 jel;6.8 jel;11.9 jel;12.3 jel;9.3 jel;14.8 jel;16.7 jel;25.6 jel;12.5 jel;5.8 jel;18.9 jel;15.4 jel;18.8 jel;21.5 jel;12.3 jel;12.3 jel;24.1 jel;27.0 jel;20.6 jel;13.9 jel;15.9 jel;27.3 jel;8.2 jel;13.8 jel;15.2 jel;10.0 jel;20.9 jel;13.5 jel;14.2 jel;17.5 jel;11.4 jel;18.6 jel;21.2 jel;20.3 jel;25.5 jel;-6.4 jel;9.2 jel;8.7 jel;17.3 jel;29.0 jel;16.4 jel;4.1 jel;15.0 jel;17.4 jel;22.6 jel;5.2 jel;28.1 jel;9.5 jel;10.3 jel;17.0 jel;17.7 jel;15.2 jel;5.9 jel;18.5 jel;21.6 jel;1.9 jel;13.6 jel;22.5 jel;17.6 jel;19.5 jel;11.7 jel;19.1 jel;14.6 jel;25.4 jel;29.1 jel;10.2 jel;17.9 jel;31.3 jel;18.2 jel;12.7 jel;14.1 jel;19.5 jel;22.7 jel;6.1 jel;5.7 jel;15.0 jel;19.7 jel;11.4 jel;13.2 jel;12.4 jel;23.5 jel;24.5 jel;19.2 jel;3.5 jel;24.3 jel;13.9 jel;11.8 jel;30.8 jel;12.8 jel;28.3 jel;8.3 jel;29.8 jel;8.0 jel;25.2 jel;22.4 jel;11.7 jel;26.8 jel;7.7 jel;20.6 jel;10.8 jel;13.6 jel;21.9 jel;29.4 jel;-3.0 jel;21.1 jel;31.7 jel;21.2 jel;16.0 jel;26.3 jel;17.6 jel;20.6 jel;24.3 jel;19.9 jel;20.4 jel;14.1 jel;24.4 jel;22.1 jel;10.0 jel;22.5 jel;19.3 jel;16.5 jel;24.9 jel;13.6 jel;16.3 jel;25.9 jel;30.8 jel;20.4 jel;24.6 jel;18.5 jel;12.8 jel;28.1 jel;15.4 jel;14.8 jel;15.5 jel;15.5 jel;16.7 jel;10.2 jel;23.9 jel;18.4 jel;12.9 jel;18.4 jel;24.8 jel;17.4 jel;23.4 jel;9.5 jel;15.4 jel;27.0 jel;18.4 jel;17.7 jel;9.2 jel;19.5 jel;16.6 jel;26.1 jel;6.8 jel;32.3 jel;17.6 jel;20.9 jel;17.5 jel;1.1 jel;21.4 jel;7.7 jel;25.0 jel;13.8 jel;5.1 jel;22.7 jel;23.4 jel;26.7 jel;17.3 jel;4.1 jel;17.0 jel;17.0 jel;13.2 jel;26.1 jel;19.8 jel;16.4 jel;8.7 jel;19.0 jel;20.6 jel;23.3 jel;20.6 jel;16.9 jel;20.3 jel;24.7 jel;9.3 jel;13.7 jel;19.2 jel;8.8 jel;14.0 jel;17.0 jel;6.8 jel;7.8 jel;24.0 jel;11.6 jel;25.6 jel;24.5 jel;12.8 jel;18.0 jel;22.6 jel;7.4 jel;28.7 jel;15.2 jel;22.6 jel;11.4 jel;14.0 jel;14.0 jel;14.7 jel;6.2 jel;27.0 jel;7.7 jel;26.6 jel;16.3 jel;24.4 jel;22.8 jel;9.5 jel;19.4 jel;15.9 jel;17.7 jel;9.3 jel;13.7 jel;25.4 jel;23.3 jel;17.8 jel;18.5 jel;13.3 jel;8.4 jel;21.3 jel;21.9 jel;4.5 jel;23.5 jel;29.8 jel;10.2 jel;17.0 jel;18.4 jel;15.5 jel;21.3 jel;19.2 jel;12.7 jel;21.3 jel;25.7 jel;7.3 jel;7.2 jel;7.2 jel;6.0 jel;9.7 jel;3.0 jel;10.6 jel;15.3 jel;16.3 jel;10.4 jel;19.8 jel;22.0 jel;20.5 jel;30.1 jel;14.7 jel;13.5 jel;19.7 jel;20.0 jel;18.3 jel;13.5 jel;39.1 jel;18.1 jel;16.9 jel;12.6 jel;9.4 jel;16.8 jel;31.2 jel;24.1 jel;11.9 jel;15.5 jel;0.7 jel;21.6 jel;10.6 jel;5.2 jel;11.5 jel;23.9 jel;22.3 jel;22.1 jel;18.6 jel;11.2 jel;17.5 jel;30.5 jel;16.7 jel;16.4 jel;21.3 jel;24.5 jel;16.5 jel;27.3 jel;27.8 jel;21.7 jel;13.4 jel;7.2 jel;13.0 jel;12.3 jel;19.0 jel;17.9 jel;25.6 jel;10.3 jel;11.5 jel;25.4 jel;9.0 jel;20.2 jel;16.2 jel;41.2 jel;15.3 jel;3.4 jel;27.3 jel;18.5 jel;4.4 jel;17.0 jel;12.8 jel;22.5 jel;16.7 jel;17.4 jel;12.4 jel;6.8 jel;26.6 jel;11.7 jel;18.2 jel;19.3 jel;19.9 jel;23.6 jel;14.9 jel;19.8 jel;20.9 jel;16.8 jel;15.7 jel;20.4 jel;28.7 jel;13.2 jel;16.8 jel;9.3 jel;15.8 jel;15.7 jel;15.1 jel;7.3 jel;23.0 jel;17.1 jel;16.9 jel;10.5 jel;20.1 jel;31.5 jel;23.7 jel;7.5 jel;12.9 jel;17.0 jel;15.4 jel;12.5 jel;13.8 jel;14.6 jel;13.0 jel;40.8 jel;14.6 jel;23.5 jel;15.5 jel;26.4 jel;23.0 jel;18.2 jel;9.6 jel;23.3 jel;20.7 jel;20.8 jel;17.2 jel;29.5 jel;26.6 jel;24.0 jel;22.9 jel;14.3 jel;24.1 jel;21.9 jel;11.9 jel;17.0 jel;29.4 jel;21.0 jel;22.3 jel;16.5 jel;9.5 jel;6.8 jel;42.5 jel;12.9 jel;13.6 jel;15.9 jel;3.3 jel;19.5 jel;17.9 jel;26.3 jel;1.3 jel;27.9 jel;8.8 jel;18.1 jel;18.1 jel;19.3 jel;8.4 jel;22.5 jel;16.8 jel;21.7 jel;14.4 jel;13.5 jel;29.3 jel;16.4 jel;16.8 jel;14.8 jel;16.1 jel;25.6 jel;11.4 jel;23.0 jel;17.8 jel;20.4 jel;15.8 jel;16.9 jel;23.0 jel;14.7 jel;26.7 jel;8.5 jel;13.7 jel;12.0 jel;12.9 jel;11.7 jel;17.9 jel;25.2 jel;20.2 jel;11.4 jel;7.2 jel;37.8 jel;14.3 jel;20.7 jel;26.8 jel;15.8 jel;25.7 jel;15.6 jel;21.4 jel;12.6 jel;9.9 jel;10.4 jel;17.2 jel;19.5 jel;9.7 jel;23.4 jel;16.5 jel;15.8 jel;20.0 jel;11.8 jel;33.1 jel;13.5 jel;13.2 jel;25.9 jel;16.7 jel;12.5 jel;15.9 jel;24.2 jel;27.7 jel;11.8 jel;15.3 jel;19.1 jel;6.9 jel;19.1 jel;18.9 jel;15.5 jel;19.8 jel;14.9 jel;15.5 jel;7.1 jel;1.3 jel;16.1 jel;17.1 jel;6.6 jel;10.6 jel;26.6 jel;9.6 jel;21.4 jel;18.9 jel;19.6 jel;23.7 jel;9.0 jel;8.5 jel;13.3 jel;17.3 jel;16.7 jel;15.9 jel;28.6 jel;18.0 jel;24.5 jel;-2.6 jel;14.7 jel;23.8 jel;19.5 jel;17.2 jel;15.6 jel;13.6 jel;17.9 jel;1.6 jel;13.5 jel;17.6 jel;20.6 jel;13.3 jel;13.3 jel;19.6 jel;18.7 jel;24.9 jel;23.7 jel;21.5 jel;20.4 jel;17.6 jel;22.9 jel;16.2 jel;24.1 jel;21.7 jel;21.7 jel;4.5 jel;27.0 jel;2.7 jel;22.4 jel;15.7 jel;17.9 jel;19.2 jel;26.8 jel;31.9 jel;11.4 jel;18.8 jel;22.8 jel;21.6 jel;25.3 jel;17.3 jel;6.0 jel;30.5 jel;29.5 jel;31.9 jel;11.7 jel;20.2 jel;15.7 jel;16.8 jel;15.1 jel;30.3 jel;0.7 jel;8.5 jel;24.2 jel;20.8 jel;36.7 jel;23.1 jel;9.9 jel;13.0 jel;16.5 jel;17.7 jel;33.0 jel;14.2 jel;11.5 jel;25.0 jel;23.3 jel;20.2 jel;22.0 jel;34.1 jel;19.3 jel;24.9 jel;27.5 jel;36.5 jel;23.1 jel;35.8 jel;19.9 jel;10.4 jel;-2.1 jel;19.3 jel;11.7 jel;20.7 jel;19.8 jel;28.3 jel;26.4 jel;15.0 jel;11.1 jel;12.0 jel;9.6 jel;21.0 jel;11.9 jel;16.4 jel;27.5 jel;21.1 jel;27.3 jel;19.5 jel;14.1 jel;24.8 jel;18.1 jel;22.8 jel;14.1 jel;13.1 jel;9.8 jel;22.6 jel;18.5 jel;17.1 jel;21.2 jel;22.3 jel;11.6 jel;3.3 jel;13.5 jel;23.1 jel;23.0 jel;23.1 jel;31.3 jel;25.3 jel;13.5 jel;22.6 jel;15.8 jel;13.6 jel;5.2 jel;16.3 jel;22.3 jel;19.0 jel;9.1 jel;20.7 jel;17.1 jel;11.1 jel;10.1 jel;19.8 jel;13.0 jel;19.5 jel;27.7 jel;24.4 jel;27.1 jel;18.3 jel;6.1 jel;29.0 jel;24.7 jel;16.7 jel;18.7 jel;15.1 jel;19.2 jel;10.0 jel;14.9 jel;27.1 jel;14.3 jel;18.9 jel;18.0 jel;22.0 jel;14.2 jel;14.4 jel;13.6 jel;12.8 jel;14.3 jel;19.4 jel;24.4 jel;11.9 jel;18.5 jel;24.1 jel;26.6 jel;27.3 jel;9.4 jel;18.2 jel;20.5 jel;25.5 jel;14.6 jel;22.2 jel;14.0 jel;26.4 jel;12.5 jel;19.3 jel;25.1 jel;16.1 jel;17.3 jel;23.3 jel;13.1 jel;6.6 jel;26.1 jel;17.7 jel;8.3 jel;25.0 jel;21.1 jel;19.5 jel;20.1 jel;15.8 jel;12.6 jel;14.7 jel;11.2 jel;22.2 jel;17.5 jel;30.1 jel;13.1 jel;18.4 jel;11.5 jel;19.0 jel;7.9 jel;21.5 jel;10.1 jel;19.2 jel;27.0 jel;39.9 jel;11.3 jel;15.6 jel;21.5 jel;10.0 jel;19.3 jel;26.5 jel;10.7 jel;22.6 jel;26.0 jel;13.3 jel;16.0 jel;21.4 jel;31.9 jel;18.8 jel;19.2 jel;14.4 jel;14.8 jel;10.5 jel;19.9 jel;17.0 jel;19.6 jel;24.1 jel;21.8 jel;18.7 jel;25.9 jel;11.6 jel;20.6 jel;27.3 jel;25.5 jel;19.1 jel;16.8 jel;21.8 jel;22.4 jel;13.7 jel;21.6 jel;2.6 jel;22.2 jel;15.6 jel;21.7 jel;14.3 jel;17.9 jel;12.5 jel;21.2 jel;19.0 jel;18.6 jel;17.0 jel;17.6 jel;23.5 jel;26.2 jel;27.2 jel;14.3 jel;11.3 jel;16.9 jel;22.7 jel;3.1 jel;18.0 jel;16.2 jel;15.6 jel;20.6 jel;14.1 jel;22.7 jel;15.8 jel;22.3 jel;26.7 jel;10.9 jel;28.3 jel;12.7 jel;22.8 jel;19.2 jel;19.5 jel;23.5 jel;28.8 jel;28.4 jel;24.3 jel;16.1 jel;11.1 jel;8.1 jel;16.8 jel;20.7 jel;23.8 jel;19.1 jel;15.9 jel;23.2 jel;22.2 jel;16.1 jel;21.1 jel;12.8 jel;16.9 jel;20.6 jel;25.7 jel;12.2 jel;14.0 jel;9.0 jel;17.1 jel;20.1 jel;25.3 jel;16.8 jel;20.9 jel;23.1 jel;20.6 jel;21.2 jel;14.2 jel;24.6 jel;28.9 jel;15.6 jel;8.5 jel;6.2 jel;19.3 jel;9.7 jel;10.7 jel;13.3 jel;31.9 jel;23.6 jel;12.0 jel;26.1 jel;8.3 jel;17.4 jel;12.7 jel;19.4 jel;22.8 jel;28.8 jel;15.9 jel;12.2 jel;24.2 jel;25.5 jel;16.6 jel;23.8 jel;17.4 jel;14.0 jel;20.7 jel;12.0 jel;24.1 jel;16.6 jel;23.3 jel;15.8 jel;7.1 jel;18.8 jel;13.6 jel;10.5 jel;21.4 jel;17.1 jel;20.5 jel;13.2 jel;12.1 jel;25.7 jel;23.0 jel;12.9 jel;23.4 jel;29.4 jel;14.7 jel;30.2 jel;3.0 jel;13.1 jel;8.3 jel;11.2 jel;19.3 jel;23.7 jel;11.0 jel;16.6 jel;9.6 jel;15.3 jel;11.3 jel;20.5 jel;23.6 jel;19.0 jel;20.5 jel;23.6 jel;19.1 jel;12.9 jel;2.8 jel;21.1 jel;26.5 jel;11.2 jel;15.7 jel;15.7 jel;9.9 jel;20.5 jel;7.4 jel;19.6 jel;27.7 jel;11.3 jel;26.7 jel;33.4 jel;25.9 jel;21.5 jel;14.0 jel;13.9 jel;23.2 jel;33.7 jel;15.6 jel;25.1 jel;15.1 jel;4.4 jel;21.5 jel;10.0 jel;18.0 jel;21.4 jel;22.6 jel;35.8 jel;7.0 jel;8.1 jel;7.1 jel;28.2 jel;17.0 jel;2.9 jel;19.3 jel;15.3 jel;13.4 jel;18.1 jel;15.4 jel;12.6 jel;17.9 jel;21.3 jel;9.5 jel;16.7 jel;17.1 jel;12.1 jel;23.2 jel;16.4 jel;23.2 jel;11.0 jel;19.9 jel;16.6 jel;14.6 jel;17.9 jel;28.9 jel;21.9 jel;31.3 jel;6.7 jel;11.5 jel;19.5 jel;22.5 jel;15.2 jel;22.9 jel;14.5 jel;6.3 jel;19.9 jel;11.6 jel;10.4 jel;17.9 jel;19.5 jel;9.9 jel;12.2 jel;13.4 jel;11.5 jel;23.1 jel;5.8 jel;16.0 jel;13.0 jel;15.8 jel;21.8 jel;20.7 jel;15.9 jel;19.9 jel;22.3 jel;10.2 jel;32.9 jel;22.4 jel;18.5 jel;23.7 jel;13.2 jel;18.5 jel;15.1 jel;22.4 jel;14.5 jel;12.2 jel;1.8 jel;22.0 jel;21.9 jel;8.7 jel;19.6 jel;21.4 jel;10.8 jel;15.4 jel;21.6 jel;15.2 jel;14.8 jel;9.4 jel;21.5 jel;27.2 jel;26.7 jel;23.5 jel;16.8 jel;18.5 jel;22.8 jel;13.8 jel;16.6 jel;25.5 jel;17.1 jel;12.8 jel;18.1 jel;34.6 jel;16.9 jel;13.5 jel;16.0 jel;23.3 jel;20.6 jel;20.3 jel;9.8 jel;6.8 jel;26.7 jel;10.7 jel;20.4 jel;27.1 jel;20.9 jel;19.1 jel;19.8 jel;13.6 jel;13.7 jel;30.7 jel;18.6 jel;11.7 jel;11.9 jel;18.4 jel;14.7 jel;10.5 jel;16.0 jel;21.1 jel;21.4 jel;21.7 jel;9.3 jel;25.5 jel;25.3 jel;11.5 jel;27.9 jel;23.9 jel;21.1 jel;10.6 jel;15.6 jel;15.5 jel;10.7 jel;15.8 jel;23.1 jel;28.8 jel;25.5 jel;27.7 jel;23.1 jel;31.7 jel;27.1 jel;15.1 jel;15.5 jel;19.9 jel;15.9 jel;20.6 jel;12.8 jel;10.9 jel;13.4 jel;15.1 jel;-0.9 jel;12.2 jel;8.0 jel;18.6 jel;21.7 jel;26.2 jel;19.9 jel;23.1 jel;10.7 jel;24.2 jel;23.1 jel;18.1 jel;17.3 jel;20.8 jel;15.6 jel;29.3 jel;15.8 jel;19.1 jel;28.6 jel;16.9 jel;11.1 jel;13.2 jel;19.4 jel;16.6 jel;23.4 jel;7.3 jel;30.5 jel;30.7 jel;12.4 jel;14.4 jel;20.6 jel;29.8 jel;13.8 jel;17.1 jel;15.5 jel;-3.5 jel;9.8 jel;15.9 jel;5.4 jel;22.1 jel;14.7 jel;25.7 jel;34.1 jel;14.4 jel;29.3 jel;30.7 jel;0.9 jel;35.5 jel;20.8 jel;31.3 jel;11.4 jel;3.1 jel;16.5 jel;1.7 jel;2.1 jel;19.3 jel;14.3 jel;31.5 jel;27.2 jel;6.8 jel;15.3 jel;32.1 jel;20.7 jel;9.9 jel;23.6 jel;17.7 jel;21.1 jel;22.1 jel;24.7 jel;25.7 jel;26.4 jel;18.6 jel;13.1 jel;23.5 jel;5.6 jel;19.7 jel;12.5 jel;26.7 jel;29.8 jel;15.6 jel;14.1 jel;11.6 jel;17.0 jel;20.7 jel;18.5 jel;22.0 jel;21.8 jel;21.9 jel;8.9 jel;21.7 jel;30.8 jel;14.7 jel;9.1 jel;26.8 jel;10.4 jel;13.6 jel;20.2 jel;12.3 jel;14.0 jel;30.1 jel;24.1 jel;20.0 jel;11.4 jel;16.2 jel;13.5 jel;16.5 jel;18.3 jel;18.5 jel;17.8 jel;27.1 jel;21.5 jel;11.3 jel;8.8 jel;19.6 jel;17.7 jel;18.0 jel;7.3 jel;20.0 jel;15.8 jel;4.4 jel;14.9 jel;18.0 jel;11.9 jel;12.7 jel;12.6 jel;15.6 jel;20.1 jel;20.2 jel;4.9 jel;16.7 jel;17.3 jel;12.4 jel;5.9 jel;24.4 jel;19.7 jel;17.4 jel;34.1 jel;22.4 jel;21.4 jel;27.5 jel;33.8 jel;16.6 jel;20.8 jel;32.4 jel;15.9 jel;22.4 jel;17.5 jel;13.9 jel;24.2 jel;27.1 jel;17.5 jel;30.1 jel;18.3 jel;15.1 jel;9.9 jel;22.3 jel;24.9 jel;22.6 jel;10.7 jel;6.7 jel;14.5 jel;27.9 jel;20.0 jel;29.4 jel;18.1 jel;4.8 jel;18.1 jel;9.4 jel;12.2 jel;12.1 jel;12.2 jel;8.8 jel;17.5 jel;19.6 jel;16.0 jel;18.4 jel;0.1 jel;27.5 jel;22.1 jel;23.9 jel;11.4 jel;21.2 jel;17.2 jel;32.0 jel;22.4 jel;16.2 jel;15.4 jel;20.0 jel;26.5 jel;18.5 jel;32.0 jel;29.3 jel;31.8 jel;13.6 jel;15.4 jel;21.7 jel;8.6 jel;24.3 jel;12.2 jel;14.5 jel;21.2 jel;22.2 jel;15.2 jel;14.3 jel;16.2 jel;24.9 jel;17.8 jel;13.9 jel;22.0 jel;27.2 jel;17.1 jel;23.7 jel;19.0 jel;16.7 jel;28.1 jel;17.2 jel;25.7 jel;17.5 jel;28.7 jel;25.4 jel;17.3 jel;11.7 jel;12.6 jel;26.6 jel;3.9 jel;14.5 jel;29.3 jel;15.6 jel;13.2 jel;31.3 jel;19.9 jel;13.9 jel;13.8 jel;6.3 jel;21.6 jel;17.4 jel;22.7 jel;19.7 jel;17.6 jel;11.8 jel;22.8 jel;27.0 jel;9.3 jel;6.1 jel;16.7 jel;12.4 jel;23.4 jel;4.4 jel;21.0 jel;16.3 jel;23.3 jel;13.4 jel;30.2 jel;29.6 jel;0.8 jel;19.0 jel;1.6 jel;12.7 jel;19.1 jel;27.6 jel;28.5 jel;13.0 jel;8.2 jel;25.2 jel;9.4 jel;14.3 jel;25.2 jel;21.5 jel;34.1 jel;17.1 jel;20.9 jel;9.3 jel;23.8 jel;12.2 jel;19.8 jel;19.5 jel;15.1 jel;27.2 jel;26.9 jel;30.2 jel;20.5 jel;19.9 jel;20.6 jel;20.5 jel;22.3 jel;10.8 jel;11.8 jel;14.3 jel;19.5 jel;13.3 jel;17.7 jel;14.9 jel;13.9 jel;22.4 jel;20.1 jel;18.6 jel;19.6 jel;13.4 jel;17.7 jel;6.0 jel;12.1 jel;19.4 jel;18.7 jel;7.6 jel;21.1 jel;13.6 jel;28.8 jel;12.0 jel;13.6 jel;22.7 jel;20.0 jel;5.1 jel;18.3 jel;23.6 jel;15.5 jel;15.2 jel;20.5 jel;21.2 jel;2.5 jel;21.7 jel;20.9 jel;22.3 jel;10.6 jel;18.3 jel;31.3 jel;10.8 jel;10.4 jel;16.9 jel;27.8 jel;10.8 jel;-4.9 jel;26.8 jel;23.0 jel;21.6 jel;10.7 jel;18.9 jel;21.2 jel;20.9 jel;24.0 jel;17.1 jel;17.0 jel;23.5 jel;15.2 jel;7.6 jel;27.4 jel;11.2 jel;20.6 jel;19.5 jel;26.7 jel;24.7 jel;10.5 jel;17.3 jel;13.6 jel;29.9 jel;7.8 jel;36.4 jel;21.4 jel;14.4 jel;23.6 jel;13.2 jel;18.5 jel;10.5 jel;10.7 jel;7.3 jel;22.1 jel;21.2 jel;21.6 jel;14.3 jel;10.7 jel;17.3 jel;4.9 jel;21.2 jel;21.2 jel;18.4 jel;15.1 jel;21.4 jel;12.6 jel;11.2 jel;24.8 jel;13.9 jel;15.1 jel;19.0 jel;26.4 jel;19.3 jel;10.3 jel;16.8 jel;29.4 jel;18.0 jel;8.0 jel;19.9 jel;15.9 jel;10.8 jel;12.2 jel;22.1 jel;13.9 jel;17.1 jel;12.5 jel;15.7 jel;20.8 jel;24.1 jel;20.6 jel;34.5 jel;29.9 jel;24.6 jel;6.6 jel;27.6 jel;25.8 jel;10.8 jel;20.2 jel;16.1 jel;17.5 jel;35.6 jel;9.9 jel;22.7 jel;24.7 jel;23.6 jel;10.2 jel;19.0 jel;8.2 jel;12.6 jel;19.6 jel;3.0 jel;22.5 jel;11.4 jel;18.1 jel;16.5 jel;27.9 jel;11.2 jel;16.7 jel;21.5 jel;2.0 jel;20.4 jel;18.7 jel;19.2 jel;10.6 jel;11.4 jel;9.5 jel;26.9 jel;23.2 jel;24.9 jel;15.3 jel;21.6 jel;13.1 jel;17.0 jel;19.4 jel;26.7 jel;16.4 jel;5.4 jel;18.3 jel;14.4 jel;25.4 jel;13.8 jel;4.8 jel;17.9 jel;24.6 jel;23.6 jel;11.2 jel;23.8 jel;17.2 jel;11.6 jel;13.6 jel;20.4 jel;29.4 jel;16.8 jel;11.6 jel;16.4 jel;19.2 jel;17.5 jel;19.5 jel;32.6 jel;30.6 jel;15.9 jel;18.7 jel;22.7 jel;9.1 jel;29.4 jel;16.2 jel;17.0 jel;27.4 jel;15.7 jel;23.6 jel;20.3 jel;11.2 jel;12.7 jel;11.3 jel;18.1 jel;24.6 jel;17.8 jel;15.7 jel;15.9 jel;21.1 jel;3.6 jel;1.5 jel;13.3 jel;22.6 jel;29.4 jel;23.9 jel;26.6 jel;18.9 jel;12.9 jel;15.3 jel;15.5 jel;13.7 jel;25.2 jel;5.6 jel;2.5 jel;22.6 jel;9.3 jel;41.1 jel;25.4 jel;18.6 jel;29.7 jel;16.4 jel;9.1 jel;31.1 jel;20.3 jel;25.6 jel;20.7 jel;14.3 jel;35.5 jel;21.4 jel;12.4 jel;14.7 jel;26.0 jel;16.7 jel;17.0 jel;25.1 jel;21.4 jel;27.0 jel;18.9 jel;15.6 jel;26.3 jel;22.9 jel;17.7 jel;17.8 jel;16.5 jel;11.9 jel;20.7 jel;17.4 jel;7.5 jel;23.0 jel;10.0 jel;17.7 jel;19.4 jel;23.2 jel;23.7 jel;18.0 jel;20.6 jel;23.1 jel;22.0 jel;14.0 jel;27.5 jel;26.7 jel;26.9 jel;19.8 jel;23.8 jel;16.3 jel;24.7 jel;21.4 jel;11.3 jel;19.0 jel;9.5 jel;21.7 jel;23.1 jel;29.2 jel;23.5 jel;9.5 jel;14.3 jel;20.2 jel;18.8 jel;11.9 jel;18.2 jel;21.0 jel;15.5 jel;25.9 jel;24.6 jel;8.5 jel;11.2 jel;24.1 jel;19.0 jel;4.3 jel;19.3 jel;19.8 jel;19.1 jel;13.3 jel;21.4 jel;26.8 jel;13.2 jel;23.7 jel;13.1 jel;15.1 jel;25.6 jel;21.3 jel;17.2 jel;9.2 jel;21.1 jel;20.7 jel;20.5 jel;31.7 jel;20.7 jel;10.6 jel;28.3 jel;17.7 jel;19.8 jel;33.6 jel;9.3 jel;10.0 jel;19.9 jel;11.9 jel;32.0 jel;15.9 jel;14.7 jel;19.4 jel;9.8 jel;18.7 jel;24.5 jel;15.3 jel;27.2 jel;20.8 jel;27.6 jel;12.9 jel;10.0 jel;22.7 jel;13.1 jel;15.8 jel;15.5 jel;17.3 jel;14.7 jel;8.1 jel;24.4 jel;13.1 jel;12.2 jel;19.6 jel;32.6 jel;18.2 jel;20.1 jel;13.1 jel;21.9 jel;22.0 jel;21.7 jel;21.0 jel;28.1 jel;9.5 jel;17.4 jel;15.8 jel;19.3 jel;8.6 jel;13.8 jel;23.4 jel;16.5 jel;29.0 jel;27.9 jel;14.8 jel;12.6 jel;20.8 jel;17.1 jel;15.4 jel;22.1 jel;11.5 jel;15.5 jel;13.4 jel;9.4 jel;9.1 jel;24.7 jel;5.6 jel;6.6 jel;23.1 jel;16.5 jel;25.7 jel;16.7 jel;21.2 jel;15.8 jel;13.1 jel;14.4 jel;16.4 jel;19.7 jel;16.1 jel;13.2 jel;2.0 jel;16.0 jel;20.3 jel;21.6 jel;26.8 jel;27.8 jel;12.2 jel;19.4 jel;23.9 jel;21.7 jel;10.3 jel;16.4 jel;11.1 jel;13.7 jel;19.1 jel;15.0 jel;16.7 jel;29.1 jel;19.0 jel;21.4 jel;14.0 jel;11.6 jel;11.1 jel;17.7 jel;8.2 jel;12.6 jel;25.2 jel;19.8 jel;12.3 jel;16.9 jel;16.9 jel;18.9 jel;10.3 jel;19.0 jel;7.8 jel;7.5 jel;8.2 jel;19.1 jel;21.5 jel;21.5 jel;6.6 jel;12.4 jel;14.3 jel;18.5 jel;14.0 jel;21.8 jel;25.3 jel;22.1 jel;22.7 jel;11.7 jel;12.1 jel;20.6 jel;23.1 jel;11.2 jel;15.9 jel;23.4 jel;27.3 jel;31.9 jel;16.7 jel;12.1 jel;9.7 jel;19.6 jel;14.1 jel;28.7 jel;19.8 jel;11.9 jel;10.9 jel;7.4 jel;3.4 jel;11.5 jel;27.6 jel;21.3 jel;7.4 jel;28.7 jel;17.6 jel;17.2 jel;14.7 jel;28.3 jel;10.8 jel;9.1 jel;35.4 jel;21.6 jel;26.2 jel;20.4 jel;26.4 jel;26.1 jel;12.2 jel;9.2 jel;24.4 jel;24.9 jel;24.1 jel;16.3 jel;20.3 jel;22.7 jel;20.4 jel;1.7 jel;0.6 jel;28.3 jel;16.8 jel;23.4 jel;12.5 jel;9.7 jel;17.9 jel;26.9 jel;15.8 jel;29.2 jel;27.3 jel;4.6 jel;4.4 jel;19.8 jel;13.5 jel;12.0 jel;18.0 jel;18.6 jel;11.2 jel;18.8 jel;20.7 jel;8.6 jel;30.9 jel;11.9 jel;16.7 jel;18.0 jel;4.3 jel;19.3 jel;13.0 jel;9.7 jel;11.7 jel;24.6 jel;21.1 jel;17.0 jel;17.4 jel;21.1 jel;20.3 jel;15.2 jel;25.6 jel;19.8 jel;12.0 jel;17.3 jel;21.0 jel;4.1 jel;23.1 jel;18.4 jel;9.4 jel;24.2 jel;10.8 jel;26.7 jel;15.4 jel;19.9 jel;12.5 jel;22.5 jel;6.5 jel;28.4 jel;28.8 jel;11.1 jel;17.6 jel;26.5 jel;11.5 jel;5.0 jel;14.8 jel;26.9 jel;17.2 jel;12.5 jel;5.8 jel;16.9 jel;17.8 jel;16.8 jel;14.9 jel;17.0 jel;11.7 jel;14.4 jel;11.2 jel;24.3 jel;13.3 jel;26.3 jel;6.3 jel;12.3 jel;8.9 jel;15.0 jel;20.3 jel;18.8 jel;28.6 jel;8.8 jel;3.5 jel;15.4 jel;14.4 jel;15.0 jel;15.1 jel;20.0 jel;17.4 jel;10.0 jel;11.3 jel;23.8 jel;13.7 jel;21.5 jel;8.3 jel;6.2 jel;21.4 jel;32.1 jel;29.0 jel;20.1 jel;13.6 jel;20.0 jel;16.3 jel;8.9 jel;19.3 jel;7.6 jel;21.7 jel;23.3 jel;14.9 jel;21.3 jel;3.8 jel;35.7 jel;7.3 jel;21.3 jel;19.3 jel;12.3 jel;19.8 jel;24.1 jel;12.6 jel;23.1 jel;22.9 jel;7.2 jel;11.1 jel;28.7 jel;14.6 jel;23.6 jel;24.1 jel;25.8 jel;18.6 jel;29.0 jel;21.2 jel;19.6 jel;15.8 jel;18.7 jel;18.9 jel;22.7 jel;9.7 jel;22.4 jel;22.9 jel;19.0 jel;16.2 jel;14.1 jel;21.2 jel;25.8 jel;23.6 jel;20.0 jel;21.9 jel;14.8 jel;19.3 jel;20.1 jel;8.3 jel;23.5 jel;5.5 jel;30.0 jel;26.2 jel;19.3 jel;21.8 jel;11.4 jel;13.0 jel;1.2 jel;25.3 jel;18.2 jel;15.9 jel;16.4 jel;27.8 jel;24.1 jel;21.9 jel;30.5 jel;11.3 jel;23.6 jel;28.5 jel;22.3 jel;31.8 jel;18.4 jel;16.4 jel;19.0 jel;6.1 jel;26.2 jel;19.5 jel;5.9 jel;12.1 jel;16.3 jel;23.5 jel;15.6 jel;6.2 jel;14.3 jel;19.6 jel;15.2 jel;27.1 jel;20.8 jel;21.7 jel;16.8 jel;14.1 jel;17.9 jel;26.6 jel;22.9 jel;25.6 jel;19.7 jel;22.6 jel;13.2 jel;14.5 jel;15.3 jel;20.8 jel;20.1 jel;4.4 jel;19.4 jel;35.1 jel;14.7 jel;23.9 jel;15.7 jel;18.1 jel;14.7 jel;24.9 jel;15.9 jel;28.5 jel;31.5 jel;8.9 jel;13.3 jel;17.7 jel;13.2 jel;13.6 jel;7.8 jel;8.4 jel;20.1 jel;3.3 jel;21.7 jel;12.2 jel;3.2 jel;15.4 jel;16.9 jel;27.8 jel;24.4 jel;13.9 jel;26.3 jel;24.2 jel;7.5 jel;13.5 jel;18.0 jel;21.4 jel;17.8 jel;24.2 jel;13.5 jel;19.3 jel;17.6 jel;7.5 jel;13.1 jel;9.6 jel;21.9 jel;10.6 jel;11.6 jel;24.1 jel;17.3 jel;17.8 jel;21.4 jel;12.2 jel;4.0 jel;20.5 jel;22.1 jel;6.3 jel;6.7 jel;13.0 jel;12.8 jel;17.9 jel;17.1 jel;18.8 jel;24.8 jel;2.8 jel;22.0 jel;20.6 jel;27.9 jel;18.4 jel;16.5 jel;21.1 jel;25.6 jel;14.5 jel;16.7 jel;16.2 jel;15.0 jel;17.7 jel;23.0 jel;13.7 jel;23.6 jel;17.6 jel;16.2 jel;9.2 jel;13.0 jel;20.4 jel;18.7 jel;18.5 jel;24.5 jel;20.5 jel;25.3 jel;6.8 jel;21.6 jel;15.0 jel;14.1 jel;15.7 jel;15.2 jel;21.3 jel;17.4 jel;21.0 jel;25.0 jel;13.2 jel;21.6 jel;23.8 jel;19.1 jel;17.3 jel;15.2 jel;29.1 jel;23.7 jel;15.7 jel;24.6 jel;8.3 jel;7.4 jel;7.7 jel;24.5 jel;12.8 jel;24.1 jel;16.2 jel;18.5 jel;17.7 jel;22.7 jel;7.2 jel;15.8 jel;14.0 jel;13.8 jel;14.5 jel;12.0 jel;9.2 jel;20.8 jel;14.7 jel;4.5 jel;19.9 jel;9.3 jel;21.5 jel;18.3 jel;16.6 jel;18.2 jel;14.9 jel;12.4 jel;20.3 jel;21.8 jel;19.2 jel;19.8 jel;19.5 jel;21.7 jel;9.4 jel;24.3 jel;13.8 jel;21.6 jel;17.4 jel;23.5 jel;7.2 jel;9.7 jel;14.7 jel;16.6 jel;23.7 jel;11.5 jel;12.9 jel;17.1 jel;21.9 jel;19.9 jel;27.1 jel;20.3 jel;6.2 jel;12.4 jel;14.4 jel;20.4 jel;20.5 jel;28.0 jel;7.6 jel;11.6 jel;25.0 jel;12.2 jel;23.7 jel;15.2 jel;19.5 jel;23.9 jel;9.2 jel;20.9 jel;10.5 jel;22.9 jel;-2.2 jel;15.5 jel;23.0 jel;11.4 jel;19.9 jel;17.8 jel;14.0 jel;11.9 jel;23.5 jel;26.4 jel;17.6 jel;15.4 jel;8.8 jel;8.6 jel;9.6 jel;8.0 jel;5.9 jel;9.3 jel;21.0 jel;19.7 jel;22.6 jel;27.6 jel;11.3 jel;17.0 jel;29.2 jel;18.8 jel;9.1 jel;18.3 jel;12.2 jel;17.6 jel;28.1 jel;5.6 jel;13.0 jel;-2.1 jel;19.0 jel;12.9 jel;25.1 jel;18.7 jel;24.8 jel;27.2 jel;19.8 jel;23.9 jel;17.7 jel;22.9 jel;20.0 jel;22.5 jel;15.6 jel;24.3 jel;24.2 jel;13.5 jel;19.2 jel;16.8 jel;6.0 jel;8.9 jel;15.5 jel;25.9 jel;17.0 jel;9.8 jel;24.2 jel;13.4 jel;21.1 jel;14.1 jel;31.0 jel;15.8 jel;16.8 jel;22.4 jel;18.8 jel;19.3 jel;18.4 jel;20.4 jel;17.7 jel;26.7 jel;15.5 jel;3.4 jel;19.8 jel;9.4 jel;18.1 jel;16.1 jel;16.4 jel;27.0 jel;19.9 jel;14.2 jel;25.9 jel;24.6 jel;16.3 jel;14.3 jel;33.0 jel;19.8 jel;0.9 jel;27.3 jel;13.8 jel;20.4 jel;22.7 jel;25.7 jel;19.1 jel;16.5 jel;28.9 jel;6.0 jel;32.5 jel;12.2 jel;10.6 jel;15.7 jel;17.8 jel;13.5 jel;13.0 jel;20.3 jel;35.2 jel;27.9 jel;14.6 jel;11.6 jel;8.5 jel;12.5 jel;20.8 jel;16.3 jel;6.6 jel;13.6 jel;9.6 jel;22.4 jel;25.6 jel;22.9 jel;21.2 jel;14.8 jel;22.5 jel;21.9 jel;16.7 jel;13.2 jel;35.0 jel;26.5 jel;15.3 jel;13.6 jel;11.9 jel;24.9 jel;15.0 jel;21.0 jel;14.1 jel;14.8 jel;13.9 jel;22.4 jel;17.2 jel;13.1 jel;23.4 jel;9.6 jel;16.3 jel;15.9 jel;9.8 jel;15.0 jel;26.1 jel;25.6 jel;20.9 jel;11.3 jel;5.9 jel;7.9 jel;26.9 jel;14.8 jel;11.1 jel;11.5 jel;24.4 jel;5.3 jel;16.3 jel;9.9 jel;18.6 jel;10.0 jel;13.1 jel;9.3 jel;13.3 jel;29.7 jel;22.4 jel;14.0 jel;10.7 jel;11.5 jel;17.0 jel;15.1 jel;19.3 jel;16.9 jel;3.9 jel;19.2 jel;16.3 jel;13.1 jel;31.0 jel;20.1 jel;12.2 jel;20.0 jel;19.1 jel;22.5 jel;18.7 jel;27.2 jel;5.3 jel;29.3 jel;19.8 jel;20.1 jel;19.2 jel;11.2 jel;34.1 jel;25.0 jel;19.6 jel;12.7 jel;24.0 jel;25.7 jel;16.6 jel;24.7 jel;5.7 jel;21.4 jel;9.6 jel;14.6 jel;23.1 jel;21.0 jel;16.3 jel;22.7 jel;12.2 jel;13.8 jel;28.7 jel;23.8 jel;24.5 jel;14.4 jel;18.5 jel;2.4 jel;10.5 jel;10.2 jel;13.9 jel;10.4 jel;20.7 jel;16.9 jel;19.9 jel;11.2 jel;20.1 jel;18.1 jel;12.0 jel;12.9 jel;15.9 jel;14.6 jel;26.7 jel;24.3 jel;32.8 jel;29.7 jel;14.9 jel;32.0 jel;10.7 jel;18.0 jel;20.2 jel;27.1 jel;12.0 jel;23.1 jel;12.2 jel;15.7 jel;16.0 jel;2.7 jel;31.2 jel;9.5 jel;27.6 jel;17.7 jel;12.5 jel;18.0 jel;7.3 jel;28.8 jel;13.4 jel;5.4 jel;19.7 jel;17.2 jel;11.5 jel;17.2 jel;10.9 jel;10.5 jel;15.3 jel;7.8 jel;16.3 jel;11.2 jel;17.8 jel;18.4 jel;18.0 jel;10.2 jel;18.2 jel;12.3 jel;35.3 jel;20.5 jel;11.1 jel;25.2 jel;16.0 jel;22.3 jel;5.4 jel;32.0 jel;20.8 jel;21.3 jel;13.7 jel;21.9 jel;11.5 jel;6.7 jel;18.2 jel;11.3 jel;33.0 jel;9.5 jel;5.3 jel;10.8 jel;15.7 jel;18.3 jel;22.0 jel;16.6 jel;21.5 jel;16.2 jel;29.4 jel;19.6 jel;29.6 jel;24.3 jel;11.3 jel;28.4 jel;17.8 jel;10.9 jel;20.7 jel;16.1 jel;14.3 jel;20.3 jel;16.8 jel;19.5 jel;22.9 jel;20.2 jel;11.0 jel;13.4 jel;16.6 jel;12.0 jel;11.9 jel;15.0 jel;16.3 jel;13.8 jel;22.4 jel;22.5 jel;20.0 jel;18.3 jel;13.9 jel;22.0 jel;16.1 jel;16.1 jel;18.4 jel;9.0 jel;20.1 jel;12.4 jel;22.1 jel;18.4 jel;24.3 jel;18.1 jel;21.2 jel;11.3 jel;14.7 jel;31.5 jel;10.5 jel;22.1 jel;16.7 jel;17.6 jel;18.9 jel;19.5 jel;14.0 jel;18.0 jel;20.3 jel;22.7 jel;25.9 jel;20.7 jel;26.7 jel;20.3 jel;17.6 jel;3.7 jel;22.7 jel;18.2 jel;24.6 jel;21.6 jel;24.8 jel;29.6 jel;17.5 jel;17.2 jel;19.1 jel;11.5 jel;8.0 jel;14.8 jel;18.3 jel;21.8 jel;19.9 jel;11.4 jel;21.8 jel;29.7 jel;7.7 jel;18.9 jel;18.4 jel;6.1 jel;25.5 jel;24.3 jel;21.7 jel;18.6 jel;18.9 jel;8.7 jel;17.7 jel;25.3 jel;17.3 jel;18.6 jel;22.3 jel;22.4 jel;21.3 jel;16.4 jel;10.6 jel;23.8 jel;27.1 jel;8.7 jel;25.9 jel;18.1 jel;19.6 jel;13.9 jel;25.6 jel;16.6 jel;14.4 jel;9.7 jel;16.0 jel;18.6 jel;24.4 jel;15.9 jel;24.1 jel;21.4 jel;20.7 jel;19.8 jel;23.5 jel;12.8 jel;16.0 jel;12.8 jel;17.1 jel;17.1 jel;16.1 jel;20.0 jel;27.0 jel;10.3 jel;15.2 jel;14.5 jel;22.1 jel;18.3 jel;19.5 jel;13.4 jel;11.6 jel;18.2 jel;29.4 jel;10.6 jel;21.1 jel;17.5 jel;16.2 jel;36.7 jel;24.0 jel;28.3 jel;18.7 jel;19.1 jel;24.5 jel;13.1 jel;25.5 jel;21.5 jel;23.0 jel;22.0 jel;18.5 jel;20.5 jel;21.9 jel;18.9 jel;31.3 jel;25.5 jel;6.5 jel;26.4 jel;0.4 jel;36.5 jel;17.8 jel;14.5 jel;21.0 jel;38.3 jel;24.1 jel;22.9 jel;5.6 jel;19.6 jel;11.2 jel;13.4 jel;34.2 jel;5.2 jel;23.4 jel;25.0 jel;15.8 jel;-2.8 jel;12.1 jel;16.8 jel;12.7 jel;22.2 jel;20.1 jel;11.2 jel;12.8 jel;16.6 jel;18.0 jel;35.3 jel;23.1 jel;12.6 jel;27.4 jel;9.5 jel;16.0 jel;7.0 jel;11.4 jel;11.5 jel;19.6 jel;16.7 jel;15.0 jel;32.0 jel;19.3 jel;14.2 jel;13.1 jel;19.3 jel;18.7 jel;19.7 jel;16.0 jel;16.0 jel;30.1 jel;16.7 jel;21.0 jel;21.1 jel;5.8 jel;20.2 jel;24.3 jel;11.7 jel;-1.8 jel;20.9 jel;14.0 jel;21.4 jel;13.1 jel;10.5 jel;18.3 jel;13.9 jel;22.6 jel;18.1 jel;22.0 jel;23.9 jel;29.3 jel;16.7 jel;23.3 jel;22.2 jel;15.3 jel;21.3 jel;22.5 jel;16.0 jel;14.0 jel;10.2 jel;32.6 jel;13.4 jel;7.9 jel;11.3 jel;17.1 jel;21.8 jel;16.2 jel;25.5 jel;3.0 jel;24.4 jel;15.1 jel;11.2 jel;23.7 jel;13.6 jel;16.6 jel;18.6 jel;19.4 jel;14.7 jel;18.6 jel;15.0 jel;18.0 jel;22.8 jel;20.7 jel;29.7 jel;8.7 jel;20.5 jel;13.3 jel;17.0 jel;18.5 jel;14.7 jel;12.4 jel;11.9 jel;14.6 jel;11.2 jel;24.1 jel;17.8 jel;13.4 jel;22.0 jel;15.2 jel;21.2 jel;14.8 jel;7.2 jel;19.8 jel;20.5 jel;16.6 jel;21.9 jel;20.6 jel;12.7 jel;15.7 jel;7.9 jel;21.7 jel;24.0 jel;27.5 jel;21.4 jel;24.6 jel;12.6 jel;4.2 jel;19.0 jel;4.2 jel;3.7 jel;27.4 jel;4.5 jel;15.5 jel;21.1 jel;16.4 jel;13.7 jel;12.6 jel;31.5 jel;18.6 jel;25.2 jel;8.8 jel;12.1 jel;23.1 jel;21.5 jel;7.2 jel;14.6 jel;16.8 jel;7.7 jel;21.4 jel;3.6 jel;13.8 jel;7.6 jel;17.2 jel;29.7 jel;19.6 jel;8.9 jel;5.1 jel;21.0 jel;17.7 jel;28.6 jel;25.9 jel;6.3 jel;6.9 jel;7.0 jel;24.2 jel;9.8 jel;18.8 jel;13.8 jel;11.9 jel;12.9 jel;22.0 jel;14.8 jel;15.0 jel;21.5 jel;20.4 jel;24.6 jel;24.6 jel;35.5 jel;19.1 jel;26.7 jel;28.1 jel;28.4 jel;21.4 jel;15.4 jel;24.9 jel;10.0 jel;15.5 jel;3.9 jel;15.3 jel;29.3 jel;16.4 jel;27.3 jel;11.1 jel;17.8 jel;24.3 jel;14.7 jel;11.8 jel;29.0 jel;20.8 jel;15.6 jel;22.0 jel;19.7 jel;18.9 jel;16.6 jel;19.1 jel;22.9 jel;19.1 jel;5.7 jel;26.5 jel;37.3 jel;42.4 jel;20.9 jel;11.8 jel;25.6 jel;26.5 jel;16.4 jel;34.8 jel;14.5 jel;26.6 jel;21.7 jel;36.0 jel;22.6 jel;18.5 jel;5.6 jel;24.0 jel;14.7 jel;9.9 jel;24.8 jel;24.6 jel;18.1 jel;14.6 jel;20.4 jel;28.8 jel;7.9 jel;9.6 jel;11.7 jel;17.9 jel;15.2 jel;13.9 jel;21.6 jel;24.9 jel;10.6 jel;7.6 jel;13.6 jel;11.2 jel;22.9 jel;15.5 jel;15.1 jel;16.1 jel;13.9 jel;15.7 jel;14.1 jel;22.6 jel;13.6 jel;18.4 jel;22.3 jel;18.5 jel;13.9 jel;16.8 jel;28.4 jel;27.2 jel;10.1 jel;11.1 jel;16.4 jel;14.2 jel;32.9 jel;11.3 jel;21.2 jel;16.6 jel;11.6 jel;20.4 jel;15.9 jel;10.7 jel;7.6 jel;19.3 jel;28.1 jel;22.7 jel;28.3 jel;9.5 jel;8.0 jel;21.1 jel;3.7 jel;18.4 jel;11.6 jel;22.4 jel;18.9 jel;24.2 jel;17.8 jel;25.0 jel;23.2 jel;17.0 jel;12.8 jel;17.2 jel;7.6 jel;20.4 jel;18.3 jel;21.6 jel;22.0 jel;22.3 jel;18.6 jel;18.0 jel;19.9 jel;12.5 jel;11.8 jel;19.7 jel;12.9 jel;9.3 jel;6.4 jel;34.0 jel;17.1 jel;26.8 jel;28.4 jel;9.6 jel;15.1 jel;5.9 jel;34.5 jel;17.4 jel;17.2 jel;7.4 jel;29.4 jel;29.4 jel;21.8 jel;19.1 jel;13.7 jel;11.1 jel;20.9 jel;22.1 jel;12.2 jel;14.6 jel;31.1 jel;21.7 jel;20.3 jel;37.0 jel;2.8 jel;26.3 jel;17.6 jel;35.3 jel;21.4 jel;18.9 jel;5.6 jel;13.2 jel;0.9 jel;18.2 jel;16.9 jel;13.1 jel;22.3 jel;9.3 jel;21.7 jel;27.1 jel;10.4 jel;20.0 jel;16.7 jel;14.5 jel;9.8 jel;16.1 jel;23.0 jel;19.2 jel;21.3 jel;15.0 jel;20.2 jel;10.7 jel;13.5 jel;21.6 jel;22.7 jel;20.3 jel;16.3 jel;4.0 jel;22.6 jel;0.4 jel;18.6 jel;25.3 jel;30.6 jel;20.9 jel;19.1 jel;26.3 jel;21.0 jel;25.6 jel;27.4 jel;14.1 jel;10.4 jel;12.4 jel;20.3 jel;16.7 jel;16.8 jel;5.7 jel;22.6 jel;14.6 jel;25.6 jel;24.1 jel;20.2 jel;20.0 jel;16.6 jel;12.0 jel;17.4 jel;18.6 jel;21.3 jel;24.2 jel;18.8 jel;13.2 jel;17.9 jel;13.1 jel;30.7 jel;8.9 jel;18.6 jel;24.1 jel;14.4 jel;18.4 jel;24.3 jel;19.3 jel;22.6 jel;25.6 jel;18.6 jel;6.7 jel;13.7 jel;11.7 jel;6.6 jel;21.3 jel;22.8 jel;17.6 jel;14.3 jel;28.3 jel;20.2 jel;29.2 jel;16.4 jel;15.8 jel;11.1 jel;15.9 jel;14.5 jel;21.4 jel;19.1 jel;14.5 jel;12.4 jel;21.4 jel;21.2 jel;17.2 jel;16.3 jel;17.3 jel;27.2 jel;18.4 jel;11.3 jel;6.0 jel;32.3 jel;20.6 jel;27.1 jel;8.4 jel;26.4 jel;24.7 jel;16.7 jel;11.8 jel;17.7 jel;22.4 jel;16.7 jel;21.7 jel;17.9 jel;16.8 jel;29.3 jel;26.4 jel;10.9 jel;31.0 jel;19.7 jel;17.4 jel;13.6 jel;25.6 jel;18.0 jel;15.1 jel;11.7 jel;19.7 jel;23.9 jel;18.6 jel;17.7 jel;20.8 jel;14.9 jel;18.8 jel;19.7 jel;24.2 jel;23.4 jel;2.2 jel;7.3 jel;18.3 jel;17.6 jel;16.3 jel;19.5 jel;16.9 jel;17.6 jel;13.3 jel;10.2 jel;7.9 jel;16.0 jel;14.2 jel;12.7 jel;20.6 jel;14.6 jel;30.8 jel;26.9 jel;16.2 jel;8.4 jel;19.1 jel;21.9 jel;31.1 jel;23.5 jel;14.4 jel;22.4 jel;26.3 jel;12.6 jel;18.8 jel;20.8 jel;19.8 jel;13.8 jel;5.7 jel;12.9 jel;10.2 jel;8.8 jel;13.1 jel;10.0 jel;18.1 jel;19.3 jel;13.1 jel;17.7 jel;13.4 jel;0.3 jel;23.3 jel;23.0 jel;26.0 jel;13.0 jel;19.5 jel;24.6 jel;11.4 jel;22.3 jel;14.5 jel;21.3 jel;12.5 jel;17.1 jel;10.2 jel;23.8 jel;16.9 jel;15.7 jel;7.0 jel;31.9 jel;14.2 jel;18.2 jel;9.7 jel;13.7 jel;8.6 jel;22.7 jel;15.7 jel;20.8 jel;30.7 jel;29.6 jel;1.0 jel;14.7 jel;8.9 jel;23.8 jel;12.7 jel;19.2 jel;25.7 jel;16.5 jel;23.7 jel;16.8 jel;18.7 jel;28.4 jel;12.8 jel;24.3 jel;13.2 jel;27.7 jel;17.0 jel;18.8 jel;19.7 jel;11.8 jel;9.3 jel;23.0 jel;25.1 jel;11.8 jel;8.0 jel;10.4 jel;21.8 jel;23.6 jel;18.7 jel;7.6 jel;26.1 jel;7.8 jel;16.5 jel;1.9 jel;14.6 jel;11.3 jel;23.8 jel;16.1 jel;7.1 jel;9.1 jel;15.6 jel;26.8 jel;13.6 jel;6.3 jel;23.2 jel;19.7 jel;26.1 jel;10.5 jel;20.9 jel;18.9 jel;26.2 jel;11.2 jel;14.5 jel;15.2 jel;17.1 jel;11.9 jel;13.3 jel;5.3 jel;21.7 jel;29.2 jel;12.3 jel;18.5 jel;10.0 jel;8.2 jel;27.6 jel;22.1 jel;22.7 jel;24.2 jel;4.3 jel;20.1 jel;13.3 jel;17.9 jel;21.1 jel;14.0 jel;19.4 jel;18.1 jel;13.6 jel;17.1 jel;17.4 jel;21.2 jel;8.8 jel;16.6 jel;36.5 jel;21.1 jel;28.5 jel;20.1 jel;27.3 jel;18.2 jel;17.3 jel;10.6 jel;20.2 jel;8.2 jel;24.7 jel;17.4 jel;23.4 jel;12.7 jel;10.8 jel;14.2 jel;21.0 jel;11.5 jel;11.8 jel;28.5 jel;-0.4 jel;20.2 jel;14.2 jel;13.9 jel;27.3 jel;16.6 jel;23.4 jel;23.5 jel;16.6 jel;15.4 jel;19.3 jel;17.4 jel;23.8 jel;26.4 jel;27.6 jel;5.1 jel;23.4 jel;17.9 jel;29.2 jel;26.7 jel;10.6 jel;19.9 jel;22.8 jel;29.0 jel;9.7 jel;24.8 jel;34.4 jel;18.8 jel;7.8 jel;21.1 jel;19.0 jel;14.9 jel;15.0 jel;17.3 jel;16.0 jel;18.5 jel;19.6 jel;10.8 jel;13.5 jel;23.6 jel;41.1 jel;-1.3 jel;16.6 jel;1.9 jel;13.6 jel;17.4 jel;22.0 jel;18.3 jel;6.6 jel;18.6 jel;20.9 jel;10.6 jel;21.9 jel;22.1 jel;19.7 jel;19.3 jel;13.2 jel;31.1 jel;26.9 jel;27.5 jel;10.4 jel;21.6 jel;25.5 jel;25.6 jel;16.3 jel;9.9 jel;7.6 jel;25.0 jel;23.1 jel;24.0 jel;36.1 jel;9.2 jel;24.3 jel;15.8 jel;15.9 jel;31.4 jel;28.5 jel;21.3 jel;3.2 jel;21.7 jel;22.1 jel;19.6 jel;17.9 jel;11.8 jel;35.6 jel;24.1 jel;15.1 jel;20.6 jel;17.1 jel;12.7 jel;24.9 jel;20.0 jel;17.3 jel;10.8 jel;27.5 jel;10.8 jel;11.9 jel;26.9 jel;14.0 jel;28.4 jel;18.1 jel;13.4 jel;16.6 jel;17.5 jel;16.5 jel;12.7 jel;11.9 jel;23.2 jel;12.9 jel;10.6 jel;25.6 jel;24.2 jel;19.1 jel;12.8 jel;11.9 jel;15.7 jel;15.8 jel;34.6 jel;17.0 jel;20.4 jel;23.1 jel;24.5 jel;28.7 jel;20.8 jel;17.6 jel;17.4 jel;14.4 jel;21.7 jel;17.6 jel;11.5 jel;17.9 jel;33.0 jel;12.0 jel;17.1 jel;9.1 jel;24.5 jel;20.2 jel;21.5 jel;4.6 jel;22.5 jel;12.9 jel;16.6 jel;11.9 jel;18.8 jel;18.2 jel;11.2 jel;13.2 jel;0.3 jel;12.0 jel;10.7 jel;10.2 jel;15.3 jel;19.8 jel;16.4 jel;23.6 jel;5.4 jel;8.0 jel;16.4 jel;5.6 jel;20.0 jel;22.7 jel;25.6 jel;19.4 jel;28.2 jel;17.3 jel;29.5 jel;26.2 jel;18.1 jel;12.4 jel;28.9 jel;10.7 jel;23.2 jel;14.9 jel;25.2 jel;17.6 jel;12.3 jel;12.5 jel;7.9 jel;34.9 jel;20.1 jel;16.7 jel;23.0 jel;23.2 jel;21.2 jel;23.2 jel;19.7 jel;4.6 jel;17.8 jel;25.1 jel;12.3 jel;11.6 jel;21.0 jel;28.1 jel;26.9 jel;9.1 jel;14.2 jel;13.9 jel;9.7 jel;16.8 jel;29.4 jel;13.5 jel;23.2 jel;22.6 jel;3.2 jel;15.8 jel;7.9 jel;24.5 jel;13.2 jel;0.7 jel;30.3 jel;4.8 jel;16.5 jel;16.5 jel;26.6 jel;14.1 jel;17.6 jel;17.3 jel;17.1 jel;10.0 jel;15.7 jel;13.1 jel;19.1 jel;30.6 jel;23.0 jel;11.0 jel;18.9 jel;25.5 jel;10.1 jel;21.2 jel;15.3 jel;12.3 jel;21.8 jel;22.0 jel;17.6 jel;26.5 jel;20.2 jel;9.9 jel;15.3 jel;13.7 jel;5.0 jel;10.8 jel;12.5 jel;29.7 jel;23.7 jel;17.4 jel;16.1 jel;15.6 jel;13.8 jel;21.3 jel;25.8 jel;6.1 jel;14.5 jel;15.7 jel;33.9 jel;13.3 jel;13.9 jel;11.5 jel;13.6 jel;21.7 jel;32.3 jel;21.4 jel;15.0 jel;6.2 jel;24.8 jel;16.5 jel;22.3 jel;20.2 jel;12.5 jel;10.2 jel;16.8 jel;16.0 jel;25.8 jel;11.4 jel;14.4 jel;9.8 jel;17.8 jel;21.7 jel;20.7 jel;33.6 jel;18.9 jel;16.3 jel;27.9 jel;19.1 jel;26.0 jel;33.8 jel;20.8 jel;19.0 jel;16.1 jel;9.9 jel;16.1 jel;6.0 jel;8.1 jel;11.3 jel;5.4 jel;10.5 jel;18.7 jel;13.3 jel;15.9 jel;26.0 jel;18.5 jel;19.1 jel;28.3 jel;17.7 jel;27.0 jel;11.2 jel;14.6 jel;16.3 jel;19.3 jel;11.5 jel;20.4 jel;7.3 jel;18.9 jel;22.1 jel;17.0 jel;9.3 jel;19.7 jel;18.0 jel;25.2 jel;19.9 jel;30.2 jel;8.5 jel;7.3 jel;7.8 jel;18.0 jel;30.2 jel;5.1 jel;19.5 jel;27.1 jel;11.8 jel;20.8 jel;19.1 jel;22.4 jel;27.9 jel;19.3 jel;4.1 jel;18.5 jel;2.4 jel;15.9 jel;8.2 jel;11.2 jel;18.9 jel;24.2 jel;29.2 jel;19.9 jel;14.6 jel;24.4 jel;23.8 jel;19.4 jel;14.2 jel;26.3 jel;20.5 jel;17.7 jel;25.1 jel;21.5 jel;20.1 jel;18.7 jel;10.6 jel;15.6 jel;26.1 jel;24.0 jel;23.8 jel;13.3 jel;23.9 jel;21.4 jel;18.1 jel;23.5 jel;22.5 jel;14.6 jel;19.5 jel;16.7 jel;6.2 jel;9.0 jel;19.7 jel;17.9 jel;19.9 jel;27.1 jel;16.1 jel;17.6 jel;12.7 jel;19.0 jel;3.3 jel;10.0 jel;14.4 jel;20.8 jel;24.3 jel;16.6 jel;11.3 jel;25.2 jel;18.3 jel;2.8 jel;26.0 jel;17.8 jel;13.9 jel;25.7 jel;7.8 jel;16.3 jel;13.6 jel;18.6 jel;15.7 jel;11.2 jel;21.6 jel;17.2 jel;19.7 jel;15.7 jel;9.2 jel;8.4 jel;10.7 jel;20.1 jel;18.0 jel;9.8 jel;23.4 jel;23.8 jel;13.8 jel;18.9 jel;25.8 jel;15.1 jel;33.4 jel;11.0 jel;19.7 jel;16.0 jel;16.1 jel;29.4 jel;19.9 jel;19.8 jel;16.3 jel;8.4 jel;13.4 jel;16.9 jel;19.6 jel;12.0 jel;6.9 jel;15.7 jel;20.2 jel;17.7 jel;28.6 jel;17.6 jel;16.6 jel;16.4 jel;12.3 jel;13.7 jel;15.4 jel;21.4 jel;19.5 jel;18.2 jel;32.5 jel;15.7 jel;20.4 jel;19.8 jel;20.7 jel;13.6 jel;19.8 jel;12.8 jel;13.7 jel;24.8 jel;4.7 jel;11.4 jel;22.3 jel;10.5 jel;8.7 jel;6.4 jel;17.0 jel;17.3 jel;28.5 jel;13.9 jel;9.4 jel;23.0 jel;14.2 jel;12.9 jel;22.3 jel;26.7 jel;17.2 jel;14.2 jel;9.5 jel;24.1 jel;19.4 jel;22.7 jel;11.2 jel;10.3 jel;13.2 jel;13.1 jel;10.5 jel;12.9 jel;24.6 jel;11.4 jel;18.8 jel;17.4 jel;23.8 jel;9.6 jel;17.4 jel;24.4 jel;6.8 jel;22.2 jel;14.6 jel;16.9 jel;17.4 jel;17.9 jel;14.7 jel;17.3 jel;20.0 jel;11.3 jel;28.5 jel;21.7 jel;8.4 jel;15.9 jel;18.4 jel;18.3 jel;10.5 jel;25.9 jel;24.9 jel;18.8 jel;35.2 jel;21.0 jel;20.8 jel;20.2 jel;17.2 jel;26.0 jel;13.6 jel;22.5 jel;14.0 jel;25.3 jel;7.5 jel;8.1 jel;18.2 jel;7.4 jel;17.8 jel;4.6 jel;19.9 jel;13.2 jel;11.0 jel;28.2 jel;8.4 jel;11.7 jel;20.7 jel;16.6 jel;7.4 jel;26.0 jel;19.4 jel;14.3 jel;12.6 jel;20.0 jel;22.0 jel;15.5 jel;22.5 jel;22.3 jel;20.2 jel;22.0 jel;14.2 jel;10.4 jel;14.5 jel;22.9 jel;18.7 jel;18.7 jel;15.4 jel;9.6 jel;10.8 jel;24.8 jel;14.7 jel;25.4 jel;12.1 jel;19.5 jel;10.0 jel;10.5 jel;23.3 jel;24.1 jel;16.5 jel;23.9 jel;32.6 jel;5.6 jel;19.4 jel;23.3 jel;22.0 jel;22.5 jel;7.1 jel;9.5 jel;19.7 jel;23.3 jel;8.6 jel;12.9 jel;7.5 jel;22.9 jel;23.7 jel;12.6 jel;7.6 jel;17.9 jel;10.3 jel;14.5 jel;21.5 jel;26.9 jel;17.3 jel;19.2 jel;19.1 jel;12.0 jel;8.1 jel;22.9 jel;8.9 jel;15.0 jel;19.7 jel;20.0 jel;20.2 jel;3.3 jel;12.3 jel;11.4 jel;28.8 jel;10.3 jel;15.7 jel;21.1 jel;20.3 jel;18.8 jel;7.6 jel;6.2 jel;21.7 jel;19.8 jel;19.0 jel;20.9 jel;10.4 jel;11.7 jel;14.2 jel;16.7 jel;9.2 jel;21.7 jel;30.9 jel;11.8 jel;17.6 jel;26.0 jel;25.9 jel;24.7 jel;4.5 jel;17.9 jel;20.0 jel;15.4 jel;16.8 jel;10.5 jel;6.8 jel;27.2 jel;15.2 jel;19.9 jel;18.0 jel;25.9 jel;12.3 jel;7.4 jel;18.7 jel;23.6 jel;25.1 jel;20.1 jel;17.9 jel;24.6 jel;31.4 jel;25.4 jel;29.7 jel;27.1 jel;18.6 jel;10.3 jel;11.1 jel;9.7 jel;23.7 jel;8.2 jel;26.4 jel;5.2 jel;20.5 jel;7.9 jel;13.6 jel;34.4 jel;13.0 jel;11.4 jel;24.0 jel;18.9 jel;5.4 jel;13.4 jel;20.0 jel;16.1 jel;14.6 jel;27.0 jel;24.6 jel;5.0 jel;15.4 jel;11.9 jel;11.4 jel;18.7 jel;6.2 jel;19.9 jel;19.0 jel;30.5 jel;17.1 jel;4.1 jel;22.0 jel;24.9 jel;9.9 jel;15.9 jel;13.7 jel;20.5 jel;29.4 jel;18.0 jel;24.7 jel;19.5 jel;10.7 jel;17.8 jel;5.2 jel;25.3 jel;15.4 jel;7.8 jel;32.5 jel;9.0 jel;21.3 jel;20.6 jel;17.1 jel;18.6 jel;19.1 jel;18.4 jel;20.7 jel;10.8 jel;12.2 jel;11.5 jel;12.0 jel;9.4 jel;22.6 jel;24.5 jel;17.1 jel;17.2 jel;15.8 jel;22.0 jel;8.8 jel;18.7 jel;16.4 jel;21.0 jel;26.0 jel;23.2 jel;15.8 jel;12.7 jel;20.6 jel;4.4 jel;15.1 jel;21.3 jel;0.8 jel;20.8 jel;27.5 jel;20.9 jel;18.4 jel;12.1 jel;14.5 jel;10.0 jel;22.5 jel;7.2 jel;12.7 jel;12.8 jel;30.4 jel;19.6 jel;25.8 jel;25.1 jel;6.0 jel;16.9 jel;23.3 jel;23.1 jel;24.1 jel;15.4 jel;19.3 jel;21.3 jel;13.6 jel;2.3 jel;26.1 jel;13.5 jel;14.4 jel;8.0 jel;4.7 jel;18.3 jel;23.8 jel;22.6 jel;24.0 jel;18.5 jel;18.5 jel;17.0 jel;20.6 jel;16.7 jel;12.2 jel;-2.5 jel;20.6 jel;21.9 jel;26.4 jel;5.6 jel;25.4 jel;25.7 jel;18.4 jel;17.3 jel;26.4 jel;29.1 jel;25.7 jel;16.8 jel;11.2 jel;15.8 jel;29.3 jel;24.3 jel;30.6 jel;16.2 jel;18.2 jel;24.5 jel;17.5 jel;30.2 jel;29.0 jel;13.2 jel;30.4 jel;14.1 jel;26.2 jel;9.6 jel;18.1 jel;23.7 jel;10.3 jel;21.3 jel;21.1 jel;28.3 jel;23.1 jel;4.0 jel;15.7 jel;14.2 jel;18.9 jel;24.8 jel;16.4 jel;14.2 jel;15.6 jel;21.8 jel;14.4 jel;25.8 jel;18.5 jel;23.9 jel;24.3 jel;15.9 jel;16.7 jel;30.4 jel;19.3 jel;25.3 jel;16.6 jel;10.2 jel;20.1 jel;13.2 jel;9.6 jel;26.6 jel;12.3 jel;17.0 jel;22.9 jel;26.1 jel;33.4 jel;20.3 jel;10.5 jel;15.4 jel;14.1 jel;28.2 jel;12.7 jel;21.7 jel;17.0 jel;17.7 jel;17.5 jel;14.3 jel;12.2 jel;18.9 jel;19.6 jel;13.0 jel;23.5 jel;28.2 jel;28.3 jel;28.1 jel;19.8 jel;18.9 jel;14.4 jel;26.9 jel;18.6 jel;10.2 jel;21.8 jel;18.8 jel;19.7 jel;14.7 jel;13.4 jel;-0.6 jel;12.9 jel;2.7 jel;24.6 jel;24.9 jel;25.6 jel;12.4 jel;1.0 jel;25.3 jel;17.5 jel;23.2 jel;18.9 jel;27.2 jel;15.8 jel;24.4 jel;22.8 jel;11.6 jel;14.9 jel;23.4 jel;17.4 jel;13.1 jel;2.5 jel;22.0 jel;26.9 jel;24.9 jel;12.5 jel;15.8 jel;15.4 jel;22.3 jel;28.3 jel;20.8 jel;19.8 jel;17.7 jel;18.5 jel;12.4 jel;19.4 jel;18.7 jel;17.4 jel;6.6 jel;13.5 jel;12.8 jel;7.7 jel;3.9 jel;5.6 jel;4.8 jel;11.9 jel;18.3 jel;17.7 jel;16.6 jel;20.2 jel;12.3 jel;22.0 jel;10.5 jel;18.0 jel;8.2 jel;8.3 jel;22.1 jel;14.3 jel;15.6 jel;22.4 jel;14.2 jel;22.4 jel;27.7 jel;20.2 jel;23.1 jel;16.5 jel;6.5 jel;-0.4 jel;22.1 jel;10.1 jel;18.2 jel;16.7 jel;13.9 jel;13.6 jel;16.6 jel;8.5 jel;21.0 jel;23.8 jel;15.7 jel;22.7 jel;13.1 jel;18.6 jel;5.6 jel;20.2 jel;7.2 jel;13.6 jel;16.9 jel;24.2 jel;11.7 jel;26.8 jel;14.2 jel;19.2 jel;19.0 jel;8.5 jel;14.4 jel;10.7 jel;11.2 jel;22.0 jel;23.5 jel;26.7 jel;13.7 jel;17.2 jel;15.6 jel;32.3 jel;24.3 jel;13.0 jel;22.4 jel;22.4 jel;27.2 jel;25.4 jel;21.0 jel;14.2 jel;12.8 jel;13.7 jel;16.6 jel;27.0 jel;16.1 jel;19.1 jel;40.6 jel;34.6 jel;30.6 jel;24.4 jel;22.0 jel;17.6 jel;12.4 jel;29.9 jel;27.8 jel;15.2 jel;18.9 jel;18.0 jel;18.6 jel;19.8 jel;12.4 jel;11.8 jel;19.9 jel;20.0 jel;23.7 jel;21.8 jel;17.3 jel;15.4 jel;15.2 jel;20.8 jel;13.0 jel;8.7 jel;21.3 jel;9.5 jel;24.8 jel;23.5 jel;11.9 jel;24.4 jel;-0.9 jel;1.2 jel;8.7 jel;6.7 jel;14.5 jel;17.9 jel;32.3 jel;6.5 jel;12.9 jel;25.2 jel;11.6 jel;26.5 jel;9.9 jel;26.4 jel;10.0 jel;26.0 jel;19.0 jel;12.0 jel;29.0 jel;11.0 jel;9.6 jel;30.4 jel;2.3 jel;19.3 jel;9.5 jel;27.2 jel;28.6 jel;11.3 jel;17.0 jel;13.9 jel;26.1 jel;9.7 jel;19.0 jel;15.0 jel;13.5 jel;22.6 jel;7.6 jel;11.6 jel;7.2 jel;15.9 jel;22.0 jel;4.3 jel;23.7 jel;19.3 jel;18.6 jel;11.6 jel;28.1 jel;11.9 jel;18.7 jel;27.7 jel;15.6 jel;10.0 jel;13.2 jel;12.4 jel;29.1 jel;12.1 jel;23.8 jel;21.0 jel;16.2 jel;23.2 jel;10.8 jel;19.4 jel;15.3 jel;4.5 jel;24.7 jel;20.9 jel;26.5 jel;21.9 jel;22.8 jel;22.3 jel;30.1 jel;8.6 jel;21.3 jel;20.1 jel;30.0 jel;24.3 jel;10.0 jel;1.7 jel;18.4 jel;16.2 jel;19.7 jel;4.4 jel;18.9 jel;28.7 jel;8.7 jel;25.3 jel;7.4 jel;12.2 jel;28.3 jel;33.1 jel;15.3 jel;11.3 jel;19.7 jel;29.5 jel;14.1 jel;21.4 jel;11.8 jel;28.1 jel;19.1 jel;14.9 jel;26.9 jel;23.8 jel;13.7 jel;19.4 jel;14.4 jel;16.7 jel;13.4 jel;8.3 jel;14.6 jel;19.7 jel;33.3 jel;17.2 jel;10.0 jel;21.9 jel;9.7 jel;9.9 jel;14.5 jel;11.4 jel;15.0 jel;9.1 jel;1.2 jel;18.3 jel;19.1 jel;15.2 jel;23.6 jel;18.2 jel;24.6 jel;11.1 jel;1.2 jel;19.7 jel;20.4 jel;6.5 jel;25.9 jel;11.7 jel;13.9 jel;18.9 jel;10.3 jel;14.2 jel;8.5 jel;11.6 jel;11.4 jel;9.4 jel;14.9 jel;5.9 jel;12.8 jel;20.0 jel;18.7 jel;22.5 jel;6.4 jel;21.4 jel;16.5 jel;6.9 jel;24.5 jel;17.1 jel;24.7 jel;11.1 jel;10.9 jel;34.6 jel;11.0 jel;16.6 jel;23.7 jel;19.7 jel;13.7 jel;25.2 jel;18.0 jel;17.8 jel;15.1 jel;10.5 jel;23.7 jel;26.5 jel;5.3 jel;9.8 jel;13.4 jel;13.6 jel;15.3 jel;19.1 jel;30.4 jel;26.1 jel;12.3 jel;10.7 jel;14.3 jel;11.7 jel;17.4 jel;18.2 jel;9.9 jel;28.2 jel;21.5 jel;16.9 jel;26.7 jel;23.8 jel;16.8 jel;7.0 jel;23.4 jel;13.8 jel;11.1 jel;12.4 jel;3.4 jel;13.1 jel;34.4 jel;4.7 jel;22.4 jel;17.7 jel;21.0 jel;28.2 jel;19.3 jel;23.6 jel;19.5 jel;13.7 jel;18.5 jel;31.9 jel;22.3 jel;11.1 jel;6.9 jel;13.2 jel;24.9 jel;25.6 jel;9.6 jel;15.5 jel;23.1 jel;21.6 jel;12.6 jel;24.2 jel;18.7 jel;25.7 jel;20.9 jel;21.2 jel;31.9 jel;24.9 jel;11.9 jel;22.3 jel;11.9 jel;18.0 jel;24.2 jel;17.9 jel;25.0 jel;9.5 jel;19.7 jel;10.1 jel;8.3 jel;24.4 jel;21.1 jel;24.3 jel;19.6 jel;28.3 jel;19.6 jel;17.4 jel;21.6 jel;30.8 jel;17.6 jel;9.3 jel;16.1 jel;16.6 jel;17.0 jel;14.9 jel;12.8 jel;10.8 jel;6.9 jel;18.6 jel;13.7 jel;14.2 jel;24.0 jel;20.2 jel;17.8 jel;0.7 jel;28.8 jel;15.0 jel;29.2 jel;18.6 jel;19.2 jel;29.5 jel;23.8 jel;21.7 jel;11.6 jel;20.2 jel;14.5 jel;26.0 jel;9.9 jel;17.9 jel;26.2 jel;28.6 jel;19.8 jel;24.3 jel;13.6 jel;25.5 jel;17.5 jel;25.5 jel;12.7 jel;7.5 jel;3.7 jel;19.6 jel;33.0 jel;13.1 jel;17.6 jel;35.0 jel;13.9 jel;18.6 jel;18.6 jel;15.1 jel;17.5 jel;28.9 jel;16.2 jel;13.6 jel;14.4 jel;16.4 jel;17.8 jel;27.0 jel;22.7 jel;9.2 jel;12.5 jel;20.3 jel;28.4 jel;16.0 jel;18.9 jel;4.3 jel;15.0 jel;16.0 jel;14.2 jel;25.7 jel;21.0 jel;30.3 jel;17.8 jel;11.0 jel;5.8 jel;23.7 jel;18.2 jel;22.4 jel;4.0 jel;18.3 jel;29.8 jel;10.6 jel;11.6 jel;17.6 jel;23.2 jel;20.2 jel;19.9 jel;14.2 jel;20.0 jel;18.3 jel;23.1 jel;30.8 jel;24.1 jel;19.5 jel;7.1 jel;23.5 jel;15.2 jel;10.8 jel;19.9 jel;24.7 jel;23.0 jel;18.7 jel;29.4 jel;15.3 jel;26.4 jel;19.4 jel;7.3 jel;20.8 jel;26.4 jel;25.3 jel;19.4 jel;13.2 jel;18.4 jel;7.6 jel;18.8 jel;13.1 jel;22.0 jel;27.1 jel;18.7 jel;8.0 jel;6.4 jel;7.7 jel;24.5 jel;26.4 jel;19.2 jel;17.0 jel;6.1 jel;24.2 jel;23.2 jel;20.9 jel;18.8 jel;11.9 jel;20.9 jel;20.1 jel;29.3 jel;15.4 jel;20.6 jel;22.9 jel;20.1 jel;22.5 jel;24.6 jel;14.0 jel;16.5 jel;13.5 jel;20.4 jel;19.3 jel;20.1 jel;22.1 jel;8.7 jel;7.9 jel;16.5 jel;16.6 jel;26.2 jel;17.8 jel;22.9 jel;18.9 jel;22.1 jel;22.5 jel;21.0 jel;41.1 jel;17.1 jel;32.7 jel;24.4 jel;-1.5 jel;17.9 jel;14.6 jel;20.5 jel;11.0 jel;18.1 jel;20.3 jel;24.1 jel;24.5 jel;25.4 jel;10.5 jel;15.4 jel;15.1 jel;20.6 jel;15.5 jel;14.2 jel;19.7 jel;10.6 jel;17.5 jel;15.4 jel;3.3 jel;29.2 jel;23.8 jel;18.5 jel;9.1 jel;11.4 jel;19.7 jel;13.5 jel;16.9 jel;13.8 jel;11.8 jel;22.5 jel;18.3 jel;19.1 jel;17.0 jel;15.0 jel;24.5 jel;17.3 jel;15.8 jel;17.5 jel;21.3 jel;10.3 jel;26.3 jel;25.0 jel;23.7 jel;23.8 jel;10.2 jel;12.9 jel;26.3 jel;14.4 jel;14.1 jel;26.0 jel;20.6 jel;21.6 jel;16.7 jel;18.9 jel;6.6 jel;19.4 jel;14.6 jel;29.5 jel;13.7 jel;17.5 jel;16.5 jel;14.5 jel;12.0 jel;19.0 jel;23.4 jel;22.0 jel;11.0 jel;21.7 jel;16.5 jel;30.3 jel;24.2 jel;29.8 jel;16.0 jel;13.6 jel;10.9 jel;12.6 jel;6.3 jel;19.4 jel;19.9 jel;21.3 jel;26.1 jel;12.7 jel;22.0 jel;20.0 jel;5.9 jel;11.8 jel;22.1 jel;33.1 jel;18.3 jel;38.2 jel;12.4 jel;17.7 jel;19.2 jel;19.0 jel;24.4 jel;15.9 jel;31.1 jel;25.7 jel;24.6 jel;20.7 jel;6.5 jel;19.9 jel;12.9 jel;21.7 jel;8.7 jel;16.3 jel;18.7 jel;22.1 jel;16.3 jel;24.9 jel;18.7 jel;20.6 jel;3.2 jel;25.2 jel;21.2 jel;10.3 jel;16.7 jel;15.4 jel;28.1 jel;20.0 jel;24.3 jel;17.6 jel;23.3 jel;21.0 jel;18.0 jel;23.6 jel;25.5 jel;25.2 jel;32.4 jel;10.9 jel;21.2 jel;8.5 jel;25.7 jel;20.1 jel;8.7 jel;14.2 jel;26.3 jel;12.7 jel;18.0 jel;21.3 jel;18.4 jel;16.6 jel;20.0 jel;15.3 jel;25.0 jel;24.3 jel;33.3 jel;14.1 jel;21.9 jel;19.8 jel;12.4 jel;11.6 jel;20.4 jel;15.2 jel;17.4 jel;15.2 jel;20.8 jel;9.4 jel;9.0 jel;33.0 jel;1.9 jel;13.7 jel;22.4 jel;16.6 jel;9.8 jel;12.6 jel;17.1 jel;13.8 jel;14.5 jel;13.7 jel;21.2 jel;12.9 jel;7.8 jel;18.9 jel;2.2 jel;16.7 jel;8.7 jel;15.0 jel;-1.5 jel;11.5 jel;9.9 jel;19.0 jel;16.5 jel;12.5 jel;4.8 jel;11.8 jel;21.8 jel;10.8 jel;29.1 jel;12.4 jel;8.1 jel;13.7 jel;16.1 jel;20.2 jel;25.7 jel;7.8 jel;35.8 jel;30.5 jel;13.3 jel;25.3 jel;12.6 jel;11.5 jel;8.6 jel;8.3 jel;9.5 jel;8.9 jel;17.6 jel;16.2 jel;23.5 jel;20.3 jel;13.6 jel;13.9 jel;15.4 jel;17.4 jel;16.1 jel;13.9 jel;21.3 jel;32.9 jel;18.2 jel;20.4 jel;7.0 jel;16.9 jel;8.2 jel;9.9 jel;9.3 jel;29.3 jel;13.4 jel;23.0 jel;17.4 jel;26.8 jel;17.3 jel;24.7 jel;18.7 jel;7.8 jel;12.6 jel;18.1 jel;7.7 jel;8.6 jel;24.2 jel;3.9 jel;-4.1 jel;11.9 jel;10.9 jel;15.8 jel;12.7 jel;23.3 jel;20.8 jel;14.5 jel;15.4 jel;27.2 jel;28.1 jel;18.7 jel;25.7 jel;15.0 jel;28.5 jel;15.8 jel;20.5 jel;22.0 jel;14.2 jel;28.2 jel;21.2 jel;11.4 jel;10.8 jel;11.0 jel;26.7 jel;24.6 jel;18.0 jel;23.6 jel;26.0 jel;14.4 jel;11.3 jel;13.5 jel;17.5 jel;27.8 jel;11.8 jel;12.3 jel;24.2 jel;13.5 jel;15.7 jel;14.8 jel;30.0 jel;14.3 jel;6.5 jel;18.7 jel;25.5 jel;8.6 jel;22.2 jel;18.0 jel;24.9 jel;17.7 jel;19.9 jel;21.8 jel;16.6 jel;31.4 jel;26.7 jel;25.6 jel;9.2 jel;21.4 jel;22.2 jel;15.6 jel;15.8 jel;13.9 jel;14.8 jel;20.2 jel;19.3 jel;23.9 jel;12.3 jel;16.6 jel;11.3 jel;26.7 jel;19.5 jel;18.3 jel;13.6 jel;23.2 jel;20.0 jel;15.2 jel;14.0 jel;15.2 jel;26.5 jel;17.8 jel;14.8 jel;23.9 jel;17.0 jel;34.9 jel;9.4 jel;6.5 jel;17.4 jel;6.3 jel;21.3 jel;15.5 jel;7.8 jel;10.0 jel;22.5 jel;19.3 jel;25.3 jel;13.9 jel;33.6 jel;9.2 jel;8.9 jel;8.8 jel;7.7 jel;19.3 jel;21.5 jel;19.5 jel;17.4 jel;21.1 jel;25.7 jel;10.8 jel;15.5 jel;10.9 jel;15.4 jel;17.6 jel;21.9 jel;19.9 jel;17.5 jel;16.5 jel;26.5 jel;17.3 jel;18.1 jel;15.8 jel;27.2 jel;21.7 jel;13.9 jel;20.5 jel;14.7 jel;8.0 jel;24.0 jel;24.9 jel;18.2 jel;21.8 jel;22.2 jel;28.7 jel;13.1 jel;17.4 jel;19.6 jel;24.0 jel;11.5 jel;8.8 jel;30.8 jel;15.4 jel;17.2 jel;14.1 jel;9.8 jel;14.7 jel;14.2 jel;25.0 jel;11.4 jel;17.3 jel;16.0 jel;14.8 jel;21.1 jel;22.9 jel;10.5 jel;14.0 jel;12.0 jel;17.2 jel;9.6 jel;14.5 jel;5.1 jel;26.7 jel;7.0 jel;18.2 jel;13.4 jel;17.3 jel;8.6 jel;1.6 jel;17.7 jel;10.8 jel;12.9 jel;24.3 jel;26.2 jel;28.0 jel;24.1 jel;19.4 jel;20.7 jel;17.2 jel;12.6 jel;7.5 jel;21.8 jel;19.9 jel;13.7 jel;27.0 jel;16.7 jel;12.7 jel;12.8 jel;5.3 jel;20.1 jel;6.6 jel;7.4 jel;23.2 jel;20.6 jel;17.0 jel;25.4 jel;15.7 jel;11.3 jel;11.9 jel;21.1 jel;6.2 jel;17.0 jel;29.4 jel;23.1 jel;21.6 jel;2.0 jel;21.9 jel;10.6 jel;21.2 jel;15.2 jel;21.0 jel;15.4 jel;20.4 jel;11.5 jel;18.3 jel;22.6 jel;13.6 jel;15.4 jel;15.8 jel;12.5 jel;14.0 jel;21.9 jel;18.1 jel;26.2 jel;13.8 jel;10.4 jel;20.3 jel;27.9 jel;31.5 jel;17.6 jel;11.7 jel;6.8 jel;10.3 jel;21.0 jel;16.7 jel;15.0 jel;27.2 jel;9.4 jel;14.9 jel;8.4 jel;24.9 jel;6.6 jel;28.0 jel;13.4 jel;14.2 jel;17.9 jel;20.2 jel;20.3 jel;3.5 jel;24.8 jel;19.4 jel;17.4 jel;25.2 jel;16.2 jel;17.2 jel;16.8 jel;17.7 jel;13.4 jel;20.3 jel;16.6 jel;4.4 jel;15.1 jel;15.3 jel;13.7 jel;8.1 jel;26.2 jel;26.6 jel;22.8 jel;17.4 jel;13.3 jel;28.9 jel;17.8 jel;15.2 jel;27.4 jel;16.4 jel;28.3 jel;24.8 jel;20.1 jel;25.7 jel;12.8 jel;3.6 jel;21.0 jel;21.5 jel;7.6 jel;21.2 jel;24.4 jel;27.4 jel;11.5 jel;24.5 jel;13.1 jel;11.6 jel;16.0 jel;17.9 jel;18.7 jel;17.0 jel;14.4 jel;13.8 jel;10.1 jel;21.0 jel;28.8 jel;29.2 jel;17.1 jel;23.7 jel;18.1 jel;14.0 jel;21.9 jel;19.5 jel;20.4 jel;22.6 jel;3.2 jel;13.5 jel;21.2 jel;9.2 jel;26.8 jel;5.0 jel;22.2 jel;15.8 jel;11.5 jel;12.5 jel;19.3 jel;22.0 jel;20.2 jel;16.4 jel;7.6 jel;19.4 jel;16.8 jel;23.7 jel;14.3 jel;17.1 jel;14.8 jel;5.6 jel;1.3 jel;18.8 jel;2.4 jel;10.8 jel;7.1 jel;25.0 jel;13.7 jel;13.1 jel;15.2 jel;13.9 jel;19.8 jel;13.0 jel;11.9 jel;16.4 jel;19.8 jel;18.1 jel;27.5 jel;24.1 jel;19.8 jel;31.1 jel;11.2 jel;20.6 jel;4.6 jel;14.7 jel;16.6 jel;24.6 jel;22.0 jel;27.5 jel;16.8 jel;33.7 jel;16.4 jel;14.7 jel;16.7 jel;18.5 jel;21.8 jel;16.9 jel;15.0 jel;18.6 jel;7.1 jel;17.0 jel;23.6 jel;25.3 jel;17.9 jel;16.2 jel;14.6 jel;24.5 jel;20.8 jel;17.5 jel;24.1 jel;12.0 jel;14.1 jel;15.8 jel;12.9 jel;16.2 jel;5.5 jel;10.5 jel;12.8 jel;18.6 jel;24.6 jel;15.1 jel;11.6 jel;14.9 jel;10.8 jel;14.6 jel;16.4 jel;14.0 jel;5.7 jel;12.0 jel;24.7 jel;18.0 jel;17.8 jel;27.2 jel;21.2 jel;22.2 jel;25.6 jel;19.8 jel;2.0 jel;15.0 jel;8.5 jel;31.6 jel;16.1 jel;29.2 jel;23.7 jel;13.2 jel;32.0 jel;15.5 jel;7.4 jel;18.3 jel;19.4 jel;16.2 jel;11.3 jel;12.0 jel;13.8 jel;18.5 jel;18.9 jel;21.1 jel;12.1 jel;15.3 jel;17.1 jel;28.3 jel;27.9 jel;19.6 jel;15.1 jel;12.8 jel;12.0 jel;20.1 jel;24.2 jel;26.2 jel;24.7 jel;23.8 jel;18.9 jel;35.6 jel;3.1 jel;16.4 jel;28.9 jel;14.2 jel;23.5 jel;21.0 jel;28.7 jel;14.9 jel;21.2 jel;8.1 jel;10.4 jel;29.5 jel;14.8 jel;0.2 jel;26.5 jel;10.2 jel;6.6 jel;9.8 jel;27.6 jel;22.4 jel;13.6 jel;15.2 jel;23.1 jel;30.9 jel;23.3 jel;23.2 jel;23.6 jel;16.9 jel;14.8 jel;28.7 jel;20.0 jel;27.8 jel;18.2 jel;15.3 jel;21.8 jel;16.7 jel;10.7 jel;13.1 jel;24.4 jel;13.8 jel;20.9 jel;25.6 jel;11.8 jel;13.7 jel;14.9 jel;11.2 jel;20.6 jel;22.5 jel;17.8 jel;16.8 jel;20.2 jel;15.5 jel;5.0 jel;12.8 jel;20.3 jel;10.3 jel;30.4 jel;12.5 jel;20.9 jel;25.7 jel;28.1 jel;7.0 jel;9.4 jel;26.6 jel;23.6 jel;29.6 jel;15.9 jel;8.9 jel;11.9 jel;29.5 jel;13.6 jel;19.2 jel;23.7 jel;30.1 jel;19.8 jel;20.5 jel;17.9 jel;18.3 jel;17.7 jel;13.3 jel;11.8 jel;11.4 jel;13.2 jel;10.3 jel;7.8 jel;21.5 jel;34.5 jel;25.7 jel;17.8 jel;19.1 jel;16.0 jel;1.0 jel;21.4 jel;5.8 jel;11.1 jel;15.5 jel;21.9 jel;17.8 jel;17.2 jel;22.9 jel;24.4 jel;24.9 jel;12.9 jel;26.6 jel;5.6 jel;20.9 jel;13.6 jel;20.6 jel;29.6 jel;15.6 jel;29.8 jel;30.7 jel;17.8 jel;26.4 jel;14.0 jel;8.1 jel;15.6 jel;18.3 jel;23.8 jel;22.5 jel;13.8 jel;26.9 jel;7.9 jel;15.2 jel;14.8 jel;13.3 jel;20.5 jel;15.9 jel;21.2 jel;14.8 jel;15.5 jel;21.5 jel;21.5 jel;16.3 jel;13.6 jel;24.6 jel;10.3 jel;11.4 jel;9.5 jel;8.1 jel;15.1 jel;18.5 jel;20.1 jel;22.0 jel;11.1 jel;21.6 jel;18.9 jel;23.3 jel;7.0 jel;16.7 jel;21.2 jel;24.1 jel;39.6 jel;20.6 jel;22.6 jel;20.6 jel;17.2 jel;22.5 jel;18.4 jel;30.2 jel;17.0 jel;21.7 jel;15.4 jel;31.4 jel;18.9 jel;30.3 jel;13.4 jel;20.9 jel;14.1 jel;18.0 jel;13.9 jel;5.9 jel;3.7 jel;17.6 jel;28.4 jel;25.0 jel;11.8 jel;15.9 jel;20.5 jel;-0.1 jel;21.0 jel;12.5 jel;5.6 jel;22.3 jel;17.0 jel;8.0 jel;24.9 jel;9.7 jel;31.4 jel;20.6 jel;14.7 jel;27.6 jel;35.3 jel;15.0 jel;9.1 jel;11.5 jel;16.2 jel;32.1 jel;17.1 jel;15.1 jel;11.0 jel;14.0 jel;23.8 jel;17.5 jel;25.9 jel;18.1 jel;15.8 jel;21.3 jel;21.4 jel;21.6 jel;3.2 jel;21.3 jel;22.0 jel;3.6 jel;18.3 jel;34.6 jel;13.2 jel;15.4 jel;20.8 jel;17.6 jel;23.8 jel;30.5 jel;25.0 jel;20.4 jel;14.5 jel;24.8 jel;18.9 jel;21.0 jel;8.6 jel;12.6 jel;22.1 jel;11.3 jel;20.9 jel;12.1 jel;19.1 jel;14.8 jel;17.1 jel;29.5 jel;21.9 jel;15.2 jel;19.4 jel;20.5 jel;15.4 jel;14.7 jel;24.2 jel;28.2 jel;14.5 jel;20.8 jel;10.2 jel;24.3 jel;14.5 jel;23.6 jel;15.8 jel;23.3 jel;15.8 jel;12.5 jel;10.2 jel;22.2 jel;21.7 jel;18.0 jel;25.8 jel;23.7 jel;27.4 jel;27.7 jel;18.7 jel;5.8 jel;17.9 jel;19.2 jel;19.4 jel;14.1 jel;28.7 jel;13.5 jel;11.0 jel;11.4 jel;30.4 jel;22.0 jel;23.1 jel;16.0 jel;25.4 jel;12.7 jel;25.5 jel;25.6 jel;17.7 jel;14.5 jel;16.8 jel;12.4 jel;20.8 jel;18.5 jel;30.5 jel;15.2 jel;19.3 jel;24.6 jel;21.5 jel;18.1 jel;23.1 jel;12.3 jel;27.0 jel;11.5 jel;23.3 jel;12.1 jel;20.6 jel;36.2 jel;17.7 jel;21.2 jel;15.2 jel;17.8 jel;8.7 jel;16.0 jel;10.6 jel;16.2 jel;32.5 jel;24.4 jel;8.5 jel;19.8 jel;18.9 jel;16.3 jel;18.3 jel;27.8 jel;6.7 jel;15.5 jel;24.8 jel;27.9 jel;24.5 jel;16.0 jel;17.6 jel;13.6 jel;7.6 jel;8.8 jel;11.3 jel;15.6 jel;20.7 jel;15.2 jel;15.1 jel;22.2 jel;20.8 jel;12.6 jel;7.5 jel;13.4 jel;15.8 jel;17.8 jel;24.0 jel;10.7 jel;10.1 jel;4.6 jel;3.5 jel;31.1 jel;25.4 jel;25.3 jel;18.8 jel;7.6 jel;20.3 jel;29.0 jel;11.9 jel;27.0 jel;30.2 jel;9.8 jel;8.1 jel;21.6 jel;25.5 jel;18.0 jel;19.2 jel;14.7 jel;21.6 jel;1.2 jel;26.8 jel;20.5 jel;13.1 jel;7.6 jel;15.3 jel;22.1 jel;17.7 jel;31.6 jel;27.2 jel;12.0 jel;6.2 jel;18.8 jel;30.8 jel;23.0 jel;19.0 jel;11.8 jel;11.1 jel;18.4 jel;15.1 jel;12.7 jel;23.6 jel;9.7 jel;19.6 jel;29.6 jel;14.9 jel;15.6 jel;21.2 jel;27.7 jel;17.3 jel;27.6 jel;10.0 jel;20.9 jel;11.8 jel;23.1 jel;25.4 jel;20.3 jel;32.4 jel;25.7 jel;24.7 jel;14.7 jel;10.4 jel;16.7 jel;9.6 jel;22.4 jel;22.6 jel;14.4 jel;14.3 jel;21.9 jel;23.6 jel;20.5 jel;24.8 jel;21.2 jel;11.3 jel;12.4 jel;24.6 jel;16.3 jel;21.5 jel;21.8 jel;13.8 jel;29.2 jel;19.1 jel;20.2 jel;16.9 jel;21.6 jel;11.6 jel;29.0 jel;20.8 jel;7.5 jel;30.9 jel;27.1 jel;18.1 jel;21.5 jel;26.1 jel;18.2 jel;17.2 jel;12.4 jel;12.2 jel;18.3 jel;10.1 jel;6.4 jel;23.0 jel;15.8 jel;14.9 jel;25.7 jel;20.1 jel;20.1 jel;19.0 jel;19.7 jel;13.1 jel;11.1 jel;17.6 jel;26.4 jel;23.3 jel;34.3 jel;15.2 jel;27.5 jel;10.9 jel;30.0 jel;7.7 jel;27.9 jel;14.0 jel;23.1 jel;15.3 jel;15.1 jel;16.5 jel;12.9 jel;12.6 jel;19.3 jel;26.6 jel;14.5 jel;15.2 jel;21.6 jel;19.7 jel;22.8 jel;20.5 jel;7.6 jel;8.0 jel;26.8 jel;3.0 jel;16.6 jel;8.5 jel;28.1 jel;20.9 jel;17.1 jel;14.3 jel;7.6 jel;9.4 jel;16.8 jel;30.1 jel;21.6 jel;5.8 jel;26.7 jel;22.4 jel;20.3 jel;19.3 jel;20.7 jel;20.2 jel;22.3 jel;7.2 jel;26.5 jel;19.8 jel;17.5 jel;14.8 jel;18.1 jel;14.1 jel;19.3 jel;16.7 jel;9.0 jel;20.9 jel;15.3 jel;32.3 jel;22.8 jel;13.0 jel;17.4 jel;17.4 jel;17.0 jel;14.9 jel;10.8 jel;15.3 jel;27.3 jel;28.3 jel;7.4 jel;21.6 jel;16.5 jel;17.3 jel;19.8 jel;14.5 jel;20.1 jel;17.9 jel;13.1 jel;26.1 jel;7.9 jel;21.2 jel;12.6 jel;14.0 jel;28.4 jel;16.0 jel;28.4 jel;10.3 jel;37.8 jel;27.5 jel;22.3 jel;5.6 jel;12.3 jel;13.3 jel;37.2 jel;21.7 jel;19.8 jel;8.1 jel;19.0 jel;19.5 jel;17.9 jel;19.8 jel;11.6 jel;34.5 jel;18.0 jel;12.9 jel;16.8 jel;13.2 jel;11.3 jel;9.3 jel;24.6 jel;20.1 jel;14.5 jel;8.4 jel;5.0 jel;13.5 jel;23.3 jel;22.7 jel;14.4 jel;12.8 jel;17.5 jel;23.1 jel;18.6 jel;24.0 jel;9.9 jel;14.5 jel;17.4 jel;20.8 jel;6.0 jel;15.1 jel;13.9 jel;17.6 jel;16.1 jel;16.9 jel;19.3 jel;16.0 jel;21.0 jel;20.8 jel;14.5 jel;21.0 jel;16.0 jel;14.7 jel;28.1 jel;17.1 jel;18.1 jel;16.3 jel;15.2 jel;9.4 jel;6.0 jel;10.8 jel;9.2 jel;15.8 jel;30.6 jel;12.8 jel;14.7 jel;15.9 jel;10.7 jel;15.1 jel;24.3 jel;16.9 jel;19.2 jel;21.8 jel;14.3 jel;21.8 jel;18.8 jel;16.2 jel;15.3 jel;20.5 jel;8.4 jel;14.9 jel;34.1 jel;8.3 jel;25.9 jel;17.3 jel;7.9 jel;23.7 jel;18.3 jel;4.0 jel;25.5 jel;8.9 jel;20.0 jel;18.7 jel;23.3 jel;14.7 jel;22.1 jel;8.2 jel;14.4 jel;15.1 jel;22.0 jel;12.1 jel;22.9 jel;16.3 jel;20.3 jel;22.6 jel;27.1 jel;16.8 jel;22.2 jel;16.3 jel;8.6 jel;12.8 jel;28.7 jel;19.5 jel;21.2 jel;15.4 jel;17.7 jel;11.7 jel;19.3 jel;19.2 jel;10.6 jel;10.2 jel;13.8 jel;17.1 jel;11.4 jel;28.4 jel;23.1 jel;27.3 jel;19.4 jel;13.8 jel;14.6 jel;8.3 jel;22.6 jel;25.4 jel;24.3 jel;20.2 jel;26.8 jel;30.1 jel;30.3 jel;7.2 jel;4.5 jel;16.2 jel;32.2 jel;20.1 jel;4.9 jel;13.6 jel;11.1 jel;14.8 jel;17.1 jel;15.7 jel;24.6 jel;14.7 jel;31.4 jel;19.8 jel;15.8 jel;8.5 jel;12.8 jel;25.8 jel;35.1 jel;10.9 jel;26.3 jel;20.3 jel;13.5 jel;3.0 jel;17.1 jel;21.3 jel;21.4 jel;22.5 jel;7.3 jel;12.9 jel;4.6 jel;25.2 jel;22.4 jel;14.1 jel;31.7 jel;14.7 jel;25.8 jel;29.2 jel;24.1 jel;16.4 jel;5.2 jel;16.4 jel;22.5 jel;26.3 jel;17.1 jel;25.1 jel;4.1 jel;3.7 jel;18.1 jel;24.1 jel;13.6 jel;15.5 jel;24.4 jel;16.5 jel;28.1 jel;26.6 jel;5.9 jel;10.8 jel;20.3 jel;16.0 jel;5.1 jel;16.3 jel;21.2 jel;19.1 jel;26.3 jel;16.5 jel;14.7 jel;24.0 jel;18.7 jel;20.1 jel;4.5 jel;16.4 jel;13.6 jel;16.6 jel;17.0 jel;17.1 jel;22.5 jel;25.8 jel;25.2 jel;19.0 jel;23.0 jel;15.7 jel;26.3 jel;2.6 jel;21.9 jel;10.6 jel;12.8 jel;12.4 jel;24.9 jel;23.5 jel;22.9 jel;12.1 jel;9.9 jel;0.2 jel;23.2 jel;8.4 jel;12.4 jel;5.7 jel;18.4 jel;9.8 jel;35.1 jel;16.5 jel;16.1 jel;13.0 jel;17.8 jel;17.6 jel;11.8 jel;18.3 jel;6.0 jel;3.6 jel;23.1 jel;29.5 jel;14.5 jel;10.0 jel;16.5 jel;32.5 jel;16.5 jel;15.6 jel;19.8 jel;27.1 jel;25.4 jel;16.3 jel;23.6 jel;9.2 jel;21.2 jel;26.6 jel;15.7 jel;22.6 jel;18.2 jel;15.1 jel;16.7 jel;22.5 jel;7.7 jel;18.0 jel;21.2 jel;27.9 jel;13.2 jel;22.0 jel;24.8 jel;18.5 jel;11.3 jel;12.2 jel;17.2 jel;17.9 jel;16.2 jel;13.1 jel;23.4 jel;20.4 jel;21.0 jel;20.8 jel;21.8 jel;22.2 jel;17.1 jel;17.6 jel;30.9 jel;19.9 jel;16.2 jel;22.9 jel;20.1 jel;24.8 jel;26.6 jel;19.5 jel;15.2 jel;16.0 jel;11.8 jel;27.7 jel;13.8 jel;27.8 jel;16.6 jel;25.7 jel;16.5 jel;13.9 jel;14.5 jel;18.9 jel;22.5 jel;30.8 jel;21.6 jel;20.3 jel;6.7 jel;32.3 jel;11.4 jel;15.1 jel;24.1 jel;16.9 jel;8.3 jel;14.3 jel;9.9 jel;16.5 jel;22.5 jel;22.3 jel;12.0 jel;11.8 jel;11.5 jel;25.6 jel;31.0 jel;26.1 jel;19.6 jel;14.6 jel;2.1 jel;27.1 jel;26.8 jel;16.3 jel;15.0 jel;18.3 jel;24.9 jel;27.2 jel;19.1 jel;19.9 jel;22.7 jel;25.1 jel;8.9 jel;16.5 jel;15.9 jel;23.2 jel;22.9 jel;16.1 jel;19.0 jel;26.5 jel;12.9 jel;24.6 jel;13.1 jel;3.2 jel;29.4 jel;10.5 jel;20.2 jel;15.8 jel;13.4 jel;11.5 jel;21.6 jel;20.8 jel;20.7 jel;28.7 jel;10.7 jel;13.6 jel;1.9 jel;23.8 jel;12.4 jel;21.7 jel;30.6 jel;32.2 jel;20.8 jel;27.8 jel;30.8 jel;30.1 jel;16.3 jel;22.3 jel;19.4 jel;20.7 jel;9.9 jel;10.8 jel;18.4 jel;7.9 jel;8.5 jel;31.1 jel;14.7 jel;23.1 jel;31.0 jel;12.7 jel;15.8 jel;16.9 jel;26.9 jel;9.5 jel;14.2 jel;18.2 jel;24.6 jel;17.8 jel;15.3 jel;5.4 jel;18.9 jel;28.4 jel;12.2 jel;15.1 jel;19.2 jel;23.9 jel;36.0 jel;23.9 jel;26.1 jel;15.3 jel;12.1 jel;9.8 jel;7.0 jel;19.9 jel;12.7 jel;-4.9 jel;19.4 jel;17.9 jel;18.7 jel;8.2 jel;29.2 jel;15.4 jel;17.2 jel;18.1 jel;15.8 jel;8.1 jel;14.9 jel;19.0 jel;24.4 jel;13.2 jel;20.3 jel;5.6 jel;14.8 jel;22.4 jel;19.0 jel;12.6 jel;12.5 jel;6.0 jel;16.9 jel;10.5 jel;15.0 jel;9.4 jel;20.7 jel;14.0 jel;23.0 jel;14.4 jel;11.4 jel;17.3 jel;18.8 jel;1.8 jel;22.9 jel;9.0 jel;19.4 jel;32.4 jel;4.0 jel;21.2 jel;8.2 jel;18.0 jel;16.6 jel;33.4 jel;27.4 jel;20.5 jel;28.2 jel;25.0 jel;13.3 jel;20.3 jel;21.0 jel;13.5 jel;17.0 jel;7.0 jel;20.6 jel;13.0 jel;18.4 jel;15.5 jel;17.8 jel;8.1 jel;19.6 jel;13.5 jel;11.7 jel;8.5 jel;5.8 jel;22.3 jel;4.3 jel;7.8 jel;-6.1 jel;23.6 jel;13.2 jel;24.2 jel;24.6 jel;19.1 jel;14.2 jel;18.2 jel;22.4 jel;15.4 jel;5.7 jel;15.5 jel;26.0 jel;16.0 jel;16.8 jel;13.3 jel;15.2 jel;16.5 jel;19.8 jel;7.5 jel;13.9 jel;24.4 jel;18.9 jel;17.1 jel;25.9 jel;16.0 jel;12.2 jel;18.7 jel;30.1 jel;7.3 jel;18.7 jel;18.2 jel;22.2 jel;33.6 jel;22.0 jel;6.4 jel;13.7 jel;16.0 jel;11.0 jel;15.3 jel;15.0 jel;22.8 jel;1.6 jel;19.2 jel;16.2 jel;7.6 jel;21.3 jel;19.6 jel;13.7 jel;8.6 jel;23.5 jel;28.8 jel;9.1 jel;18.1 jel;13.0 jel;17.0 jel;17.8 jel;27.2 jel;18.2 jel;13.6 jel;15.4 jel;17.4 jel;3.5 jel;26.4 jel;28.0 jel;23.3 jel;19.2 jel;25.3 jel;38.9 jel;15.3 jel;22.4 jel;26.3 jel;18.6 jel;25.6 jel;14.8 jel;36.0 jel;18.4 jel;13.6 jel;24.2 jel;8.4 jel;16.8 jel;19.9 jel;8.9 jel;21.6 jel;15.2 jel;12.4 jel;20.3 jel;20.7 jel;16.2 jel;24.8 jel;15.5 jel;18.6 jel;15.0 jel;10.8 jel;28.8 jel;19.0 jel;10.0 jel;4.8 jel;13.7 jel;21.5 jel;28.0 jel;21.5 jel;11.1 jel;9.6 jel;19.1 jel;19.6 jel;27.9 jel;19.3 jel;26.2 jel;21.3 jel;26.3 jel;22.1 jel;18.5 jel;19.7 jel;27.1 jel;9.6 jel;15.6 jel;10.7 jel;27.3 jel;17.8 jel;13.2 jel;9.2 jel;26.8 jel;26.1 jel;15.5 jel;24.8 jel;19.9 jel;19.7 jel;21.5 jel;19.3 jel;17.1 jel;7.5 jel;11.5 jel;12.4 jel;8.1 jel;26.9 jel;13.6 jel;9.5 jel;15.6 jel;12.1 jel;14.9 jel;24.9 jel;19.2 jel;20.4 jel;15.7 jel;31.3 jel;26.9 jel;11.6 jel;19.9 jel;19.0 jel;19.1 jel;25.9 jel;-0.7 jel;5.6 jel;24.8 jel;15.5 jel;13.7 jel;13.1 jel;28.6 jel;24.1 jel;12.8 jel;12.7 jel;13.7 jel;24.6 jel;9.6 jel;18.9 jel;21.8 jel;14.0 jel;22.5 jel;10.4 jel;13.0 jel;10.8 jel;24.0 jel;12.0 jel;20.8 jel;26.0 jel;8.3 jel;14.1 jel;21.5 jel;2.2 jel;11.2 jel;2.1 jel;33.5 jel;15.2 jel;19.1 jel;18.6 jel;19.2 jel;12.6 jel;18.5 jel;18.6 jel;26.6 jel;24.0 jel;23.5 jel;12.6 jel;12.1 jel;8.1 jel;20.9 jel;17.7 jel;9.4 jel;9.2 jel;16.3 jel;14.2 jel;16.8 jel;5.2 jel;8.9 jel;18.8 jel;19.9 jel;19.2 jel;10.1 jel;26.3 jel;14.2 jel;9.5 jel;10.4 jel;13.3 jel;21.8 jel;11.3 jel;15.2 jel;18.8 jel;15.4 jel;25.6 jel;26.9 jel;20.1 jel;7.0 jel;30.5 jel;18.3 jel;21.6 jel;13.1 jel;4.9 jel;28.1 jel;7.3 jel;15.1 jel;27.2 jel;21.6 jel;15.6 jel;14.9 jel;25.1 jel;16.4 jel;3.2 jel;9.5 jel;20.2 jel;12.8 jel;12.3 jel;10.3 jel;19.4 jel;18.7 jel;19.8 jel;28.7 jel;23.5 jel;22.4 jel;15.2 jel;15.1 jel;23.0 jel;21.5 jel;13.3 jel;26.9 jel;25.2 jel;13.2 jel;27.9 jel;13.1 jel;27.7 jel;-1.3 jel;30.6 jel;16.8 jel;20.2 jel;16.8 jel;20.3 jel;26.5 jel;17.7 jel;22.3 jel;26.5 jel;16.4 jel;15.6 jel;10.6 jel;15.1 jel;21.3 jel;35.6 jel;12.4 jel;-1.2 jel;14.3 jel;24.3 jel;25.1 jel;18.5 jel;25.3 jel;11.8 jel;11.6 jel;18.7 jel;1.5 jel;10.7 jel;22.9 jel;27.7 jel;15.6 jel;27.4 jel;23.5 jel;0.7 jel;13.1 jel;22.6 jel;16.3 jel;15.6 jel;9.6 jel;11.5 jel;17.0 jel;17.9 jel;13.8 jel;22.1 jel;7.1 jel;8.9 jel;17.7 jel;30.7 jel;28.5 jel;22.0 jel;6.0 jel;23.1 jel;17.4 jel;31.7 jel;20.9 jel;10.4 jel;20.1 jel;16.3 jel;17.7 jel;19.2 jel;28.8 jel;20.6 jel;20.4 jel;18.6 jel;8.4 jel;13.6 jel;11.3 jel;6.3 jel;17.0 jel;13.8 jel;14.3 jel;22.7 jel;24.3 jel;21.6 jel;25.7 jel;16.2 jel;17.5 jel;21.2 jel;24.8 jel;12.8 jel;12.7 jel;11.2 jel;15.5 jel;3.0 jel;22.3 jel;21.0 jel;23.1 jel;9.3 jel;12.3 jel;12.2 jel;24.0 jel;13.3 jel;15.7 jel;5.2 jel;16.5 jel;19.7 jel;11.4 jel;17.7 jel;6.6 jel;12.9 jel;26.0 jel;26.3 jel;10.2 jel;19.8 jel;19.7 jel;20.2 jel;19.7 jel;14.8 jel;17.9 jel;29.6 jel;15.1 jel;8.9 jel;18.4 jel;16.6 jel;23.7 jel;18.6 jel;27.5 jel;30.2 jel;26.2 jel;19.8 jel;23.1 jel;9.3 jel;18.8 jel;1.8 jel;21.7 jel;24.3 jel;5.6 jel;22.4 jel;22.3 jel;28.4 jel;15.9 jel;35.6 jel;16.7 jel;20.6 jel;16.5 jel;16.1 jel;16.7 jel;12.3 jel;0.8 jel;20.5 jel;9.4 jel;14.6 jel;12.6 jel;21.6 jel;20.0 jel;19.6 jel;29.5 jel;5.1 jel;7.7 jel;5.1 jel;19.8 jel;24.7 jel;26.3 jel;11.8 jel;5.8 jel;19.7 jel;14.6 jel;17.9 jel;25.8 jel;19.6 jel;14.6 jel;30.9 jel;26.3 jel;20.8 jel;4.7 jel;15.9 jel;21.5 jel;11.3 jel;15.6 jel;17.2 jel;25.0 jel;21.3 jel;15.7 jel;23.0 jel;22.6 jel;20.6 jel;14.5 jel;23.3 jel;22.9 jel;10.3 jel;18.0 jel;7.9 jel;14.1 jel;10.1 jel;27.9 jel;19.4 jel;23.6 jel;19.1 jel;26.5 jel;22.8 jel;19.2 jel;22.7 jel;21.1 jel;25.1 jel;28.6 jel;17.6 jel;7.5 jel;20.8 jel;24.3 jel;29.0 jel;18.1 jel;19.9 jel;21.7 jel;24.0 jel;13.0 jel;16.5 jel;22.9 jel;13.4 jel;9.5 jel;27.0 jel;15.4 jel;16.2 jel;16.5 jel;13.7 jel;10.5 jel;18.5 jel;29.5 jel;34.5 jel;19.3 jel;20.4 jel;14.0 jel;19.3 jel;10.2 jel;29.7 jel;12.0 jel;15.3 jel;18.4 jel;17.3 jel;16.1 jel;17.8 jel;22.7 jel;2.2 jel;22.2 jel;21.1 jel;10.7 jel;15.1 jel;22.6 jel;12.8 jel;12.8 jel;15.5 jel;6.8 jel;9.9 jel;25.3 jel;18.9 jel;13.8 jel;-3.7 jel;7.6 jel;11.9 jel;11.3 jel;7.8 jel;20.9 jel;18.7 jel;10.6 jel;25.3 jel;11.4 jel;18.5 jel;20.6 jel;14.6 jel;12.7 jel;10.3 jel;18.6 jel;21.4 jel;26.9 jel;18.1 jel;14.5 jel;4.3 jel;33.2 jel;20.2 jel;8.3 jel;17.4 jel;18.4 jel;16.5 jel;20.6 jel;18.3 jel;10.1 jel;32.5 jel;23.1 jel;14.3 jel;22.3 jel;14.7 jel;25.8 jel;30.7 jel;21.6 jel;9.6 jel;13.0 jel;11.8 jel;13.3 jel;19.1 jel;8.5 jel;22.5 jel;13.8 jel;16.6 jel;14.6 jel;14.3 jel;26.5 jel;12.2 jel;12.3 jel;40.4 jel;12.6 jel;22.2 jel;19.8 jel;13.4 jel;17.4 jel;10.6 jel;19.7 jel;17.2 jel;17.7 jel;3.0 jel;16.1 jel;18.7 jel;17.4 jel;5.4 jel;10.4 jel;20.3 jel;22.1 jel;10.9 jel;11.5 jel;15.6 jel;21.4 jel;6.4 jel;12.8 jel;24.5 jel;17.5 jel;9.4 jel;22.0 jel;5.4 jel;7.1 jel;5.4 jel;31.0 jel;5.7 jel;31.3 jel;27.3 jel;21.0 jel;9.2 jel;16.4 jel;24.3 jel;6.3 jel;18.9 jel;19.0 jel;15.0 jel;12.2 jel;21.1 jel;15.8 jel;14.9 jel;14.9 jel;28.6 jel;15.6 jel;18.8 jel;26.3 jel;23.3 jel;10.5 jel;15.6 jel;22.5 jel;24.5 jel;14.2 jel;13.5 jel;19.1 jel;14.6 jel;17.8 jel;10.6 jel;16.9 jel;19.1 jel;25.2 jel;22.6 jel;22.6 jel;11.2 jel;25.0 jel;11.6 jel;19.1 jel;13.5 jel;10.6 jel;22.4 jel;16.5 jel;19.6 jel;12.1 jel;13.4 jel;14.2 jel;30.2 jel;17.3 jel;14.2 jel;15.6 jel;11.7 jel;12.1 jel;24.1 jel;19.2 jel;17.3 jel;10.6 jel;11.7 jel;25.4 jel;3.7 jel;33.4 jel;17.6 jel;10.7 jel;29.9 jel;22.4 jel;16.0 jel;19.1 jel;20.0 jel;2.5 jel;22.6 jel;16.1 jel;4.8 jel;15.9 jel;11.3 jel;10.1 jel;22.4 jel;17.8 jel;34.8 jel;5.3 jel;13.6 jel;18.1 jel;25.4 jel;16.7 jel;15.6 jel;12.2 jel;19.2 jel;20.5 jel;12.6 jel;16.3 jel;18.9 jel;20.3 jel;15.8 jel;15.7 jel;20.5 jel;19.0 jel;15.4 jel;11.1 jel;21.7 jel;14.8 jel;22.7 jel;14.5 jel;16.1 jel;22.3 jel;26.7 jel;21.3 jel;30.7 jel;6.3 jel;11.7 jel;18.0 jel;13.3 jel;18.0 jel;25.7 jel;11.6 jel;36.4 jel;28.4 jel;21.1 jel;19.8 jel;30.4 jel;13.5 jel;8.0 jel;12.7 jel;11.3 jel;11.9 jel;20.8 jel;17.7 jel;20.8 jel;19.8 jel;9.6 jel;15.8 jel;24.9 jel;10.9 jel;15.9 jel;18.1 jel;5.9 jel;18.8 jel;19.7 jel;21.3 jel;22.3 jel;23.6 jel;29.5 jel;22.4 jel;16.4 jel;16.0 jel;31.4 jel;23.5 jel;10.3 jel;31.2 jel;16.0 jel;27.3 jel;23.5 jel;13.2 jel;17.4 jel;20.2 jel;16.8 jel;23.0 jel;18.5 jel;21.4 jel;22.8 jel;20.9 jel;22.2 jel;19.8 jel;12.5 jel;31.2 jel;21.8 jel;14.8 jel;24.7 jel;11.9 jel;14.5 jel;11.1 jel;20.2 jel;14.3 jel;26.2 jel;22.7 jel;16.0 jel;14.5 jel;25.8 jel;12.0 jel;20.8 jel;26.9 jel;17.3 jel;16.5 jel;24.9 jel;10.1 jel;20.9 jel;7.1 jel;7.9 jel;17.8 jel;18.9 jel;23.8 jel;9.8 jel;8.9 jel;16.0 jel;11.2 jel;14.2 jel;19.2 jel;19.9 jel;18.7 jel;9.7 jel;23.2 jel;31.4 jel;16.9 jel;13.2 jel;16.1 jel;21.5 jel;8.3 jel;21.7 jel;3.9 jel;5.7 jel;13.5 jel;12.1 jel;17.3 jel;11.0 jel;31.0 jel;11.1 jel;21.3 jel;13.0 jel;16.5 jel;7.2 jel;14.4 jel;24.3 jel;14.3 jel;21.1 jel;14.2 jel;16.1 jel;24.7 jel;16.6 jel;25.4 jel;25.4 jel;21.9 jel;20.5 jel;19.0 jel;1.7 jel;13.2 jel;25.5 jel;13.1 jel;18.2 jel;18.5 jel;14.1 jel;13.2 jel;20.7 jel;17.9 jel;32.1 jel;23.3 jel;25.7 jel;16.9 jel;11.3 jel;12.2 jel;16.9 jel;10.3 jel;13.6 jel;11.5 jel;25.0 jel;11.8 jel;14.2 jel;15.6 jel;30.3 jel;8.9 jel;23.1 jel;11.7 jel;32.1 jel;20.1 jel;21.4 jel;31.1 jel;16.5 jel;22.3 jel;8.7 jel;27.1 jel;10.9 jel;18.6 jel;11.6 jel;5.0 jel;18.3 jel;20.4 jel;26.8 jel;15.3 jel;15.5 jel;6.2 jel;14.2 jel;21.5 jel;13.0 jel;30.8 jel;19.2 jel;14.8 jel;7.9 jel;13.8 jel;10.1 jel;15.7 jel;21.8 jel;11.3 jel;27.5 jel;16.8 jel;23.5 jel;21.4 jel;16.3 jel;16.7 jel;18.9 jel;11.6 jel;21.6 jel;21.1 jel;25.8 jel;18.0 jel;7.4 jel;24.6 jel;29.0 jel;18.9 jel;23.0 jel;22.4 jel;21.7 jel;13.6 jel;22.8 jel;17.9 jel;12.5 jel;17.6 jel;22.1 jel;10.0 jel;12.9 jel;17.6 jel;26.4 jel;23.1 jel;23.5 jel;24.3 jel;14.5 jel;24.6 jel;21.6 jel;21.0 jel;46.5 jel;26.3 jel;20.2 jel;9.3 jel;19.7 jel;12.3 jel;23.1 jel;17.9 jel;20.0 jel;0.4 jel;15.7 jel;14.0 jel;23.0 jel;16.7 jel;5.3 jel;24.8 jel;14.5 jel;21.9 jel;19.5 jel;24.1 jel;6.7 jel;16.2 jel;21.0 jel;15.5 jel;1.0 jel;17.8 jel;20.3 jel;8.4 jel;7.4 jel;11.0 jel;14.8 jel;25.2 jel;26.3 jel;8.4 jel;12.3 jel;19.4 jel;21.1 jel;16.3 jel;12.6 jel;21.4 jel;20.6 jel;21.0 jel;24.9 jel;21.0 jel;13.8 jel;24.5 jel;21.4 jel;9.7 jel;17.2 jel;24.9 jel;18.5 jel;12.6 jel;15.7 jel;17.1 jel;12.5 jel;33.4 jel;19.4 jel;6.4 jel;11.3 jel;22.5 jel;17.2 jel;25.5 jel;11.5 jel;12.6 jel;11.5 jel;17.0 jel;14.3 jel;20.0 jel;19.1 jel;15.8 jel;15.0 jel;25.8 jel;9.9 jel;15.0 jel;13.4 jel;4.8 jel;27.6 jel;10.5 jel;27.2 jel;13.8 jel;20.1 jel;23.7 jel;13.5 jel;21.0 jel;25.5 jel;21.9 jel;20.4 jel;28.4 jel;31.5 jel;24.2 jel;23.9 jel;19.3 jel;4.7 jel;-1.5 jel;13.8 jel;18.0 jel;22.4 jel;22.0 jel;15.1 jel;17.6 jel;13.7 jel;22.2 jel;19.9 jel;27.7 jel;28.9 jel;23.3 jel;12.9 jel;25.7 jel;19.6 jel;8.0 jel;14.7 jel;20.0 jel;13.1 jel;26.8 jel;17.1 jel;32.3 jel;16.6 jel;23.2 jel;18.4 jel;17.9 jel;15.5 jel;24.7 jel;18.5 jel;10.7 jel;15.5 jel;18.4 jel;26.2 jel;22.2 jel;18.0 jel;25.4 jel;29.2 jel;5.3 jel;19.8 jel;24.7 jel;21.8 jel;17.1 jel;21.5 jel;26.0 jel;18.0 jel;16.2 jel;26.8 jel;19.0 jel;23.0 jel;11.1 jel;21.4 jel;19.5 jel;22.0 jel;17.2 jel;18.3 jel;6.1 jel;13.9 jel;10.4 jel;21.6 jel;25.7 jel;18.0 jel;23.0 jel;25.3 jel;11.1 jel;14.6 jel;10.1 jel;19.8 jel;11.3 jel;15.3 jel;26.8 jel;12.2 jel;23.4 jel;9.0 jel;16.4 jel;11.6 jel;19.7 jel;16.4 jel;23.0 jel;9.7 jel;25.4 jel;15.6 jel;23.7 jel;25.5 jel;22.7 jel;12.1 jel;7.6 jel;28.5 jel;26.0 jel;6.8 jel;28.3 jel;17.7 jel;15.4 jel;11.5 jel;7.5 jel;15.9 jel;22.7 jel;21.1 jel;-0.1 jel;24.6 jel;20.2 jel;11.3 jel;20.4 jel;11.5 jel;18.2 jel;24.9 jel;6.1 jel;25.0 jel;12.1 jel;21.0 jel;19.9 jel;19.9 jel;22.1 jel;14.2 jel;14.4 jel;15.9 jel;21.1 jel;21.8 jel;9.2 jel;13.8 jel;16.4 jel;20.1 jel;25.0 jel;26.3 jel;12.4 jel;19.0 jel;19.4 jel;6.8 jel;4.7 jel;8.5 jel;13.5 jel;11.8 jel;22.3 jel;16.0 jel;35.3 jel;25.5 jel;35.3 jel;7.9 jel;20.7 jel;17.3 jel;13.5 jel;10.6 jel;21.6 jel;19.0 jel;18.2 jel;14.7 jel;15.6 jel;19.5 jel;13.5 jel;22.1 jel;23.1 jel;23.9 jel;28.1 jel;2.1 jel;15.4 jel;12.9 jel;17.6 jel;15.3 jel;24.1 jel;30.1 jel;6.9 jel;21.0 jel;24.9 jel;7.5 jel;21.2 jel;22.3 jel;14.4 jel;8.5 jel;14.7 jel;17.8 jel;20.5 jel;13.8 jel;11.9 jel;12.9 jel;11.3 jel;41.5 jel;11.6 jel;19.0 jel;19.5 jel;12.8 jel;23.6 jel;13.5 jel;17.1 jel;35.1 jel;8.2 jel;24.3 jel;12.6 jel;18.5 jel;12.9 jel;23.5 jel;18.0 jel;18.8 jel;7.8 jel;2.9 jel;2.0 jel;25.0 jel;10.7 jel;19.7 jel;31.0 jel;15.5 jel;15.4 jel;10.7 jel;9.2 jel;26.1 jel;13.0 jel;12.2 jel;22.6 jel;19.2 jel;18.2 jel;26.5 jel;28.5 jel;13.2 jel;15.3 jel;17.3 jel;6.0 jel;15.7 jel;13.7 jel;22.0 jel;12.9 jel;24.3 jel;13.4 jel;7.0 jel;9.8 jel;13.1 jel;17.8 jel;22.3 jel;12.6 jel;14.7 jel;16.2 jel;8.6 jel;28.8 jel;11.0 jel;24.2 jel;25.3 jel;0.8 jel;20.6 jel;13.3 jel;19.7 jel;0.3 jel;22.5 jel;14.0 jel;30.2 jel;14.3 jel;18.8 jel;15.0 jel;16.9 jel;6.0 jel;18.4 jel;16.8 jel;18.5 jel;20.8 jel;11.5 jel;15.7 jel;17.7 jel;12.6 jel;22.7 jel;3.5 jel;14.8 jel;25.4 jel;15.6 jel;13.8 jel;22.4 jel;10.4 jel;28.3 jel;17.8 jel;19.9 jel;18.8 jel;25.1 jel;9.0 jel;8.8 jel;18.4 jel;14.3 jel;14.0 jel;12.7 jel;18.5 jel;13.5 jel;22.8 jel;23.9 jel;24.3 jel;22.4 jel;19.1 jel;21.1 jel;13.4 jel;25.7 jel;20.2 jel;26.4 jel;25.6 jel;27.4 jel;25.0 jel;31.2 jel;28.5 jel;29.8 jel;14.0 jel;16.6 jel;14.9 jel;21.3 jel;22.9 jel;19.7 jel;20.3 jel;14.3 jel;13.6 jel;24.5 jel;15.0 jel;16.7 jel;21.1 jel;28.4 jel;10.6 jel;15.4 jel;14.6 jel;18.0 jel;13.2 jel;27.0 jel;19.3 jel;15.0 jel;23.8 jel;16.8 jel;15.9 jel;19.0 jel;15.4 jel;31.0 jel;8.3 jel;26.9 jel;16.4 jel;21.2 jel;18.4 jel;15.4 jel;9.8 jel;24.1 jel;13.4 jel;23.7 jel;21.9 jel;18.7 jel;2.6 jel;23.1 jel;7.4 jel;15.6 jel;6.2 jel;10.9 jel;22.2 jel;18.2 jel;8.1 jel;18.5 jel;12.8 jel;27.7 jel;20.0 jel;12.3 jel;20.6 jel;14.8 jel;19.5 jel;17.9 jel;26.9 jel;24.9 jel;26.4 jel;25.3 jel;28.0 jel;15.6 jel;18.3 jel;27.3 jel;17.6 jel;15.7 jel;16.8 jel;13.8 jel;25.1 jel;15.8 jel;25.8 jel;20.6 jel;11.7 jel;32.5 jel;17.9 jel;19.1 jel;17.3 jel;16.2 jel;25.6 jel;11.3 jel;18.4 jel;24.0 jel;-3.2 jel;32.6 jel;23.1 jel;19.5 jel;12.9 jel;15.5 jel;20.2 jel;20.7 jel;15.8 jel;17.5 jel;18.0 jel;17.6 jel;21.8 jel;26.8 jel;28.8 jel;15.4 jel;18.7 jel;1.3 jel;17.8 jel;14.1 jel;7.9 jel;19.1 jel;10.5 jel;11.7 jel;5.9 jel;13.5 jel;26.4 jel;11.8 jel;2.2 jel;16.8 jel;20.4 jel;25.1 jel;27.6 jel;13.8 jel;20.6 jel;18.3 jel;23.8 jel;30.3 jel;12.6 jel;32.3 jel;15.9 jel;8.4 jel;16.4 jel;24.9 jel;16.1 jel;19.5 jel;12.7 jel;28.5 jel;14.1 jel;10.9 jel;28.3 jel;13.9 jel;32.6 jel;20.0 jel;9.4 jel;7.3 jel;6.5 jel;23.5 jel;13.7 jel;19.7 jel;20.7 jel;14.3 jel;20.1 jel;25.3 jel;26.9 jel;17.2 jel;18.9 jel;21.7 jel;6.6 jel;25.9 jel;21.4 jel;17.5 jel;20.2 jel;20.3 jel;12.5 jel;37.5 jel;18.9 jel;13.1 jel;21.0 jel;17.2 jel;10.7 jel;23.3 jel;24.1 jel;16.5 jel;14.7 jel;21.9 jel;22.9 jel;17.0 jel;27.8 jel;8.7 jel;19.7 jel;17.9 jel;10.9 jel;19.4 jel;19.7 jel;3.9 jel;36.7 jel;21.5 jel;24.8 jel;14.5 jel;11.6 jel;18.9 jel;4.4 jel;26.2 jel;26.1 jel;21.7 jel;21.1 jel;24.2 jel;24.5 jel;12.3 jel;36.1 jel;23.2 jel;16.2 jel;18.2 jel;21.3 jel;6.4 jel;16.8 jel;18.8 jel;25.3 jel;9.4 jel;15.1 jel;14.0 jel;16.1 jel;31.1 jel;11.4 jel;24.0 jel;30.8 jel;14.2 jel;15.6 jel;16.7 jel;23.0 jel;33.2 jel;19.9 jel;21.7 jel;17.7 jel;21.4 jel;10.1 jel;27.6 jel;11.4 jel;24.8 jel;14.8 jel;25.2 jel;22.6 jel;38.2 jel;19.0 jel;22.7 jel;18.6 jel;22.2 jel;12.9 jel;23.8 jel;12.4 jel;14.0 jel;-4.8 jel;29.2 jel;23.3 jel;26.5 jel;22.2 jel;12.7 jel;10.7 jel;12.4 jel;19.4 jel;21.9 jel;3.9 jel;18.9 jel;10.4 jel;14.9 jel;22.8 jel;13.6 jel;12.4 jel;7.7 jel;26.3 jel;22.6 jel;12.8 jel;18.6 jel;16.4 jel;16.4 jel;28.5 jel;20.3 jel;30.0 jel;20.7 jel;13.5 jel;18.1 jel;13.2 jel;6.1 jel;18.8 jel;17.6 jel;22.9 jel;4.8 jel;30.4 jel;11.1 jel;19.6 jel;21.3 jel;23.8 jel;21.2 jel;19.7 jel;18.8 jel;17.7 jel;21.9 jel;17.3 jel;12.9 jel;18.6 jel;15.2 jel;15.7 jel;16.0 jel;17.9 jel;18.7 jel;22.1 jel;23.8 jel;18.9 jel;26.6 jel;12.7 jel;22.0 jel;22.3 jel;27.5 jel;27.8 jel;11.9 jel;0.2 jel;18.7 jel;19.2 jel;13.4 jel;14.0 jel;16.2 jel;22.2 jel;24.5 jel;20.1 jel;16.6 jel;11.6 jel;25.3 jel;15.4 jel;11.0 jel;19.6 jel;19.6 jel;23.3 jel;21.8 jel;8.4 jel;22.5 jel;19.6 jel;21.1 jel;20.9 jel;28.7 jel;17.8 jel;18.9 jel;21.8 jel;28.3 jel;19.8 jel;11.9 jel;15.1 jel;32.3 jel;13.1 jel;30.5 jel;14.5 jel;25.2 jel;16.0 jel;17.2 jel;12.4 jel;21.0 jel;25.0 jel;9.5 jel;25.0 jel;10.7 jel;25.2 jel;24.6 jel;20.6 jel;30.5 jel;15.6 jel;15.1 jel;29.9 jel;12.7 jel;25.2 jel;12.2 jel;14.4 jel;1.4 jel;16.2 jel;33.0 jel;23.0 jel;14.7 jel;8.3 jel;26.4 jel;16.0 jel;10.2 jel;1.8 jel;13.0 jel;6.4 jel;5.7 jel;19.1 jel;32.8 jel;13.7 jel;14.2 jel;8.4 jel;21.0 jel;16.3 jel;29.6 jel;26.9 jel;23.1 jel;14.9 jel;14.8 jel;14.0 jel;10.6 jel;12.8 jel;29.6 jel;10.7 jel;13.0 jel;17.2 jel;27.7 jel;12.9 jel;16.1 jel;16.5 jel;6.0 jel;11.0 jel;21.6 jel;12.9 jel;13.2 jel;9.7 jel;-5.0 jel;8.2 jel;31.3 jel;17.0 jel;19.9 jel;26.9 jel;17.8 jel;18.1 jel;23.1 jel;2.8 jel;19.7 jel;21.6 jel;18.8 jel;21.3 jel;12.7 jel;26.2 jel;13.1 jel;4.5 jel;23.7 jel;27.3 jel;9.5 jel;23.7 jel;10.7 jel;12.8 jel;27.8 jel;14.6 jel;30.2 jel;31.6 jel;29.8 jel;27.5 jel;28.6 jel;21.6 jel;22.2 jel;26.8 jel;20.4 jel;19.3 jel;22.5 jel;22.4 jel;11.1 jel;21.1 jel;9.8 jel;9.9 jel;22.8 jel;7.5 jel;7.5 jel;28.4 jel;21.5 jel;16.6 jel;20.2 jel;19.6 jel;22.1 jel;27.8 jel;11.9 jel;25.0 jel;1.0 jel;26.9 jel;19.4 jel;23.0 jel;25.8 jel;14.5 jel;10.5 jel;13.0 jel;17.1 jel;19.2 jel;29.8 jel;14.8 jel;6.5 jel;12.4 jel;23.7 jel;23.9 jel;23.2 jel;26.7 jel;21.3 jel;13.3 jel;15.2 jel;18.9 jel;19.7 jel;2.8 jel;15.5 jel;18.4 jel;21.9 jel;16.9 jel;21.1 jel;1.7 jel;6.5 jel;18.0 jel;28.1 jel;14.8 jel;21.7 jel;16.8 jel;26.5 jel;26.7 jel;25.4 jel;36.8 jel;13.1 jel;11.6 jel;28.0 jel;26.8 jel;24.0 jel;19.3 jel;13.9 jel;25.5 jel;30.3 jel;9.7 jel;18.2 jel;15.3 jel;8.4 jel;9.3 jel;12.2 jel;27.3 jel;12.0 jel;21.4 jel;16.2 jel;15.8 jel;28.1 jel;27.6 jel;14.5 jel;17.4 jel;1.8 jel;22.7 jel;17.6 jel;21.7 jel;20.9 jel;28.0 jel;11.1 jel;11.8 jel;19.3 jel;14.4 jel;22.0 jel;24.2 jel;23.2 jel;7.0 jel;15.8 jel;6.7 jel;19.0 jel;25.2 jel;21.4 jel;10.3 jel;16.5 jel;15.3 jel;15.3 jel;21.4 jel;9.6 jel;21.3 jel;27.9 jel;10.9 jel;20.1 jel;22.9 jel;22.8 jel;35.0 jel;10.7 jel;24.2 jel;26.6 jel;19.0 jel;24.6 jel;21.3 jel;18.1 jel;16.6 jel;6.0 jel;37.8 jel;23.7 jel;20.6 jel;18.7 jel;26.0 jel;13.2 jel;11.6 jel;19.5 jel;28.3 jel;27.8 jel;7.3 jel;20.3 jel;29.8 jel;25.8 jel;20.0 jel;33.0 jel;27.6 jel;16.8 jel;28.2 jel;33.2 jel;20.1 jel;18.0 jel;11.0 jel;19.2 jel;24.6 jel;13.6 jel;10.4 jel;13.7 jel;22.7 jel;14.8 jel;25.8 jel;16.1 jel;15.1 jel;15.4 jel;7.4 jel;19.6 jel;18.8 jel;16.1 jel;20.0 jel;19.7 jel;5.3 jel;16.1 jel;23.0 jel;22.0 jel;28.7 jel;8.3 jel;12.5 jel;19.9 jel;32.0 jel;16.8 jel;24.0 jel;18.3 jel;16.9 jel;28.0 jel;8.7 jel;9.3 jel;23.0 jel;10.5 jel;10.8 jel;12.6 jel;23.5 jel;18.8 jel;12.4 jel;14.2 jel;17.9 jel;12.1 jel;19.7 jel;25.5 jel;19.9 jel;16.7 jel;15.4 jel;20.4 jel;22.4 jel;18.4 jel;27.7 jel;28.8 jel;1.6 jel;4.3 jel;13.8 jel;6.7 jel;13.9 jel;23.7 jel;12.7 jel;12.9 jel;19.1 jel;17.6 jel;9.6 jel;11.1 jel;23.3 jel;5.9 jel;14.6 jel;21.3 jel;26.9 jel;16.7 jel;27.3 jel;23.0 jel;8.0 jel;18.5 jel;23.1 jel;12.6 jel;26.3 jel;21.0 jel;10.1 jel;19.8 jel;25.7 jel;17.9 jel;24.4 jel;19.1 jel;28.5 jel;15.3 jel;8.7 jel;13.7 jel;10.5 jel;24.3 jel;7.7 jel;13.9 jel;25.7 jel;15.5 jel;13.2 jel;11.9 jel;19.8 jel;14.1 jel;19.9 jel;15.9 jel;21.8 jel;13.8 jel;13.1 jel;27.8 jel;16.4 jel;16.4 jel;17.7 jel;24.0 jel;22.3 jel;21.7 jel;9.6 jel;23.6 jel;0.4 jel;17.4 jel;21.3 jel;14.9 jel;11.6 jel;19.3 jel;17.6 jel;-1.8 jel;20.8 jel;19.2 jel;25.4 jel;12.1 jel;13.1 jel;19.7 jel;14.2 jel;20.0 jel;23.5 jel;14.0 jel;10.5 jel;12.1 jel;19.6 jel;21.3 jel;19.5 jel;26.1 jel;9.8 jel;30.8 jel;19.4 jel;23.6 jel;18.4 jel;12.0 jel;19.0 jel;18.3 jel;11.3 jel;5.6 jel;10.8 jel;19.2 jel;15.7 jel;14.3 jel;9.0 jel;7.5 jel;4.4 jel;13.1 jel;25.0 jel;16.1 jel;15.6 jel;9.2 jel;22.3 jel;16.6 jel;17.1 jel;18.2 jel;23.9 jel;6.9 jel;15.5 jel;16.5 jel;22.9 jel;19.8 jel;26.5 jel;17.8 jel;28.3 jel;19.5 jel;22.6 jel;9.7 jel;18.1 jel;28.5 jel;10.0 jel;23.9 jel;26.1 jel;21.1 jel;28.2 jel;16.8 jel;13.3 jel;28.8 jel;21.5 jel;15.0 jel;24.8 jel;13.9 jel;10.5 jel;18.8 jel;23.3 jel;32.4 jel;11.4 jel;2.0 jel;30.2 jel;9.3 jel;13.8 jel;12.7 jel;15.2 jel;22.0 jel;16.7 jel;18.1 jel;18.1 jel;17.3 jel;10.4 jel;23.6 jel;12.3 jel;14.3 jel;15.6 jel;-0.6 jel;-1.7 jel;10.8 jel;7.9 jel;16.0 jel;15.2 jel;22.0 jel;29.4 jel;19.2 jel;10.0 jel;21.9 jel;27.4 jel;31.2 jel;22.7 jel;16.5 jel;26.0 jel;19.3 jel;28.2 jel;7.9 jel;21.9 jel;13.8 jel;17.1 jel;25.3 jel;7.8 jel;7.8 jel;19.4 jel;11.2 jel;21.6 jel;36.5 jel;12.7 jel;18.3 jel;23.0 jel;17.5 jel;26.1 jel;10.4 jel;13.4 jel;26.2 jel;24.0 jel;17.2 jel;16.3 jel;18.5 jel;12.1 jel;26.9 jel;7.8 jel;21.2 jel;18.0 jel;28.2 jel;16.2 jel;24.0 jel;21.5 jel;20.7 jel;14.4 jel;21.7 jel;5.5 jel;19.7 jel;21.8 jel;9.2 jel;24.2 jel;13.2 jel;14.0 jel;20.1 jel;6.8 jel;20.6 jel;0.3 jel;17.9 jel;34.8 jel;14.5 jel;33.3 jel;20.2 jel;21.9 jel;26.9 jel;21.2 jel;20.6 jel;21.9 jel;17.1 jel;31.7 jel;20.7 jel;20.3 jel;26.3 jel;30.6 jel;23.6 jel;0.8 jel;22.2 jel;22.5 jel;15.3 jel;14.3 jel;7.9 jel;11.4 jel;23.8 jel;13.7 jel;33.1 jel;35.5 jel;27.8 jel;14.4 jel;21.6 jel;17.3 jel;16.0 jel;22.7 jel;15.6 jel;26.8 jel;26.6 jel;24.5 jel;10.0 jel;16.5 jel;13.1 jel;14.6 jel;9.9 jel;18.5 jel;19.3 jel;13.8 jel;18.8 jel;23.1 jel;15.6 jel;18.8 jel;20.4 jel;-3.1 jel;7.7 jel;16.8 jel;26.3 jel;17.7 jel;21.7 jel;25.0 jel;21.6 jel;7.3 jel;9.0 jel;21.9 jel;16.0 jel;13.4 jel;19.9 jel;30.7 jel;20.3 jel;12.4 jel;17.4 jel;17.3 jel;26.2 jel;10.5 jel;21.0 jel;14.5 jel;38.1 jel;14.3 jel;24.5 jel;22.2 jel;20.1 jel;15.9 jel;11.8 jel;11.2 jel;36.8 jel;23.6 jel;14.8 jel;11.8 jel;19.7 jel;15.6 jel;28.3 jel;9.9 jel;10.7 jel;18.6 jel;15.6 jel;16.7 jel;5.2 jel;23.5 jel;13.5 jel;14.5 jel;15.7 jel;26.4 jel;13.5 jel;24.9 jel;7.1 jel;30.8 jel;5.0 jel;23.7 jel;21.2 jel;22.5 jel;15.7 jel;18.9 jel;6.5 jel;4.5 jel;16.9 jel;22.4 jel;22.2 jel;20.8 jel;16.0 jel;11.8 jel;23.1 jel;14.9 jel;24.8 jel;15.6 jel;19.1 jel;15.0 jel;14.2 jel;33.2 jel;18.8 jel;26.8 jel;26.9 jel;6.8 jel;18.4 jel;23.8 jel;19.6 jel;-0.6 jel;31.5 jel;29.4 jel;18.0 jel;9.8 jel;5.3 jel;17.8 jel;16.4 jel;32.9 jel;19.7 jel;27.6 jel;24.7 jel;19.9 jel;22.4 jel;12.8 jel;28.5 jel;34.2 jel;8.0 jel;13.3 jel;25.4 jel;16.6 jel;12.7 jel;18.4 jel;2.9 jel;18.6 jel;31.3 jel;10.7 jel;22.9 jel;16.7 jel;16.8 jel;27.5 jel;26.7 jel;16.7 jel;29.8 jel;12.1 jel;24.0 jel;18.1 jel;18.5 jel;15.7 jel;9.1 jel;7.1 jel;4.5 jel;7.4 jel;26.3 jel;5.5 jel;14.2 jel;16.0 jel;18.4 jel;26.3 jel;20.2 jel;21.2 jel;22.1 jel;13.8 jel;25.5 jel;13.4 jel;33.3 jel;23.6 jel;18.4 jel;23.6 jel;13.6 jel;21.9 jel;19.7 jel;12.0 jel;30.4 jel;31.2 jel;19.0 jel;31.0 jel;24.1 jel;7.7 jel;16.3 jel;11.3 jel;24.0 jel;22.3 jel;25.4 jel;19.6 jel;24.3 jel;11.1 jel;18.4 jel;20.4 jel;14.5 jel;18.9 jel;13.2 jel;9.3 jel;20.7 jel;14.1 jel;5.9 jel;27.7 jel;22.8 jel;17.0 jel;22.5 jel;8.1 jel;15.5 jel;20.5 jel;15.4 jel;21.5 jel;10.4 jel;17.2 jel;23.5 jel;18.9 jel;22.9 jel;18.8 jel;19.0 jel;21.1 jel;18.0 jel;12.6 jel;14.7 jel;26.4 jel;21.9 jel;18.2 jel;17.4 jel;20.1 jel;14.7 jel;20.4 jel;17.6 jel;16.8 jel;23.3 jel;23.5 jel;22.3 jel;9.1 jel;24.7 jel;5.6 jel;15.3 jel;16.3 jel;16.8 jel;26.0 jel;25.4 jel;18.9 jel;29.4 jel;11.8 jel;10.6 jel;32.6 jel;7.4 jel;14.8 jel;12.8 jel;8.0 jel;33.2 jel;6.6 jel;20.1 jel;0.1 jel;18.5 jel;31.9 jel;26.5 jel;11.8 jel;22.7 jel;11.1 jel;19.7 jel;26.3 jel;20.8 jel;18.0 jel;24.4 jel;16.4 jel;11.0 jel;1.9 jel;18.6 jel;27.4 jel;13.5 jel;17.6 jel;15.3 jel;22.7 jel;10.1 jel;16.3 jel;16.6 jel;24.8 jel;24.4 jel;20.3 jel;15.0 jel;26.0 jel;17.2 jel;22.5 jel;13.5 jel;12.0 jel;29.3 jel;16.7 jel;15.7 jel;16.9 jel;31.2 jel;14.7 jel;34.7 jel;18.8 jel;17.3 jel;9.5 jel;17.5 jel;7.4 jel;15.7 jel;5.1 jel;18.4 jel;8.1 jel;17.0 jel;7.9 jel;12.3 jel;28.2 jel;14.8 jel;13.3 jel;18.7 jel;14.9 jel;19.9 jel;16.4 jel;24.6 jel;8.1 jel;25.0 jel;16.0 jel;10.6 jel;22.4 jel;22.6 jel;24.2 jel;8.6 jel;11.9 jel;12.8 jel;16.4 jel;20.6 jel;19.7 jel;19.1 jel;6.3 jel;12.0 jel;6.0 jel;17.7 jel;19.5 jel;10.7 jel;24.8 jel;17.9 jel;14.9 jel;22.4 jel;-1.4 jel;25.3 jel;18.1 jel;25.9 jel;17.9 jel;18.1 jel;18.7 jel;13.3 jel;14.5 jel;12.1 jel;13.5 jel;15.6 jel;23.3 jel;22.8 jel;22.4 jel;7.3 jel;2.4 jel;23.7 jel;22.2 jel;22.4 jel;14.7 jel;16.0 jel;22.6 jel;21.8 jel;19.9 jel;22.8 jel;0.4 jel;14.0 jel;11.0 jel;9.4 jel;11.9 jel;6.3 jel;29.0 jel;20.3 jel;10.2 jel;15.6 jel;19.7 jel;27.2 jel;18.1 jel;15.4 jel;19.7 jel;27.3 jel;11.0 jel;19.9 jel;13.9 jel;26.7 jel;9.6 jel;10.3 jel;25.5 jel;23.3 jel;20.5 jel;21.4 jel;13.1 jel;20.4 jel;25.5 jel;20.2 jel;4.2 jel;21.5 jel;29.0 jel;14.6 jel;17.0 jel;32.7 jel;5.0 jel;25.4 jel;13.1 jel;19.3 jel;32.4 jel;22.5 jel;25.1 jel;25.5 jel;16.9 jel;13.2 jel;25.0 jel;21.2 jel;14.7 jel;17.3 jel;11.5 jel;20.0 jel;11.3 jel;18.0 jel;23.1 jel;20.5 jel;13.4 jel;20.3 jel;22.8 jel;21.7 jel;13.4 jel;19.4 jel;5.2 jel;8.5 jel;18.2 jel;15.7 jel;11.6 jel;1.2 jel;24.9 jel;18.8 jel;-1.2 jel;23.8 jel;14.6 jel;21.8 jel;21.8 jel;24.0 jel;12.2 jel;11.2 jel;23.7 jel;14.6 jel;15.1 jel;11.7 jel;24.9 jel;12.8 jel;25.0 jel;12.1 jel;23.1 jel;20.2 jel;11.6 jel;11.3 jel;20.1 jel;15.8 jel;13.9 jel;20.2 jel;9.5 jel;17.1 jel;17.1 jel;15.9 jel;17.5 jel;17.4 jel;8.7 jel;21.5 jel;7.2 jel;21.4 jel;15.5 jel;20.8 jel;7.7 jel;15.2 jel;17.5 jel;23.1 jel;24.9 jel;8.4 jel;23.4 jel;17.8 jel;21.6 jel;22.2 jel;12.9 jel;15.4 jel;39.4 jel;23.1 jel;8.8 jel;21.7 jel;16.4 jel;11.4 jel;14.6 jel;20.9 jel;11.5 jel;18.9 jel;16.3 jel;11.4 jel;19.8 jel;18.4 jel;4.4 jel;26.1 jel;22.0 jel;7.0 jel;18.7 jel;24.4 jel;-0.2 jel;22.0 jel;16.5 jel;11.0 jel;16.6 jel;3.9 jel;24.4 jel;12.2 jel;13.5 jel;9.8 jel;9.6 jel;12.6 jel;14.7 jel;16.5 jel;13.0 jel;14.1 jel;18.5 jel;20.5 jel;33.5 jel;19.0 jel;19.1 jel;16.1 jel;20.4 jel;16.9 jel;19.7 jel;-0.4 jel;15.6 jel;24.9 jel;29.9 jel;21.4 jel;13.5 jel;19.8 jel;17.0 jel;30.3 jel;28.7 jel;16.3 jel;21.1 jel;8.3 jel;12.7 jel;19.0 jel;33.1 jel;19.9 jel;18.9 jel;22.7 jel;1.4 jel;10.5 jel;17.4 jel;26.8 jel;22.4 jel;17.5 jel;21.5 jel;17.1 jel;20.8 jel;13.1 jel;7.0 jel;14.0 jel;24.4 jel;14.9 jel;11.0 jel;24.1 jel;11.5 jel;13.1 jel;14.0 jel;21.9 jel;27.0 jel;28.2 jel;27.0 jel;4.5 jel;20.3 jel;14.1 jel;18.5 jel;1.4 jel;10.6 jel;0.6 jel;29.6 jel;15.6 jel;7.5 jel;24.2 jel;35.0 jel;26.9 jel;13.4 jel;10.2 jel;12.0 jel;16.0 jel;21.9 jel;17.7 jel;8.1 jel;11.0 jel;11.5 jel;12.6 jel;13.3 jel;12.5 jel;15.7 jel;10.0 jel;8.0 jel;8.3 jel;30.9 jel;22.3 jel;37.6 jel;15.0 jel;6.4 jel;15.6 jel;14.6 jel;18.0 jel;21.7 jel;19.7 jel;23.8 jel;8.3 jel;18.2 jel;18.8 jel;15.2 jel;14.9 jel;24.0 jel;8.7 jel;16.1 jel;17.8 jel;19.1 jel;16.1 jel;27.0 jel;17.9 jel;7.5 jel;4.9 jel;10.3 jel;15.7 jel;16.1 jel;22.2 jel;14.6 jel;0.4 jel;10.8 jel;27.4 jel;23.8 jel;24.8 jel;15.8 jel;9.2 jel;13.1 jel;27.1 jel;20.1 jel;17.3 jel;17.0 jel;11.4 jel;20.3 jel;8.1 jel;31.4 jel;17.5 jel;16.1 jel;8.6 jel;16.2 jel;19.8 jel;21.8 jel;17.7 jel;14.4 jel;20.3 jel;9.2 jel;25.3 jel;31.1 jel;24.0 jel;8.3 jel;17.3 jel;24.4 jel;17.2 jel;18.4 jel;20.1 jel;6.8 jel;19.8 jel;17.1 jel;11.3 jel;18.1 jel;25.4 jel;26.6 jel;18.2 jel;21.2 jel;10.4 jel;23.4 jel;22.9 jel;11.6 jel;8.4 jel;2.0 jel;14.0 jel;28.2 jel;22.2 jel;19.7 jel;33.6 jel;14.7 jel;6.7 jel;24.3 jel;13.7 jel;13.2 jel;2.4 jel;3.9 jel;17.6 jel;27.7 jel;16.1 jel;1.0 jel;18.3 jel;11.6 jel;16.7 jel;13.3 jel;38.4 jel;17.9 jel;18.8 jel;8.4 jel;16.2 jel;12.0 jel;16.8 jel;31.2 jel;18.9 jel;27.7 jel;11.0 jel;13.8 jel;36.6 jel;2.0 jel;20.2 jel;21.2 jel;6.9 jel;12.6 jel;11.1 jel;15.6 jel;5.6 jel;22.5 jel;16.9 jel;20.1 jel;25.8 jel;18.0 jel;10.5 jel;17.7 jel;17.0 jel;14.5 jel;9.4 jel;6.1 jel;20.6 jel;12.6 jel;18.0 jel;10.4 jel;17.6 jel;21.8 jel;9.6 jel;18.9 jel;15.0 jel;26.8 jel;23.8 jel;21.6 jel;16.4 jel;13.6 jel;17.2 jel;17.9 jel;21.2 jel;30.1 jel;20.5 jel;16.9 jel;21.1 jel;7.7 jel;28.6 jel;35.5 jel;22.8 jel;4.1 jel;17.3 jel;20.1 jel;18.5 jel;19.5 jel;13.7 jel;18.7 jel;10.1 jel;23.9 jel;27.5 jel;19.7 jel;21.3 jel;24.0 jel;27.6 jel;13.9 jel;24.8 jel;12.3 jel;19.3 jel;17.3 jel;25.3 jel;14.1 jel;8.2 jel;22.2 jel;20.6 jel;21.9 jel;21.1 jel;0.2 jel;10.5 jel;11.3 jel;18.2 jel;12.9 jel;23.9 jel;14.2 jel;16.1 jel;0.2 jel;15.0 jel;6.1 jel;11.1 jel;17.6 jel;12.3 jel;9.0 jel;14.8 jel;25.8 jel;22.2 jel;20.9 jel;17.2 jel;17.9 jel;1.5 jel;7.1 jel;12.4 jel;25.1 jel;22.7 jel;27.7 jel;8.5 jel;26.8 jel;25.0 jel;15.3 jel;20.2 jel;16.6 jel;16.9 jel;21.7 jel;27.2 jel;19.4 jel;16.0 jel;33.8 jel;30.8 jel;29.8 jel;25.4 jel;22.4 jel;16.2 jel;26.4 jel;15.5 jel;1.3 jel;18.1 jel;16.5 jel;20.1 jel;10.1 jel;9.2 jel;23.9 jel;15.3 jel;18.4 jel;12.9 jel;-1.9 jel;11.2 jel;13.6 jel;24.0 jel;14.5 jel;22.0 jel;11.6 jel;8.4 jel;31.6 jel;21.0 jel;23.3 jel;16.9 jel;21.0 jel;15.4 jel;23.0 jel;17.0 jel;22.0 jel;6.4 jel;22.9 jel;5.2 jel;15.5 jel;17.0 jel;12.4 jel;11.7 jel;16.3 jel;14.5 jel;23.6 jel;26.2 jel;22.7 jel;20.3 jel;11.7 jel;20.2 jel;16.1 jel;24.9 jel;21.3 jel;19.7 jel;24.2 jel;16.3 jel;9.7 jel;24.0 jel;4.6 jel;18.9 jel;22.4 jel;14.6 jel;8.2 jel;17.3 jel;19.5 jel;20.1 jel;14.2 jel;17.4 jel;26.6 jel;24.6 jel;17.6 jel;13.1 jel;8.4 jel;8.8 jel;15.8 jel;14.1 jel;16.9 jel;10.5 jel;16.2 jel;30.0 jel;18.2 jel;22.1 jel;5.2 jel;12.9 jel;9.6 jel;14.7 jel;14.7 jel;25.9 jel;22.1 jel;22.0 jel;17.0 jel;9.0 jel;16.8 jel;9.1 jel;16.4 jel;25.2 jel;17.3 jel;18.5 jel;15.3 jel;18.2 jel;20.0 jel;29.7 jel;13.2 jel;9.0 jel;23.1 jel;9.9 jel;17.7 jel;24.6 jel;19.2 jel;22.2 jel;19.9 jel;21.8 jel;21.2 jel;12.5 jel;20.6 jel;17.2 jel;23.3 jel;8.9 jel;15.6 jel;20.3 jel;21.3 jel;20.4 jel;30.7 jel;27.0 jel;24.1 jel;11.1 jel;20.7 jel;20.1 jel;10.6 jel;22.7 jel;6.7 jel;13.5 jel;19.5 jel;17.9 jel;15.0 jel;11.1 jel;2.9 jel;21.6 jel;13.4 jel;24.0 jel;16.1 jel;16.9 jel;13.4 jel;15.2 jel;9.8 jel;34.3 jel;27.4 jel;8.7 jel;24.3 jel;17.9 jel;16.9 jel;14.0 jel;14.8 jel;21.9 jel;15.8 jel;19.1 jel;25.2 jel;10.5 jel;14.7 jel;9.5 jel;35.3 jel;10.8 jel;26.5 jel;25.7 jel;22.9 jel;-0.6 jel;9.7 jel;16.8 jel;6.3 jel;11.6 jel;20.5 jel;11.5 jel;13.3 jel;25.7 jel;20.2 jel;16.9 jel;23.3 jel;13.2 jel;4.2 jel;23.5 jel;8.4 jel;21.4 jel;15.3 jel;18.1 jel;11.0 jel;17.9 jel;30.9 jel;23.7 jel;16.5 jel;18.6 jel;15.6 jel;21.4 jel;27.9 jel;2.4 jel;16.7 jel;20.4 jel;18.1 jel;11.5 jel;20.0 jel;15.8 jel;10.0 jel;6.7 jel;20.9 jel;13.0 jel;20.2 jel;20.3 jel;23.0 jel;31.3 jel;14.8 jel;18.2 jel;10.6 jel;19.9 jel;20.1 jel;25.0 jel;18.3 jel;23.8 jel;24.7 jel;22.8 jel;11.1 jel;10.0 jel;23.6 jel;27.0 jel;17.3 jel;14.0 jel;7.8 jel;18.8 jel;24.8 jel;9.3 jel;10.2 jel;22.7 jel;12.8 jel;18.4 jel;25.8 jel;34.0 jel;14.5 jel;13.3 jel;14.2 jel;15.7 jel;19.0 jel;15.1 jel;24.2 jel;21.9 jel;12.7 jel;11.3 jel;20.3 jel;14.6 jel;17.3 jel;20.6 jel;16.8 jel;19.1 jel;16.5 jel;27.5 jel;12.3 jel;13.0 jel;14.5 jel;20.6 jel;11.6 jel;11.5 jel;12.8 jel;16.9 jel;25.8 jel;16.5 jel;24.7 jel;12.5 jel;25.4 jel;3.9 jel;24.0 jel;24.6 jel;26.9 jel;10.4 jel;7.9 jel;22.7 jel;17.3 jel;18.4 jel;6.3 jel;26.0 jel;28.2 jel;15.7 jel;15.9 jel;23.3 jel;14.9 jel;19.6 jel;11.7 jel;10.7 jel;20.2 jel;7.3 jel;18.5 jel;19.6 jel;10.0 jel;20.1 jel;26.1 jel;17.0 jel;30.4 jel;21.2 jel;19.4 jel;20.5 jel;29.2 jel;24.5 jel;11.2 jel;16.6 jel;18.2 jel;24.3 jel;22.2 jel;11.7 jel;20.4 jel;14.8 jel;19.2 jel;18.5 jel;19.2 jel;22.8 jel;13.0 jel;10.1 jel;20.4 jel;29.1 jel;15.5 jel;29.6 jel;14.0 jel;21.6 jel;14.2 jel;19.8 jel;28.6 jel;26.2 jel;19.5 jel;9.9 jel;20.8 jel;19.5 jel;16.9 jel;11.5 jel;17.7 jel;10.6 jel;10.7 jel;14.9 jel;26.2 jel;15.5 jel;9.5 jel;9.8 jel;18.1 jel;5.4 jel;14.2 jel;17.1 jel;27.8 jel;25.5 jel;25.0 jel;15.0 jel;15.9 jel;22.7 jel;16.3 jel;20.0 jel;4.2 jel;19.9 jel;22.1 jel;19.7 jel;17.1 jel;14.6 jel;27.2 jel;13.7 jel;16.9 jel;25.6 jel;16.1 jel;9.1 jel;10.1 jel;16.6 jel;29.6 jel;21.6 jel;16.9 jel;22.3 jel;13.3 jel;21.3 jel;25.1 jel;21.5 jel;14.4 jel;10.1 jel;5.4 jel;10.7 jel;22.7 jel;11.7 jel;9.5 jel;27.1 jel;19.3 jel;15.3 jel;13.4 jel;26.4 jel;7.7 jel;23.1 jel;19.4 jel;25.7 jel;30.1 jel;21.3 jel;14.5 jel;11.9 jel;20.8 jel;17.7 jel;17.0 jel;15.1 jel;21.8 jel;17.2 jel;21.1 jel;20.6 jel;17.4 jel;19.9 jel;14.4 jel;2.8 jel;17.1 jel;10.1 jel;14.4 jel;21.9 jel;14.5 jel;18.8 jel;21.4 jel;20.5 jel;19.3 jel;16.9 jel;18.6 jel;24.0 jel;17.7 jel;6.6 jel;13.7 jel;11.0 jel;8.1 jel;18.9 jel;17.1 jel;17.9 jel;12.0 jel;17.9 jel;11.3 jel;18.3 jel;17.7 jel;23.9 jel;37.8 jel;15.4 jel;18.8 jel;19.2 jel;24.9 jel;26.8 jel;15.4 jel;16.0 jel;21.5 jel;20.4 jel;22.1 jel;20.3 jel;28.8 jel;0.0 jel;14.4 jel;17.0 jel;18.3 jel;12.9 jel;6.1 jel;12.5 jel;15.5 jel;15.5 jel;-0.3 jel;21.8 jel;2.5 jel;8.1 jel;16.0 jel;13.0 jel;20.4 jel;19.2 jel;23.7 jel;10.6 jel;18.7 jel;8.9 jel;20.4 jel;9.0 jel;15.8 jel;15.5 jel;19.1 jel;6.7 jel;14.9 jel;12.9 jel;23.2 jel;13.5 jel;27.0 jel;20.6 jel;29.9 jel;14.7 jel;26.1 jel;17.7 jel;27.6 jel;22.0 jel;8.0 jel;13.3 jel;33.2 jel;22.1 jel;20.9 jel;20.5 jel;20.5 jel;16.2 jel;19.0 jel;22.0 jel;28.2 jel;22.3 jel;23.0 jel;19.1 jel;13.2 jel;22.2 jel;23.8 ================================================ FILE: src/test/resources/samples/measurements-short.out ================================================ {a=1.0/1.0/1.0, b=1.0/1.5/2.0} ================================================ FILE: src/test/resources/samples/measurements-short.txt ================================================ a;1.0 b;1.0 b;2.0 ================================================ FILE: src/test/resources/samples/measurements-shortest.out ================================================ {a=1.0/1.0/1.0} ================================================ FILE: src/test/resources/samples/measurements-shortest.txt ================================================ a;1.0 ================================================ FILE: test.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # set -euo pipefail DEFAULT_INPUT="src/test/resources/samples/*.txt" FORK=${1:-""} INPUT=${2:-$DEFAULT_INPUT} if [ "$#" -eq 0 ] || [ "$#" -gt 2 ] || [ "$FORK" = "-h" ]; then echo "Usage: ./test.sh [input file pattern]" echo echo "For each test sample matching (default '$DEFAULT_INPUT')" echo "runs implementation and diffs the result with the expected output." echo "Note that optional should be quoted if contains wild cards." echo echo "Examples:" echo "./test.sh baseline" echo "./test.sh baseline src/test/resources/samples/measurements-1.txt" echo "./test.sh baseline 'src/test/resources/samples/measurements-*.txt'" exit 1 fi if [ -f "./prepare_$FORK.sh" ]; then "./prepare_$FORK.sh" fi for sample in $(ls $INPUT); do echo "Validating calculate_average_$FORK.sh -- $sample" rm -f measurements.txt ln -s $sample measurements.txt diff --color=always <("./calculate_average_$FORK.sh" | ./tocsv.sh) <(./tocsv.sh < ${sample%.txt}.out) done rm measurements.txt ================================================ FILE: test_all.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # set -euo pipefail INPUT=${1:-""} if [ "$INPUT" = "-h" ] || [ "$#" -gt 1 ]; then echo "Usage: ./test_all.sh [input file pattern]" echo echo "For each available fork run ./test.sh [input file pattern]." echo "Note that optional should be quoted if contains wild cards." echo echo "Examples:" echo "./test_all.sh" echo "./test_all.sh 2>/dev/null" echo "./test_all.sh src/test/resources/samples/measurements-1.txt" echo "./test_all.sh 'src/test/resources/samples/measurements-*.txt'" exit 1 fi if [ -t 1 ]; then GREEN='\033[0;32m' RED='\033[0;31m' RESET='\033[0m' else GREEN="" RED="" RESET="" fi WITH_TIMEOUT="" if [ -x "$(command -v timeout)" ]; then WITH_TIMEOUT="timeout -s KILL 5s" elif [ -x "$(command -v gtimeout)" ]; then # MacOS from `brew install coreutils` WITH_TIMEOUT="gtimeout -s KILL 5s" else echo "$0: timeout command not available, tests may run indefinitely long." 1>&2 fi for impl in $(ls calculate_average_*.sh | sort); do noext="${impl%%.sh}" fork=${noext##calculate_average_} # ./test.sh calls ./prepare_$fork.sh e.g. to build native image # which may take some time. # Here we run it upfront, assuming that prepare result is cached # to avoid timeout due to long preparation. if [ -f "./prepare_$fork.sh" ]; then if ! output=$("./prepare_$fork.sh" 2>&1); then echo "$output" 1>&2 echo "FAIL $fork" continue fi fi if output=$($WITH_TIMEOUT ./test.sh "$fork" "$INPUT" 2>&1); then echo -e "${GREEN}PASS${RESET} $fork" elif [ $? -eq 137 ]; then echo -e "${RED}TIME${RESET} $fork" else echo "$output" 1>&2 echo -e "${RED}FAIL${RESET} $fork" fi done ================================================ FILE: test_ci.sh ================================================ #!/bin/bash # # Copyright 2023 The original authors # # 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. # set -eo pipefail if [ -z "$1" ] then echo "Usage: test_ci.sh ( ...)" echo " for each fork, there must be a 'calculate_average_.sh' script and an optional 'prepare_.sh'." exit 1 fi BOLD_WHITE='\033[1;37m' CYAN='\033[0;36m' GREEN='\033[0;32m' PURPLE='\033[0;35m' BOLD_RED='\033[1;31m' RED='\033[0;31m' BOLD_YELLOW='\033[1;33m' RESET='\033[0m' # No Color MEASUREMENTS_FILE="measurements_10M.txt" RUNS=5 DEFAULT_JAVA_VERSION="21.0.1-open" RUN_TIME_LIMIT=300 # seconds TIMEOUT="" if [ "$(uname -s)" == "Linux" ]; then TIMEOUT="timeout -v $RUN_TIME_LIMIT" else # MacOs if [ -x "$(command -v gtimeout)" ]; then TIMEOUT="gtimeout -v $RUN_TIME_LIMIT" # from `brew install coreutils` else echo -e "${BOLD_YELLOW}WARNING${RESET} gtimeout not available, benchmark runs may take indefinitely long." fi fi function check_command_installed { if ! [ -x "$(command -v $1)" ]; then echo "Error: $1 is not installed." >&2 exit 1 fi } function print_and_execute() { echo "+ $@" >&2 "$@" } check_command_installed java # Validate that ./calculate_average_.sh exists for each fork for fork in "$@"; do if [ ! -f "./calculate_average_$fork.sh" ]; then echo -e "${BOLD_RED}ERROR${RESET}: ./calculate_average_$fork.sh does not exist." >&2 exit 1 fi done ## SDKMAN Setup # 1. Custom check for sdkman installed; not sure why check_command_installed doesn't detect it properly if [ ! -f "$HOME/.sdkman/bin/sdkman-init.sh" ]; then echo -e "${BOLD_RED}ERROR${RESET}: sdkman is not installed." >&2 exit 1 fi # 2. Init sdkman in this script source "$HOME/.sdkman/bin/sdkman-init.sh" # 3. make sure the default java version is installed if [ ! -d "$HOME/.sdkman/candidates/java/$DEFAULT_JAVA_VERSION" ]; then print_and_execute sdk install java $DEFAULT_JAVA_VERSION fi # 4. Install missing SDK java versions in any of the prepare_*.sh scripts for the provided forks for fork in "$@"; do if [ -f "./prepare_$fork.sh" ]; then grep -h "^sdk use" "./prepare_$fork.sh" | cut -d' ' -f4 | while read -r version; do if [ ! -d "$HOME/.sdkman/candidates/java/$version" ]; then print_and_execute sdk install java $version fi done || true # grep returns exit code 1 when no match, `|| true` prevents the script from exiting early fi done ## END - SDKMAN Setup # Run tests and benchmark for each fork filetimestamp=$(date +"%Y%m%d%H%M%S") # same for all fork.out files from this run failed=() for fork in "$@"; do set +e # we don't want prepare.sh, test.sh or hyperfine failing on 1 fork to exit the script early # Run prepare script if [ -f "./prepare_$fork.sh" ]; then print_and_execute source "./prepare_$fork.sh" else print_and_execute sdk use java $DEFAULT_JAVA_VERSION fi # Run the test suite print_and_execute $TIMEOUT ./test.sh $fork if [ $? -ne 0 ]; then failed+=("$fork") echo "" echo -e "${BOLD_RED}FAILURE${RESET}: ./test.sh $fork failed" exit 1 fi echo "" done ================================================ FILE: tocsv.sh ================================================ #!/bin/sh # # Copyright 2023 The original authors # # 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. # exec sed ' # # Transform calculate_average*.sh output into semicolon-separated values, one per line. # # 1. remove "{" and "}" s/[{}]//g; # 2. replace "=" and "/" with semicolon s/[=/]/;/g; # 3. id may contain comma, e.g. "Washington, D.C.;-15.1;14.8;44.8, Wau;-2.1;27.4;53.4" # so replace ", " with a newline only if it is preceded by a digit s/\([0-9]\), /\1\n/g '