diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..e6b61d5 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,13 @@ +{ + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "quoteProps": "as-needed", + "trailingComma": "es5", + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "always", + "proseWrap": "preserve" +} diff --git a/example/index.ts b/example/index.ts index 733ff7e..ea75a53 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,47 +1,170 @@ import BoolArray from '../src/bitarray'; -import { log as _, logHeader as _$ } from "./util"; - +import { log as _, logHeader as _$ } from './util'; const len = 33; // choose any integer value -_$("Randomly initializing an array"); - - const randomArray = new BoolArray(len); - for( let i=0, bool; i 0.5); - - _( "instanceof == BoolArray", randomArray instanceof BoolArray ) +_$('Randomly initializing an array'); - _( "array == ", randomArray); +const randomArray = new BoolArray(len); +for ( + let i = 0, bool; + i < len; + i++ // @ts-ignore +) + randomArray[i] = Math.random() > 0.5; +_('instanceof == BoolArray', randomArray instanceof BoolArray); -_$("properties"); +_('array == ', randomArray); - _( ".count", randomArray.count); +_$('properties'); +_('.count', randomArray.count); -_$("Bitwise operations"); +_$('Bitwise operations'); - const sample1 = BoolArray.from([true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,true ] - .concat([ true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,true ])); +const sample1 = BoolArray.from( + [ + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + true, + ].concat([ + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + true, + ]) +); - const sample2 = BoolArray.from([false,true,false,true,false,false,false,false, - true,false,false,true,false,false,false,false, - false,true,false,true,false,false,false,false, - true,false,false,true,false,false,false,true ] - .concat([ true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,false, - true,false,false,true,false,false,false,true ])); +const sample2 = BoolArray.from( + [ + false, + true, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + false, + true, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + true, + ].concat([ + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + false, + true, + false, + false, + true, + false, + false, + false, + true, + ]) +); - _( "sample1 = ", sample1.toString() ); - _( "sample2 = ", sample2.toString() ); - _( "s1 | s2 = ", (sample1)['|'](sample2) .toString() ); - _( "s1 ^ s2 = ", (sample1)['^'](sample2) .toString() ); - _( "s1 & s2 = ", (sample1)['&'](sample2) .toString() ); +_('sample1 = ', sample1.toString()); +_('sample2 = ', sample2.toString()); +_('s1 | s2 = ', sample1['|'](sample2).toString()); +_('s1 ^ s2 = ', sample1['^'](sample2).toString()); +_('s1 & s2 = ', sample1['&'](sample2).toString()); diff --git a/example/util.ts b/example/util.ts index 53e771b..ff0be7d 100644 --- a/example/util.ts +++ b/example/util.ts @@ -1,14 +1,15 @@ // style for console output formatting -const styleHeader = new Array(200).fill(".").join("") // make sure the formatting text is not visible on the html document - // (it is far to the right, hence overflows) - + "; color:blue; font-weight: bold; font-size: x-large;" +const styleHeader = + new Array(200).fill('.').join('') + // make sure the formatting text is not visible on the html document + // (it is far to the right, hence overflows) + '; color:blue; font-weight: bold; font-size: x-large;'; -function logHeader( arg0, ...rest ) { - console.info( "%c> "+arg0, ...rest, styleHeader ); +function logHeader(arg0, ...rest) { + console.info('%c> ' + arg0, ...rest, styleHeader); } function log(_1, _2) { console.log(...arguments); } -export { log, logHeader } +export { log, logHeader }; diff --git a/package-lock.json b/package-lock.json index 6371fae..71db5b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@bitarray/typedarray": "^1.0.0" }, "devDependencies": { + "prettier": "^2.6.2", "ts-node": "^10.7.0", "typescript": "^4.6.3" } @@ -121,6 +122,21 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/ts-node": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", @@ -281,6 +297,12 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "prettier": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz", + "integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==", + "dev": true + }, "ts-node": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", diff --git a/package.json b/package.json index 4a9d732..2470260 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,8 @@ "postbuild": "bash ./scripts/postbuild.sh", "prepare": "npm run build", "prepublishOnly": "npm run test", + "style:fix": "prettier {example,src,test}/**/*.ts --write", + "style:check": "prettier {example,src,test}/**/*.ts --check", "test": "node --loader ts-node/esm test" }, "author": "swiing", @@ -35,6 +37,7 @@ "@bitarray/typedarray": "^1.0.0" }, "devDependencies": { + "prettier": "^2.6.2", "ts-node": "^10.7.0", "typescript": "^4.6.3" }, diff --git a/src/alphabet.ts b/src/alphabet.ts index 1a30d66..2ca727d 100644 --- a/src/alphabet.ts +++ b/src/alphabet.ts @@ -1,10 +1,11 @@ -/** - * Some useful alphabets - */ - -const lettersAndDigits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' - -const base64MIMEChars = lettersAndDigits + '+/'; -const base64UrlChars = lettersAndDigits + '-_'; - -export {base64MIMEChars, base64UrlChars}; +/** + * Some useful alphabets + */ + +const lettersAndDigits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + +const base64MIMEChars = lettersAndDigits + '+/'; +const base64UrlChars = lettersAndDigits + '-_'; + +export { base64MIMEChars, base64UrlChars }; diff --git a/src/bitarray.ts b/src/bitarray.ts index 7d9f598..c25fb9a 100644 --- a/src/bitarray.ts +++ b/src/bitarray.ts @@ -23,241 +23,244 @@ * */ -import BitTypedArray from "@bitarray/typedarray"; +import BitTypedArray from '@bitarray/typedarray'; -import {base64MIMEChars, base64UrlChars} from "./alphabet"; +import { base64MIMEChars, base64UrlChars } from './alphabet'; -const alphabetLengthErrorMsg = 'Alphabet\'s length must be a power of 2'; +const alphabetLengthErrorMsg = "Alphabet's length must be a power of 2"; // I could leverage _views from @bitarray/typedarray, or create a new one here. // import { _views } from "./bit-typedarray.js"; -const _views = new WeakMap(); - -function getViewer( instance:BitArray ) { - let viewer = _views.get( instance.buffer ); - if( !viewer ) { - _views.set( instance.buffer, viewer = new Uint32Array( instance.buffer ) ); - } - return viewer; +const _views = new WeakMap(); + +function getViewer(instance: BitArray) { + let viewer = _views.get(instance.buffer); + if (!viewer) { + _views.set(instance.buffer, (viewer = new Uint32Array(instance.buffer))); + } + return viewer; } // This is an extension of BitTypedArray with features (methods,getters) // not available in native TypedArray's, essentially, bit operations. export default class BitArray extends BitTypedArray { - - and: (bitArray: BitArray) => BitArray; - or : (bitArray: BitArray) => BitArray; - xor: (bitArray: BitArray) => BitArray; - - static from: (source: Iterable) => BitArray; - static of: ( ...args ) => BitArray; - - /** - * returns the number of 'true' entries - */ - // this is meant to be performant - // inspired by https://github.com/bramstein/bit-array/blob/master/lib/bit-array.js - get count(): number { - let count = 0; - let view = getViewer( this ); - for( let i=0; i> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = x + (x >> 4); - x &= 0xF0F0F0F; - count += (x * 0x01010101) >> 24; - } - return count; - } - - // Implementation note 1: I once used the maximum length of the two operands (for | and for ^), - // defaulting to 'false' for the operand with "missing" values. However, it is arguable that - // this is a good default assumption (it depends on context) so I finally prefer to go - // conservative, and compute only when there is actual data available (i.e. not assuming - // 'false' otherwise). - // As a positive side effect, it also simplifies implementation (but this is not the driver). - - // Implementation note 2: I could have a very simple implementation doing e.g. - // ret[i] = this[i] && bitArray[i], for every index - // However, this would go through the array bit by bit. Instead, I go 4 bytes by 4 bytes (Uint32). - // This is where improved performance is expected. - - /** - * - * @param bitArray - * @returns a BitArray instance with the logical 'or' applied to this and the passed parameter. - * The length of the resulting BitArray is the minimum of the length of the two operands. - */ - ["|"]( bitArray: BitArray ): BitArray { - const ret = new BitArray( Math./*max*/min( this.length, bitArray.length ) ); - const retView = new Uint32Array( ret.buffer ); - _views.set( ret.buffer, retView); - const thisView = getViewer( this ); - const bitArrayView = getViewer( bitArray ); - - // let offset = 0; - // -1 needed for the case len == 32*n - let offset = ( ret.length - 1 >> 5 ) + 1; // divide 32, and add 1 because of next i--. - - // for( /**/; - // // -1 needed for the case Math.min() == 32*n. - // offset < ( ret.length /*== Math.min( this.length, bitArray.length )*/ - 1 >> 5 ) + 1; - // offset++ ) - while( offset-- ) - // note: ret is a proxy; - retView[offset] = thisView[offset] | bitArrayView[offset]; - - // Needed only if length of the returned value would be the max length of the two operands - // for( /**/; - // // offset < ( Math.max( this.length, bitArray.length ) >> 5 ) + 1; - // // (tbc) same as: - // offset < ret.length >> 5; - // offset++ ) - // _views.get( ret.buffer )[ offset ] = _views.get( ( this.length <= bitArray.length ? bitArray : _targets.get( this ) ).buffer )[ offset ]; - - // when the two operands have a different length, and since bitwise - // operations treat by bunch of 32 bits, we may have set bits to 1 - // beyond the length of ret. We apply a mask to set them back to zero - // (so that other operations like .count are correct) - retView[ret.length>>5] &= (1 << ( ret.length & 31 )) -1 /* modulo 32 , -1*/ - - return ret; + and: (bitArray: BitArray) => BitArray; + or: (bitArray: BitArray) => BitArray; + xor: (bitArray: BitArray) => BitArray; + + static from: (source: Iterable) => BitArray; + static of: (...args) => BitArray; + + /** + * returns the number of 'true' entries + */ + // this is meant to be performant + // inspired by https://github.com/bramstein/bit-array/blob/master/lib/bit-array.js + get count(): number { + let count = 0; + let view = getViewer(this); + for (let i = 0; i < this.length / 32; i++) { + let x = view[i]; + // See: http://bits.stephan-brumme.com/countBits.html for an explanation + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = x + (x >> 4); + x &= 0xf0f0f0f; + count += (x * 0x01010101) >> 24; } - - /** - * - * @param bitArray - * @returns a BitArray instance with the logical 'and' applied to this and the passed parameter. - * The length of the resulting BitArray is the minimum of the length of the two operands. - */ - ["&"]( bitArray: BitArray ): BitArray { - const ret = new BitArray( Math.min( this.length, bitArray.length ) ); - const retView = new Uint32Array( ret.buffer ); - _views.set( ret.buffer, retView); - const thisView = getViewer( this ); - const bitArrayView = getViewer( bitArray ); - - // -1 needed for the case len == 32*n - let offset = ( ret.length - 1 >> 5 ) + 1; // divide 32, and add 1 because of next i--. - - while( offset-- ) - retView[offset] = thisView[offset] & bitArrayView[offset]; - - return ret; - } - - /** - * - * @param bitArray - * @returns a BitArray instance with the logical 'xor' applied to this and the passed parameter. - * The length of the resulting BitArray is the minimum of the length of the two operands. - */ - ["^"]( bitArray: BitArray ): BitArray { - const ret = new BitArray( Math.min( this.length, bitArray.length ) ); - const retView = new Uint32Array( ret.buffer ); - _views.set( ret.buffer, retView); - const thisView = getViewer( this ); - const bitArrayView = getViewer( bitArray ); - - // -1 needed for the case len == 32*n - let offset = ( ret.length - 1 >> 5 ) + 1; // divide 32, and add 1 because of next i--. - - while( offset-- ) - retView[offset] = thisView[offset] ^ bitArrayView[offset]; - - // when the two operands have a different length, and since bitwise - // operations treat by bunch of 32 bits, we may have set bits to 1 - // beyond the length of ret. We apply a mask to set them back to zero - // (so that other operations like .count are correct) - retView[ret.length>>5] &= (1 << ( ret.length & 31 )) -1 /* modulo 32 , -1*/ - - return ret; + return count; + } + + // Implementation note 1: I once used the maximum length of the two operands (for | and for ^), + // defaulting to 'false' for the operand with "missing" values. However, it is arguable that + // this is a good default assumption (it depends on context) so I finally prefer to go + // conservative, and compute only when there is actual data available (i.e. not assuming + // 'false' otherwise). + // As a positive side effect, it also simplifies implementation (but this is not the driver). + + // Implementation note 2: I could have a very simple implementation doing e.g. + // ret[i] = this[i] && bitArray[i], for every index + // However, this would go through the array bit by bit. Instead, I go 4 bytes by 4 bytes (Uint32). + // This is where improved performance is expected. + + /** + * + * @param bitArray + * @returns a BitArray instance with the logical 'or' applied to this and the passed parameter. + * The length of the resulting BitArray is the minimum of the length of the two operands. + */ + ['|'](bitArray: BitArray): BitArray { + const ret = new BitArray(Math./*max*/ min(this.length, bitArray.length)); + const retView = new Uint32Array(ret.buffer); + _views.set(ret.buffer, retView); + const thisView = getViewer(this); + const bitArrayView = getViewer(bitArray); + + // let offset = 0; + // -1 needed for the case len == 32*n + let offset = ((ret.length - 1) >> 5) + 1; // divide 32, and add 1 because of next i--. + + // for( /**/; + // // -1 needed for the case Math.min() == 32*n. + // offset < ( ret.length /*== Math.min( this.length, bitArray.length )*/ - 1 >> 5 ) + 1; + // offset++ ) + while (offset--) + // note: ret is a proxy; + retView[offset] = thisView[offset] | bitArrayView[offset]; + + // Needed only if length of the returned value would be the max length of the two operands + // for( /**/; + // // offset < ( Math.max( this.length, bitArray.length ) >> 5 ) + 1; + // // (tbc) same as: + // offset < ret.length >> 5; + // offset++ ) + // _views.get( ret.buffer )[ offset ] = _views.get( ( this.length <= bitArray.length ? bitArray : _targets.get( this ) ).buffer )[ offset ]; + + // when the two operands have a different length, and since bitwise + // operations treat by bunch of 32 bits, we may have set bits to 1 + // beyond the length of ret. We apply a mask to set them back to zero + // (so that other operations like .count are correct) + retView[ret.length >> 5] &= + (1 << (ret.length & 31)) - 1; /* modulo 32 , -1*/ + + return ret; + } + + /** + * + * @param bitArray + * @returns a BitArray instance with the logical 'and' applied to this and the passed parameter. + * The length of the resulting BitArray is the minimum of the length of the two operands. + */ + ['&'](bitArray: BitArray): BitArray { + const ret = new BitArray(Math.min(this.length, bitArray.length)); + const retView = new Uint32Array(ret.buffer); + _views.set(ret.buffer, retView); + const thisView = getViewer(this); + const bitArrayView = getViewer(bitArray); + + // -1 needed for the case len == 32*n + let offset = ((ret.length - 1) >> 5) + 1; // divide 32, and add 1 because of next i--. + + while (offset--) retView[offset] = thisView[offset] & bitArrayView[offset]; + + return ret; + } + + /** + * + * @param bitArray + * @returns a BitArray instance with the logical 'xor' applied to this and the passed parameter. + * The length of the resulting BitArray is the minimum of the length of the two operands. + */ + ['^'](bitArray: BitArray): BitArray { + const ret = new BitArray(Math.min(this.length, bitArray.length)); + const retView = new Uint32Array(ret.buffer); + _views.set(ret.buffer, retView); + const thisView = getViewer(this); + const bitArrayView = getViewer(bitArray); + + // -1 needed for the case len == 32*n + let offset = ((ret.length - 1) >> 5) + 1; // divide 32, and add 1 because of next i--. + + while (offset--) retView[offset] = thisView[offset] ^ bitArrayView[offset]; + + // when the two operands have a different length, and since bitwise + // operations treat by bunch of 32 bits, we may have set bits to 1 + // beyond the length of ret. We apply a mask to set them back to zero + // (so that other operations like .count are correct) + retView[ret.length >> 5] &= + (1 << (ret.length & 31)) - 1; /* modulo 32 , -1*/ + + return ret; + } + + /** + * + * @param alphabet a set of n characters to use to encode the BitArray; alphabet.length must be a power of 2 (2, 4, 8, etc) + * The more characters in the set, the more compact the resulting output will be + * @returns a string encoded using the provided character set (e.g., base64 encoding can be achieved with this) + */ + encode(alphabet: string): string { + const log2 = Math.log2(alphabet.length); + + if (log2 < 1 || log2 % 1 !== 0) { + throw new RangeError(alphabetLengthErrorMsg); } - /** - * - * @param alphabet a set of n characters to use to encode the BitArray; alphabet.length must be a power of 2 (2, 4, 8, etc) - * The more characters in the set, the more compact the resulting output will be - * @returns a string encoded using the provided character set (e.g., base64 encoding can be achieved with this) - */ - encode(alphabet: string): string { - const log2 = Math.log2(alphabet.length); - - if (log2 < 1 || log2 % 1 !== 0) { - throw new RangeError(alphabetLengthErrorMsg); - } - - const ret = []; - - let val = 0; - let valLen = 0; - for (const b of this) { - valLen++; - val <<= 1; - val += b; + const ret = []; - if (valLen === log2) { - ret.push(alphabet[val]); - valLen = val = 0; - } - } - - if (valLen !== 0) { - val <<= (log2 - valLen); - ret.push(alphabet[val]); - } + let val = 0; + let valLen = 0; + for (const b of this) { + valLen++; + val <<= 1; + val += b; - return ret.join(''); + if (valLen === log2) { + ret.push(alphabet[val]); + valLen = val = 0; + } } - /** - * - * @param alphabet a set of n characters to use to encode the BitArray; alphabet.length must be a power of 2 (2, 4, 8, etc), - * and should generally match the set used in the original encoding - * @param encodedString an encoded string built with encode - * @returns a BitArray of the encodedString decoded using alphabet - */ - static decode(encodedString: string, alphabet: string): BitArray { - const log2 = Math.log2(alphabet.length); - - if (log2 < 1 || log2 % 1 !== 0) { - throw new RangeError(alphabetLengthErrorMsg); - } - - const pad = (s: string) => '0'.repeat(log2 - s.length) + s + if (valLen !== 0) { + val <<= log2 - valLen; + ret.push(alphabet[val]); + } - const charMap = {} // maps each character to its integral value - for (var i = 0; i < alphabet.length; i++) { - charMap[alphabet[i]] = pad(i.toString(2)) - } + return ret.join(''); + } + + /** + * + * @param alphabet a set of n characters to use to encode the BitArray; alphabet.length must be a power of 2 (2, 4, 8, etc), + * and should generally match the set used in the original encoding + * @param encodedString an encoded string built with encode + * @returns a BitArray of the encodedString decoded using alphabet + */ + static decode(encodedString: string, alphabet: string): BitArray { + const log2 = Math.log2(alphabet.length); + + if (log2 < 1 || log2 % 1 !== 0) { + throw new RangeError(alphabetLengthErrorMsg); + } - const deserialized = Array.from(encodedString).map(c => { - if (!(c in charMap)) { - throw new RangeError('Invalid character found in encoded string'); - } - return charMap[c]; - }).join('') + const pad = (s: string) => '0'.repeat(log2 - s.length) + s; - return BitArray.from(deserialized); + const charMap = {}; // maps each character to its integral value + for (var i = 0; i < alphabet.length; i++) { + charMap[alphabet[i]] = pad(i.toString(2)); } - // Convenience specializations for encoding base64MIME and base64Url - encodeBase64MIME() { - return this.encode(base64MIMEChars); - } - static decodeBase64MIME(encodedString: string) { - return BitArray.decode(encodedString, base64MIMEChars); - } - - encodeBase64Url() { return this.encode(base64UrlChars) } - static decodeBase64Url(encodedString: string) { - return BitArray.decode(encodedString, base64UrlChars); - } + const deserialized = Array.from(encodedString) + .map((c) => { + if (!(c in charMap)) { + throw new RangeError('Invalid character found in encoded string'); + } + return charMap[c]; + }) + .join(''); + + return BitArray.from(deserialized); + } + + // Convenience specializations for encoding base64MIME and base64Url + encodeBase64MIME() { + return this.encode(base64MIMEChars); + } + static decodeBase64MIME(encodedString: string) { + return BitArray.decode(encodedString, base64MIMEChars); + } + + encodeBase64Url() { + return this.encode(base64UrlChars); + } + static decodeBase64Url(encodedString: string) { + return BitArray.decode(encodedString, base64UrlChars); + } } // create aliases -BitArray.prototype.and = BitArray.prototype["&"]; -BitArray.prototype.or = BitArray.prototype["|"]; -BitArray.prototype.xor = BitArray.prototype["^"]; +BitArray.prototype.and = BitArray.prototype['&']; +BitArray.prototype.or = BitArray.prototype['|']; +BitArray.prototype.xor = BitArray.prototype['^']; diff --git a/test/index.ts b/test/index.ts index 756a565..08b075a 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1,4 +1,4 @@ -import suite from "./suite"; -import test from "./test"; +import suite from './suite'; +import test from './test'; -test( suite ); +test(suite); diff --git a/test/suite.ts b/test/suite.ts index f8ca8df..f15cd7a 100644 --- a/test/suite.ts +++ b/test/suite.ts @@ -1,21 +1,21 @@ -import BitArray from "../src/bitarray"; +import BitArray from '../src/bitarray'; const len = 42; // choose whatever value // we fill randomly, but we could choose fixed values, or whatever, -const arr1 = new Array( len ).fill(false).map( x => Math.random() > 0.5 ) +const arr1 = new Array(len).fill(false).map((x) => Math.random() > 0.5); // arr2 is arbitrarily longer here. Could also be shorter, or same size -const arr2 = new Array( len + 10 ).fill(false).map( x => Math.random() > 0.5 ) +const arr2 = new Array(len + 10).fill(false).map((x) => Math.random() > 0.5); -const sample1 = BitArray.from( arr1 ); -const sample2 = BitArray.of( ...arr2 ); -const sample3 = BitArray.from( '0110'); +const sample1 = BitArray.from(arr1); +const sample2 = BitArray.of(...arr2); +const sample3 = BitArray.from('0110'); // Returns true if the block throws -function expectThrow( fn: () => void) { +function expectThrow(fn: () => void) { try { fn(); - } catch(e) { + } catch (e) { return true; } @@ -23,76 +23,96 @@ function expectThrow( fn: () => void) { } // matches the format of BitArray.toSting() -function toString( arr ) { - return arr.map( Number ) - // add space between each byte - .map( (b,i) => (i+1)%8 ? b : b+" " ) - .join("") - // remove possible end space - .trim(); +function toString(arr) { + return ( + arr + .map(Number) + // add space between each byte + .map((b, i) => ((i + 1) % 8 ? b : b + ' ')) + .join('') + // remove possible end space + .trim() + ); } - /** suite 1 */ const instantiating = { - "BitArray.from": sample1 instanceof BitArray, - "BitArray.of": sample2 instanceof BitArray + 'BitArray.from': sample1 instanceof BitArray, + 'BitArray.of': sample2 instanceof BitArray, }; /** suite 2 */ const properties = { - ".count": sample1.count == arr1.reduce( (acc,bit) => acc+Number(bit), 0 ) + '.count': sample1.count == arr1.reduce((acc, bit) => acc + Number(bit), 0), }; - /** suite 3 */ -const binary_operations = (()=>{ - - const shortestArr = arr2.length>arr1.length ? arr1 : arr2; +const binary_operations = (() => { + const shortestArr = arr2.length > arr1.length ? arr1 : arr2; return { - "&": (sample1)['&'](sample2) .toString() === toString( shortestArr.map((_,i)=> arr1[i] && arr2[i] )), - - "|": (sample1)['|'](sample2) .toString() === toString( shortestArr.map((_,i)=> arr1[i] || arr2[i] )) - // make sure we don't have bits set beyond the length of the bitarray - && (sample1)['|'](sample2) .count == arr1.reduce( (acc,bit,i) => acc+(Number(arr1[i])|Number(arr2[i])), 0 ), - - "^": (sample1)['^'](sample2) .toString() === toString( shortestArr.map((_,i)=> arr1[i] !== arr2[i] )) - && (sample1)['^'](sample2) .count == arr1.reduce( (acc,bit,i) => acc+(Number(arr1[i])^Number(arr2[i])), 0 ), - - "or" : (sample1).or (sample2) .toString() === toString( shortestArr.map((_,i)=> arr1[i] || arr2[i] )), - "and": (sample1).and (sample2) .toString() === toString( shortestArr.map((_,i)=> arr1[i] && arr2[i] )), - "xor": (sample1).xor (sample2) .toString() === toString( shortestArr.map((_,i)=> arr1[i] !== arr2[i] )) - } - + '&': + sample1['&'](sample2).toString() === + toString(shortestArr.map((_, i) => arr1[i] && arr2[i])), + + '|': + sample1['|'](sample2).toString() === + toString(shortestArr.map((_, i) => arr1[i] || arr2[i])) && + // make sure we don't have bits set beyond the length of the bitarray + sample1['|'](sample2).count == + arr1.reduce( + (acc, bit, i) => acc + (Number(arr1[i]) | Number(arr2[i])), + 0 + ), + + '^': + sample1['^'](sample2).toString() === + toString(shortestArr.map((_, i) => arr1[i] !== arr2[i])) && + sample1['^'](sample2).count == + arr1.reduce( + (acc, bit, i) => acc + (Number(arr1[i]) ^ Number(arr2[i])), + 0 + ), + + or: + sample1.or(sample2).toString() === + toString(shortestArr.map((_, i) => arr1[i] || arr2[i])), + and: + sample1.and(sample2).toString() === + toString(shortestArr.map((_, i) => arr1[i] && arr2[i])), + xor: + sample1.xor(sample2).toString() === + toString(shortestArr.map((_, i) => arr1[i] !== arr2[i])), + }; })(); /** suite 4 */ const character_encoding_from_set = { - ".encode_1bit": sample3.encode('ab') === 'abba', - ".encode_3bit": sample3.encode('abcdefgh') === 'da', - ".encode_": expectThrow(() => sample3.encode('')), - ".encode_a": expectThrow(() => sample3.encode('a')), - ".encode_abc": expectThrow(() => sample3.encode('abc')) + '.encode_1bit': sample3.encode('ab') === 'abba', + '.encode_3bit': sample3.encode('abcdefgh') === 'da', + '.encode_': expectThrow(() => sample3.encode('')), + '.encode_a': expectThrow(() => sample3.encode('a')), + '.encode_abc': expectThrow(() => sample3.encode('abc')), }; /** suite 5 */ const character_encode_decode = { - ".decode_1bit": BitArray.decode('abba', 'ab').toString() === sample3.toString(), + '.decode_1bit': + BitArray.decode('abba', 'ab').toString() === sample3.toString(), // Note: the substring is needed because when deserializing, we have some number of padding 0s that we can't know were // in the original string or not - ".decode_3bit": BitArray.decode('da', 'abcdefgh').toString().substring(0, 4) === sample3.toString(), - ".decode_empty": BitArray.decode('', 'ab').toString() === '', - ".decode_invalid": expectThrow(() => BitArray.decode('abc', 'ab')), - ".decode_": expectThrow(() => BitArray.decode('abba', '')) + '.decode_3bit': + BitArray.decode('da', 'abcdefgh').toString().substring(0, 4) === + sample3.toString(), + '.decode_empty': BitArray.decode('', 'ab').toString() === '', + '.decode_invalid': expectThrow(() => BitArray.decode('abc', 'ab')), + '.decode_': expectThrow(() => BitArray.decode('abba', '')), }; - export default { instantiating, properties, binary_operations, character_encoding_from_set, - character_encode_decode + character_encode_decode, }; - diff --git a/test/test.ts b/test/test.ts index 8cf5f77..ebd7789 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,44 +1,43 @@ // coloring in node -const - NodeFailure = "\x1b[31m", // red - NodeSuccess = "\x1b[32m", // green - NodeInfo = "\x1b[37m"; // white +const NodeFailure = '\x1b[31m', // red + NodeSuccess = '\x1b[32m', // green + NodeInfo = '\x1b[37m'; // white // coloring in browser -const - BrowserFailure = "color:red;", - BrowserSuccess = "color:green;", - BrowserInfo = "color:darkgrey;font-size: x-large;"; - -const log = typeof window === "object" - ? // in browser - function( success: boolean, text: string, tab: string ) { - success == true ? console.log( "%c" + tab + text, BrowserSuccess ) - : success == false ? console.log( "%c" + tab + text, BrowserFailure ) - : console.log( "%c" + tab + text, BrowserInfo ); - } - : // in node - function( success: boolean, text: string, tab: string ) { - success == true ? console.log( NodeSuccess + tab + text + NodeInfo ) - : success == false ? console.log( NodeFailure + tab + text + NodeInfo ) - : console.log( tab + text ); - }; - - -export default function test( suite: object, tab="" ) { +const BrowserFailure = 'color:red;', + BrowserSuccess = 'color:green;', + BrowserInfo = 'color:darkgrey;font-size: x-large;'; + +const log = + typeof window === 'object' + ? // in browser + function (success: boolean, text: string, tab: string) { + success == true + ? console.log('%c' + tab + text, BrowserSuccess) + : success == false + ? console.log('%c' + tab + text, BrowserFailure) + : console.log('%c' + tab + text, BrowserInfo); + } + : // in node + function (success: boolean, text: string, tab: string) { + success == true + ? console.log(NodeSuccess + tab + text + NodeInfo) + : success == false + ? console.log(NodeFailure + tab + text + NodeInfo) + : console.log(tab + text); + }; + +export default function test(suite: object, tab = '') { let success: boolean; // for( let [desc, success] of Object.entries( suite ) ) // es2017 - for( let desc of Object.keys( suite ) ) { + for (let desc of Object.keys(suite)) { success = suite[desc]; - if( typeof success === "boolean" ) - log( success, desc, tab ); + if (typeof success === 'boolean') log(success, desc, tab); else { - log( undefined, desc, tab ); - test( success, tab+"\t" ); + log(undefined, desc, tab); + test(success, tab + '\t'); } - } - -}; +}