-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
nebuxadnezzar
committed
Jun 27, 2024
1 parent
6c7693d
commit 469a5ea
Showing
36 changed files
with
59,658 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Interview take home coding assignments | ||
|
||
- [alpaca](./alpaca/README.md) | ||
- [esusu](./esusu/README.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
BINARY_NAME=lottery-winners | ||
MAIN_NAME=cmd/cli.go | ||
DIST=./dist | ||
|
||
run: | ||
go run ${MAIN_NAME} $(arg1) | ||
|
||
build: | ||
GOARCH=amd64 GOOS=darwin go build -o ${DIST}-darwin/${BINARY_NAME} ${MAIN_NAME} | ||
GOARCH=amd64 GOOS=linux go build -o ${DIST}-linux/${BINARY_NAME} ${MAIN_NAME} | ||
GOARCH=amd64 GOOS=windows go build -o ${DIST}-windows/${BINARY_NAME} ${MAIN_NAME} | ||
|
||
deps: | ||
@[ -d $(DIST)-linux ] || mkdir -p $(DIST)-linux | ||
@[ -d $(DIST)-darwin ] || mkdir -p $(DIST)-darwin | ||
@[ -d $(DIST)-windows ] || mkdir -p $(DIST)-windows | ||
|
||
clean: | ||
@rm -rf ${DIST}* | ||
|
||
all: deps build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
|
||
## Story | ||
__Alpaca Inc(?)__ presented me with this take home assignment described [here](pdf/Software_Engineering_Test.pdf). | ||
|
||
## Data | ||
Sample file [here](data/lottery-300.txt) | ||
``` | ||
4 79 13 80 56 | ||
71 84 48 85 38 | ||
41 65 39 82 36 | ||
36 42 79 21 58 | ||
... | ||
``` | ||
__or__ to generate 10 million records lottery numbers file yourself | ||
```ruby | ||
ruby -e '50000000.times.map{rand(1..90)}.each_with_index{|a,i| printf("\n") if i > 0 && i % 5 == 0; printf("%s ", a)}' | ||
|
||
or to generate without trailing space | ||
|
||
ruby -e '50000000.times.map{rand(1..90)}.each_with_index{|a,i| if i > 0 then i % 5 == 0 ? printf("\n") : print(" ") end; printf("%d",a)}' | ||
``` | ||
### Building binary | ||
``` | ||
make all | ||
``` | ||
### Running code | ||
``` | ||
make run arg1=~/path/to/10m-v2.txt | ||
``` | ||
|
||
### Run results | ||
|
||
``` | ||
go run cmd/cli.go ~/test-data/10m-v2.txt | ||
error loading record 85 21 67 93 2 | ||
: value 93 out of range | ||
error loading record 10 85 69 -7 50 | ||
: value -7 out of range | ||
LOADED 10000000 records | ||
READY: enter your numbers below or press Ctrl+C to exit | ||
--> 77 88 21 33 1 | ||
numbers matching | winners | ||
5 | 0 | ||
4 | 12 | ||
3 | 917 | ||
2 | 25008 | ||
winners for numbers [77 88 21 33 1] | ||
report numbers , execution time 63.910126ms | ||
``` | ||
|
||
### Alpaca feedback | ||
*I will leave it mostly uncommented ...* | ||
|
||
>Unfortunately you were not selected to advance to the next stage of screening. | ||
> | ||
>For the homework I would like to share the following feedback: | ||
> | ||
>Pros: | ||
>- Divided into multiple packages | ||
>- Faster than linear search | ||
>- Fun CLI with history | ||
> | ||
>Cons: | ||
> | ||
>- It doesn't implement the assignment (e.g. it should return the number of winners with 5, 4, 3 and 2 matches) <span style="color:blue">*Why?*</span> | ||
>- No unit tests <span style="color:blue">*None were required in the assignment.*</span> | ||
>- No validation of the lotto numbers (e.g they should be in the range of [1 ... 90], each line should contain 5 entries) | ||
>- O(N) solution with a constant less than one <span style="color:blue">*Is sub-linear solution that searches 10 million records in ~60ms bad?*</span> | ||
>- The prompt handling requires external utilities (stty) instead of relying on libraries | ||
>- CTRL-C handling should be done using signals instead of terminal manipulation | ||
>- Anding the bitsets could be done inplace preventing unnecessary memory allocation in cmd.op | ||
> | ||
I can think of some kind of solution using sort whereby you first sort every line, then sort whole file thus having the same items together and then use binary search to locate one of the same items in the sequence and then you would need to count them. But at this point I can't think of anything else, so if someone can please share. | ||
I think the code performed pretty good finding and counting winners in 10 million set under 70ms (I tried several runs). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package bitset | ||
|
||
import ( | ||
"alpaca/util" | ||
"fmt" | ||
"math" | ||
) | ||
|
||
const MAXBITSETSZ = 20_000_000 | ||
|
||
type Bitset []uint8 | ||
|
||
func NewBitset(n int) Bitset { | ||
sz := 0 | ||
if n > 0 { | ||
if n > MAXBITSETSZ { | ||
n = MAXBITSETSZ | ||
} | ||
sz = int(math.Ceil(float64(n+7) / 8)) | ||
} | ||
return make(Bitset, sz) | ||
} | ||
|
||
func (bs Bitset) Len() int { | ||
return 8 * len(bs) | ||
} | ||
|
||
func (bs Bitset) Get(idx int) (bool, error) { | ||
if pos, offset, err := getPosAndOffset(idx, bs.Len()); err == nil { | ||
return (bs[pos] & (uint8(1) << offset)) != 0, nil | ||
} else { | ||
return false, err | ||
} | ||
} | ||
|
||
func (bs Bitset) Set(idx int, val bool) error { | ||
if pos, offset, err := getPosAndOffset(idx, bs.Len()); err == nil { | ||
if val { | ||
bs[pos] |= (uint8(1) << offset) | ||
} else { | ||
bs[pos] &= ^(uint8(1) << offset) | ||
} | ||
} else { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (bs Bitset) SetAll(idx []int, val bool) error { | ||
for _, v := range idx { | ||
if err := bs.Set(v, val); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func getPosAndOffset(idx int, ln int) (int, uint, error) { | ||
if idx < 0 { | ||
idx = ln + idx | ||
} | ||
if !(idx >= 0 && idx < ln) { | ||
return -1, 0, fmt.Errorf("index %d out of range", idx) | ||
} | ||
pos := idx / 8 | ||
offset := uint(idx % 8) | ||
return pos, offset, nil | ||
} | ||
|
||
func (bs Bitset) Intersection(bp *Bitset) Bitset { | ||
f := func(b1, b2 uint8) uint8 { return (b1 & b2) } | ||
return op(bs, *bp, f) | ||
} | ||
|
||
func (bs Bitset) Union(bp *Bitset) Bitset { | ||
f := func(b1, b2 uint8) uint8 { return (b1 | b2) } | ||
return op(bs, *bp, f) | ||
} | ||
|
||
func (bs Bitset) Difference(bp *Bitset) Bitset { | ||
f := func(b1, b2 uint8) uint8 { return (b1 & b2) } | ||
a := op(bs, *bp, f).Slice() | ||
c := bs.Clone() | ||
for _, v := range a { | ||
c.Set(v, false) | ||
} | ||
return c | ||
} | ||
|
||
func op(bs1, bs2 Bitset, opfunc func(uint8, uint8) uint8) Bitset { | ||
l1 := bs1.Len() | ||
l2 := bs2.Len() | ||
if l1 == 0 || l2 == 0 { | ||
return NewBitset(0) | ||
} | ||
sz := util.Min(l1, l2) / 8 | ||
bi := NewBitset(util.Max(l1, l2)) | ||
for i, k := 0, sz; i < k; i++ { | ||
b := opfunc(bs1[i], bs2[i]) | ||
if b > 0 { | ||
bi[i] |= b | ||
} | ||
} | ||
return bi | ||
} | ||
|
||
func (bs Bitset) Invert() { | ||
for k, v := range bs { | ||
bs[k] = ^v | ||
} | ||
} | ||
|
||
func (bs Bitset) ClearAll() { | ||
for k, v := range bs { | ||
bs[k] = v ^ v | ||
} | ||
} | ||
|
||
func (bs Bitset) Clone() Bitset { | ||
bn := make(Bitset, len(bs)) | ||
copy(bn, bs) | ||
return bn | ||
} | ||
|
||
func (bs Bitset) Slice() []int { | ||
l := len(bs) * 8 / 3 | ||
if l < 1 { | ||
l = 1 | ||
} | ||
a := make([]int, 0, l) | ||
mask := uint8(1) | ||
for k, v := range bs { | ||
if v < 1 { | ||
continue | ||
} | ||
for i := uint(0); i < 8; i++ { | ||
b := mask & (v >> i) | ||
if b > 0 { | ||
a = append(a, k*8+int(i)) | ||
} | ||
} | ||
} | ||
return a | ||
} | ||
|
||
func (bs Bitset) dump() { | ||
for k, v := range bs { | ||
fmt.Printf("%02d %08b\n", k, v) | ||
} | ||
} |
Oops, something went wrong.