Skip to content

Commit

Permalink
Add detempered Ploidacot interpretations (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
inthar-raven committed Sep 12, 2024
1 parent 9420966 commit b104799
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 14 deletions.
10 changes: 10 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ stack()`
h2.innerText = `Scale profile for ${currentWord}`;
el.appendChild(h2);
if (currentProfile) {
const ploidacot = currentProfile["ploidacot"];
const structure = currentProfile["structure"];
if (structure) {
el.innerHTML += `<b>Guide frame</b><br/><small>`;
Expand All @@ -487,6 +488,15 @@ stack()`
el.innerHTML += `Interleaving polyoffset ${structure["polyoffset"].map((g) => alsoInCurrentTuning(g))}<br/>`; // TODO prettify
el.innerHTML += `Multiplicity ${JSON.stringify(structure["multiplicity"])}<br/>`; // TODO prettify
el.innerHTML += `Complexity ${JSON.stringify(structure["complexity"])}<br/><br/>`; // TODO prettify
if (ploidacot) {
const ploid = ploidacot["ploid"];
const ploidString = ploid === 1 ? "" : `${ploid}-ploid `;
const shear = ploidacot["shear"];
const shearString = shear === 0 ? "" : `${shear}-sheared `;
const cot = ploidacot["cot"];
const cotString = `${cot}-cot`;
el.innerHTML += `Detempered <a href="https://en.xen.wiki/w/Ploidacot" target="_blank">Ploidacot</a>: ${ploidString}${shearString}${cotString}<br/><br/>`; // TODO prettify
}
el.innerHTML += `<b>Monotone MOS properties</b><br/>`;
el.innerHTML += currentProfile["lm"] ? `L = m<br/>` : "";
el.innerHTML += currentProfile["ms"] ? `m = s<br/>` : "";
Expand Down
143 changes: 130 additions & 13 deletions src/guide.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::BTreeSet;

use itertools::Itertools;
use serde::Serialize;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
Expand Down Expand Up @@ -100,6 +101,71 @@ fn guided_gs_list_for_subscale(subscale: &[CountVector<usize>]) -> Vec<Vec<Count
.collect()
}
}

/// This is [Ploidacot](https://en.xen.wiki/w/Ploidacot) but with possible "contorsion".
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize)]
pub struct Ploidacot {
/// The number of parts the 2/1 is split into.
pub ploid: usize,
/// The number of parts the voicing of 3/2 is split into
/// If the 3/2 is comprised of several ploids, this is 0 ("acot").
pub cot: usize,
/// Determines the voicing of 3/2 used
/// 0 indicates 3/2 is used; each increment elevates the voicing by a 2/1.
pub shear: usize,
}

impl Ploidacot {
pub fn try_get_ploidacot(scale: &[Letter]) -> Option<Self> {
println!("gf: {:?}", guide_frames(scale));
if let Some(gf) = guide_frames(scale).first() {
let n = scale.len();
println!("gf: {:?}", gf.clone());
let po = &gf.polyoffset;
let gs = &gf.gs;
let multigen_class = gs.len() * gs[0].len();
let patent_3_mapping =
f64::round(n as f64 * f64::log(3.0, std::f64::consts::E) / std::f64::consts::LN_2)
as usize;
let ploid = if po.len() == 1 || gcd(patent_3_mapping as u64, n as u64) > 1 {
1 // do this instead of polyploid acot
} else {
n / po[1].len()
};
let patent_fifth_mapping = patent_3_mapping - n;
let patent_fourth_mapping = 2 * n - patent_3_mapping;
// Check if one generator in the GS can be interpreted as a fifth
if gs[0].len() == patent_fifth_mapping || gs[0].len() == patent_fourth_mapping {
Some(Self {
ploid,
cot: 1,
shear: 0,
})
} else {
// Check if aggregate generator can be interpreted as a fifth
for i in 0..gs.len() {
if patent_fifth_mapping + i * n == multigen_class {
let cot = gs.len();
let shear = i;
return Some(Self { ploid, cot, shear });
}
}
let patent_fourth_mapping = 2 * n - patent_3_mapping;
for i in 0..gs.len() {
if patent_fourth_mapping + i * n == multigen_class {
let cot = gs.len();
let shear = (cot - 1 - i) % cot;
return Some(Self { ploid, cot, shear });
}
}
None
}
} else {
None
}
}
}

/// A guide frame structure for a scale word, consisting of a generator sequence together with a set of offsets or a multiplicity.
/// Multiplicity greater than 1 is a generalization of diregular MV3s;
/// scales of this type always have the number of notes divisible by the multiplicity.
Expand Down Expand Up @@ -163,7 +229,7 @@ impl GuideFrame {
let co_d = scale.len() / d;
if co_d % multiplicity != 0 {
if d == multiplicity {
println!("interleaved");
// println!("interleaved");
// It's an interleaved scale.
let subscales = (0..d)
.map(|degree| rotate(scale, degree))
Expand Down Expand Up @@ -228,7 +294,7 @@ impl GuideFrame {
vec![]
}
} else {
println!("not interleaved");
// println!("not interleaved");
// stack at most this many k-steps
let chain_length: usize = scale.len() / multiplicity;
if chain_length == 1 {
Expand All @@ -251,16 +317,14 @@ impl GuideFrame {
// `init` will be nonempty, so the following check won't be vacuous.
init.all(|k_step| *k_step != *last)
});
let g: Vec<_> = gen_chains_enumerated.clone().collect();
println!("gen_chains_enumerated: {:?}", g);
let gses: Vec<Vec<CountVector<Letter>>> = gen_chains_enumerated
.clone()
// Take prefix of gs_length_limit - 1 elements and get what GS it is generated by
.map(|(_, chain)| weak_period(&chain[0..chain_length - 1]))
.sorted()
.dedup()
.collect();
println!("{:?}", gses);
// println!("{:?}", gses);
gses.iter()
.map(|gs| {
(
Expand All @@ -269,11 +333,11 @@ impl GuideFrame {
.clone()
// Check only the prefix of gs_length_limit - 1 elements, because that's what the guided GS is based on.
.filter(|(_, gen_chain)| {
println!("gen_chain: {:?}", gen_chain);
println!(
"weak_period: {:?}",
weak_period(&gen_chain[..chain_length - 1])
);
// println!("gen_chain: {:?}", gen_chain);
//println!(
// "weak_period: {:?}",
// weak_period(&gen_chain[..chain_length - 1])
// );
weak_period(&gen_chain[..chain_length - 1]) == *gs.clone()
})
// Get all indices on which this particular GS occurs.
Expand All @@ -294,13 +358,13 @@ impl GuideFrame {
.collect();
union_of_chains.sort();
union_of_chains.dedup();
println!("union_of_chains: {:?}", union_of_chains);
// println!("union_of_chains: {:?}", union_of_chains);
let chains_are_disjoint: bool = union_of_chains.len() == scale.len();
chains_are_disjoint && polyoffset_indices.len() == multiplicity
})
.map(|(gs, polyoffset_indices)| {
println!("gs: {:?}", gs);
println!("polyoffset_indices: {:?}", polyoffset_indices);
// println!("gs: {:?}", gs);
// println!("polyoffset_indices: {:?}", polyoffset_indices);
let first_deg = polyoffset_indices[0];
let polyoffset: Vec<CountVector<Letter>> = polyoffset_indices
.iter()
Expand Down Expand Up @@ -363,6 +427,59 @@ mod tests {

use super::*;
#[test]
fn test_ploidacot() {
let pinedye = [0, 0, 1, 0, 1, 0, 0, 2];
assert_eq!(
// Pinedye is monocot
Ploidacot::try_get_ploidacot(&pinedye),
Some(Ploidacot {
ploid: 1,
cot: 1,
shear: 0,
})
);
let diasem = [0, 1, 0, 2, 0, 1, 0, 2, 0];
assert_eq!(
// Diasem is 1-sheared dicot
Ploidacot::try_get_ploidacot(&diasem),
Some(Ploidacot {
ploid: 1,
cot: 2,
shear: 1,
})
);
let diamech_4sl: [usize; 11] = [1, 0, 2, 0, 2, 0, 1, 0, 2, 0, 2];
assert_eq!(
// Diamech is tricot
Ploidacot::try_get_ploidacot(&diamech_4sl),
Some(Ploidacot {
ploid: 1,
cot: 3,
shear: 0,
})
);
let diachrome_5sc = [0, 2, 0, 2, 0, 1, 2, 0, 2, 0, 2, 1];
assert_eq!(
// Central diachrome is diploid monocot
Ploidacot::try_get_ploidacot(&diachrome_5sc),
Some(Ploidacot {
ploid: 2,
cot: 1,
shear: 0,
})
);
let blackdye: [usize; 10] = [0, 1, 0, 2, 0, 1, 0, 2, 0, 2];
assert_eq!(
// Blackdye is haploid monocot (ignore the contorsion caused by the offset)
Ploidacot::try_get_ploidacot(&blackdye),
Some(Ploidacot {
ploid: 1,
cot: 1,
shear: 0,
})
);
}
#[test]
fn test_lllmllms() {
let bad_scale: [usize; 8] = [0, 0, 0, 1, 0, 0, 1, 2];
let complexity_2_gses = GuideFrame::try_multiple(&bad_scale, 2, 2);
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ use serde::Serialize;
use serde_wasm_bindgen::to_value;

use guide::guide_frames;
use guide::GuideFrame;
use guide::{GuideFrame, Ploidacot};
use interval::JiRatio;
use words::{least_mode, maximum_variety, monotone_lm, monotone_ms, monotone_s0, CountVector};

Expand Down Expand Up @@ -104,6 +104,8 @@ pub struct ScaleProfile {
reversed: String,
/// lowest-complexity guide frame structure provided there is one
structure: Option<GuideResult>,
/// detempered Ploidacot interpretation
ploidacot: Option<Ploidacot>,
/// whether scale is L=M monotone MOS
lm: bool,
/// whether scale is M=s monotone MOS
Expand Down Expand Up @@ -324,6 +326,7 @@ pub fn word_to_profile(query: &[usize]) -> ScaleProfile {
ScaleProfile {
word: brightest,
lattice_basis: Some(lattice_basis),
ploidacot: Ploidacot::try_get_ploidacot(query),
chirality,
reversed,
structure: Some(structure),
Expand All @@ -336,6 +339,7 @@ pub fn word_to_profile(query: &[usize]) -> ScaleProfile {
ScaleProfile {
word: brightest,
lattice_basis: None,
ploidacot: Ploidacot::try_get_ploidacot(query),
chirality,
reversed,
structure: None,
Expand Down

0 comments on commit b104799

Please sign in to comment.