Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

20 subtree #21

Merged
merged 8 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions subtree.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import digest256 from './digest256.mjs'
const { merge } = digest256

/**
* @typedef {readonly [bigint, bigint, bigint]} Node
*/

const nodeRoot = 0
const nodeLast = 1
const nodeHeight = 2

/** @typedef {Node[]} State */

/**
* @template T
* @typedef {null | T} Nullable
*/

/** @type {(bu256: bigint) => bigint} */
const highestOne256 = bu256 => {
let result = 0n
let index = 256n
while (bu256 !== 0n) {
index >>= 1n
if (index === 0n) {
return result + 1n
}
const high = bu256 >> index
if (high === 0n) {
bu256 &= (1n << index) - 1n
} else {
bu256 = high
result += index
}
}
return result
}

/** @type {(bu256: bigint) => bigint} */
const leadingZero256 = bu256 => 256n - highestOne256(bu256)

/** @type {(a: bigint) => (b: bigint) => bigint} */
const height = a => b => {
let v = a ^ b
return leadingZero256(v)
}

/** @type {(state: State) => (last0: bigint) => bigint} */
const end = state => last0 => {
while (true) {
let last1 = state.pop()
if (last1 === undefined) {
return last0
}
last0 = merge(last1[nodeRoot])(last0)
}
}

/** @type {(state: State) => (last0: bigint) => Nullable<bigint>} */
const push = state => last0 => {
let height10 = 0n
let last1 = state.pop()
if (last1 !== undefined) {
if (last0 >= last1[nodeLast]) {
return end(state)(merge(last1[nodeRoot])(last0))
}
height10 = height(last1[nodeLast])(last0)
while (last1[nodeHeight] > height10) {
let last2 = state.pop()
if (last2 === undefined) throw 'invalid state'
last1 = [
merge(last2[nodeRoot])(last1[nodeRoot]),
last1[nodeLast],
last2[nodeHeight]
]
}
state.push(last1)
}
state.push([last0, last0, height10])
return null
}

export default {
highestOne256,
height,
push
}
131 changes: 131 additions & 0 deletions test.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import index from './index.mjs'
import sha224 from './sha224.mjs'
import digest256 from './digest256.mjs'
import subtree from './subtree.mjs'
/** @typedef {import('./subtree.mjs').State} State */
const { getParityBit } = index
const { compress } = sha224
const { merge, byteToDigest, len } = digest256
const { highestOne256, height, push } = subtree

console.log(`test start`)

Expand Down Expand Up @@ -58,4 +61,132 @@ console.log(`test start`)
const result = merge(0n)(a)
if (result !== a) { throw result }
}
}

{
{
const result = highestOne256(0n)
if (result !== 0n) { throw result }
}

{
const result = highestOne256(1n)
if (result !== 1n) { throw result }
}

{
const result = highestOne256(2n)
if (result !== 2n) { throw result }
}

{
const result = highestOne256(3n)
if (result !== 2n) { throw result }
}

{
const result = highestOne256(4n)
if (result !== 3n) { throw result }
}

{
const result = highestOne256(8n)
if (result !== 4n) { throw result }
}

{
const result = highestOne256(8n)
if (result !== 4n) { throw result }
}

{
const result = highestOne256(0x8000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000n)
if (result !== 256n) { throw result }
}

{
const result = height(0n)(0n)
if (result !== 256n) { throw result }
}

{
const result = height(0n)(1n)
if (result !== 255n) { throw result }
}

{
const result = height(1n)(0n)
if (result !== 255n) { throw result }
}

{
const result = height(0n)(0x1_0000_0000_0000_0000_0000_0000_0000_0000n)
if (result !== 127n) { throw result }
}

{
const result = height(0x1_0000_0000_0000_0000_0000_0000_0000_0000n)(0x1_0000_0000_0000_0000_0000_0000_0000_0000n)
if (result !== 256n) { throw result }
}

{
const result = height(1n)(1n)
if (result !== 256n) { throw result }
}

{
const result = height(0n)(0x1_0000_0000_0000_0000_0000_0000_0000_01can)
if (result !== 127n) { throw result }
}

{
const result = height(0n)(0x1ca_0000_0000_0000_0000_0000_0000_0000_0001n)
if (result !== 119n) { throw result }
}

{
const result = height(0x7_0000_0000_0000_0000_0000_0000_0000_0000n)(0x4_0000_0000_0000_0000_0000_0000_0000_01can)
if (result !== 126n) { throw result }
}

{
const result = height(0n)(0x8000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_01can)
if (result !== 0n) { throw result }
}

{
let a = byteToDigest(0b01)
let b = byteToDigest(0b10)
let c = byteToDigest(0b11)
{
/** @type {State} */
let state = []
push(state)(a)
if (state.length !== 1) { throw state.length }
const state0 = state[0]
if (state0[0] !== a) { throw state0[0] }
if (state0[1] !== a) { throw state0[1] }
if (state0[2] !== 0n) { throw state0[2] }
const ab = push(state)(b)
const mergeAB = merge(a)(b)
if (ab !== mergeAB) { throw ab }
state = state
if (state.length !== 0) { throw state.length }
}
{
/** @type {State} */
let state = []
let result = push(state)(c)
if (result !== null) { throw result }
result = push(state)(b)
if (result !== null) { throw result }
if (state.length !== 2 ) { throw state.length }
result = push(state)(a)
if (result !== null) { throw result }
if (state.length !== 2 ) { throw state.length }
result = push(state)(a)
const mergeCB_AA = merge(merge(c)(b))(merge(a)(a))
if (result != mergeCB_AA) { throw result }
}
}
}