monument_cli 0.13.0

CLI interface to Monument, a fast and flexible composing engine.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
# Monument CLI Guide

**NOTE:** There is no guarantee of the stability of this format; it is, in theory, able to change at
any point without warning.  I'm working on a GUI for Monument which, unlike that of Wheatley in
Ringing Room, will be fully featured and will become the intended way to use Monument (the core Rust
library is still accessible to anyone who wants to do really complex things).  I won't make breaking
changes to the TOML format for the fun of it, but it may change to reflect the GUI layout.  The GUI
also won't obsolete the TOML-based format (I use it for testing purposes), but it's only really
intended for my own use and I won't recommend it for users once the GUI exists.

## Contents

- [Installation]#installation
- [Example]#example
- [Quick List of Parameters]#quick-list-of-parameters
- [More Detail on Parameters]#more-detail-on-parameters

---

## Installation

If you already have Rust installed, you can get the latest version of Monument with `cargo install
monument_cli`.  If you don't already have Rust ~what are you doing with your life~ you can download
a pre-built copy of the latest version from
[here](https://github.com/kneasle/ringing/releases/latest).

---

## Example

More examples can be found in [the `examples/` directory](examples/).  This exact example can also
be found there (as [`examples/guide.toml`](example/guide.toml) and
[`examples/guide-music-8.toml`](examples/guide-music-8.toml)).  Monument's CLI reads files are in
the TOML data format, so if you're wondering then check out TOML's
[helpful website](https://toml.io/en/) for more info.

The following example is for quarter peal compositions of Yorkshire, Lessness and Bastow with
changes of method only at calls, and a 2-part with `124365` as the part-head.  It's designed to
cover as many areas of Monument's interface as possible, hence it's rather complex.

### File: `guide.toml`
```toml
# General
length = "QP"
num_comps = 10 # just for brevity; you probably want more comps than this

# Methods
methods = [
    "Yorkshire Surprise Major",
    { title = "Lessness Surprise Major", shorthand = "E" },
    { name = "Bastow", place_notation = "x2,1", stage = 8 },
]
splice_style = "calls"
method_count = { min = 100, max = 600 } # relax method balance to allow for Bastow

# Calls
base_calls = "near" # optional, since this is the default
bob_weight = -7
single_weight = -10
calls = [{ symbol = "b", place_notation = "1456", weight = -12 }]

# Music
music_file = "guide-music-8.toml" # Relative to this file, so expects `music-8.toml` in the same folder
music = [
    { patterns = ["5678*", "8765*"], weight = 2 }, # Boost music off the front
    { pattern = "*87", weight = -1, stroke = "back" }, # Slightly penalise 87s at back
]

# Courses
part_head = "124365"
split_tenors = true
# courses = ["*78", "*7856"] # uncomment to override `split_tenors`
leadwise = false # optional: Monument would have determined this
course_weights = [{ pattern = "*78", weight = 0.05 }] # slight boost for tenors-together courses

# Starts/Ends (commented because they currently play badly with multi-part spliced)
# snap_start = true
# end_indices = [0] # only allow non-snap finishes
```

### File: `guide-music-8.toml`

```toml
# This file contains just an array called `music`, and can be imported into other files.  This is
# useful for e.g. default music schemes
[[music]]
run_lengths = [4, 5, 6, 7, 8]
internal = false # optional; this is the default

[[music]]
pattern = "*6578"
weight = 1.2

[[music]]
patterns = ["*5678", "*8765", "5678*", "8765*"]
weight = 0.5
```

### Output

Running `monument_cli guide.toml` outputs:

```text
-- snip --

SEARCH COMPLETE!

len: 1264, ms: [576, 576, 112], score: 137.40, avg: 0.108703, str: BBBB[bB]EEE[H]EEE[B]EEE[sH]YYYY[W]YYYYY[B]BBBBBBB[B]BBB
len: 1296, ms: [576, 512, 208], score: 141.80, avg: 0.109414, str: BBBB[B]YYYY[B]YYYYY[M]BBBBBB[W]E[sH]BBBB[B]E[V]BBBBBBB[F]E[bB]EEE[H]BBBB[B]EE[W]B
len: 1272, ms: [576, 512, 184], score: 139.30, avg: 0.109513, str: BBBB[B]EEE[bH]YYYY[W]YYYYY[B]BBBBBBB[bB]BBBBBBB[B]EEE[H]BBBB[B]EE[W]B
len: 1328, ms: [576, 576, 176], score: 146.30, avg: 0.110166, str: BBBB[B]EEE[bH]EEE[B]BBBB[sH]BBBB[B]EEE[bH]YYYY[W]YYYYY[B]BBBBBBB[bB]BBB
len: 1288, ms: [576, 576, 136], score: 144.10, avg: 0.111879, str: BBBB[B]BBBBB[M]EEEEEE[bH]YYYY[W]YYYYY[bB]EEE[H]BBBB[B]BBB[W]B
len: 1312, ms: [512, 576, 224], score: 148.20, avg: 0.112957, str: BBBB[B]YY[H]YY[B]YY[H]YY[B]BBBBBBB[B]EEEE[sM]EEEE[V]BBBBBBB[F]E[B]BBBBBBB[bB]BBB
len: 1280, ms: [576, 576, 128], score: 146.20, avg: 0.114219, str: BBBB[B]YYYY[B]BBBBBBB[B]EEEE[sM]EEEEE[W]B[W]YYYYY[B]BBBB[sH]
len: 1296, ms: [576, 576, 144], score: 158.00, avg: 0.121914, str: BBBB[B]EEEE[M]BBBB[F]B[I]EEEEE[M]BBBBBB[W]YYYYY[B]YYYY[B]BBB
len: 1296, ms: [576, 576, 144], score: 158.00, avg: 0.121914, str: BBBB[B]YYYY[B]YYYYY[M]BBBBBB[W]EEEEE[F]B[I]BBBB[W]EEEE[B]BBB
len: 1288, ms: [576, 576, 136], score: 159.00, avg: 0.123447, str: EEE[B]E[V]BBBB[M]BBBB[F]E[B]EEEE[M]BBBBBB[W]YYYYY[B]YYYY[B]BBB
Search completed in 3.364357029s
```

3.4s is not bad!  And those compositions are pretty decent too.  You can see how the call weighting
is promoting compositions which make clever use of few calls.

You can use [`to-complib.py`](to-complib.py) to generate a calling string to paste into CompLib's
composition input spreadsheet.  The string will be automatically copied to your system's clipboard.
For example, running `./to-complib.py
BBBB[B]YYYY[B]YYYYY[M]BBBBBB[W]E[sH]BBBB[B]E[V]BBBBBBB[F]E[bB]EEE[H]BBBB[B]EE[W]B` will copy this
into your clipboard:
```text
M	F	Methods	V	B	H	W
				-
				-
-
						-
					s
				-
			-
	-
				b
					-
				-
						-
		BBBBYYYYYYYYYBBBBBBEBBBBEBBBBBBBEEEEBBBBEEB
```

When pasted into CompLib, this gives
[this comp](https://complib.org/composition/90918?accessKey=88cedf2b68369eb0987a752ca5b17bc76931eb8c).

---

## Quick List of Parameters

Here's a short list of the parameters, along with their default value.  Clicking each value will
take you to more in-depth docs about it.

**General:**
- [`length`](#length-required)
- [`num_comps = 100`](#num_comps)
- ~~[`allow_false = false`](#allow_false)~~ _(removed in v0.13.0)_
- [`require_truth = true`](#require_truth)
- ~~[`queue_limit`](#queue_limit)~~ _(removed in v0.12.0)_
- [`graph_size_limit`](#graph_size_limit)

**Methods:**
- [`method`](#method)
- [`methods`](#methods-2)
- [`method_count`](#method_count) (default to ±10% balance)
- [`splice_style = "leads"`](#splice_style)
- [`splice_weight = 0.0`](#splice_weight) _(since v0.7.0)_

**Calls:**
- [`base_calls = "near"`](#base_calls)
- [`bobs_only = false`](#bobs_only-and-singles_only) _(since v0.6.0)_
- [`singles_only = false`](#bobs_only-and-singles_only) _(since v0.6.0)_
- [`bob_weight = -1.8`](#bob_weight-and-single_weight)
- [`single_weight = -2.3`](#bob_weight-and-single_weight)
- [`calls = []`](#calls-2)

**Music:**
- ~~[`default_music = true`](#default_music)~~ _(since v0.8.0, replaced by `base_music` in v0.9.0)_
- [`base_music = "default"`](#base_music) _(since v0.9.0)_
- [`music_file`](#music_file) (optional)
- [`music = []`](#music-2)
- [`start_stroke = "back"`](#start_stroke)

**Courses:**
- [`part_head = ""`](#part_head) (i.e. default to 1-part)
- [`split_tenors = false`](#split_tenors)
- [`courses`](#courses) (default determined by `split_tenors`, _renamed from `course_heads` in v0.13.0)_
- [`course_weights = []`](#course_weights) _(renamed from `ch_weights` in v0.13.0)_
- [`handbell_coursing_weight = 0`](#handbell_coursing_weight)
- ~~[`leadwise`](#leadwise) (default set by Monument)~~ _(removed in v0.10.0)_
- [`non_duffer_courses`](#non_duffer_courses) _(added in v0.12.0)_
- [`max_total_duffer`](#max_total_duffer-and-max_contiguous_duffer) _(added in v0.12.0)_
- [`max_contiguous_duffer`](#max_total_duffer-and-max_contiguous_duffer) _(added in v0.12.0)_

**Starts/Ends:**
- [`start_row = <rounds>`](#start_row-and-end_row) _(since v0.10.0)_
- [`end_row = <rounds>`](#start_row-and-end_row) _(since v0.10.0)_
- [`snap_start = false`](#snap_start)
- [`start_indices`](#start_indices-and-end_indices) (default set by `snap_start`)
- [`end_indices`](#start_indices-and-end_indices) (default to allow any finish)


------

## More Detail on Parameters

### General

#### `length` (required)

Determines the _inclusive_ range of lengths into which the compositions must fit.  This can take the
following forms:

```toml
length = { min = 600, max = 700 } # require length of 600-700 rows (inclusive)
length = 1729        # require exact length

length = "practice"  # equivalent to `{ min =    0, max =  300 }`
length = "QP"        # equivalent to `{ min = 1250, max = 1350 }`
length = "half peal" # equivalent to `{ min = 2500, max = 2600 }`
length = "peal"      # equivalent to `{ min = 5000, max = 5200 }`
```

#### `num_comps`

The number of compositions you want.  Defaults to `100`

#### `allow_false`

**_(replaced by `require_truth` in v0.13.0)_**

If `true`, Monument will ignore falseness and generate potentially false compositions.  Defaults to
`false`.

#### `queue_limit`

**_(since v0.11.0)_**

Sets a limit on the number of compositions that Monument will consider at any time.  Monument's
memory usage is proportional to this queue's length.  Defaults to 10 million.

#### `graph_size_limit`

**_(since v0.11.0)_**

Sets a limit on the number of chunks in the composition graph.  Defaults to 100,000.

### Methods

#### `method`

Specifies a single method for the composition:

```toml
method = "Bristol Surprise Major"

# or

[method]
title = "Lincolnshire Surprise Major"
shorthand = "N" # (optional; defaults to the first letter of the title)
labels = { LE = 0, HL = 16 } # (optional; defaults to `{ LE = 0 }`)
lead_locations = { LE = 0, HL = 16 } # (pre-v0.11.0 name for `labels`)
# Overrides for global values (all optional):
count = { min = 224, max = 600 }
courses = ["*78"]
start_indices = [2]
end_indices = [2]

# or

[method]
name = "Double Norwich Court" # Note this is *name*, not *title*
place_notation = "x4x36x5x8,8"
stage = 8
shorthand = "N" # (optional; defaults to the first letter of the title)
labels = { LE = 0, HL = 8 } # (optional; defaults to `{ LE = 0 }`)
lead_locations = { LE = 0, HL = 16 } # (pre-v0.11.0 name for `labels`)
# Overrides for global values (all optional):
count = { min = 224, max = 600 }
courses = ["*78"]
start_indices = [2]
end_indices = [2]
```

You can also specify multiple indices for the same `label`, useful for e.g. Stedman:

```toml
[method]
title = "Stedman Triples"
labels = { SE = [3, 9] }
```

#### `methods`

Same as `method`, but takes a list of methods:

```toml
methods = [
    "Bristol Surprise Major",
    { title = "Lincolnshire Surprise Major", shorthand = "N" },
    { name = "Bastow", place_notation = "x2,1", stage = 8 },
]
```

#### `splice_style`

Determines how methods can be spliced.  Has no effect for single-method compositions.  Options:
```toml
splice_style = "leads"          # (default; change method at every defined lead location)
# or
splice_style = "calls"          # only change method when a call does happen
```

Before `v0.10.0`, `splice_style = "call locations"` was possible and would only add splices where a
call _could have_ been made (even if it wasn't).

(Note that, for versions before `v0.8.0`, `splice_style = "call locations"` and `splice_style =
"calls"` will both allow splices over part heads.  This is fixed in `v0.8.0` and later)

#### `method_count`

Min-max limits on how many rows of each method is allowed.  Defaults to ±10% method balance.
```toml
method_count.min = 0 # Allow Monument to ignore methods, but keep the default maximum
# or
method_count = { min = 100, max = 300 } # Force a given method count range
```

#### `splice_weight`

**_(since v0.7.0)_**

Weight applied to each change of method.  Positive values will encourage more c.o.m.; negative
values will encourage few c.o.m.  Defaults to 0 (i.e. don't care about c.o.m.).

### Calls

#### `base_calls`

Lead-end calls which are automatically generated:
```toml
base_calls = "near" # default; 14 bob and 1234 single
# or
base_calls = "far"  # 1(n-2) bob and 1(n-2)(n-1)n single
# or
base_calls = "none" # no base calls, only what you've added
```

#### `bobs_only` and `singles_only`

**_(since v0.6.0)_**

If either are `true`, then `base_calls` will only generate that call type.  Setting both `bobs_only`
and `singles_only` to `true` makes no sense and causes an error. By default, both bobs and singles
are generated.

#### `bob_weight` and `single_weight`

Sets the score given to the bob/single generated by `base_calls`.  Defaults to `bob_weight = -1.8`,
`single_weight = -2.5`.

#### `calls`

Array of custom calls:
```toml
[[calls]]
place_notation = "16"
symbol = "x"
label = "LE"          # Optional; where in the method to apply the call.  Defaults to "LE"
lead_location = "LE"  # Optional; pre-v0.11.0 name for `label`
weight = -4           # Optional; Score given to each instance of this call.  Defaults to -3
calling_positions = "LIBFVXSMWH" # Optional; defaults to 'LIBFVXSEN...' with 'MWH' added
```

> ##### Note: The (obselete) `debug_symbol` parameter
>
> Up to v0.12.0, calls had a `debug_symbol` which specified the 'long' symbol used when displaying
> the call.  For example, a custom bob would be specified like:
>
> ```
> [[calls]]
> symbol = ""
> debug_symbol = "-"
> place_notation = "12"
> ```
>
> In v0.13.0, this was removed and is calculated automatically, so a custom bob simply needs a symbol
> of `-`:
>
> ```
> [[calls]]
> symbol = "-"
> place_notation = "12"
> ```

> ##### Note: Using different calls in different methods
>
> Since _v0.9.0_, calls can go from/to different lead `labels`.  This is useful if, for example, you
> want to make sure you only apply some calls to some methods.  The following example adds `16` bobs
> only in 8ths place methods, and `14` bobs in 2nds place methods (as in
> [Leary's 23]https://complib.org/composition/21607):
>
> ```toml
> length = "QP"
> methods = [
>     { title = "Bristol Surprise Major",     labels = { LE = 0, 8ths = 0 } },
>     { title = "Deva Surprise Major",        labels = { LE = 0, 8ths = 0 } },
>     { title = "Cambridge Surprise Major",   labels = { LE = 0, 2nds = 0 } },
>     { title = "Superlative Surprise Major", labels = { LE = 0, 2nds = 0 } },
> ]
> part_head = "13456782"
>
> base_calls = "none" # Only use our own custom calls
> [[calls]]
> symbol = ""
> debug_symbol = "-"
> place_notation = "14"
> # '14' bobs go *from* only 2nds place methods, but *to* any method
> label = { from = "2nds", to = "LE" }
>
> [[calls]]
> symbol = "x"
> place_notation = "16"
> # '16' bobs go *from* only 8ths place methods, but *to* any method
> label = { from = "8ths", to = "LE" }
> ```
>
> Notice how we're using lead labels `2nds` and `8ths` to control which calls are able to be placed at
> the end of a lead of each method.  Also note how all calls lead to `LE`, which means that any method
> can follow any call (if the calls didn't change lead location, then 2nds/8ths place methods couldn't
> be spliced over a call).

### Music

#### `default_music`

**_(since v0.8.0, replaced by `base_music` in v0.9.0)_**

See `base_music`:
- `default_music = false` is equivalent to `base_music = "none"`
- `default_music = true` is equivalent to `base_music = "default"`

#### `base_music`

Like [`base_calls`](#base_calls), `base_music` adds a set of basic music definitions to the search.
`base_music` has two values, the default being `base_music = "default"`:

1. `base_music = "none"`:  Adds no music to the search; the only music will be what you explicitly
   specify.
2. `base_music = "default"`:  For most stages, this adds a basic music profile intended to be a sane
   default for most music tastes.  This roughly follows the 'headline' music in CompLib (i.e. the
   summary line shown below every composition.

   The default music profiles are equivalent to importing the following music files:
   - [Minor]src/default-music-minor.toml
   - [Triples]src/default-music-triples.toml
   - [Major]src/default-music-major.toml
   - [Royal]src/default-music-royal.toml
   - [Maximus]src/default-music-maximus.toml

#### `music_file`

Relative path to a file containing music definitions (i.e. a single `music` array).

#### `music`

Array of custom music types:
```toml
[[music]]
run_lengths = [4, 5, 6, 7, 8] # or a single length: `run_length = 4`
internal = true               # Optional; defaults to `false`
# or
patterns = ["*6578", "6578*"]       # or a single pattern: `pattern = "*5x6x7x8*"`
count_each = { min = 12, max = 24 } # Count range applied per-pattern.
                                    # Optional; defaults to allowing anything

# common values:
weight = 2    # Score applied per instance of this music type.  Optional; defaults to `1`
count = { min = 12, max = 24 } # Overall required count range
stroke = "back" # On which stroke(s) to count this music.
                # Options: "both" (default), "back", "hand".
show = true # If `true`, display this music in the composition summary.
            # Optional; defaults to `true`
name = "87s at back" # If `show = true`, sets a custom name used in the summary output.
                     # By default, Monument will decide how to display music (often combining
                     # separate patterns together)
```

#### `start_stroke`

The stroke of the first non-rounds row (technically, the first row that isn't `start_row`).
```toml
start_stroke = "hand" # Default
# or
start_stroke = "back"
```
Before v0.10.0, this was reversed (i.e. the opposite way round to what people expect).

### Courses

#### `part_head`

A row which determines which part heads will be generated.  Note that Monument can generate
compositions with a different part head, provided the same set of parts are generated (so
`part_head = "23456781"` and `part_head = "81234567"` are equivalent but `part_head = "56781234"` is
not).  Defaults to rounds (i.e. one part, or `part_head = ""`).

#### `courses`

List of masks which define the courses that Monument can use.  Defaults to tenors together, or any
course (if `split_tenors` is set).  For example:
```toml
courses = ["*78", "xxxx7856", "12345xxx"]
```

#### `split_tenors`

If `courses` isn't specified, this lets Monument use any courses, as opposed to just those with
the tenors together.  On higher stages, this will almost certainly cause Monument's graph size limit
to be reached.  Defaults to `false`.

#### `course_weights`

Applies some score to every row in a course which contains a lead head which matches a given mask.
For example, the following will weight Monument to create compositions where handbell pairs are
coursing often:
```toml
[[course_weights]]
patterns = [
    "*78",
    "*56", "*65",
    "*34", "*43",
] # can also use e.g. `pattern = "*78"`
weight = 0.05 # this is small because the weight is applied per row
```

#### `handbell_coursing_weight`

Generates `course_weights` which apply the given weight to every row where a handbell pair coursing
(this score gets multiplied for courses with multiple handbell pairs coursing).  Equivalent to
something like this (truncated according to stage):
```toml
[[course_weights]]
patterns = [
    "*12", "*21",
    "*34", "*43",
    "*56", "*65",
    "*78", "*87",
    "*90", "*09",
        ...
]
```
Defaults to 0.

#### `leadwise`

**_(removed in v0.10.0)_**

If set, this will stop Monument using calling positions, and instead label all the calls
positionally.  `courses`, `split_tenors` and `course_weights` will obviously have no effect, and
this will always generate split-tenors compositions.  You should rarely have to set this yourself; by
default, Monument will set this automatically if the tenor is affected by the part head (e.g. in
cyclic) but otherwise will stick to course-wise compositions.  The only times you're likely to need
this is for weird cases like differential methods, which don't have a well-defined concept of a
'course head'.

#### `non_duffer_courses`

Specifies which courses are 'non-duffer'.  Courses which don't satisfy anything in
`non_duffer_courses` are considered 'duffers' and `max_{total,contiguous}_duffer` can be used to
restrict how much 'duffer' is allowed in a given composition.

Shorthands for `any_bells` and `any_stroke` can be used to easily specify lots of courses based on
some pattern.  For example, all the following specify the 4-bell run courses in most Major methods:

1. Specifying all courses explicitly:
   ```toml
   non_duffer_courses = [
       "*5678", "*8765", "*6587", "*7856",
       "*4567", "*7654", "*5476", "*6745",
       "*3456", "*6543", "*4365", "*5634",
       "*2345", "*5432", "*3254", "*4523",
   ]
   ```
2. Using `any_bells` to expand the courses with every similar pattern of bells:
   ```toml
   non_duffer_courses = [
       { courses = ["*5678", "*8765", "*6587", "*7856"], any_bells = true },
   ]
   ```
3. Using `any_stroke` to expand music on backstroke to that on handstroke:
   ```toml
   non_duffer_courses = [
       { courses = ["*5678", "*8765"], any_bells = true, any_stroke = true },
   ]
   ```

#### Quick note on `non_duffer_courses` and multi-parts

Currently, Monument does not consider the non-duffer status of each part separately.  Thus, a
'chunk' of ringing is a non-duffer if and only if it's a non-duffer in **every** part.  This is a
potential gotcha when combined with e.g. cyclic multi-parts.  For example, if you want
'non-duffers' to be courses which generate 4-bell runs in _some_ part, the first instinct is often
to specify 4-bell run courses like so:

```toml
non_duffer_courses = [{ courses = ["*5678", "*8765"], any_bells = true, any_stroke = true }]
```

This, however, makes only the plain course a non-duffer.  Any other course will generate `*6782` in
some part and therefore all other 4-bell run courses are a duffer in some part.  To get the
expected behaviour, you need to explicitly add `*6782`, `*7823` and `*8234` as non-duffers, like so:

```toml
non_duffer_courses = [
  { courses = ["*5678", "*8765"], any_bells = true, any_stroke = true },
  { courses = ["*6782", "*7823", "*8234"], any_stroke = true } # Not using `any_bells` here
]
```

#### `max_total_duffer` and `max_contiguous_duffer`

`max_total_duffer` and `max_contiguous_duffer` specify limits (in number of rows) on how much
'duffer' can be rung.  For example, the following produces QPs of Bristol with at most two leads of
_consecutive_ duffer and at most four leads of _total_ duffer:

```toml
length = "qp"
method = "Bristol Surprise Major"
bobs_only = true

non_duffer_courses = [
    { courses = [ "*5678", "*5x678", "*8765", "*8x765" ], any_stroke = true, any_bells = true },
    "*6578", "*5x678",
]
max_contiguous_duffer = 64 # Limited by contiguous duffers
max_total_duffer = 128
```

### Starts/Ends

#### `start_row` and `end_row`

**_(since v0.10.0)_**

Specifies the row used to start or finish the composition, both defaulting to rounds.  These don't
work with multi-parts, but are useful for things like getting Monument to extend 720s of Minor into
a quarter-peal length.

#### `snap_start`

If no `start_indices` have been set, `snap_start = true` allows just snap starts (i.e. is equivalent
to `start_indices = [2]`).  Defaults to `false` (i.e. just lead end starts).

#### `start_indices` and `end_indices`

Sets the indices within the lead where the composition can start/end.  The default value of
`start_indices` is determined by `snap_start`, whereas the `end_indices` defaults to allowing any
value. These indices are taken modulo the lead length and can be negative, so for example 2, -30 and
34 would all refer to the backstroke snap in treble dodging Major.

---

### That's all, folks.  Happy composing!