@@ -111,105 +111,120 @@ function verifyRoundTrip(addr, resolution) {
111111 return checks
112112}
113113
114- // --- Bitarithmetic demonstration ---
114+ // --- Hierarchical index demonstration ---
115+ //
116+ // Z7 and Z3 use FIXED BIT-POSITION encoding (NOT simple positional radix).
117+ // Layout (64-bit):
118+ // Z7: bits[63:60] = quad (4 bits), then 20 x 3-bit digits (res 1..20)
119+ // Z3: bits[63:60] = quad (4 bits), then 30 x 2-bit digits (res 1..30)
120+ //
121+ // Rather than trying to replicate DGGRID's internal conventions, we use the
122+ // API for parent/children and then OBSERVE the digit patterns to show how
123+ // the encoding works.
115124
116- function computeBitOps (addr , resolution ) {
117- const ops = { sections: [] }
125+ const Z7_BITS = 3n
126+ const Z7_MAX = 20
127+ const Z3_BITS = 2n
128+ const Z3_MAX = 30
118129
119- // Z3 arithmetic (aperture 3)
120- if (addr .z3 !== null ) {
121- const z3 = addr .z3
122- const base = 3n
123- // Extract digits: each resolution level is one base-3 digit
124- // The Z3 value encodes digits from MSB (coarsest) to LSB (finest)
125- const digits = extractDigits (z3, base, resolution)
126-
127- // Parent via bit manipulation: drop the last digit
128- const parentZ3Bits = z3 / base
129- let parentSeqBits = null
130- try { parentSeqBits = dggs .z3ToSequenceNum (parentZ3Bits, resolution - 1 ) } catch { /* skip */ }
131-
132- // Actual parent via API
133- let parentSeqApi = null
134- try { parentSeqApi = dggs .sequenceNumParent ([addr .seqnum ], resolution)[0 ] } catch { /* skip */ }
135-
136- // Children via bit manipulation: append digits 0,1,2
137- const childrenBits = []
138- for (let d = 0n ; d < base; d++ ) {
139- const childZ3 = z3 * base + d
140- try {
141- const childSeq = dggs .z3ToSequenceNum (childZ3, resolution + 1 )
142- childrenBits .push ({ digit: Number (d), z3: childZ3, seqnum: childSeq })
143- } catch { childrenBits .push ({ digit: Number (d), z3: childZ3, seqnum: null }) }
144- }
130+ function zGetQuad (value ) {
131+ return Number ((value >> 60n ) & 0xFn )
132+ }
145133
146- // Actual children via API
147- let childrenApi = []
148- try { childrenApi = dggs .sequenceNumChildren ([addr .seqnum ], resolution)[0 ] } catch { /* skip */ }
134+ function zGetDigit (value , res , bitsPerDigit , maxRes ) {
135+ const shift = BigInt (maxRes - res) * bitsPerDigit
136+ const mask = (1n << bitsPerDigit) - 1n
137+ return Number ((value >> shift) & mask)
138+ }
149139
150- ops .sections .push ({
151- type: ' Z3' ,
152- base: 3 ,
153- value: z3,
154- hex: ' 0x' + z3 .toString (16 ),
155- digits,
156- parent: {
157- formula: ` z3 / ${ base} = ${ parentZ3Bits} ` ,
158- bitResult: parentSeqBits,
159- apiResult: parentSeqApi,
160- match: parentSeqBits !== null && parentSeqApi !== null && parentSeqBits === parentSeqApi,
161- },
162- children: childrenBits,
163- childrenApi,
164- })
140+ function zExtractDigits (value , resolution , bitsPerDigit , maxRes ) {
141+ const digits = []
142+ for (let r = 1 ; r <= resolution; r++ ) {
143+ digits .push (zGetDigit (value, r, bitsPerDigit, maxRes))
165144 }
145+ return digits
146+ }
166147
167- // Z7 arithmetic (aperture 7)
168- if (addr .z7 !== null ) {
169- const z7 = addr .z7
170- const base = 7n
171- const digits = extractDigits (z7, base, resolution)
172-
173- const parentZ7Bits = z7 / base
174- let parentSeqBits = null
175- try { parentSeqBits = dggs .z7ToSequenceNum (parentZ7Bits, resolution - 1 ) } catch { /* skip */ }
176-
177- let parentSeqApi = null
178- try { parentSeqApi = dggs .sequenceNumParent ([addr .seqnum ], resolution)[0 ] } catch { /* skip */ }
179-
180- const childrenBits = []
181- for (let d = 0n ; d < base; d++ ) {
182- const childZ7 = z7 * base + d
183- try {
184- const childSeq = dggs .z7ToSequenceNum (childZ7, resolution + 1 )
185- childrenBits .push ({ digit: Number (d), z7: childZ7, seqnum: childSeq })
186- } catch { childrenBits .push ({ digit: Number (d), z7: childZ7, seqnum: null }) }
148+ function computeZSection (type , value , seqnum , resolution , bitsPerDigit , maxRes , toZ , fromZ ) {
149+ const quad = zGetQuad (value)
150+ const digits = zExtractDigits (value, resolution, bitsPerDigit, maxRes)
151+
152+ // Use API for parent, then convert to Z to observe digit change
153+ let parentSeq = null
154+ let parentZ = null
155+ let parentDigits = null
156+ let parentQuad = null
157+ try {
158+ parentSeq = resolution > 0 ? dggs .sequenceNumParent ([seqnum], resolution)[0 ] : null
159+ if (parentSeq !== null ) {
160+ parentZ = toZ (parentSeq, resolution - 1 )
161+ parentQuad = zGetQuad (parentZ)
162+ parentDigits = zExtractDigits (parentZ, resolution - 1 , bitsPerDigit, maxRes)
187163 }
164+ } catch { /* skip */ }
188165
189- let childrenApi = []
190- try { childrenApi = dggs .sequenceNumChildren ([addr .seqnum ], resolution)[0 ] } catch { /* skip */ }
166+ // Use API for children, then convert each to Z to observe digit patterns
167+ let childrenApi = []
168+ try { childrenApi = dggs .sequenceNumChildren ([seqnum], resolution)[0 ] } catch { /* skip */ }
191169
192- ops .sections .push ({
193- type: ' Z7' ,
194- base: 7 ,
195- value: z7,
196- hex: ' 0x' + z7 .toString (16 ),
197- digits,
198- parent: {
199- formula: ` z7 / ${ base} = ${ parentZ7Bits} ` ,
200- bitResult: parentSeqBits,
201- apiResult: parentSeqApi,
202- match: parentSeqBits !== null && parentSeqApi !== null && parentSeqBits === parentSeqApi,
203- },
204- children: childrenBits,
205- childrenApi,
206- })
170+ const childrenInfo = childrenApi .map (childSeq => {
171+ try {
172+ const childZVal = toZ (childSeq, resolution + 1 )
173+ const childDigits = zExtractDigits (childZVal, resolution + 1 , bitsPerDigit, maxRes)
174+ return {
175+ seqnum: childSeq,
176+ zValue: childZVal,
177+ hex: ' 0x' + childZVal .toString (16 ).padStart (16 , ' 0' ),
178+ digits: childDigits,
179+ newDigit: childDigits[resolution], // the digit at the child's resolution level
180+ }
181+ } catch {
182+ return { seqnum: childSeq, zValue: null , hex: ' err' , digits: [], newDigit: ' ?' }
183+ }
184+ })
185+
186+ return {
187+ type,
188+ base: Number (1n << bitsPerDigit),
189+ bitsPerDigit: Number (bitsPerDigit),
190+ value,
191+ hex: ' 0x' + value .toString (16 ).padStart (16 , ' 0' ),
192+ quad,
193+ digits,
194+ parent: {
195+ seqnum: parentSeq,
196+ zValue: parentZ,
197+ hex: parentZ !== null ? ' 0x' + parentZ .toString (16 ).padStart (16 , ' 0' ) : null ,
198+ quad: parentQuad,
199+ digits: parentDigits,
200+ samePrefix: parentDigits !== null && digits .slice (0 , - 1 ).every ((d , i ) => d === parentDigits[i]),
201+ },
202+ children: childrenInfo,
203+ }
204+ }
205+
206+ function computeBitOps (addr , resolution ) {
207+ const ops = { sections: [] }
208+
209+ if (addr .z3 !== null ) {
210+ ops .sections .push (computeZSection (
211+ ' Z3' , addr .z3 , addr .seqnum , resolution, Z3_BITS , Z3_MAX ,
212+ (seq , res ) => dggs .sequenceNumToZ3 (seq, res),
213+ (z , res ) => dggs .z3ToSequenceNum (z, res),
214+ ))
215+ }
216+
217+ if (addr .z7 !== null ) {
218+ ops .sections .push (computeZSection (
219+ ' Z7' , addr .z7 , addr .seqnum , resolution, Z7_BITS , Z7_MAX ,
220+ (seq , res ) => dggs .sequenceNumToZ7 (seq, res),
221+ (z , res ) => dggs .z7ToSequenceNum (z, res),
222+ ))
207223 }
208224
209225 // ZORDER spatial locality
210226 if (addr .zorder !== null ) {
211227 const zorder = addr .zorder
212- // Show neighbor ZORDER values to demonstrate spatial locality
213228 let neighbors = []
214229 try {
215230 const nIds = dggs .sequenceNumNeighbors ([addr .seqnum ], resolution)[0 ]
@@ -229,7 +244,6 @@ function computeBitOps(addr, resolution) {
229244 })
230245 } catch { /* skip */ }
231246
232- // Common prefix with neighbors (shared ancestor)
233247 const centerHex = zorder .toString (16 ).padStart (16 , ' 0' )
234248 const prefixes = neighbors .filter (n => n .zorder !== null ).map (n => {
235249 const nHex = n .zorder .toString (16 ).padStart (16 , ' 0' )
@@ -252,16 +266,6 @@ function computeBitOps(addr, resolution) {
252266 return ops .sections .length > 0 ? ops : null
253267}
254268
255- function extractDigits (value , base , numDigits ) {
256- const digits = []
257- let v = value
258- for (let i = 0 ; i < numDigits; i++ ) {
259- digits .unshift (Number (v % base))
260- v = v / base
261- }
262- return digits
263- }
264-
265269function formatAddress (addr , type ) {
266270 if (! addr) return ' ?'
267271 switch (type) {
@@ -513,76 +517,95 @@ function loadScript(src) {
513517
514518 <div v-for =" section in state.bitOps.sections" :key =" section.type" class =" bitops-section" >
515519
516- <!-- Z3 / Z7 hierarchical arithmetic -->
520+ <!-- Z3 / Z7 hierarchical encoding -->
517521 <template v-if =" section .type === ' Z3' || section .type === ' Z7' " >
518- <div class =" bitops-title" >{{ section.type }} — Base- {{ section.base }} Hierarchical Arithmetic </div >
522+ <div class =" bitops-title" >{{ section.type }} — Fixed Bit-Position Encoding ( {{ section.bitsPerDigit }} bits/digit) </div >
519523
520- <!-- Digit breakdown -->
524+ <!-- Bit layout of selected cell -->
521525 <div class =" digit-row" >
522- <span class =" digit-label" >{{ section.type }} value :</span >
526+ <span class =" digit-label" >Selected cell :</span >
523527 <span class =" digit-value" >{{ section.hex }}</span >
524528 </div >
525529 <div class =" digit-row" >
526- <span class =" digit-label" >Digits (res 1→{{ state.selectedRes }}) :</span >
530+ <span class =" digit-label" >Digit layout :</span >
527531 <span class =" digit-cells" >
532+ <span class =" digit-cell digit-quad" title =" Quad (icosahedron face)" >Q{{ section.quad }}</span >
528533 <span
529534 v-for =" (d, i) in section.digits"
530535 :key =" i"
531536 class =" digit-cell"
532537 :class =" { 'digit-last': i === section.digits.length - 1 }"
538+ :title =" 'Res ' + (i + 1) + ' → digit ' + d"
533539 >{{ d }}</span >
534540 </span >
535- <span class =" digit-hint" >each digit = which child at that level </span >
541+ <span class =" digit-hint" >4-bit quad + {{ section.bitsPerDigit }}-bit digits at fixed bit positions </span >
536542 </div >
537543
538- <!-- Parent via division -->
539- <div class =" op-block" >
540- <div class =" op-title" >Find Parent (drop last digit)</div >
541- <div class =" op-code" >
542- <code >{{ section.type.toLowerCase() }} / {{ section.base }}</code >
543- = <code >{{ section.parent.formula.split('=')[1]?.trim() }}</code >
544- </div >
544+ <!-- Parent — observe the digit pattern -->
545+ <div class =" op-block" v-if =" section.parent.seqnum !== null" >
546+ <div class =" op-title" >Parent Cell (res {{ state.selectedRes - 1 }})</div >
545547 <div class =" op-row" >
546- <span class =" op-label" >Bit arithmetic result :</span >
547- <span class =" op-val" >SEQNUM {{ section.parent.bitResult? .toString() ?? 'error' }}</span >
548+ <span class =" op-label" >SEQNUM :</span >
549+ <span class =" op-val" >{{ section.parent.seqnum .toString() }}</span >
548550 </div >
549551 <div class =" op-row" >
550- <span class =" op-label" >API sequenceNumParent() :</span >
551- <span class =" op-val" >SEQNUM {{ section.parent.apiResult?.toString() ?? 'error' }}</span >
552+ <span class =" op-label" >{{ section.type }} :</span >
553+ <span class =" op-val" >{{ section.parent.hex }}</span >
552554 </div >
553- <div class =" op-row" >
554- <span class =" op-label" >Match:</span >
555- <span :class =" section.parent.match ? 'rt-ok' : 'rt-fail'" >
556- {{ section.parent.match ? 'Yes — identical!' : 'No' }}
555+ <div class =" digit-row" style =" margin-top : 4px " >
556+ <span class =" digit-label" >Parent digits:</span >
557+ <span class =" digit-cells" >
558+ <span class =" digit-cell digit-quad" >Q{{ section.parent.quad }}</span >
559+ <span
560+ v-for =" (d, i) in section.parent.digits"
561+ :key =" i"
562+ class =" digit-cell"
563+ :class =" { 'digit-match': i < section.digits.length - 1 && d === section.digits[i] }"
564+ >{{ d }}</span >
565+ </span >
566+ </div >
567+ <div class =" digit-row" >
568+ <span class =" digit-label" >Selected digits:</span >
569+ <span class =" digit-cells" >
570+ <span class =" digit-cell digit-quad" >Q{{ section.quad }}</span >
571+ <span
572+ v-for =" (d, i) in section.digits"
573+ :key =" i"
574+ class =" digit-cell"
575+ :class =" { 'digit-match': i < section.digits.length - 1 && section.parent.digits && d === section.parent.digits[i], 'digit-last': i === section.digits.length - 1 }"
576+ >{{ d }}</span >
557577 </span >
558578 </div >
579+ <p class =" op-hint" v-if =" section.parent.samePrefix" >
580+ The parent shares digits 1–{{ state.selectedRes - 1 }} with the selected cell. Only the last digit differs — it encodes which child this cell is within its parent.
581+ </p >
559582 </div >
560583
561- <!-- Children via multiplication -->
562- <div class =" op-block" >
563- <div class =" op-title" >Find Children (append digit 0–{{ section.base - 1 }})</div >
584+ <!-- Children — observe how each gets a unique digit -->
585+ <div class =" op-block" v-if =" section.children.length" >
586+ <div class =" op-title" >Children ({{ section.children.length }} cells, res {{ state.selectedRes + 1 }})</div >
587+ <p class =" op-hint" >Each child inherits the selected cell's digits and adds one new digit at resolution {{ state.selectedRes + 1 }}.</p >
564588 <table class =" children-table" >
565589 <thead >
566590 <tr >
567- <th >Digit</th >
568- <th >Formula</th >
569- <th >{{ section.type }}</th >
570- <th >SEQNUM (bit)</th >
571- <th >SEQNUM (API)</th >
572- <th >Match</th >
591+ <th >SEQNUM</th >
592+ <th >{{ section.type }} (hex)</th >
593+ <th >Digits 1–{{ state.selectedRes }}</th >
594+ <th >New digit</th >
573595 </tr >
574596 </thead >
575597 <tbody >
576- <tr v-for =" child in section.children" :key =" child.digit" >
577- <td class =" digit-cell-inline" >{{ child.digit }}</td >
578- <td ><code >{{ section.type.toLowerCase() }} * {{ section.base }} + {{ child.digit }}</code ></td >
579- <td class =" mono" >{{ child[section.type.toLowerCase()]?.toString() ?? '?' }}</td >
598+ <tr class =" zorder-center" >
599+ <td class =" mono" >{{ state.selectedCell.toString() }} (selected)</td >
600+ <td class =" mono" >{{ section.hex }}</td >
601+ <td class =" mono" >{{ section.digits.join(' ') }}</td >
602+ <td >—</td >
603+ </tr >
604+ <tr v-for =" child in section.children" :key =" child.seqnum?.toString()" >
580605 <td class =" mono" >{{ child.seqnum?.toString() ?? 'err' }}</td >
581- <td class =" mono" >{{ section.childrenApi[child.digit]?.toString() ?? 'err' }}</td >
582- <td >
583- <span v-if =" child.seqnum !== null && section.childrenApi[child.digit] !== undefined && child.seqnum === section.childrenApi[child.digit]" class =" rt-ok" >OK</span >
584- <span v-else class =" rt-fail" >—</span >
585- </td >
606+ <td class =" mono" >{{ child.hex }}</td >
607+ <td class =" mono" >{{ child.digits.slice(0, state.selectedRes).join(' ') }}</td >
608+ <td class =" digit-cell-inline" >{{ child.newDigit }}</td >
586609 </tr >
587610 </tbody >
588611 </table >
@@ -854,6 +877,17 @@ function loadScript(src) {
854877 background : var (--vp-c-bg , #fff );
855878 color : var (--vp-c-text-1 );
856879}
880+ .digit-quad {
881+ background : #7c3aed ;
882+ color : #fff ;
883+ border-color : #7c3aed ;
884+ font-weight : 700 ;
885+ }
886+ .digit-match {
887+ background : #ecfdf5 ;
888+ border-color : #2ba52b ;
889+ color : #2ba52b ;
890+ }
857891.digit-last {
858892 background : #e8f4fd ;
859893 border-color : #2b7fd4 ;
0 commit comments