diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6a34d2..a988c93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: branches: [ main, dev ] jobs: - test: + clojure-test: runs-on: ubuntu-latest steps: @@ -35,5 +35,28 @@ jobs: restore-keys: | ${{ runner.os }}-gitlibs- - - name: Run tests + - name: Run Clojure tests run: clojure -M:test + + node-test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [lts/*] # LTS plus specific versions + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' # Enable dependency caching + + - name: Install dependencies + run: npm ci + + - name: Run npm tests + run: CI=false npm test diff --git a/.gitignore b/.gitignore index 739cc56..5ccf1af 100755 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ src/archive/huygens-fokker-scala-archive/*.scl **/.DS_Store /node_modules /dist +/out .cider-repl-history .shadow-cljs/ npm-test/ diff --git a/README.md b/README.md index 3ac1477..90e2a00 100755 --- a/README.md +++ b/README.md @@ -34,15 +34,18 @@ https://en.xen.wiki/w/MOS_scale (def generator 13) (def my-mos (mos/make period generator)) (submos/make-all-submos (nth my-mos 3) generator) ;; this will generate all secondary MOS and all possible "traverse" MOS - - - ``` ## Note -This library is a work in progress and mostly a workshop for myself, so the code is not polished as it should. If you are using this library, feel free to let make me aware of so that I can take more care of the code and the documentation. +This library is a work in progress and mostly a workshop for myself, so the code is not polished as it should. If you are using this library, feel free to let make me aware of it so that I can take more care of the code and the documentation. + + +## To Do +- [ ] In JS, by using exact, operations involving floating point or double instances may be broken as they expect the source to be strings containing parseable integers or rationals. Floating point support needs to be tested and supported. +## Development +On `emacs` one can run `cider-jackin-clj&cljs` then select the `shadow-cljs` server and the `:browser-build`. ## License diff --git a/deps.edn b/deps.edn index 3a3bc78..8602adc 100755 --- a/deps.edn +++ b/deps.edn @@ -1,16 +1,33 @@ {:paths ["src" "test" "dev"] - :deps {org.clojure/clojure {:mvn/version "1.11.1"} - org.clojure/clojurescript {:mvn/version "1.11.60"} - com.google.javascript/closure-compiler-unshaded {:mvn/version "v20221102"} + :deps {org.clojure/clojure {:mvn/version "1.12.0"} + org.clojure/core.async {:mvn/version "1.3.618"} org.clojure/math.combinatorics {:mvn/version "0.3.0"} com.taoensso/timbre {:mvn/version "4.10.0"} quil/quil {:mvn/version "4.0.0-SNAPSHOT"} + metosin/malli {:mvn/version "0.19.1"} com.gfredericks/exact {:mvn/version "0.1.11"} org.clojure/tools.namespace {:mvn/version "1.3.0"} table/table {:mvn/version "0.5.0"} - org.clojure/data.json {:mvn/version "2.4.0"}} - :aliases {:test {:extra-paths ["test"] + org.clojure/data.json {:mvn/version "2.4.0"} + hiccup/hiccup {:mvn/version "2.0.0"}} + :aliases {:cljs {:extra-deps + {thheller/shadow-cljs {:mvn/version "3.2.1"} + binaryage/devtools {:mvn/version "0.9.7"} + ;; the following block of dependencies copied from shadow-cljs own deps + org.clojure/clojurescript {:mvn/version "1.12.35" + :exclusions + [com.google.javascript/closure-compiler + org.clojure/google-closure-library + org.clojure/google-closure-library-third-party]} + com.google.javascript/closure-compiler {:mvn/version "v20250407"} + org.clojure/google-closure-library {:mvn/version "0.0-20250418-2ce9ab6d"} + org.clojure/google-closure-library-third-party {:mvn/version "0.0-20250418-2ce9ab6d"}}} + :test {:extra-paths ["test"] :extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}} :main-opts ["-m" "cognitect.test-runner"] - :exec-fn cognitect.test-runner.api/test}}} + :exec-fn cognitect.test-runner.api/test} + :codox {:extra-deps {codox/codox {:mvn/version "0.10.8"}} + :exec-fn codox.main/generate-docs + :codox {:output-path "codox"} + :exec-args {:source-paths ["src"]}}}} diff --git a/package-lock.json b/package-lock.json index 0b0273b..72100cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,58 +1,15 @@ { - "name": "erv", - "version": "0.0.4", + "name": "@diegovdc/erv", + "version": "0.0.14", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "erv", - "version": "0.0.4", - "license": "GNU", + "name": "@diegovdc/erv", + "version": "0.0.14", + "license": "GNU GLP v3.0", "devDependencies": { - "shadow-cljs": "2.25.4" - } - }, - "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "util": "0.10.3" - } - }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", - "dev": true - }, - "node_modules/assert/node_modules/util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", - "dev": true, - "dependencies": { - "inherits": "2.0.1" + "shadow-cljs": "3.2.1" } }, "node_modules/base64-js": { @@ -73,102 +30,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } + ], + "license": "MIT" }, - "node_modules/browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "dev": true, - "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - } - }, - "node_modules/browserify-sign/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/browserify-sign/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, "funding": [ { @@ -183,1819 +51,257 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "dependencies": { - "pako": "~1.0.5" - } - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true - }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, + ], + "license": "MIT", "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } + "license": "MIT" }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - }, + "license": "ISC", "engines": { - "node": "*" + "node": ">=16" } }, - "node_modules/des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">= 0.6.0" } }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "node_modules/readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", "dev": true, "engines": { - "node": ">=0.4", - "npm": ">=1.2" + "node": ">= 0.8.0" } }, - "node_modules/elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "node_modules/shadow-cljs": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/shadow-cljs/-/shadow-cljs-3.2.1.tgz", + "integrity": "sha512-xsTSHGUBGZqotbjdKTbKUuPaYoj41ozMPbylr0aQNHvpG+TEner7YTALPdthNGUsIseE+U7kNHV9HNTfRXc/Zw==", "dev": true, + "license": "ISC", "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" + "buffer": "^6.0.3", + "process": "^0.11.10", + "readline-sync": "^1.4.10", + "shadow-cljs-jar": "1.3.4", + "source-map-support": "^0.5.21", + "which": "^5.0.0", + "ws": "^8.18.1" + }, + "bin": { + "shadow-cljs": "cli/runner.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "node_modules/shadow-cljs-jar": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/shadow-cljs-jar/-/shadow-cljs-jar-1.3.4.tgz", + "integrity": "sha512-cZB2pzVXBnhpJ6PQdsjO+j/MksR28mv4QD/hP/2y1fsIa9Z9RutYgh3N34FZ8Ktl4puAXaIGlct+gMCJ5BmwmA==", "dev": true }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=0.8.x" + "node": ">=0.10.0" } }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, + "license": "ISC", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "isexe": "^3.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "node": ">=10.0.0" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash-base/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/hash-base/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "peerDependenciesMeta": { + "bufferutil": { + "optional": true }, - { - "type": "consulting", - "url": "https://feross.org/support" + "utf-8-validate": { + "optional": true } - ] - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" } + } + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/ieee754": { + "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } + "readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", + "dev": true }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "shadow-cljs": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/shadow-cljs/-/shadow-cljs-3.2.1.tgz", + "integrity": "sha512-xsTSHGUBGZqotbjdKTbKUuPaYoj41ozMPbylr0aQNHvpG+TEner7YTALPdthNGUsIseE+U7kNHV9HNTfRXc/Zw==", "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" + "requires": { + "buffer": "^6.0.3", + "process": "^0.11.10", + "readline-sync": "^1.4.10", + "shadow-cljs-jar": "1.3.4", + "source-map-support": "^0.5.21", + "which": "^5.0.0", + "ws": "^8.18.1" } }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "shadow-cljs-jar": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/shadow-cljs-jar/-/shadow-cljs-jar-1.3.4.tgz", + "integrity": "sha512-cZB2pzVXBnhpJ6PQdsjO+j/MksR28mv4QD/hP/2y1fsIa9Z9RutYgh3N34FZ8Ktl4puAXaIGlct+gMCJ5BmwmA==", "dev": true }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "node_modules/node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "dependencies": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "requires": { + "isexe": "^3.1.1" } }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "dependencies": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "dev": true, - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shadow-cljs": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/shadow-cljs/-/shadow-cljs-2.25.4.tgz", - "integrity": "sha512-pxQok9zxKv8Ohp0bhJS7/lZq7Pe3P2NQ189FGGw3wcq/AgwHBH61ysFbGKZpQ5ZliYF8gJ4VwZM4Bqsbm7AqPw==", - "dev": true, - "dependencies": { - "node-libs-browser": "^2.2.1", - "readline-sync": "^1.4.7", - "shadow-cljs-jar": "1.3.4", - "source-map-support": "^0.4.15", - "which": "^1.3.1", - "ws": "^7.4.6" - }, - "bin": { - "shadow-cljs": "cli/runner.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/shadow-cljs-jar": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/shadow-cljs-jar/-/shadow-cljs-jar-1.3.4.tgz", - "integrity": "sha512-cZB2pzVXBnhpJ6PQdsjO+j/MksR28mv4QD/hP/2y1fsIa9Z9RutYgh3N34FZ8Ktl4puAXaIGlct+gMCJ5BmwmA==", - "dev": true - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "dependencies": { - "source-map": "^0.5.6" - } - }, - "node_modules/stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "dependencies": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", - "dev": true, - "dependencies": { - "setimmediate": "^1.0.4" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", - "dev": true - }, - "node_modules/tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", - "dev": true - }, - "node_modules/url": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.1.tgz", - "integrity": "sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==", - "dev": true, - "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.11.0" - } - }, - "node_modules/util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "dependencies": { - "inherits": "2.0.3" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - } - }, - "dependencies": { - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "requires": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "dev": true, - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "dev": true, - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - } - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "readline-sync": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", - "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==", - "dev": true - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shadow-cljs": { - "version": "2.25.4", - "resolved": "https://registry.npmjs.org/shadow-cljs/-/shadow-cljs-2.25.4.tgz", - "integrity": "sha512-pxQok9zxKv8Ohp0bhJS7/lZq7Pe3P2NQ189FGGw3wcq/AgwHBH61ysFbGKZpQ5ZliYF8gJ4VwZM4Bqsbm7AqPw==", - "dev": true, - "requires": { - "node-libs-browser": "^2.2.1", - "readline-sync": "^1.4.7", - "shadow-cljs-jar": "1.3.4", - "source-map-support": "^0.4.15", - "which": "^1.3.1", - "ws": "^7.4.6" - } - }, - "shadow-cljs-jar": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/shadow-cljs-jar/-/shadow-cljs-jar-1.3.4.tgz", - "integrity": "sha512-cZB2pzVXBnhpJ6PQdsjO+j/MksR28mv4QD/hP/2y1fsIa9Z9RutYgh3N34FZ8Ktl4puAXaIGlct+gMCJ5BmwmA==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", - "dev": true - }, - "url": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.1.tgz", - "integrity": "sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==", - "dev": true, - "requires": { - "punycode": "^1.4.1", - "qs": "^6.11.0" - } - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "requires": {} - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true } } } diff --git a/package.json b/package.json index 6cdeb09..321c95a 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,23 @@ { "name": "@diegovdc/erv", - "version": "0.0.8", + "version": "0.0.14", "description": "Calculations and other algorithmic treatments of some of [Erv Wilson's](http://anaphoria.com/wilson.html) music scale theories.", "scripts": { "release:lib": "shadow-cljs release lib; sed -i '' 's/global/globalThis/g' dist/lib.js", "watch:lib": "shadow-cljs watch lib", - "prepublishOnly": "npm run release:lib" + "prepublishOnly": "npm run release:lib", + "test": "npx shadow-cljs compile test && node out/node-tests.js" }, "main": "dist/lib.js", + "files": [ + "dist/" + ], "keywords": [ "microtonality", "music theory" ], "devDependencies": { - "shadow-cljs": "2.25.4" + "shadow-cljs": "3.2.1" }, "license": "GNU GLP v3.0", "dependencies": {} diff --git a/project.clj b/project.clj deleted file mode 100755 index 7c40503..0000000 --- a/project.clj +++ /dev/null @@ -1,15 +0,0 @@ -(defproject erv "0.1.0-SNAPSHOT" - :description "A library to design microtonal scales with ideas mainly derived from Erv Wilson's work" - :url "https://github.com/diegovdc/erv" - :license {:name "GPL-3.0-or-later WITH Classpath-exception-2.0" - :url "https://www.gnu.org/licenses/gpl-3.0.en.html#license-text"} - :dependencies [[org.clojure/clojure "1.11.1"] - [org.clojure/math.combinatorics "0.3.0"] - [org.clojure/data.json "2.4.0"] - [com.gfredericks/exact "0.1.11"] - [com.taoensso/timbre "4.10.0"] - [table "0.5.0"] - [org.clojure/tools.namespace "1.3.0"] - [quil "4.0.0-SNAPSHOT"]] - :jvm-opts ["-Xmx8g"] - :repl-options {:init-ns user}) diff --git a/shadow-cljs.edn b/shadow-cljs.edn index b432a13..b469c7e 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -1,14 +1,20 @@ -{:source-paths ["src"] +{:source-paths ["src" "test"] :dependencies [[binaryage/devtools "0.9.7"] [cider/cider-nrepl "0.25.3"] - [com.taoensso/timbre "5.1.0"] [refactor-nrepl "2.5.0"] + [com.taoensso/timbre "5.1.0"] [table/table "0.5.0"] [com.gfredericks/exact "0.1.11"] [org.clojure/math.combinatorics "0.1.6"] - [com.taoensso/timbre "4.10.0"]] - :dev-http {5678 "build/browser"} + [quil/quil "4.0.0-SNAPSHOT"] + [metosin/malli "0.19.1"] + [hiccup/hiccup "2.0.0"]] + :dev-http {5678 "build/browser" + 3001 "out/test"} :builds ; https://shadow-cljs.github.io/docs/UsersGuide.html#target-node-script {:lib {:target :node-library :output-to "dist/lib.js" - :exports-fn js.export-fn/generate-exports}}} + :exports-fn js.export-fn/generate-exports} + :browser-test {:target :browser-test + :devtools {:ignore-warnings true} + :test-dir "out/test"}}} diff --git a/src/erv/beating_analyzer/v1.cljc b/src/erv/beating_analyzer/v1.cljc new file mode 100644 index 0000000..db77b89 --- /dev/null +++ b/src/erv/beating_analyzer/v1.cljc @@ -0,0 +1,211 @@ +(ns erv.beating-analyzer.v1 + #?(:cljs (:refer-clojure :exclude [>= <= > < + - * / - numerator denominator integer? + mod rem quot even? odd? abs])) + + (:require + [clojure.core :as core] + [clojure.math.combinatorics :as combo] + [clojure.string :as str] + [erv.utils.exact :as exact.utils] + #?(:cljs [com.gfredericks.exact :as e :refer [>= <= > < + - * / - abs]]) + [erv.utils.conversions :refer [cps->name*]] + [erv.utils.core :refer [decompose-ratio factorize-ratio factors->hiccup + make-map-by-key pow prime-factors]] + [erv.utils.ratios :refer [ratios->scale-data]] + [taoensso.timbre :as timbre] + #?(:clj [hiccup2.core :as h]))) + +(def ^:private default-partials (map exact.utils/->exact (range 1 9))) + +(defn get-beat-data-by-pairs + ([ratios] (get-beat-data-by-pairs default-partials ratios)) + ([partials ratios] + (->> ratios + (mapcat + (fn [degree ratio] + (map (fn [i] {:degree degree + :ratio ratio + :partial i + :partial*ratio (* i ratio)}) + (map exact.utils/->exact partials))) + (range)) + (#(combo/combinations % 2)) + (remove (fn [[x1 x2]] (= (:ratio x1) (:ratio x2)))) + (map (fn [pair] {:pair pair + :diff (abs (- (:partial*ratio (first pair)) + (:partial*ratio (second pair))))})) + (sort-by :diff) + #_(map (fn [pair] + (assoc pair + :diff-c4 (double (* root (:diff pair))) + :diff-c3 (double (/ (* root (:diff pair)) + 2)) + :diff-c2 (double (/ (* root (:diff pair)) + 4)) + :diff-c1 (double (/ (* root (:diff pair)) + 8)))))))) + +(comment + (->> (get-beat-data-by-pairs [1 2 3 4 5 6] [1 5/4 3/2]) + (map :pair) + (filter (fn [[a b]] + (and (= 0 (:degree a)) + (= 2 (:degree b)) + (= 3 (:partial a)) + (= 2 (:partial b)))))) + + (require '[erv.utils.exact :refer [parse-ratios]] + '[erv.utils.exact :as exact.utils]) + (def ratios (parse-ratios "1 + 67/64 + 279/256 + 9/8 + 75/64 + 39/32 + 5/4 + 167/128 + 87/64 + 45/32 + 187/128 + 3/2 + 25/16 + 417/256 + 27/16 + 7/4 + 233/128 + 15/8 + 125/64")) + (take 10 (get-beat-data-by-pairs ratios)) + (exact.utils/->exact period)) + +(def ^:private lowest-freq (exact.utils/->exact 20)) + +(defn- get-root-note-lowest-freq + [period freq] + (loop + [freq freq] + (cond + (< freq lowest-freq) (recur (* freq period)) + (>= freq (* lowest-freq period)) (recur (/ freq period)) + (and (>= freq lowest-freq) (> (* lowest-freq period) freq)) freq))) + +(comment + (get-root-note-lowest-freq (exact.utils/->exact 2) + (exact.utils/->exact 1))) + +(defn- get-freq-periods-range + [period initial-freq max-freq] + (->> (range) + (map #(* initial-freq (pow period %))) + (take-while #(<= % max-freq)))) + +(def ^:private max-freq (exact.utils/->exact 4000)) +(def ^:private max-beat-freq (exact.utils/->exact 20)) + +(comment + (require '[erv.utils.core :refer [interval]] + '[erv.utils.conversions :as conv]) + (conv/ratio->cents (interval 187/128 7/4)) + (conv/ratio->cents 6/5) + (get-beat-data-by-pairs (range 1 7) [5/4])) + +(defn get-beat-data + "Using a root note, map the beat-data over the a range from 20 to 4000hz" + [period root partials ratios] + (let [beat-data (get-beat-data-by-pairs partials ratios) + lowest-root (get-root-note-lowest-freq period root) + root-periods-freqs (get-freq-periods-range period lowest-root max-freq) + pair->beat-data (->> beat-data + (map + (fn [pair] + (->> root-periods-freqs + (map + (fn [period root-freq] + (let [k (keyword (str "diff-period-" period))] + ;; (println (:diff pair)) + #_(double) (* root-freq (:diff pair)))) + (range)) + #_(into {}) + (assoc pair + :root-hz lowest-root + :beat-hz-by-period)))) + (group-by (comp set #(map :ratio %) :pair)) + (map (fn [[k v]] + [k (sort-by (juxt + (comp :partial first :pair) + (comp :partial second :pair)) + v)])) + (into {}) + #_(make-map-by-key (comp set #(map :ratio %) :pair))) + ratio-pairs (->> (keys pair->beat-data) + (sort-by (juxt first second)))] + (mapcat + (fn [period root] + (mapcat + (fn [pair] + (->> (pair->beat-data pair) + (keep (fn [beat-data] + (let [data ((juxt (comp (juxt :partial :degree) first :pair) + (comp (juxt :partial :degree) second :pair) + :beat-hz-by-period) + beat-data) + [[pr1 deg1] [pr2 deg2] beats-by-period] data + beats (nth beats-by-period period) + [r1 r2] (sort pair)] + (when (< beats max-beat-freq) + {:period period + :root-hz (exact.utils/->native root) + :root (cps->name* root) + :degree-1 deg1 + :degree-2 deg2 + :ratio-1 r1 + :ratio-2 r2 + :ratio-1-partial pr1 + :ratio-2-partial pr2 + :beat-freq.ratio beats + :beat-freq.hz (float (exact.utils/->native beats)) + :beat-freq.factors (factorize-ratio beats)})))))) + ratio-pairs)) + (range) + root-periods-freqs))) + +(comment + ;; degrees 9&14 harmonics 6 & 5 should beat at 1hz + (->> (get-beat-data (exact.utils/->exact 2) + (exact.utils/->exact 1) + (map exact.utils/->exact (range 1 7)) + [67/64 + 279/256 + 9/8 + 75/64 + 39/32 + 5/4 + 167/128 + 87/64 + 45/32 + 187/128 + 3/2 + 25/16 + 417/256 + 27/16 + 7/4 + 233/128 + 15/8 + 125/64 + 2/1]) + (filter (fn [{:keys [period degree-1 degree-2]}] + (let [degs #{9 14}] + (and + (= 1 period) + (degs degree-1) + (degs degree-2))))) + #_#_(map :beat-freq.hz) + sort) + (exact.utils/make-readable (take 10 (get-beat-data (exact.utils/->exact 2) + (exact.utils/->exact 1) + (map exact.utils/->exact (range 1 6)) + ratios))) + (count (exact.utils/make-readable (get-beat-data (exact.utils/->exact 2) + (exact.utils/->exact 1) + (map exact.utils/->exact (range 1 6)) + (parse-ratios "1 5/4 3/2"))))) diff --git a/src/erv/constant_structures/brute_force.cljc b/src/erv/constant_structures/brute_force.cljc index 45e457b..7ea3eb8 100644 --- a/src/erv/constant_structures/brute_force.cljc +++ b/src/erv/constant_structures/brute_force.cljc @@ -2,7 +2,7 @@ "Find constant structures by brute force" (:require [clojure.math.combinatorics :refer [combinations]] - [erv.constant-structures.core :refer [analyze maybe-round]] + [erv.constant-structures.core :refer [analyze maybe-rationalize]] [erv.utils.conversions :as conv] [erv.utils.core :refer [interval]] [erv.utils.ratios :refer [ratios->scale]] @@ -92,7 +92,7 @@ (let [[deg1 deg2] (sort deg-pair) a (:bounded-ratio (nth scale deg1)) b (:bounded-ratio (nth scale deg2))] - (maybe-round (interval a b)))) + (maybe-rationalize (interval a b) 10))) (def ^:private memoized-deg-combinations (memoize (comp #(map sort %) @@ -102,22 +102,35 @@ (def ^:private conj-set (fnil conj #{})) +(defn- invert-interval [interval period] + (* period (/ 1 interval))) + (defn- quick-check-cs? [deg-combinations scale] - (reduce - (fn [data deg-pair] - (let [[deg1 deg2] deg-pair - steps (- deg2 deg1) - interval (get-interval scale deg-pair) - updated-data (update data interval conj-set steps) - interval-steps (updated-data interval)] - (if (> (count interval-steps) 1) - (reduced {}) - updated-data))) - {} - deg-combinations)) + (let [period (-> scale first :bounding-period) + size (count scale)] + (reduce + (fn [data deg-pair] + (let [[deg1 deg2] deg-pair + steps (- deg2 deg1) + interval (get-interval scale deg-pair) + inversion-steps (- size steps) + inversion (invert-interval interval period) + updated-data (-> data + (update interval conj-set steps) + (update inversion conj-set inversion-steps)) + interval-steps (updated-data interval)] + (when (> inversion period) + (timbre/error "Error in calculation, `inversion` cannot be larger than period")) + (if (> (count interval-steps) 1) + (reduced {}) + updated-data))) + {} + deg-combinations))) (defn quick-cs-subsets + "Calculate all CS subset of the given sizes. + FIXME only really works for JI scales." [cs-sizes scale] (eduction (mapcat #(combinations (range (count scale)) %)) @@ -128,7 +141,9 @@ (when (seq (quick-check-cs? deg-pairs subscale)) (map #(nth scale %) degs-combination))))) cs-sizes)) - +(comment + (require '[clojure.math.combinatorics :as combo]) + (combo/count-combinations (range 31) 22)) (defn take-quick-cs-subsets "Takes n-items after dropping n-items. Takes an `eduction` as returned by `quick-cs-subsets`. NOTE this code is a general solution, but has specific names for documentation purposes. diff --git a/src/erv/constant_structures/core.cljc b/src/erv/constant_structures/core.cljc index 540548b..7d664f3 100644 --- a/src/erv/constant_structures/core.cljc +++ b/src/erv/constant_structures/core.cljc @@ -1,34 +1,61 @@ (ns erv.constant-structures.core "A constant structure is a scale where every interval is subtended by the same number of steps." + #?(:cljs (:refer-clojure :exclude [* / <])) (:require + #?(:cljs [com.gfredericks.exact :as e :refer [/ * <]]) [clojure.math.combinatorics :as combo] [erv.cps.core :as cps] - [erv.edo.core :as edo] - [erv.utils.core :refer [interval round2]])) + [erv.utils.core :refer [interval round2]] + [erv.utils.exact :as exact.utils])) (defn maybe-round [n] #?(:clj (if (rational? n) n (round2 6 n)) + :cljs (if (e/ratio? n) + n + (round2 6 n)))) + +(defn maybe-rationalize + [n decimals] + #?(:clj (if (rational? n) + n + (rationalize (round2 decimals n))) + ;; TODO implement :cljs (round2 6 n))) +(defn- invert-interval + [period intvl] + (* (exact.utils/->exact period) (/ #?(:clj 1 :cljs e/ONE) intvl))) + + +(comment + (def scale (map-indexed #(assoc %2 :index %1) (:scale (cps/make 2 [11 13 5 7])))) + (get-intervals 6 (take 2 scale)) + (maybe-round (interval (:bounded-ratio (second (take 2 scale))) + (:bounded-ratio (first (take 2 scale))))) + (invert-interval 2 (interval (:bounded-ratio (second (take 2 scale))) + (:bounded-ratio (first (take 2 scale)))))) (defn get-intervals - [note-pair] + [scale-size note-pair] (->> note-pair ((fn [[a b]] - {(maybe-round (interval (:bounded-ratio a) - (:bounded-ratio b))) - {:steps #{(- (:index b) - (:index a))} - :intervals [[a b]]}})))) + (let [period (:bounding-period a) + intvl (maybe-round (interval (:bounded-ratio a) + (:bounded-ratio b))) + inversion (invert-interval period intvl)] + {intvl {:steps #{(- (:index b) (:index a))} + :intervals [[a b]]} + inversion {:steps #{(- scale-size (- (:index b) (:index a)))} + :intervals [[b a]]}}))))) (defn analyze [scale] (let [interval-data (->> (combo/combinations (map-indexed #(assoc %2 :index %1) scale) 2) - (map get-intervals) + (map (partial get-intervals (count scale))) (apply merge-with (partial merge-with concat)) (map (fn [[interval data]] {interval (update data :steps (partial into #{}))})) @@ -48,7 +75,7 @@ (map :bounded-ratio pair)})))])) - (sort-by first)) + (sort-by first <)) non-cs-intervals (->> interval-data (filter (fn [[_interval {:keys [steps]}]] (> (count steps) 1))))] @@ -63,32 +90,32 @@ #_(:constant-structure? (analyze (:scale (cps/make 2 [11 13 5 7])))) -(:constant-structure? (analyze - (:scale (cps/make 2 [1 3 5 7 9])))) -(:constant-structure? (analyze - (:scale (edo/from-pattern [2 2 1 2 2 2 1])))) +#_(:constant-structure? (analyze + (:scale (cps/make 2 [1 3 5 7])))) +#_(:constant-structure? (analyze + (:scale (edo/from-pattern [2 2 1 2 2 2 1])))) #_(:non-cs-intervals (analyze (:scale (cps/make 3 (map #(* 1 %) [1 3 7 9 11 15]) :norm-fac (* 1 9 11))))) -(:non-cs-intervals (analyze +#_(:non-cs-intervals (analyze ;; taken from http://anaphoria.com/Wilson1-3-7-9-11-15x3_pascal_eikosany.scl ;; See also tieminos.learning.cps-lumatone.analysis.1-3-7-9-11-15 - (:scale - (-> (cps/make 3 (map #(* 1 %) [1 3 7 9 11 15]) - :norm-fac (* 1 9 11)) - (update :scale - (comp - #(map-indexed (fn [i n] (assoc n :index i)) %) - #(sort-by :bounded-ratio %) - concat) - [#_{:set #{4/3 7 15} - :ratio 140/99 - :bounded-ratio 140/99 - :bounding-period 2} - #_{:set #{11 9} - :set-vecs [[11 9 9] [11 9 3 3]] - :ratio 9 - :bounded-ratio 18/16 - :bounding-period 2}]))))) + (:scale + (-> (cps/make 3 (map #(* 1 %) [1 3 7 9 11 15]) + :norm-fac (* 1 9 11)) + (update :scale + (comp + #(map-indexed (fn [i n] (assoc n :index i)) %) + #(sort-by :bounded-ratio %) + concat) + [#_{:set #{4/3 7 15} + :ratio 140/99 + :bounded-ratio 140/99 + :bounding-period 2} + #_{:set #{11 9} + :set-vecs [[11 9 9] [11 9 3 3]] + :ratio 9 + :bounded-ratio 18/16 + :bounding-period 2}]))))) #_(< 12/11 19/17 7/6) diff --git a/src/erv/constant_structures/graphics.cljc b/src/erv/constant_structures/graphics.cljc index 6711336..b9f483a 100644 --- a/src/erv/constant_structures/graphics.cljc +++ b/src/erv/constant_structures/graphics.cljc @@ -83,7 +83,6 @@ (q/arc 0 0 (+ i* (- radius 100)) (+ i* (- radius 100)) start end)))))) ;; Draw a circle at x y with the correct diameter - (defn make-state [scale added-notes] (let [scale+added-notes* (scale+added-notes scale added-notes) diff --git a/src/erv/cps/core.cljc b/src/erv/cps/core.cljc index 2e70219..62bcff7 100755 --- a/src/erv/cps/core.cljc +++ b/src/erv/cps/core.cljc @@ -1,14 +1,23 @@ (ns erv.cps.core ;; TODO use https://github.com/Engelberg/ubergraph for the graphs + #?(:cljs (:refer-clojure :exclude [> < >= <= = + - * / numerator denominator integer? + mod rem quot even? odd?])) + #_(:require + #?(:cljs [com.gfredericks.exact :as e :refer [* + - - /]]) + [clojure.string]) (:require + #? (:cljs [goog.string :as gstr]) + #? (:cljs [goog.string.format]) + #?(:cljs [com.gfredericks.exact :as e :refer [> < >= <= = + - * /]]) + #?(:cljs [erv.utils.exact :as exact.utils]) + [clojure.core :as core] [clojure.math.combinatorics :as combo] [clojure.set :as set] [clojure.spec.alpha :as s] [clojure.string :as str] [clojure.walk :as walk] - [erv.utils.core :refer [interval validate]] - #? (:cljs [goog.string :as gstr]) - #? (:cljs [goog.string.format]))) + [erv.utils.core :refer [validate period-reduce]] + [erv.utils.scale :refer [+degree]])) #?(:cljs (def format gstr/format)) @@ -24,14 +33,14 @@ :graph :some-graph}}) (s/def ::cps (s/and (s/coll-of set? :distinct true) - #(->> % (map count) (apply =)))) + #(->> % (map count) (apply core/=)))) (s/def ::sub-cps-set (s/coll-of ::cps :distinct true)) (defn- num->kw [n] - (-> (char (+ 65 n)) str str/lower-case keyword)) + (-> (char (core/+ 65 n)) str str/lower-case keyword)) (defn ->cps [size factors] - (if (> size (count factors)) + (if (core/> size (count factors)) #{#{}} (with-meta (->> (combo/combinations (into [] factors) size) @@ -60,20 +69,7 @@ (map (fn [pair] {:set pair :archi-set (set (map archi-factors pair))}) cps-set))) -(defn within-bounding-period - "Transposes a ratio withing a bounding-period. - The octave is a `bounding-period` of 2,the tritave of 3, etc." - [bounding-period ratio] - {:pre [(> bounding-period 1)]} - (loop [r ratio] - (cond - (> r bounding-period) (recur (/ r bounding-period)) - (< r 1) (recur (* r bounding-period)) - (= bounding-period r) 1 - :else r))) - -#_(within-bounding-period 2 1/21) - +;; TODO: can this be replaced with the function at utils? (defn bound-ratio ;;; TODO, how to be able to specify multiple bounding-periods "Calculate all the ratios within the bounding-period (e.g. octave, tritave, etc.) @@ -82,20 +78,22 @@ i.e. for a cps with factors [1 3 5 7] you can use either 3, 5, or 7" ([bounding-period cps-map] (bound-ratio bounding-period 1 cps-map)) ([bounding-period normalization-generator cps-map] - (->> cps-map - (map (fn [node*] - (let [ratio (/ (apply * (node* :set)) normalization-generator)] - (if bounding-period;; TODO test `nil` `bounding-period` with sub-cps - (assoc node* - :ratio ratio - :bounded-ratio (within-bounding-period - bounding-period - ratio) - :bounding-period bounding-period) - (assoc node* - :ratio ratio - :bounded-ratio ratio - :bounding-period nil)))))))) + (let [norm-gen #?(:clj normalization-generator + :cljs (exact.utils/parse-ratio (str normalization-generator)))] + (->> cps-map + (map (fn [node*] + (let [set* #?(:clj (node* :set) + :cljs (map e/native->integer (node* :set))) + ratio (/ (apply * set*) norm-gen)] + (if bounding-period ;; TODO test `nil` `bounding-period` with sub-cps + (assoc node* + :ratio ratio + :bounded-ratio (period-reduce bounding-period ratio) + :bounding-period bounding-period) + (assoc node* + :ratio ratio + :bounded-ratio ratio + :bounding-period nil))))))))) (defn add-edge ;; TODO use https://github.com/Engelberg/ubergraph for the graphs @@ -114,8 +112,8 @@ (let [rest* (rest nodes) current-node (first nodes) edges (->> rest* - (filter #(= (dec (count (:set %))) (count (set/intersection (:set current-node) - (:set %))))) + (filter #(core/= (dec (count (:set %))) (count (set/intersection (:set current-node) + (:set %))))) (map #(conj [] current-node %)))] (recur rest* (reduce @@ -164,7 +162,7 @@ (defn find-subcps [cps-set-size factors sub-cps-set-size subcps-factors-size] (let [base-cps (->cps subcps-factors-size factors) - diff-set-size (Math/abs (- cps-set-size sub-cps-set-size)) + diff-set-size (Math/abs (core/- cps-set-size sub-cps-set-size)) gens-set (set factors) meta* {::type (str sub-cps-set-size ")" subcps-factors-size " of " cps-set-size ")" (count factors))}] (->> base-cps (map @@ -333,7 +331,8 @@ set->maps (bound-ratio period norm-fac) (maps->data :bounded-ratio) - (+meta size factors norm-fac))) + (+meta size factors norm-fac) + (#(update % :scale +degree)))) (defn +subcps [cps-data set-size factors-size] (let [{:keys [cps/size cps/factors period cps/normalized-by]} (:meta cps-data)] @@ -350,8 +349,9 @@ [cps-size cps-row] (->> (range 1 cps-row) (map (fn [row] - [row (range (max 1 (+ cps-size (- row cps-row))) + [row (range (max 1 (core/+ cps-size (core/- row cps-row))) (inc (min row cps-size)))])))) + (defn +all-subcps-row [cps-data [row cps-sizes]] (reduce (fn [cps-data* set-size] (+subcps cps-data* set-size row)) diff --git a/src/erv/cps/cycles.cljc b/src/erv/cps/cycles.cljc index 9a66274..956ec57 100644 --- a/src/erv/cps/cycles.cljc +++ b/src/erv/cps/cycles.cljc @@ -2,8 +2,6 @@ (:require [clojure.set :as set] [erv.utils.core :as utils])) - - (defn get-next-nodes [graph cycle] (->> (graph (-> cycle :seq first)) (reduce @@ -33,13 +31,11 @@ :status :open}))) ()))) - (defn update-finder-state [previous-state new-interation-result] (let [{:keys [open closed]} (group-by :status new-interation-result)] {:open open :closed (into (:closed previous-state) closed)})) - (defn init-state [graph] {:open (mapv (fn [node] {:seq (list node) @@ -73,9 +69,8 @@ (if (-> acc :set (contains? cycle*)) acc {:set (apply conj (:set acc) (utils/get-all-rotations cycle*)) - :cycles (conj (:cycles acc) cycle*)} - )) - ) + :cycles (conj (:cycles acc) cycle*)}))) + {:set #{} :cycles []} cycles))))) (comment @@ -152,6 +147,4 @@ {:seq (#{7 5} #{1 3} #{7 1}), :set #{#{7 1} #{7 5} #{1 3}}, :status :open} {:seq (#{7 3} #{1 3} #{7 1}), :set #{#{7 1} #{7 3} #{1 3}}, :status :open} {:seq (#{1 5} #{1 3} #{7 1}), :set #{#{7 1} #{1 5} #{1 3}}, :status :open} - {:seq (#{1 3} #{7 1}), :set #{#{7 1} #{1 3}}, :status :closed})) - - ) + {:seq (#{1 3} #{7 1}), :set #{#{7 1} #{1 3}}, :status :closed}))) diff --git a/src/erv/cps/cycles/v2.cljc b/src/erv/cps/cycles/v2.cljc index db5c13a..3bba02d 100644 --- a/src/erv/cps/cycles/v2.cljc +++ b/src/erv/cps/cycles/v2.cljc @@ -170,12 +170,13 @@ (str/join " - "))) (str/join "\n") (spit "eikosany-harmonic-triad-cycles-of-A.B.C.txt"))) +(comment -(->> hexany - :meta - :cps/factors - (map-indexed - (fn - [i fac] - {fac (str (char (+ 65 i)))})) - (into {})) + (->> hexany + :meta + :cps/factors + (map-indexed + (fn + [i fac] + {fac (str (char (+ 65 i)))})) + (into {}))) diff --git a/src/erv/cps/similarity.clj b/src/erv/cps/similarity.clj index 86a469f..4489e8c 100644 --- a/src/erv/cps/similarity.clj +++ b/src/erv/cps/similarity.clj @@ -8,8 +8,6 @@ [erv.utils.core :as utils] [clojure.string :as str])) - - (defn twelvulate [scale] (map #(-> % (/ 100) float (Math/round) (* 100)) scale)) @@ -42,7 +40,6 @@ 813.6862861351651 933.1290943962624)) - (defn +cents [cps] (assoc cps :cents (->> cps :scale @@ -83,11 +80,10 @@ %1 %2))))) (+euclidean-distance {:cents '(0 204 316 519 702 1018)}) -(sort (map #(-> % (- 182) (mod 1200)) '(0 182 386 498 701 884) )) +(sort (map #(-> % (- 182) (mod 1200)) '(0 182 386 498 701 884))) (defn +gens [factors cps] (assoc cps :factors factors)) - (comment (require '[clojure.math.combinatorics :as combo] ;; '[clojure.data.csv :as csv] @@ -122,9 +118,9 @@ (with-open [writer (io/writer "3oo7-similarity-to-12edo-scales-up-to-23.csv")] (csv/write-csv writer (->> #_cps-sorted-by-euclidean-distance - #_cps-sorted-by-euclidean-distance-up-to-53 - cps-sorted-by-euclidean-distance-up-to-23 - (mapv (juxt :factors :mode :cents :closest-12-edo :euclidean-distance )) + #_cps-sorted-by-euclidean-distance-up-to-53 + cps-sorted-by-euclidean-distance-up-to-23 + (mapv (juxt :factors :mode :cents :closest-12-edo :euclidean-distance)) (mapv (fn [data] (mapv #(cond (= java.lang.Long (type %)) % diff --git a/src/erv/cps/utils.cljc b/src/erv/cps/utils.cljc index 58552f1..b05977c 100644 --- a/src/erv/cps/utils.cljc +++ b/src/erv/cps/utils.cljc @@ -52,8 +52,7 @@ {}))) (comment - (make-degree->note (erv.cps.core/make 2 [1 3 5 7])) - ) + (make-degree->note (erv.cps.core/make 2 [1 3 5 7]))) (defn make-set->degrees-map [{:keys [scale] :as _cps}] @@ -68,7 +67,6 @@ (reduce (fn [m [deg set]] (update m deg (fnil conj #{}) set)) {}))) - (defn- set-d1-intersection? [set1 set2] (= 1 (count (set/difference set1 set2)))) @@ -86,7 +84,7 @@ (map #(set (conj % degree-set))) (filter #(->> (combo/combinations % 2) (map (partial apply set-d1-intersection?)) - (every? true?)) ) + (every? true?))) set))) (comment @@ -94,7 +92,6 @@ 4 0))) - (defn harmonic-set-degrees "Returns a list of harmonic sets (as degrees) for a specific degree of a cps A harmonic set is a set where all notes are connected with any other by all of its factors except one." @@ -104,16 +101,16 @@ (sort (map (comp #(into [] %) sort (partial map set->degrees)) - sets))))= + sets)))) = (comment (= - [#{#{#{7 5} #{3 5} #{7 3}} - #{#{7 5} #{3 5} #{1 5}} - #{#{7 1} #{7 5} #{7 3}} - #{#{7 1} #{7 5} #{1 5}}} + [#{#{#{7 5} #{3 5} #{7 3}} + #{#{7 5} #{3 5} #{1 5}} + #{#{7 1} #{7 5} #{7 3}} + #{#{7 1} #{7 5} #{1 5}}} '((0 1 4) (0 1 5) (0 2 4) (0 2 5))] - (let [cps (erv.cps.core/make 2 [1 3 5 7]) - set-size 3] - [(harmonic-sets cps set-size 0) - (harmonic-set-degrees cps set-size 0)]))) + (let [cps (erv.cps.core/make 2 [1 3 5 7]) + set-size 3] + [(harmonic-sets cps set-size 0) + (harmonic-set-degrees cps set-size 0)]))) diff --git a/src/erv/edo/core.cljc b/src/erv/edo/core.cljc index 6e4c373..1de425e 100644 --- a/src/erv/edo/core.cljc +++ b/src/erv/edo/core.cljc @@ -9,27 +9,6 @@ (defn pattern->degrees [pattern] (->> pattern (drop-last 1) (reduce #(conj %1 (+ (last %1) %2)) [0]))) -(comment - (require - '[erv.cps.core :as cps] - '[erv.utils.conversions :as conv] - '[erv.scale.core :refer [demo!]] - '[erv.mos.submos :refer [make-all-submos]] - '[erv.mos.mos :refer [make-mos]]) - - (map conv/ratio->cents (edo-ratios (* 2 3 5 7))) - - (def mos (make-mos 31 5)) - (-> mos) - (def submosi (make-all-submos (mos 6) 5)) - (do) - (def submos) (-> submosi #_(->> (filter :true-submos?)) - #_ #_ (nth 0) :submos - #_ #_ (nth 1) :mos) - (demo! (:scale (from-pattern submos)) :note-dur 200 :direction :down) - (demo! (:scale (from-pattern [3,5,2,5,3,5,4])) :note-dur 200 :direction :down) - (demo! (:scale (from-pattern[6, 3, 4, 3, 7, 4, 3, 1] 2)) :note-dur 200 :direction :up )) - (defn from-pattern "For use with `mos` patterns or other custom intervalic patterns, i.e. [3 2 3 2 2]" ([pattern] (from-pattern pattern 2)) @@ -46,6 +25,3 @@ :bounded-ratio (nth edo degree) :bounding-period period}) degrees)}))) - - -(from-pattern [ 2, 2, 5, 2, 5, 2, 5, 2, 2, 5, 2, 5, 2, 5, 2, 5]) diff --git a/src/erv/lattice/v2.cljc b/src/erv/lattice/v2.cljc index 3bfe26c..e38456d 100644 --- a/src/erv/lattice/v2.cljc +++ b/src/erv/lattice/v2.cljc @@ -1,6 +1,9 @@ (ns erv.lattice.v2 + #?(:cljs (:refer-clojure :exclude [/ +])) (:require [erv.utils.core :refer [period-reduce]] + #?(:cljs [com.gfredericks.exact :as e :refer [/ +]]) + [erv.utils.exact :as exact.utils] [erv.utils.ratios :refer [analyze-ratio]])) (def base-coords @@ -17,8 +20,8 @@ (defn make-coords [base-coords numerator-factors denominator-factors] (let [numer-coords (reduce (fn [{:keys [x y]} factor] - {:x (+ x (get-in base-coords [factor :x])) - :y (+ y (get-in base-coords [factor :y]))}) + {:x (clojure.core/+ x (get-in base-coords [factor :x])) + :y (clojure.core/+ y (get-in base-coords [factor :y]))}) {:x 0 :y 0} numerator-factors)] (reduce (fn [{:keys [x y]} factor] @@ -37,9 +40,6 @@ :denom-factors denom-factors :coords (make-coords base-coords numer-factors denom-factors)}})) -(comment - (ratio->lattice-point)) - (defn get-point-data-difference "`factor-type` should be `:numer-factors` or `:denom-factors`" [period point-data1 point-data2 factor-type] @@ -85,17 +85,18 @@ (defn make-connection [diff-count-set connections-set period point-data1 point-data2 custom-edges] - #_(println period point-data1 point-data2) (let [diffs (partial get-point-data-difference period point-data1 point-data2) - get-diff-count (comp #(apply + %) vals) + get-diff-count (comp #(apply clojure.core/+ %) vals) num-diff (diffs :numer-factors) denom-diff (diffs :denom-factors) - diff (diff-count-set (+ (get-diff-count num-diff) - (get-diff-count denom-diff))) + diff (diff-count-set (clojure.core/+ (get-diff-count num-diff) + (get-diff-count denom-diff))) + custom? (custom-connection? period point-data1 point-data2 custom-edges)] + (if (or diff custom?) (let [points #{(:ratio point-data1) (:ratio point-data2)}] @@ -186,7 +187,8 @@ & {:keys [custom-edges period] :or {custom-edges #{} period 2}}] - (let [coords-data-map (->> ratios + (let [period (exact.utils/->exact period) + coords-data-map (->> ratios (map #(ratio->lattice-point % base-coords)) (into {})) coords-data (vals coords-data-map) @@ -211,6 +213,21 @@ :data coords-data :edges edges}))) +(comment + (exact.utils/make-readable (ratios->lattice-data base-coords + (exact.utils/parse-ratios "3/2 9/8 2/1"))) + (ratios->lattice-data base-coords #_["1/1" "15/14" "5/4" "10/7" "3/2" "12/7"] + (map exact.utils/parse-ratio ["1/1" + "80/77" + "12/11" + "8/7" + "96/77" + "10/7" + "16/11" + "120/77" + "12/7" + "20/11"]))) + (defn swap-coords [coords coord-pairs] (reduce (fn [coords [prime1 prime2]] diff --git a/src/erv/math/pascals_triangle.clj b/src/erv/math/pascals_triangle.clj deleted file mode 100644 index 99de89a..0000000 --- a/src/erv/math/pascals_triangle.clj +++ /dev/null @@ -1,12 +0,0 @@ -(ns erv.math.pascals-triangle) - -(defn make [size] - (reduce (fn [acc _] - (conj acc - (concat [1] - (mapv #(apply + %) (partition 2 1 (last acc))) - [1]))) - [[]] - (range size))) - -(defn row [n] (last (make n))) diff --git a/src/erv/math/pascals_triangle.cljc b/src/erv/math/pascals_triangle.cljc index 99de89a..1492a58 100644 --- a/src/erv/math/pascals_triangle.cljc +++ b/src/erv/math/pascals_triangle.cljc @@ -1,12 +1,49 @@ (ns erv.math.pascals-triangle) -(defn make [size] - (reduce (fn [acc _] - (conj acc - (concat [1] - (mapv #(apply + %) (partition 2 1 (last acc))) - [1]))) - [[]] - (range size))) +(defn make + ([size] (make 1 1 size)) + ([seed-l seed-r size] + (reduce (fn [acc _] + (->> (concat [seed-l] + (mapv #(apply + %) + (into [] (partition 2 1 (last acc)))) + [seed-r]) + (map #?(:clj bigint :cljs js/BigInt)) + (into []) + (conj acc))) + [[seed-r]] ;; seed-r is privileged so that it works well for calculating the meru diagonals (as shown in merutwo.pdf) + (range size)))) + +(comment + (make 100)) (defn row [n] (last (make n))) + +(defn factorial [x] + (apply * (map #?(:clj bigint :cljs js/BigInt) + (range 1 (inc x))))) + +(defn default-coord-map + "Calculates a point the pascal's triangle based on an `x,y` coordinate. + Taken from Thomas M. Green's Recurrent Sequences and Pascal's Triangle (referred in meruone.pdf)." + [[x y]] + (/ (factorial (+ x y)) + (* (factorial x) (factorial y)))) + +(defn- pascal-coordinates + [size] + (->> (range size) + (mapv + (fn [size*] + (->> (range 0 (inc size*)) + (map + (fn [i] [(- size* i) i]))))))) + +(defn make-coord-map + "Returns a `hash-map` that maps between a pascal coordinate (a [pos-int? pos-int?] vector) and the corresponding pascal-number. Works the same as `default-coord-map` (except for the row `size` constraint) but works for custom seeded pascal triangles. " + [seed-l seed-r size] + (with-meta (->> (map vector + (apply concat (pascal-coordinates size)) + (apply concat (make seed-l seed-r size))) + (into {})) + {:triangle-seed {:left seed-l :right seed-r}})) diff --git a/src/erv/meru/core.clj b/src/erv/meru/core.clj index 61ed3cb..5e0d8ad 100644 --- a/src/erv/meru/core.clj +++ b/src/erv/meru/core.clj @@ -1,140 +1,89 @@ (ns erv.meru.core - (:require [clojure.math.combinatorics :as combo] - [erv.cps.core :refer [within-bounding-period]] - [erv.constant-structures.graphics :as sketch])) + (:require + [clojure.math.combinatorics :as combo] + [erv.cps.core :refer [within-bounding-period]] + [erv.meru.diagonals] + [erv.meru.recurrent-series] + [erv.mos.v3.core :refer [gen->mos-ratios]] + [erv.utils.core :refer [round2]])) -(defn seq-ratios* [recurrent-seq] - (->> recurrent-seq - (partition 2 1) - (map (fn [[a b]] (/ b a))))) +(def recurrent-series #'erv.meru.recurrent-series/recurrent-series) -(defn seq-ratios [recurrent-seq] - (->> recurrent-seq - (partition 2 1) - (map (fn [[a b]] (double (/ b a)))))) +(def diagonals #'erv.meru.diagonals/diagonals) -(declare converges-at) -(defn converges-at [recurrent-seq & {:keys [ignore-first] - :or {ignore-first 0}}] - (->> recurrent-seq - (drop ignore-first) - seq-ratios - (partition 5 1) - (take-while (fn [ns] (apply not= ns))) - count - (+ ignore-first))) +(defn convergence-mos-data + ([convergence-double] (convergence-mos-data {} convergence-double)) + ([{:keys [period max-size] + :or {period 2 max-size 100}} + convergence-double] + (->> (gen->mos-ratios (rationalize (round2 3 convergence-double)) period max-size) + (map :meta)))) -(def scale-formulas - {:fibonacci {:i1 1 :i2 2 :f +} - :meta-pelog {:i1 1 :i2 3 :f +} - :meta-slendro {:i1 2 :i2 3 :f +}}) - -(defn recurrent-series - "Creates a recurrent integer sequence and some data associated to it. - Config: - `:seed` A sequence of intergers to start the recurrent sequence. - `:formula` A keyword that should be contained in `scale-formulas`. It automatically provides the arguments below, so can be used in place of these. - In case no `:formula` is used: - `:i1` The lowest index in the formula. - If this is confusing, read below. - `:i2` The next index in the formula. - `:f` The function to apply to these indexes (probably always, it will be +) - - - For example on page 40 of https://anaphoria.com/merufour.pdf there is the Meta-Slendro formula: - Hn-3 + Hn-2 = Hn - TODO this may be mixed up... but the test function with the `:meta-slendro` `:formula` does works.... so review is needed - `:i1` corresponds to 3, taken from Hn-3 - `:i2` corresponds to 2, taken from Hn-2." - [{:keys [seed formula] :as config}] - (let [config* (get scale-formulas formula config) - {:keys [i1 i2 f] :or {f +}} config* - seed* (mapv bigint seed) - _ (when (> i2 (count seed)) - (throw (ex-info "The `seed` size must be equal or greater than `i1`" config*))) - _ (when (>= i1 i2) - (throw (ex-info "`i2` must be greater than `i1`" config*))) - series (loop [seq* seed* - a (first (take-last i1 seed)) - b (first (take-last i2 seed))] - (let [seq** (conj seq* (f a b)) - a* (first (take-last i1 seq**)) - b* (first (take-last i2 seq**))] - (if (apply = (seq-ratios (take-last 6 seq**))) - seq** - (recur seq** a* b*))))] - {:convergence-double (last (seq-ratios series)) - :convergence (last (seq-ratios* series)) - :converges-at (converges-at series) - :series series})) - -(comment - (recurrent-series #_{:seed [1 1 1] - :i1 2 - :i2 3 - :f (fn [a b] (+ a b))} - {:seed [1 1 1] - :formula :meta-slendro})) - -(defn within-period [period seq*] - (let [max* (apply max seq*) - min* (/ max* period)] - (map (fn [n] - (if (> min* n) - (* n (int (Math/ceil (/ min* n)))) - n)) - seq*))) +(defn convergence-mos-data-summary + ([meru-diagonals-or-series-data] (convergence-mos-data-summary {} meru-diagonals-or-series-data)) + ([{:keys [_period _max-size] :as calc-config} + {:keys [convergence-double] :as _meru-diagonals-or-series-data}] + (->> convergence-double + (convergence-mos-data calc-config) + (map (fn [meta] + (select-keys meta [:size + :mos/pattern.name + :mos/sL-ratio.float + :mos/s.cents + :mos/L.cents])))))) (comment (do - (def test1 - (let [seed [1 1 1] - period 2] - (->> (recurrent-series (mapv bigint seed) - :i1 3 - :i2 2 - :f (fn [a b] (+ a b))) - (partition 9 1) - (map (fn [seq*] - (let [seq** (sort (set (map (partial within-bounding-period period) - seq*))) - indexed-seq (->> seq** - (map-indexed (fn [i x] {x i})) - (apply merge)) - min* (/ (apply max seq**) 2)] - (->> seq** - (#(combo/combinations % 3)) - (reduce (fn [acc ns] - (let [diffs (->> ns - sort - (partition 2 1) - (map (fn [[a b]] (- b a))))] - (if (= 1 (count (set diffs))) - (update acc :proportional-triads - conj {:ratios ns - :degrees (->> (map indexed-seq ns)) - :diff (first diffs)}) - acc))) - {:meta {:scale :meru - :period period - :seed seed - :size (count seq**)} - :scale (map (fn [r] - {:ratio r - :bounded-ratio (/ r min*) - :bounding-period 2}) - seq**)}) - (#(assoc-in % [:meta :total-triads] (count (:proportional-triads %)))) - (#(assoc-in % [:meta :proportional-triads] (:proportional-triads %))) - (#(dissoc % :proportional-triads)))))) - (remove (comp empty? :proportional-triads :meta))))) + #_(def test1 + (let [seed [1 1 1] + period 2] + (->> (recurrent-series {:seed (mapv bigint seed) + :i1 2 + :i2 3 + :f (fn [a b] (+ a b))}) + (partition 9 1) + (map (fn [seq*] + (let [seq** (sort (set (map (partial within-bounding-period period) + seq*))) + indexed-seq (->> seq** + (map-indexed (fn [i x] {x i})) + (apply merge)) + min* (/ (apply max seq**) 2)] + (->> seq** + (#(combo/combinations % 3)) + (reduce (fn [acc ns] + (let [diffs (->> ns + sort + (partition 2 1) + (map (fn [[a b]] (- b a))))] + (if (= 1 (count (set diffs))) + (update acc :proportional-triads + conj {:ratios ns + :degrees (->> (map indexed-seq ns)) + :diff (first diffs)}) + acc))) + {:meta {:scale :meru + :period period + :seed seed + :size (count seq**)} + :scale (map (fn [r] + {:ratio r + :bounded-ratio (/ r min*) + :bounding-period 2}) + seq**)}) + #_(#(assoc-in % [:meta :total-triads] (count (:proportional-triads %)))) + #_(#(assoc-in % [:meta :proportional-triads] (:proportional-triads %))) + #_(#(dissoc % :proportional-triads)))))) + #_(remove (comp empty? :proportional-triads :meta))))) (def test1 (let [seed [1 1] period 2] - (->> (recurrent-series (mapv bigint seed) - :i1 1 - :i2 2 - ;; :f (fn [a b] (+ a b)) + (->> (recurrent-series {:seed (mapv bigint [1 1]) + :i1 1 + :i2 2} + ;; :f (fn [a b] (+ a b)) ) + :series (partition 21 1) (map (fn [seq*] (let [seq** (sort (set (map (partial within-bounding-period period) @@ -165,15 +114,15 @@ :bounded-ratio (/ r min*) :bounding-period 2}) seq**)}) - (#(assoc-in % [:meta :total-triads] (count (:proportional-triads %)))) - (#(assoc-in % [:meta :proportional-triads] (:proportional-triads %))) - (#(dissoc % :proportional-triads)))))) - (remove (comp empty? :proportional-triads :meta))))) + #_(#(assoc-in % [:meta :total-triads] (count (:proportional-triads %)))) + #_(#(assoc-in % [:meta :proportional-triads] (:proportional-triads %))) + #_(#(dissoc % :proportional-triads)))))) + #_(remove (comp empty? :proportional-triads :meta))))) (->> test1 - (sort-by (comp :size :meta) >) - first - :scale + #_#_#_(sort-by (comp :size :meta) >) + first + :scale #_(map (comp (juxt :size :total-triads) :meta))))) diff --git a/src/erv/meru/diagonals.clj b/src/erv/meru/diagonals.clj new file mode 100644 index 0000000..8281c14 --- /dev/null +++ b/src/erv/meru/diagonals.clj @@ -0,0 +1,123 @@ +(ns erv.meru.diagonals + "Based on: https://www.anaphoria.com/meru.pdf" + (:require + [erv.math.pascals-triangle :as pascals-triangle] + [erv.meru.utils :refer [get-convergence-double-with-precision]] + [erv.mos.v3.core :refer [gen->mos-ratios]] + [erv.utils.core :refer [round2]] + [taoensso.timbre :as timbre])) + +;;;;;;;;;;;;;;;;;;;;;;;; +;; V3 +;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- slope->n-increment ;; TODO rename + [{:keys [x _y] :as _slope}] + (/ 1 x)) + +(defn- safe-division + ([a b] (safe-division 0 a b)) + ([default-val a b] + (if (zero? a) default-val (double (/ b a))))) + +(defn- default-convergence?-fn + [last-10-ratios] + (and (= 10 (count last-10-ratios)) + (apply = last-10-ratios))) + +(defn- decimal-places-convergence?-fn + "Evaluate convergence according to a given number of decimal places in the provided ratios." + [decimal-places last-10-ratios] + (->> last-10-ratios + (map #(if (nil? %) nil (round2 decimal-places %))) + default-convergence?-fn)) + +(decimal-places-convergence?-fn 2 [1.111234 + 1.111235]) + +(defn- convergence-analysis + ([diagonals-series] (convergence-analysis default-convergence?-fn diagonals-series)) + ([convergence?-fn diagonals-series] + (->> diagonals-series + (partition 2 1) + ((fn [parts] + (reduce (fn [{:keys [last-10 convergence-index series-data] :as acc} [a b]] + (let [ratio (safe-division nil (:value a) (:value b))] + (if (convergence?-fn last-10) + (reduced (-> acc + (update :convergence-index - 10) + (assoc :reached-convergence? true))) + (-> acc + (assoc + :series-data (conj series-data (assoc b :ratio-vs-previous ratio)) + :convergence-double ratio + :last-10 (take 10 (conj last-10 ratio)) + :convergence-index (inc convergence-index)))))) + {:convergence-double nil + :convergence-index -1 + :last-10 () + :series-data [(first (first parts))] + :reached-convergence? false} + parts))) + (#(dissoc % :last-10)) + (#(assoc % :series (mapv :value (:series-data %))))))) + +(defn intish? [n] (= n (int n))) + +(defn get-x + "x = (slope-x/slope-y) * (y - n) + NOTE: Multiplied by -1 because the line is assumed to be descending." + [y n slope] + (* -1 (/ (:x slope) (:y slope)) (- y n))) + +(defn get-y + "y = (slope-y/slope-x)*x + n + NOTE: Multiplied by -1 because the line is assumed to be descending." + [x n slope] + (+ n (* x -1 (/ (:y slope) (:x slope))))) + +(defn make-diagonal + "Given the linear formula `y = (slope-y/slope-x)*x + n`, the algorithm + first calculates the crossing at `x` (when `y` is 0). This gives the + range of `x` integer points to check. Given that range use the line + formula to find all `y` points that are also integers. + When both `x` and `y` are integers the coordinate belongs to the pascal diagonal. + `n-inc-size` is the space between each diagonal, and the `diagonal-index` serves to calcualte the resulting diagonal given the `n-inc-size`." + [slope n-inc-size diagonal-index] + (let [n (* diagonal-index n-inc-size) + x-at-y0 (get-x 0 n slope) + x-range (range (-> x-at-y0 int inc))] + (keep (fn [x] (let [y (get-y x n slope)] + (when (intish? y) {:x x :y y}))) + x-range))) + +#_(make-diagonal {:x 1 :y 2} 1 4) + +(defn diagonals + [{:keys [size slope pascal-coord->number convergence?-fn convergence-precision] + :or {pascal-coord->number pascals-triangle/default-coord-map}}] + (when (and convergence-precision convergence?-fn) + (timbre/warn "Both `convergence?-fn` and `convergence-precision` have been provided. The latter is going to be ignored.")) + (let [convergence?-fn (cond + convergence?-fn convergence?-fn + convergence-precision (partial decimal-places-convergence?-fn convergence-precision) + :else default-convergence?-fn) + update-convergence-data (fn [data] + (assoc data + :triangle-seed (if (= pascal-coord->number pascals-triangle/default-coord-map) + {:left 1 :right 1} + (:triangle-seed (meta pascal-coord->number))) + :convergence-precision convergence-precision + :convergence-double-with-precision (get-convergence-double-with-precision + convergence-precision + (:convergence-double data))))] + (->> (range size) + (map #(make-diagonal slope (slope->n-increment slope) %)) + (map (fn [coords] + {:value (->> coords + (map (fn [{:keys [x y]}] + (pascal-coord->number [x y]))) + (apply +)) + :coords (vec coords)})) + (convergence-analysis convergence?-fn) + update-convergence-data))) diff --git a/src/erv/meru/recurrent_series.cljc b/src/erv/meru/recurrent_series.cljc new file mode 100644 index 0000000..f761211 --- /dev/null +++ b/src/erv/meru/recurrent_series.cljc @@ -0,0 +1,78 @@ +(ns erv.meru.recurrent-series + (:require + [erv.meru.utils :refer [get-convergence-double-with-precision]])) + +(defn seq-ratios* [recurrent-seq] + (->> recurrent-seq + (partition 2 1) + (map (fn [[a b]] (/ b a))))) + +(defn seq-ratios [recurrent-seq] + (->> recurrent-seq + (partition 2 1) + (map (fn [[a b]] (double (/ b a)))))) + +(declare converges-at) + +(defn converges-at + "Returns the index at which the recurrent-seq converges." + [recurrent-seq & {:keys [ignore-first] + :or {ignore-first 0}}] + (->> recurrent-seq + (drop ignore-first) + seq-ratios + (partition 5 1) + (take-while (fn [ns] (apply not= ns))) + count + (+ ignore-first))) + +(def scale-formulas + {:fibonacci {:i1 1 :i2 2 :f +} + :meta-pelog {:i1 1 :i2 3 :f +} + :meta-slendro {:i1 2 :i2 3 :f +}}) + +(defn recurrent-series + "Creates a recurrent integer sequence and some data associated to it. + Config: + `:seed` A sequence of intergers to start the recurrent sequence. + `:formula` A keyword that should be contained in `scale-formulas`. It automatically provides the arguments below, so can be used in place of these. + In case no `:formula` is used: + `:i1` The lowest index in the formula. - If this is confusing, read below. + `:i2` The next index in the formula. + `:f` The function to apply to these indexes (probably always, it will be +) + + + For example on page 40 of https://anaphoria.com/merufour.pdf there is the Meta-Slendro formula: + Hn-3 + Hn-2 = Hn + `:i1` corresponds to 2, taken from Hn-2 + `:i2` corresponds to 3, taken from Hn-3." + [{:keys [seed formula _i1 _i2 _f convergence-precision] :as config}] + (let [preset-config (get scale-formulas formula) + config* (or preset-config config) + {:keys [i1 i2 f] :or {f +}} config* + seed* (mapv #?(:clj bigint :cljs js/BigInt) seed) + _ (when (> i2 (count seed)) + (throw (ex-info "The `seed` size must be equal or greater than `i1`" config*))) + _ (when (>= i1 i2) + (throw (ex-info "`i2` must be greater than `i1`" config*))) + series (loop [seq* seed* + a (first (take-last i1 seed)) + b (first (take-last i2 seed))] + (let [seq** (conj seq* (f a b)) + a* (first (take-last i1 seq**)) + b* (first (take-last i2 seq**))] + (if (apply = (seq-ratios (take-last 6 seq**))) + seq** + (recur seq** a* b*)))) + convergence-double (last (seq-ratios series))] + {:seed seed + :preset-formula formula + :convergence-precision convergence-precision + :convergence-double convergence-double + :convergence-double-with-precision (get-convergence-double-with-precision + convergence-precision + convergence-double) + :convergence (last (seq-ratios* series)) + :convergence-index (converges-at series) + :reached-convergence? true + :series series})) diff --git a/src/erv/meru/scratch/beatings.cljc b/src/erv/meru/scratch/beatings.cljc index b303771..be01e4b 100644 --- a/src/erv/meru/scratch/beatings.cljc +++ b/src/erv/meru/scratch/beatings.cljc @@ -12,13 +12,13 @@ [root ratios] (->> ratios (mapcat - (fn [degree ratio] - (map (fn [i] {:degree degree - :ratio ratio - :partial i - :partial-ratio (* i ratio)}) - (range 1 9))) - (range)) + (fn [degree ratio] + (map (fn [i] {:degree degree + :ratio ratio + :partial i + :partial-ratio (* i ratio)}) + (range 1 9))) + (range)) #_sort (#(combo/combinations % 2)) (remove (fn [[x1 x2]] (= (:ratio x1) (:ratio x2)))) @@ -86,7 +86,7 @@ 7/4 57/32 465/256])) - + (->> metameantone-beatings #_(map :diff-c4) #_(remove zero?) @@ -100,9 +100,7 @@ #_(dedupe) #_(map #(/ % (* 1/16 1/64)))) - (->> metaslendro-beatings #_(map (juxt :diff :diff-c4)) #_(dedupe) - (filter #(->> % :pair (map :ratio) set ((fn [%] (% 1))))) - ) + (filter #(->> % :pair (map :ratio) set ((fn [%] (% 1)))))) diff --git a/src/erv/meru/scratch/beatings2.cljc b/src/erv/meru/scratch/beatings2.cljc new file mode 100644 index 0000000..38d03da --- /dev/null +++ b/src/erv/meru/scratch/beatings2.cljc @@ -0,0 +1,357 @@ +(ns erv.meru.scratch.beatings2 + (:require + [clojure.math.combinatorics :as combo] + [clojure.string :as str] + [erv.utils.conversions :refer [cps->name*]] + [erv.utils.core :refer [decompose-ratio factorize-ratio factors->hiccup + make-map-by-key pow prime-factors]] + [erv.utils.ratios :refer [ratios->scale-data]] + [taoensso.timbre :as timbre] + #?(:clj [hiccup2.core :as h]))) + +(comment + (def a 1)) +#_(def c (* 3 11 8)) + +(def c 256 #_(* 3 11 8)) + +(defn get-beat-data-by-pairs + ([ratios] (get-beat-data-by-pairs (range 1 9) ratios)) + ([partials ratios] + (->> ratios + (mapcat + (fn [degree ratio] + (map (fn [i] {:degree degree + :ratio ratio + :partial i + :partial*ratio (* i ratio)}) + partials)) + (range)) + (#(combo/combinations % 2)) + (remove (fn [[x1 x2]] (= (:ratio x1) (:ratio x2)))) + (map (fn [pair] {:pair pair + :diff (abs (- (:partial*ratio (first pair)) + (:partial*ratio (second pair))))})) + (sort-by :diff) + #_(map (fn [pair] + (assoc pair + :diff-c4 (double (* root (:diff pair))) + :diff-c3 (double (/ (* root (:diff pair)) + 2)) + :diff-c2 (double (/ (* root (:diff pair)) + 4)) + :diff-c1 (double (/ (* root (:diff pair)) + 8)))))))) + +(comment + + (def metameantone-beatings) + (get-beat-data-by-pairs [1 + 67/64 + 279/256 + 9/8 + 75/64 + 39/32 + 5/4 + 167/128 + 87/64 + 45/32 + 187/128 + 3/2 + 25/16 + 417/256 + 27/16 + 7/4 + 233/128 + 15/8 + 125/64]) + (get-beat-data-by-pairs [1 + 5/4 + 3/2 + 7/4])) + +(do + (defn- get-root-note-lowest-freq + [period freq] + (let [lowest-freq 20] + (loop + [freq freq] + (cond + (< freq lowest-freq) (recur (* freq period)) + (>= freq (* lowest-freq period)) (recur (/ freq period)) + (and (>= freq lowest-freq) (> (* lowest-freq period) freq)) freq)))) + (get-root-note-lowest-freq 2 20) + (get-root-note-lowest-freq 2 40) + (get-root-note-lowest-freq 2 16)) + +(do + (defn- get-freq-periods-range + [period initial-freq max-freq] + (->> (range) + (map #(* initial-freq (pow period %))) + (take-while #(<= % max-freq)))) + (get-freq-periods-range 2 1 4000)) + +;; TODO refactor to smaller functions and rename + +(defn get-beat-data + "Using a root note, map the beat-data over the a range from 20 to 4000hz" + [period root partials ratios] + (let [max-freq 8000 + beat-data (get-beat-data-by-pairs partials ratios) + lowest-root (get-root-note-lowest-freq period root) + root-periods-freqs (get-freq-periods-range period lowest-root max-freq) + pair->beat-data (->> beat-data + (mapv + (fn [pair] + (->> root-periods-freqs + (mapv + (fn [period root-freq] + (let [k (keyword (str "diff-period-" period))] + #_(double) (* root-freq (:diff pair)))) + (range)) + #_(into {}) + (assoc pair + :root-hz lowest-root + :beat-hz-by-period)))) + (group-by (comp set #(map :ratio %) :pair)) + (mapv (fn [[k v]] + [k (sort-by (juxt + (comp :partial first :pair) + (comp :partial second :pair)) + v)])) + (into {}) + #_(make-map-by-key (comp set #(map :ratio %) :pair))) + ratio-pairs (->> (keys pair->beat-data) + (sort-by (juxt first second)))] + (mapcat + (fn [period root] + (mapcat + (fn [pair] + (->> (pair->beat-data pair) + (keep (fn [beat-data] + (let [data ((juxt (comp (juxt :partial :degree) first :pair) + (comp (juxt :partial :degree) second :pair) + :beat-hz-by-period) + beat-data) + [[pr1 deg1] [pr2 deg2] beats-by-period] data + beats (nth beats-by-period period) + [r1 r2] (sort pair)] + (when (and (< beats 20) + (not (zero? beats))) + {:period period + :root-hz root + :root (cps->name* root) + :degree-1 deg1 + :degree-2 deg2 + :ratio-1 r1 + :ratio-2 r2 + :ratio-1-partial pr1 + :ratio-2-partial pr2 + :beat-freq.ratio beats + :beat-freq.hz (float beats) + :beat-freq.factors (factorize-ratio beats)})))))) + ratio-pairs)) + (range) + root-periods-freqs))) + +(defn +beat-data + [root-freq partials scale-data] + (let [beat-data (get-beat-data (-> scale-data :meta :period) + root-freq + partials + (map :bounded-ratio (:scale scale-data))) + root (-> beat-data first :root-hz) + root-kw (keyword (str root "hz"))] + (when (not= root-freq root) + (timbre/info "root-freq normalized to:" root)) + #?(:clj + (timbre/info (format "Access beat data like: (-> scale-data :beat-data %s)" root-kw))) + (assoc-in scale-data + [:beat-data root-kw] + beat-data))) +(comment + (+beat-data + 1 + (range 1 6) + (ratios->scale-data [1 + 67/64 + 279/256 + 9/8 + 75/64 + 39/32 + 5/4 + 167/128 + 87/64 + 45/32 + 187/128 + 3/2 + 25/16 + 417/256 + 27/16 + 7/4 + 233/128 + 15/8 + 125/64])) + (->> (get-beat-data 2 1 #_[1 5/4 3/2 7/4] + (range 1 6) + [1 + 67/64 + 279/256 + 9/8 + 75/64 + 39/32 + 5/4 + 167/128 + 87/64 + 45/32 + 187/128 + 3/2 + 25/16 + 417/256 + 27/16 + 7/4 + 233/128 + 15/8 + 125/64] + #_[4181/4096 + 2178309/2097152 + 17/16 + 17711/16384 + 9227465/8388608 + 9/8 + 75025/65536 + 39088169/33554432 + 305/256 + 317811/262144 + 165580141/134217728 + 323/256 + 1346269/1048576 + 21/16 + 5473/4096 + 5702887/4194304 + 89/64 + 1449/1024 + 24157817/16777216 + 377/256 + 98209/65536 + 102334155/67108864 + 1597/1024 + 104005/65536 + 13/8 + 6765/4096 + 1762289/1048576 + 55/32 + 28657/16384 + 933147/524288 + 233/128 + 121393/65536 + 31622993/16777216 + 987/512 + 514229/262144 + 2/1]) + reverse + #_(map :beat-freq) + #_(map #(update % :beat-freq float)) + #_#_#_frequencies + (sort-by second) + reverse) + (def scale-data (+beat-data + 1 + (range 1 6) + (ratios->scale-data [1 + 67/64 + 279/256 + 9/8 + 75/64 + 39/32 + 5/4 + 167/128 + 87/64 + 45/32 + 187/128 + 3/2 + 25/16 + 417/256 + 27/16 + 7/4 + 233/128 + 15/8 + 125/64])))) +(defn beat-factors-hiccup + [{:keys [numer denom]}] + [:span (factors->hiccup numer) "/" (factors->hiccup denom)]) + +(defn hiccup-table [beat-data] + (let [data-by-deg-pairs (group-by (comp (juxt :degree-1 :degree-2 :period)) beat-data) + beat-freqs (->> beat-data + (group-by #(select-keys % [:beat-freq.hz :beat-freq.factors])) + (map (fn [[m vs]] (assoc m :instances (count vs)))) + (sort-by :beat-freq.hz)) + rows (->> data-by-deg-pairs + (sort-by (comp (juxt #(nth % 2 nil) first second) first)) + (map + (fn [[deg-pair pair-beat-data]] + (println (:root-hz (first pair-beat-data))) + (let [data-by-hz (group-by :beat-freq.hz pair-beat-data) + beating-harmonics-at-beat-hz-index (map + (fn [{hz :beat-freq.hz}] + [:td + {:style {:white-space "nowrap"}} + (->> hz + data-by-hz + (map (comp #(str/join "," %) (juxt :ratio-1-partial :ratio-2-partial))) + (str/join " "))]) + beat-freqs)] + (into [:tr + [:td (nth deg-pair 2) "@" (:root-hz (first pair-beat-data)) "hz"] + [:td (str/join "," (take 2 deg-pair))]] + beating-harmonics-at-beat-hz-index))))) + thead [:thead (into [:tr + [:th "Period"] + [:th "Degrees"]] + (map (fn [bf] + [:th (:beat-freq.hz bf) "hz " "(" (:instances bf) ")" + [:br] + (beat-factors-hiccup (:beat-freq.factors bf))]) + beat-freqs))]] + [:table thead [:tbody rows]] + #_beat-freqs + #_data-by-deg-pairs) + #_(h/html [:p])) + +(comment + (hiccup-table (:32hz (:beat-data scale-data)))) + +(defn html-template [body] + [:html [:head [:style + " +table, th, td { + border: 1px solid black; + border-collapse: collapse; +} + +thead th { + position: sticky; /* Makes the header cells sticky */ + top: 0; + background: #fff; /* Prevents content from showing through */ + z-index: 1; /* Ensures the header stays above the scrolling rows */ +} + +th { +min-width: 70px; +padding: 0 4px; +} +"]] [:body body]]) +#?(:clj + (defn spit-html-table + [out-path beat-data] + (let [hiccup-data (-> beat-data + hiccup-table + html-template)] + (->> hiccup-data + h/html + str + (spit out-path))))) +(comment + (spit-html-table "resources/meta-meantone-beats.html" (:32hz (:beat-data scale-data)))) diff --git a/src/erv/meru/utils.cljc b/src/erv/meru/utils.cljc new file mode 100644 index 0000000..6804405 --- /dev/null +++ b/src/erv/meru/utils.cljc @@ -0,0 +1,10 @@ +(ns erv.meru.utils + (:require + [erv.utils.core :refer [round2]])) + +(defn get-convergence-double-with-precision + [convergence-precision convergence-double] + (if convergence-precision + (round2 convergence-precision + convergence-double) + convergence-double)) diff --git a/src/erv/mos/mos.cljc b/src/erv/mos/mos.cljc index 325d740..227d7d4 100755 --- a/src/erv/mos/mos.cljc +++ b/src/erv/mos/mos.cljc @@ -13,8 +13,10 @@ common factors other than 1. 5. The numerator (generator) and denominator (period) representing MOS are also co-prime." - (:require [taoensso.timbre :as timbre] - [erv.utils.core :refer [coprime?]])) + (:require + [erv.utils.core :refer [coprime?]] + [erv.utils.exact :as exact.utils] + [taoensso.timbre :as timbre])) (do ;; TODO, find a proper name and move to utils; called `degs->scale` in mawra.core @@ -39,12 +41,11 @@ (fn [{:keys [moses waiting]} point] (let [points (into [] (sort (concat waiting (last moses) [point]))) intervals (get-diffs points)] - (->> intervals frequencies vals (apply coprime?)) (cond (= #{1 2} (set intervals)) ;; NOTE Do we really not want any more MOS? (reduced {:moses (conj moses points) :waiting []}) (and (<= (count (set intervals)) 2) - (->> intervals frequencies vals (apply coprime?))) + (->> intervals frequencies vals (map exact.utils/->exact) (apply coprime?))) {:moses (conj moses points) :waiting []} :else {:moses moses :waiting (conj waiting point)}))) @@ -61,7 +62,8 @@ (def make-mos (memoize (fn [period generator] - (let [true-mos? (coprime? period generator)] + (let [true-mos? (coprime? (exact.utils/->exact period) + (exact.utils/->exact generator))] (when-not true-mos? (timbre/warn "The generated data is not a true MOS because the period (" period ") and generator (" generator ") are not coprime.")) (with-meta @@ -70,4 +72,4 @@ mos-as-intervals) {:true-mos? true-mos?}))))) -(def make make-mos) +(def make #'make-mos) diff --git a/src/erv/mos/v3/core.clj b/src/erv/mos/v3/core.clj deleted file mode 100644 index 3036f63..0000000 --- a/src/erv/mos/v3/core.clj +++ /dev/null @@ -1,102 +0,0 @@ -(ns erv.mos.v3.core - (:require - [clojure.string :as str] - [erv.utils.conversions :refer [ratio->cents]] - [erv.utils.core :refer [coprime? interval period-reduce round2]] - [erv.utils.ratios :refer [ratios->scale]])) - -;; rational mos - -;; TODO move to ratios -(defn seq-intervals - [period ratios] - (->> ratios - sort - (into []) - (#(conj % period)) - (partition 2 1) - (map #(apply interval %)))) -(seq-intervals 2 [1 3/2 9/8]) -(defn interval-frequencies - [period ratios] - (->> ratios - (seq-intervals period) - frequencies)) -(interval-frequencies 2 [1 9/8 81/64 729/512 3/2 27/16 243/128]) - -(do - (defn mos? - [period ratios] - (let [interval-freqs (interval-frequencies period ratios) - interval-quantities (vals interval-freqs)] - - (and (= 2 (count (keys interval-freqs))) - (apply coprime? interval-quantities)))) - - (mos? 2 [1 3/2 9/8 27/16 81/64 243/128])) - -(defn ratios->mos-data - [{:keys [period ratios gen]}] () - (let [ratio-intervals (seq-intervals period ratios) - interval-freqs (interval-frequencies period ratios) - [s L] (->> ratio-intervals set sort)] - {:meta {:scale :mos - :period period - :size (count ratios) - :intervals/ratios ratio-intervals - :intervals/cents (map ratio->cents ratio-intervals) - :mos/pattern.name (str (interval-freqs s) "s" (interval-freqs L) "L") - :mos/pattern (str/join (map (fn [interval] (if (= interval s) "s" "L")) ratio-intervals)) - :mos/s s - :mos/s.cents (ratio->cents s) - :mos/L L - :mos/L.cents (ratio->cents L) - :mos/sL-ratio (/ L s) - :mos/sL-ratio.float (float (/ (ratio->cents L) (ratio->cents s))) - :mos/sL-ratio.cents (ratio->cents (/ (ratio->cents L) (ratio->cents s))) - :mos/generator gen - :mos/normalized-by 1 - :mos/type :ratio} - :scale (ratios->scale period ratios)})) -(do - (defn gen->mos-ratios - ([gen period] (gen->mos-ratios gen period 100)) - ([gen period max-len] - (let [gen* (cond (int? gen) (bigint gen) - (rational? gen) gen - :else (rationalize gen)) - gen-seq (reductions * (repeat gen*)) - mos-ratios (->> (range max-len) - (map (fn [i] - (->> (conj (take i gen-seq) period) - (map #(period-reduce period %)) - sort))) - (filter #(mos? period %)))] - (map (fn [ratios] - (ratios->mos-data {:period period - :ratios ratios - :gen gen})) - mos-ratios)))) - - (map (comp (juxt :size :mos/pattern - :mos/pattern.name :mos/s.cents :mos/L.cents :mos/sL-ratio.float) - :meta) - (gen->mos-ratios 2 3)) - - (map (comp #(select-keys % - [:size :mos/pattern - :mos/pattern.name :mos/s.cents :mos/L.cents :mos/sL-ratio.float]) - :meta) - #_(comp (partial map :bounded-ratio) :scale) - (gen->mos-ratios 11/8 - (rationalize (round2 4 (erv.utils.conversions/cents->ratio 400))) - 50)) - #_(map count (gen->mos 3/2 2 12))) - -(comment - (->> (gen->mos-ratios 7/4 3) - (filter #(-> % :meta :size (= 53))) - first - :scale) - (rotate-scale (:scale (nth (gen->mos-ratios 3/2 2) 2)) - 2)) diff --git a/src/erv/mos/v3/core.cljc b/src/erv/mos/v3/core.cljc new file mode 100644 index 0000000..8126608 --- /dev/null +++ b/src/erv/mos/v3/core.cljc @@ -0,0 +1,95 @@ +(ns erv.mos.v3.core + #?(:cljs (:refer-clojure :exclude [+ - * / - numerator denominator integer? + mod rem quot even? odd? rationalize])) + (:require + #?(:cljs [com.gfredericks.exact :as e :refer [* - - /]]) + [erv.utils.exact :as exact.utils #?@(:cljs [:refer [rationalize]])] + [clojure.core :as core] + [clojure.string :as str] + [erv.utils.conversions :refer [ratio->cents]] + [erv.utils.core :refer [coprime? interval period-reduce]] + [erv.utils.ratios :refer [ratios->scale]])) + +;; rational mos + +;; TODO move to ratios +(defn seq-intervals + [period ratios] + (->> ratios + sort + (into []) + (#(conj % period)) + (partition 2 1) + (map #(apply interval %)))) + +(comment + (seq-intervals (e/native->integer 2) (erv.utils.exact/parse-ratios "1 3/2 9/8"))) + +(defn interval-frequencies + [period ratios] + (->> ratios + (seq-intervals period) + frequencies)) +#_(interval-frequencies 2 [1 9/8 81/64 729/512 3/2 27/16 243/128]) + +(defn mos? + [period ratios] + (let [interval-freqs (interval-frequencies period ratios) + interval-quantities (vals interval-freqs)] + (and (= 2 (count (keys interval-freqs))) + (apply coprime? interval-quantities)))) +(comment + (mos? (erv.utils.exact/->exact 2) (erv.utils.exact/parse-ratios "1 3/2 9/8 27/16 81/64 243/128")) + (mos? 2 [1 3/2 9/8 27/16 81/64 243/128])) + +(defn ratios->mos-data + [{:keys [period ratios gen]}] () + (let [ratio-intervals (seq-intervals period ratios) + interval-freqs (interval-frequencies period ratios) + [s L] (->> ratio-intervals set sort)] + {:meta {:scale :mos + :period period + :size (count ratios) + :intervals/ratios ratio-intervals + :intervals/cents (map ratio->cents ratio-intervals) + :mos/pattern.name (str (interval-freqs s) "s" (interval-freqs L) "L") + :mos/pattern (str/join (map (fn [interval] (if (= interval s) "s" "L")) ratio-intervals)) + :mos/s s + :mos/s.cents (ratio->cents s) + :mos/L L + :mos/L.cents (ratio->cents L) + :mos/sL-ratio (/ L s) + :mos/sL-ratio.float (float (core// (ratio->cents L) (ratio->cents s))) + :mos/sL-ratio.cents (ratio->cents (core// (ratio->cents L) (ratio->cents s))) + :mos/generator gen + :mos/normalized-by 1 + :mos/type :ratio} + :scale (ratios->scale period ratios)})) + +(defn gen->mos-ratios + ([gen period] (gen->mos-ratios gen period 100)) + ([gen period max-len] + (let [gen* (cond (int? gen) (#?(:clj bigint :cljs e/native->integer) gen) + (#?(:clj rational? :cljs e/ratio?) gen) gen + :else (rationalize gen)) + gen-seq (reductions * (repeat gen*)) + period (exact.utils/->exact period) + mos-ratios (->> (range max-len) + (map (fn [i] + (->> (conj (take i gen-seq) period) + (map #(period-reduce period %)) + sort))) + (filter #(mos? period %)))] + + (map (fn [ratios] + (ratios->mos-data {:period period + :ratios ratios + :gen gen})) + mos-ratios)))) + +(comment + ;; TODO: nice scale, save somewhere + (->> (gen->mos-ratios 7/4 3) + (filter #(-> % :meta :size (= 53))) + first + :scale)) diff --git a/src/erv/scale/scl.cljc b/src/erv/scale/scl.cljc index 4326b28..b207d96 100644 --- a/src/erv/scale/scl.cljc +++ b/src/erv/scale/scl.cljc @@ -133,3 +133,121 @@ (do (make-parents filepath) (spit filepath (:content (make-scl-file scale-data)))) :cljs (throw (js/Error. "Cannot spit file in JS, use make-scl-file instead")))) + +;;;;;;;;;;; +;; KBM Files +;;;;;;;;;;; + +(def kbm-template + "Template for a keyboard mapping" + "! KBM file for: %s; %s +! Size of map. The pattern repeats every so many keys: +%s +! First MIDI note number to retune: +0 +! Last MIDI note number to retune: +127 +! Middle note where the first entry of the mapping is mapped to: +%s +! Reference note for which frequency is given: +%s +! Frequency to tune the above note to +%s +! Scale degree to consider as formal octave (determines difference in pitch +! between adjacent mapping patterns): +%s +! Mapping. +! The numbers represent scale degrees mapped to keys. The first entry is for +! the given middle note, the next for subsequent higher keys. +! For an unmapped key, put in an \"x\". At the end, unmapped keys may be left out. +%s +! %s +") + +(defn make-kbm + [{:as _kbm-template-config + :keys [scale-data degrees middle-note middle-note-freq comments?] + :or {middle-note-freq (conv/midi->cps 60) + middle-note 60 + comments? true}}] + (let [scale-description (get-description-data scale-data) + scale (:scale scale-data) + scale-size (count scale) + degrees* (->> degrees + (map #(mod % scale-size)) + sort + (str/join "\n"))] + (cond-> (format kbm-template + (:name scale-description "unknown.scl") + (:description scale-description "") + (count degrees) + middle-note ;; middle note + middle-note ;; reference note + middle-note-freq ;; frequency + scale-size + degrees* + made-with) + (not comments?) ((fn [kbm] + (let [lines (str/split-lines kbm)] + (->> lines + (remove #(str/starts-with? % "!")) + (str/join "\n")))))))) + +(comment + (require '[erv.utils.ratios :refer [ratios->scale]] + '[erv.cps.core :as cps]) + (def scale-data (cps/make 2 [1 3 5 7])) + + (println (make-kbm {:scale-data scale-data + :comments? false + :degrees [0 6 12 16 22 28 32]})) + + (println (make-kbm {:scale-data {:scale (ratios->scale [4181/4096 + 2178309/2097152 + 17/16 + 17711/16384 + 9227465/8388608 + 9/8 + 75025/65536 + 39088169/33554432 + 305/256 + 317811/262144 + 165580141/134217728 + 323/256 + 1346269/1048576 + 21/16 + 5473/4096 + 5702887/4194304 + 89/64 + 1449/1024 + 24157817/16777216 + 377/256 + 98209/65536 + 102334155/67108864 + 1597/1024 + 104005/65536 + 13/8 + 6765/4096 + 1762289/1048576 + 55/32 + 28657/16384 + 933147/524288 + 233/128 + 121393/65536 + 31622993/16777216 + 987/512 + 514229/262144 + 2/1])} + :comments? false + :degrees [0 6 12 16 22 28 32]})) + + (spit-kbm {:scale-data scale-data + :degrees [1 3]})) + +(defn ^:export spit-kbm + #_{:clj-kondo/ignore [:unused-binding]} + [{:keys [filepath] :as kbm-template-config}] + #?(:clj + (do (make-parents filepath) + (spit filepath (make-kbm kbm-template-config))) + :cljs (throw (js/Error. "Cannot spit file in JS, use make-kbm instead")))) diff --git a/src/erv/scratch.clj b/src/erv/scratch.clj index 65f8c4c..776f0b8 100644 --- a/src/erv/scratch.clj +++ b/src/erv/scratch.clj @@ -18,7 +18,6 @@ (if (> n target) n (recur (+ period n))))) ;; inversions of a dekany - (defn simplify-inversion [inversion] (let [common-factors (map (comp frequencies prime-factors) inversion) common-denominator (->> common-factors diff --git a/src/erv/types.cljc b/src/erv/types.cljc new file mode 100644 index 0000000..d883157 --- /dev/null +++ b/src/erv/types.cljc @@ -0,0 +1,94 @@ +(ns erv.types + (:require [malli.core :as m] + [malli.util :as mu])) + +(def Intish + #_{:clj-kondo/ignore [:unresolved-symbol]} + [:or :int [:fn #(instance? clojure.lang.BigInt %)]]) + +(def MeruBaseData + [:map + [:convergence-double double?] + [:convergence-index int?] + [:reached-convergence? :boolean] + [:series [:vector Intish]] + [:convergence-precision [:or :int :nil]] + [:convergence-double-with-precision :double]]) + +(def MeruRecurrentSeriesData + (mu/merge #'MeruBaseData + [:map + [:seed [:vector :int]]])) + +(def MeruDiagonalsData + (mu/merge #'MeruBaseData + [:map + [:series-data [:vector + [:map + [:value #'Intish] + [:coords [:vector + [:map + [:x :int] + [:y #'Intish]]]] + [:ratio-vs-previous {:optional true} [:maybe :double]]]]] + [:triangle-seed [:map [:left :int] [:right :int]]] + [:convergence-precision [:or :int :nil]] + [:convergence-double-with-precision :double]])) + +(comment + (m/explain MeruDiagonalsData + (erv.meru.diagonals/diagonals + {:size 120 + :slope {:x 1 :y 2} + :pascal-coord->number erv.math.pascals-triangle/default-coord-map})) + (:errors (m/explain MeruRecurrentSeriesData + (erv.meru.recurrent-series/recurrent-series {:seed [1 1 1] + :formula :meta-slendro})))) + +(def Ratio + #?(:clj [:or :int ratio?])) + +(comment + (m/explain Ratio 3/2) + (println (keys (m/default-schemas)))) + +(defn make-scale-meta + [fields] + (into [:map + [:period number?] + [:size :int]] + fields)) + +(def CPSMeta (make-scale-meta + [[:scale [:enum :cps]] + [:cps/size :int] + [:cps/factors [:vector :int]] + [:cps/normalized-by :int] + [:cps/type :string]])) + +(def Note + [:map + [:ratio Ratio] + [:bounded-ratio Ratio] + [:bounding-period number?] + [:degree :int]]) + +(def ScaleData + [:map + [:meta (make-scale-meta [])] + [:scale [:sequential Note]]]) + +;; You can validate your data like this: + +(comment + (require '[erv.cps.core :as cps]) + (def scale-data (cps/make 2 [1 3 5 7])) + (-> scale-data :scale) + (m/explain ScaleData (-> scale-data + (update :scale #(into [] %)))) + (m/explain Note {:set #{7 5}, + :archi-set #{:c :d}, + :ratio 35, + :bounded-ratio 35/32, + :bounding-period 2, + :degree 0})) diff --git a/src/erv/utils/conversions.cljc b/src/erv/utils/conversions.cljc index 16e19b8..768de0e 100755 --- a/src/erv/utils/conversions.cljc +++ b/src/erv/utils/conversions.cljc @@ -1,8 +1,11 @@ (ns erv.utils.conversions - (:require [erv.utils.core :refer [round2]])) + (:require + [clojure.math :refer [log]] + [erv.utils.core :refer [round2]] + [erv.utils.exact :as exact.utils])) (defn ratio->cents [ratio] - (-> (Math/log ratio) (/ (Math/log 2)) (* 1200))) + (-> (log (exact.utils/->native ratio)) (/ (log 2)) (* 1200))) (defn cents->ratio [cents] (-> (/ cents 1200) (* (Math/log 2)) Math/exp)) @@ -38,10 +41,13 @@ "If note is not exactly in 12ET it adds a suffix to the note with the upwards deviation in cents. i.e. 60.4 -> C3+40" [midi] - (let [deviation (-> (mod midi 1) - (->> (round2 2)) - (* 100) - int) + (let [deviation (-> + midi + (exact.utils/->native) + (mod 1) + (->> (round2 2)) + (* 100) + int) octave (get-octave midi)] (str (nth note-names (mod (int midi) 12)) octave diff --git a/src/erv/utils/core.cljc b/src/erv/utils/core.cljc index 59a95a8..4955105 100755 --- a/src/erv/utils/core.cljc +++ b/src/erv/utils/core.cljc @@ -1,7 +1,12 @@ (ns erv.utils.core + #?(:cljs (:refer-clojure :exclude [> >= < <= = + - * / -compare compare numerator denominator integer? + mod rem quot even? odd? pos? zero? inc])) (:require + #?(:cljs [com.gfredericks.exact :as e :refer [* / < = > mod rem zero? inc numerator denominator]]) + [clojure.core :as core] [clojure.set :as set] - [clojure.spec.alpha :as s])) + [clojure.spec.alpha :as s] + [erv.utils.exact :as exact.utils])) (defn validate [spec input] (or (s/valid? spec input) @@ -9,18 +14,18 @@ (defn wrap-at [i coll] (let [size (count coll) - i* (if (zero? size) 0 (mod i size))] + i* (if (core/zero? size) 0 (core/mod i size))] (nth coll i* nil))) (defn round2 "Round a double to the given precision (number of significant digits)" [precision d] (let [factor (Math/pow 10 precision)] - (/ (Math/round (* d factor)) factor))) + (core// (Math/round (core/* d factor)) factor))) (defn rotate [xs n] (let [l (count xs) - off (mod (+ (mod n l) l) l)] + off (core/mod (core/+ (core/mod n l) l) l)] (concat (drop off xs) (take off xs)))) (defn get-all-rotations [pattern] @@ -31,14 +36,20 @@ (filter #(= 0 (rem n %)) (range 2 n))) (defn prime-factors [n] - (loop [n n divisor 2 factors []] - (if (< n 2) - factors - (if (zero? (rem n divisor)) - (recur (/ n divisor) divisor (conj factors divisor)) - (recur n (inc divisor) factors))))) + (let [_2 (exact.utils/->exact 2) + n (exact.utils/->exact n)] + (loop [n n + divisor _2 + factors []] + (if (< n _2) + factors + (if (zero? (rem n divisor)) + (recur (/ n divisor) divisor (conj factors divisor)) + (recur n (inc divisor) factors)))))) + (comment - (prime-factors 2)) + (prime-factors (exact.utils/->exact 1)) + (prime-factors 1)) (defn coprime? [& ns] (->> ns (map (comp set prime-factors)) (apply set/intersection) empty?)) @@ -55,27 +66,48 @@ (defn period-reduce ([ratio] (period-reduce 2 ratio)) ([period ratio] - (loop [ratio ratio] - (cond - (> period ratio 1) ratio - (or (= period ratio) (= 1 ratio)) 1 - (> ratio period) (recur (/ ratio period)) - (< ratio period) (recur (* ratio period)))))) + (let [_1 (exact.utils/->exact 1) + period* (exact.utils/->exact period)] + (loop [ratio ratio] + (cond + (> period* ratio _1) ratio + (or (= period* ratio) (= _1 ratio)) _1 + (> ratio period*) (recur (/ ratio period*)) + (< ratio period*) (recur (* ratio period*))))))) (defn indexes-of [el coll] (keep-indexed #(when (= el %2) %1) coll)) -(defn ^:export pow [n power] - (when-not (int? power) - (throw (ex-info "`power` must be an int" {:power power}))) - (cond - (zero? power) 1 - (> power 0) (apply * (repeat power n)) - :else (apply / 1 (repeat (abs power) n)))) +#?(:clj + (defn ^:export pow [n power] + (when-not (int? power) + (throw (ex-info "`power` must be an int" {:power power}))) + (cond + (zero? power) 1 + (> power 0) (apply * (repeat power n)) + :else (apply / 1 (repeat (abs power) n)))) + :cljs + (defn ^:export pow [n power] + (when-not (int? power) + (throw (ex-info "`power` must be an int" {:power power}))) + (let [n (exact.utils/->exact n) + power (exact.utils/->exact power)] + (cond + (zero? power) (exact.utils/->exact 1) + (> power (exact.utils/->exact 0)) (apply * (repeat power n)) + :else (apply / (repeat (abs power) n)))))) +(comment + (pow (exact.utils/->exact 2) + (exact.utils/->exact 0)) + (pow (exact.utils/->exact 2) + (exact.utils/->exact 3)) + (pow (exact.utils/->exact 2) + (exact.utils/->exact -3)) + (pow 2 -3)) (defn pattern->degrees [pattern] (->> pattern - (reduce (fn [acc el] (conj acc (+ el (last acc)))) + (reduce (fn [acc el] (conj acc (core/+ el (last acc)))) [0]) drop-last)) @@ -96,19 +128,31 @@ indexes))) ;; TODO add tests -(defn gcd +(defn gcd* "Greatest common divisor" [a b] (if (zero? b) a (recur b (mod a b)))) +(defn gcd + "Greatest common divisor" + [a b] + (gcd* (exact.utils/->exact a) + (exact.utils/->exact b))) + ;; TODO add tests -(defn lcm +(defn lcm* "Least common multiple" [a b] (/ (* a b) (gcd a b))) +(defn lcm + "Least common multiple" + [a b] + (lcm* (exact.utils/->exact a) + (exact.utils/->exact b))) + ;; TODO add tests (defn lcm-of-list "Find the least common multiple of a list of numbers" @@ -120,3 +164,53 @@ "Find the greatest common divisor of a list of numbers" [nums] (reduce gcd nums)) + +(defn intish? [n] (= n (int n))) + +(defn decompose-ratio + ([ratio] + ;; NOTE the following code does not work in prod, as e/numerator returns nil and doesn't throw on integers + ;; keeping the code here for documentation purposes and to avoid any refactoring to a similar procedure + #_(try + {:numer (numerator ratio) :denom (denominator ratio)} + (catch #?(:clj Exception :cljs js/Error) _ + {:numer ratio :denom (exact.utils/->exact 1)})) + #?(:cljs + (cond + (e/integer? ratio) {:numer ratio :denom (exact.utils/->exact 1)} + (e/ratio? ratio) {:numer (numerator ratio) :denom (denominator ratio)}) + :clj + (cond + (intish? ratio) {:numer ratio :denom 1} + (ratio? ratio) {:numer (numerator ratio) :denom (denominator ratio)})))) +(comment + (decompose-ratio 21/16)) +(defn decompose-ratios + ([ratios] (mapv decompose-ratio ratios))) + +(defn factorize-ratio + [n] + (-> n decompose-ratio + (update :numer prime-factors) + (update :denom prime-factors))) +(do + (defn factors->hiccup + "Outputs hiccup with factors in power notation" + [factors] + (if-not (seq factors) + [:span 1] + (->> (frequencies factors) + (sort-by first) + (map (fn [[factor power]] [:span factor [:sup power]]))))) + (factors->hiccup [3 3 7 5]) + (factors->hiccup [])) + +(defn make-map-by-key + "Given a vector of hash-maps with a specific `k`, return a map of `k`->hash-map. + The user is responsible for providing a unique `k`, otherwise data may be missing." + [key-fn maps] + (reduce + (fn [acc m] + (assoc acc (key-fn m) m)) + {} + maps)) diff --git a/src/erv/utils/exact.cljc b/src/erv/utils/exact.cljc new file mode 100644 index 0000000..94c7cbb --- /dev/null +++ b/src/erv/utils/exact.cljc @@ -0,0 +1,102 @@ +(ns erv.utils.exact + "Parse numbers into ratios using `gfredericks/exact`. Also provides helpers for working around `exact` based numbers." + (:require + #?(:cljs [clojure.math :refer [pow]]) + [clojure.string :as str] + [clojure.walk :as walk] + [com.gfredericks.exact :as e])) + +(defn- parseable-ratio? + [s] + (boolean (when (string? s) + (re-matches #"^-?\d+(/-?\d+)?$" s)))) + +(defn parse-ratio + [ratio-str] + (when-not (parseable-ratio? ratio-str) + (throw (ex-info "Cannot parse string into a ratio or an integer" + {:ratio-str ratio-str}))) + (let [[numer denom] + (->> (str/split ratio-str #"/") + (map (comp e/string->integer)))] + (e// numer (or denom e/ONE)))) + +(defn ->exact + "If on a `cljs` environment: + Turn `x` into an `exact` integer or ratio. If the value is already and instance of those, + return the value as is." + [x] + #?(:clj x + :cljs (cond + (e/integer? x) x + (e/ratio? x) x + (int? x) (e/native->integer x) + (parseable-ratio? x) (parse-ratio x) + :else (throw (ex-info "Don't know how to turn value into `exact` instance" + {:value x}))))) + +(defn exact-ratio->number + [eratio] + (/ (-> eratio + e/numerator + e/integer->native) + (-> eratio + e/denominator + e/integer->native))) + +#_(exact-ratio->number (e// (e/native->integer 2) + (e/native->integer 3))) +#_(number? (e// (e/native->integer 2) + (e/native->integer 3))) +(defn ->native + [x] + (cond + (number? x) x + (e/integer? x) (e/integer->native x) + (e/ratio? x) (exact-ratio->number x) + :else (throw (ex-info "Don't know how to turn value into number" + {:value x})))) + +(defn exact? + [x] + (or (e/integer? x) (e/ratio? x))) + +(defn parse-ratios + "Parses a string of ratios separated by `,` or whitespaces" + [ratios-str] + (->> (str/split ratios-str #"[,|\s]") + (remove empty?) + (map parse-ratio))) + +#_(parse-scale "1 3/2\n 8/7") + +(defn exact->string + [exact-int-or-ratio] + (cond + (e/ratio? exact-int-or-ratio) (str + (e/numerator exact-int-or-ratio) + "/" + (e/denominator exact-int-or-ratio)) + (e/integer? exact-int-or-ratio) (e/integer->string exact-int-or-ratio) + :else (throw (ex-info "Don't know how to parse ratio" + {:value exact-int-or-ratio})))) + +#_(map print-ratio (parse-scale "1 3/2\n 8/7")) + +(defn make-readable + "Takes a walkeable structure and converts all exact instances to a readable string" + [coll] + (walk/postwalk + (fn [x] + (if (or (e/integer? x) (e/ratio? x)) + (exact->string x) + x)) + coll)) + +#?(:cljs + (defn rationalize + [num] + (let [decimal-places (-> num str (str/split ".") last count) + denom (int (pow 10 decimal-places))] + (e// (e/native->integer (* denom num)) + (e/native->integer denom))))) diff --git a/src/erv/utils/impl.cljc b/src/erv/utils/impl.cljc new file mode 100644 index 0000000..95a349a --- /dev/null +++ b/src/erv/utils/impl.cljc @@ -0,0 +1,13 @@ +(ns erv.utils.impl + "Contains implementations or functions that are shared among different files." + + #? (:cljs (:require [goog.string :as gstr] + [goog.string.format]))) + +#?(:cljs + (def format gstr/format)) + +(defn +degree + "Adds degrees to a scale" + [scale] + (map-indexed (fn [i n] (assoc n :degree i)) scale)) diff --git a/src/erv/utils/ratios.cljc b/src/erv/utils/ratios.cljc index 6167fb8..84226b2 100644 --- a/src/erv/utils/ratios.cljc +++ b/src/erv/utils/ratios.cljc @@ -1,19 +1,27 @@ (ns erv.utils.ratios + ;; TODO: improve namespace definition #?@ (:clj [(:require + [clojure.core :as core] [clojure.edn :as edn] [clojure.string :as str] [com.gfredericks.exact :as e] [erv.utils.conversions :as conv] [erv.utils.core :refer [gcd-of-list interval period-reduce prime-factors - round2]])] + round2]] + [erv.utils.exact :as exact.utils])] :cljs - [(:require + [(:refer-clojure :exclude [> < + - * / - numerator denominator integer? + mod rem quot even? odd? min]) + (:require + [clojure.core :as core] [clojure.string :as str] - [com.gfredericks.exact :as e] + [erv.utils.exact :as exact.utils] + [com.gfredericks.exact :as e :refer [> < + - * / - mod min numerator denominator]] [erv.utils.conversions :as conv] - [erv.utils.core :refer [interval period-reduce round2 prime-factors]])])) + [erv.utils.core :refer [gcd-of-list interval period-reduce round2 prime-factors]] + [erv.utils.impl :as impl :refer [format]])])) (defn ratio-proximity-list "Make a list of `ratios` that approximate a `target-ratio` in a list of `target-ratios`" @@ -75,53 +83,46 @@ (defn ratio-string->ratio [ratio-string] - (let [[numer denom] (-> ratio-string - (str/split #"/"))] + (let [[numer denom] (-> ratio-string (str/split #"/"))] #?(:clj (/ (edn/read-string numer) (edn/read-string denom)) :cljs (e// (e/string->integer numer) (e/string->integer denom))))) -(do - #?(:clj (defn analyze-ratio +;; TODO: simplify definition +#?(:clj (defn analyze-ratio + [ratio] + (let [numerator* (if (integer? ratio) + ;; in case it's big int,or something like 1N + (int ratio) + (numerator ratio)) + denominator* (if (integer? ratio) + 1 + (denominator ratio))] + {:numerator numerator* + :denominator denominator* + :numer-factors (prime-factors numerator*) + :denom-factors (prime-factors denominator*)})) + :cljs (defn analyze-ratio [ratio] - (let [numerator* (if (integer? ratio) - ;; in case it's big int,or something like 1N - (int ratio) - (numerator ratio)) - denominator* (if (integer? ratio) - 1 - (denominator ratio))] + (let [numerator* (if-not (e/ratio? ratio) ;when receiving something like 1/1 the above cond will not return a ratio type + ratio + (e/numerator ratio)) + denominator* (if-not (e/ratio? ratio) + e/ONE + (e/denominator ratio))] + {:numerator numerator* :denominator denominator* :numer-factors (prime-factors numerator*) - :denom-factors (prime-factors denominator*)})) - :cljs (defn analyze-ratio - [ratio] - (let [ratio* (cond - #?(:clj (ratio? ratio) :cljs false) ratio - (float? ratio) (float->ratio ratio) - (and (string? ratio) (str/includes? ratio "/")) (ratio-string->ratio ratio) - (and (string? ratio) (seq ratio)) (e/string->integer ratio) - :else (throw (ex-info (str "Don't know how to convert ratio, received " ratio) {:ratio ratio}))) - numerator* (if-not (e/ratio? ratio*) ;when receiving something like 1/1 the above cond will not return a ratio type - (e/integer->native ratio*) - (e/integer->native (e/numerator ratio*))) - denominator* (if-not (e/ratio? ratio*) - 1 - (e/integer->native (e/denominator ratio*)))] - {:numerator numerator* - :denominator denominator* - :numer-factors (prime-factors numerator*) - :denom-factors (prime-factors denominator*)})))) + :denom-factors (prime-factors denominator*)}))) (defn ratio->factor-string [ratio] (->> ratio analyze-ratio - ((juxt (comp #(str/join "." (if (seq %) % [1])) :numer-factors) - (comp #(str/join "." (if (seq %) % [1])) :denom-factors))) - #?(:clj (apply format "%s/%s") - :cljs ((fn [n d] (str n "/" d)))))) + ((juxt (comp #(str/join "." (if (seq %) (map exact.utils/->native %) [1])) :numer-factors) + (comp #(str/join "." (if (seq %) (map exact.utils/->native %) [1])) :denom-factors))) + (apply format "%s/%s"))) (defn seq-interval-analysis [ratios] @@ -137,12 +138,22 @@ ([ratios] (ratios->scale 2 ratios)) ([period ratios] (->> ratios - (map (fn [r] - (let [ratio (period-reduce period r)] - {:ratio ratio - :bounded-ratio ratio - :bounding-period period}))) - (sort-by :bounded-ratio)))) + (mapv (fn [r] + (let [ratio (period-reduce period r)] + {:ratio ratio + :bounded-ratio ratio + :bounding-period (exact.utils/->exact period)}))) + (sort-by :bounded-ratio) + ;; impl/+degree ;; TODO: should this be used here? + ))) + +(defn ratios->scale-data + ([ratios] (ratios->scale-data 2 ratios)) + ([period ratios] + (let [scale (ratios->scale period ratios)] + {:meta {:period period + :size (count scale)} + :scale scale}))) (defn ratios-intervals "Get the intervals between the ratios in the sequence. @@ -153,39 +164,39 @@ (map #(apply interval %)))) (defn interval-seq->ratio-stack - [interval-seq size] - (loop [ratios [1] + [size interval-seq] + (loop [ratios [(exact.utils/->exact 1)] index 0] (if (= size (count ratios)) ratios (recur (conj ratios (* (last ratios) - (nth interval-seq (mod index (count interval-seq))))) + (nth interval-seq (core/mod index (count interval-seq))))) (inc index))))) (defn normalize-ratios "Will return a vector of ratios sorted and normalized so that the smallest one is 1/1." - ([ratios] (normalize-ratios nil ratios) - (let [min* (apply min ratios)] - (->> ratios sort (map #(/ % min*))))) + ([ratios] (normalize-ratios nil ratios)) ([period ratios] - (let [min* (apply min ratios) + (let [period (when period (exact.utils/->exact period)) + ratios (mapv exact.utils/->exact ratios) + min* (apply min ratios) ratios* (->> ratios sort (map #(/ % min*)))] (if period (map #(period-reduce period %) ratios*) ratios*)))) +(normalize-ratios [1 3 4]) + (defn ratios->harmonic-series [ratios] - #?(:clj - (let [denominators (map (fn [r] (if (int? r) r (denominator r))) ratios) - anti-denom (apply * denominators) - harmonics (map #(* anti-denom %) ratios) - gcd (gcd-of-list harmonics)] - (map #(/ % gcd) harmonics)) - :cljs (throw (js/Error (str "ratios->harmonic-series not implemented, cannot process:" ratios))))) + (let [denominators (map (fn [r] (if (int? r) r (denominator r))) ratios) + anti-denom (apply * denominators) + harmonics (map #(* anti-denom %) ratios) + gcd (gcd-of-list harmonics)] + (map #(/ % gcd) harmonics))) (defn gen-chain "Create a chain of ratios starting from 1" [length generator] (->> (range length) - (map (fn [i] (apply * (repeat i generator)))))) + (map (fn [i] (apply core/* (repeat i generator)))))) diff --git a/src/erv/utils/scale.clj b/src/erv/utils/scale.cljc similarity index 62% rename from src/erv/utils/scale.clj rename to src/erv/utils/scale.cljc index 74beafa..fae81bc 100644 --- a/src/erv/utils/scale.clj +++ b/src/erv/utils/scale.cljc @@ -1,10 +1,19 @@ (ns erv.utils.scale + #?(:cljs (:refer-clojure :exclude [+ - * / - numerator denominator integer? + mod rem quot even? odd?])) (:require + #?(:cljs [com.gfredericks.exact :as e :refer [* + - - /]]) + [clojure.core :as core] [clojure.math.combinatorics :as combo] - [erv.utils.core :refer [interval period-reduce rotate wrap-at]] + [erv.utils.core :refer [decompose-ratios interval lcm-of-list period-reduce + rotate wrap-at]] + [erv.utils.exact :as exact.utils] + [erv.utils.impl :as impl] [erv.utils.ratios :refer [interval-seq->ratio-stack normalize-ratios ratios->scale ratios-intervals]])) +(def +degree #'impl/+degree) + (defn degree-stack "Generate a stack ratios from a single (degree) generator" [{:keys [scale gen offset]}] @@ -14,7 +23,7 @@ ratio-subset #{} offset offset gen-index 0] - (let [i (mod offset (count scale)) + (let [i (core/mod offset (count scale)) new-note (assoc (nth scale i) :gen/index gen-index) ratio (:bounded-ratio new-note)] @@ -22,7 +31,7 @@ subset (recur (conj subset new-note) (conj ratio-subset ratio) - (+ offset gen) + (core/+ offset gen) (inc gen-index))))))) (defn scale-intervals @@ -43,7 +52,7 @@ (defn tritriadic "Make a scale from stacking a triad three times. https://en.xen.wiki/w/Tritriadic_scale" - ([triad-ratios] (tritriadic 2 triad-ratios)) + ([triad-ratios] (tritriadic (exact.utils/->exact 2) triad-ratios)) ([period triad-ratios] (let [triad-ratios (normalize-ratios period triad-ratios)] {:meta {:scale :tritriadic @@ -51,7 +60,8 @@ :scale (ratios->scale period (map #(* (last triad-ratios) %) (interval-seq->ratio-stack - (ratios-intervals triad-ratios) 7)))}))) + 7 + (ratios-intervals triad-ratios))))}))) (defn scale->stacked-subscale "Make a scale from a stack of generator steps from a parent scale. @@ -64,10 +74,11 @@ :offset offset} degree-stack scale-intervals - (interval-seq->ratio-stack size) - (->> (ratios->scale period)) + (->> (interval-seq->ratio-stack size) + (ratios->scale period)) distinct)] {:meta {:scale :stacked-subscale + :period period :intervals (scale-intervals scale) :parent-scale scale :gen gen @@ -103,15 +114,16 @@ rotation))) (defn cross-set - [period & ratios] - (let [scale (->> ratios + [period & ratio-vecs] + (let [scale (->> ratio-vecs + #?(:cljs (map (partial map exact.utils/->exact))) (apply combo/cartesian-product) (map #(apply * %)) flatten - (ratios->scale period) + (ratios->scale (exact.utils/->exact period)) dedupe-scale)] {:meta {:scale :cross-set - :sets ratios + :sets ratio-vecs :size (count scale) :period period} :scale scale})) @@ -129,12 +141,12 @@ scale) total-subset (count subset-set) total-subscale (count subscale)] - (when (<= (- total-subset total-subscale) - max-missing-notes) + (when (core/<= (core/- total-subset total-subscale) + max-missing-notes) (let [degrees (map :rotated-scale/original-degree subscale) matched-ratios (map :matched-ratio subscale)] {:degrees degrees - :matched (/ total-subscale total-subset) + :matched (core// total-subscale total-subset) :subscale/matched-ratios matched-ratios})))) scale-rotations))) @@ -143,10 +155,62 @@ (keep #(wrap-at % scale) degrees)) (defn scale-steps->degrees - "Convert a sequence of scale-steps defining a scale (e.g. [2 2 1 2 2 2 1] into a sequence of degrees" + "Convert a sequence of scale-steps defining a scale (e.g. [2 2 1 2 2 2 1]) into a sequence of degrees" ([scale-steps] (scale-steps->degrees scale-steps true)) ([scale-steps remove-octave?] (->> scale-steps - (reduce (fn [acc n] (conj acc (+ n (or (last acc) 0)))) + (reduce (fn [acc n] (conj acc (core/+ n (or (last acc) 0)))) [0]) (drop-last (if remove-octave? 1 0))))) + +(defn diamond + [period factors] + (let [factors (map exact.utils/->exact factors) + scale (->> (combo/cartesian-product factors factors) + (mapv (fn [[a b]] (/ a b))) + (ratios->scale period) + dedupe-scale)] + {:meta {:scale :diamond + :factors factors + :size (count scale) + :period period} + :scale scale})) + +;; TODO add tests +;; +;; +;; +;; +;; +(defn proportional-difference + "Returns the difference between the ratios if the chord is proportional, otherwiser returns `nil`" + [ratios] + (let [ratio-analysis (decompose-ratios ratios) + lcm (lcm-of-list (mapv :denom ratio-analysis))] + (->> ratio-analysis + (mapv (fn [{:keys [denom numer]}] + (* numer (/ lcm denom)))) + sort + (partition 2 1) + (mapv (fn [[a b]] (- b a))) + (#(when (apply = %) (first %)))))) + +(defn proportional-chords + "Returns a map with keys `:by-notes` and `:by-degrees` with the notes or degrees that form proportional chords of a given size. + The map groups these notes or degrees by the difference in beats common to them." + [chord-size scale] + (let [scale (+degree scale) + proportional-chords-by-notes (->> (combo/combinations scale chord-size) + (keep (fn [ns] + (when-let [diff (->> ns (mapv :ratio) proportional-difference)] + [diff ns]))))] + {:by-notes (reduce + (fn [acc [diff ns]] + (update acc diff (fnil conj []) (mapv :ratio ns))) + {} + proportional-chords-by-notes) + :by-degrees (reduce + (fn [acc [diff ns]] + (update acc diff (fnil conj []) (mapv :degree ns))) + {} + proportional-chords-by-notes)})) diff --git a/src/js/export_fn.cljs b/src/js/export_fn.cljs index 6799dba..125577c 100644 --- a/src/js/export_fn.cljs +++ b/src/js/export_fn.cljs @@ -3,7 +3,9 @@ [erv.cps.core :as cps] [erv.edo.core :as edo] [erv.mos.mos :as mos] + [erv.scale.core :as scale] [erv.utils.conversions :as conv] + [erv.utils.ratios :as ratios] [erv.utils.core :as utils])) (defn generate-exports [] @@ -11,4 +13,12 @@ :mos {:make (comp clj->js mos/make)} :edo {:fromPattern (comp clj->js edo/from-pattern)} :utils {:rotate (comp clj->js utils/rotate) - :ratioToCents (comp clj->js conv/ratio->cents)}})) + :ratioToCents (comp clj->js conv/ratio->cents) + :centsToRatio (comp clj->js conv/cents->ratio) + :freqToMidi (comp clj->js conv/cps->midi) + :ratiosToScale (comp clj->js + (fn [period ratios] + (ratios/ratios->scale period (js->clj ratios))))} + :scale {:degToFreq (comp clj->js (fn [scale root degree] + (let [scale* (js->clj scale {:keywordize-keys true})] + (scale/deg->freq scale* root degree))))}})) diff --git a/test/erv/beating_analyzer/v1_test.clj b/test/erv/beating_analyzer/v1_test.clj new file mode 100644 index 0000000..84e4da6 --- /dev/null +++ b/test/erv/beating_analyzer/v1_test.clj @@ -0,0 +1,75 @@ +(ns erv.beating-analyzer.v1-test + (:require + [clojure.edn :as edn] + [clojure.set :as set] + [clojure.test :refer [deftest is]] + [erv.beating-analyzer.v1 :as subject] + [erv.utils.exact :as exact.utils])) + +(deftest get-beat-data-test + (let [data (subject/get-beat-data 2 1 (range 1 6) + [1 5/4 3/2])] + #_(is (= [{:root-hz 64, + :degree-1 0, + :ratio-2-partial "1", + :degree-2 2, + :beat-freq.ratio "32", + :beat-freq.factors {:numer ["2" "2" "2" "2" "2"], :denom []}, + :root "B0+62", + :ratio-1 "1", + :ratio-1-partial "1", + :period 1, + :ratio-2 "3/2", + :beat-freq.hz 32}] + + (->> data + (drop 99) + (take 1) + (exact.utils/make-readable)))) + (is (= 30 (count data))))) + +(subject/get-beat-data 2 1 (range 1 6) [1 5/4 3/2]) + +(comment + + (def clj (->> + (subject/get-beat-data 2 1 (range 1 6) [1 5/4 3/2]) + (exact.utils/make-readable) + (into #{}))) + (-> clj count) + (->> + (subject/get-beat-data 2 1 (range 1 6) [1 5/4 3/2]) + (exact.utils/make-readable) + frequencies + vals + (sort) + reverse) + + (->> (edn/read-string (slurp "test_data.edn")) + (map #(-> % (update :beat-freq.hz float))) + (exact.utils/make-readable) + frequencies + vals + count) + + (def cljs (->> (edn/read-string (slurp "test_data.edn")) + (map #(-> % (update :beat-freq.hz float))) + (exact.utils/make-readable) + (into #{}))) + (->> clj (map #(:beat-freq.hz %))) + (->> clj (filter #(zero? (:beat-freq.hz %)))) + (->> clj (filter + (fn [%] + (and + (= (% :degree-1) "0") , + (= (% :degree-2) "2") , + (= (% :ratio-2-partial) "2") , + #_(= (% :ratio-1) "1") , + (= (% :ratio-1-partial) "3") , + (= (% :period) "1") , + #_(= (% :ratio-2) "3/2"))))) + (-> clj first) + (-> cljs first) + (set/difference cljs clj) + + (-> 90/72)) diff --git a/test/erv/beating_analyzer/v1_test.cljs b/test/erv/beating_analyzer/v1_test.cljs new file mode 100644 index 0000000..3d4f88a --- /dev/null +++ b/test/erv/beating_analyzer/v1_test.cljs @@ -0,0 +1,38 @@ +(ns erv.beating-analyzer.v1-test + (:require + [cljs.pprint :as pprint] + [clojure.test :refer [deftest is]] + [erv.beating-analyzer.v1 :as subject] + [erv.utils.exact :as exact.utils])) + +(deftest get-beat-data-test + (let [data (subject/get-beat-data + (exact.utils/->exact 2) + (exact.utils/->exact 1) + (map exact.utils/->exact (range 1 6)) + (exact.utils/parse-ratios "1 5/4 3/2"))] + (is (= [{:root-hz 32, + :degree-1 0, + :ratio-2-partial "1", + :degree-2 1, + :beat-freq.ratio "8", + :beat-freq.factors {:numer ["2" "2" "2"], :denom []}, + :root "B0+62", + :ratio-1 "1", + :ratio-1-partial "1", + :period 0, + :ratio-2 "5/4", + :beat-freq.hz 8}] + (->> data + (take 1) + (exact.utils/make-readable)))) + (is (= 30 (count data))))) + +#_(pprint/pprint + (first + (exact.utils/make-readable + (subject/get-beat-data + (exact.utils/->exact 2) + (exact.utils/->exact 1) + (map exact.utils/->exact (range 1 6)) + (exact.utils/parse-ratios "1 5/4 3/2"))))) diff --git a/test/erv/constant_structures/core_test.clj b/test/erv/constant_structures/core_test.clj new file mode 100644 index 0000000..a4abd51 --- /dev/null +++ b/test/erv/constant_structures/core_test.clj @@ -0,0 +1,84 @@ +(ns erv.constant-structures.core-test + (:require + [clojure.test :refer [deftest is]] + [erv.constant-structures.core :refer [analyze]] + [erv.cps.core :as cps])) + +(deftest analyze-test + (is (= '{:constant-structure? true, + :interval-data + ([143/140 + {:intervals ({:interval (35/32 143/128), :steps 1}), :steps #{1}}] + [14/13 + {:intervals + ({:interval (65/64 35/32), :steps 1} + {:interval (143/128 77/64), :steps 1}), + :steps #{1}}] + [11/10 + {:intervals + ({:interval (65/64 143/128), :steps 2} + {:interval (35/32 77/64), :steps 2}), + :steps #{2}}] + [13/11 + {:intervals + ({:interval (55/32 65/64), :steps -5} + {:interval (77/64 91/64), :steps 1}), + :steps #{1}}] + [77/65 + {:intervals ({:interval (65/64 77/64), :steps 3}), :steps #{3}}] + [110/91 + {:intervals ({:interval (91/64 55/32), :steps 1}), :steps #{1}}] + [14/11 + {:intervals + ({:interval (55/32 35/32), :steps -4} + {:interval (143/128 91/64), :steps 2}), + :steps #{2}}] + [13/10 + {:intervals + ({:interval (35/32 91/64), :steps 3} + {:interval (55/32 143/128), :steps -3}), + :steps #{3}}] + [7/5 + {:intervals + ({:interval (65/64 91/64), :steps 4} + {:interval (55/32 77/64), :steps -2}), + :steps #{4}}] + [10/7 + {:intervals + ({:interval (91/64 65/64), :steps -4} + {:interval (77/64 55/32), :steps 2}), + :steps #{2}}] + [20/13 + {:intervals + ({:interval (91/64 35/32), :steps -3} + {:interval (143/128 55/32), :steps 3}), + :steps #{3}}] + [11/7 + {:intervals + ({:interval (35/32 55/32), :steps 4} + {:interval (91/64 143/128), :steps -2}), + :steps #{4}}] + [91/55 + {:intervals ({:interval (55/32 91/64), :steps -1}), :steps #{5}}] + [130/77 + {:intervals ({:interval (77/64 65/64), :steps -3}), :steps #{3}}] + [22/13 + {:intervals + ({:interval (65/64 55/32), :steps 5} + {:interval (91/64 77/64), :steps -1}), + :steps #{5}}] + [20/11 + {:intervals + ({:interval (143/128 65/64), :steps -2} + {:interval (77/64 35/32), :steps -2}), + :steps #{4}}] + [13/7 + {:intervals + ({:interval (35/32 65/64), :steps -1} + {:interval (77/64 143/128), :steps -1}), + :steps #{5}}] + [280/143 + {:intervals ({:interval (143/128 35/32), :steps -1}), + :steps #{5}}]), + :non-cs-intervals {:intervals (), :total 0}} + (analyze (:scale (cps/make 2 [11 13 5 7])))))) diff --git a/test/erv/constant_structures/core_test.cljs b/test/erv/constant_structures/core_test.cljs new file mode 100644 index 0000000..818f791 --- /dev/null +++ b/test/erv/constant_structures/core_test.cljs @@ -0,0 +1,85 @@ +(ns erv.constant-structures.core-test + (:require + [clojure.test :refer [deftest is]] + [erv.constant-structures.core :refer [analyze]] + [erv.cps.core :as cps] + [erv.utils.exact :as exact.utils])) + +(deftest analyze-test + (is (= '{:interval-data + (["143/140" + {:steps #{1}, :intervals ({:steps 1, :interval ("35/32" "143/128")})}] + ["14/13" + {:steps #{1}, + :intervals + ({:steps 1, :interval ("65/64" "35/32")} + {:steps 1, :interval ("143/128" "77/64")})}] + ["11/10" + {:steps #{2}, + :intervals + ({:steps 2, :interval ("65/64" "143/128")} + {:steps 2, :interval ("35/32" "77/64")})}] + ["13/11" + {:steps #{1}, + :intervals + ({:steps -5, :interval ("55/32" "65/64")} + {:steps 1, :interval ("77/64" "91/64")})}] + ["77/65" {:steps #{3}, :intervals ({:steps 3, :interval ("65/64" "77/64")})}] + ["110/91" + {:steps #{1}, :intervals ({:steps 1, :interval ("91/64" "55/32")})}] + ["14/11" + {:steps #{2}, + :intervals + ({:steps -4, :interval ("55/32" "35/32")} + {:steps 2, :interval ("143/128" "91/64")})}] + ["13/10" + {:steps #{3}, + :intervals + ({:steps 3, :interval ("35/32" "91/64")} + {:steps -3, :interval ("55/32" "143/128")})}] + ["7/5" + {:steps #{4}, + :intervals + ({:steps 4, :interval ("65/64" "91/64")} + {:steps -2, :interval ("55/32" "77/64")})}] + ["10/7" + {:steps #{2}, + :intervals + ({:steps -4, :interval ("91/64" "65/64")} + {:steps 2, :interval ("77/64" "55/32")})}] + ["20/13" + {:steps #{3}, + :intervals + ({:steps -3, :interval ("91/64" "35/32")} + {:steps 3, :interval ("143/128" "55/32")})}] + ["11/7" + {:steps #{4}, + :intervals + ({:steps 4, :interval ("35/32" "55/32")} + {:steps -2, :interval ("91/64" "143/128")})}] + ["91/55" + {:steps #{5}, :intervals ({:steps -1, :interval ("55/32" "91/64")})}] + ["130/77" + {:steps #{3}, :intervals ({:steps -3, :interval ("77/64" "65/64")})}] + ["22/13" + {:steps #{5}, + :intervals + ({:steps 5, :interval ("65/64" "55/32")} + {:steps -1, :interval ("91/64" "77/64")})}] + ["20/11" + {:steps #{4}, + :intervals + ({:steps -2, :interval ("143/128" "65/64")} + {:steps -2, :interval ("77/64" "35/32")})}] + ["13/7" + {:steps #{5}, + :intervals + ({:steps -1, :interval ("35/32" "65/64")} + {:steps -1, :interval ("77/64" "143/128")})}] + ["280/143" + {:steps #{5}, :intervals ({:steps -1, :interval ("143/128" "35/32")})}]), + :non-cs-intervals {:total 0, :intervals ()}, + :constant-structure? true} + (->> (analyze + (:scale (cps/make 2 [11 13 5 7]))) + (exact.utils/make-readable))))) diff --git a/test/erv/cps/core_test.cljs b/test/erv/cps/core_test.cljs new file mode 100644 index 0000000..22fbb76 --- /dev/null +++ b/test/erv/cps/core_test.cljs @@ -0,0 +1,401 @@ +(ns erv.cps.core-test + (:require + [cljs.test :refer [deftest is]] + [erv.cps.core :as cps] + [erv.utils.exact :as exact.utils])) + +(deftest make-test + (is (= '{:meta + {:scale :cps, + :period 2, + :size 3, + :cps/size 2, + :cps/factors (1 3 5), + :cps/normalized-by 1, + :cps/type "2)3"}, + :scale + ({:set #{1 5}, + :archi-set #{:c :a}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2, + :degree 0} + {:set #{1 3}, + :archi-set #{:b :a}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2, + :degree 1} + {:set #{3 5}, + :archi-set #{:c :b}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2, + :degree 2}), + :nodes + ({:set #{1 3}, + :archi-set #{:b :a}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + {:set #{1 5}, + :archi-set #{:c :a}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2} + {:set #{3 5}, + :archi-set #{:c :b}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :graphs + {:full + {{:set #{1 3}, + :archi-set #{:b :a}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + #{{:set #{3 5}, + :archi-set #{:c :b}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2} + {:set #{1 5}, + :archi-set #{:c :a}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}}, + {:set #{1 5}, + :archi-set #{:c :a}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2} + #{{:set #{3 5}, + :archi-set #{:c :b}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2} + {:set #{1 3}, + :archi-set #{:b :a}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}}, + {:set #{3 5}, + :archi-set #{:c :b}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2} + #{{:set #{1 3}, + :archi-set #{:b :a}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + {:set #{1 5}, + :archi-set #{:c :a}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}}}, + :simple + {#{1 3} #{#{3 5} #{1 5}}, #{1 5} #{#{3 5} #{1 3}}, #{3 5} #{#{1 5} #{1 3}}}}, + :subcps + {"1)1 of 2)3 3.5" + {:meta + {:scale :cps, + :period 2, + :size 1, + :cps/size 2, + :cps/factors (3 5), + :cps/normalized-by 1, + :cps/type "1)1 of 2)3"}, + :scale + ({:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :nodes + ({:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :graphs {:full {}, :simple {}}}, + "1)1 of 2)3 1.5" + {:meta + {:scale :cps, + :period 2, + :size 1, + :cps/size 2, + :cps/factors (1 5), + :cps/normalized-by 1, + :cps/type "1)1 of 2)3"}, + :scale + ({:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}), + :nodes + ({:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}), + :graphs {:full {}, :simple {}}}, + "2)2 of 2)3 1.3" + {:meta + {:scale :cps, + :period 2, + :size 1, + :cps/size 2, + :cps/factors (1 3), + :cps/normalized-by 1, + :cps/type "2)2 of 2)3"}, + :scale + ({:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}), + :nodes + ({:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}), + :graphs {:full {}, :simple {}}}, + "1)2 of 2)3 5-1.3" + {:meta + {:scale :cps, + :period 2, + :size 2, + :cps/size 2, + :cps/factors (1 3 5), + :cps/normalized-by 1, + :cps/type "1)2 of 2)3"}, + :scale + ({:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2} + {:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :nodes + ({:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2} + {:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :graphs + {:full + {{:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2} + #{{:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}}, + {:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2} + #{{:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}}}, + :simple {#{1 5} #{#{3 5}}, #{3 5} #{#{1 5}}}}}, + "1)2 of 2)3 3-1.5" + {:meta + {:scale :cps, + :period 2, + :size 2, + :cps/size 2, + :cps/factors (1 3 5), + :cps/normalized-by 1, + :cps/type "1)2 of 2)3"}, + :scale + ({:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + {:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :nodes + ({:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + {:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :graphs + {:full + {{:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + #{{:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}}, + {:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2} + #{{:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}}}, + :simple {#{1 3} #{#{3 5}}, #{3 5} #{#{1 3}}}}}, + "2)2 of 2)3 1.5" + {:meta + {:scale :cps, + :period 2, + :size 1, + :cps/size 2, + :cps/factors (1 5), + :cps/normalized-by 1, + :cps/type "2)2 of 2)3"}, + :scale + ({:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}), + :nodes + ({:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}), + :graphs {:full {}, :simple {}}}, + "1)2 of 2)3 1-3.5" + {:meta + {:scale :cps, + :period 2, + :size 2, + :cps/size 2, + :cps/factors (1 3 5), + :cps/normalized-by 1, + :cps/type "1)2 of 2)3"}, + :scale + ({:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2} + {:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}), + :nodes + ({:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + {:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}), + :graphs + {:full + {{:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2} + #{{:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2}}, + {:set #{1 5}, + :archi-set #{nil}, + :ratio "5", + :bounded-ratio "5/4", + :bounding-period 2} + #{{:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}}}, + :simple {#{1 3} #{#{1 5}}, #{1 5} #{#{1 3}}}}}, + "2)2 of 2)3 3.5" + {:meta + {:scale :cps, + :period 2, + :size 1, + :cps/size 2, + :cps/factors (3 5), + :cps/normalized-by 1, + :cps/type "2)2 of 2)3"}, + :scale + ({:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :nodes + ({:set #{3 5}, + :archi-set #{nil}, + :ratio "15", + :bounded-ratio "15/8", + :bounding-period 2}), + :graphs {:full {}, :simple {}}}, + "1)1 of 2)3 1.3" + {:meta + {:scale :cps, + :period 2, + :size 1, + :cps/size 2, + :cps/factors (1 3), + :cps/normalized-by 1, + :cps/type "1)1 of 2)3"}, + :scale + ({:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}), + :nodes + ({:set #{1 3}, + :archi-set #{nil}, + :ratio "3", + :bounded-ratio "3/2", + :bounding-period 2}), + :graphs {:full {}, :simple {}}}}} + (->> (cps/make 2 [1 3 5]) + (cps/+all-subcps) + exact.utils/make-readable)))) + +(->> (cps/make 2 [1 3 5]) + (cps/+all-subcps) + exact.utils/make-readable) diff --git a/test/erv/edo/core_test.cljc b/test/erv/edo/core_test.cljc new file mode 100644 index 0000000..6229a0b --- /dev/null +++ b/test/erv/edo/core_test.cljc @@ -0,0 +1,80 @@ +(ns erv.edo.core-test + (:require + [clojure.math :refer [round]] + [clojure.test :refer [deftest is]] + [erv.edo.core :refer [from-pattern]] + [erv.utils.conversions :refer [ratio->cents]])) + +(deftest from-pattern-test + (is (= #?(:clj + {:meta {:edo/pattern [2 2 1 2 2 2 1] + :edo/divisions 12 + :edo/period 2}, + :scale + [{:bounded-ratio 1.0, + :bounding-period 2, + :edo/degree 0, + :edo/original-degree 0} + {:bounded-ratio 1.122462048309373, + :bounding-period 2, + :edo/degree 1, + :edo/original-degree 2} + {:bounded-ratio 1.2599210498948732, + :bounding-period 2, + :edo/degree 2, + :edo/original-degree 4} + {:bounded-ratio 1.3348398541700344, + :bounding-period 2, + :edo/degree 3, + :edo/original-degree 5} + {:bounded-ratio 1.4983070768766813, + :bounding-period 2, + :edo/degree 4, + :edo/original-degree 7} + {:bounded-ratio 1.6817928305074292, + :bounding-period 2, + :edo/degree 5, + :edo/original-degree 9} + {:bounded-ratio 1.887748625363387, + :bounding-period 2, + :edo/degree 6, + :edo/original-degree 11}]} + ;;NOTE slightly different value on cljs + :cljs + {:meta {:edo/pattern [2 2 1 2 2 2 1] + :edo/divisions 12 + :edo/period 2} + :scale [{:edo/original-degree 0 + :edo/degree 0 + :bounded-ratio 1 + :bounding-period 2} + {:edo/original-degree 2 + :edo/degree 1 + :bounded-ratio 1.1224620483093728 + :bounding-period 2} + {:edo/original-degree 4 + :edo/degree 2 + :bounded-ratio 1.2599210498948732 + :bounding-period 2} + {:edo/original-degree 5 + :edo/degree 3 + :bounded-ratio 1.3348398541700344 + :bounding-period 2} + {:edo/original-degree 7 + :edo/degree 4 + :bounded-ratio 1.4983070768766815 + :bounding-period 2} + {:edo/original-degree 9 + :edo/degree 5 + :bounded-ratio 1.6817928305074292 + :bounding-period 2} + {:edo/original-degree 11 + :edo/degree 6 + :bounded-ratio 1.8877486253633868 + :bounding-period 2}]}) + + (from-pattern [2 2 1 2 2 2 1]))) + (is (= [0 200 400 500 700 900 1100] + (->> (from-pattern [2 2 1 2 2 2 1]) + :scale + (map (comp round ratio->cents :bounded-ratio)))))) diff --git a/test/erv/lattice/v2_test.cljc b/test/erv/lattice/v2_test.clj similarity index 100% rename from test/erv/lattice/v2_test.cljc rename to test/erv/lattice/v2_test.clj diff --git a/test/erv/lattice/v2_test.cljs b/test/erv/lattice/v2_test.cljs new file mode 100644 index 0000000..b87f85f --- /dev/null +++ b/test/erv/lattice/v2_test.cljs @@ -0,0 +1,38 @@ +(ns erv.lattice.v2-test + (:require + [clojure.test :refer [deftest is]] + [erv.lattice.v2 :refer [base-coords ratios->lattice-data]] + [erv.utils.exact :as exact.utils])) + +(deftest ratios->lattice-data-test + (is (= '{:period "2", + :min-x 0, + :max-x 0, + :min-y 0, + :max-y 0, + :data + ({:ratio "3/2", + :numerator "3", + :denominator "2", + :numer-factors ["3"], + :denom-factors ["2"], + :coords {:x 0, :y 0}} + {:ratio "9/8", + :numerator "9", + :denominator "8", + :numer-factors ["3" "3"], + :denom-factors ["2" "2" "2"], + :coords {:x 0, :y 0}} + {:ratio "2", + :numerator "2", + :denominator "1", + :numer-factors ["2"], + :denom-factors [], + :coords {:x 0, :y 0}}), + :edges (({:x 0, :y 0} {:x 0, :y 0}) + ({:x 0, :y 0} {:x 0, :y 0}))} + + (->> "3/2 9/8 2/1" + exact.utils/parse-ratios + (ratios->lattice-data base-coords) + exact.utils/make-readable)))) diff --git a/test/erv/math/pascals_triangle_test.clj b/test/erv/math/pascals_triangle_test.clj new file mode 100644 index 0000000..c14053b --- /dev/null +++ b/test/erv/math/pascals_triangle_test.clj @@ -0,0 +1,18 @@ +(ns erv.math.pascals-triangle-test + (:require + [clojure.test :refer [deftest is]] + [erv.math.pascals-triangle :as subject])) + +(deftest make-test + (is (= [[1] + [1 1] + [1 2 1] + [1 3 3 1] + [1 4 6 4 1] + [1 5 10 10 5 1] + [1 6 15 20 15 6 1] + [1 7 21 35 35 21 7 1] + [1 8 28 56 70 56 28 8 1] + [1 9 36 84 126 126 84 36 9 1] + [1 10 45 120 210 252 210 120 45 10 1]] + (subject/make 10)))) diff --git a/test/erv/meru/core_test.clj b/test/erv/meru/core_test.clj index 6768a92..7f4e0e2 100644 --- a/test/erv/meru/core_test.clj +++ b/test/erv/meru/core_test.clj @@ -1,21 +1,26 @@ (ns erv.meru.core-test (:require [clojure.test :refer [deftest is testing]] - [erv.meru.core :refer [recurrent-series]])) + [erv.meru.core :as subject])) -(deftest recurrent-series-test - (let [meta-slendro-series - {:convergence-double 1.324717957244746, - :convergence 53406819691/40315615410, - :converges-at 84, - :series [1 1 1 2 2 3 4 5 7 9 12 16 21 28 37 49 65 86 114 151 200 265 351 465 616 816 1081 1432 1897 2513 3329 4410 5842 7739 10252 13581 17991 23833 31572 41824 55405 73396 97229 128801 170625 226030 299426 396655 525456 696081 922111 1221537 1618192 2143648 2839729 3761840 4983377 6601569 8745217 11584946 15346786 20330163 26931732 35676949 47261895 62608681 82938844 109870576 145547525 192809420 255418101 338356945 448227521 593775046 786584466 1042002567 1380359512 1828587033 2422362079 3208946545 4250949112 5631308624 7459895657 9882257736 13091204281 17342153393 22973462017 30433357674 40315615410 53406819691]}] - (testing "Can use a `:seed` and a `:formula`" - (is (= meta-slendro-series - (recurrent-series {:seed [1 1 1] - :formula :meta-slendro})))) - (testing "Can use a `:seed` and a custom configuration of indexes and an operation for apply the values of the indexes." - (is (= meta-slendro-series - (recurrent-series {:seed [1 1 1] - :i1 2 - :i2 3 - :f (fn [a b] (+ a b))})))))) +(deftest convergence-mos-data-summary-test + (is (= [{:size 2, + :mos/pattern.name "1s1L", + :mos/sL-ratio.float (float 2.2702353), + :mos/s.cents 366.9460706785318, + :mos/L.cents 833.0539293214689} + {:size 3, + :mos/pattern.name "2s1L", + :mos/sL-ratio.float (float 1.2702353), + :mos/s.cents 366.9460706785318, + :mos/L.cents 466.10785864293723} + {:size 4, + :mos/pattern.name "1s3L", + :mos/sL-ratio.float (float 3.7004786), + :mos/s.cents 99.16178796440605, + :mos/L.cents 366.9460706785318}] + (subject/convergence-mos-data-summary + {:max-size 4 + :period 2} + (subject/diagonals {:size 20 + :slope {:x 1 :y 2}}))))) diff --git a/test/erv/meru/diagonals_test.clj b/test/erv/meru/diagonals_test.clj new file mode 100644 index 0000000..8bc87e3 --- /dev/null +++ b/test/erv/meru/diagonals_test.clj @@ -0,0 +1,52 @@ +(ns erv.meru.diagonals-test + (:require + [clojure.test :refer [deftest is testing]] + [erv.math.pascals-triangle :as pascals-triangle] + [erv.meru.diagonals :as subject] + [erv.types :refer [MeruDiagonalsData]] + [malli.core :as m])) + +(deftest diagonals-test + (testing "The Malli type is up to date" + (let [data (subject/diagonals + {:size 12 + :slope {:x 1 :y 2} + :pascal-coord->number pascals-triangle/default-coord-map})] + (is (m/validate MeruDiagonalsData data) + (m/explain MeruDiagonalsData data)))) + (testing "Has a `:series` key" + (is (= [1 1N 2N 3N 5N 8N 13N 21N 34N 55N 89N 144N] + (->> (subject/diagonals + {:size 12 + :slope {:x 1 :y 2} + :pascal-coord->number pascals-triangle/default-coord-map}) + :series)))) + (testing "Can work with a custom `pascal-coord->number function (or map)" + (is (= [1 1N 2N 3N 5N 8N 13N 21N 34N 55N 89N 144N] + (->> (subject/diagonals + {:size 12 + :slope {:x 1 :y 2} + :pascal-coord->number (pascals-triangle/make-coord-map 1 1 30)}) + :series)))) + (testing "Has a `:convergence-double` and a `:convergence-double-with-precision` key" + (is (= [1.617977528089888 1.618] + (->> (subject/diagonals + {:size 12 + :slope {:x 1 :y 2} + :convergence-precision 3 + :pascal-coord->number pascals-triangle/default-coord-map}) + ((juxt :convergence-double :convergence-double-with-precision)))))) + (testing "May have a `:triangle-seed` key, specially if `:pascal-coord->number` is `pascals-triangle/default-coord-map` or was created with `pascals-triangle/make-coord-map`." + (is (= {:left 1, :right 1} + (->> (subject/diagonals + {:size 12 + :slope {:x 1 :y 2} + :convergence-precision 3 + :pascal-coord->number pascals-triangle/default-coord-map}) + :triangle-seed) + (->> (subject/diagonals + {:size 12 + :slope {:x 1 :y 2} + :convergence-precision 3 + :pascal-coord->number (pascals-triangle/make-coord-map 1 1 30)}) + :triangle-seed))))) diff --git a/test/erv/meru/recurrent_series_test.clj b/test/erv/meru/recurrent_series_test.clj new file mode 100644 index 0000000..a222a3e --- /dev/null +++ b/test/erv/meru/recurrent_series_test.clj @@ -0,0 +1,32 @@ +(ns erv.meru.recurrent-series-test + (:require + [clojure.test :refer [deftest is testing]] + [erv.math.pascals-triangle :as pascals-triangle] + [erv.meru.recurrent-series :as subject] + [erv.types :refer [MeruRecurrentSeriesData]] + [malli.core :as m])) + +(deftest recurrent-series-test + (testing "The Malli type is up to date" + (let [data (subject/recurrent-series + {:seed [1 1 1] + :formula :meta-slendro})] + (is (m/validate MeruRecurrentSeriesData data) + (m/explain MeruRecurrentSeriesData data)))) + (testing "Basic usage" + (let [meta-slendro-series {:convergence-double 1.324717957244746, + :convergence 53406819691/40315615410, + :convergence-index 84, + :series [1 1 1 2 2 3 4 5 7 9 12 16 21 28 37 49 65 86 114 151 200 265 351 465 616 816 1081 1432 1897 2513 3329 4410 5842 7739 10252 13581 17991 23833 31572 41824 55405 73396 97229 128801 170625 226030 299426 396655 525456 696081 922111 1221537 1618192 2143648 2839729 3761840 4983377 6601569 8745217 11584946 15346786 20330163 26931732 35676949 47261895 62608681 82938844 109870576 145547525 192809420 255418101 338356945 448227521 593775046 786584466 1042002567 1380359512 1828587033 2422362079 3208946545 4250949112 5631308624 7459895657 9882257736 13091204281 17342153393 22973462017 30433357674 40315615410 53406819691]}] + (testing "Can use a `:seed` and a `:formula`" + (is (= meta-slendro-series + (-> (subject/recurrent-series {:seed [1 1 1] + :formula :meta-slendro}) + (select-keys [:convergence-double :convergence :convergence-index :series]))))) + (testing "Can use a `:seed` and a custom configuration of indexes and an operation for apply the values of the indexes." + (is (= meta-slendro-series + (-> (subject/recurrent-series {:seed [1 1 1] + :i1 2 + :i2 3 + :f (fn [a b] (+ a b))}) + (select-keys [:convergence-double :convergence :convergence-index :series])))))))) diff --git a/test/erv/mos/mos_test.cljc b/test/erv/mos/mos_test.cljc new file mode 100644 index 0000000..4bbc7b2 --- /dev/null +++ b/test/erv/mos/mos_test.cljc @@ -0,0 +1,13 @@ +(ns erv.mos.mos-test + (:require + [clojure.test :refer [deftest is]] + [erv.mos.mos :as subject])) + +(deftest make-test + (is (= [[12] + [7 5] + [2 5 5] + [2 2 3 2 3] + [2 2 2 1 2 2 1] + [1 1 1 1 1 1 1 1 1 1 1 1]] + (subject/make 12 7)))) diff --git a/test/erv/mos/submos_test.cljc b/test/erv/mos/submos_test.cljc new file mode 100644 index 0000000..22a10fe --- /dev/null +++ b/test/erv/mos/submos_test.cljc @@ -0,0 +1,729 @@ +(ns erv.mos.submos-test + (:require + [clojure.test :refer [deftest is]] + [erv.mos.submos :as subject])) + +(deftest make-all-submos-test + (let [all-submos '({:generator 1, + :pattern [1 6], + :period 7, + :submos + ({:degree 0, + :mos [1 11], + :mos-degrees [0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 11]}} + {:degree 1, + :mos [2 10], + :mos-degrees [1 2], + :rotation {:at-zero? false, :degree 1, :mos [2 10]}} + {:degree 2, + :mos [2 10], + :mos-degrees [2 3], + :rotation {:at-zero? false, :degree 2, :mos [2 10]}} + {:degree 3, + :mos [1 11], + :mos-degrees [3 4], + :rotation {:at-zero? false, :degree 3, :mos [1 11]}} + {:degree 4, + :mos [2 10], + :mos-degrees [4 5], + :rotation {:at-zero? false, :degree 4, :mos [2 10]}} + {:degree 5, + :mos [2 10], + :mos-degrees [5 6], + :rotation {:at-zero? false, :degree 5, :mos [2 10]}} + {:degree 6, + :mos [2 10], + :mos-degrees [6 0], + :rotation {:at-zero? true, :degree 0, :mos [10 2]}}), + :submos-by-mos + {[1 11] + [{:degree 0, + :mos [1 11], + :mos-degrees [0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 11]}} + {:degree 3, + :mos [1 11], + :mos-degrees [3 4], + :rotation {:at-zero? false, :degree 3, :mos [1 11]}}], + [2 10] + [{:degree 1, + :mos [2 10], + :mos-degrees [1 2], + :rotation {:at-zero? false, :degree 1, :mos [2 10]}} + {:degree 2, + :mos [2 10], + :mos-degrees [2 3], + :rotation {:at-zero? false, :degree 2, :mos [2 10]}} + {:degree 4, + :mos [2 10], + :mos-degrees [4 5], + :rotation {:at-zero? false, :degree 4, :mos [2 10]}} + {:degree 5, + :mos [2 10], + :mos-degrees [5 6], + :rotation {:at-zero? false, :degree 5, :mos [2 10]}} + {:degree 6, + :mos [2 10], + :mos-degrees [6 0], + :rotation {:at-zero? true, :degree 0, :mos [10 2]}}]}, + :true-submos? false} + {:generator 1, + :pattern [1 1 5], + :period 7, + :submos + ({:degree 0, + :mos [1 2 9], + :mos-degrees [0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 9]}} + {:degree 1, + :mos [2 2 8], + :mos-degrees [1 2 3], + :rotation {:at-zero? false, :degree 1, :mos [2 2 8]}} + {:degree 2, + :mos [2 1 9], + :mos-degrees [2 3 4], + :rotation {:at-zero? false, :degree 2, :mos [2 1 9]}} + {:degree 3, + :mos [1 2 9], + :mos-degrees [3 4 5], + :rotation {:at-zero? false, :degree 3, :mos [1 2 9]}} + {:degree 4, + :mos [2 2 8], + :mos-degrees [4 5 6], + :rotation {:at-zero? false, :degree 4, :mos [2 2 8]}} + {:degree 5, + :mos [2 2 8], + :mos-degrees [5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [8 2 2]}} + {:degree 6, + :mos [2 1 9], + :mos-degrees [6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 9 2]}}), + :submos-by-mos + {[1 2 9] + [{:degree 0, + :mos [1 2 9], + :mos-degrees [0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 9]}} + {:degree 3, + :mos [1 2 9], + :mos-degrees [3 4 5], + :rotation {:at-zero? false, :degree 3, :mos [1 2 9]}}], + [2 1 9] + [{:degree 2, + :mos [2 1 9], + :mos-degrees [2 3 4], + :rotation {:at-zero? false, :degree 2, :mos [2 1 9]}} + {:degree 6, + :mos [2 1 9], + :mos-degrees [6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 9 2]}}], + [2 2 8] + [{:degree 1, + :mos [2 2 8], + :mos-degrees [1 2 3], + :rotation {:at-zero? false, :degree 1, :mos [2 2 8]}} + {:degree 4, + :mos [2 2 8], + :mos-degrees [4 5 6], + :rotation {:at-zero? false, :degree 4, :mos [2 2 8]}} + {:degree 5, + :mos [2 2 8], + :mos-degrees [5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [8 2 2]}}]}, + :true-submos? false} + {:generator 1, + :pattern [1 1 1 4], + :period 7, + :submos + ({:degree 0, + :mos [1 2 2 7], + :mos-degrees [0 1 2 3], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 7]}} + {:degree 1, + :mos [2 2 1 7], + :mos-degrees [1 2 3 4], + :rotation {:at-zero? false, :degree 1, :mos [2 2 1 7]}} + {:degree 2, + :mos [2 1 2 7], + :mos-degrees [2 3 4 5], + :rotation {:at-zero? false, :degree 2, :mos [2 1 2 7]}} + {:degree 3, + :mos [1 2 2 7], + :mos-degrees [3 4 5 6], + :rotation {:at-zero? false, :degree 3, :mos [1 2 2 7]}} + {:degree 4, + :mos [2 2 2 6], + :mos-degrees [4 5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [6 2 2 2]}} + {:degree 5, + :mos [2 2 1 7], + :mos-degrees [5 6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 7 2 2]}} + {:degree 6, + :mos [2 1 2 7], + :mos-degrees [6 0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 7 2]}}), + :submos-by-mos + {[1 2 2 7] + [{:degree 0, + :mos [1 2 2 7], + :mos-degrees [0 1 2 3], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 7]}} + {:degree 3, + :mos [1 2 2 7], + :mos-degrees [3 4 5 6], + :rotation {:at-zero? false, :degree 3, :mos [1 2 2 7]}}], + [2 1 2 7] + [{:degree 2, + :mos [2 1 2 7], + :mos-degrees [2 3 4 5], + :rotation {:at-zero? false, :degree 2, :mos [2 1 2 7]}} + {:degree 6, + :mos [2 1 2 7], + :mos-degrees [6 0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 7 2]}}], + [2 2 1 7] + [{:degree 1, + :mos [2 2 1 7], + :mos-degrees [1 2 3 4], + :rotation {:at-zero? false, :degree 1, :mos [2 2 1 7]}} + {:degree 5, + :mos [2 2 1 7], + :mos-degrees [5 6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 7 2 2]}}], + [2 2 2 6] + [{:degree 4, + :mos [2 2 2 6], + :mos-degrees [4 5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [6 2 2 2]}}]}, + :true-submos? false} + {:generator 1, + :pattern [1 1 1 1 3], + :period 7, + :submos + ({:degree 0, + :mos [1 2 2 1 6], + :mos-degrees [0 1 2 3 4], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 1 6]}} + {:degree 1, + :mos [2 2 1 2 5], + :mos-degrees [1 2 3 4 5], + :rotation {:at-zero? false, :degree 1, :mos [2 2 1 2 5]}} + {:degree 2, + :mos [2 1 2 2 5], + :mos-degrees [2 3 4 5 6], + :rotation {:at-zero? false, :degree 2, :mos [2 1 2 2 5]}} + {:degree 3, + :mos [1 2 2 2 5], + :mos-degrees [3 4 5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [5 1 2 2 2]}} + {:degree 4, + :mos [2 2 2 1 5], + :mos-degrees [4 5 6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 5 2 2 2]}} + {:degree 5, + :mos [2 2 1 2 5], + :mos-degrees [5 6 0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 5 2 2]}} + {:degree 6, + :mos [2 1 2 2 5], + :mos-degrees [6 0 1 2 3], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 5 2]}}), + :submos-by-mos + {[1 2 2 1 6] + [{:degree 0, + :mos [1 2 2 1 6], + :mos-degrees [0 1 2 3 4], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 1 6]}}], + [1 2 2 2 5] + [{:degree 3, + :mos [1 2 2 2 5], + :mos-degrees [3 4 5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [5 1 2 2 2]}}], + [2 1 2 2 5] + [{:degree 2, + :mos [2 1 2 2 5], + :mos-degrees [2 3 4 5 6], + :rotation {:at-zero? false, :degree 2, :mos [2 1 2 2 5]}} + {:degree 6, + :mos [2 1 2 2 5], + :mos-degrees [6 0 1 2 3], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 5 2]}}], + [2 2 1 2 5] + [{:degree 1, + :mos [2 2 1 2 5], + :mos-degrees [1 2 3 4 5], + :rotation {:at-zero? false, :degree 1, :mos [2 2 1 2 5]}} + {:degree 5, + :mos [2 2 1 2 5], + :mos-degrees [5 6 0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 5 2 2]}}], + [2 2 2 1 5] + [{:degree 4, + :mos [2 2 2 1 5], + :mos-degrees [4 5 6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 5 2 2 2]}}]}, + :true-submos? false} + {:generator 1, + :pattern [1 1 1 1 1 2], + :period 7, + :submos + ({:degree 0, + :mos [1 2 2 1 2 4], + :mos-degrees [0 1 2 3 4 5], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 1 2 4]}} + {:degree 1, + :mos [2 2 1 2 2 3], + :mos-degrees [1 2 3 4 5 6], + :rotation {:at-zero? false, :degree 1, :mos [2 2 1 2 2 3]}} + {:degree 2, + :mos [2 1 2 2 2 3], + :mos-degrees [2 3 4 5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [3 2 1 2 2 2]}} + {:degree 3, + :mos [1 2 2 2 1 4], + :mos-degrees [3 4 5 6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 2 2 2]}} + {:degree 4, + :mos [2 2 2 1 2 3], + :mos-degrees [4 5 6 0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 3 2 2 2]}} + {:degree 5, + :mos [2 2 1 2 2 3], + :mos-degrees [5 6 0 1 2 3], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 3 2 2]}} + {:degree 6, + :mos [2 1 2 2 1 4], + :mos-degrees [6 0 1 2 3 4], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 1 4 2]}}), + :submos-by-mos + {[1 2 2 1 2 4] + [{:degree 0, + :mos [1 2 2 1 2 4], + :mos-degrees [0 1 2 3 4 5], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 1 2 4]}}], + [1 2 2 2 1 4] + [{:degree 3, + :mos [1 2 2 2 1 4], + :mos-degrees [3 4 5 6 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 2 2 2]}}], + [2 1 2 2 1 4] + [{:degree 6, + :mos [2 1 2 2 1 4], + :mos-degrees [6 0 1 2 3 4], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 1 4 2]}}], + [2 1 2 2 2 3] + [{:degree 2, + :mos [2 1 2 2 2 3], + :mos-degrees [2 3 4 5 6 0], + :rotation {:at-zero? true, :degree 0, :mos [3 2 1 2 2 2]}}], + [2 2 1 2 2 3] + [{:degree 1, + :mos [2 2 1 2 2 3], + :mos-degrees [1 2 3 4 5 6], + :rotation {:at-zero? false, :degree 1, :mos [2 2 1 2 2 3]}} + {:degree 5, + :mos [2 2 1 2 2 3], + :mos-degrees [5 6 0 1 2 3], + :rotation {:at-zero? true, :degree 0, :mos [1 2 2 3 2 2]}}], + [2 2 2 1 2 3] + [{:degree 4, + :mos [2 2 2 1 2 3], + :mos-degrees [4 5 6 0 1 2], + :rotation {:at-zero? true, :degree 0, :mos [1 2 3 2 2 2]}}]}, + :true-submos? false} + {:generator 2, + :pattern [2 5], + :period 7, + :submos + ({:degree 0, + :mos [3 9], + :mos-degrees [0 2], + :rotation {:at-zero? true, :degree 0, :mos [3 9]}} + {:degree 1, + :mos [4 8], + :mos-degrees [1 3], + :rotation {:at-zero? false, :degree 1, :mos [4 8]}} + {:degree 2, + :mos [3 9], + :mos-degrees [2 4], + :rotation {:at-zero? false, :degree 2, :mos [3 9]}} + {:degree 3, + :mos [3 9], + :mos-degrees [3 5], + :rotation {:at-zero? false, :degree 3, :mos [3 9]}} + {:degree 4, + :mos [4 8], + :mos-degrees [4 6], + :rotation {:at-zero? false, :degree 4, :mos [4 8]}} + {:degree 5, + :mos [4 8], + :mos-degrees [5 0], + :rotation {:at-zero? true, :degree 0, :mos [8 4]}} + {:degree 6, + :mos [3 9], + :mos-degrees [6 1], + :rotation {:at-zero? false, :degree 1, :mos [9 3]}}), + :submos-by-mos + {[3 9] + [{:degree 0, + :mos [3 9], + :mos-degrees [0 2], + :rotation {:at-zero? true, :degree 0, :mos [3 9]}} + {:degree 2, + :mos [3 9], + :mos-degrees [2 4], + :rotation {:at-zero? false, :degree 2, :mos [3 9]}} + {:degree 3, + :mos [3 9], + :mos-degrees [3 5], + :rotation {:at-zero? false, :degree 3, :mos [3 9]}} + {:degree 6, + :mos [3 9], + :mos-degrees [6 1], + :rotation {:at-zero? false, :degree 1, :mos [9 3]}}], + [4 8] + [{:degree 1, + :mos [4 8], + :mos-degrees [1 3], + :rotation {:at-zero? false, :degree 1, :mos [4 8]}} + {:degree 4, + :mos [4 8], + :mos-degrees [4 6], + :rotation {:at-zero? false, :degree 4, :mos [4 8]}} + {:degree 5, + :mos [4 8], + :mos-degrees [5 0], + :rotation {:at-zero? true, :degree 0, :mos [8 4]}}]}, + :true-submos? false} + {:generator 2, + :pattern [2 2 3], + :period 7, + :submos + ({:degree 0, + :mos [3 3 6], + :mos-degrees [0 2 4], + :rotation {:at-zero? true, :degree 0, :mos [3 3 6]}} + {:degree 1, + :mos [4 3 5], + :mos-degrees [1 3 5], + :rotation {:at-zero? false, :degree 1, :mos [4 3 5]}} + {:degree 2, + :mos [3 4 5], + :mos-degrees [2 4 6], + :rotation {:at-zero? false, :degree 2, :mos [3 4 5]}} + {:degree 3, + :mos [3 4 5], + :mos-degrees [3 5 0], + :rotation {:at-zero? true, :degree 0, :mos [5 3 4]}} + {:degree 4, + :mos [4 3 5], + :mos-degrees [4 6 1], + :rotation {:at-zero? false, :degree 1, :mos [5 4 3]}} + {:degree 5, + :mos [4 3 5], + :mos-degrees [5 0 2], + :rotation {:at-zero? true, :degree 0, :mos [3 5 4]}} + {:degree 6, + :mos [3 4 5], + :mos-degrees [6 1 3], + :rotation {:at-zero? false, :degree 1, :mos [4 5 3]}}), + :submos-by-mos + {[3 3 6] + [{:degree 0, + :mos [3 3 6], + :mos-degrees [0 2 4], + :rotation {:at-zero? true, :degree 0, :mos [3 3 6]}}], + [3 4 5] + [{:degree 2, + :mos [3 4 5], + :mos-degrees [2 4 6], + :rotation {:at-zero? false, :degree 2, :mos [3 4 5]}} + {:degree 3, + :mos [3 4 5], + :mos-degrees [3 5 0], + :rotation {:at-zero? true, :degree 0, :mos [5 3 4]}} + {:degree 6, + :mos [3 4 5], + :mos-degrees [6 1 3], + :rotation {:at-zero? false, :degree 1, :mos [4 5 3]}}], + [4 3 5] + [{:degree 1, + :mos [4 3 5], + :mos-degrees [1 3 5], + :rotation {:at-zero? false, :degree 1, :mos [4 3 5]}} + {:degree 4, + :mos [4 3 5], + :mos-degrees [4 6 1], + :rotation {:at-zero? false, :degree 1, :mos [5 4 3]}} + {:degree 5, + :mos [4 3 5], + :mos-degrees [5 0 2], + :rotation {:at-zero? true, :degree 0, :mos [3 5 4]}}]}, + :true-submos? false} + {:generator 2, + :pattern [2 2 2 1], + :period 7, + :submos + ({:degree 0, + :mos [3 3 4 2], + :mos-degrees [0 2 4 6], + :rotation {:at-zero? true, :degree 0, :mos [3 3 4 2]}} + {:degree 1, + :mos [4 3 4 1], + :mos-degrees [1 3 5 0], + :rotation {:at-zero? true, :degree 0, :mos [1 4 3 4]}} + {:degree 2, + :mos [3 4 3 2], + :mos-degrees [2 4 6 1], + :rotation {:at-zero? false, :degree 1, :mos [2 3 4 3]}} + {:degree 3, + :mos [3 4 3 2], + :mos-degrees [3 5 0 2], + :rotation {:at-zero? true, :degree 0, :mos [3 2 3 4]}} + {:degree 4, + :mos [4 3 4 1], + :mos-degrees [4 6 1 3], + :rotation {:at-zero? false, :degree 1, :mos [4 1 4 3]}} + {:degree 5, + :mos [4 3 3 2], + :mos-degrees [5 0 2 4], + :rotation {:at-zero? true, :degree 0, :mos [3 3 2 4]}} + {:degree 6, + :mos [3 4 3 2], + :mos-degrees [6 1 3 5], + :rotation {:at-zero? false, :degree 1, :mos [4 3 2 3]}}), + :submos-by-mos + {[3 3 4 2] + [{:degree 0, + :mos [3 3 4 2], + :mos-degrees [0 2 4 6], + :rotation {:at-zero? true, :degree 0, :mos [3 3 4 2]}}], + [3 4 3 2] + [{:degree 2, + :mos [3 4 3 2], + :mos-degrees [2 4 6 1], + :rotation {:at-zero? false, :degree 1, :mos [2 3 4 3]}} + {:degree 3, + :mos [3 4 3 2], + :mos-degrees [3 5 0 2], + :rotation {:at-zero? true, :degree 0, :mos [3 2 3 4]}} + {:degree 6, + :mos [3 4 3 2], + :mos-degrees [6 1 3 5], + :rotation {:at-zero? false, :degree 1, :mos [4 3 2 3]}}], + [4 3 3 2] + [{:degree 5, + :mos [4 3 3 2], + :mos-degrees [5 0 2 4], + :rotation {:at-zero? true, :degree 0, :mos [3 3 2 4]}}], + [4 3 4 1] + [{:degree 1, + :mos [4 3 4 1], + :mos-degrees [1 3 5 0], + :rotation {:at-zero? true, :degree 0, :mos [1 4 3 4]}} + {:degree 4, + :mos [4 3 4 1], + :mos-degrees [4 6 1 3], + :rotation {:at-zero? false, :degree 1, :mos [4 1 4 3]}}]}, + :true-submos? false} + {:generator 3, + :pattern [3 4], + :period 7, + :submos + ({:degree 0, + :mos [5 7], + :mos-degrees [0 3], + :rotation {:at-zero? true, :degree 0, :mos [5 7]}} + {:degree 1, + :mos [5 7], + :mos-degrees [1 4], + :rotation {:at-zero? false, :degree 1, :mos [5 7]}} + {:degree 2, + :mos [5 7], + :mos-degrees [2 5], + :rotation {:at-zero? false, :degree 2, :mos [5 7]}} + {:degree 3, + :mos [5 7], + :mos-degrees [3 6], + :rotation {:at-zero? false, :degree 3, :mos [5 7]}} + {:degree 4, + :mos [6 6], + :mos-degrees [4 0], + :rotation {:at-zero? true, :degree 0, :mos [6 6]}} + {:degree 5, + :mos [5 7], + :mos-degrees [5 1], + :rotation {:at-zero? false, :degree 1, :mos [7 5]}} + {:degree 6, + :mos [5 7], + :mos-degrees [6 2], + :rotation {:at-zero? false, :degree 2, :mos [7 5]}}), + :submos-by-mos + {[5 7] + [{:degree 0, + :mos [5 7], + :mos-degrees [0 3], + :rotation {:at-zero? true, :degree 0, :mos [5 7]}} + {:degree 1, + :mos [5 7], + :mos-degrees [1 4], + :rotation {:at-zero? false, :degree 1, :mos [5 7]}} + {:degree 2, + :mos [5 7], + :mos-degrees [2 5], + :rotation {:at-zero? false, :degree 2, :mos [5 7]}} + {:degree 3, + :mos [5 7], + :mos-degrees [3 6], + :rotation {:at-zero? false, :degree 3, :mos [5 7]}} + {:degree 5, + :mos [5 7], + :mos-degrees [5 1], + :rotation {:at-zero? false, :degree 1, :mos [7 5]}} + {:degree 6, + :mos [5 7], + :mos-degrees [6 2], + :rotation {:at-zero? false, :degree 2, :mos [7 5]}}], + [6 6] + [{:degree 4, + :mos [6 6], + :mos-degrees [4 0], + :rotation {:at-zero? true, :degree 0, :mos [6 6]}}]}, + :true-submos? true} + {:generator 3, + :pattern [3 3 1], + :period 7, + :submos + ({:degree 0, + :mos [5 5 2], + :mos-degrees [0 3 6], + :rotation {:at-zero? true, :degree 0, :mos [5 5 2]}} + {:degree 1, + :mos [5 6 1], + :mos-degrees [1 4 0], + :rotation {:at-zero? true, :degree 0, :mos [1 5 6]}} + {:degree 2, + :mos [5 5 2], + :mos-degrees [2 5 1], + :rotation {:at-zero? false, :degree 1, :mos [2 5 5]}} + {:degree 3, + :mos [5 5 2], + :mos-degrees [3 6 2], + :rotation {:at-zero? false, :degree 2, :mos [2 5 5]}} + {:degree 4, + :mos [6 5 1], + :mos-degrees [4 0 3], + :rotation {:at-zero? true, :degree 0, :mos [5 1 6]}} + {:degree 5, + :mos [5 5 2], + :mos-degrees [5 1 4], + :rotation {:at-zero? false, :degree 1, :mos [5 2 5]}} + {:degree 6, + :mos [5 5 2], + :mos-degrees [6 2 5], + :rotation {:at-zero? false, :degree 2, :mos [5 2 5]}}), + :submos-by-mos + {[5 5 2] + [{:degree 0, + :mos [5 5 2], + :mos-degrees [0 3 6], + :rotation {:at-zero? true, :degree 0, :mos [5 5 2]}} + {:degree 2, + :mos [5 5 2], + :mos-degrees [2 5 1], + :rotation {:at-zero? false, :degree 1, :mos [2 5 5]}} + {:degree 3, + :mos [5 5 2], + :mos-degrees [3 6 2], + :rotation {:at-zero? false, :degree 2, :mos [2 5 5]}} + {:degree 5, + :mos [5 5 2], + :mos-degrees [5 1 4], + :rotation {:at-zero? false, :degree 1, :mos [5 2 5]}} + {:degree 6, + :mos [5 5 2], + :mos-degrees [6 2 5], + :rotation {:at-zero? false, :degree 2, :mos [5 2 5]}}], + [5 6 1] + [{:degree 1, + :mos [5 6 1], + :mos-degrees [1 4 0], + :rotation {:at-zero? true, :degree 0, :mos [1 5 6]}}], + [6 5 1] + [{:degree 4, + :mos [6 5 1], + :mos-degrees [4 0 3], + :rotation {:at-zero? true, :degree 0, :mos [5 1 6]}}]}, + :true-submos? true} + {:generator 3, + :pattern [2 1 2 1 1], + :period 7, + :submos + ({:degree 0, + :mos [3 2 3 2 2], + :mos-degrees [0 2 3 5 6], + :rotation {:at-zero? true, :degree 0, :mos [3 2 3 2 2]}} + {:degree 1, + :mos [4 1 4 2 1], + :mos-degrees [1 3 4 6 0], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 4 2]}} + {:degree 2, + :mos [3 2 4 1 2], + :mos-degrees [2 4 5 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 2 3 2 4]}} + {:degree 3, + :mos [3 2 3 2 2], + :mos-degrees [3 5 6 1 2], + :rotation {:at-zero? false, :degree 1, :mos [2 2 3 2 3]}} + {:degree 4, + :mos [4 2 3 2 1], + :mos-degrees [4 6 0 2 3], + :rotation {:at-zero? true, :degree 0, :mos [3 2 1 4 2]}} + {:degree 5, + :mos [4 1 4 1 2], + :mos-degrees [5 0 1 3 4], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 2 4]}} + {:degree 6, + :mos [3 2 3 2 2], + :mos-degrees [6 1 2 4 5], + :rotation {:at-zero? false, :degree 1, :mos [2 3 2 2 3]}}), + :submos-by-mos + {[3 2 3 2 2] + [{:degree 0, + :mos [3 2 3 2 2], + :mos-degrees [0 2 3 5 6], + :rotation {:at-zero? true, :degree 0, :mos [3 2 3 2 2]}} + {:degree 3, + :mos [3 2 3 2 2], + :mos-degrees [3 5 6 1 2], + :rotation {:at-zero? false, :degree 1, :mos [2 2 3 2 3]}} + {:degree 6, + :mos [3 2 3 2 2], + :mos-degrees [6 1 2 4 5], + :rotation {:at-zero? false, :degree 1, :mos [2 3 2 2 3]}}], + [3 2 4 1 2] + [{:degree 2, + :mos [3 2 4 1 2], + :mos-degrees [2 4 5 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 2 3 2 4]}}], + [4 1 4 1 2] + [{:degree 5, + :mos [4 1 4 1 2], + :mos-degrees [5 0 1 3 4], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 2 4]}}], + [4 1 4 2 1] + [{:degree 1, + :mos [4 1 4 2 1], + :mos-degrees [1 3 4 6 0], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 4 2]}}], + [4 2 3 2 1] + [{:degree 4, + :mos [4 2 3 2 1], + :mos-degrees [4 6 0 2 3], + :rotation {:at-zero? true, :degree 0, :mos [3 2 1 4 2]}}]}, + :true-submos? true})] + (is (= all-submos + (subject/make-all-submos [1 2 2 1 2 2 2] 5))))) diff --git a/test/erv/mos/v2/submos_test.cljc b/test/erv/mos/v2/submos_test.cljc new file mode 100644 index 0000000..8419669 --- /dev/null +++ b/test/erv/mos/v2/submos_test.cljc @@ -0,0 +1,103 @@ +(ns erv.mos.v2.submos-test + (:require + [clojure.test :refer [deftest is]] + [erv.mos.v2.submos :as subject])) + +(deftest make-all-submos-test + (is (= '({:pattern [3 4], + :period 7, + :submos + ({:degree 0, + :mos [5 7], + :mos-degrees [0 3], + :rotation {:at-zero? true, :degree 0, :mos [5 7]}} + {:degree 1, + :mos [5 7], + :mos-degrees [1 4], + :rotation {:at-zero? false, :degree 1, :mos [5 7]}} + {:degree 2, + :mos [5 7], + :mos-degrees [2 5], + :rotation {:at-zero? false, :degree 2, :mos [5 7]}} + {:degree 3, + :mos [5 7], + :mos-degrees [3 6], + :rotation {:at-zero? false, :degree 3, :mos [5 7]}} + {:degree 4, + :mos [6 6], + :mos-degrees [4 0], + :rotation {:at-zero? true, :degree 0, :mos [6 6]}} + {:degree 5, + :mos [5 7], + :mos-degrees [5 1], + :rotation {:at-zero? false, :degree 1, :mos [7 5]}} + {:degree 6, + :mos [5 7], + :mos-degrees [6 2], + :rotation {:at-zero? false, :degree 2, :mos [7 5]}}), + :true-submos? true} + {:pattern [3 3 1], + :period 7, + :submos + ({:degree 0, + :mos [5 5 2], + :mos-degrees [0 3 6], + :rotation {:at-zero? true, :degree 0, :mos [5 5 2]}} + {:degree 1, + :mos [5 6 1], + :mos-degrees [1 4 0], + :rotation {:at-zero? true, :degree 0, :mos [1 5 6]}} + {:degree 2, + :mos [5 5 2], + :mos-degrees [2 5 1], + :rotation {:at-zero? false, :degree 1, :mos [2 5 5]}} + {:degree 3, + :mos [5 5 2], + :mos-degrees [3 6 2], + :rotation {:at-zero? false, :degree 2, :mos [2 5 5]}} + {:degree 4, + :mos [6 5 1], + :mos-degrees [4 0 3], + :rotation {:at-zero? true, :degree 0, :mos [5 1 6]}} + {:degree 5, + :mos [5 5 2], + :mos-degrees [5 1 4], + :rotation {:at-zero? false, :degree 1, :mos [5 2 5]}} + {:degree 6, + :mos [5 5 2], + :mos-degrees [6 2 5], + :rotation {:at-zero? false, :degree 2, :mos [5 2 5]}}), + :true-submos? true} + {:pattern [2 1 2 1 1], + :period 7, + :submos + ({:degree 0, + :mos [3 2 3 2 2], + :mos-degrees [0 2 3 5 6], + :rotation {:at-zero? true, :degree 0, :mos [3 2 3 2 2]}} + {:degree 1, + :mos [4 1 4 2 1], + :mos-degrees [1 3 4 6 0], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 4 2]}} + {:degree 2, + :mos [3 2 4 1 2], + :mos-degrees [2 4 5 0 1], + :rotation {:at-zero? true, :degree 0, :mos [1 2 3 2 4]}} + {:degree 3, + :mos [3 2 3 2 2], + :mos-degrees [3 5 6 1 2], + :rotation {:at-zero? false, :degree 1, :mos [2 2 3 2 3]}} + {:degree 4, + :mos [4 2 3 2 1], + :mos-degrees [4 6 0 2 3], + :rotation {:at-zero? true, :degree 0, :mos [3 2 1 4 2]}} + {:degree 5, + :mos [4 1 4 1 2], + :mos-degrees [5 0 1 3 4], + :rotation {:at-zero? true, :degree 0, :mos [1 4 1 2 4]}} + {:degree 6, + :mos [3 2 3 2 2], + :mos-degrees [6 1 2 4 5], + :rotation {:at-zero? false, :degree 1, :mos [2 3 2 2 3]}}), + :true-submos? true}) + (subject/make-all-submos 5 [1 2 2 1 2 2 2])))) diff --git a/test/erv/mos/v3/core_test.clj b/test/erv/mos/v3/core_test.clj new file mode 100644 index 0000000..5071766 --- /dev/null +++ b/test/erv/mos/v3/core_test.clj @@ -0,0 +1,113 @@ +(ns erv.mos.v3.core-test + (:require + [clojure.test :refer [deftest is]] + [erv.mos.v3.core :as subject])) + +(deftest gen->mos-ratios-test + (is (= [{:meta + {:period 2, + :scale :mos, + :size 2, + :intervals/cents '(701.9550008653874 498.04499913461217), + :intervals/ratios '(3/2 4/3), + :mos/L 3/2, + :mos/L.cents 701.9550008653874, + :mos/generator 3/2, + :mos/normalized-by 1, + :mos/pattern "Ls", + :mos/pattern.name "1s1L", + :mos/s 4/3, + :mos/s.cents 498.04499913461217, + :mos/sL-ratio 9/8, + :mos/sL-ratio.cents 594.1229411833639, + :mos/sL-ratio.float (float 1.4094208), + :mos/type :ratio}, + :scale + '({:bounded-ratio 1, :bounding-period 2, :ratio 1} + {:bounded-ratio 3/2, :bounding-period 2, :ratio 3/2})} + {:meta + {:period 2, + :scale :mos, + :size 3, + :intervals/cents + '(203.91000173077484 498.04499913461217 498.04499913461217), + :intervals/ratios '(9/8 4/3 4/3), + :mos/L 4/3, + :mos/L.cents 498.04499913461217, + :mos/generator 3/2, + :mos/normalized-by 1, + :mos/pattern "sLL", + :mos/pattern.name "1s2L", + :mos/s 9/8, + :mos/s.cents 203.91000173077484, + :mos/sL-ratio 32/27, + :mos/sL-ratio.cents 1546.0122684152425, + :mos/sL-ratio.float (float 2.4424746), + :mos/type :ratio}, + :scale + '({:bounded-ratio 1, :bounding-period 2, :ratio 1} + {:bounded-ratio 9/8, :bounding-period 2, :ratio 9/8} + {:bounded-ratio 3/2, :bounding-period 2, :ratio 3/2})} + {:meta + {:period 2, + :scale :mos, + :size 5, + :intervals/cents + '(203.91000173077484 + 203.91000173077484 + 294.1349974038373 + 203.91000173077484 + 294.1349974038373), + :intervals/ratios '(9/8 9/8 32/27 9/8 32/27), + :mos/L 32/27, + :mos/L.cents 294.1349974038373, + :mos/generator 3/2, + :mos/normalized-by 1, + :mos/pattern "ssLsL", + :mos/pattern.name "3s2L", + :mos/s 9/8, + :mos/s.cents 203.91000173077484, + :mos/sL-ratio 256/243, + :mos/sL-ratio.cents 634.2550936716368, + :mos/sL-ratio.float (float 1.4424746), + :mos/type :ratio}, + :scale + '({:bounded-ratio 1, :bounding-period 2, :ratio 1} + {:bounded-ratio 9/8, :bounding-period 2, :ratio 9/8} + {:bounded-ratio 81/64, :bounding-period 2, :ratio 81/64} + {:bounded-ratio 3/2, :bounding-period 2, :ratio 3/2} + {:bounded-ratio 27/16, :bounding-period 2, :ratio 27/16})} + {:meta + {:period 2, + :scale :mos, + :size 7, + :intervals/cents + '(203.91000173077484 + 203.91000173077484 + 203.91000173077484 + 90.22499567306232 + 203.91000173077484 + 203.91000173077484 + 90.22499567306232), + :intervals/ratios '(9/8 9/8 9/8 256/243 9/8 9/8 256/243), + :mos/L 9/8, + :mos/L.cents 203.91000173077484, + :mos/generator 3/2, + :mos/normalized-by 1, + :mos/pattern "LLLsLLs", + :mos/pattern.name "2s5L", + :mos/s 256/243, + :mos/s.cents 90.22499567306232, + :mos/sL-ratio 2187/2048, + :mos/sL-ratio.cents 1411.6001602157576, + :mos/sL-ratio.float (float 2.2600167), + :mos/type :ratio}, + :scale + '({:bounded-ratio 1, :bounding-period 2, :ratio 1} + {:bounded-ratio 9/8, :bounding-period 2, :ratio 9/8} + {:bounded-ratio 81/64, :bounding-period 2, :ratio 81/64} + {:bounded-ratio 729/512, :bounding-period 2, :ratio 729/512} + {:bounded-ratio 3/2, :bounding-period 2, :ratio 3/2} + {:bounded-ratio 27/16, :bounding-period 2, :ratio 27/16} + {:bounded-ratio 243/128, :bounding-period 2, :ratio 243/128})}] + (subject/gen->mos-ratios 3/2 2 7)))) diff --git a/test/erv/mos/v3/core_test.cljs b/test/erv/mos/v3/core_test.cljs new file mode 100644 index 0000000..99c1aaa --- /dev/null +++ b/test/erv/mos/v3/core_test.cljs @@ -0,0 +1,135 @@ +(ns erv.mos.v3.core-test + (:require + [clojure.test :refer [deftest is testing]] + [erv.mos.v3.core :refer [gen->mos-ratios]] + [erv.utils.exact :as exact.utils])) + +(deftest gen->mos-ratios-test + (testing "Basic test" + (let [result (-> (gen->mos-ratios (exact.utils/parse-ratio "3/2") 2 7) + (exact.utils/make-readable))] + (is (= '{:meta + {:mos/pattern.name "2s5L", + :mos/normalized-by 1, + :mos/generator "3/2", + :scale :mos, + :mos/s.cents 90.22499567306306, + :mos/L.cents 203.91000173077484, + :mos/pattern "LLLsLLs", + :intervals/cents + (203.91000173077484 + 203.91000173077484 + 203.91000173077484 + 90.22499567306306 + 203.91000173077484 + 203.91000173077484 + 90.22499567306306), + :size 7, + :intervals/ratios ("9/8" "9/8" "9/8" "256/243" "9/8" "9/8" "256/243"), + :mos/s "256/243", + :mos/L "9/8", + :mos/sL-ratio.float 2.2600167526708206, + :period "2", + :mos/sL-ratio.cents 1411.600160215743, + :mos/type :ratio, + :mos/sL-ratio "2187/2048"}, + :scale + ({:ratio "1", :bounded-ratio "1", :bounding-period "2"} + {:ratio "9/8", :bounded-ratio "9/8", :bounding-period "2"} + {:ratio "81/64", :bounded-ratio "81/64", :bounding-period "2"} + {:ratio "729/512", :bounded-ratio "729/512", :bounding-period "2"} + {:ratio "3/2", :bounded-ratio "3/2", :bounding-period "2"} + {:ratio "27/16", :bounded-ratio "27/16", :bounding-period "2"} + {:ratio "243/128", :bounded-ratio "243/128", :bounding-period "2"})} + (last result))))) + (testing "Integer generator" + (let [result (-> (gen->mos-ratios 3 2 7) + (exact.utils/make-readable))] + (is (= '{:meta + {:mos/pattern.name "2s5L", + :mos/normalized-by 1, + :mos/generator 3, + :scale :mos, + :mos/s.cents 90.22499567306306, + :mos/L.cents 203.91000173077484, + :mos/pattern "LLLsLLs", + :intervals/cents + (203.91000173077484 + 203.91000173077484 + 203.91000173077484 + 90.22499567306306 + 203.91000173077484 + 203.91000173077484 + 90.22499567306306), + :size 7, + :intervals/ratios ("9/8" "9/8" "9/8" "256/243" "9/8" "9/8" "256/243"), + :mos/s "256/243", + :mos/L "9/8", + :mos/sL-ratio.float 2.2600167526708206, + :period "2", + :mos/sL-ratio.cents 1411.600160215743, + :mos/type :ratio, + :mos/sL-ratio "2187/2048"}, + :scale + ({:ratio "1", :bounded-ratio "1", :bounding-period "2"} + {:ratio "9/8", :bounded-ratio "9/8", :bounding-period "2"} + {:ratio "81/64", :bounded-ratio "81/64", :bounding-period "2"} + {:ratio "729/512", :bounded-ratio "729/512", :bounding-period "2"} + {:ratio "3/2", :bounded-ratio "3/2", :bounding-period "2"} + {:ratio "27/16", :bounded-ratio "27/16", :bounding-period "2"} + {:ratio "243/128", :bounded-ratio "243/128", :bounding-period "2"})} + (last result))))) + (testing "Floating point generator" + (let [result (-> (gen->mos-ratios 1.618 2 7) + (exact.utils/make-readable))] + (is (= '{:meta + {:mos/pattern.name "4s3L", + :mos/normalized-by 1, + :mos/generator 1.618, + :scale :mos, + :mos/s.cents 99.16178796440605, + :mos/L.cents 267.78428271412537, + :mos/pattern "ssLsLsL", + :intervals/cents + (99.16178796440605 + 99.16178796440605 + 267.78428271412537 + 99.16178796440605 + 267.78428271412537 + 99.16178796440605 + 267.78428271412537), + :size 7, + :intervals/ratios + ("529475129/500000000" + "529475129/500000000" + "500000000000/428345379361" + "529475129/500000000" + "500000000000/428345379361" + "529475129/500000000" + "500000000000/428345379361"), + :mos/s "529475129/500000000", + :mos/L "500000000000/428345379361", + :mos/sL-ratio.float 2.700478563478969, + :period "2", + :mos/sL-ratio.cents 1719.8581153882103, + :mos/type :ratio, + :mos/sL-ratio "250000000000000000000/226798224993719412569"}, + :scale + ({:ratio "1", :bounded-ratio "1", :bounding-period "2"} + {:ratio "529475129/500000000", + :bounded-ratio "529475129/500000000", + :bounding-period "2"} + {:ratio "280343912229566641/250000000000000000", + :bounded-ratio "280343912229566641/250000000000000000", + :bounding-period "2"} + {:ratio "654481/500000", + :bounded-ratio "654481/500000", + :bounding-period "2"} + {:ratio "346531411903049/250000000000000", + :bounded-ratio "346531411903049/250000000000000", + :bounding-period "2"} + {:ratio "809/500", :bounded-ratio "809/500", :bounding-period "2"} + {:ratio "428345379361/250000000000", + :bounded-ratio "428345379361/250000000000", + :bounding-period "2"})} + (last result)))))) diff --git a/test/erv/scale/core_test.clj b/test/erv/scale/core_test.clj index 66c41f0..de87142 100755 --- a/test/erv/scale/core_test.clj +++ b/test/erv/scale/core_test.clj @@ -16,7 +16,6 @@ (cps/bound-ratio 2) (cps/maps->data :bounded-ratio))) - (deftest cps-scale-fulfills-the-scale-spec (is (true? (s/valid? :erv.scale.core/scale (hexany :scale))))) @@ -69,7 +68,6 @@ (= [1 3 6 10] (mapv i->d4 [1 2 3 4])))))) - (deftest demo-scale*-test (testing "single period, default base-freq @ 440hz" (is (= '(1925/4 1155/2 2475/4 1485/2 825N 3465/4 1925/2 3465/4 825N 1485/2 2475/4 1155/2 1925/4) diff --git a/test/erv/utils/conversions_test.clj b/test/erv/utils/conversions_test.clj new file mode 100644 index 0000000..5e86744 --- /dev/null +++ b/test/erv/utils/conversions_test.clj @@ -0,0 +1,8 @@ +(ns erv.utils.conversions-test + (:require + [clojure.test :refer [deftest is]] + [erv.utils.conversions :as subject])) + +(deftest ratio->cents-test + (is (= 701.9550008653874 + (subject/ratio->cents 3/2)))) diff --git a/test/erv/utils/conversions_test.cljs b/test/erv/utils/conversions_test.cljs new file mode 100644 index 0000000..161ac98 --- /dev/null +++ b/test/erv/utils/conversions_test.cljs @@ -0,0 +1,9 @@ +(ns erv.utils.conversions-test + (:require + [clojure.test :refer [deftest is]] + [erv.utils.conversions :as subject] + [erv.utils.exact :as exact.utils])) + +(deftest ratio->cents-test + (is (= 701.9550008653874 + (subject/ratio->cents (exact.utils/parse-ratio "3/2"))))) diff --git a/test/erv/utils/core_test.cljc b/test/erv/utils/core_test.cljc index 7f591b8..2d125ea 100644 --- a/test/erv/utils/core_test.cljc +++ b/test/erv/utils/core_test.cljc @@ -1,7 +1,8 @@ (ns erv.utils.core-test (:require [clojure.test :refer [deftest is]] - [erv.utils.core :refer [pattern->degrees pick-degrees pick-pattern]])) + [erv.utils.core :refer [make-map-by-key pattern->degrees pick-degrees + pick-pattern]])) (deftest pattern->indexes-test (is (= [0 2 4 5 7 9 11] @@ -20,3 +21,7 @@ (pick-degrees (range 5) (range 10))))) + +(deftest make-map-by-key-test + (is (= {1 {:id 1}, 2 {:id 2}} + (make-map-by-key :id [{:id 1} {:id 2}])))) diff --git a/test/erv/utils/ratios_test.clj b/test/erv/utils/ratios_test.clj index 13b5d4f..642b0c4 100644 --- a/test/erv/utils/ratios_test.clj +++ b/test/erv/utils/ratios_test.clj @@ -18,7 +18,7 @@ (deftest interval-seq->ratio-stack-test (is (= [1 3/2 2N 3N 4N 6N 8N] - (interval-seq->ratio-stack [3/2 4/3] 7)))) + (interval-seq->ratio-stack 7 [3/2 4/3])))) (deftest normalize-ratios-test (is (= [1 7/6 3/2] diff --git a/test/erv/utils/ratios_test.cljs b/test/erv/utils/ratios_test.cljs new file mode 100644 index 0000000..eb46b6c --- /dev/null +++ b/test/erv/utils/ratios_test.cljs @@ -0,0 +1,70 @@ +(ns erv.utils.ratios-test + (:require + [cljs.test :refer [deftest is]] + [erv.utils.exact :as exact.utils] + [erv.utils.ratios :refer [gen-chain interval-seq->ratio-stack + normalize-ratios ratios->harmonic-series + ratios->scale ratios-intervals + seq-interval-analysis]])) + +(deftest ratios->scale-test + (is (= [{:ratio "1", :bounded-ratio "1", :bounding-period "2"} + {:ratio "5/4", :bounded-ratio "5/4", :bounding-period "2"} + {:ratio "3/2", :bounded-ratio "3/2", :bounding-period "2"}] + (->> "1 3 5/4" + exact.utils/parse-ratios + (ratios->scale 2) + exact.utils/make-readable)))) + +(deftest ratios-intervals-test + (is (= ["5/4" "6/5"] + (->> "1 5/4 3/2" + exact.utils/parse-ratios + ratios-intervals + exact.utils/make-readable)))) + +(deftest interval-seq->ratio-stack-test + (is (= ["1" "3/2" "2" "3" "4" "6" "8"] + (->> "3/2 4/3" + exact.utils/parse-ratios + (interval-seq->ratio-stack 7) + exact.utils/make-readable)))) + +(deftest normalize-ratios-test + (is (= ["1" "7/6" "3/2"] + (->> (normalize-ratios [6 7 9]) + exact.utils/make-readable)))) + +(deftest ratios->harmonic-series-test + (is (= ["6" "7" "9"] + (->> "1 7/6 3/2" + exact.utils/parse-ratios + ratios->harmonic-series + exact.utils/make-readable)))) + +(deftest seq-interval-analysis-test + (is (= {:rooted-seq + [["1" "1/1" 0] + ["14/11" "2.7/11" 417.50796410436817] + ["3/2" "3/2" 701.9550008653874] + ["21/11" "3.7/11" 1119.4629649697556] + ["2" "2/1" 1200]] + :pairs + [[["4/3" "56/33"] ["14/11" "2.7/11" 417.50796410436817]] + [["56/33" "2"] ["33/28" "3.11/2.2.7" 284.44703676101926]] + [["2" "28/11"] ["14/11" "2.7/11" 417.50796410436817]] + [["28/11" "8/3"] ["22/21" "2.11/3.7" 80.53703503024445]]] + :ratio-factorization + [["4/3" "2.2/3"] + ["56/33" "2.2.2.7/3.11"] + ["2" "2/1"] + ["28/11" "2.2.7/11"] + ["8/3" "2.2.2/3"]]} + (->> "4/3 56/33 2 28/11 8/3" + exact.utils/parse-ratios + seq-interval-analysis + exact.utils/make-readable)))) + +(deftest gen-chain-test + (is (= [1 3 9 27 81] + (gen-chain 5 3)))) diff --git a/test/erv/utils/scale_test.clj b/test/erv/utils/scale_test.clj index d77074b..89f14bd 100644 --- a/test/erv/utils/scale_test.clj +++ b/test/erv/utils/scale_test.clj @@ -3,10 +3,11 @@ [clojure.test :refer [deftest is testing]] [erv.edo.core :as edo] [erv.utils.ratios :refer [ratios->scale]] - [erv.utils.scale :refer [cross-set dedupe-scale degree-stack - find-subset-degrees get-degrees rotate-scale - scale->stacked-subscale scale-intervals - scale-steps->degrees tritriadic]])) + [erv.utils.scale :refer [cross-set dedupe-scale degree-stack diamond + find-subset-degrees get-degrees + proportional-chords proportional-difference + rotate-scale scale->stacked-subscale + scale-intervals scale-steps->degrees tritriadic]])) (deftest degree-stack-test (is (= [0 4 8] @@ -52,6 +53,7 @@ (deftest scale->stacked-subscale-test (is (= {:meta {:scale :stacked-subscale + :period 4 :intervals [3/2 4/3 3/2 4/3], :parent-scale [{:ratio 1, :bounded-ratio 1, :bounding-period 4} @@ -207,3 +209,48 @@ (deftest scale-steps->degrees-test (is (= [0 2 4 5 7 9 11 12] (scale-steps->degrees [2 2 1 2 2 2 1] false)))) + +(deftest diamond-test + (is (= {:meta {:scale :diamond + :factors [1 3 5 7 9] + :period 2 + :size 19} + :scale + [{:bounded-ratio 1 :bounding-period 2 :ratio 1} + {:bounded-ratio 10/9 :bounding-period 2 :ratio 10/9} + {:bounded-ratio 9/8 :bounding-period 2 :ratio 9/8} + {:bounded-ratio 8/7 :bounding-period 2 :ratio 8/7} + {:bounded-ratio 7/6 :bounding-period 2 :ratio 7/6} + {:bounded-ratio 6/5 :bounding-period 2 :ratio 6/5} + {:bounded-ratio 5/4 :bounding-period 2 :ratio 5/4} + {:bounded-ratio 9/7 :bounding-period 2 :ratio 9/7} + {:bounded-ratio 4/3 :bounding-period 2 :ratio 4/3} + {:bounded-ratio 7/5 :bounding-period 2 :ratio 7/5} + {:bounded-ratio 10/7 :bounding-period 2 :ratio 10/7} + {:bounded-ratio 3/2 :bounding-period 2 :ratio 3/2} + {:bounded-ratio 14/9 :bounding-period 2 :ratio 14/9} + {:bounded-ratio 8/5 :bounding-period 2 :ratio 8/5} + {:bounded-ratio 5/3 :bounding-period 2 :ratio 5/3} + {:bounded-ratio 12/7 :bounding-period 2 :ratio 12/7} + {:bounded-ratio 7/4 :bounding-period 2 :ratio 7/4} + {:bounded-ratio 16/9 :bounding-period 2 :ratio 16/9} + {:bounded-ratio 9/5 :bounding-period 2 :ratio 9/5}]} + (diamond 2 [1 3 5 7 9]))) + + (testing "A `diamond` is a special case of `cross-set`" + (is (= + (map :bounded-ratio (:scale (diamond 2 [1 3 5 7 9]))) + (map :bounded-ratio (:scale (cross-set 2 + [1 3 5 7 9] + (map #(/ 1 %) [1 3 5 7 9])))))))) + +(deftest proportional-difference-test + (is (= 1 + (proportional-difference [1 5/4 3/2]))) + (is (= nil + (proportional-difference [1 9/8 3/2])))) + +(deftest proportional-chords-test + (is (= {:by-notes {1N [[1 9/8 5/4] [1 5/4 3/2] [5/4 3/2 7/4]]}, + :by-degrees {1N [[0 1 2] [0 2 3] [2 3 4]]}} + (proportional-chords 3 (ratios->scale [1 3 5 7 9]))))) diff --git a/test/erv/utils/scale_test.cljs b/test/erv/utils/scale_test.cljs new file mode 100644 index 0000000..cbefa89 --- /dev/null +++ b/test/erv/utils/scale_test.cljs @@ -0,0 +1,218 @@ +(ns erv.utils.scale-test + (:require + [clojure.test :refer [deftest is testing]] + [com.gfredericks.exact :as e] + [erv.edo.core :as edo] + [erv.utils.exact :as exact.utils] + [erv.utils.ratios :refer [ratios->scale]] + [erv.utils.scale :refer [cross-set dedupe-scale degree-stack diamond + find-subset-degrees get-degrees + proportional-chords proportional-difference + rotate-scale scale->stacked-subscale + scale-intervals scale-steps->degrees tritriadic]])) + +(deftest degree-stack-test + (is (= [0 4 8] + (map :edo/degree + (degree-stack {:scale (:scale (edo/from-pattern (repeat 12 1))) + :gen 4 + :offset 0})))) + (testing "Will make an ordered list with the degrees from the stack" + (is (= [0 7 2 9 4 11 6 1 8 3 10 5] + (map :edo/degree + (degree-stack {:scale (:scale (edo/from-pattern (repeat 12 1))) + :gen 7 + :offset 0})))))) + +(deftest scale-intervals-test + (is (= ["5/4" "6/5" "4/3"] + (->> "1 5/4 3/2" + exact.utils/parse-ratios + ratios->scale + scale-intervals + exact.utils/make-readable)))) + +(deftest tritriadic-test + (testing "Can make a tritriadic scale (Tritriad69)" + (is (= {:meta {:scale :tritriadic + :triad-ratios ["1" "7/6" "3/2"]} + :scale [{:ratio "9/8" + :bounded-ratio "9/8" + :bounding-period "2"} + {:ratio "81/64" + :bounded-ratio "81/64" + :bounding-period "2"} + {:ratio "21/16" + :bounded-ratio "21/16" + :bounding-period "2"} + {:ratio "3/2" + :bounded-ratio "3/2" + :bounding-period "2"} + {:ratio "27/16" + :bounded-ratio "27/16" + :bounding-period "2"} + {:ratio "7/4" + :bounded-ratio "7/4" + :bounding-period "2"} + {:ratio "63/32" + :bounded-ratio "63/32" + :bounding-period "2"}]} + (->> "1 7/6 3/2" + exact.utils/parse-ratios + tritriadic + exact.utils/make-readable))))) + +(deftest scale->stacked-subscale-test + (is (= {:meta {:scale :stacked-subscale + :period 4 + :intervals ["3/2" "4/3" "3/2" "4/3"] + :parent-scale [{:ratio "1" + :bounded-ratio "1" + :bounding-period "4"} + {:ratio "3/2" + :bounded-ratio "3/2" + :bounding-period "4"} + {:ratio "2" + :bounded-ratio "2" + :bounding-period "4"} + {:ratio "3" + :bounded-ratio "3" + :bounding-period "4"}] + :gen 2 + :starting-offset 0} + :scale [{:ratio "1" + :bounded-ratio "1" + :bounding-period "4"} + {:ratio "3/2" + :bounded-ratio "3/2" + :bounding-period "4"} + {:ratio "2" + :bounded-ratio "2" + :bounding-period "4"} + {:ratio "3" + :bounded-ratio "3" + :bounding-period "4"}]} + (-> (scale->stacked-subscale {:scale (ratios->scale (exact.utils/parse-ratios "1 5/4 3/2 15/8")) + :gen 2 + :offset 0 + :size 40 + :period 4}) + exact.utils/make-readable)))) + +(deftest dedupe-scale-test + (is (= [{:bounded-ratio 1} + {:bounded-ratio "5/4"} + {:bounded-ratio "3/2"}] + (dedupe-scale [{:bounded-ratio 1} + {:bounded-ratio "5/4"} + {:bounded-ratio "3/2"} + {:bounded-ratio "3/2"}])))) + +(deftest rotate-scale-test + (testing "Rotates the scale and leaves a trace of the original degree and ratio" + (is (= [{:ratio "1", + :bounded-ratio "1", + :bounding-period "2", + :rotated-scale/original-degree 1, + :rotated-scale/original-ratio "5/4"} + {:ratio "6/5", + :bounded-ratio "6/5", + :bounding-period "2", + :rotated-scale/original-degree 2, + :rotated-scale/original-ratio "3/2"} + {:ratio "8/5", + :bounded-ratio "8/5", + :bounding-period "2", + :rotated-scale/original-degree 0, + :rotated-scale/original-ratio "1"}] + (->> "1 5/4 3/2" + exact.utils/parse-ratios + ratios->scale + (rotate-scale 1) + exact.utils/make-readable))))) + +(deftest cross-set-test + (is (= {:meta + {:scale :cross-set, + :sets [[1 3 9] ["1" "5" "25"] ["1" "7" "49"]], + :size 27, + :period 2}, + :scale + [{:ratio "1", :bounded-ratio "1", :bounding-period "2"} + {:ratio "525/512", :bounded-ratio "525/512", :bounding-period "2"} + {:ratio "2205/2048", :bounded-ratio "2205/2048", :bounding-period "2"} + {:ratio "35/32", :bounded-ratio "35/32", :bounding-period "2"} + {:ratio "9/8", :bounded-ratio "9/8", :bounding-period "2"} + {:ratio "147/128", :bounded-ratio "147/128", :bounding-period "2"} + {:ratio "75/64", :bounded-ratio "75/64", :bounding-period "2"} + {:ratio "1225/1024", :bounded-ratio "1225/1024", :bounding-period "2"} + {:ratio "315/256", :bounded-ratio "315/256", :bounding-period "2"} + {:ratio "5/4", :bounded-ratio "5/4", :bounding-period "2"} + {:ratio "21/16", :bounded-ratio "21/16", :bounding-period "2"} + {:ratio "11025/8192", :bounded-ratio "11025/8192", :bounding-period "2"} + {:ratio "175/128", :bounded-ratio "175/128", :bounding-period "2"} + {:ratio "45/32", :bounded-ratio "45/32", :bounding-period "2"} + {:ratio "735/512", :bounded-ratio "735/512", :bounding-period "2"} + {:ratio "3/2", :bounded-ratio "3/2", :bounding-period "2"} + {:ratio "49/32", :bounded-ratio "49/32", :bounding-period "2"} + {:ratio "1575/1024", :bounded-ratio "1575/1024", :bounding-period "2"} + {:ratio "25/16", :bounded-ratio "25/16", :bounding-period "2"} + {:ratio "105/64", :bounded-ratio "105/64", :bounding-period "2"} + {:ratio "441/256", :bounded-ratio "441/256", :bounding-period "2"} + {:ratio "7/4", :bounded-ratio "7/4", :bounding-period "2"} + {:ratio "225/128", :bounded-ratio "225/128", :bounding-period "2"} + {:ratio "3675/2048", :bounded-ratio "3675/2048", :bounding-period "2"} + {:ratio "15/8", :bounded-ratio "15/8", :bounding-period "2"} + {:ratio "245/128", :bounded-ratio "245/128", :bounding-period "2"} + {:ratio "63/32", :bounded-ratio "63/32", :bounding-period "2"}]} + (-> (cross-set 2 + [1 3 9] ;; suports ordinary vectors and vectors of `exact` numbers + (map exact.utils/->exact [1 5 25]) + (map exact.utils/->exact [1 7 49])) + exact.utils/make-readable)))) + +(deftest find-subset-degrees-test + (is (= '({:degrees (0 2 4), :matched 1, :subscale/matched-ratios ("1" "5/4" "3/2")} + {:degrees (3 5 0), :matched 1, :subscale/matched-ratios ("1" "5/4" "3/2")} + {:degrees (4 6 1), :matched 1, :subscale/matched-ratios ("1" "5/4" "3/2")}) + (-> (find-subset-degrees + {:scale (ratios->scale (exact.utils/parse-ratios "1 9/8 5/4 4/3 3/2 5/3 15/8")) + :subset-ratios (exact.utils/parse-ratios "1 5/4 3/2")}) + exact.utils/make-readable)))) + +(deftest get-degrees-test + (is (= '({:ratio "1", :bounded-ratio "1", :bounding-period "2"} + {:ratio "5/4", :bounded-ratio "5/4", :bounding-period "2"} + {:ratio "3/2", :bounded-ratio "3/2", :bounding-period "2"}) + (-> "1 9/8 5/4 4/3 3/2 5/3 15/8" + (exact.utils/parse-ratios) + (ratios->scale) + (get-degrees [0 2 4]) + exact.utils/make-readable)))) + +(deftest scale-steps->degrees-test + (is (= [0 2 4 5 7 9 11 12] + (scale-steps->degrees [2 2 1 2 2 2 1] false)))) + +(deftest diamond-test + (testing "A `diamond` is a special case of `cross-set`" + (is (= (map :bounded-ratio (:scale (diamond 2 [1 3 5 7 9]))) + (map :bounded-ratio (:scale (cross-set 2 + [1 3 5 7 9] + (map #(e// (exact.utils/->exact %)) + [1 3 5 7 9])))))))) + +(deftest proportional-difference-test + (is (= e/ONE + (proportional-difference (exact.utils/parse-ratios "1 5/4 3/2")))) + (is (= nil + (proportional-difference (exact.utils/parse-ratios "1 9/8 3/2"))))) + +(deftest proportional-chords-test + (is (= {:by-notes {"1" [["1" "9/8" "5/4"] ["1" "5/4" "3/2"] ["5/4" "3/2" "7/4"]]}, + :by-degrees {"1" [[0 1 2] [0 2 3] [2 3 4]]}} + (->> "1 3 5 7 9" + exact.utils/parse-ratios + ratios->scale + (proportional-chords 3) + exact.utils/make-readable))))