Guest User

day14.go

a guest
Dec 15th, 2016
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 3.66 KB | None | 0 0
  1. package main
  2.  
  3. import (
  4.     "crypto/md5"
  5.     "fmt"
  6.     "strconv"
  7.     "time"
  8. )
  9.  
  10. const salt = "zpqevtbw"
  11.  
  12. type buffer struct {
  13.     cache  []node
  14.     base   []byte
  15.     rounds int
  16. }
  17.  
  18. func (b *buffer) get(n int) *node {
  19.     i := n % len(b.cache)
  20.     if b.cache[i].index != n {
  21.         b.hash(i, n)
  22.         b.cache[i].index = n
  23.     }
  24.     return &b.cache[i]
  25. }
  26.  
  27. func newBuffer(size int, base []byte, extra int) *buffer {
  28.     b := &buffer{
  29.         cache:  make([]node, size),
  30.         base:   append([]byte(nil), base...),
  31.         rounds: extra,
  32.     }
  33.     // get(0) would return an empty hash, since the index would match
  34.     // so initialize it manually
  35.     b.hash(0, 0)
  36.  
  37.     return b
  38. }
  39.  
  40. type node struct {
  41.     index int
  42.     hash  [2 * md5.Size]byte
  43.     wide  uint16
  44. }
  45.  
  46. func (b *buffer) hash(idx, n int) {
  47.     buf := b.cache[idx].hash[:]
  48.  
  49.     copy(buf, b.base)
  50.  
  51.     sum := md5.Sum((strconv.AppendInt(buf[:len(b.base)], int64(n), 10)))
  52.     encode(buf, sum[:])
  53.     for i := 0; i < b.rounds; i++ {
  54.         sum = md5.Sum(buf)
  55.         encode(buf, sum[:])
  56.     }
  57.  
  58.     // scan through the entire hash string now, and setup a bitmap that can
  59.     // just be indexed each time
  60.     // NOTE: this turns out to not be that significant
  61.     b.cache[idx].wide = 0
  62.     for i := 0; i+4 < len(buf); i++ {
  63.         if buf[i] == buf[i+1] && buf[i] == buf[i+2] && buf[i] == buf[i+3] && buf[i] == buf[i+4] {
  64.             b.cache[idx].wide |= 1 << hexLookup[buf[i]]
  65.         }
  66.     }
  67. }
  68.  
  69. // shaves tiny amounts of time off of 'hex.Encode' by pre-calculating the bit manipulations
  70. func encode(dst, src []byte) {
  71.     for i, b := range src {
  72.         dst[2*i] = hexBytes[b][0]
  73.         dst[2*i+1] = hexBytes[b][1]
  74.     }
  75. }
  76.  
  77. var hexBytes [256][2]byte
  78.  
  79. func init() {
  80.     chars := []byte("0123456789abcdef")
  81.     for i := 0; i < 256; i++ {
  82.         hexBytes[i] = [2]byte{chars[i>>4], chars[i&0xf]}
  83.     }
  84. }
  85.  
  86. // trip looks for a triple-character, and returns the integer value of the hex character
  87. func trip(s []byte) int {
  88.     for i := 0; i+2 < len(s); i++ {
  89.         if s[i] == s[i+1] && s[i] == s[i+2] {
  90.             return int(hexLookup[s[i]])
  91.         }
  92.     }
  93.  
  94.     return -1
  95. }
  96.  
  97. // tried [256]int instead of the map, didn't seem to change much
  98. var hexLookup = make(map[byte]uint, 16)
  99.  
  100. func init() {
  101.     for c := '0'; c <= '9'; c++ {
  102.         hexLookup[byte(c)] = uint(c - '0')
  103.     }
  104.     for c := 'a'; c <= 'f'; c++ {
  105.         hexLookup[byte(c)] = uint(c - 'a' + 10)
  106.     }
  107. }
  108.  
  109. func main() {
  110.     parts := []int{0, 2016}
  111.     answers := make([]int, len(parts))
  112.     times := make([]time.Duration, len(parts))
  113.     bufSize := 1000
  114.  
  115.     for p, x := range parts {
  116.         begin := time.Now()
  117.         buf := newBuffer(bufSize, []byte(salt), x)
  118.         count := 0
  119.  
  120.         var recentList [16]int
  121.         var startList [16]int
  122.  
  123.         i := 0
  124.         for ; count < 64; i++ {
  125.             if five := trip(buf.get(i).hash[:]); five >= 0 {
  126.                 if n := recentList[five]; n > i {
  127.                     // we've encountered a 5-wide segment of this same character
  128.                     // within our buffer (also our scan limit) count it and move on
  129.                     count++
  130.                     //fmt.Printf("recent %2d %6d %6d\n", count, i, n)
  131.                     continue
  132.                 }
  133.                 n := 1
  134.                 if l := startList[five]; l > i {
  135.                     // we've searched for a 5-wide segment of this same character
  136.                     // up-to, but not including 'l', so start search from there
  137.                     n = l - i
  138.                 }
  139.                 for ; n <= bufSize; n++ {
  140.                     if buf.get(i+n).wide&(1<<uint(five)) != 0 {
  141.                         // record this 5-wide segment for future lookups
  142.                         recentList[five] = i + n
  143.                         count++
  144.                         //fmt.Printf("scan   %2d %6d %6d\n", count, i, i+n)
  145.                         break
  146.                     }
  147.                 }
  148.                 // no match, we've searched up to (i + n - 1), so we'll start
  149.                 // at (i + n) next time when looking for this character
  150.                 startList[five] = i + n
  151.             }
  152.         }
  153.  
  154.         answers[p] = i - 1
  155.         times[p] = time.Since(begin)
  156.     }
  157.  
  158.     for i, n := range answers {
  159.         fmt.Printf("Part %d: %d (%v)\n", i+1, n, times[i])
  160.     }
  161. }
Add Comment
Please, Sign In to add comment