/packet/binary_pattern.go
package packet
import (
"fmt"
"sort"
"strings"
"bytex64.net/code/bitsmash/ac"
)
var patterns [][]uint8 = [][]uint8{
{0, 1, 1, 1, 0},
{0, 1, 1, 1, 1},
{0, 0, 1, 1, 1},
{0, 0, 0, 1, 1},
{0, 0, 0, 0, 1},
{0, 1, 0, 1},
{0, 1, 1, 1},
{0, 1, 1, 0},
{0, 0, 0, 1},
{0, 0, 1, 0},
{0, 1, 0, 0},
{0, 0, 1, 1},
{0, 0, 1},
{0, 1, 1},
{0, 1, 0},
{0, 1},
}
type binaryPatternMatch struct {
Length int
Pattern uint8
Repeat uint8
}
type byPatternLength []binaryPatternMatch
func (a byPatternLength) Len() int {
return len(a)
}
func (a byPatternLength) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
func (a byPatternLength) Less(i, j int) bool {
return a[j].Length < a[i].Length
}
type BinaryPatternPacket struct {
c1, c2 uint8
pattern uint8
repeat uint8
}
func (self *BinaryPatternPacket) Length() int {
return len(patterns[self.pattern]) * (int(self.repeat) + 1)
}
func (*BinaryPatternPacket) ByteLength() int {
return 2
}
func (self *BinaryPatternPacket) Encode(encoder ac.CodecManager) {
encoder.Encode(CONTEXT_PATTERN_NUMBER, uint32(self.pattern))
encoder.Encode(CONTEXT_PATTERN_REPEAT, uint32(self.repeat))
encoder.Encode(CONTEXT_COLOR, uint32(self.c1))
encoder.Encode(CONTEXT_COLOR, uint32(self.c2))
}
func DecodeBinaryPattern(decoder ac.CodecManager) Packet {
pattern := byte(decoder.Decode(CONTEXT_PATTERN_NUMBER))
repeat := byte(decoder.Decode(CONTEXT_PATTERN_REPEAT))
c1 := byte(decoder.Decode(CONTEXT_COLOR))
c2 := byte(decoder.Decode(CONTEXT_COLOR))
return &BinaryPatternPacket{c1, c2, pattern, repeat}
}
func (self *BinaryPatternPacket) Pixels() []uint8 {
px := make([]uint8, self.Length())
patternLength := len(patterns[self.pattern])
c := []uint8{self.c1, self.c2}
for i := 0; i < int(self.repeat) + 1; i++ {
for j := 0; j < patternLength; j++ {
px[i * patternLength + j] = c[patterns[self.pattern][j]]
}
}
return px
}
func (self *BinaryPatternPacket) String() string {
patternbit := make([]string, len(patterns[self.pattern]))
for i, v := range(patterns[self.pattern]) {
patternbit[i] = fmt.Sprintf("%d", v)
}
patternstr := strings.Join(patternbit, "")
return fmt.Sprintf("BinaryPattern colors (%d, %d) pattern %s repeat %d", self.c1, self.c2, patternstr, self.repeat)
}
func findBinaryPattern(pixels []uint8) (uint8, uint8, []uint8) {
pat := []uint8{0}
a := pixels[0]
i := 1
for i < len(pixels) && pixels[i] == a {
pat = append(pat, 0)
i++
}
if i == len(pixels) {
return 0, 0, nil
}
b := pixels[i]
for i < len(pixels) && (pixels[i] == a || pixels[i] == b) {
if pixels[i] == a {
pat = append(pat, 0)
} else if pixels[i] == b {
pat = append(pat, 1)
}
i++
}
return a, b, pat
}
func patternPrefixMatch(needle []uint8, haystack []uint8) bool {
if len(haystack) < len(needle) {
return false
}
for i := 0; i < len(needle); i++ {
if haystack[i] != needle[i] {
return false
}
}
return true
}
func ScanBinaryPattern(pixels []uint8) Packet {
if len(pixels) < 2 {
return nil
}
c1, c2, pat := findBinaryPattern(pixels)
if pat == nil {
return nil
}
potential_matches := make([]binaryPatternMatch, 0)
for i, p := range(patterns) {
l := len(p)
repeat := 0
// If this pattern matches the prefix of the found pattern
if patternPrefixMatch(p, pat) {
// Find a repeat of this match
j := l
for patternPrefixMatch(p, pat[j:]) && repeat < 7 {
repeat++
j += l
}
potential_matches = append(potential_matches, binaryPatternMatch{l * (repeat + 1), uint8(i), uint8(repeat)})
}
}
if len(potential_matches) == 0 {
return nil
}
// Select longest match
sort.Sort(byPatternLength(potential_matches))
best_match := potential_matches[0]
if best_match.Length < 5 {
return nil
}
return &BinaryPatternPacket{c1, c2, best_match.Pattern, best_match.Repeat}
}