Skip to content

Commit

Permalink
Add support for size hints in rank2 and parallellotope
Browse files Browse the repository at this point in the history
ref #325
  • Loading branch information
frostburn committed May 23, 2024
1 parent d3e5a4d commit 35b7344
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 19 deletions.
11 changes: 7 additions & 4 deletions documentation/BUILTIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ Obtain a non-negative integer that is the Unicode code point value of the charac
### colorOf(*interval*)
Return the color of the interval.

### compare(*x*, *y*)
Compare two values. Result is -1 if x < y, +1 if x > y and 0 if the arguments are equal.

### complexityOf(*interval*, *countZeros = false*)
Compute the prime limit ordinal of an interval or val. 1/1 has a complexity of 0, 2/1 has complexity 1, 3/1 has complexity 2, 5/1 has complexity 3, etc.. If `countZeros` is true, measure the complexity of the internal representation instead.

Expand Down Expand Up @@ -611,8 +614,8 @@ Reduce the current/given scale by its last interval, sort the result and filter
### organized(*tolerance = niente*, *action = "simplest"*, *preserveBoundary = false*, *scale = $$*)
Obtain a copy of the current/given scale reduced by its last interval, sorted and with duplicates filtered out. If `tolerance` is given near-duplicates are coalesced instead using the given `action`. If `preserveBoundary` is `true` intervals close to unison and the equave are not eliminated.

### parallelotope(*basis*, *ups = niente*, *downs = niente*, *equave = 2*)
Span a parallelotope by extending a basis combinatorically. `ups` defaults to all ones while `downs` defaults to all zeros.
### parallelotope(*basis*, *ups = niente*, *downs = niente*, *equave = 2*, *basisSizeHints = niente*, *equaveSizeHint = niente*)
Span a parallelotope by extending a basis combinatorically. `ups` defaults to all ones while `downs` defaults to all zeros. The size hints are used to get the correct period reduction when generating a preimage.

### periodiff(*array*)
Calculate the geometric differences of the periodic interval pattern.
Expand All @@ -635,8 +638,8 @@ Obtain a copy of the current/given scale with random variance added.
### range(*start*, *stop = niente*, *step = 1*)
Obtain an array of integers from `start` to `stop - 1`. When only a single parameter is given `range(0, n)` is returned.

### rank2(*generator*, *up*, *down = 0*, *period = 2*, *numPeriods = 1*)
Generate a finite segment of a Rank-2 scale generated by stacking the given generator against the given period (or the octave `2/1` by default). `up` and `down` must be multiples of `numPeriods`.
### rank2(*generator*, *up*, *down = 0*, *period = 2*, *numPeriods = 1*, *generatorSizeHint = niente*, *periodSizeHint = niente*)
Create a finite segment of a Rank-2 scale by stacking the given generator against the given period (or the octave `2/1` by default). `up` and `down` must be multiples of `numPeriods`. The size hints are used to get the correct period reduction when generating a preimage.

### realizeWord(*word*, *sizes*, *equave = niente*)
Realize a scale word like "LLsLLLs" as a concrete scale with the given step sizes. One step size may be omitted and inferred based on the size of the `equave` (default `2`).
Expand Down
40 changes: 40 additions & 0 deletions src/parser/__tests__/stdlib.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1538,4 +1538,44 @@ describe('SonicWeave standard library', () => {
'-12\\12',
]);
});

it('supports size hints for rank-2 preimages', () => {
const scale = expand(`
rank2(10/9, 7, 0, 2/1, 1, 2\\15)
i => i str(i)
15@
`);
expect(scale).toEqual([
'2\\15 "10/9"',
'4\\15 "100/81"',
'6\\15 "1000/729"',
'8\\15 "10000/6561"',
'10\\15 "100000/59049"',
'12\\15 "1000000/531441"',
'14\\15 "10000000/4782969"',
'15\\15 "2/1"',
]);
});

it('supports size hints for rank-3 preimages', () => {
const scale = expand(`
parallelotope([3, 5], [2, 3], niente, 2, [1892.986, 2795.938], 1196.430)
i => i str(i)
PrimeMapping(1196.430, 1892.986, 2795.938, 3382.895)
`);
expect(scale).toEqual([
'12.804 "125/128"',
'196.682 "9/8"',
'209.486 "1125/1024"',
'306.282 "75/64"',
'403.078 "5/4"',
'599.76 "45/32"',
'696.556 "3/2"',
'709.36 "375/256"',
'806.156 "25/16"',
'1002.838 "225/128"',
'1099.634 "15/8"',
'1196.43 "2"',
]);
});
});
25 changes: 25 additions & 0 deletions src/stdlib/builtin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,30 @@ lcm.__doc__ =
'Obtain the smallest (linear) interval that shares both arguments as multiplicative factors. Applies to the current scale if not arguments are given.';
lcm.__node__ = builtinNode(lcm);

function builtinCompare(
this: ExpressionVisitor,
x: SonicWeaveValue,
y: SonicWeaveValue
): SonicWeaveValue {
if (isArrayOrRecord(x) || isArrayOrRecord(y)) {
return binaryBroadcast.bind(this)(x, y, builtinCompare.bind(this));
}
const c = compare.bind(this)(upcastBool(x), upcastBool(y));
if (c < 0) {
return fromInteger(-1);
} else if (c > 0) {
return fromInteger(1);
}
return fromInteger(0);
}
Object.defineProperty(builtinCompare, 'name', {
value: 'compare',
enumerable: false,
});
builtinCompare.__doc__ =
'Compare two values. Result is -1 if x < y, +1 if x > y and 0 if the arguments are equal.';
builtinCompare.__node__ = builtinNode(builtinCompare);

function toPrimeArray(
this: ExpressionVisitor,
interval: SonicWeaveValue
Expand Down Expand Up @@ -2558,6 +2582,7 @@ export const BUILTIN_CONTEXT: Record<string, Interval | SonicWeaveFunction> = {
wilsonHeight,
gcd,
lcm,
compare: builtinCompare,
toPrimeArray,
monzoFromPrimeArray,
valFromPrimeArray,
Expand Down
78 changes: 63 additions & 15 deletions src/stdlib/prelude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,18 +481,32 @@ riff mos(numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep = 2, sizeOfSmal
step => step \\ divisions ed equave;
}
riff rank2(generator, up, down = 0, period = 2, numPeriods = 1) {
"Generate a finite segment of a Rank-2 scale generated by stacking the given generator against the given period (or the octave \`2/1\` by default). \`up\` and \`down\` must be multiples of \`numPeriods\`.";
riff rank2(generator, up, down = 0, period = 2, numPeriods = 1, generatorSizeHint = niente, periodSizeHint = niente) {
"Create a finite segment of a Rank-2 scale by stacking the given generator against the given period (or the octave \`2/1\` by default).\\
\`up\` and \`down\` must be multiples of \`numPeriods\`. The size hints are used to get the correct period reduction when generating a preimage.";
if (up ~mod numPeriods)
throw "Up must be a multiple of the number of periods.";
if (down ~mod numPeriods)
throw "Down must be a multiple of the number of periods.";
up ~%= numPeriods
down ~%= numPeriods
generator ~^ [-down..-1] ~rd period;
generator ~^ [1..up] ~rd period;
period;
sort();
if (generatorSizeHint == niente and periodSizeHint == niente) {
generator ~^ [-down..-1] ~rd period;
generator ~^ [1..up] ~rd period;
period;
sort();
repeat(numPeriods);
return $;
}
generatorSizeHint al= generator;
periodSizeHint al= period;
const hint = generatorSizeHint ~^ [-down..up];
const periods = ceil(hint ~/_ periodSizeHint) - 1;
const scale = generator ~^ [-down..up] ~% period ~^ periods;
const scales = zip(scale, hint ~rdc periodSizeHint);
sort(scales, (a, b) => compare(a[1], b[1]));
[a for [a, b] of scales];
pop() vor period;
repeat(numPeriods);
}
Expand All @@ -518,13 +532,17 @@ riff wellTemperament(commaFractions, comma = 81/80, down = 0, generator = 3/2, p
period vor pop();
}
riff parallelotope(basis, ups = niente, downs = niente, equave = 2) {
"Span a parallelotope by extending a basis combinatorically. \`ups\` defaults to all ones while \`downs\` defaults to all zeros.";
basis = basis[..];
ups = ups[..] if ups else [];
downs = downs[..] if downs else [];
while (length(ups) < length(basis)) push(1, ups);
while (length(downs) < length(basis)) push(0, downs);
riff parallelotope(basis, ups = niente, downs = niente, equave = 2, basisSizeHints = niente, equaveSizeHint = niente) {
"Span a parallelotope by extending a basis combinatorically. \`ups\` defaults to all ones while \`downs\` defaults to all zeros.\\
The size hints are used to get the correct period reduction when generating a preimage.";
const basis_ = basis[..];
const ups_ = ups[..] if ups else [];
const downs_ = downs[..] if downs else [];
while (length(ups_) < length(basis_)) push(1, ups_);
while (length(downs_) < length(basis_)) push(0, downs_);
basis = basis_[..];
ups = ups_[..];
downs = downs_[..];
equave ~^ 0;
Expand All @@ -536,9 +554,39 @@ riff parallelotope(basis, ups = niente, downs = niente, equave = 2) {
popAll($$) tns~ generator ~^ [-down..up];
}
i => i ~rdc equave;
if (basisSizeHints == niente and equaveSizeHint == niente) {
$ = $$;
i => i ~rdc equave;
sort();
return $;
}
const scale = popAll();
basis = basis_;
ups = ups_;
downs = downs_;
sort();
basisSizeHints al= [];
equaveSizeHint al= equave;
while (length(basisSizeHints) < length(basis))
push(basis[length(basisSizeHints)], basisSizeHints);
equaveSizeHint ~^ 0;
while (basisSizeHints) {
const generator = pop(basisSizeHints);
const up = pop(ups);
const down = pop(downs);
popAll($$) tns~ generator ~^ [-down..up];
}
const hint = popAll();
const equaves = ceil(hint ~/_ equaveSizeHint) - 1;
const scales = zip(scale ~% equave ~^ equaves, hint ~rdc equaveSizeHint);
sort(scales, (a, b) => compare(a[1], b[1]));
[a for [a, b] of scales];
}
riff eulerGenus(guide, root = 1, equave = 2) {
Expand Down

0 comments on commit 35b7344

Please sign in to comment.