├── LICENSE_1_0.txt
├── Makefile
├── README.md
├── plots.gnuplot
├── plots
├── .done
├── gbooks_freq.svg
├── m3killer.svg
├── organpipe.svg
├── plot.svg
├── random.svg
├── random01.svg
├── rotated.svg
└── sorted.svg
├── results
├── gbooks_freq
├── gbooks_freq.comps
├── gbooks_freq.rsd
├── gbooks_freq.swaps
├── m3killer
├── m3killer.comps
├── m3killer.max_comps
├── m3killer.rsd
├── m3killer.swaps
├── organpipe
├── organpipe.comps
├── organpipe.max_comps
├── organpipe.rsd
├── organpipe.swaps
├── random
├── random.comps
├── random.max_comps
├── random.rsd
├── random.swaps
├── random01
├── random01.comps
├── random01.max_comps
├── random01.rsd
├── random01.swaps
├── rotated
├── rotated.comps
├── rotated.max_comps
├── rotated.rsd
├── rotated.swaps
├── sorted
├── sorted.comps
├── sorted.max_comps
├── sorted.rsd
└── sorted.swaps
├── src
├── bfprt_baseline.cpp
├── common.h
├── instrumented_double.h
├── main.cpp
├── median_of_ninthers.cpp
├── median_of_ninthers.h
├── ninther.cpp
├── nth_element.cpp
├── nth_element_reference.cpp
├── rnd3pivot.cpp
└── timer.h
└── support
├── aggregate_ngrams.d
├── binarize.d
└── generate.d
/LICENSE_1_0.txt:
--------------------------------------------------------------------------------
1 | Boost Software License - Version 1.0 - August 17th, 2003
2 |
3 | Permission is hereby granted, free of charge, to any person or organization
4 | obtaining a copy of the software and accompanying documentation covered by
5 | this license (the "Software") to use, reproduce, display, distribute,
6 | execute, and transmit the Software, and to prepare derivative works of the
7 | Software, and to permit third-parties to whom the Software is furnished to
8 | do so, all subject to the following:
9 |
10 | The copyright notices in the Software and this entire statement, including
11 | the above license grant, this restriction and the following disclaimer,
12 | must be included in all copies of the Software, in whole or in part, and
13 | all derivative works of the Software, unless such copies or derivative
14 | works are solely in the form of machine-executable object code generated by
15 | a source language processor.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Definitions pertanining to the project
2 |
3 | # $D is a directory for data that is generated but should not be discarded
4 | # naively. $T is a temporary directory for object files, generated binaries, and
5 | # intermediate results. $R is the directory where the final summary results are
6 | # kept.
7 | D = $(HOME)/data/median
8 | R = results
9 | T = /tmp/MedianOfNinthers
10 | $(shell mkdir -p $D $R $T)
11 |
12 | # Data sizes present in the paper
13 | SIZES = 10000 31620 100000 316220 1000000 3162280 10000000
14 |
15 | # Compiler flags for instrumented and fast build
16 | CFLAGS_INSTRUMENTED = -std=c++14 -O3 -DCOUNT_SWAPS -DCOUNT_WASTED_SWAPS -DCOUNT_COMPARISONS
17 | CFLAGS = -std=c++14 -O3 -DNDEBUG -DMEASURE_TIME
18 |
19 | # Utils
20 | XPROD = $(foreach a,$1,$(foreach b,$3,$a$2$b))
21 | XPROD3 = $(call XPROD,$1,$2,$(call XPROD,$3,$4,$5))
22 |
23 | # Sources (without algos)
24 | CXX_CODE = $(addprefix src/,main.cpp common.h timer.h)
25 |
26 | # Algorithms
27 | ALGOS = nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
28 |
29 | # Data sets (synthetic)
30 | SYNTHETIC_DATASETS = m3killer organpipe random random01 rotated sorted
31 | # Benchmark files we're interested in
32 | MK_OUTFILES = MEASUREMENTS_$1 = $(foreach n,$(SIZES),$(foreach a,$(ALGOS),$T/$1_$n_$a.time))
33 | $(foreach d,$(SYNTHETIC_DATASETS),$(eval $(call MK_OUTFILES,$d)))
34 |
35 | # Data sets (Google Books)
36 | L = a b c d e f g h i j k l m n o p q r s t u v w x y z
37 | GBOOKS_LANGS = eng fre ger ita rus spa
38 | GBOOKS_CORPORA = $(foreach l,$(GBOOKS_LANGS),googlebooks-$l-all-1gram-20120701)
39 |
40 | # All measurement output files
41 | MEASUREMENT_OUTPUTS = $(foreach x,$(SYNTHETIC_DATASETS),$(MEASUREMENTS_$x)) \
42 | $(foreach x,$(call XPROD,$(ALGOS),_,$(GBOOKS_CORPORA)),$T/$x_freq.time)
43 |
44 | # Results files will be included in the paper. Change this to affect what
45 | # experiments are run.
46 | DATASETS = $(SYNTHETIC_DATASETS) gbooks_freq
47 | RESULTS = $(addprefix $R/,$(DATASETS))
48 | PLOTS = $(addsuffix .svg,$(addprefix plots/,$(DATASETS)))
49 |
50 | ###############################################################################
51 | # Targets of interest
52 | ###############################################################################
53 |
54 | all: $(RESULTS) plots/.done
55 |
56 | clean:
57 | latexmk -C
58 | rm -rf $D/*.tmp $R*/* $T*/ $(PLOTS) plots/.done
59 |
60 | pristine:
61 | rm -rf $D/ $R/* $T/ $(PLOTS) plots/.done
62 |
63 | ################################################################################
64 | # Data
65 | ################################################################################
66 |
67 | .PHONY: data
68 | data: $(foreach x,$(call XPROD,$(SYNTHETIC_DATASETS),_,$(SIZES)),$D/$x.dat) $(foreach c,$(GBOOKS_CORPORA),$D/$c_freq.dat)
69 |
70 | $D/googlebooks-%_freq.dat: $D/googlebooks-%.txt
71 | cut -f 2 <$^ | rdmd -O -inline support/binarize.d >$@.tmp
72 | mv $@.tmp $@
73 | $D/googlebooks-%.txt: $(foreach l,$L,$D/googlebooks-%-$l.gz)
74 | gunzip --stdout $^ | rdmd -O -inline support/aggregate_ngrams.d >$@.tmp
75 | mv $@.tmp $@
76 | $D/googlebooks-%.gz:
77 | curl --fail http://storage.googleapis.com/books/ngrams/books/googlebooks-$*.gz >$@.tmp
78 | mv $@.tmp $@
79 |
80 | define GENERATE_DATA
81 | $D/$1_%.dat: support/generate.d
82 | rdmd -O -inline support/generate.d --kind=$1 --n=$$* >$$@.tmp
83 | mv $$@.tmp $$@
84 | endef
85 |
86 | $(foreach d,$(SYNTHETIC_DATASETS),$(eval $(call GENERATE_DATA,$d)))
87 |
88 | ################################################################################
89 | # Measurements
90 | ################################################################################
91 |
92 | .PHONY: measurements $(SYNTHETIC_DATASETS) $(GBOOKS_CORPORA)
93 | measurements: $(SYNTHETIC_DATASETS) $(GBOOKS_CORPORA)
94 |
95 | gbooks: $(foreach x,$(call XPROD,$(GBOOKS_CORPORA),_freq_,$(ALGOS)),$T/$x.time)
96 |
97 | $(foreach d,$(SYNTHETIC_DATASETS),$(eval \
98 | $d: $(MEASUREMENTS_$d);\
99 | ))
100 |
101 | define MAKE_MEASUREMENT
102 | $T/%_$1.time: $T/$1 $T/$1_instrumented $D/%.dat
103 | $T/$1 $D/$$*.dat >$T/$$*_$1.tmp
104 | $T/$1_instrumented $D/$$*.dat >>$T/$$*_$1.tmp
105 | mv $T/$$*_$1.tmp $T/$$*_$1.stats
106 | sed -n '/^milliseconds: /s/milliseconds: //p' $T/$$*_$1.stats >$T/$$*_$1.tmp
107 | mv $T/$$*_$1.tmp $$@
108 | sed -n '/^rsd: /s/rsd: //p' $T/$$*_$1.stats >$T/$$*_$1.tmp
109 | mv $T/$$*_$1.tmp $T/$$*_$1.rsd
110 | sed -n '/^comparisons: /s/comparisons: //p' $T/$$*_$1.stats >$T/$$*_$1.tmp
111 | mv $T/$$*_$1.tmp $T/$$*_$1.comps
112 | sed -n '/^max_comparisons: /s/max_comparisons: //p' $T/$$*_$1.stats >$T/$$*_$1.tmp
113 | mv $T/$$*_$1.tmp $T/$$*_$1.max_comps
114 | sed -n '/^swaps: /s/swaps: //p' $T/$$*_$1.stats >$T/$$*_$1.tmp
115 | mv $T/$$*_$1.tmp $T/$$*_$1.swaps
116 | $T/$1: src/$1.cpp $(CXX_CODE)
117 | $(CXX) $(CFLAGS) -o $$@ $$(patsubst %.h,,$$^)
118 | $T/$1_instrumented: src/$1.cpp $(CXX_CODE)
119 | $(CXX) $(CFLAGS_INSTRUMENTED) -o $$@ $$(patsubst %.h,,$$^)
120 | endef
121 |
122 | $(foreach a,$(ALGOS),$(eval $(call MAKE_MEASUREMENT,$a)))
123 |
124 | ################################################################################
125 | # Assemble measurement results
126 | ################################################################################
127 |
128 | $R/gbooks_freq: gbooks
129 | echo "Corpus" $(foreach a,$(ALGOS), " $a") >$@.tmp
130 | $(foreach l,$(GBOOKS_LANGS),printf "$l " >>$@.tmp && paste $(foreach a,$(ALGOS),$T/googlebooks-$l-all-1gram-20120701_freq_$a.time) >>$@.tmp &&) true
131 | mv $@.tmp $@
132 | echo "Corpus" $(foreach a,$(ALGOS), " $a") >$@.tmp
133 | $(foreach l,$(GBOOKS_LANGS),printf "$l " >>$@.tmp && paste $(foreach a,$(ALGOS),$T/googlebooks-$l-all-1gram-20120701_freq_$a.rsd) >>$@.tmp &&) true
134 | mv $@.tmp $@.rsd
135 | echo "Corpus" $(foreach a,$(ALGOS), " $a") >$@.tmp
136 | $(foreach l,$(GBOOKS_LANGS),printf "$l " >>$@.tmp && paste $(foreach a,$(ALGOS),$T/googlebooks-$l-all-1gram-20120701_freq_$a.comps) >>$@.tmp &&) true
137 | mv $@.tmp $@.comps
138 | echo "Corpus" $(foreach a,$(ALGOS), " $a") >$@.tmp
139 | $(foreach l,$(GBOOKS_LANGS),printf "$l " >>$@.tmp && paste $(foreach a,$(ALGOS),$T/googlebooks-$l-all-1gram-20120701_freq_$a.swaps) >>$@.tmp &&) true
140 | mv $@.tmp $@.swaps
141 |
142 | define MAKE_RESULT_FILE
143 | $R/$1: $$(MEASUREMENTS_$1)
144 | echo "Size" $$(foreach a,$$(ALGOS), " $$a") >$$@.tmp
145 | $$(foreach n,$$(SIZES),printf "$$n\t" >>$$@.tmp && paste $$(foreach a,$$(ALGOS),$$T/$1_$$n_$$a.time) >>$$@.tmp &&) true
146 | mv $$@.tmp $$@
147 | # Relative Standard Deviation
148 | echo "Size" $$(foreach a,$$(ALGOS), " $$a") >$$@.tmp
149 | $$(foreach n,$$(SIZES),printf "$$n\t" >>$$@.tmp && paste $$(foreach a,$$(ALGOS),$$T/$1_$$n_$$a.rsd) >>$$@.tmp &&) true
150 | mv $$@.tmp $$@.rsd
151 | # Comparisons
152 | echo "Size" $$(foreach a,$$(ALGOS), " $$a") >$$@.tmp
153 | $$(foreach n,$$(SIZES),printf "$$n\t" >>$$@.tmp && paste $$(foreach a,$$(ALGOS),$$T/$1_$$n_$$a.comps) >>$$@.tmp &&) true
154 | mv $$@.tmp $$@.comps
155 | # Worst-case Comparisons
156 | echo "Size" $$(foreach a,$$(ALGOS), " $$a") >$$@.tmp
157 | $$(foreach n,$$(SIZES),printf "$$n\t" >>$$@.tmp && paste $$(foreach a,$$(ALGOS),$$T/$1_$$n_$$a.max_comps) >>$$@.tmp &&) true
158 | mv $$@.tmp $$@.max_comps
159 | # Swaps
160 | echo "Size" $$(foreach a,$$(ALGOS), " $$a") >$$@.tmp
161 | $$(foreach n,$$(SIZES),printf "$$n\t" >>$$@.tmp && paste $$(foreach a,$$(ALGOS),$$T/$1_$$n_$$a.swaps) >>$$@.tmp &&) true
162 | mv $$@.tmp $$@.swaps
163 | endef
164 |
165 | $(foreach a,$(SYNTHETIC_DATASETS),$(eval $(call MAKE_RESULT_FILE,$a)))
166 |
167 | ################################################################################
168 | # Plots
169 | ################################################################################
170 |
171 | plots/.done : $(RESULTS)
172 | gnuplot plots.gnuplot
173 | touch $@
174 |
175 | # Supplemental dependencies
176 | median_of_ninthers.cpp: median_of_ninthers.h
177 |
178 | # Don't delete intermediary files
179 | .SECONDARY:
180 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repository contains the implementation and benchmarks for the paper ["Fast Deterministic Selection"](https://erdani.com/research/sea2017.pdf).
2 |
3 | # Prerequisites
4 |
5 | You need the GNU C++ compiler (or the clang drop-in replacement). To prepare the datasets, you need the D compiler dmd, downloadable from http://dlang.org/download.html.
6 |
7 | # Running Benchmarks
8 |
9 | To initiate bulding and running benchmarks, simply run `make` from the repository directory. The first run will take a long time because it downloads and preprocesses the Google Ngrams corpus, The default directory of all corpora is `$(HOME)/data/median`.
10 |
11 | Binaries and intermediate files are produced by default in `/tmp/MedianOfNinthers`. The final (summarized) results are output in `./results`. You may change these locations by editing `Makefile`.
12 |
--------------------------------------------------------------------------------
/plots.gnuplot:
--------------------------------------------------------------------------------
1 | set term svg mouse standalone size 800, 600
2 | set boxwidth 0.9 relative
3 | set style data histogram
4 | set style histogram clustered
5 | set style fill solid 1.0 border lt -1
6 | set xlabel "Input size"
7 | set ylabel "Speedup"
8 | set yrange [0:]
9 | set grid noxtics ytics
10 | set key outside
11 | set ytics 0.1
12 |
13 | xlabel(n) = gprintf("%.2t · 10^{%T}", n)
14 |
15 | do for [data in "m3killer organpipe random random01 rotated sorted"] {
16 | set title "Speedup relative to GNUIntroselect (" . data . " dataset)"
17 | input = "results/" . data
18 | set output "plots/" . data . ".svg"
19 | plot input using (column("nth_element")/column("nth_element")):xticlabels(xlabel($1)) title "GNUIntroselect", \
20 | input using (column("nth_element")/column("rnd3pivot")):xticlabels(xlabel($1)) title "RND3Pivot", \
21 | input using (column("nth_element")/column("ninther")):xticlabels(xlabel($1)) title "Ninther", \
22 | input using (column("nth_element")/column("median_of_ninthers")):xticlabels(xlabel($1)) title "QuickselectAdaptive"
23 | }
24 |
25 | set title "Speedup relative to GNUIntroselect (googlebooks dataset)"
26 | input = "results/gbooks_freq"
27 | set output "plots/gbooks_freq.svg"
28 | plot input using (column("nth_element")/column("nth_element")):xticlabels(1) title "GNUIntroselect", \
29 | input using (column("nth_element")/column("rnd3pivot")):xticlabels(1) title "RND3Pivot", \
30 | input using (column("nth_element")/column("ninther")):xticlabels(1) title "Ninther", \
31 | input using (column("nth_element")/column("median_of_ninthers")):xticlabels(1) title "QuickselectAdaptive"
32 |
--------------------------------------------------------------------------------
/plots/.done:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andralex/MedianOfNinthers/9fa75b267e74d67b15dbd555311f1ea5f8568e1b/plots/.done
--------------------------------------------------------------------------------
/plots/gbooks_freq.svg:
--------------------------------------------------------------------------------
1 |
2 |
924 |
925 |
--------------------------------------------------------------------------------
/plots/m3killer.svg:
--------------------------------------------------------------------------------
1 |
2 |
910 |
911 |
--------------------------------------------------------------------------------
/plots/random01.svg:
--------------------------------------------------------------------------------
1 |
2 |
910 |
911 |
--------------------------------------------------------------------------------
/results/gbooks_freq:
--------------------------------------------------------------------------------
1 | Corpus nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | eng 117.813 94.3962 162.796 135.193 396.285
3 | fre 49.6884 43.2072 70.1164 70.1366 169.702
4 | ger 77.9788 54.8565 92.1645 100.674 222.228
5 | ita 37.9593 27.978 43.4923 33.1497 106.293
6 | rus 64.8337 56.4429 93.1499 93.1186 224.038
7 | spa 50.0063 39.0704 55.7444 54.5203 134.703
8 |
--------------------------------------------------------------------------------
/results/gbooks_freq.comps:
--------------------------------------------------------------------------------
1 | Corpus nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | eng 2.02023 2.01368 2.18472 1.95447 7.29315
3 | fre 2.57805 2.031 3.13799 2.28996 7.44904
4 | ger 2.38017 2.03388 3.03868 2.61722 7.53545
5 | ita 2.19042 2.05765 2.18802 1.95419 7.57667
6 | rus 1.98374 2.03477 3.56788 2.13806 7.43694
7 | spa 2.1926 2.08892 2.2668 2.09468 7.38337
8 |
--------------------------------------------------------------------------------
/results/gbooks_freq.rsd:
--------------------------------------------------------------------------------
1 | Corpus nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | eng 0.00457156 0.00543685 0.129571 0.0247221 0.0242972
3 | fre 0.00476557 0.0156726 0.118464 0.00451085 0.00487697
4 | ger 0.00562102 0.00637091 0.113532 0.00658467 0.00535848
5 | ita 0.00598068 0.00791397 0.126841 0.00535468 0.00535212
6 | rus 0.00449159 0.00460021 0.115736 0.00460794 0.00421864
7 | spa 0.00480117 0.0065601 0.124025 0.00465915 0.00449202
8 |
--------------------------------------------------------------------------------
/results/gbooks_freq.swaps:
--------------------------------------------------------------------------------
1 | Corpus nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | eng 0.451191 0.252984 0.537967 0.450908 3.43385
3 | fre 0.483192 0.267631 0.553471 0.549875 3.5031
4 | ger 0.530952 0.257616 0.648872 0.602313 3.48959
5 | ita 0.52195 0.27591 0.475848 0.434848 3.54749
6 | rus 0.449897 0.265186 0.592245 0.53424 3.4982
7 | spa 0.537337 0.309803 0.537237 0.519552 3.44127
8 |
--------------------------------------------------------------------------------
/results/m3killer:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.00858097 0.0161205 0.0187849 0.0200504 0.111591
3 | 31620 0.0278762 0.0489345 0.0582746 0.0543233 0.281784
4 | 100000 0.0864959 0.158022 0.179497 0.163724 1.0518
5 | 316220 0.248881 0.473205 0.532748 0.527734 2.4164
6 | 1000000 1.25232 1.73201 1.85456 1.90807 9.43251
7 | 3162280 5.42039 7.12598 7.28006 4.89131 33.1493
8 | 10000000 19.0578 19.516 27.2404 27.9941 109.985
9 |
--------------------------------------------------------------------------------
/results/m3killer.comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.001 2.3812 3.0937 2.8589 7.2334
3 | 31620 2.00032 2.37226 3.14298 2.96866 6.73153
4 | 100000 2.0001 2.31551 2.96003 2.83656 7.60242
5 | 316220 2.00003 2.5148 2.95383 2.99744 6.93506
6 | 1000000 2.00001 2.5111 5.09133 2.83433 7.67326
7 | 3162280 2 2.50604 3.94582 1.80001 7.78699
8 | 10000000 2 2.01958 3.09213 2.83404 7.7502
9 |
--------------------------------------------------------------------------------
/results/m3killer.max_comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.001 2.3812 3.0937 2.8589 7.2334
3 | 31620 2.00032 2.37226 3.14298 2.96866 6.73153
4 | 100000 2.0001 2.31551 2.96003 2.83656 7.60242
5 | 316220 2.00003 2.5148 2.95383 2.99744 6.93506
6 | 1000000 2.00001 2.5111 5.09133 2.83433 7.67326
7 | 3162280 2 2.50604 3.94582 1.80001 7.78699
8 | 10000000 2 2.01958 3.09213 2.83404 7.7502
9 |
--------------------------------------------------------------------------------
/results/m3killer.rsd:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.134957 0.104188 0.206411 0.0511648 0.142841
3 | 31620 0.262639 0.0508439 0.224724 0.108405 0.0596018
4 | 100000 0.107368 0.101568 0.233842 0.103733 0.0430431
5 | 316220 0.0224785 0.0211817 0.219022 0.0295251 0.00577407
6 | 1000000 0.0436512 0.0172808 0.186331 0.0143903 0.0180113
7 | 3162280 0.0517219 0.0302072 0.18317 0.0117676 0.0300457
8 | 10000000 0.0218366 0.0205977 0.173585 0.0233922 0.0117546
9 |
--------------------------------------------------------------------------------
/results/m3killer.swaps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.2502 0.3458 0.6152 0.4516 3.2523
3 | 31620 0.250063 0.341493 0.540987 0.411448 3.07622
4 | 100000 0.25002 0.33648 0.58706 0.44736 3.42197
5 | 316220 0.250006 0.448653 0.587164 0.408744 3.18851
6 | 1000000 0.250002 0.446196 0.525279 0.447043 3.39751
7 | 3162280 0.250001 0.445979 0.432731 0.250002 3.57144
8 | 10000000 0.25 0.255726 0.541548 0.447006 3.47586
9 |
--------------------------------------------------------------------------------
/results/organpipe:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.00518679 0.00409736 0.00883075 0.00289263 0.0224094
3 | 31620 0.0160057 0.0123639 0.027103 0.0101452 0.0623793
4 | 100000 0.0486396 0.0391985 0.0790202 0.0289573 0.286742
5 | 316220 0.14899 0.102629 0.243459 0.0960446 0.739839
6 | 1000000 0.471531 0.324085 0.742923 0.27113 2.60552
7 | 3162280 2.33289 1.27396 2.92005 1.15959 9.9974
8 | 10000000 8.36737 4.13401 9.61188 3.70869 37.2826
9 |
--------------------------------------------------------------------------------
/results/organpipe.comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.5036 1.2018 3.3144 1.0026 6.179
3 | 31620 2.50127 1.20335 3.31657 1.00082 5.52739
4 | 100000 2.50052 1.20426 3.31532 1.00026 6.22998
5 | 316220 2.50015 1.01261 3.31518 1.00008 5.87896
6 | 1000000 2.50006 1.01275 3.31506 1.00003 6.30194
7 | 3162280 2.50002 1.01191 3.31514 1.00001 6.68181
8 | 10000000 2.50001 1.01191 3.31868 1 6.3507
9 |
--------------------------------------------------------------------------------
/results/organpipe.max_comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.5036 1.2018 3.3144 1.0026 6.179
3 | 31620 2.50127 1.20335 3.31657 1.00082 5.52739
4 | 100000 2.50052 1.20426 3.31532 1.00026 6.22998
5 | 316220 2.50015 1.01261 3.31518 1.00008 5.87896
6 | 1000000 2.50006 1.01275 3.31506 1.00003 6.30194
7 | 3162280 2.50002 1.01191 3.31514 1.00001 6.68181
8 | 10000000 2.50001 1.01191 3.31868 1 6.3507
9 |
--------------------------------------------------------------------------------
/results/organpipe.rsd:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0260573 0.00161172 0.223087 0.027141 0.0889261
3 | 31620 0.00347532 0.024052 0.219561 0.113355 0.0395298
4 | 100000 0.0467533 0.104677 0.219616 0.0671471 0.041455
5 | 316220 0.0623617 0.0283419 0.200283 0.0503981 0.0536869
6 | 1000000 0.0263516 0.0275057 0.200534 0.0417299 0.0215271
7 | 3162280 0.0189947 0.0491533 0.198481 0.0287048 0.0310142
8 | 10000000 0.0235939 0.0265271 0.225431 0.0391198 0.0240235
9 |
--------------------------------------------------------------------------------
/results/organpipe.swaps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0024 0.0008 0.0044 0.0004 2.141
3 | 31620 0.000885515 0.000253004 0.00177103 0.000126502 1.80348
4 | 100000 0.00032 0.0001 0.00064 4e-05 2.17794
5 | 316220 0.00010752 2.52988e-05 0.000259313 1.26494e-05 1.9946
6 | 1000000 3.8e-05 1e-05 8e-05 4e-06 2.22486
7 | 3162280 1.32816e-05 2.52982e-06 2.97254e-05 1.26491e-06 2.46104
8 | 10000000 4.4e-06 1e-06 1.12e-05 4e-07 2.22673
9 |
--------------------------------------------------------------------------------
/results/random:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0744633 0.0601273 0.0813791 0.0781392 0.192157
3 | 31620 0.222571 0.177082 0.248516 0.25026 0.614056
4 | 100000 0.719618 0.529 0.772346 0.760976 1.97042
5 | 316220 2.15402 1.60674 2.48384 2.46957 6.19136
6 | 1000000 6.98936 4.95673 7.89867 7.95574 19.7354
7 | 3162280 23.1086 15.7516 25.7251 25.5195 62.8602
8 | 10000000 71.6749 48.5689 80.7408 79.8404 198.408
9 |
--------------------------------------------------------------------------------
/results/random.comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.7954 2.30708 2.76658 2.39681 6.74818
3 | 31620 2.80688 2.26105 2.70252 2.42741 6.90953
4 | 100000 2.88963 2.21808 2.72259 2.32443 7.13767
5 | 316220 2.71749 2.06928 2.77001 2.46768 7.21298
6 | 1000000 2.73834 2.04151 2.76815 2.47151 7.29563
7 | 3162280 2.88673 2.02714 2.77313 2.51668 7.32883
8 | 10000000 2.80251 2.02048 2.75472 2.43825 7.34187
9 |
--------------------------------------------------------------------------------
/results/random.max_comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 5.6567 2.6444 5.4636 3.4958 7.091
3 | 31620 4.21056 2.40525 4.47122 3.46635 7.22982
4 | 100000 4.22522 2.33424 4.85339 3.12605 7.31947
5 | 316220 4.32007 2.30147 4.72109 3.54377 7.43757
6 | 1000000 4.46092 2.12137 4.83704 3.7784 7.44431
7 | 3162280 5.78789 2.08221 4.54884 3.70242 7.471
8 | 10000000 4.73983 2.04093 4.33485 3.65776 7.49956
9 |
--------------------------------------------------------------------------------
/results/random.rsd:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.125297 0.106343 0.12579 0.129747 0.106755
3 | 31620 0.140019 0.0697499 0.111638 0.109927 0.0863264
4 | 100000 0.112458 0.0504663 0.13721 0.0980392 0.0256999
5 | 316220 0.132162 0.0772275 0.128897 0.109546 0.0512982
6 | 1000000 0.121938 0.0608932 0.140533 0.100063 0.015636
7 | 3162280 0.111793 0.0266239 0.119195 0.11317 0.0160538
8 | 10000000 0.132279 0.0186661 0.121333 0.112876 0.0157413
9 |
--------------------------------------------------------------------------------
/results/random.swaps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.550255 0.327614 0.554048 0.529884 3.2772
3 | 31620 0.550875 0.300538 0.545093 0.53858 3.34504
4 | 100000 0.569518 0.286254 0.542897 0.524516 3.4619
5 | 316220 0.539733 0.288869 0.552467 0.543306 3.50093
6 | 1000000 0.54189 0.271818 0.54576 0.545208 3.53975
7 | 3162280 0.567651 0.262274 0.558248 0.55211 3.54427
8 | 10000000 0.550179 0.257747 0.550371 0.543869 3.54681
9 |
--------------------------------------------------------------------------------
/results/random01:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0575925 0.053695 0.0639878 0.064631 0.142829
3 | 31620 0.17287 0.162906 0.197321 0.205514 0.440888
4 | 100000 0.547084 0.515732 0.61858 0.635683 1.35206
5 | 316220 1.70679 1.43593 1.96716 2.01609 4.19058
6 | 1000000 5.49728 5.11804 6.32683 6.47476 13.3105
7 | 3162280 17.9226 16.2571 20.1311 20.7026 43.5439
8 | 10000000 57.4716 50.6564 64.4811 65.9602 142.782
9 |
--------------------------------------------------------------------------------
/results/random01.comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.75306 2.97201 2.74161 2.71338 9.75048
3 | 31620 2.74603 2.74853 2.72335 2.70896 10.1076
4 | 100000 2.73136 2.85335 2.72929 2.70466 10.2775
5 | 316220 2.74767 2.37534 2.71658 2.70199 10.4048
6 | 1000000 2.76116 2.55553 2.72204 2.70421 10.4626
7 | 3162280 2.74026 2.49366 2.73703 2.70407 10.5152
8 | 10000000 2.76355 2.42263 2.72733 2.70809 10.5232
9 |
--------------------------------------------------------------------------------
/results/random01.max_comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 3.2235 3.567 2.8817 2.8942 9.9446
3 | 31620 3.26996 3.46784 2.88624 2.86312 10.2157
4 | 100000 2.985 3.46079 2.88256 2.85407 10.3932
5 | 316220 2.96718 3.03067 2.88359 2.84786 10.5091
6 | 1000000 3.21648 3.03093 2.88151 2.85187 10.5838
7 | 3162280 3.21456 3.02939 2.98299 2.85099 10.6254
8 | 10000000 3.32846 3.02781 2.9829 2.84628 10.6499
9 |
--------------------------------------------------------------------------------
/results/random01.rsd:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.046534 0.10545 0.0669739 0.0283408 0.0648692
3 | 31620 0.0500447 0.10777 0.0376455 0.0341108 0.0289966
4 | 100000 0.0405839 0.0971195 0.0393276 0.0258694 0.0155957
5 | 316220 0.0421543 0.066931 0.0356055 0.0267332 0.0140662
6 | 1000000 0.052202 0.105467 0.0310807 0.0224796 0.0125655
7 | 3162280 0.0319659 0.0768927 0.0332682 0.0199616 0.0130261
8 | 10000000 0.0210483 0.0756416 0.0214473 0.019275 0.0111106
9 |
--------------------------------------------------------------------------------
/results/random01.swaps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.99995 0.270627 0.999359 1.00036 3.47198
3 | 31620 0.999968 0.264268 0.999872 1.00028 3.5619
4 | 100000 0.99915 0.26336 0.99999 1.00011 3.61178
5 | 316220 1 0.250807 1.00003 0.999862 3.63045
6 | 1000000 0.999992 0.250955 1.00001 1.00001 3.6582
7 | 3162280 1 0.250773 1 1.00001 3.67114
8 | 10000000 1 0.250683 1 0.99998 3.66502
9 |
--------------------------------------------------------------------------------
/results/rotated:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0944806 0.0116615 0.0172713 0.0107525 0.0529267
3 | 31620 0.315224 0.0348388 0.0518263 0.0333019 0.149213
4 | 100000 1.12481 0.107144 0.155908 0.102438 0.587988
5 | 316220 3.92167 0.31186 0.471631 0.321495 1.56744
6 | 1000000 16.9122 1.15158 1.70474 1.27462 5.9
7 | 3162280 98.911 3.64135 5.75325 3.92595 20.2904
8 | 10000000 366.409 11.7121 18.8893 12.3791 82.0939
9 |
--------------------------------------------------------------------------------
/results/rotated.comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 27.1516 1.70283 3.31667 1.50125 6.26957
3 | 31620 29.2138 1.70418 3.31681 1.5004 5.71234
4 | 100000 33.235 1.69054 3.31463 1.50012 6.47477
5 | 316220 37.244 1.51271 3.31486 1.50004 6.09781
6 | 1000000 39.2479 1.51276 3.31532 1.50001 6.54633
7 | 3162280 43.2492 1.51192 3.31514 1.5 5.98908
8 | 10000000 47.2497 1.51191 3.31864 1.5 6.5823
9 |
--------------------------------------------------------------------------------
/results/rotated.max_comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 27.1516 1.70283 3.31667 1.50125 6.26957
3 | 31620 29.2138 1.70418 3.31681 1.5004 5.71234
4 | 100000 33.235 1.69054 3.31463 1.50012 6.47477
5 | 316220 37.244 1.51271 3.31486 1.50004 6.09781
6 | 1000000 39.2479 1.51276 3.31532 1.50001 6.54633
7 | 3162280 43.2492 1.51192 3.31514 1.5 5.98908
8 | 10000000 47.2497 1.51191 3.31864 1.5 6.5823
9 |
--------------------------------------------------------------------------------
/results/rotated.rsd:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0377674 0.0402735 0.230788 0.106823 0.0792906
3 | 31620 0.0326602 0.0631192 0.225742 0.0570579 0.0576544
4 | 100000 0.0132905 0.0500467 0.258086 0.0289721 0.0181346
5 | 316220 0.0105863 0.029395 0.225428 0.0303987 0.0178659
6 | 1000000 0.0686864 0.0277156 0.183019 0.0347488 0.0131135
7 | 3162280 0.0240797 0.0261649 0.201305 0.0428449 0.0183971
8 | 10000000 0.0161341 0.0292904 0.205179 0.0271785 0.0162955
9 |
--------------------------------------------------------------------------------
/results/rotated.swaps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.00529947 0.00059994 0.00449955 0.00039996 2.19318
3 | 31620 0.0018026 0.000221372 0.00170773 0.000126498 2.01733
4 | 100000 0.000649994 5.99994e-05 0.000489995 3.99996e-05 2.30494
5 | 316220 0.000230851 1.89741e-05 0.000183416 1.26494e-05 2.20789
6 | 1000000 7.69999e-05 6.99999e-06 5.99999e-05 4e-06 2.32079
7 | 3162280 2.68793e-05 2.21359e-06 2.43495e-05 1.26491e-06 2.01211
8 | 10000000 9.3e-06 7e-07 8.6e-06 4e-07 2.35244
9 |
--------------------------------------------------------------------------------
/results/sorted:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.00991682 0.00771125 0.0173701 0.00569748 0.0553767
3 | 31620 0.0312159 0.0232467 0.0481715 0.0172812 0.14243
4 | 100000 0.0950594 0.0722384 0.151356 0.0540983 0.789689
5 | 316220 0.297247 0.202431 0.472186 0.169374 1.60031
6 | 1000000 1.39764 0.875969 1.80751 0.73581 5.83824
7 | 3162280 4.92823 2.50552 5.94299 2.50004 20.277
8 | 10000000 17.104 7.77382 19.6747 7.65679 81.6003
9 |
--------------------------------------------------------------------------------
/results/sorted.comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.502 1.2029 3.3146 1.0013 6.3408
3 | 31620 2.5007 1.20421 3.31556 1.00041 5.63786
4 | 100000 2.50028 1.19055 3.31528 1.00013 6.47248
5 | 316220 2.50008 1.01271 3.31504 1.00004 6.07512
6 | 1000000 2.50003 1.01276 3.31544 1.00001 6.54869
7 | 3162280 2.50001 1.01192 3.31516 1 5.97787
8 | 10000000 2.5 1.01191 3.31858 1 6.57698
9 |
--------------------------------------------------------------------------------
/results/sorted.max_comps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 2.502 1.2029 3.3146 1.0013 6.3408
3 | 31620 2.5007 1.20421 3.31556 1.00041 5.63786
4 | 100000 2.50028 1.19055 3.31528 1.00013 6.47248
5 | 316220 2.50008 1.01271 3.31504 1.00004 6.07512
6 | 1000000 2.50003 1.01276 3.31544 1.00001 6.54869
7 | 3162280 2.50001 1.01192 3.31516 1 5.97787
8 | 10000000 2.5 1.01191 3.31858 1 6.57698
9 |
--------------------------------------------------------------------------------
/results/sorted.rsd:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0347544 0.0205802 0.223643 0.0536861 0.0758209
3 | 31620 0.0335265 0.0403367 0.201573 0.0241301 0.0250886
4 | 100000 0.032431 0.012113 0.213826 0.0398935 0.153203
5 | 316220 0.045292 0.0384148 0.217565 0.04733 0.0423037
6 | 1000000 0.0222171 0.0258816 0.188323 0.0298593 0.0282589
7 | 3162280 0.022853 0.0504264 0.211683 0.0279252 0.0214452
8 | 10000000 0.0416055 0.0408904 0.210291 0.0306775 0.015699
9 |
--------------------------------------------------------------------------------
/results/sorted.swaps:
--------------------------------------------------------------------------------
1 | Size nth_element median_of_ninthers rnd3pivot ninther bfprt_baseline
2 | 10000 0.0013 0.0004 0.0023 0.0002 2.2369
3 | 31620 0.000474383 0.000158128 0.000980392 6.32511e-05 1.96689
4 | 100000 0.00017 4e-05 0.00038 2e-05 2.29844
5 | 316220 5.69224e-05 1.26494e-05 0.000139144 6.32471e-06 2.1839
6 | 1000000 2e-05 5e-06 4.7e-05 2e-06 2.31864
7 | 3162280 6.95701e-06 1.58114e-06 1.77087e-05 6.32455e-07 2.00662
8 | 10000000 2.3e-06 5e-07 4e-06 2e-07 2.33882
9 |
--------------------------------------------------------------------------------
/src/bfprt_baseline.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #include "common.h"
8 | #include
9 |
10 | template
11 | static T* partition(T* r, T* end)
12 | {
13 | const size_t len = end - r;
14 | if (len < 5) return pivotPartition(r, len / 2, len);
15 | size_t j = 0;
16 | for (size_t i = 4; i < len; i += 5, ++j)
17 | {
18 | partition5(r, i - 4, i - 3, i, i - 2, i - 1);
19 | cswap(r[i], r[j]);
20 | }
21 | quickselect(r, r + j / 2, r + j);
22 | return pivotPartition(r, j / 2, len);
23 | }
24 |
25 | void (*computeSelection)(double*, double*, double*)
26 | = &quickselect>;
27 |
--------------------------------------------------------------------------------
/src/common.h:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #pragma once
8 | #include
9 | #include
10 |
11 | /**
12 | Instrumentation counters
13 | */
14 | #ifdef COUNT_SWAPS
15 | extern unsigned long g_swaps;
16 | #endif
17 | #ifdef COUNT_WASTED_SWAPS
18 | extern unsigned long g_wastedSwaps;
19 | #endif
20 | #ifdef COUNT_COMPARISONS
21 | extern unsigned long g_comparisons;
22 | #endif
23 |
24 | /**
25 | Instrumented swap
26 | */
27 | template
28 | inline void cswap(T& lhs, T& rhs)
29 | {
30 | std::swap(lhs, rhs);
31 | #ifdef COUNT_SWAPS
32 | ++g_swaps;
33 | #endif
34 | #ifdef COUNT_WASTED_SWAPS
35 | if (lhs == rhs) ++g_wastedSwaps;
36 | #endif
37 | }
38 |
39 | /**
40 | Instrumented comparisons
41 | */
42 | #ifdef COUNT_COMPARISONS
43 | #define CNT (++g_comparisons, 0)+
44 | #else
45 | #define CNT
46 | #endif
47 |
48 | /**
49 | Swaps the median of r[a], r[b], and r[c] into r[b].
50 | */
51 | template
52 | void median3(T* r, size_t a, size_t b, size_t c)
53 | {
54 | if (r[b] = r[b] && r[b] >= r[c]);
72 | }
73 |
74 | /**
75 | Sorts in place r[a], r[b], and r[c].
76 | */
77 | template
78 | void sort3(T* r, size_t a, size_t b, size_t c)
79 | {
80 | if (r[b]
125 | void partition4(T* r, size_t a, size_t b, size_t c, size_t d)
126 | {
127 | assert(a != b && a != c && a != d && b != c && b != d
128 | && c != d);
129 | /* static */ if (leanRight)
130 | {
131 | // In the median of 5 algorithm, consider r[e] infinite
132 | if (r[c] a. c->b, d->c, e->d
151 | if (r[c]
176 | void partition5(T* r, size_t a, size_t b, size_t c, size_t d, size_t e)
177 | {
178 | assert(a != b && a != c && a != d && a != e && b != c && b != d && b != e
179 | && c != d && c != e && d != e);
180 | if (r[c]
210 | T* pivotPartition(T* r, size_t k, size_t length)
211 | {
212 | assert(k < length);
213 | cswap(*r, r[k]);
214 | size_t lo = 1, hi = length - 1;
215 | for (;; ++lo, --hi)
216 | {
217 | for (;; ++lo)
218 | {
219 | if (lo > hi) goto loop_done;
220 | if (r[lo] >=CNT *r) break;
221 | }
222 | // found the left bound: r[lo] >= r[0]
223 | assert(lo <= hi);
224 | for (; *r = hi) break;
228 | // found the right bound: r[hi] <= r[0], swap & make progress
229 | assert(r[lo] >= r[hi]);
230 | cswap(r[lo], r[hi]);
231 | }
232 | loop_done:
233 | --lo;
234 | cswap(r[lo], *r);
235 | return r + lo;
236 | }
237 |
238 | /**
239 | Implements the quickselect algorithm, parameterized with a partition function.
240 | */
241 | template
242 | void quickselect(T* r, T* mid, T* end)
243 | {
244 | if (r == end || mid >= end) return;
245 | assert(r <= mid && mid < end);
246 | for (;;) switch (end - r)
247 | {
248 | case 1:
249 | return;
250 | case 2:
251 | if (*r >CNT r[1]) cswap(*r, r[1]);
252 | return;
253 | case 3:
254 | sort3(r, 0, 1, 2);
255 | return;
256 | case 4:
257 | switch (mid - r)
258 | {
259 | case 0: goto select_min;
260 | case 1: partition4(r, 0, 1, 2, 3); break;
261 | case 2: partition4(r, 0, 1, 2, 3); break;
262 | case 3: goto select_max;
263 | default: assert(false);
264 | }
265 | return;
266 | default:
267 | assert(end - r > 4);
268 | if (r == mid)
269 | {
270 | select_min:
271 | auto pivot = r;
272 | for (++mid; mid < end; ++mid)
273 | if (*mid mid)
289 | {
290 | end = pivot;
291 | }
292 | else
293 | {
294 | r = pivot + 1;
295 | }
296 | }
297 | }
298 |
299 | /**
300 | Returns the index of the median of r[a], r[b], and r[c] without writing
301 | anything.
302 | */
303 | template
304 | size_t medianIndex(const T* r, size_t a, size_t b, size_t c)
305 | {
306 | if (r[a] >CNT r[c]) std::swap(a, c);
307 | if (r[b] >CNT r[c]) return c;
308 | if (r[b]
318 | static size_t medianIndex(T* r, size_t a, size_t b, size_t c, size_t d)
319 | {
320 | if (r[d] =CNT r[a])
334 | {
335 | assert(r[d] >= r[c] && r[d] >= r[a]); // so r[d] is out
336 | return medianIndex(r, a, b, c);
337 | }
338 | assert(r[a] > r[d] && r[a] > r[c]); // so r[a] is out
339 | }
340 | // Could return medianIndex(r, b, c, d) but we already know r[c] <= r[d]
341 | if (r[b] <=CNT r[c]) return c;
342 | if (r[b] >CNT r[d]) return d;
343 | return b;
344 | }
345 |
346 | /**
347 | Tukey's Ninther: compute the median of r[_1], r[_2], r[_3], then the median of
348 | r[_4], r[_5], r[_6], then the median of r[_7], r[_8], r[_9], and then swap the
349 | median of those three medians into r[_5].
350 | */
351 | template
352 | void ninther(T* r, size_t _1, size_t _2, size_t _3, size_t _4, size_t _5,
353 | size_t _6, size_t _7, size_t _8, size_t _9)
354 | {
355 | _2 = medianIndex(r, _1, _2, _3);
356 | _8 = medianIndex(r, _7, _8, _9);
357 | if (r[_2] >CNT r[_8]) std::swap(_2, _8);
358 | if (r[_4] >CNT r[_6]) std::swap(_4, _6);
359 | // Here we know that r[_2] and r[_8] are the other two medians and that
360 | // r[_2] <= r[_8]. We also know that r[_4] <= r[_6]
361 | if (r[_5] CNT r[_6])
366 | {
367 | // r[_6] is the median of r[_4], r[_5], r[_6]
368 | _4 = _6;
369 | }
370 | else
371 | {
372 | // Here we know r[_5] is the median of r[_4], r[_5], r[_6]
373 | if (r[_5] CNT r[_8]) return cswap(r[_5], r[_8]);
375 | // This is the only path that returns with no swap
376 | return;
377 | }
378 | // Here we know r[_4] is the median of r[_4], r[_5], r[_6]
379 | if (r[_4] CNT r[_8]) _4 = _8;
381 | cswap(r[_5], r[_4]);
382 | }
383 |
384 | /**
385 | Input assumptions:
386 |
387 | (a) hi <= rite
388 | (c) the range r[0 .. hi] contains elements no smaller than r[0]
389 |
390 | Output guarantee: same as Hoare partition using r[0] as pivot. Returns the new
391 | position of the pivot.
392 | */
393 | template
394 | size_t expandPartitionRight(T* r, size_t hi, size_t rite)
395 | {
396 | size_t pivot = 0;
397 | assert(pivot <= hi);
398 | assert(hi <= rite);
399 | // First loop: spend r[pivot .. hi]
400 | for (; pivot < hi; --rite)
401 | {
402 | if (rite == hi) goto done;
403 | if (r[rite] >=CNT r[0]) continue;
404 | ++pivot;
405 | assert(r[pivot] >= r[0]);
406 | cswap(r[rite], r[pivot]);
407 | }
408 | // Second loop: make left and pivot meet
409 | for (; rite > pivot; --rite)
410 | {
411 | if (r[rite] >=CNT r[0]) continue;
412 | while (rite > pivot)
413 | {
414 | ++pivot;
415 | if (r[0] 0, lo <= pivot
432 | (b) the range r[lo .. pivot] already contains elements no greater than r[pivot]
433 |
434 | Output guarantee: Same as Hoare partition around r[pivot]. Returns the new
435 | position of the pivot.
436 |
437 | */
438 | template
439 | size_t expandPartitionLeft(T* r, size_t lo, size_t pivot)
440 | {
441 | assert(lo > 0 && lo <= pivot);
442 | size_t left = 0;
443 | const auto oldPivot = pivot;
444 | for (; lo < pivot; ++left)
445 | {
446 | if (left == lo) goto done;
447 | if (r[oldPivot] >=CNT r[left]) continue;
448 | --pivot;
449 | assert(r[oldPivot] >= r[pivot]);
450 | cswap(r[left], r[pivot]);
451 | }
452 | // Second loop: make left and pivot meet
453 | for (;; ++left)
454 | {
455 | if (left == pivot) break;
456 | if (r[oldPivot] >=CNT r[left]) continue;
457 | for (;;)
458 | {
459 | if (left == pivot) goto done;
460 | --pivot;
461 | if (r[pivot]
487 | size_t expandPartition(T* r, size_t lo, size_t pivot, size_t hi, size_t length)
488 | {
489 | assert(lo <= pivot && pivot < hi && hi <= length);
490 | --hi;
491 | --length;
492 | size_t left = 0;
493 | for (;; ++left, --length)
494 | {
495 | for (;; ++left)
496 | {
497 | if (left == lo)
498 | return pivot +
499 | expandPartitionRight(r + pivot, hi - pivot, length - pivot);
500 | if (r[left] >CNT r[pivot]) break;
501 | }
502 | for (;; --length)
503 | {
504 | if (length == hi)
505 | return left +
506 | expandPartitionLeft(r + left, lo - left, pivot - left);
507 | if (r[pivot] >=CNT r[length]) break;
508 | }
509 | cswap(r[left], r[length]);
510 | }
511 | }
512 |
--------------------------------------------------------------------------------
/src/instrumented_double.h:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2017-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #pragma once
8 |
9 | #if defined(_GLIBCXX_ALGORITHM) || defined(_LIBCPP_ALGORITHM)
10 | #error Please include this file before
11 | #endif
12 |
13 | #if defined(_GLIBCXX_UTILITY) || defined(_LIBCPP_UTILITY)
14 | #error Please include this file before
15 | #endif
16 |
17 | struct Double
18 | {
19 | private:
20 | double payload;
21 |
22 | public:
23 | friend inline bool operator<(const Double& a, const Double& b)
24 | {
25 | #ifdef COUNT_COMPARISONS
26 | extern unsigned long g_comparisons;
27 | ++g_comparisons;
28 | #endif
29 | return a.payload < b.payload;
30 | }
31 |
32 | void swap(Double & b)
33 | {
34 | #ifdef COUNT_SWAPS
35 | extern unsigned long g_swaps;
36 | ++g_swaps;
37 | #endif
38 | #ifdef COUNT_WASTED_SWAPS
39 | extern unsigned long g_wastedSwaps;
40 | if (payload == b.payload) ++g_wastedSwaps;
41 | #endif
42 | auto t = payload;
43 | payload = b.payload;
44 | b.payload = t;
45 | }
46 | };
47 |
48 | // So sue me
49 | namespace std
50 | {
51 | inline void swap(Double& a, Double& b)
52 | {
53 | a.swap(b);
54 | }
55 | }
56 |
57 | #include
58 | #ifndef _GLIBCXX_ALGORITHM
59 | #ifndef _LIBCPP_ALGORITHM
60 | #error Portability error, expecting to define _GLIBCXX_ALGORITHM or _LIBCPP_ALGORITHM
61 | #endif
62 | #endif
63 |
64 | #include
65 | #ifndef _GLIBCXX_UTILITY
66 | #ifndef _LIBCPP_UTILITY
67 | #error Portability error, expecting to define _GLIBCXX_UTILITY or _LIBCPP_UTILITY
68 | #endif
69 | #endif
70 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include "timer.h"
13 | using namespace std;
14 |
15 | extern void (*computeSelection)(double*, double*, double*);
16 | #ifdef COUNT_SWAPS
17 | unsigned long g_swaps = 0;
18 | #endif
19 | #ifdef COUNT_WASTED_SWAPS
20 | unsigned long g_wastedSwaps = 0;
21 | #endif
22 | #ifdef COUNT_COMPARISONS
23 | unsigned long g_comparisons = 0;
24 | #endif
25 |
26 | double avg(const double* b, const double*const e)
27 | {
28 | double result = 0;
29 | const double length = e - b;
30 | for (; b < e; ++b) result += *b;
31 | return result / length;
32 | }
33 |
34 | // Compute standard deviation
35 | double stddev(const double* b, const double* e, double avg)
36 | {
37 | const double size = e - b;
38 | double result = 0;
39 | for (; b != e; ++b)
40 | {
41 | auto x = *b - avg;
42 | result += x * x;
43 | }
44 | return sqrt(result / size);
45 | }
46 |
47 | int main(int argc, char** argv)
48 | {
49 | if (argc != 2) return 1;
50 |
51 | // Is this file random? If so, we should randomInput after each run
52 | const bool randomInput = strstr(argv[1], "random") != nullptr;
53 |
54 | // Figure out how many epochs we need
55 | #ifdef MEASURE_TIME
56 | const size_t epochs = 102;
57 | const size_t outlierEpochs = 2;
58 | #else
59 | const size_t epochs = randomInput ? 100 : 1;
60 | const size_t outlierEpochs = 0;
61 | #endif
62 |
63 | // Load data from input file
64 | struct stat stat_buf;
65 | if (stat(argv[1], &stat_buf) != 0) return 2;
66 | if (stat_buf.st_size == 0 || stat_buf.st_size % 8 != 0) return 3;
67 | const size_t dataLen = stat_buf.st_size / 8;
68 | auto data = new double[dataLen];
69 | const auto f = fopen(argv[1], "rb");
70 | if (!f) return 4;
71 | if (fread(data, sizeof(double), dataLen, f) != dataLen) return 5;
72 | if (fclose(f) != 0) return 6;
73 |
74 | // The fraction we're searching for (2 for median)
75 | const size_t frac = 2;
76 | // The order statistic we're looking for
77 | size_t index = dataLen / frac;
78 |
79 | std::random_device rd;
80 | std::mt19937 g(1);
81 |
82 | double durations[epochs];
83 | double median = 0;
84 | #ifdef COUNT_COMPARISONS
85 | unsigned long maxComparisons = 0;
86 | #endif
87 |
88 | for (size_t i = 0; i < epochs; ++i)
89 | {
90 | if (randomInput && i > 0)
91 | shuffle(data, data + dataLen, g);
92 |
93 | vector v {data, data + dataLen};
94 | auto b = &v[0];
95 |
96 | #ifdef COUNT_COMPARISONS
97 | const auto tally = g_comparisons;
98 | #endif
99 |
100 | //////////////////// TIMING {
101 | Timer t;
102 | (*computeSelection)(b, b + index, b + dataLen);
103 | durations[i] = t.elapsed();
104 | //////////////////// } TIMING
105 |
106 | // Verify consistency
107 | if (median == 0)
108 | {
109 | median = v[index];
110 | }
111 | else
112 | {
113 | if (median != v[index]) return 7;
114 | }
115 |
116 | #ifdef COUNT_COMPARISONS
117 | maxComparisons = max(g_comparisons - tally, maxComparisons);
118 | #endif
119 | }
120 |
121 | // Verify
122 | vector v {data, data + dataLen};
123 | sort(v.begin(), v.end());
124 | if (median != v[index]) return 8;
125 |
126 | // Print results
127 | sort(durations, durations + epochs);
128 |
129 | #ifdef MEASURE_TIME
130 | const size_t experiments = epochs - outlierEpochs;
131 | const double avg1 = avg(durations, durations + experiments);
132 | printf("milliseconds: %g\n", avg1);
133 | const double stddev1 = stddev(durations, durations + experiments, avg1);
134 | printf("stddev: %g\n", stddev1);
135 | // Relative standard deviation
136 | printf("rsd: %g\n", stddev1 / avg1);
137 | #endif
138 | printf("size: %lu\nmedian: %g\n", dataLen, median);
139 | if (randomInput) printf("shuffled: 1\n");
140 | #ifdef COUNT_COMPARISONS
141 | printf("comparisons: %g\n", double(g_comparisons) / (epochs * dataLen));
142 | printf("max_comparisons: %g\n",
143 | double(maxComparisons) / dataLen);
144 | #endif
145 | #ifdef COUNT_SWAPS
146 | printf("swaps: %g\n", double(g_swaps) / (epochs * dataLen));
147 | #endif
148 | #ifdef COUNT_WASTED_SWAPS
149 | printf("wasted_swaps: %g\n", double(g_wastedSwaps) / (epochs * dataLen));
150 | #endif
151 | }
152 |
--------------------------------------------------------------------------------
/src/median_of_ninthers.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #include "median_of_ninthers.h"
8 |
9 | template
10 | static void quickselect(T* beg, T* mid, T* end)
11 | {
12 | if (beg == end || mid >= end) return;
13 | assert(beg <= mid && mid < end);
14 | adaptiveQuickselect(beg, mid - beg, end - beg);
15 | }
16 |
17 | void (*computeSelection)(double*, double*, double*)
18 | = &quickselect;
19 |
--------------------------------------------------------------------------------
/src/median_of_ninthers.h:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #pragma once
8 | #include "common.h"
9 | #include
10 |
11 | template
12 | size_t partitionImpl(T* beg, size_t length);
13 | template
14 | void adaptiveQuickselect(T* beg, size_t n, size_t length);
15 |
16 | /**
17 | Median of minima
18 | */
19 | template
20 | size_t medianOfMinima(T*const r, const size_t n, const size_t length)
21 | {
22 | assert(length >= 2);
23 | assert(n * 4 <= length);
24 | assert(n > 0);
25 | const size_t subset = n * 2,
26 | computeMinOver = (length - subset) / subset;
27 | assert(computeMinOver > 0);
28 | for (size_t i = 0, j = subset; i < subset; ++i)
29 | {
30 | const auto limit = j + computeMinOver;
31 | size_t minIndex = j;
32 | while (++j < limit)
33 | if (r[j]
47 | size_t medianOfMaxima(T*const r, const size_t n, const size_t length)
48 | {
49 | assert(length >= 2);
50 | assert(n * 4 >= length * 3 && n < length);
51 | const size_t subset = (length - n) * 2,
52 | subsetStart = length - subset,
53 | computeMaxOver = subsetStart / subset;
54 | assert(computeMaxOver > 0);
55 | for (size_t i = subsetStart, j = i - subset * computeMaxOver;
56 | i < length; ++i)
57 | {
58 | const auto limit = j + computeMaxOver;
59 | size_t maxIndex = j;
60 | while (++j < limit)
61 | if (r[j] >CNT r[maxIndex])
62 | maxIndex = j;
63 | if (r[maxIndex] >CNT r[i])
64 | cswap(r[i], r[maxIndex]);
65 | assert(j != 0 || i + 1 == length);
66 | }
67 | adaptiveQuickselect(r + subsetStart, length - n, subset);
68 | return expandPartition(r, subsetStart, n, length, length);
69 | }
70 |
71 | /**
72 | Partitions r[0 .. length] using a pivot of its own choosing. Attempts to pick a
73 | pivot that approximates the median. Returns the position of the pivot.
74 | */
75 | template
76 | size_t medianOfNinthers(T*const r, const size_t length)
77 | {
78 | assert(length >= 12);
79 | const auto frac =
80 | length <= 1024 ? length / 12 :
81 | length <= 128 * 1024 ? length / 64
82 | : length / 1024;
83 | auto pivot = frac / 2;
84 | const auto lo = length / 2 - pivot, hi = lo + frac;
85 | assert(lo >= frac * 4);
86 | assert(length - hi >= frac * 4);
87 | assert(lo / 2 >= pivot);
88 | const auto gap = (length - 9 * frac) / 4;
89 | auto a = lo - 4 * frac - gap, b = hi + gap;
90 | for (size_t i = lo; i < hi; ++i, a += 3, b += 3)
91 | {
92 | ninther(r, a, i - frac, b, a + 1, i, b + 1, a + 2, i + frac, b + 2);
93 | }
94 |
95 | adaptiveQuickselect(r + lo, pivot, frac);
96 | return expandPartition(r, lo, lo + pivot, hi, length);
97 | }
98 |
99 | /**
100 |
101 | Quickselect driver for medianOfNinthers, medianOfMinima, and medianOfMaxima.
102 | Dispathes to each depending on the relationship between n (the sought order
103 | statistics) and length.
104 |
105 | */
106 | template
107 | void adaptiveQuickselect(T* r, size_t n, size_t length)
108 | {
109 | assert(n < length);
110 | for (;;)
111 | {
112 | // Decide strategy for partitioning
113 | if (n == 0)
114 | {
115 | // That would be the max
116 | auto pivot = n;
117 | for (++n; n < length; ++n)
118 | if (r[n] = length * 5)
138 | pivot = medianOfMaxima(r, n, length);
139 | else
140 | pivot = medianOfNinthers(r, length);
141 |
142 | // See how the pivot fares
143 | if (pivot == n)
144 | {
145 | return;
146 | }
147 | if (pivot > n)
148 | {
149 | length = pivot;
150 | }
151 | else
152 | {
153 | ++pivot;
154 | r += pivot;
155 | length -= pivot;
156 | n -= pivot;
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/ninther.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #include "common.h"
8 |
9 | template
10 | static T* partition(T* r, T* end)
11 | {
12 | const size_t n = end - r;
13 | size_t p;
14 | if (n >= 10)
15 | {
16 | auto k = n / 10;
17 | p = 5 * k;
18 | ninther(r, k, 2 * k, 3 * k, 4 * k, p, 6 * k, 7 * k, 8 * k, 9 * k);
19 | }
20 | else
21 | {
22 | p = n / 2;
23 | }
24 | return pivotPartition(r, p, n);
25 | }
26 |
27 | void (*computeSelection)(double*, double*, double*)
28 | = &quickselect>;
29 |
--------------------------------------------------------------------------------
/src/nth_element.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #include "instrumented_double.h"
8 |
9 | void nth_element(double* b, double* m, double* e)
10 | {
11 | return std::nth_element((Double*)b, (Double*)m, (Double*)e);
12 | }
13 |
14 | void (*computeSelection)(double*, double*, double*)
15 | = &nth_element;
16 |
--------------------------------------------------------------------------------
/src/nth_element_reference.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #include
8 |
9 | void (*computeSelection)(double*, double*, double*)
10 | = &std::nth_element;
11 |
--------------------------------------------------------------------------------
/src/rnd3pivot.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #include "common.h"
8 | #include
9 |
10 | template
11 | static T* partition(T* r, T* end)
12 | {
13 | const size_t len = end - r;
14 | assert(len >= 3);
15 | static std::mt19937 gen(1);
16 | std::uniform_int_distribution<> dis(0, len - 1);
17 | size_t x = dis(gen), y = dis(gen), z = dis(gen);
18 | return pivotPartition(r, medianIndex(r, x, y, z), len);
19 | }
20 |
21 | void (*computeSelection)(double*, double*, double*)
22 | = &quickselect>;
23 |
--------------------------------------------------------------------------------
/src/timer.h:
--------------------------------------------------------------------------------
1 | /* Copyright Andrei Alexandrescu, 2016-.
2 | * Distributed under the Boost Software License, Version 1.0.
3 | * (See accompanying file LICENSE_1_0.txt or copy at
4 | * https://boost.org/LICENSE_1_0.txt)
5 | */
6 |
7 | #pragma once
8 | #include
9 | #include
10 |
11 | class Timer
12 | {
13 | public:
14 | Timer() : beg_(clock_::now()) {}
15 | void reset() { beg_ = clock_::now(); }
16 | double elapsed() const
17 | {
18 | return std::chrono::duration_cast
19 | (clock_::now() - beg_).count();
20 | }
21 |
22 | private:
23 | using clock_ = std::chrono::high_resolution_clock;
24 | using second_ = std::chrono::duration>;
25 | using ms_ = std::chrono::duration;
26 | std::chrono::time_point beg_;
27 | };
28 |
--------------------------------------------------------------------------------
/support/aggregate_ngrams.d:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env rdmd
2 |
3 | void main(string[] args)
4 | {
5 | import std.algorithm.searching, std.conv, std.stdio;
6 | string lastNgram;
7 | uint runningCount;
8 | foreach (line; stdin.byLine)
9 | {
10 | char[] ngram = line;
11 | line = line.find('\t');
12 | ngram = ngram[0 .. $ - line.length];
13 | line = line[1 .. $];
14 | auto year = parse!uint(line);
15 | line = line[1 .. $];
16 | auto count = parse!uint(line);
17 | if (ngram == lastNgram)
18 | {
19 | // Add to the running count
20 | runningCount += count;
21 | }
22 | else
23 | {
24 | if (lastNgram.length)
25 | writeln(lastNgram, '\t', runningCount, '\t', len(lastNgram));
26 | runningCount = count;
27 | //delete lastNgram;
28 | lastNgram = ngram.idup;
29 | }
30 | }
31 | writeln(lastNgram, '\t', runningCount, '\t', len(lastNgram));
32 | }
33 |
34 | size_t len(string w)
35 | {
36 | import std.range, std.string;
37 | auto i = indexOf(w, '_');
38 | if (i != -1) w = w[0 .. i];
39 | return w.walkLength;
40 | }
41 |
--------------------------------------------------------------------------------
/support/binarize.d:
--------------------------------------------------------------------------------
1 | void main(string[] args)
2 | {
3 | import std.format, std.stdio;
4 | double x;
5 | foreach (line; stdin.byLine)
6 | {
7 | line.formattedRead("%f", &x) == 1 || assert(0);
8 | stdout.rawWrite((&x)[0 .. 1]);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/support/generate.d:
--------------------------------------------------------------------------------
1 | void main(string[] args)
2 | {
3 | import std.exception, std.getopt, std.random, std.stdio;
4 | double minValue = -300_000, maxValue = 300_000;
5 | string kind;
6 | size_t n = 10_000_000;
7 | uint seed = 1;
8 |
9 | args.getopt(
10 | "n", &n,
11 | "kind", &kind,
12 | "seed", &seed,
13 | "min", &minValue,
14 | "max", &maxValue);
15 |
16 | if (kind == "random")
17 | {
18 | auto rng = Random(seed ? seed : unpredictableSeed);
19 | foreach (i; 0 .. n)
20 | {
21 | //writeln(uniform(minValue, maxValue, rng), ',');
22 | double x = uniform(minValue, maxValue, rng);
23 | stdout.rawWrite((&x)[0 .. 1]);
24 | //import core.stdc.stdio;
25 | //fwrite(&x, x.sizeof, 1, stdout) == 1 || assert(0);
26 | }
27 | }
28 | else if (kind == "random01")
29 | {
30 | enforce(n % 2 == 0);
31 | auto rng = Random(seed ? seed : unpredictableSeed);
32 | auto a = new double[n];
33 | a[0 .. $ / 2] = 0;
34 | a[$ / 2] = 0.5;
35 | a[$ / 2 + 1 .. $] = 1;
36 | randomShuffle(a, rng);
37 | stdout.rawWrite(a);
38 | }
39 | else if (kind == "sorted")
40 | {
41 | foreach (double i; 0 .. n)
42 | {
43 | stdout.rawWrite((&i)[0 .. 1]);
44 | }
45 | }
46 | else if (kind == "rotated")
47 | {
48 | foreach (i; 1 .. n + 1)
49 | {
50 | double x = i;
51 | stdout.rawWrite((&x)[0 .. 1]);
52 | }
53 | double last = 0;
54 | stdout.rawWrite((&last)[0 .. 1]);
55 | }
56 | else if (kind == "organpipe")
57 | {
58 | foreach (double i; 0 .. n / 2)
59 | {
60 | stdout.rawWrite((&i)[0 .. 1]);
61 | }
62 | foreach_reverse (double i; 0 .. n / 2)
63 | {
64 | stdout.rawWrite((&i)[0 .. 1]);
65 | }
66 | }
67 | else if (kind == "m3killer")
68 | {
69 | enforce((n & 3) == 0);
70 | auto k = n / 2;
71 | foreach (i; 1 .. k + 1)
72 | {
73 | if (i & 1)
74 | {
75 | double x = i;
76 | stdout.rawWrite((&x)[0 .. 1]);
77 | }
78 | else
79 | {
80 | double x = k + i - 1;
81 | stdout.rawWrite((&x)[0 .. 1]);
82 | }
83 | }
84 | foreach (i; 1 .. k + 1)
85 | {
86 | double x = 2 * i;
87 | stdout.rawWrite((&x)[0 .. 1]);
88 | }
89 | }
90 | else
91 | {
92 | enforce(0, "Must specify --kind");
93 | }
94 | }
95 |
--------------------------------------------------------------------------------