state.go raw
1 // Package state defines the three bits and eight trigrams — the minimum
2 // instruction set that generates all lattice dynamics from the axiom pair.
3 package state
4
5 // Trigram is a 3-bit value encoding the eight change vectors.
6 type Trigram uint8
7
8 const (
9 Earth Trigram = 0b000 // ☷ dissolving, free, depleted — substrate
10 Thunder Trigram = 0b001 // ☳ accreting, free, depleted — nucleation
11 Water Trigram = 0b010 // ☵ dissolving, bound, depleted — frozen defect
12 Lake Trigram = 0b011 // ☱ accreting, bound, depleted — ambiguity zone
13 Fire Trigram = 0b100 // ☲ dissolving, free, energized — noisy growth
14 Heaven Trigram = 0b101 // ☰ accreting, free, energized — ideal growth
15 Wind Trigram = 0b110 // ☴ dissolving, bound, energized — coherence pruning
16 Mountain Trigram = 0b111 // ☶ accreting, bound, energized — equilibrium
17 )
18
19 // Bit positions.
20 const (
21 BitBonding = 0 // bottom line
22 BitConstraint = 1 // middle line
23 BitEnergy = 2 // top line
24 )
25
26 // Bonding reports whether the accreting bit is set.
27 func (t Trigram) Bonding() bool { return t&(1<<BitBonding) != 0 }
28
29 // Constraint reports whether the bound bit is set.
30 func (t Trigram) Constraint() bool { return t&(1<<BitConstraint) != 0 }
31
32 // Energy reports whether the supersaturated bit is set.
33 func (t Trigram) Energy() bool { return t&(1<<BitEnergy) != 0 }
34
35 // SetBonding returns the trigram with the bonding bit set or cleared.
36 func (t Trigram) SetBonding(v bool) Trigram {
37 if v {
38 return t | (1 << BitBonding)
39 }
40 return t &^ (1 << BitBonding)
41 }
42
43 // SetConstraint returns the trigram with the constraint bit set or cleared.
44 func (t Trigram) SetConstraint(v bool) Trigram {
45 if v {
46 return t | (1 << BitConstraint)
47 }
48 return t &^ (1 << BitConstraint)
49 }
50
51 // SetEnergy returns the trigram with the energy bit set or cleared.
52 func (t Trigram) SetEnergy(v bool) Trigram {
53 if v {
54 return t | (1 << BitEnergy)
55 }
56 return t &^ (1 << BitEnergy)
57 }
58
59 // Flip returns the trigram with the specified bit inverted.
60 // This is a moving line — a transition between dynamical regimes.
61 func (t Trigram) Flip(bit uint8) Trigram {
62 if bit > 2 {
63 return t
64 }
65 return t ^ (1 << bit)
66 }
67
68 // Hexagram is two trigrams packed into a single byte: inner (the site's
69 // own state) in the low 3 bits, outer (the environment) in the high 3 bits.
70 // Values 0-63.
71 type Hexagram uint8
72
73 // Hex constructs a hexagram from inner and outer trigrams.
74 func Hex(inner, outer Trigram) Hexagram {
75 return Hexagram(uint8(inner) | uint8(outer)<<3)
76 }
77
78 // Inner returns the site's own trigram (low 3 bits).
79 func (h Hexagram) Inner() Trigram { return Trigram(h & 0b111) }
80
81 // Outer returns the environment trigram (high 3 bits).
82 func (h Hexagram) Outer() Trigram { return Trigram(h >> 3 & 0b111) }
83
84 // MoveLine returns a new hexagram with the specified line moved.
85 // inner=true flips an inner line, inner=false flips an outer line.
86 func (h Hexagram) MoveLine(inner bool, bit uint8) Hexagram {
87 if bit > 2 {
88 return h
89 }
90 if inner {
91 return Hexagram(uint8(h) ^ (1 << bit))
92 }
93 return Hexagram(uint8(h) ^ (1 << (bit + 3)))
94 }
95
96 // EncodeBytes converts raw bytes to a slice of Hexagram tokens.
97 // Every 3 bytes produce 4 hexagram tokens (24 bits = 4 × 6 bits).
98 // If len(data) is not a multiple of 3, the final group is zero-padded.
99 func EncodeBytes(data []byte) []Hexagram {
100 if len(data) == 0 {
101 return nil
102 }
103 groups := (len(data) + 2) / 3 // ceil(len/3)
104 out := make([]Hexagram, groups*4)
105 for i := 0; i < len(data); i += 3 {
106 var block [3]byte
107 copy(block[:], data[i:min(i+3, len(data))])
108 bits := uint32(block[0])<<16 | uint32(block[1])<<8 | uint32(block[2])
109 j := (i / 3) * 4
110 out[j+0] = Hexagram((bits >> 18) & 0x3F)
111 out[j+1] = Hexagram((bits >> 12) & 0x3F)
112 out[j+2] = Hexagram((bits >> 6) & 0x3F)
113 out[j+3] = Hexagram(bits & 0x3F)
114 }
115 return out
116 }
117
118 // DecodeHexagrams converts hexagram tokens back to raw bytes.
119 // Every 4 tokens produce 3 bytes. origLen is the original byte count
120 // (needed because the final group may have been zero-padded).
121 func DecodeHexagrams(tokens []Hexagram, origLen int) []byte {
122 if len(tokens) == 0 {
123 return nil
124 }
125 out := make([]byte, 0, origLen)
126 for i := 0; i+3 < len(tokens); i += 4 {
127 bits := uint32(tokens[i]&0x3F)<<18 | uint32(tokens[i+1]&0x3F)<<12 |
128 uint32(tokens[i+2]&0x3F)<<6 | uint32(tokens[i+3]&0x3F)
129 out = append(out, byte(bits>>16), byte(bits>>8), byte(bits))
130 }
131 if len(out) > origLen {
132 out = out[:origLen]
133 }
134 return out
135 }
136