Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+5
View File
@@ -0,0 +1,5 @@
language: node_js
node_js:
- "4"
- "6"
- "7"
+73
View File
@@ -0,0 +1,73 @@
# sparse-array
[![Build Status](https://travis-ci.org/pgte/js-sparse-array.svg?branch=master)](https://travis-ci.org/pgte/js-sparse-array)
Sparse array implementation in JS with no dependencies
## Install
```bash
$ npm install sparse-array --save
```
## Use
### Create:
```js
const SparseArray = require('sparse-array')
const arr = new SparseArray()
```
### Set, get and unset:
```js
const index = 0
arr.set(index, 'value')
arr.get(index) // 'value'
arr.unset(index)
arr.get(index) // undefined
```
### Iterate:
```js
arr.forEach((elem, index) => {
console.log('elem: %j at %d', elem, index)
})
const mapped = arr.map((elem, index) => {
return elem + 1
})
const result = arr.reduce((acc, elem, index) => {
return acc + Number(elem)
}, 0)
```
### Find:
```js
const firstEven = arr.find((elem) => (elem % 2) === 0)
```
### Internal representation:
#### Bit field:
```js
const bitField = arr.bitField()
```
#### Compact array:
```js
const compacted = arr.compactArray()
```
## License
ISC
+250
View File
@@ -0,0 +1,250 @@
'use strict'
// JS treats subjects of bitwise operators as SIGNED 32 bit numbers,
// which means the maximum amount of bits we can store inside each byte
// is 7..
const BITS_PER_BYTE = 7
module.exports = class SparseArray {
constructor () {
this._bitArrays = []
this._data = []
this._length = 0
this._changedLength = false
this._changedData = false
}
set (index, value) {
let pos = this._internalPositionFor(index, false)
if (value === undefined) {
// unsetting
if (pos !== -1) {
// remove item from bit array and array itself
this._unsetInternalPos(pos)
this._unsetBit(index)
this._changedLength = true
this._changedData = true
}
} else {
let needsSort = false
if (pos === -1) {
pos = this._data.length
this._setBit(index)
this._changedData = true
} else {
needsSort = true
}
this._setInternalPos(pos, index, value, needsSort)
this._changedLength = true
}
}
unset (index) {
this.set(index, undefined)
}
get (index) {
this._sortData()
const pos = this._internalPositionFor(index, true)
if (pos === -1) {
return undefined
}
return this._data[pos][1]
}
push (value) {
this.set(this.length, value)
return this.length
}
get length () {
this._sortData()
if (this._changedLength) {
const last = this._data[this._data.length - 1]
this._length = last ? last[0] + 1 : 0
this._changedLength = false
}
return this._length
}
forEach (iterator) {
let i = 0
while(i < this.length) {
iterator(this.get(i), i, this)
i++
}
}
map (iterator) {
let i = 0
let mapped = new Array(this.length)
while(i < this.length) {
mapped[i] = iterator(this.get(i), i, this)
i++
}
return mapped
}
reduce (reducer, initialValue) {
let i = 0
let acc = initialValue
while(i < this.length) {
const value = this.get(i)
acc = reducer(acc, value, i)
i++
}
return acc
}
find (finder) {
let i = 0, found, last
while ((i < this.length) && !found) {
last = this.get(i)
found = finder(last)
i++
}
return found ? last : undefined
}
_internalPositionFor (index, noCreate) {
const bytePos = this._bytePosFor(index, noCreate)
if (bytePos >= this._bitArrays.length) {
return -1
}
const byte = this._bitArrays[bytePos]
const bitPos = index - bytePos * BITS_PER_BYTE
const exists = (byte & (1 << bitPos)) > 0
if (!exists) {
return -1
}
const previousPopCount = this._bitArrays.slice(0, bytePos).reduce(popCountReduce, 0)
const mask = ~(0xffffffff << (bitPos + 1))
const bytePopCount = popCount(byte & mask)
const arrayPos = previousPopCount + bytePopCount - 1
return arrayPos
}
_bytePosFor (index, noCreate) {
const bytePos = Math.floor(index / BITS_PER_BYTE)
const targetLength = bytePos + 1
while (!noCreate && this._bitArrays.length < targetLength) {
this._bitArrays.push(0)
}
return bytePos
}
_setBit (index) {
const bytePos = this._bytePosFor(index, false)
this._bitArrays[bytePos] |= (1 << (index - (bytePos * BITS_PER_BYTE)))
}
_unsetBit(index) {
const bytePos = this._bytePosFor(index, false)
this._bitArrays[bytePos] &= ~(1 << (index - (bytePos * BITS_PER_BYTE)))
}
_setInternalPos(pos, index, value, needsSort) {
const data =this._data
const elem = [index, value]
if (needsSort) {
this._sortData()
data[pos] = elem
} else {
// new element. just shove it into the array
// but be nice about where we shove it
// in order to make sorting it later easier
if (data.length) {
if (data[data.length - 1][0] >= index) {
data.push(elem)
} else if (data[0][0] <= index) {
data.unshift(elem)
} else {
const randomIndex = Math.round(data.length / 2)
this._data = data.slice(0, randomIndex).concat(elem).concat(data.slice(randomIndex))
}
} else {
this._data.push(elem)
}
this._changedData = true
this._changedLength = true
}
}
_unsetInternalPos (pos) {
this._data.splice(pos, 1)
}
_sortData () {
if (this._changedData) {
this._data.sort(sortInternal)
}
this._changedData = false
}
bitField () {
const bytes = []
let pendingBitsForResultingByte = 8
let pendingBitsForNewByte = 0
let resultingByte = 0
let newByte
const pending = this._bitArrays.slice()
while (pending.length || pendingBitsForNewByte) {
if (pendingBitsForNewByte === 0) {
newByte = pending.shift()
pendingBitsForNewByte = 7
}
const usingBits = Math.min(pendingBitsForNewByte, pendingBitsForResultingByte)
const mask = ~(0b11111111 << usingBits)
const masked = newByte & mask
resultingByte |= masked << (8 - pendingBitsForResultingByte)
newByte = newByte >>> usingBits
pendingBitsForNewByte -= usingBits
pendingBitsForResultingByte -= usingBits
if (!pendingBitsForResultingByte || (!pendingBitsForNewByte && !pending.length)) {
bytes.push(resultingByte)
resultingByte = 0
pendingBitsForResultingByte = 8
}
}
// remove trailing zeroes
for(var i = bytes.length - 1; i > 0; i--) {
const value = bytes[i]
if (value === 0) {
bytes.pop()
} else {
break
}
}
return bytes
}
compactArray () {
this._sortData()
return this._data.map(valueOnly)
}
}
function popCountReduce (count, byte) {
return count + popCount(byte)
}
function popCount(_v) {
let v = _v
v = v - ((v >> 1) & 0x55555555) // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333) // temp
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24
}
function sortInternal (a, b) {
return a[0] - b[0]
}
function valueOnly (elem) {
return elem[1]
}
+28
View File
@@ -0,0 +1,28 @@
{
"name": "sparse-array",
"version": "1.3.2",
"description": "Sparse array implementation in JS with no dependencies",
"main": "index.js",
"scripts": {
"test": "tape tests/*.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/pgte/js-sparse-array.git"
},
"keywords": [
"sparse",
"array",
"sparse",
"array"
],
"author": "pgte",
"license": "ISC",
"bugs": {
"url": "https://github.com/pgte/js-sparse-array/issues"
},
"homepage": "https://github.com/pgte/js-sparse-array#readme",
"devDependencies": {
"tape": "^4.6.3"
}
}
+64
View File
@@ -0,0 +1,64 @@
'use strict'
const test = require('tape')
const SparseArray = require('../')
let arr
test('allows to be constructed', (t) => {
arr = new SparseArray()
t.end()
})
test('bit field is empty', (t) => {
t.deepEqual(arr.bitField(), [])
t.end()
})
test('one item at 0 position', (t) => {
arr.set(0, 0)
t.deepEqual(arr.bitField(), [0b1])
t.end()
})
test('another item at 1 position', (t) => {
arr.set(1, 1)
t.deepEqual(arr.bitField(), [0b11])
t.end()
})
test('another item at 7th position', (t) => {
arr.set(6, 6)
t.deepEqual(arr.bitField(), [0b1000011])
t.end()
})
test('another item at 8th position', (t) => {
arr.set(7, 7)
t.deepEqual(arr.bitField(), [0b11000011])
t.end()
})
test('another item at 9th position', (t) => {
arr.set(8, 8)
t.deepEqual(arr.bitField(), [0b11000011, 0b1])
t.end()
})
test('another item at 11th position', (t) => {
arr.set(10, 10)
t.deepEqual(arr.bitField(), [0b11000011, 0b101])
t.end()
})
test('another item at 16th position', (t) => {
arr.set(15, 15)
t.deepEqual(arr.bitField(), [0b11000011, 0b10000101])
t.end()
})
test('another item at 17th position', (t) => {
arr.set(16, 16)
t.deepEqual(arr.bitField(), [0b11000011, 0b10000101, 0b1])
t.end()
})
+70
View File
@@ -0,0 +1,70 @@
'use strict'
const test = require('tape')
const SparseArray = require('../')
const max = 100
let arr
test('allows creation', (t) => {
arr = new SparseArray()
t.end()
})
test('allows pushing', (t) => {
for(let i = 0; i < max; i++) {
const pos = arr.push(i.toString())
t.equal(pos, i + 1)
}
t.end()
})
test('has length', (t) => {
t.equal(arr.length, 100)
t.end()
})
test('can iterate', (t) => {
let next = 0
arr.forEach((elem, index, arr) => {
t.equal(elem, next.toString())
t.equal(index, next)
t.equal(arr, arr)
next ++
})
t.equal(next, max)
t.end()
})
test('can map', (t) => {
let next = 0
const result = arr.map((elem, index, arr) => {
t.equal(elem, next.toString())
t.equal(index, next)
t.equal(arr, arr)
next ++
return Number(elem) + 1
})
t.equal(next, max)
t.equal(result.length, arr.length)
next = 0
result.forEach((elem) => {
t.equal(elem, next + 1)
next ++
})
t.equal(next, max)
t.end()
})
test('can reduce', (t) => {
let next = 0
arr.reduce((acc, elem, index) => {
t.equal(elem, next.toString())
t.equal(index, next)
next ++
return acc + Number(elem)
}, 0)
t.equal(next, max)
t.end()
})
+28
View File
@@ -0,0 +1,28 @@
'use strict'
const test = require('tape')
const SparseArray = require('../')
let arr
test('allows to be constructed', (t) => {
arr = new SparseArray()
t.end()
})
test('compact array is empty', (t) => {
t.deepEqual(arr.compactArray(), [])
t.end()
})
test('compact array containing one pos', (t) => {
arr.set(10, '10')
t.deepEqual(arr.compactArray(), ['10'])
t.end()
})
test('compact array containing two positions', (t) => {
arr.set(5, '5')
t.deepEqual(arr.compactArray(), ['5', '10'])
t.end()
})
+31
View File
@@ -0,0 +1,31 @@
'use strict'
const test = require('tape')
const SparseArray = require('../')
const max = 100
let arr
test('allows creation', (t) => {
arr = new SparseArray()
t.end()
})
test('allows pushing', (t) => {
for(let i = 0; i < max; i++) {
const pos = arr.push(i.toString())
t.equal(pos, i + 1)
}
t.end()
})
test('find foundable', (t) => {
const min = Math.floor(max / 2)
t.equal(arr.find(elem => Number(elem) >= min), min.toString())
t.end()
})
test('does not find unfoundable', (t) => {
t.equal(arr.find(elem => Number(elem) > max), undefined)
t.end()
})
+57
View File
@@ -0,0 +1,57 @@
'use strict'
const test = require('tape')
const SparseArray = require('../')
const max = 100
let arr
test('allows to be constructed', (t) => {
arr = new SparseArray()
t.end()
})
test('get an index that is not set returns undefined', (t) => {
t.equal(arr.get(0), undefined)
t.end()
})
test('can set a determined place', (t) => {
arr.set(0, 'v0')
t.end()
})
test('can get a value', (t) => {
t.equal(arr.get(0), 'v0')
t.end()
})
test('getting an unset value yields undefined', (t) => {
t.equal(arr.get(1), undefined)
t.end()
})
test('can set a bunch of values', (t) => {
for(let i = 0; i < max; i++) {
arr.set(i, i.toString())
}
t.end()
})
test('can get that bunch of values', (t) => {
for(let i = 0; i < max; i++) {
t.equal(arr.get(i), i.toString())
}
t.end()
})
test('can unset a bunch of values and still get the rest', (t) => {
for(let i = 0; i < max; i++) {
arr.unset(i)
t.equal(arr.get(i), undefined)
for(let j = i + 1; j < max; j++) {
t.equal(arr.get(j), j.toString())
}
}
t.end()
})
+68
View File
@@ -0,0 +1,68 @@
'use strict'
const test = require('tape')
const SparseArray = require('../')
let arr
test('allows to be constructed', (t) => {
arr = new SparseArray()
t.end()
})
test('get an index that is not set returns undefined', (t) => {
t.equal(arr.get(9), undefined)
t.end()
})
test('can set a 9th and 6th positions', (t) => {
arr.set(9, 'v9')
arr.set(6, 'v6')
t.end()
})
test('length is 10', (t) => {
t.equal(arr.length, 10)
t.end()
})
test('can get those values', (t) => {
t.equal(arr.get(9), 'v9')
t.equal(arr.get(6), 'v6')
t.end()
})
test('delete 6th position', (t) => {
arr.unset(6)
t.end()
})
test('length is still 10', (t) => {
t.equal(arr.length, 10)
t.end()
})
test('position 6 is gone', (t) => {
t.equal(arr.get(6), undefined)
t.end()
})
test('can still get position 9', (t) => {
t.equal(arr.get(9), 'v9')
t.end()
})
test('delete 9th position', (t) => {
arr.unset(9)
t.end()
})
test('can not get position 9', (t) => {
t.equal(arr.get(9), undefined)
t.end()
})
test('length is now 0', (t) => {
t.equal(arr.length, 0)
t.end()
})