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