Merge branch 'PreetamSing-task/binary-search-leaf'
This commit is contained in:
commit
ab6543a74e
150
src/Base.ts
150
src/Base.ts
|
@ -10,7 +10,7 @@ export class Base {
|
||||||
*tree.print()
|
*tree.print()
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
print ():void {
|
print (): void {
|
||||||
Base.print(this)
|
Base.print(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,9 +26,103 @@ export class Base {
|
||||||
*const index = tree.bufferIndexOf(haystack, needle)
|
*const index = tree.bufferIndexOf(haystack, needle)
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
protected _bufferIndexOf (array: Buffer[], element: Buffer):number {
|
protected _bufferIndexOf (
|
||||||
|
array: Buffer[],
|
||||||
|
element: Buffer,
|
||||||
|
isSorted: boolean = false
|
||||||
|
): number {
|
||||||
|
if (isSorted) {
|
||||||
|
return this.binarySearch(array, element, Buffer.compare)
|
||||||
|
}
|
||||||
|
|
||||||
|
const eqChecker = (buffer1, buffer2) => buffer1.equals(buffer2)
|
||||||
|
return this.linearSearch(array, element, eqChecker)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binarySearch
|
||||||
|
* @desc Returns the first index of which given item is found in array using binary search.
|
||||||
|
* @param {Buffer[]} array - Array of items.
|
||||||
|
* @param {Buffer} element - Item to find.
|
||||||
|
* @param {Function} compareFunction
|
||||||
|
* @return {Number} - Index number
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
*const index = MerkleTree.binarySearch(array, element, Buffer.compare)
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
static binarySearch (
|
||||||
|
array: Buffer[],
|
||||||
|
element: Buffer,
|
||||||
|
compareFunction: (a: unknown, b: unknown) => number
|
||||||
|
): number {
|
||||||
|
let start = 0
|
||||||
|
let end = array.length - 1
|
||||||
|
|
||||||
|
// Iterate while start not meets end
|
||||||
|
while (start <= end) {
|
||||||
|
// Find the mid index
|
||||||
|
const mid = Math.floor((start + end) / 2)
|
||||||
|
|
||||||
|
// Check if the mid value is greater than, equal to, or less than search element.
|
||||||
|
const ordering = compareFunction(array[mid], element)
|
||||||
|
|
||||||
|
// If element is present at mid, start iterating for searching first appearance.
|
||||||
|
if (ordering === 0) {
|
||||||
|
// Linear reverse iteration until the first matching item index is found.
|
||||||
|
for (let i = mid - 1; i >= 0; i--) {
|
||||||
|
if (compareFunction(array[i], element) === 0) continue
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
} /* Else look in left or right half accordingly */ else if (ordering < 0) {
|
||||||
|
start = mid + 1
|
||||||
|
} else {
|
||||||
|
end = mid - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* binarySearch
|
||||||
|
* @desc Returns the first index of which given item is found in array using binary search.
|
||||||
|
* @param {Buffer[]} array - Array of items.
|
||||||
|
* @param {Buffer} element - Item to find.
|
||||||
|
* @param {Function} compareFunction
|
||||||
|
* @return {Number} - Index number
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
*const index = tree.binarySearch(array, element, Buffer.compare)
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
binarySearch (
|
||||||
|
array: Buffer[],
|
||||||
|
element: Buffer,
|
||||||
|
compareFunction: (a: unknown, b: unknown) => number
|
||||||
|
): number {
|
||||||
|
return Base.binarySearch(array, element, compareFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* linearSearch
|
||||||
|
* @desc Returns the first index of which given item is found in array using linear search.
|
||||||
|
* @param {Buffer[]} array - Array of items.
|
||||||
|
* @param {Buffer} element - Item to find.
|
||||||
|
* @param {Function} eqChecker
|
||||||
|
* @return {Number} - Index number
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
*const index = MerkleTree.linearSearch(array, element, (a, b) => a === b)
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
static linearSearch (array: Buffer[], element: Buffer, eqChecker: (a: unknown, b: unknown) => boolean):number {
|
||||||
for (let i = 0; i < array.length; i++) {
|
for (let i = 0; i < array.length; i++) {
|
||||||
if (element.equals(array[i])) {
|
if (eqChecker(array[i], element)) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +130,23 @@ export class Base {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* linearSearch
|
||||||
|
* @desc Returns the first index of which given item is found in array using linear search.
|
||||||
|
* @param {Buffer[]} array - Array of items.
|
||||||
|
* @param {Buffer} element - Item to find.
|
||||||
|
* @param {Function} eqChecker
|
||||||
|
* @return {Number} - Index number
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js
|
||||||
|
*const index = tree.linearSearch(array, element, (a, b) => a === b)
|
||||||
|
*```
|
||||||
|
*/
|
||||||
|
linearSearch (array: Buffer[], element: Buffer, eqChecker: (a: unknown, b: unknown) => boolean):number {
|
||||||
|
return Base.linearSearch(array, element, eqChecker)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bufferify
|
* bufferify
|
||||||
* @desc Returns a buffer type for the given value.
|
* @desc Returns a buffer type for the given value.
|
||||||
|
@ -47,7 +158,7 @@ export class Base {
|
||||||
*const buf = MerkleTree.bufferify('0x1234')
|
*const buf = MerkleTree.bufferify('0x1234')
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
static bufferify (value: any):Buffer {
|
static bufferify (value: any): Buffer {
|
||||||
if (!Buffer.isBuffer(value)) {
|
if (!Buffer.isBuffer(value)) {
|
||||||
// crypto-js support
|
// crypto-js support
|
||||||
if (typeof value === 'object' && value.words) {
|
if (typeof value === 'object' && value.words) {
|
||||||
|
@ -81,8 +192,8 @@ export class Base {
|
||||||
*console.log(MerkleTree.isHexString('0x1234'))
|
*console.log(MerkleTree.isHexString('0x1234'))
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
static isHexString (v: string):boolean {
|
static isHexString (v: string): boolean {
|
||||||
return (typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v))
|
return typeof v === 'string' && /^(0x)?[0-9A-Fa-f]*$/.test(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,7 +206,7 @@ export class Base {
|
||||||
*MerkleTree.print(tree)
|
*MerkleTree.print(tree)
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
static print (tree: any):void {
|
static print (tree: any): void {
|
||||||
console.log(tree.toString())
|
console.log(tree.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +220,7 @@ export class Base {
|
||||||
*const hexStr = tree.bufferToHex(Buffer.from('A'))
|
*const hexStr = tree.bufferToHex(Buffer.from('A'))
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
bufferToHex (value: Buffer, withPrefix: boolean = true):string {
|
bufferToHex (value: Buffer, withPrefix: boolean = true): string {
|
||||||
return Base.bufferToHex(value, withPrefix)
|
return Base.bufferToHex(value, withPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +234,10 @@ export class Base {
|
||||||
*const hexStr = MerkleTree.bufferToHex(Buffer.from('A'))
|
*const hexStr = MerkleTree.bufferToHex(Buffer.from('A'))
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
static bufferToHex (value: Buffer, withPrefix: boolean = true):string {
|
static bufferToHex (value: Buffer, withPrefix: boolean = true): string {
|
||||||
return `${withPrefix ? '0x' : ''}${(value || Buffer.alloc(0)).toString('hex')}`
|
return `${withPrefix ? '0x' : ''}${(value || Buffer.alloc(0)).toString(
|
||||||
|
'hex'
|
||||||
|
)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,7 +251,7 @@ export class Base {
|
||||||
*const buf = tree.bufferify('0x1234')
|
*const buf = tree.bufferify('0x1234')
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
bufferify (value: any):Buffer {
|
bufferify (value: any): Buffer {
|
||||||
return Base.bufferify(value)
|
return Base.bufferify(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +266,7 @@ export class Base {
|
||||||
*const fn = tree.bufferifyFn((value) => sha256(value))
|
*const fn = tree.bufferifyFn((value) => sha256(value))
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
bufferifyFn (f: any):any {
|
bufferifyFn (f: any): any {
|
||||||
return (value: any): Buffer => {
|
return (value: any): Buffer => {
|
||||||
const v = f(value)
|
const v = f(value)
|
||||||
if (Buffer.isBuffer(v)) {
|
if (Buffer.isBuffer(v)) {
|
||||||
|
@ -173,7 +286,12 @@ export class Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
// crypto-js support
|
// crypto-js support
|
||||||
return Buffer.from(f(CryptoJS.enc.Hex.parse(value.toString('hex'))).toString(CryptoJS.enc.Hex), 'hex')
|
return Buffer.from(
|
||||||
|
f(CryptoJS.enc.Hex.parse(value.toString('hex'))).toString(
|
||||||
|
CryptoJS.enc.Hex
|
||||||
|
),
|
||||||
|
'hex'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +306,7 @@ export class Base {
|
||||||
*console.log(MerkleTree.isHexString('0x1234'))
|
*console.log(MerkleTree.isHexString('0x1234'))
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
protected _isHexString (value: string):boolean {
|
protected _isHexString (value: string): boolean {
|
||||||
return Base.isHexString(value)
|
return Base.isHexString(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +316,7 @@ export class Base {
|
||||||
* @param {Number} value
|
* @param {Number} value
|
||||||
* @return {Number}
|
* @return {Number}
|
||||||
*/
|
*/
|
||||||
protected _log2 (n: number):number {
|
protected _log2 (n: number): number {
|
||||||
return n === 1 ? 0 : 1 + this._log2((n / 2) | 0)
|
return n === 1 ? 0 : 1 + this._log2((n / 2) | 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +333,7 @@ export class Base {
|
||||||
*console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ]
|
*console.log(zipped) // [ [ 'a', 'A' ], [ 'b', 'B' ] ]
|
||||||
*```
|
*```
|
||||||
*/
|
*/
|
||||||
protected _zip (a: any[], b: any[]):any[][] {
|
protected _zip (a: any[], b: any[]): any[][] {
|
||||||
return a.map((e, i) => [e, b[i]])
|
return a.map((e, i) => [e, b[i]])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,7 @@ export class MerkleTree extends Base {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.leaves.filter(leaf => this._bufferIndexOf(values, leaf) !== -1)
|
return this.leaves.filter(leaf => this._bufferIndexOf(values, leaf, this.sortLeaves) !== -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.leaves
|
return this.leaves
|
||||||
|
@ -518,13 +518,7 @@ export class MerkleTree extends Base {
|
||||||
const proof = []
|
const proof = []
|
||||||
|
|
||||||
if (!Number.isInteger(index)) {
|
if (!Number.isInteger(index)) {
|
||||||
index = -1
|
index = this._bufferIndexOf(this.leaves, leaf, this.sortLeaves)
|
||||||
|
|
||||||
for (let i = 0; i < this.leaves.length; i++) {
|
|
||||||
if (Buffer.compare(leaf, this.leaves[i]) === 0) {
|
|
||||||
index = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index <= -1) {
|
if (index <= -1) {
|
||||||
|
@ -870,7 +864,7 @@ export class MerkleTree extends Base {
|
||||||
els = els.sort(Buffer.compare)
|
els = els.sort(Buffer.compare)
|
||||||
}
|
}
|
||||||
|
|
||||||
let ids = els.map((el) => this._bufferIndexOf(this.leaves, el)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1)
|
let ids = els.map((el) => this._bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1)
|
||||||
if (!ids.every((idx) => idx !== -1)) {
|
if (!ids.every((idx) => idx !== -1)) {
|
||||||
throw new Error('Element does not exist in Merkle tree')
|
throw new Error('Element does not exist in Merkle tree')
|
||||||
}
|
}
|
||||||
|
@ -993,7 +987,7 @@ export class MerkleTree extends Base {
|
||||||
if (leaves.every(Number.isInteger)) {
|
if (leaves.every(Number.isInteger)) {
|
||||||
ids = [...leaves].sort((a, b) => a === b ? 0 : a > b ? 1 : -1) // Indices where passed
|
ids = [...leaves].sort((a, b) => a === b ? 0 : a > b ? 1 : -1) // Indices where passed
|
||||||
} else {
|
} else {
|
||||||
ids = leaves.map((el) => this._bufferIndexOf(this.leaves, el)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1)
|
ids = leaves.map((el) => this._bufferIndexOf(this.leaves, el, this.sortLeaves)).sort((a, b) => a === b ? 0 : a > b ? 1 : -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ids.every((idx: number) => idx !== -1)) {
|
if (!ids.every((idx: number) => idx !== -1)) {
|
||||||
|
|
|
@ -20,3 +20,17 @@ test('bufferifyFn', t => {
|
||||||
t.deepEqual(fn('0x123'), Buffer.from('123', 'hex'))
|
t.deepEqual(fn('0x123'), Buffer.from('123', 'hex'))
|
||||||
t.deepEqual(fn('XYZ'), Buffer.from('XYZ'))
|
t.deepEqual(fn('XYZ'), Buffer.from('XYZ'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('binarySearch', t => {
|
||||||
|
t.plan(3)
|
||||||
|
|
||||||
|
const base = new Base()
|
||||||
|
const compareFunction = (a, b) => {
|
||||||
|
if (a === b) return 0
|
||||||
|
else if (a < b) return -1
|
||||||
|
else return 1
|
||||||
|
}
|
||||||
|
t.equal(base.binarySearch([2, 2, 3, 3, 3, 4, 4, 4, 4], 3, compareFunction), 2)
|
||||||
|
t.equal(base.binarySearch([3, 3, 3, 3, 3, 4, 4, 4, 4], 3, compareFunction), 0)
|
||||||
|
t.equal(base.binarySearch([2, 2, 3, 3, 3, 4, 4, 4, 4], 1, compareFunction), -1)
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in New Issue