1
+ /*
2
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3
+ */
4
+
5
+ package benchmarks .flow .scrabble ;
6
+
7
+ import benchmarks .flow .scrabble .IterableSpliterator ;
8
+ import benchmarks .flow .scrabble .ShakespearePlaysScrabble ;
9
+ import io .reactivex .Flowable ;
10
+ import io .reactivex .Maybe ;
11
+ import io .reactivex .Single ;
12
+ import io .reactivex .functions .Function ;
13
+ import org .openjdk .jmh .annotations .*;
14
+
15
+ import java .util .*;
16
+ import java .util .Map .Entry ;
17
+ import java .util .concurrent .TimeUnit ;
18
+
19
+ /**
20
+ * Shakespeare plays Scrabble with RxJava 2 Flowable.
21
+ * @author José
22
+ * @author akarnokd
23
+ */
24
+ @ Warmup (iterations = 7 , time = 1 , timeUnit = TimeUnit .SECONDS )
25
+ @ Measurement (iterations = 7 , time = 1 , timeUnit = TimeUnit .SECONDS )
26
+ @ Fork (value = 1 )
27
+ @ BenchmarkMode (Mode .AverageTime )
28
+ @ OutputTimeUnit (TimeUnit .MILLISECONDS )
29
+ @ State (Scope .Benchmark )
30
+ public class RxJava2PlaysScrabble extends ShakespearePlaysScrabble {
31
+
32
+ @ Benchmark
33
+ @ Override
34
+ public List <Entry <Integer , List <String >>> play () throws Exception {
35
+
36
+ // Function to compute the score of a given word
37
+ Function <Integer , Flowable <Integer >> scoreOfALetter = letter -> Flowable .just (letterScores [letter - 'a' ]) ;
38
+
39
+ // score of the same letters in a word
40
+ Function <Entry <Integer , LongWrapper >, Flowable <Integer >> letterScore =
41
+ entry ->
42
+ Flowable .just (
43
+ letterScores [entry .getKey () - 'a' ] *
44
+ Integer .min (
45
+ (int )entry .getValue ().get (),
46
+ scrabbleAvailableLetters [entry .getKey () - 'a' ]
47
+ )
48
+ ) ;
49
+
50
+ Function <String , Flowable <Integer >> toIntegerFlowable =
51
+ string -> Flowable .fromIterable (IterableSpliterator .of (string .chars ().boxed ().spliterator ())) ;
52
+
53
+ // Histogram of the letters in a given word
54
+ Function <String , Single <HashMap <Integer , LongWrapper >>> histoOfLetters =
55
+ word -> toIntegerFlowable .apply (word )
56
+ .collect (
57
+ () -> new HashMap <>(),
58
+ (HashMap <Integer , LongWrapper > map , Integer value ) ->
59
+ {
60
+ LongWrapper newValue = map .get (value ) ;
61
+ if (newValue == null ) {
62
+ newValue = () -> 0L ;
63
+ }
64
+ map .put (value , newValue .incAndSet ()) ;
65
+ }
66
+
67
+ ) ;
68
+
69
+ // number of blanks for a given letter
70
+ Function <Entry <Integer , LongWrapper >, Flowable <Long >> blank =
71
+ entry ->
72
+ Flowable .just (
73
+ Long .max (
74
+ 0L ,
75
+ entry .getValue ().get () -
76
+ scrabbleAvailableLetters [entry .getKey () - 'a' ]
77
+ )
78
+ ) ;
79
+
80
+ // number of blanks for a given word
81
+ Function <String , Maybe <Long >> nBlanks =
82
+ word -> histoOfLetters .apply (word )
83
+ .flatMapPublisher (map -> Flowable .fromIterable (() -> map .entrySet ().iterator ()))
84
+ .flatMap (blank )
85
+ .reduce (Long ::sum ) ;
86
+
87
+
88
+ // can a word be written with 2 blanks?
89
+ Function <String , Maybe <Boolean >> checkBlanks =
90
+ word -> nBlanks .apply (word )
91
+ .flatMap (l -> Maybe .just (l <= 2L )) ;
92
+
93
+ // score taking blanks into account letterScore1
94
+ Function <String , Maybe <Integer >> score2 =
95
+ word -> histoOfLetters .apply (word )
96
+ .flatMapPublisher (map -> Flowable .fromIterable (() -> map .entrySet ().iterator ()))
97
+ .flatMap (letterScore )
98
+ .reduce (Integer ::sum ) ;
99
+
100
+ // Placing the word on the board
101
+ // Building the streams of first and last letters
102
+ Function <String , Flowable <Integer >> first3 =
103
+ word -> Flowable .fromIterable (IterableSpliterator .of (word .chars ().boxed ().limit (3 ).spliterator ())) ;
104
+ Function <String , Flowable <Integer >> last3 =
105
+ word -> Flowable .fromIterable (IterableSpliterator .of (word .chars ().boxed ().skip (3 ).spliterator ())) ;
106
+
107
+
108
+ // Stream to be maxed
109
+ Function <String , Flowable <Integer >> toBeMaxed =
110
+ word -> Flowable .just (first3 .apply (word ), last3 .apply (word ))
111
+ .flatMap (observable -> observable ) ;
112
+
113
+ // Bonus for double letter
114
+ Function <String , Maybe <Integer >> bonusForDoubleLetter =
115
+ word -> toBeMaxed .apply (word )
116
+ .flatMap (scoreOfALetter )
117
+ .reduce (Integer ::max ) ;
118
+
119
+ // score of the word put on the board
120
+ Function <String , Maybe <Integer >> score3 =
121
+ word ->
122
+ Maybe .merge (Arrays .asList (
123
+ score2 .apply (word ),
124
+ score2 .apply (word ),
125
+ bonusForDoubleLetter .apply (word ),
126
+ bonusForDoubleLetter .apply (word ),
127
+ Maybe .just (word .length () == 7 ? 50 : 0 )
128
+ )
129
+ )
130
+ .reduce (Integer ::sum ) ;
131
+
132
+ Function <Function <String , Maybe <Integer >>, Single <TreeMap <Integer , List <String >>>> buildHistoOnScore =
133
+ score -> Flowable .fromIterable (() -> shakespeareWords .iterator ())
134
+ .filter (scrabbleWords ::contains )
135
+ .filter (word -> checkBlanks .apply (word ).blockingGet ())
136
+ .collect (
137
+ () -> new TreeMap <>(Comparator .reverseOrder ()),
138
+ (TreeMap <Integer , List <String >> map , String word ) -> {
139
+ Integer key = score .apply (word ).blockingGet () ;
140
+ List <String > list = map .get (key ) ;
141
+ if (list == null ) {
142
+ list = new ArrayList <>() ;
143
+ map .put (key , list ) ;
144
+ }
145
+ list .add (word ) ;
146
+ }
147
+ ) ;
148
+
149
+ // best key / value pairs
150
+ List <Entry <Integer , List <String >>> finalList2 =
151
+ buildHistoOnScore .apply (score3 )
152
+ .flatMapPublisher (map -> Flowable .fromIterable (() -> map .entrySet ().iterator ()))
153
+ .take (3 )
154
+ .collect (
155
+ () -> new ArrayList <Entry <Integer , List <String >>>(),
156
+ (list , entry ) -> {
157
+ list .add (entry ) ;
158
+ }
159
+ )
160
+ .blockingGet () ;
161
+ return finalList2 ;
162
+ }
163
+ }
0 commit comments