-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeed.xml
More file actions
1714 lines (1345 loc) · 180 KB
/
feed.xml
File metadata and controls
1714 lines (1345 loc) · 180 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2024-12-14T10:04:35-08:00</updated><id>/feed.xml</id><title type="html">Show me the code!</title><subtitle>Talk is cheap, show me the code!</subtitle><author><name>Prabhakar Kumar</name><email>your-email@domain.com</email></author><entry><title type="html">Binary Search like a Staff SWE</title><link href="/2024/08/14/binary-search-like-a-staff-engineer.html" rel="alternate" type="text/html" title="Binary Search like a Staff SWE" /><published>2024-08-14T06:25:00-07:00</published><updated>2024-08-14T06:25:00-07:00</updated><id>/2024/08/14/binary-search-like-a-staff-engineer</id><content type="html" xml:base="/2024/08/14/binary-search-like-a-staff-engineer.html"><![CDATA[<h3 id="intro">Intro</h3>
<p>Every FAANG SWE is familiar with binary search. When it is time to code, most solutions, even by senior candidates appear to be
rote memorized and reproduced to find an integer (or not) in an array. These implementations are one offs and can’t be reused for
other problems. This article attempts to build a generic implementation and then use this to solve multiple problems. What is better
than Leetcode problems to test the correctness and efficiency of this implementation?! That’s what a FAANG Staff SWE would do! Right?</p>
<h3 id="key-idea">Key Idea</h3>
<p>I am not going to talk about binary search in great detail since by reaching until this point, you have proven you know its inner workings.
If not, please consult any of the umpteen number of references on this topic available on the interwebs.</p>
<p>Instead I am going to talk about how an abstract binary search implementation can be written that can be applied on multiple problems.</p>
<p>A binary search can be parameterized with the following:</p>
<ul>
<li>A comparison function, that can be executed on the middle element to decide whether we have found the target element or whether we should look in
left half of the search space, or the right half</li>
<li>A function that gives us <code class="language-plaintext highlighter-rouge">i</code>th element in the list. For an array, it just gives the element at <code class="language-plaintext highlighter-rouge">i</code>th index.</li>
<li>A <code class="language-plaintext highlighter-rouge">startIndex</code> and an <code class="language-plaintext highlighter-rouge">endIndex</code> that set the boundaries of the list we are looking into.</li>
</ul>
<p>With these parameters, let’s attempt to write a generic implementation of binary search.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="cm">/**
* Given a comparison function, a function to get element at provided index and the start and end bounds of the search space, returns the index
* of element that satisfies the comparison function.
* The `testFunc` should return 0 upon success, a negative integer upon indicating that the element is less than the middle element and positive
* integer indicating that the element is greater than the middle element.
* Returns the index of the element if found, else returns -1.
*/</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="kt">int</span> <span class="nf">binSearch</span><span class="o">(</span><span class="nc">Function</span><span class="o"><</span><span class="no">T</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">testFunc</span><span class="o">,</span> <span class="nc">IntFunction</span><span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="n">elementAt</span><span class="o">,</span> <span class="kt">int</span> <span class="n">start</span><span class="o">,</span> <span class="kt">int</span> <span class="n">end</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">end</span> <span class="o"><</span> <span class="n">start</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="o">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="o">)</span> <span class="o">/</span> <span class="mi">2</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">compResult</span> <span class="o">=</span> <span class="n">testFunc</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">elementAt</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">mid</span><span class="o">));</span>
<span class="k">if</span> <span class="o">(</span><span class="n">compResult</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">mid</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">compResult</span> <span class="o"><</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">testFunc</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="n">start</span><span class="o">,</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">testFunc</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="o">,</span> <span class="n">end</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="kt">int</span> <span class="nf">binSearch</span><span class="o">(</span><span class="nc">BiFunction</span><span class="o"><</span><span class="no">T</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">testFunc</span><span class="o">,</span> <span class="nc">IntFunction</span><span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="n">elementAt</span><span class="o">,</span> <span class="kt">int</span> <span class="n">start</span><span class="o">,</span> <span class="kt">int</span> <span class="n">end</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">end</span> <span class="o"><</span> <span class="n">start</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="kt">int</span> <span class="n">mid</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="o">(</span><span class="n">end</span> <span class="o">-</span> <span class="n">start</span><span class="o">)</span> <span class="o">/</span> <span class="mi">2</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">compResult</span> <span class="o">=</span> <span class="n">testFunc</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">elementAt</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">mid</span><span class="o">),</span> <span class="n">mid</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">compResult</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">mid</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">compResult</span> <span class="o"><</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">testFunc</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="n">start</span><span class="o">,</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">testFunc</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="o">,</span> <span class="n">end</span><span class="o">);</span>
<span class="o">}</span></code></pre></figure>
<p>The second overload uses a <code class="language-plaintext highlighter-rouge">BiFunction</code> instead of a <code class="language-plaintext highlighter-rouge">Function</code> and passes the current index of <code class="language-plaintext highlighter-rouge">mid</code> as the second parameter
so that in certain problems <code class="language-plaintext highlighter-rouge">mid</code> is made available to the <code class="language-plaintext highlighter-rouge">testFunc</code>. In this pattern of problems <code class="language-plaintext highlighter-rouge">testFunc</code> needs to check for a
condition with any of the elements adjacent to mid.</p>
<p>Here are the equivalent C# implementations.</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">static</span> <span class="kt">int</span> <span class="n">BinSearch</span><span class="p"><</span><span class="n">T</span><span class="p">>(</span><span class="n">Func</span><span class="p"><</span><span class="n">T</span><span class="p">,</span> <span class="kt">int</span><span class="p">></span> <span class="n">testFunc</span><span class="p">,</span> <span class="n">Func</span><span class="p"><</span><span class="kt">int</span><span class="p">,</span> <span class="n">T</span><span class="p">></span> <span class="n">elementAt</span><span class="p">,</span> <span class="kt">int</span> <span class="n">start</span><span class="p">,</span> <span class="kt">int</span> <span class="n">end</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">end</span> <span class="p"><</span> <span class="n">start</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">mid</span> <span class="p">=</span> <span class="n">start</span> <span class="p">+</span> <span class="p">(</span><span class="n">end</span> <span class="p">-</span> <span class="n">start</span><span class="p">)</span> <span class="p">/</span> <span class="m">2</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">compResult</span> <span class="p">=</span> <span class="nf">testFunc</span><span class="p">(</span><span class="nf">elementAt</span><span class="p">(</span><span class="n">mid</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">compResult</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">mid</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">compResult</span> <span class="p"><</span> <span class="m">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nf">BinSearch</span><span class="p">(</span><span class="n">testFunc</span><span class="p">,</span> <span class="n">elementAt</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">mid</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nf">BinSearch</span><span class="p">(</span><span class="n">testFunc</span><span class="p">,</span> <span class="n">elementAt</span><span class="p">,</span> <span class="n">mid</span> <span class="p">+</span> <span class="m">1</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="kt">int</span> <span class="n">BinSearch</span><span class="p"><</span><span class="n">T</span><span class="p">>(</span><span class="n">Func</span><span class="p"><</span><span class="n">T</span><span class="p">,</span> <span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">></span> <span class="n">testFunc</span><span class="p">,</span> <span class="n">Func</span><span class="p"><</span><span class="kt">int</span><span class="p">,</span> <span class="n">T</span><span class="p">></span> <span class="n">elementAt</span><span class="p">,</span> <span class="kt">int</span> <span class="n">start</span><span class="p">,</span> <span class="kt">int</span> <span class="n">end</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">end</span> <span class="p"><</span> <span class="n">start</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">mid</span> <span class="p">=</span> <span class="n">start</span> <span class="p">+</span> <span class="p">(</span><span class="n">end</span> <span class="p">-</span> <span class="n">start</span><span class="p">)</span> <span class="p">/</span> <span class="m">2</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">compResult</span> <span class="p">=</span> <span class="nf">testFunc</span><span class="p">(</span><span class="nf">elementAt</span><span class="p">(</span><span class="n">mid</span><span class="p">),</span> <span class="n">mid</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">compResult</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">mid</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">compResult</span> <span class="p"><</span> <span class="m">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nf">BinSearch</span><span class="p">(</span><span class="n">testFunc</span><span class="p">,</span> <span class="n">elementAt</span><span class="p">,</span> <span class="n">start</span><span class="p">,</span> <span class="n">mid</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nf">BinSearch</span><span class="p">(</span><span class="n">testFunc</span><span class="p">,</span> <span class="n">elementAt</span><span class="p">,</span> <span class="n">mid</span> <span class="p">+</span> <span class="m">1</span><span class="p">,</span> <span class="n">end</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>Armed with this implementation, can we solve some common Leetcode problems that require binary search?</p>
<p>Let’s try LC 74 <a href="https://leetcode.com/problems/search-a-2d-matrix/description/">Search a 2D Matrix</a>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.util.function.Function</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">searchMatrix</span><span class="o">(</span><span class="kt">int</span><span class="o">[][]</span> <span class="n">matrix</span><span class="o">,</span> <span class="kt">int</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">matrix</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>
<span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
<span class="nc">Function</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">testFunc</span> <span class="o">=</span> <span class="o">(</span><span class="n">elem</span><span class="o">)</span> <span class="o">-></span> <span class="nc">Integer</span><span class="o">.</span><span class="na">compare</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">elem</span><span class="o">);</span>
<span class="nc">IntFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">elementAt</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="n">matrix</span><span class="o">[</span><span class="n">i</span> <span class="o">/</span> <span class="n">matrix</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">length</span><span class="o">][</span><span class="n">i</span> <span class="o">%</span> <span class="n">matrix</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">length</span><span class="o">];</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">testFunc</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">matrix</span><span class="o">.</span><span class="na">length</span> <span class="o">*</span> <span class="n">matrix</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>Wow! What was that? Driver function was just three lines (excluding the bad input check) in Java which is considered a very verbose language. That’s the magic of higher order functions :-)
<a href="https://leetcode.com/problems/search-a-2d-matrix/submissions/1354960780/">Submission Link</a>
<img src="/images/LC74_Accepted.png" alt="Submission" /></p>
<p>Let’s try another one. LC 69 <a href="https://leetcode.com/problems/sqrtx/">Sqrt(x)</a>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.util.function.Function</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.function.IntFunction</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">mySqrt</span><span class="o">(</span><span class="kt">int</span> <span class="n">x</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">x</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">x</span> <span class="o">==</span> <span class="mi">1</span><span class="o">)</span>
<span class="k">return</span> <span class="n">x</span><span class="o">;</span>
<span class="nc">IntFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">elementAt</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="n">i</span><span class="o">;</span>
<span class="nc">Function</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">sqrtCheck</span> <span class="o">=</span> <span class="n">i</span> <span class="o">-></span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">x</span> <span class="o">/</span> <span class="n">i</span> <span class="o">==</span> <span class="n">i</span> <span class="o">||</span> <span class="o">(</span><span class="n">x</span> <span class="o">/</span> <span class="o">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)</span> <span class="o">==</span> <span class="n">i</span><span class="o">))</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">x</span> <span class="o">/</span> <span class="n">i</span> <span class="o">></span> <span class="n">i</span><span class="o">)</span>
<span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">};</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">sqrtCheck</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">x</span> <span class="o">/</span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>Let’s try LC 162 <a href="https://leetcode.com/problems/find-peak-element/">Find Peak Element</a>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">findPeakElement</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">nums</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">IntFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">elementAt</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
<span class="nc">BiFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">peakCheck</span> <span class="o">=</span> <span class="o">(</span><span class="n">elem</span><span class="o">,</span> <span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="k">if</span> <span class="o">((</span><span class="n">i</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">elem</span> <span class="o">></span> <span class="n">elementAt</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="o">))</span> <span class="o">&&</span>
<span class="o">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">||</span> <span class="n">elem</span> <span class="o">></span> <span class="n">elementAt</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">)))</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">elem</span> <span class="o"><</span> <span class="n">elementAt</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="o">))</span>
<span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">};</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">peakCheck</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p><a href="https://leetcode.com/problems/find-peak-element/submissions/1355459214/">Submission Link</a></p>
<p><img src="/images/LC162_Accepted.png" alt="Submission Stats" /></p>
<p>Let’s try LC 35 <a href="https://leetcode.com/problems/search-insert-position/description/">Search Insert Position</a>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.util.function.Function</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.function.BiFunction</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">searchInsert</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">nums</span><span class="o">,</span> <span class="kt">int</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// Get the edge cases out</span>
<span class="k">if</span> <span class="o">(</span><span class="n">target</span> <span class="o">></span> <span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">])</span>
<span class="k">return</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">target</span> <span class="o"><</span> <span class="n">nums</span><span class="o">[</span><span class="mi">0</span><span class="o">])</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="c1">// For an integer array, elementAt should return element at i</span>
<span class="nc">IntFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">elementAt</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
<span class="nc">BiFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">indexCheck</span> <span class="o">=</span> <span class="o">(</span><span class="n">elem</span><span class="o">,</span> <span class="n">mid</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">elem</span> <span class="o">==</span> <span class="n">target</span> <span class="o">||</span> <span class="o">(</span><span class="n">elem</span> <span class="o">></span> <span class="n">target</span> <span class="o">&&</span> <span class="n">nums</span><span class="o">[</span><span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="o">]</span> <span class="o"><</span> <span class="n">target</span><span class="o">))</span> <span class="o">{</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">elem</span> <span class="o"><</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">};</span>
<span class="k">return</span> <span class="nf">binSearch</span><span class="o">(</span><span class="n">indexCheck</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>This too runs in 1ms and beats 100% of the solutions in runtime. Here’s the <a href="https://leetcode.com/problems/search-insert-position/submissions/1354993527/">submission link</a>
if you want to verify.
<img src="/images/LC35_Accepted.png" alt="Submission" /></p>
<p>The driver function here is slightly longer with the indexCheck method taking up a few lines. Still, the program is very easy to understand!</p>
<p>Let’s try LC 153 <a href="https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/">Find min in Rotated Sorted Array</a>. This
involves finding the index at which array is pivoted i.e. rotated and then returning the element at that index.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.util.function.Function</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.function.IntFunction</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">findMin</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">nums</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// Search the pivot index first</span>
<span class="kt">int</span> <span class="n">pivotIndex</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nums</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o"><</span> <span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">])</span> <span class="o">{</span>
<span class="c1">// array is not pivoted</span>
<span class="n">pivotIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">pivotIndex</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">IntFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">elementAt</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
<span class="nc">BiFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">pivotCheck</span> <span class="o">=</span> <span class="o">(</span><span class="n">elem</span><span class="o">,</span> <span class="n">mid</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mid</span> <span class="o">></span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">nums</span><span class="o">[</span><span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="o">]</span> <span class="o">></span> <span class="n">elem</span><span class="o">)</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">elem</span> <span class="o"><</span> <span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">]</span> <span class="o">&&</span> <span class="n">elem</span> <span class="o"><</span> <span class="n">nums</span><span class="o">[</span><span class="mi">0</span><span class="o">])</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="k">else</span> <span class="nf">if</span> <span class="o">((</span><span class="n">elem</span> <span class="o">></span> <span class="n">nums</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">&&</span> <span class="n">elem</span> <span class="o">></span> <span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">])</span> <span class="o">||</span> <span class="o">(</span><span class="n">mid</span> <span class="o"><</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&&</span> <span class="n">nums</span><span class="o">[</span><span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="o">]</span> <span class="o"><</span> <span class="n">elem</span><span class="o">))</span>
<span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">};</span>
<span class="n">pivotIndex</span> <span class="o">=</span> <span class="n">binSearch</span><span class="o">(</span><span class="n">pivotCheck</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">nums</span><span class="o">[</span><span class="n">pivotIndex</span><span class="o">];</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p><a href="https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/submissions/1355057450/">Submission Link</a></p>
<p>Let’s try LC 33 <a href="https://leetcode.com/problems/search-in-rotated-sorted-array/description/">Search in Rotated Sorted Array</a>. This one
uses the logic for finding pivot element from the previous problem and then builds on top of that.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.util.function.Function</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.function.IntFunction</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">Solution</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kt">int</span> <span class="nf">search</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">nums</span><span class="o">,</span> <span class="kt">int</span> <span class="n">target</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// Search the pivot index first</span>
<span class="kt">int</span> <span class="n">pivotIndex</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="n">nums</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o"><</span> <span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">])</span> <span class="o">{</span>
<span class="c1">// array is not pivoted</span>
<span class="n">pivotIndex</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">pivotIndex</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">IntFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">elementAt</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="n">nums</span><span class="o">[</span><span class="n">i</span><span class="o">];</span>
<span class="nc">BiFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">pivotCheck</span> <span class="o">=</span> <span class="o">(</span><span class="n">elem</span><span class="o">,</span> <span class="n">mid</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">mid</span> <span class="o">></span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">nums</span><span class="o">[</span><span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="o">]</span> <span class="o">></span> <span class="n">elem</span><span class="o">)</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">elem</span> <span class="o"><</span> <span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">]</span> <span class="o">&&</span> <span class="n">elem</span> <span class="o"><</span> <span class="n">nums</span><span class="o">[</span><span class="mi">0</span><span class="o">])</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="k">else</span> <span class="nf">if</span> <span class="o">((</span><span class="n">elem</span> <span class="o">></span> <span class="n">nums</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">&&</span> <span class="n">elem</span> <span class="o">></span> <span class="n">nums</span><span class="o">[</span><span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">])</span> <span class="o">||</span> <span class="o">(</span><span class="n">mid</span> <span class="o"><</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">&&</span> <span class="n">nums</span><span class="o">[</span><span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="o">]</span> <span class="o"><</span> <span class="n">elem</span><span class="o">))</span>
<span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">return</span> <span class="mi">0</span><span class="o">;</span>
<span class="o">};</span>
<span class="n">pivotIndex</span> <span class="o">=</span> <span class="n">binSearch</span><span class="o">(</span><span class="n">pivotCheck</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">final</span> <span class="kt">int</span> <span class="n">pivot</span> <span class="o">=</span> <span class="n">pivotIndex</span><span class="o">;</span>
<span class="nc">IntFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">elementAt</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">-></span> <span class="n">nums</span><span class="o">[(</span><span class="n">i</span> <span class="o">+</span> <span class="n">pivot</span><span class="o">)</span> <span class="o">%</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span><span class="o">];</span>
<span class="nc">BiFunction</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="n">numCheck</span> <span class="o">=</span> <span class="o">(</span><span class="n">elem</span><span class="o">,</span> <span class="n">mid</span><span class="o">)</span> <span class="o">-></span> <span class="nc">Integer</span><span class="o">.</span><span class="na">compare</span><span class="o">(</span><span class="n">target</span><span class="o">,</span> <span class="n">elem</span><span class="o">);</span>
<span class="kt">int</span> <span class="n">searchResult</span> <span class="o">=</span> <span class="n">binSearch</span><span class="o">(</span><span class="n">numCheck</span><span class="o">,</span> <span class="n">elementAt</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="k">return</span> <span class="n">searchResult</span> <span class="o">>=</span> <span class="mi">0</span> <span class="o">?</span> <span class="o">(</span><span class="n">searchResult</span> <span class="o">+</span> <span class="n">pivot</span><span class="o">)</span> <span class="o">%</span> <span class="n">nums</span><span class="o">.</span><span class="na">length</span> <span class="o">:</span> <span class="n">searchResult</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p><a href="https://leetcode.com/problems/search-in-rotated-sorted-array/submissions/1355048370/">Submission Link</a></p>
<p><img src="/images/LC33_Accepted.png" alt="AcceptedImage" /></p>
<p>How about LC 981 <a href="https://leetcode.com/problems/time-based-key-value-store/description/">Time Based Key-Value Store</a></p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">TimeMap</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">struct</span> <span class="nc">Pair</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">Timestamp</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Value</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">Dictionary</span><span class="p"><</span><span class="kt">string</span><span class="p">,</span> <span class="n">List</span><span class="p"><</span><span class="n">Pair</span><span class="p">>></span> <span class="n">data</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">Set</span><span class="p">(</span><span class="kt">string</span> <span class="n">key</span><span class="p">,</span> <span class="kt">string</span> <span class="k">value</span><span class="p">,</span> <span class="kt">int</span> <span class="n">timestamp</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">List</span><span class="p"><</span><span class="n">Pair</span><span class="p">></span> <span class="n">list</span><span class="p">;</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="n">list</span> <span class="p">=</span> <span class="n">data</span><span class="p">[</span><span class="n">key</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">catch</span> <span class="p">(</span><span class="n">KeyNotFoundException</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">list</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
<span class="n">data</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="p">=</span> <span class="n">list</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">list</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="n">Pair</span><span class="p">{</span><span class="n">Timestamp</span> <span class="p">=</span> <span class="n">timestamp</span><span class="p">,</span> <span class="n">Value</span> <span class="p">=</span> <span class="k">value</span><span class="p">});</span>
<span class="p">}</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nf">Get</span><span class="p">(</span><span class="kt">string</span> <span class="n">key</span><span class="p">,</span> <span class="kt">int</span> <span class="n">timestamp</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">dataList</span> <span class="p">=</span> <span class="n">data</span><span class="p">[</span><span class="n">key</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="n">timestamp</span> <span class="p"><</span> <span class="n">dataList</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">Timestamp</span><span class="p">)</span>
<span class="k">return</span> <span class="s">""</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">foundIndex</span> <span class="p">=</span> <span class="nf">BinSearch</span><span class="p">((</span><span class="n">elem</span><span class="p">,</span> <span class="n">mid</span><span class="p">)</span> <span class="p">=></span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elem</span><span class="p">.</span><span class="n">Timestamp</span> <span class="p">==</span> <span class="n">timestamp</span><span class="p">)</span>
<span class="k">return</span> <span class="m">0</span><span class="p">;</span>
<span class="k">if</span> <span class="p">((</span><span class="n">elem</span><span class="p">.</span><span class="n">Timestamp</span> <span class="p"><</span> <span class="n">timestamp</span><span class="p">)</span> <span class="p">&&</span>
<span class="p">((</span><span class="n">mid</span> <span class="p">==</span> <span class="n">dataList</span><span class="p">.</span><span class="n">Count</span> <span class="p">-</span> <span class="m">1</span><span class="p">)</span> <span class="p">||</span> <span class="n">dataList</span><span class="p">[</span><span class="n">mid</span> <span class="p">+</span> <span class="m">1</span><span class="p">].</span><span class="n">Timestamp</span> <span class="p">></span> <span class="n">timestamp</span><span class="p">))</span>
<span class="k">return</span> <span class="m">0</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elem</span><span class="p">.</span><span class="n">Timestamp</span> <span class="p">></span> <span class="n">timestamp</span><span class="p">)</span>
<span class="k">return</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="k">return</span> <span class="m">1</span><span class="p">;</span>
<span class="p">},</span>
<span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">=></span> <span class="n">dataList</span><span class="p">[</span><span class="n">i</span><span class="p">],</span>
<span class="m">0</span><span class="p">,</span>
<span class="n">dataList</span><span class="p">.</span><span class="n">Count</span> <span class="p">-</span> <span class="m">1</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">foundIndex</span> <span class="p">==</span> <span class="p">-</span><span class="m">1</span><span class="p">)</span>
<span class="k">return</span> <span class="s">""</span><span class="p">;</span>
<span class="k">return</span> <span class="n">dataList</span><span class="p">[</span><span class="n">foundIndex</span><span class="p">].</span><span class="n">Value</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">catch</span> <span class="p">(</span><span class="n">KeyNotFoundException</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="s">""</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p><a href="https://leetcode.com/problems/time-based-key-value-store/submissions/1360430234/">Submission Link</a></p>
<p>How about LC 875 <a href="https://leetcode.com/problems/koko-eating-bananas/">Koko Eating Bananas</a>?</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">class</span> <span class="nc">Solution</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">int</span> <span class="nf">MinEatingSpeed</span><span class="p">(</span><span class="kt">int</span><span class="p">[]</span> <span class="n">piles</span><span class="p">,</span> <span class="kt">int</span> <span class="n">h</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">max</span> <span class="p">=</span> <span class="n">piles</span><span class="p">.</span><span class="nf">Max</span><span class="p">()</span> <span class="p">+</span> <span class="m">1</span><span class="p">;</span>
<span class="n">Func</span><span class="p"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">></span> <span class="n">elementAt</span> <span class="p">=</span> <span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">=></span> <span class="n">i</span><span class="p">;</span>
<span class="n">Func</span><span class="p"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">></span> <span class="n">testFunc</span> <span class="p">=</span> <span class="p">(</span><span class="n">mid</span><span class="p">)</span> <span class="p">=></span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(!</span><span class="nf">CanEatWithinHours</span><span class="p">(</span><span class="n">piles</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">mid</span><span class="p">))</span>
<span class="k">return</span> <span class="m">1</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">CanEatWithinHours</span><span class="p">(</span><span class="n">piles</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">mid</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mid</span> <span class="p">==</span> <span class="m">1</span> <span class="p">||</span> <span class="p">!</span><span class="nf">CanEatWithinHours</span><span class="p">(</span><span class="n">piles</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="n">mid</span> <span class="p">-</span> <span class="m">1</span><span class="p">))</span>
<span class="k">return</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">return</span> <span class="nf">BinSearch</span><span class="p">(</span><span class="n">testFunc</span><span class="p">,</span> <span class="n">elementAt</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="n">max</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">static</span> <span class="kt">bool</span> <span class="nf">CanEatWithinHours</span><span class="p">(</span><span class="kt">int</span><span class="p">[]</span> <span class="n">piles</span><span class="p">,</span> <span class="kt">int</span> <span class="n">permittedHours</span><span class="p">,</span> <span class="kt">int</span> <span class="n">eatingSpeed</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">hoursToEat</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p"><</span> <span class="n">piles</span><span class="p">.</span><span class="nf">Count</span><span class="p">();</span> <span class="n">i</span><span class="p">++)</span>
<span class="p">{</span>
<span class="kt">bool</span> <span class="n">finishEvenly</span> <span class="p">=</span> <span class="n">piles</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">%</span> <span class="n">eatingSpeed</span> <span class="p">==</span> <span class="m">0</span><span class="p">;</span>
<span class="n">hoursToEat</span> <span class="p">+=</span> <span class="p">(</span><span class="n">piles</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">/</span> <span class="n">eatingSpeed</span><span class="p">)</span> <span class="p">+</span> <span class="p">(</span><span class="n">finishEvenly</span> <span class="p">?</span> <span class="m">0</span> <span class="p">:</span> <span class="m">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">hoursToEat</span> <span class="p"><=</span> <span class="n">permittedHours</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p><a href="https://leetcode.com/problems/koko-eating-bananas/submissions/1363612732/">Submission Link</a></p>
<p>With these few examples communicating the idea, I leave it to the reader to solve other binary search problems with this generic template.</p>]]></content><author><name>Prabhakar Kumar</name><email>your-email@domain.com</email></author><category term="leetcode" /><category term="binary-search" /><category term="programming" /><category term="computer-science" /><summary type="html"><![CDATA[Intro]]></summary></entry><entry><title type="html">Setup Arch Linux on a Dell XPS 13 with Windows 10 dual boot</title><link href="/2020/01/15/setup-arch-linux-on-dell-xps-13-7390.html" rel="alternate" type="text/html" title="Setup Arch Linux on a Dell XPS 13 with Windows 10 dual boot" /><published>2020-01-15T17:48:53-08:00</published><updated>2020-01-15T17:48:53-08:00</updated><id>/2020/01/15/setup-arch-linux-on-dell-xps-13-7390</id><content type="html" xml:base="/2020/01/15/setup-arch-linux-on-dell-xps-13-7390.html"><![CDATA[<h3 id="intro">Intro</h3>
<p>This article details steps for setting up a functional Arch Linux installation (with a beautiful Deepin Desktop Environment GUI) on a Dell XPS 13, along with the pre-installed Windows 10
in a dual boot configuration. As a developer, I needed an ultrabook class laptop which wasn’t underpowered. Dell XPS 13 fits perfectly in this segment. It is extremely light, has a
unibody design with sturdy hinge and is specwise pretty juicy. After being spoilt with a Macbook Pro for a few years during my previous employment, I never liked the touchpads of
non-mac laptops. Dell XPS 13’s was the first one I did. I believe new HP Spectre and Lenovo carbon laptops might have good touchpads but I haven’t played with them yet.</p>
<p>I bought the 2019 version of Dell XPS 13 7390 with the following specs</p>
<ul>
<li>Core i7 10th Gen 10710U processor (<a href="https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i7-10710U+%40+1.10GHz&id=3567">CPU Benchmark</a>)</li>
<li>16GB LPDDR3 2133 MHz RAM,</li>
<li>1TB M.2 PCIe NVMe SSD</li>
<li>13.3 inch 4K touchscreen display</li>
<li>Backlit chiclet keyboard with Fingerprint Reader on the power button</li>
</ul>
<p>It cost me $1705.01 USD inclusive of the 10% Washington state sales tax. Too bad, Dell released the 2020 version a few days after I purchased it but I console myself by thinking that
similar configuration is 500 dollars costlier for the 2020 version as of this writing.</p>
<p>Before you start judging me for why didn’t I buy the Developer Edition as it should have flawless driver support for Linux, I would like to clarify that I did
consider it but for the similar configuration it was only 10 dollars cheaper and didn’t have a fingerprint sensor; the Dell Website didn’t list these in the spec page of
developer edition. I made up my mind to fight driver issues head-on on the Windows edition, should they come up; but touchwood, I didn’t get any that hinder my ability to use the laptop effectively!</p>
<h4 id="requirements">Requirements</h4>
<p>These set of steps should take less than an hour of your time, assuming you have a fairly performant internet connection as per 2019-2020 standards accessible via a WPA2 encrypted WiFi network.
This article also assumes that you have some basic understanding and familiarity with setting up and running any Linux distro for a while.
This article doesn’t require you to have another laptop with you during the installation process. You should have a USB thumbdrive (a.k.a. pendrive) and the USB-C to USB-A adapter
that comes free with the XPS 13 that may look something like below image.
<img src="/images/thumb_drive.png" alt="Thumb drive" /></p>
<h4 id="why-arch">Why Arch</h4>
<p>I have been an Arch user since 2011 and never had any issues with it. <a href="https://wiki.archlinux.org/">ArchWiki</a> has probably the best documentation among all distros, and
<a href="https://aur.archlinux.org/">AUR</a> has a comprehensive collection of packages that can be installed if not found in official repositories. With a rolling release model so you DO NOT have to
update your distro every 6 months the new version comes, and serves the latest versions of almost all packages from its repositories. Despite of being rolling release,
it is very stable. It is very lean; on top of a basic Linux system it’s the user who makes all choices for installing only the packages required. And because of rolling release nature, you have
the goodies rolling in everyday. The package manager <code class="language-plaintext highlighter-rouge">pacman</code> is very fast and processes installation of packages in a jiffy.
Using Arch gives you a feeling of being in control of everything.</p>
<h3 id="step-1-download-the-arch-installation-image-and-prepare-the-arch-linux-boot-disk">Step 1: Download the arch installation image and prepare the Arch Linux boot disk</h3>
<p>Boot into the pre-installed Windows 10 of your Dell XPS 13 and download the Arch Linux latest installation image from this page: <a href="https://www.archlinux.org/download/">Arch Linux - Downloads</a>.
I chose to download the image over BitTorrent protocol. I find it usually faster than HTTP, and it also frees up bandwidth for other needy users to download from HTTP server.</p>
<p>Once you have the installation image downloaded, you can create a boot disk with it. I use the <code class="language-plaintext highlighter-rouge">DD for windows</code> tool for this which can be downloaded from this
<a href="http://www.chrysocome.net/download">download page</a>. The page appears confusing on first looks as it has lots of files listed for download. You can download the file named
<code class="language-plaintext highlighter-rouge">dd-0.6beta3.zip</code> and extract it, then open up a command window or powershell window, and navigate to the extracted folder.</p>
<p>The following command will create a Arch boot image in a pendrive that has been inserted and has been assigned the drive letter D. Adjust the command to reflect the correct path
of the downloaded Arch image and the drive letter assigned to the thumbdrive. Ensure that you put the backslashes exactly as shown in the <code class="language-plaintext highlighter-rouge">of</code> parameter.</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.\dd <span class="k">if</span><span class="o">=</span><span class="kd">C</span>:\Users\your<span class="na">-windows-username</span>\Downloads\archlinux<span class="o">-</span><span class="m">2020</span>.01.01<span class="na">-x</span><span class="m">86</span>_64.iso <span class="kd">of</span><span class="o">=</span>\\.\d: <span class="kd">bs</span><span class="o">=</span><span class="m">16</span><span class="kd">M</span>
</code></pre></div></div>
<h3 id="step-2-disable-bitlocker-and-partition-the-disk-for-arch">Step 2: Disable Bitlocker and partition the disk for Arch</h3>
<p>Microsoft’s Bitlocker disk encryption can annoy you by forcing you to enter a long encryption key when you attempt to boot Windows after Arch installation. So it is a good idea to disable Bitlocker
beforehand. Type <code class="language-plaintext highlighter-rouge">Manage BitLocker</code> in start menu and disable it for C drive. Or you can go to <code class="language-plaintext highlighter-rouge">Control Panel</code> > <code class="language-plaintext highlighter-rouge">System and Security</code> > <code class="language-plaintext highlighter-rouge">Bitlocker drive encryption</code> and disable it from there.</p>
<p>Now you need to free some space up for your Linux installation. You can do this by using the Disk Management tool that comes built-in with Windows 10 or the partitioning tools for linux during
installation process. In this article we are doing it from Windows. Microsoft has nice online documentation for it here:
<a href="https://docs.microsoft.com/en-us/windows-server/storage/disk-management/overview-of-disk-management">Disk Management</a></p>
<p>Press the start button, or the Window key on the keyboard and start typing <code class="language-plaintext highlighter-rouge">Create and Format Disk Partitions</code>. Launch the program. It will show you the layout of the hard disk partition
structure. By default, it comes with a 500MB EFI partition, a very large C drive partition, a WINRETOOLS parition, a factory image partition and a DELLSUPPORT parition. You can shrink the
existing C drive parition to make space for Linux.</p>
<p>Right click on the C drive partition and choose <code class="language-plaintext highlighter-rouge">Shrink Volume ...</code>. Enter the size for the new partition. The row under it should autocalculate and show you the final size of C drive.
Make sure to keep it reasonable for your Windows needs. I entered 500000 and it resulted in a 488GB of raw unallocated free space.</p>
<p>I chose to create a 8GB volume for swap space and remaining space for the <code class="language-plaintext highlighter-rouge">/</code> partition. For this, just right click the newly created unallocated space and click <code class="language-plaintext highlighter-rouge">New Simple Volume</code>
and enter 8192 MB as the size. Choose to not assign a drive letter when asked. Then right click the remaining space, click <code class="language-plaintext highlighter-rouge">New Simple Volume</code> and allocate all the remaining space for the volume
for <code class="language-plaintext highlighter-rouge">/</code> partition.</p>
<p>Some people create separate partition for <code class="language-plaintext highlighter-rouge">/home</code> as well which in my opinion is actually a good idea, because if for some reason your Linux installation gets borked, you can nuke the <code class="language-plaintext highlighter-rouge">/</code> partition,
install a fresh copy of your OS on it (even different distro altogether) and retain everything in the <code class="language-plaintext highlighter-rouge">/home</code> by setting the mount point in <code class="language-plaintext highlighter-rouge">fstab</code>. This makes everything look like it used to
earlier (assuming certain conditions hold), because of all the user level customizations in the desktop environment being retained in users’ home directory. I find separate partitions for /usr and
/var on personal computers an overkill. For the sake of simplicity we are not creating a separate <code class="language-plaintext highlighter-rouge">/home</code> partition in this article.</p>
<p>Ultimate result should look something like the below image. If you have created more partitions, they will appear too.</p>
<p><img src="/images/disk_layout.jpg" alt="Disk Layout" /></p>
<h3 id="step-3-change-sata-mode-to-ahci-and-disable-secureboot">Step 3: Change SATA mode to AHCI and disable SecureBoot</h3>
<p>Time to fiddle with some BIOS settings. Before we start installation, we need to change the SATA operation mode in BIOS to AHCI so that the Arch boot disk can recognize the hard disk volumes. If you do not
change the SATA mode to AHCI, Arch installation disk won’t recognize your NVMe M.2 hard disk in XPS 13.</p>
<p>Get into the BIOS settings by rebooting the computer and pressing F12 repeatedly until you see the text <code class="language-plaintext highlighter-rouge">Preparing one time boot menu</code> in the top right corner of the screen.
Then click <code class="language-plaintext highlighter-rouge">BIOS Setup1 and change the SATA operation to AHCI under </code>System Configuration` section of the left sidebar. Here’s a screenshot that may come handy identifying where to do this.</p>
<p><img src="/images/sata_mode.png" alt="SATA Mode AHCI" /></p>
<p>Also, disable SecureBoot in BIOS because it won’t allow setting up Grub which is the de-facto bootloader for Linux distributions. Below screenshot shows how to disable SecureBoot.</p>
<p><img src="/images/secure_boot.jpg" alt="Disable secure boot" /></p>
<p>Save the change which will result in a system restart.</p>
<h3 id="step-4-boot-the-arch-installation-media">Step 4: Boot the arch installation media</h3>
<p>Press the F12 key repeatedly while the ssytem is starting and get into the One Time Boot Menu again and choose the pendrive as the boot device, as shown in the screenshot below.
<img src="/images/boot_device.png" alt="Boot device" /></p>
<p>If the disk image was created correctly you should see the Arch Boot Menu as shown below. Choose the first option and press enter.
<img src="/images/arch_boot.png" alt="Arch boot menu" /></p>
<p>You should see the <code class="language-plaintext highlighter-rouge">root@archiso</code> prompt in a few moments. Please note that, on the 4K display XPS 13, the letters in the terminal during installation look extremely small. However, you have to
face this problem only during installation and after the installation of a GUI everything should start looking good.</p>
<h3 id="step-5-connect-to-the-network-and-enable-ntp-time-sync">Step 5: Connect to the network and enable NTP time sync</h3>
<p>I assume you have a WiFi network in range. You can use the <code class="language-plaintext highlighter-rouge">wifi-menu</code> command that comes pre-installed in the installation disk. It has a nice curses based UI where you can choose the WiFi network
from a menu of options and enter its password to connect to it. Test the connection by running <code class="language-plaintext highlighter-rouge">ping archlinux.org</code>.</p>
<p>Now set the time with NTP by running</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>timedatectl set-ntp <span class="nb">true</span>
</code></pre></div></div>
<h3 id="step-6-setup-filesystems">Step 6: Setup filesystems</h3>
<p>Run the below command</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> /dev/nvme<span class="k">*</span>
</code></pre></div></div>
<p>to check for the block devices. It is highly likely that you should be able to identify <code class="language-plaintext highlighter-rouge">/dev/nvme0n1</code> as the block device representing your hard disk, which is because all XPS 13s come
with NVME based M.2 SSDs. You can verify this by running <code class="language-plaintext highlighter-rouge">fdisk -l /dev/nvme0n1</code> which should output the list of Windows partition that already exist and the raw partitions.</p>
<p>Carefully check the output of the fdisk command above, and figure out the partition number for the swap and / partitions that we created in Step 1. For me, the output looks like this.</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fdisk -l /dev/nvme0n1
Disk /dev/nvme0n1: 953.89 GiB, 1024209543168 bytes, 2000409264 sectors
Disk model: PC601 NVMe SK hynix 1TB
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 7EED0FB4-482A-45A9-A7CE-55E525352534
Device Start End Sectors Size Type
/dev/nvme0n1p1 2048 1394687 1392640 680M EFI System
/dev/nvme0n1p2 1394688 1656831 262144 128M Microsoft reserved
/dev/nvme0n1p3 1656832 944373759 942716928 449.5G Microsoft basic data
/dev/nvme0n1p4 944373760 961150975 16777216 8G Microsoft basic data
/dev/nvme0n1p5 961150976 1968371711 1007220736 480.3G Microsoft basic data
/dev/nvme0n1p6 1968373760 1970401279 2027520 990M Windows recovery environment
/dev/nvme0n1p7 1970401280 1997346815 26945536 12.9G Windows recovery environment
/dev/nvme0n1p8 1997348864 2000408575 3059712 1.5G Windows recovery environment
</code></pre></div></div>
<p>I was able to figure out that <code class="language-plaintext highlighter-rouge">/dev/nvme0n1p4</code> was the 8GB partition I created for swap and the <code class="language-plaintext highlighter-rouge">/dev/nvme0n1p5</code> was the partition I created for root filesystem. Enable swap on the swap parititon
and format the partition for root filesystem into ext4.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkfs.ext4 /dev/nvme0n1p5
mkswap /dev/nvme0n1p4
swapon /dev/nvme0n1p4
</code></pre></div></div>
<p>Now that we are ready with filesystems, we can go ahead mounting them and starting installation.</p>
<h3 id="step-7-mount-partitions-and-install-needed-packages">Step 7: Mount partitions and install needed packages</h3>
<p>Create a directory <code class="language-plaintext highlighter-rouge">/mnt/root</code> to mount the root filesystem and EFI system partition on <code class="language-plaintext highlighter-rouge">/mnt/root/boot</code>.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> <span class="nt">-p</span> /mnt/root /mnt/root/boot
mount /dev/nvme0n1p5 /mnt/root
mount /dev/nvme0n1p1 /mnt/root/boot
</code></pre></div></div>
<p>Open the file <code class="language-plaintext highlighter-rouge">/etc/pacman.d/mirrorlist</code> in a text editor like <code class="language-plaintext highlighter-rouge">vim</code> or <code class="language-plaintext highlighter-rouge">nano</code>. Bring a few mirror URLs closest to you geographically to the top of the file. Since, I live on the west coast of USA,
I brought a few US west coast pacman mirrors onto the top. This should ensure that the package installation command runs fast.</p>
<p>Now install the necessary packages using pacstrap.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pacstrap /mnt/root base linux linux-firmware base-devel grub os-prober intel-ucode alsa deepin deepin-extra <span class="nb">sudo </span>lightdm lightdm-deepin-greeter
</code></pre></div></div>
<p>I like deepin as my desktop environment as it looks beautiful and has a perfect traditional desktop look and feel. If you want Gnome, or KDE, or LXDE, or LXQT, or XFCE or any other desktop environment
instead, feel free to modify the above command and install corresponding packages.</p>
<h3 id="step-8-configure-base-system">Step 8: Configure base system</h3>
<p>Generate an fstab file by running <code class="language-plaintext highlighter-rouge">genfstab -U /mnt/root >> /mnt/root/etc/fstab</code>.</p>
<p>Now chroot into the system to configure some other necessary stuff. Arch installation image has a handy chroot wrapper script named <code class="language-plaintext highlighter-rouge">arch-chroot</code>, which chroots into the installed root filesystem
and also binds necessary virtual filesystem mounts like <code class="language-plaintext highlighter-rouge">proc</code>, <code class="language-plaintext highlighter-rouge">sys</code> and <code class="language-plaintext highlighter-rouge">dev</code> correctly from the currently running installation OS.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>arch-chroot /mnt/root
</code></pre></div></div>
<h4 id="set-timezone">Set timezone</h4>
<p>Set the time zone by creating a symlink to the zoneinfo file for your region at <code class="language-plaintext highlighter-rouge">/etc/localtime</code>. Command below is for US West Coast. You can change it according to your region.
Also, set the clock to use UTC.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ln</span> <span class="nt">-sf</span> /usr/share/zoneinfo/Americas/Los_Angeles
hwclock <span class="nt">--systohc</span>
</code></pre></div></div>
<h4 id="set-locale">Set locale</h4>
<p>Open <code class="language-plaintext highlighter-rouge">/etc/locale.gen</code> in vi or nano and uncomment the <code class="language-plaintext highlighter-rouge">en_US.UTF-8 UTF-8</code> line. Then run <code class="language-plaintext highlighter-rouge">locale-gen</code>. After this, open <code class="language-plaintext highlighter-rouge">/etc/locale.conf</code> and add a line <code class="language-plaintext highlighter-rouge">LANG=en_US.UTF-8</code>.</p>
<h4 id="network-configurations">Network configurations</h4>
<p>Create the hostname file <code class="language-plaintext highlighter-rouge">/etc/hostname</code> and add a name for your laptop, for example <code class="language-plaintext highlighter-rouge">my-xps</code>. Also update the hostname in <code class="language-plaintext highlighter-rouge">/etc/hosts</code> so it looks like below:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>127.0.0.1 localhost
::1 localhost
127.0.1.1 my-xps.localdomain my-xps
</code></pre></div></div>
<p>Deepin comes with NetworkManager but it didn’t work as expected for me for connecting to WiFi. So you might want to disable NetworkManager and install netctl.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl disable NetworkManager
pacman <span class="nt">-S</span> netctl wpa_supplicant dhcpcd
</code></pre></div></div>
<p>You can also create a <code class="language-plaintext highlighter-rouge">netctl</code> profile right away so that you do not need to struggle connecting to the internet when you reboot after the installation is finished.</p>
<p>Create a file named <code class="language-plaintext highlighter-rouge">/etc/netctl/MyWiFi</code> and put the following content. This assumes that your home WiFi network name is <code class="language-plaintext highlighter-rouge">MyWiFi</code>. I also faced lag and disconnection issues with
5GHz wifi networks so for now I am using a 2.4GHz network only. I haven’t spent time figuring out the cause and fixing it, I may update this article when I do.</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Description='My home WiFi connection'
Interface=wlp2s0
Connection=wireless
Security=wpa
IP=dhcp
ESSID='MyWiFi' # <=== Put your WiFi network's name here
Key='12345678' # <== Put your WiFi password here
</code></pre></div></div>
<h4 id="build-initramfs">Build initramfs</h4>
<p>Run <code class="language-plaintext highlighter-rouge">mkinitcpio -P</code> to build the initramfs.</p>
<h3 id="set-root-password">Set root password</h3>
<p>Set a root password by running the <code class="language-plaintext highlighter-rouge">passwd</code> command.</p>
<h3 id="step-9-configure-grub">Step 9: Configure Grub</h3>
<p>During pacstrap we installed <code class="language-plaintext highlighter-rouge">grub</code> and <code class="language-plaintext highlighter-rouge">os-prober</code> so, now we just need to install grub and create a config for it. If <code class="language-plaintext highlighter-rouge">os-prober</code> is installed, <code class="language-plaintext highlighter-rouge">grub-mkconfig</code> command automatically
detects Windows partition, if any and adds an entry for booting into it in the boot menu.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grub-install <span class="nt">--target</span><span class="o">=</span>x86_64-efi <span class="nt">--efi-directory</span><span class="o">=</span>/mnt/root/boot <span class="nt">--bootloader-id</span><span class="o">=</span>GRUB
grub-mkconfig <span class="nt">-o</span> /boot/grub/grub.cfg
</code></pre></div></div>
<h3 id="step-10-create-a-user-and-enable-sudo">Step 10: Create a user and enable sudo</h3>
<p>Create a non-root user and add it to necessary groups (at least <code class="language-plaintext highlighter-rouge">wheel</code>). Replace the username <code class="language-plaintext highlighter-rouge">master</code> in below command with your desired username.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>useradd <span class="nt">-m</span> <span class="nt">-G</span> wheel <span class="nt">-s</span> /bin/bash master
</code></pre></div></div>
<p>Also set a password for this account by running <code class="language-plaintext highlighter-rouge">passwd master</code>.</p>
<p>Run <code class="language-plaintext highlighter-rouge">visudo</code> to open sudoers file. Find the line that allows members of group wheen to run sudo commands without password and uncomment it. It should look something like below:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>%wheel ALL=(ALL) NOPASSWD: ALL
</code></pre></div></div>
<p>However, it is not recommended for everybody to configure sudo without password. You might want to configure it to be used with password instead by uncommenting the following line instead.</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>%wheel ALL=(ALL) ALL
</code></pre></div></div>
<h3 id="step-11-prepare-for-reboot">Step 11: Prepare for reboot</h3>
<p>Now your arch installation is ready to boot into. But before that, we need to perform one final configuration, which is to configure <code class="language-plaintext highlighter-rouge">lightdm</code> - the display manager to use <code class="language-plaintext highlighter-rouge">deepin</code>’s greeter.</p>
<p>Open the file <code class="language-plaintext highlighter-rouge">/etc/lightdm/lightdm.conf</code>. Find the line that says <code class="language-plaintext highlighter-rouge">#greeter-session</code>, uncomment it and update it to use deepin’s greeter as shown below.</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>greeter-session=lightdm-deepin-greeter
</code></pre></div></div>
<p>Also, enable lightdm by running <code class="language-plaintext highlighter-rouge">systemctl enable lightm</code>. Now reboot by typing <code class="language-plaintext highlighter-rouge">reboot</code>. You should be greeted by a beautiful login screen. When you login, you will see a desktop resembling the below
screenshot.</p>
<p><img src="/images/deepin_desktop.png" alt="Deepin desktop" /></p>
<p>Deepin desktop comes with a dock by default but I prefer a task bar instead. You can right click the dock and change the mode to <code class="language-plaintext highlighter-rouge">Efficient Mode</code>.</p>
<p><img src="/images/dock_mode.png" alt="Deepin taskbar" /></p>
<p>You might also want to change the size of icons to small instead of large.</p>
<p><img src="/images/dock_size.png" alt="Deepin taskbar size" /></p>
<p>If the font sizes look too small or too large, you can adjust the scaling factor by right clicking the desktop, choosing <code class="language-plaintext highlighter-rouge">Display Settings</code> as shown in the screenshot below.</p>
<p><img src="/images/display_scale.png" alt="Display Scaling" /></p>
<h3 id="step-13-fix-windows">Step 13: Fix windows</h3>
<p>Since we overwrote Windows’s EFI partition, it will refuse to boot. But there is a simple trick to fix that. You just need to boot Windows in safe mode once and it figures out stuff automatically.
Kudos to Microsoft for this! If you try booting Windows from the Grub menu it will get into the dreaded blue screen of death. But from there you will get an option to attempt to diagnose
and fix boot problems. There you can choose to boot into safe mode. Once you have successfully made it into safe mode, you should also make a change in Windows registry so that it treats hardware
clock to be in UTC. This way the time shown in Linux as well as Windows will be similar. For that, just open a command prompt with administrator privileges and run the following command:</p>
<div class="language-bat highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">reg</span> <span class="kd">add</span> <span class="s2">"HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation"</span> <span class="na">/v </span><span class="kd">RealTimeIsUniversal</span> <span class="na">/d </span><span class="m">1</span> <span class="na">/t </span><span class="kd">REG_QWORD</span> <span class="na">/f
</span></code></pre></div></div>
<p>Or you could open <code class="language-plaintext highlighter-rouge">regedit</code> and manually navigate to the above key and add a new QWORD value of 1 for <code class="language-plaintext highlighter-rouge">RealTimeIsUniversal</code>.</p>
<h3 id="step-14-all-set">Step 14: All set</h3>
<p>Assuming that you sailed through the process smoothly, congratulations! You now have your Dell XPS 13 with a dual boot setup of Windows 10 and Arch Linux. If you are a developer like me, you might want
to change your shell to zsh, install Git, Vim, htop, Ruby, NodeJS, JDK, Postman, Docker, Visual Studio Code, IntelliJ Idea Community Edition and other development tools and platforms to facilitate your
development workflow. As general utilities, you might want to install Gimp for photo editing, imagemagick, VLC media player, xarchiver, p7zip-full, unrar, Evince for PDF viewing, Chromium and Firefox
for web browsing and Audacious for listening to your music collection.</p>
<p>Here’s an obligatory screenshot (compressed JPEG) of my final setup.</p>
<p><img src="/images/arch_desktop.jpg" alt="Arch Desktop" /></p>
<h3 id="known-issues">Known Issues</h3>
<p>I have found the following issues on my Dell XPS 13 (with factory installed Windows) while using Arch Linux:</p>
<ul>
<li>Looses connection to 5G WiFi network intermittently (however 2.4GHz networks work perfectly)</li>
<li>Cannot use NetworkManager to connect to WiFi networks. Netctl works flawlessly, but to make your life easier install wifi-menu from AUR.</li>
</ul>]]></content><author><name>Prabhakar Kumar</name><email>your-email@domain.com</email></author><category term="archlinux" /><category term="dell-xps-13" /><summary type="html"><![CDATA[Intro]]></summary></entry><entry><title type="html">Run your webapps on Kubernetes for cheap</title><link href="/2019/04/11/run-your-webapps-on-kubernetes-for-cheap.html" rel="alternate" type="text/html" title="Run your webapps on Kubernetes for cheap" /><published>2019-04-11T09:48:53-07:00</published><updated>2019-04-11T09:48:53-07:00</updated><id>/2019/04/11/run-your-webapps-on-kubernetes-for-cheap</id><content type="html" xml:base="/2019/04/11/run-your-webapps-on-kubernetes-for-cheap.html"><![CDATA[<h3 id="tldr">TL;DR</h3>
<p>This post details how to spin off a 3 node Kubernetes cluster on Google Cloud, paying close to 7 dollars a month for it and host multiple database backed dynamic websites and API apps.</p>
<h3 id="disclaimer">Disclaimer</h3>
<p>This is a hands on article to get started with Kubernetes. Some of the terminology and descriptions in this article have been simplified to make it approachable to people unfamiliar with
Kubernetes. For technical purity, Kubernetes’s official documentation can be consulted.</p>
<h3 id="intro">Intro</h3>
<p>Kubernetes is the new cool kid on the block (bye bye blockchain!). Listing it as an skill in your LinkedIn profile will definitely get you some recruiter attention.
It is <em>the solution</em> for all your dev-ops woes!</p>
<p>I used to run all of my hobby projects in docker containers with lifecycles managed by docker-compose files. I had a VM from <a href="https://scaleway.com">scaleway</a> with 4 gigs of memory,
100 gigs of SSD storage and dual core x86 CPU for which I used to pay around 5 Euros a month. I was happy until I learnt about Kubernetes and then I realized <em>ignorance is bliss</em>.
After reading this post, you will be able to spin off a 3 node Kubernetes cluster on Google Cloud with 0.6GB memory each and 1 vCPU each and pay roughly ~7 dollars a month for it!
If you want a beefier cluster you can choose a different configuration for nodes and for 40 dollars a month you get 11.25GB of memory at your disposal, three compute cores with which you can do
wonders - host several high traffic webapps backed by different databases! Adding to that, with Google Cloud’s free trial program you can have this cluster running for free for almost an year!</p>
<p>My three node cluster currently runs <code class="language-plaintext highlighter-rouge">mongodb</code>, <code class="language-plaintext highlighter-rouge">postgres</code>, <code class="language-plaintext highlighter-rouge">nginx</code>, two rails web applications, a nodeJS web application, a dotnetcore web application, a spring boot based Java REST API application
which powers an Android app, and still has a lot of resources remaining to do 3-4 times more of what it currently does. All the web apps are fronted by nginx and configured with different server blocks
for different domain names (a.k.a virtual hosts). All the domain names have their own letsencrypt https certificates that automatically renew every three months. Two of the apps use mongodb and
the other two postgres.</p>
<h4 id="single-vm-vs-cluster">Single VM vs Cluster</h4>
<p>Running all your workloads in a single VM is like putting all the eggs in the same basket. If ever the VM is restarted for updating the Kernel, reclaim some leaked memory or something else;
all the apps hosted on the box incur downtime. Running on Kubernetes is better, because:</p>
<ol>
<li>Cloud provider (GCP) manages updates and patches on your VMs, so you don’t need to explicitly restart nodes. In case of node restarts, Kubernetes ensures relocating workloads running on the unavailable VM.</li>
<li>Kubernetes restarts containers automatically when underlying process crashes due to some issue.</li>
<li>Kubernetes handles load balancing of requests and scaling of resources to fulfill the requests. For example, you can tell Kubernetes declaratively to run more instances of your webapp when the load is higher.</li>
<li>Kubernetes sets up internal networking in the cluster so that your apps can talk to each other. This enables you to build microservices based large scale services.</li>
</ol>
<p>Besides, using Kubernetes to run your compute infrastructure is considered cool in 2019!</p>
<h3 id="what-is-kubernetes">What is Kubernetes?</h3>
<p>Kubernetes’s official website writes:</p>
<blockquote>
<p>Kubernetes is a portable, extensible open-source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation.</p>
</blockquote>
<p>This might not be quite an approachable description for everybody. Here is my attempt:</p>
<blockquote>
<p>Kubernetes is like an operating system, for your distributed computing cluster that lets you run your scalable applications without being bothered about nodes in the cluster going down, request routing, load balancing, networking and other devops hassles.</p>
</blockquote>
<h3 id="lets-get-started">Let’s get started</h3>
<h4 id="step-1-setup-a-3-node-cluster-on-google-cloud">Step 1: Setup a 3-node cluster on Google Cloud</h4>
<p>Register on Google Cloud, if you already haven’t. Create a project, setup billing. Click the hamburger menu icon (<img src="/images/hamburger.png" alt="menu" />) on the top left, and in <em>Compute</em> section,
click <em>Kubernetes Engine</em>. Click the <code class="language-plaintext highlighter-rouge">Create Clusters</code> button. Make sure in the left pane, cluster template is set to <code class="language-plaintext highlighter-rouge">Standard cluster</code>. In the right pane, name your cluster;
for example <em>blog-cluster</em>. Fill in the settings as listed below:</p>
<ul>
<li><em>Location type</em>: Zonal</li>
<li><em>Zone</em>: us-central1-a</li>
</ul>
<p>Under node pools:</p>
<ul>
<li><em>Number of nodes</em>: 3</li>
<li><em>Machine type</em>: <code class="language-plaintext highlighter-rouge">n1-standard-1</code> (this will cost roughly $40/month for 3 nodes). Choose <code class="language-plaintext highlighter-rouge">f1-micro</code> for $7/month instead.</li>
</ul>
<p>Click <em>More node pool options</em> and ensure that the following are set:</p>
<ul>
<li><em>Enable autoscaling</em>: off</li>
<li><em>Boot disk type</em>: Standard persistent disk</li>
<li><em>Boot disk size</em>: 10 GB</li>
<li><em>Enable pre-emptible nodes</em>: Yes - you must do this for cost cutting. These nodes get terminated within 24 hours and get replaced with another one and cost around one-fourth of regular nodes.
Since Kubernetes can handle node downtimes, we need not worry about our app going down when GCP replaces a VM.</li>
</ul>
<p>Click <em>Availability, networking, security, and additional features</em> and ensure the following:</p>
<ul>
<li><em>Enable HTTP load balancing</em>: No - this is costly! We will use Kubernetes’s default balancer</li>
<li><em>Enable Stackdriver Logging service</em>: No</li>
<li><em>Enable Stackdriver Monitoring service</em>: No</li>
<li><em>Enable Kubernetes Dashboard</em>: No</li>
</ul>
<p>Leave all the other settings to their sane defaults. Changing some of them might cost you extra money. Click the <code class="language-plaintext highlighter-rouge">Create</code> button and wait for the cluster to come up. The clusters page should
show your newly created cluster.</p>
<p><img src="/images/kubernetesclusters.png" alt="Clusters" /></p>
<h4 id="step-2-connect-to-your-newly-minted-cluster">Step 2: Connect to your newly minted cluster</h4>
<p>Install <code class="language-plaintext highlighter-rouge">kubectl</code> command line utility for your OS. In the <em>Clusters</em> page, click <em>Connect</em> and it will show you a <code class="language-plaintext highlighter-rouge">gcloud</code> command which downloads the necessary authentication information
for the <code class="language-plaintext highlighter-rouge">kubectl</code> command. Run the command. Once you have done that, test your connection by running <code class="language-plaintext highlighter-rouge">kubectl get nodes</code> which should return you a list of three nodes.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-blog-cluster-default-pool-edcca0b2-4mf2 Ready <none> 1m v1.11.7-gke.12
gke-blog-cluster-default-pool-edcca0b2-hd1p Ready <none> 1m v1.11.7-gke.12
gke-blog-cluster-default-pool-edcca0b2-z6f1 Ready <none> 1m v1.11.7-gke.12
</code></pre></div></div>
<h4 id="step-3-prepare-your-first-application">Step 3: Prepare your first application</h4>
<p>Let’s prepare a rails application that uses a <code class="language-plaintext highlighter-rouge">postgresql</code> database to deploy on this cluster. We will name it <code class="language-plaintext highlighter-rouge">RailsPortal</code>, you are free to name whatever you want.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem <span class="nb">install </span>bundler
gem <span class="nb">install </span>rails
rails new RailsPortal
bundle <span class="nb">exec </span>rails s
</code></pre></div></div>
<p>Last command should start a local server at port 3000 and you should be able to access it at <code class="language-plaintext highlighter-rouge">localhost:3000</code>. Stop the server by running <code class="language-plaintext highlighter-rouge">Ctrl-C</code>. Open <code class="language-plaintext highlighter-rouge">config/database.yml</code> and around the
end change the production db connection settings so that they look like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>production:
<<: <span class="k">*</span>default
adapter: postgresql
url: <%<span class="o">=</span> ENV[<span class="s1">'DATABASE_URL'</span><span class="o">]</span> %>
</code></pre></div></div>
<p>Also, edit the <code class="language-plaintext highlighter-rouge">Gemfile</code> and add a line <code class="language-plaintext highlighter-rouge">gem 'pg'</code> then run <code class="language-plaintext highlighter-rouge">bundle update</code>. The above changes will configure the production instance of app to use postgresql instead of default sqlite and
will let us pass the <code class="language-plaintext highlighter-rouge">postgresql</code> connection string through an environment variable that Kubernetes will setup for us later.</p>
<p>Let’s create a scaffold with two fields and see whether things are working fine.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>rails g scaffold User name:string about:string
bin/rails db:migrate <span class="nv">RAILS_ENV</span><span class="o">=</span>development
</code></pre></div></div>
<p>This will generate necessary views and controllers which we can use to insert some data and ensure that things are working fine. Second command will commit pending database migrations.
Start the server again by running <code class="language-plaintext highlighter-rouge">bundle exec rails s</code> and access the url <code class="language-plaintext highlighter-rouge">http://localhost:3000/users</code> and click <code class="language-plaintext highlighter-rouge">New User</code> and try adding a new record.</p>
<p>Now that our test application is ready, let’s containerize it. If you do not have docker installed, now is the time to install it and start the docker service.
Once we build the container, we want to push it to a private docker repository. <a href="https://hub.docker.com">Docker Hub</a> lets you store container images for free. Register on their website then connect
your local <code class="language-plaintext highlighter-rouge">docker</code> command to your docker hub account by running the following command:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker login <span class="nt">--username</span><span class="o">=</span>DockerHubUsername <span class="nt">--email</span><span class="o">=</span>DockerHubEmail
</code></pre></div></div>
<p>For building the container image, we need to create a file named <code class="language-plaintext highlighter-rouge">Dockerfile</code> which tells docker how to build the container. Create this <em>Dockerfile</em> in rails portal’s root directory.</p>
<div class="language-docker highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> ruby:2.5-alpine</span>
<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> <span class="nt">--update</span> nodejs postgresql-dev libpq tzdata imagemagick
<span class="k">RUN </span>apk add <span class="nt">--no-cache</span> <span class="nt">--update</span> <span class="nt">--virtual</span> build-deps build-base git
<span class="k">RUN </span><span class="nb">cp</span> /usr/share/zoneinfo/UTC /etc/localtime
<span class="k">COPY</span><span class="s"> . /app</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">RUN </span><span class="nb">echo</span> <span class="s1">'gem: --no-document'</span> <span class="o">></span> /etc/gemrc
<span class="k">RUN </span>bundle <span class="nb">install</span> <span class="nt">--jobs</span> 4 <span class="nt">--without</span> development <span class="nb">test</span>
<span class="k">RUN </span>apk del build-deps
</code></pre></div></div>
<p>Make sure the ruby version in Dockerfile is consistent with the one in Gemfile. Now build and push the container to docker hub by running following commands:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">-t</span> DockerHubUsername/RailsPortal <span class="nb">.</span>
docker push DockerHubUsername/RailsPortal
</code></pre></div></div>
<h4 id="step-4-architect-your-infrastructure">Step 4. Architect your infrastructure</h4>
<p><img src="/images/kube_rails.png" alt="Rails on Kubernetes" /></p>
<p>We are going to use a simple architecture <em>client -> Webserver (nginx) -> Web app (rails) -> Database (postgres)</em> which is quite popular for single VM setups too. Only difference is that instead
of processes running on a VM, these will be containers and will be run on different nodes of the cluster by Kubernetes.</p>
<h4 id="step-5-plan-kubernetes-resources">Step 5. Plan Kubernetes resources</h4>
<p>Kubernetes has the concept of resources. The ones we are going to use in this project are:</p>
<ul>
<li>Deployment</li>
<li>Service</li>
<li>StorageClass</li>
<li>PersitentVolumeClaim</li>
<li>PersistentVolume</li>
<li>Secret</li>
<li>ConfigMap</li>
</ul>
<p>A <em>deployment</em> describes a container to run along with the volumes it needs, environment variables, ports it should listen to, number of replicas of it Kubernetes should run in the cluster etc.
There are many more things we can configure for a deployment, but these are the ones we are going to use in our little project. In this article, I might use the terms deployment and container
interchangeably - because in practice a Kubernetes deployment is a container running in the cluster.</p>
<p>A <em>service</em> fronts a deployment by giving it a stable private or public endpoint on which the deployment (running container) can be accessed. Private endpoints are exposed by service of type
ClusterIP and public endpoints are exposed by service of type LoadBalancer. Private endpoints are accessible only inside the cluster, from other containers. For example, a postgres deployment
should only be accessible from deployments running in the cluster, for example a web application.</p>
<p>A <em>storage class</em> is to define the characteristics of volumes that can be created on demand. We create this once and create persistent volume claims that refer to it.</p>
<p>A <em>persistent volume</em> is a storage accessible from any of the running containers on the cluster which are configured to access it. As containers are stateless, we need a reliable persistent store
to store our stateful data, for example postgres’s data files.</p>
<p>A <em>persistent volume claim</em> is an access mechanism for a container to access a persistent volume. We will talk about it ahead in the article when we need it.</p>
<p>A <em>secret</em> is used to store authentication credentials and expose it to only the resources that need it.</p>
<p>A <em>config map</em> is a keyvalue pair, that can be used to store configuration information in the cluster which can be used from a container.</p>
<p>Kubernetes provides us a declarative way of telling it about the resources we need to create (a <em>goal state</em> of our infrastructure) and then it tries to create those resources (or achieve the
<em>goal state</em>). We declare the properties for resources in <em>yaml</em> files and Kubernetes takes care of it.</p>
<p>For our little project, we will be creating the following resources:</p>
<ul>
<li>A deployment for nginx, which we will use to front our web applications (rails)</li>
<li>A service of type load balancer for nginx, such that users can access our applications from external the internet</li>
<li>A config map for the nginx configuration files</li>
<li>A secret to store authentication information for docker hub so that kubernetes can pull the rails application container that we pushed to our private docker hub repository</li>
<li>A deployment for rails portal, whose container we built in step 4</li>
<li>A service for rails portal, so that nginx can access it forward requests to it</li>
<li>A deployment for postgres, which will run the container</li>
<li>A service for postgres so that rails application can connect to it</li>
<li>A storage class for postgres that defines the kind of storage to be used for persistent volumes. E.g, SSD/magnetic etc.</li>
<li>A persistent volume claim for postgres so that the postgres deployment can access the persistent volume</li>
<li>A persistent volume for postgres, so that we can have postgres store its data files there</li>
</ul>
<h4 id="step-6-setting-up-nginx">Step 6. Setting up nginx</h4>
<p>Let’s begin by creating a deployment for nginx. Create a directory named nginx. Below gist is for a yaml file that we will use to describe nginx deployment on Kubernetes. Copy
its contents to a file named <code class="language-plaintext highlighter-rouge">nginx-dep.yml</code> on your local computer.</p>
<noscript><pre>400: Invalid request</pre></noscript>
<script src="https://gist.github.com/6e870acd7e91273a747996961a2a04c2.js"> </script>
<p>It declares that we want to create a resource of type <code class="language-plaintext highlighter-rouge">Deployment</code>, label it with name <code class="language-plaintext highlighter-rouge">nginx</code>, use the container <code class="language-plaintext highlighter-rouge">nginx</code> with tag <code class="language-plaintext highlighter-rouge">mainline-alpine</code>, and expose the container’s port 80.
By default the container image is pulled from docker hub. Now run <code class="language-plaintext highlighter-rouge">kubectl apply -f nginx-dep.yml</code> to let Kubernetes do its magic.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% kubectl apply <span class="nt">-f</span> nginx-dep.yml
deployment.apps/nginx-dep created
% kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-dep-776c646dc6-g4w9x 1/1 Running 0 25s
</code></pre></div></div>
<p>The output of last command tells us that an nginx deployment is running since 25 seconds. Yes, you just noticed that we used a new term <code class="language-plaintext highlighter-rouge">pods</code> in the command. A pod can be considered as an application
for all practical purposes. You can find about it more in official Kubernetes documentation.</p>
<p>Let’s create an LB now so that we can access the running container from outside. Copy the below contents to a file named <code class="language-plaintext highlighter-rouge">nginx-svc.yml</code>.</p>
<noscript><pre>400: Invalid request</pre></noscript>
<script src="https://gist.github.com/d76f720c5b9d666965e959861e5e33b3.js"> </script>
<p>This file declares that we want a resource of kind <code class="language-plaintext highlighter-rouge">Service</code> of type <code class="language-plaintext highlighter-rouge">LoadBalancer</code> which should accept connections on port 80 and direct to port 80 of a service. Which service? The service is selected
using the selector parameter which says to match it with the <code class="language-plaintext highlighter-rouge">app: nginx</code> which we had specified in our <code class="language-plaintext highlighter-rouge">nginx-dep.yml</code> file. Let’s deploy this file too.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% kubectl apply <span class="nt">-f</span> nginx-svc.yml
service/nginx-lb created
% kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT<span class="o">(</span>S<span class="o">)</span> AGE
kubernetes ClusterIP 10.59.240.1 <none> 443/TCP 4m
nginx-lb LoadBalancer 10.59.240.108 <pending> 80:31463/TCP 24s
</code></pre></div></div>
<p>This shows that the resource with name nginx-lb and type LoadBalancer has been created. But it is pending an external ip. Wait for some time and run the command again to get the external ip.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT<span class="o">(</span>S<span class="o">)</span> AGE
kubernetes ClusterIP 10.59.240.1 <none> 443/TCP 5m
nginx-lb LoadBalancer 10.59.240.108 34.66.92.238 80:31463/TCP 1m
</code></pre></div></div>
<p>Now, if you try typing the shown external ip in a browser, you should be able to get the nginx’s welcome page! Awesome! You can change a domain’s A record to point to this IP address and this
will be accessible from any browser through the domain name.</p>
<h4 id="step-7-setting-up-postgres">Step 7. Setting up postgres</h4>
<p>Our webapp will need Postgres to store its data. Since we need postgres’s data to be persistent across container and node restarts, we need to create a persistent volume. For that we first
create a storage class with type SSD. Then we create a persistent volume claim that uses this newly created storage class. A persistent volume gets automatically created, when we try to create
a persistent volume claim using the storage class of <code class="language-plaintext highlighter-rouge">kubernetes.io/gce-pd</code>. We can also club two related storage related declarations into a single yaml file as shown below.</p>
<noscript><pre>400: Invalid request</pre></noscript>
<script src="https://gist.github.com/45ddec715c5e9840e715db5d1b0282ab.js"> </script>
<p>Let’s create the resources from this yml file and check their statuses.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>% kubectl apply <span class="nt">-f</span> postgres-storage.yml
storageclass.storage.k8s.io/postgres-ssd created
persistentvolumeclaim/postgres-disk-claim created
% kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
postgres-disk-claim Bound pvc-79f8200b-5c64-11e9-978f-42010a800070 1Gi RWO postgres-ssd 12s
% kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-79f8200b-5c64-11e9-978f-42010a800070 1Gi RWO Delete Bound default/postgres-disk-claim postgres-ssd 12s
</code></pre></div></div>