total-serialism
Advanced tools
Comparing version 1.5.0 to 1.6.0
@@ -15,8 +15,21 @@ //============================================================================== | ||
// - hexBeat() inspired by Steven Yi's implementation in the csound | ||
// livecode environment from https://github.com/kunstmusik/csound-live-code | ||
// livecode environment from | ||
// https://github.com/kunstmusik/csound-live-code | ||
// and here https://kunstmusik.github.io/learn-hex-beats/ | ||
// - fibonacci(), nbonacci() and pisano() inspired by 'fibonacci motion' | ||
// used by composer Iannis Xenakis and 'symbolic music'. See further | ||
// reading in README.md. Also inspired by Numberphile videos on | ||
// pisano period on youtube. | ||
//============================================================================== | ||
const Transform = require('./transform.js'); | ||
const BigNumber = require('bignumber.js'); | ||
const JSON = require('jsonfile'); | ||
// configure the bignumber settings | ||
BigNumber.config({ | ||
DECIMAL_PLACES: 20, | ||
EXPONENTIAL_AT: [-7, 20] | ||
}); | ||
// A hexadecimal rhythm generator. Generates values of 0 and 1 | ||
@@ -120,1 +133,169 @@ // based on the input of a hexadecimal character string | ||
exports.linden = linden; | ||
// Generate any n-bonacci sequence as an array of BigNumber objects | ||
// F(n) = t * F(n-1) + F(n-2). This possibly generatres various | ||
// integer sequences: fibonacci, pell, tribonacci | ||
// | ||
// @param {Int} -> output length of array | ||
// @param {Int} -> multiplier t | ||
// @param {Int} -> start value 1 | ||
// @param {Int} -> start value 2 | ||
// @return {Array} -> array of BigNumber objects | ||
// | ||
function numBonacci(len=1, s1=0, s2=1, t=1){ | ||
var n1 = new BigNumber(s2); //startvalue n-1 | ||
var n2 = new BigNumber(s1); //startvalue n-2 | ||
len = Math.max(1, len-2); | ||
var cur = 0, arr = [n2, n1]; | ||
if (len < 2) { | ||
return arr.slice(0, len); | ||
} else { | ||
for (var i=0; i<len; i++){ | ||
// general method for nbonacci sequences | ||
// Fn = t * Fn-1 + Fn-2 | ||
cur = n1.times(t).plus(n2); | ||
n2 = n1; // store n-1 as n-2 | ||
n1 = cur; // store current number as n-1 | ||
arr.push(cur); // store BigNumber in array | ||
} | ||
return arr; | ||
} | ||
} | ||
// Generate any n-bonacci sequence as an array of BigNumber objects | ||
// for export fuction | ||
// | ||
// @return {String-Array} -> array of bignumbers as strings | ||
// | ||
function nbonacci(len=1, s1=0, s2=1, t=1){ | ||
return numBonacci(len, s1, s2, t).map(x => x.toFixed()); | ||
} | ||
exports.nbonacci = nbonacci; | ||
// Generate the Fibonacci sequence as an array of BigNumber objects | ||
// F(n) = F(n-1) + F(n-2). The ratio between consecutive numbers in | ||
// the fibonacci sequence tends towards the Golden Ratio (1+√5)/2 | ||
// OEIS: A000045 (Online Encyclopedia of Integer Sequences) | ||
// | ||
// @param {Int} -> output length of array | ||
// @return {String-Array} -> array of bignumbers as strings | ||
// | ||
function fibonacci(len=1, offset=0){ | ||
var f = numBonacci(len+offset, 0, 1, 1).map(x => x.toFixed()) | ||
if (offset > 0){ | ||
return f.slice(offset, offset+len); | ||
} | ||
return f; | ||
} | ||
exports.fibonacci = fibonacci; | ||
// Generate the Pisano period sequence as an array of BigNumber objects | ||
// Returns array of [0] if no period is found within the default length | ||
// of fibonacci numbers (256). In that case | ||
// | ||
// F(n) = (F(n-1) + F(n-2)) mod a. | ||
// | ||
// @param {Int} -> output length of array | ||
// @param {Int} -> modulus for pisano period | ||
// @return {Int-Array} -> array of integers | ||
// | ||
function pisano(mod, len=-1){ | ||
mod = (mod < 1)? 1 : mod; | ||
if (len < 1){ | ||
return pisanoPeriod(mod); | ||
} else { | ||
return numBonacci(len, 0, 1, 1).map(x => x.mod(mod).toNumber()); | ||
} | ||
} | ||
exports.pisano = pisano; | ||
function pisanoPeriod(mod, length=64){ | ||
// console.log('pisano', '@mod', mod, '@length', length); | ||
var seq = numBonacci(length, 0, 1, 1).map(x => x.mod(mod).toNumber()); | ||
var p = [], l = 0; | ||
for (var i=0; i<seq.length; i++){ | ||
p.push(seq[i]); | ||
if (p.length > 2){ | ||
var c = [0, 1, 1]; | ||
var equals = 0; | ||
for (k in p){ | ||
equals += p[k] === c[k]; | ||
} | ||
if (equals === 3 && l > 3){ | ||
return seq.slice(0, l); | ||
// return { 'length' : l }; | ||
} | ||
p = p.slice(1, 3); | ||
l++; | ||
} | ||
} | ||
// console.log('no period, next iteration'); | ||
return pisanoPeriod(mod, length*2); | ||
} | ||
// Generate the Pell numbers as an array of BigNumber objects | ||
// F(n) = 2 * F(n-1) + F(n-2). The ratio between consecutive numbers | ||
// in the pell sequence tends towards the Silver Ratio 1 + √2. | ||
// OEIS: A006190 (Online Encyclopedia of Integer Sequences) | ||
// | ||
// @param {Int} -> output length of array | ||
// @return {String-Array} -> array of bignumbers as strings | ||
// | ||
function pell(len=1){ | ||
return numBonacci(len, 0, 1, 2).map(x => x.toFixed()); | ||
} | ||
exports.pell = pell; | ||
// Generate the Tribonacci numbers as an array of BigNumber objects | ||
// F(n) = 2 * F(n-1) + F(n-2). The ratio between consecutive numbers in | ||
// the 3-bonacci sequence tends towards the Bronze Ratio (3 + √13) / 2. | ||
// OEIS: A000129 (Online Encyclopedia of Integer Sequences) | ||
// | ||
// @param {Int} -> output length of array | ||
// @return {String-Array} -> array of bignumbers as strings | ||
// | ||
function threeFibonacci(len=1){ | ||
return numBonacci(len, 0, 1, 3).map(x => x.toFixed()); | ||
} | ||
exports.threeFibonacci = threeFibonacci; | ||
// Generate the Lucas numbers as an array of BigNumber objects | ||
// F(n) = F(n-1) + F(n-2), with F0=2 and F1=1. | ||
// OEIS: A000032 (Online Encyclopedia of Integer Sequences) | ||
// | ||
// @param {Int} -> output length of array | ||
// @return {String-Array} -> array of bignumbers as strings | ||
// | ||
function lucas(len=1){ | ||
return numBonacci(len, 2, 1, 1).map(x => x.toFixed()); | ||
} | ||
exports.lucas = lucas; | ||
// build the integer sequences dataset for quick access | ||
// buildDataSet(); | ||
function buildDataSet(){ | ||
const file = './data/fibonacci.json'; | ||
let data = { | ||
"fibonacci" : [], | ||
"pell" : [], | ||
"tribonacci" : [] | ||
} | ||
data.fibonacci = fibonacci(1024); | ||
data.pell = pell(1024); | ||
data.tribonacci = tribonacci(1024); | ||
JSON.writeFile(file, data, { spaces: 2, EOL: '\r\n' }, function (err) { | ||
if (err) { | ||
console.error(err); | ||
} else { | ||
console.log("//=> Integer Sequences dataset generated"); | ||
} | ||
}); | ||
} | ||
exports.buildDataSet = buildDataSet; |
@@ -71,5 +71,6 @@ //============================================================================== | ||
/* WORK IN PROGRESS | ||
// generate a list of random integer values | ||
// but the next random value is within a limited range of the | ||
// previous value | ||
// previous value generating a random "drunk" walk | ||
// | ||
@@ -80,8 +81,16 @@ // @param {Int} -> length of output array | ||
// @return {Array} | ||
// function drunk(len=1, lo=null, hi=null, start=0){ | ||
// len = Math.max(1, Math.abs(len)); | ||
// | ||
function drunk(len=1, step=2){ | ||
var p = 0, arr = []; | ||
for (var i=0; i<Math.max(len); i++){ | ||
var n = (rng() > 0.5) * 2 - 1; | ||
var s = Math.floor(rng() * step + 1) * n; | ||
p += s; | ||
arr.push(p); | ||
} | ||
return arr; | ||
} | ||
exports.drunk = drunk; | ||
WORK IN PROGRESS */ | ||
// } | ||
// exports.drunk = drunk; | ||
// generate a list of random integer values 0 or 1 | ||
@@ -112,6 +121,5 @@ // like a coin toss, heads/tails | ||
// shuffle a list, based on the | ||
// Fisher-Yates shuffle algorithm | ||
// shuffle a list, based on the Fisher-Yates shuffle algorithm | ||
// by Ronald Fisher and Frank Yates in 1938 | ||
// algorithm has run time complexity of O(n) | ||
// The algorithm has run time complexity of O(n) | ||
// | ||
@@ -158,10 +166,44 @@ // @param {Array} -> array to shuffle | ||
if (lo > hi){ var t=lo, lo=hi, hi=t; } | ||
// len is minimum of 1 | ||
len = (len > 1)? len : 1; | ||
// generate array with values and shuffle | ||
var jar = Gen.spread(hi-lo, lo, hi); | ||
// generate array with values and pick | ||
return pick(len, Gen.spread(hi-lo, lo, hi)); | ||
} | ||
exports.urn = urn; | ||
// Choose random items from an array provided | ||
// The default array is an array of 0 and 1 | ||
// | ||
// @param {Int} -> output length | ||
// @param {Array} -> items to choose from | ||
// @return {Array} -> randomly selected items | ||
// | ||
function choose(len=1, a=[0, 1]){ | ||
// if a is no Array make it an array | ||
a = (!Array.isArray(a))? [a] : a; | ||
var arr = []; | ||
for (var i=0; i<Math.max(1,len); i++){ | ||
arr.push(a[Math.floor(rng()*a.length)]); | ||
} | ||
return arr; | ||
} | ||
exports.choose = choose; | ||
// Pick random items from an array provided | ||
// An 'urn' is filled with values and when one is picked it is removed | ||
// from the urn. If the outputlist is longer then the range, the urn | ||
// refills when empty. On refill it is made sure no repeating value | ||
// can be picked. | ||
// | ||
// @param {Int} -> output length | ||
// @param {Array} -> items to choose from | ||
// @return {Array} -> randomly selected items | ||
// | ||
function pick(len=1, a=[0, 1]){ | ||
// fill the jar with the input | ||
var jar = (!Array.isArray(a))? [a] : a; | ||
// shuffle the jar | ||
var s = shuffle(jar); | ||
// value, previous, output-array | ||
var v, p, arr = []; | ||
for (var i=0; i<len; i++){ | ||
for (var i=0; i<Math.max(1,len); i++){ | ||
v = s.pop(); | ||
@@ -181,2 +223,2 @@ if (v === undefined){ | ||
} | ||
exports.urn = urn; | ||
exports.pick = pick; |
{ | ||
"name": "total-serialism", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"description": "A set of methods for the generation and transformation of number sequences useful in algorithmic composition", | ||
@@ -33,3 +33,8 @@ "main": "index.js", | ||
"permutation", | ||
"hexbeat" | ||
"hexbeat", | ||
"fibonacci", | ||
"pisano", | ||
"lindenmayer", | ||
"l-system", | ||
"euclidean" | ||
], | ||
@@ -43,2 +48,4 @@ "author": "Timo Hoogland", | ||
"dependencies": { | ||
"bignumber.js": "^9.0.0", | ||
"jsonfile": "^5.0.0", | ||
"seedrandom": "^3.0.5", | ||
@@ -45,0 +52,0 @@ "tonal": "^2.2.2" |
106
README.md
@@ -10,2 +10,6 @@ # Total Serialism | ||
- [Algorithmic Methods](#algorithmic-methods) | ||
- [Euclidean Rhythm](#euclidean-rhythm-generator) | ||
- [Hexadecimal Rhythm](#hexadecimal-rhythm-generator) | ||
- [Lindenmayer System](#lindenmayer-string-expansion-l-system) | ||
- [Fibonacci Sequence](#fibonacci-sequence) | ||
- [Stochastic Methods](#stochastic-methods) | ||
@@ -28,10 +32,2 @@ - [Transformative Methods](#transformative-methods) | ||
Generate Twelve-tone sequences | ||
```js | ||
// generate a twelve-tone series, influenced by the random seed | ||
Rand.twelveTone(); | ||
//=> [ 11, 0, 8, 2, 4, 9, 1, 6, 3, 5, 7, 10 ] | ||
``` | ||
Generate Lindenmayer system sequences | ||
@@ -52,2 +48,16 @@ | ||
Generate Pisano periods from Fibonacci sequences | ||
```js | ||
Algo.pisano(7); | ||
//=> [ 0, 1, 1, 2, 3, 5, 1, 6, 0, 6, 6, 5, 4, 2, 6, 1 ] | ||
``` | ||
Get fibonacci values as strings (to preserve the big numbers) | ||
```js | ||
Algo.fibonacci(2, 100); | ||
//=> [ '354224848179261915075', '573147844013817084101' ] | ||
``` | ||
# Install | ||
@@ -188,2 +198,53 @@ | ||
### Fibonacci Sequence | ||
Generate an array of Fibonacci numbers `F[n] = F[n-1] + F[n-2]`. Numbers are by default represented as Strings in order to allow for bigger numbers than 64-bit integers can represent. The calculations are done using the bignumber.js library. A second argument sets an offset to pick a certain number from the sequence. | ||
```js | ||
// 10 fibonacci numbers, starting from 0, 1, 1 etc... | ||
Algo.fibonacci(12); | ||
//=> [ '0', '1', '1', '2', '3', '5', '8', '13', '21', '34', '55', '89' ] | ||
// 2 fibonacci numbers, starting from the 100th value | ||
Algo.fibonacci(2, 100); | ||
//=> [ '354224848179261915075', '573147844013817084101' ] | ||
``` | ||
Generate Pisano periods for the Fibonacci sequence. The pisano period is a result of applying a modulo operation on the Fibonacci sequence `F[n] = (F[n-1] + F[n-2]) mod a`. The length of the period differs per modulus value, but the sequence will always have a repetition. | ||
```js | ||
// the pisano period for mod 7 has a length of 16 | ||
Algo.pisano(7); | ||
//=> [ 0, 1, 1, 2, 3, 5, 1, 6, 0, 6, 6, 5, 4, 2, 6, 1 ] | ||
// second argument gives a fixed length output | ||
Algo.pisano(4, 10); | ||
//=> [ 0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1 ] | ||
``` | ||
Other integer sequences based on Fibonacci are also available | ||
```js | ||
Algo.pell(10); | ||
//=> [ '0', '1', '2', '5', '12', '29', '70', '169', '408', '985' ] | ||
Algo.threeFibonacci(10); | ||
//=> [ '0', '1', '3', '10', '33', '109', '360', '1189', '3927', '12970' ] | ||
Algo.lucas(10); | ||
//=> [ '2', '1', '3', '4', '7', '11', '18', '29', '47', '76' ] | ||
``` | ||
Set a custom starting pair of numbers to generate an n-bonacci sequence according to the following method: `F[n] = t * F[n-1] + F[n-2]` | ||
```js | ||
// start with 1, 3, then multiply [n-1] by 2 before adding with [n-2] | ||
Algo.nbonacci(10, 1, 3, 2); | ||
//=> [ '1', '3', '7', '17', '41', '99', '239', '577', '1393', '3363' ] | ||
// this is the same as Algo.fibonacci(10) | ||
Algo.nbonacci(10, 0, 1, 1); | ||
//=> [ '0', '1', '1', '2', '3', '5', '8', '13', '21', '34' ] | ||
``` | ||
## Stochastic Methods | ||
@@ -222,3 +283,5 @@ | ||
//=> [ 11, 0, 8, 2, 4, 9, 1, 6, 3, 5, 7, 10 ] | ||
``` | ||
```js | ||
// generate an array with random values picked from an urn | ||
@@ -240,2 +303,21 @@ // with default range 0 to 12 (exclusive) | ||
```js | ||
// Choose random items from an array provided, uniform distribution | ||
Rand.choose(5, [0, 1, 2, 3, 5, 8, 13]); | ||
//=> [ 3, 0, 13, 3, 2 ] | ||
// Array can have any datatype | ||
Rand.choose(5, ['c', 'e', 'g']); | ||
//=> [ 'c', 'c', 'g', 'e', 'g' ] | ||
// Pick random items from an array similar to urn | ||
// no repeating values untill urn is empty | ||
Rand.pick(5, [0, 1, 2, 3, 5, 8, 13]); | ||
//=> [ 2, 5, 8, 1, 3 ] | ||
// Array can have any datatype | ||
Rand.pick(5, ['c', 'e', ['g', 'd']]); | ||
//=> [ 'e', [ 'g', 'd' ], 'c', [ 'g', 'd' ], 'e' ] | ||
``` | ||
## Transformative Methods | ||
@@ -308,2 +390,4 @@ | ||
The inspiration for usage of Integer Sequences came from composers such as Iannis Xenakis, who used the fibonacci formula in his piece *Nomos Alpha* and referred to the technique as *Fibonacci Motion*. Also Xenakis referred to the usuage of set theory for composition as *Symbolic Music*. | ||
- [Serialism on Wikipedia](https://en.wikipedia.org/wiki/Serialism) | ||
@@ -327,2 +411,8 @@ | ||
- [Iannis Xenakis - Formalized Music, Thought and Mathematics in Music](https://books.google.nl/books?hl=en&lr=&id=y6lL3I0vmMwC&oi=fnd&pg=PR7&dq=symbolic+music+xenakis&ots=W_s_gzotb2&sig=Y6-2zjquOIwju7q8uaoRcPuboC8&redir_esc=y#v=onepage&q=symbolic%20music%20xenakis&f=false) | ||
- [Thomas DeLio - Nomos Alpha: The Dialects of Structure and Materials](https://www.jstor.org/stable/843739?seq=1) | ||
- [Online Encyclopedia of Integer Sequences](https://oeis.org/A000045) | ||
# Missing Something? | ||
@@ -329,0 +419,0 @@ |
@@ -21,4 +21,4 @@ | ||
// testGen(); | ||
// testAlgo(); | ||
testRand(); | ||
testAlgo(); | ||
// testRand(); | ||
// testMod(); | ||
@@ -98,2 +98,18 @@ // // testTranslate(); | ||
test("Algo.linden(0, 3, complexRules)"); | ||
test("Algo.fibonacci(12)"); | ||
test("Algo.fibonacci(2, 100)"); | ||
test('Algo.fibonacci(1, 100)[0].split("").map(x => Number(x))'); | ||
test("Algo.pisano(12)"); | ||
test("Algo.pisano(7)"); | ||
test("Algo.pisano(4, 10)"); | ||
test("Algo.pell(10)"); | ||
test("Algo.threeFibonacci(10)"); | ||
test("Algo.lucas(10)"); | ||
test("Algo.nbonacci(10, 1, 3, 2)"); | ||
test("Algo.nbonacci(10, 0, 1, 1)"); | ||
} | ||
@@ -144,2 +160,7 @@ | ||
test('Rand.urn(12, -3, 3)'); | ||
test("Rand.choose(5, [0, 1, 2, 3, 5, 8, 13])"); | ||
test("Rand.choose(5, ['c', 'e', 'g'])"); | ||
test("Rand.pick(5, [0, 1, 2, 3, 5, 8, 13])"); | ||
test("Rand.pick(5, ['c', 'e', ['g', 'd']])"); | ||
} | ||
@@ -146,0 +167,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
676992
16
5649
424
4
2
+ Addedbignumber.js@^9.0.0
+ Addedjsonfile@^5.0.0
+ Addedbignumber.js@9.1.2(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedjsonfile@5.0.0(transitive)
+ Addeduniversalify@0.1.2(transitive)