[med-svn] [praat] 01/07: New upstream version 6.0.32

Rafael Laboissiere rafael at debian.org
Sun Sep 24 06:50:25 UTC 2017


This is an automated email from the git hooks/post-receive script.

rafael pushed a commit to branch master
in repository praat.

commit a8528919f73189c81efe192423e0699c24aee8ad
Author: Rafael Laboissiere <rafael at debian.org>
Date:   Wed Sep 20 08:59:50 2017 -0300

    New upstream version 6.0.32
---
 EEG/EEG.cpp                                        |    6 +-
 EEG/EEG.h                                          |    4 +-
 EEG/ERPTier.cpp                                    |   25 +-
 EEG/ERPTier.h                                      |   12 +-
 EEG/ERPWindow.cpp                                  |   26 +-
 EEG/praat_EEG.cpp                                  |   22 +-
 LPC/Cepstrogram.cpp                                |   16 +-
 LPC/Cepstrogram.h                                  |    2 +-
 LPC/Cepstrum.h                                     |    4 +-
 LPC/LPC.cpp                                        |   32 +-
 LPC/LPC_and_LineSpectralFrequencies.cpp            |   46 +-
 LPC/LPC_and_Polynomial.cpp                         |    8 +-
 LPC/LPC_to_Spectrogram.cpp                         |   13 +-
 LPC/Sound_and_LPC.cpp                              |   10 +-
 LPC/Sound_and_LPC_robust.cpp                       |   14 +-
 LPC/manual_LPC.cpp                                 |    6 -
 artsynth/Art_Speaker.cpp                           |   38 +-
 artsynth/Art_Speaker_Delta.cpp                     |   16 +-
 artsynth/Artword.cpp                               |   38 +-
 artsynth/Artword.h                                 |   40 +-
 artsynth/ArtwordEditor.cpp                         |   24 +-
 artsynth/ArtwordEditor.h                           |    6 +-
 artsynth/praat_Artsynth.cpp                        |   36 +-
 dwsys/FileInMemory.cpp                             |    2 +-
 dwsys/NUM2.cpp                                     |   25 +-
 dwsys/NUM2.h                                       |   10 +-
 dwsys/NUMcomplex.cpp                               |   73 +-
 dwsys/NUMhuber.cpp                                 |   18 +-
 dwsys/NUMstring.cpp                                |    2 +-
 dwtest/speechsynthesizer_test.praat                |    6 +-
 dwtest/test_ActivationList.praat                   |    2 +-
 dwtest/test_Eigen.praat                            |    2 +-
 dwtest/test_MDS.praat                              |    2 +-
 dwtest/test_PatternList.praat                      |    2 +-
 dwtest/test_Polynomial.praat                       |    8 +-
 dwtools/CCA.cpp                                    |    4 +-
 dwtools/ComplexSpectrogram.cpp                     |    4 +-
 dwtools/DTW.cpp                                    |   15 +-
 dwtools/DataModeler.cpp                            |    7 +-
 dwtools/Discriminant.cpp                           |    4 +-
 dwtools/FilterBank.cpp                             |   21 +-
 dwtools/GaussianMixture.cpp                        |    2 +-
 dwtools/ICA.cpp                                    |  226 ++--
 dwtools/ICA.h                                      |    1 +
 dwtools/KlattGrid.cpp                              |    4 +-
 dwtools/LongSound_extensions.cpp                   |    6 +-
 dwtools/Matrix_extensions.cpp                      |    2 +-
 dwtools/MixingMatrix.cpp                           |   10 +-
 dwtools/MixingMatrix.h                             |    2 +
 dwtools/PCA.cpp                                    |    4 +-
 dwtools/Pitch_extensions.cpp                       |   44 +-
 dwtools/Polynomial.cpp                             |  151 +--
 dwtools/Polynomial.h                               |    4 +-
 dwtools/Polynomial_def.h                           |    2 +-
 dwtools/SSCP.cpp                                   |    4 +-
 dwtools/Sound_and_MixingMatrix.cpp                 |    3 +
 dwtools/Sound_and_Spectrogram_extensions.cpp       |   28 +-
 dwtools/Sound_extensions.cpp                       |   50 +-
 dwtools/Sound_extensions.h                         |    6 +-
 dwtools/Sound_to_Pitch2.cpp                        |   25 +-
 dwtools/Sound_to_SPINET.cpp                        |   78 +-
 dwtools/SpeechSynthesizer_and_TextGrid.cpp         |    4 +-
 dwtools/Table_extensions.cpp                       |   24 +-
 dwtools/Table_extensions.h                         |    4 +-
 dwtools/TextGrid_and_DurationTier.cpp              |    4 +-
 dwtools/TextGrid_and_DurationTier.h                |    2 +-
 dwtools/TextGrid_and_PitchTier.cpp                 |   55 +-
 dwtools/TextGrid_and_PitchTier.h                   |    6 +-
 dwtools/TextGrid_extensions.cpp                    |   14 +-
 dwtools/VowelEditor.cpp                            |   91 +-
 dwtools/manual_BSS.cpp                             |   43 +-
 dwtools/manual_dwtools.cpp                         |  144 ++-
 dwtools/praat_BSS_init.cpp                         |   54 +-
 dwtools/praat_DataModeler_init.cpp                 |   16 +-
 dwtools/praat_David_init.cpp                       |   25 +-
 fon/ExperimentMFC.cpp                              |   10 +-
 fon/Experiment_enums.h                             |    5 +-
 fon/FormantGrid.cpp                                |    8 +-
 fon/FormantGridEditor.cpp                          |    2 +-
 fon/Ltas.cpp                                       |    4 +-
 fon/ManipulationEditor.cpp                         |   71 +-
 fon/Matrix.cpp                                     |    4 +-
 fon/Pitch.cpp                                      |  156 +--
 fon/Pitch.h                                        |   32 +-
 fon/PitchEditor.cpp                                |    6 +-
 fon/PitchTier.cpp                                  |   24 +-
 fon/PitchTier.h                                    |    9 +-
 fon/Pitch_AnyTier_to_PitchTier.cpp                 |    2 +-
 fon/Pitch_Intensity.cpp                            |    2 +-
 fon/Pitch_to_PointProcess.cpp                      |   12 +-
 fon/PointEditor.cpp                                |    2 +-
 fon/Praat_tests.cpp                                |  152 ++-
 fon/Praat_tests.h                                  |    2 +-
 fon/Praat_tests_enums.h                            |    9 +-
 fon/RealTier.cpp                                   |    4 +-
 fon/RealTierEditor.cpp                             |    6 +-
 fon/RunnerMFC.cpp                                  |   12 +-
 fon/Sampled.cpp                                    |    6 +-
 fon/Sampled.h                                      |    4 +-
 fon/SampledXY_def.h                                |    4 +-
 fon/Sampled_def.h                                  |    4 +-
 fon/Sound.cpp                                      |   82 +-
 fon/Sound.h                                        |   10 +-
 fon/SoundEditor.cpp                                |    2 +-
 fon/SoundRecorder.cpp                              |   20 +-
 fon/Sound_and_Spectrogram.cpp                      |   20 +-
 fon/Sound_and_Spectrogram.h                        |    4 +-
 fon/Sound_audio.cpp                                |    8 +-
 fon/Sound_files.cpp                                |    2 +-
 fon/Sound_to_Cochleagram.cpp                       |    2 +-
 fon/Sound_to_Formant.cpp                           |    4 +-
 fon/Sound_to_Harmonicity_GNE.cpp                   |    4 +-
 fon/Sound_to_Intensity.cpp                         |   24 +-
 fon/Sound_to_Pitch.cpp                             |   20 +-
 fon/Spectrum.cpp                                   |    2 +-
 fon/TextGrid.cpp                                   |   50 +-
 fon/TextGrid.h                                     |   26 +-
 fon/TextGridEditor.cpp                             |   36 +-
 fon/TextGrid_Sound.cpp                             |   48 +-
 fon/TextGrid_Sound.h                               |    8 +-
 fon/TimeSoundAnalysisEditor.cpp                    |  132 +-
 fon/TimeSoundAnalysisEditor_enums.h                |    4 +-
 fon/TimeSoundEditor.cpp                            |   16 +-
 fon/VocalTract_to_Spectrum.cpp                     |   35 +-
 fon/VoiceAnalysis.cpp                              |   10 +-
 fon/manual_Fon.cpp                                 |   41 -
 fon/manual_Manual.cpp                              |    4 +-
 fon/manual_Picture.cpp                             |    6 +-
 fon/manual_Script.cpp                              |  363 ++++--
 fon/manual_annotation.cpp                          |    4 +-
 fon/manual_glossary.cpp                            |    4 +-
 fon/manual_sound.cpp                               |   78 +-
 fon/manual_soundFiles.cpp                          |    6 +-
 fon/manual_spectrum.cpp                            |   26 +-
 fon/manual_tutorials.cpp                           |   25 +-
 fon/praat_Fon.cpp                                  |   64 +-
 fon/praat_Matrix.cpp                               |   20 +-
 fon/praat_Sound.cpp                                |   30 +-
 fon/praat_TextGrid_init.cpp                        |   62 +-
 fon/praat_Tiers.cpp                                |   29 +-
 fon/praat_TimeTier.cpp                             |    4 +-
 gram/Network.cpp                                   |   10 +-
 gram/Network.h                                     |    4 +-
 gram/Network_enums.h                               |    5 +-
 gram/OTGrammar.cpp                                 |  118 +-
 gram/OTGrammarEditor.cpp                           |   18 +-
 gram/OTGrammar_enums.h                             |    5 +-
 gram/OTMulti.cpp                                   |   92 +-
 gram/OTMultiEditor.cpp                             |    2 +-
 main/main_Praat.cpp                                |    6 +-
 num/NUMarrays.cpp                                  |    2 +-
 num/NUMsort.cpp                                    |  117 +-
 stat/Table.cpp                                     |   16 +-
 stat/Table.h                                       |    6 +-
 stat/TableEditor.cpp                               |    2 +-
 stat/TableOfReal.cpp                               |   34 +-
 stat/TableOfReal.h                                 |    8 +-
 stat/praat_Stat.cpp                                |  118 +-
 stat/praat_TableOfReal.cpp                         |    8 +-
 sys/Data.cpp                                       |   64 +-
 sys/Data.h                                         |   43 +-
 sys/DataEditor.cpp                                 |   43 +-
 sys/DemoEditor.cpp                                 |    4 +-
 sys/Editor.cpp                                     |    8 +-
 sys/EditorM.h                                      |  126 +-
 sys/Editor_enums.h                                 |   10 +-
 sys/Formula.cpp                                    |  786 +++++++++---
 sys/Formula.h                                      |    6 +-
 sys/Graphics.cpp                                   |   24 +-
 sys/Graphics.h                                     |   16 +-
 sys/GraphicsP.h                                    |   12 +-
 sys/GraphicsPostscript.cpp                         |   20 +-
 sys/Graphics_enums.h                               |   24 +-
 sys/Graphics_image.cpp                             |    4 +-
 sys/Graphics_record.cpp                            |    2 +-
 sys/Graphics_text.cpp                              |  208 +--
 sys/Graphics_utils.cpp                             |    2 +-
 sys/GuiText.cpp                                    |    6 +-
 sys/HyperPage.cpp                                  |  136 +-
 sys/HyperPage.h                                    |   54 +-
 sys/Interpreter.cpp                                | 1343 +++++++++++++-------
 sys/Interpreter.h                                  |   10 +-
 sys/ManPage.h                                      |    4 +-
 sys/ManPages.cpp                                   |   63 +-
 sys/ManPagesM.h                                    |   50 +-
 sys/Manual.cpp                                     |   67 +-
 sys/PAIRWISE_SUM.h                                 |  542 ++++++++
 sys/Preferences.cpp                                |   10 +-
 sys/Preferences.h                                  |   16 +-
 sys/Printer.cpp                                    |   14 +-
 sys/Printer.h                                      |   10 +-
 sys/TextEditor.cpp                                 |    2 +-
 sys/Ui.cpp                                         |   71 +-
 sys/abcio.cpp                                      |  152 ++-
 sys/abcio.h                                        |   15 +-
 sys/abcio_enums.h                                  |    8 +-
 sys/complex.cpp                                    |  193 +--
 sys/complex.h                                      |  111 +-
 sys/enums.h                                        |   18 +-
 sys/enums_getText.h                                |   10 +-
 sys/enums_getValue.h                               |   14 +-
 sys/melder.cpp                                     |   42 +-
 sys/melder.h                                       |  138 +-
 sys/melder_alloc.cpp                               |   11 +-
 sys/melder_atof.cpp                                |   78 +-
 sys/melder_audio.cpp                               |   60 +-
 sys/melder_audiofiles.cpp                          |   14 +-
 sys/melder_debug.cpp                               |   15 +-
 sys/melder_files.cpp                               |   20 +-
 sys/melder_ftoa.cpp                                |   48 +-
 sys/melder_readtext.cpp                            |   32 +-
 sys/melder_textencoding.cpp                        |   74 +-
 sys/melder_writetext.cpp                           |   20 +-
 sys/oo.h                                           |   31 +-
 sys/oo_CAN_WRITE_AS_ENCODING.h                     |   16 +-
 sys/oo_COPY.h                                      |   22 +-
 sys/oo_DESCRIPTION.h                               |   16 +-
 sys/oo_DESTROY.h                                   |   16 +-
 sys/oo_EQUAL.h                                     |   24 +-
 sys/oo_READ_BINARY.h                               |   40 +-
 sys/oo_READ_TEXT.h                                 |   42 +-
 sys/oo_WRITE_BINARY.h                              |   28 +-
 sys/oo_WRITE_TEXT.h                                |   46 +-
 sys/oo_undef.h                                     |    8 +-
 sys/praat.cpp                                      |   26 +-
 sys/praat.h                                        |   43 +-
 sys/praat_logo.cpp                                 |    2 +-
 sys/praat_objectMenus.cpp                          |   14 +-
 sys/praat_picture.cpp                              |   38 +-
 sys/praat_script.cpp                               |    4 +-
 sys/praat_version.h                                |    8 +-
 sys/prefs.h                                        |    2 +-
 sys/prefs_define.h                                 |    6 +-
 sys/prefs_install.h                                |    4 +-
 sys/tensor.cpp                                     | 1052 ++-------------
 sys/tensor.h                                       |   42 +-
 test/createPraatTests.praat                        |   83 ++
 test/dwtools/Discriminant.praat                    |    2 +-
 test/fon/Sound_to_Formant.praat                    |    5 +-
 test/fon/Sound_to_LPC.praat                        |    4 +-
 test/fon/Spectrum_draw.praat                       |    4 +-
 test/fon/data.praat                                |  Bin 13492 -> 13554 bytes
 test/fon/endian.praat                              |   29 +-
 test/fon/resample16_8.praat                        |   12 +-
 test/fon/resample22_8.praat                        |   10 +-
 test/fon/resample44_8.praat                        |   12 +-
 test/fon/soundFiles.praat                          |    6 +-
 test/num/alloc.praat                               |   44 +
 test/num/assign.praat                              |   11 +
 test/num/mean.R                                    |    2 +-
 test/num/mean.praat                                |   38 +-
 test/num/mul.praat                                 |   26 +
 test/num/plusgets.praat                            |   17 +
 test/num/sort.praat                                |   21 +
 test/num/sum.praat                                 |    7 +-
 test/{runAllTests.praat => runAllPraatTests.praat} |    4 +-
 ...ests_leak.praat => runAllPraatTests_leak.praat} |   10 +-
 test/script/RBM.praat                              |   74 +-
 test/script/script.praat                           |  Bin 390 -> 576 bytes
 test/sys/Formula.cpp.praat                         |   59 +
 test/sys/object.praat                              |   34 +-
 test/sys/script2.praat                             |  Bin 4276 -> 4734 bytes
 262 files changed, 6166 insertions(+), 4998 deletions(-)

diff --git a/EEG/EEG.cpp b/EEG/EEG.cpp
index 7d14e0e..976b7a3 100644
--- a/EEG/EEG.cpp
+++ b/EEG/EEG.cpp
@@ -534,11 +534,11 @@ void EEG_setChannelToZero (EEG me, const char32 *channelName) {
 	}
 }
 
-void EEG_removeTriggers (EEG me, int which_Melder_STRING, const char32 *criterion) {
+void EEG_removeTriggers (EEG me, kMelder_string which, const char32 *criterion) {
 	try {
 		if (my textgrid -> tiers->size < 2 || ! Melder_equ (my textgrid -> tiers->at [2] -> name, U"Trigger"))
 			Melder_throw (me, U" does not have a Trigger channel.");
-		TextGrid_removePoints (my textgrid.get(), 2, which_Melder_STRING, criterion);
+		TextGrid_removePoints (my textgrid.get(), 2, which, criterion);
 	} catch (MelderError) {
 		Melder_throw (me, U": triggers not removed.");
 	}
@@ -618,7 +618,7 @@ autoEEG EEG_extractPart (EEG me, double tmin, double tmax, bool preserveTimes) {
 		for (long ichan = 1; ichan <= my numberOfChannels; ichan ++) {
 			thy channelNames [ichan] = Melder_dup (my channelNames [ichan]);
 		}
-		thy sound = Sound_extractPart (my sound.get(), tmin, tmax, kSound_windowShape_RECTANGULAR, 1.0, preserveTimes);
+		thy sound = Sound_extractPart (my sound.get(), tmin, tmax, kSound_windowShape::RECTANGULAR, 1.0, preserveTimes);
 		thy textgrid = TextGrid_extractPart (my textgrid.get(), tmin, tmax, preserveTimes);
 		thy xmin = thy textgrid -> xmin;
 		thy xmax = thy textgrid -> xmax;
diff --git a/EEG/EEG.h b/EEG/EEG.h
index 4f8395c..b04d35c 100644
--- a/EEG/EEG.h
+++ b/EEG/EEG.h
@@ -2,7 +2,7 @@
 #define _EEG_h_
 /* EEG.h
  *
- * Copyright (C) 2011-2012,2014,2015 Paul Boersma
+ * Copyright (C) 2011-2012,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ void EEG_subtractReference (EEG me, const char32 *channelNumber1, const char32 *
 void EEG_subtractMeanChannel (EEG me, long fromChannel, long toChannel);
 void EEG_setChannelToZero (EEG me, long channelNumber);
 void EEG_setChannelToZero (EEG me, const char32 *channelName);
-void EEG_removeTriggers (EEG me, int which_Melder_STRING, const char32 *criterion);
+void EEG_removeTriggers (EEG me, kMelder_string which, const char32 *criterion);
 autoEEG EEG_extractChannel (EEG me, long channelNumber);
 autoEEG EEG_extractChannel (EEG me, const char32 *channelName);
 static inline autoSound EEG_extractSound (EEG me) { return Data_copy (my sound.get()); }
diff --git a/EEG/ERPTier.cpp b/EEG/ERPTier.cpp
index 1a09e43..a0a063c 100644
--- a/EEG/ERPTier.cpp
+++ b/EEG/ERPTier.cpp
@@ -109,7 +109,7 @@ static autoERPTier EEG_PointProcess_to_ERPTier (EEG me, PointProcess events, dou
 
 autoERPTier EEG_to_ERPTier_bit (EEG me, double fromTime, double toTime, int markerBit) {
 	try {
-		autoPointProcess events = TextGrid_getStartingPoints (my textgrid.get(), markerBit, kMelder_string_EQUAL_TO, U"1");
+		autoPointProcess events = TextGrid_getStartingPoints (my textgrid.get(), markerBit, kMelder_string::EQUAL_TO, U"1");
 		autoERPTier thee = EEG_PointProcess_to_ERPTier (me, events.get(), fromTime, toTime);
 		return thee;
 	} catch (MelderError) {
@@ -124,7 +124,7 @@ static autoPointProcess TextGrid_getStartingPoints_multiNumeric (TextGrid me, ui
 		for (int ibit = 0; ibit < numberOfBits; ibit ++) {
 			(void) TextGrid_checkSpecifiedTierIsIntervalTier (me, ibit + 1);
 			if (number & (1 << ibit)) {
-				autoPointProcess bitEvents = TextGrid_getStartingPoints (me, ibit + 1, kMelder_string_EQUAL_TO, U"1");
+				autoPointProcess bitEvents = TextGrid_getStartingPoints (me, ibit + 1, kMelder_string::EQUAL_TO, U"1");
 				if (thee) {
 					thee = PointProcesses_intersection (thee.get(), bitEvents.get());
 				} else {
@@ -133,7 +133,7 @@ static autoPointProcess TextGrid_getStartingPoints_multiNumeric (TextGrid me, ui
 			}
 		}
 		for (int ibit = 0; ibit < numberOfBits; ibit ++) {
-			autoPointProcess bitEvents = TextGrid_getStartingPoints (me, ibit + 1, kMelder_string_EQUAL_TO, U"1");
+			autoPointProcess bitEvents = TextGrid_getStartingPoints (me, ibit + 1, kMelder_string::EQUAL_TO, U"1");
 			if (! (number & (1 << ibit))) {
 				if (thee) {
 					thee = PointProcesses_difference (thee.get(), bitEvents.get());
@@ -159,10 +159,10 @@ autoERPTier EEG_to_ERPTier_marker (EEG me, double fromTime, double toTime, uint1
 }
 
 autoERPTier EEG_to_ERPTier_triggers (EEG me, double fromTime, double toTime,
-	int which_Melder_STRING, const char32 *criterion)
+	kMelder_string which, const char32 *criterion)
 {
 	try {
-		autoPointProcess events = TextGrid_getPoints (my textgrid.get(), 2, which_Melder_STRING, criterion);
+		autoPointProcess events = TextGrid_getPoints (my textgrid.get(), 2, which, criterion);
 		autoERPTier thee = EEG_PointProcess_to_ERPTier (me, events.get(), fromTime, toTime);
 		return thee;
 	} catch (MelderError) {
@@ -171,13 +171,12 @@ autoERPTier EEG_to_ERPTier_triggers (EEG me, double fromTime, double toTime,
 }
 
 autoERPTier EEG_to_ERPTier_triggers_preceded (EEG me, double fromTime, double toTime,
-	int which_Melder_STRING, const char32 *criterion,
-	int which_Melder_STRING_precededBy, const char32 *criterion_precededBy)
+	kMelder_string which, const char32 *criterion,
+	kMelder_string precededBy, const char32 *criterion_precededBy)
 {
 	try {
 		autoPointProcess events = TextGrid_getPoints_preceded (my textgrid.get(), 2,
-			which_Melder_STRING, criterion,
-			which_Melder_STRING_precededBy, criterion_precededBy);
+			which, criterion, precededBy, criterion_precededBy);
 		autoERPTier thee = EEG_PointProcess_to_ERPTier (me, events.get(), fromTime, toTime);
 		return thee;
 	} catch (MelderError) {
@@ -296,7 +295,7 @@ autoERP ERPTier_to_ERP_mean (ERPTier me) {
 	}
 }
 
-autoERPTier ERPTier_extractEventsWhereColumn_number (ERPTier me, Table table, long columnNumber, int which_Melder_NUMBER, double criterion) {
+autoERPTier ERPTier_extractEventsWhereColumn_number (ERPTier me, Table table, long columnNumber, kMelder_number which, double criterion) {
 	try {
 		Table_checkSpecifiedColumnNumberWithinRange (table, columnNumber);
 		Table_numericize_Assert (table, columnNumber);   // extraction should work even if cells are not defined
@@ -313,7 +312,7 @@ autoERPTier ERPTier_extractEventsWhereColumn_number (ERPTier me, Table table, lo
 		for (long ievent = 1; ievent <= my points.size; ievent ++) {
 			ERPPoint oldEvent = my points.at [ievent];
 			TableRow row = table -> rows.at [ievent];
-			if (Melder_numberMatchesCriterion (row -> cells [columnNumber]. number, which_Melder_NUMBER, criterion)) {
+			if (Melder_numberMatchesCriterion (row -> cells [columnNumber]. number, which, criterion)) {
 				autoERPPoint newEvent = Data_copy (oldEvent);
 				thy points. addItem_move (newEvent.move());
 			}
@@ -328,7 +327,7 @@ autoERPTier ERPTier_extractEventsWhereColumn_number (ERPTier me, Table table, lo
 }
 
 autoERPTier ERPTier_extractEventsWhereColumn_string (ERPTier me, Table table,
-	long columnNumber, int which_Melder_STRING, const char32 *criterion)
+	long columnNumber, kMelder_string which, const char32 *criterion)
 {
 	try {
 		Table_checkSpecifiedColumnNumberWithinRange (table, columnNumber);
@@ -345,7 +344,7 @@ autoERPTier ERPTier_extractEventsWhereColumn_string (ERPTier me, Table table,
 		for (long ievent = 1; ievent <= my points.size; ievent ++) {
 			ERPPoint oldEvent = my points.at [ievent];
 			TableRow row = table -> rows.at [ievent];
-			if (Melder_stringMatchesCriterion (row -> cells [columnNumber]. string, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (row -> cells [columnNumber]. string, which, criterion)) {
 				autoERPPoint newEvent = Data_copy (oldEvent);
 				thy points. addItem_move (newEvent.move());
 			}
diff --git a/EEG/ERPTier.h b/EEG/ERPTier.h
index 023413b..cee9794 100644
--- a/EEG/ERPTier.h
+++ b/EEG/ERPTier.h
@@ -2,7 +2,7 @@
 #define _ERPTier_h_
 /* ERPTier.h
  *
- * Copyright (C) 2011,2014,2015 Paul Boersma
+ * Copyright (C) 2011,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -36,16 +36,16 @@ void ERPTier_subtractBaseline (ERPTier me, double tmin, double tmax);
 void ERPTier_rejectArtefacts (ERPTier me, double threshold);
 autoERP ERPTier_extractERP (ERPTier me, long pointNumber);
 autoERP ERPTier_to_ERP_mean (ERPTier me);
-autoERPTier ERPTier_extractEventsWhereColumn_number (ERPTier me, Table table, long columnNumber, int which_Melder_NUMBER, double criterion);
-autoERPTier ERPTier_extractEventsWhereColumn_string (ERPTier me, Table table, long columnNumber, int which_Melder_STRING, const char32 *criterion);
+autoERPTier ERPTier_extractEventsWhereColumn_number (ERPTier me, Table table, long columnNumber, kMelder_number which, double criterion);
+autoERPTier ERPTier_extractEventsWhereColumn_string (ERPTier me, Table table, long columnNumber, kMelder_string which, const char32 *criterion);
 
 autoERPTier EEG_to_ERPTier_bit (EEG me, double fromTime, double toTime, int markerBit);
 autoERPTier EEG_to_ERPTier_marker (EEG me, double fromTime, double toTime, uint16 marker);
 autoERPTier EEG_to_ERPTier_triggers (EEG me, double fromTime, double toTime,
-	int which_Melder_STRING, const char32 *criterion);
+	kMelder_string which, const char32 *criterion);
 autoERPTier EEG_to_ERPTier_triggers_preceded (EEG me, double fromTime, double toTime,
-	int which_Melder_STRING, const char32 *criterion,
-	int which_Melder_STRING_precededBy, const char32 *criterion_precededBy);
+	kMelder_string which, const char32 *criterion,
+	kMelder_string precededBy, const char32 *criterion_precededBy);
 
 /* End of file ERPTier.h */
 #endif
diff --git a/EEG/ERPWindow.cpp b/EEG/ERPWindow.cpp
index 54259f5..1580307 100644
--- a/EEG/ERPWindow.cpp
+++ b/EEG/ERPWindow.cpp
@@ -159,7 +159,7 @@ void ERP_drawScalp_garnish (Graphics graphics, double vmin, double vmax, enum kG
 	}
 	Graphics_setColourScale (graphics, colourScale);
 	Graphics_image (graphics, legend.peek(), 1, 2, 0.85, 0.98, 1, n, -0.8, +0.8, 0.0, 1.0);
-	Graphics_setColourScale (graphics, kGraphics_colourScale_GREY);
+	Graphics_setColourScale (graphics, kGraphics_colourScale::GREY);
 	Graphics_rectangle (graphics, 0.85, 0.98, -0.8, +0.8);
 	Graphics_setTextAlignment (graphics, Graphics_RIGHT, Graphics_TOP);
 	Graphics_text (graphics, 1.0, -0.8,   vmin * 1e6, U" μV");
@@ -220,7 +220,7 @@ void ERP_drawScalp (ERP me, Graphics graphics, double tmin, double tmax, double
 			}
 		}
 	}
-	double whiteValue = colourScale == kGraphics_colourScale_BLUE_TO_RED ? 0.5 * (vmin + vmax) : vmin;
+	double whiteValue = colourScale == kGraphics_colourScale::BLUE_TO_RED ? 0.5 * (vmin + vmax) : vmin;
 	Graphics_setColourScale (graphics, colourScale);
 	for (long irow = 1; irow <= n; irow ++) {
 		double y = -1.0 + (irow - 1) * d;
@@ -232,12 +232,12 @@ void ERP_drawScalp (ERP me, Graphics graphics, double tmin, double tmax, double
 		}
 	}
 	Graphics_image (graphics, image.peek(), 1, n, -1.0-0.5/n, 1.0+0.5/n, 1, n, -1.0-0.5/n, 1.0+0.5/n, vmin, vmax);
-	Graphics_setColourScale (graphics, kGraphics_colourScale_GREY);
+	Graphics_setColourScale (graphics, kGraphics_colourScale::GREY);
 	Graphics_setLineWidth (graphics, 2.0);
 	/*
 	 * Nose.
 	 */
-	Graphics_setGrey (graphics, colourScale == kGraphics_colourScale_BLUE_TO_RED ? 1.0 : 0.5);
+	Graphics_setGrey (graphics, colourScale == kGraphics_colourScale::BLUE_TO_RED ? 1.0 : 0.5);
 	{// scope
 		double x [3] = { -0.08, 0.0, 0.08 }, y [3] = { 0.99, 1.18, 0.99 };
 		Graphics_fillArea (graphics, 3, x, y);
@@ -248,7 +248,7 @@ void ERP_drawScalp (ERP me, Graphics graphics, double tmin, double tmax, double
 	/*
 	 * Ears.
 	 */
-	Graphics_setGrey (graphics, colourScale == kGraphics_colourScale_BLUE_TO_RED ? 1.0 : 0.5);
+	Graphics_setGrey (graphics, colourScale == kGraphics_colourScale::BLUE_TO_RED ? 1.0 : 0.5);
 	Graphics_fillRectangle (graphics, -1.09, -1.00, -0.08, 0.08);
 	Graphics_fillRectangle (graphics, 1.09, 1.00, -0.08, 0.08);
 	Graphics_setColour (graphics, Graphics_BLACK);
@@ -332,10 +332,10 @@ void structERPWindow :: v_drawSelectionViewer () {
 		}
 	}
 	double absoluteExtremum = - minimum > maximum ? - minimum : maximum;
-	if (p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_FIXED_RANGE) {
+	if (p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::FIXED_RANGE) {
 		minimum = p_sound_scaling_minimum;
 		maximum = p_sound_scaling_maximum;
-	} else if (p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_FIXED_HEIGHT) {
+	} else if (p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::FIXED_HEIGHT) {
 		double mean = 0.5 * (minimum + maximum);
 		minimum = mean - 0.5 * p_sound_scaling_height;
 		maximum = mean + 0.5 * p_sound_scaling_height;
@@ -349,19 +349,19 @@ void structERPWindow :: v_drawSelectionViewer () {
 			double x = -1.0 + (icol - 1) * d;
 			if (x * x + y * y > 1.0) {
 				image [irow] [icol] = minimum +
-					( our p_scalp_colourScale == kGraphics_colourScale_BLUE_TO_RED ? 0.46 : 0.1875 ) * (maximum - minimum);
+					( our p_scalp_colourScale == kGraphics_colourScale::BLUE_TO_RED ? 0.46 : 0.1875 ) * (maximum - minimum);
 					   // -0.625 * absoluteExtremum;
 			}
 		}
 	}
 	Graphics_setColourScale (our graphics.get(), our p_scalp_colourScale);
 	Graphics_image (our graphics.get(), image.peek(), 1, n, -1.0-0.5/n, 1.0+0.5/n, 1, n, -1.0-0.5/n, 1.0+0.5/n, minimum, maximum);
-	Graphics_setColourScale (our graphics.get(), kGraphics_colourScale_GREY);
+	Graphics_setColourScale (our graphics.get(), kGraphics_colourScale::GREY);
 	Graphics_setLineWidth (our graphics.get(), 2.0);
 	/*
 	 * Nose.
 	 */
-	Graphics_setGrey (our graphics.get(), our p_scalp_colourScale == kGraphics_colourScale_BLUE_TO_RED ? 1.0 : 0.5);
+	Graphics_setGrey (our graphics.get(), our p_scalp_colourScale == kGraphics_colourScale::BLUE_TO_RED ? 1.0 : 0.5);
 	{// scope
 		double x [3] = { -0.08, 0.0, 0.08 }, y [3] = { 0.99, 1.18, 0.99 };
 		Graphics_fillArea (our graphics.get(), 3, x, y);
@@ -372,7 +372,7 @@ void structERPWindow :: v_drawSelectionViewer () {
 	/*
 	 * Ears.
 	 */
-	Graphics_setGrey (our graphics.get(), our p_scalp_colourScale == kGraphics_colourScale_BLUE_TO_RED ? 1.0 : 0.5);
+	Graphics_setGrey (our graphics.get(), our p_scalp_colourScale == kGraphics_colourScale::BLUE_TO_RED ? 1.0 : 0.5);
 	Graphics_fillRectangle (our graphics.get(), -1.09, -1.00, -0.08, 0.08);
 	Graphics_fillRectangle (our graphics.get(), 1.09, 1.00, -0.08, 0.08);
 	Graphics_setColour (our graphics.get(), Graphics_BLACK);
@@ -390,8 +390,8 @@ void structERPWindow :: v_drawSelectionViewer () {
 }
 
 void structERPWindow :: v_prefs_addFields (EditorCommand cmd) {
-	UiField radio;
-	OPTIONMENU_ENUM (U"Scalp colour space", kGraphics_colourScale, kGraphics_colourScale_BLUE_TO_RED)
+	UiField _radio_;
+	OPTIONMENU_ENUM (U"Scalp colour space", kGraphics_colourScale, kGraphics_colourScale::BLUE_TO_RED)
 }
 void structERPWindow :: v_prefs_setValues (EditorCommand cmd) {
 	SET_ENUM (U"Scalp colour space", kGraphics_colourScale, p_scalp_colourScale)
diff --git a/EEG/praat_EEG.cpp b/EEG/praat_EEG.cpp
index 688106e..8244d84 100644
--- a/EEG/praat_EEG.cpp
+++ b/EEG/praat_EEG.cpp
@@ -130,7 +130,7 @@ FORM (MODIFY_EEG_removeTriggers, U"Remove triggers", nullptr) {
 	OK
 DO
 	MODIFY_EACH (EEG)
-		EEG_removeTriggers (me, removeEveryTriggerThat___, ___theText);
+		EEG_removeTriggers (me, (kMelder_string) removeEveryTriggerThat___, ___theText);
 	MODIFY_EACH_END
 }
 
@@ -253,7 +253,7 @@ FORM (NEW_EEG_to_ERPTier_triggers, U"To ERPTier (triggers)", nullptr) {
 	OK
 DO
 	CONVERT_EACH (EEG)
-		autoERPTier result = EEG_to_ERPTier_triggers (me, fromTime, toTime, getEveryEventWithATriggerThat, theText);
+		autoERPTier result = EEG_to_ERPTier_triggers (me, fromTime, toTime, (kMelder_string) getEveryEventWithATriggerThat, theText);
 	CONVERT_EACH_END (my name, U"_trigger", theText)
 }
 
@@ -268,7 +268,7 @@ FORM (NEW_EEG_to_ERPTier_triggers_preceded, U"To ERPTier (triggers, preceded)",
 DO
 	CONVERT_EACH (EEG)
 		autoERPTier result = EEG_to_ERPTier_triggers_preceded (me, fromTime, toTime,
-			getEveryEventWithATriggerThat, text1, andIsPrecededByATriggerThat, text2);
+			(kMelder_string) getEveryEventWithATriggerThat, text1, (kMelder_string) andIsPrecededByATriggerThat, text2);
 	CONVERT_EACH_END (my name, U"_trigger", text2)
 }
 
@@ -379,7 +379,7 @@ FORM (GRAPHICS_ERP_drawScalp, U"ERP: Draw scalp", nullptr) {
 DO
 	GRAPHICS_EACH (ERP)
 		ERP_drawScalp (me, GRAPHICS, fromTime, toTime,
-			fromVoltage, toVoltage, kGraphics_colourScale_GREY, garnish);
+			fromVoltage, toVoltage, kGraphics_colourScale::GREY, garnish);
 	GRAPHICS_EACH_END
 }
 
@@ -487,7 +487,7 @@ DO
 		long channelNumber = ERP_getChannelNumber (me, channelName);
 		if (channelNumber == 0) Melder_throw (me, U": no channel named \"", channelName, U"\".");
 		double result;
-		Vector_getMaximumAndX (me, GET_REAL (U"left Time range"), GET_REAL (U"right Time range"), channelNumber, GET_INTEGER (U"Interpolation") - 1, & result, nullptr);
+		Vector_getMaximumAndX (me, fromTime, toTime, channelNumber, interpolation, & result, nullptr);
 	NUMBER_ONE_END (U" Volt")
 }
 
@@ -676,7 +676,7 @@ FORM (NEW1_ERPTier_Table_extractEventsWhereColumn_number, U"Extract events where
 DO
 	CONVERT_TWO (ERPTier, Table)
 		long columnNumber = Table_getColumnIndexFromColumnLabel (you, extractAllEventsWhereColumn___);
-		autoERPTier result = ERPTier_extractEventsWhereColumn_number (me, you, columnNumber, ___is___, ___theNumber);
+		autoERPTier result = ERPTier_extractEventsWhereColumn_number (me, you, columnNumber, (kMelder_number) ___is___, ___theNumber);
 	CONVERT_TWO_END (my name)
 }
 
@@ -688,7 +688,7 @@ FORM (NEW1_ERPTier_Table_extractEventsWhereColumn_text, U"Extract events where c
 DO
 	CONVERT_TWO (ERPTier, Table)
 		long columnNumber = Table_getColumnIndexFromColumnLabel (you, extractAllEventsWhereColumn___);
-		autoERPTier result = ERPTier_extractEventsWhereColumn_string (me, you, columnNumber, ___, ___theText);
+		autoERPTier result = ERPTier_extractEventsWhereColumn_string (me, you, columnNumber, (kMelder_string) ___, ___theText);
 	CONVERT_TWO_END (my name)
 }
 
@@ -696,10 +696,10 @@ DO
 
 static autoDaata bdfFileRecognizer (int nread, const char * /* header */, MelderFile file) {
 	const char32 *fileName = MelderFile_name (file);
-	bool isBdfFile = Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".bdf") ||
-	                 Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".BDF");
-	bool isEdfFile = Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".edf") ||
-	                 Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".EDF");
+	bool isBdfFile = Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".bdf") ||
+	                 Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".BDF");
+	bool isEdfFile = Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".edf") ||
+	                 Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".EDF");
 	if (nread < 512 || (! isBdfFile && ! isEdfFile)) return autoDaata ();
 	return EEG_readFromBdfFile (file);
 }
diff --git a/LPC/Cepstrogram.cpp b/LPC/Cepstrogram.cpp
index 0eba4ae..9713c43 100644
--- a/LPC/Cepstrogram.cpp
+++ b/LPC/Cepstrogram.cpp
@@ -272,7 +272,7 @@ autoPowerCepstrogram Sound_to_PowerCepstrogram (Sound me, double pitchFloor, dou
 		// minimum analysis window has 3 periods of lowest pitch
 		double analysisWidth = 3.0  / pitchFloor;
 		double windowDuration = 2.0 * analysisWidth; /* gaussian window */
-		long nFrames;
+		integer nFrames;
 
 		// Convenience: analyse the whole sound into one Cepstrogram_frame
 		if (windowDuration > my dx * my nx) {
@@ -365,7 +365,7 @@ static void complexfftoutput_to_power (double *fft, long nfft, double *dbs, bool
 autoPowerCepstrogram Sound_to_PowerCepstrogram_hillenbrand (Sound me, double pitchFloor, double dt) {
 	try {
 		// minimum analysis window has 3 periods of lowest pitch
-		double analysisWidth = 3  / pitchFloor;
+		double analysisWidth = 3.0 / pitchFloor;
 		if (analysisWidth > my dx * my nx) {
 			analysisWidth = my dx * my nx;
 		}
@@ -381,12 +381,12 @@ autoPowerCepstrogram Sound_to_PowerCepstrogram_hillenbrand (Sound me, double pit
 		for (long i = thy nx; i > 1; i--) {
 			thy z[1][i] -= 0.9 * thy z[1][i - 1];
 		}
-		long nosInWindow = (long) floor (analysisWidth * samplingFrequency), nFrames;
+		integer nosInWindow = (integer) floorl (analysisWidth * samplingFrequency), numberOfFrames;
 		if (nosInWindow < 8) {
 			Melder_throw (U"Analysis window too short.");
 		}
 		double t1;
-		Sampled_shortTermAnalysis (thee.get(), analysisWidth, dt, & nFrames, & t1);
+		Sampled_shortTermAnalysis (thee.get(), analysisWidth, dt, & numberOfFrames, & t1);
 		autoNUMvector<double> hamming (1, nosInWindow);
 		for (long i = 1; i <= nosInWindow; i++) {
 			hamming[i] = 0.54 -0.46 * cos(2 * NUMpi * (i - 1) / (nosInWindow - 1));
@@ -400,11 +400,11 @@ autoPowerCepstrogram Sound_to_PowerCepstrogram_hillenbrand (Sound me, double pit
 		NUMfft_Table_init (&fftTable, nfft); // sound to spectrum
 		
 		double qmax = 0.5 * nfft / samplingFrequency, dq = qmax / (nfftdiv2 + 1);
-		autoPowerCepstrogram him = PowerCepstrogram_create (my xmin, my xmax, nFrames, dt, t1, 0, qmax, nfftdiv2+1, dq, 0);
+		autoPowerCepstrogram him = PowerCepstrogram_create (my xmin, my xmax, numberOfFrames, dt, t1, 0, qmax, nfftdiv2+1, dq, 0);
 		
 		autoMelderProgress progress (U"Cepstrogram analysis");
 		
-		for (long iframe = 1; iframe <= nFrames; iframe++) {
+		for (long iframe = 1; iframe <= numberOfFrames; iframe ++) {
 			double tbegin = t1 + (iframe - 1) * dt - analysisWidth / 2;
 			tbegin = tbegin < thy xmin ? thy xmin : tbegin;
 			long istart = Sampled_xToLowIndex (thee.get(), tbegin);   // ppgb: afronding naar beneden?
@@ -446,8 +446,8 @@ autoPowerCepstrogram Sound_to_PowerCepstrogram_hillenbrand (Sound me, double pit
 				his z[i][iframe] = fftbuf[i] * fftbuf[i];
 			}
 			if ((iframe % 10) == 1) {
-				Melder_progress ((double) iframe / nFrames, U"Cepstrogram analysis of frame ",
-					 iframe, U" out of ", nFrames, U".");
+				Melder_progress ((double) iframe / numberOfFrames, U"Cepstrogram analysis of frame ",
+					 iframe, U" out of ", numberOfFrames, U".");
 			}
 		}
 		return him;
diff --git a/LPC/Cepstrogram.h b/LPC/Cepstrogram.h
index 68d3ac3..e01d65e 100644
--- a/LPC/Cepstrogram.h
+++ b/LPC/Cepstrogram.h
@@ -24,7 +24,7 @@
 */
 
 /*
-	The Cepstrogram is a matrix of REAL numbers.
+	The Cepstrogram is a matrix of real numbers.
 	It is the inverse-fourier-transformed logarithm of the spectrum
 	of a (sound) signal.
 */
diff --git a/LPC/Cepstrum.h b/LPC/Cepstrum.h
index 7170337..12c7a1f 100644
--- a/LPC/Cepstrum.h
+++ b/LPC/Cepstrum.h
@@ -25,7 +25,7 @@
 */
 
 /*
-	The Cepstrum is a sequence of REAL numbers.
+	The Cepstrum is a sequence of real numbers.
 	It is the spectrum of the power spectrum of a (sound) signal.
 */
 
@@ -38,7 +38,7 @@ Thing_define (Cepstrum, Matrix) {
 };
 
 /*
-	The Cepstrum is a sequence of REAL numbers.
+	The Cepstrum is a sequence of real numbers.
 	It is the power spectrum of the power spectrum of a (sound) signal.
 */
 
diff --git a/LPC/LPC.cpp b/LPC/LPC.cpp
index 71361c8..3a8bd05 100644
--- a/LPC/LPC.cpp
+++ b/LPC/LPC.cpp
@@ -1,6 +1,6 @@
 /* LPC.cpp
  *
- * Copyright (C) 1994-2016 David Weenink
+ * Copyright (C) 1994-2017 David Weenink
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -67,16 +67,14 @@ void LPC_Frame_init (LPC_Frame me, int nCoefficients) {
 	my nCoefficients = nCoefficients;
 }
 
-void LPC_init (LPC me, double tmin, double tmax, long nt, double dt, double t1,
-               int predictionOrder, double samplingPeriod) {
+void LPC_init (LPC me, double tmin, double tmax, long nt, double dt, double t1, int predictionOrder, double samplingPeriod) {
 	my samplingPeriod = samplingPeriod;
 	my maxnCoefficients = predictionOrder;
 	Sampled_init (me, tmin, tmax, nt, dt, t1);
 	my d_frames = NUMvector<structLPC_Frame> (1, nt);
 }
 
-autoLPC LPC_create (double tmin, double tmax, long nt, double dt, double t1,
-                int predictionOrder, double samplingPeriod) {
+autoLPC LPC_create (double tmin, double tmax, long nt, double dt, double t1, int predictionOrder, double samplingPeriod) {
 	try {
 		autoLPC me = Thing_new (LPC);
 		LPC_init (me.get(), tmin, tmax, nt, dt, t1, predictionOrder, samplingPeriod);
@@ -133,10 +131,10 @@ void LPC_drawPoles (LPC me, Graphics g, double time, int garnish) {
 autoMatrix LPC_downto_Matrix_lpc (LPC me) {
 	try {
 		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 0.5, 0.5 + my maxnCoefficients, my maxnCoefficients, 1.0, 1.0);
-		for (long j = 1; j <= my nx; j++) {
-			LPC_Frame lpc = & my d_frames[j];
-			for (long i = 1; i <= lpc -> nCoefficients; i++) {
-				thy z[i][j] = lpc -> a[i];
+		for (long j = 1; j <= my nx; j ++) {
+			LPC_Frame lpc = & my d_frames [j];
+			for (long i = 1; i <= lpc -> nCoefficients; i ++) {
+				thy z [i] [j] = lpc -> a [i];
 			}
 		}
 		return thee;
@@ -149,11 +147,11 @@ autoMatrix LPC_downto_Matrix_rc (LPC me) {
 	try {
 		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 0.5, 0.5 + my maxnCoefficients, my maxnCoefficients, 1.0, 1.0);
 		autoNUMvector<double> rc (1, my maxnCoefficients);
-		for (long j = 1; j <= my nx; j++) {
-			LPC_Frame lpc = & my d_frames[j];
+		for (long j = 1; j <= my nx; j ++) {
+			LPC_Frame lpc = & my d_frames [j];
 			NUMlpc_lpc_to_rc (lpc -> a, lpc -> nCoefficients, rc.peek());
-			for (long i = 1; i <= lpc -> nCoefficients; i++) {
-				thy z[i][j] = rc[i];
+			for (long i = 1; i <= lpc -> nCoefficients; i ++) {
+				thy z [i] [j] = rc [i];
 			}
 		}
 		return thee;
@@ -167,12 +165,12 @@ autoMatrix LPC_downto_Matrix_area (LPC me) {
 		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 0.5, 0.5 + my maxnCoefficients, my maxnCoefficients, 1.0, 1.0);
 		autoNUMvector<double> rc (1, my maxnCoefficients);
 		autoNUMvector<double> area (1, my maxnCoefficients);
-		for (long j = 1; j <= my nx; j++) {
-			LPC_Frame lpc = & my d_frames[j];
+		for (long j = 1; j <= my nx; j ++) {
+			LPC_Frame lpc = & my d_frames [j];
 			NUMlpc_lpc_to_rc (lpc -> a, lpc -> nCoefficients, rc.peek());
 			NUMlpc_rc_to_area (rc.peek(), lpc -> nCoefficients, area.peek());
-			for (long i = 1; i <= lpc -> nCoefficients; i++) {
-				thy z[i][j] = area[i];
+			for (long i = 1; i <= lpc -> nCoefficients; i ++) {
+				thy z [i] [j] = area [i];
 			}
 		}
 		return thee;
diff --git a/LPC/LPC_and_LineSpectralFrequencies.cpp b/LPC/LPC_and_LineSpectralFrequencies.cpp
index 198d254..843f7e0 100644
--- a/LPC/LPC_and_LineSpectralFrequencies.cpp
+++ b/LPC/LPC_and_LineSpectralFrequencies.cpp
@@ -1,6 +1,6 @@
 /* LPC_and_LineSpectralFrequencies.cpp
  *
- * Copyright (C) 2016 David Weenink
+ * Copyright (C) 2016-2017 David Weenink
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,8 +32,8 @@
  */
 static void cos2x (double *g, long order) {
 	for (long i = 2; i <= order; i ++) {
-		for (long j = order; j > i; j--) {
-			g [j - 2] -= g[j];
+		for (long j = order; j > i; j --) {
+			g [j - 2] -= g [j];
 		}
 		g [i - 2] -= 2.0 * g [i];
 	}
@@ -44,7 +44,7 @@ static void Polynomial_fromLPC_Frame_lspsum (Polynomial me, LPC_Frame lpc) {
 	
 	long order = lpc -> nCoefficients, g_order = (order + 1) / 2; // orders
 	my coefficients [order + 2] = 1.0;
-	for (long i = 1; i <= order; i++) {
+	for (long i = 1; i <= order; i ++) {
 		my coefficients [order + 2 - i] = lpc -> a [i] + lpc -> a [order + 1 - i];
 	}
 	my coefficients [1] = 1.0;
@@ -54,7 +54,7 @@ static void Polynomial_fromLPC_Frame_lspsum (Polynomial me, LPC_Frame lpc) {
 		Polynomial_divide_firstOrderFactor (me, -1.0, nullptr);
 	}
 	/* transform to cos(w) terms */
-	for (long i = 1; i <= g_order + 1; i++) {
+	for (long i = 1; i <= g_order + 1; i ++) {
 		my coefficients [i] = my coefficients [g_order + i];
 	}
 	my numberOfCoefficients = g_order + 1;
@@ -66,8 +66,8 @@ static void Polynomial_fromLPC_Frame_lspdif (Polynomial me, LPC_Frame lpc) {
 	/* Fa (z) = A(z) - z^-(p+1)A(1/z) */
 	long order = lpc -> nCoefficients;
 	my coefficients [order + 2] = -1.0;
-	for (long i = 1; i <= order; i++) {
-		my coefficients [order + 2 - i] = -lpc -> a [i] + lpc -> a [order + 1 - i];
+	for (long i = 1; i <= order; i ++) {
+		my coefficients [order + 2 - i] = - lpc -> a [i] + lpc -> a [order + 1 - i];
 	}
 	my coefficients [1] = 1.0;
 	my numberOfCoefficients = order + 2;
@@ -79,7 +79,7 @@ static void Polynomial_fromLPC_Frame_lspdif (Polynomial me, LPC_Frame lpc) {
 	}
 	/* transform to cos(w) terms */
 	long g_order = my numberOfCoefficients / 2;
-	for (long i = 1; i <= g_order + 1; i++) {
+	for (long i = 1; i <= g_order + 1; i ++) {
 		my coefficients [i] = my coefficients [g_order + i];
 	}
 	my numberOfCoefficients = g_order + 1;
@@ -116,7 +116,7 @@ static void Roots_fromPolynomial (Roots me, Polynomial g, long numberOfDerivativ
 	/* The constraints M[j] (Semenov et al. eq. (8)) can be calculated by taking absolute values of 
 	 * the polynomial coefficients and evaluating the polynomial and the derivatives at x = 1.0
 	 */
-	for (long k = 0; k <= g_order; k++) {
+	for (long k = 0; k <= g_order; k ++) {
 		gabs [k] = fabs (g -> coefficients [k + 1]);
 	}
 	evaluatePolynomialAndDerivatives (gabs, g_order, 1.0, constraints, numberOfDerivatives);
@@ -131,26 +131,26 @@ static void Roots_fromPolynomial (Roots me, Polynomial g, long numberOfDerivativ
 		while (j <= numberOfDerivatives && (rootsOnIntervalPossible_f || rootsOnIntervalPossible_df)) {
 			intervals [j] = intervals [j - 1] * (xmax - xmin);
 			long k = j - 1;
-			if (j > 1) { // start at first derivative
+			if (j > 1) {   // start at first derivative
 				dsum1 += fabs (derivatives [k]) * intervals [k] / (p2 [k] * fact [k]);
 			}
-			if (j > 2) { // start at second derivative
+			if (j > 2) {   // start at second derivative
 				dsum2 += fabs (derivatives [k]) * intervals [k - 1] / (p2 [k - 1] * fact [k - 1]);
 				if (rootsOnIntervalPossible_f) {
 					double testValue1 = dsum1 + constraints [j] * intervals [j] / (p2 [j] * fact [j]);
-					rootsOnIntervalPossible_f = ! (fxmid + testValue1 < 0 || fxmid - testValue1 > 0);
+					rootsOnIntervalPossible_f = ! (fxmid + testValue1 < 0.0 || fxmid - testValue1 > 0.0);
 				}
 				if (rootsOnIntervalPossible_df) {
 					double testValue2 = dsum2 + constraints [j] * intervals [j - 1] / (p2 [j - 1] * fact [j - 1]);
-					rootsOnIntervalPossible_df = ! (fdxmin + testValue2 < 0 || fdxmin - testValue2 > 0);
+					rootsOnIntervalPossible_df = ! (fdxmin + testValue2 < 0.0 || fdxmin - testValue2 > 0.0);
 				}
 			}
 			j++;
 		}
 		if (rootsOnIntervalPossible_f) {
-			if (rootsOnIntervalPossible_df) { // f(x) uncertain && f'(x) uncertain : bisect
+			if (rootsOnIntervalPossible_df) {   // f(x) uncertain && f'(x) uncertain: bisect
 				xmax = xmid;
-			} else {// f(x) uncertain; f'(x) certain
+			} else {   // f(x) uncertain; f'(x) certain
 				double fxmin = evaluatePolynomial (g, g_order, xmin);
 				double fxmax = evaluatePolynomial (g, g_order, xmax);
 				if (fxmin * fxmax <= 0.0) {
@@ -207,11 +207,11 @@ static void LineSpectralFrequencies_Frame_initFromLPC_Frame_grid (LineSpectralFr
 		Melder_throw (U"Too many bisections.");
 	}
 	// [g1-> xmin, g1 -> xmax] <==> [nyquistFrequency, 0] i.e. highest root corresponds to lowest frequency
-	for (long i = 1; i <= half_order_g1; i++) {
+	for (long i = 1; i <= half_order_g1; i ++) {
 		my frequencies [2 * i - 1] = acos (roots -> v [half_order_g1 + 1 - i].re / 2.0) / NUMpi * maximumFrequency; 
 	}
 	// the roots of g2 lie inbetween the roots of g1
-	for (long i = 1; i <= half_order_g2; i++) {
+	for (long i = 1; i <= half_order_g2; i ++) {
 		double xmax = roots -> v [half_order_g1 + 1 - i].re;
 		double xmin = i == half_order_g1 ? g1 -> xmin : roots -> v [half_order_g1 - i].re;
 		double root = Polynomial_findOneSimpleRealRoot_ridders (g2, xmin, xmax);
@@ -255,7 +255,7 @@ static void LPC_Frame_initFromLineSpectralFrequencies_Frame (LPC_Frame me, LineS
 
 	/* Reconstruct Fs (z) */
 	long numberOfOmegas = (thy numberOfFrequencies + 1) / 2;
-	for (long i = 1; i <= numberOfOmegas; i++) {
+	for (long i = 1; i <= numberOfOmegas; i ++) {
 		double omega = thy frequencies [2 * i -1] / maximumFrequency * NUMpi;
 		my a[i] = -2.0 * cos (omega);
 	}
@@ -263,21 +263,21 @@ static void LPC_Frame_initFromLineSpectralFrequencies_Frame (LPC_Frame me, LineS
 
 	/* Reconstruct Fa (z) */
 	numberOfOmegas = thy numberOfFrequencies / 2;
-	for (long i = 1; i <= numberOfOmegas; i++) {
+	for (long i = 1; i <= numberOfOmegas; i ++) {
 		double omega = thy frequencies [2 * i] / maximumFrequency * NUMpi;
 		my a [i] = -2.0 * cos (omega);
 	}
 	Polynomial_initFromProductOfSecondOrderTerms (fa, my a, numberOfOmegas);
 	
 	if (thy numberOfFrequencies % 2 == 0) {
-		Polynomial_multiply_firstOrderFactor (fs, -1.0); // * (z + 1)
-		Polynomial_multiply_firstOrderFactor (fa, 1.0); // * (z - 1)
+		Polynomial_multiply_firstOrderFactor (fs, -1.0);   // * (z + 1)
+		Polynomial_multiply_firstOrderFactor (fa, 1.0);   // * (z - 1)
 	} else {
-		Polynomial_multiply_secondOrderFactor (fa, 1.0); // * (z^2 - 1)
+		Polynomial_multiply_secondOrderFactor (fa, 1.0);   // * (z^2 - 1)
 	}
 	Melder_assert (fs -> numberOfCoefficients == fa -> numberOfCoefficients);
 	/* A(z) = (Fs(z) + Fa(z) / 2 */
-	for (long i = 1; i <= fs -> numberOfCoefficients - 2; i++) {
+	for (long i = 1; i <= fs -> numberOfCoefficients - 2; i ++) {
 		my a [thy numberOfFrequencies - i + 1] = 0.5 * (fs -> coefficients [i+1] + fa -> coefficients [i+1]);
 	}
 }
diff --git a/LPC/LPC_and_Polynomial.cpp b/LPC/LPC_and_Polynomial.cpp
index 277280c..28eeb9e 100644
--- a/LPC/LPC_and_Polynomial.cpp
+++ b/LPC/LPC_and_Polynomial.cpp
@@ -1,6 +1,6 @@
 /* LPC_and_Polynomial.cpp
  *
- * Copyright (C) 1994-2011, 2015-2016 David Weenink
+ * Copyright (C) 1994-2011, 2015-2017 David Weenink
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,8 +25,8 @@
 autoPolynomial LPC_Frame_to_Polynomial (LPC_Frame me) {
 	long degree = (long) my nCoefficients;
 	autoPolynomial thee = Polynomial_create (-1, 1, degree);
-	for (long i = 1; i <= degree; i++) {
-		thy coefficients[i] = my a[degree - i + 1];
+	for (long i = 1; i <= degree; i ++) {
+		thy coefficients [i] = my a [degree - i + 1];
 	}
 	thy coefficients[degree + 1] = 1.0;
 	return thee;
@@ -41,7 +41,7 @@ autoPolynomial LPC_to_Polynomial (LPC me, double time) {
 		if (iFrame > my nx) {
 			iFrame = my nx;
 		}
-		autoPolynomial thee = LPC_Frame_to_Polynomial (&my d_frames[iFrame]);
+		autoPolynomial thee = LPC_Frame_to_Polynomial (& my d_frames [iFrame]);
 		return thee;
 	} catch (MelderError) {
 		Melder_throw (me, U":no Polynomial created.");
diff --git a/LPC/LPC_to_Spectrogram.cpp b/LPC/LPC_to_Spectrogram.cpp
index b341a6a..820777a 100644
--- a/LPC/LPC_to_Spectrogram.cpp
+++ b/LPC/LPC_to_Spectrogram.cpp
@@ -1,6 +1,6 @@
 /* LPC_to_Spectrogram.cpp
  *
- * Copyright (C) 1994-2011, 2015 David Weenink
+ * Copyright (C) 1994-2011, 2015-2017 David Weenink
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,15 +35,14 @@ autoSpectrogram LPC_to_Spectrogram (LPC me, double dfMin, double bandwidthReduct
 		}
 		double freqStep = samplingFrequency / nfft;
 
-		autoSpectrogram thee = Spectrogram_create (my xmin, my xmax, my nx, my dx, my x1,
-		                                           0.0, samplingFrequency / 2.0, nfft / 2 + 1, freqStep, 0.0);
+		autoSpectrogram thee = Spectrogram_create (my xmin, my xmax, my nx, my dx, my x1, 0.0, samplingFrequency / 2.0, nfft / 2 + 1, freqStep, 0.0);
 
-		for (long i = 1; i <= my nx; i++) {
+		for (long i = 1; i <= my nx; i ++) {
 			double t = Sampled_indexToX (me, i);
 			autoSpectrum spec = LPC_to_Spectrum (me, t, dfMin, bandwidthReduction, deEmphasisFrequency);
-			for (long j = 1; j <= spec -> nx; j++) {
-				double re = spec -> z[1][j], im = spec -> z[2][j];
-				thy z[j][i] =  re * re + im * im;
+			for (long j = 1; j <= spec -> nx; j ++) {
+				double re = spec -> z [1] [j], im = spec -> z [2] [j];
+				thy z [j] [i] =  re * re + im * im;
 			}
 		}
 		return thee;
diff --git a/LPC/Sound_and_LPC.cpp b/LPC/Sound_and_LPC.cpp
index 0985981..2ebb133 100644
--- a/LPC/Sound_and_LPC.cpp
+++ b/LPC/Sound_and_LPC.cpp
@@ -371,7 +371,7 @@ end:
 static autoLPC _Sound_to_LPC (Sound me, int predictionOrder, double analysisWidth, double dt, double preEmphasisFrequency, int method, double tol1, double tol2) {
 	double t1, samplingFrequency = 1.0 / my dx;
 	double windowDuration = 2 * analysisWidth; /* gaussian window */
-	long nFrames, frameErrorCount = 0;
+	integer numberOfFrames, frameErrorCount = 0;
 
 	if (floor (windowDuration / my dx) < predictionOrder + 1) {
 		Melder_throw (U"Analysis window duration too short.\n For a prediction order of ", predictionOrder,
@@ -381,11 +381,11 @@ static autoLPC _Sound_to_LPC (Sound me, int predictionOrder, double analysisWidt
 	if (windowDuration > my dx * my nx) {
 		windowDuration = my dx * my nx;
 	}
-	Sampled_shortTermAnalysis (me, windowDuration, dt, & nFrames, & t1);
+	Sampled_shortTermAnalysis (me, windowDuration, dt, & numberOfFrames, & t1);
 	autoSound sound = Data_copy (me);
 	autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
 	autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
-	autoLPC thee = LPC_create (my xmin, my xmax, nFrames, dt, t1, predictionOrder, my dx);
+	autoLPC thee = LPC_create (my xmin, my xmax, numberOfFrames, dt, t1, predictionOrder, my dx);
 
 	autoMelderProgress progress (U"LPC analysis");
 
@@ -393,7 +393,7 @@ static autoLPC _Sound_to_LPC (Sound me, int predictionOrder, double analysisWidt
 		Sound_preEmphasis (sound.get(), preEmphasisFrequency);
 	}
 
-	for (long i = 1; i <= nFrames; i++) {
+	for (integer i = 1; i <= numberOfFrames; i ++) {
 		LPC_Frame lpcframe = (LPC_Frame) & thy d_frames[i];
 		double t = Sampled_indexToX (thee.get(), i);
 		LPC_Frame_init (lpcframe, predictionOrder);
@@ -418,7 +418,7 @@ static autoLPC _Sound_to_LPC (Sound me, int predictionOrder, double analysisWidt
 			}
 		}
 		if ((i % 10) == 1) {
-			Melder_progress ( (double) i / nFrames, U"LPC analysis of frame ", i, U" out of ", nFrames, U".");
+			Melder_progress ( (double) i / numberOfFrames, U"LPC analysis of frame ", i, U" out of ", numberOfFrames, U".");
 		}
 	}
 	return thee;
diff --git a/LPC/Sound_and_LPC_robust.cpp b/LPC/Sound_and_LPC_robust.cpp
index 3737f1a..c61a471 100644
--- a/LPC/Sound_and_LPC_robust.cpp
+++ b/LPC/Sound_and_LPC_robust.cpp
@@ -188,7 +188,7 @@ autoLPC LPC_and_Sound_to_LPC_robust (LPC thee, Sound me, double analysisWidth, d
 	try {
 		double t1, samplingFrequency = 1.0 / my dx, tol_svd = 0.000001;
 		double location = 0, windowDuration = 2 * analysisWidth; /* Gaussian window */
-		long nFrames, frameErrorCount = 0, iter = 0;
+		integer numberOfFrames, frameErrorCount = 0, iter = 0;
 		long p = thy maxnCoefficients;
 
 		if (my xmin != thy xmin || my xmax != thy xmax) {
@@ -200,8 +200,8 @@ autoLPC LPC_and_Sound_to_LPC_robust (LPC thee, Sound me, double analysisWidth, d
 		if (floor (windowDuration / my dx) < p + 1) {
 			Melder_throw (U"Analysis window too short.");
 		}
-		Sampled_shortTermAnalysis (me, windowDuration, thy dx, & nFrames, & t1);
-		if (nFrames != thy nx || t1 != thy x1) {
+		Sampled_shortTermAnalysis (me, windowDuration, thy dx, & numberOfFrames, & t1);
+		if (numberOfFrames != thy nx || t1 != thy x1) {
 			Melder_throw (U"Incorrect retrieved analysis width");
 		}
 
@@ -220,7 +220,7 @@ autoLPC LPC_and_Sound_to_LPC_robust (LPC thee, Sound me, double analysisWidth, d
 
 		Sound_preEmphasis (sound.get(), preEmphasisFrequency);
 
-		for (long i = 1; i <= nFrames; i++) {
+		for (integer i = 1; i <= numberOfFrames; i ++) {
 			LPC_Frame lpc = (LPC_Frame) & thy d_frames[i];
 			LPC_Frame lpcto = (LPC_Frame) & his d_frames[i];
 			double t = Sampled_indexToX (thee, i);
@@ -238,14 +238,14 @@ autoLPC LPC_and_Sound_to_LPC_robust (LPC thee, Sound me, double analysisWidth, d
 			iter += struct_huber.iter;
 
 			if ( (i % 10) == 1) {
-				Melder_progress ( (double) i / nFrames, U"LPC analysis of frame ", i, U" out of ", nFrames, U".");
+				Melder_progress ((double) i / numberOfFrames, U"LPC analysis of frame ", i, U" out of ", numberOfFrames, U".");
 			}
 		}
 
 		if (frameErrorCount) Melder_warning (U"Results of ", frameErrorCount,
-			U" frame(s) out of ", nFrames, U" could not be optimised.");
+			U" frame(s) out of ", numberOfFrames, U" could not be optimised.");
 		MelderInfo_writeLine (U"Number of iterations: ", iter,
-			U"\n   Average per frame: ", ((double) iter) / nFrames);
+			U"\n   Average per frame: ", (double) iter / numberOfFrames);
 		huber_struct_destroy (&struct_huber);
 		return him;
 	} catch (MelderError) {
diff --git a/LPC/manual_LPC.cpp b/LPC/manual_LPC.cpp
index c7f9d0e..5fa28cc 100644
--- a/LPC/manual_LPC.cpp
+++ b/LPC/manual_LPC.cpp
@@ -692,10 +692,4 @@ MAN_END
 
 }
 
-/*
- BUGS:
- 19980217 djmw LPC_and_Sound_filter ilast was not always defined.
- 19980322 djmw Sound_into_LPC_Frame_auto did not return (nCoefficients == 0) when (r[1] == 0).
-*/
-
 /* End of file manual_LPC.cpp */
diff --git a/artsynth/Art_Speaker.cpp b/artsynth/Art_Speaker.cpp
index 23d89db..cfe46d5 100644
--- a/artsynth/Art_Speaker.cpp
+++ b/artsynth/Art_Speaker.cpp
@@ -1,6 +1,6 @@
 /* Art_Speaker.cpp
  *
- * Copyright (C) 1992-2012,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2012,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -41,9 +41,9 @@ void Art_Speaker_toVocalTract (Art _art, Speaker speaker,
 	/* The sternohyoid muscle pulls the hyoid bone down.			*/
 	/* The sphincter muscle pulls the hyoid bone backwards.		*/
 
-	hyoid.dx = -5 * f * art [kArt_muscle_SPHINCTER];
-	hyoid.dy = 20 * f * (art [kArt_muscle_STYLOHYOID]
-		- art [kArt_muscle_STERNOHYOID]);
+	hyoid.dx = -5 * f * art [(int) kArt_muscle::SPHINCTER];
+	hyoid.dy = 20 * f * (art [(int) kArt_muscle::STYLOHYOID]
+		- art [(int) kArt_muscle::STERNOHYOID]);
 
 	/* The larynx moves up and down with the hyoid bone.			*/
 	/* Only the lowest point (Mermelstein's K)				*/
@@ -65,31 +65,31 @@ void Art_Speaker_toVocalTract (Art _art, Speaker speaker,
 	/* The lower pharynx moves up and down with the hyoid bone.			*/
 	/* The lower constrictor muscle pulls the rear pharyngeal wall forwards.	*/
 
-	extX [3] = -34 * f + art [kArt_muscle_SPHINCTER] * 5 * f;	extY [3] = extY [2];
+	extX [3] = -34 * f + art [(int) kArt_muscle::SPHINCTER] * 5 * f;	extY [3] = extY [2];
 
 	/* The upper pharynx is fixed at the height of the velum. */
 	/* The upper constrictor muscle pulls the rear pharyngeal wall forwards. */
 
-	extX [5] = -34 * f + art [kArt_muscle_SPHINCTER] * 5 * f;
+	extX [5] = -34 * f + art [(int) kArt_muscle::SPHINCTER] * 5 * f;
 	extY [5] = speaker -> velum.y;
 
 	/* The height of the middle pharynx is in between the lower and upper pharynx. */
 	/* The middle constrictor muscle pulls the rear pharyngeal wall forwards. */
 
-	extX [4] = -34 * f + art [kArt_muscle_SPHINCTER] * 5 * f;
+	extX [4] = -34 * f + art [(int) kArt_muscle::SPHINCTER] * 5 * f;
 	extY [4] = 0.5 * (extY [3] + extY [5]);
 
 	/* Tongue root. */
 
 	jaw.x = -75 * f, jaw.y = 53 * f;   /* Position of the condyle. */
-	jaw.da = art [kArt_muscle_MASSETER] * 0.15
-		- art [kArt_muscle_MYLOHYOID] * 0.20;
+	jaw.da = art [(int) kArt_muscle::MASSETER] * 0.15
+		- art [(int) kArt_muscle::MYLOHYOID] * 0.20;
 	body.x = jaw.x + 81 * f * cos (-0.60 + jaw.da)
-		- art [kArt_muscle_STYLOGLOSSUS] * 10 * f
-		+ art [kArt_muscle_GENIOGLOSSUS] * 10 * f;
+		- art [(int) kArt_muscle::STYLOGLOSSUS] * 10 * f
+		+ art [(int) kArt_muscle::GENIOGLOSSUS] * 10 * f;
 	body.y = jaw.y + 81 * f * sin (-0.60 + jaw.da)
-		- art [kArt_muscle_HYOGLOSSUS] * 10 * f
-		+ art [kArt_muscle_STYLOGLOSSUS] * 5 * f;
+		- art [(int) kArt_muscle::HYOGLOSSUS] * 10 * f
+		+ art [(int) kArt_muscle::STYLOGLOSSUS] * 5 * f;
 	*bodyX = body.x;
 	*bodyY = body.y;
 	body.r = sqrt ((jaw.x - body.x) * (jaw.x - body.x)
@@ -126,8 +126,8 @@ void Art_Speaker_toVocalTract (Art _art, Speaker speaker,
 
 	/* Tip. */
 
-	tip.a = (art [kArt_muscle_UPPER_TONGUE]
-		- art [kArt_muscle_LOWER_TONGUE]) * 1.0;
+	tip.a = (art [(int) kArt_muscle::UPPER_TONGUE]
+		- art [(int) kArt_muscle::LOWER_TONGUE]) * 1.0;
 	blade.a = teeth.a
 		+ 0.004 * (body.r - speaker -> neutralBodyDistance) + tip.a;
 	intX [8] = intX [7] + speaker -> tip.length * cos (blade.a);
@@ -147,8 +147,8 @@ void Art_Speaker_toVocalTract (Art _art, Speaker speaker,
 
 	/* Lower lip. */
 
-	lowerLip.dx = speaker -> lowerLip.dx + art [kArt_muscle_ORBICULARIS_ORIS] * 0.02 - 5e-3;
-	lowerLip.dy = speaker -> lowerLip.dy + art [kArt_muscle_ORBICULARIS_ORIS] * 0.01;
+	lowerLip.dx = speaker -> lowerLip.dx + art [(int) kArt_muscle::ORBICULARIS_ORIS] * 0.02 - 5e-3;
+	lowerLip.dy = speaker -> lowerLip.dy + art [(int) kArt_muscle::ORBICULARIS_ORIS] * 0.01;
 	intX [12] = teeth.x;
 	intY [12] = teeth.y + lowerLip.dy;
 	intX [13] = teeth.x + lowerLip.dx;
@@ -168,8 +168,8 @@ void Art_Speaker_toVocalTract (Art _art, Speaker speaker,
 
 	/* Upper lip. */
 
-	upperLip.dx = speaker -> upperLip.dx + art [kArt_muscle_ORBICULARIS_ORIS] * 0.02 - 5e-3;
-	upperLip.dy = speaker -> upperLip.dy - art [kArt_muscle_ORBICULARIS_ORIS] * 0.01;
+	upperLip.dx = speaker -> upperLip.dx + art [(int) kArt_muscle::ORBICULARIS_ORIS] * 0.02 - 5e-3;
+	upperLip.dy = speaker -> upperLip.dy - art [(int) kArt_muscle::ORBICULARIS_ORIS] * 0.01;
 	extX [9] = extX [8];
 	extY [9] = extY [8] + upperLip.dy;
 	extX [10] = extX [9] + upperLip.dx;
diff --git a/artsynth/Art_Speaker_Delta.cpp b/artsynth/Art_Speaker_Delta.cpp
index 671e7c6..5bc05b6 100644
--- a/artsynth/Art_Speaker_Delta.cpp
+++ b/artsynth/Art_Speaker_Delta.cpp
@@ -1,6 +1,6 @@
 /* Art_Speaker_Delta.cpp
  *
- * Copyright (C) 1992-2011 Paul Boersma
+ * Copyright (C) 1992-2011,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,22 +29,22 @@ void Art_Speaker_intoDelta (Art art, Speaker speaker, Delta delta)
 	/* Lungs. */
 
 	for (itube = 7; itube <= 18; itube ++)
-		delta -> tube [itube]. Dyeq = 120 * f * (1 + art -> art [kArt_muscle_LUNGS]);
+		delta -> tube [itube]. Dyeq = 120 * f * (1 + art -> art [(int) kArt_muscle::LUNGS]);
 
 	/* Glottis. */
 
 	{
 		Delta_Tube t = delta -> tube + 36;
-		t -> Dyeq = f * (5 - 10 * art -> art [kArt_muscle_INTERARYTENOID]
-		      + 3 * art -> art [kArt_muscle_POSTERIOR_CRICOARYTENOID]
-		      - 3 * art -> art [kArt_muscle_LATERAL_CRICOARYTENOID]);   /* 4.38 */
-		t -> k1 = speaker -> lowerCord.k1 * (1 + art -> art [kArt_muscle_CRICOTHYROID]);
+		t -> Dyeq = f * (5 - 10 * art -> art [(int) kArt_muscle::INTERARYTENOID]
+		      + 3 * art -> art [(int) kArt_muscle::POSTERIOR_CRICOARYTENOID]
+		      - 3 * art -> art [(int) kArt_muscle::LATERAL_CRICOARYTENOID]);   /* 4.38 */
+		t -> k1 = speaker -> lowerCord.k1 * (1 + art -> art [(int) kArt_muscle::CRICOTHYROID]);
 		t -> k3 = t -> k1 * (20 / t -> Dz) * (20 / t -> Dz);
 	}
 	if (speaker -> cord.numberOfMasses >= 2) {
 		Delta_Tube t = delta -> tube + 37;
 		t -> Dyeq = delta -> tube [36]. Dyeq;
-		t -> k1 = speaker -> upperCord.k1 * (1 + art -> art [kArt_muscle_CRICOTHYROID]);
+		t -> k1 = speaker -> upperCord.k1 * (1 + art -> art [(int) kArt_muscle::CRICOTHYROID]);
 		t -> k3 = t -> k1 * (20 / t -> Dz) * (20 / t -> Dz);
 	}
 	if (speaker -> cord.numberOfMasses >= 10) {
@@ -74,7 +74,7 @@ void Art_Speaker_intoDelta (Art art, Speaker speaker, Delta delta)
 
 	/* Nasopharyngeal port. */
 
-	delta -> tube [65]. Dyeq = f * (18 - 25 * art -> art [kArt_muscle_LEVATOR_PALATINI]);   /* 4.40 */
+	delta -> tube [65]. Dyeq = f * (18 - 25 * art -> art [(int) kArt_muscle::LEVATOR_PALATINI]);   /* 4.40 */
 
 	for (itube = 1; itube <= delta -> numberOfTubes; itube ++) {
 		Delta_Tube t = delta -> tube + itube;
diff --git a/artsynth/Artword.cpp b/artsynth/Artword.cpp
index 232b0d3..ea9b3a5 100644
--- a/artsynth/Artword.cpp
+++ b/artsynth/Artword.cpp
@@ -42,13 +42,13 @@ Thing_implement (Artword, Daata, 0);
 autoArtword Artword_create (double totalTime) {
 	autoArtword me = Thing_new (Artword);
 	my totalTime = totalTime;
-	for (int i = 1; i <= kArt_muscle_MAX; i ++)
-		Artword_setDefault (me.get(), i);
+	for (int i = 1; i <= (int) kArt_muscle::MAX; i ++)
+		Artword_setDefault (me.get(), (kArt_muscle) i);
 	return me;
 }
 
-void Artword_setDefault (Artword me, int feature) {
-	ArtwordData f = & my data [feature];
+void Artword_setDefault (Artword me, kArt_muscle muscle) {
+	ArtwordData f = & my data [(int) muscle];
 	NUMvector_free <double> (f -> times, 1);
 	NUMvector_free <double> (f -> targets, 1);
 	f -> times = NUMvector <double> (1, 2);
@@ -61,11 +61,11 @@ void Artword_setDefault (Artword me, int feature) {
 	f -> _iTarget = 1;
 }
 
-void Artword_setTarget (Artword me, int feature, double time, double target) {
+void Artword_setTarget (Artword me, kArt_muscle muscle, double time, double target) {
 	try {
-		Melder_assert (feature >= 1);
-		Melder_assert (feature <= kArt_muscle_MAX);
-		ArtwordData f = & my data [feature];
+		Melder_assert ((int) muscle >= 1);
+		Melder_assert ((int) muscle <= (int) kArt_muscle::MAX);
+		ArtwordData f = & my data [(int) muscle];
 		Melder_assert (f -> numberOfTargets >= 2);
 		int32 insertionPosition = 1;   // should be able to go up to 32768
 		if (time < 0.0) time = 0.0;
@@ -89,8 +89,8 @@ void Artword_setTarget (Artword me, int feature, double time, double target) {
 	}
 }
 
-double Artword_getTarget (Artword me, int feature, double time) {
-	ArtwordData f = & my data [feature];
+double Artword_getTarget (Artword me, kArt_muscle muscle, double time) {
+	ArtwordData f = & my data [(int) muscle];
 	double *times = f -> times, *targets = f -> targets;
 	int16 targetNumber = f -> _iTarget;
 	if (! targetNumber) targetNumber = 1;
@@ -105,8 +105,8 @@ double Artword_getTarget (Artword me, int feature, double time) {
 		(times [targetNumber + 1] - times [targetNumber]);
 }
 
-void Artword_removeTarget (Artword me, int feature, int16 targetNumber) {
-	ArtwordData f = & my data [feature];
+void Artword_removeTarget (Artword me, kArt_muscle muscle, int16 targetNumber) {
+	ArtwordData f = & my data [(int) muscle];
 	Melder_assert (targetNumber >= 1);
 	Melder_assert (targetNumber <= f -> numberOfTargets);
 	if (targetNumber == 1) {
@@ -124,21 +124,21 @@ void Artword_removeTarget (Artword me, int feature, int16 targetNumber) {
 }
 
 void Artword_intoArt (Artword me, Art art, double time) {
-	for (int feature = 1; feature <= kArt_muscle_MAX; feature ++) {
-		art -> art [feature] = Artword_getTarget (me, feature, time);
+	for (int muscle = 1; muscle <= (int) kArt_muscle::MAX; muscle ++) {
+		art -> art [muscle] = Artword_getTarget (me, (kArt_muscle) muscle, time);
 	}
 }
 
-void Artword_draw (Artword me, Graphics g, int feature, bool garnish) {
-	int16 numberOfTargets = my data [feature]. numberOfTargets;
+void Artword_draw (Artword me, Graphics g, kArt_muscle muscle, bool garnish) {
+	int16 numberOfTargets = my data [(int) muscle]. numberOfTargets;
 	if (numberOfTargets > 0) {
 		autoNUMvector <double> x (1, numberOfTargets);
 		autoNUMvector <double> y (1, numberOfTargets);
 		Graphics_setInner (g);
 		Graphics_setWindow (g, 0, my totalTime, -1.0, 1.0);
 		for (int16 i = 1; i <= numberOfTargets; i ++) {
-			x [i] = my data [feature]. times [i];
-			y [i] = my data [feature]. targets [i];
+			x [i] = my data [(int) muscle]. times [i];
+			y [i] = my data [(int) muscle]. targets [i];
 		}
 		Graphics_polyline (g, numberOfTargets, & x [1], & y [1]);         
 		Graphics_unsetInner (g);
@@ -148,7 +148,7 @@ void Artword_draw (Artword me, Graphics g, int feature, bool garnish) {
 		Graphics_drawInnerBox (g);
 		Graphics_marksBottom (g, 2, true, true, false);
 		Graphics_marksLeft (g, 3, true, true, true);
-		Graphics_textTop (g, false, kArt_muscle_getText (feature));
+		Graphics_textTop (g, false, kArt_muscle_getText (muscle));
 		Graphics_textBottom (g, true, U"Time (s)");
 	}
 }
diff --git a/artsynth/Artword.h b/artsynth/Artword.h
index 1380c40..19ebdb5 100644
--- a/artsynth/Artword.h
+++ b/artsynth/Artword.h
@@ -25,42 +25,42 @@
 
 autoArtword Artword_create (double totalTime);
 
-void Artword_setDefault (Artword me, int feature);
+void Artword_setDefault (Artword me, kArt_muscle muscle);
 /*
 	Postconditions:
-		my data [feature]. numberOfTargets == 2;
-		my data [feature]. times [1] == 0.0;
-		my data [feature]. times [2] == self -> totalTime;
-		my data [feature]. targets [1] == 0.0;
-		my data [feature]. targets [2] == 0.0;
+		my data [(int) muscle]. numberOfTargets == 2;
+		my data [(int) muscle]. times [1] == 0.0;
+		my data [(int) muscle]. times [2] == self -> totalTime;
+		my data [(int) muscle]. targets [1] == 0.0;
+		my data [(int) muscle]. targets [2] == 0.0;
 		rest unchanged;	
 */
 
-void Artword_setTarget (Artword me, int feature, double time, double value);
+void Artword_setTarget (Artword me, kArt_muscle muscle, double time, double value);
 
-double Artword_getTarget (Artword me, int feature, double time);
+double Artword_getTarget (Artword me, kArt_muscle muscle, double time);
 
-void Artword_removeTarget (Artword me, int feature, int16 targetNumber);
+void Artword_removeTarget (Artword me, kArt_muscle muscle, int16 targetNumber);
 /*
 	Function:
-		remove one target from the target list of "feature".
+		remove one target from the target list of "muscle".
 		If "iTarget" is the first or the last target in the list,
 		only set the target to zero (begin and end targets remain).
 	Preconditions:
 		self != nullptr;
-		feature in enum Art_MUSCLE;
+		muscle in enum class Art_MUSCLE;
 		iTarget >= 1;
-		iTarget <= self -> data [feature]. numberOfTargets;
+		iTarget <= self -> data [(int) muscle]. numberOfTargets;
 	Postconditions:
 		if (iTarget == 1)
-			self -> data [feature]. targets [1] == 0.0;
-		else if (iTarget == self -> data [feature]. numberOfTargets)
-			self -> data [feature]. targets [iTarget] == 0.0;
+			self -> data [(int) muscle]. targets [1] == 0.0;
+		else if (iTarget == self -> data [(int) muscle]. numberOfTargets)
+			self -> data [(int) muscle]. targets [iTarget] == 0.0;
 		else
-			self -> data [feature]. numberOfTargets == old self -> data [feature]. numberOfTargets - 1;
-			for (i == iTarget..self -> data [feature]. numberOfTargets)
-				self -> data [feature]. times [i] == old self -> data [feature]. times [i + 1];
-				self -> data [feature]. targets [i] == old self -> data [feature]. targets [i + 1];	
+			self -> data [(int) muscle]. numberOfTargets == old self -> data [(int) muscle]. numberOfTargets - 1;
+			for (i == iTarget..self -> data [(int) muscle]. numberOfTargets)
+				self -> data [(int) muscle]. times [i] == old self -> data [(int) muscle]. times [i + 1];
+				self -> data [(int) muscle]. targets [i] == old self -> data [(int) muscle]. targets [i + 1];
 */
 
 void Artword_intoArt (Artword me, Art art, double time);
@@ -72,7 +72,7 @@ void Artword_intoArt (Artword me, Art art, double time);
 		art != nullptr;
 */
 	
-void Artword_draw (Artword me, Graphics graphics, int feature, bool garnish);
+void Artword_draw (Artword me, Graphics graphics, kArt_muscle muscle, bool garnish);
 
 /* End of file Artword.h */
 #endif
diff --git a/artsynth/ArtwordEditor.cpp b/artsynth/ArtwordEditor.cpp
index fcda0f7..721b231 100644
--- a/artsynth/ArtwordEditor.cpp
+++ b/artsynth/ArtwordEditor.cpp
@@ -27,7 +27,7 @@ void structArtwordEditor :: v_destroy () noexcept {
 
 static void updateList (ArtwordEditor me) {
 	Artword artword = (Artword) my data;
-	ArtwordData a = & artword -> data [my feature];
+	ArtwordData a = & artword -> data [(int) my muscle];
 	GuiList_deleteAllItems (my list);
 	for (int16 i = 1; i <= a -> numberOfTargets; i ++) {
 		GuiList_insertItem (my list,
@@ -45,7 +45,7 @@ static void gui_button_cb_removeTarget (ArtwordEditor me, GuiButtonEvent /* even
 		for (long ipos = numberOfSelectedPositions; ipos > 0; ipos --) {
 			long position = selectedPositions [ipos];
 			Melder_assert (position >= 1 && position <= INT16_MAX);
-			Artword_removeTarget (artword, my feature, (int16) position);   // guarded conversion
+			Artword_removeTarget (artword, my muscle, (int16) position);   // guarded conversion
 		}
 	}
 	NUMvector_free (selectedPositions, 1);
@@ -59,11 +59,11 @@ static void gui_button_cb_addTarget (ArtwordEditor me, GuiButtonEvent /* event *
 	double tim = Melder_atof (timeText);
 	char32 *valueText = GuiText_getString (my value);
 	double value = Melder_atof (valueText);
-	ArtwordData a = & artword -> data [my feature];
+	ArtwordData a = & artword -> data [(int) my muscle];
 	int i = 1, oldCount = a -> numberOfTargets;
 	Melder_free (timeText);
 	Melder_free (valueText);
-	Artword_setTarget (artword, my feature, tim, value);
+	Artword_setTarget (artword, my muscle, tim, value);
 
 	/* Optimization instead of "updateList (me)". */
 
@@ -84,9 +84,9 @@ static void gui_button_cb_addTarget (ArtwordEditor me, GuiButtonEvent /* event *
 }
 
 static void gui_radiobutton_cb_toggle (ArtwordEditor me, GuiRadioButtonEvent event) {
-	my feature = event -> position;
-	Melder_assert (my feature > 0);
-	Melder_assert (my feature <= kArt_muscle_MAX);
+	my muscle = (kArt_muscle) event -> position;
+	Melder_assert ((int) my muscle > 0);
+	Melder_assert (my muscle <= kArt_muscle::MAX);
 	updateList (me);
 }
 
@@ -94,7 +94,7 @@ static void gui_drawingarea_cb_expose (ArtwordEditor me, GuiDrawingArea_ExposeEv
 	if (! my graphics) return;
 	Artword artword = (Artword) my data;
 	Graphics_clearWs (my graphics.get());
-	Artword_draw (artword, my graphics.get(), my feature, true);
+	Artword_draw (artword, my graphics.get(), my muscle, true);
 }
 
 static void gui_drawingarea_cb_click (ArtwordEditor me, GuiDrawingArea_ClickEvent event) {
@@ -136,15 +136,15 @@ void structArtwordEditor :: v_createChildren () {
 
 	dy = Machine_getMenuBarHeight ();
 	GuiRadioGroup_begin ();
-	for (int i = 1; i <= kArt_muscle_MAX; i ++) {
+	for (int i = 1; i <= (int) kArt_muscle::MAX; i ++) {
 		button [i] = GuiRadioButton_createShown (our windowForm,
 			480, 0, dy, dy + Gui_RADIOBUTTON_HEIGHT,
-			kArt_muscle_getText (i), gui_radiobutton_cb_toggle, this, 0);
+			kArt_muscle_getText ((kArt_muscle) i), gui_radiobutton_cb_toggle, this, 0);
 		dy += Gui_RADIOBUTTON_HEIGHT + Gui_RADIOBUTTON_SPACING - 2;
 	}
 	GuiRadioGroup_end ();
-	feature = 1;
-	GuiRadioButton_set (button [feature]);
+	muscle = (kArt_muscle) 1;
+	GuiRadioButton_set (button [(int) muscle]);
 }
 
 autoArtwordEditor ArtwordEditor_create (const char32 *title, Artword data) {
diff --git a/artsynth/ArtwordEditor.h b/artsynth/ArtwordEditor.h
index 4617eb1..58bbd3e 100644
--- a/artsynth/ArtwordEditor.h
+++ b/artsynth/ArtwordEditor.h
@@ -2,7 +2,7 @@
 #define _ArtwordEditor_h_
 /* ArtwordEditor.h
  *
- * Copyright (C) 1992-2011,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,11 +23,11 @@
 
 Thing_define (ArtwordEditor, Editor) {
 	autoGraphics graphics;
-	int feature;
+	kArt_muscle muscle;
 	GuiList list;
 	GuiDrawingArea drawingArea;
 	GuiText time, value;
-	GuiRadioButton button [1 + kArt_muscle_MAX];
+	GuiRadioButton button [1 + (int) kArt_muscle::MAX];
 
 	void v_destroy () noexcept
 		override;
diff --git a/artsynth/praat_Artsynth.cpp b/artsynth/praat_Artsynth.cpp
index 554e67a..87f088c 100644
--- a/artsynth/praat_Artsynth.cpp
+++ b/artsynth/praat_Artsynth.cpp
@@ -39,18 +39,18 @@ DO
 }
 
 FORM (WINDOW_Art_viewAndEdit, U"View & Edit Articulation", nullptr) {
-	for (int i = 1; i <= kArt_muscle_MAX; i ++)
-		REAL (kArt_muscle_getText (i), U"0.0")
+	for (int i = 1; i <= (int) kArt_muscle::MAX; i ++)
+		REAL (kArt_muscle_getText ((kArt_muscle) i), U"0.0")
 OK
 	FIND_ONE (Art)
-	for (int i = 1; i <= kArt_muscle_MAX; i ++)
-		SET_REAL (kArt_muscle_getText (i), my art [i]);
+	for (int i = 1; i <= (int) kArt_muscle::MAX; i ++)
+		SET_REAL (kArt_muscle_getText ((kArt_muscle) i), my art [i]);
 DO
 	FIND_ONE (Art)
 		if (theCurrentPraatApplication -> batch)
 			Melder_throw (U"Cannot edit an Art from batch.");
-		for (int i = 1; i <= kArt_muscle_MAX; i ++)
-			my art [i] = GET_REAL (kArt_muscle_getText (i));
+		for (int i = 1; i <= (int) kArt_muscle::MAX; i ++)
+			my art [i] = GET_REAL (kArt_muscle_getText ((kArt_muscle) i));
 	END
 }
 
@@ -67,14 +67,14 @@ DO
 }
 
 FORM (GRAPHICS_Artword_draw, U"Draw one Artword tier", nullptr) {
-	OPTIONMENUVAR (muscle, U"Muscle", kArt_muscle_LUNGS)
-	for (int ienum = 1; ienum <= kArt_muscle_MAX; ienum ++)
-		OPTION (kArt_muscle_getText (ienum))
+	OPTIONMENUVAR (muscle, U"Muscle", (int) kArt_muscle::LUNGS)
+	for (int ienum = 1; ienum <= (int) kArt_muscle::MAX; ienum ++)
+		OPTION (kArt_muscle_getText ((kArt_muscle) ienum))
 	BOOLEANVAR (garnish, U"Garnish", true)
 	OK
 DO
 	GRAPHICS_EACH (Artword)
-		Artword_draw (me, GRAPHICS, muscle, garnish);
+		Artword_draw (me, GRAPHICS, (kArt_muscle) muscle, garnish);
 	GRAPHICS_EACH_END
 }
 
@@ -89,13 +89,13 @@ DIRECT (WINDOW_Artword_viewAndEdit) {
 
 FORM (REAL_Artword_getTarget, U"Get one Artword target", nullptr) {
 	REALVAR (time, U"Time (seconds)", U"0.0")
-	OPTIONMENUVAR (muscle, U"Muscle", kArt_muscle_LUNGS)
-	for (int ienum = 1; ienum <= kArt_muscle_MAX; ienum ++)
-		OPTION (kArt_muscle_getText (ienum))
+	OPTIONMENUVAR (muscle, U"Muscle", (int) kArt_muscle::LUNGS)
+	for (int ienum = 1; ienum <= (int) kArt_muscle::MAX; ienum ++)
+		OPTION (kArt_muscle_getText ((kArt_muscle) ienum))
 	OK
 DO
 	NUMBER_ONE (Artword)
-		double result = Artword_getTarget (me, muscle, time);
+		double result = Artword_getTarget (me, (kArt_muscle) muscle, time);
 	NUMBER_ONE_END (U"")
 }
 
@@ -106,14 +106,14 @@ DIRECT (HELP_Artword_help) {
 FORM (MODIFY_Artword_setTarget, U"Set one Artword target", nullptr) {
 	REALVAR (time, U"Time (seconds)", U"0.0")
 	REALVAR (targetValue, U"Target value (0-1)", U"0.0")
-	OPTIONMENUVAR (muscle, U"Muscle", kArt_muscle_LUNGS)
-	for (int ienum = 1; ienum <= kArt_muscle_MAX; ienum ++)
-		OPTION (kArt_muscle_getText (ienum))
+	OPTIONMENUVAR (muscle, U"Muscle", (int) kArt_muscle::LUNGS)
+	for (int ienum = 1; ienum <= (int) kArt_muscle::MAX; ienum ++)
+		OPTION (kArt_muscle_getText ((kArt_muscle) ienum))
 	OK
 DO
 	if (time < 0.0) Melder_throw (U"The specified time should not be less than 0.");
 	MODIFY_EACH (Artword)
-		Artword_setTarget (me, muscle, time, targetValue);
+		Artword_setTarget (me, (kArt_muscle) muscle, time, targetValue);
 	MODIFY_EACH_END
 }
 
diff --git a/dwsys/FileInMemory.cpp b/dwsys/FileInMemory.cpp
index de15189..4d32ae0 100644
--- a/dwsys/FileInMemory.cpp
+++ b/dwsys/FileInMemory.cpp
@@ -52,7 +52,7 @@ autoFileInMemory FileInMemory_create (MelderFile file) {
 		if (! MelderFile_readable (file)) {
 			Melder_throw (U"File not readable.");
 		}
-		long length = MelderFile_length (file);
+		integer length = MelderFile_length (file);
 		if (length <= 0) {
 			Melder_throw (U"File is empty.");
 		}
diff --git a/dwsys/NUM2.cpp b/dwsys/NUM2.cpp
index 8fe53e6..d688a25 100644
--- a/dwsys/NUM2.cpp
+++ b/dwsys/NUM2.cpp
@@ -912,15 +912,15 @@ void NUMpseudoInverse (double **y, long nr, long nc, double **yinv, double toler
 	autoSVD me = SVD_create_d (y, nr, nc);
 
 	(void) SVD_zeroSmallSingularValues (me.get(), tolerance);
-	for (long i = 1; i <= nc; i++) {
-		for (long j = 1; j <= nr; j++) {
-			double s = 0.0;
-			for (long k = 1; k <= nc; k++) {
+	for (long i = 1; i <= nc; i ++) {
+		for (long j = 1; j <= nr; j ++) {
+			real80 s = 0.0;
+			for (long k = 1; k <= nc; k ++) {
 				if (my d[k] != 0.0) {
-					s += my v[i][k] * my u[j][k] / my d[k];
+					s += my v [i] [k] * my u [j] [k] / my d [k];
 				}
 			}
-			yinv[i][j] = s;
+			yinv [i] [j] = (double) s;
 		}
 	}
 }
@@ -2665,7 +2665,7 @@ void NUMlineFit_theil (double *x, double *y, long numberOfPoints, double *p_m, d
 			m = intercept = undefined;
 		} else if (numberOfPoints == 1) {
 			intercept = y[1];
-			m = 0;
+			m = 0.0;
 		} else if (numberOfPoints == 2) {
 			m = (y[2] - y[1]) / (x[2] - x[1]);
 			intercept = y[1] - m * x[1];
@@ -3148,7 +3148,8 @@ void NUMlngamma_complex (double zr, double zi, double *lnr, double *arg) {
 	double ln_re = undefined, ln_arg = undefined;
 	gsl_sf_result gsl_lnr, gsl_arg;
 	if (gsl_sf_lngamma_complex_e (zr, zi, & gsl_lnr, & gsl_arg)) {
-		ln_re = gsl_lnr.val; ln_arg = gsl_arg.val;
+		ln_re = gsl_lnr.val;
+		ln_arg = gsl_arg.val;
 	}
 	if (lnr) {
 		*lnr = ln_re;
@@ -3158,15 +3159,15 @@ void NUMlngamma_complex (double zr, double zi, double *lnr, double *arg) {
 	}
 }
 
-bool NUMdmatrix_hasFiniteElements (double **m, long row1, long row2, long col1, long col2) {
+bool NUMdmatrix_containsUndefinedElements (double **m, long row1, long row2, long col1, long col2) {
 	for (long i = row1; i <= row2; i ++) {
 		for (long j = col1; j <= col2; j ++) {
-			if (! isfinite (m [i] [j])) {
-				return false;
+			if (isundef (m [i] [j])) {
+				return true;
 			}
 		}
 	}
-	return true;
+	return false;
 }
 
 void NUMdmatrix_diagnoseCells (double **m, long rb, long re, long cb, long ce, long maximumNumberOfPositionsToReport) {
diff --git a/dwsys/NUM2.h b/dwsys/NUM2.h
index 1d5f080..888f2ba 100644
--- a/dwsys/NUM2.h
+++ b/dwsys/NUM2.h
@@ -129,8 +129,8 @@ void NUMdmatrix_printMatlabForm (double **m, long nr, long nc, const char32 *nam
 	7, 8, 9];
 */
 
-bool NUMdmatrix_hasFiniteElements (double **m, long row1, long row2, long col1, long col2);
-/* true if all the elements are finite i.e. not plus or minus infinity, and not NaN */
+bool NUMdmatrix_containsUndefinedElements (double **m, long row1, long row2, long col1, long col2);
+/* true if at least one of the elements is undefined (i.e. infinite or NaN) */
 
 void NUMdmatrix_diagnoseCells (double **m, long rb, long re, long cb, long ce, long maximumNumberOfPositionsToReport);
 /* which cells are not finite? */
@@ -286,7 +286,7 @@ double NUMmultivariateKurtosis (double **x, long nrows, long ncols, int method);
 	method = 1 : Schott (2001), J. of Statistical planning and Inference 94, 25-36.
 */
 
-void NUMmad (double *x, long n, double *location, int wantlocation, double *mad, double *work);
+void NUMmad (double *x, integer n, double *location, bool wantlocation, double *mad, double *work);
 /*
 	Computes the median absolute deviation, i.e., the median of the
 	absolute deviations from the median, and adjust by a factor for
@@ -299,8 +299,8 @@ void NUMmad (double *x, long n, double *location, int wantlocation, double *mad,
 	If work == NULL, the routine allocates (and destroys) its own memory.
  */
 
-void NUMstatistics_huber (double *x, long n, double *location, int wantlocation,
-	double *scale, int wantscale, double k, double tol, double *work);
+void NUMstatistics_huber (double *x, integer n, double *location, bool wantlocation,
+	double *scale, bool wantscale, double k, double tol, double *work);
 /*
 	Finds the Huber M-estimator for location with scale specified,
 	scale with location specified, or both if neither is specified.
diff --git a/dwsys/NUMcomplex.cpp b/dwsys/NUMcomplex.cpp
index 948e50f..37ded69 100644
--- a/dwsys/NUMcomplex.cpp
+++ b/dwsys/NUMcomplex.cpp
@@ -20,15 +20,60 @@
 #include <complex>
 #include "NUMcomplex.h"
 
-// The following code was translated from fortran into c++ by David weenink.
-// The fortran code is from the article of Eric Kostlan and Dmitry Gokhman, A program for calculating the incomplete 
-//		gamma function. Technical report, Dept. of Mathematics, Univ. of California, Berkeley, 1987.
+/*
+	The code to calculate the complex incomplete gamma function was translated from fortran code into c++ by David Weenink.
+	The fortran code is from the following article: 
+	Eric Kostlan & Dmitry Gokhman, A program for calculating the incomplete gamma function. 
+	Technical report, Dept. of Mathematics, Univ. of California, Berkeley, 1987.
+	
+	Their algorithm calcutes the complex incomplete gamma function Γ(α,x) by using the following formula:
+	(1)	Γ(α,x)= exp(-x)x^α / h(α,x), 
+	where h(α,x) is a continued fraction:
+	(2)	h(α,x)=x+(1-α)
+		         -----
+		         1+1
+		           -------
+				   x+(2-α)
+				     ------
+				     1+2
+				       ------
+				       x+(3-α)
+				         -------
+				         1+...
+	Efficient calculation of h(α,x) is possible because consecutive terms of a continued fraction can be computed by means of
+	a two term linear recursion relation.
+	If for the sequences {p[k]} and {q[k]}, k=0,1,2,... the following difference equations hold
+	(3)	y[k+2]=x*y[k+1]+((k+2)/2)* y[k] for k even
+		      =y[k+1]((k+3)/2-α)*y[k]  for k is odd
+	and initial conditions p[0]=x, p[1]=x+1-α, q[0]=1, q[1]=1, then p[k]/q[k] is the k-th term
+	in the continued fraction for h(α,x).
+	
+	The value of h(α+1,x) can be reduced to h(α,x) 
+	(4) x/h(α+1,x) = α/h(α,x)+1 by using the following relation between gamma functions:
+		Γ(α+1,x)=αΓ(α,x)+x^α exp(-x).
+	If x is near the negative real axis then x can be moved to some new value y:
+	(5)	Γ(α,y)-Γ(α,x)=sum(n=0,Infinity,(-1)^n(x^(α+n)-y^(α+n))/(n!(α+n)),
+	where in the code below a valye of y=1 is used to facilitate the computation.
+	
+	Numerical considerations:
+	1. In formula (5) Kostlan & Gokhman use y=1 to facilitate the computation
+	2. If (α,x) is near (−n ,0), where n is a non-negative integer, but x is not equal to zero, 
+	the n-th term in (5) can turn out to be 0.0/0.0. They replace this term with -log(x)-(α+n)log(x)²/2. 
+	Essentially they are helping the computer to calculate (1-x^(α+n))/(α+n).
+	
+	Problems:
+	Kostlan & Gokhman notice three intrinsic problems that any algorithm for calculating Γ(α,x) must face:
+	1. For x=0 and α a nonpositive integer Γ(α,x)=∞.
+	2. If α is not an integer Γ(α,x) is a multi-valued function with a branch point at x=0
+	3. As we approach ∞ along certain directions computation of Γ(α,x) is limited  because of the inability to compute x^α accurately.
+
+*/
 
 static double norm1 (std::complex<double> *x) {
 	return fabs (x -> real()) + fabs (imag(*x));
 }
 
-static void term (std::complex<double> *alpha, std::complex<double> *x, long i, std::complex<double> *p, std::complex<double> *q) {
+static void xShiftTerm (std::complex<double> *alpha, std::complex<double> *x, long i, std::complex<double> *p, std::complex<double> *q) {
 // Calculate p*q = (-1)^i (1-x^(alpha+i))/(alpha+i)i! 
 	std::complex<double> zero = 0.0;
 	double tol = 3e-7, xlim = 39.0, di = i;
@@ -62,7 +107,7 @@ static void term (std::complex<double> *alpha, std::complex<double> *x, long i,
 	}
 }
 
-static void cdhs (std::complex<double> *alpha, std::complex<double> *x, std::complex<double> *result) {
+static void continuedFractionExpansion (std::complex<double> *alpha, std::complex<double> *x, std::complex<double> *result) {
 	std::complex<double> zero (0.0,0.0);
 	std::complex<double> q0 = 1.0, q1 = 1.0, p0 = *x, p1 = *x + 1.0 - *alpha, r0;
 	double tol1 = 1e10, tol2 = 1e-10, error = 1e-18;
@@ -87,13 +132,17 @@ static void cdhs (std::complex<double> *alpha, std::complex<double> *x, std::com
 			q0 = *x * q1 + di * q0;
 			p1 = p0 + (di + 1.0 - *alpha) * p1; // y[k+2] = y[k+1] + ((k+3)/2 - alpha) * y[k] with k odd
 			q1 = q0 + (di + 1.0 - *alpha) * q1;
+		} else {
+			// We should not come here at all!
+			*result = 0.5 * (r0 + *result);
+			return;
 		}
 	}
 	// We should not come here at all!
 	*result = 0.5 * (r0 + *result);
 }
 
-static void cdh (std::complex<double> *alpha, std::complex<double> *x, std::complex<double> *result) {
+static void shiftAlphaByOne (std::complex<double> *alpha, std::complex<double> *x, std::complex<double> *result) {
 	std::complex<double> one (1.0, 0.0);
 	long n = (long) (*alpha - *x).real();
 	if (n > 0) {
@@ -101,16 +150,16 @@ static void cdh (std::complex<double> *alpha, std::complex<double> *x, std::comp
 		std::complex<double> alpha1 = *alpha - cn;
 		std::complex<double> term = one / *x;
 		std::complex<double> sum = term;
-		for (long i = 1; i <= n; i++) {
+		for (long i = 1; i <= n; i ++) {
 			cn = n - i + 1;
 			term *= (alpha1 + cn) / *x;
 			sum += term;
 		}
-		cdhs (&alpha1, x, result);
+		continuedFractionExpansion (& alpha1, x, result);
 		sum += term * alpha1 / *result;
 		*result = one / sum;
 	} else {
-		cdhs (alpha, x, result);
+		continuedFractionExpansion (alpha, x, result);
 	}
 }
 
@@ -121,15 +170,15 @@ void NUMincompleteGammaFunction (double alpha_re, double alpha_im, double x_re,
 	long ibuf = 34;
 	std::complex<double> re = 0.36787944117144232, one = 1.0, p, q, r;
 	if (norm1 (& x) < xlim || x.real() < 0.0 && fabs (imag (x)) < xlim) {
-		cdh (& alpha, & one, & r);
+		shiftAlphaByOne (& alpha, & one, & r);
 		result = re / r;
 		long ilim = (long) (x / re).real();
 		for (long i = 0; i <= ibuf - ilim; i++) {
-			term (& alpha, & x, i, & p, & q);
+			xShiftTerm (& alpha, & x, i, & p, & q);
 			result += p * q;
 		}
 	} else {
-		cdh (& alpha, & x, & r);
+		shiftAlphaByOne (& alpha, & x, & r);
 		result = exp (-x + alpha * log (x)) / r;
 	}
 	if (result_re) {
diff --git a/dwsys/NUMhuber.cpp b/dwsys/NUMhuber.cpp
index aef5385..9d6f9db 100644
--- a/dwsys/NUMhuber.cpp
+++ b/dwsys/NUMhuber.cpp
@@ -23,7 +23,7 @@
 
 #include "NUM2.h"
 
-void NUMmad (double *x, long n, double *location, int wantlocation, double *mad, double *work) {
+void NUMmad (double *x, integer n, double *location, bool wantlocation, double *mad, double *work) {
 	double *tmp = work;
 
 	*mad = undefined;
@@ -40,7 +40,7 @@ void NUMmad (double *x, long n, double *location, int wantlocation, double *mad,
 		tmp = atmp.peek();
 	}
 
-	for (long i = 1; i <= n; i++) {
+	for (integer i = 1; i <= n; i++) {
 		tmp[i] = x[i];
 	}
 
@@ -49,7 +49,7 @@ void NUMmad (double *x, long n, double *location, int wantlocation, double *mad,
 		*location = NUMquantile (n, tmp, 0.5);
 	}
 
-	for (long i = 1; i <= n; i++) {
+	for (integer i = 1; i <= n; i++) {
 		tmp[i] = fabs (tmp[i] - *location);
 	}
 
@@ -61,12 +61,12 @@ static double NUMgauss (double x) {
 	return NUM1_sqrt2pi * exp (- 0.5 * x * x);
 }
 
-void NUMstatistics_huber (double *x, long n, double *location, int wantlocation,
-                          double *scale, int wantscale, double k, double tol, double *work) {
+void NUMstatistics_huber (double *x, integer n, double *location, bool wantlocation,
+                          double *scale, bool wantscale, double k, double tol, double *work) {
 	double *tmp = work;
 	double theta = 2.0 * NUMgaussP (k) - 1.0;
 	double beta = theta + k * k * (1.0 - theta) - 2.0 * k * NUMgauss (k);
-	long n1 = n;
+	integer n1 = n;
 
 	autoNUMvector<double> atmp;
 	if (work == 0)  {
@@ -96,7 +96,7 @@ void NUMstatistics_huber (double *x, long n, double *location, int wantlocation,
 		double low  = mu0 - k * s0;
 		double high = mu0 + k * s0;
 
-		for (long i = 1; i <= n; i++) {
+		for (integer i = 1; i <= n; i++) {
 			if (x[i] < low) {
 				tmp[i] = low;
 			} else if (x[i] > high) {
@@ -107,14 +107,14 @@ void NUMstatistics_huber (double *x, long n, double *location, int wantlocation,
 		}
 		if (wantlocation) {
 			mu1 = 0.0;
-			for (long i = 1; i <= n; i++) {
+			for (integer i = 1; i <= n; i++) {
 				mu1 += tmp[i];
 			}
 			mu1 /= n;
 		}
 		if (wantscale) {
 			s1 = 0.0;
-			for (long i = 1; i <= n; i++) {
+			for (integer i = 1; i <= n; i++) {
 				double dx = tmp[i] - mu1;
 				s1 += dx * dx;
 			}
diff --git a/dwsys/NUMstring.cpp b/dwsys/NUMstring.cpp
index a2353ee..2245895 100644
--- a/dwsys/NUMstring.cpp
+++ b/dwsys/NUMstring.cpp
@@ -492,7 +492,7 @@ static void NUMlvector_getUniqueNumbers (long *numbers, long *p_numberOfElements
 	
 	numbers [1] = sorted [1];
 	long numberOfUniques = 1;
-	for (long i = 2; i = *p_numberOfElements; i++) {
+	for (long i = 2; i <= *p_numberOfElements; i++) {
 		if (sorted[i] != sorted[i - 1]) {
 			numbers [++numberOfUniques] = sorted[i];
 		} else {
diff --git a/dwtest/speechsynthesizer_test.praat b/dwtest/speechsynthesizer_test.praat
index 3a0d2c5..af8bc4d 100644
--- a/dwtest/speechsynthesizer_test.praat
+++ b/dwtest/speechsynthesizer_test.praat
@@ -17,13 +17,13 @@ for i to numberOfTries
 endfor
 selectObject: table
 Append column: "diff"
-Formula (column range): "diff", "diff", "self[row,""bytes""]-self[row-1,""bytes""]"
+Formula (column range): "diff", "diff", ~ self [row, "bytes"] - self [row-1, "bytes"]
 result = Extract rows where: "row > 1"
 removeObject: table
 selectObject: result
 minimum = Get minimum: "diff"
 Append column: "rdiff"
-Formula (column range): "rdiff", "rdiff",  "self[""diff""] - minimum"
+Formula (column range): "rdiff", "rdiff", ~ self ["diff"] - minimum
 
 Erase all
 @asSpectrum: result, 3
@@ -66,7 +66,7 @@ procedure asSpectrum: .table, .column
 	.sound = Extract one channel: .column
 	Override sampling frequency: 1000
 	.spectrum = To Spectrum: "no"
-	Formula: "if col > 1 then self else 0 fi"
+	Formula: ~ if col > 1 then self else 0 fi
 	removeObject: .tor, .mat, .matt, .sound1
 	selectObject: .spectrum	
 endproc
diff --git a/dwtest/test_ActivationList.praat b/dwtest/test_ActivationList.praat
index 6d16a0c..23891f2 100644
--- a/dwtest/test_ActivationList.praat
+++ b/dwtest/test_ActivationList.praat
@@ -16,7 +16,7 @@ procedure test_with_old_type
 		for ncols to 10
 			tab = Create simple Matrix: "act", nrows, ncols, "0"
 			act = To Activation
-			Formula:  "randomUniform (0, 0.99)"
+			Formula: ~ randomUniform (0, 0.99)
 			mat = To Matrix
 			for irow to nrows
 				for icol to ncols
diff --git a/dwtest/test_Eigen.praat b/dwtest/test_Eigen.praat
index 5187df6..e695d3c 100644
--- a/dwtest/test_Eigen.praat
+++ b/dwtest/test_Eigen.praat
@@ -9,7 +9,7 @@ procedure testInterface
 	for .i to 5
 		.numberOfColumns = randomInteger (3, 12)
 		.tableofreal = Create TableOfReal: "t", 100, .numberOfColumns
-		Formula: "randomGauss (0, 1)"
+		Formula: ~ randomGauss (0, 1)
 		.pca = To PCA
 		.eigen = Extract Eigen
 		.numberOfEigenvalues = Get number of eigenvalues
diff --git a/dwtest/test_MDS.praat b/dwtest/test_MDS.praat
index 23e452f..6468de3 100644
--- a/dwtest/test_MDS.praat
+++ b/dwtest/test_MDS.praat
@@ -169,7 +169,7 @@ procedure testDissimilarityInterface
 	# Create a random configuration
 	.command$ = .mdsCommand$ [1] + .numberOfDimensions$ [1] + .extraParameters$ [1] + .minimizationParameters$
 	.randomConfiguration = '.command$'
-	Formula: "randomUniform (-1,1)"
+	Formula: ~ randomUniform (-1, 1)
 
 	for .itype to 6
 		selectObject: .dissimilarity
diff --git a/dwtest/test_PatternList.praat b/dwtest/test_PatternList.praat
index bf6d246..9552c18 100644
--- a/dwtest/test_PatternList.praat
+++ b/dwtest/test_PatternList.praat
@@ -16,7 +16,7 @@ procedure test_with_old_type
 		for ncols to 10
 			tab = Create simple Matrix: "act", nrows, ncols, "0"
 			act = To Pattern: 1
-			Formula:  "randomUniform (0, 0.99)"
+			Formula: ~ randomUniform (0, 0.99)
 			mat = To Matrix
 			for irow to nrows
 				for icol to ncols
diff --git a/dwtest/test_Polynomial.praat b/dwtest/test_Polynomial.praat
index 994e104..1551e7d 100644
--- a/dwtest/test_Polynomial.praat
+++ b/dwtest/test_Polynomial.praat
@@ -9,9 +9,9 @@ printline test_Polynomial
 printline test_Polynomial OK
 
 procedure test_roots
-	# random polynomials can behave very wildly. Therefor we are not 
-	# too strictly in checking the differences between generated and 
-	#measured roots
+	# random polynomials can behave very wildly. Therefore we cannot
+	# be too strict in checking the differences between generated and 
+	# measured roots
 	.eps1 = 1e-6
 	.eps2 = 1e-6
 	printline ...Roots
@@ -20,7 +20,7 @@ procedure test_roots
 		.rootsTable = Create TableOfReal: "r", .numberOfRoots, 1
 		.xmin = randomUniform (-2, 0)
 		.xmax = randomUniform (0, 2)
-		Formula: "randomUniform (.xmin, .xmax)"
+		Formula: ~ randomUniform (.xmin, .xmax)
 		Sort by column: 1, 0
 		.roots$ = ""
 		for .j to .numberOfRoots
diff --git a/dwtools/CCA.cpp b/dwtools/CCA.cpp
index f930a4e..ce6a50e 100644
--- a/dwtools/CCA.cpp
+++ b/dwtools/CCA.cpp
@@ -108,8 +108,8 @@ autoCCA TableOfReal_to_CCA (TableOfReal me, long ny) {
 			Melder_throw (U"The number of observations must be larger then ", ny, U".");
 		}
 			
-		if (! NUMdmatrix_hasFiniteElements (my data, 1, my numberOfRows, 1, my numberOfColumns)) {
-			Melder_throw (U"At least one of the table's elements is not finite or undefined.");;
+		if (NUMdmatrix_containsUndefinedElements (my data, 1, my numberOfRows, 1, my numberOfColumns)) {
+			Melder_throw (U"At least one of the table's elements is undefined.");
 		}
 		// Use svd as (temporary) storage, and copy data
 
diff --git a/dwtools/ComplexSpectrogram.cpp b/dwtools/ComplexSpectrogram.cpp
index c60448b..23fb410 100644
--- a/dwtools/ComplexSpectrogram.cpp
+++ b/dwtools/ComplexSpectrogram.cpp
@@ -68,8 +68,8 @@ autoComplexSpectrogram Sound_to_ComplexSpectrogram (Sound me, double windowLengt
 			Melder_throw (U"Your analysis window is too short: less than two samples.");
 		}
 		
-		long numberOfFrames;
-		Sampled_shortTermAnalysis (me, windowLength, timeStep, &numberOfFrames, &t1);
+		integer numberOfFrames;
+		Sampled_shortTermAnalysis (me, windowLength, timeStep, & numberOfFrames, & t1);
 
 		// Compute sampling of the spectrum
 
diff --git a/dwtools/DTW.cpp b/dwtools/DTW.cpp
index 984dde7..c056e97 100644
--- a/dwtools/DTW.cpp
+++ b/dwtools/DTW.cpp
@@ -1051,13 +1051,13 @@ autoDTW Pitches_to_DTW_sgc (Pitch me, Pitch thee, double vuv_costs, double time_
 		*/
 		autoDTW him = DTW_create (my xmin, my xmax, my nx, my dx, my x1, thy xmin, thy xmax, thy nx, thy dx, thy x1);
 		autoNUMvector<double> pitchx (1, thy nx);
-		int unit = kPitch_unit_SEMITONES_100;
+		kPitch_unit unit = kPitch_unit::SEMITONES_100;
 		for (long j = 1; j <= thy nx; j++) {
-			pitchx [j] = Sampled_getValueAtSample (thee, j, Pitch_LEVEL_FREQUENCY, unit);
+			pitchx [j] = Sampled_getValueAtSample (thee, j, Pitch_LEVEL_FREQUENCY, (int) unit);
 		}
 
 		for (long i = 1; i <= my nx; i++) {
-			double pitchy = Sampled_getValueAtSample (me, i, Pitch_LEVEL_FREQUENCY, unit);
+			double pitchy = Sampled_getValueAtSample (me, i, Pitch_LEVEL_FREQUENCY, (int) unit);
 			double t1 = my x1 + (i - 1) * my dx;
 			for (long j = 1; j <= thy nx; j++) {
 				double t2 = thy x1 + (j - 1) * thy dx;
@@ -1093,13 +1093,13 @@ autoDTW Pitches_to_DTW (Pitch me, Pitch thee, double vuv_costs, double time_weig
 
 		autoDTW him = DTW_create (my xmin, my xmax, my nx, my dx, my x1, thy xmin, thy xmax, thy nx, thy dx, thy x1);
 		autoNUMvector<double> pitchx (1, thy nx);
-		int unit = kPitch_unit_SEMITONES_100;
+		kPitch_unit unit = kPitch_unit::SEMITONES_100;
 		for (long j = 1; j <= thy nx; j ++) {
-			pitchx[j] = Sampled_getValueAtSample (thee, j, Pitch_LEVEL_FREQUENCY, unit);
+			pitchx[j] = Sampled_getValueAtSample (thee, j, Pitch_LEVEL_FREQUENCY, (int) unit);
 		}
 
 		for (long i = 1; i <= my nx; i ++) {
-			double pitchy = Sampled_getValueAtSample (me, i, Pitch_LEVEL_FREQUENCY, unit);
+			double pitchy = Sampled_getValueAtSample (me, i, Pitch_LEVEL_FREQUENCY, (int) unit);
 			double t1 = my x1 + (i - 1) * my dx;
 			for (long j = 1; j <= thy nx; j++) {
 				double t2 = thy x1 + (j - 1) * thy dx;
@@ -1164,9 +1164,10 @@ autoMatrix DTW_to_Matrix_cumulativeDistances (DTW me, double sakoeChibaBand, int
     }
 }
 
-
 static void DTW_relaxConstraints (DTW me, double band, int slope, double *relaxedBand, int *relaxedSlope) {
+	(void) slope;
 	double dtw_slope = (my ymax - my ymin - band) / (my xmax - my xmin - band);
+	dtw_slope = dtw_slope+1.0; // fake instruction t avoid compiler warning
 	*relaxedBand = 0.0;
 	*relaxedSlope = 1;
 }
diff --git a/dwtools/DataModeler.cpp b/dwtools/DataModeler.cpp
index 084e7a3..cc36ef9 100644
--- a/dwtools/DataModeler.cpp
+++ b/dwtools/DataModeler.cpp
@@ -2116,7 +2116,7 @@ autoFormant Sound_to_Formant_interval (Sound me, double startTime, double endTim
 		// extract part +- windowLength because of Gaussian windowing in the formant analysis
 		// +timeStep/2 to have the analysis points maximally spread in the new domain.
 		
-		autoSound part = Sound_extractPart (me, startTime - windowLength + timeStep / 2.0, endTime + windowLength + timeStep / 2.0, kSound_windowShape_RECTANGULAR, 1, 1);
+		autoSound part = Sound_extractPart (me, startTime - windowLength + timeStep / 2.0, endTime + windowLength + timeStep / 2.0, kSound_windowShape::RECTANGULAR, 1, 1);
 
 		// Resample to 2*maxFreq to reduce resampling load in Sound_to_Formant
 		
@@ -2170,7 +2170,7 @@ autoFormant Sound_to_Formant_interval_robust (Sound me, double startTime, double
 		// extract part +- windowLength because of Gaussian windowing in the formant analysis
 		// +timeStep/2 to have the analysis points maximally spread in the new domain.
 		
-		autoSound part = Sound_extractPart (me, startTime - windowLength + timeStep / 2, endTime + windowLength + timeStep / 2, kSound_windowShape_RECTANGULAR, 1, 1);
+		autoSound part = Sound_extractPart (me, startTime - windowLength + timeStep / 2, endTime + windowLength + timeStep / 2, kSound_windowShape::RECTANGULAR, 1, 1);
 
 		// Resample to 2*maxFreq to reduce resampling load in Sound_to_Formant
 		
@@ -2224,7 +2224,8 @@ autoOptimalCeilingTier Sound_to_OptimalCeilingTier (Sound me, double windowLengt
 			autoFormant formant = Sound_to_Formant_burg (me, timeStep, 5, ceiling, windowLength, preemphasisFrequency);
 			formants. addItem_move (formant.move());
 		}
-		long numberOfFrames; double firstTime, modelingTimeStep = timeStep;
+		integer numberOfFrames;
+		double firstTime, modelingTimeStep = timeStep;
 		autoOptimalCeilingTier octier = OptimalCeilingTier_create (my xmin, my xmax);
 		Sampled_shortTermAnalysis (me, smoothingWindow, modelingTimeStep, & numberOfFrames, & firstTime);
 		for (long iframe = 1; iframe <= numberOfFrames; iframe++) {
diff --git a/dwtools/Discriminant.cpp b/dwtools/Discriminant.cpp
index c964638..afa295d 100644
--- a/dwtools/Discriminant.cpp
+++ b/dwtools/Discriminant.cpp
@@ -413,8 +413,8 @@ autoDiscriminant TableOfReal_to_Discriminant (TableOfReal me) {
 		autoDiscriminant thee = Thing_new (Discriminant);
 		long dimension = my numberOfColumns;
 
-		if (! NUMdmatrix_hasFiniteElements(my data, 1, my numberOfRows, 1, my numberOfColumns)) {
-			Melder_throw (U"At least one of the table's elements is not finite or undefined.");
+		if (NUMdmatrix_containsUndefinedElements (my data, 1, my numberOfRows, 1, my numberOfColumns)) {
+			Melder_throw (U"At least one of the table's elements is undefined.");
 		}
 
 		if (! TableOfReal_hasRowLabels (me)) {
diff --git a/dwtools/FilterBank.cpp b/dwtools/FilterBank.cpp
index ee6c63b..20cc551 100644
--- a/dwtools/FilterBank.cpp
+++ b/dwtools/FilterBank.cpp
@@ -953,7 +953,7 @@ autoBarkFilter Sound_to_BarkFilter (Sound me, double analysisWidth, double dt, d
 		double windowDuration = 2 * analysisWidth; /* gaussian window */
 		double zmax = NUMhertzToBark2 (nyquist);
 		double fmin_bark = 0;
-		long nt, frameErrorCount = 0;
+		integer nt, frameErrorCount = 0;
 
 		// Check defaults.
 
@@ -1044,7 +1044,7 @@ autoMelFilter Sound_to_MelFilter (Sound me, double analysisWidth, double dt, dou
 		double windowDuration = 2 * analysisWidth; /* gaussian window */
 		double fmin_mel = 0;
 		double fbottom = HZTOMEL (100.0), fceiling = HZTOMEL (nyquist);
-		long nt, frameErrorCount = 0;
+		integer nt, frameErrorCount = 0;
 
 		// Check defaults.
 
@@ -1066,7 +1066,7 @@ autoMelFilter Sound_to_MelFilter (Sound me, double analysisWidth, double dt, dou
 		long nf = lround ((fmax_mel - f1_mel) / df_mel);
 		fmax_mel = f1_mel + nf * df_mel;
 
-		Sampled_shortTermAnalysis (me, windowDuration, dt, &nt, &t1);
+		Sampled_shortTermAnalysis (me, windowDuration, dt, & nt, & t1);
 		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
 		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
 		autoMelFilter thee = MelFilter_create (my xmin, my xmax, nt, dt, t1, fmin_mel, fmax_mel, nf, df_mel, f1_mel);
@@ -1154,14 +1154,14 @@ autoFormantFilter Sound_to_FormantFilter (Sound me, double analysisWidth, double
 
 autoFormantFilter Sound_and_Pitch_to_FormantFilter (Sound me, Pitch thee, double analysisWidth, double dt, double f1_hz, double fmax_hz, double df_hz, double relative_bw) {
 	try {
-		double t1, windowDuration = 2 * analysisWidth; /* gaussian window */
+		double t1, windowDuration = 2 * analysisWidth;   // Gaussian window
 		double nyquist = 0.5 / my dx, samplingFrequency = 2 * nyquist, fmin_hz = 0;
-		long nt, f0_undefined = 0;
+		integer nt, f0_undefined = 0;
 
 		if (my xmin > thy xmin || my xmax > thy xmax) Melder_throw
 			(U"The domain of the Sound is not included in the domain of the Pitch.");
 
-		double f0_median = Pitch_getQuantile (thee, thy xmin, thy xmax, 0.5, kPitch_unit_HERTZ);
+		double f0_median = Pitch_getQuantile (thee, thy xmin, thy xmax, 0.5, kPitch_unit::HERTZ);
 
 		if (isundef (f0_median) || f0_median == 0.0) {
 			f0_median = 100;
@@ -1184,18 +1184,19 @@ autoFormantFilter Sound_and_Pitch_to_FormantFilter (Sound me, Pitch thee, double
 		fmax_hz = MIN (fmax_hz, nyquist);
 		long nf = lround ( (fmax_hz - f1_hz) / df_hz);
 
-		Sampled_shortTermAnalysis (me, windowDuration, dt, &nt, &t1);
+		Sampled_shortTermAnalysis (me, windowDuration, dt, & nt, & t1);
 		autoFormantFilter him = FormantFilter_create (my xmin, my xmax, nt, dt, t1,
 		                        fmin_hz, fmax_hz, nf, df_hz, f1_hz);
 
-		// Temporary objects
-
+		/*
+			Temporary objects.
+		*/
 		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
 		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
 		autoMelderProgress progress (U"Sound & Pitch: To FormantFilter");
 		for (long i = 1; i <= nt; i++) {
 			double t = Sampled_indexToX (him.get(), i);
-			double b, f0 = Pitch_getValueAtTime (thee, t, kPitch_unit_HERTZ, 0);
+			double b, f0 = Pitch_getValueAtTime (thee, t, kPitch_unit::HERTZ, 0);
 
 			if (isundef (f0) || f0 == 0.0) {
 				f0_undefined ++;
diff --git a/dwtools/GaussianMixture.cpp b/dwtools/GaussianMixture.cpp
index 3ac3513..3c4fa6c 100644
--- a/dwtools/GaussianMixture.cpp
+++ b/dwtools/GaussianMixture.cpp
@@ -259,7 +259,7 @@ autoGaussianMixture TableOfReal_to_GaussianMixture_fromRowLabels (TableOfReal me
 		GaussianMixture_setLabelsFromTableOfReal (thee.get(), me);
 
 		for (long i = 1; i <= numberOfComponents; i ++) {
-			autoTableOfReal tab = TableOfReal_extractRowsWhereLabel (me, kMelder_string_EQUAL_TO, dist -> rowLabels [i]);
+			autoTableOfReal tab = TableOfReal_extractRowsWhereLabel (me, kMelder_string::EQUAL_TO, dist -> rowLabels [i]);
 			autoCovariance cov = TableOfReal_to_Covariance (tab.get());
 			Covariance_into_Covariance (cov.get(), thy covariances->at [i]);
 			Thing_setName (thy covariances->at [i], dist -> rowLabels [i]);
diff --git a/dwtools/ICA.cpp b/dwtools/ICA.cpp
index 37419c0..12a1fc8 100644
--- a/dwtools/ICA.cpp
+++ b/dwtools/ICA.cpp
@@ -28,20 +28,20 @@
 #include "SVD.h"
 
 // matrix multiply R = V*C*V', V is nrv x ncv, C is ncv x ncv, R is nrv x nrv
-static void NUMdmatrices_multiply_VCVp (double **r, double **v, long nrv, long ncv, double **c, int csym) {
-	for (long i = 1; i <= nrv; i++) {
+static void NUMdmatrices_multiply_VCVp (double **r, double **v, long nrv, long ncv, double **c, bool csym) {
+	for (long i = 1; i <= nrv; i ++) {
 		long jstart = csym ? i : 1;
-		for (long j = jstart; j <= nrv; j++) {
+		for (long j = jstart; j <= nrv; j ++) {
 			// V_ik C_kl V'_lj = V_ik C_kl V_jl
-			double vcv = 0;
-			for (long k = 1; k <= ncv; k++) {
-				for (long l = 1; l <= ncv; l++) {
-					vcv += v[i][k] * c[k][l] * v[j][l];
+			double vcv = 0.0;
+			for (long k = 1; k <= ncv; k ++) {
+				for (long l = 1; l <= ncv; l ++) {
+					vcv += v [i] [k] * c [k] [l] * v [j] [l];
 				}
 			}
-			r[i][j] = vcv;
+			r [i] [j] = vcv;
 			if (csym) {
-				r[j][i] = vcv;
+				r [j] [i] = vcv;
 			}
 		}
 	}
@@ -71,28 +71,28 @@ static void NUMdmatrices_multiply_VpCV (double **r, double **v, long nrv, long n
 
 // matrix multiply V*C, V is nrv x ncv, C is ncv x ncc, R is nrv x ncc;
 static void NUMdmatrices_multiply_VC (double **r, double **v, long nrv, long ncv, double **c, long ncc) {
-	for (long i = 1; i <= nrv; i++) {
-		for (long j = 1; j <= ncc; j++) {
+	for (long i = 1; i <= nrv; i ++) {
+		for (long j = 1; j <= ncc; j ++) {
 			// V_ik C_kj
-			double vc = 0;
-			for (long k = 1; k <= ncv; k++) {
-				vc += v[i][k] * c[k][j];
+			real80 vc = 0.0;
+			for (long k = 1; k <= ncv; k ++) {
+				vc += v [i] [k] * c [k] [j];
 			}
-			r[i][j] = vc;
+			r [i] [j] = (double) vc;
 		}
 	}
 }
 
 // matrix multiply V'*C, V is nrv x ncv, C is nrv x ncc, R is ncv x ncc;
 static void NUMdmatrices_multiply_VpC (double **r, double **v, long nrv, long ncv, double **c, long ncc) {
-	for (long i = 1; i <= ncv; i++) {
-		for (long j = 1; j <= ncc; j++) {
+	for (long i = 1; i <= ncv; i ++) {
+		for (long j = 1; j <= ncc; j ++) {
 			// V'_ik C_kj
-			double vc = 0;
-			for (long k = 1; k <= nrv; k++) {
-				vc += v[k][i] * c[k][j];
+			real80 vc = 0.0;
+			for (long k = 1; k <= nrv; k ++) {
+				vc += v [k] [i] * c [k] [j];
 			}
-			r[i][j] = vc;
+			r [i] [j] = (double) vc;
 		}
 	}
 }
@@ -102,7 +102,7 @@ static void NUMdmatrices_multiplyScaleAdd (double **r, double **m, long nrm, lon
 	for (long i = 1; i <= nrm; i++) {
 		for (long j = 1; j <= nrm; j++) {
 			// M_ik M'_kj = M_ik M_jk
-			double mm = 0;
+			real80 mm = 0.0;
 			for (long k = 1; k <= ncm; k++) {
 				mm += m[i][k] * m[j][k];
 			}
@@ -119,27 +119,27 @@ static void NUMdmatrices_multiplyScaleAdd (double **r, double **m, long nrm, lon
 */
 static void NUMdmatrix_normalizeColumnVectors (double **w, long nrw, long ncw, double **c) {
 	for (long i = 1; i <= ncw; i++) {
-		double di = 0;
-		for (long k = 1; k <= ncw; k++)
-			for (long l = 1; l <= nrw; l++) {
-				di += w[k][i] * c[k][l] * w[l][i];
+		real80 di = 0.0;
+		for (long k = 1; k <= ncw; k ++)
+			for (long l = 1; l <= nrw; l ++) {
+				di += w [k] [i] * c [k] [l] * w [l] [i];
 			}
-		di = 1 / sqrt (di);
-		for (long j = 1; j <= nrw; j++) {
+		di = 1.0 / sqrt (di);
+		for (long j = 1; j <= nrw; j ++) {
 			w[j][i] *= di;
 		}
 	}
 }
 
 static double NUMdmatrix_diagonalityMeasure (double **v, long dimension) {
-	double dmsq = 0;
+	real80 dmsq = 0;
 	if (dimension < 2) {
-		return 0;
+		return 0.0;
 	}
-	for (long i = 1; i <= dimension; i++) {
-		for (long j = 1; j <= dimension; j++) {
+	for (long i = 1; i <= dimension; i ++) {
+		for (long j = 1; j <= dimension; j ++) {
 			if (i != j) {
-				dmsq += v[i][j] * v[i][j];
+				dmsq += v [i] [j] * v [i] [j];
 			}
 		}
 	}
@@ -192,40 +192,40 @@ static void Diagonalizer_and_CrossCorrelationTableList_ffdiag (Diagonalizer me,
 		autoNUMmatrix<double> vnew (1, dimension, 1, dimension);
 		autoNUMmatrix<double> cc (1, dimension, 1, dimension);
 
-		for (long i = 1; i <= dimension; i++) {
-			w[i][i] = 1;
+		for (long i = 1; i <= dimension; i ++) {
+			w [i] [i] = 1.0;
 		}
 
 		autoMelderProgress progress (U"Simultaneous diagonalization of many CrossCorrelationTables...");
-		double dm_new = CrossCorrelationTableList_getDiagonalityMeasure (ccts.get(), nullptr, 0, 0);
+		real80 dm_new = CrossCorrelationTableList_getDiagonalityMeasure (ccts.get(), nullptr, 0, 0);
 		try {
-			double dm_old, theta = 1.0, dm_start = dm_new;
+			real80 dm_old, theta = 1.0, dm_start = dm_new;
 			do {
 				dm_old = dm_new;
-				for (long i = 1; i <= dimension; i++) {
+				for (long i = 1; i <= dimension; i ++) {
 					for (long j = i + 1; j <= dimension; j ++) {
-						double zii = 0.0, zij = 0.0, zjj = 0.0, yij = 0.0, yji = 0.0;   // zij == zji
-						for (long k = 1; k <= ccts->size; k ++) {
-							CrossCorrelationTable ct = ccts->at [k];
+						real80 zii = 0.0, zij = 0.0, zjj = 0.0, yij = 0.0, yji = 0.0;   // zij == zji
+						for (long k = 1; k <= ccts -> size; k ++) {
+							CrossCorrelationTable ct = ccts -> at [k];
 							zii += ct -> data [i] [i] * ct -> data [i] [i];
 							zij += ct -> data [i] [i] * ct -> data [j] [j];
 							zjj += ct -> data [j] [j] * ct -> data [j] [j];
 							yij += ct -> data [j] [j] * ct -> data [i] [j];
 							yji += ct -> data [i] [i] * ct -> data [i] [j];
 						}
-						double denom = zjj * zii - zij * zij;
-						if (denom != 0) {
-							w[i][j] = (zij * yji - zii * yij) / denom;
-							w[j][i] = (zij * yij - zjj * yji) / denom;
+						real80 denom = zjj * zii - zij * zij;
+						if (denom != 0.0) {
+							w [i][j] = (zij * yji - zii * yij) / denom;
+							w [j][i] = (zij * yij - zjj * yji) / denom;
 						}
 					}
 				}
-				double norma = 0.0;
+				real80 norma = 0.0;
 				for (long i = 1; i <= dimension; i ++) {
-					double normai = 0.0;
+					real80 normai = 0.0;
 					for (long j = 1; j <= dimension; j ++) {
 						if (i != j) {
-							normai += fabs (w[ i] [j]);
+							normai += fabs (w [i] [j]);
 						}
 					}
 					if (normai > norma) {
@@ -234,13 +234,13 @@ static void Diagonalizer_and_CrossCorrelationTableList_ffdiag (Diagonalizer me,
 				}
 				// evaluate the norm
 				if (norma > theta) {
-					double normf = 0;
+					real80 normf = 0.0;
 					for (long i = 1; i <= dimension; i ++)
 						for (long j = 1; j <= dimension; j ++)
 							if (i != j) {
 								normf += w [i] [j] * w [i] [j];
 							}
-					double scalef = theta / sqrt (normf);
+					real80 scalef = theta / sqrt (normf);
 					for (long i = 1; i <= dimension; i ++) {
 						for (long j = 1; j <= dimension; j ++) {
 							if (i != j) {
@@ -252,14 +252,14 @@ static void Diagonalizer_and_CrossCorrelationTableList_ffdiag (Diagonalizer me,
 				// update V
 				NUMmatrix_copyElements (v, vnew.peek(), 1, dimension, 1, dimension);
 				NUMdmatrices_multiply_VC (v, w.peek(), dimension, dimension, vnew.peek(), dimension);
-				for (long k = 1; k <= ccts->size; k ++) {
-					CrossCorrelationTable ct = ccts->at [k];
+				for (long k = 1; k <= ccts -> size; k ++) {
+					CrossCorrelationTable ct = ccts -> at [k];
 					NUMmatrix_copyElements (ct -> data, cc.peek(), 1, dimension, 1, dimension);
-					NUMdmatrices_multiply_VCVp (ct -> data, w.peek(), dimension, dimension, cc.peek(), 1);
+					NUMdmatrices_multiply_VCVp (ct -> data, w.peek(), dimension, dimension, cc.peek(), true);
 				}
 				dm_new = CrossCorrelationTableList_getDiagonalityMeasure (ccts.get(), nullptr, 0, 0);
 				iter++;
-				Melder_progress ((double) iter / (double) maxNumberOfIterations, U"Iteration: ", iter, U", measure: ", dm_new, U"\n fractional measure: ", dm_new / dm_start);
+				Melder_progress ((double) iter / (double) maxNumberOfIterations, U"Iteration: ", iter, U", measure: ", (double) dm_new, U"\n fractional measure: ", (double)(dm_new / dm_start));
 			} while (fabs ((dm_old - dm_new) / dm_new) > delta && iter < maxNumberOfIterations);
 		} catch (MelderError) {
 			Melder_clearError ();
@@ -314,9 +314,9 @@ static void Diagonalizer_and_CrossCorrelationTable_qdiag (Diagonalizer me, Cross
 		autoNUMvector<double> wnew (1, dimension);
 		autoNUMvector<double> mvec (1, dimension);
 
-		for (long i = 1; i <= dimension; i++) // Transpose W
-			for (long j = 1; j <= dimension; j++) {
-				wc[i][j] = w[j][i];
+		for (long i = 1; i <= dimension; i ++) // Transpose W
+			for (long j = 1; j <= dimension; j ++) {
+				wc [i] [j] = w [j] [i];
 			}
 
 		// d = diag(diag(W'*C0*W));
@@ -329,13 +329,13 @@ static void Diagonalizer_and_CrossCorrelationTable_qdiag (Diagonalizer me, Cross
 		// P = db^(-1/2)*vb';
 
 		Eigen_initFromSymmetricMatrix (eigen.get(), c0 -> data, dimension);
-		for (long i = 1; i <= dimension; i++) {
+		for (long i = 1; i <= dimension; i ++) {
 			if (eigen -> eigenvalues[i] < 0) {
 				Melder_throw (U"Covariance matrix not positive definite, eigenvalue[", i, U"] is negative.");
 			}
-			double scalef = 1 / sqrt (eigen -> eigenvalues[i]);
-			for (long j = 1; j <= dimension; j++) {
-				p[dimension - i + 1][j] = scalef * eigen -> eigenvectors[i][j];
+			double scalef = 1.0 / sqrt (eigen -> eigenvalues [i]);
+			for (long j = 1; j <= dimension; j ++) {
+				p [dimension - i + 1] [j] = scalef * eigen -> eigenvectors [i] [j];
 			}
 		}
 
@@ -344,7 +344,7 @@ static void Diagonalizer_and_CrossCorrelationTable_qdiag (Diagonalizer me, Cross
 		for (long ic = 1; ic <= thy size; ic ++) {
 			CrossCorrelationTable cov1 = thy at [ic];
 			CrossCorrelationTable cov2 = ccts -> at [ic];
-			NUMdmatrices_multiply_VCVp (cov2 -> data, p.peek(), dimension, dimension, cov1 -> data, 1);
+			NUMdmatrices_multiply_VCVp (cov2 -> data, p.peek(), dimension, dimension, cov1 -> data, true);
 		}
 
 		// W = P'\W == inv(P') * W
@@ -360,7 +360,7 @@ static void Diagonalizer_and_CrossCorrelationTable_qdiag (Diagonalizer me, Cross
 			// C * W
 			NUMdmatrices_multiply_VC (m1.peek(), cov -> data, dimension, dimension, w, dimension);
 			// D += scalef * M1*M1'
-			NUMdmatrices_multiplyScaleAdd (d.peek(), m1.peek(), dimension, dimension, 2 * cweights[ic]);
+			NUMdmatrices_multiplyScaleAdd (d.peek(), m1.peek(), dimension, dimension, 2 * cweights [ic]);
 		}
 
 		long iter = 0;
@@ -373,9 +373,9 @@ static void Diagonalizer_and_CrossCorrelationTable_qdiag (Diagonalizer me, Cross
 				// differences of eigenvectors.
 
 				delta_w = 0;
-				for (long kol = 1; kol <= dimension; kol++) {
-					for (long i = 1; i <= dimension; i++) {
-						wvec[i] = w[i][kol];
+				for (long kol = 1; kol <= dimension; kol ++) {
+					for (long i = 1; i <= dimension; i ++) {
+						wvec [i] = w [i] [kol];
 					}
 
 					update_one_column (ccts.get(), d.peek(), cweights, wvec.peek(), -1, mvec.peek());
@@ -384,29 +384,30 @@ static void Diagonalizer_and_CrossCorrelationTable_qdiag (Diagonalizer me, Cross
 
 					// Eigenvalues already sorted; get eigenvector of smallest !
 
-					for (long i = 1; i <= dimension; i++) {
-						wnew[i] = eigen -> eigenvectors[dimension][i];
+					for (long i = 1; i <= dimension; i ++) {
+						wnew [i] = eigen -> eigenvectors [dimension] [i];
 					}
 
 					update_one_column (ccts.get(), d.peek(), cweights, wnew.peek(), 1, mvec.peek());
-					for (long i = 1; i <= dimension; i++) {
-						w[i][kol] = wnew[i];
+					for (long i = 1; i <= dimension; i ++) {
+						w [i] [kol] = wnew [i];
 					}
 
 					// compare norms of eigenvectors. We have to compare ||wvec +/- w_new|| because eigenvectors
 					//  may change sign.
 
 					double normp = 0, normm = 0;
-					for (long j = 1; j <= dimension; j++) {
-						double dm = wvec[j] - wnew[j], dp = wvec[j] + wnew[j];
-						normp += dm * dm; normm += dp * dp;
+					for (long j = 1; j <= dimension; j ++) {
+						double dm = wvec [j] - wnew [j], dp = wvec [j] + wnew [j];
+						normp += dm * dm; 
+						normm += dp * dp;
 					}
 
 					normp = normp < normm ? normp : normm;
 					normp = sqrt (normp);
 					delta_w = normp > delta_w ? normp : delta_w;
 				}
-				iter++;
+				iter ++;
 
 				Melder_progress ((double) iter / (double) (maxNumberOfIterations + 1), U"Iteration: ", iter, U", norm: ", delta_w);
 			} while (delta_w > delta && iter < maxNumberOfIterations);
@@ -444,20 +445,20 @@ void MixingMatrix_and_CrossCorrelationTableList_improveUnmixing (MixingMatrix me
 static void NUMcrossCorrelate_rows (double **x, long nrows, long icol1, long icol2, long lag, double **cc, double *centroid, double scale) {
 	lag = labs (lag);
 	long nsamples = icol2 - icol1 + 1 + lag;
-	for (long i = 1; i <= nrows; i++) {
-		double sum = 0;
-		for (long k = icol1; k <= icol2 + lag; k++) {
-			sum += x[i][k];
+	for (long i = 1; i <= nrows; i ++) {
+		double sum = 0.0;
+		for (long k = icol1; k <= icol2 + lag; k ++) {
+			sum += x [i] [k];
 		}
 		centroid[i] = sum / nsamples;
 	}
-	for (long i = 1; i <= nrows; i++) {
-		for (long j = i; j <= nrows; j++) {
+	for (long i = 1; i <= nrows; i ++) {
+		for (long j = i; j <= nrows; j ++) {
 			double ccor = 0;
-			for (long k = icol1; k <= icol2; k++) {
-				ccor += (x[i][k] - centroid[i]) * (x[j][k + lag] - centroid[j]);
+			for (long k = icol1; k <= icol2; k ++) {
+				ccor += (x [i] [k] - centroid [i]) * (x [j] [k + lag] - centroid [j]);
 			}
-			cc[j][i] = cc[i][j] = ccor * scale;
+			cc [j] [i] = cc [i] [j] = ccor * scale;
 		}
 	}
 }
@@ -528,11 +529,11 @@ autoCrossCorrelationTable Sounds_to_CrossCorrelationTable_combined (Sound me, So
 		}
 		autoCrossCorrelationTable him = CrossCorrelationTable_create (nchannels);
 		autoNUMvector<double *> data (1, nchannels);
-		for (long i = 1; i <= my ny; i++) {
-			data[i] = my z[i];
+		for (long i = 1; i <= my ny; i ++) {
+			data [i] = my z [i];
 		}
-		for (long i = 1; i <= thy ny; i++) {
-			data[i + my ny] = thy z[i];
+		for (long i = 1; i <= thy ny; i ++) {
+			data [i + my ny] = thy z [i];
 		}
 
 		NUMcrossCorrelate_rows (data.peek(), nchannels, i1, i2, ndelta, his data, his centroid, my dx);
@@ -599,8 +600,8 @@ autoDiagonalizer Diagonalizer_create (long dimension) {
 	try {
 		autoDiagonalizer me = Thing_new (Diagonalizer);
 		TableOfReal_init (me.get(), dimension, dimension);
-		for (long i = 1; i <= dimension; i++) {
-			my data[i][i] = 1;
+		for (long i = 1; i <= dimension; i ++) {
+			my data [i] [i] = 1.0;
 		}
 		return me;
 	} catch (MelderError) {
@@ -608,10 +609,6 @@ autoDiagonalizer Diagonalizer_create (long dimension) {
 	}
 }
 
-
-/************************ MixingMatrix **********************************/
-
-
 /***************** Diagonalizer & MixingMatrix *************************/
 
 
@@ -639,11 +636,16 @@ autoMixingMatrix Diagonalizer_to_MixingMatrix (Diagonalizer me) {
 	}
 }
 
+void MixingMatrix_and_Sound_improveUnmixing (MixingMatrix me, Sound thee, double startTime, double endTime, long ncovars, double lagStep, long maxNumberOfIterations, double tol, int method) {
+	autoCrossCorrelationTableList ccs = Sound_to_CrossCorrelationTableList (thee, startTime, endTime, lagStep, ncovars);
+	MixingMatrix_and_CrossCorrelationTableList_improveUnmixing (me, ccs.get(), maxNumberOfIterations, tol, method);
+}
+
 autoMixingMatrix Sound_to_MixingMatrix (Sound me, double startTime, double endTime, long ncovars, double lagStep, long maxNumberOfIterations, double tol, int method) {
 	try {
 		autoCrossCorrelationTableList ccs = Sound_to_CrossCorrelationTableList (me, startTime, endTime, lagStep, ncovars);
 		autoMixingMatrix thee = MixingMatrix_create (my ny, my ny);
-		MixingMatrix_setRandomGauss ( thee.get(), 0.0, 1.0);
+		MixingMatrix_setRandomGauss (thee.get(), 0.0, 1.0);
 		MixingMatrix_and_CrossCorrelationTableList_improveUnmixing (thee.get(), ccs.get(), maxNumberOfIterations, tol, method);
 		return thee;
 	} catch (MelderError) {
@@ -690,31 +692,33 @@ autoCrossCorrelationTable CrossCorrelationTable_createSimple (char32 *covars, ch
 		long ncovars = Melder_countTokens (covars);
 		long ncovars_wanted = dimension * (dimension + 1) / 2;
 		if (ncovars != ncovars_wanted) Melder_throw (U"The number of matrix elements and the number of "
-			        U"centroid elements are not in concordance. There should be \"d(d+1)/2\" matrix values and \"d\" centroid values.");
+			U"centroid elements are not in concordance. There should be \"d(d+1)/2\" matrix values and \"d\" centroid values.");
 
 		autoCrossCorrelationTable me = CrossCorrelationTable_create (dimension);
 
-		// Construct the full matrix from the upper-diagonal elements
+		/*
+			Construct the full matrix from the upper-diagonal elements
+		*/
 
 		long inum = 1, irow = 1;
-		for (char32 *token = Melder_firstToken (covars); token != nullptr && inum <= ncovars_wanted; token = Melder_nextToken (), inum++) {
+		for (char32 *token = Melder_firstToken (covars); token != nullptr && inum <= ncovars_wanted; token = Melder_nextToken (), inum ++) {
 			double number;
 			long nmissing = (irow - 1) * irow / 2;
 			long inumc = inum + nmissing;
 			irow = (inumc - 1) / dimension + 1;
 			long icol = ( (inumc - 1) % dimension) + 1;
 			Interpreter_numericExpression (nullptr, token, &number);
-			my data[irow][icol] = my data[icol][irow] = number;
+			my data [irow] [icol] = my data [icol] [irow] = number;
 			if (icol == dimension) {
-				irow++;
+				irow ++;
 			}
 		}
 
 		inum = 1;
 		for (char32 *token = Melder_firstToken (centroid); token != nullptr && inum <= dimension; token = Melder_nextToken (), inum++) {
 			double number;
-			Interpreter_numericExpression (nullptr, token, &number);
-			my centroid[inum] = number;
+			Interpreter_numericExpression (nullptr, token, & number);
+			my centroid [inum] = number;
 		}
 		my numberOfObservations = numberOfSamples;
 		return me;
@@ -744,7 +748,7 @@ autoCrossCorrelationTableList CrossCorrelationTables_to_CrossCorrelationTableLis
 	try {
 		autoCrossCorrelationTableList thee = CrossCorrelationTableList_create ();
 		long numberOfRows = 0, numberOfColumns = 0, numberOfSelected = 0;
-		for (long i = 1; i <= my size; i++) {
+		for (long i = 1; i <= my size; i ++) {
 			CrossCorrelationTable item = my at [i];
 			numberOfSelected++;
 			if (numberOfSelected == 1) {
@@ -782,7 +786,7 @@ double CrossCorrelationTableList_getDiagonalityMeasure (CrossCorrelationTableLis
 	for (long k = start; k <= end; k ++) {
 		CrossCorrelationTable thee = my at [k];
 		double dmksq = NUMdmatrix_diagonalityMeasure (thy data, dimension);
-		dmsq += ( w ? dmksq * w [k] : dmksq / ntables );
+		dmsq += (w ? dmksq * w [k] : dmksq / ntables);
 	}
 	return dmsq;
 }
@@ -801,7 +805,7 @@ autoCrossCorrelationTable CrossCorrelationTable_and_Diagonalizer_diagonalize (Cr
 			Melder_throw (U"The CrossCorrelationTable and the Diagonalizer matrix dimensions must be equal.");
 		}
 		autoCrossCorrelationTable him = CrossCorrelationTable_create (my numberOfColumns);
-		NUMdmatrices_multiply_VCVp (his data, thy data, my numberOfColumns, my numberOfColumns, my data, 1);
+		NUMdmatrices_multiply_VCVp (his data, thy data, my numberOfColumns, my numberOfColumns, my data, true);
 		return him;
 	} catch (MelderError) {
 		Melder_throw (U"CrossCorrelationTable not diagonalized.");
@@ -838,7 +842,7 @@ void Diagonalizer_and_CrossCorrelationTableList_improveDiagonality (Diagonalizer
 	if (method == 1) {
 		autoNUMvector<double> cweights (1, thy size);
 		for (long i = 1; i <= thy size; i ++) {
-			cweights[i] = 1.0 / thy size;
+			cweights [i] = 1.0 / thy size;
 		}
 		Diagonalizer_and_CrossCorrelationTable_qdiag (me, thee, cweights.peek(), maxNumberOfIterations, tol);
 	} else {
@@ -872,8 +876,10 @@ autoSound Sound_and_Covariance_whitenChannels (Sound me, Covariance thee, double
  */
 autoCrossCorrelationTableList CrossCorrelationTableList_createTestSet (long dimension, long n, int firstPositiveDefinite, double sigma) {
 	try {
-		// Start with a square matrix with random gaussian elements and make its singular value decomposition UDV'
-		// The V matrix will be the common diagonalizer matrix that we use.
+		/*
+			Start with a square matrix with random gaussian elements and make its singular value decomposition UDV'
+			The V matrix will be the common diagonalizer matrix that we use.
+		*/
 
 		autoNUMmatrix<double> d (1, dimension, 1, dimension);
 		for (long i = 1; i <= dimension; i ++) { // Generate the rotation matrix
@@ -895,7 +901,7 @@ autoCrossCorrelationTableList CrossCorrelationTableList_createTestSet (long dime
 
 		for (long k = 1; k <= n; k ++) {
 			autoCrossCorrelationTable ct = CrossCorrelationTable_create (dimension);
-			double low = ( k == 1 && firstPositiveDefinite ? 0.1 : -1.0 );
+			double low = (k == 1 && firstPositiveDefinite ? 0.1 : -1.0);
 			for (long i = 1; i <= dimension; i ++) {
 				d [i] [i] = NUMrandomUniform (low, 1.0);
 			}
@@ -905,7 +911,7 @@ autoCrossCorrelationTableList CrossCorrelationTableList_createTestSet (long dime
 				}
 			}
 			// we need V'DV, however our V has eigenvectors row-wise -> VDV'
-			NUMdmatrices_multiply_VCVp (ct -> data, v.peek(), dimension, dimension, d.peek(), 1);
+			NUMdmatrices_multiply_VCVp (ct -> data, v.peek(), dimension, dimension, d.peek(), true);
             my addItem_move (ct.move());
 		}
 		return me;
@@ -931,4 +937,4 @@ static void Sound_and_MixingMatrix_improveUnmixing_fica (Sound me, MixingMatrix
 }
 #endif
 
-/* End of file ICA.cpp 987*/
+/* End of file ICA.cpp */
diff --git a/dwtools/ICA.h b/dwtools/ICA.h
index 4251074..a580e0c 100644
--- a/dwtools/ICA.h
+++ b/dwtools/ICA.h
@@ -77,6 +77,7 @@ autoSound Sound_and_Covariance_whitenChannels (Sound me, Covariance thee, double
 
 void MixingMatrix_and_CrossCorrelationTableList_improveUnmixing (MixingMatrix me, CrossCorrelationTableList thee, long maxNumberOfIterations, double tol, int method);
 
+void MixingMatrix_and_Sound_improveUnmixing (MixingMatrix me, Sound thee, double startTime, double endTime, long ncovars, double lagStep, long maxNumberOfIterations, double tol, int method);
 /*
 	Determine the matrix that diagonalizes a series of CrossCorrelationTables as well as possible.
 */
diff --git a/dwtools/KlattGrid.cpp b/dwtools/KlattGrid.cpp
index 9ffa5a3..ad5bcb9 100644
--- a/dwtools/KlattGrid.cpp
+++ b/dwtools/KlattGrid.cpp
@@ -2461,10 +2461,10 @@ void KlattGrid_formula_amplitudes (KlattGrid me, int formantType, const char32 *
 			for (long icol = 1; icol <= amplitudes -> points.size; icol ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				if (isundef (result. result.numericResult)) {
+				if (isundef (result. numericResult)) {
 					Melder_throw (U"Cannot put an undefined value into the tier.\nFormula not finished.");
 				}
-				amplitudes -> points.at [icol] -> value = result. result.numericResult;
+				amplitudes -> points.at [icol] -> value = result. numericResult;
 			}
 		}
 	} catch (MelderError) {
diff --git a/dwtools/LongSound_extensions.cpp b/dwtools/LongSound_extensions.cpp
index dd7b0fa..d644e00 100644
--- a/dwtools/LongSound_extensions.cpp
+++ b/dwtools/LongSound_extensions.cpp
@@ -178,7 +178,7 @@ static void writePartToOpenFile16 (LongSound me, int audioFileType, long imin, l
 }
 
 void LongSounds_appendToExistingSoundFile (OrderedOf<structSampled>* me, MelderFile file) {
-	long pre_append_endpos = 0, numberOfBitsPerSamplePoint = 16;
+	integer pre_append_endpos = 0, numberOfBitsPerSamplePoint = 16;
 	try {
 		if (my size < 1) {
 			Melder_throw (U"No Sound or LongSound objects to append.");
@@ -198,9 +198,9 @@ void LongSounds_appendToExistingSoundFile (OrderedOf<structSampled>* me, MelderF
 		file -> filePointer = f; // essential !!
 		double sampleRate_d;
 		long startOfData;
-		int32 numberOfSamples;
+		integer numberOfSamples;
 		int numberOfChannels, encoding;
-		int audioFileType = MelderFile_checkSoundFile (file, &numberOfChannels, &encoding, &sampleRate_d, &startOfData, &numberOfSamples);
+		int audioFileType = MelderFile_checkSoundFile (file, & numberOfChannels, & encoding, & sampleRate_d, & startOfData, & numberOfSamples);
 
 		if (audioFileType == 0) {
 			Melder_throw (U"Not a sound file.");
diff --git a/dwtools/Matrix_extensions.cpp b/dwtools/Matrix_extensions.cpp
index c202a90..575e9ba 100644
--- a/dwtools/Matrix_extensions.cpp
+++ b/dwtools/Matrix_extensions.cpp
@@ -419,7 +419,7 @@ autoDaata IDXFormattedMatrixFileRecognizer (int numberOfBytesRead, const char *h
 	trace (U"Cell size =", cellSizeBytes);
 	double numberOfBytes = numberOfCells * cellSizeBytes + 4 + numberOfDimensions * 4;
 	trace (U"Number of bytes =", numberOfBytes);
-	long numberOfBytesInFile = MelderFile_length (file);
+	integer numberOfBytesInFile = MelderFile_length (file);
 	trace (U"File size = ", numberOfBytesInFile);
 	if (numberOfBytes > numberOfBytesInFile || (long) numberOfBytes < numberOfBytesInFile) { // may occur if it is not an IDX file
 		return autoDaata ();
diff --git a/dwtools/MixingMatrix.cpp b/dwtools/MixingMatrix.cpp
index 55b6f93..b59c0fb 100644
--- a/dwtools/MixingMatrix.cpp
+++ b/dwtools/MixingMatrix.cpp
@@ -73,6 +73,14 @@ void MixingMatrix_setRandomGauss (MixingMatrix me, double mean, double stdev) {
 	}
 }
 
+void MixingMatrix_multiplyInputChannel (MixingMatrix me, long inputChannel, double value) {
+	if (inputChannel >= 1 && inputChannel <= my numberOfColumns) {
+		for (long i = 1; i <= my numberOfRows; i ++) {
+			my data [i] [inputChannel] *= value;
+		}
+	}
+}
+
 // https://developer.mozilla.org/en-US/docs/Web/API/AudioNode/channelInterpretation
 void MixingMatrix_setStandardChannelInterpretation (MixingMatrix me) {
 	for (long i = 1; i <= my numberOfRows; i++) {
@@ -214,7 +222,7 @@ void MixingMatrix_muteAndActivateChannels (MixingMatrix me, bool *muteChannels)
 			numberOfMuteChannels ++;
 		}
 	}
-	// Set all mute channels to 0 and all other channels to 1. To pervent overflow scale by the number of channels that are on.
+	// Set all mute channels to 0 and all other channels to 1. To prevent overflow scale by the number of channels that are on.
 	double coefficient = my numberOfColumns > numberOfMuteChannels ? 1.0 / (my numberOfColumns - numberOfMuteChannels) : 0.0;
 	for (long icol = 1; icol <= my numberOfColumns; icol ++) {
 		double channelScaling = muteChannels [icol] ? 0.0 : coefficient;
diff --git a/dwtools/MixingMatrix.h b/dwtools/MixingMatrix.h
index 0ff2815..71cc4b4 100644
--- a/dwtools/MixingMatrix.h
+++ b/dwtools/MixingMatrix.h
@@ -27,6 +27,8 @@ autoMixingMatrix MixingMatrix_create (long numberOfOutputChannels, long numberOf
 
 autoMixingMatrix MixingMatrix_createSimple (long numberOfOutputChannels, long numberOfInputChannels, char32 *elements);
 
+void MixingMatrix_multiplyInputChannel (MixingMatrix me, long inputChannel, double value);
+
 void MixingMatrix_muteAndActivateChannels (MixingMatrix me, bool *muteChannels);
 
 void MixingMatrix_setStandardChannelInterpretation (MixingMatrix me);
diff --git a/dwtools/PCA.cpp b/dwtools/PCA.cpp
index 3b31e4b..9306027 100644
--- a/dwtools/PCA.cpp
+++ b/dwtools/PCA.cpp
@@ -151,8 +151,8 @@ autoEigen PCA_to_Eigen (PCA me) {
 
 static autoPCA NUMdmatrix_to_PCA (double **m, long numberOfRows, long numberOfColumns, bool byColumns) {
 	try {
-		if (! NUMdmatrix_hasFiniteElements(m, 1, numberOfRows, 1, numberOfColumns)) {
-			Melder_throw (U"At least one of the matrix elements is not finite or undefined.");
+		if (NUMdmatrix_containsUndefinedElements (m, 1, numberOfRows, 1, numberOfColumns)) {
+			Melder_throw (U"At least one of the matrix elements is undefined.");
 		}
 		if (NUMfrobeniusnorm (numberOfRows, numberOfColumns, m) == 0.0) {
 			Melder_throw (U"All values in your table are zero.");
diff --git a/dwtools/Pitch_extensions.cpp b/dwtools/Pitch_extensions.cpp
index 037dd9b..041fa39 100644
--- a/dwtools/Pitch_extensions.cpp
+++ b/dwtools/Pitch_extensions.cpp
@@ -120,32 +120,32 @@ autoPitch Pitch_scaleTime (Pitch me, double scaleFactor) {
 	}
 }
 
-static double HertzToSpecial (double value, int pitchUnit) {
-	return	pitchUnit == kPitch_unit_HERTZ ? value :
-		pitchUnit == kPitch_unit_HERTZ_LOGARITHMIC ? value <= 0.0 ? undefined : log10 (value) :
-		pitchUnit == kPitch_unit_MEL ? NUMhertzToMel (value) :
-		pitchUnit == kPitch_unit_LOG_HERTZ ? value <= 0.0 ? undefined : log10 (value) :
-		pitchUnit == kPitch_unit_SEMITONES_1 ? value <= 0.0 ? undefined : 12.0 * log (value / 1.0) / NUMln2 :
-		pitchUnit == kPitch_unit_SEMITONES_100 ? value <= 0.0 ? undefined : 12.0 * log (value / 100.0) / NUMln2 :
-		pitchUnit == kPitch_unit_SEMITONES_200 ? value <= 0.0 ? undefined : 12.0 * log (value / 200.0) / NUMln2 :
-		pitchUnit == kPitch_unit_SEMITONES_440 ? value <= 0.0 ? undefined : 12.0 * log (value / 440.0) / NUMln2 :
-		pitchUnit == kPitch_unit_ERB ? NUMhertzToErb (value) : undefined;
+static double HertzToSpecial (double value, kPitch_unit pitchUnit) {
+	return	pitchUnit == kPitch_unit::HERTZ ? value :
+		pitchUnit == kPitch_unit::HERTZ_LOGARITHMIC ? value <= 0.0 ? undefined : log10 (value) :
+		pitchUnit == kPitch_unit::MEL ? NUMhertzToMel (value) :
+		pitchUnit == kPitch_unit::LOG_HERTZ ? value <= 0.0 ? undefined : log10 (value) :
+		pitchUnit == kPitch_unit::SEMITONES_1 ? value <= 0.0 ? undefined : 12.0 * log (value / 1.0) / NUMln2 :
+		pitchUnit == kPitch_unit::SEMITONES_100 ? value <= 0.0 ? undefined : 12.0 * log (value / 100.0) / NUMln2 :
+		pitchUnit == kPitch_unit::SEMITONES_200 ? value <= 0.0 ? undefined : 12.0 * log (value / 200.0) / NUMln2 :
+		pitchUnit == kPitch_unit::SEMITONES_440 ? value <= 0.0 ? undefined : 12.0 * log (value / 440.0) / NUMln2 :
+		pitchUnit == kPitch_unit::ERB ? NUMhertzToErb (value) : undefined;
 }
 
-static double SpecialToHertz (double value, int pitchUnit) {
-	return	pitchUnit == kPitch_unit_HERTZ ? value :
-		pitchUnit == kPitch_unit_HERTZ_LOGARITHMIC ? pow (10.0, value) :
-		pitchUnit == kPitch_unit_MEL ? NUMmelToHertz (value) :
-		pitchUnit == kPitch_unit_LOG_HERTZ ? pow (10.0, value) :
-		pitchUnit == kPitch_unit_SEMITONES_1 ? 1.0 * exp (value * (NUMln2 / 12.0)) :
-		pitchUnit == kPitch_unit_SEMITONES_100 ? 100.0 * exp (value * (NUMln2 / 12.0)) :
-		pitchUnit == kPitch_unit_SEMITONES_200 ? 200.0 * exp (value * (NUMln2 / 12.0)) :
-		pitchUnit == kPitch_unit_SEMITONES_440 ? 440.0 * exp (value * (NUMln2 / 12.0)) :
-		pitchUnit == kPitch_unit_ERB ? NUMerbToHertz (value) : undefined;
+static double SpecialToHertz (double value, kPitch_unit pitchUnit) {
+	return	pitchUnit == kPitch_unit::HERTZ ? value :
+		pitchUnit == kPitch_unit::HERTZ_LOGARITHMIC ? pow (10.0, value) :
+		pitchUnit == kPitch_unit::MEL ? NUMmelToHertz (value) :
+		pitchUnit == kPitch_unit::LOG_HERTZ ? pow (10.0, value) :
+		pitchUnit == kPitch_unit::SEMITONES_1 ? 1.0 * exp (value * (NUMln2 / 12.0)) :
+		pitchUnit == kPitch_unit::SEMITONES_100 ? 100.0 * exp (value * (NUMln2 / 12.0)) :
+		pitchUnit == kPitch_unit::SEMITONES_200 ? 200.0 * exp (value * (NUMln2 / 12.0)) :
+		pitchUnit == kPitch_unit::SEMITONES_440 ? 440.0 * exp (value * (NUMln2 / 12.0)) :
+		pitchUnit == kPitch_unit::ERB ? NUMerbToHertz (value) : undefined;
 }
 
-autoPitchTier PitchTier_normalizePitchRange (PitchTier me, double pitchMin_ref_Hz, double pitchMax_ref_Hz, double pitchMin_Hz, double pitchMax_Hz, int pitchUnit);
-autoPitchTier PitchTier_normalizePitchRange (PitchTier me, double pitchMin_ref_Hz, double pitchMax_ref_Hz, double pitchMin_Hz, double pitchMax_Hz, int pitchUnit) {
+autoPitchTier PitchTier_normalizePitchRange (PitchTier me, double pitchMin_ref_Hz, double pitchMax_ref_Hz, double pitchMin_Hz, double pitchMax_Hz, kPitch_unit pitchUnit);
+autoPitchTier PitchTier_normalizePitchRange (PitchTier me, double pitchMin_ref_Hz, double pitchMax_ref_Hz, double pitchMin_Hz, double pitchMax_Hz, kPitch_unit pitchUnit) {
 	try {
 		double fminr = HertzToSpecial (pitchMin_ref_Hz, pitchUnit);
 		double fmaxr = HertzToSpecial (pitchMax_ref_Hz, pitchUnit);
diff --git a/dwtools/Polynomial.cpp b/dwtools/Polynomial.cpp
index 38cdfee..da702b3 100644
--- a/dwtools/Polynomial.cpp
+++ b/dwtools/Polynomial.cpp
@@ -1,6 +1,6 @@
 /* Polynomial.cpp
  *
- * Copyright (C) 1993-2016 David Weenink
+ * Copyright (C) 1993-2016,2017 David Weenink
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -278,19 +278,16 @@ static void svdcvm (double **v, long mfit, long ma, int *frozen, double *w, doub
 
 Thing_implement (FunctionTerms, Function, 0);
 
-double structFunctionTerms :: v_evaluate (double x) {
-	(void) x;
+double structFunctionTerms :: v_evaluate (double /* x */) {
 	return undefined;
 }
 
-void structFunctionTerms :: v_evaluate_z (dcomplex *z, dcomplex *p) {
-	(void) z;
-	p -> re = p -> im = undefined;
+dcomplex structFunctionTerms :: v_evaluate_z (dcomplex /* z */) {
+	return { undefined, undefined };
 }
 
-void structFunctionTerms :: v_evaluateTerms (double x, double terms[]) {
-	(void) x;
-	for (long i = 1; i <= numberOfCoefficients; i++) {
+void structFunctionTerms :: v_evaluateTerms (double /* x */, double terms []) {
+	for (long i = 1; i <= numberOfCoefficients; i ++) {
 		terms [i] = undefined;
 	}
 }
@@ -379,8 +376,8 @@ double FunctionTerms_evaluate (FunctionTerms me, double x) {
 	return my v_evaluate (x);
 }
 
-void FunctionTerms_evaluate_z (FunctionTerms me, dcomplex *z, dcomplex *p) {
-	my v_evaluate_z (z, p);
+dcomplex FunctionTerms_evaluate_z (FunctionTerms me, dcomplex z) {
+	return my v_evaluate_z (z);
 }
 
 void FunctionTerms_evaluateTerms (FunctionTerms me, double x, double terms[]) {
@@ -564,17 +561,17 @@ double structPolynomial :: v_evaluate (double x) {
 	return (double) p;
 }
 
-void structPolynomial :: v_evaluate_z (dcomplex *z, dcomplex *p) {
-	long double x = z -> re, y = z -> im;
+dcomplex structPolynomial :: v_evaluate_z (dcomplex z) {
+	real80 x = z.re, y = z.im;
 
-	long double pr = coefficients [numberOfCoefficients];
-	long double pi = 0;
-	for (long i = numberOfCoefficients - 1; i > 0; i--) {
+	real80 pr = coefficients [numberOfCoefficients];
+	real80 pi = 0.0;
+	for (long i = numberOfCoefficients - 1; i > 0; i --) {
 		long double prtmp = pr;
-		pr =  pr * x - pi * y + coefficients[i];
+		pr =  pr * x - pi * y + coefficients [i];
 		pi = prtmp * y + pi * x;
 	}
-	p -> re = (double) pr; p -> im = (double) pi;
+	return { (double) pr, (double) pi };
 }
 
 void structPolynomial :: v_evaluateTerms (double x, double terms[]) {
@@ -688,8 +685,8 @@ autoPolynomial Polynomial_scaleX (Polynomial me, double xmin, double xmax) {
 
 		double a = (my xmin - my xmax) / (xmin - xmax);
 		double b = my xmin - a * xmin;
-		thy coefficients[2] = my coefficients[2] * a;
-		thy coefficients[1] += my coefficients[2] * b;
+		thy coefficients [2] = my coefficients [2] * a;
+		thy coefficients [1] += my coefficients [2] * b;
 		if (my numberOfCoefficients == 2) {
 			return thee;
 		}
@@ -700,13 +697,15 @@ autoPolynomial Polynomial_scaleX (Polynomial me, double xmin, double xmax) {
 
 		// Start the recursion: P[1] = a x + b; P[0] = 1;
 
-		pnm1[2] = a; pnm1[1] = b; pnm2[1] = 1;
-		for (long n = 2; n <= my numberOfCoefficients - 1; n++) {
+		pnm1 [2] = a;
+		pnm1 [1] = b;
+		pnm2 [1] = 1;
+		for (long n = 2; n <= my numberOfCoefficients - 1; n ++) {
 			double *t1 = pnm1, *t2 = pnm2;
 			NUMpolynomial_recurrence (pn, n, a, b, 0, pnm1, pnm2);
 			if (my coefficients[n + 1] != 0) {
-				for (long j = 1; j <= n + 1; j++) {
-					thy coefficients[j] += my coefficients[n + 1] * pn[j];
+				for (long j = 1; j <= n + 1; j ++) {
+					thy coefficients [j] += my coefficients [n + 1] * pn [j];
 				}
 			}
 			pnm1 = pn;
@@ -723,22 +722,23 @@ double Polynomial_evaluate (Polynomial me, double x) {
 	return my v_evaluate (x);
 }
 
-void Polynomial_evaluate_z (Polynomial me, dcomplex *z, dcomplex *p) {
-	my v_evaluate_z (z, p);
+dcomplex Polynomial_evaluate_z (Polynomial me, dcomplex z) {
+	my v_evaluate_z (z);
 }
 
 static void Polynomial_evaluate_z_cart (Polynomial me, double r, double phi, double *re, double *im) {
 	double rn = 1;
 
-	*re = my coefficients[1]; *im = 0;
-	if (r == 0) {
+	*re = my coefficients [1];
+	*im = 0.0;
+	if (r == 0.0) {
 		return;
 	}
-	for (long i = 2; i <= my numberOfCoefficients; i++) {
+	for (long i = 2; i <= my numberOfCoefficients; i ++) {
 		rn *= r;
 		double arg = (i - 1) * phi;
-		*re += my coefficients[i] * rn * cos (arg);
-		*im += my coefficients[i] * rn * sin (arg);
+		*re += my coefficients [i] * rn * cos (arg);
+		*im += my coefficients [i] * rn * sin (arg);
 	}
 }
 
@@ -749,8 +749,8 @@ autoPolynomial Polynomial_getDerivative (Polynomial me) {
 			return Polynomial_create (my xmin, my xmax, 0);
 		}
 		autoPolynomial thee = Polynomial_create (my xmin, my xmax, my numberOfCoefficients - 2);
-		for (long i = 1; i <= thy numberOfCoefficients; i++) {
-			thy coefficients[i] = i * my coefficients[i + 1];
+		for (long i = 1; i <= thy numberOfCoefficients; i ++) {
+			thy coefficients [i] = i * my coefficients [i + 1];
 		}
 		return thee;
 	} catch (MelderError) {
@@ -761,8 +761,8 @@ autoPolynomial Polynomial_getDerivative (Polynomial me) {
 autoPolynomial Polynomial_getPrimitive (Polynomial me, double constant) {
 	try {
 		autoPolynomial thee = Polynomial_create (my xmin, my xmax, my numberOfCoefficients);
-		for (long i = 1; i <= my numberOfCoefficients; i++) {
-			thy coefficients[i + 1] = my coefficients[i] / i;
+		for (long i = 1; i <= my numberOfCoefficients; i ++) {
+			thy coefficients [i + 1] = my coefficients [i] / i;
 		}
 		thy coefficients [1] = constant;
 		return thee;
@@ -780,9 +780,9 @@ void Polynomial_initFromRealRoots (Polynomial me, double *roots, long numberOfRo
 		FunctionTerms_extendCapacityIf (me, numberOfRoots + 1);
 		double *c = & my coefficients [1];
 		long n = 1;
-		c [0] = - roots[1];
+		c [0] = - roots [1];
 		c [1] = 1.0;
-		for (long i = 2; i <= numberOfRoots; i++) {
+		for (long i = 2; i <= numberOfRoots; i ++) {
 			c [n + 1] = c [n];
 			for (long j = n; j >= 1; j --) {
 				c [j] = c [j - 1] - c [j] * roots [i];
@@ -827,7 +827,7 @@ void Polynomial_initFromProductOfSecondOrderTerms (Polynomial me, double *a, lon
 		for (long j = numberOfCoefficients; j > 2; j --) {
 			my coefficients [j] += a [i] * my coefficients [j - 1] + my coefficients [j - 2];
 		}
-		my coefficients [2] += a [i]; // a [i] * my coefficients [1]
+		my coefficients [2] += a [i];   // a [i] * my coefficients [1]
 		numberOfCoefficients += 2;
 	}
 	my numberOfCoefficients = numberOfCoefficients;
@@ -934,7 +934,7 @@ void Polynomials_divide (Polynomial me, Polynomial thee, autoPolynomial *q, auto
 		if (degree < 0) {
 			degree = 0;
 		}
-		while (degree > 1 && rc[degree] == 0) {
+		while (degree > 1 && rc [degree] == 0.0) {
 			degree--;
 		}
 		ar = Polynomial_create (my xmin, my xmax, degree);
@@ -949,7 +949,7 @@ void Polynomials_divide (Polynomial me, Polynomial thee, autoPolynomial *q, auto
 Thing_implement (LegendreSeries, FunctionTerms, 0);
 
 double structLegendreSeries :: v_evaluate (double x) {
-	double p = coefficients[1];
+	double p = coefficients [1];
 
 	// Transform x from domain [xmin, xmax] to domain [-1, 1]
 
@@ -960,7 +960,7 @@ double structLegendreSeries :: v_evaluate (double x) {
 	double pim1 = x = (2 * x - xmin - xmax) / (xmax - xmin);
 
 	if (numberOfCoefficients > 1) {
-		double pim2 = 1, twox = 2 * x, f2 = x, d = 1.0;
+		double pim2 = 1, twox = 2.0 * x, f2 = x, d = 1.0;
 		p += coefficients[2] * pim1;
 		for (long i = 3; i <= numberOfCoefficients; i++) {
 			double f1 = d++;
@@ -975,7 +975,7 @@ double structLegendreSeries :: v_evaluate (double x) {
 
 void structLegendreSeries :: v_evaluateTerms (double x, double terms[]) {
 	if (x < xmin || x > xmax) {
-		for (long i = 1; i <= numberOfCoefficients; i++) {
+		for (long i = 1; i <= numberOfCoefficients; i ++) {
 			terms[i] = undefined;
 		}
 		return;
@@ -987,12 +987,12 @@ void structLegendreSeries :: v_evaluateTerms (double x, double terms[]) {
 
 	terms[1] = 1;
 	if (numberOfCoefficients > 1) {
-		double twox = 2 * x, f2 = x, d = 1.0;
+		double twox = 2.0 * x, f2 = x, d = 1.0;
 		terms[2] = x;
-		for (long i = 3; i <= numberOfCoefficients; i++) {
-			double f1 = d++;
+		for (long i = 3; i <= numberOfCoefficients; i ++) {
+			double f1 = d ++;
 			f2 += twox;
-			terms[i] = (f2 * terms[i - 1] - f1 * terms[i - 2]) / d;
+			terms[i] = (f2 * terms [i - 1] - f1 * terms [i - 2]) / d;
 		}
 	}
 }
@@ -1031,12 +1031,12 @@ autoLegendreSeries LegendreSeries_getDerivative (LegendreSeries me) {
 	try {
 		autoLegendreSeries thee = LegendreSeries_create (my xmin, my xmax, my numberOfCoefficients - 1);
 
-		for (long n = 1; n <= my numberOfCoefficients - 1; n++) {
+		for (long n = 1; n <= my numberOfCoefficients - 1; n ++) {
 			// P[n]'(x) = Sum (k=0..nonNegative, (2n - 4k - 1) P[n-2k-1](x))
 
 			long n2 = n - 1;
-			for (long k = 0; n2 >= 0; k++, n2 -= 2) {
-				thy coefficients [n2 + 1] += (2 * n - 4 * k - 1) * my coefficients[n + 1];
+			for (long k = 0; n2 >= 0; k ++, n2 -= 2) {
+				thy coefficients [n2 + 1] += (2 * n - 4 * k - 1) * my coefficients [n + 1];
 			}
 		}
 		return thee;
@@ -1050,12 +1050,12 @@ autoPolynomial LegendreSeries_to_Polynomial (LegendreSeries me) {
 		double xmin = -1, xmax = 1;
 		autoPolynomial thee = Polynomial_create (xmin, xmax, my numberOfCoefficients - 1);
 
-		thy coefficients[1] = my coefficients[1]; /* * p[1] */
+		thy coefficients [1] = my coefficients [1];   /* * p[1] */
 		if (my numberOfCoefficients == 1) {
 			return thee;
 		}
 
-		thy coefficients[2] = my coefficients[2]; /* * p[2] */
+		thy coefficients [2] = my coefficients [2];   /* * p[2] */
 		if (my numberOfCoefficients > 2) {
 			autoNUMvector<double> buf (1, 3 * my numberOfCoefficients);
 
@@ -1065,15 +1065,16 @@ autoPolynomial LegendreSeries_to_Polynomial (LegendreSeries me) {
 
 			// Start the recursion: P[1] = x; P[0] = 1;
 
-			pnm1[2] = 1; pnm2[1] = 1;
-			for (long n = 2; n <= my numberOfCoefficients - 1; n++) {
+			pnm1 [2] = 1;
+			pnm2 [1] = 1;
+			for (long n = 2; n <= my numberOfCoefficients - 1; n ++) {
 				double a = (2 * n - 1.0) / n;
 				double c = - (n - 1.0) / n;
 				double *t1 = pnm1, *t2 = pnm2;
 				NUMpolynomial_recurrence (pn, n, a, 0, c, pnm1, pnm2);
-				if (my coefficients[n + 1] != 0) {
-					for (long j = 1; j <= n + 1; j++) {
-						thy coefficients[j] += my coefficients[n + 1] * pn[j];
+				if (my coefficients [n + 1] != 0) {
+					for (long j = 1; j <= n + 1; j ++) {
+						thy coefficients [j] += my coefficients [n + 1] * pn [j];
 					}
 				}
 				pnm1 = pn; pnm2 = t1; pn = t2;
@@ -1103,17 +1104,17 @@ autoRoots Roots_create (long numberOfRoots) {
 }
 
 void Roots_fixIntoUnitCircle (Roots me) {
-	dcomplex z10 = dcomplex_create (1, 0);
-	for (long i = my min; i <= my max; i++) {
-		if (dcomplex_abs (my v[i]) > 1.0) {
-			my v[i] = dcomplex_div (z10, dcomplex_conjugate (my v[i]));
+	dcomplex z10 { 1.0, 0.0 };
+	for (long i = my min; i <= my max; i ++) {
+		if (dcomplex_abs (my v [i]) > 1.0) {
+			my v [i] = dcomplex_div (z10, dcomplex_conjugate (my v [i]));
 		}
 	}
 }
 
 static void NUMdcvector_extrema_re (dcomplex v[], long lo, long hi, double *min, double *max) {
 	*min = *max = v[lo].re;
-	for (long i = lo + 1; i <= hi; i++) {
+	for (long i = lo + 1; i <= hi; i ++) {
 		if (v[i].re < *min) {
 			*min = v[i].re;
 		} else if (v[i].re > *max) {
@@ -1124,11 +1125,11 @@ static void NUMdcvector_extrema_re (dcomplex v[], long lo, long hi, double *min,
 
 static void NUMdcvector_extrema_im (dcomplex v[], long lo, long hi, double *min, double *max) {
 	*min = *max = v[lo].im;
-	for (long i = lo + 1; i <= hi; i++) {
-		if (v[i].im < *min) {
-			*min = v[i].im;
-		} else if (v[i].im > *max) {
-			*max = v[i].im;
+	for (long i = lo + 1; i <= hi; i ++) {
+		if (v [i]. im < *min) {
+			*min = v [i]. im;
+		} else if (v [i]. im > *max) {
+			*max = v [i]. im;
 		}
 	}
 }
@@ -1138,14 +1139,15 @@ void Roots_draw (Roots me, Graphics g, double rmin, double rmax, double imin, do
 	double eps = 1e-6;
 
 	if (rmax <= rmin) {
-		NUMdcvector_extrema_re (my v, 1, my max, &rmin, &rmax);
+		NUMdcvector_extrema_re (my v, 1, my max, & rmin, & rmax);
 	}
 	double denum = fabs (rmax) > fabs (rmin) ? fabs (rmax) : fabs (rmin);
 	if (denum == 0) {
 		denum = 1;
 	}
 	if (fabs ( (rmax - rmin) / denum) < eps) {
-		rmin -= 1; rmax += 1;
+		rmin -= 1;
+		rmax += 1;
 	}
 	if (imax <= imin) {
 		NUMdcvector_extrema_im (my v, 1, my max, &imin, &imax);
@@ -1155,7 +1157,8 @@ void Roots_draw (Roots me, Graphics g, double rmin, double rmax, double imin, do
 		denum = 1;
 	}
 	if (fabs ( (imax - imin) / denum) < eps) {
-		imin -= 1; imax += 1;
+		imin -= 1;
+		imax += 1;
 	}
 	Graphics_setInner (g);
 	Graphics_setWindow (g, rmin, rmax, imin, imax);
@@ -1171,10 +1174,10 @@ void Roots_draw (Roots me, Graphics g, double rmin, double rmax, double imin, do
 	Graphics_unsetInner (g);
 	if (garnish) {
 		Graphics_drawInnerBox (g);
-		if (rmin * rmax < 0) {
+		if (rmin * rmax < 0.0) {
 			Graphics_markLeft (g, 0.0, true, true, true, U"0");
 		}
-		if (imin * imax < 0) {
+		if (imin * imax < 0.0) {
 			Graphics_markBottom (g, 0.0, true, true, true, U"0");
 		}
 		Graphics_marksLeft (g, 2, true, true, false);
@@ -1196,8 +1199,8 @@ autoRoots Polynomial_to_Roots (Polynomial me) {
 		// parts of eigenvalues wr[1..n] and wi[1..n].
 
 		autoNUMvector<double> hes (1, n2 + n + n);
-		double *wr = &hes[n2];
-		double *wi = &hes[n2 + n];
+		double *wr = & hes [n2];
+		double *wi = & hes [n2 + n];
 
 		// Fill the upper Hessenberg matrix (storage is Fortran)
 		// C: [i][j] -> Fortran: (j-1)*n + i
@@ -1213,7 +1216,7 @@ autoRoots Polynomial_to_Roots (Polynomial me) {
 
 		char job = 'E', compz = 'N';
 		long ilo = 1, ihi = n, ldh = n, ldz = n, lwork = -1, info;
-		double *z = 0, wt[1];
+		double *z = 0, wt [1];
 		NUMlapack_dhseqr (&job, &compz, &n, &ilo, &ihi, &hes[1], &ldh, &wr[1], &wi[1], z, &ldz, wt, &lwork, &info);
 		if (info != 0) {
 			if (info < 0) {
diff --git a/dwtools/Polynomial.h b/dwtools/Polynomial.h
index 22d879d..984922a 100644
--- a/dwtools/Polynomial.h
+++ b/dwtools/Polynomial.h
@@ -87,7 +87,7 @@ Thing_define (Polynomial, FunctionTerms) {
 	// overridden methods:
 	public:
 		virtual double v_evaluate (double x);
-		virtual void v_evaluate_z (dcomplex *z, dcomplex *p);
+		virtual dcomplex v_evaluate_z (dcomplex z);
 		virtual void v_evaluateTerms (double x, double terms[]);
 		virtual void v_getExtrema (double x1, double x2, double *xmin, double *ymin, double *xmax, double *ymax);
 		//virtual long v_getDegree ();   David, is het OK dat deze niet overschreven wordt? Ja
@@ -103,7 +103,7 @@ void Polynomial_scaleCoefficients_monic (Polynomial me);
 autoPolynomial Polynomial_scaleX (Polynomial me, double xmin, double xmax);
 /* x' = (x-location) / scale */
 
-void Polynomial_evaluate_z (Polynomial me, dcomplex *z, dcomplex *p);
+dcomplex Polynomial_evaluate_z (Polynomial me, dcomplex z);
 /* Evaluate at complex z = x + iy */
 
 
diff --git a/dwtools/Polynomial_def.h b/dwtools/Polynomial_def.h
index 3609185..f940be2 100644
--- a/dwtools/Polynomial_def.h
+++ b/dwtools/Polynomial_def.h
@@ -38,7 +38,7 @@ oo_DEFINE_CLASS (FunctionTerms, Function)
 	#if oo_DECLARING
 		// new methods:
 			virtual double v_evaluate (double x);
-			virtual void v_evaluate_z (dcomplex *z, dcomplex *p);
+			virtual dcomplex v_evaluate_z (dcomplex z);
 			virtual void v_evaluateTerms (double x, double terms[]);
 			virtual void v_getExtrema (double x1, double x2, double *xmin, double *ymin, double *xmax, double *ymax);
 			virtual long v_getDegree ();
diff --git a/dwtools/SSCP.cpp b/dwtools/SSCP.cpp
index 7e6385b..f0ba2e7 100644
--- a/dwtools/SSCP.cpp
+++ b/dwtools/SSCP.cpp
@@ -516,8 +516,8 @@ autoTableOfReal Covariance_to_TableOfReal_randomSampling (Covariance me, long nu
 autoSSCP TableOfReal_to_SSCP (TableOfReal me, long rowb, long rowe, long colb, long cole) {
 	try {
 		
-		if (! NUMdmatrix_hasFiniteElements(my data, 1, my numberOfRows, 1, my numberOfColumns)) {
-			Melder_throw (U"At least one of the table's elements is not finite or undefined.");
+		if (NUMdmatrix_containsUndefinedElements (my data, 1, my numberOfRows, 1, my numberOfColumns)) {
+			Melder_throw (U"At least one of the table's elements is undefined.");
 		}
 
 		if (rowb == 0 && rowe == 0) {
diff --git a/dwtools/Sound_and_MixingMatrix.cpp b/dwtools/Sound_and_MixingMatrix.cpp
index 80cf87f..51f1fd7 100644
--- a/dwtools/Sound_and_MixingMatrix.cpp
+++ b/dwtools/Sound_and_MixingMatrix.cpp
@@ -130,8 +130,11 @@ autoSound Sound_and_MixingMatrix_unmix (Sound me, MixingMatrix thee) {
 	}
 }
 
+#if 0
+// TODO
 void LongSound_and_MixingMatrix_playPart (LongSound me, MixingMatrix thee, double fromTime, double toTime, Sound_PlayCallback callback, Thing boss) {
 	
 }
+#endif
 
 /* End of file Sound_and_MixingMatrix.cpp */
diff --git a/dwtools/Sound_and_Spectrogram_extensions.cpp b/dwtools/Sound_and_Spectrogram_extensions.cpp
index 2aa23a2..3a9e258 100644
--- a/dwtools/Sound_and_Spectrogram_extensions.cpp
+++ b/dwtools/Sound_and_Spectrogram_extensions.cpp
@@ -138,7 +138,8 @@ autoBarkSpectrogram Sound_to_BarkSpectrogram (Sound me, double analysisWidth, do
 			Melder_throw (U"The combination of filter parameters is not valid.");
 		}
 
-		long numberOfFrames; double t1;
+		integer numberOfFrames;
+		double t1;
 		Sampled_shortTermAnalysis (me, windowDuration, dt, & numberOfFrames, & t1);
 		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
 		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
@@ -146,7 +147,7 @@ autoBarkSpectrogram Sound_to_BarkSpectrogram (Sound me, double analysisWidth, do
 
 		autoMelderProgress progess (U"BarkSpectrogram analysis");
 
-		for (long iframe = 1; iframe <= numberOfFrames; iframe++) {
+		for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
 			double t = Sampled_indexToX (thee.get(), iframe);
 
 			Sound_into_Sound (me, sframe.get(), t - windowDuration / 2.0);
@@ -191,11 +192,10 @@ static void Sound_into_MelSpectrogram_frame (Sound me, MelSpectrogram thee, long
 
 autoMelSpectrogram Sound_to_MelSpectrogram (Sound me, double analysisWidth, double dt, double f1_mel, double fmax_mel, double df_mel) {
 	try {
-		double t1, samplingFrequency = 1.0 / my dx, nyquist = 0.5 * samplingFrequency;
-		double windowDuration = 2.0 * analysisWidth;   // gaussian window
+		double samplingFrequency = 1.0 / my dx, nyquist = 0.5 * samplingFrequency;
+		double windowDuration = 2.0 * analysisWidth;   // Gaussian window
 		double fmin_mel = 0.0;
 		double fbottom = NUMhertzToMel2 (100.0), fceiling = NUMhertzToMel2 (nyquist);
-		long numberOfFrames;
 
 		// Check defaults.
 
@@ -214,17 +214,19 @@ autoMelSpectrogram Sound_to_MelSpectrogram (Sound me, double analysisWidth, doub
 
 		// Determine the number of filters.
 
-		long numberOfFilters = lround ((fmax_mel - f1_mel) / df_mel);
+		integer numberOfFilters = lround ((fmax_mel - f1_mel) / df_mel);
 		fmax_mel = f1_mel + numberOfFilters * df_mel;
 
-		Sampled_shortTermAnalysis (me, windowDuration, dt, &numberOfFrames, &t1);
+		integer numberOfFrames;
+		double t1;
+		Sampled_shortTermAnalysis (me, windowDuration, dt, & numberOfFrames, & t1);
 		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
 		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
 		autoMelSpectrogram thee = MelSpectrogram_create (my xmin, my xmax, numberOfFrames, dt, t1, fmin_mel, fmax_mel, numberOfFilters, df_mel, f1_mel);
 
 		autoMelderProgress progress (U"MelSpectrograms analysis");
 
-		for (long iframe = 1; iframe <= numberOfFrames; iframe++) {
+		for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
 			double t = Sampled_indexToX (thee.get(), iframe);
 			Sound_into_Sound (me, sframe.get(), t - windowDuration / 2.0);
 			Sounds_multiply (sframe.get(), window.get());
@@ -296,12 +298,12 @@ autoSpectrogram Sound_and_Pitch_to_Spectrogram (Sound me, Pitch thee, double ana
 	try {
 		double t1, windowDuration = 2.0 * analysisWidth; /* gaussian window */
 		double nyquist = 0.5 / my dx, samplingFrequency = 2.0 * nyquist, fmin_hz = 0.0;
-		long numberOfFrames, f0_undefined = 0.0;
+		integer numberOfFrames, numberOfUndefinedPitchFrames = 0;
 
 		if (my xmin > thy xmin || my xmax > thy xmax) Melder_throw
 			(U"The domain of the Sound is not included in the domain of the Pitch.");
 
-		double f0_median = Pitch_getQuantile (thee, thy xmin, thy xmax, 0.5, kPitch_unit_HERTZ);
+		double f0_median = Pitch_getQuantile (thee, thy xmin, thy xmax, 0.5, kPitch_unit::HERTZ);
 
 		if (isundef (f0_median) || f0_median == 0.0) {
 			f0_median = 100.0;
@@ -324,7 +326,7 @@ autoSpectrogram Sound_and_Pitch_to_Spectrogram (Sound me, Pitch thee, double ana
 		fmax_hz = MIN (fmax_hz, nyquist);
 		long numberOfFilters = lround ( (fmax_hz - f1_hz) / df_hz);
 
-		Sampled_shortTermAnalysis (me, windowDuration, dt, &numberOfFrames, &t1);
+		Sampled_shortTermAnalysis (me, windowDuration, dt, & numberOfFrames, & t1);
 		autoSpectrogram him = Spectrogram_create (my xmin, my xmax, numberOfFrames, dt, t1, fmin_hz, fmax_hz, numberOfFilters, df_hz, f1_hz);
 
 		// Temporary objects
@@ -334,10 +336,10 @@ autoSpectrogram Sound_and_Pitch_to_Spectrogram (Sound me, Pitch thee, double ana
 		autoMelderProgress progress (U"Sound & Pitch: To FormantFilter");
 		for (long iframe = 1; iframe <= numberOfFrames; iframe++) {
 			double t = Sampled_indexToX (him.get(), iframe);
-			double b, f0 = Pitch_getValueAtTime (thee, t, kPitch_unit_HERTZ, 0);
+			double b, f0 = Pitch_getValueAtTime (thee, t, kPitch_unit::HERTZ, 0);
 
 			if (isundef (f0) || f0 == 0.0) {
-				f0_undefined ++;
+				numberOfUndefinedPitchFrames ++;
 				f0 = f0_median;
 			}
 			b = relative_bw * f0;
diff --git a/dwtools/Sound_extensions.cpp b/dwtools/Sound_extensions.cpp
index 7d9ca20..84ae2af 100644
--- a/dwtools/Sound_extensions.cpp
+++ b/dwtools/Sound_extensions.cpp
@@ -323,7 +323,7 @@ autoSound Sound_readFromRawFile (MelderFile file, const char *format, int nBitsC
 		if (skipNBytes <= 0) {
 			skipNBytes = 0;
 		}
-		long nSamples = (MelderFile_length (file) - skipNBytes) / nBytesPerSample;
+		integer nSamples = (MelderFile_length (file) - skipNBytes) / nBytesPerSample;
 		if (nSamples < 1) {
 			Melder_throw (U"No samples left to read");
 		}
@@ -478,7 +478,7 @@ autoSound Sound_readFromDialogicADPCMFile (MelderFile file, double sampleRate) {
 	try {
 		autofile f = Melder_fopen (file, "rb");
 
-		long filelength = MelderFile_length (file);
+		integer filelength = MelderFile_length (file);
 		if (filelength <= 0) {
 			Melder_throw (U"File is empty.");
 		}
@@ -639,7 +639,7 @@ autoSound Sound_createMistunedHarmonicComplex (double minimumTime, double maximu
 	and so: f = f0 + c /(2 pi t)
 	Irino: bandwidth = (frequency * (6.23e-6 * frequency + 93.39e-3) + 28.52)
 */
-autoSound Sound_createGammaTone (double minimumTime, double maximumTime, double samplingFrequency, long gamma, double frequency, double bandwidth, double initialPhase, double addition, int scaleAmplitudes) {
+autoSound Sound_createGammaTone (double minimumTime, double maximumTime, double samplingFrequency, double gamma, double frequency, double bandwidth, double initialPhase, double addition, int scaleAmplitudes) {
 	try {
 		autoSound me = Sound_create2 (minimumTime, maximumTime, samplingFrequency);
 		for (long i = 1; i <= my nx; i++) {
@@ -659,6 +659,8 @@ autoSound Sound_createGammaTone (double minimumTime, double maximumTime, double
 	}
 }
 
+#if 0
+// This routine is unstable for small values of f and large b. Better to use cross-correlation of gammatone with sound.
 static void NUMgammatoneFilter4 (double *x, double *y, long n, double centre_frequency, double bandwidth, double samplingFrequency) {
 	double a[5], b[9], dt = 1.0 / samplingFrequency, wt = NUMpi * centre_frequency * dt;
 	double bt = 2 * NUMpi * bandwidth * dt, dt2 = dt * dt, dt4 = dt2 * dt2;
@@ -793,7 +795,7 @@ static void NUMgammatoneFilter4 (double *x, double *y, long n, double centre_fre
 		       - b[5] * y[i - 5] - b[6] * y[i - 6] - b[7] * y[i - 7] - b[8] * y[i - 8];
 	}
 }
-#if 0
+
 autoSound Sound_filterByGammaToneFilter4 (Sound me, double centre_frequency, double bandwidth) {
 	try {
 		if (centre_frequency <= 0) {
@@ -828,14 +830,14 @@ autoSound Sound_filterByGammaToneFilter4 (Sound me, double centre_frequency, dou
 
 
 autoSound Sound_filterByGammaToneFilter4 (Sound me, double centre_frequency, double bandwidth) {
-	return Sound_filterByGammaToneFilter (me, centre_frequency, bandwidth, 4, 0.0);
+	return Sound_filterByGammaToneFilter (me, centre_frequency, bandwidth, 4.0, 0.0);
 }
 
-autoSound Sound_filterByGammaToneFilter (Sound me, double centre_frequency, double bandwidth, long gamma, double initialPhase) {
+autoSound Sound_filterByGammaToneFilter (Sound me, double centre_frequency, double bandwidth, double gamma, double initialPhase) {
 	try {
 		autoSound gammaTone = Sound_createGammaTone (my xmin, my xmax, 1.0 / my dx, gamma, centre_frequency, bandwidth, initialPhase, 0.0, 0);
 		// kSounds_convolve_scaling_INTEGRAL, SUM, NORMALIZE, PEAK_099
-		autoSound thee = Sounds_convolve (me, gammaTone.get(), kSounds_convolve_scaling_INTEGRAL, kSounds_convolve_signalOutsideTimeDomain_ZERO);
+		autoSound thee = Sounds_convolve (me, gammaTone.get(), kSounds_convolve_scaling::INTEGRAL, kSounds_convolve_signalOutsideTimeDomain::ZERO);
 		
 		double response_re, response_im;
 		gammaToneFilterResponseAtResonance (centre_frequency, bandwidth, gamma, initialPhase, my xmax - my xmin, & response_re, & response_im);
@@ -1227,7 +1229,7 @@ void Sound_overwritePart (Sound me, double t1, double t2, Sound thee, double t3)
 
 void Sound_filter_part_formula (Sound me, double t1, double t2, const char32 *formula, Interpreter interpreter) {
 	try {
-		autoSound part = Sound_extractPart (me, t1, t2, kSound_windowShape_RECTANGULAR, 1, 1);
+		autoSound part = Sound_extractPart (me, t1, t2, kSound_windowShape::RECTANGULAR, 1, 1);
 		autoSpectrum spec = Sound_to_Spectrum (part.get(), true);
 		Matrix_formula ( (Matrix) spec.get(), formula, interpreter, 0);
 		autoSound filtered = Spectrum_to_Sound (spec.get());
@@ -1294,7 +1296,7 @@ autoSound Sound_and_Pitch_changeSpeaker (Sound me, Pitch him, double formantMult
 		autoPointProcess pulses = Sound_Pitch_to_PointProcess_cc (sound.get(), pitch.get());
 		autoPitchTier pitchTier = Pitch_to_PitchTier (pitch.get());
 
-		double median = Pitch_getQuantile (pitch.get(), 0.0, 0.0, 0.5, kPitch_unit_HERTZ);
+		double median = Pitch_getQuantile (pitch.get(), 0.0, 0.0, 0.5, kPitch_unit::HERTZ);
 		if (isdefined (median) && median != 0.0) {
 			/* Incorporate pitch shift from overriding the sampling frequency */
 			PitchTier_multiplyFrequencies (pitchTier.get(), sound -> xmin, sound -> xmax, pitchMultiplier / formantMultiplier);
@@ -1556,7 +1558,7 @@ autoSound Sound_and_Pitch_changeGender_old (Sound me, Pitch him, double formantR
 		autoPointProcess pulses = Sound_Pitch_to_PointProcess_cc (sound.get(), pitch.get());
 		autoPitchTier pitchTier = Pitch_to_PitchTier (pitch.get());
 
-		double median = Pitch_getQuantile (pitch.get(), 0, 0, 0.5, kPitch_unit_HERTZ);
+		double median = Pitch_getQuantile (pitch.get(), 0, 0, 0.5, kPitch_unit::HERTZ);
 		if (isdefined (median) && median != 0.0) {
 			// Incorporate pitch shift from overriding the sampling frequency
 			if (new_pitch == 0.0) {
@@ -1572,7 +1574,7 @@ autoSound Sound_and_Pitch_changeGender_old (Sound me, Pitch him, double formantR
 		RealTier_addPoint (duration.get(), (my xmin + my xmax) / 2, formantRatio * durationFactor);
 
 		autoSound thee = Sound_Point_Pitch_Duration_to_Sound (sound.get(), pulses.get(), pitchTier.get(),
-		                 duration.get(), 1.25 / Pitch_getMinimum (pitch.get(), 0.0, 0.0, kPitch_unit_HERTZ, false));
+		                 duration.get(), 1.25 / Pitch_getMinimum (pitch.get(), 0.0, 0.0, kPitch_unit::HERTZ, false));
 
 		// Resample to the original sampling frequency
 
@@ -1898,7 +1900,7 @@ static void Sound_findIntermediatePoint_bs (Sound me, long ichannel, long isampl
 		Formula_Result result;
 		Formula_compile (interpreter, thee.get(), formula, kFormula_EXPRESSION_TYPE_NUMERIC, true);
 		Formula_run (ichannel, 2, & result);
-		bool current = (result.result.numericResult != 0.0);
+		bool current = (result. numericResult != 0.0);
 
 		dx /= 2.0;
 		if (left == current) {
@@ -1945,7 +1947,7 @@ void Sound_drawWhere (Sound me, Graphics g, double tmin, double tmax, double min
 		if (str32str (method, U"bars") || str32str (method, U"Bars")) {
 			for (long ix = ixmin; ix <= ixmax; ix ++) {
 				Formula_run (channel, ix, & result);
-				if (result.result.numericResult != 0.0) {
+				if (result. numericResult != 0.0) {
 					double x = Sampled_indexToX (me, ix);
 					double y = my z [channel] [ix];
 					double left = x - 0.5 * my dx, right = x + 0.5 * my dx;
@@ -1966,7 +1968,7 @@ void Sound_drawWhere (Sound me, Graphics g, double tmin, double tmax, double min
 		} else if (str32str (method, U"poles") || str32str (method, U"Poles")) {
 			for (long ix = ixmin; ix <= ixmax; ix ++) {
 				Formula_run (channel, ix, & result);
-				if (result.result.numericResult != 0.0) {
+				if (result. numericResult != 0.0) {
 					double x = Sampled_indexToX (me, ix);
 					double y = my z[channel][ix];
 					if (y > maximum) {
@@ -1981,7 +1983,7 @@ void Sound_drawWhere (Sound me, Graphics g, double tmin, double tmax, double min
 		} else if (str32str (method, U"speckles") || str32str (method, U"Speckles")) {
 			for (long ix = ixmin; ix <= ixmax; ix ++) {
 				Formula_run (channel, ix, & result);
-				if (result.result.numericResult != 0.0) {
+				if (result. numericResult != 0.0) {
 					double x = Sampled_indexToX (me, ix);
 					Graphics_speckle (g, x, my z [channel] [ix]);
 				}
@@ -1994,7 +1996,7 @@ void Sound_drawWhere (Sound me, Graphics g, double tmin, double tmax, double min
 			double xb = Sampled_indexToX (me, ixmin), yb = my z[channel][ixmin], xe, ye;
 			for (long ix = ixmin; ix <= ixmax; ix++) {
 				Formula_run (channel, ix, & result);
-				current = (result.result.numericResult != 0.0 ); // true means draw
+				current = (result. numericResult != 0.0 ); // true means draw
 				if (previous && not current) { // leaving drawing segment
 					if (ix != ixmin) {
 						if (ix - istart > 1) {
@@ -2057,7 +2059,7 @@ void Sound_paintWhere (Sound me, Graphics g, Graphics_Colour colour, double tmin
 			long ix = ixmin;
 			do {
 				Formula_run (channel, ix, & result);
-				current = ( result.result.numericResult != 0.0 );
+				current = ( result. numericResult != 0.0 );
 				if (ix == ixmin) {
 					previous = current;
 				}
@@ -2164,13 +2166,16 @@ static autoSound Sound_removeNoiseBySpectralSubtraction_mono (Sound me, Sound no
 		autoSound analysisWindow = Sound_createSimple (1, windowLength, samplingFrequency);
 		long windowSamples = analysisWindow -> nx;
 		autoSound noise_copy = Data_copy (noise);
-		Sound_multiplyByWindow (noise_copy.get(), kSound_windowShape_HANNING);
+		Sound_multiplyByWindow (noise_copy.get(), kSound_windowShape::HANNING);
 		double bandwidth = samplingFrequency / windowSamples;
 		autoLtas noiseLtas = Sound_to_Ltas (noise_copy.get(), bandwidth);
 		autoNUMvector<double> noiseAmplitudes (1, noiseLtas -> nx);
 		for (long i = 1; i <= noiseLtas -> nx; i++) {
 			noiseAmplitudes[i] = pow (10.0, (noiseLtas -> z[1][i] - 94) / 20);
 		}
+		
+		autoMelderProgress progress (U"Remove noise");
+		
 		long stepSizeSamples = windowSamples / 4;
 		long numberOfSteps = my nx / stepSizeSamples;
 		for (long istep = 1; istep <= numberOfSteps; istep++) {
@@ -2198,10 +2203,13 @@ static autoSound Sound_removeNoiseBySpectralSubtraction_mono (Sound me, Sound no
 				x[i] *= factor; y[i] *= factor;
 			}
 			autoSound suppressed = Spectrum_to_Sound (analysisSpectrum.get());
-			Sound_multiplyByWindow (suppressed.get(), kSound_windowShape_HANNING);
+			Sound_multiplyByWindow (suppressed.get(), kSound_windowShape::HANNING);
 			for (long j = 1; j <= nsamples; j++) {
 				denoised -> z[1][istart - 1 + j] += 0.5 * suppressed -> z[1][j]; // 0.5 because of 2-fold oversampling
 			}
+			if ((istep % 10) == 1) {
+				Melder_progress ( (double) istep / numberOfSteps, U"Remove noise: frame ", istep, U" out of ", numberOfSteps, U".");
+			}
 		}
 		return denoised;
 	} catch (MelderError) {
@@ -2241,9 +2249,9 @@ autoSound Sound_removeNoise (Sound me, double noiseStart, double noiseEnd, doubl
 		for (long ichannel = 1; ichannel <= my ny; ichannel++) {
 			autoSound denoisedi, channeli = Sound_extractChannel (filtered.get(), ichannel);
 			if (findNoise) {
-				Sound_findNoise (channeli.get(), minimumNoiseDuration, &noiseStart, &noiseEnd);
+				Sound_findNoise (channeli.get(), minimumNoiseDuration, & noiseStart, & noiseEnd);
 			}
-			autoSound noise = Sound_extractPart (channeli.get(), noiseStart, noiseEnd, kSound_windowShape_RECTANGULAR, 1.0, false);
+			autoSound noise = Sound_extractPart (channeli.get(), noiseStart, noiseEnd, kSound_windowShape::RECTANGULAR, 1.0, false);
 			if (method == 1) { // spectral subtraction
 				denoisedi = Sound_removeNoiseBySpectralSubtraction_mono (filtered.get(), noise.get(), windowLength);
 			}
diff --git a/dwtools/Sound_extensions.h b/dwtools/Sound_extensions.h
index 261d980..afa7894 100644
--- a/dwtools/Sound_extensions.h
+++ b/dwtools/Sound_extensions.h
@@ -2,7 +2,7 @@
 #define _Sound_extensions_h_
 /* Sound_extensions.h
  *
- * Copyright (C) 1993-2012,2015-2016 David Weenink
+ * Copyright (C) 1993-2012,2015-2017 David Weenink
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -76,7 +76,7 @@ autoSound Sound_createMistunedHarmonicComplex (double minimumTime, double maximu
 	double firstFrequency, long numberOfComponents, long mistunedComponent, double mistuningFraction, int scaleAmplitudes);
 
 autoSound Sound_createGammaTone (double minimumTime, double maximumTime, double samplingFrequency,
-	long gamma, double frequency, double bandwidth, double initialPhase, double addition, int scaleAmplitudes);
+	double gamma, double frequency, double bandwidth, double initialPhase, double addition, int scaleAmplitudes);
 
 autoSound Sound_createShepardTone (double minimumTime, double maximumTime, double samplingFrequency,
 	double lowestFrequency, long numberOfComponents, double frequencyChange, double amplitudeRange);
@@ -96,7 +96,7 @@ autoSound Sound_createFromWindowFunction (double effectiveTime, double samplingF
 
 autoSound Sound_filterByGammaToneFilter4 (Sound me, double centre_frequency, double bandwidth);
 
-autoSound Sound_filterByGammaToneFilter (Sound me, double centre_frequency, double bandwidth, long gamma, double initialPhase);
+autoSound Sound_filterByGammaToneFilter (Sound me, double centre_frequency, double bandwidth, double gamma, double initialPhase);
 
 void Sounds_multiply (Sound me, Sound thee);
 /* precondition: my nx = thy nx */
diff --git a/dwtools/Sound_to_Pitch2.cpp b/dwtools/Sound_to_Pitch2.cpp
index 507b7b0..5a75e4c 100644
--- a/dwtools/Sound_to_Pitch2.cpp
+++ b/dwtools/Sound_to_Pitch2.cpp
@@ -76,28 +76,33 @@ autoPitch Sound_to_Pitch_shs (Sound me, double timeStep, double minimumPitch,
 		double firstTime, newSamplingFrequency = 2 * maximumFrequency;
 		double windowDuration = 2 / minimumPitch, halfWindow = windowDuration / 2;
 		double atans = nPointsPerOctave * NUMlog2 (65.0 / 50.0) - 1;
-		// Number of speech samples in the downsampled signal in each frame:
-		// 100 for windowDuration == 0.04 and newSamplingFrequency == 2500
+		/*
+			Number of speech samples in the downsampled signal in each frame:
+			100 for windowDuration == 0.04 and newSamplingFrequency == 2500
+		*/
 		long nx = lround (windowDuration * newSamplingFrequency);
 
-		// The minimum number of points for the fft is 256.
-		long nfft = 1;
-		while ( (nfft *= 2) < nx || nfft <= 128) {
+		/*
+			The minimum number of points for the FFT is 256.
+		*/
+		integer nfft = 1;
+		while ((nfft *= 2) < nx || nfft <= 128) {
 			;
 		}
-		long nfft2 = nfft / 2 + 1;
+		integer nfft2 = nfft / 2 + 1;
 		double frameDuration = nfft / newSamplingFrequency;
 		double df = newSamplingFrequency / nfft;
 
-		// The number of points on the octave scale
-
+		/*
+			The number of points on the octave scale.
+		*/
 		double fminl2 = NUMlog2 (minimumPitch), fmaxl2 = NUMlog2 (maximumFrequency);
 		long nFrequencyPoints = (long) floor ((fmaxl2 - fminl2) * nPointsPerOctave);
 		double dfl2 = (fmaxl2 - fminl2) / (nFrequencyPoints - 1);
 
 		autoSound sound = Sound_resample (me, newSamplingFrequency, 50);
-		long numberOfFrames;
-		Sampled_shortTermAnalysis (sound.get(), windowDuration, timeStep, &numberOfFrames, &firstTime);
+		integer numberOfFrames;
+		Sampled_shortTermAnalysis (sound.get(), windowDuration, timeStep, & numberOfFrames, & firstTime);
 		autoSound frame = Sound_createSimple (1, frameDuration, newSamplingFrequency);
 		autoSound hamming = Sound_createHamming (nx / newSamplingFrequency, newSamplingFrequency);
 		autoPitch thee = Pitch_create (my xmin, my xmax, numberOfFrames, timeStep, firstTime, ceiling, maxnCandidates);
diff --git a/dwtools/Sound_to_SPINET.cpp b/dwtools/Sound_to_SPINET.cpp
index ff2525f..83348c7 100644
--- a/dwtools/Sound_to_SPINET.cpp
+++ b/dwtools/Sound_to_SPINET.cpp
@@ -25,11 +25,11 @@
 #include "NUM2.h"
 
 static double fgamma (double x, long n) {
-	double x2p1 = 1 + x * x, d = x2p1;
-	for (long i = 2; i <= n; i++) {
+	double x2p1 = 1.0 + x * x, d = x2p1;
+	for (long i = 2; i <= n; i ++) {
 		d *= x2p1;
 	}
-	return 1 / d;
+	return 1.0 / d;
 }
 
 /*
@@ -39,17 +39,18 @@ static double fgamma (double x, long n) {
 
 autoSPINET Sound_to_SPINET (Sound me, double timeStep, double windowDuration, double minimumFrequencyHz, double maximumFrequencyHz, long nFilters, double excitationErbProportion, double inhibitionErbProportion) {
 	try {
-		double firstTime, b = 1.02, samplingFrequency = 1 / my dx;
+		const double b = 1.02, samplingFrequency = 1.0 / my dx;
 
 		if (timeStep < my dx) {
 			timeStep = my dx;
 		}
-		if (maximumFrequencyHz > samplingFrequency / 2) {
-			maximumFrequencyHz = samplingFrequency / 2;
+		if (maximumFrequencyHz > samplingFrequency / 2.0) {
+			maximumFrequencyHz = samplingFrequency / 2.0;
 		}
 
-		long numberOfFrames;
-		Sampled_shortTermAnalysis (me, windowDuration, timeStep, &numberOfFrames, &firstTime);
+		integer numberOfFrames;
+		double firstTime;
+		Sampled_shortTermAnalysis (me, windowDuration, timeStep, & numberOfFrames, & firstTime);
 		autoSPINET thee = SPINET_create (my xmin, my xmax, numberOfFrames, timeStep, firstTime, minimumFrequencyHz, maximumFrequencyHz, nFilters, excitationErbProportion, inhibitionErbProportion);
 		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
 		autoSound frame = Sound_createSimple (1, windowDuration, samplingFrequency);
@@ -58,39 +59,42 @@ autoSPINET Sound_to_SPINET (Sound me, double timeStep, double windowDuration, do
 		autoNUMvector<double> aex (1, nFilters);
 		autoNUMvector<double> ain (1, nFilters);
 
-		// Cochlear filterbank: gammatone
-
-		for (long i = 1; i <= nFilters; i++) {
-			f[i] = NUMerbToHertz (thy y1 + (i - 1) * thy dy);
-			bw[i] = 2 * NUMpi * b * (f[i] * (6.23e-6 * f[i] + 93.39e-3) + 28.52);
+		/*
+			Cochlear filterbank: gammatone.
+		*/
+		for (integer i = 1; i <= nFilters; i ++) {
+			f [i] = NUMerbToHertz (thy y1 + (i - 1) * thy dy);
+			bw [i] = 2.0 * NUMpi * b * (f [i] * (6.23e-6 * f [i] + 93.39e-3) + 28.52);
 		}
 
 		autoMelderProgress progress (U"SPINET analysis");
 
-		for (long i = 1; i <= nFilters; i++) {
-			double bb = (f[i] / 1000) * exp (- f[i] / 1000); // outer & middle ear and phase locking
-			double tgammaMax = (thy gamma - 1) / bw[i]; // Time where gammafunction envelope has maximum
-			double gammaMaxAmplitude = pow ( (thy gamma - 1) / (NUMe * bw[i]), (thy gamma - 1)); // tgammaMax
-			double timeCorrection = tgammaMax - windowDuration / 2;
-
-			autoSound gammaTone = Sound_createGammaTone (0, 0.1, samplingFrequency, thy gamma, b, f[i], 0, 0, 0);
-			autoSound filtered = Sounds_convolve (me, gammaTone.get(), kSounds_convolve_scaling_SUM, kSounds_convolve_signalOutsideTimeDomain_ZERO);
+		for (integer i = 1; i <= nFilters; i ++) {
+			double bb = (f [i] / 1000.0) * exp (- f [i] / 1000.0);   // outer & middle ear and phase locking
+			double tgammaMax = (thy gamma - 1) / bw [i];   // the time where the gamma function envelope has its maximum
+			double gammaMaxAmplitude = pow ((thy gamma - 1) / (NUMe * bw [i]), thy gamma - 1);   // tgammaMax
+			double timeCorrection = tgammaMax - windowDuration / 2.0;
 
-			// To energy measure: weigh with broad-band transfer function
+			autoSound gammaTone = Sound_createGammaTone (0.0, 0.1, samplingFrequency, thy gamma, b, f [i], 0.0, 0.0, 0);
+			autoSound filtered = Sounds_convolve (me, gammaTone.get(), kSounds_convolve_scaling::SUM, kSounds_convolve_signalOutsideTimeDomain::ZERO);
 
-			for (long j = 1; j <= numberOfFrames; j++) {
+			/*
+				To energy measure: weigh with broad-band transfer function.
+			*/
+			for (integer j = 1; j <= numberOfFrames; j ++) {
 				Sound_into_Sound (filtered.get(), frame.get(), Sampled_indexToX (thee.get(), j) + timeCorrection);
 				Sounds_multiply (frame.get(), window.get());
-				thy y[i][j] = Sound_power (frame.get()) * bb / gammaMaxAmplitude;
+				thy y [i] [j] = Sound_power (frame.get()) * bb / gammaMaxAmplitude;
 			}
-			Melder_progress ( (double) i / nFilters, U"SPINET: filter ", i, U" from ", nFilters, U".");
+			Melder_progress ((double) i / nFilters, U"SPINET: filter ", i, U" from ", nFilters, U".");
 		}
 
-		// Excitatory and inhibitory area functions
-
-		for (long i = 1; i <= nFilters; i++) {
-			for (long k = 1; k <= nFilters; k++) {
-				double fr = (f[k] - f[i]) / bw[i];
+		/*
+			Excitatory and inhibitory area functions.
+		*/
+		for (integer i = 1; i <= nFilters; i ++) {
+			for (integer k = 1; k <= nFilters; k ++) {
+				double fr = (f [k] - f [i]) / bw [i];
 				aex[i] += fgamma (fr / thy excitationErbProportion, thy gamma);
 				ain[i] += fgamma (fr / thy inhibitionErbProportion, thy gamma);
 			}
@@ -98,16 +102,16 @@ autoSPINET Sound_to_SPINET (Sound me, double timeStep, double windowDuration, do
 
 		// On-center off-surround interactions
 
-		for (long j = 1; j <= numberOfFrames; j++)
-			for (long i = 1; i <= nFilters; i++) {
-				double a = 0;
-				for (long k = 1; k <= nFilters; k++) {
-					double fr = (f[k] - f[i]) / bw[i];
+		for (integer j = 1; j <= numberOfFrames; j ++)
+			for (integer i = 1; i <= nFilters; i ++) {
+				real80 a = 0.0;
+				for (long k = 1; k <= nFilters; k ++) {
+					double fr = (f [k] - f [i]) / bw [i];
 					double hexsq = fgamma (fr / thy excitationErbProportion, thy gamma);
 					double hinsq = fgamma (fr / thy inhibitionErbProportion, thy gamma);
-					a += thy y[k][j] * (hexsq / aex[i] - hinsq / ain[i]);
+					a += thy y [k] [j] * (hexsq / aex [i] - hinsq / ain [i]);
 				}
-				thy s[i][j] = a > 0 ? a : 0;
+				thy s [i] [j] = a > 0.0 ? (real) a : 0.0;
 			}
 		return thee;
 	} catch (MelderError) {
diff --git a/dwtools/SpeechSynthesizer_and_TextGrid.cpp b/dwtools/SpeechSynthesizer_and_TextGrid.cpp
index 1d529af..cd74035 100644
--- a/dwtools/SpeechSynthesizer_and_TextGrid.cpp
+++ b/dwtools/SpeechSynthesizer_and_TextGrid.cpp
@@ -624,7 +624,7 @@ autoTextGrid SpeechSynthesizer_and_Sound_and_IntervalTier_align (SpeechSynthesiz
         for (long iint = istart; iint <= iend; iint ++) {
             TextInterval ti = his intervals.at [iint];
             if (ti -> text && str32len (ti -> text) > 0) {
-                autoSound sound = Sound_extractPart (thee, ti -> xmin, ti -> xmax,  kSound_windowShape_RECTANGULAR, 1, true);
+                autoSound sound = Sound_extractPart (thee, ti -> xmin, ti -> xmax,  kSound_windowShape::RECTANGULAR, 1, true);
                 autoTextGrid grid = SpeechSynthesizer_and_Sound_and_TextInterval_align (me, sound.get(), ti, silenceThreshold, minSilenceDuration, minSoundingDuration);
                 textgrids. addItem_move (grid.move());
             }
@@ -651,7 +651,7 @@ static autoTextGrid SpeechSynthesizer_and_Sound_and_IntervalTier_align2 (SpeechS
         for (long iint = istart; iint <= iend; iint ++) {
             TextInterval ti = his intervals.at [iint];
             if (ti -> text && str32len (ti -> text) > 0) {
-                autoSound sound = Sound_extractPart (thee, ti -> xmin, ti -> xmax,  kSound_windowShape_RECTANGULAR, 1, true);
+                autoSound sound = Sound_extractPart (thee, ti -> xmin, ti -> xmax,  kSound_windowShape::RECTANGULAR, 1, true);
                 autoTextGrid grid = SpeechSynthesizer_and_Sound_and_TextInterval_align2 (me, sound.get(), ti, silenceThreshold, minSilenceDuration, minSoundingDuration, trimDuration);
                 textgrids. addItem_move (grid.move());
             }
diff --git a/dwtools/Table_extensions.cpp b/dwtools/Table_extensions.cpp
index 685296a..20ff734 100644
--- a/dwtools/Table_extensions.cpp
+++ b/dwtools/Table_extensions.cpp
@@ -3367,7 +3367,7 @@ void Table_verticalErrorBarsPlotWhere (Table me, Graphics g,
 	}
 }
 
-double Table_getMedianAbsoluteDeviation (Table me, long columnNumber)
+double Table_getMedianAbsoluteDeviation (Table me, integer columnNumber)
 	try {
 		Table_checkSpecifiedColumnNumberWithinRange (me, columnNumber);
 		Table_numericize_Assert (me, columnNumber);
@@ -3375,7 +3375,7 @@ double Table_getMedianAbsoluteDeviation (Table me, long columnNumber)
 			return undefined;
 		}
 		autoNUMvector<double> data (1, my rows.size);
-		for (long irow = 1; irow <= my rows.size; irow ++) {
+		for (integer irow = 1; irow <= my rows.size; irow ++) {
 			TableRow row = my rows.at [irow];
 			data[irow] = row -> cells[columnNumber].number;
 			if (isundef (data [irow])) {
@@ -3384,7 +3384,7 @@ double Table_getMedianAbsoluteDeviation (Table me, long columnNumber)
 			}
 		}
 		double mad, location;
-		NUMmad (data.peek(), my rows.size, &location, 1, &mad, nullptr);
+		NUMmad (data.peek(), my rows.size, & location, true, & mad, nullptr);
 		return mad;
 	} catch (MelderError) {
 		Melder_throw (me, U": cannot compute median absolute deviation of column ", columnNumber, U".");
@@ -4151,7 +4151,7 @@ void Table_boxPlotsWhere (Table me, Graphics g,
 					if (si -> classIndex[irow] == ilevel) {
 						Formula_Result result;
 						Formula_run (irow, dataColumns [icol], & result);
-						if (result.result.numericResult != 0.0) {
+						if (result. numericResult != 0.0) {
 							data [++ numberOfDataInLevelColumn] = Table_getNumericValue_Assert (me, irow, dataColumns [icol]);
 						}
 					}
@@ -4190,7 +4190,7 @@ void Table_distributionPlotWhere (Table me, Graphics g,
 		for (long irow = 1; irow <= n; irow ++) {
 			Formula_Result result;
 			Formula_run (irow, dataColumn, & result);
-			if (result.result.numericResult != 0.0) {
+			if (result. numericResult != 0.0) {
 				thy z[1][++mrow] = Table_getNumericValue_Assert (me, irow, dataColumn);
 			}
 		}
@@ -4262,7 +4262,7 @@ long Table_getNumberOfRowsWhere (Table me, const char32 *formula, Interpreter in
 	for (long irow = 1; irow <= my rows.size; irow ++) {
 		Formula_Result result;
 		Formula_run (irow, 1, & result);
-		if (result.result.numericResult != 0.0) {
+		if (result. numericResult != 0.0) {
 			numberOfRows++;
 		}
 	}
@@ -4281,7 +4281,7 @@ integer *Table_findRowsMatchingCriterion (Table me, const char32 *formula, Inter
 		for (long irow =1; irow <= my rows.size; irow ++) {
 			Formula_Result result;
 			Formula_run (irow, 1, & result);
-			if (result.result.numericResult != 0.0) {
+			if (result. numericResult != 0.0) {
 				selectedRows [++ n] = irow;
 			}
 		}
@@ -4550,7 +4550,7 @@ autoTable Table_extractRowsWhere (Table me, const char32 *formula, Interpreter i
 		for (long irow = 1; irow <= my rows.size; irow ++) {
 			Formula_Result result;
 			Formula_run (irow, 1, & result);
-			if (result.result.numericResult != 0.0) {
+			if (result. numericResult != 0.0) {
 				TableRow row = my rows.at [irow];
 				autoTableRow newRow = Data_copy (row);
 				thy rows. addItem_move (newRow.move());
@@ -4610,7 +4610,7 @@ static long SSCPList_findIndexOfGroupLabel (SSCPList me, const char32 *label) {
 	return 0;
 }
 
-static autoTable Table_and_SSCPList_extractMahalanobisWhere (Table me, SSCPList thee, double numberOfSigmas, int which_Melder_NUMBER, const char32 *factorColumn, const char32 *formula, Interpreter interpreter) {
+static autoTable Table_and_SSCPList_extractMahalanobisWhere (Table me, SSCPList thee, double numberOfSigmas, kMelder_number which, const char32 *factorColumn, const char32 *formula, Interpreter interpreter) {
 	try {
 		integer numberOfGroups = thy size;
 		Melder_assert (numberOfGroups > 0);
@@ -4650,7 +4650,7 @@ static autoTable Table_and_SSCPList_extractMahalanobisWhere (Table me, SSCPList
 				vector [icol] = Table_getNumericValue_Assert (me, irow, columnIndex [icol]);
 			}
 			double dm2 = NUMmahalanobisDistance_chi (covi -> lowerCholesky, vector.peek(), covi -> centroid, numberOfColumns, numberOfColumns);
-			if (Melder_numberMatchesCriterion (sqrt (dm2), which_Melder_NUMBER, numberOfSigmas)) {
+			if (Melder_numberMatchesCriterion (sqrt (dm2), which, numberOfSigmas)) {
 				TableRow row = my rows.at [irow];
 				autoTableRow newRow = Data_copy (row);
 				his rows. addItem_move (newRow.move());
@@ -4662,10 +4662,10 @@ static autoTable Table_and_SSCPList_extractMahalanobisWhere (Table me, SSCPList
 	}
 }
 
-autoTable Table_extractMahalanobisWhere(Table me, const char32 *columnLabels, const char32 *factorColumn, double numberOfSigmas, int which_Melder_NUMBER, const char32 *formula, Interpreter interpreter) {
+autoTable Table_extractMahalanobisWhere (Table me, const char32 *columnLabels, const char32 *factorColumn, double numberOfSigmas, kMelder_number which, const char32 *formula, Interpreter interpreter) {
 	try {
 		autoSSCPList thee = Table_to_SSCPList_where (me, columnLabels, factorColumn, formula, interpreter);
-		autoTable him = Table_and_SSCPList_extractMahalanobisWhere (me, thee.get(), numberOfSigmas, which_Melder_NUMBER, factorColumn, formula, interpreter);
+		autoTable him = Table_and_SSCPList_extractMahalanobisWhere (me, thee.get(), numberOfSigmas, which, factorColumn, formula, interpreter);
 		return him;
 	} catch (MelderError) {
 		Melder_throw (me, U"Table not extracted.");
diff --git a/dwtools/Table_extensions.h b/dwtools/Table_extensions.h
index 2428e26..90a7c99 100644
--- a/dwtools/Table_extensions.h
+++ b/dwtools/Table_extensions.h
@@ -44,7 +44,7 @@ autoTable Table_create_esposito2006 ();
 
 autoTable Table_create_ganong1980 ();
 
-double Table_getMedianAbsoluteDeviation (Table me, long columnNumber);
+double Table_getMedianAbsoluteDeviation (Table me, integer columnNumber);
 
 // Two one-way tests for normal and non-normally distributed data, respectively.
 autoTable Table_getOneWayAnalysisOfVarianceF (Table me, long column, long groupColumn, autoTable *means, autoTable *meansDiff, autoTable *meansDiffProbabilities);
@@ -73,7 +73,7 @@ autoTable Table_extractRowsWhere (Table me, const char32 *formula, Interpreter i
 
 autoTable Table_extractColumnRanges (Table me, const char32 *ranges);
 
-autoTable Table_extractMahalanobisWhere (Table me, const char32 *columnLabels, const char32 *factorColumn, double numberOfSigmas, int which_Melder_NUMBER, const char32 *formula, Interpreter interpreter);
+autoTable Table_extractMahalanobisWhere (Table me, const char32 *columnLabels, const char32 *factorColumn, double numberOfSigmas, kMelder_number which, const char32 *formula, Interpreter interpreter);
 
 void Table_distributionPlotWhere (Table me, Graphics g, long dataColumn, double minimum, double maximum, long nBins, double freqMin, double freqMax, bool garnish, const char32 *formula, Interpreter interpreter);
 
diff --git a/dwtools/TextGrid_and_DurationTier.cpp b/dwtools/TextGrid_and_DurationTier.cpp
index db9b171..4454830 100644
--- a/dwtools/TextGrid_and_DurationTier.cpp
+++ b/dwtools/TextGrid_and_DurationTier.cpp
@@ -73,13 +73,13 @@ autoTextGrid TextGrid_and_DurationTier_scaleTimes (TextGrid me, DurationTier the
 	}
 }
 
-autoDurationTier TextGrid_to_DurationTier (TextGrid me, long tierNumber, double timeScalefactor, double leftTransitionDuration, double rightTransitionDuration, int which_Melder_STRING, const char32 *criterion) {
+autoDurationTier TextGrid_to_DurationTier (TextGrid me, long tierNumber, double timeScalefactor, double leftTransitionDuration, double rightTransitionDuration, kMelder_string which, const char32 *criterion) {
 	try {
 		autoDurationTier him = DurationTier_create (my xmin, my xmax);
 		IntervalTier tier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
 		for (long i = 1; i <= tier ->intervals.size; i++) {
 			TextInterval segment = tier -> intervals.at [i];
-			if (Melder_stringMatchesCriterion (segment -> text, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (segment -> text, which, criterion)) {
 				double xmin = segment -> xmin, xmax = segment -> xmax;
 				RealTier_addPoint (him.get(), xmin, 1.0);
 				RealTier_addPoint (him.get(), xmin + leftTransitionDuration, timeScalefactor);
diff --git a/dwtools/TextGrid_and_DurationTier.h b/dwtools/TextGrid_and_DurationTier.h
index d59d7c3..eb778d2 100644
--- a/dwtools/TextGrid_and_DurationTier.h
+++ b/dwtools/TextGrid_and_DurationTier.h
@@ -28,6 +28,6 @@ void TextTier_and_DurationTier_scaleTimes (TextTier me, DurationTier thee);
 
 autoTextGrid TextGrid_and_DurationTier_scaleTimes (TextGrid me, DurationTier thee);
 
-autoDurationTier TextGrid_to_DurationTier (TextGrid me, long tierNumber, double timeScalefactor, double leftTransitionDuration, double rightTransitionDuration, int which_Melder_STRING, const char32 *criterion);
+autoDurationTier TextGrid_to_DurationTier (TextGrid me, long tierNumber, double timeScalefactor, double leftTransitionDuration, double rightTransitionDuration, kMelder_string which, const char32 *criterion);
 
 #endif
diff --git a/dwtools/TextGrid_and_PitchTier.cpp b/dwtools/TextGrid_and_PitchTier.cpp
index 87fff63..76994a8 100644
--- a/dwtools/TextGrid_and_PitchTier.cpp
+++ b/dwtools/TextGrid_and_PitchTier.cpp
@@ -18,6 +18,7 @@
 
 #include "Interpreter.h"
 #include "NUM2.h"
+#include "RealTier.h"
 #include "Strings_extensions.h"
 #include "TextGrid_and_PitchTier.h"
 #include "Thing.h"
@@ -46,11 +47,11 @@
 #define PITCH_ANCHOR_IS_MAXIMUM 7
 #define PITCH_ANCHOR_IS_MINIMUM 8
 
-static real RealTier_getMinimumValue_interval (RealTier me, real tmin, real tmax) {
+static double RealTier_getMinimumValue_interval (RealTier me, double tmin, double tmax) {
 	integer imin, imax;
 	(void) AnyTier_getWindowPoints ((AnyTier) me, tmin, tmax, & imin, & imax);
-	real result = undefined;
-	for (integer i = imin; i <= imax; i ++) {
+	double result = undefined;
+	for (long i = imin; i <= imax; i ++) {
 		RealPoint point = my points.at [i];
 		if (isundef (result) || point -> value < result) {
 			result = point -> value;
@@ -59,11 +60,11 @@ static real RealTier_getMinimumValue_interval (RealTier me, real tmin, real tmax
 	return result;
 }
 
-static real RealTier_getMaximumValue_interval (RealTier me, real tmin, real tmax) {
+static double RealTier_getMaximumValue_interval (RealTier me, double tmin, double tmax) {
 	integer imin, imax;
 	(void) AnyTier_getWindowPoints ((AnyTier) me, tmin, tmax, & imin, & imax);
-	real result = undefined;
-	for (integer i = imin; i <= imax; i ++) {
+	double result = undefined;
+	for (long i = imin; i <= imax; i ++) {
 		RealPoint point = my points.at [i];
 		if (isundef (result) || point -> value > result) {
 			result = point -> value;
@@ -84,7 +85,7 @@ static autoPitchTier PitchTier_createFromPoints (double xmin, double xmax, doubl
 	} 
 }
 
-static double * getTimesFromString (double tmin, double tmax, const char32 *times_string, int time_offset, integer *numberOfTimes) {
+static double * getTimesFromRelativeTimesString (double tmin, double tmax, const char32 *times_string, int time_offset, integer *numberOfTimes) {
 	autoNUMvector<double> times (NUMstring_to_numbers (times_string, numberOfTimes), 1);
 	/*
 		translate the "times" to real time
@@ -104,11 +105,11 @@ static double * getTimesFromString (double tmin, double tmax, const char32 *time
 }
 
 /*
-	a1, a#1, b1,b#1, ... g#1, a2, b2, a#1,b#2, a a# b c c# d d# e f f# g g#
+	a1, a#1, b1, b#1, ... g#1, a2, a#2, b2, c2, ....; a a# b c c# d d# e f f# g g#
 */
 static double note_to_frequency (const char32 *token, double a4) {
 	double base = a4 / 8.0;
-	integer octave, index;
+	long octave, index;
 	const char32 note = *token++, char2 = *token++;
 	if (note == U'a' || note == U'A') {
 		index = 1;
@@ -123,7 +124,7 @@ static double note_to_frequency (const char32 *token, double a4) {
 	} else if (note == U'f' || note == U'F') {
 		index = 9;
 	} else if (note == U'g' || note == U'G') {
-		index = 10;
+		index = 11;
 	} else {
 		return undefined;
 	}
@@ -140,11 +141,11 @@ static double note_to_frequency (const char32 *token, double a4) {
 	} else {
 		return undefined;
 	}
-	double frequency = base * pow (2, octave - 1.0 + (index - 1.0) / 12.0);
+	double frequency = base * pow (2.0, octave - 1.0 + (index - 1.0) / 12.0);
 	return frequency;
 }
 
-static autoPitchTier PitchTier_extractModifiedInterval (PitchTier me, double tmin, double tmax, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status) {
+static autoPitchTier PitchTier_createAsModifiedPart (PitchTier me, double tmin, double tmax, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status) {
 	(void) pitch_unit;
 	try {
 		if (tmin >= tmax) {
@@ -157,7 +158,7 @@ static autoPitchTier PitchTier_extractModifiedInterval (PitchTier me, double tmi
 		}
 		
 		integer numberOfTimes;
-		autoNUMvector<double> times (getTimesFromString (tmin, tmax, times_string, time_offset, & numberOfTimes), 1);
+		autoNUMvector<double> times (getTimesFromRelativeTimesString (tmin, tmax, times_string, time_offset, & numberOfTimes), 1);
 		
 		autoStrings items = Strings_createAsTokens (pitches_string, U" ");
 		integer numberOfPitches = items -> numberOfStrings;
@@ -165,7 +166,7 @@ static autoPitchTier PitchTier_extractModifiedInterval (PitchTier me, double tmi
 			Melder_throw (U"The number of items in the times and the pitches string have to be equal.");
 		}
 		autoNUMvector<double> pitchesraw (1, numberOfPitches);
-		for (integer i = 1; i <= numberOfPitches; i ++) {
+		for (long i = 1; i <= numberOfPitches; i ++) {
 			const char32 *token = items -> strings [i];
 			if (pitch_as == PITCH_VALUE_AS_MUSIC_NOTE) {
 				pitchesraw [i] = note_to_frequency (token, 440.0);
@@ -253,7 +254,7 @@ static autoPitchTier PitchTier_extractModifiedInterval (PitchTier me, double tmi
 
 static void PitchTiers_replacePoints (PitchTier me, PitchTier thee) {
 	AnyTier_removePointsBetween ((AnyTier) me, thy xmin, thy xmax);
-	for (integer i = 1; i <= thy points.size; i ++) {
+	for (long i = 1; i <= thy points.size; i ++) {
 		RealPoint pp = thy points.at [i];
 		RealTier_addPoint (me, pp -> number, pp -> value);
 	}
@@ -261,7 +262,7 @@ static void PitchTiers_replacePoints (PitchTier me, PitchTier thee) {
 
 void PitchTier_modifyInterval (PitchTier me, double tmin, double tmax, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status) {
 	try {
-		autoPitchTier thee = PitchTier_extractModifiedInterval (me, tmin, tmax, times_string, time_offset, pitches_string, pitch_unit, pitch_as, pitchAnchor_status);
+		autoPitchTier thee = PitchTier_createAsModifiedPart (me, tmin, tmax, times_string, time_offset, pitches_string, pitch_unit, pitch_as, pitchAnchor_status);
 		PitchTiers_replacePoints (me, thee.get());
 	} catch (MelderError) {
 		Melder_throw (me, U": interval modification not completed.");
@@ -269,14 +270,14 @@ void PitchTier_modifyInterval (PitchTier me, double tmin, double tmax, const cha
 }
 
 
-autoPitchTier IntervalTier_and_PitchTier_to_PitchTier (IntervalTier me, PitchTier thee, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, int which_Melder_STRING, const char32 *criterion) {
+autoPitchTier IntervalTier_and_PitchTier_to_PitchTier (IntervalTier me, PitchTier thee, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, kMelder_string which, const char32 *criterion) {
 	try {
 		autoPitchTier him = Data_copy (thee);
-		for (integer i = 1; i <= my intervals.size; i ++) {
+		for (long i = 1; i <= my intervals.size; i ++) {
 			TextInterval segment = my intervals.at [i];
-			if (Melder_stringMatchesCriterion (segment -> text, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (segment -> text, which, criterion)) {
 				double xmin = segment -> xmin, xmax = segment -> xmax;
-				autoPitchTier modified = PitchTier_extractModifiedInterval (thee, xmin, xmax, times_string, time_offset, pitches_string, pitch_unit, pitch_as, pitchAnchor_status);
+				autoPitchTier modified = PitchTier_createAsModifiedPart (thee, xmin, xmax, times_string, time_offset, pitches_string, pitch_unit, pitch_as, pitchAnchor_status);
 				PitchTiers_replacePoints (him.get(), modified.get());
 			}
 		}
@@ -286,10 +287,10 @@ autoPitchTier IntervalTier_and_PitchTier_to_PitchTier (IntervalTier me, PitchTie
 	}
 }
 
-autoPitchTier TextGrid_and_PitchTier_to_PitchTier (TextGrid me, PitchTier thee, integer tierNumber, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, int which_Melder_STRING, const char32 *criterion) {
+static autoPitchTier TextGrid_and_PitchTier_to_PitchTier (TextGrid me, PitchTier thee, long tierNumber, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, kMelder_string which, const char32 *criterion) {
 	try {
 		IntervalTier tier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
-		return IntervalTier_and_PitchTier_to_PitchTier (tier, thee, times_string, time_offset, pitches_string, pitch_unit, pitch_as, pitchAnchor_status, which_Melder_STRING, criterion);
+		return IntervalTier_and_PitchTier_to_PitchTier (tier, thee, times_string, time_offset, pitches_string, pitch_unit, pitch_as, pitchAnchor_status, which, criterion);
 	} catch (MelderError) {
 		Melder_throw (me, U": cannot create PitchTier.");
 	}
@@ -299,7 +300,7 @@ autoPitchTier TextGrid_and_PitchTier_to_PitchTier (TextGrid me, PitchTier thee,
 	We specify pitches as tone levels (1 - numberOfToneLevels). These levels are relative to the pitch range of a speaker.
 	(normally in Mandarin Chinese they count 5 levels).
 */
-static autoPitchTier PitchTier_extractModifiedInterval_toneLevels (PitchTier me, double tmin, double tmax, double fmin, double fmax, integer numberOfToneLevels, const char32 *times_string, int time_offset, const char32 *pitches_string) {
+static autoPitchTier PitchTier_createAsModifiedPart_toneLevels (PitchTier me, double tmin, double tmax, double fmin, double fmax, long numberOfToneLevels, const char32 *times_string, int time_offset, const char32 *pitches_string) {
 	try {
 		if (tmin >= tmax) {
 			tmin = my xmin; tmax = my xmax;
@@ -308,13 +309,13 @@ static autoPitchTier PitchTier_extractModifiedInterval_toneLevels (PitchTier me,
 			Melder_throw (U"The lowest frequency must be lower than the highest frequency.");
 		}
 		integer numberOfTimes, numberOfPitches;
-		autoNUMvector<double> times (getTimesFromString (tmin, tmax, times_string, time_offset, & numberOfTimes), 1);
+		autoNUMvector<double> times (getTimesFromRelativeTimesString (tmin, tmax, times_string, time_offset, & numberOfTimes), 1);
 		autoNUMvector<double> pitches (NUMstring_to_numbers (pitches_string, & numberOfPitches), 1);
 		if (numberOfTimes != numberOfPitches) {
 			Melder_throw (U"The number of items in the times and the pitches string have to be equal.");
 		}
 		double scale = log10 (fmax / fmin) / numberOfToneLevels;
-		for (integer i = 1; i <= numberOfPitches; i ++) {
+		for (long i = 1; i <= numberOfPitches; i ++) {
 			pitches [i] = fmin * pow (10.0, scale * pitches [i]);
 		}
 		NUMsort2<double, double> (numberOfTimes, times.peek(), pitches.peek());
@@ -325,9 +326,9 @@ static autoPitchTier PitchTier_extractModifiedInterval_toneLevels (PitchTier me,
 	}
 }
 
-void PitchTier_modifyInterval_toneLevels (PitchTier me, double tmin, double tmax, double fmin, double fmax, integer numberOfToneLevels, const char32 *times_string, int time_offset, const char32 *pitches_string) {
+void PitchTier_modifyInterval_toneLevels (PitchTier me, double tmin, double tmax, double fmin, double fmax, long numberOfToneLevels, const char32 *times_string, int time_offset, const char32 *pitches_string) {
 	try {
-		autoPitchTier thee = PitchTier_extractModifiedInterval_toneLevels (me, tmin, tmax, fmin, fmax, numberOfToneLevels, times_string, time_offset, pitches_string);
+		autoPitchTier thee = PitchTier_createAsModifiedPart_toneLevels (me, tmin, tmax, fmin, fmax, numberOfToneLevels, times_string, time_offset, pitches_string);
 		PitchTiers_replacePoints (me, thee.get());
 	} catch (MelderError) {
 		Melder_throw (me, U": interval modification as tone levels not succeeded.");
diff --git a/dwtools/TextGrid_and_PitchTier.h b/dwtools/TextGrid_and_PitchTier.h
index 81310bf..eb8ecbf 100644
--- a/dwtools/TextGrid_and_PitchTier.h
+++ b/dwtools/TextGrid_and_PitchTier.h
@@ -23,10 +23,10 @@
 
 void PitchTier_modifyInterval (PitchTier me, double tmin, double tmax, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitch_is);
 
-void PitchTier_modifyInterval_toneLevels (PitchTier me, double tmin, double tmax, double fmin, double fmax, integer numberOfToneLevels, const char32 *times_string, int time_offset, const char32 *pitches_string);
+void PitchTier_modifyInterval_toneLevels (PitchTier me, double tmin, double tmax, double fmin, double fmax, long numberOfToneLevels, const char32 *times_string, int time_offset, const char32 *pitches_string);
 
-autoPitchTier IntervalTier_and_PitchTier_to_PitchTier (IntervalTier me, PitchTier thee, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, int which_Melder_STRING, const char32 *criterion);
+autoPitchTier IntervalTier_and_PitchTier_to_PitchTier (IntervalTier me, PitchTier thee, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, kMelder_string which, const char32 *criterion);
 
-autoPitchTier TextGrid_and_PitchTier_to_PitchTier (TextGrid me, integer tierNumber, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, int which_Melder_STRING, const char32 *criterion);
+autoPitchTier TextGrid_and_PitchTier_to_PitchTier (TextGrid me, long tierNumber, const char32 *times_string, int time_offset, const char32 *pitches_string, int pitch_unit, int pitch_as, int pitchAnchor_status, kMelder_string which, const char32 *criterion);
 
 #endif
diff --git a/dwtools/TextGrid_extensions.cpp b/dwtools/TextGrid_extensions.cpp
index 66c6e71..72cdf16 100644
--- a/dwtools/TextGrid_extensions.cpp
+++ b/dwtools/TextGrid_extensions.cpp
@@ -432,7 +432,7 @@ void TextGrid_setLaterEndTime (TextGrid me, double xmax, const char32 *imark, co
 		if (xmax <= my xmax) {
 			return;
 		}
-		for (long tierNumber =1 ; tierNumber <= my tiers->size; tierNumber ++) {
+		for (long tierNumber = 1 ; tierNumber <= my tiers->size; tierNumber ++) {
 			Function tier = my tiers->at [tierNumber];
 			if (tier -> classInfo == classIntervalTier) {
 				IntervalTier_setLaterEndTime ((IntervalTier) tier, xmax, imark);
@@ -818,16 +818,4 @@ autoTextGrid TextGrids_to_TextGrid_appendContinuous (OrderedOf<structTextGrid>*
 	}
 }
 
-static void NUMshift (double *x, double dx) {   // TODO: make global
-	*x += dx;
-}
-
-static autoIntervalTier IntervalTier_shiftBoundaries (IntervalTier me, double startTime, double shiftTime) {   // TODO: make global
-	autoIntervalTier result;   // TODO: implement
-	(void) me;   // TODO: use
-	(void) startTime;   // TODO: use
-	(void) shiftTime;   // TODO: use
-	return result;
-}
-
 /* End of file TextGrid_extensions.cpp */
diff --git a/dwtools/VowelEditor.cpp b/dwtools/VowelEditor.cpp
index dc81f36..8a62b20 100644
--- a/dwtools/VowelEditor.cpp
+++ b/dwtools/VowelEditor.cpp
@@ -56,7 +56,6 @@ trajectory --> path ????
 #include "FormantGrid.h"
 #include "KlattGrid.h"
 #include "../external/portaudio/portaudio.h"
-#include "praat.h"
 #include "PitchTier_to_PointProcess.h"
 #include "PitchTier_to_Sound.h"
 #include "PointProcess_and_Sound.h"
@@ -103,8 +102,8 @@ Thing_implement (VowelEditor, Editor, 0);
 // maximum number of marks
 #define VowelEditor_MAXIMUM_MARKERS 30
 
-static struct structVowelEditor_F0 f0default { 140.0, 0.0, 40.0, 2000.0, SAMPLING_FREQUENCY, 1, 0.0, 2000 };
-static struct structVowelEditor_F1F2Grid griddefault { 200.0, 500.0, 0, 1, 0, 1, 0.5 };
+static structVowelEditor_F0 f0default { 140.0, 0.0, 40.0, 2000.0, SAMPLING_FREQUENCY, 1, 0.0, 2000 };
+static structVowelEditor_F1F2Grid griddefault { 200.0, 500.0, 0, 1, 0, 1, 0.5 };
 
 #include "oo_DESTROY.h"
 #include "Vowel_def.h"
@@ -139,37 +138,33 @@ static struct {
 	double f1min, f1max, f2min, f2max;
 	double f3, b3, f4, b4;
 	double markTraceEvery, extendDuration;
-	int frequencyScale;
-	int axisOrientation;
 	int speakerType, marksDataset, numberOfMarks, marksFontSize;
-	char32 mark[VowelEditor_MAXIMUM_MARKERS][Preferences_STRING_BUFFER_SIZE];
+	char32 mark [VowelEditor_MAXIMUM_MARKERS] [Preferences_STRING_BUFFER_SIZE];
 } prefs;
 
 void VowelEditor_prefs () {
-	Preferences_addInt (U"VowelEditor.shellWidth", &prefs.shellWidth, 500);
-	Preferences_addInt (U"VowelEditor.shellHeight", &prefs.shellHeight, 500);
-	Preferences_addBool (U"VowelEditor.soundFollowsMouse", &prefs.soundFollowsMouse, true);
-	Preferences_addDouble (U"VowelEditor.f1min", &prefs.f1min, 200.0);
-	Preferences_addDouble (U"VowelEditor.f1max", &prefs.f1max, 1200.0);
-	Preferences_addDouble (U"VowelEditor.f2min", &prefs.f2min, 500.0);
-	Preferences_addDouble (U"VowelEditor.f2max", &prefs.f2max, 3500.0);
-	Preferences_addDouble (U"VowelEditor.f3", &prefs.f3, 2500.0);
-	Preferences_addDouble (U"VowelEditor.b3", &prefs.b3, 250.0);
-	Preferences_addDouble (U"VowelEditor.f4", &prefs.f4, 3500.0);
-	Preferences_addDouble (U"VowelEditor.b4", &prefs.b4, 350.0);
-	Preferences_addDouble (U"VowelEditor.markTraceEvery", &prefs.markTraceEvery, 0.05);
-	Preferences_addDouble (U"VowelEditor.extendDuration", &prefs.extendDuration, 0.05);
-	Preferences_addInt (U"VowelEditor.frequencyScale", &prefs.frequencyScale, 0);
-	Preferences_addInt (U"VowelEditor.axisOrientation", &prefs.axisOrientation, 0);
-	Preferences_addInt (U"VowelEditor.speakerType", &prefs.speakerType, 1);
-	Preferences_addInt (U"VowelEditor.marksDataset", &prefs.marksDataset, 2);
-	Preferences_addInt (U"VowelEditor.marksFontsize", &prefs.marksFontSize, 14);
-	Preferences_addInt (U"VowelEditor.numberOfMarks", &prefs.numberOfMarks, 12);   // 12 is the number of vowels in the default (Dutch) marksDataset
+	Preferences_addInt (U"VowelEditor.shellWidth", & prefs.shellWidth, 500);
+	Preferences_addInt (U"VowelEditor.shellHeight", & prefs.shellHeight, 500);
+	Preferences_addBool (U"VowelEditor.soundFollowsMouse", & prefs.soundFollowsMouse, true);
+	Preferences_addDouble (U"VowelEditor.f1min", & prefs.f1min, 200.0);
+	Preferences_addDouble (U"VowelEditor.f1max", & prefs.f1max, 1200.0);
+	Preferences_addDouble (U"VowelEditor.f2min", & prefs.f2min, 500.0);
+	Preferences_addDouble (U"VowelEditor.f2max", & prefs.f2max, 3500.0);
+	Preferences_addDouble (U"VowelEditor.f3", & prefs.f3, 2500.0);
+	Preferences_addDouble (U"VowelEditor.b3", & prefs.b3, 250.0);
+	Preferences_addDouble (U"VowelEditor.f4", & prefs.f4, 3500.0);
+	Preferences_addDouble (U"VowelEditor.b4", & prefs.b4, 350.0);
+	Preferences_addDouble (U"VowelEditor.markTraceEvery", & prefs.markTraceEvery, 0.05);
+	Preferences_addDouble (U"VowelEditor.extendDuration", & prefs.extendDuration, 0.05);
+	Preferences_addInt (U"VowelEditor.speakerType", & prefs.speakerType, 1);   // TODO: replace with enum
+	Preferences_addInt (U"VowelEditor.marksDataset", & prefs.marksDataset, 2);   // TODO: replace with enum
+	Preferences_addInt (U"VowelEditor.marksFontsize", & prefs.marksFontSize, 14);
+	Preferences_addInt (U"VowelEditor.numberOfMarks", & prefs.numberOfMarks, 12);   // 12 is the number of vowels in the default (Dutch) marksDataset
 	/*
 	 * We don't know how many markers there will be, so the prefs file needs to have the maximum number.
 	 */
 	for (long i = 1; i <= VowelEditor_MAXIMUM_MARKERS; i++) {
-		Preferences_addString (Melder_cat (U"VowelEditor.mark", (i < 10 ? U"0" : U""), i), & prefs.mark[i-1][0], U"x");
+		Preferences_addString (Melder_cat (U"VowelEditor.mark", (i < 10 ? U"0" : U""), i), & prefs.mark [i - 1] [0], U"x");
 	}
 }
 
@@ -194,18 +189,18 @@ static autoVowel Vowel_create_twoFormantSchwa (double duration) {
 		autoVowel me = Vowel_create (duration);
 		autoFormantPoint fp = FormantPoint_create (0.0);
 		fp -> formant [0] = 500.0;
-		fp -> bandwidth[0] = 50.0;
+		fp -> bandwidth [0] = 50.0;
 		fp -> formant [1] = 1500.0;
-		fp -> bandwidth[1] = 150.0;
+		fp -> bandwidth [1] = 150.0;
 		fp -> numberOfFormants = 2;
 		my ft -> points. addItem_move (fp.move());
 		RealTier_addPoint (my pt.get(), 0.0, 140.0);
 
 		fp = FormantPoint_create (duration);
 		fp -> formant [0] = 500.0;
-		fp -> bandwidth[0] = 50.0;
+		fp -> bandwidth [0] = 50.0;
 		fp -> formant [1] = 1500.0;
-		fp -> bandwidth[1] = 150.0;
+		fp -> bandwidth [1] = 150.0;
 		fp -> numberOfFormants = 2;
 		my ft -> points. addItem_move (fp.move());
 		RealTier_addPoint (my pt.get(), duration, 140.0);
@@ -233,9 +228,9 @@ static autoFormantGrid FormantTier_to_FormantGrid (FormantTier me) {
 		for (long ipoint = 1; ipoint <= my points.size; ipoint ++) {
 			FormantPoint fp = my points.at [ipoint];
 			double t = fp -> number;
-			for (long iformant = 1; iformant <= fp -> numberOfFormants; iformant++) {
-				FormantGrid_addFormantPoint (thee.get(), iformant, t, fp -> formant[iformant - 1]);
-				FormantGrid_addBandwidthPoint (thee.get(), iformant, t, fp -> bandwidth[iformant - 1]);
+			for (long iformant = 1; iformant <= fp -> numberOfFormants; iformant ++) {
+				FormantGrid_addFormantPoint (thee.get(), iformant, t, fp -> formant [iformant - 1]);
+				FormantGrid_addBandwidthPoint (thee.get(), iformant, t, fp -> bandwidth [iformant - 1]);
 			}
 		}
 		return thee;
@@ -659,14 +654,14 @@ static void VowelEditor_setMarks (VowelEditor me, int marksDataset, int speakerT
 	const char32 *Sex[3] = { U"", U"m", U"f"};
 	if (marksDataset == 1) {   // American-English
 		autoTable thee = Table_create_petersonBarney1952 ();
-		te = Table_extractRowsWhereColumn_string (thee.get(), 1, kMelder_string_EQUAL_TO, Type[speakerType]);
+		te = Table_extractRowsWhereColumn_string (thee.get(), 1, kMelder_string::EQUAL_TO, Type[speakerType]);
 	} else if (marksDataset == 2) {   // Dutch
 		if (speakerType == 1 || speakerType == 2) {   // male + female from Pols van Nierop
 			autoTable thee = Table_create_polsVanNierop1973 ();
-			te = Table_extractRowsWhereColumn_string (thee.get(), 1, kMelder_string_EQUAL_TO, Sex[speakerType]);
+			te = Table_extractRowsWhereColumn_string (thee.get(), 1, kMelder_string::EQUAL_TO, Sex[speakerType]);
 		} else {
 			autoTable thee = Table_create_weenink1983 ();
-			te = Table_extractRowsWhereColumn_string (thee.get(), 1, kMelder_string_EQUAL_TO, Type[speakerType]);
+			te = Table_extractRowsWhereColumn_string (thee.get(), 1, kMelder_string::EQUAL_TO, Type[speakerType]);
 		}
 	} else if (marksDataset == 3) {   // none
 		my marks.reset();
@@ -881,14 +876,12 @@ static void menu_cb_help (VowelEditor /* me */, EDITOR_ARGS_DIRECT) {
 }
 
 static void menu_cb_prefs (VowelEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Preferences", 0);
-		BOOLEAN (U"Sound-follows-mouse", true)
+	EDITOR_FORM (U"Preferences", nullptr);
+		BOOLEAN (U"Sound follows mouse", true)
 	EDITOR_OK
-		SET_INTEGER (U"Sound-follows-mouse", prefs.soundFollowsMouse)
+		SET_INTEGER (U"Sound follows mouse", prefs.soundFollowsMouse)
 	EDITOR_DO
-		my frequencyScale = prefs.frequencyScale;
-		my axisOrientation = prefs.axisOrientation;
-		my soundFollowsMouse = prefs.soundFollowsMouse = GET_INTEGER (U"Sound-follows-mouse");
+		my soundFollowsMouse = prefs.soundFollowsMouse = GET_INTEGER (U"Sound follows mouse");
 		Graphics_updateWs (my graphics.get());
 	EDITOR_END
 }
@@ -905,8 +898,6 @@ static void menu_cb_ranges_f1f2 (VowelEditor me, EDITOR_ARGS_FORM) {
 		SET_REAL (U"left F2 range", prefs.f2min)
 		SET_REAL (U"right F2 range", prefs.f2max)
 	EDITOR_DO
-		my frequencyScale = prefs.frequencyScale;
-		my axisOrientation = prefs.axisOrientation;
 		my f1min = prefs.f1min = GET_REAL (U"left F1 range");
 		my f1max = prefs.f1max = GET_REAL (U"right F1 range");
 		my f2min = prefs.f2min = GET_REAL (U"left F2 range");
@@ -1347,15 +1338,6 @@ static void gui_drawingarea_cb_key (VowelEditor /* me */, GuiDrawingArea_KeyEven
 }
 #endif
 
-static void cb_publish (Editor /*editor*/, autoDaata publish) {
-	try {
-		praat_new (publish.move(), U"");
-		praat_updateSelection ();
-	} catch (MelderError) {
-		Melder_flushError ();
-	}
-}
-
 static void updateWidgets (void *void_me) {
 	iam (VowelEditor);
 	(void) me;
@@ -1470,14 +1452,11 @@ autoVowelEditor VowelEditor_create (const char32 *title, Daata data) {
 		my graphics = Graphics_create_xmdrawingarea (my drawingArea);
 		Melder_assert (my graphics);
 		Graphics_setFontSize (my graphics.get(), 12);
-		Editor_setPublicationCallback (me.get(), cb_publish);
 
 		my f1min = prefs.f1min;
 		my f1max = prefs.f1max;
 		my f2min = prefs.f2min;
 		my f2max = prefs.f2max;
-		my frequencyScale = prefs.frequencyScale;
-		my axisOrientation = prefs.axisOrientation;
 		my speakerType = prefs.speakerType;
 		my marksDataset = prefs.marksDataset;
 		my marksFontSize = prefs.marksFontSize;
diff --git a/dwtools/manual_BSS.cpp b/dwtools/manual_BSS.cpp
index 6c6e1fa..d121c24 100644
--- a/dwtools/manual_BSS.cpp
+++ b/dwtools/manual_BSS.cpp
@@ -25,8 +25,8 @@
 void manual_BSS (ManPages me);
 void manual_BSS (ManPages me)
 {
-MAN_BEGIN (U"CrossCorrelationTable", U"djmw", 20110105)
-INTRO (U"One of the types of objects in Praat. A CrossCorrelationTable represents the cross-correlations between "
+MAN_BEGIN (U"CrossCorrelationTable", U"djmw", 20170908)
+INTRO (U"One of the types of objects in Praat. A ##CrossCorrelationTable# represents the cross-correlations between "
 	"a number of signals. Cell [%i,%j] of a CrossCorrelationTable contains the cross-correlation between the %i-th "
 	"and the %j-th signal. For example, the CrossCorrelationTable of an %n-channel sound is a %n\\xx%n table where "
 	"the number in cell [%i,%j] is the cross-correlation of channel %i with channel %j (for a particular lag time %\\ta).")
@@ -36,7 +36,7 @@ ENTRY (U"Remarks")
 NORMAL (U"Sometimes in the statistical literature, the cross-correlation between signals is also called "
 	"\"covariance\". However, the only thing a @@Covariance@ has in common with a CrossCorrelationTable is that "
 	"both are symmetric matrices. The differences between a CrossCorrelationTable and a Covariance are:")
-TAG (U"1. a Covariance matrix is always positive-definite; for a cross-correlation table this is only guaranteed if "
+TAG (U"1. A Covariance matrix is always positive-definite; for a cross-correlation table this is only guaranteed if "
 	"the lag time %\\ta = 0.")
 TAG (U"2. The elements %%c__ij_% in a Covariance always satisfy |%%c__ij_%/\\Vr(%%c__ii_%\\.c%%c__jj_%)| \\<_ 1; this is "
 	"generally not the case for cross-correlations.")
@@ -76,9 +76,22 @@ NORMAL (U"If the first matrix has to be positive definite, the numbers on the di
 	"chosen from the [0.1,1] interval.")
 MAN_END
 
-MAN_BEGIN (U"MixingMatrix", U"djmw", 20170421)
-INTRO (U"One of the @@Types of objects|type of Objects@ in Praat. A ##MixingMatrix# shows a mapping of the channels in a @Sound to the channels of another Sound.")
-NORMAL (U"The MixingMatrix is normally used in combination with a Sound object to produce a new Sound object in which the samples in each channels are a mix of the original channels, i.e. a linear combination of the original channels. Each row in the MixingMatrix then represents the weighting of the channels in the original Sound object while the number of rows determines the number of channels in the resulting Sound object.")
+MAN_BEGIN (U"Create simple MixingMatrix...", U"djmw", 20170908)
+INTRO (U"Create a new @@MixingMatrix at .")
+ENTRY (U"Settings")
+TAG (U"##Number of inputs")
+DEFINITION (U"defines the number of input channels, i.e. the number of columns in the matrix.")
+TAG (U"##Number of outputs")
+DEFINITION (U"defines the number of output channels, i.e. the number of rows in the matrix.")
+TAG (U"##Mixing coefficients")
+DEFINITION (U"define the coefficients. You input them row-wise. There have to be %%numberOfOutPuts% \\xx %%numberOfInputs% values. ")
+MAN_END
+
+MAN_BEGIN (U"MixingMatrix", U"djmw", 20170907)
+INTRO (U"One of the @@Types of objects|type of Objects@ in Praat. A ##MixingMatrix# shows a mapping of the channels of an input @Sound to the channels of an output Sound. A channel in the input sound is called an %%input% channel. Each output channel is a linear combination of input channels. ")
+NORMAL (U"The mixing of input channels can be written as the matrix multiplication ##R=M\\.cS#. Here #S is the matrix that represents the input sound, with %numberOfInputs% rows and %%numberOfSamples% columns. Each row in #S corresponds to one input channel. #M is the %%numberOfOutputChannels%\\xx %%numberOfInputs% MixingMatrix and #R is the %%numberOfOutputChannels%\\xx %%numberOfSamples% matrix that is the result of the mixing.")
+NORMAL (U"Row %i in the MixingMatrix #M therefore represents the weights %m__%ij_ of the different input channels %j in output channel %i, the number of rows of #M determines the number of output channels in the resulting #R. Column %j in #M represents the weight factors %m__%ij_ of input %j in the different output channels %i.")
+
 ENTRY (U"Examples")
 NORMAL (U"Given the following stereo Sound with a tone of 300 Hz in channel 1 and a tone of 600 Hz in channel two: ")
 CODE (U"stereo = Create Sound from formula: \"s\", 2, 0, 1, 44100, \"sin(2*pi*row*300*x)\"")
@@ -86,7 +99,7 @@ TAG (U"Example 1")
 CODE (U"mm1 = Create simple MixingMatrix: \"mm1\", 2, 1, \"1 0\"")
 CODE (U"selectObject: mm1, stereo")
 CODE (U"Mix")
-DEFINITION (U"will produce a new mono Sound object that shows a tone with a frequency of 300 Hz.")
+DEFINITION (U"will produce a new %mono Sound object that shows a tone with a frequency of 300 Hz.")
 DEFINITION (U"The example creates a Mixing matrix with one row and two columns. The resulting new Sound object will have only one channel which is the result of adding the two channels from the stereo sound with weights of 1.0 and 0.0, respectively.")  
 TAG (U"Example 2")
 CODE (U"mm2 = Create simple MixingMatrix: \"mm2\", 2, 1, \"0 1\"")
@@ -97,7 +110,7 @@ TAG (U"Example 3")
 CODE (U"mm3 = Create simple MixingMatrix: \"mm3\", 2, 1, \"1 1\"")
 CODE (U"selectObject: mm3, stereo")
 CODE (U"Mix")
-DEFINITION (U"will produce a new mono Sound object that shows a complex tone composed of frequencies 300 and 600 Hz. The amplitude of the resulting sound will be larger than 1")
+DEFINITION (U"will produce a new mono Sound object that shows a complex tone composed of frequencies 300 and 600 Hz. The amplitude of the output sound will be larger than 1")
 TAG (U"Example 4")
 CODE (U"mm4 = Create simple MixingMatrix: \"mm4\", 2, 2, \"1 0 1 0\"")
 CODE (U"selectObject: mm4, stereo")
@@ -107,21 +120,31 @@ TAG (U"Example 5")
 CODE (U"mm5 = Create simple MixingMatrix: \"mm5\", 2, 1, \"0.5 0.5\"")
 CODE (U"selectObject: mm5, stereo")
 CODE (U"Mix")
-DEFINITION (U"will produce a new mono Sound object that shows a complex tone composed of frequencies 300 and 600 Hz. The amplitudes of the resulting sound are now half the amplitude of the new object in example 3.")
+DEFINITION (U"will produce a new mono Sound object that shows a complex tone composed of frequencies 300 and 600 Hz. The amplitudes of the output sound are now half the amplitude of the output sound of example 3.")
 TAG (U"Example 6")
 CODE (U"mono = Create Sound from formula: \"s\", 1, 0, 1, 44100, \"sin(2*pi*300*x)\"")
 CODE (U"mm6 = Create simple MixingMatrix: \"mm6\", 1, 2, \"1 1\"")
 CODE (U"selectObject: mm6, mono")
 CODE (U"Mix")
-DEFINITION (U"will produce from the mono Sound object a new stereo Sound object that shows a tone of frequency 300 Hz in both channels.")
+DEFINITION (U"will produce from the mono input sound a stereo output sound that shows a tone of frequency 300 Hz in both channels.")
 TAG (U"Example 7")
 CODE (U"mm7 = Create simple MixingMatrix: \"mm7\", 2, 2, \"0 1 1 0\"")
 CODE (U"selectObject: mm7, stereo")
 CODE (U"Mix")
 DEFINITION (U"will interchange the channels.")
+TAG (U"Example 8")
 
 MAN_END
 
+MAN_BEGIN (U"MixingMatrix: Multiply input channel...", U"djmw", 20170908)
+INTRO (U"Multiply an input channel of the selected @@MixingMatrix@ by a value.")
+ENTRY (U"Examples")
+TAG (U"Increase the contribution of input channel 1 in each output channel by a factor of 2")
+CODE (U"Multiply input channel: 1, 2")
+TAG (U"Remove the contribution of channel 1 in each output channel")
+CODE (U"Multiply input channel: 1, 0")
+MAN_END
+
 MAN_BEGIN (U"Sound: To CrossCorrelationTable...", U"djmw", 20110212)
 INTRO (U"A command that creates a @@CrossCorrelationTable@ form every selected @@Sound@ object.")
 ENTRY (U"Settings")
diff --git a/dwtools/manual_dwtools.cpp b/dwtools/manual_dwtools.cpp
index 10eceec..b291a28 100644
--- a/dwtools/manual_dwtools.cpp
+++ b/dwtools/manual_dwtools.cpp
@@ -500,9 +500,9 @@ NORMAL (U"The scores for the dependent data will be in the lower numbered column
 MAN_END
 
 
-MAN_BEGIN (U"Canonical correlation analysis", U"djmw", 20140509)
+MAN_BEGIN (U"Canonical correlation analysis", U"djmw", 20170829)
 INTRO (U"This tutorial will show you how to perform canonical correlation "
-       "analysis with  P\\s{RAAT}.")
+       "analysis with Praat.")
 ENTRY (U"1. Objective of canonical correlation analysis")
 NORMAL (U"In canonical correlation analysis we try to find the correlations between "
 	"two data sets. One data set is called the %dependent set, the other the "
@@ -523,7 +523,7 @@ NORMAL (U"As an example, we will use the dataset from @@Pols et al. (1973)@ "
 	"how to take the logarithm of the formant frequency values and how to "
 	"standardize them. The following script summarizes:")
 CODE (U"pols50m = Create TableOfReal (Pols 1973): \"yes\"")
-CODE (U"Formula: \"if col < 4 then log10 (self) else self endif\"")
+CODE (U"Formula: ~ if col < 4 then log10 (self) else self endif")
 CODE (U"Standardize columns")
 NORMAL (U"Before we start with the %canonical correlation analysis we will first have "
 	"a look at the %Pearson correlations of this table and  "
@@ -601,7 +601,7 @@ NORMAL (U"The scores with a dot are zero to numerical precision. In this table t
 CODE (U"selectObject: cca, pols50m")
 CODE (U"To TableOfReal (scores): 3)")
 CODE (U"To Correlation")
-CODE (U"Draw as numbers if: 1, 0, \"decimal\", 2, \"abs(self) > 1e-14")
+CODE (U"Draw as numbers if: 1, 0, \"decimal\", 2, ~ abs(self) > 1e-14")
 ENTRY (U"5. How to predict one dataset from the other")
 NORMAL (U"@@CCA & TableOfReal: Predict...@")
 NORMAL (U"Additional information can be found in @@Weenink (2003)@.")
@@ -1203,7 +1203,7 @@ INTRO (U"Extract those rows from the selected @TableOfReal object whose @@Mahala
 	"quantile range.")
 MAN_END
 
-MAN_BEGIN (U"Covariance & TableOfReal: To TableOfReal (mahalanobis)...", U"djmw", 20151209)
+MAN_BEGIN (U"Covariance & TableOfReal: To TableOfReal (mahalanobis)...", U"djmw", 20170828)
 INTRO (U"Calculate @@Mahalanobis distance@ for the selected @TableOfReal with respect to the "
 	"selected @Covariance object.")
 ENTRY (U"Setting")
@@ -1216,14 +1216,14 @@ NORMAL (U"We first create a table with only one column and 10000 rows and fill i
 	"one dimensional. We next create a table with Mahalanobis distances.")
 CODE (U"n = 100000")
 CODE (U"t0 = Create TableOfReal: \"table\", n, 1")
-CODE (U"Formula:  \"randomGauss(0,1)\"")
+CODE (U"Formula: ~ randomGauss (0, 1)")
 CODE (U"c = To Covariance")
 CODE (U"selectObject: c, t0")
 CODE (U"ts = To TableOfReal (mahalanobis): \"no\"")
 CODE (U"")
 CODE (U"for nsigma to 5")
 CODE1 (U"  selectObject: ts")
-CODE1 (U"  extraction = Extract rows where:  \"self < nsigma\"")
+CODE1 (U"  extraction = Extract rows where:  ~ self < nsigma")
 CODE1 (U"  nr = Get number of rows")
 CODE1 (U"  nrp = nr / n * 100")
 CODE1 (U"  expect = (1 - 2 * gaussQ (nsigma)) * 100")
@@ -1545,8 +1545,8 @@ LIST_ITEM (U"\\bu Draw eigenvector...")
 LIST_ITEM (U"\\bu @@Discriminant: Draw sigma ellipses...|Draw sigma ellipses...@")
 MAN_END
 
-MAN_BEGIN (U"Discriminant analysis", U"djmw", 20151224)
-INTRO (U"This tutorial will show you how to perform discriminant analysis with P\\s{RAAT}")
+MAN_BEGIN (U"Discriminant analysis", U"djmw", 20170829)
+INTRO (U"This tutorial will show you how to perform discriminant analysis with Praat.")
 NORMAL (U"As an example, we will use the dataset from @@Pols et al. (1973)@ "
 	"with the frequencies and levels of the first three formants from the 12 "
 	"Dutch monophthongal vowels as spoken in /h_t/ context by 50 male speakers. "
@@ -1566,7 +1566,7 @@ NORMAL (U"Pols et al. use logarithms of frequency values, we will too. Because "
 	"three columns in dB, it is probably better to standardize the columns. "
 	"The following script summarizes our achievements up till now:")
 CODE (U"table = Create TableOfReal (Pols 1973): \"yes\"")
-CODE (U"Formula: \"if col < 4 then log10 (self) else self fi\"")
+CODE (U"Formula: ~ if col < 4 then log10 (self) else self fi")
 CODE (U"Standardize columns")
 CODE (U"\\#  change the column labels too, for nice plot labels.")
 CODE (U"Set column label (index): 1, \"standardized log (\\% F\\_ \\_ 1\\_ )\"")
@@ -1645,9 +1645,9 @@ CODE (U"selectObject: table")
 CODE (U"numberOfRows = Get number of rows")
 CODE (U"for irow to numberOfRows")
 	CODE1 (U"selectObject: table")
-	CODE1 (U"rowi = Extract rows where: \"row = irow\"")
+	CODE1 (U"rowi = Extract rows where: ~ row = irow")
 	CODE1 (U"selectObject: table")
-	CODE1 (U"rest = Extract rows where: \"row <> irow\"")
+	CODE1 (U"rest = Extract rows where: ~ row <> irow")
 	CODE1 (U"discriminant = To Discriminant")
 	CODE1 (U"plusObject: rowi")
 	CODE1 (U"classification = To ClassificationTable: \"yes\", \"yes\"")
@@ -1820,7 +1820,7 @@ NORMAL (U"The dimension of the Discriminant and the Configuration must conform i
 NORMAL (U"See also @@Eigen & TableOfReal: Project... at .")
 MAN_END
 
-MAN_BEGIN (U"Discriminant & TableOfReal: To TableOfReal (mahalanobis)...", U"djmw", 20140509)
+MAN_BEGIN (U"Discriminant & TableOfReal: To TableOfReal (mahalanobis)...", U"djmw", 20170828)
 INTRO (U"Calculate @@Mahalanobis distance at s for the selected @TableOfReal with respect to one group in the "
 	"selected @Discriminant object.")
 ENTRY (U"Settings")
@@ -1835,7 +1835,7 @@ NORMAL (U"Calculate the number of datapoints that are within the one-sigma elips
 	"the number of data points that are in the overlapping area. ")
 NORMAL (U"Suppose the group labels are \\o/ and \\yc.")
 CODE (U"pols50m = Create TableOfReal (Pols 1973): \"no\"")
-CODE (U"Formula: \"log10(self)\"")
+CODE (U"Formula: ~ log10 (self)")
 CODE (U"discriminant = To Discriminant")
 CODE (U"selectObject: pols50m, discriminant")
 CODE (U"t1 = To TableOfReal (mahalanobis): \"\\bso/\", \"no\"")
@@ -1843,7 +1843,7 @@ CODE (U"selectObject: pols50m, discriminant")
 CODE (U"t2 = To TableOfReal (mahalanobis): \"\\bsyc\", \"no\"")
 NORMAL (U"Now we count when both the t1 and t2 values are smaller than 1 (sigma):")
 CODE (U"Copy: \"tr\"")
-CODE (U"Formula: \"Object_'t1'[] < 1 and Object_'t2'[] < 1\"")
+CODE (U"Formula: ~ object [t1] < 1 and object [t2] < 1")
 CODE (U"Extract rows where column: 1, \"equal to\", 1")
 CODE (U"no = Get number of rows\"")
 MAN_END
@@ -3643,7 +3643,7 @@ DEFINITION (U"The method of %%spectral subtraction% was defined in @@Boll (1979)
 	"after a script by Ton Wempe.")
 MAN_END
 
-MAN_BEGIN (U"Sound: Draw where...", U"djmw", 20140509)
+MAN_BEGIN (U"Sound: Draw where...", U"djmw", 20170829)
 INTRO (U"A command to draw only those parts of a @Sound where a condition holds.")
 ENTRY (U"Settings")
 SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (5), U""
@@ -3664,38 +3664,38 @@ DEFINITION (U"determines the part of the sound that will be drawn. All parts whe
 	"This formula may ##not# contain references to the sampling of the sound, i.e. don't use 'col', 'x1', 'dx' and 'ncol' in it.")
 ENTRY (U"Example 1")
 NORMAL (U"The following script draws all amplitudes larger than one in red.")
-CODE (U"Create Sound from formula: \"s\", \"Mono\", 0, 1, 2000, \"1.8*sin(2*pi*5*x)+randomGauss(0,0.1)\"")
+CODE (U"Create Sound from formula: \"s\", \"Mono\", 0, 1, 2000, ~ 1.8*sin(2*pi*5*x)+randomGauss(0,0.1)")
 CODE (U"Colour: \"Red\"")
-CODE (U"Draw where: 0, 0, -2, 2, \"no\", \"Curve\", \"abs(self)>1\"")
+CODE (U"Draw where: 0, 0, -2, 2, \"no\", \"Curve\", ~ abs(self)>1")
 CODE (U"Colour: \"Black\"")
-CODE (U"Draw where: 0, 0, -2, 2, \"yes\", \"Curve\", \"not (abs(self)>1)\"")
+CODE (U"Draw where: 0, 0, -2, 2, \"yes\", \"Curve\", ~ not (abs(self)>1)")
 SCRIPT (8, 3,
-	U"Create Sound from formula: \"s\", \"Mono\", 0, 1, 2000, \"1.8*sin(2*pi*5*x)+randomGauss(0,0.1)\"\n"
+	U"Create Sound from formula: \"s\", \"Mono\", 0, 1, 2000, ~ 1.8*sin(2*pi*5*x)+randomGauss(0,0.1)\n"
 	"Colour: \"Red\"\n"
-	"Draw where: 0, 0, -2, 2, \"no\", \"Curve\", \"abs(self)>1\"\n"
+	"Draw where: 0, 0, -2, 2, \"no\", \"Curve\", ~ abs(self)>1\n"
 	"Colour: \"Black\"\n"
-	"Draw where:  0, 0, -2, 2, \"yes\", \"Curve\", \"not (abs(self)>1)\"\n"
+	"Draw where:  0, 0, -2, 2, \"yes\", \"Curve\", ~ not (abs(self)>1)\n"
 	"Remove\n"
 )
 ENTRY (U"Example 2")
 NORMAL (U"Draw the second half of a sound:")
-CODE (U"Draw where: 0, 0, -1, 1, \"no\", \"Curve\", \"x > xmin + (xmax - xmin) / 2\"")
+CODE (U"Draw where: 0, 0, -1, 1, \"no\", \"Curve\", ~ x > xmin + (xmax - xmin) / 2")
 ENTRY (U"Example 3")
 NORMAL (U"Draw only positive amplitudes:")
-CODE (U"Draw where: 0, 0, -1, 1, \"no\", \"Curve\", \"self>0\"")
+CODE (U"Draw where: 0, 0, -1, 1, \"no\", \"Curve\", ~ self > 0")
 ENTRY (U"Example 4")
 NORMAL (U"Draw parts where pitch is larger than 300 Hz in red:")
 CODE (U"s = selected (\"Sound\")")
 CODE (U"p = To Pitch: 0, 75, 600")
-CODE (U"pt = Down to PitchTier\"")
+CODE (U"pt = Down to PitchTier")
 CODE (U"selectObject: s")
-CODE (U"Colour: \"Red\"")
-CODE (U"Draw where: 0, 0, -1, 1, \"yes\", \"Curve\", \"Object_'pt'(x) > 300\"")
-CODE (U"Colour: \"Black\"")
-CODE (U"Draw where: 0, 0, -1, 1, \"yes\", \"Curve\", \"not (Object_'pt'(x) > 300)\"")
+CODE (U"Colour: ~ Red")
+CODE (U"Draw where: 0, 0, -1, 1, \"yes\", \"Curve\", ~ object (pt, x) > 300")
+CODE (U"Colour: ~ Black")
+CODE (U"Draw where: 0, 0, -1, 1, \"yes\", \"Curve\", ~ not (object (pt, x) > 300)")
 MAN_END
 
-MAN_BEGIN (U"Sound: Fade in...", U"djmw", 20140117)
+MAN_BEGIN (U"Sound: Fade in...", U"djmw", 20170829)
 INTRO (U"A command to gradually increase the amplitude of a selected @Sound.")
 ENTRY (U"Settings")
 TAG (U"##Channel")
@@ -3713,11 +3713,11 @@ ENTRY (U"Cross-fading two sounds")
 NORMAL (U"The following script cross-fades two sounds s1 and s2 at time 1 second and leaves the result in s2.")
 CODE1 (U"crossFTime = 0.5")
 CODE1 (U"t = 1")
-CODE1 (U"Create Sound from formula: \"s1\", 1, 0, 2, 44100, \"sin(2*pi*500*x)\"")
+CODE1 (U"s = Create Sound from formula: \"s1\", 1, 0, 2, 44100, ~ sin(2*pi*500*x)")
 CODE1 (U"Fade out: 0, t-crossFTime/2, crossFTime, \"yes\"")
-CODE1 (U"Create Sound from formula: \"s2\", 1, 0, 2, 44100, \"sin(2*pi*1000*x)\"")
+CODE1 (U"Create Sound from formula: \"s2\", 1, 0, 2, 44100, ~ sin(2*pi*1000*x)")
 CODE1 (U"Fade in.: 0, t-crossFTime/2, crossFTime, \"yes\"")
-CODE1 (U"Formula: \"self+Sound_s1[]\"")
+CODE1 (U"Formula: ~ self + object [s]")
 MAN_END
 
 MAN_BEGIN (U"Sound: Fade out...", U"djmw", 20121010)
@@ -3735,21 +3735,19 @@ ENTRY (U"Algorithm")
 NORMAL (U"Multiplication with the first half period of a (1+cos(%%x%))/2 function.")
 MAN_END
 
-MAN_BEGIN (U"Sound: Filter (gammatone)...", U"djmw", 19980712)
+MAN_BEGIN (U"Sound: Filter (gammatone)...", U"djmw", 20170829)
 INTRO (U"A command to filter a Sound by a fourth order gammatone bandpass filter.")
 ENTRY (U"Settings")
 TAG (U"##Centre frequency (Hz)#, ##Bandwidth (Hz)#")
 DEFINITION (U"determine the passband of the filter.")
 ENTRY (U"Algorithm")
-NORMAL (U"The impulse response of the filter is a 4-th order @@gammatone at . This "
-	"filter is implemented as a simple 8-th order recursive digital filter with "
-	"4 zeros and 8 poles (these 8 poles consist of one conjugate pole pair to the "
-	"4-th power). In the Z-domain its formula is: ")
-FORMULA (U"%#H (%z) = (1 + \\su__%i=1..4_ %a__%i_%z^^%\\--i^) / "
-	"(1 + \\su__%j=1..8_ %b__%j_%z^^%\\--j^)")
-NORMAL (U"The derivation of the filter coefficients %a__%i_ and %b__%j_ is "
-	"according to @@Slaney (1993)@. "
+NORMAL (U"The impulse response of the filter is a 4-th order @@gammatone at . The "
+	"filter is implemented as the convolution of the gammatone with the sound. "
 	"The gain of the filter is scaled to unity at the centre frequency.")
+ENTRY (U"Remark")
+NORMAL (U"The old implementation with a simple 8-th order recursive digital filter with "
+	"4 zeros and 8 poles (these 8 poles consist of one conjugate pole pair to the "
+	"4-th power) as suggested by  @@Slaney (1993)@ was not stable for low frequencies. ")
 MAN_END
 
 MAN_BEGIN (U"Sound: Play as frequency shifted...", U"djmw", 20140106)
@@ -3798,7 +3796,7 @@ LIST_ITEM (U"2. We perform a filter bank analysis on a linear frequency scale. "
 	"The bandwidth of the filters depends on the measured pitch (see @@Sound & Pitch: To Spectrogram...@ for details).")
 MAN_END
 
-MAN_BEGIN (U"Sound: Paint where...", U"djmw", 20140509)
+MAN_BEGIN (U"Sound: Paint where...", U"djmw", 20170829)
 INTRO (U"A command to paint only those parts of a @Sound where a condition holds. The painted area is the area "
 	"between the Sound and a horizontal line at a certain level.")
 ENTRY (U"Settings")
@@ -3827,39 +3825,39 @@ ENTRY (U"Example 1")
 NORMAL (U"The following script paints the area under a sine curve in red and the area above in green."
 	"For the first paint the horizontal line is at y=-1, for the second paint the line is at y=+1. "
 	"The formula always evaluates to true.")
-CODE (U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, \"0.5*sin(2*pi*5*x)\"")
-CODE (U"Paint where: \"Red\", 0, 0, -1, 1, -1, \"yes\", \"1\"")
-CODE (U"Paint where: \"Green\", 0, 0, -1, 1, 1, \"no\", \"1\"")
+CODE (U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, ~ 0.5*sin(2*pi*5*x)")
+CODE (U"Paint where: \"Red\", 0, 0, -1, 1, -1, \"yes\", ~ 1")
+CODE (U"Paint where: \"Green\", 0, 0, -1, 1, 1, \"no\", ~ 1 ")
 SCRIPT (8, 5,
-	U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, \"0.5*sin(2*pi*5*x)\"\n"
-	"Paint where: \"Red\", 0, 0, -1, 1, -1, \"no\", \"1\"\n"
-	"Paint where: \"Green\", 0, 0, -1, 1, 1, \"yes\", \"1\"\n"
+	U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, ~ 0.5*sin(2*pi*5*x)\n"
+	"Paint where: \"Red\", 0, 0, -1, 1, -1, \"no\", ~ 1\n"
+	"Paint where: \"Green\", 0, 0, -1, 1, 1, \"yes\", ~ 1\n"
 	"Remove\n")
 ENTRY (U"Example 2")
 NORMAL (U"The following script paints the area below zero in red and the area above in green."
 	"The horizontal line is now always at y=0 and we use the formula to differentiate the areas.")
-CODE (U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, \"0.5*sin(2*pi*5*x)\"")
-CODE (U"Paint where: \"Red\", 0, 0, -1, 1, 0, \"no\", \"self>0\"")
-CODE (U"Paint where: \"Green\", 0, 0, -1, 1, 0, \"yes\", \"self<0\"")
+CODE (U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, ~ 0.5*sin(2*pi*5*x)")
+CODE (U"Paint where: \"Red\", 0, 0, -1, 1, 0, \"no\", ~ self > 0")
+CODE (U"Paint where: \"Green\", 0, 0, -1, 1, 0, \"yes\", ~ self < 0")
 SCRIPT (8, 5,
-	U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, \"0.5*sin(2*pi*5*x)\"\n"
-	"Paint where: \"Red\", 0, 0, -1, 1, 0, \"no\", \"self<0\"\n"
-	"Paint where: \"Green\", 0, 0, -1, 1, 0, \"yes\", \"self>0\"\n"
+	U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, ~ 0.5*sin(2*pi*5*x)\n"
+	"Paint where: \"Red\", 0, 0, -1, 1, 0, \"no\", ~ self < 0\n"
+	"Paint where: \"Green\", 0, 0, -1, 1, 0, \"yes\", ~ self > 0\n"
 	"removeObject: s\n")
 ENTRY (U"Example 3")
 NORMAL (U"To give an indication that the area under a 1/x curve between the points %a and %b and the area "
 	"between %c and %d are equal if %b/%a = %d/%c. For example, for %a=1, %b=2, %c=4 and %d=8: ")
-CODE (U"Create Sound from formula: \"1dx\", \"Mono\", 0, 20, 100, \"1/x\"")
+CODE (U"Create Sound from formula: \"1dx\", \"Mono\", 0, 20, 100, ~ 1.0 / x ")
 CODE (U"Draw: 0, 20, 0, 1.5, \"yes\", \"Curve\"")
-CODE (U"Paint where: \"Grey\", 0, 20, 0, 1.5, 0, \"yes\", \"(x >= 1 and x <2) or (x>=4 and x<8)\"")
+CODE (U"Paint where: \"Grey\", 0, 20, 0, 1.5, 0, \"yes\", ~ (x >= 1 and x < 2) or (x >= 4 and x < 8)")
 CODE (U"One mark bottom: 1, \"yes\", \"yes\", \"no\", \"\"")
 CODE (U"One mark bottom: 2, \"yes\", \"yes\", \"no\", \"\"")
 CODE (U"One mark bottom: 4, \"yes\", \"yes\", \"no\", \"\"")
 CODE (U"One mark bottom: 8, \"yes\", \"yes\", \"no\", \"\"")
 SCRIPT (8, 5,
-	U"s = Create Sound from formula: \"1dx\", \"Mono\", 0, 20, 100, \"1/x\"\n"
+	U"s = Create Sound from formula: \"1dx\", \"Mono\", 0, 20, 100, ~ 1.0 / x\n"
 	"Draw: 0, 20, 0, 1.5, \"yes\", \"Curve\"\n"
-	"Paint where: \"Grey\", 0, 20, 0, 1.5, 0, \"yes\", \"(x >= 1 and x <2) or (x>=4 and x<8)\"\n"
+	"Paint where: \"Grey\", 0, 20, 0, 1.5, 0, \"yes\", ~ (x >= 1 and x < 2) or (x >= 4 and x < 8)\n"
 	"One mark bottom: 1, \"yes\", \"yes\", \"no\", \"\"\n"
 	"One mark bottom: 2, \"yes\", \"yes\", \"no\", \"\"\n"
 	"One mark bottom: 4, \"yes\", \"yes\", \"no\", \"\"\n"
@@ -3867,7 +3865,7 @@ SCRIPT (8, 5,
 	"removeObject: s\n")
 MAN_END
 
-MAN_BEGIN (U"Sounds: Paint enclosed...", U"djmw", 20140509)
+MAN_BEGIN (U"Sounds: Paint enclosed...", U"djmw", 20170829)
 INTRO (U"Paints the area between the two selected @@Sound at s. ")
 ENTRY (U"Settings")
 SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (4), U""
@@ -3885,19 +3883,19 @@ TAG (U"##Vertical range")
 DEFINITION (U"defines the vertical limits, larger amplitudes will be clipped.")
 ENTRY (U"Example")
 NORMAL (U"The following script paints the area enclosed between a sine tone of 5 Hz and the straight line %y = %x/2.")
-CODE (U"s1 = Create Sound from formula: \"sine\", \"Mono\", 0, 1, 10000, \"1/2 * sin(2*pi*5*x)\"")
-CODE (U"s2 = Create Sound from formula: \"line\", \"Mono\", 0, 1, 10000, \"x / 2\"")
+CODE (U"s1 = Create Sound from formula: \"sine\", \"Mono\", 0, 1, 10000, ~ 1/2 * sin(2*pi*5*x)\"")
+CODE (U"s2 = Create Sound from formula: \"line\", \"Mono\", 0, 1, 10000, ~ x / 2")
 CODE (U"plusObject (s1)")
-CODE (U"Paint enclosed: \"Grey\", 0, 0, -1, 1, \"yes\"")
+CODE (U"Paint enclosed: \"Grey\", 0, 0, -1, 1, ~ yes")
 SCRIPT ( 4, 2,
-	 U"s1 = Create Sound from formula: \"sine\", \"Mono\", 0, 1, 10000, \"1/2 * sin(2*pi*5*x)\"\n"
-	"s2 = Create Sound from formula: \"line\", \"Mono\", 0, 1, 10000, \"x / 2\"\n"
+	 U"s1 = Create Sound from formula: \"sine\", \"Mono\", 0, 1, 10000, ~ 1/2 * sin(2*pi*5*x)\n"
+	"s2 = Create Sound from formula: \"line\", \"Mono\", 0, 1, 10000, ~ x / 2\n"
 	"selectObject: s1, s2\n"
 	"Paint enclosed: \"Grey\", 0, 0, -1, 1, \"yes\"\n"
 	"removeObject: s1, s2\n")
 MAN_END
 
-MAN_BEGIN (U"Sound: To Polygon...", U"djmw", 20140509)
+MAN_BEGIN (U"Sound: To Polygon...", U"djmw", 20170829)
 INTRO (U"A command that creates a @@Polygon@ from a selected @@Sound@, where the Polygon's "
 	" points are defined by the (%time, %amplitude) pairs of the sound. ")
 ENTRY (U"Settings")
@@ -3912,7 +3910,7 @@ DEFINITION (U"defines the y-value of the first and last point of the Polygon. Th
 	" draw a closed Polygon with the horizontal connection line at any position you like. ")
 ENTRY (U"Example")
 NORMAL (U"The following script paints the area under a sound curve in red and the area above in green.")
-CODE (U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, \"0.5*sin(2*pi*5*x)\"")
+CODE (U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, ~ 0.5*sin(2*pi*5*x)")
 CODE (U"\\# Connection y-value is at amplitude -1: area under the curve.")
 CODE (U"p1 = To Polygon: 1, 0, 0, -1, 1, -1")
 CODE (U"Paint: \"{1,0,0}\", 0, 0, -1, 1")
@@ -3921,7 +3919,7 @@ CODE (U"\\# Connection y-value is now at amplitude 1: area above the curve.")
 CODE (U"p2 = To Polygon: 1, 0, 0, -1, 1, 1")
 CODE (U"Paint: \"{0,1,0}\", 0, 0, -1, 1")
 SCRIPT (4.5, 2,
-	U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, \"0.5*sin(2*pi*5*x)\"\n"
+	U"s = Create Sound from formula: \"s\", 1, 0, 1, 10000, ~ 0.5*sin(2*pi*5*x)\n"
 	"p1 = To Polygon: 1, 0, 0, -1, 1, -1\n"
 	"Paint: \"{1,0,0}\", 0, 0, -1, 1\n"
 	"selectObject: s\n"
@@ -4491,7 +4489,7 @@ SCRIPT (5,3, U"pb = Create formant table (Peterson & Barney 1952)\n"
 )
 MAN_END
 
-MAN_BEGIN (U"Table: Line graph where...", U"djmw", 20140509)
+MAN_BEGIN (U"Table: Line graph where...", U"djmw", 20170829)
 INTRO (U"Draws a line graph from the data in a column of the selected @Table. In a line plot the horizontal axis can have a nominal scale or a numeric scale. The data point are connected by line segments.")
 ENTRY (U"Settings")
 SCRIPT (7, Manual_SETTINGS_WINDOW_HEIGHT (8), U""
@@ -4551,9 +4549,9 @@ CODE (U"Text left: 1, \"Prop. of voiced responses\"")
 
 SCRIPT (5,3, U"ganong = Create Table (Ganong 1980)\n"
 	"Dotted line\n"
-	"Line graph where: \"dash-tash\", 0, 1, \"VOT\", -20, 20, \"wn\", 0, 0, \"1\"\n"
+	"Line graph where: \"dash-tash\", 0, 1, \"VOT\", -20, 20, \"wn\", 0, 0, ~1\n"
 	"Dashed line\n"
-	"Line graph where: \"dask-task\", 0, 1, \"VOT\", -20, 20, \"nw\", 0, 0, \"1\"\n"
+	"Line graph where: \"dask-task\", 0, 1, \"VOT\", -20, 20, \"nw\", 0, 0, ~1\n"
 	"Draw inner box\n"
 	"One mark bottom: 2.5, 0, 1, 0, \"+2.5\"\n"
 	"One mark bottom: -2.5, 1, 1, 0, \"\"\n"
@@ -4569,11 +4567,11 @@ SCRIPT (5,3, U"ganong = Create Table (Ganong 1980)\n"
 )
 NORMAL (U"As an example of what happens if you don't supply an argument for the \"Horizontal column\" we will use the same table as for the previous plot. However the resulting plot may not be as meaningful (note that the horizontal nominal scale makes all points equidistant in the horizontal direction.)")
 CODE (U"Dotted line\")\n")
-CODE (U"Line graph where: \"dash-tash\", 0, 1, \"\", 0, 0, \"wn\", 0, 1, \"1\"")
+CODE (U"Line graph where: \"dash-tash\", 0, 1, \"\", 0, 0, \"wn\", 0, 1, ~ 1")
 CODE (U"One mark bottom: 1, 0, 1, 0, \"Short VOT\"")
 SCRIPT (5,3, U"ganong = Create Table (Ganong 1980)\n"
 	"Dotted line\n"
-	"Line graph where: \"dash-tash\", 0, 1, \"\", 0, 0, \"wn\", 0, 1, \"1\"\n"
+	"Line graph where: \"dash-tash\", 0, 1, \"\", 0, 0, \"wn\", 0, 1, ~1\n"
 	"One mark bottom: 1, 0, 1, 0, \"Short VOT\"\n"
 	"removeObject: ganong\n"
 )
diff --git a/dwtools/praat_BSS_init.cpp b/dwtools/praat_BSS_init.cpp
index 5aaa404..1c9c377 100644
--- a/dwtools/praat_BSS_init.cpp
+++ b/dwtools/praat_BSS_init.cpp
@@ -176,7 +176,11 @@ DO
 	CREATE_ONE_END (name)
 }
 
-FORM (NEW1_MixingMatrix_createSimple, U"Create simple MixingMatrix", U"MixingMatrix") {
+DIRECT (HELP_MixingMatrix_help) {
+	HELP (U"MixingMatrix");
+}
+
+FORM (NEW1_MixingMatrix_createSimple, U"Create simple MixingMatrix", U"Create simple MixingMatrix...") {
 	WORDVAR (name, U"Name", U"mm")
 	NATURALVAR (numberOfInputs, U"Number of inputs", U"2")
 	NATURALVAR (numberOfOutputs, U"Number of outputs", U"2")
@@ -333,18 +337,37 @@ DO
 	MODIFY_FIRST_OF_TWO_END
 }
 
+DIRECT (HELP_Diagonalizer_help) {
+	HELP (U"Diagonalizer");
+}
+
 DIRECT (NEW_Diagonalizer_to_MixingMatrix) {
 	CONVERT_EACH (Diagonalizer)
 		autoMixingMatrix result = Diagonalizer_to_MixingMatrix (me);
 	CONVERT_EACH_END (my name)
 }
 
+FORM (MODIFY_MixingMatrix_multiplyInputChannel, U"MixingMatrix: Multiply input channel", U"MixingMatrix: Multiply input channel...") {
+	NATURAL4 (inputChannel, U"Input channel number", U"1")
+	REAL4  (value, U"Multiply by", U"1.0")
+	OK
+DO
+	MODIFY_EACH (MixingMatrix)
+		MixingMatrix_multiplyInputChannel (me, inputChannel, value);
+	MODIFY_EACH_END
+}
+
 DIRECT (MODIFY_MixingMatrix_setStandardChannelInterpretation) {
 	MODIFY_EACH (MixingMatrix)
 		MixingMatrix_setStandardChannelInterpretation (me);
 	MODIFY_EACH_END
 }
 
+DIRECT (NEW1_MixingMatrix_to_Diagonalizer) {
+	CONVERT_EACH (MixingMatrix)
+		autoDiagonalizer result = MixingMatrix_to_Diagonalizer (me);
+	CONVERT_EACH_END (my name)
+}
 FORM (NEW_Sound_to_MixingMatrix, U"Sound: To MixingMatrix", nullptr) {
 	praat_TimeFunction_RANGE (fromTime, toTime)
 	NATURALVAR (numberOfCrossCorrelations, U"Number of cross-correlations", U"40")
@@ -362,6 +385,23 @@ DO
 	CONVERT_EACH_END (my name)
 }
 
+FORM (MODIFY_Sound_and_MixingMatrix_improveUnmixing, U"", nullptr) {
+	praat_TimeFunction_RANGE (fromTime, toTime)
+	NATURALVAR (numberOfCrossCorrelations, U"Number of cross-correlations", U"40")
+	POSITIVEVAR (lagTime, U"Lag step (s)", U"0.002")
+	LABEL (U"", U"Iteration parameters")
+	NATURALVAR (maximumNumberOfIterations, U"Maximum number of iterations", U"100")
+	POSITIVEVAR (tolerance, U"Tolerance", U"0.001")
+	OPTIONMENUVAR (diagonalizationMethod, U"Diagonalization method", 2)
+		OPTION (U"qdiag")
+		OPTION (U"ffdiag")
+	OK
+DO
+		MODIFY_FIRST_OF_TWO (MixingMatrix, Sound)
+		MixingMatrix_and_Sound_improveUnmixing (me, you, fromTime, toTime, numberOfCrossCorrelations, lagTime, maximumNumberOfIterations, tolerance, diagonalizationMethod);
+		MODIFY_FIRST_OF_TWO_END
+}
+
 FORM (NEW_Sound_to_CrossCorrelationTableList, U"Sound: To CrossCorrelationTableList", nullptr) {
 	praat_TimeFunction_RANGE (fromTime, toTime)
 	NATURALVAR (numberOfCrossCorrelations, U"Number of cross-correlations", U"40")
@@ -457,9 +497,10 @@ void praat_BSS_init () {
 	praat_addAction1 (classCrossCorrelationTableList, 1, U"Extract CrossCorrelationTable...", 0, 0, NEW_CrossCorrelationTableList_extractCrossCorrelationTable);
 	praat_addAction1 (classCrossCorrelationTableList, 1, U"Get diagonality measure...", 0, 0, REAL_CrossCorrelationTableList_getDiagonalityMeasure);
 	praat_addAction1 (classCrossCorrelationTableList, 0, U"To Diagonalizer...", 0, 0, NEW_CrossCorrelationTableList_to_Diagonalizer);
-
+	
+	praat_addAction1 (classDiagonalizer, 0, U"Diagonalizer help", 0, 0, HELP_Diagonalizer_help);
 	praat_TableOfReal_init3 (classDiagonalizer);
-	praat_addAction1 (classDiagonalizer, 0, U"To MixingMatrix", 0, 0,NEW_Diagonalizer_to_MixingMatrix);
+	praat_addAction1 (classDiagonalizer, 0, U"To MixingMatrix", 0, 0, NEW_Diagonalizer_to_MixingMatrix);
 
 	praat_addAction1 (classEEG, 0, U"To Sound (mc modulated)...", U"To ERPTier...", praat_HIDDEN, NEW_EEG_to_Sound_modulated);
 	praat_addAction1 (classEEG, 0, U"To Sound (frequency shifted)...", U"To ERPTier...", 0, NEW_EEG_to_Sound_frequencyShifted);
@@ -474,9 +515,13 @@ void praat_BSS_init () {
 	praat_addAction2 (classEEG, 1, classPCA, 1, U"To EEG (principal components)...", 0, 0, NEW1_EEG_and_PCA_to_EEG_principalComponents);
 	praat_addAction2 (classEEG, 1, classPCA, 1, U"To EEG (whiten)...", 0, 0, NEW1_EEG_and_PCA_to_EEG_whiten);
 
+	praat_addAction1 (classMixingMatrix, 0, U"MixingMatrix help", 0, 0, HELP_MixingMatrix_help);
 	praat_TableOfReal_init3 (classMixingMatrix);
-	//praat_addAction1 (classMixingMatrix, 0, U"-- set MixingMatrix --", U"Set column label (label)...", praat_DEPTH_1, nullptr);
+		praat_addAction1 (classMixingMatrix, 0, U"Multiply input channel...", U"Set value...", praat_DEPTH_1, MODIFY_MixingMatrix_multiplyInputChannel);
+		praat_removeAction (classMixingMatrix, nullptr, nullptr, U"Sort by label...");	
+		praat_removeAction (classMixingMatrix, nullptr, nullptr, U"Sort by column...");	
 	praat_addAction1 (classMixingMatrix, 0, U"Set standard channel interpretation", U"Set column label (label)...", praat_DEPTH_1, MODIFY_MixingMatrix_setStandardChannelInterpretation);
+	praat_addAction1 (classMixingMatrix, 0, U"To Diagonalizer", U"To Matrix", praat_DEPTH_1, NEW1_MixingMatrix_to_Diagonalizer);
 
 	praat_addAction1 (classSound, 0, U"To MixingMatrix...",  U"Resample...", praat_HIDDEN + praat_DEPTH_1, NEW_Sound_to_MixingMatrix);
     praat_addAction1 (classSound, 0, U"To CrossCorrelationTable...",  U"Resample...", 1, NEW_Sound_to_CrossCorrelationTable);
@@ -494,6 +539,7 @@ void praat_BSS_init () {
 	praat_addAction2 (classSound, 1, classMixingMatrix, 1, U"Mix", 0, 0, NEW1_Sound_and_MixingMatrix_mix);
 	praat_addAction2 (classSound, 1, classMixingMatrix, 1, U"Mix part...", 0, 0, NEW1_Sound_and_MixingMatrix_mixPart);
 	praat_addAction2 (classSound, 1, classMixingMatrix, 1, U"Unmix", 0, 0, NEW1_Sound_and_MixingMatrix_unmix);
+	praat_addAction2 (classSound, 1, classMixingMatrix, 1, U"Improve unmixing...", 0, 0, MODIFY_Sound_and_MixingMatrix_improveUnmixing);
 
 	praat_addAction2 (classSound, 1, classPCA, 1, U"To Sound (white channels)...", 0 , 0, NEW1_Sound_and_PCA_whitenChannels);
 	praat_addAction2 (classSound, 1, classPCA, 1, U"To Sound (principal components)...", 0 , 0, NEW1_Sound_and_PCA_principalComponents);
diff --git a/dwtools/praat_DataModeler_init.cpp b/dwtools/praat_DataModeler_init.cpp
index d5ba17b..758261a 100644
--- a/dwtools/praat_DataModeler_init.cpp
+++ b/dwtools/praat_DataModeler_init.cpp
@@ -703,7 +703,7 @@ FORM (REAL_FormantModeler_getParameterValue, U"FormantModeler: Get parameter val
 DO
 	NUMBER_ONE (FormantModeler)
 		double result = FormantModeler_getParameterValue (me, formantNumber, parameterNumber);
-	NUMBER_ONE_END (U" (= parameter[", parameterNumber, U"] for F", formantNumber, U")")
+	NUMBER_ONE_END (U" (= parameter ", parameterNumber, U" for F", formantNumber, U")")
 }
 
 FORM (INFO_FormantModeler_getParameterStatus, U"FormantModeler: Get parameter status", nullptr) {
@@ -714,7 +714,7 @@ DO
 	STRING_ONE (FormantModeler)
 		int status = FormantModeler_getParameterStatus (me, formantNumber, parameterNumber);
 		const char32 *result = Melder_cat (status == DataModeler_PARAMETER_FREE ? U"Free" : (status == DataModeler_PARAMETER_FIXED ? U"Fixed" :
-		U"Undefined"), U" (= parameter[", parameterNumber, U"] for F", formantNumber, U")");
+		U"Undefined"), U" (= status of parameter ", parameterNumber, U" for F", formantNumber, U")");
 	STRING_ONE_END
 }
 
@@ -725,7 +725,7 @@ FORM (REAL_FormantModeler_getParameterStandardDeviation, U"FormantModeler: Get p
 DO
 	NUMBER_ONE (FormantModeler)
 		double result = FormantModeler_getParameterStandardDeviation (me, formantNumber, parameterNumber);
-	NUMBER_ONE_END (U" (= parameter[", parameterNumber, U"] for F", formantNumber, U")")
+	NUMBER_ONE_END (U" (= standard deviation of parameter ", parameterNumber, U" for F", formantNumber, U")")
 }
 
 
@@ -736,10 +736,10 @@ FORM (REAL_FormantModeler_getVarianceOfParameters, U"FormantModeler: Get varianc
 	INTEGERVAR (toParameter, U"right Parameter range", U"0")
 	OK
 DO
-	long nofp;
+	long numberOfFreeParameters;
 	NUMBER_ONE (FormantModeler)
-		double result = FormantModeler_getVarianceOfParameters (me, fromFormant, toFormant, fromParameter, toParameter, &nofp);
-	NUMBER_ONE_END (U" (for ", nofp, U" free parameters.)")
+		double result = FormantModeler_getVarianceOfParameters (me, fromFormant, toFormant, fromParameter, toParameter, & numberOfFreeParameters);
+	NUMBER_ONE_END (U" (for ", numberOfFreeParameters, U" free parameters.)")
 }
 
 
@@ -760,7 +760,7 @@ FORM (REAL_FormantModeler_getResidualSumOfSquares, U"FormantModeler: Get residua
 DO
 	NUMBER_ONE (FormantModeler)
 		double result = FormantModeler_getResidualSumOfSquares (me, formantNumber, nullptr);
-	NUMBER_ONE_END (U" Hz^2,  (= RSS of F", formantNumber, U")")
+	NUMBER_ONE_END (U" Hz^2,  (= residual sum of squares of F", formantNumber, U")")
 }
 
 FORM (REAL_FormantModeler_getStandardDeviation, U"FormantModeler: Get formant standard deviation", nullptr) {
@@ -769,7 +769,7 @@ FORM (REAL_FormantModeler_getStandardDeviation, U"FormantModeler: Get formant st
 DO
 	NUMBER_ONE (FormantModeler)
 		double result = FormantModeler_getStandardDeviation (me, formantNumber);
-	NUMBER_ONE_END (U" Hz (= std. dev. of F", formantNumber, U")")
+	NUMBER_ONE_END (U" Hz (= standard deviation of F", formantNumber, U")")
 }
 
 FORM (INFO_FormantModeler_reportChiSquared, U"FormantModeler: Report chi squared", nullptr) {
diff --git a/dwtools/praat_David_init.cpp b/dwtools/praat_David_init.cpp
index b8033bc..298bd3d 100644
--- a/dwtools/praat_David_init.cpp
+++ b/dwtools/praat_David_init.cpp
@@ -4771,11 +4771,11 @@ FORM (INFO_Polynomial_evaluate_z, U"Polynomial: Get value (complex)", U"Polynomi
 	REALVAR (y, U"Imaginary part", U"0.0")
 	OK
 DO
-	dcomplex p, z = dcomplex_create (x, y);
+	dcomplex z { x, y };
 	INFO_ONE (Polynomial)
-		Polynomial_evaluate_z (me, & z, & p);
+		dcomplex result = Polynomial_evaluate_z (me, z);
 		MelderInfo_open ();
-			MelderInfo_writeLine (p.re, U" + ", p.im, U" i");
+		MelderInfo_writeLine (result);
 		MelderInfo_close ();	
 	INFO_ONE_END
 }
@@ -4853,7 +4853,7 @@ DO
 	INFO_ONE (Roots)
 		dcomplex z = Roots_getRoot (me, rootNumber);
 		MelderInfo_open ();
-			MelderInfo_writeLine (z.re, U"+", z.im,  U"i");
+		MelderInfo_writeLine (z);
 		MelderInfo_close ();
 	INFO_ONE_END
 }
@@ -4946,7 +4946,7 @@ FORM (REAL_Praat_getInvTukeyQ, U"Get invTukeyQ", nullptr) {
 	NATURALVAR (numberOfRows, U"Number of rows", U"1")
 	OK
 DO
-	REQUIRE (probability >= 0 && probability <= 1, U"Probability must be in (0,1).")
+	REQUIRE (probability >= 0.0 && probability <= 1.0, U"The probability should be in the interval [0, 1].")
 	double result = NUMinvTukeyQ (probability, numberOfMeans, degreesOfFreedon, numberOfRows);
 	Melder_information (result, U" (inv tukeyQ)");
 END }
@@ -7071,12 +7071,13 @@ FORM (NEW_TextGrid_to_DurationTier, U"TextGrid: To DurationTier", U"TextGrid: To
 	POSITIVEVAR (timeScaleFactor, U"Time scale factor", U"2.0")
 	POSITIVEVAR (leftTransitionDuration, U"Left transition duration (s)", U"1e-10")
 	POSITIVEVAR (rightTransitionDuration, U"Right transition duration (s)", U"1e-10")
-	OPTIONMENU_ENUM4 (___, U"Scale intervals whose label ", kMelder_string, DEFAULT)
+	OPTIONMENU_ENUM4 (scaleIntervalsWhoseLabel___, U"Scale intervals whose label... ", kMelder_string, DEFAULT)
 	SENTENCE4 (___theText, U"...the text", U"hi")
 	OK
 DO
 	CONVERT_EACH (TextGrid)
-		autoDurationTier result = TextGrid_to_DurationTier (me,tierNumber, timeScaleFactor,leftTransitionDuration, rightTransitionDuration, ___, ___theText);
+		autoDurationTier result = TextGrid_to_DurationTier (me,tierNumber, timeScaleFactor,
+			leftTransitionDuration, rightTransitionDuration, (kMelder_string) scaleIntervalsWhoseLabel___, ___theText);
 	CONVERT_EACH_END (my name)
 }
 
@@ -7106,11 +7107,21 @@ DO
 	MODIFY_EACH_END
 }
 
+static void cb_publish (Editor /*editor*/, autoDaata publish) {
+	try {
+		praat_new (publish.move(), U"");
+		praat_updateSelection ();
+	} catch (MelderError) {
+		Melder_flushError ();
+	}
+}
+
 DIRECT (WINDOW_VowelEditor_create) {
 	if (theCurrentPraatApplication -> batch) {
 		Melder_throw (U"Cannot edit from batch.");
 	}
 	autoVowelEditor vowelEditor = VowelEditor_create (U"VowelEditor", nullptr);
+	Editor_setPublicationCallback (vowelEditor.get(), cb_publish);
 	vowelEditor.releaseToUser();
 END }
 
diff --git a/fon/ExperimentMFC.cpp b/fon/ExperimentMFC.cpp
index 3df6d25..5aafb3e 100644
--- a/fon/ExperimentMFC.cpp
+++ b/fon/ExperimentMFC.cpp
@@ -249,21 +249,21 @@ void ExperimentMFC_start (ExperimentMFC me) {
 		/*
 		 * Determine the order in which the stimuli will be presented to the subject.
 		 */
-		if (my randomize == kExperiment_randomize_CYCLIC_NON_RANDOM) {
+		if (my randomize == kExperiment_randomize::CYCLIC_NON_RANDOM) {
 			for (long itrial = 1; itrial <= my numberOfTrials; itrial ++)
 				my stimuli [itrial] = (itrial - 1) % my numberOfDifferentStimuli + 1;
-		} else if (my randomize == kExperiment_randomize_PERMUTE_ALL) {
+		} else if (my randomize == kExperiment_randomize::PERMUTE_ALL) {
 			for (long itrial = 1; itrial <= my numberOfTrials; itrial ++)
 				my stimuli [itrial] = (itrial - 1) % my numberOfDifferentStimuli + 1;
 			permuteRandomly (me, 1, my numberOfTrials);
-		} else if (my randomize == kExperiment_randomize_PERMUTE_BALANCED) {
+		} else if (my randomize == kExperiment_randomize::PERMUTE_BALANCED) {
 			for (long ireplica = 1; ireplica <= my numberOfReplicationsPerStimulus; ireplica ++) {
 				long offset = (ireplica - 1) * my numberOfDifferentStimuli;
 				for (long istim = 1; istim <= my numberOfDifferentStimuli; istim ++)
 					my stimuli [offset + istim] = istim;
 				permuteRandomly (me, offset + 1, offset + my numberOfDifferentStimuli);
 			}
-		} else if (my randomize == kExperiment_randomize_PERMUTE_BALANCED_NO_DOUBLETS) {
+		} else if (my randomize == kExperiment_randomize::PERMUTE_BALANCED_NO_DOUBLETS) {
 			for (long ireplica = 1; ireplica <= my numberOfReplicationsPerStimulus; ireplica ++) {
 				long offset = (ireplica - 1) * my numberOfDifferentStimuli;
 				for (long istim = 1; istim <= my numberOfDifferentStimuli; istim ++)
@@ -272,7 +272,7 @@ void ExperimentMFC_start (ExperimentMFC me) {
 					permuteRandomly (me, offset + 1, offset + my numberOfDifferentStimuli);
 				} while (ireplica != 1 && my stimuli [offset + 1] == my stimuli [offset] && my numberOfDifferentStimuli > 1);
 			}
-		} else if (my randomize == kExperiment_randomize_WITH_REPLACEMENT) {
+		} else if (my randomize == kExperiment_randomize::WITH_REPLACEMENT) {
 			for (long itrial = 1; itrial <= my numberOfTrials; itrial ++)
 				my stimuli [itrial] = NUMrandomInteger (1, my numberOfDifferentStimuli);
 		}
diff --git a/fon/Experiment_enums.h b/fon/Experiment_enums.h
index 622bd32..220b1c9 100644
--- a/fon/Experiment_enums.h
+++ b/fon/Experiment_enums.h
@@ -1,6 +1,6 @@
 /* Experiment_enums.h
  *
- * Copyright (C) 2001-2009,2015 Paul Boersma
+ * Copyright (C) 2001-2009,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,6 +22,9 @@ enums_begin (kExperiment_randomize, 0)
 	enums_add (kExperiment_randomize, 2, PERMUTE_BALANCED, U"PermuteBalanced")
 	enums_add (kExperiment_randomize, 3, PERMUTE_BALANCED_NO_DOUBLETS, U"PermuteBalancedNoDoublets")
 	enums_add (kExperiment_randomize, 4, WITH_REPLACEMENT, U"WithReplacement")
+	/*
+		As this enumerated type occurs in data, you should add new randomization methods only at the end.
+	*/
 enums_end (kExperiment_randomize, 4, PERMUTE_BALANCED_NO_DOUBLETS)
 
 /* End of file Experiment_enums.h */
diff --git a/fon/FormantGrid.cpp b/fon/FormantGrid.cpp
index ee9e188..a9c3bae 100644
--- a/fon/FormantGrid.cpp
+++ b/fon/FormantGrid.cpp
@@ -258,9 +258,9 @@ void FormantGrid_formula_bandwidths (FormantGrid me, const char32 *expression, I
 			for (long icol = 1; icol <= bandwidth -> points.size; icol ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				if (isundef (result. result.numericResult))
+				if (isundef (result. numericResult))
 					Melder_throw (U"Cannot put an undefined value into the tier.\nFormula not finished.");
-				bandwidth -> points.at [icol] -> value = result. result.numericResult;
+				bandwidth -> points.at [icol] -> value = result. numericResult;
 			}
 		}
 	} catch (MelderError) {
@@ -277,9 +277,9 @@ void FormantGrid_formula_frequencies (FormantGrid me, const char32 *expression,
 			for (long icol = 1; icol <= formant -> points.size; icol ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				if (isundef (result. result.numericResult))
+				if (isundef (result. numericResult))
 					Melder_throw (U"Cannot put an undefined value into the tier.\nFormula not finished.");
-				formant -> points.at [icol] -> value = result. result.numericResult;
+				formant -> points.at [icol] -> value = result. numericResult;
 			}
 		}
 	} catch (MelderError) {
diff --git a/fon/FormantGridEditor.cpp b/fon/FormantGridEditor.cpp
index 2b153be..13f538f 100644
--- a/fon/FormantGridEditor.cpp
+++ b/fon/FormantGridEditor.cpp
@@ -305,7 +305,7 @@ static void drawWhileDragging (FormantGridEditor me, double /* xWC */, double /*
 		RealPoint point = tier -> points.at [first];
 		double t = point -> number + dt, y = point -> value + dy;
 		Graphics_line (my graphics.get(), t, ymin, t, ymax - Graphics_dyMMtoWC (my graphics.get(), 4.0));
-		Graphics_setTextAlignment (my graphics.get(), kGraphics_horizontalAlignment_CENTRE, Graphics_TOP);
+		Graphics_setTextAlignment (my graphics.get(), kGraphics_horizontalAlignment::CENTRE, Graphics_TOP);
 		Graphics_text (my graphics.get(), t, ymax, Melder_fixed (t, 6));
 		Graphics_line (my graphics.get(), my startWindow, y, my endWindow, y);
 		Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_BOTTOM);
diff --git a/fon/Ltas.cpp b/fon/Ltas.cpp
index 1ae80fc..7fac843 100644
--- a/fon/Ltas.cpp
+++ b/fon/Ltas.cpp
@@ -331,7 +331,7 @@ autoLtas PointProcess_Sound_to_Ltas (PointProcess pulses, Sound sound,
 				 */
 				autoSound period = Sound_extractPart (sound,
 					pulses -> t [ipulse] - 0.5 * leftInterval, pulses -> t [ipulse] + 0.5 * rightInterval,
-					kSound_windowShape_RECTANGULAR, 1.0, false);
+					kSound_windowShape::RECTANGULAR, 1.0, false);
 				autoSpectrum spectrum = Sound_to_Spectrum (period.get(), false);
 				for (long ifreq = 1; ifreq <= spectrum -> nx; ifreq ++) {
 					double frequency = spectrum -> xmin + (ifreq - 1) * spectrum -> dx;
@@ -444,7 +444,7 @@ autoLtas PointProcess_Sound_to_Ltas_harmonics (PointProcess pulses, Sound sound,
 				long localMaximumHarmonic;
 				autoSound period = Sound_extractPart (sound,
 					pulses -> t [ipulse] - 0.5 * leftInterval, pulses -> t [ipulse] + 0.5 * rightInterval,
-					kSound_windowShape_RECTANGULAR, 1.0, false);
+					kSound_windowShape::RECTANGULAR, 1.0, false);
 				autoSpectrum spectrum = Sound_to_Spectrum (period.get(), false);
 				localMaximumHarmonic = maximumHarmonic < spectrum -> nx ? maximumHarmonic : spectrum -> nx;
 				for (long iharm = 1; iharm <= localMaximumHarmonic; iharm ++) {
diff --git a/fon/ManipulationEditor.cpp b/fon/ManipulationEditor.cpp
index d5b8607..cf5011c 100644
--- a/fon/ManipulationEditor.cpp
+++ b/fon/ManipulationEditor.cpp
@@ -52,8 +52,8 @@ static const char32 *units_strings [] = { 0, U"Hz", U"st" };
 static int prefs_synthesisMethod = Manipulation_OVERLAPADD;   /* Remembered across editor creations, not across Praat sessions. */
 
 /* BUG: 25 should be fmin */
-#define YLIN(freq)  (my p_pitch_units == kManipulationEditor_pitchUnits_HERTZ ? ((freq) < 25 ? 25 : (freq)) : NUMhertzToSemitones ((freq) < 25 ? 25 : (freq)))
-#define YLININV(freq)  (my p_pitch_units == kManipulationEditor_pitchUnits_HERTZ ? (freq) : NUMsemitonesToHertz (freq))
+#define YLIN(freq)  (my p_pitch_units == kManipulationEditor_pitchUnits::HERTZ ? ((freq) < 25 ? 25 : (freq)) : NUMhertzToSemitones ((freq) < 25 ? 25 : (freq)))
+#define YLININV(freq)  (my p_pitch_units == kManipulationEditor_pitchUnits::HERTZ ? (freq) : NUMsemitonesToHertz (freq))
 
 static void updateMenus (ManipulationEditor me) {
 	Melder_assert (my synthPulsesButton);
@@ -255,7 +255,7 @@ static void menu_cb_addPitchPointAtSlice (ManipulationEditor me, EDITOR_ARGS_DIR
 }	
 
 static void menu_cb_addPitchPointAt (ManipulationEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Add pitch point", 0)
+	EDITOR_FORM (U"Add pitch point", nullptr)
 		REAL (U"Time (s)", U"0.0")
 		REAL (U"Frequency (Hz or st)", U"100.0")
 	EDITOR_OK
@@ -302,7 +302,7 @@ static void menu_cb_stylizePitch_2st (ManipulationEditor me, EDITOR_ARGS_DIRECT)
 }
 
 static void menu_cb_interpolateQuadratically (ManipulationEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Interpolate quadratically", 0)
+	EDITOR_FORM (U"Interpolate quadratically", nullptr)
 		NATURAL (U"Number of points per parabola", my default_pitch_interpolateQuadratically_numberOfPointsPerParabola ())
 	EDITOR_OK
 		SET_INTEGER (U"Number of points per parabola", my p_pitch_interpolateQuadratically_numberOfPointsPerParabola)
@@ -312,7 +312,7 @@ static void menu_cb_interpolateQuadratically (ManipulationEditor me, EDITOR_ARGS
 		Editor_save (me, U"Interpolate quadratically");
 		RealTier_interpolateQuadratically (ana -> pitch.get(),
 			my pref_pitch_interpolateQuadratically_numberOfPointsPerParabola () = my p_pitch_interpolateQuadratically_numberOfPointsPerParabola = GET_INTEGER (U"Number of points per parabola"),
-			my p_pitch_units == kManipulationEditor_pitchUnits_SEMITONES);
+			my p_pitch_units == kManipulationEditor_pitchUnits::SEMITONES);
 		FunctionEditor_redraw (me);
 		Editor_broadcastDataChanged (me);
 	EDITOR_END
@@ -322,13 +322,13 @@ static void menu_cb_interpolateQuadratically_4pts (ManipulationEditor me, EDITOR
 	Manipulation ana = (Manipulation) my data;
 	if (! ana -> pitch) return;
 	Editor_save (me, U"Interpolate quadratically");
-	RealTier_interpolateQuadratically (ana -> pitch.get(), 4, my p_pitch_units == kManipulationEditor_pitchUnits_SEMITONES);
+	RealTier_interpolateQuadratically (ana -> pitch.get(), 4, my p_pitch_units == kManipulationEditor_pitchUnits::SEMITONES);
 	FunctionEditor_redraw (me);
 	Editor_broadcastDataChanged (me);
 }
 
 static void menu_cb_shiftPitchFrequencies (ManipulationEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Shift pitch frequencies", 0)
+	EDITOR_FORM (U"Shift pitch frequencies", nullptr)
 		REAL (U"Frequency shift", U"-20.0")
 		OPTIONMENU (U"Unit", 1)
 			OPTION (U"Hertz")
@@ -339,13 +339,13 @@ static void menu_cb_shiftPitchFrequencies (ManipulationEditor me, EDITOR_ARGS_FO
 	EDITOR_OK
 	EDITOR_DO
 		Manipulation ana = (Manipulation) my data;
-		int unit = GET_INTEGER (U"Unit");
-		unit =
-			unit == 1 ? kPitch_unit_HERTZ :
-			unit == 2 ? kPitch_unit_MEL :
-			unit == 3 ? kPitch_unit_LOG_HERTZ :
-			unit == 4 ? kPitch_unit_SEMITONES_1 :
-			kPitch_unit_ERB;
+		int unit_i = GET_INTEGER (U"Unit");
+		kPitch_unit unit =
+			unit_i == 1 ? kPitch_unit::HERTZ :
+			unit_i == 2 ? kPitch_unit::MEL :
+			unit_i == 3 ? kPitch_unit::LOG_HERTZ :
+			unit_i == 4 ? kPitch_unit::SEMITONES_1 :
+			kPitch_unit::ERB;
 		if (! ana -> pitch) return;
 		Editor_save (me, U"Shift pitch frequencies");
 		try {
@@ -362,7 +362,7 @@ static void menu_cb_shiftPitchFrequencies (ManipulationEditor me, EDITOR_ARGS_FO
 }
 
 static void menu_cb_multiplyPitchFrequencies (ManipulationEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Multiply pitch frequencies", 0)
+	EDITOR_FORM (U"Multiply pitch frequencies", nullptr)
 		POSITIVE (U"Factor", U"1.2")
 		LABEL (U"", U"The multiplication is always done in hertz.")
 	EDITOR_OK
@@ -377,7 +377,7 @@ static void menu_cb_multiplyPitchFrequencies (ManipulationEditor me, EDITOR_ARGS
 }
 
 static void menu_cb_setPitchRange (ManipulationEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Set pitch range", 0)
+	EDITOR_FORM (U"Set pitch range", nullptr)
 		/* BUG: should include Minimum */
 		REAL (U"Maximum (Hz or st)", my default_pitch_maximum ())
 	EDITOR_OK
@@ -385,14 +385,15 @@ static void menu_cb_setPitchRange (ManipulationEditor me, EDITOR_ARGS_FORM) {
 	EDITOR_DO
 		double maximum = GET_REAL (U"Maximum");
 		if (maximum <= my pitchTier.minPeriodic)
-			Melder_throw (U"Maximum pitch must be greater than ", Melder_half (my pitchTier.minPeriodic), U" ", units_strings [my p_pitch_units], U".");
+			Melder_throw (U"Maximum pitch must be greater than ",
+				Melder_half (my pitchTier.minPeriodic), U" ", units_strings [(int) my p_pitch_units], U".");
 		my pref_pitch_maximum () = my p_pitch_maximum = maximum;
 		FunctionEditor_redraw (me);
 	EDITOR_END
 }
 
 static void menu_cb_setPitchUnits (ManipulationEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Set pitch units", 0)
+	EDITOR_FORM (U"Set pitch units", nullptr)
 		RADIO_ENUM (U"Pitch units", kManipulationEditor_pitchUnits, my default_pitch_units ())
 	EDITOR_OK
 		SET_ENUM (U"Pitch units", kManipulationEditor_pitchUnits, my p_pitch_units)
@@ -400,7 +401,7 @@ static void menu_cb_setPitchUnits (ManipulationEditor me, EDITOR_ARGS_FORM) {
 		enum kManipulationEditor_pitchUnits oldPitchUnits = my p_pitch_units;
 		my pref_pitch_units () = my p_pitch_units = GET_ENUM (kManipulationEditor_pitchUnits, U"Pitch units");
 		if (my p_pitch_units == oldPitchUnits) return;
-		if (my p_pitch_units == kManipulationEditor_pitchUnits_HERTZ) {
+		if (my p_pitch_units == kManipulationEditor_pitchUnits::HERTZ) {
 			my p_pitch_minimum = 25.0;
 			my pitchTier.minPeriodic = 50.0;
 			my pref_pitch_maximum () = my p_pitch_maximum = NUMsemitonesToHertz (my p_pitch_maximum);
@@ -418,7 +419,7 @@ static void menu_cb_setPitchUnits (ManipulationEditor me, EDITOR_ARGS_FORM) {
 /***** DURATION MENU *****/
 
 static void menu_cb_setDurationRange (ManipulationEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Set duration range", 0)
+	EDITOR_FORM (U"Set duration range", nullptr)
 		REAL (U"Minimum", my default_duration_minimum ())
 		REAL (U"Maximum", my default_duration_maximum ())
 	EDITOR_OK
@@ -448,7 +449,7 @@ static void menu_cb_setDraggingStrategy (ManipulationEditor me, EDITOR_ARGS_FORM
 	EDITOR_FORM (U"Set dragging strategy", U"ManipulationEditor")
 		RADIO_ENUM (U"Dragging strategy", kManipulationEditor_draggingStrategy, my default_pitch_draggingStrategy ())
 	EDITOR_OK
-		SET_INTEGER (U"Dragging strategy", my p_pitch_draggingStrategy)
+		SET_ENUM (U"Dragging strategy", kManipulationEditor_draggingStrategy, my p_pitch_draggingStrategy)
 	EDITOR_DO
 		my pref_pitch_draggingStrategy () = my p_pitch_draggingStrategy = GET_ENUM (kManipulationEditor_draggingStrategy, U"Dragging strategy");
 	EDITOR_END
@@ -605,11 +606,11 @@ static void drawSoundArea (ManipulationEditor me, double ymin, double ymax) {
 	Graphics_setColour (my graphics.get(), Graphics_BLACK);
 	Graphics_rectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
 	Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_TOP);
-	Graphics_setFont (my graphics.get(), kGraphics_font_TIMES);
+	Graphics_setFont (my graphics.get(), kGraphics_font::TIMES);
 	Graphics_text (my graphics.get(), 1.0, 1.0, U"%%Sound");
 	Graphics_setColour (my graphics.get(), Graphics_BLUE);
 	Graphics_text (my graphics.get(), 1.0, 1.0 - Graphics_dyMMtoWC (my graphics.get(), 3), U"%%Pulses");
-	Graphics_setFont (my graphics.get(), kGraphics_font_HELVETICA);
+	Graphics_setFont (my graphics.get(), kGraphics_font::HELVETICA);
 
 	/*
 	 * Draw blue pulses.
@@ -683,12 +684,12 @@ static void drawPitchArea (ManipulationEditor me, double ymin, double ymax) {
 	Graphics_setColour (my graphics.get(), Graphics_BLACK);
 	Graphics_rectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
 	Graphics_setColour (my graphics.get(), Graphics_GREEN);
-	Graphics_setFont (my graphics.get(), kGraphics_font_TIMES);
+	Graphics_setFont (my graphics.get(), kGraphics_font::TIMES);
 	Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_TOP);
 	Graphics_text (my graphics.get(), 1.0, 1.0, U"%%Pitch manip");
 	Graphics_setGrey (my graphics.get(), 0.7);
 	Graphics_text (my graphics.get(), 1.0, 1.0 - Graphics_dyMMtoWC (my graphics.get(), 3), U"%%Pitch from pulses");
-	Graphics_setFont (my graphics.get(), kGraphics_font_HELVETICA);
+	Graphics_setFont (my graphics.get(), kGraphics_font::HELVETICA);
 
 	Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, my p_pitch_minimum, my p_pitch_maximum);
 
@@ -711,16 +712,16 @@ static void drawPitchArea (ManipulationEditor me, double ymin, double ymax) {
 
 	FunctionEditor_drawGridLine (me, minimumFrequency);
 	FunctionEditor_drawRangeMark (me, my p_pitch_maximum,
-		Melder_fixed (my p_pitch_maximum, rangePrecisions [my p_pitch_units]), rangeUnits [my p_pitch_units], Graphics_TOP);
+		Melder_fixed (my p_pitch_maximum, rangePrecisions [(int) my p_pitch_units]), rangeUnits [(int) my p_pitch_units], Graphics_TOP);
 	FunctionEditor_drawRangeMark (me, my p_pitch_minimum,
-		Melder_fixed (my p_pitch_minimum, rangePrecisions [my p_pitch_units]), rangeUnits [my p_pitch_units], Graphics_BOTTOM);
+		Melder_fixed (my p_pitch_minimum, rangePrecisions [(int) my p_pitch_units]), rangeUnits [(int) my p_pitch_units], Graphics_BOTTOM);
 	if (my startSelection == my endSelection && my pitchTier.cursor >= my p_pitch_minimum && my pitchTier.cursor <= my p_pitch_maximum)
 		FunctionEditor_drawHorizontalHair (me, my pitchTier.cursor,
-			Melder_fixed (my pitchTier.cursor, rangePrecisions [my p_pitch_units]), rangeUnits [my p_pitch_units]);
+			Melder_fixed (my pitchTier.cursor, rangePrecisions [(int) my p_pitch_units]), rangeUnits [(int) my p_pitch_units]);
 	if (cursorVisible && n > 0) {
 		double y = YLIN (RealTier_getValueAtTime (pitch, my startSelection));
 		FunctionEditor_insertCursorFunctionValue (me, y,
-			Melder_fixed (y, rangePrecisions [my p_pitch_units]), rangeUnits [my p_pitch_units],
+			Melder_fixed (y, rangePrecisions [(int) my p_pitch_units]), rangeUnits [(int) my p_pitch_units],
 			my p_pitch_minimum, my p_pitch_maximum);
 	}
 	if (pitch) {
@@ -789,10 +790,10 @@ static void drawDurationArea (ManipulationEditor me, double ymin, double ymax) {
 	Graphics_setColour (my graphics.get(), Graphics_BLACK);
 	Graphics_rectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
 	Graphics_setColour (my graphics.get(), Graphics_GREEN);
-	Graphics_setFont (my graphics.get(), kGraphics_font_TIMES);
+	Graphics_setFont (my graphics.get(), kGraphics_font::TIMES);
 	Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_TOP);
 	Graphics_text (my graphics.get(), 1.0, 1.0, U"%%Duration manip");
-	Graphics_setFont (my graphics.get(), kGraphics_font_HELVETICA);
+	Graphics_setFont (my graphics.get(), kGraphics_font::HELVETICA);
 
 	Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, my p_duration_minimum, my p_duration_maximum);
 	FunctionEditor_drawGridLine (me, 1.0);
@@ -972,9 +973,9 @@ static bool clickPitch (ManipulationEditor me, double xWC, double yWC, bool shif
 	  */
 	Graphics_xorOn (my graphics.get(), Graphics_MAROON);
 	drawWhileDragging (me, xWC, yWC, ifirstSelected, ilastSelected, dt, df);
-	dragHorizontal = my p_pitch_draggingStrategy != kManipulationEditor_draggingStrategy_VERTICAL &&
-		(! shiftKeyPressed || my p_pitch_draggingStrategy != kManipulationEditor_draggingStrategy_HYBRID);
-	dragVertical = my p_pitch_draggingStrategy != kManipulationEditor_draggingStrategy_HORIZONTAL;
+	dragHorizontal = my p_pitch_draggingStrategy != kManipulationEditor_draggingStrategy::VERTICAL &&
+		(! shiftKeyPressed || my p_pitch_draggingStrategy != kManipulationEditor_draggingStrategy::HYBRID);
+	dragVertical = my p_pitch_draggingStrategy != kManipulationEditor_draggingStrategy::HORIZONTAL;
 	while (Graphics_mouseStillDown (my graphics.get())) {
 		double xWC_new, yWC_new;
 		Graphics_getMouseLocation (my graphics.get(), & xWC_new, & yWC_new);
@@ -1214,7 +1215,7 @@ autoManipulationEditor ManipulationEditor_create (const char32 *title, Manipulat
 		FunctionEditor_init (me.get(), title, ana);
 
 		double maximumPitchValue = RealTier_getMaximumValue (ana -> pitch.get());
-		if (my p_pitch_units == kManipulationEditor_pitchUnits_HERTZ) {
+		if (my p_pitch_units == kManipulationEditor_pitchUnits::HERTZ) {
 			my p_pitch_minimum = 25.0;
 			my pitchTier.minPeriodic = 50.0;
 			my p_pitch_maximum = maximumPitchValue;
diff --git a/fon/Matrix.cpp b/fon/Matrix.cpp
index 864dbd6..961c594 100644
--- a/fon/Matrix.cpp
+++ b/fon/Matrix.cpp
@@ -632,7 +632,7 @@ void Matrix_formula (Matrix me, const char32 *expression, Interpreter interprete
 		for (integer irow = 1; irow <= my ny; irow ++) {
 			for (integer icol = 1; icol <= my nx; icol ++) {
 				Formula_run (irow, icol, & result);
-				target -> z [irow] [icol] = result. result.numericResult;
+				target -> z [irow] [icol] = result. numericResult;
 			}
 		}
 	} catch (MelderError) {
@@ -655,7 +655,7 @@ void Matrix_formula_part (Matrix me, double xmin, double xmax, double ymin, doub
 		for (integer irow = iymin; irow <= iymax; irow ++) {
 			for (integer icol = ixmin; icol <= ixmax; icol ++) {
 				Formula_run (irow, icol, & result);
-				target -> z [irow] [icol] = result. result.numericResult;
+				target -> z [irow] [icol] = result. numericResult;
 			}
 		}
 	} catch (MelderError) {
diff --git a/fon/Pitch.cpp b/fon/Pitch.cpp
index 2684382..bf17491 100644
--- a/fon/Pitch.cpp
+++ b/fon/Pitch.cpp
@@ -49,35 +49,35 @@ Thing_implement (Pitch, Sampled, 1);
 
 #define FREQUENCY(frame)  ((frame) -> candidate [1]. frequency)
 #define STRENGTH(frame)  ((frame) -> candidate [1]. strength)
-#define NOT_VOICED(f)  (! ((f) > 0.0 && (f) < my ceiling))
+#define NOT_VOICED(f)  (! ((f) > 0.0 && (f) < my ceiling))   /* a NaN-resistant formulation */
 
 int structPitch :: v_getMinimumUnit (long ilevel) {
-	return ilevel == Pitch_LEVEL_FREQUENCY ? kPitch_unit_MIN : Pitch_STRENGTH_UNIT_min;
+	return ilevel == Pitch_LEVEL_FREQUENCY ? (int) kPitch_unit::MIN : Pitch_STRENGTH_UNIT_min;
 }
 
 int structPitch :: v_getMaximumUnit (long ilevel) {
-	return ilevel == Pitch_LEVEL_FREQUENCY ? kPitch_unit_MAX : Pitch_STRENGTH_UNIT_max;
+	return ilevel == Pitch_LEVEL_FREQUENCY ? (int) kPitch_unit::MAX : Pitch_STRENGTH_UNIT_max;
 }
 
 const char32 * structPitch :: v_getUnitText (long ilevel, int unit, unsigned long flags) {
 	if (ilevel == Pitch_LEVEL_FREQUENCY) {
 		return
-			unit == kPitch_unit_HERTZ ?
+			unit == (int) kPitch_unit::HERTZ ?
 				flags & Function_UNIT_TEXT_MENU ? U"Hertz" : U"Hz" :
-			unit == kPitch_unit_HERTZ_LOGARITHMIC ?
+			unit == (int) kPitch_unit::HERTZ_LOGARITHMIC ?
 				flags & Function_UNIT_TEXT_MENU ? U"Hertz (logarithmic)" : (flags & Function_UNIT_TEXT_SHORT) && (flags & Function_UNIT_TEXT_GRAPHICAL) ? U"%%Hz%" : U"Hz" :
-			unit == kPitch_unit_MEL ? U"mel" :
-			unit == kPitch_unit_LOG_HERTZ ?
+			unit == (int) kPitch_unit::MEL ? U"mel" :
+			unit == (int) kPitch_unit::LOG_HERTZ ?
 				flags & Function_UNIT_TEXT_MENU ? U"logHertz" : U"logHz" :
-			unit == kPitch_unit_SEMITONES_1 ?
+			unit == (int) kPitch_unit::SEMITONES_1 ?
 				flags & Function_UNIT_TEXT_SHORT ? U"st__1_" : flags & Function_UNIT_TEXT_GRAPHICAL ? U"semitones %%re% 1 Hz" : U"semitones re 1 Hz" :
-			unit == kPitch_unit_SEMITONES_100 ?
+			unit == (int) kPitch_unit::SEMITONES_100 ?
 				flags & Function_UNIT_TEXT_SHORT ? U"st__100_" : flags & Function_UNIT_TEXT_GRAPHICAL ? U"semitones %%re% 100 Hz" : U"semitones re 100 Hz" :
-			unit == kPitch_unit_SEMITONES_200 ?
+			unit == (int) kPitch_unit::SEMITONES_200 ?
 				flags & Function_UNIT_TEXT_SHORT ? U"st__200_" : flags & Function_UNIT_TEXT_GRAPHICAL ? U"semitones %%re% 200 Hz" : U"semitones re 200 Hz" :
-			unit == kPitch_unit_SEMITONES_440 ?
+			unit == (int) kPitch_unit::SEMITONES_440 ?
 				flags & Function_UNIT_TEXT_SHORT ? U"st__a_" : flags & Function_UNIT_TEXT_GRAPHICAL ? U"semitones %%re% 440 Hz" : U"semitones re 440 Hz" :
-			unit == kPitch_unit_ERB ?
+			unit == (int) kPitch_unit::ERB ?
 				flags & Function_UNIT_TEXT_SHORT ? U"erb" : U"ERB" :
 			U"";
 	} else if (ilevel == Pitch_LEVEL_STRENGTH) {
@@ -91,21 +91,21 @@ const char32 * structPitch :: v_getUnitText (long ilevel, int unit, unsigned lon
 }
 
 bool structPitch :: v_isUnitLogarithmic (long ilevel, int unit) {
-	return ilevel == Pitch_LEVEL_FREQUENCY && unit == kPitch_unit_HERTZ_LOGARITHMIC;
+	return ilevel == Pitch_LEVEL_FREQUENCY && unit == (int) kPitch_unit::HERTZ_LOGARITHMIC;
 }
 
 double structPitch :: v_convertStandardToSpecialUnit (double value, long ilevel, int unit) {
 	if (ilevel == Pitch_LEVEL_FREQUENCY) {
 		return
-			unit == kPitch_unit_HERTZ ? value :
-			unit == kPitch_unit_HERTZ_LOGARITHMIC ? value <= 0.0 ? undefined : log10 (value) :
-			unit == kPitch_unit_MEL ? NUMhertzToMel (value) :
-			unit == kPitch_unit_LOG_HERTZ ? value <= 0.0 ? undefined : log10 (value) :
-			unit == kPitch_unit_SEMITONES_1 ? value <= 0.0 ? undefined : 12.0 * log (value / 1.0) / NUMln2 :
-			unit == kPitch_unit_SEMITONES_100 ? value <= 0.0 ? undefined : 12.0 * log (value / 100.0) / NUMln2 :
-			unit == kPitch_unit_SEMITONES_200 ? value <= 0.0 ? undefined : 12.0 * log (value / 200.0) / NUMln2 :
-			unit == kPitch_unit_SEMITONES_440 ? value <= 0.0 ? undefined : 12.0 * log (value / 440.0) / NUMln2 :
-			unit == kPitch_unit_ERB ? NUMhertzToErb (value) :
+			unit == (int) kPitch_unit::HERTZ ? value :
+			unit == (int) kPitch_unit::HERTZ_LOGARITHMIC ? value <= 0.0 ? undefined : log10 (value) :
+			unit == (int) kPitch_unit::MEL ? NUMhertzToMel (value) :
+			unit == (int) kPitch_unit::LOG_HERTZ ? value <= 0.0 ? undefined : log10 (value) :
+			unit == (int) kPitch_unit::SEMITONES_1 ? value <= 0.0 ? undefined : 12.0 * log (value / 1.0) / NUMln2 :
+			unit == (int) kPitch_unit::SEMITONES_100 ? value <= 0.0 ? undefined : 12.0 * log (value / 100.0) / NUMln2 :
+			unit == (int) kPitch_unit::SEMITONES_200 ? value <= 0.0 ? undefined : 12.0 * log (value / 200.0) / NUMln2 :
+			unit == (int) kPitch_unit::SEMITONES_440 ? value <= 0.0 ? undefined : 12.0 * log (value / 440.0) / NUMln2 :
+			unit == (int) kPitch_unit::ERB ? NUMhertzToErb (value) :
 			undefined;
 	} else {
 		return
@@ -121,15 +121,15 @@ double structPitch :: v_convertStandardToSpecialUnit (double value, long ilevel,
 double structPitch :: v_convertSpecialToStandardUnit (double value, long ilevel, int unit) {
 	if (ilevel == Pitch_LEVEL_FREQUENCY) {
 		return
-			unit == kPitch_unit_HERTZ ? value :
-			unit == kPitch_unit_HERTZ_LOGARITHMIC ? pow (10.0, value) :
-			unit == kPitch_unit_MEL ? NUMmelToHertz (value) :
-			unit == kPitch_unit_LOG_HERTZ ? pow (10.0, value) :
-			unit == kPitch_unit_SEMITONES_1 ? 1.0 * exp (value * (NUMln2 / 12.0)):
-			unit == kPitch_unit_SEMITONES_100 ? 100.0 * exp (value * (NUMln2 / 12.0)):
-			unit == kPitch_unit_SEMITONES_200 ? 200.0 * exp (value * (NUMln2 / 12.0)):
-			unit == kPitch_unit_SEMITONES_440 ? 440.0 * exp (value * (NUMln2 / 12.0)):
-			unit == kPitch_unit_ERB ? NUMerbToHertz (value) :
+			unit == (int) kPitch_unit::HERTZ ? value :
+			unit == (int) kPitch_unit::HERTZ_LOGARITHMIC ? pow (10.0, value) :
+			unit == (int) kPitch_unit::MEL ? NUMmelToHertz (value) :
+			unit == (int) kPitch_unit::LOG_HERTZ ? pow (10.0, value) :
+			unit == (int) kPitch_unit::SEMITONES_1 ? 1.0 * exp (value * (NUMln2 / 12.0)):
+			unit == (int) kPitch_unit::SEMITONES_100 ? 100.0 * exp (value * (NUMln2 / 12.0)):
+			unit == (int) kPitch_unit::SEMITONES_200 ? 200.0 * exp (value * (NUMln2 / 12.0)):
+			unit == (int) kPitch_unit::SEMITONES_440 ? 440.0 * exp (value * (NUMln2 / 12.0)):
+			unit == (int) kPitch_unit::ERB ? NUMerbToHertz (value) :
 			undefined;
 	} else {
 		return undefined;
@@ -137,9 +137,9 @@ double structPitch :: v_convertSpecialToStandardUnit (double value, long ilevel,
 }
 
 #define doesUnitAllowNegativeValues(unit)  \
-	( (unit) == kPitch_unit_HERTZ_LOGARITHMIC || (unit) == kPitch_unit_LOG_HERTZ ||  \
-	  (unit) == kPitch_unit_SEMITONES_1 || (unit) == kPitch_unit_SEMITONES_100 ||  \
-	  (unit) == kPitch_unit_SEMITONES_200 || (unit) == kPitch_unit_SEMITONES_440 )
+	( (unit) == kPitch_unit::HERTZ_LOGARITHMIC || (unit) == kPitch_unit::LOG_HERTZ ||  \
+	  (unit) == kPitch_unit::SEMITONES_1 || (unit) == kPitch_unit::SEMITONES_100 ||  \
+	  (unit) == kPitch_unit::SEMITONES_200 || (unit) == kPitch_unit::SEMITONES_440 )
 
 double structPitch :: v_getValueAtSample (long iframe, long ilevel, int unit) {
 	double f = frame [iframe]. candidate [1]. frequency;
@@ -148,88 +148,88 @@ double structPitch :: v_getValueAtSample (long iframe, long ilevel, int unit) {
 }
 
 bool Pitch_isVoiced_i (Pitch me, long iframe) {
-	return isdefined (Sampled_getValueAtSample (me, iframe, Pitch_LEVEL_FREQUENCY, kPitch_unit_HERTZ));
+	return isdefined (Sampled_getValueAtSample (me, iframe, Pitch_LEVEL_FREQUENCY, (int) kPitch_unit::HERTZ));
 }
 
 bool Pitch_isVoiced_t (Pitch me, double time) {
-	return isdefined (Sampled_getValueAtX (me, time, Pitch_LEVEL_FREQUENCY, kPitch_unit_HERTZ, false));
+	return isdefined (Sampled_getValueAtX (me, time, Pitch_LEVEL_FREQUENCY, (int) kPitch_unit::HERTZ, false));
 }
 
-double Pitch_getValueAtTime (Pitch me, double time, int unit, bool interpolate) {
-	return Sampled_getValueAtX (me, time, Pitch_LEVEL_FREQUENCY, unit, interpolate);
+double Pitch_getValueAtTime (Pitch me, double time, kPitch_unit unit, bool interpolate) {
+	return Sampled_getValueAtX (me, time, Pitch_LEVEL_FREQUENCY, (int) unit, interpolate);
 }
 
-double Pitch_getStrengthAtTime (Pitch me, double time, int unit, bool interpolate) {
-	return Sampled_getValueAtX (me, time, Pitch_LEVEL_STRENGTH, unit, interpolate);
+double Pitch_getStrengthAtTime (Pitch me, double time, kPitch_unit unit, bool interpolate) {
+	return Sampled_getValueAtX (me, time, Pitch_LEVEL_STRENGTH, (int) unit, interpolate);
 }
 
 long Pitch_countVoicedFrames (Pitch me) {
-	return Sampled_countDefinedSamples (me, Pitch_LEVEL_FREQUENCY, kPitch_unit_HERTZ);
+	return Sampled_countDefinedSamples (me, Pitch_LEVEL_FREQUENCY, (int) kPitch_unit::HERTZ);
 }
 
-double Pitch_getMean (Pitch me, double tmin, double tmax, int unit) {
-	return Sampled_getMean (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, unit, true);
+double Pitch_getMean (Pitch me, double tmin, double tmax, kPitch_unit unit) {
+	return Sampled_getMean (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, (int) unit, true);
 }
 
-double Pitch_getMeanStrength (Pitch me, double tmin, double tmax, int unit) {
-	return Sampled_getMean (me, tmin, tmax, Pitch_LEVEL_STRENGTH, unit, true);
+double Pitch_getMeanStrength (Pitch me, double tmin, double tmax, int strengthUnit) {
+	return Sampled_getMean (me, tmin, tmax, Pitch_LEVEL_STRENGTH, strengthUnit, true);
 }
 
-double Pitch_getQuantile (Pitch me, double tmin, double tmax, double quantile, int unit) {
-	double value = Sampled_getQuantile (me, tmin, tmax, quantile, Pitch_LEVEL_FREQUENCY, unit);
+double Pitch_getQuantile (Pitch me, double tmin, double tmax, double quantile, kPitch_unit unit) {
+	double value = Sampled_getQuantile (me, tmin, tmax, quantile, Pitch_LEVEL_FREQUENCY, (int) unit);
 	if (value <= 0.0 && ! doesUnitAllowNegativeValues (unit)) {
 		value = undefined;
 	}
 	return value;
 }
 
-double Pitch_getStandardDeviation (Pitch me, double tmin, double tmax, int unit) {
-	return Sampled_getStandardDeviation (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, unit, true);
+double Pitch_getStandardDeviation (Pitch me, double tmin, double tmax, kPitch_unit unit) {
+	return Sampled_getStandardDeviation (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, (int) unit, true);
 }
 
 #define MEL(f)  NUMhertzToMel (f)
 #define SEMITONES(f)  NUMhertzToSemitones (f)
 #define ERB(f)  NUMhertzToErb (f)
 
-void Pitch_getMaximumAndTime (Pitch me, double tmin, double tmax, int unit, bool interpolate,
+void Pitch_getMaximumAndTime (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate,
 	double *return_maximum, double *return_timeOfMaximum)
 {
-	Sampled_getMaximumAndX (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, unit, interpolate, return_maximum, return_timeOfMaximum);
+	Sampled_getMaximumAndX (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, (int) unit, interpolate, return_maximum, return_timeOfMaximum);
 	if (! doesUnitAllowNegativeValues (unit) && return_maximum && *return_maximum <= 0.0)
 	{
 		*return_maximum = undefined;   // unlikely
 	}
 }
 
-double Pitch_getMaximum (Pitch me, double tmin, double tmax, int unit, bool interpolate) {
+double Pitch_getMaximum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate) {
 	double maximum;
 	Pitch_getMaximumAndTime (me, tmin, tmax, unit, interpolate, & maximum, nullptr);
 	return maximum;
 }
 
-double Pitch_getTimeOfMaximum (Pitch me, double tmin, double tmax, int unit, bool interpolate) {
+double Pitch_getTimeOfMaximum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate) {
 	double time;
 	Pitch_getMaximumAndTime (me, tmin, tmax, unit, interpolate, nullptr, & time);
 	return time;
 }
 
-void Pitch_getMinimumAndTime (Pitch me, double tmin, double tmax, int unit, bool interpolate,
+void Pitch_getMinimumAndTime (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate,
 	double *return_minimum, double *return_timeOfMinimum)
 {
-	Sampled_getMinimumAndX (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, unit, interpolate, return_minimum, return_timeOfMinimum);
+	Sampled_getMinimumAndX (me, tmin, tmax, Pitch_LEVEL_FREQUENCY, (int) unit, interpolate, return_minimum, return_timeOfMinimum);
 	if (! doesUnitAllowNegativeValues (unit) && return_minimum && *return_minimum <= 0.0)
 	{
 		*return_minimum = undefined;   // not so unlikely
 	}
 }
 
-double Pitch_getMinimum (Pitch me, double tmin, double tmax, int unit, bool interpolate) {
+double Pitch_getMinimum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate) {
 	double minimum;
 	Pitch_getMinimumAndTime (me, tmin, tmax, unit, interpolate, & minimum, nullptr);
 	return minimum;
 }
 
-double Pitch_getTimeOfMinimum (Pitch me, double tmin, double tmax, int unit, bool interpolate) {
+double Pitch_getTimeOfMinimum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate) {
 	double time;
 	Pitch_getMinimumAndTime (me, tmin, tmax, unit, interpolate, nullptr, & time);
 	return time;
@@ -302,7 +302,7 @@ long Pitch_getMeanAbsSlope_noOctave (Pitch me, double *slope) {
 
 void structPitch :: v_info () {
 	long nVoiced;
-	autoNUMvector <double> frequencies (Sampled_getSortedValues (this, Pitch_LEVEL_FREQUENCY, kPitch_unit_HERTZ, & nVoiced), 1);
+	autoNUMvector <double> frequencies (Sampled_getSortedValues (this, Pitch_LEVEL_FREQUENCY, (int) kPitch_unit::HERTZ, & nVoiced), 1);
 	structDaata :: v_info ();
 	MelderInfo_writeLine (U"Time domain:");
 	MelderInfo_writeLine (U"   Start time: ", xmin, U" seconds");
@@ -344,8 +344,8 @@ void structPitch :: v_info () {
 		}
 	}
 	if (nVoiced >= 1) {   // extrema, range, mean and standard deviation
-		double minimum = Pitch_getMinimum (this, xmin, xmax, kPitch_unit_HERTZ, false);
-		double maximum = Pitch_getMaximum (this, xmin, xmax, kPitch_unit_HERTZ, false);
+		double minimum = Pitch_getMinimum (this, xmin, xmax, kPitch_unit::HERTZ, false);
+		double maximum = Pitch_getMaximum (this, xmin, xmax, kPitch_unit::HERTZ, false);
 		double meanHertz, meanMel, meanSemitones, meanErb;
 		MelderInfo_write (U"\nMinimum ", Melder_single (minimum), U" Hz = ", Melder_single (MEL (minimum)), U" Mel = ");
 		MelderInfo_writeLine (Melder_single (SEMITONES (minimum)), U" semitones above 100 Hz = ", Melder_single (ERB (minimum)), U" ERB");
@@ -353,17 +353,17 @@ void structPitch :: v_info () {
 		MelderInfo_writeLine (Melder_single (SEMITONES (maximum)), U" semitones above 100 Hz = ", Melder_single (ERB (maximum)), U" ERB");
 		MelderInfo_write (U"Range ", Melder_half (maximum - minimum), U" Hz = ", Melder_single (MEL (maximum) - MEL (minimum)), U" Mel = ");
 		MelderInfo_writeLine (Melder_half (SEMITONES (maximum) - SEMITONES (minimum)), U" semitones = ", Melder_half (ERB (maximum) - ERB (minimum)), U" ERB");
-		meanHertz = Pitch_getMean (this, 0, 0, kPitch_unit_HERTZ);
-		meanMel = Pitch_getMean (this, 0, 0, kPitch_unit_MEL);
-		meanSemitones = Pitch_getMean (this, 0, 0, kPitch_unit_SEMITONES_100);
-		meanErb = Pitch_getMean (this, 0, 0, kPitch_unit_ERB);
+		meanHertz = Pitch_getMean (this, 0, 0, kPitch_unit::HERTZ);
+		meanMel = Pitch_getMean (this, 0, 0, kPitch_unit::MEL);
+		meanSemitones = Pitch_getMean (this, 0, 0, kPitch_unit::SEMITONES_100);
+		meanErb = Pitch_getMean (this, 0, 0, kPitch_unit::ERB);
 		MelderInfo_write (U"Average: ", Melder_single (meanHertz), U" Hz = ", Melder_single (meanMel), U" Mel = ");
 		MelderInfo_writeLine (Melder_single (meanSemitones), U" semitones above 100 Hz = ", Melder_single (meanErb), U" ERB");
 		if (nVoiced >= 2) {
-			double stdevHertz = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit_HERTZ);
-			double stdevMel = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit_MEL);
-			double stdevSemitones = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit_SEMITONES_100);
-			double stdevErb = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit_ERB);
+			double stdevHertz = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit::HERTZ);
+			double stdevMel = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit::MEL);
+			double stdevSemitones = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit::SEMITONES_100);
+			double stdevErb = Pitch_getStandardDeviation (this, 0, 0, kPitch_unit::ERB);
 			MelderInfo_write (U"Standard deviation: ", Melder_half (stdevHertz), U" Hz = ", Melder_half (stdevMel), U" Mel = ");
 			MelderInfo_writeLine (Melder_half (stdevSemitones), U" semitones = ", Melder_half (stdevErb), U" ERB");
 		}
@@ -581,11 +581,11 @@ void Pitch_pathFinder (Pitch me, double silenceThreshold, double voicingThreshol
 	}
 }
 
-void Pitch_drawInside (Pitch me, Graphics g, double xmin, double xmax, double fmin, double fmax, bool speckle, int unit) {
-	Sampled_drawInside (me, g, xmin, xmax, fmin, fmax, speckle, Pitch_LEVEL_FREQUENCY, unit);
+void Pitch_drawInside (Pitch me, Graphics g, double xmin, double xmax, double fmin, double fmax, bool speckle, kPitch_unit unit) {
+	Sampled_drawInside (me, g, xmin, xmax, fmin, fmax, speckle, Pitch_LEVEL_FREQUENCY, (int) unit);
 }
 
-void Pitch_draw (Pitch me, Graphics g, double tmin, double tmax, double fmin, double fmax, bool garnish, bool speckle, int unit) {
+void Pitch_draw (Pitch me, Graphics g, double tmin, double tmax, double fmin, double fmax, bool garnish, bool speckle, kPitch_unit unit) {
 	Graphics_setInner (g);
 	Pitch_drawInside (me, g, tmin, tmax, fmin, fmax, speckle, unit);
 	Graphics_unsetInner (g);
@@ -593,8 +593,8 @@ void Pitch_draw (Pitch me, Graphics g, double tmin, double tmax, double fmin, do
 		Graphics_drawInnerBox (g);
 		Graphics_textBottom (g, true, U"Time (s)");
 		Graphics_marksBottom (g, 2, true, true, false);
-		Graphics_textLeft (g, true, Melder_cat (U"Pitch (", Function_getUnitText (me, Pitch_LEVEL_FREQUENCY, unit, Function_UNIT_TEXT_GRAPHICAL), U")"));
-		if (Function_isUnitLogarithmic (me, Pitch_LEVEL_FREQUENCY, unit)) {
+		Graphics_textLeft (g, true, Melder_cat (U"Pitch (", Function_getUnitText (me, Pitch_LEVEL_FREQUENCY, (int) unit, Function_UNIT_TEXT_GRAPHICAL), U")"));
+		if (Function_isUnitLogarithmic (me, Pitch_LEVEL_FREQUENCY, (int) unit)) {
 			Graphics_marksLeftLogarithmic (g, 6, true, true, false);
 		} else {
 			Graphics_marksLeft (g, 2, true, true, false);
@@ -711,7 +711,7 @@ autoPitch Pitch_interpolate (Pitch me) {
 	}
 }
 
-autoPitch Pitch_subtractLinearFit (Pitch me, int unit) {
+autoPitch Pitch_subtractLinearFit (Pitch me, kPitch_unit unit) {
 	try {
 		autoPitch thee = Pitch_interpolate (me);
 		/*
@@ -729,7 +729,7 @@ autoPitch Pitch_subtractLinearFit (Pitch me, int unit) {
 		 */
 		double sum = 0.0;
 		for (long i = imin; i <= imax; i ++) {
-			sum += Sampled_getValueAtSample (thee.get(), i, Pitch_LEVEL_FREQUENCY, unit);
+			sum += Sampled_getValueAtSample (thee.get(), i, Pitch_LEVEL_FREQUENCY, (int) unit);
 		}
 		double fmean = sum / n;
 		double tmean = thy x1 + (0.5 * (imin + imax) - 1) * thy dx;
@@ -739,7 +739,7 @@ autoPitch Pitch_subtractLinearFit (Pitch me, int unit) {
 		double numerator = 0.0, denominator = 0.0;
 		for (long i = imin; i <= imax; i ++) {
 			double t = thy x1 + (i - 1) * thy dx - tmean;
-			double f = Sampled_getValueAtSample (thee.get(), i, Pitch_LEVEL_FREQUENCY, unit) - fmean;
+			double f = Sampled_getValueAtSample (thee.get(), i, Pitch_LEVEL_FREQUENCY, (int) unit) - fmean;
 			numerator += f * t;
 			denominator += t * t;
 		}
@@ -750,12 +750,12 @@ autoPitch Pitch_subtractLinearFit (Pitch me, int unit) {
 		for (long i = imin; i <= imax; i ++) {
 			Pitch_Frame myFrame = & my frame [i], thyFrame = & thy frame [i];
 			double t = thy x1 + (i - 1) * thy dx - tmean, myFreq = FREQUENCY (myFrame);
-			double f = Sampled_getValueAtSample (thee.get(), i, Pitch_LEVEL_FREQUENCY, unit);
+			double f = Sampled_getValueAtSample (thee.get(), i, Pitch_LEVEL_FREQUENCY, (int) unit);
 			f -= slope * t;
 			if (NOT_VOICED (myFreq))
 				FREQUENCY (thyFrame) = 0.0;
 			else
-				FREQUENCY (thyFrame) = Function_convertSpecialToStandardUnit (me, f, Pitch_LEVEL_FREQUENCY, unit);
+				FREQUENCY (thyFrame) = Function_convertSpecialToStandardUnit (me, f, Pitch_LEVEL_FREQUENCY, (int) unit);
 		}
 		return thee;
 	} catch (MelderError) {
diff --git a/fon/Pitch.h b/fon/Pitch.h
index 8caa8a8..d8dde33 100644
--- a/fon/Pitch.h
+++ b/fon/Pitch.h
@@ -2,7 +2,7 @@
 #define _Pitch_h_
 /* Pitch.h
  *
- * Copyright (C) 1992-2011,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2011,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -90,23 +90,23 @@ bool Pitch_isVoiced_t (Pitch me, double t);
 #define Pitch_NEAREST  0
 #define Pitch_LINEAR  1
 
-double Pitch_getValueAtTime (Pitch me, double time, int unit, bool interpolate);
-double Pitch_getStrengthAtTime (Pitch me, double time, int unit, bool interpolate);
+double Pitch_getValueAtTime (Pitch me, double time, kPitch_unit unit, bool interpolate);
+double Pitch_getStrengthAtTime (Pitch me, double time, kPitch_unit unit, bool interpolate);
 
 long Pitch_countVoicedFrames (Pitch me);
 
-double Pitch_getMean (Pitch me, double tmin, double tmax, int unit);
-double Pitch_getMeanStrength (Pitch me, double tmin, double tmax, int unit);
-double Pitch_getQuantile (Pitch me, double tmin, double tmax, double quantile, int unit);
-double Pitch_getStandardDeviation (Pitch me, double tmin, double tmax, int unit);
-void Pitch_getMaximumAndTime (Pitch me, double tmin, double tmax, int unit, bool interpolate,
+double Pitch_getMean (Pitch me, double tmin, double tmax, kPitch_unit unit);
+double Pitch_getMeanStrength (Pitch me, double tmin, double tmax, int strengthUnit);
+double Pitch_getQuantile (Pitch me, double tmin, double tmax, double quantile, kPitch_unit unit);
+double Pitch_getStandardDeviation (Pitch me, double tmin, double tmax, kPitch_unit unit);
+void Pitch_getMaximumAndTime (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate,
 	double *return_maximum, double *return_timeOfMaximum);
-double Pitch_getMaximum (Pitch me, double tmin, double tmax, int unit, bool interpolate);
-double Pitch_getTimeOfMaximum (Pitch me, double tmin, double tmax, int unit, bool interpolate);
-void Pitch_getMinimumAndTime (Pitch me, double tmin, double tmax, int unit, bool interpolate,
+double Pitch_getMaximum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate);
+double Pitch_getTimeOfMaximum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate);
+void Pitch_getMinimumAndTime (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate,
 	double *return_minimum, double *return_timeOfMinimum);
-double Pitch_getMinimum (Pitch me, double tmin, double tmax, int unit, bool interpolate);
-double Pitch_getTimeOfMinimum (Pitch me, double tmin, double tmax, int unit, bool interpolate);
+double Pitch_getMinimum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate);
+double Pitch_getTimeOfMinimum (Pitch me, double tmin, double tmax, kPitch_unit unit, bool interpolate);
 
 int Pitch_getMaxnCandidates (Pitch me);
 /*
@@ -127,9 +127,9 @@ void Pitch_pathFinder (Pitch me, double silenceThreshold, double voicingThreshol
 #define Pitch_speckle_NO  false
 #define Pitch_speckle_YES  true
 void Pitch_drawInside (Pitch me, Graphics g, double tmin, double tmax, double fmin, double fmax,
-	bool speckle, int yscale);
+	bool speckle, kPitch_unit yscale);
 void Pitch_draw (Pitch me, Graphics g, double tmin, double tmax, double fmin, double fmax, bool garnish,
-	bool speckle, int yscale);
+	bool speckle, kPitch_unit yscale);
 /*
 	draw a pitch contour into a Graphics.
 	If tmax <= tmin, draw whole time domain.
@@ -164,7 +164,7 @@ autoPitch Pitch_interpolate (Pitch me);
 /* Interpolate the pitch values of unvoiced frames. */
 /* No extrapolation beyond first and last voiced frames. */
 
-autoPitch Pitch_subtractLinearFit (Pitch me, int unit);
+autoPitch Pitch_subtractLinearFit (Pitch me, kPitch_unit unit);
 
 autoPitch Pitch_smooth (Pitch me, double bandWidth);
 /* Smoothing by convolution with Gaussian curve.
diff --git a/fon/PitchEditor.cpp b/fon/PitchEditor.cpp
index 3c8b3be..440e587 100644
--- a/fon/PitchEditor.cpp
+++ b/fon/PitchEditor.cpp
@@ -69,9 +69,9 @@ static void menu_cb_pathFinder (PitchEditor me, EDITOR_ARGS_FORM) {
 
 static void menu_cb_getPitch (PitchEditor me, EDITOR_ARGS_DIRECT) {
 	if (my startSelection == my endSelection) {
-		Melder_informationReal (Pitch_getValueAtTime ((Pitch) my data, my startSelection, kPitch_unit_HERTZ, 1), U"Hz");
+		Melder_informationReal (Pitch_getValueAtTime ((Pitch) my data, my startSelection, kPitch_unit::HERTZ, 1), U"Hz");
 	} else {
-		Melder_informationReal (Pitch_getMean ((Pitch) my data, my startSelection, my endSelection, kPitch_unit_HERTZ), U"Hz");
+		Melder_informationReal (Pitch_getMean ((Pitch) my data, my startSelection, my endSelection, kPitch_unit::HERTZ), U"Hz");
 	}
 }
 
@@ -192,7 +192,7 @@ void structPitchEditor :: v_draw () {
 		/* Horizontal hair at current pitch. */
 
 		if (our startSelection == our endSelection && our startSelection >= our startWindow && our startSelection <= our endWindow) {
-			double f = Pitch_getValueAtTime (pitch, our startSelection, kPitch_unit_HERTZ, Pitch_LINEAR);
+			double f = Pitch_getValueAtTime (pitch, our startSelection, kPitch_unit::HERTZ, Pitch_LINEAR);
 			if (isdefined (f)) {
 				Graphics_setColour (our graphics.get(), Graphics_RED);
 				Graphics_line (our graphics.get(), our startWindow - radius, f, our endWindow, f);
diff --git a/fon/PitchTier.cpp b/fon/PitchTier.cpp
index 0d8070d..4d2f3a2 100644
--- a/fon/PitchTier.cpp
+++ b/fon/PitchTier.cpp
@@ -17,7 +17,6 @@
  */
 
 #include "PitchTier.h"
-#include "Pitch.h"
 
 Thing_implement (PitchTier, RealTier, 0);
 
@@ -43,7 +42,7 @@ autoPitchTier PitchTier_create (double tmin, double tmax) {
 }
 
 void PitchTier_draw (PitchTier me, Graphics g, double tmin, double tmax,
-	double fmin, double fmax, int garnish, const char32 *method)
+	double fmin, double fmax, bool garnish, const char32 *method)
 {
 	RealTier_draw (me, g, tmin, tmax, fmin, fmax, garnish, method, U"Frequency (Hz)");
 }
@@ -57,12 +56,11 @@ autoPitchTier PointProcess_upto_PitchTier (PointProcess me, double frequency) {
 	}
 }
 
-void PitchTier_stylize (PitchTier me, double frequencyResolution, int useSemitones) {
-	double dfmin;
+void PitchTier_stylize (PitchTier me, double frequencyResolution, bool useSemitones) {
 	for (;;) {
-		long i, imin = 0;
-		dfmin = 1e308;
-		for (i = 2; i <= my points.size - 1; i ++) {
+		long imin = 0;
+		double dfmin = 1e308;
+		for (long i = 2; i <= my points.size - 1; i ++) {
 			RealPoint pm = my points.at [i];
 			RealPoint pl = my points.at [i - 1];
 			RealPoint pr = my points.at [i + 1];
@@ -108,27 +106,27 @@ void PitchTier_writeToHeaderlessSpreadsheetFile (PitchTier me, MelderFile file)
 	}
 }
 
-void PitchTier_shiftFrequencies (PitchTier me, double tmin, double tmax, double shift, int unit) {
+void PitchTier_shiftFrequencies (PitchTier me, double tmin, double tmax, double shift, kPitch_unit unit) {
 	try {
 		for (long i = 1; i <= my points.size; i ++) {
 			RealPoint point = my points.at [i];
 			double frequency = point -> value;
 			if (point -> number < tmin || point -> number > tmax) continue;
 			switch (unit) {
-				case kPitch_unit_HERTZ: {	
+				case kPitch_unit::HERTZ: {
 					frequency += shift;
 					if (frequency <= 0.0)
 						Melder_throw (U"The resulting frequency has to be greater than 0 Hz.");
-				} break; case kPitch_unit_MEL: {
+				} break; case kPitch_unit::MEL: {
 					frequency = NUMhertzToMel (frequency) + shift;
 					if (frequency <= 0.0)
 						Melder_throw (U"The resulting frequency has to be greater than 0 mel.");
 					frequency = NUMmelToHertz (frequency);
-				} break; case kPitch_unit_LOG_HERTZ: {
+				} break; case kPitch_unit::LOG_HERTZ: {
 					frequency = pow (10.0, log10 (frequency) + shift);
-				} break; case kPitch_unit_SEMITONES_1: {
+				} break; case kPitch_unit::SEMITONES_1: {
 					frequency = NUMsemitonesToHertz (NUMhertzToSemitones (frequency) + shift);
-				} break; case kPitch_unit_ERB: {
+				} break; case kPitch_unit::ERB: {
 					frequency = NUMhertzToErb (frequency) + shift;
 					if (frequency <= 0.0)
 						Melder_throw (U"The resulting frequency has to be greater than 0 ERB.");
diff --git a/fon/PitchTier.h b/fon/PitchTier.h
index 10db8d5..c8c80d0 100644
--- a/fon/PitchTier.h
+++ b/fon/PitchTier.h
@@ -2,7 +2,7 @@
 #define _PitchTier_h_
 /* PitchTier.h
  *
- * Copyright (C) 1992-2011,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
 #include "RealTier.h"
 #include "Graphics.h"
 #include "Sound.h"
+#include "Pitch.h"
 
 /********** class PitchTier **********/
 
@@ -39,14 +40,14 @@ autoPitchTier PitchTier_create (double tmin, double tmax);
 		result -> points.size == 0;
 */
 
-void PitchTier_shiftFrequencies (PitchTier me, double tmin, double tmax, double shift, int units);
+void PitchTier_shiftFrequencies (PitchTier me, double tmin, double tmax, double shift, kPitch_unit units);
 void PitchTier_multiplyFrequencies (PitchTier me, double tmin, double tmax, double factor);
 
 void PitchTier_draw (PitchTier me, Graphics g, double tmin, double tmax,
-	double fmin, double fmax, int garnish, const char32 *method);
+	double fmin, double fmax, bool garnish, const char32 *method);
 
 autoPitchTier PointProcess_upto_PitchTier (PointProcess me, double frequency);
-void PitchTier_stylize (PitchTier me, double frequencyResolution, int useSemitones);
+void PitchTier_stylize (PitchTier me, double frequencyResolution, bool useSemitones);
 
 void PitchTier_writeToPitchTierSpreadsheetFile (PitchTier me, MelderFile file);
 void PitchTier_writeToHeaderlessSpreadsheetFile (PitchTier me, MelderFile file);
diff --git a/fon/Pitch_AnyTier_to_PitchTier.cpp b/fon/Pitch_AnyTier_to_PitchTier.cpp
index d623f70..56ef3e6 100644
--- a/fon/Pitch_AnyTier_to_PitchTier.cpp
+++ b/fon/Pitch_AnyTier_to_PitchTier.cpp
@@ -66,7 +66,7 @@ autoPitchTier Pitch_AnyTier_to_PitchTier (Pitch pitch, AnyTier tier, int checkMe
 		for (long ipoint = 1; ipoint <= tier -> points.size; ipoint ++) {
 			AnyPoint point = tier -> points.at [ipoint];
 			double time = point -> number;
-			double frequency = Pitch_getValueAtTime (pitch, time, kPitch_unit_HERTZ, Pitch_LINEAR);
+			double frequency = Pitch_getValueAtTime (pitch, time, kPitch_unit::HERTZ, Pitch_LINEAR);
 			if (isundef (frequency) && checkMethod != 0)
 				Melder_throw (U"No periodicity at time ", time, U" seconds.");
 			RealTier_addPoint (thee.get(), time, frequency);
diff --git a/fon/Pitch_Intensity.cpp b/fon/Pitch_Intensity.cpp
index 5a92750..f10528e 100644
--- a/fon/Pitch_Intensity.cpp
+++ b/fon/Pitch_Intensity.cpp
@@ -46,7 +46,7 @@ void Pitch_Intensity_draw (Pitch pitch, Intensity intensity, Graphics g,
 	for (long i = 1; i <= pitch -> nx; i ++) {
 		double t = Sampled_indexToX (pitch, i);
 		double x = pitch -> frame [i]. candidate [1]. frequency;
-		double y = Sampled_getValueAtX (intensity, t, Pitch_LEVEL_FREQUENCY, kPitch_unit_HERTZ, true);
+		double y = Sampled_getValueAtX (intensity, t, Pitch_LEVEL_FREQUENCY, (int) kPitch_unit::HERTZ, true);
 		if (x == 0) {
 			continue;   // voiceless
 		}
diff --git a/fon/Pitch_to_PointProcess.cpp b/fon/Pitch_to_PointProcess.cpp
index 310ddb2..8b8345d 100644
--- a/fon/Pitch_to_PointProcess.cpp
+++ b/fon/Pitch_to_PointProcess.cpp
@@ -195,7 +195,7 @@ autoPointProcess Sound_Pitch_to_PointProcess_cc (Sound sound, Pitch pitch) {
 			 */
 			double tmiddle = (tleft + tright) / 2;
 			Melder_progress ((tmiddle - sound -> xmin) / (sound -> xmax - sound -> xmin), U"Sound & Pitch to PointProcess");
-			double f0middle = Pitch_getValueAtTime (pitch, tmiddle, kPitch_unit_HERTZ, Pitch_LINEAR);
+			double f0middle = Pitch_getValueAtTime (pitch, tmiddle, kPitch_unit::HERTZ, Pitch_LINEAR);
 
 			/*
 			 * Our first point is near this middle.
@@ -213,7 +213,7 @@ autoPointProcess Sound_Pitch_to_PointProcess_cc (Sound sound, Pitch pitch) {
 
 			double tsave = tmax;
 			for (;;) {
-				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit_HERTZ, Pitch_LINEAR), correlation;
+				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR), correlation;
 				if (isundef (f0)) break;
 				correlation = Sound_findMaximumCorrelation (sound, tmax, 1.0 / f0, tmax - 1.25 / f0, tmax - 0.8 / f0, & tmax, & peak);
 				if (correlation == -1) /*break*/ tmax -= 1.0 / f0;   // this one period will drop out
@@ -231,7 +231,7 @@ autoPointProcess Sound_Pitch_to_PointProcess_cc (Sound sound, Pitch pitch) {
 			}
 			tmax = tsave;
 			for (;;) {
-				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit_HERTZ, Pitch_LINEAR), correlation;
+				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR), correlation;
 				if (isundef (f0)) break;
 				correlation = Sound_findMaximumCorrelation (sound, tmax, 1.0 / f0, tmax + 0.8 / f0, tmax + 1.25 / f0, & tmax, & peak);
 				if (correlation == -1) /*break*/ tmax += 1.0 / f0;
@@ -274,7 +274,7 @@ autoPointProcess Sound_Pitch_to_PointProcess_peaks (Sound sound, Pitch pitch, in
 			 */
 			double tmiddle = (tleft + tright) / 2;
 			Melder_progress ((tmiddle - sound -> xmin) / (sound -> xmax - sound -> xmin), U"Sound & Pitch: To PointProcess");
-			double f0middle = Pitch_getValueAtTime (pitch, tmiddle, kPitch_unit_HERTZ, Pitch_LINEAR);
+			double f0middle = Pitch_getValueAtTime (pitch, tmiddle, kPitch_unit::HERTZ, Pitch_LINEAR);
 
 			/*
 			 * Our first point is near this middle.
@@ -286,7 +286,7 @@ autoPointProcess Sound_Pitch_to_PointProcess_peaks (Sound sound, Pitch pitch, in
 
 			double tsave = tmax;
 			for (;;) {
-				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit_HERTZ, Pitch_LINEAR);
+				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR);
 				if (isundef (f0)) break;
 				tmax = Sound_findExtremum (sound, tmax - 1.25 / f0, tmax - 0.8 / f0, includeMaxima, includeMinima);
 				if (tmax < tleft) {
@@ -301,7 +301,7 @@ autoPointProcess Sound_Pitch_to_PointProcess_peaks (Sound sound, Pitch pitch, in
 			}
 			tmax = tsave;
 			for (;;) {
-				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit_HERTZ, Pitch_LINEAR);
+				double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR);
 				if (isundef (f0)) break;
 				tmax = Sound_findExtremum (sound, tmax + 0.8 / f0, tmax + 1.25 / f0, includeMaxima, includeMinima);
 				if (tmax > tright) {
diff --git a/fon/PointEditor.cpp b/fon/PointEditor.cpp
index 6613448..f61da8f 100644
--- a/fon/PointEditor.cpp
+++ b/fon/PointEditor.cpp
@@ -158,7 +158,7 @@ void structPointEditor :: v_draw () {
 	Graphics_setWindow (our graphics.get(), 0.0, 1.0, 0.0, 1.0);
 	Graphics_fillRectangle (our graphics.get(), 0.0, 1.0, 0.0, 1.0);
 	double minimum = -1.0, maximum = +1.0;
-	if (sound && (p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_BY_WINDOW || p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_BY_WINDOW_AND_CHANNEL)) {
+	if (sound && (p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW || p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW_AND_CHANNEL)) {
 		integer first, last;
 		if (Sampled_getWindowSamples (sound, our startWindow, our endWindow, & first, & last) >= 1) {
 			Matrix_getWindowExtrema (sound, first, last, 1, 1, & minimum, & maximum);
diff --git a/fon/Praat_tests.cpp b/fon/Praat_tests.cpp
index 8feda17..a1477ad 100644
--- a/fon/Praat_tests.cpp
+++ b/fon/Praat_tests.cpp
@@ -54,7 +54,7 @@ static int length (const char32 *s) {
 	return result;
 }
 
-int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *arg4) {
+int Praat_tests (kPraatTests itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *arg4) {
 	int64 n = Melder_atoi (arg1);
 	double t = 0.0;
 	(void) arg1;
@@ -64,58 +64,60 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 	Melder_clearInfo ();
 	Melder_stopwatch ();
 	switch (itest) {
-		case kPraatTests_TIME_RANDOM_FRACTION: {
+		case kPraatTests::TIME_RANDOM_FRACTION: {
 			for (int64 i = 1; i <= n; i ++)
 				(void) NUMrandomFraction ();
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_RANDOM_GAUSS: {
+		case kPraatTests::TIME_RANDOM_GAUSS: {
 			for (int64 i = 1; i <= n; i ++)
 				(void) NUMrandomGauss (0.0, 1.0);
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_SORT: {
-			long m = Melder_atoi (arg2);
-			long *array = NUMvector <long> (1, m);
-			for (int64 i = 1; i <= m; i ++)
-				array [i] = NUMrandomInteger (1, 100);
+		case kPraatTests::TIME_SORT: {
+			integer size = Melder_atoi (arg2);
+			double *array = NUMvector <double> (1, size);
 			Melder_stopwatch ();
-			for (int64 i = 1; i <= n; i ++)
-				NUMsort_l (m, array);
-			t = Melder_stopwatch ();
+			for (int64 iteration = 1; iteration <= n; iteration ++) {
+				for (int64 i = 1; i <= size; i ++) {
+					array [i] = NUMrandomFraction ();
+				}
+				NUMsort_d (size, array);
+			}
+			t = Melder_stopwatch () / (size * log2 (size));
 			NUMvector_free (array, 1);
 		} break;
-		case kPraatTests_TIME_INTEGER: {
+		case kPraatTests::TIME_INTEGER: {
 			int64 sum = 0;
 			for (int64 i = 1; i <= n; i ++)
 				sum += i * (i - 1) * (i - 2);
 			t = Melder_stopwatch ();
 			MelderInfo_writeLine (sum);
 		} break;
-		case kPraatTests_TIME_FLOAT: {
+		case kPraatTests::TIME_FLOAT: {
 			double sum = 0.0, fn = n;
-			for (double fi = 1.0; fi <= fn; fi ++)
+			for (double fi = 1.0; fi <= fn; fi += 1.0)
 				sum += fi * (fi - 1.0) * (fi - 2.0);
 			t = Melder_stopwatch ();   // 2.02 ns
 			MelderInfo_writeLine (sum);
 		} break;
-		case kPraatTests_TIME_FLOAT_TO_UNSIGNED_BUILTIN: {
+		case kPraatTests::TIME_FLOAT_TO_UNSIGNED_BUILTIN: {
 			uint64_t sum = 0;
 			double fn = n;
-			for (double fi = 1.0; fi <= fn; fi = fi + 1.0)
+			for (double fi = 1.0; fi <= fn; fi += 1.0)
 				sum += (uint32) fi;
 			t = Melder_stopwatch ();   // 1.45 ns
 			MelderInfo_writeLine (sum);
 		} break;
-		case kPraatTests_TIME_FLOAT_TO_UNSIGNED_EXTERN: {
+		case kPraatTests::TIME_FLOAT_TO_UNSIGNED_EXTERN: {
 			uint64_t sum = 0;
 			double fn = n;
-			for (double fi = 1.0; fi <= fn; fi = fi + 1.0)
+			for (double fi = 1.0; fi <= fn; fi += 1.0)
 				sum += (uint32) ((int32) (fi - 2147483648.0) + 2147483647L + 1);
 			t = Melder_stopwatch ();   // 1.47 ns
 			MelderInfo_writeLine (sum);
 		} break;
-		case kPraatTests_TIME_UNSIGNED_TO_FLOAT_BUILTIN: {
+		case kPraatTests::TIME_UNSIGNED_TO_FLOAT_BUILTIN: {
 			double sum = 0.0;
 			uint32 nu = (uint32) n;
 			for (uint32 iu = 1; iu <= nu; iu ++)
@@ -123,7 +125,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();   // 0.88 ns
 			MelderInfo_writeLine (sum);
 		} break;
-		case kPraatTests_TIME_UNSIGNED_TO_FLOAT_EXTERN: {
+		case kPraatTests::TIME_UNSIGNED_TO_FLOAT_EXTERN: {
 			double sum = 0.0;
 			uint32 nu = (uint32) n;
 			for (uint32 iu = 1; iu <= nu; iu ++)
@@ -131,7 +133,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();   // 0.87 ns
 			MelderInfo_writeLine (sum);
 		} break;
-		case kPraatTests_TIME_STRING_MELDER_32: {
+		case kPraatTests::TIME_STRING_MELDER_32: {
 			autoMelderString string;
 			char32 word [] { U"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
@@ -142,7 +144,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRING_MELDER_32_ALLOC: {
+		case kPraatTests::TIME_STRING_MELDER_32_ALLOC: {
 			char32 word [] { U"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
 			for (int64 i = 1; i <= n; i ++) {
@@ -153,7 +155,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRING_CPP_S: {
+		case kPraatTests::TIME_STRING_CPP_S: {
 			std::string s = "";
 			char word [] { "abc" };
 			word [2] = (char) NUMrandomInteger ('a', 'z');
@@ -164,7 +166,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRING_CPP_C: {
+		case kPraatTests::TIME_STRING_CPP_C: {
 			std::basic_string<char> s = "";
 			char word [] { "abc" };
 			word [2] = (char) NUMrandomInteger ('a', 'z');
@@ -175,7 +177,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRING_CPP_WS: {
+		case kPraatTests::TIME_STRING_CPP_WS: {
 			std::wstring s = L"";
 			wchar_t word [] { L"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
@@ -186,7 +188,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRING_CPP_WC: {
+		case kPraatTests::TIME_STRING_CPP_WC: {
 			std::basic_string<wchar_t> s = L"";
 			wchar_t word [] { L"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
@@ -197,7 +199,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRING_CPP_32: {
+		case kPraatTests::TIME_STRING_CPP_32: {
 			std::basic_string<char32_t> s = U"";
 			char32 word [] { U"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
@@ -208,7 +210,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRING_CPP_U32STRING: {
+		case kPraatTests::TIME_STRING_CPP_U32STRING: {
 			std::u32string s = U"";
 			char32 word [] { U"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
@@ -219,7 +221,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STRCPY: {
+		case kPraatTests::TIME_STRCPY: {
 			char buffer [100];
 			char word [] { "abc" };
 			word [2] = (char) NUMrandomInteger ('a', 'z');
@@ -231,7 +233,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();
 			MelderInfo_writeLine (Melder_peek8to32 (buffer));
 		} break;
-		case kPraatTests_TIME_WCSCPY: {
+		case kPraatTests::TIME_WCSCPY: {
 			wchar_t buffer [100];
 			wchar_t word [] { L"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
@@ -242,7 +244,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_STR32CPY: {
+		case kPraatTests::TIME_STR32CPY: {
 			char32 buffer [100];
 			char32 word [] { U"abc" };
 			word [2] = NUMrandomInteger ('a', 'z');
@@ -254,14 +256,14 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();
 			MelderInfo_writeLine (buffer);
 		} break;
-		case kPraatTests_TIME_GRAPHICS_TEXT_TOP: {
+		case kPraatTests::TIME_GRAPHICS_TEXT_TOP: {
 			autoPraatPicture picture;
 			for (int64 i = 1; i <= n; i ++) {
 				Graphics_textTop (GRAPHICS, false, U"hello world");
 			}
 			t = Melder_stopwatch ();
 		} break;
-		case kPraatTests_TIME_UNDEFINED_NUMUNDEFINED: {
+		case kPraatTests::TIME_UNDEFINED_NUMUNDEFINED: {
 			bool isAllDefined = true;
 			double x = 0.0;
 			for (int64 i = 1; i <= n; i ++) {
@@ -271,17 +273,18 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();   // 0.86 ns
 			MelderInfo_writeLine (isAllDefined, U" ", x);
 		} break;
-		case kPraatTests_TIME_UNDEFINED_ISINF_OR_ISNAN: {
+		case kPraatTests::TIME_UNDEFINED_ISINF_OR_ISNAN: {
 			bool isAllDefined = true;
 			double x = 0.0;
 			for (int64 i = 1; i <= n; i ++) {
 				x += (double) i;
 				isAllDefined &= ! isinf (x) && ! isnan (x);
+				//isAllDefined &= ! isfinite (x);   // same
 			}
 			t = Melder_stopwatch ();   // 1.29 ns
 			MelderInfo_writeLine (isAllDefined, U" ", x);
 		} break;
-		case kPraatTests_TIME_UNDEFINED_0x7FF: {
+		case kPraatTests::TIME_UNDEFINED_0x7FF: {
 			bool isAllDefined = true;
 			double x = 0.0;
 			for (int64 i = 1; i <= n; i ++) {
@@ -291,7 +294,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();   // 0.90 ns
 			MelderInfo_writeLine (isAllDefined, U" ", x);
 		} break;
-		case kPraatTests_TIME_INNER: {
+		case kPraatTests::TIME_INNER: {
 			int size = Melder_atoi (arg2);
 			autonumvec x { size, false }, y { size, false };
 			for (int64 i = 1; i <= size; i ++) {
@@ -305,7 +308,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch () / size;   // 0.43 ns per multiplication-addition pair
 			MelderInfo_writeLine (z);
 		} break;
-		case kPraatTests_TIME_OUTER_NUMMAT: {
+		case kPraatTests::TIME_OUTER_NUMMAT: {
 			int nrow = 100, ncol = 100;
 			numvec x { NUMvector<double> (1, nrow), nrow }, y { NUMvector<double> (1, ncol), ncol };
 			for (int64 i = 1; i <= nrow; i ++)
@@ -319,10 +322,10 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			NUMvector_free (x.at, 1);
 			NUMvector_free (y.at, 1);
 		} break;
-		case kPraatTests_CHECK_INVFISHERQ: {
+		case kPraatTests::CHECK_INVFISHERQ: {
 			MelderInfo_writeLine (NUMinvFisherQ (0.003, 1, 100000));
 		} break;
-		case kPraatTests_TIME_AUTOSTRING: {
+		case kPraatTests::TIME_AUTOSTRING: {
 			const char32 *strings [6] = { U"ghdg", U"jhd", U"hkfjjd", U"fhfj", U"jhksfd", U"hfjs" };
 			int64 sumOfLengths = 0;
 			for (int64 i = 1; i <= n; i ++) {
@@ -333,7 +336,7 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();   // 72 ns (but 152 bytes more)
 			MelderInfo_writeLine (sumOfLengths);
 		} break;
-		case kPraatTests_TIME_CHAR32: {
+		case kPraatTests::TIME_CHAR32: {
 			const char32 *strings [6] = { U"ghdg", U"jhd", U"hkfjjd", U"fhfj", U"jhksfd", U"hfjs" };
 			int64 sumOfLengths = 0;
 			for (int64 i = 1; i <= n; i ++) {
@@ -344,11 +347,11 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch ();   // 72 ns
 			MelderInfo_writeLine (sumOfLengths);
 		} break;
-		case kPraatTests_TIME_SUM: {
+		case kPraatTests::TIME_SUM: {
 			integer size = Melder_atoi (arg2);
 			autonumvec x { size, false };
 			for (integer i = 1; i <= size; i ++)
-				x.at [i] = NUMrandomGauss (0.0, 1.0);
+				x [i] = NUMrandomGauss (0.0, 1.0);
 			double z = 0.0;
 			for (int64 i = 1; i <= n; i ++) {
 				real sum = sum_scalar (x.get());
@@ -357,11 +360,11 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch () / size;   // for size == 100: 0.31 ns
 			MelderInfo_writeLine (z);
 		} break;
-		case kPraatTests_TIME_MEAN: {
+		case kPraatTests::TIME_MEAN: {
 			integer size = Melder_atoi (arg2);
 			autonumvec x { size, false };
 			for (integer i = 1; i <= size; i ++)
-				x.at [i] = NUMrandomGauss (0.0, 1.0);
+				x [i] = NUMrandomGauss (0.0, 1.0);
 			double z = 0.0;
 			for (int64 i = 1; i <= n; i ++) {
 				real sum = mean_scalar (x.get());
@@ -370,11 +373,11 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch () / size;   // for size == 100: 0.34 ns
 			MelderInfo_writeLine (z);
 		} break;
-		case kPraatTests_TIME_STDEV: {
+		case kPraatTests::TIME_STDEV: {
 			integer size = 10000;
 			autonumvec x { size, false };
 			for (integer i = 1; i <= size; i ++)
-				x.at [i] = NUMrandomGauss (0.0, 1.0);
+				x [i] = NUMrandomGauss (0.0, 1.0);
 			double z = 0.0;
 			for (int64 i = 1; i <= n; i ++) {
 				real stdev = stdev_scalar (x.get());
@@ -383,7 +386,62 @@ int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *ar
 			t = Melder_stopwatch () / size;
 			MelderInfo_writeLine (z);
 		} break;
-		case kPraatTests_THING_AUTO: {
+		case kPraatTests::TIME_ALLOC: {
+			integer size = Melder_atoi (arg2);
+			for (int64 iteration = 1; iteration <= n; iteration ++) {
+				autonumvec result (size, false);
+			}
+			t = Melder_stopwatch () / size;
+		} break;
+		case kPraatTests::TIME_ALLOC0: {
+			integer size = Melder_atoi (arg2);
+			for (int64 iteration = 1; iteration <= n; iteration ++) {
+				autonumvec result (size, true);
+			}
+			t = Melder_stopwatch () / size;
+		} break;
+		case kPraatTests::TIME_ZERO: {
+			integer size = Melder_atoi (arg2);
+			autonumvec result { size, false };
+			double z = 0.0;
+			for (int64 iteration = 1; iteration <= n; iteration ++) {
+				for (long i = 1; i <= size; i ++) {
+					result [i] = (real) i;
+				}
+				z += result [size - 1];
+			}
+			t = Melder_stopwatch () / size;
+			MelderInfo_writeLine (z);
+		} break;
+		case kPraatTests::TIME_MALLOC: {
+			integer size = Melder_atoi (arg2);
+			double z = 0.0;
+			for (int64 iteration = 1; iteration <= n; iteration ++) {
+				double *result = (double *) malloc (sizeof (double) * (size_t) size);
+				for (long i = 0; i < size; i ++) {
+					result [i] = (real) i;
+				}
+				z += result [size - 1];
+				free (result);
+			}
+			t = Melder_stopwatch () / size;
+			MelderInfo_writeLine (z);
+		} break;
+		case kPraatTests::TIME_CALLOC: {
+			integer size = Melder_atoi (arg2);
+			double z = 0.0;
+			for (int64 iteration = 1; iteration <= n; iteration ++) {
+				double *result = (double *) calloc (sizeof (double), (size_t) size);
+				for (long i = 0; i < size; i ++) {
+					result [i] = (real) i;
+				}
+				z += result [size - 1];
+				free (result);
+			}
+			t = Melder_stopwatch () / size;
+			MelderInfo_writeLine (z);
+		} break;
+		case kPraatTests::THING_AUTO: {
 			int numberOfThingsBefore = theTotalNumberOfThings;
 			{
 				Melder_casual (U"1\n");
diff --git a/fon/Praat_tests.h b/fon/Praat_tests.h
index 9ec3799..cbda66b 100644
--- a/fon/Praat_tests.h
+++ b/fon/Praat_tests.h
@@ -22,7 +22,7 @@
 
 #include "Praat_tests_enums.h"
 
-int Praat_tests (int itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *arg4);
+int Praat_tests (kPraatTests itest, char32 *arg1, char32 *arg2, char32 *arg3, char32 *arg4);
 
 #endif
 /* End of file Praat_tests.h */
diff --git a/fon/Praat_tests_enums.h b/fon/Praat_tests_enums.h
index 35947ef..56b46d4 100644
--- a/fon/Praat_tests_enums.h
+++ b/fon/Praat_tests_enums.h
@@ -51,7 +51,12 @@ enums_begin (kPraatTests, 0)
 	enums_add (kPraatTests, 31, TIME_SUM, U"TimeSum")
 	enums_add (kPraatTests, 32, TIME_MEAN, U"TimeMean")
 	enums_add (kPraatTests, 33, TIME_STDEV, U"TimeStdev")
-	enums_add (kPraatTests, 34, THING_AUTO, U"ThingAuto")
-enums_end (kPraatTests, 34, CHECK_RANDOM_1009_2009)
+	enums_add (kPraatTests, 34, TIME_ALLOC, U"TimeAlloc")
+	enums_add (kPraatTests, 35, TIME_ALLOC0, U"TimeAlloc0")
+	enums_add (kPraatTests, 36, TIME_ZERO, U"TimeZero")
+	enums_add (kPraatTests, 37, TIME_MALLOC, U"TimeMalloc")
+	enums_add (kPraatTests, 38, TIME_CALLOC, U"TimeCalloc")
+	enums_add (kPraatTests, 39, THING_AUTO, U"ThingAuto")
+enums_end (kPraatTests, 39, CHECK_RANDOM_1009_2009)
 
 /* End of file Praat_tests_enums.h */
diff --git a/fon/RealTier.cpp b/fon/RealTier.cpp
index 3052cb8..cb3c153 100644
--- a/fon/RealTier.cpp
+++ b/fon/RealTier.cpp
@@ -468,9 +468,9 @@ void RealTier_formula (RealTier me, const char32 *expression, Interpreter interp
 		for (long icol = 1; icol <= my points.size; icol ++) {
 			Formula_Result result;
 			Formula_run (0, icol, & result);
-			if (isundef (result. result.numericResult))
+			if (isundef (result. numericResult))
 				Melder_throw (U"Cannot put an undefined value into the tier.");
-			thy points.at [icol] -> value = result. result.numericResult;
+			thy points.at [icol] -> value = result. numericResult;
 		}
 	} catch (MelderError) {
 		Melder_throw (me, U": formula not completed.");
diff --git a/fon/RealTierEditor.cpp b/fon/RealTierEditor.cpp
index 51da7f5..8f93ea2 100644
--- a/fon/RealTierEditor.cpp
+++ b/fon/RealTierEditor.cpp
@@ -50,7 +50,7 @@ static void menu_cb_addPointAtCursor (RealTierEditor me, EDITOR_ARGS_DIRECT) {
 }
 
 static void menu_cb_addPointAt (RealTierEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Add point", 0)
+	EDITOR_FORM (U"Add point", nullptr)
 		REAL (U"Time (s)", U"0.0")
 		REAL (my v_quantityText (), U"0.0")
 	EDITOR_OK
@@ -71,7 +71,7 @@ static void menu_cb_addPointAt (RealTierEditor me, EDITOR_ARGS_FORM) {
 }
 
 static void menu_cb_setRange (RealTierEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (my v_setRangeTitle (), 0)
+	EDITOR_FORM (my v_setRangeTitle (), nullptr)
 		REAL (my v_yminText (), my v_defaultYminText ())
 		REAL (my v_ymaxText (), my v_defaultYmaxText ())
 	EDITOR_OK
@@ -233,7 +233,7 @@ static void drawWhileDragging (RealTierEditor me, double /* xWC */, double /* yW
 		RealPoint point = data -> points.at [first];
 		double t = point -> number + dt, y = point -> value + dy;
 		Graphics_line (my graphics.get(), t, my ymin, t, my ymax - Graphics_dyMMtoWC (my graphics.get(), 4.0));
-		Graphics_setTextAlignment (my graphics.get(), kGraphics_horizontalAlignment_CENTRE, Graphics_TOP);
+		Graphics_setTextAlignment (my graphics.get(), kGraphics_horizontalAlignment::CENTRE, Graphics_TOP);
 		Graphics_text (my graphics.get(), t, my ymax, Melder_fixed (t, 6));
 		Graphics_line (my graphics.get(), my startWindow, y, my endWindow, y);
 		Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_BOTTOM);
diff --git a/fon/RunnerMFC.cpp b/fon/RunnerMFC.cpp
index 6aedfb7..d9bedc3 100644
--- a/fon/RunnerMFC.cpp
+++ b/fon/RunnerMFC.cpp
@@ -86,7 +86,7 @@ static void drawNow (RunnerMFC me) {
 		const char32 *visibleText = experiment -> stimulus [experiment -> stimuli [experiment -> trial]]. visibleText;
 		autostring32 visibleText_dup = Melder_dup_f (visibleText ? visibleText : U"");
 		char32 *visibleText_p = visibleText_dup.peek();
-		Graphics_setFont (my graphics.get(), kGraphics_font_TIMES);
+		Graphics_setFont (my graphics.get(), kGraphics_font::TIMES);
 		Graphics_setFontSize (my graphics.get(), 10);
 		Graphics_setColour (my graphics.get(), Graphics_BLACK);
 		Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_TOP);
@@ -212,7 +212,7 @@ static void do_ok (RunnerMFC me) {
 		if (experiment -> stimuliAreSounds) {
 			autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 			if (experiment -> blankWhilePlaying)
-				 MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
+				 MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::SYNCHRONOUS);
 			ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 		}
 		my blanked = false;
@@ -241,7 +241,7 @@ static void do_oops (RunnerMFC me) {
 	if (experiment -> stimuliAreSounds) {
 		autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 		if (experiment -> blankWhilePlaying)
-			MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
+			MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::SYNCHRONOUS);
 		ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 	}
 	my blanked = false;
@@ -261,7 +261,7 @@ static void do_replay (RunnerMFC me) {
 	if (experiment -> stimuliAreSounds) {
 		autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 		if (experiment -> blankWhilePlaying)
-			MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
+			MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::SYNCHRONOUS);
 		ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 	}
 	my blanked = false;
@@ -293,7 +293,7 @@ static void gui_drawingarea_cb_click (RunnerMFC me, GuiDrawingArea_ClickEvent ev
 			}
 			autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 			if (experiment -> blankWhilePlaying)
-				MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
+				MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::SYNCHRONOUS);
 			ExperimentMFC_playStimulus (experiment, experiment -> stimuli [1]);   // works only if there is at least one trial
 		}
 		my blanked = false;
@@ -315,7 +315,7 @@ static void gui_drawingarea_cb_click (RunnerMFC me, GuiDrawingArea_ClickEvent ev
 			if (experiment -> stimuliAreSounds) {
 				autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 				if (experiment -> blankWhilePlaying)
-					MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
+					MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::SYNCHRONOUS);
 				ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 			}
 			my blanked = false;
diff --git a/fon/Sampled.cpp b/fon/Sampled.cpp
index 77ac6b6..f17c567 100644
--- a/fon/Sampled.cpp
+++ b/fon/Sampled.cpp
@@ -60,7 +60,7 @@ integer Sampled_getWindowSamples (Sampled me, double xmin, double xmax, integer
 	return *ixmax - *ixmin + 1;
 }
 
-void Sampled_init (Sampled me, double xmin, double xmax, long nx, double dx, double x1) {
+void Sampled_init (Sampled me, double xmin, double xmax, integer nx, double dx, double x1) {
 	my xmin = xmin;
 	my xmax = xmax;
 	my nx = nx;
@@ -68,13 +68,13 @@ void Sampled_init (Sampled me, double xmin, double xmax, long nx, double dx, dou
 	my x1 = x1;
 }
 
-void Sampled_shortTermAnalysis (Sampled me, double windowDuration, double timeStep, long *numberOfFrames, double *firstTime) {
+void Sampled_shortTermAnalysis (Sampled me, double windowDuration, double timeStep, integer *numberOfFrames, double *firstTime) {
 	Melder_assert (windowDuration > 0.0);
 	Melder_assert (timeStep > 0.0);
 	volatile double myDuration = my dx * my nx;
 	if (windowDuration > myDuration)
 		Melder_throw (me, U": shorter than window length.");
-	*numberOfFrames = (long) floor ((myDuration - windowDuration) / timeStep) + 1;
+	*numberOfFrames = (integer) floorl ((myDuration - windowDuration) / timeStep) + 1;
 	Melder_assert (*numberOfFrames >= 1);
 	double ourMidTime = my x1 - 0.5 * my dx + 0.5 * myDuration;
 	double thyDuration = *numberOfFrames * timeStep;
diff --git a/fon/Sampled.h b/fon/Sampled.h
index 69c7cbb..75bd068 100644
--- a/fon/Sampled.h
+++ b/fon/Sampled.h
@@ -37,10 +37,10 @@ static inline integer Sampled_xToNearestIndex (Sampled me, double x) { return (i
 
 integer Sampled_getWindowSamples (Sampled me, double xmin, double xmax, integer *ixmin, integer *ixmax);
 
-void Sampled_init (Sampled me, double xmin, double xmax, long nx, double dx, double x1);
+void Sampled_init (Sampled me, double xmin, double xmax, integer nx, double dx, double x1);
 
 void Sampled_shortTermAnalysis (Sampled me, double windowDuration, double timeStep,
-		long *numberOfFrames, double *firstTime);
+	integer *numberOfFrames, double *firstTime);
 /*
 	Function:
 		how to put as many analysis windows of length 'windowDuration' as possible into my duration,
diff --git a/fon/SampledXY_def.h b/fon/SampledXY_def.h
index f0cc1fd..fb59e62 100644
--- a/fon/SampledXY_def.h
+++ b/fon/SampledXY_def.h
@@ -1,6 +1,6 @@
 /* SampledXY_def.h
  *
- * Copyright (C) 1992-2011,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@ oo_DEFINE_CLASS (SampledXY, Sampled)
 
 	oo_DOUBLE (ymin)
 	oo_DOUBLE (ymax)
-	oo_LONG (ny)
+	oo_INTEGER (ny)
 	oo_DOUBLE (dy)
 	oo_DOUBLE (y1)
 
diff --git a/fon/Sampled_def.h b/fon/Sampled_def.h
index 70f85f0..94f0eb6 100644
--- a/fon/Sampled_def.h
+++ b/fon/Sampled_def.h
@@ -1,6 +1,6 @@
 /* Sampled_def.h
  *
- * Copyright (C) 1992-2011,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
 #define ooSTRUCT Sampled
 oo_DEFINE_CLASS (Sampled, Function)
 
-	oo_INT32 (nx)
+	oo_INTEGER (nx)
 	oo_DOUBLE (dx)
 	oo_DOUBLE (x1)
 
diff --git a/fon/Sound.cpp b/fon/Sound.cpp
index dd2513b..4b26c6d 100644
--- a/fon/Sound.cpp
+++ b/fon/Sound.cpp
@@ -506,7 +506,7 @@ autoSound Sounds_concatenate (OrderedOf<structSound>& list, double overlapTime)
 	}
 }
 
-autoSound Sounds_convolve (Sound me, Sound thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
+autoSound Sounds_convolve (Sound me, Sound thee, kSounds_convolve_scaling scaling, kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
 	try {
 		if (my ny > 1 && thy ny > 1 && my ny != thy ny)
 			Melder_throw (U"The numbers of channels of the two sounds have to be equal or 1.");
@@ -542,10 +542,10 @@ autoSound Sounds_convolve (Sound me, Sound thee, enum kSounds_convolve_scaling s
 			}
 		}
 		switch (signalOutsideTimeDomain) {
-			case kSounds_convolve_signalOutsideTimeDomain_ZERO: {
+			case kSounds_convolve_signalOutsideTimeDomain::ZERO: {
 				// do nothing
 			} break;
-			case kSounds_convolve_signalOutsideTimeDomain_SIMILAR: {
+			case kSounds_convolve_signalOutsideTimeDomain::SIMILAR: {
 				for (long channel = 1; channel <= numberOfChannels; channel ++) {
 					double *a = his z [channel];
 					double edge = n1 < n2 ? n1 : n2;
@@ -559,25 +559,25 @@ autoSound Sounds_convolve (Sound me, Sound thee, enum kSounds_convolve_scaling s
 			//case kSounds_convolve_signalOutsideTimeDomain_PERIODIC: {
 				// do nothing
 			//} break;
-			default: Melder_fatal (U"Sounds_convolve: unimplemented outside-time-domain strategy ", signalOutsideTimeDomain);
+			default: Melder_fatal (U"Sounds_convolve: unimplemented outside-time-domain strategy ", (int) signalOutsideTimeDomain);
 		}
 		switch (scaling) {
-			case kSounds_convolve_scaling_INTEGRAL: {
+			case kSounds_convolve_scaling::INTEGRAL: {
 				Vector_multiplyByScalar (him.get(), my dx / nfft);
 			} break;
-			case kSounds_convolve_scaling_SUM: {
+			case kSounds_convolve_scaling::SUM: {
 				Vector_multiplyByScalar (him.get(), 1.0 / nfft);
 			} break;
-			case kSounds_convolve_scaling_NORMALIZE: {
+			case kSounds_convolve_scaling::NORMALIZE: {
 				double normalizationFactor = Matrix_getNorm (me) * Matrix_getNorm (thee);
 				if (normalizationFactor != 0.0) {
 					Vector_multiplyByScalar (him.get(), 1.0 / nfft / normalizationFactor);
 				}
 			} break;
-			case kSounds_convolve_scaling_PEAK_099: {
+			case kSounds_convolve_scaling::PEAK_099: {
 				Vector_scale (him.get(), 0.99);
 			} break;
-			default: Melder_fatal (U"Sounds_convolve: unimplemented scaling ", scaling);
+			default: Melder_fatal (U"Sounds_convolve: unimplemented scaling ", (int) scaling);
 		}
 		return him;
 	} catch (MelderError) {
@@ -585,7 +585,7 @@ autoSound Sounds_convolve (Sound me, Sound thee, enum kSounds_convolve_scaling s
 	}
 }
 
-autoSound Sounds_crossCorrelate (Sound me, Sound thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
+autoSound Sounds_crossCorrelate (Sound me, Sound thee, kSounds_convolve_scaling scaling, kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
 	try {
 		if (my ny > 1 && thy ny > 1 && my ny != thy ny)
 			Melder_throw (U"The numbers of channels of the two sounds have to be equal or 1.");
@@ -625,10 +625,10 @@ autoSound Sounds_crossCorrelate (Sound me, Sound thee, enum kSounds_convolve_sca
 			}
 		}
 		switch (signalOutsideTimeDomain) {
-			case kSounds_convolve_signalOutsideTimeDomain_ZERO: {
+			case kSounds_convolve_signalOutsideTimeDomain::ZERO: {
 				// do nothing
 			} break;
-			case kSounds_convolve_signalOutsideTimeDomain_SIMILAR: {
+			case kSounds_convolve_signalOutsideTimeDomain::SIMILAR: {
 				for (long channel = 1; channel <= numberOfChannels; channel ++) {
 					double *a = his z [channel];
 					double edge = n1 < n2 ? n1 : n2;
@@ -642,25 +642,25 @@ autoSound Sounds_crossCorrelate (Sound me, Sound thee, enum kSounds_convolve_sca
 			//case kSounds_convolve_signalOutsideTimeDomain_PERIODIC: {
 				// do nothing
 			//} break;
-			default: Melder_fatal (U"Sounds_crossCorrelate: unimplemented outside-time-domain strategy ", signalOutsideTimeDomain);
+			default: Melder_fatal (U"Sounds_crossCorrelate: unimplemented outside-time-domain strategy ", (int) signalOutsideTimeDomain);
 		}
 		switch (scaling) {
-			case kSounds_convolve_scaling_INTEGRAL: {
+			case kSounds_convolve_scaling::INTEGRAL: {
 				Vector_multiplyByScalar (him.get(), my dx / nfft);
 			} break;
-			case kSounds_convolve_scaling_SUM: {
+			case kSounds_convolve_scaling::SUM: {
 				Vector_multiplyByScalar (him.get(), 1.0 / nfft);
 			} break;
-			case kSounds_convolve_scaling_NORMALIZE: {
+			case kSounds_convolve_scaling::NORMALIZE: {
 				double normalizationFactor = Matrix_getNorm (me) * Matrix_getNorm (thee);
 				if (normalizationFactor != 0.0) {
 					Vector_multiplyByScalar (him.get(), 1.0 / nfft / normalizationFactor);
 				}
 			} break;
-			case kSounds_convolve_scaling_PEAK_099: {
+			case kSounds_convolve_scaling::PEAK_099: {
 				Vector_scale (him.get(), 0.99);
 			} break;
-			default: Melder_fatal (U"Sounds_crossCorrelate: unimplemented scaling ", scaling);
+			default: Melder_fatal (U"Sounds_crossCorrelate: unimplemented scaling ", (int) scaling);
 		}
 		return him;
 	} catch (MelderError) {
@@ -668,7 +668,7 @@ autoSound Sounds_crossCorrelate (Sound me, Sound thee, enum kSounds_convolve_sca
 	}
 }
 
-autoSound Sound_autoCorrelate (Sound me, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
+autoSound Sound_autoCorrelate (Sound me, kSounds_convolve_scaling scaling, kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
 	try {
 		long numberOfChannels = my ny, n1 = my nx, n2 = n1 + n1 - 1, nfft = 1;
 		while (nfft < n2) nfft *= 2;
@@ -696,10 +696,10 @@ autoSound Sound_autoCorrelate (Sound me, enum kSounds_convolve_scaling scaling,
 			}
 		}
 		switch (signalOutsideTimeDomain) {
-			case kSounds_convolve_signalOutsideTimeDomain_ZERO: {
+			case kSounds_convolve_signalOutsideTimeDomain::ZERO: {
 				// do nothing
 			} break;
-			case kSounds_convolve_signalOutsideTimeDomain_SIMILAR: {
+			case kSounds_convolve_signalOutsideTimeDomain::SIMILAR: {
 				for (long channel = 1; channel <= numberOfChannels; channel ++) {
 					double *a = thy z [channel];
 					double edge = n1;
@@ -713,25 +713,25 @@ autoSound Sound_autoCorrelate (Sound me, enum kSounds_convolve_scaling scaling,
 			//case kSounds_convolve_signalOutsideTimeDomain_PERIODIC: {
 				// do nothing
 			//} break;
-			default: Melder_fatal (U"Sounds_autoCorrelate: unimplemented outside-time-domain strategy ", signalOutsideTimeDomain);
+			default: Melder_fatal (U"Sounds_autoCorrelate: unimplemented outside-time-domain strategy ", (int) signalOutsideTimeDomain);
 		}
 		switch (scaling) {
-			case kSounds_convolve_scaling_INTEGRAL: {
+			case kSounds_convolve_scaling::INTEGRAL: {
 				Vector_multiplyByScalar (thee.get(), my dx / nfft);
 			} break;
-			case kSounds_convolve_scaling_SUM: {
+			case kSounds_convolve_scaling::SUM: {
 				Vector_multiplyByScalar (thee.get(), 1.0 / nfft);
 			} break;
-			case kSounds_convolve_scaling_NORMALIZE: {
+			case kSounds_convolve_scaling::NORMALIZE: {
 				double normalizationFactor = Matrix_getNorm (me) * Matrix_getNorm (me);
 				if (normalizationFactor != 0.0) {
 					Vector_multiplyByScalar (thee.get(), 1.0 / nfft / normalizationFactor);
 				}
 			} break;
-			case kSounds_convolve_scaling_PEAK_099: {
+			case kSounds_convolve_scaling::PEAK_099: {
 				Vector_scale (thee.get(), 0.99);
 			} break;
-			default: Melder_fatal (U"Sounds_autoCorrelate: unimplemented scaling ", scaling);
+			default: Melder_fatal (U"Sounds_autoCorrelate: unimplemented scaling ", (int) scaling);
 		}
 		return thee;
 	} catch (MelderError) {
@@ -965,52 +965,52 @@ autoSound Sound_createAsToneComplex (double startTime, double endTime, double sa
 	}
 }
 
-void Sound_multiplyByWindow (Sound me, enum kSound_windowShape windowShape) {
+void Sound_multiplyByWindow (Sound me, kSound_windowShape windowShape) {
 	for (long channel = 1; channel <= my ny; channel ++) {
 		integer n = my nx;
 		double *amp = my z [channel];
 		switch (windowShape) {
-			case kSound_windowShape_RECTANGULAR: {
+			case kSound_windowShape::RECTANGULAR: {
 				;
-			} break; case kSound_windowShape_TRIANGULAR: {   // "Bartlett"
+			} break; case kSound_windowShape::TRIANGULAR: {   // "Bartlett"
 				for (integer i = 1; i <= n; i ++) { double phase = (double) i / n;   // 0..1
 					amp [i] *= 1.0 - fabs ((2.0 * phase - 1.0)); }
-			} break; case kSound_windowShape_PARABOLIC: {   // "Welch"
+			} break; case kSound_windowShape::PARABOLIC: {   // "Welch"
 				for (integer i = 1; i <= n; i ++) { double phase = (double) i / n;
 					amp [i] *= 1.0 - (2.0 * phase - 1.0) * (2.0 * phase - 1.0); }
-			} break; case kSound_windowShape_HANNING: {
+			} break; case kSound_windowShape::HANNING: {
 				for (integer i = 1; i <= n; i ++) { double phase = (double) i / n;
 					amp [i] *= 0.5 * (1.0 - cos (2.0 * NUMpi * phase)); }
-			} break; case kSound_windowShape_HAMMING: {
+			} break; case kSound_windowShape::HAMMING: {
 				for (integer i = 1; i <= n; i ++) { double phase = (double) i / n;
 					amp [i] *= 0.54 - 0.46 * cos (2.0 * NUMpi * phase); }
-			} break; case kSound_windowShape_GAUSSIAN_1: {
+			} break; case kSound_windowShape::GAUSSIAN_1: {
 				real imid = 0.5 * (n + 1), edge = exp (-3.0), onebyedge1 = 1.0 / (1.0 - edge);   // -0.5..+0.5
 				for (integer i = 1; i <= n; i ++) { double phase = ((double) i - imid) / n;
 					amp [i] *= (exp (-12.0 * phase * phase) - edge) * onebyedge1; }
-			} break; case kSound_windowShape_GAUSSIAN_2: {
+			} break; case kSound_windowShape::GAUSSIAN_2: {
 				real imid = 0.5 * (double) (n + 1), edge = exp (-12.0), onebyedge1 = 1.0 / (1.0 - edge);
 				for (integer i = 1; i <= n; i ++) { double phase = ((double) i - imid) / n;
 					amp [i] *= (exp (-48.0 * phase * phase) - edge) * onebyedge1; }
-			} break; case kSound_windowShape_GAUSSIAN_3: {
+			} break; case kSound_windowShape::GAUSSIAN_3: {
 				real imid = 0.5 * (double) (n + 1), edge = exp (-27.0), onebyedge1 = 1.0 / (1.0 - edge);
 				for (integer i = 1; i <= n; i ++) { double phase = ((double) i - imid) / n;
 					amp [i] *= (exp (-108.0 * phase * phase) - edge) * onebyedge1; }
-			} break; case kSound_windowShape_GAUSSIAN_4: {
+			} break; case kSound_windowShape::GAUSSIAN_4: {
 				real imid = 0.5 * (double) (n + 1), edge = exp (-48.0), onebyedge1 = 1.0 / (1.0 - edge);
 				for (integer i = 1; i <= n; i ++) { double phase = ((double) i - imid) / n;
 					amp [i] *= (exp (-192.0 * phase * phase) - edge) * onebyedge1; }
-			} break; case kSound_windowShape_GAUSSIAN_5: {
+			} break; case kSound_windowShape::GAUSSIAN_5: {
 				real imid = 0.5 * (double) (n + 1), edge = exp (-75.0), onebyedge1 = 1.0 / (1.0 - edge);
 				for (integer i = 1; i <= n; i ++) { double phase = ((double) i - imid) / n;
 					amp [i] *= (exp (-300.0 * phase * phase) - edge) * onebyedge1; }
-			} break; case kSound_windowShape_KAISER_1: {
+			} break; case kSound_windowShape::KAISER_1: {
 				real imid = 0.5 * (double) (n + 1);
 				real factor = 1.0 / NUMbessel_i0_f (2 * NUMpi);
 				for (integer i = 1; i <= n; i ++) { double phase = 2.0 * ((double) i - imid) / n;   // -1..+1
 					double root = 1.0 - phase * phase;
 					amp [i] *= root <= 0.0 ? 0.0 : factor * NUMbessel_i0_f (2.0 * NUMpi * sqrt (root)); }
-			} break; case kSound_windowShape_KAISER_2: {
+			} break; case kSound_windowShape::KAISER_2: {
 				real imid = 0.5 * (double) (n + 1);
 				real factor = 1.0 / NUMbessel_i0_f (2 * NUMpi * NUMpi + 0.5);
 				for (integer i = 1; i <= n; i ++) { double phase = 2.0 * ((double) i - imid) / n;   // -1..+1
@@ -1039,7 +1039,7 @@ void Sound_overrideSamplingFrequency (Sound me, double rate) {
 	my xmax = my xmin + my nx * my dx;
 }
 
-autoSound Sound_extractPart (Sound me, double t1, double t2, enum kSound_windowShape windowShape, double relativeWidth, bool preserveTimes) {
+autoSound Sound_extractPart (Sound me, double t1, double t2, kSound_windowShape windowShape, double relativeWidth, bool preserveTimes) {
 	try {
 		/*
 		 * We do not clip to the Sound's time domain.
diff --git a/fon/Sound.h b/fon/Sound.h
index b604352..b556139 100644
--- a/fon/Sound.h
+++ b/fon/Sound.h
@@ -134,7 +134,7 @@ autoSound Sounds_append (Sound me, double silenceDuration, Sound thee);
 		result -> z [1] [i + my nx + round (silenceDuration / my dx)] == thy z [1] [i]
 */
  
-autoSound Sounds_convolve (Sound me, Sound thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
+autoSound Sounds_convolve (Sound me, Sound thee, kSounds_convolve_scaling scaling, kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
 /*
 	Function:
 		convolve two Sounds.
@@ -150,9 +150,9 @@ autoSound Sounds_convolve (Sound me, Sound thee, enum kSounds_convolve_scaling s
 			result -> z [1] [i] == result -> dx *
 				sum (j = 1..i, my z [1] [j] * thy z [1] [i - j + 1])
 */
-autoSound Sounds_crossCorrelate (Sound me, Sound thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
+autoSound Sounds_crossCorrelate (Sound me, Sound thee, kSounds_convolve_scaling scaling, kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
 autoSound Sounds_crossCorrelate_short (Sound me, Sound thee, double tmin, double tmax, bool normalize);
-autoSound Sound_autoCorrelate (Sound me, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
+autoSound Sound_autoCorrelate (Sound me, kSounds_convolve_scaling scaling, kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
 
 double Sound_getRootMeanSquare (Sound me, double xmin, double xmax);
 double Sound_getEnergy (Sound me, double xmin, double xmax);
@@ -174,10 +174,10 @@ autoSound Sound_createAsToneComplex (double startingTime, double endTime,
 #define Sound_TONE_COMPLEX_COSINE  1
 
 autoSound Sounds_concatenate (OrderedOf<structSound>& list, double overlapTime);
-void Sound_multiplyByWindow (Sound me, enum kSound_windowShape windowShape);
+void Sound_multiplyByWindow (Sound me, kSound_windowShape windowShape);
 void Sound_scaleIntensity (Sound me, double newAverageIntensity);
 void Sound_overrideSamplingFrequency (Sound me, double newSamplingFrequency);
-autoSound Sound_extractPart (Sound me, double t1, double t2, enum kSound_windowShape windowShape, double relativeWidth, bool preserveTimes);
+autoSound Sound_extractPart (Sound me, double t1, double t2, kSound_windowShape windowShape, double relativeWidth, bool preserveTimes);
 autoSound Sound_extractPartForOverlap (Sound me, double t1, double t2, double overlap);
 void Sound_filterWithFormants (Sound me, double tmin, double tmax,
 	int numberOfFormants, double formant [], double bandwidth []);
diff --git a/fon/SoundEditor.cpp b/fon/SoundEditor.cpp
index 4b8a14b..83a4be9 100644
--- a/fon/SoundEditor.cpp
+++ b/fon/SoundEditor.cpp
@@ -40,7 +40,7 @@ void structSoundEditor :: v_dataChanged () {
 static void menu_cb_Copy (SoundEditor me, EDITOR_ARGS_DIRECT) {
 	try {
 		Sound_clipboard = my d_longSound.data ? LongSound_extractPart ((LongSound) my data, my startSelection, my endSelection, false) :
-			Sound_extractPart ((Sound) my data, my startSelection, my endSelection, kSound_windowShape_RECTANGULAR, 1.0, false);
+			Sound_extractPart ((Sound) my data, my startSelection, my endSelection, kSound_windowShape::RECTANGULAR, 1.0, false);
 	} catch (MelderError) {
 		Melder_throw (U"Sound selection not copied to clipboard.");
 	}
diff --git a/fon/SoundRecorder.cpp b/fon/SoundRecorder.cpp
index 605e012..c43be6e 100644
--- a/fon/SoundRecorder.cpp
+++ b/fon/SoundRecorder.cpp
@@ -291,7 +291,7 @@ static void showMeter (SoundRecorder me, short *buffer, long nsamp) {
 		Graphics_text (my graphics.get(), 0.5, 0.5, U"Not recording.");
 		return;
 	}
-	if (my p_meter_which == kSoundRecorder_meter_INTENSITY) {
+	if (my p_meter_which == kSoundRecorder_meter::INTENSITY) {
 		int leftMaximum = 0, rightMaximum = 0;
 		if (my numberOfChannels == 1) {
 			for (long i = 0; i < nsamp; i ++) {
@@ -319,7 +319,7 @@ static void showMeter (SoundRecorder me, short *buffer, long nsamp) {
 			showMaximum (me, 2, rightMaximum);
 			my lastRightMaximum = rightMaximum;
 		}
-	} else if (my p_meter_which == kSoundRecorder_meter_CENTRE_OF_GRAVITY_VERSUS_INTENSITY) {
+	} else if (my p_meter_which == kSoundRecorder_meter::CENTRE_OF_GRAVITY_VERSUS_INTENSITY) {
 		autoSound sound = Sound_create (my numberOfChannels,
 			0.0, nsamp / theControlPanel. sampleRate,
 			nsamp, 1.0 / theControlPanel. sampleRate, 0.5 / theControlPanel. sampleRate);
@@ -329,7 +329,7 @@ static void showMeter (SoundRecorder me, short *buffer, long nsamp) {
 				sound -> z [ichan] [isamp] = * (p ++) / 32768.0;
 			}
 		}
-		Sound_multiplyByWindow (sound.get(), kSound_windowShape_KAISER_2);
+		Sound_multiplyByWindow (sound.get(), kSound_windowShape::KAISER_2);
 		double intensity = Sound_getIntensity_dB (sound.get());
 		autoSpectrum spectrum = Sound_to_Spectrum (sound.get(), true);
 		double centreOfGravity = Spectrum_getCentreOfGravity (spectrum.get(), 1.0);
@@ -954,17 +954,17 @@ static void menu_cb_writeNist (SoundRecorder me, EDITOR_ARGS_FORM) {
 
 static void updateMenus (SoundRecorder me) {
 	GuiMenuItem_check (my meterIntensityButton,
-		my p_meter_which == kSoundRecorder_meter_INTENSITY);
+		my p_meter_which == kSoundRecorder_meter::INTENSITY);
 	GuiMenuItem_check (my meterCentreOfGravityVersusIntensityButton,
-		my p_meter_which == kSoundRecorder_meter_CENTRE_OF_GRAVITY_VERSUS_INTENSITY);
+		my p_meter_which == kSoundRecorder_meter::CENTRE_OF_GRAVITY_VERSUS_INTENSITY);
 }
 
 static void menu_cb_intensity (SoundRecorder me, EDITOR_ARGS_DIRECT) {
-	my pref_meter_which () = my p_meter_which = kSoundRecorder_meter_INTENSITY;
+	my pref_meter_which () = my p_meter_which = kSoundRecorder_meter::INTENSITY;
 	updateMenus (me);
 }
 static void menu_cb_centreOfGravityVersusIntensity (SoundRecorder me, EDITOR_ARGS_DIRECT) {
-	my pref_meter_which () = my p_meter_which = kSoundRecorder_meter_CENTRE_OF_GRAVITY_VERSUS_INTENSITY;
+	my pref_meter_which () = my p_meter_which = kSoundRecorder_meter::CENTRE_OF_GRAVITY_VERSUS_INTENSITY;
 	updateMenus (me);
 }
 
@@ -994,11 +994,11 @@ autoSoundRecorder SoundRecorder_create (int numberOfChannels) {
 		autoSoundRecorder me = Thing_new (SoundRecorder);
 		my inputUsesPortAudio =
 			#if defined (_WIN32)
-				MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem_MME_VIA_PORTAUDIO;
+				MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::MME_VIA_PORTAUDIO;
 			#elif defined (macintosh)
-				MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem_COREAUDIO_VIA_PORTAUDIO;
+				MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::COREAUDIO_VIA_PORTAUDIO;
 			#else
-				MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem_ALSA_VIA_PORTAUDIO;
+				MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::ALSA_VIA_PORTAUDIO;
 			#endif
 
 		if (my inputUsesPortAudio) {
diff --git a/fon/Sound_and_Spectrogram.cpp b/fon/Sound_and_Spectrogram.cpp
index 574e54a..b2edceb 100644
--- a/fon/Sound_and_Spectrogram.cpp
+++ b/fon/Sound_and_Spectrogram.cpp
@@ -1,6 +1,6 @@
 /* Sound_and_Spectrogram.cpp
  *
- * Copyright (C) 1992-2011,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -41,13 +41,13 @@
 #include "Sound_and_Spectrogram_enums.h"
 
 autoSpectrogram Sound_to_Spectrogram (Sound me, double effectiveAnalysisWidth, double fmax,
-	double minimumTimeStep1, double minimumFreqStep1, enum kSound_to_Spectrogram_windowShape windowType,
+	double minimumTimeStep1, double minimumFreqStep1, kSound_to_Spectrogram_windowShape windowType,
 	double maximumTimeOversampling, double maximumFreqOversampling)
 {
 	try {
 		double nyquist = 0.5 / my dx;
 		double physicalAnalysisWidth =
-			windowType == kSound_to_Spectrogram_windowShape_GAUSSIAN ? 2 * effectiveAnalysisWidth : effectiveAnalysisWidth;
+			windowType == kSound_to_Spectrogram_windowShape::GAUSSIAN ? 2 * effectiveAnalysisWidth : effectiveAnalysisWidth;
 		double effectiveTimeWidth = effectiveAnalysisWidth / sqrt (NUMpi);
 		double effectiveFreqWidth = 1 / effectiveTimeWidth;
 		double minimumTimeStep2 = effectiveTimeWidth / maximumTimeOversampling;
@@ -67,7 +67,7 @@ autoSpectrogram Sound_to_Spectrogram (Sound me, double effectiveAnalysisWidth, d
 		if (physicalAnalysisWidth > duration)
 			Melder_throw (U"Your sound is too short:\n"
 				U"it should be at least as long as ",
-				windowType == kSound_to_Spectrogram_windowShape_GAUSSIAN ? U"two window lengths." : U"one window length.");
+				windowType == kSound_to_Spectrogram_windowShape::GAUSSIAN ? U"two window lengths." : U"one window length.");
 		long numberOfTimes = 1 + (long) floor ((duration - physicalAnalysisWidth) / timeStep);   // >= 1
 		double t1 = my x1 + 0.5 * ((double) (my nx - 1) * my dx - (double) (numberOfTimes - 1) * timeStep);
 			/* Centre of first frame. */
@@ -108,17 +108,17 @@ autoSpectrogram Sound_to_Spectrogram (Sound me, double effectiveAnalysisWidth, d
 			double phase = (double) i / nSamplesPerWindow_f;   // 0 .. 1
 			double value;
 			switch (windowType) {
-				case kSound_to_Spectrogram_windowShape_SQUARE:
+				case kSound_to_Spectrogram_windowShape::SQUARE:
 					value = 1.0;
-				break; case kSound_to_Spectrogram_windowShape_HAMMING:
+				break; case kSound_to_Spectrogram_windowShape::HAMMING:
 					value = 0.54 - 0.46 * cos (2.0 * NUMpi * phase);
-				break; case kSound_to_Spectrogram_windowShape_BARTLETT:
+				break; case kSound_to_Spectrogram_windowShape::BARTLETT:
 					value = 1.0 - fabs ((2.0 * phase - 1.0));
-				break; case kSound_to_Spectrogram_windowShape_WELCH:
+				break; case kSound_to_Spectrogram_windowShape::WELCH:
 					value = 1.0 - (2.0 * phase - 1.0) * (2.0 * phase - 1.0);
-				break; case kSound_to_Spectrogram_windowShape_HANNING:
+				break; case kSound_to_Spectrogram_windowShape::HANNING:
 					value = 0.5 * (1.0 - cos (2.0 * NUMpi * phase));
-				break; case kSound_to_Spectrogram_windowShape_GAUSSIAN:
+				break; case kSound_to_Spectrogram_windowShape::GAUSSIAN:
 				{
 					double imid = 0.5 * (double) (nsamp_window + 1), edge = exp (-12.0);
 					phase = ((double) i - imid) / nSamplesPerWindow_f;   /* -0.5 .. +0.5 */
diff --git a/fon/Sound_and_Spectrogram.h b/fon/Sound_and_Spectrogram.h
index f076e92..0e93e72 100644
--- a/fon/Sound_and_Spectrogram.h
+++ b/fon/Sound_and_Spectrogram.h
@@ -2,7 +2,7 @@
 #define _Sound_and_Spectrogram_h_
 /* Sound_and_Spectrogram.h
  *
- * Copyright (C) 1992-2011,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
 #include "Sound_and_Spectrogram_enums.h"
 
 autoSpectrogram Sound_to_Spectrogram (Sound me, double effectiveAnalysisWidth, double fmax,
-	double minimumTimeStep1, double minimumFreqStep1, enum kSound_to_Spectrogram_windowShape windowShape,
+	double minimumTimeStep1, double minimumFreqStep1, kSound_to_Spectrogram_windowShape windowShape,
 	double maximumTimeOversampling, double maximumFreqOversampling);
 
 autoSound Spectrogram_to_Sound (Spectrogram me, double fsamp);
diff --git a/fon/Sound_audio.cpp b/fon/Sound_audio.cpp
index de09903..17b0285 100644
--- a/fon/Sound_audio.cpp
+++ b/fon/Sound_audio.cpp
@@ -1,6 +1,6 @@
 /* Sound_audio.cpp
  *
- * Copyright (C) 1992-2011,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2011,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -127,11 +127,11 @@ static int portaudioStreamCallback (
 autoSound Sound_record_fixedTime (int inputSource, double gain, double balance, double sampleRate, double duration) {
 	bool inputUsesPortAudio =
 		#if defined (_WIN32)
-			MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem_MME_VIA_PORTAUDIO;
+			MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::MME_VIA_PORTAUDIO;
 		#elif defined (macintosh)
-			MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem_COREAUDIO_VIA_PORTAUDIO;
+			MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::COREAUDIO_VIA_PORTAUDIO;
 		#else
-			MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem_ALSA_VIA_PORTAUDIO;
+			MelderAudio_getInputSoundSystem () == kMelder_inputSoundSystem::ALSA_VIA_PORTAUDIO;
 		#endif
 	PaStream *portaudioStream = nullptr;
 	#if defined (macintosh)
diff --git a/fon/Sound_files.cpp b/fon/Sound_files.cpp
index 607db5a..35074af 100644
--- a/fon/Sound_files.cpp
+++ b/fon/Sound_files.cpp
@@ -71,7 +71,7 @@ autoSound Sound_readFromSoundFile (MelderFile file) {
 		int numberOfChannels, encoding;
 		double sampleRate;
 		long startOfData;
-		int32 numberOfSamples;
+		integer numberOfSamples;
 		int fileType = MelderFile_checkSoundFile (file, & numberOfChannels, & encoding, & sampleRate, & startOfData, & numberOfSamples);
 		if (fileType == 0)
 			Melder_throw (U"Not an audio file.");
diff --git a/fon/Sound_to_Cochleagram.cpp b/fon/Sound_to_Cochleagram.cpp
index e9e8a97..32177c4 100644
--- a/fon/Sound_to_Cochleagram.cpp
+++ b/fon/Sound_to_Cochleagram.cpp
@@ -129,7 +129,7 @@ autoCochleagram Sound_to_Cochleagram_edb
 			double midFrequency_Bark = (ifreq - 0.5) * dfreq;
 			double midFrequency_Hertz = Excitation_barkToHertz (midFrequency_Bark);
 			autoSound gammatone = createGammatone (midFrequency_Hertz, 1.0 / my dx);
-			autoSound basil = Sounds_convolve (me, gammatone.get(), kSounds_convolve_scaling_SUM, kSounds_convolve_signalOutsideTimeDomain_ZERO);
+			autoSound basil = Sounds_convolve (me, gammatone.get(), kSounds_convolve_scaling::SUM, kSounds_convolve_signalOutsideTimeDomain::ZERO);
 
 			/* Stage 4: detection = rectify + integrate + low-pass 500 Hz. */
 			/* From basilar membrane response to firing rate. */
diff --git a/fon/Sound_to_Formant.cpp b/fon/Sound_to_Formant.cpp
index 8c80fea..558d4d7 100644
--- a/fon/Sound_to_Formant.cpp
+++ b/fon/Sound_to_Formant.cpp
@@ -313,8 +313,8 @@ static autoFormant Sound_to_Formant_any_inline (Sound me, double dt_in, int numb
 				maximumIntensity = value * value;
 			}
 		}
-		if (maximumIntensity == HUGE_VAL)
-			Melder_throw (U"Sound contains infinities.");
+		if (isundef (maximumIntensity))
+			Melder_throw (U"Sound contains infinities or other non-numbers.");
 		thy d_frames [iframe]. intensity = maximumIntensity;
 		if (maximumIntensity == 0.0) continue;   // Burg cannot stand all zeroes
 
diff --git a/fon/Sound_to_Harmonicity_GNE.cpp b/fon/Sound_to_Harmonicity_GNE.cpp
index e621004..87c58a3 100644
--- a/fon/Sound_to_Harmonicity_GNE.cpp
+++ b/fon/Sound_to_Harmonicity_GNE.cpp
@@ -1,6 +1,6 @@
 /* Sound_to_Harmonicity_GNE.cpp
  *
- * Copyright (C) 1999-2011,2015,2016 Paul Boersma
+ * Copyright (C) 1999-2011,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -111,7 +111,7 @@ autoMatrix Sound_to_Harmonicity_GNE (Sound me,
 			}*/
 			Melder_monitor (ienvelope / (nenvelopes + 1.0), U"Computing Hilbert envelope ", ienvelope, U"...");
 			autoSound hilbertBand = Spectrum_to_Sound (hilbertBandSpectrum.get());
-			envelope [ienvelope] = Sound_extractPart (band.get(), 0, duration, kSound_windowShape_RECTANGULAR, 1.0, true);
+			envelope [ienvelope] = Sound_extractPart (band.get(), 0, duration, kSound_windowShape::RECTANGULAR, 1.0, true);
 			/*
 			 * 3c: Compute the Hilbert envelope of the band-passed flat signal.
 			 */
diff --git a/fon/Sound_to_Intensity.cpp b/fon/Sound_to_Intensity.cpp
index 9d53731..65d623a 100644
--- a/fon/Sound_to_Intensity.cpp
+++ b/fon/Sound_to_Intensity.cpp
@@ -55,12 +55,12 @@ static autoIntensity Sound_to_Intensity_ (Sound me, double minimumPitch, double
 		autoNUMvector <double> amplitude (- halfWindowSamples, halfWindowSamples);
 		autoNUMvector <double> window (- halfWindowSamples, halfWindowSamples);
 
-		for (long i = - halfWindowSamples; i <= halfWindowSamples; i ++) {
+		for (integer i = - halfWindowSamples; i <= halfWindowSamples; i ++) {
 			const double x = i * my dx / halfWindowDuration, root = 1 - x * x;
 			window [i] = root <= 0.0 ? 0.0 : NUMbessel_i0_f ((2.0 * NUMpi * NUMpi + 0.5) * sqrt (root));
 		}
 
-		long numberOfFrames;
+		integer numberOfFrames;
 		double thyFirstTime;
 		try {
 			Sampled_shortTermAnalysis (me, windowDuration, timeStep, & numberOfFrames, & thyFirstTime);
@@ -70,29 +70,29 @@ static autoIntensity Sound_to_Intensity_ (Sound me, double minimumPitch, double
 				U"i.e. at least ", 6.4 / minimumPitch, U" s, instead of ", my nx * my dx, U" s.");
 		}
 		autoIntensity thee = Intensity_create (my xmin, my xmax, numberOfFrames, timeStep, thyFirstTime);
-		for (long iframe = 1; iframe <= numberOfFrames; iframe ++) {
+		for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
 			const double midTime = Sampled_indexToX (thee.get(), iframe);
-			const long midSample = Sampled_xToNearestIndex (me, midTime);   // time accuracy is half a sampling period
-			long leftSample = midSample - halfWindowSamples, rightSample = midSample + halfWindowSamples;
-			long double sumxw = 0.0, sumw = 0.0;
+			const integer midSample = Sampled_xToNearestIndex (me, midTime);   // time accuracy is half a sampling period
+			integer leftSample = midSample - halfWindowSamples, rightSample = midSample + halfWindowSamples;
+			real80 sumxw = 0.0, sumw = 0.0;
 			if (leftSample < 1) leftSample = 1;
 			if (rightSample > my nx) rightSample = my nx;
 
-			for (long channel = 1; channel <= my ny; channel ++) {
-				for (long i = leftSample; i <= rightSample; i ++) {
+			for (integer channel = 1; channel <= my ny; channel ++) {
+				for (integer i = leftSample; i <= rightSample; i ++) {
 					amplitude [i - midSample] = my z [channel] [i];
 				}
 				if (subtractMeanPressure) {
-					long double sum = 0.0;
-					for (long i = leftSample; i <= rightSample; i ++) {
+					real80 sum = 0.0;
+					for (integer i = leftSample; i <= rightSample; i ++) {
 						sum += amplitude [i - midSample];
 					}
 					double mean = (double) sum / (rightSample - leftSample + 1);
-					for (long i = leftSample; i <= rightSample; i ++) {
+					for (integer i = leftSample; i <= rightSample; i ++) {
 						amplitude [i - midSample] -= mean;
 					}
 				}
-				for (long i = leftSample; i <= rightSample; i ++) {
+				for (integer i = leftSample; i <= rightSample; i ++) {
 					sumxw += amplitude [i - midSample] * amplitude [i - midSample] * window [i - midSample];
 					sumw += window [i - midSample];
 				}
diff --git a/fon/Sound_to_Pitch.cpp b/fon/Sound_to_Pitch.cpp
index 30a5472..8f0a249 100644
--- a/fon/Sound_to_Pitch.cpp
+++ b/fon/Sound_to_Pitch.cpp
@@ -352,10 +352,10 @@ autoPitch Sound_to_Pitch_any (Sound me,
 	try {
 		autoNUMfft_Table fftTable;
 		double t1;
-		long nFrames, minimumLag, maximumLag;
-		long nsampFFT;
+		integer numberOfFrames, minimumLag, maximumLag;
+		integer nsampFFT;
 		double interpolation_depth;
-		long brent_ixmax, brent_depth;
+		integer brent_ixmax, brent_depth;
 		double globalPeak;
 
 		Melder_assert (maxnCandidates >= 2);
@@ -423,7 +423,7 @@ autoPitch Sound_to_Pitch_any (Sound me,
 		 * because that allows us to compare the two methods.
 		 */
 		try {
-			Sampled_shortTermAnalysis (me, method >= FCC_NORMAL ? 1.0 / minimumPitch + dt_window : dt_window, dt, & nFrames, & t1);
+			Sampled_shortTermAnalysis (me, method >= FCC_NORMAL ? 1.0 / minimumPitch + dt_window : dt_window, dt, & numberOfFrames, & t1);
 		} catch (MelderError) {
 			Melder_throw (U"The pitch analysis would give zero pitch frames.");
 		}
@@ -431,12 +431,12 @@ autoPitch Sound_to_Pitch_any (Sound me,
 		/*
 		 * Create the resulting pitch contour.
 		 */
-		autoPitch thee = Pitch_create (my xmin, my xmax, nFrames, dt, t1, ceiling, maxnCandidates);
+		autoPitch thee = Pitch_create (my xmin, my xmax, numberOfFrames, dt, t1, ceiling, maxnCandidates);
 
 		/*
 		 * Create (too much) space for candidates.
 		 */
-		for (long iframe = 1; iframe <= nFrames; iframe ++) {
+		for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
 			Pitch_Frame pitchFrame = & thy frame [iframe];
 			Pitch_Frame_init (pitchFrame, maxnCandidates);
 		}
@@ -528,21 +528,21 @@ autoPitch Sound_to_Pitch_any (Sound me,
 
 		autoMelderProgress progress (U"Sound to Pitch...");
 
-		long numberOfFramesPerThread = 20;
-		int numberOfThreads = (nFrames - 1) / numberOfFramesPerThread + 1;
+		integer numberOfFramesPerThread = 20;
+		int numberOfThreads = (numberOfFrames - 1) / numberOfFramesPerThread + 1;
 		const int numberOfProcessors = MelderThread_getNumberOfProcessors ();
 		trace (numberOfProcessors, U" processors");
 		if (numberOfThreads > numberOfProcessors) numberOfThreads = numberOfProcessors;
 		if (numberOfThreads > 16) numberOfThreads = 16;
 		if (numberOfThreads < 1) numberOfThreads = 1;
-		numberOfFramesPerThread = (nFrames - 1) / numberOfThreads + 1;
+		numberOfFramesPerThread = (numberOfFrames - 1) / numberOfThreads + 1;
 
 		if (! mutex_inited) { MelderThread_MUTEX_INIT (mutex); mutex_inited = true; }
 		autoSound_into_Pitch_Args args [16];
 		long firstFrame = 1, lastFrame = numberOfFramesPerThread;
 		volatile int cancelled = 0;
 		for (int ithread = 1; ithread <= numberOfThreads; ithread ++) {
-			if (ithread == numberOfThreads) lastFrame = nFrames;
+			if (ithread == numberOfThreads) lastFrame = numberOfFrames;
 			args [ithread - 1] = Sound_into_Pitch_Args_create (me, thee.get(),
 				firstFrame, lastFrame, minimumPitch, maxnCandidates, method,
 				voicingThreshold, octaveCost,
diff --git a/fon/Spectrum.cpp b/fon/Spectrum.cpp
index da6240f..b30de1e 100644
--- a/fon/Spectrum.cpp
+++ b/fon/Spectrum.cpp
@@ -132,7 +132,7 @@ void Spectrum_drawInside (Spectrum me, Graphics g, double fmin, double fmax, dou
 		minimum = maximum - defaultDynamicRange_dB;
 		if (minimum == maximum) {   // because infinity minus something is still infinity
 			Graphics_setWindow (g, 0.0, 1.0, 0.0, 1.0);
-			Graphics_setTextAlignment (g, kGraphics_horizontalAlignment_CENTRE, Graphics_HALF);
+			Graphics_setTextAlignment (g, kGraphics_horizontalAlignment::CENTRE, Graphics_HALF);
 			Graphics_text (g, 0.5, 0.5, U"(undefined spectrum values cannot be drawn)");
 			return;
 		}
diff --git a/fon/TextGrid.cpp b/fon/TextGrid.cpp
index 0d1ec5b..d086dde 100644
--- a/fon/TextGrid.cpp
+++ b/fon/TextGrid.cpp
@@ -465,13 +465,13 @@ long TextGrid_countLabels (TextGrid me, long tierNumber, const char32 *text) {
 	}
 }
 
-long TextGrid_countIntervalsWhere (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion) {
+long TextGrid_countIntervalsWhere (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		long count = 0;
 		IntervalTier tier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
 		for (long iinterval = 1; iinterval <= tier -> intervals.size; iinterval ++) {
 			TextInterval interval = tier -> intervals.at [iinterval];
-			if (Melder_stringMatchesCriterion (interval -> text, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (interval -> text, which, criterion)) {
 				count ++;
 			}
 		}
@@ -481,13 +481,13 @@ long TextGrid_countIntervalsWhere (TextGrid me, long tierNumber, int which_Melde
 	}
 }
 
-long TextGrid_countPointsWhere (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion) {
+long TextGrid_countPointsWhere (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		long count = 0;
 		TextTier tier = TextGrid_checkSpecifiedTierIsPointTier (me, tierNumber);
 		for (long ipoint = 1; ipoint <= tier -> points.size; ipoint ++) {
 			TextPoint point = tier -> points.at [ipoint];
-			if (Melder_stringMatchesCriterion (point -> mark, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (point -> mark, which, criterion)) {
 				count ++;
 			}
 		}
@@ -690,13 +690,13 @@ autoPointProcess IntervalTier_getCentrePoints (IntervalTier me, const char32 *te
 	}
 }
 
-autoPointProcess TextGrid_getStartingPoints (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion) {
+autoPointProcess TextGrid_getStartingPoints (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		IntervalTier tier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
 		autoPointProcess thee = PointProcess_create (my xmin, my xmax, 10);
 		for (long iinterval = 1; iinterval <= tier -> intervals.size; iinterval ++) {
 			TextInterval interval = tier -> intervals.at [iinterval];
-			if (Melder_stringMatchesCriterion (interval -> text, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (interval -> text, which, criterion)) {
 				PointProcess_addPoint (thee.get(), interval -> xmin);
 			}
 		}
@@ -706,13 +706,13 @@ autoPointProcess TextGrid_getStartingPoints (TextGrid me, long tierNumber, int w
 	}
 }
 
-autoPointProcess TextGrid_getEndPoints (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion) {
+autoPointProcess TextGrid_getEndPoints (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		IntervalTier tier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
 		autoPointProcess thee = PointProcess_create (my xmin, my xmax, 10);
 		for (long iinterval = 1; iinterval <= tier -> intervals.size; iinterval ++) {
 			TextInterval interval = tier -> intervals.at [iinterval];
-			if (Melder_stringMatchesCriterion (interval -> text, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (interval -> text, which, criterion)) {
 				PointProcess_addPoint (thee.get(), interval -> xmax);
 			}
 		}
@@ -722,13 +722,13 @@ autoPointProcess TextGrid_getEndPoints (TextGrid me, long tierNumber, int which_
 	}
 }
 
-autoPointProcess TextGrid_getCentrePoints (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion) {
+autoPointProcess TextGrid_getCentrePoints (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		IntervalTier tier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
 		autoPointProcess thee = PointProcess_create (my xmin, my xmax, 10);
 		for (long iinterval = 1; iinterval <= tier -> intervals.size; iinterval ++) {
 			TextInterval interval = tier -> intervals.at [iinterval];
-			if (Melder_stringMatchesCriterion (interval -> text, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (interval -> text, which, criterion)) {
 				PointProcess_addPoint (thee.get(), 0.5 * (interval -> xmin + interval -> xmax));
 			}
 		}
@@ -738,13 +738,13 @@ autoPointProcess TextGrid_getCentrePoints (TextGrid me, long tierNumber, int whi
 	}
 }
 
-autoPointProcess TextGrid_getPoints (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion) {
+autoPointProcess TextGrid_getPoints (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		TextTier tier = TextGrid_checkSpecifiedTierIsPointTier (me, tierNumber);
 		autoPointProcess thee = PointProcess_create (my xmin, my xmax, 10);
 		for (long ipoint = 1; ipoint <= tier -> points.size; ipoint ++) {
 			TextPoint point = tier -> points.at [ipoint];
-			if (Melder_stringMatchesCriterion (point -> mark, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (point -> mark, which, criterion)) {
 				PointProcess_addPoint (thee.get(), point -> number);
 			}
 		}
@@ -755,17 +755,17 @@ autoPointProcess TextGrid_getPoints (TextGrid me, long tierNumber, int which_Mel
 }
 
 autoPointProcess TextGrid_getPoints_preceded (TextGrid me, long tierNumber,
-	int which_Melder_STRING, const char32 *criterion,
-	int which_Melder_STRING_precededBy, const char32 *criterion_precededBy)
+	kMelder_string which, const char32 *criterion,
+	kMelder_string precededBy, const char32 *criterion_precededBy)
 {
 	try {
 		TextTier tier = TextGrid_checkSpecifiedTierIsPointTier (me, tierNumber);
 		autoPointProcess thee = PointProcess_create (my xmin, my xmax, 10);
 		for (long ipoint = 1; ipoint <= tier -> points.size; ipoint ++) {
 			TextPoint point = tier -> points.at [ipoint];
-			if (Melder_stringMatchesCriterion (point -> mark, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (point -> mark, which, criterion)) {
 				TextPoint preceding = ( ipoint <= 1 ? nullptr : tier -> points.at [ipoint - 1] );
-				if (Melder_stringMatchesCriterion (preceding -> mark, which_Melder_STRING_precededBy, criterion_precededBy)) {
+				if (Melder_stringMatchesCriterion (preceding -> mark, precededBy, criterion_precededBy)) {
 					PointProcess_addPoint (thee.get(), point -> number);
 				}
 			}
@@ -777,17 +777,17 @@ autoPointProcess TextGrid_getPoints_preceded (TextGrid me, long tierNumber,
 }
 
 autoPointProcess TextGrid_getPoints_followed (TextGrid me, long tierNumber,
-	int which_Melder_STRING, const char32 *criterion,
-	int which_Melder_STRING_followedBy, const char32 *criterion_followedBy)
+	kMelder_string which, const char32 *criterion,
+	kMelder_string followedBy, const char32 *criterion_followedBy)
 {
 	try {
 		TextTier tier = TextGrid_checkSpecifiedTierIsPointTier (me, tierNumber);
 		autoPointProcess thee = PointProcess_create (my xmin, my xmax, 10);
 		for (long ipoint = 1; ipoint <= tier -> points.size; ipoint ++) {
 			TextPoint point = tier -> points.at [ipoint];
-			if (Melder_stringMatchesCriterion (point -> mark, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (point -> mark, which, criterion)) {
 				TextPoint following = ( ipoint >= tier -> points.size ? nullptr : tier -> points.at [ipoint + 1] );
-				if (Melder_stringMatchesCriterion (following -> mark, which_Melder_STRING_followedBy, criterion_followedBy)) {
+				if (Melder_stringMatchesCriterion (following -> mark, followedBy, criterion_followedBy)) {
 					PointProcess_addPoint (thee.get(), point -> number);
 				}
 			}
@@ -1233,16 +1233,16 @@ void TextTier_removePoint (TextTier me, long ipoint) {
 	my points. removeItem (ipoint);
 }
 
-void TextTier_removePoints (TextTier me, int which_Melder_STRING, const char32 *criterion) {
+void TextTier_removePoints (TextTier me, kMelder_string which, const char32 *criterion) {
 	for (long i = my points.size; i > 0; i --)
-		if (Melder_stringMatchesCriterion (my points.at [i] -> mark, which_Melder_STRING, criterion))
+		if (Melder_stringMatchesCriterion (my points.at [i] -> mark, which, criterion))
 			my points. removeItem (i);
 }
 
-void TextGrid_removePoints (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion) {
+void TextGrid_removePoints (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		TextTier tier = TextGrid_checkSpecifiedTierIsPointTier (me, tierNumber);
-		TextTier_removePoints (tier, which_Melder_STRING, criterion);
+		TextTier_removePoints (tier, which, criterion);
 	} catch (MelderError) {
 		Melder_throw (me, U": points not removed.");
 	}
@@ -1483,7 +1483,7 @@ autoTextGrid TextGrid_readFromCgnSyntaxFile (MelderFile file) {
 		if (! strequ (line, "<!DOCTYPE ttext SYSTEM \"ttext.dtd\">"))
 			Melder_throw (U"This is not a CGN syntax file.");
 		line = MelderFile_readLine (file);
-		long startOfData = MelderFile_tell (file);
+		integer startOfData = MelderFile_tell (file);
 		/*
 		 * Get duration.
 		 */
diff --git a/fon/TextGrid.h b/fon/TextGrid.h
index 65e6d29..2824c19 100644
--- a/fon/TextGrid.h
+++ b/fon/TextGrid.h
@@ -2,7 +2,7 @@
 #define _TextGrid_h_
 /* TextGrid.h
  *
- * Copyright (C) 1992-2012,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2012,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -65,18 +65,18 @@ autoTextGrid TextGrid_createWithoutTiers (double tmin, double tmax);
 autoTextGrid TextGrid_create (double tmin, double tmax, const char32 *tierNames, const char32 *pointTiers);
 
 long TextGrid_countLabels (TextGrid me, long itier, const char32 *text);
-long TextGrid_countIntervalsWhere (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion);
-long TextGrid_countPointsWhere (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion);
-autoPointProcess TextGrid_getStartingPoints (TextGrid me, long itier, int which_Melder_STRING, const char32 *criterion);
-autoPointProcess TextGrid_getEndPoints (TextGrid me, long itier, int which_Melder_STRING, const char32 *criterion);
-autoPointProcess TextGrid_getCentrePoints (TextGrid me, long itier, int which_Melder_STRING, const char32 *criterion);
-autoPointProcess TextGrid_getPoints (TextGrid me, long itier, int which_Melder_STRING, const char32 *criterion);
+long TextGrid_countIntervalsWhere (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion);
+long TextGrid_countPointsWhere (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion);
+autoPointProcess TextGrid_getStartingPoints (TextGrid me, long itier, kMelder_string which, const char32 *criterion);
+autoPointProcess TextGrid_getEndPoints (TextGrid me, long itier, kMelder_string which, const char32 *criterion);
+autoPointProcess TextGrid_getCentrePoints (TextGrid me, long itier, kMelder_string which, const char32 *criterion);
+autoPointProcess TextGrid_getPoints (TextGrid me, long itier, kMelder_string which, const char32 *criterion);
 autoPointProcess TextGrid_getPoints_preceded (TextGrid me, long tierNumber,
-	int which_Melder_STRING, const char32 *criterion,
-	int which_Melder_STRING_precededBy, const char32 *criterion_precededBy);
+	kMelder_string which, const char32 *criterion,
+	kMelder_string precededBy, const char32 *criterion_precededBy);
 autoPointProcess TextGrid_getPoints_followed (TextGrid me, long tierNumber,
-	int which_Melder_STRING, const char32 *criterion,
-	int which_Melder_STRING_followedBy, const char32 *criterion_followedBy);
+	kMelder_string which, const char32 *criterion,
+	kMelder_string followedBy, const char32 *criterion_followedBy);
 
 Function TextGrid_checkSpecifiedTierNumberWithinRange (TextGrid me, long tierNumber);
 IntervalTier TextGrid_checkSpecifiedTierIsIntervalTier (TextGrid me, long tierNumber);
@@ -110,8 +110,8 @@ void TextPoint_removeText (TextPoint me);
 void IntervalTier_removeText (IntervalTier me);
 void TextTier_removeText (TextTier me);
 
-void TextTier_removePoints (TextTier me, int which_Melder_STRING, const char32 *criterion);
-void TextGrid_removePoints (TextGrid me, long tierNumber, int which_Melder_STRING, const char32 *criterion);
+void TextTier_removePoints (TextTier me, kMelder_string which, const char32 *criterion);
+void TextGrid_removePoints (TextGrid me, long tierNumber, kMelder_string which, const char32 *criterion);
 
 void TextGrid_insertBoundary (TextGrid me, int itier, double t);
 void TextGrid_removeBoundaryAtTime (TextGrid me, int itier, double t);
diff --git a/fon/TextGridEditor.cpp b/fon/TextGridEditor.cpp
index f4af4ff..363feb0 100644
--- a/fon/TextGridEditor.cpp
+++ b/fon/TextGridEditor.cpp
@@ -41,9 +41,9 @@ Thing_implement (TextGridEditor, TimeSoundAnalysisEditor, 0);
 
 void structTextGridEditor :: v_info () {
 	TextGridEditor_Parent :: v_info ();
-	MelderInfo_writeLine (U"Selected tier: ", selectedTier);
-	MelderInfo_writeLine (U"TextGrid uses text styles: ", p_useTextStyles);
-	MelderInfo_writeLine (U"TextGrid font size: ", p_fontSize);
+	MelderInfo_writeLine (U"Selected tier: ", our selectedTier);
+	MelderInfo_writeLine (U"TextGrid uses text styles: ", our p_useTextStyles);
+	MelderInfo_writeLine (U"TextGrid font size: ", our p_fontSize);
 	MelderInfo_writeLine (U"TextGrid alignment: ", kGraphics_horizontalAlignment_getText (p_alignment));
 }
 
@@ -241,7 +241,7 @@ static void menu_cb_DrawVisibleSoundAndTextGrid (TextGridEditor me, EDITOR_ARGS_
 			autoSound sound = my d_longSound.data ?
 				LongSound_extractPart (my d_longSound.data, my startWindow, my endWindow, true) :
 				Sound_extractPart (my d_sound.data, my startWindow, my endWindow,
-					kSound_windowShape_RECTANGULAR, 1.0, true);
+					kSound_windowShape::RECTANGULAR, 1.0, true);
 			TextGrid_Sound_draw ((TextGrid) my data, sound.get(), my pictureGraphics,
 				my startWindow, my endWindow, true, my p_useTextStyles, my pref_picture_garnish ());
 		}
@@ -507,10 +507,10 @@ static void menu_cb_DrawTextGridAndPitch (TextGridEditor me, EDITOR_ARGS_FORM) {
 			if (! my d_pitch) Melder_throw (U"Cannot compute pitch.");
 		}
 		Editor_openPraatPicture (me);
-		double pitchFloor_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_floor, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		double pitchCeiling_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_ceiling, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		double pitchFloor_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchFloor_hidden, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		double pitchCeiling_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchCeiling_hidden, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
+		double pitchFloor_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_floor, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		double pitchCeiling_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_ceiling, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		double pitchFloor_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchFloor_hidden, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		double pitchCeiling_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchCeiling_hidden, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
 		double pitchViewFrom_overt = ( my p_pitch_viewFrom < my p_pitch_viewTo ? my p_pitch_viewFrom : pitchFloor_overt );
 		double pitchViewTo_overt = ( my p_pitch_viewFrom < my p_pitch_viewTo ? my p_pitch_viewTo : pitchCeiling_overt );
 		TextGrid_Pitch_drawSeparately ((TextGrid) my data, my d_pitch.get(), my pictureGraphics, my startWindow, my endWindow,
@@ -1554,12 +1554,12 @@ void structTextGridEditor :: v_draw () {
 		Graphics_setFontSize (our graphics.get(), oldFontSize);
 		if (anyTier -> name && anyTier -> name [0]) {
 			Graphics_setTextAlignment (our graphics.get(), Graphics_LEFT,
-				p_showNumberOf == kTextGridEditor_showNumberOf_NOTHING ? Graphics_HALF : Graphics_BOTTOM);
+				p_showNumberOf == kTextGridEditor_showNumberOf::NOTHING ? Graphics_HALF : Graphics_BOTTOM);
 			Graphics_text (our graphics.get(), our endWindow, 0.5, anyTier -> name);
 		}
-		if (our p_showNumberOf != kTextGridEditor_showNumberOf_NOTHING) {
+		if (our p_showNumberOf != kTextGridEditor_showNumberOf::NOTHING) {
 			Graphics_setTextAlignment (our graphics.get(), Graphics_LEFT, Graphics_TOP);
-			if (p_showNumberOf == kTextGridEditor_showNumberOf_INTERVALS_OR_POINTS) {
+			if (our p_showNumberOf == kTextGridEditor_showNumberOf::INTERVALS_OR_POINTS) {
 				long count = isIntervalTier ? ((IntervalTier) anyTier) -> intervals.size : ((TextTier) anyTier) -> points.size;
 				long position = itier == selectedTier ? ( isIntervalTier ? getSelectedInterval (this) : getSelectedPoint (this) ) : 0;
 				if (position) {
@@ -1568,7 +1568,7 @@ void structTextGridEditor :: v_draw () {
 					Graphics_text (our graphics.get(), our endWindow, 0.5,   U"(", count, U")");
 				}
 			} else {
-				Melder_assert (kTextGridEditor_showNumberOf_NONEMPTY_INTERVALS_OR_POINTS);
+				Melder_assert (our p_showNumberOf == kTextGridEditor_showNumberOf::NONEMPTY_INTERVALS_OR_POINTS);
 				long count = 0;
 				if (isIntervalTier) {
 					IntervalTier tier = (IntervalTier) anyTier;
@@ -1594,7 +1594,7 @@ void structTextGridEditor :: v_draw () {
 		}
 
 		Graphics_setColour (our graphics.get(), Graphics_BLACK);
-		Graphics_setFont (our graphics.get(), kGraphics_font_TIMES);
+		Graphics_setFont (our graphics.get(), kGraphics_font::TIMES);
 		Graphics_setFontSize (our graphics.get(), p_fontSize);
 		if (isIntervalTier)
 			do_drawIntervalTier (this, (IntervalTier) anyTier, itier);
@@ -1661,7 +1661,7 @@ void structTextGridEditor :: v_drawSelectionViewer () {
 	Graphics_setColour (our graphics.get(), Graphics_WHITE);
 	Graphics_fillRectangle (our graphics.get(), 0.5, 10.5, 0.5, 12.5);
 	Graphics_setColour (our graphics.get(), Graphics_BLACK);
-	Graphics_setFont (our graphics.get(), kGraphics_font_TIMES);
+	Graphics_setFont (our graphics.get(), kGraphics_font::TIMES);
 	Graphics_setFontSize (our graphics.get(), 12);
 	Graphics_setTextAlignment (our graphics.get(), Graphics_CENTRE, Graphics_HALF);
 	for (int irow = 1; irow <= 12; irow ++) {
@@ -2189,17 +2189,17 @@ void structTextGridEditor :: v_updateText () {
 }
 
 void structTextGridEditor :: v_prefs_addFields (EditorCommand cmd) {
-	UiField radio;
+	UiField _radio_;
 	NATURAL (U"Font size (points)", default_fontSize ())
-	OPTIONMENU_ENUM (U"Text alignment in intervals", kGraphics_horizontalAlignment, kGraphics_horizontalAlignment_DEFAULT)
+	OPTIONMENU_ENUM (U"Text alignment in intervals", kGraphics_horizontalAlignment, kGraphics_horizontalAlignment::DEFAULT)
 	OPTIONMENU (U"The symbols %#_^ in labels", default_useTextStyles () + 1)
 		OPTION (U"are shown as typed")
 		OPTION (U"mean italic/bold/sub/super")
 	OPTIONMENU (U"With the shift key, you drag", default_shiftDragMultiple () + 1)
 		OPTION (U"a single boundary")
 		OPTION (U"multiple boundaries")
-	OPTIONMENU_ENUM (U"Show number of", kTextGridEditor_showNumberOf, kTextGridEditor_showNumberOf_DEFAULT)
-	OPTIONMENU_ENUM (U"Paint intervals green whose label...", kMelder_string, kMelder_string_DEFAULT)
+	OPTIONMENU_ENUM (U"Show number of", kTextGridEditor_showNumberOf, kTextGridEditor_showNumberOf::DEFAULT)
+	OPTIONMENU_ENUM (U"Paint intervals green whose label...", kMelder_string, kMelder_string::DEFAULT)
 	SENTENCE (U"...the text", default_greenString ())
 }
 void structTextGridEditor :: v_prefs_setValues (EditorCommand cmd) {
diff --git a/fon/TextGrid_Sound.cpp b/fon/TextGrid_Sound.cpp
index fcb8077..d416090 100644
--- a/fon/TextGrid_Sound.cpp
+++ b/fon/TextGrid_Sound.cpp
@@ -155,7 +155,7 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 		autoSound part =
 			anySound -> classInfo == classLongSound ? 
 				LongSound_extractPart (static_cast <LongSound> (anySound), interval -> xmin, interval -> xmax, true) :
-				Sound_extractPart (static_cast <Sound> (anySound), interval -> xmin, interval -> xmax, kSound_windowShape_RECTANGULAR, 1.0, true);
+				Sound_extractPart (static_cast <Sound> (anySound), interval -> xmin, interval -> xmax, kSound_windowShape::RECTANGULAR, 1.0, true);
 		autoSpeechSynthesizer synthesizer = SpeechSynthesizer_create (languageName, U"default");
 		synthesizer -> d_samplingFrequency = round (
 			anySound -> classInfo == classLongSound ?
@@ -442,7 +442,7 @@ autoSoundList TextGrid_Sound_extractAllIntervals (TextGrid me, Sound sound, long
 		autoSoundList list = SoundList_create ();
 		for (long iseg = 1; iseg <= tier -> intervals.size; iseg ++) {
 			TextInterval segment = tier -> intervals.at [iseg];
-			autoSound interval = Sound_extractPart (sound, segment -> xmin, segment -> xmax, kSound_windowShape_RECTANGULAR, 1.0, preserveTimes);
+			autoSound interval = Sound_extractPart (sound, segment -> xmin, segment -> xmax, kSound_windowShape::RECTANGULAR, 1.0, preserveTimes);
 			Thing_setName (interval.get(), segment -> text ? segment -> text : U"untitled");
 			list -> addItem_move (interval.move());
 		}
@@ -461,7 +461,7 @@ autoSoundList TextGrid_Sound_extractNonemptyIntervals (TextGrid me, Sound sound,
 		for (long iseg = 1; iseg <= tier -> intervals.size; iseg ++) {
 			TextInterval segment = tier -> intervals.at [iseg];
 			if (segment -> text && segment -> text [0] != U'\0') {
-				autoSound interval = Sound_extractPart (sound, segment -> xmin, segment -> xmax, kSound_windowShape_RECTANGULAR, 1.0, preserveTimes);
+				autoSound interval = Sound_extractPart (sound, segment -> xmin, segment -> xmax, kSound_windowShape::RECTANGULAR, 1.0, preserveTimes);
 				Thing_setName (interval.get(), segment -> text ? segment -> text : U"untitled");
 				list -> addItem_move (interval.move());
 			}
@@ -474,7 +474,7 @@ autoSoundList TextGrid_Sound_extractNonemptyIntervals (TextGrid me, Sound sound,
 }
 
 autoSoundList TextGrid_Sound_extractIntervalsWhere (TextGrid me, Sound sound, long tierNumber,
-	int comparison_Melder_STRING, const char32 *text, bool preserveTimes)
+	kMelder_string which, const char32 *text, bool preserveTimes)
 {
 	try {
 		IntervalTier tier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
@@ -482,14 +482,14 @@ autoSoundList TextGrid_Sound_extractIntervalsWhere (TextGrid me, Sound sound, lo
 		long count = 0;
 		for (long iseg = 1; iseg <= tier -> intervals.size; iseg ++) {
 			TextInterval segment = tier -> intervals.at [iseg];
-			if (Melder_stringMatchesCriterion (segment -> text, comparison_Melder_STRING, text)) {
-				autoSound interval = Sound_extractPart (sound, segment -> xmin, segment -> xmax, kSound_windowShape_RECTANGULAR, 1.0, preserveTimes);
+			if (Melder_stringMatchesCriterion (segment -> text, which, text)) {
+				autoSound interval = Sound_extractPart (sound, segment -> xmin, segment -> xmax, kSound_windowShape::RECTANGULAR, 1.0, preserveTimes);
 				Thing_setName (interval.get(), Melder_cat (sound -> name ? sound -> name : U"", U"_", text, U"_", ++ count));
 				list -> addItem_move (interval.move());
 			}
 		}
 		if (list->size == 0)
-			Melder_warning (U"No label that ", kMelder_string_getText (comparison_Melder_STRING), U" the text \"", text, U"\" was found.");
+			Melder_warning (U"No label that ", kMelder_string_getText (which), U" the text \"", text, U"\" was found.");
 		return list;
 	} catch (MelderError) {
 		Melder_throw (me, U" & ", sound, U": intervals not extracted.");
@@ -578,15 +578,15 @@ static void autoMarks_semitones (Graphics g, double ymin, double ymax, bool have
 }
 
 void TextGrid_Pitch_drawSeparately (TextGrid grid, Pitch pitch, Graphics g, double tmin, double tmax,
-	double fmin, double fmax, bool showBoundaries, bool useTextStyles, bool garnish, bool speckle, int unit)
+	double fmin, double fmax, bool showBoundaries, bool useTextStyles, bool garnish, bool speckle, kPitch_unit unit)
 {
 	int ntier = grid -> tiers->size;
 	if (tmax <= tmin) tmin = grid -> xmin, tmax = grid -> xmax;
-	if (Function_isUnitLogarithmic (pitch, Pitch_LEVEL_FREQUENCY, unit)) {
-		fmin = Function_convertStandardToSpecialUnit (pitch, fmin, Pitch_LEVEL_FREQUENCY, unit);
-		fmax = Function_convertStandardToSpecialUnit (pitch, fmax, Pitch_LEVEL_FREQUENCY, unit);
+	if (Function_isUnitLogarithmic (pitch, Pitch_LEVEL_FREQUENCY, (int) unit)) {
+		fmin = Function_convertStandardToSpecialUnit (pitch, fmin, Pitch_LEVEL_FREQUENCY, (int) unit);
+		fmax = Function_convertStandardToSpecialUnit (pitch, fmax, Pitch_LEVEL_FREQUENCY, (int) unit);
 	}
-	if (unit == kPitch_unit_HERTZ_LOGARITHMIC)
+	if (unit == kPitch_unit::HERTZ_LOGARITHMIC)
 		Pitch_draw (pitch, g, tmin, tmax, pow (10.0, fmin - 0.25 * (fmax - fmin) * ntier), pow (10.0, fmax), false, speckle, unit);
 	else
 		Pitch_draw (pitch, g, tmin, tmax, fmin - 0.25 * (fmax - fmin) * ntier, fmax, false, speckle, unit);
@@ -595,15 +595,15 @@ void TextGrid_Pitch_drawSeparately (TextGrid grid, Pitch pitch, Graphics g, doub
 	 * Restore window for the sake of margin drawing.
 	 */
 	Graphics_setWindow (g, tmin, tmax, fmin - 0.25 * (fmax - fmin) * ntier, fmax);
-	if (unit == kPitch_unit_HERTZ_LOGARITHMIC)
+	if (unit == kPitch_unit::HERTZ_LOGARITHMIC)
 		fmin = pow (10, fmin), fmax = pow (10.0, fmax);
 	if (garnish) {
 		Graphics_drawInnerBox (g);
-		if (unit == kPitch_unit_HERTZ_LOGARITHMIC) {
+		if (unit == kPitch_unit::HERTZ_LOGARITHMIC) {
 			Graphics_markLeftLogarithmic (g, fmin, true, true, false, nullptr);
 			Graphics_markLeftLogarithmic (g, fmax, true, true, false, nullptr);
 			autoMarks_logarithmic (g, fmin, fmax, false);
-		} else if (unit == kPitch_unit_SEMITONES_100) {
+		} else if (unit == kPitch_unit::SEMITONES_100) {
 			Graphics_markLeft (g, fmin, true, true, false, nullptr);
 			Graphics_markLeft (g, fmax, true, true, false, nullptr);
 			autoMarks_semitones (g, fmin, fmax, false);
@@ -612,7 +612,7 @@ void TextGrid_Pitch_drawSeparately (TextGrid grid, Pitch pitch, Graphics g, doub
 			Graphics_markLeft (g, fmax, true, true, false, nullptr);
 			autoMarks (g, fmin, fmax, false);
 		}
-		Graphics_textLeft (g, true, Melder_cat (U"Pitch (", Function_getUnitText (pitch, Pitch_LEVEL_FREQUENCY, unit, Function_UNIT_TEXT_GRAPHICAL), U")"));
+		Graphics_textLeft (g, true, Melder_cat (U"Pitch (", Function_getUnitText (pitch, Pitch_LEVEL_FREQUENCY, (int) unit, Function_UNIT_TEXT_GRAPHICAL), U")"));
 		Graphics_textBottom (g, true, U"Time (s)");
 		Graphics_marksBottom (g, 2, true, true, false);
 	}
@@ -620,7 +620,7 @@ void TextGrid_Pitch_drawSeparately (TextGrid grid, Pitch pitch, Graphics g, doub
 
 void TextGrid_Pitch_draw (TextGrid grid, Pitch pitch, Graphics g,
 	long tierNumber, double tmin, double tmax, double fmin, double fmax,
-	double fontSize, bool useTextStyles, int horizontalAlignment, bool garnish, bool speckle, int unit)
+	double fontSize, bool useTextStyles, int horizontalAlignment, bool garnish, bool speckle, kPitch_unit unit)
 {
 	try {
 		Function anyTier = TextGrid_checkSpecifiedTierNumberWithinRange (grid, tierNumber);
@@ -628,11 +628,11 @@ void TextGrid_Pitch_draw (TextGrid grid, Pitch pitch, Graphics g,
 		Pitch_draw (pitch, g, tmin, tmax, fmin, fmax, garnish, speckle, unit);
 		if (tmax <= tmin) tmin = grid -> xmin, tmax = grid -> xmax;
 		autoPitchTier pitchTier = Pitch_to_PitchTier (pitch);
-		if (Function_isUnitLogarithmic (pitch, Pitch_LEVEL_FREQUENCY, unit)) {
-			fmin = Function_convertStandardToSpecialUnit (pitch, fmin, Pitch_LEVEL_FREQUENCY, unit);
-			fmax = Function_convertStandardToSpecialUnit (pitch, fmax, Pitch_LEVEL_FREQUENCY, unit);
+		if (Function_isUnitLogarithmic (pitch, Pitch_LEVEL_FREQUENCY, (int) unit)) {
+			fmin = Function_convertStandardToSpecialUnit (pitch, fmin, Pitch_LEVEL_FREQUENCY, (int) unit);
+			fmax = Function_convertStandardToSpecialUnit (pitch, fmax, Pitch_LEVEL_FREQUENCY, (int) unit);
 		}
-		Graphics_setTextAlignment (g, horizontalAlignment, Graphics_BOTTOM);
+		Graphics_setTextAlignment (g, (kGraphics_horizontalAlignment) horizontalAlignment, Graphics_BOTTOM);
 		Graphics_setInner (g);
 		Graphics_setFontSize (g, fontSize);
 		Graphics_setPercentSignIsItalic (g, useTextStyles);
@@ -649,10 +649,10 @@ void TextGrid_Pitch_draw (TextGrid grid, Pitch pitch, Graphics g,
 				if (tright > pitch -> xmax) tright = pitch -> xmax;
 				tmid = (tleft + tright) / 2;
 				if (tmid < tmin || tmid > tmax) continue;
-				f0 = Function_convertStandardToSpecialUnit (pitch, RealTier_getValueAtTime (pitchTier.get(), tmid), Pitch_LEVEL_FREQUENCY, unit);
+				f0 = Function_convertStandardToSpecialUnit (pitch, RealTier_getValueAtTime (pitchTier.get(), tmid), Pitch_LEVEL_FREQUENCY, (int) unit);
 				if (f0 < fmin || f0 > fmax) continue;
 				Graphics_text (g,
-					horizontalAlignment == Graphics_LEFT ? tleft : horizontalAlignment == Graphics_RIGHT ? tright : tmid,
+					horizontalAlignment == (int) Graphics_LEFT ? tleft : horizontalAlignment == (int) Graphics_RIGHT ? tright : tmid,
 					f0, interval -> text);
 			}
 		} else {
@@ -662,7 +662,7 @@ void TextGrid_Pitch_draw (TextGrid grid, Pitch pitch, Graphics g,
 				double t = point -> number;
 				if (! point -> mark || ! point -> mark [0]) continue;
 				if (t < tmin || t > tmax) continue;
-				double f0 = Function_convertStandardToSpecialUnit (pitch, RealTier_getValueAtTime (pitchTier.get(), t), Pitch_LEVEL_FREQUENCY, unit);
+				double f0 = Function_convertStandardToSpecialUnit (pitch, RealTier_getValueAtTime (pitchTier.get(), t), Pitch_LEVEL_FREQUENCY, (int) unit);
 				if (f0 < fmin || f0 > fmax) continue;
 				Graphics_text (g, t, f0, point -> mark);
 			}
diff --git a/fon/TextGrid_Sound.h b/fon/TextGrid_Sound.h
index 0852614..a5f50a7 100644
--- a/fon/TextGrid_Sound.h
+++ b/fon/TextGrid_Sound.h
@@ -1,6 +1,6 @@
 /* TextGrid_Sound.h
  *
- * Copyright (C) 1992-2011,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,13 +29,13 @@ Collection_define (SoundList, OrderedOf, Sound) {
 autoSoundList TextGrid_Sound_extractAllIntervals (TextGrid me, Sound sound, long itier, bool preserveTimes);
 autoSoundList TextGrid_Sound_extractNonemptyIntervals (TextGrid me, Sound sound, long itier, bool preserveTimes);
 autoSoundList TextGrid_Sound_extractIntervalsWhere (TextGrid me, Sound sound,
-	long itier, int which_Melder_STRING, const char32 *text, bool preserveTimes);
+	long itier, kMelder_string which, const char32 *text, bool preserveTimes);
 
 void TextGrid_Pitch_draw (TextGrid grid, Pitch pitch, Graphics g,
 	long itier, double tmin, double tmax, double fmin, double fmax,
-	double fontSize, bool useTextStyles, int horizontalAlignment, bool garnish, bool speckle, int yscale);
+	double fontSize, bool useTextStyles, int horizontalAlignment, bool garnish, bool speckle, kPitch_unit unit);
 void TextGrid_Pitch_drawSeparately (TextGrid grid, Pitch pitch, Graphics g, double tmin, double tmax,
-	double fmin, double fmax, bool showBoundaries, bool useTextStyles, bool garnish, bool speckle, int yscale);
+	double fmin, double fmax, bool showBoundaries, bool useTextStyles, bool garnish, bool speckle, kPitch_unit unit);
 
 void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierNumber, long intervalNumber,
 	const char32 *languageName, bool includeWords, bool includePhonemes);
diff --git a/fon/TimeSoundAnalysisEditor.cpp b/fon/TimeSoundAnalysisEditor.cpp
index ee0142b..add7506 100644
--- a/fon/TimeSoundAnalysisEditor.cpp
+++ b/fon/TimeSoundAnalysisEditor.cpp
@@ -81,11 +81,11 @@ void structTimeSoundAnalysisEditor :: v_info () {
 		/* Pitch settings: */
 		MelderInfo_writeLine (U"Pitch floor: ", p_pitch_floor, U" Hz");
 		MelderInfo_writeLine (U"Pitch ceiling: ", p_pitch_ceiling, U" Hz");
-		MelderInfo_writeLine (U"Pitch unit: ", Function_getUnitText (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, p_pitch_unit, Function_UNIT_TEXT_MENU));
+		MelderInfo_writeLine (U"Pitch unit: ", Function_getUnitText (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, (int) p_pitch_unit, Function_UNIT_TEXT_MENU));
 		MelderInfo_writeLine (U"Pitch drawing method: ", kTimeSoundAnalysisEditor_pitch_drawingMethod_getText (p_pitch_drawingMethod));
 		/* Advanced pitch settings: */
-		MelderInfo_writeLine (U"Pitch view from: ", p_pitch_viewFrom, U" ", Function_getUnitText (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, p_pitch_unit, Function_UNIT_TEXT_MENU));
-		MelderInfo_writeLine (U"Pitch view to: ", p_pitch_viewTo, U" ", Function_getUnitText (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, p_pitch_unit, Function_UNIT_TEXT_MENU));
+		MelderInfo_writeLine (U"Pitch view from: ", p_pitch_viewFrom, U" ", Function_getUnitText (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, (int) p_pitch_unit, Function_UNIT_TEXT_MENU));
+		MelderInfo_writeLine (U"Pitch view to: ", p_pitch_viewTo, U" ", Function_getUnitText (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, (int) p_pitch_unit, Function_UNIT_TEXT_MENU));
 		MelderInfo_writeLine (U"Pitch method: ", kTimeSoundAnalysisEditor_pitch_analysisMethod_getText (p_pitch_method));
 		MelderInfo_writeLine (U"Pitch very accurate: ", p_pitch_veryAccurate);
 		MelderInfo_writeLine (U"Pitch max. number of candidates: ", p_pitch_maximumNumberOfCandidates);
@@ -305,7 +305,7 @@ static void do_log (TimeSoundAnalysisEditor me, int which) {
 			if (part == TimeSoundAnalysisEditor_PART_CURSOR) {
 				value = Vector_getValueAtX (my d_intensity.get(), tmin, Vector_CHANNEL_1, Vector_VALUE_INTERPOLATION_LINEAR);
 			} else {
-				value = Intensity_getAverage (my d_intensity.get(), tmin, tmax, my p_intensity_averagingMethod);
+				value = Intensity_getAverage (my d_intensity.get(), tmin, tmax, (int) my p_intensity_averagingMethod);
 			}
 		} else if (str32equ (varName, U"power")) {
 			if (! my p_spectrogram_show)
@@ -522,7 +522,7 @@ static void menu_cb_getSpectralPowerAtCursorCross (TimeSoundAnalysisEditor me, E
 static void menu_cb_moveFrequencyCursorTo (TimeSoundAnalysisEditor me, EDITOR_ARGS_FORM) {
 	if (! my p_spectrogram_show)
 		Melder_throw (U"No spectrogram is visible.\nFirst choose \"Show spectrogram\" from the Spectrum menu.");
-	EDITOR_FORM (U"Move frequency cursor to", 0)
+	EDITOR_FORM (U"Move frequency cursor to", nullptr)
 		REAL (U"Frequency (Hz)", U"0.0")
 	EDITOR_OK
 		SET_REAL (U"Frequency", my d_spectrogram_cursor)
@@ -542,7 +542,7 @@ static autoSound extractSound (TimeSoundAnalysisEditor me, double tmin, double t
 	} else if (my d_sound.data) {
 		if (tmin < my d_sound.data -> xmin) tmin = my d_sound.data -> xmin;
 		if (tmax > my d_sound.data -> xmax) tmax = my d_sound.data -> xmax;
-		sound = Sound_extractPart (my d_sound.data, tmin, tmax, kSound_windowShape_RECTANGULAR, 1.0, true);
+		sound = Sound_extractPart (my d_sound.data, tmin, tmax, kSound_windowShape::RECTANGULAR, 1.0, true);
 	}
 	return sound;
 }
@@ -560,19 +560,19 @@ static void menu_cb_extractVisibleSpectrogram (TimeSoundAnalysisEditor me, EDITO
 
 static void menu_cb_viewSpectralSlice (TimeSoundAnalysisEditor me, EDITOR_ARGS_DIRECT) {
 	double start = my startSelection == my endSelection ?
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_GAUSSIAN ? my startSelection - my p_spectrogram_windowLength :
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::GAUSSIAN ? my startSelection - my p_spectrogram_windowLength :
 		my startSelection - my p_spectrogram_windowLength / 2 : my startSelection;
 	double finish = my startSelection == my endSelection ?
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_GAUSSIAN ? my endSelection + my p_spectrogram_windowLength :
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::GAUSSIAN ? my endSelection + my p_spectrogram_windowLength :
 		my endSelection + my p_spectrogram_windowLength / 2 : my endSelection;
 	autoSound sound = extractSound (me, start, finish);
 	Sound_multiplyByWindow (sound.get(),
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_SQUARE ? kSound_windowShape_RECTANGULAR :
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_HAMMING ? kSound_windowShape_HAMMING :
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_BARTLETT ? kSound_windowShape_TRIANGULAR :
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_WELCH ? kSound_windowShape_PARABOLIC :
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_HANNING ? kSound_windowShape_HANNING :
-		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_GAUSSIAN ? kSound_windowShape_GAUSSIAN_2 : kSound_windowShape_RECTANGULAR);
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::SQUARE ? kSound_windowShape::RECTANGULAR :
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::HAMMING ? kSound_windowShape::HAMMING :
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::BARTLETT ? kSound_windowShape::TRIANGULAR :
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::WELCH ? kSound_windowShape::PARABOLIC :
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::HANNING ? kSound_windowShape::HANNING :
+		my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::GAUSSIAN ? kSound_windowShape::GAUSSIAN_2 : kSound_windowShape::RECTANGULAR);
 	autoSpectrum publish = Sound_to_Spectrum (sound.get(), true);
 	Thing_setName (publish.get(), Melder_cat (( my data == nullptr ? U"untitled" : my data -> name ),
 		U"_", Melder_fixed (0.5 * (my startSelection + my endSelection), 3)));
@@ -719,18 +719,18 @@ static void menu_cb_pitchListing (TimeSoundAnalysisEditor me, EDITOR_ARGS_DIRECT
 		if (! my d_pitch) Melder_throw (theMessage_Cannot_compute_pitch);
 	}
 	MelderInfo_open ();
-	MelderInfo_writeLine (U"Time_s   F0_", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, Function_UNIT_TEXT_SHORT));
+	MelderInfo_writeLine (U"Time_s   F0_", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, Function_UNIT_TEXT_SHORT));
 	if (part == TimeSoundAnalysisEditor_PART_CURSOR) {
 		double f0 = Pitch_getValueAtTime (my d_pitch.get(), tmin, my p_pitch_unit, true);
-		f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
+		f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
 		MelderInfo_writeLine (Melder_fixed (tmin, 6), U"   ", Melder_fixed (f0, 6));
 	} else {
 		integer i, i1, i2;
 		Sampled_getWindowSamples (my d_pitch.get(), tmin, tmax, & i1, & i2);
 		for (i = i1; i <= i2; i ++) {
 			double t = Sampled_indexToX (my d_pitch.get(), i);
-			double f0 = Sampled_getValueAtSample (my d_pitch.get(), i, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-			f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
+			double f0 = Sampled_getValueAtSample (my d_pitch.get(), i, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+			f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
 			MelderInfo_writeLine (Melder_fixed (t, 6), U"   ", Melder_fixed (f0, 6));
 		}
 	}
@@ -748,13 +748,13 @@ static void menu_cb_getPitch (TimeSoundAnalysisEditor me, EDITOR_ARGS_DIRECT) {
 	}
 	if (part == TimeSoundAnalysisEditor_PART_CURSOR) {
 		double f0 = Pitch_getValueAtTime (my d_pitch.get(), tmin, my p_pitch_unit, true);
-		f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, 0),
+		f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, 0),
 			U" (interpolated pitch at CURSOR)");
 	} else {
 		double f0 = Pitch_getMean (my d_pitch.get(), tmin, tmax, my p_pitch_unit);
-		f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, 0),
+		f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, 0),
 			U" (mean pitch ", TimeSoundAnalysisEditor_partString_locative (part), U")");
 	}
 }
@@ -769,8 +769,8 @@ static void menu_cb_getMinimumPitch (TimeSoundAnalysisEditor me, EDITOR_ARGS_DIR
 		if (! my d_pitch) Melder_throw (theMessage_Cannot_compute_pitch);
 	}
 	f0 = Pitch_getMinimum (my d_pitch.get(), tmin, tmax, my p_pitch_unit, true);
-	f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-	Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, 0),
+	f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+	Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, 0),
 		U" (minimum pitch ", TimeSoundAnalysisEditor_partString_locative (part), U")");
 }
 
@@ -784,8 +784,8 @@ static void menu_cb_getMaximumPitch (TimeSoundAnalysisEditor me, EDITOR_ARGS_DIR
 		if (! my d_pitch) Melder_throw (theMessage_Cannot_compute_pitch);   // BUG
 	}
 	f0 = Pitch_getMaximum (my d_pitch.get(), tmin, tmax, my p_pitch_unit, true);
-	f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-	Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, 0),
+	f0 = Function_convertToNonlogarithmic (my d_pitch.get(), f0, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+	Melder_information (f0, U" ", Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, 0),
 		U" (maximum pitch ", TimeSoundAnalysisEditor_partString_locative (part), U")");
 }
 
@@ -867,10 +867,10 @@ static void menu_cb_drawVisiblePitchContour (TimeSoundAnalysisEditor me, EDITOR_
 			if (! my d_pitch) Melder_throw (theMessage_Cannot_compute_pitch);
 		}
 		Editor_openPraatPicture (me);
-		double pitchFloor_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_floor, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		double pitchCeiling_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_ceiling, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		double pitchFloor_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchFloor_hidden, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-		double pitchCeiling_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchCeiling_hidden, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
+		double pitchFloor_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_floor, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		double pitchCeiling_hidden = Function_convertStandardToSpecialUnit (my d_pitch.get(), my p_pitch_ceiling, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		double pitchFloor_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchFloor_hidden, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+		double pitchCeiling_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchCeiling_hidden, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
 		double pitchViewFrom_overt = my p_pitch_viewFrom < my p_pitch_viewTo ? my p_pitch_viewFrom : pitchFloor_overt;
 		double pitchViewTo_overt = my p_pitch_viewFrom < my p_pitch_viewTo ? my p_pitch_viewTo : pitchCeiling_overt;
 		Pitch_draw (my d_pitch.get(), my pictureGraphics, my startWindow, my endWindow, pitchViewFrom_overt, pitchViewTo_overt,
@@ -996,8 +996,8 @@ static void menu_cb_getIntensity (TimeSoundAnalysisEditor me, EDITOR_ARGS_DIRECT
 		Melder_information (Vector_getValueAtX (my d_intensity.get(), tmin, Vector_CHANNEL_1, Vector_VALUE_INTERPOLATION_LINEAR), U" dB (intensity at CURSOR)");
 	} else {
 		static const char32 *methodString [] = { U"median", U"mean-energy", U"mean-sones", U"mean-dB" };
-		Melder_information (Intensity_getAverage (my d_intensity.get(), tmin, tmax, my p_intensity_averagingMethod),
-			U" dB (", methodString [my p_intensity_averagingMethod], U" intensity ", TimeSoundAnalysisEditor_partString_locative (part), U")");
+		Melder_information (Intensity_getAverage (my d_intensity.get(), tmin, tmax, (int) my p_intensity_averagingMethod),
+			U" dB (", methodString [(int) my p_intensity_averagingMethod], U" intensity ", TimeSoundAnalysisEditor_partString_locative (part), U")");
 	}
 }
 
@@ -1306,7 +1306,7 @@ static void menu_cb_voiceReport (TimeSoundAnalysisEditor me, EDITOR_ARGS_DIRECT)
 	autoSound sound = extractSound (me, tmin, tmax);
 	MelderInfo_open ();
 	MelderInfo_writeLine (U"-- Voice report for ", my name, U" --\nDate: ", Melder_peek8to32 (ctime (& today)));
-	if (my p_pitch_method != kTimeSoundAnalysisEditor_pitch_analysisMethod_CROSS_CORRELATION)
+	if (my p_pitch_method != kTimeSoundAnalysisEditor_pitch_analysisMethod::CROSS_CORRELATION)
 		MelderInfo_writeLine (U"WARNING: some of the following measurements may be imprecise.\n"
 			"For more precision, go to \"Pitch settings\" and choose \"Optimize for voice analysis\".\n");
 	MelderInfo_writeLine (U"Time range of ", TimeSoundAnalysisEditor_partString (part));
@@ -1561,7 +1561,7 @@ void TimeSoundAnalysisEditor_computeSpectrogram (TimeSoundAnalysisEditor me) {
 	if (my p_spectrogram_show && my endWindow - my startWindow <= my p_longestAnalysis &&
 		(! my d_spectrogram || my d_spectrogram -> xmin != my startWindow || my d_spectrogram -> xmax != my endWindow))
 	{
-		double margin = my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape_GAUSSIAN ? my p_spectrogram_windowLength : 0.5 * my p_spectrogram_windowLength;
+		double margin = my p_spectrogram_windowShape == kSound_to_Spectrogram_windowShape::GAUSSIAN ? my p_spectrogram_windowLength : 0.5 * my p_spectrogram_windowLength;
 		my d_spectrogram.reset();
 		try {
 			autoSound sound = extractSound (me, my startWindow - margin, my endWindow + margin);
@@ -1582,14 +1582,14 @@ static void computePitch_inside (TimeSoundAnalysisEditor me) {
 	try {
 		autoSound sound = extractSound (me, my startWindow - margin, my endWindow + margin);
 		double pitchTimeStep =
-			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy_FIXED ? my p_fixedTimeStep :
-			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy_VIEW_DEPENDENT ? (my endWindow - my startWindow) / my p_numberOfTimeStepsPerView :
+			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy::FIXED_ ? my p_fixedTimeStep :
+			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy::VIEW_DEPENDENT ? (my endWindow - my startWindow) / my p_numberOfTimeStepsPerView :
 			0.0;   // the default: determined by pitch floor
 		my d_pitch = Sound_to_Pitch_any (sound.get(), pitchTimeStep,
 			my p_pitch_floor,
-			my p_pitch_method == kTimeSoundAnalysisEditor_pitch_analysisMethod_AUTOCORRELATION ? 3.0 : 1.0,
+			my p_pitch_method == kTimeSoundAnalysisEditor_pitch_analysisMethod::AUTOCORRELATION ? 3.0 : 1.0,
 			my p_pitch_maximumNumberOfCandidates,
-			(my p_pitch_method - 1) * 2 + my p_pitch_veryAccurate,
+			((int) my p_pitch_method - 1) * 2 + my p_pitch_veryAccurate,
 			my p_pitch_silenceThreshold, my p_pitch_voicingThreshold,
 			my p_pitch_octaveCost, my p_pitch_octaveJumpCost, my p_pitch_voicedUnvoicedCost, my p_pitch_ceiling);
 		my d_pitch -> xmin = my startWindow;
@@ -1643,12 +1643,12 @@ void TimeSoundAnalysisEditor_computeFormants (TimeSoundAnalysisEditor me) {
 						0.5 * (my startWindow + my endWindow + my p_longestAnalysis) + margin) :
 					extractSound (me, my startWindow - margin, my endWindow + margin);
 			double formantTimeStep =
-				my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy_FIXED ? my p_fixedTimeStep :
-				my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy_VIEW_DEPENDENT ? (my endWindow - my startWindow) / my p_numberOfTimeStepsPerView :
+				my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy::FIXED_ ? my p_fixedTimeStep :
+				my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy::VIEW_DEPENDENT ? (my endWindow - my startWindow) / my p_numberOfTimeStepsPerView :
 				0.0;   // the default: determined by analysis window length
 			my d_formant = Sound_to_Formant_any (sound.get(), formantTimeStep,
 				lround (my p_formant_numberOfFormants * 2), my p_formant_maximumFormant,
-				my p_formant_windowLength, my p_formant_method, my p_formant_preemphasisFrom, 50.0);
+				my p_formant_windowLength, (int) my p_formant_method, my p_formant_preemphasisFrom, 50.0);
 			my d_formant -> xmin = my startWindow;
 			my d_formant -> xmax = my endWindow;
 		} catch (MelderError) {
@@ -1682,14 +1682,14 @@ static void TimeSoundAnalysisEditor_v_draw_analysis (TimeSoundAnalysisEditor me)
 	 * d_pitch may not exist yet (if shown at all, it may be going to be created in TimeSoundAnalysisEditor_computePitch (),
 	 * and even if that fails the user should see what the pitch settings are). So we use a dummy object.
 	 */
-	double pitchFloor_hidden = Function_convertStandardToSpecialUnit (Thing_dummyObject (Pitch), my p_pitch_floor, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-	double pitchCeiling_hidden = Function_convertStandardToSpecialUnit (Thing_dummyObject (Pitch), my p_pitch_ceiling, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-	double pitchFloor_overt = Function_convertToNonlogarithmic (Thing_dummyObject (Pitch), pitchFloor_hidden, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
-	double pitchCeiling_overt = Function_convertToNonlogarithmic (Thing_dummyObject (Pitch), pitchCeiling_hidden, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
+	double pitchFloor_hidden = Function_convertStandardToSpecialUnit (Thing_dummyObject (Pitch), my p_pitch_floor, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+	double pitchCeiling_hidden = Function_convertStandardToSpecialUnit (Thing_dummyObject (Pitch), my p_pitch_ceiling, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+	double pitchFloor_overt = Function_convertToNonlogarithmic (Thing_dummyObject (Pitch), pitchFloor_hidden, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
+	double pitchCeiling_overt = Function_convertToNonlogarithmic (Thing_dummyObject (Pitch), pitchCeiling_hidden, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
 	double pitchViewFrom_overt = my p_pitch_viewFrom < my p_pitch_viewTo ? my p_pitch_viewFrom : pitchFloor_overt;
 	double pitchViewTo_overt = my p_pitch_viewFrom < my p_pitch_viewTo ? my p_pitch_viewTo : pitchCeiling_overt;
-	double pitchViewFrom_hidden = Function_isUnitLogarithmic (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, my p_pitch_unit) ? log10 (pitchViewFrom_overt) : pitchViewFrom_overt;
-	double pitchViewTo_hidden = Function_isUnitLogarithmic (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, my p_pitch_unit) ? log10 (pitchViewTo_overt) : pitchViewTo_overt;
+	double pitchViewFrom_hidden = Function_isUnitLogarithmic (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit) ? log10 (pitchViewFrom_overt) : pitchViewFrom_overt;
+	double pitchViewTo_hidden = Function_isUnitLogarithmic (Thing_dummyObject (Pitch), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit) ? log10 (pitchViewTo_overt) : pitchViewTo_overt;
 
 	Graphics_setWindow (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
 	Graphics_setColour (my graphics.get(), Graphics_WHITE);
@@ -1698,7 +1698,7 @@ static void TimeSoundAnalysisEditor_v_draw_analysis (TimeSoundAnalysisEditor me)
 	Graphics_rectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
 
 	if (my endWindow - my startWindow > my p_longestAnalysis) {
-		Graphics_setFont (my graphics.get(), kGraphics_font_HELVETICA);
+		Graphics_setFont (my graphics.get(), kGraphics_font::HELVETICA);
 		Graphics_setFontSize (my graphics.get(), 10);
 		Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
 		Graphics_text (my graphics.get(), 0.5, 0.67,   U"(To see the analyses, zoom in to at most ", Melder_half (my p_longestAnalysis), U" seconds,");
@@ -1714,36 +1714,36 @@ static void TimeSoundAnalysisEditor_v_draw_analysis (TimeSoundAnalysisEditor me)
 	}
 	TimeSoundAnalysisEditor_computePitch (me);
 	if (my p_pitch_show && my d_pitch) {
-		double periodsPerAnalysisWindow = my p_pitch_method == kTimeSoundAnalysisEditor_pitch_analysisMethod_AUTOCORRELATION ? 3.0 : 1.0;
+		double periodsPerAnalysisWindow = my p_pitch_method == kTimeSoundAnalysisEditor_pitch_analysisMethod::AUTOCORRELATION ? 3.0 : 1.0;
 		double greatestNonUndersamplingTimeStep = 0.5 * periodsPerAnalysisWindow / my p_pitch_floor;
 		double defaultTimeStep = 0.5 * greatestNonUndersamplingTimeStep;
 		double timeStep =
-			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy_FIXED ? my p_fixedTimeStep :
-			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy_VIEW_DEPENDENT ? (my endWindow - my startWindow) / my p_numberOfTimeStepsPerView :
+			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy::FIXED_ ? my p_fixedTimeStep :
+			my p_timeStepStrategy == kTimeSoundAnalysisEditor_timeStepStrategy::VIEW_DEPENDENT ? (my endWindow - my startWindow) / my p_numberOfTimeStepsPerView :
 			defaultTimeStep;
 		int undersampled = timeStep > greatestNonUndersamplingTimeStep;
 		long numberOfVisiblePitchPoints = (long) ((my endWindow - my startWindow) / timeStep);
 		Graphics_setColour (my graphics.get(), Graphics_CYAN);
 		Graphics_setLineWidth (my graphics.get(), 3.0);
-		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_AUTOMATIC && (undersampled || numberOfVisiblePitchPoints < 101)) ||
-		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_SPECKLE)
+		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::AUTOMATIC && (undersampled || numberOfVisiblePitchPoints < 101)) ||
+		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::SPECKLE)
 		{
 			Pitch_drawInside (my d_pitch.get(), my graphics.get(), my startWindow, my endWindow, pitchViewFrom_overt, pitchViewTo_overt, 2, my p_pitch_unit);
 		}
-		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_AUTOMATIC && ! undersampled) ||
-		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_CURVE)
+		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::AUTOMATIC && ! undersampled) ||
+		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::CURVE)
 		{
 			Pitch_drawInside (my d_pitch.get(), my graphics.get(), my startWindow, my endWindow, pitchViewFrom_overt, pitchViewTo_overt, false, my p_pitch_unit);
 		}
 		Graphics_setColour (my graphics.get(), Graphics_BLUE);
 		Graphics_setLineWidth (my graphics.get(), 1.0);
-		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_AUTOMATIC && (undersampled || numberOfVisiblePitchPoints < 101)) ||
-		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_SPECKLE)
+		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::AUTOMATIC && (undersampled || numberOfVisiblePitchPoints < 101)) ||
+		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::SPECKLE)
 		{
 			Pitch_drawInside (my d_pitch.get(), my graphics.get(), my startWindow, my endWindow, pitchViewFrom_overt, pitchViewTo_overt, 1, my p_pitch_unit);
 		}
-		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_AUTOMATIC && ! undersampled) ||
-		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod_CURVE)
+		if ((my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::AUTOMATIC && ! undersampled) ||
+		    my p_pitch_drawingMethod == kTimeSoundAnalysisEditor_pitch_drawingMethod::CURVE)
 		{
 			Pitch_drawInside (my d_pitch.get(), my graphics.get(), my startWindow, my endWindow, pitchViewFrom_overt, pitchViewTo_overt, false, my p_pitch_unit);
 		}
@@ -1778,24 +1778,24 @@ static void TimeSoundAnalysisEditor_v_draw_analysis (TimeSoundAnalysisEditor me)
 				pitchCursor_hidden = Pitch_getValueAtTime (my d_pitch.get(), my startSelection, my p_pitch_unit, 1);
 			else
 				pitchCursor_hidden = Pitch_getMean (my d_pitch.get(), my startSelection, my endSelection, my p_pitch_unit);
-			pitchCursor_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchCursor_hidden, Pitch_LEVEL_FREQUENCY, my p_pitch_unit);
+			pitchCursor_overt = Function_convertToNonlogarithmic (my d_pitch.get(), pitchCursor_hidden, Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit);
 			if (isdefined (pitchCursor_hidden)) {
 				Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_HALF);
 				Graphics_text (my graphics.get(), my endWindow, pitchCursor_hidden,
 					Melder_float (Melder_half (pitchCursor_overt)), U" ",
-					Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, Function_UNIT_TEXT_SHORT | Function_UNIT_TEXT_GRAPHICAL));
+					Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, Function_UNIT_TEXT_SHORT | Function_UNIT_TEXT_GRAPHICAL));
 			}
 			if (isundef (pitchCursor_hidden) || Graphics_dyWCtoMM (my graphics.get(), pitchCursor_hidden - pitchViewFrom_hidden) > 5.0) {
 				Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_BOTTOM);
 				Graphics_text (my graphics.get(), my endWindow, pitchViewFrom_hidden - Graphics_dyMMtoWC (my graphics.get(), 0.5),
 					Melder_float (Melder_half (pitchViewFrom_overt)), U" ",
-					Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, Function_UNIT_TEXT_SHORT | Function_UNIT_TEXT_GRAPHICAL));
+					Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, Function_UNIT_TEXT_SHORT | Function_UNIT_TEXT_GRAPHICAL));
 			}
 			if (isundef (pitchCursor_hidden) || Graphics_dyWCtoMM (my graphics.get(), pitchViewTo_hidden - pitchCursor_hidden) > 5.0) {
 				Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_TOP);
 				Graphics_text (my graphics.get(), my endWindow, pitchViewTo_hidden,
 					Melder_float (Melder_half (pitchViewTo_overt)), U" ",
-					Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, my p_pitch_unit, Function_UNIT_TEXT_SHORT | Function_UNIT_TEXT_GRAPHICAL));
+					Function_getUnitText (my d_pitch.get(), Pitch_LEVEL_FREQUENCY, (int) my p_pitch_unit, Function_UNIT_TEXT_SHORT | Function_UNIT_TEXT_GRAPHICAL));
 			}
 		} else {
 			Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
@@ -1809,7 +1809,7 @@ static void TimeSoundAnalysisEditor_v_draw_analysis (TimeSoundAnalysisEditor me)
 	if (my p_intensity_show) {
 		double intensityCursor = undefined;
 		Graphics_Colour textColour;
-		int alignment;
+		kGraphics_horizontalAlignment alignment;
 		double y;
 		if (! my p_pitch_show) textColour = Graphics_GREEN, alignment = Graphics_LEFT, y = my endWindow;
 		else if (! my p_spectrogram_show && ! my p_formant_show) textColour = Graphics_GREEN, alignment = Graphics_RIGHT, y = my startWindow;
@@ -1820,7 +1820,7 @@ static void TimeSoundAnalysisEditor_v_draw_analysis (TimeSoundAnalysisEditor me)
 				if (my startSelection == my endSelection) {
 					intensityCursor = Vector_getValueAtX (my d_intensity.get(), my startSelection, Vector_CHANNEL_1, Vector_VALUE_INTERPOLATION_LINEAR);
 				} else {
-					intensityCursor = Intensity_getAverage (my d_intensity.get(), my startSelection, my endSelection, my p_intensity_averagingMethod);
+					intensityCursor = Intensity_getAverage (my d_intensity.get(), my startSelection, my endSelection, (int) my p_intensity_averagingMethod);
 				}
 			}
 			Graphics_setColour (my graphics.get(), textColour);
@@ -1831,7 +1831,7 @@ static void TimeSoundAnalysisEditor_v_draw_analysis (TimeSoundAnalysisEditor me)
 				Graphics_setTextAlignment (my graphics.get(), alignment, Graphics_HALF);
 				Graphics_text (my graphics.get(), y, intensityCursor,
 					Melder_float (Melder_half (intensityCursor)), U" dB",
-					my startSelection == my endSelection ? U"" : methodString [my p_intensity_averagingMethod]);
+					my startSelection == my endSelection ? U"" : methodString [(int) my p_intensity_averagingMethod]);
 			}
 			if (! intensityCursorVisible || Graphics_dyWCtoMM (my graphics.get(), intensityCursor - my p_intensity_viewFrom) > 5.0) {
 				Graphics_setTextAlignment (my graphics.get(), alignment, Graphics_BOTTOM);
diff --git a/fon/TimeSoundAnalysisEditor_enums.h b/fon/TimeSoundAnalysisEditor_enums.h
index 6339545..16fc419 100644
--- a/fon/TimeSoundAnalysisEditor_enums.h
+++ b/fon/TimeSoundAnalysisEditor_enums.h
@@ -1,6 +1,6 @@
 /* TimeSoundAnalysisEditor_enums.h
  *
- * Copyright (C) 2007,2013,2015 Paul Boersma
+ * Copyright (C) 2007,2013,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 
 enums_begin (kTimeSoundAnalysisEditor_timeStepStrategy, 1)
 	enums_add (kTimeSoundAnalysisEditor_timeStepStrategy, 1, AUTOMATIC, U"automatic")
-	enums_add (kTimeSoundAnalysisEditor_timeStepStrategy, 2, FIXED, U"fixed")
+	enums_add (kTimeSoundAnalysisEditor_timeStepStrategy, 2, FIXED_, U"fixed")
 	enums_add (kTimeSoundAnalysisEditor_timeStepStrategy, 3, VIEW_DEPENDENT, U"view-dependent")
 enums_end (kTimeSoundAnalysisEditor_timeStepStrategy, 3, AUTOMATIC)
 
diff --git a/fon/TimeSoundEditor.cpp b/fon/TimeSoundEditor.cpp
index af7fb34..0a01289 100644
--- a/fon/TimeSoundEditor.cpp
+++ b/fon/TimeSoundEditor.cpp
@@ -82,7 +82,7 @@ static void menu_cb_DrawVisibleSound (TimeSoundEditor me, EDITOR_ARGS_FORM) {
 			Melder_throw (U"There is no sound to draw.");
 		autoSound publish = my d_longSound.data ?
 			LongSound_extractPart (my d_longSound.data, my startWindow, my endWindow, my pref_picture_preserveTimes ()) :
-			Sound_extractPart (my d_sound.data, my startWindow, my endWindow, kSound_windowShape_RECTANGULAR, 1.0, my pref_picture_preserveTimes ());
+			Sound_extractPart (my d_sound.data, my startWindow, my endWindow, kSound_windowShape::RECTANGULAR, 1.0, my pref_picture_preserveTimes ());
 		Editor_openPraatPicture (me);
 		Sound_draw (publish.get(), my pictureGraphics, 0.0, 0.0, my pref_picture_bottom (), my pref_picture_top (),
 			my pref_picture_garnish (), U"Curve");
@@ -118,7 +118,7 @@ static void menu_cb_DrawSelectedSound (TimeSoundEditor me, EDITOR_ARGS_FORM) {
 			Melder_throw (U"There is no sound to draw.");
 		autoSound publish = my d_longSound.data ?
 			LongSound_extractPart (my d_longSound.data, my startSelection, my endSelection, my pref_picture_preserveTimes ()) :
-			Sound_extractPart (my d_sound.data, my startSelection, my endSelection, kSound_windowShape_RECTANGULAR, 1.0, my pref_picture_preserveTimes ());
+			Sound_extractPart (my d_sound.data, my startSelection, my endSelection, kSound_windowShape::RECTANGULAR, 1.0, my pref_picture_preserveTimes ());
 		Editor_openPraatPicture (me);
 		Sound_draw (publish.get(), my pictureGraphics, 0.0, 0.0, my pref_picture_bottom (), my pref_picture_top (),
 			my pref_picture_garnish (), U"Curve");
@@ -133,7 +133,7 @@ static void do_ExtractSelectedSound (TimeSoundEditor me, bool preserveTimes) {
 	if (my d_longSound.data) {
 		extract = LongSound_extractPart (my d_longSound.data, my startSelection, my endSelection, preserveTimes);
 	} else if (my d_sound.data) {
-		extract = Sound_extractPart (my d_sound.data, my startSelection, my endSelection, kSound_windowShape_RECTANGULAR, 1.0, preserveTimes);
+		extract = Sound_extractPart (my d_sound.data, my startSelection, my endSelection, kSound_windowShape::RECTANGULAR, 1.0, preserveTimes);
 	}
 	Editor_broadcastPublication (me, extract.move());
 }
@@ -490,7 +490,7 @@ void TimeSoundEditor_drawSound (TimeSoundEditor me, double globalMinimum, double
 	int lastVisibleChannel = my d_sound.channelOffset + numberOfVisibleChannels;
 	if (lastVisibleChannel > nchan) lastVisibleChannel = nchan;
 	double maximumExtent = 0.0, visibleMinimum = 0.0, visibleMaximum = 0.0;
-	if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_BY_WINDOW) {
+	if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW) {
 		if (longSound)
 			LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, firstVisibleChannel, & visibleMinimum, & visibleMaximum);
 		else
@@ -519,7 +519,7 @@ void TimeSoundEditor_drawSound (TimeSoundEditor me, double globalMinimum, double
 		Graphics_Viewport vp = Graphics_insetViewport (my graphics.get(), 0.0, 1.0, ymin, ymax);
 		bool horizontal = false;
 		double minimum = sound ? globalMinimum : -1.0, maximum = sound ? globalMaximum : 1.0;
-		if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_BY_WINDOW) {
+		if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW) {
 			if (nchan > 2) {
 				if (longSound) {
 					LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, ichan, & minimum, & maximum);
@@ -535,13 +535,13 @@ void TimeSoundEditor_drawSound (TimeSoundEditor me, double globalMinimum, double
 				minimum = visibleMinimum;
 				maximum = visibleMaximum;
 			}
-		} else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_BY_WINDOW_AND_CHANNEL) {
+		} else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW_AND_CHANNEL) {
 			if (longSound) {
 				LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, ichan, & minimum, & maximum);
 			} else {
 				Matrix_getWindowExtrema (sound, first, last, ichan, ichan, & minimum, & maximum);
 			}
-		} else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_FIXED_HEIGHT) {
+		} else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::FIXED_HEIGHT) {
 			if (longSound) {
 				LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, ichan, & minimum, & maximum);
 			} else {
@@ -551,7 +551,7 @@ void TimeSoundEditor_drawSound (TimeSoundEditor me, double globalMinimum, double
 			double middle = 0.5 * (minimum + maximum);
 			minimum = middle - 0.5 * channelExtent;
 			maximum = middle + 0.5 * channelExtent;
-		} else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy_FIXED_RANGE) {
+		} else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::FIXED_RANGE) {
 			minimum = my p_sound_scaling_minimum;
 			maximum = my p_sound_scaling_maximum;
 		}
diff --git a/fon/VocalTract_to_Spectrum.cpp b/fon/VocalTract_to_Spectrum.cpp
index 273420a..3b540d3 100644
--- a/fon/VocalTract_to_Spectrum.cpp
+++ b/fon/VocalTract_to_Spectrum.cpp
@@ -1,6 +1,6 @@
 /* VocalTract_to_Spectrum.cpp
  *
- * Copyright (C) 1991-2011,2015 Paul Boersma
+ * Copyright (C) 1991-2011,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -44,29 +44,26 @@ static void TUBE_transfer (double area [], int numberOfSections, double sectionL
 	int section;
 	double omega = 2 * NUMpi * frequency;
 	if (hasInternalDamping) {
-		double bareResistance = sqrt (mu * rho / 2) * sqrt (omega);
-		double bareConductance = (eta - 1) / (rho * cc * cc) *
-			sqrt (lambda / (2 * cp * rho)) * sqrt (omega);
-		dcomplex c = dcomplex_create (glottalDamping * area [1] / (rho * cc), 0.0);
-		dcomplex d = dcomplex_create (1.0, 0.0);
+		double bareResistance = sqrt (mu * rho / 2.0) * sqrt (omega);
+		double bareConductance = (eta - 1.0) / (rho * cc * cc) *
+			sqrt (lambda / (2.0 * cp * rho)) * sqrt (omega);
+		dcomplex c { glottalDamping * area [1] / (rho * cc), 0.0 };
+		dcomplex d { 1.0, 0.0 };
 		for (section = 1; section <= numberOfSections; section ++) {
-			dcomplex help, help1, sinhg, coshg;
 			double a = area [section];
-			double perimeter = shapeFactor * 2 * sqrt (NUMpi * a);
+			double perimeter = shapeFactor * 2.0 * sqrt (NUMpi * a);
 			double perimeter_by_a2 = perimeter / (a * a);
 			double inertance = rho / a;
 			double compliance = a / (rho * cc * cc);
-			dcomplex cascade = dcomplex_create (perimeter_by_a2 * bareResistance,
-				omega * inertance + perimeter_by_a2 * bareResistance);
-			dcomplex parallel = dcomplex_create (perimeter * bareConductance,
-				omega * compliance);
+			dcomplex cascade { perimeter_by_a2 * bareResistance, omega * inertance + perimeter_by_a2 * bareResistance };
+			dcomplex parallel { perimeter * bareConductance, omega * compliance };
 			dcomplex gamma = dcomplex_sqrt (dcomplex_mul (cascade, parallel));
 			dcomplex impedance = dcomplex_div (gamma, parallel);
 			gamma = dcomplex_rmul (sectionLength, gamma);
-			help = dcomplex_rmul (0.5, dcomplex_exp (gamma));
-			help1 = dcomplex_div (dcomplex_create (0.25, 0), help);
-			sinhg = dcomplex_sub (help, help1);
-			coshg = dcomplex_add (help, help1);
+			dcomplex help = dcomplex_rmul (0.5, dcomplex_exp (gamma));
+			dcomplex help1 = dcomplex_div ({ 0.25, 0.0 }, help);
+			dcomplex sinhg = dcomplex_sub (help, help1);
+			dcomplex coshg = dcomplex_add (help, help1);
 			help = dcomplex_add (dcomplex_mul (c, coshg), dcomplex_div (dcomplex_mul (d, sinhg), impedance));
 			d = dcomplex_add (dcomplex_mul (d, coshg), dcomplex_mul (dcomplex_mul (impedance, c), sinhg));
 			c = help;
@@ -74,8 +71,8 @@ static void TUBE_transfer (double area [], int numberOfSections, double sectionL
 		if (hasRadiationDamping) {
 			double ka = omega * sqrt (area [numberOfSections] / NUMpi) / cc;
 			double z = rho * cc / area [numberOfSections];
-			double radiationResistance = z * ka * ka / 2;
-			double radiationReactance = z * 8 * ka / 3 / NUMpi;
+			double radiationResistance = z * ka * ka / 2.0;
+			double radiationReactance = z * 8.0 * ka / 3.0 / NUMpi;
 			*re = d.re + c.re * radiationResistance - c.im * radiationReactance;
 			*im = d.im + c.im * radiationResistance + c.re * radiationReactance;
 		} else { *re = d.re; *im = d.im; };
@@ -102,7 +99,7 @@ static void TUBE_transfer (double area [], int numberOfSections, double sectionL
 		if (hasRadiationDamping) {
 			double ka = omega * sqrt (area [numberOfSections] / NUMpi) / cc;
 			double radiationResistance = ka * ka / 2;
-			double radiationReactance = 8 * ka / 3 / NUMpi;
+			double radiationReactance = 8.0 * ka / 3.0 / NUMpi;
 			*re += c_re * radiationResistance - c_im * radiationReactance;
 			*im += c_re * radiationReactance + c_im * radiationResistance;
 		}
diff --git a/fon/VoiceAnalysis.cpp b/fon/VoiceAnalysis.cpp
index abfbb48..f62b88c 100644
--- a/fon/VoiceAnalysis.cpp
+++ b/fon/VoiceAnalysis.cpp
@@ -273,11 +273,11 @@ void Sound_Pitch_PointProcess_voiceReport (Sound sound, Pitch pitch, PointProces
 			Pitch statistics.
 		*/
 		MelderInfo_writeLine (U"Pitch:");
-		MelderInfo_writeLine (U"   Median pitch: ", Melder_fixed (Pitch_getQuantile (pitch, tmin, tmax, 0.50, kPitch_unit_HERTZ), 3), U" Hz");
-		MelderInfo_writeLine (U"   Mean pitch: ", Melder_fixed (Pitch_getMean (pitch, tmin, tmax, kPitch_unit_HERTZ), 3), U" Hz");
-		MelderInfo_writeLine (U"   Standard deviation: ", Melder_fixed (Pitch_getStandardDeviation (pitch, tmin, tmax, kPitch_unit_HERTZ), 3), U" Hz");
-		MelderInfo_writeLine (U"   Minimum pitch: ", Melder_fixed (Pitch_getMinimum (pitch, tmin, tmax, kPitch_unit_HERTZ, 1), 3), U" Hz");
-		MelderInfo_writeLine (U"   Maximum pitch: ", Melder_fixed (Pitch_getMaximum (pitch, tmin, tmax, kPitch_unit_HERTZ, 1), 3), U" Hz");
+		MelderInfo_writeLine (U"   Median pitch: ", Melder_fixed (Pitch_getQuantile (pitch, tmin, tmax, 0.50, kPitch_unit::HERTZ), 3), U" Hz");
+		MelderInfo_writeLine (U"   Mean pitch: ", Melder_fixed (Pitch_getMean (pitch, tmin, tmax, kPitch_unit::HERTZ), 3), U" Hz");
+		MelderInfo_writeLine (U"   Standard deviation: ", Melder_fixed (Pitch_getStandardDeviation (pitch, tmin, tmax, kPitch_unit::HERTZ), 3), U" Hz");
+		MelderInfo_writeLine (U"   Minimum pitch: ", Melder_fixed (Pitch_getMinimum (pitch, tmin, tmax, kPitch_unit::HERTZ, 1), 3), U" Hz");
+		MelderInfo_writeLine (U"   Maximum pitch: ", Melder_fixed (Pitch_getMaximum (pitch, tmin, tmax, kPitch_unit::HERTZ, 1), 3), U" Hz");
 		/*
 			Pulses statistics.
 		*/
diff --git a/fon/manual_Fon.cpp b/fon/manual_Fon.cpp
index 53d7339..eef55ad 100644
--- a/fon/manual_Fon.cpp
+++ b/fon/manual_Fon.cpp
@@ -1123,47 +1123,6 @@ NORMAL (U"Every cell of the matrix is drawn as a rectangle filled with a grey va
 	"of the cell is small) and black (if the content is large).")
 MAN_END
 
-/*
-if x > 1 and x < 1.5 then self * 1.3 else self fi
-
-
-
-je had gewoon de manual kunnen lezen (Help klikken bij Formula...).
-
->poging 1:
->  for col:=16000 to 24000 do {self[col] := self[col] * 1.3 }
-
-geen lussen in formules. Er is een automatische lus:
-for row := 1 to nrow do for col := 1 to ncol do self := ...
-
->poging 2:
->  col:=16000; for col:=16000 to 24000 do {self[col] := self[col] * 1.3 }
-
-geen toekenningen. Als je het 1000ste element op 8 wilt zetten, kun je doen
-
-Formula... if col=1000 then 8 else self fi
-
-of sneller:
-
-Set value... 1 1000 8
-
-Wat dus wel werkt, maar ERG traag is:
-
-for col = 16000 to 24000
-   value = Get value... 1 col
-   Set value... 1 col value*1.3
-endfor
-
->toen ben ik het wilde weg gaan proberen:
->  col=16000; for col:=16000 to 24000 do {self[col] := self[col] * 1.3 }
-
-de puntkomma betekent einde formule. Deze formule zet alles op 0,
-behalve element 16000, dat op 1 gezet wordt. De expressie is namelijk booleaans:
-"is col gelijk aan 16000"?
-
-Dit staat allemaal in de on-line handleiding, dus je hoeft niet te gokken!
-*/
-
 MAN_BEGIN (U"Matrix: Set value...", U"ppgb", 19980319)
 INTRO (U"A command to change the value of one cell in each selected @Matrix object.")
 ENTRY (U"Settings")
diff --git a/fon/manual_Manual.cpp b/fon/manual_Manual.cpp
index 1cf228a..0bb7394 100644
--- a/fon/manual_Manual.cpp
+++ b/fon/manual_Manual.cpp
@@ -52,7 +52,7 @@ ENTRY (U"Your own manual pages")
 NORMAL (U"To create your own manual pages, create @ManPages text files.")
 MAN_END
 
-MAN_BEGIN (U"ManPages", U"ppgb", 20140421)
+MAN_BEGIN (U"ManPages", U"ppgb", 20170904)
 INTRO (U"You can create a documentation or education system with files that you and others "
 	"can read into Praat (with the @@Read from file...@ command). "
 	"Your files will become a hypertext system very similar to the usual @Manual.")
@@ -183,7 +183,7 @@ NORMAL (U"Please note that the script is enclosed within double quotes. "
 NORMAL (U"If needed, a script like this can create objects in the object list of the manual. "
 	"However, you have to make sure that you remove them after use:")
 CODE (U"<script> 6 3 \"")
-CODE1 (U"Create Sound from formula: \"sineWithNoise\", 1, 0.0, 1.0, 44100, \"1/2*sin(2*pi*377*x)+randomGauss(0,0.1)\"")
+CODE1 (U"Create Sound from formula: \"sineWithNoise\", 1, 0.0, 1.0, 44100, ~1/2*sin(2*pi*377*x)+randomGauss(0,0.1)")
 CODE1 (U"To Spectrogram: 0.005, 5000, 0.002, 20, \"Gaussian\"")
 CODE1 (U"Paint: 0, 0, 0, 0, 100.0, \"yes\", 50.0, 6.0, 0.0, \"yes\"")
 CODE1 (U"plusObject: \"Sound sineWithNoise\"")
diff --git a/fon/manual_Picture.cpp b/fon/manual_Picture.cpp
index dfd4e4d..b9ebcbe 100644
--- a/fon/manual_Picture.cpp
+++ b/fon/manual_Picture.cpp
@@ -1,6 +1,6 @@
 /* manual_Picture.cpp
  *
- * Copyright (C) 1992-2011,2012,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2012,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@ static void drawOneIpaSymbol (Graphics g, double x, double y, const char32 *symb
 	Graphics_rectangle (g, x - 0.5, x + 0.5, y - 0.5, y + 0.5);
 	if (! symbol) return;
 	Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
-	Graphics_setFont (g, kGraphics_font_TIMES);
+	Graphics_setFont (g, kGraphics_font::TIMES);
 	Graphics_setFontSize (g, fontSize * 3 / 2);
 	Graphics_text (g, x, y + 0.25, symbol);
 	while (*symbol) {
@@ -34,7 +34,7 @@ static void drawOneIpaSymbol (Graphics g, double x, double y, const char32 *symb
 		symbol ++;
 	}
 	*p = '\0';   // trailing null byte
-	Graphics_setFont (g, kGraphics_font_HELVETICA);
+	Graphics_setFont (g, kGraphics_font::HELVETICA);
 	Graphics_setFontSize (g, fontSize * 5 / 6);
 	Graphics_text (g, x, y - 0.25, buffer);
 	Graphics_setFontSize (g, fontSize);
diff --git a/fon/manual_Script.cpp b/fon/manual_Script.cpp
index 8143d1a..87377fc 100644
--- a/fon/manual_Script.cpp
+++ b/fon/manual_Script.cpp
@@ -280,7 +280,7 @@ INTRO (U"The commands in the fixed menus of the @@Object window@ (#Praat, #New,
 NORMAL (U"These commands are always clickable (if not hidden) and scriptable (if not added).")
 MAN_END
 
-MAN_BEGIN (U"Formulas", U"ppgb", 20040414)
+MAN_BEGIN (U"Formulas", U"ppgb", 20170916)
 INTRO (U"You can use numeric expressions and string (text) expressions in many places in Praat:")
 LIST_ITEM (U"\\bu in the @calculator in Praat's @Goodies submenu;")
 LIST_ITEM (U"\\bu in the numeric fields of most settings windows;")
@@ -289,38 +289,40 @@ NORMAL (U"For some types of objects (mainly Sound and Matrix), you can also appl
 LIST_ITEM (U"\\bu when you create a Sound or a Matrix from the @@New menu@;")
 LIST_ITEM (U"\\bu when you choose @@Formula...@ from the @Modify menu for a selected object.")
 NORMAL (U"You can read this tutorial sequentially with the help of the \"< 1\" and \"1 >\" buttons.")
-LIST_ITEM (U"1. @@Formulas 1. My first formulas|My first formulas@ (where to use)")
+LIST_ITEM (U"1. @@Formulas 1. My first formulas|My first formulas@")
 LIST_ITEM1 (U"1.1. @@Formulas 1.1. Formulas in the calculator|Formulas in the calculator@")
 LIST_ITEM1 (U"1.2. @@Formulas 1.2. Numeric expressions|Numeric expressions@")
 LIST_ITEM1 (U"1.3. @@Formulas 1.3. String expressions|String expressions@")
-LIST_ITEM1 (U"1.4. @@Formulas 1.4. Representation of numbers|Representation of numbers@")
-LIST_ITEM1 (U"1.5. @@Formulas 1.5. Representation of strings|Representation of strings@")
-LIST_ITEM1 (U"1.6. @@Formulas 1.6. Formulas in settings windows|Formulas in settings windows@")
-LIST_ITEM1 (U"1.7. @@Formulas 1.7. Formulas for creation|Formulas for creation@")
-LIST_ITEM1 (U"1.8. @@Formulas 1.8. Formulas for modification|Formulas for modification@")
-LIST_ITEM1 (U"1.9. @@Formulas 1.9. Formulas in scripts|Formulas in scripts@")
-LIST_ITEM (U"2. @@Formulas 2. Operators|Operators@ (+, -, *, /, \\^ )")
-LIST_ITEM (U"3. @@Formulas 3. Constants|Constants@ (pi, e, undefined)")
-LIST_ITEM (U"4. @@Formulas 4. Mathematical functions|Mathematical functions@")
-LIST_ITEM (U"5. @@Formulas 5. String functions|String functions@")
-LIST_ITEM (U"6. @@Formulas 6. Control structures|Control structures@ (if then else fi, semicolon)")
-LIST_ITEM (U"7. @@Formulas 7. Attributes of objects|Attributes of objects@")
-LIST_ITEM (U"8. @@Formulas 8. Data in objects|Data in objects@")
+LIST_ITEM1 (U"1.4. @@Formulas 1.4. Array expressions|Array expressions@")
+LIST_ITEM1 (U"1.5. @@Formulas 1.5. Formulas in settings windows|Formulas in settings windows@")
+LIST_ITEM1 (U"1.6. @@Formulas 1.6. Formulas for creation|Formulas for creation@")
+LIST_ITEM1 (U"1.7. @@Formulas 1.7. Formulas for modification|Formulas for modification@")
+LIST_ITEM1 (U"1.8. @@Formulas 1.8. Formulas in scripts|Formulas in scripts@")
+LIST_ITEM (U"2. @@Formulas 2. Representations|Representations@")
+LIST_ITEM (U"2.1. @@Formulas 2.1. Representation of numbers|Representation of numbers@")
+LIST_ITEM (U"2.2. @@Formulas 2.2. Representation of strings|Representation of strings@")
+LIST_ITEM (U"2.3. @@Formulas 2.3. Representation of arrays|Representation of arrays@")
+LIST_ITEM (U"3. @@Formulas 3. Operators|Operators@ (+, -, *, /, \\^ )")
+LIST_ITEM (U"4. @@Formulas 4. Constants|Constants@ (pi, e, undefined)")
+LIST_ITEM (U"5. @@Formulas 5. Mathematical functions|Mathematical functions@")
+LIST_ITEM (U"6. @@Formulas 6. String functions|String functions@")
+LIST_ITEM (U"7. @@Formulas 7. Control structures|Control structures@ (if then else fi, semicolon)")
+LIST_ITEM (U"8. @@Formulas 8. Attributes of objects|Attributes of objects@")
+LIST_ITEM (U"9. @@Formulas 9. Data in objects|Data in objects@")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1. My first formulas", U"ppgb", 20040414)
+MAN_BEGIN (U"Formulas 1. My first formulas", U"ppgb", 20170916)
 LIST_ITEM (U"1.1. @@Formulas 1.1. Formulas in the calculator|Formulas in the calculator@")
 LIST_ITEM (U"1.2. @@Formulas 1.2. Numeric expressions|Numeric expressions@")
 LIST_ITEM (U"1.3. @@Formulas 1.3. String expressions|String expressions@")
-LIST_ITEM (U"1.4. @@Formulas 1.4. Representation of numbers|Representation of numbers@")
-LIST_ITEM (U"1.5. @@Formulas 1.5. Representation of strings|Representation of strings@")
-LIST_ITEM (U"1.6. @@Formulas 1.6. Formulas in settings windows|Formulas in settings windows@")
-LIST_ITEM (U"1.7. @@Formulas 1.7. Formulas for creation|Formulas for creation@")
-LIST_ITEM (U"1.8. @@Formulas 1.8. Formulas for modification|Formulas for modification@")
-LIST_ITEM (U"1.9. @@Formulas 1.9. Formulas in scripts|Formulas in scripts@")
+LIST_ITEM (U"1.4. @@Formulas 1.4. Array expressions|Array expressions@")
+LIST_ITEM (U"1.5. @@Formulas 1.5. Formulas in settings windows|Formulas in settings windows@")
+LIST_ITEM (U"1.6. @@Formulas 1.6. Formulas for creation|Formulas for creation@")
+LIST_ITEM (U"1.7. @@Formulas 1.7. Formulas for modification|Formulas for modification@")
+LIST_ITEM (U"1.8. @@Formulas 1.8. Formulas in scripts|Formulas in scripts@")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.1. Formulas in the calculator", U"ppgb", 20050822)
+MAN_BEGIN (U"Formulas 1.1. Formulas in the calculator", U"ppgb", 20170916)
 INTRO (U"To use the Praat @calculator, go to the @@Praat menu@ "
 	"and choose @@Calculator...@ from the @Goodies submenu. Or simply type Command-U anywhere in Praat.")
 ENTRY (U"Calculating numbers")
@@ -333,9 +335,16 @@ NORMAL (U"You can also do text computations. Type the formula")
 CODE (U"\"see\" + \"king\"")
 NORMAL (U"and click OK. The Info window will show the result:")
 CODE (U"seeking")
+ENTRY (U"Calculating arrays")
+NORMAL (U"You can perform computations on vectors (arrays of numbers). Type the formula")
+CODE (U"{ 11, 13, 17 } + 0.5")
+NORMAL (U"and click OK. The Info window will show the result:")
+CODE (U"11.5")
+CODE (U"13.5")
+CODE (U"17.5")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.2. Numeric expressions", U"ppgb", 20050901)
+MAN_BEGIN (U"Formulas 1.2. Numeric expressions", U"ppgb", 20170916)
 INTRO (U"All the formulas whose outcome is a number are called numeric expressions. "
 	"For the following examples, all the outcomes can be checked with the @calculator.")
 ENTRY (U"Examples with numbers")
@@ -356,11 +365,18 @@ TAG (U"##index (\"internationalization\", \"ation\")")
 DEFINITION (U"computes the location of the first occurrence of the string \"ation\" in the string \"internationalization\". Outcome: 7, "
 	"because the first letter of \"ation\" lines up with the seventh letter of \"internationalization\". "
 	"If the substring does not occur, the outcome is 0.")
+ENTRY (U"Examples with arrays")
+NORMAL (U"Some numeric expressions compute numeric properties of numeric vectors:")
+TAG (U"##size ({ 40, 70, 60, 50 })")
+DEFINITION (U"computes the length of the vector { 40, 70, 60, 50 }, i.e. the number of its elements. Outcome: 4.")
+TAG (U"##mean ({ 40, 70, 60, 50 })")
+DEFINITION (U"computes the mean of the four numbers 40, 70, 60 and 50. Outcome: 55.")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.3. String expressions", U"ppgb", 20040414)
-INTRO (U"All the formulas whose outcome is a text string are called string expressions. "
-	"Again, the outcomes of the following examples can be checked with the @calculator.")
+MAN_BEGIN (U"Formulas 1.3. String expressions", U"ppgb", 20170916)
+INTRO (U"All the formulas whose outcome is a text are called string expressions. "
+	"A %string is programming jargon for a text; think of a text as a \"string\" of characters (letters).")
+NORMAL (U"Again, the outcomes of the following examples can be checked with the @calculator.")
 TAG (U"##\"see\" + \"king\"")
 DEFINITION (U"concatenates two strings. Outcome: seeking.")
 TAG (U"##left\\$  (\"internationalization\", 6)")
@@ -371,75 +387,39 @@ TAG (U"##date\\$  ( )")
 DEFINITION (U"computes the current date and time. Outcome at the time I am writing this: Mon Dec  2 02:23:45 2002.")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.4. Representation of numbers", U"ppgb", 20040414)
-INTRO (U"Formulas can work with integer numbers as well as with real numbers.")
-ENTRY (U"Real numbers")
-NORMAL (U"You can type many real numbers by using a decimal notation, for instance 3.14159, 299792.5, or -0.000123456789. "
-	"For very large or small numbers, you can use the %e-notation: 6.022\\.c10^^23^ is typed as 6.022e23 or 6.022e+23, "
-	"and -1.6021917\\.c10^^-19^ is typed as -1.6021917e-19. You can use also use the percent notation: 0.157 "
-	"can be typed as 15.7\\% .")
-NORMAL (U"There are some limitations as to the values that real numbers can have in Praat. "
-	"The numbers must lie between -10^^308^ and +10^^308^. If you type")
-CODE (U"1e200 * 1e100")
-NORMAL (U"the outcome will be")
-CODE (U"1e+300")
-NORMAL (U"but if you type")
-CODE (U"1e300 * 1e100")
-NORMAL (U"the outcome will be")
-CODE (U"--undefined--")
-NORMAL (U"Another limitation is that the smallest non-zero numbers lie near -10^^-308^ and +10^^-308^. If you type")
-CODE (U"1e-200 / 1e100")
-NORMAL (U"the outcome will be")
-CODE (U"1e-300")
-NORMAL (U"but if you type")
-CODE (U"1e-300 / 1e100")
-NORMAL (U"the outcome will be")
+MAN_BEGIN (U"Formulas 1.4. Array expressions", U"ppgb", 20170916)
+INTRO (U"A numeric vector expression is an expression whose value is a numeric vector.")
+NORMAL (U"You can check the outcomes of the following examples with the @calculator.")
+TAG (U"##{ 11, 13, 17 } + 0.5")
+DEFINITION (U"adds 0.5 to each element of a vector with three elements, giving a new vector with three elements. "
+	"Praat writes the outcome to the Info window as three lines, containing the numbers 11.5, 13.5 and 17.5:")
+CODE (U"11.5")
+CODE (U"13.5")
+CODE (U"17.5")
+TAG (U"##zero\\#  (5)")
+DEFINITION (U"creates a vector with 5 zeroes. Praat writes them on five lines:")
 CODE (U"0")
-NORMAL (U"Finally, the precision of real numbers is limited by the number of bits that every real number is stored with "
-	"in the computer, namely 64. For instance, if you type")
-CODE (U"pi")
-NORMAL (U"the outcome will be")
-CODE (U"3.141592653589793")
-NORMAL (U"because only 16 digits of precision are stored. This can lead to unexpected results caused by rounding. "
-	"For instance, the formula")
-CODE (U"0.34999999999999999 - 0.35")
-NORMAL (U"will result in")
 CODE (U"0")
-NORMAL (U"rather than the correct value of 1e-17. This is because the numbers 0.34999999999999999 and 0.35 cannot "
-	"be distinguished in the computer's memory. If you simply type")
-CODE (U"0.34999999999999999")
-NORMAL (U"the outcome will be")
-CODE (U"0.35")
-NORMAL (U"(as in this example, the calculator will always come up with the minimum number of digits needed to represent the number unambiguously).")
-NORMAL (U"Another example of inaccuracy is the formula")
-CODE (U"1 / 7 / 59 * 413")
-NORMAL (U"Because of rounding errors, the result will be")
-CODE (U"0.9999999999999999")
-ENTRY (U"Integer numbers")
-NORMAL (U"Formulas can work with integer (whole) numbers between -1,000,000,000,000,000 and +1,000,000,000,000,000. "
-	"You type them without commas and without the plus sign: 337, -848947328345289.")
-NORMAL (U"You %can work with larger numbers than that (up to 10^^308^), but there will again be rounding errors. "
-	"For instance, the formula")
-CODE (U"1000000000000000 + 1")
-NORMAL (U"correctly yields")
-CODE (U"1000000000000001")
-NORMAL (U"but the formula")
-CODE (U"10000000000000000 + 1")
-NORMAL (U"yields an incorrect outcome:")
-CODE (U"1e16")
-MAN_END
-
-MAN_BEGIN (U"Formulas 1.5. Representation of strings", U"ppgb", 20021203)
-INTRO (U"Formulas can work with strings that are put between two double quotes, "
-	"as in \"goodbye\" or \"how are you doing?\".")
-NORMAL (U"If a string has to contain a double quote, "
-	"you have to type it twice. For instance, if you type")
-CODE (U"\"I asked: \\\" \\\" how are you doing?\\\" \\\" \"")
-NORMAL (U"into the calculator, the outcome will be")
-CODE (U"I asked: \"how are you doing?\"")
+CODE (U"0")
+CODE (U"0")
+CODE (U"0")
+TAG (U"##repeat\\#  ({ 1, 5 }, 6)")
+DEFINITION (U"creates a vector with 12 elements, in which the sequence { 1, 5 } is repeated 6 times. Outcome:")
+CODE (U"1")
+CODE (U"5")
+CODE (U"1")
+CODE (U"5")
+CODE (U"1")
+CODE (U"5")
+CODE (U"1")
+CODE (U"5")
+CODE (U"1")
+CODE (U"5")
+CODE (U"1")
+CODE (U"5")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.6. Formulas in settings windows", U"ppgb", 20070225)
+MAN_BEGIN (U"Formulas 1.5. Formulas in settings windows", U"ppgb", 20170916)
 INTRO (U"Into numeric fields in settings windows you usually simply type a number. "
 	"However, you can use any numeric expression instead.")
 NORMAL (U"For instance, suppose you want to create a Sound that contains exactly 10000 samples. "
@@ -449,10 +429,11 @@ NORMAL (U"For instance, suppose you want to create a Sound that contains exactly
 CODE (U"10000/44100")
 NORMAL (U"into the ##End time# field.")
 NORMAL (U"Into text fields in settings windows, you can only type text directly; there is no way "
-	"to use string expressions (except if you use scripts; see @@Formulas 1.9. Formulas in scripts@).")
+	"to use string expressions (except if you use scripts; see @@Formulas 1.8. Formulas in scripts@).")
+NORMAL (U"Into numeric vector fields in settings windows, you can type any numeric vector (array) expression.")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.7. Formulas for creation", U"ppgb", 20110128)
+MAN_BEGIN (U"Formulas 1.6. Formulas for creation", U"ppgb", 20110128)
 INTRO (U"With some commands in the @@New menu@, you can supply a formula that Praat will apply to all elements of the new object.")
 ENTRY (U"Creating a Sound from a formula")
 NORMAL (U"Choose @@Create Sound from formula...@ and type the following into the #%Formula field:")
@@ -476,7 +457,7 @@ NORMAL (U"For the resulting Matrix, "
 	"distance along the vertical axis; see the following page for examples.")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.8. Formulas for modification", U"ppgb", 20021204)
+MAN_BEGIN (U"Formulas 1.7. Formulas for modification", U"ppgb", 20170916)
 INTRO (U"Analogously to the formulas that you can use for creating new objects (see the previous page), "
 	"you can use formulas for modifying existing objects. You do this with the command ##Formula...# that you "
 	"can find in the @Modify menu when you select an object.")
@@ -515,7 +496,7 @@ TAG (U"#col")
 DEFINITION (U"the current column")
 TAG (U"#x")
 DEFINITION (U"the %x value associated with the current column:")
-DEFINITION (U"    for a Sound, Spectrogram, Cochleagram, or Harmonicity: time")
+DEFINITION (U"    for a Sound, Spectrogram, Cochleagram, or Harmonicity: time, as in the 377-Hz sine wave example above")
 DEFINITION (U"    for a Spectrum: frequency (Hz)")
 DEFINITION (U"    for an Excitation: frequency (Bark)")
 TAG (U"#y")
@@ -536,16 +517,17 @@ TAG (U"##self [%%row-expression%, %%column-expression%]")
 DEFINITION (U"refers to the value in the current Matrix (or Spectrogram etc.) at the specified row and column. "
 	"The expressions are rounded to the nearest integers.")
 NORMAL (U"You can refer to values in the current Matrix (or Spectrogram, etc.) by %x and %y position:")
-TAG (U"\\bu ##self (%%x-expression%, %%y-expression%)")
+TAG (U"##self (%%x-expression%, %%y-expression%)")
 DEFINITION (U"the expressions are linearly interpolated between the four nearest matrix points.")
 NORMAL (U"You can refer to values in the current Sound (or Intensity etc.) by %x position:")
-TAG (U"\\bu ##self (%%x-expression%)")
+TAG (U"##self (%%x-expression%)")
 DEFINITION (U"the expression is linearly interpolated between the two nearest samples (or frames).")
 MAN_END
 
-MAN_BEGIN (U"Formulas 1.9. Formulas in scripts", U"ppgb", 20140223)
+MAN_BEGIN (U"Formulas 1.8. Formulas in scripts", U"ppgb", 20170916)
 INTRO (U"In scripts, you can assign numeric expressions to numeric variables, "
-	"and string expressions to string variables. You can also use numeric and string variables in expressions.")
+	"string expressions to string variables, and array expressions to array variables. "
+	"You can also use numeric, string and array variables in expressions.")
 ENTRY (U"Example: report a square")
 NORMAL (U"Choose @@New Praat script@ from the @@Praat menu at . A script editor window will become visible. "
 	"Type the following lines into that window:")
@@ -570,10 +552,17 @@ NORMAL (U"This script assigns the results of four string expressions to the four
 	"for a function whose result is a string (like ##left\\$ #). Note that the formula in the fourth line refers to three existing "
 	"variables.")
 NORMAL (U"To see what the new name of the capital will be, choose #Run.")
+ENTRY (U"Example: report five squares")
+NORMAL (U"Type the following script:")
+CODE (U"x\\#  = { 1, 2, 3, 4, 5 }")
+CODE (U"x2\\#  = x\\#  * x\\# ")
+CODE (U"writeInfoLine: \"The squares of \", x\\# , \" are \", x2\\# , \".\"")
+NORMAL (U"Praat will then write the following text into the Info window:")
+CODE (U"The squares of 1 2 3 4 5 are 1 4 9 16 25.")
 ENTRY (U"Example: numeric expressions in settings in scripts")
 NORMAL (U"As in real settings windows, you can use numeric expressions in all numeric fields. "
 	"The example of two pages back becomes:")
-CODE (U"Create Sound from formula: \"sine\", \"Mono\", 0, 10000 / 44100, 44100, \"0.9 * sin (2*pi*377*x)\"")
+CODE (U"Create Sound from formula: \"sine\", 1, 0, 10000 / 44100, 44100, ~ 0.9 * sin (2*pi*377*x)")
 ENTRY (U"Example: string expressions in settings in scripts")
 NORMAL (U"As in real settings windows, you can use string expressions in all text fields:")
 CODE (U"soundName\\$  = \"hello\"")
@@ -581,12 +570,94 @@ CODE (U"Read from file: soundName\\$  + \".wav\"")
 ENTRY (U"Example: numeric expressions in creation in scripts")
 NORMAL (U"Suppose you want to generate a sine wave whose frequency is held in a variable. This is the way:")
 CODE (U"frequency = 377")
-CODE (U"Create Sound from formula: \"sine\", \"Mono\", 0, 1, 44100, \"0.9 * sin (2*pi*frequency*x)\"")
+CODE (U"Create Sound from formula: \"sine\", 1, 0.0, 1.0, 44100, ~ 0.9 * sin (2*pi*frequency*x)")
 NORMAL (U"In this example, Praat will protest if %x is a variable as well, because that would be ambiguous "
-	"with the %x that refers to the time in the sound (see @@Formulas 1.8. Formulas for modification@).")
+	"with the %x that refers to the time in the sound (see @@Formulas 1.7. Formulas for modification@).")
 MAN_END
 
-MAN_BEGIN (U"Formulas 2. Operators", U"ppgb", 20060127)
+MAN_BEGIN (U"Formulas 2. Representations", U"ppgb", 20170916)
+LIST_ITEM (U"2.1. @@Formulas 2.1. Representation of numbers|Representation of numbers@")
+LIST_ITEM (U"2.2. @@Formulas 2.2. Representation of strings|Representation of strings@")
+LIST_ITEM (U"2.3. @@Formulas 2.3. Representation of arrays|Representation of arrays@")
+MAN_END
+
+MAN_BEGIN (U"Formulas 2.1. Representation of numbers", U"ppgb", 20170916)
+INTRO (U"Formulas can work with integer numbers as well as with real numbers.")
+ENTRY (U"Real numbers")
+NORMAL (U"You can type many real numbers by using a decimal notation, for instance 3.14159, 299792.5, or -0.000123456789. "
+	"For very large or small numbers, you can use the %e-notation: 6.022\\.c10^^23^ is typed as 6.022e23 or 6.022e+23, "
+	"and -1.6021917\\.c10^^-19^ is typed as -1.6021917e-19. You can use also use the percent notation: 0.157 "
+	"can be typed as 15.7\\% .")
+NORMAL (U"There are some limitations as to the values that real numbers can have in Praat. "
+	"The numbers must lie between -10^^308^ and +10^^308^. If you type")
+CODE (U"1e200 * 1e100")
+NORMAL (U"the outcome will be")
+CODE (U"1e+300")
+NORMAL (U"but if you type")
+CODE (U"1e300 * 1e100")
+NORMAL (U"the outcome will be")
+CODE (U"--undefined--")
+NORMAL (U"Another limitation is that the smallest non-zero numbers lie near -10^^-308^ and +10^^-308^. If you type")
+CODE (U"1e-200 / 1e100")
+NORMAL (U"the outcome will be")
+CODE (U"1e-300")
+NORMAL (U"but if you type")
+CODE (U"1e-300 / 1e100")
+NORMAL (U"the outcome will be")
+CODE (U"0")
+NORMAL (U"Finally, the precision of real numbers is limited by the number of bits that every real number is stored with "
+	"in the computer, namely 64. For instance, if you type")
+CODE (U"pi")
+NORMAL (U"the outcome will be")
+CODE (U"3.141592653589793")
+NORMAL (U"because only 16 digits of precision are stored. This can lead to unexpected results caused by rounding. "
+	"For instance, the formula")
+CODE (U"0.34999999999999999 - 0.35")
+NORMAL (U"will result in")
+CODE (U"0")
+NORMAL (U"rather than the correct value of 1e-17. This is because the numbers 0.34999999999999999 and 0.35 cannot "
+	"be distinguished in the computer's memory. If you simply type")
+CODE (U"0.34999999999999999")
+NORMAL (U"the outcome will be")
+CODE (U"0.35")
+NORMAL (U"(as in this example, the calculator will always come up with the minimum number of digits needed to represent the number unambiguously).")
+NORMAL (U"Another example of inaccuracy is the formula")
+CODE (U"1 / 7 / 59 * 413")
+NORMAL (U"Because of rounding errors, the result will be")
+CODE (U"0.9999999999999999")
+ENTRY (U"Integer numbers")
+NORMAL (U"Formulas can work with integer (whole) numbers between -1,000,000,000,000,000 and +1,000,000,000,000,000. "
+	"You type them without commas and without the plus sign: 337, -848947328345289.")
+NORMAL (U"You %can work with larger numbers than that (up to 10^^308^), but there will again be rounding errors. "
+	"For instance, the formula")
+CODE (U"1000000000000000 + 1")
+NORMAL (U"correctly yields")
+CODE (U"1000000000000001")
+NORMAL (U"but the formula")
+CODE (U"10000000000000000 + 1")
+NORMAL (U"yields an incorrect outcome:")
+CODE (U"1e16")
+MAN_END
+
+MAN_BEGIN (U"Formulas 2.2. Representation of strings", U"ppgb", 20021203)
+INTRO (U"Formulas can work with strings that are put between two double quotes, "
+	"as in \"goodbye\" or \"how are you doing?\".")
+NORMAL (U"If a string has to contain a double quote, "
+	"you have to type it twice. For instance, if you type")
+CODE (U"\"I asked: \\\" \\\" how are you doing?\\\" \\\" \"")
+NORMAL (U"into the calculator, the outcome will be")
+CODE (U"I asked: \"how are you doing?\"")
+MAN_END
+
+MAN_BEGIN (U"Formulas 2.3. Representation of arrays", U"ppgb", 20170916)
+INTRO (U"Formulas can work with vectors that are put between opening and closing braces, "
+	"as in { 11, 17, 13.5 }. If you type this into the calculator, the outcome will be")
+CODE (U"11")
+CODE (U"17")
+CODE (U"13.5")
+MAN_END
+
+MAN_BEGIN (U"Formulas 3. Operators", U"ppgb", 20170913)
 NORMAL (U"In formulas you can use the numerical and logical operators that are described on this page. "
 	"The order of evaluation of the operators is the order that is most usual in programming languages. "
 	"To force a different order, you use parentheses.")
@@ -662,21 +733,21 @@ NORMAL (U"The operators of lowest precedence are the #logical operators (#not, #
 	"highest precedence and #or the lowest:")
 CODE (U"not 5 + 6 = 10 \\-> 1")
 CODE (U"x > 5 and x < 10               (is x between 5 and 10?)")
-CODE (U"not x <= 5 and not x >= 10     (same as previous line)")
-CODE (U"not (x <= 5 or x >= 10)        (same as previous line)")
+CODE (U"not x <= 5 and not x >= 10     (means the same as the previous line)")
+CODE (U"not (x <= 5 or x >= 10)        (means the same as the previous line, unless x is undefined)")
 ENTRY (U"String comparison")
 TAG (U"##a\\$  = b\\$ ")
 DEFINITION (U"gives the value %true (= 1) if the strings are equal, and %false (= 0) otherwise.")
 TAG (U"##a\\$  <> b\\$ ")
 DEFINITION (U"gives the value %true if the strings are unequal, and %false otherwise.")
 TAG (U"##a\\$  < b\\$ ")
-DEFINITION (U"gives %true if the string %%a\\$ % precedes the string %%b\\$ % in ASCII sorting order. "
+DEFINITION (U"gives %true if the string %%a\\$ % precedes the string %%b\\$ % in Unicode sorting order. "
 	"Thus, \"ha\" < \"hal\" and \"ha\" < \"ja\" are true, but \"ha\" < \"JA\" is false, "
-	"because all capitals precede all lower-case characters in the ASCII sorting order.")
+	"because all capitals precede all lower-case characters in the Unicode sorting order.")
 TAG (U"##a\\$  > b\\$ ")
-DEFINITION (U"%true if %%a\\$ % comes after %%b\\$ % in ASCII sorting order. ")
+DEFINITION (U"%true if %%a\\$ % comes after %%b\\$ % in Unicode sorting order. ")
 TAG (U"##a\\$  <= b\\$ ")
-DEFINITION (U"gives the value %true if the string %%a\\$ % precedes the string %%b\\$ % in ASCII sorting order, "
+DEFINITION (U"gives the value %true if the string %%a\\$ % precedes the string %%b\\$ % in Unicode sorting order, "
 	"or if the strings are equal.")
 TAG (U"##a\\$  >= b\\$ ")
 DEFINITION (U"%true if %%a\\$ % comes after %%b\\$ % or the two are equal.")
@@ -693,7 +764,7 @@ DEFINITION (U"the variable %%textgridFileName\\$ % contains the string \"hallo.T
 	"If the first string %%a\\$ % does not end in the string %%b\\$ %, the result of the subtraction is the string %%a\\$ %.")
 MAN_END
 
-MAN_BEGIN (U"Formulas 3. Constants", U"ppgb", 20080318)
+MAN_BEGIN (U"Formulas 4. Constants", U"ppgb", 20080318)
 TAG (U"##pi")
 DEFINITION (U"%\\pi, 3.14159265358979323846264338328")
 TAG (U"##e")
@@ -702,7 +773,7 @@ TAG (U"##undefined")
 DEFINITION (U"a special value, see @undefined")
 MAN_END
 
-MAN_BEGIN (U"Formulas 4. Mathematical functions", U"ppgb", 20170718)
+MAN_BEGIN (U"Formulas 5. Mathematical functions", U"ppgb", 20170718)
 TAG (U"##abs (%x)")
 DEFINITION (U"absolute value")
 TAG (U"##round (%x)")
@@ -841,9 +912,10 @@ DEFINITION (U"the inverse of the previous: ln (1 + %x / 30) / ln (61 / 60).")
 TAG (U"##beta (%x, %y)")
 TAG (U"##besselI (%n, %x)")
 TAG (U"##besselK (%n, %x)")
+NORMAL (U"For functions with arrays, see @@Scripting 5.7. Vectors and matrices at .")
 MAN_END
 
-MAN_BEGIN (U"Formulas 5. String functions", U"ppgb", 20140223)
+MAN_BEGIN (U"Formulas 6. String functions", U"ppgb", 20140223)
 INTRO (U"String functions are functions that either return a text string or have at least one text string as an argument. "
 	"Since string computations are not very useful in the @calculator, in settings windows, or in creation and "
 	"modification formulas, this page only gives examples of strings in scripts, so that the example may contain "
@@ -946,7 +1018,7 @@ TAG (U"##backslashTrigraphsToUnicode\\$  (x\\$ ), unicodeToBackslashTrigraphs\\$
 DEFINITION (U"converts e.g. \\bsct to \\ct or the reverse. See @@Special symbols at .")
 MAN_END
 
-MAN_BEGIN (U"Formulas 6. Control structures", U"ppgb", 20030519)
+MAN_BEGIN (U"Formulas 7. Control structures", U"ppgb", 20030519)
 ENTRY (U"if ... then ... else ... fi")
 NORMAL (U"You can use conditional expressions in all formulas. For example, ")
 CODE (U"3 * if 52\\%  * 3809 > 2000 then 5 else 6 fi")
@@ -960,7 +1032,7 @@ CODE (U"800;sqrt(2)*sin(2*pi*103*0.5)+10\\^ (-40/20)*randomGauss(0,1)")
 NORMAL (U"evaluates to 800.")
 MAN_END
 
-MAN_BEGIN (U"Formulas 7. Attributes of objects", U"ppgb", 20170614)
+MAN_BEGIN (U"Formulas 8. Attributes of objects", U"ppgb", 20170614)
 NORMAL (U"You can refer to several attributes of objects that are visible in the @@List of Objects at . "
 	"To do so, use either the unique ID of the object, or the type and the name of the object. "
 	"Thus, $$object[113]$ refers to the object that has the number 113 in the list, "
@@ -1054,7 +1126,7 @@ NORMAL (U"causes the sound to decay exponentially in such a way that it has only
 NORMAL (U"More examples of the use of attributes are on the next page.")
 MAN_END
 
-MAN_BEGIN (U"Formulas 8. Data in objects", U"ppgb", 20170614)
+MAN_BEGIN (U"Formulas 9. Data in objects", U"ppgb", 20170614)
 NORMAL (U"With square brackets, you can get the values inside some objects.")
 ENTRY (U"Object contents in the calculator")
 NORMAL (U"The outcomes of the following examples can be checked with the @calculator.")
@@ -1490,7 +1562,7 @@ NORMAL (U"It advisable to use$$ .praat $as the extension for script file names.
 	"On the Mac and on Windows, if you drag a$$ .praat $file on the Praat icon, Praat will also start up and show the script.")
 MAN_END
 
-MAN_BEGIN (U"Scripting 2. How to script settings windows", U"ppgb", 20140119)
+MAN_BEGIN (U"Scripting 2. How to script settings windows", U"ppgb", 20170828)
 INTRO (U"Not all menu commands are as simple as those on the @@Scripting 1. Your first scripts|previous page@, "
 	"which act immediately once you choose them from a menu (e.g. ##Play#, ##Erase all#). "
 	"Most commands in Praat require the user to supply additional information; "
@@ -1566,7 +1638,28 @@ NORMAL (U"In a script this would look like:")
 CODE (U"Create Sound from formula: \"sine\", 1, 0.0, 1.0, 44100, \"1/2 * sin(2*pi*377*x)\"")
 NORMAL (U"Both the first argument (#Name) and the sixth argument (#Formula) are %%text arguments%. "
 	"In a script they are written within quotes.")
-ENTRY (U"5. File arguments")
+ENTRY (U"5. Formula arguments")
+NORMAL (U"Sometimes it would be a bit awkward to write a formula that contains double quotes. "
+	"Imagine you have a @Table object with a column \"stimulus\", a column \"response\", and a column \"correct\", "
+	"and you want to have a 1 in column \"correct\" if the texts in the columns \"stimulus\" and \"response\" "
+	"are the same, and a 0 otherwise. You would do this by selecting the Table and choosing the ##Formula...# command:")
+SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (2.6), U""
+	Manual_DRAW_SETTINGS_WINDOW ("Table: Formula", 2.6)   // 0.6 extra for the text
+	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Column (label)", "correct")
+	Manual_DRAW_SETTINGS_WINDOW_TEXT ("Formula", "self\\$  [\\\" response\\\" ] = self\\$  [\\\" stimulus\\\" ]")
+)
+NORMAL (U"According to section 4 above, and according to @@Formulas 2.2. Representation of strings@, "
+	"you would have to write this in the following way in a script:")
+CODE (U"Formula: \"correct\", \"self\\$  [\"\"response\"\"] = self\\$  [\"\"stimulus\"\"]\"")
+NORMAL (U"The required doubling of string-internal double quotes is awkward. "
+	"Therefore, there exists a special way for typing formula arguments, namely with the tilde (\"~\"):")
+CODE (U"Formula: \"correct\", ~ self\\$  [\"response\"] = self\\$  [\"stimulus\"]")
+NORMAL (U"This means that you can write the example of section 4 in an analogous way:")
+CODE (U"Create Sound from formula: \"sine\", 1, 0.0, 1.0, 44100, ~ 1/2 * sin(2*pi*377*x)")
+NORMAL (U"The tilde is probably the preferred way to write formula arguments. "
+	"You should remember, however, that the result is still a string, "
+	"and you can treat it as a string with the string methods described later on in this tutorial.")
+ENTRY (U"6. File arguments")
 NORMAL (U"The commands from the Open and Save menus, and several other commands whose names "
 	"start with #Read, #Open, or #Save, present a %%file selector window% instead of a typical Praat "
 	"settings window. File selector windows ask the user to supply a single argument: the file name.")
@@ -1584,7 +1677,8 @@ NORMAL (U"(the part before your user name may be slightly different on your comp
 	"use your command or terminal window to find out)")
 NORMAL (U"In these examples, \"C\" is the Windows %%drive letter% and "
 	"##/Users/Miep# or ##/home/Miep# is your %%home directory%. Both the home directory and the drive letter "
-	"can be abbreviated away by using the tilde (\"~\"):")
+	"can be abbreviated away by using the tilde (\"~\") in the path "
+	"(this tilde has nothing to do with the tilde used in formula arguments):")
 CODE (U"Read from file: \"~/Sounds/Animals/miauw.wav\"")
 NORMAL (U"If your #Sounds folder is not in your home directory but on your desktop, you do")
 CODE (U"Read from file: \"~/Desktop/Sounds/Animals/miauw.wav\"")
@@ -1608,7 +1702,7 @@ CODE (U"Read from file: \"../Animals/miauw.aifc\"")
 NORMAL (U"where \"..\" is the general way on all platforms to go one folder up in the hierarchy.")
 NORMAL (U"Note that on Windows you could use the backslash (\"\\bs\") instead of the forward slash (\"/\"), "
 	"but with the forward slash your script will work on all three platforms.")
-ENTRY (U"6. How to supply arguments automatically")
+ENTRY (U"7. How to supply arguments automatically")
 NORMAL (U"Now you know all the ways to write the arguments of commands in a script line. "
 	"If you dislike manually copying arguments from settings windows into your script, "
 	"or if you are not sure whether something is a numeric or a string argument, "
@@ -2017,7 +2111,7 @@ CODE1 (U"appendInfoLine: \"Interval \", intervalNumber, \" is \", duration, \" s
 CODE (U"endfor")
 MAN_END
 
-MAN_BEGIN (U"Scripting 3.7. Layout", U"ppgb", 20140111)
+MAN_BEGIN (U"Scripting 3.7. Layout", U"ppgb", 20170904)
 INTRO (U"This chapter handles the way you use white space, comments, and continuation lines in a Praat script.")
 ENTRY (U"White space")
 NORMAL (U"Praat ignores all white space (spaces and tabs) that you put at the beginning of lines. The indentation "
@@ -2039,7 +2133,7 @@ ENTRY (U"Comments")
 NORMAL (U"Comments are lines that start with \"\\# \" or \";\". Praat ignores these lines when your script is running:")
 CODE (U"\\#  Create 1 second of a sine wave with a frequency of 100 Hertz,")
 CODE (U"\\#  sampled at 44100 Hz:")
-CODE (U"Create Sound from formula: \"sine\", 1, 0, 1, 44100, \"sin (2*pi*100*x)\"")
+CODE (U"Create Sound from formula: \"sine\", 1, 0, 1, 44100, ~ sin (2*pi*100*x)")
 NORMAL (U"Because of its visibility, you are advised to use \"\\# \" for comments that structure your script, "
 	"and \";\" perhaps only for \"commenting out\" a statement, i.e. to temporarily put it before a line "
 	"that you don't want to execute.")
@@ -2053,7 +2147,7 @@ CODE1 (U"appendInfoLine: \"Interval \", intervalNumber, \" is \", duration, \" s
 CODE1 (U"... and contains the text: \", text\\$ ")
 NORMAL (U"Here is another common type of example:")
 CODE (U"Create Sound from formula: \"windowedSine\", 1, 0, 1, 44100,")
-CODE (U"... \"0.5 * sin(2*pi*1000*x) * exp(-0.5*((x-0.5)/0.1)\\^ 2)\"")
+CODE (U"... ~ 0.5 * sin(2*pi*1000*x) * exp(-0.5*((x-0.5)/0.1)\\^ 2)")
 NORMAL (U"You will normally want to follow such an ellipsis with a space, unless you want to concatenate "
 	"the parts of a long word:")
 CODE (U"Select outer viewport: 0, 10, 0, 4")
@@ -2412,7 +2506,7 @@ NORMAL (U"If the expression evaluates to zero or %false to begin with, the state
 	"are not executed even once.")
 MAN_END
 
-MAN_BEGIN (U"Scripting 5.5. Procedures", U"ppgb", 20140126)
+MAN_BEGIN (U"Scripting 5.5. Procedures", U"ppgb", 20170904)
 NORMAL (U"Sometimes in a Praat script, you will want to perform the same thing more than once. "
 	"In @@Scripting 5.4. Loops|\\SS5.4@ we saw how %loops can help there. "
 	"In this section we will see how %procedures (also called %subroutines) can help us.")
@@ -2555,11 +2649,11 @@ CODE (U"\\@ playOctave: 400")
 CODE (U"\\@ playOctave: 500")
 CODE (U"#writeInfoLine: frequency")
 CODE (U"#procedure playOctave: frequency")
-	CODE1 (U"Create Sound from formula: \"note\", 1, 0, 0.3, 44100, frequency, 0.2, 0.01, 0.01")
+	CODE1 (U"Create Sound as pure tone: \"note\", 1, 0, 0.3, 44100, frequency, 0.2, 0.01, 0.01")
 	CODE1 (U"Play")
 	CODE1 (U"Remove")
 	CODE1 (U"octaveHigher = 2 * frequency")
-	CODE1 (U"Create Sound from formula: \"note\", 1, 0, 0.3, 44100, octaveHigher, 0.2, 0.01, 0.01")
+	CODE1 (U"Create Sound as pure tone: \"note\", 1, 0, 0.3, 44100, octaveHigher, 0.2, 0.01, 0.01")
 	CODE1 (U"Play")
 	CODE1 (U"Remove")
 CODE (U"#endproc")
@@ -2678,7 +2772,7 @@ NORMAL (U"You can use any number of array and dictionary variables in a script,
 	"or to use Matrix or Sound objects.")
 MAN_END
 
-MAN_BEGIN (U"Scripting 5.7. Vectors and matrices", U"ppgb", 20170821)
+MAN_BEGIN (U"Scripting 5.7. Vectors and matrices", U"ppgb", 20170916)
 ENTRY (U"1. What is a vector?")
 NORMAL (U"A ##numeric vector# is an array of numbers, regarded as a single object. "
 	"For instance, the squares of the first five integers can be collected in the vector { 1, 4, 9, 16, 25 }. "
@@ -2753,11 +2847,11 @@ ENTRY (U"4. Converting vectors to vectors")
 CODE (U"a\\#  = squares\\#  + 5   ; adding a number to each element of a vector")
 NORMAL (U"causes a\\#  to become the vector { 6, 9, 14, 21, 30 }.")
 CODE (U"b\\#  = a\\#  + { 3.14, 2.72, 3.16, -1, 7.5 }   ; adding two vectors of the same length")
-NORMAL (U"causes b\\#  to become the vector { 9.14, 16.72, 17.16, 20, 37.5 }.")
+NORMAL (U"causes b\\#  to become the vector { 9.14, 11.72, 17.16, 20, 37.5 }.")
 CODE (U"c\\#  = b\\#  / 2   ; dividing each element of a vector")
-NORMAL (U"causes c\\#  to become the vector { 4.57, 8.36, 8.58, 10, 18.75 }.")
+NORMAL (U"causes c\\#  to become the vector { 4.57, 5.86, 8.58, 10, 18.75 }.")
 CODE (U"d\\#  = b\\#  * c\\#    ; elementwise multiplication")
-NORMAL (U"causes d\\#  to become the vector { xx, 8.36, 8.58, 10, 18.75 }.")
+NORMAL (U"causes d\\#  to become the vector { 41.7698, 68.6792, 147.2328, 200, 703.125 }.")
 NORMAL (U"A vector can also be given to a ##menu command# that returns another vector. "
 	"For instance, to get a vector representing the pitch frequencies at 0.01-second intervals in a Pitch object, "
 	"you can do")
@@ -3836,7 +3930,7 @@ LIST_ITEM (U"3. Click #OK and ensure that the button is clickable if you select
 	"to remove it from the dynamic menus, use the @ButtonEditor.")
 MAN_END
 
-MAN_BEGIN (U"undefined", U"ppgb", 20140112)
+MAN_BEGIN (U"undefined", U"ppgb", 20170910)
 INTRO (U"When you give a query command for a numeric value, Praat sometimes writes the numeric value ##--undefined--# "
 	"into the @@Info window@ (two hyphens at both sides of the word). This happens if the value you ask for is not defined, "
 	"as in the following examples:")
@@ -3858,7 +3952,8 @@ CODE (U"endif")
 ENTRY (U"Details for hackers")
 NORMAL (U"In text files, this value is written as ##--undefined--#. "
 	"In binary files, it is written as a big-endian IEEE positive infinity. "
-	"In memory, it is the ANSI-C constant HUGE_VAL, which equals infinity on IEEE machines.")
+	"In memory, it is usually a specific \"not-a-number\" (NaN), namely the result of dividing 0 by 0, "
+	"although other NaNs, and also infinities, will equally be reported as --undefined--.")
 MAN_END
 
 MAN_BEGIN (U"Scripting examples", U"ppgb", 20040222)
@@ -3952,12 +4047,12 @@ NORMAL (U"and add lines in the following way:")
 	CODE1 (U"... fixed\\$  (stdev, 2)")
 MAN_END
 
-MAN_BEGIN (U"Script for creating a frequency sweep", U"ppgb", 20140107)
+MAN_BEGIN (U"Script for creating a frequency sweep", U"ppgb", 20170904)
 INTRO (U"\"I have to find a formula for a sinewave that sweeps from 1 kHz to 12 kHz in "
 	"60 seconds while ramping the amplitude from 1 to 12 volts in the same amount of time.\"")
 NORMAL (U"The absolute amplitude in volts cannot be handled, of course, but linear crescendo is easy:")
 CODE (U"Create Sound from formula: \"sweep\", 1, 0, 60, 44100,")
-CODE (U"... \"0.05 * (1 + 11 * x/60) * sin (2*pi * (1000 + 11000/2 * x/60) * x)\"")
+CODE (U"... ~ 0.05 * (1 + 11 * x/60) * sin (2*pi * (1000 + 11000/2 * x/60) * x)")
 NORMAL (U"Note the \"/2\" in this formula. Here is the derivation of the formula:")
 FORMULA (U"%frequency (%t) = 1000 + 11000 %t / 60")
 FORMULA (U"%phase (%t) = \\in %frequency (%t) %dt = 1000 %t + 11000 (%t^2/2) / 60")
diff --git a/fon/manual_annotation.cpp b/fon/manual_annotation.cpp
index f11b00b..019906f 100644
--- a/fon/manual_annotation.cpp
+++ b/fon/manual_annotation.cpp
@@ -281,7 +281,7 @@ NORMAL (U"You can check the spelling of the intervals in your tiers by including
 	"which will search for the next word in the tier or interval that does not occur in the lexicon.")
 MAN_END
 
-MAN_BEGIN (U"WordList", U"ppgb", 20140421)
+MAN_BEGIN (U"WordList", U"ppgb", 20170913)
 INTRO (U"One of the @@types of objects@ in Praat. "
 	"An object of class WordList contains a sorted list of strings in a system-independent format. "
 	"WordList objects can be used for spelling checking after conversion to a @SpellingChecker object.")
@@ -362,7 +362,7 @@ CODE (U"cookie")
 CODE (U"cookies")
 CODE (U"cooking")
 CODE (U"cooks")
-NORMAL (U"The strings are now in the ASCII order, in which capitals come before lower-case letters, "
+NORMAL (U"The strings are now in the Unicode sorting order, in which capitals come before lower-case letters, "
 	"and backslashes come in between these two series.")
 NORMAL (U"Clicking ##To WordList# now succeeds, and a WordList object appears in the list. "
 	"If you save it to a text file (with the Save menu), you will get the following file:")
diff --git a/fon/manual_glossary.cpp b/fon/manual_glossary.cpp
index 85ecf14..65cb0c7 100644
--- a/fon/manual_glossary.cpp
+++ b/fon/manual_glossary.cpp
@@ -1,6 +1,6 @@
 /* manual_glossary.cpp
  *
- * Copyright (C) 1992-2008,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2008,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@ static void draw_TimeDomain_Sound (Graphics g) {
 	Sound_draw (manual_exampleSound (), g, 0, 0, 0, 0, true, U"curve");
 }
 static void draw_TimeDomain_Pitch (Graphics g) {
-	Pitch_draw (manual_examplePitch (), g, 0, 0, 200.0, 500.0, true, Pitch_speckle_NO, kPitch_unit_HERTZ);
+	Pitch_draw (manual_examplePitch (), g, 0, 0, 200.0, 500.0, true, Pitch_speckle_NO, kPitch_unit::HERTZ);
 }
 
 void manual_glossary_init (ManPages me);
diff --git a/fon/manual_sound.cpp b/fon/manual_sound.cpp
index 154aee7..40d4cbf 100644
--- a/fon/manual_sound.cpp
+++ b/fon/manual_sound.cpp
@@ -19,6 +19,7 @@
 #include "ManPagesM.h"
 
 #include "Sound.h"
+#include "UnicodeData.h"
 
 static void draw_SoundDeepen_filter (Graphics g) {
 	try {
@@ -108,7 +109,7 @@ Create Sound from formula... 'Naam' Mono begintijd eindtijd samplefrequentie
 */
 MAN_END
 
-MAN_BEGIN (U"Create Sound as tone complex...", U"ppgb", 20161013)
+MAN_BEGIN (U"Create Sound as tone complex...", U"ppgb", 20170828)
 INTRO (U"A command in the @@New menu@ to create a @Sound as the sum of a number of sine waves "
 	"with equidistant frequencies.")
 ENTRY (U"Settings")
@@ -173,13 +174,13 @@ CODE (U"form Add waves with decreasing amplitudes")
 CODE1 (U"natural Number_of_components 19")
 CODE (U"endform")
 CODE (U"\\#  Create a Matrix with frequency and amplitude information in each row:")
-CODE (U"Create simple Matrix: \"freqAndGain\", number_of_components, 2, \"0\"")
-CODE (U"Formula: \"if col = 1 then row * 100 + 5 else 1 / row fi\"")
+CODE (U"Create simple Matrix: \"freqAndGain\", number_of_components, 2, ~ 0")
+CODE (U"Formula: ~ if col = 1 then row * 100 + 5 else 1 / row fi")
 CODE (U"\\#  Create a large Matrix with all the component sine waves:")
-CODE (U"Create Matrix: \"components\", 0, 1, 10000, 1e-4, 0.5e-4, 1, number_of_components, number_of_components, 1, 1, \"0\"")
-CODE (U"Formula: \"Matrix_freqAndGain [2] * sin (2 * pi * Matrix_freqAndGain [1] * x)\"")
+CODE (U"Create Matrix: \"components\", 0, 1, 10000, 1e-4, 0.5e-4, 1, number_of_components, number_of_components, 1, 1, ~ 0")
+CODE (U"Formula: ~ object [\"Matrix freqAndGain\", 2] * sin (2 * pi * object [\"Matrix freqAndGain\", 1] * x)\"")
 CODE (U"\\#  Integrate:")
-CODE (U"Formula: \"self + self [row - 1, col]\"")
+CODE (U"Formula: ~ self + self [row - 1, col]")
 CODE (U"\\#  Publish last row:")
 CODE (U"To Sound (slice): number_of_components")
 CODE (U"Scale amplitudes: 0.99")
@@ -995,7 +996,7 @@ CODE (U"Set value at sample number: 100, 1/2")
 NORMAL (U"This sets the value of the 100th sample to 0.5.")
 MAN_END
 
-MAN_BEGIN (U"SoundEditor", U"ppgb", 20100404)
+MAN_BEGIN (U"SoundEditor", U"ppgb", 20170909)
 INTRO (U"An @@Editors|Editor@ for viewing and editing a @Sound object. "
 	"Most of the functions of this editor are described in the @Intro.")
 ENTRY (U"The markers")
@@ -1008,6 +1009,7 @@ ENTRY (U"Playing")
 NORMAL (U"To play any part of the sound, click on one of the rectangles "
 	"below or above the sound window (there can be 1 to 8 of these rectangles), "
 	"or choose a Play command from the View menu.")
+NORMAL (U"For multi-channel sounds you can mute one or more channels by a Ctrl-click on the " UNITEXT_SPEAKER " icon at the right side of the corresponding channel number. The icon will turn to " UNITEXT_SPEAKER_WITH_CANCELLATION_STROKE ". In subsequent playing actions the channel will not be played. Another Ctrl-click on a muted channel icon will activate the channel again. ")
 ENTRY (U"Publishing")
 NORMAL (U"To perform analyses on the selection, or save it to a file, "
 	"create an independent Sound as a copy of the selection, "
@@ -1091,7 +1093,7 @@ ENTRY (U"See also")
 NORMAL (U"If you want the sounds to fade into each other smoothly, choose @@Sounds: Concatenate with overlap...@ instead.")
 MAN_END
 
-MAN_BEGIN (U"Sounds: Concatenate with overlap...", U"ppgb", 20110211)
+MAN_BEGIN (U"Sounds: Concatenate with overlap...", U"ppgb", 20170904)
 INTRO (U"A command to concatenate all selected @Sound objects into a single large Sound, with smooth cross-fading between the sounds.")
 NORMAL (U"All sounds must have equal sampling frequencies and equal numbers of channels. "
 	"They are concatenated in the order in which they appear in the list of objects (not in the order in which you select them; remember: What You See Is What You Get).")
@@ -1107,12 +1109,12 @@ ENTRY (U"Procedure")
 NORMAL (U"Suppose we start with the following two sounds. They are both 0.1 seconds long. "
 	"The first sound is a sine wave with a frequency of 100 Hz, the second a sine wave with a frequency of 230 Hz:")
 SCRIPT (5.0, 3, U""
-	"Create Sound from formula... sine100 1 0 0.1 10000 0.9*sin(2*pi*100*x)\n"
-	"Draw... 0 0 -1 1 yes Curve\n"
+	"Create Sound from formula: \"sine100\", 1, 0, 0.1, 10000, ~ 0.9*sin(2*pi*100*x)\n"
+	"Draw: 0, 0, -1, 1, \"yes\", \"Curve\"\n"
 	"Remove")
 SCRIPT (5.0, 3, U""
-	"Create Sound from formula... sine230 1 0 0.1 10000 0.9*sin(2*pi*230*x)\n"
-	"Draw... 0 0 -1 1 yes Curve\n"
+	"Create Sound from formula: \"sine230\", 1, 0, 0.1, 10000, ~ 0.9*sin(2*pi*230*x)\n"
+	"Draw: 0, 0, -1, 1, \"yes\", \"Curve\"\n"
 	"Remove")
 NORMAL (U"If the overlap time is 0.01 seconds, the concatenation of these two sounds will produce a Sound with a duration of 0.19 seconds, "
 	"which is the sum of the durations of the two sounds, minus the overlap time.")
@@ -1120,38 +1122,38 @@ NORMAL (U"The concatenation works in the following way. "
 	"The last 0.01 seconds of the first sound is multiplied by a falling raised cosine (the second half of a Hann window, see the first red curve), "
 	"and the first 0.01 seconds of the second sound is multiplied by a rising raised cosine (the first half of a Hann window, see the second red curve):")
 SCRIPT (6.7, 5, U""
-	"Create Sound from formula... sine 1 0 0.1 10000 0.9\n"
-	"Formula (part)... 0.09 0.1 1 1 self*(0.5-0.5*cos(pi*(xmax-x)/0.01))\n"
-	"Select inner viewport... 0.5 3.5 0.5 2.5\n"
+	"Create Sound from formula: \"sine\", 1, 0, 0.1, 10000, ~ 0.9\n"
+	"Formula (part): 0.09, 0.1, 1, 1, ~ self*(0.5-0.5*cos(pi*(xmax-x)/0.01))\n"
+	"Select inner viewport: 0.5, 3.5, 0.5, 2.5\n"
 	"Red\n"
-	"Draw... 0 0 -1 1 no Curve\n"
-	"Formula... self*sin(2*pi*100*x)\n"
+	"Draw: 0, 0, -1, 1, \"no\", \"Curve\"\n"
+	"Formula: ~ self*sin(2*pi*100*x)\n"
 	"Black\n"
-	"Draw... 0 0 -1 1 no Curve\n"
+	"Draw: 0, 0, -1, 1, \"no\", \"Curve\"\n"
 	"Draw inner box\n"
-	"One mark top... 0 yes yes no\n"
-	"One mark top... 0.09 yes yes yes\n"
-	"One mark top... 0.1 yes yes no\n"
-	"Text top... no Time (s)\n"
-	"One mark left... -1 yes yes no\n"
-	"One mark left... 0 yes yes yes\n"
-	"One mark left... 1 yes yes no\n"
-	"Formula... 0.9\n"
-	"Formula (part)... 0 0.01 1 1 self*(0.5-0.5*cos(pi*x/0.01))\n"
-	"Select inner viewport... 3.2 6.2 2.5 4.5\n"
+	"One mark top: 0, \"yes\", \"yes\", \"no\", \"\"\n"
+	"One mark top: 0.09, \"yes\", \"yes\", \"yes\", \"\"\n"
+	"One mark top: 0.1, \"yes\", \"yes\", \"no\", \"\"\n"
+	"Text top: \"no\", \"Time (s)\"\n"
+	"One mark left: -1, \"yes\", \"yes\", \"no\", \"\"\n"
+	"One mark left: 0, \"yes\", \"yes\", \"yes\", \"\"\n"
+	"One mark left: 1, \"yes\", \"yes\", \"no\", \"\"\n"
+	"Formula: ~ 0.9\n"
+	"Formula (part): 0, 0.01, 1, 1, ~ self*(0.5-0.5*cos(pi*x/0.01))\n"
+	"Select inner viewport: 3.2, 6.2, 2.5, 4.5\n"
 	"Red\n"
-	"Draw... 0 0 -1 1 no Curve\n"
-	"Formula... self*sin(2*pi*230*x)\n"
+	"Draw: 0, 0, -1, 1, \"no\", \"Curve\"\n"
+	"Formula: ~ self*sin(2*pi*230*x)\n"
 	"Black\n"
-	"Draw... 0 0 -1 1 no Curve\n"
+	"Draw: 0, 0, -1, 1, \"no\", \"Curve\"\n"
 	"Draw inner box\n"
-	"One mark bottom... 0 yes yes no\n"
-	"One mark bottom... 0.01 yes yes yes\n"
-	"One mark bottom... 0.1 yes yes no\n"
-	"Text bottom... no Time (s)\n"
-	"One mark right... -1 yes yes no\n"
-	"One mark right... 0 yes yes yes\n"
-	"One mark right... 1 yes yes no\n"
+	"One mark bottom: 0, \"yes\", \"yes\", \"no\", \"\"\n"
+	"One mark bottom: 0.01, \"yes\", \"yes\", \"yes\", \"\"\n"
+	"One mark bottom: 0.1, \"yes\", \"yes\", \"no\", \"\"\n"
+	"Text bottom: \"no\", \"Time (s)\"\n"
+	"One mark right: -1, \"yes\", \"yes\", \"no\", \"\"\n"
+	"One mark right: 0, \"yes\", \"yes\", \"yes\", \"\"\n"
+	"One mark right: 1, \"yes\", \"yes\", \"no\", \"\"\n"
 	"Remove\n"
 )
 NORMAL (U"This figure shows how the two sounds are windowed (faded out and in), as well as how they will overlap.")
diff --git a/fon/manual_soundFiles.cpp b/fon/manual_soundFiles.cpp
index b289b57..2e405b1 100644
--- a/fon/manual_soundFiles.cpp
+++ b/fon/manual_soundFiles.cpp
@@ -418,7 +418,7 @@ INTRO (U"A command in the @@Open menu@ that creates a @LongSound object.")
 NORMAL (U"The file will be opened for reading only. The file stays open until you remove the LongSound object.")
 MAN_END
 
-MAN_BEGIN (U"Sesam/LVS files", U"ppgb", 20141001)
+MAN_BEGIN (U"Sesam/LVS files", U"ppgb", 20170828)
 INTRO (U"A way for storing a @Sound object on disk.")
 ENTRY (U"File format")
 NORMAL (U"The sound files used by the SESAM and LVS programs. Each sample is normally quantized into 12 bits.")
@@ -430,7 +430,7 @@ NORMAL (U"The 12-bit sample values are divided by 2048 so that the amplitude "
 NORMAL (U"The resulting #Sound will appear in the List of Objects; "
 	"its name will be equal to the file name, without extension.")
 NORMAL (U"If the sound was encoded in 16 bits per sample, you should divide by 16 after reading "
-	"(with $$Formula: \"self/16\"$)")
+	"(with $$Formula: ~ self / 16$)")
 ENTRY (U"Saving")
 NORMAL (U"With ##Save as Sesam file...#. Praat then asks you for a file name. "
 	"After you click OK, the samples of the Sound are multiplied by 2048 "
@@ -440,7 +440,7 @@ NORMAL (U"To avoid clipping, keep the absolute amplitude below 1.000. "
 	"If the maximum sound pressure level is 91 dB (top = 2047), "
 	"the quantization threshold is (top = 1/2) 19 dB.")
 NORMAL (U"If you prefer 16-bit encoding, you should multiply by 16 before saving "
-	"(with $$Formula: \"self*16\"$)")
+	"(with $$Formula: ~ self * 16$)")
 MAN_END
 
 }
diff --git a/fon/manual_spectrum.cpp b/fon/manual_spectrum.cpp
index 2bfc257..f8b1b59 100644
--- a/fon/manual_spectrum.cpp
+++ b/fon/manual_spectrum.cpp
@@ -1,6 +1,6 @@
 /* manual_spectrum.cpp
  *
- * Copyright (C) 1992-2010,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2010,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -288,7 +288,7 @@ TAG (U"##Bin number")
 DEFINITION (U"the bin whose value is to be looked up.")
 MAN_END
 
-MAN_BEGIN (U"Sound: To Spectrogram...", U"ppgb", 20140421)
+MAN_BEGIN (U"Sound: To Spectrogram...", U"ppgb", 20170828)
 INTRO (U"A command that creates a @Spectrogram from every selected @Sound object. "
 	"It performs a %%short-term spectral analysis%, which means that for a number of time points in the Sound, "
 	"Praat computes an approximation of the spectrum at that time. Each such spectrum is called an %%analysis frame%.")
@@ -343,48 +343,48 @@ CODE (U"Marks bottom every: 1, 2, \"yes\", \"yes\", \"yes\"")
 CODE (U"Marks left every: 1, 2, \"yes\", \"yes\", \"yes\"")
 CODE (U"! now you should see a peak at 1000 Hz with a 3 dB bandwidth of 7 Hz (20 dB: 17 Hz)")
 CODE (U"! more precise: compute the position and width of the peak, and write them to the console")
-CODE (U"Formula: \"if x<980 or x>1020 then 0 else self fi\"")
+CODE (U"Formula: ~ if x<980 or x>1020 then 0 else self fi")
 CODE (U"To Formant (peaks): 20")
 CODE (U"Write to console")
 CODE (U"! now you should be able to read that a peak was found at 999.99982 Hz")
 CODE (U"! with a bandwidth of 6.497 Hz; the theory above predicted 6.491 Hz")
 CODE (U"")
 CODE (U"! The same, windowed by a 0.1-seconds Hamming window.")
-CODE (U"Create Sound from formula: \"Hamming\", 1, 0.0, 1.0, 44100, \"if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(0.54+0.46*cos(pi*(x-0.5)/0.1)) fi\"")
+CODE (U"Create Sound from formula: \"Hamming\", 1, 0.0, 1.0, 44100, ~ if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(0.54+0.46*cos(pi*(x-0.5)/0.1)) fi")
 CODE (U"To Spectrum: \"yes\"")
-CODE (U"Formula: \"if x<970 or x>1030 then 0 else self fi\"")
+CODE (U"Formula: ~ if x<970 or x>1030 then 0 else self fi")
 CODE (U"To Formant (peaks): 20")
 CODE (U"Write to console")
 CODE (U"! peak at 999.99817 Hz, 3 dB bw 6.518 Hz, 20 dB bw 15 Hz, zero bw 20 Hz, sidelobe -42 dB")
 CODE (U"")
 CODE (U"! The same, windowed by a 0.1-seconds rectangular window.")
-CODE (U"Create Sound from formula: \"rectangular\", 1, 0.0, 1.0, 44100, \"if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x) fi\"")
+CODE (U"Create Sound from formula: \"rectangular\", 1, 0.0, 1.0, 44100, ~ if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x) fi")
 CODE (U"To Spectrum: \"yes\"")
-CODE (U"Formula: \"if x<970 or x>1030 then 0 else self fi\"")
+CODE (U"Formula: ~ if x<970 or x>1030 then 0 else self fi")
 CODE (U"To Formant (peaks): 20")
 CODE (U"Write to console")
 CODE (U"! peak at 999.99506 Hz, 3 dB bw 4.440 Hz, 20 dB bw 27 Hz, zero bw 10 Hz, sidelobe -14 dB")
 CODE (U"")
 CODE (U"! The same, windowed by a 0.1-seconds Hanning window.")
-CODE (U"Create Sound from formula: \"Hanning\", 1, 0.0, 1.0, 44100, \"if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(0.5+0.5*cos(pi*(x-0.5)/0.1)) fi\"")
+CODE (U"Create Sound from formula: \"Hanning\", 1, 0.0, 1.0, 44100, ~ if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(0.5+0.5*cos(pi*(x-0.5)/0.1)) fi")
 CODE (U"To Spectrum: \"yes\"")
-CODE (U"Formula: \"if x<970 or x>1030 then 0 else self fi\"")
+CODE (U"Formula: ~ if x<970 or x>1030 then 0 else self fi")
 CODE (U"To Formant (peaks): 20")
 CODE (U"Write to console")
 CODE (U"! peak at 999.99945 Hz, 3 dB bw 7.212 Hz, 20 dB bw 16 Hz, zero bw 20 Hz, sidelobe -31 dB")
 CODE (U"")
 CODE (U"! The same, windowed by a 0.1-seconds triangular window.")
-CODE (U"Create Sound from formula: \"triangular\", 1, 0.0, 1.0, 44100, \"if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(1-abs((x-0.5)/0.1)) fi\"")
+CODE (U"Create Sound from formula: \"triangular\", 1, 0.0, 1.0, 44100, ~ if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(1-abs((x-0.5)/0.1)) fi")
 CODE (U"To Spectrum: \"yes\"")
-CODE (U"Formula: \"if x<970 or x>1030 then 0 else self fi\"")
+CODE (U"Formula: ~ if x<970 or x>1030 then 0 else self fi")
 CODE (U"To Formant (peaks): 20")
 CODE (U"Write to console")
 CODE (U"! peak at 999.99933 Hz, 3 dB bw 6.384 Hz, 20 dB bw 15 Hz, zero bw 20 Hz, sidelobe -26 dB")
 CODE (U"")
 CODE (U"! The same, windowed by a 0.1-seconds parabolic window.")
-CODE (U"Create Sound from formula: \"parabolic\", 1, 0.0, 1.0, 44100, \"if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(1-((x-0.5)/0.1)^2) fi\"")
+CODE (U"Create Sound from formula: \"parabolic\", 1, 0.0, 1.0, 44100, ~ if x<0.4 or x>0.6 then 0 else sin(2*pi*1000*x)*(1-((x-0.5)/0.1)^2) fi")
 CODE (U"To Spectrum: \"yes\"")
-CODE (U"Formula: \"if x<970 or x>1030 then 0 else self fi\"")
+CODE (U"Formula: ~ if x<970 or x>1030 then 0 else self fi")
 CODE (U"To Formant (peaks): 20")
 CODE (U"Write to console")
 CODE (U"! peak at 999.99921 Hz, 3 dB bw 5.786 Hz, 20 dB bw 12 Hz, zero bw 15 Hz, sidelobe -21 dB")
diff --git a/fon/manual_tutorials.cpp b/fon/manual_tutorials.cpp
index a4f98c7..4906149 100644
--- a/fon/manual_tutorials.cpp
+++ b/fon/manual_tutorials.cpp
@@ -22,10 +22,17 @@
 void manual_tutorials_init (ManPages me);
 void manual_tutorials_init (ManPages me) {
 
-MAN_BEGIN (U"What's new?", U"ppgb", 20170722)
+MAN_BEGIN (U"What's new?", U"ppgb", 20170916)
 INTRO (U"Latest changes in Praat.")
 //LIST_ITEM (U"• Manual page about @@drawing a vowel triangle at .")
 
+NORMAL (U"##6.0.32# (16 September 2017)")
+LIST_ITEM (U"• Table: allow spaces in column names.")
+LIST_ITEM (U"• Settings windows no longer replace your visible expressions with their resulting values.")
+LIST_ITEM (U"• Scripting: formulas using the tilde.")
+LIST_ITEM (U"• Optimality Theory learning: corrected a bug introduced in 6.0.30 that could cause the editor to crash on the Mac.")
+LIST_ITEM (U"• EEG: corrected a bug that could cause an infinite loop when doing principal component analysis.")
+LIST_ITEM (U"• Scripting: faster interpreter.")
 NORMAL (U"##6.0.31# (21 August 2017)")
 LIST_ITEM (U"• Scripting: more vectors and matrices.")
 LIST_ITEM (U"• Numerics: faster and more precise sums, means, standard deviations.")
@@ -1192,7 +1199,7 @@ LIST_ITEM (U"• Scripting: regular expressions allow replacement with empty str
 NORMAL (U"##4.4.23# (1 June 2006)")
 LIST_ITEM (U"• Table: ignore more white space.")
 NORMAL (U"##4.4.22# (30 May 2006)")
-LIST_ITEM (U"• Scripting: replacing with regular expression. See @@Formulas 5. String functions at .")
+LIST_ITEM (U"• Scripting: replacing with regular expression. See @@Formulas 6. String functions at .")
 NORMAL (U"##4.4.21# (29 May 2006)")
 LIST_ITEM (U"• Made Manipulation objects readable again.")
 NORMAL (U"##4.4.20# (3 May 2006)")
@@ -1210,7 +1217,7 @@ LIST_ITEM (U"• Table: #Formula accepts string expressions as well as numeric e
 LIST_ITEM (U"• Table: #Sort can sort by any number of columns.")
 LIST_ITEM (U"• Table: ##Create with column names#.")
 LIST_ITEM (U"• Table: ##Report mean#.")
-LIST_ITEM (U"• Formulas: @@Formulas 7. Attributes of objects|row\\$  and col\\$  attributes at .")
+LIST_ITEM (U"• Formulas: @@Formulas 8. Attributes of objects|row\\$  and col\\$  attributes at .")
 LIST_ITEM (U"• Warning when trying to read data files whose format is newer than the Praat version.")
 NORMAL (U"##4.4.16# (1 April 2006)")
 LIST_ITEM (U"• Spectrum window: dynamic range setting.")
@@ -1425,7 +1432,7 @@ ENTRY (U"Praat 4.2, 4 March 2004")
 		"on non-PostScript printers, improves the looks of pictures copied to your wordprocessor when printed, "
 		"and changes the looks of pictures copied to your presentation program.")
 	NORMAL (U"OT learning:")
-	LIST_ITEM (U"• Metrics grammar supports \'impoverished overt forms\', "
+	LIST_ITEM (U"• Metrics grammar supports `impoverished overt forms', "
 		"i.e. without secondary stress even if surface structures do have secondary stress.")
 	LIST_ITEM (U"• Support for crucially tied constraints and tied candidates.")
 	LIST_ITEM (U"• Support for backtracking in EDCD.")
@@ -1485,7 +1492,7 @@ ENTRY (U"Praat 4.1, 5 June 2003")
 	LIST_ITEM (U"• Refer to any matrices and tables in formulas, e.g. Sound_hello (x) or Table_everything [row, col] "
 		"or Table_tokens [i, \"F1\"].")
 	LIST_ITEM (U"• Assignment by modification, as with += -= *= /=.")
-	LIST_ITEM (U"• New functions: date\\$ (), extractNumber, extractWord\\$ , extractLine\\$ . See @@Formulas 5. String functions at .")
+	LIST_ITEM (U"• New functions: date\\$ (), extractNumber, extractWord\\$ , extractLine\\$ . See @@Formulas 6. String functions at .")
 	LIST_ITEM (U"• @@Scripting 5.8. Including other scripts at .")
 	LIST_ITEM (U"• String formulas in the calculator.")
 	LIST_ITEM (U"• Stopped support of things that had been undocumented for the last four years: "
@@ -1597,7 +1604,7 @@ ENTRY (U"Praat 3.9, 18 October 2000")
 	LIST_ITEM (U"• Read Sound from raw Alaw file.")
 	LIST_ITEM (U"• Artword & Speaker (& Sound) movie: real time on all platforms.")
 	NORMAL (U"Scripting:")
-	LIST_ITEM (U"• @@Formulas 4. Mathematical functions@: added statistical functions: %\\ci^2, Student T, Fisher F, binomial, "
+	LIST_ITEM (U"• @@Formulas 5. Mathematical functions@: added statistical functions: %\\ci^2, Student T, Fisher F, binomial, "
 		"and their inverse functions.")
 	LIST_ITEM (U"• Windows: program #praatcon for use as a Unix-style console application.")
 	LIST_ITEM (U"• Windows and Unix: Praat can be run with a command-line interface without quitting on errors.")
@@ -1681,7 +1688,7 @@ ENTRY (U"Praat 3.7, 24 March 1998")
 	LIST_ITEM (U"• OT learning: new strategies: weighted symmetric plasticity (uncancelled or all).")
 	NORMAL (U"Praat shell")
 	LIST_ITEM (U"• First Linux version.")
-	LIST_ITEM (U"• Eight new functions like e.g. %hertzToBark in @@Formulas 4. Mathematical functions at .")
+	LIST_ITEM (U"• Eight new functions like e.g. %hertzToBark in @@Formulas 5. Mathematical functions at .")
 	LIST_ITEM (U"• @@Praat script@: procedure arguments; object names.")
 	NORMAL (U"Documentation:")
 	LIST_ITEM (U"• 230 more man pages (now 630).")
@@ -3579,7 +3586,7 @@ NORMAL (U"In this example, filtering was done without automatic scaling, so that
 	"between -1 and +1 Pascal.")
 MAN_END
 
-MAN_BEGIN (U"Source-filter synthesis 4. Using existing sounds", U"ppgb", 20140421)
+MAN_BEGIN (U"Source-filter synthesis 4. Using existing sounds", U"ppgb", 20170828)
 ENTRY (U"1. How to extract the %filter from an existing speech sound")
 NORMAL (U"You can separate source and filter with the help of the technique of %%linear prediction% "
 	"(see @@Sound: LPC analysis@). This technique tries to approximate a given frequency spectrum with "
@@ -3692,7 +3699,7 @@ NORMAL (U"A @Formant object can be changed in a friendlier way, with @@Formant:
 CODE (U"#selectObject: \"Formant filter\"")
 CODE (U"Formula (frequencies): \"self * 0.9\"")
 NORMAL (U"To add 200 hertz to all values of %F__2_, you do")
-CODE (U"Formula (frequencies): \"if row = 2 then self + 200 else self fi\"")
+CODE (U"Formula (frequencies): ~ if row = 2 then self + 200 else self fi")
 NORMAL (U"A @FormantGrid object can be changed by adding or removing points:")
 LIST_ITEM (U"@@FormantGrid: Add formant point...@")
 LIST_ITEM (U"@@FormantGrid: Add bandwidth point...@")
diff --git a/fon/praat_Fon.cpp b/fon/praat_Fon.cpp
index 15e8747..969219b 100644
--- a/fon/praat_Fon.cpp
+++ b/fon/praat_Fon.cpp
@@ -1462,7 +1462,7 @@ DO
 	if (toFrequency <= fromFrequency) Melder_throw (U"Maximum frequency must be greater than minimum frequency.");
 	GRAPHICS_EACH (Pitch)
 		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			garnish, Pitch_speckle_NO, kPitch_unit_HERTZ);
+			garnish, Pitch_speckle_NO, kPitch_unit::HERTZ);
 	GRAPHICS_EACH_END
 }
 
@@ -1475,7 +1475,7 @@ FORM (GRAPHICS_Pitch_drawErb, U"Pitch: Draw erb", U"Pitch: Draw...") {
 DO
 	GRAPHICS_EACH (Pitch)
 		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			garnish, Pitch_speckle_NO, kPitch_unit_ERB);
+			garnish, Pitch_speckle_NO, kPitch_unit::ERB);
 	GRAPHICS_EACH_END
 }
 
@@ -1489,7 +1489,7 @@ DO
 	if (toFrequency <= fromFrequency) Melder_throw (U"Maximum frequency must be greater than minimum frequency.");
 	GRAPHICS_EACH (Pitch)
 		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			garnish, Pitch_speckle_NO, kPitch_unit_HERTZ_LOGARITHMIC);
+			garnish, Pitch_speckle_NO, kPitch_unit::HERTZ_LOGARITHMIC);
 	GRAPHICS_EACH_END
 }
 
@@ -1502,7 +1502,7 @@ FORM (GRAPHICS_Pitch_drawMel, U"Pitch: Draw mel", U"Pitch: Draw...") {
 DO
 	GRAPHICS_EACH (Pitch)
 		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			garnish, Pitch_speckle_NO, kPitch_unit_MEL);
+			garnish, Pitch_speckle_NO, kPitch_unit::MEL);
 	GRAPHICS_EACH_END
 }
 
@@ -1516,7 +1516,7 @@ FORM (GRAPHICS_Pitch_drawSemitones100, U"Pitch: Draw semitones (re 100 Hz)", U"P
 DO
 	GRAPHICS_EACH (Pitch)
 		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			garnish, Pitch_speckle_NO, kPitch_unit_SEMITONES_100);
+			garnish, Pitch_speckle_NO, kPitch_unit::SEMITONES_100);
 	GRAPHICS_EACH_END
 }
 
@@ -1530,7 +1530,7 @@ FORM (GRAPHICS_Pitch_drawSemitones200, U"Pitch: Draw semitones (re 200 Hz)", U"P
 DO
 	GRAPHICS_EACH (Pitch)
 		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			garnish, Pitch_speckle_NO, kPitch_unit_SEMITONES_200);
+			garnish, Pitch_speckle_NO, kPitch_unit::SEMITONES_200);
 	GRAPHICS_EACH_END
 }
 
@@ -1544,7 +1544,7 @@ FORM (GRAPHICS_Pitch_drawSemitones440, U"Pitch: Draw semitones (re 440 Hz)", U"P
 DO
 	GRAPHICS_EACH (Pitch)
 		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			garnish, Pitch_speckle_NO, kPitch_unit_SEMITONES_440);
+			garnish, Pitch_speckle_NO, kPitch_unit::SEMITONES_440);
 	GRAPHICS_EACH_END
 }
 
@@ -1576,7 +1576,7 @@ FORM (REAL_Pitch_getMinimum, U"Pitch: Get minimum", 0) {
 	OK
 DO
 	NUMBER_ONE (Pitch)
-		double result = Pitch_getMinimum (me, fromTime, toTime, unit, interpolation);
+		double result = Pitch_getMinimum (me, fromTime, toTime, (kPitch_unit) unit, interpolation);
 		result = Function_convertToNonlogarithmic (me, result, Pitch_LEVEL_FREQUENCY, unit);
 	NUMBER_ONE_END (U" ", Function_getUnitText (me, Pitch_LEVEL_FREQUENCY, unit, 0))
 }
@@ -1590,7 +1590,7 @@ FORM (REAL_Pitch_getMaximum, U"Pitch: Get maximum", nullptr) {
 	OK
 DO
 	NUMBER_ONE (Pitch)
-		double result = Pitch_getMaximum (me, fromTime, toTime, unit, interpolation);
+		double result = Pitch_getMaximum (me, fromTime, toTime, (kPitch_unit) unit, interpolation);
 		result = Function_convertToNonlogarithmic (me, result, Pitch_LEVEL_FREQUENCY, unit);
 	NUMBER_ONE_END (U" ", Function_getUnitText (me, Pitch_LEVEL_FREQUENCY, unit, 0))
 }
@@ -1601,7 +1601,7 @@ FORM (REAL_Pitch_getMean, U"Pitch: Get mean", nullptr) {
 	OK
 DO
 	NUMBER_ONE (Pitch)
-		double result = Pitch_getMean (me, fromTime, toTime, unit);
+		double result = Pitch_getMean (me, fromTime, toTime, (kPitch_unit) unit);
 		result = Function_convertToNonlogarithmic (me, result, Pitch_LEVEL_FREQUENCY, unit);
 	NUMBER_ONE_END (U" ", Function_getUnitText (me, Pitch_LEVEL_FREQUENCY, unit, 0));
 }
@@ -1647,7 +1647,7 @@ DO
 
 FORM (REAL_Pitch_getStandardDeviation, U"Pitch: Get standard deviation", nullptr) {
 	praat_TimeFunction_RANGE (fromTime, toTime)
-	OPTIONMENUVAR (unit, U"Unit", 1)
+	OPTIONMENUVAR (unit_i, U"Unit", 1)
 		OPTION (U"Hertz")
 		OPTION (U"mel")
 		OPTION (U"logHertz")
@@ -1655,19 +1655,19 @@ FORM (REAL_Pitch_getStandardDeviation, U"Pitch: Get standard deviation", nullptr
 		OPTION (U"ERB")
 	OK
 DO
-	unit =
-		unit == 1 ? kPitch_unit_HERTZ :
-		unit == 2 ? kPitch_unit_MEL :
-		unit == 3 ? kPitch_unit_LOG_HERTZ :
-		unit == 4 ? kPitch_unit_SEMITONES_1 :
-		kPitch_unit_ERB;
+	kPitch_unit unit =
+		unit_i == 1 ? kPitch_unit::HERTZ :
+		unit_i == 2 ? kPitch_unit::MEL :
+		unit_i == 3 ? kPitch_unit::LOG_HERTZ :
+		unit_i == 4 ? kPitch_unit::SEMITONES_1 :
+		kPitch_unit::ERB;
 	NUMBER_ONE (Pitch)
 		double result = Pitch_getStandardDeviation (me, fromTime, toTime, unit);
 		const char32 *unitText =
-			unit == kPitch_unit_HERTZ ? U"Hz" :
-			unit == kPitch_unit_MEL ? U"mel" :
-			unit == kPitch_unit_LOG_HERTZ ? U"logHz" :
-			unit == kPitch_unit_SEMITONES_1 ? U"semitones" :
+			unit == kPitch_unit::HERTZ ? U"Hz" :
+			unit == kPitch_unit::MEL ? U"mel" :
+			unit == kPitch_unit::LOG_HERTZ ? U"logHz" :
+			unit == kPitch_unit::SEMITONES_1 ? U"semitones" :
 			U"ERB";
 	NUMBER_ONE_END (U" ", unitText)
 }
@@ -1681,7 +1681,7 @@ FORM (REAL_Pitch_getTimeOfMaximum, U"Pitch: Get time of maximum", nullptr) {
 	OK
 DO
 	NUMBER_ONE (Pitch)
-		double result = Pitch_getTimeOfMaximum (me, fromTime, toTime, unit, interpolation);
+		double result = Pitch_getTimeOfMaximum (me, fromTime, toTime, (kPitch_unit) unit, interpolation);
 	NUMBER_ONE_END (U" seconds")
 }
 
@@ -1694,7 +1694,7 @@ FORM (REAL_Pitch_getTimeOfMinimum, U"Pitch: Get time of minimum", nullptr) {
 	OK
 DO
 	NUMBER_ONE (Pitch)
-		double result = Pitch_getTimeOfMinimum (me, fromTime, toTime, unit, interpolation);
+		double result = Pitch_getTimeOfMinimum (me, fromTime, toTime, (kPitch_unit) unit, interpolation);
 	NUMBER_ONE_END (U" seconds")
 }
 
@@ -1769,7 +1769,7 @@ FORM (GRAPHICS_Pitch_speckle, U"Pitch: Speckle", U"Pitch: Draw...") {
 DO
 	if (toFrequency <= fromFrequency) Melder_throw (U"Maximum frequency should be greater than minimum frequency.");
 	GRAPHICS_EACH (Pitch)
-		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit_HERTZ);
+		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit::HERTZ);
 	GRAPHICS_EACH_END
 }
 
@@ -1781,7 +1781,7 @@ FORM (GRAPHICS_Pitch_speckleErb, U"Pitch: Speckle erb", U"Pitch: Draw...") {
 	OK
 DO
 	GRAPHICS_EACH (Pitch)
-		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit_ERB);
+		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit::ERB);
 	GRAPHICS_EACH_END
 }
 
@@ -1794,7 +1794,7 @@ FORM (GRAPHICS_Pitch_speckleLogarithmic, U"Pitch: Speckle logarithmic", U"Pitch:
 DO
 	if (toFrequency <= fromFrequency) Melder_throw (U"Maximum frequency must be greater than minimum frequency.");
 	GRAPHICS_EACH (Pitch)
-		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit_HERTZ_LOGARITHMIC);
+		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit::HERTZ_LOGARITHMIC);
 	GRAPHICS_EACH_END
 }
 
@@ -1806,7 +1806,7 @@ FORM (GRAPHICS_Pitch_speckleMel, U"Pitch: Speckle mel", U"Pitch: Draw...") {
 	OK
 DO
 	GRAPHICS_EACH (Pitch)
-		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit_MEL);
+		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit::MEL);
 	GRAPHICS_EACH_END
 }
 
@@ -1819,7 +1819,7 @@ FORM (GRAPHICS_Pitch_speckleSemitones100, U"Pitch: Speckle semitones (re 100 Hz)
 	OK
 DO
 	GRAPHICS_EACH (Pitch)
-		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit_SEMITONES_100);
+		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit::SEMITONES_100);
 	GRAPHICS_EACH_END
 }
 
@@ -1832,7 +1832,7 @@ FORM (GRAPHICS_Pitch_speckleSemitones200, U"Pitch: Speckle semitones (re 200 Hz)
 	OK
 DO
 	GRAPHICS_EACH (Pitch)
-		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit_SEMITONES_200);
+		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit::SEMITONES_200);
 	GRAPHICS_EACH_END
 }
 
@@ -1845,7 +1845,7 @@ FORM (GRAPHICS_Pitch_speckleSemitones440, U"Pitch: Speckle semitones (re 440 Hz)
 	OK
 DO
 	GRAPHICS_EACH (Pitch)
-		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit_SEMITONES_440);
+		Pitch_draw (me, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency, garnish, Pitch_speckle_YES, kPitch_unit::SEMITONES_440);
 	GRAPHICS_EACH_END
 }
 
@@ -1859,7 +1859,7 @@ FORM (NEW_Pitch_subtractLinearFit, U"Pitch: subtract linear fit", nullptr) {
 	OK
 DO
 	CONVERT_EACH (Pitch)
-		autoPitch result = Pitch_subtractLinearFit (me, unit);
+		autoPitch result = Pitch_subtractLinearFit (me, (kPitch_unit) unit);
 	CONVERT_EACH_END (my name)
 }
 
@@ -2858,7 +2858,7 @@ FORM (INFO_Praat_test, U"Praat test", 0) {
 	OK
 DO
 	INFO_NONE
-		Praat_tests (test, arg1, arg2, arg3, arg4);
+		Praat_tests ((kPraatTests) test, arg1, arg2, arg3, arg4);
 	INFO_NONE_END
 }
 
diff --git a/fon/praat_Matrix.cpp b/fon/praat_Matrix.cpp
index e95f658..3f35d9f 100644
--- a/fon/praat_Matrix.cpp
+++ b/fon/praat_Matrix.cpp
@@ -792,16 +792,16 @@ DIRECT (WINDOW_Movie_viewAndEdit) {
 
 static autoDaata imageFileRecognizer (int /* nread */, const char * /* header */, MelderFile file) {
 	const char32 *fileName = MelderFile_name (file);
-	if (Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".jpg") ||
-	    Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".JPG") ||
-	    Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".jpeg") ||
-		Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".JPEG") ||
-	    Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".png") ||
-		Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".PNG") ||
-	    Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".tiff") ||
-		Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".TIFF") ||
-		Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".tif") ||
-		Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".TIF"))
+	if (Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".jpg") ||
+	    Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".JPG") ||
+	    Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".jpeg") ||
+		Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".JPEG") ||
+	    Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".png") ||
+		Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".PNG") ||
+	    Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".tiff") ||
+		Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".TIFF") ||
+		Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".tif") ||
+		Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".TIF"))
 	{
 		return Photo_readFromImageFile (file);
 	}
diff --git a/fon/praat_Sound.cpp b/fon/praat_Sound.cpp
index 17ccf9b..ac91d03 100644
--- a/fon/praat_Sound.cpp
+++ b/fon/praat_Sound.cpp
@@ -113,18 +113,18 @@ FORM (PLAY_LongSound_playPart, U"LongSound: Play part", nullptr) {
 DO
 	int n = 0;
 	LOOP n ++;
-	if (n == 1 || MelderAudio_getOutputMaximumAsynchronicity () < kMelder_asynchronicityLevel_ASYNCHRONOUS) {
+	if (n == 1 || MelderAudio_getOutputMaximumAsynchronicity () < kMelder_asynchronicityLevel::ASYNCHRONOUS) {
 		LOOP {
 			iam (LongSound);
 			LongSound_playPart (me, fromTime, toTime, nullptr, nullptr);
 		}
 	} else {
-		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_INTERRUPTABLE);
+		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::INTERRUPTABLE);
 		LOOP {
 			iam (LongSound);
 			LongSound_playPart (me, fromTime, toTime, nullptr, nullptr);
 		}
-		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_ASYNCHRONOUS);
+		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::ASYNCHRONOUS);
 	}
 END }
 
@@ -430,7 +430,7 @@ DIRECT (NEW_Sound_convertToStereo) {
 DIRECT (NEW1_Sounds_convolve_old) {
 	CONVERT_COUPLE (Sound)
 		autoSound result = Sounds_convolve (me, you,
-			kSounds_convolve_scaling_SUM, kSounds_convolve_signalOutsideTimeDomain_ZERO);
+			kSounds_convolve_scaling::SUM, kSounds_convolve_signalOutsideTimeDomain::ZERO);
 	CONVERT_COUPLE_END (my name, U"_", your name)
 }
 
@@ -1171,18 +1171,18 @@ DIRECT (PLAY_Sound_play) {
 	LOOP {
 		n ++;
 	}
-	if (n == 1 || MelderAudio_getOutputMaximumAsynchronicity () < kMelder_asynchronicityLevel_ASYNCHRONOUS) {
+	if (n == 1 || MelderAudio_getOutputMaximumAsynchronicity () < kMelder_asynchronicityLevel::ASYNCHRONOUS) {
 		LOOP {
 			iam (Sound);
 			Sound_play (me, nullptr, nullptr);
 		}
 	} else {
-		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_INTERRUPTABLE);
+		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::INTERRUPTABLE);
 		LOOP {
 			iam (Sound);
 			Sound_play (me, nullptr, nullptr);   // BUG: exception-safe?
 		}
-		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_ASYNCHRONOUS);
+		MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel::ASYNCHRONOUS);
 	}
 END }
 
@@ -2011,8 +2011,8 @@ static autoDaata soundFileRecognizer (int nread, const char *header, MelderFile
 	if (strnequ (header, ".snd", 4)) return Sound_readFromSoundFile (file);
 	if (strnequ (header, "NIST_1A", 7)) return Sound_readFromSoundFile (file);
 	if (strnequ (header, "fLaC", 4)) return Sound_readFromSoundFile (file);   // Erez Volk, March 2007
-	if ((Melder_stringMatchesCriterion (MelderFile_name (file), kMelder_string_ENDS_WITH, U".mp3") ||
-	     Melder_stringMatchesCriterion (MelderFile_name (file), kMelder_string_ENDS_WITH, U".MP3"))
+	if ((Melder_stringMatchesCriterion (MelderFile_name (file), kMelder_string::ENDS_WITH, U".mp3") ||
+	     Melder_stringMatchesCriterion (MelderFile_name (file), kMelder_string::ENDS_WITH, U".MP3"))
 		&& mp3_recognize (nread, header)) return Sound_readFromSoundFile (file);   // Erez Volk, May 2007
 	return autoDaata ();
 }
@@ -2023,18 +2023,18 @@ static autoDaata movieFileRecognizer (int nread, const char * /* header */, Meld
 		header [1], header [2], header [3],
 		header [4], header [5], header [6],
 		header [7], header [8], header [9]);*/
-	if (nread < 512 || (! Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".mov") &&
-	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".MOV") &&
-	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".avi") &&
-	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".AVI"))) return autoDaata ();
+	if (nread < 512 || (! Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".mov") &&
+	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".MOV") &&
+	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".avi") &&
+	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".AVI"))) return autoDaata ();
 	Melder_throw (U"This Praat version cannot open movie files.");
 	return autoDaata ();
 }
 
 static autoDaata sesamFileRecognizer (int nread, const char * /* header */, MelderFile file) {
 	const char32 *fileName = MelderFile_name (file);
-	if (nread < 512 || (! Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".sdf") &&
-	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string_ENDS_WITH, U".SDF"))) return autoDaata ();
+	if (nread < 512 || (! Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".sdf") &&
+	                    ! Melder_stringMatchesCriterion (fileName, kMelder_string::ENDS_WITH, U".SDF"))) return autoDaata ();
 	return Sound_readFromSesamFile (file);
 }
 
diff --git a/fon/praat_TextGrid_init.cpp b/fon/praat_TextGrid_init.cpp
index d612d70..5e6c631 100644
--- a/fon/praat_TextGrid_init.cpp
+++ b/fon/praat_TextGrid_init.cpp
@@ -151,7 +151,7 @@ FORM (GRAPHICS_TextGrid_Pitch_draw, U"TextGrid & Pitch: Draw", nullptr) {
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_draw (me, you, GRAPHICS, tierNumber, fromTime, toTime, fromFrequency, toFrequency,
-			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit_HERTZ);
+			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit::HERTZ);
 	GRAPHICS_TWO_END
 }
 
@@ -168,7 +168,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawErb, U"TextGrid & Pitch: Draw erb", nullptr) {
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_draw (me, you, GRAPHICS, tierNumber, fromTime, toTime, fromFrequency, toFrequency,
-			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit_ERB);
+			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit::ERB);
 	GRAPHICS_TWO_END
 }
 
@@ -185,7 +185,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawLogarithmic, U"TextGrid & Pitch: Draw logarith
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_draw (me, you, GRAPHICS, tierNumber, fromTime, toTime, fromFrequency, toFrequency,
-			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit_HERTZ_LOGARITHMIC);
+			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit::HERTZ_LOGARITHMIC);
 	GRAPHICS_TWO_END
 }
 
@@ -202,7 +202,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawMel, U"TextGrid & Pitch: Draw mel", nullptr) {
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_draw (me, you, GRAPHICS, tierNumber, fromTime, toTime, fromFrequency, toFrequency,
-			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit_MEL);
+			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit::MEL);
 	GRAPHICS_TWO_END
 }
 
@@ -220,7 +220,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawSemitones, U"TextGrid & Pitch: Draw semitones"
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_draw (me, you, GRAPHICS, tierNumber, fromTime, toTime, fromFrequency, toFrequency,
-			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit_SEMITONES_100);
+			fontSize, useTextStyles, textAlignment, garnish, Pitch_speckle_NO, kPitch_unit::SEMITONES_100);
 	GRAPHICS_TWO_END
 }
 
@@ -235,7 +235,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawSeparately, U"TextGrid & Pitch: Draw separatel
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit_HERTZ);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit::HERTZ);
 	GRAPHICS_TWO_END
 }
 
@@ -250,7 +250,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawSeparatelyErb, U"TextGrid & Pitch: Draw separa
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit_ERB);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit::ERB);
 	GRAPHICS_TWO_END
 }
 
@@ -265,7 +265,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawSeparatelyLogarithmic, U"TextGrid & Pitch: Dra
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit_HERTZ_LOGARITHMIC);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit::HERTZ_LOGARITHMIC);
 	GRAPHICS_TWO_END
 }
 
@@ -280,7 +280,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawSeparatelyMel, U"TextGrid & Pitch: Draw separa
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit_MEL);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit::MEL);
 	GRAPHICS_TWO_END
 }
 
@@ -296,7 +296,7 @@ FORM (GRAPHICS_TextGrid_Pitch_drawSeparatelySemitones, U"TextGrid & Pitch: Draw
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit_SEMITONES_100);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_NO, kPitch_unit::SEMITONES_100);
 	GRAPHICS_TWO_END
 }
 
@@ -312,7 +312,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckle, U"TextGrid & Pitch: Speckle", nullptr) {
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_HERTZ);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::HERTZ);
 	GRAPHICS_TWO_END
 }
 
@@ -328,7 +328,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleErb, U"TextGrid & Pitch: Speckle erb", null
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_ERB);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::ERB);
 	GRAPHICS_TWO_END
 }
 
@@ -344,7 +344,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleLogarithmic, U"TextGrid & Pitch: Speckle lo
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_HERTZ_LOGARITHMIC);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::HERTZ_LOGARITHMIC);
 	GRAPHICS_TWO_END
 }
 
@@ -360,7 +360,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleMel, U"TextGrid & Pitch: Speckle mel", null
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_MEL);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::MEL);
 	GRAPHICS_TWO_END
 }
 
@@ -377,7 +377,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleSemitones, U"TextGrid & Pitch: Speckle semi
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_SEMITONES_100);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::SEMITONES_100);
 	GRAPHICS_TWO_END
 }
 
@@ -392,7 +392,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleSeparately, U"TextGrid & Pitch: Speckle sep
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_HERTZ);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::HERTZ);
 	GRAPHICS_TWO_END
 }
 
@@ -407,7 +407,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleSeparatelyErb, U"TextGrid & Pitch: Speckle
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_ERB);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::ERB);
 	GRAPHICS_TWO_END
 }
 
@@ -422,7 +422,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleSeparatelyLogarithmic, U"TextGrid & Pitch:
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_HERTZ_LOGARITHMIC);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::HERTZ_LOGARITHMIC);
 	GRAPHICS_TWO_END
 }
 
@@ -437,7 +437,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleSeparatelyMel, U"TextGrid & Pitch: Speckle
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_MEL);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::MEL);
 	GRAPHICS_TWO_END
 }
 
@@ -453,7 +453,7 @@ FORM (GRAPHICS_TextGrid_Pitch_speckleSeparatelySemitones, U"TextGrid & Pitch: Sp
 DO
 	GRAPHICS_TWO (TextGrid, Pitch)
 		TextGrid_Pitch_drawSeparately (me, you, GRAPHICS, fromTime, toTime, fromFrequency, toFrequency,
-			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit_MEL);
+			showBoundaries, useTextStyles, garnish, Pitch_speckle_YES, kPitch_unit::MEL);
 	GRAPHICS_TWO_END
 }
 
@@ -515,7 +515,7 @@ FORM (NEW1_TextGrid_Sound_extractIntervals, U"TextGrid & Sound: Extract interval
 DO
 	CONVERT_TWO (TextGrid, Sound)
 		autoSoundList result = TextGrid_Sound_extractIntervalsWhere (me, you,
-			tierNumber, kMelder_string_EQUAL_TO, labelText, preserveTimes);
+			tierNumber, kMelder_string::EQUAL_TO, labelText, preserveTimes);
 		result -> classInfo = classCollection;   // YUCK, in order to force automatic unpacking
 	CONVERT_TWO_END (U"dummy")
 }
@@ -529,7 +529,7 @@ FORM (NEW1_TextGrid_Sound_extractIntervalsWhere, U"TextGrid & Sound: Extract int
 DO
 	CONVERT_TWO (TextGrid, Sound)
 		autoSoundList result = TextGrid_Sound_extractIntervalsWhere (me, you, tierNumber,
-			extractEveryIntervalWhoseLabel___, __theText, preserveTimes);
+			(kMelder_string) extractEveryIntervalWhoseLabel___, __theText, preserveTimes);
 		result -> classInfo = classCollection;   // YUCK, in order to force automatic unpacking
 	CONVERT_TWO_END (U"dummy")
 }
@@ -994,7 +994,7 @@ FORM (INTEGER_TextGrid_countIntervalsWhere, U"Count intervals", U"TextGrid: Coun
 	OK
 DO
 	NUMBER_ONE (TextGrid)
-		long result = TextGrid_countIntervalsWhere (me, tierNumber, countIntervalsWhoseLabel___, ___theText);
+		long result = TextGrid_countIntervalsWhere (me, tierNumber, (kMelder_string) countIntervalsWhoseLabel___, ___theText);
 	NUMBER_ONE_END (U" intervals containing ", ___theText);
 }
 
@@ -1083,7 +1083,7 @@ FORM (INTEGER_TextGrid_countPointsWhere, U"Count points", U"TextGrid: Count poin
 	OK
 DO
 	NUMBER_ONE (TextGrid)
-		long result = TextGrid_countPointsWhere (me, tierNumber, countPointsWhoseLabel___, ___theText);
+		long result = TextGrid_countPointsWhere (me, tierNumber, (kMelder_string) countPointsWhoseLabel___, ___theText);
 	NUMBER_ONE_END (U" points containing ", ___theText);
 }
 
@@ -1289,7 +1289,7 @@ FORM (MODIFY_TextGrid_removePoints, U"Remove points", nullptr) {
 	OK
 DO
 	MODIFY_EACH (TextGrid)
-		TextGrid_removePoints (me, tierNumber, removeEveryPointWhoseLabel___, ___theText);
+		TextGrid_removePoints (me, tierNumber, (kMelder_string) removeEveryPointWhoseLabel___, ___theText);
 	MODIFY_EACH_END
 }
 
@@ -1346,7 +1346,7 @@ FORM (NEW_TextGrid_getStartingPoints, U"TextGrid: Get starting points", nullptr)
 	OK
 DO
 	CONVERT_EACH (TextGrid)
-		autoPointProcess result = TextGrid_getStartingPoints (me, tierNumber, getStartingPointsWhoseLabel___, ___theText);
+		autoPointProcess result = TextGrid_getStartingPoints (me, tierNumber, (kMelder_string) getStartingPointsWhoseLabel___, ___theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
@@ -1357,7 +1357,7 @@ FORM (NEW_TextGrid_getEndPoints, U"TextGrid: Get end points", nullptr) {
 	OK
 DO
 	CONVERT_EACH (TextGrid)
-		autoPointProcess result = TextGrid_getEndPoints (me, tierNumber, getEndPointsWhoseLabel___, ___theText);
+		autoPointProcess result = TextGrid_getEndPoints (me, tierNumber, (kMelder_string) getEndPointsWhoseLabel___, ___theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
@@ -1368,7 +1368,7 @@ FORM (NEW_TextGrid_getCentrePoints, U"TextGrid: Get centre points", nullptr) {
 	OK
 DO
 	CONVERT_EACH (TextGrid)
-		autoPointProcess result = TextGrid_getCentrePoints (me, tierNumber, getCentrePointsWhoseLabel___, ___theText);
+		autoPointProcess result = TextGrid_getCentrePoints (me, tierNumber, (kMelder_string) getCentrePointsWhoseLabel___, ___theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
@@ -1379,7 +1379,7 @@ FORM (NEW_TextGrid_getPoints, U"Get points", nullptr) {
 	OK
 DO
 	CONVERT_EACH (TextGrid)
-		autoPointProcess result = TextGrid_getPoints (me, tierNumber, getPointsWhoseLabel___, ___theText);
+		autoPointProcess result = TextGrid_getPoints (me, tierNumber, (kMelder_string) getPointsWhoseLabel___, ___theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
@@ -1393,7 +1393,7 @@ FORM (NEW_TextGrid_getPoints_preceded, U"Get points (preceded)", nullptr) {
 DO
 	CONVERT_EACH (TextGrid)
 		autoPointProcess result = TextGrid_getPoints_preceded (me, tierNumber,
-			getPointsWhoseLabel___, ___theText, ___precededByALabelThat___, ____theText);
+			(kMelder_string) getPointsWhoseLabel___, ___theText, (kMelder_string) ___precededByALabelThat___, ____theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
@@ -1407,7 +1407,7 @@ FORM (NEW_TextGrid_getPoints_followed, U"Get points (followed)", nullptr) {
 DO
 	CONVERT_EACH (TextGrid)
 		autoPointProcess result = TextGrid_getPoints_followed (me, tierNumber,
-			getPointsWhoseLabel___, ___theText, ___followedByALabelThat___, ____theText);
+			(kMelder_string) getPointsWhoseLabel___, ___theText, (kMelder_string) ___followedByALabelThat___, ____theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
diff --git a/fon/praat_Tiers.cpp b/fon/praat_Tiers.cpp
index 0cc62aa..00a3683 100644
--- a/fon/praat_Tiers.cpp
+++ b/fon/praat_Tiers.cpp
@@ -906,7 +906,7 @@ FORM (MODIFY_PitchTier_shiftFrequencies, U"PitchTier: Shift frequencies", nullpt
 	REALVAR (fromTime, U"left Time range (s)", U"0.0")
 	REALVAR (toTime, U"right Time range (s)", U"1000.0")
 	REALVAR (frequencyShift, U"Frequency shift", U"-20.0")
-	OPTIONMENUVAR (unit, U"Unit", 1)
+	OPTIONMENUVAR (unit_i, U"Unit", 1)
 		OPTION (U"Hertz")
 		OPTION (U"mel")
 		OPTION (U"logHertz")
@@ -914,12 +914,12 @@ FORM (MODIFY_PitchTier_shiftFrequencies, U"PitchTier: Shift frequencies", nullpt
 		OPTION (U"ERB")
 	OK
 DO
-	unit =
-		unit == 1 ? kPitch_unit_HERTZ :
-		unit == 2 ? kPitch_unit_MEL :
-		unit == 3 ? kPitch_unit_LOG_HERTZ :
-		unit == 4 ? kPitch_unit_SEMITONES_1 :
-		kPitch_unit_ERB;
+	kPitch_unit unit =
+		unit_i == 1 ? kPitch_unit::HERTZ :
+		unit_i == 2 ? kPitch_unit::MEL :
+		unit_i == 3 ? kPitch_unit::LOG_HERTZ :
+		unit_i == 4 ? kPitch_unit::SEMITONES_1 :
+		kPitch_unit::ERB;
 	MODIFY_EACH_WEAK (PitchTier)
 		PitchTier_shiftFrequencies (me, fromTime, toTime, frequencyShift, unit);
 	MODIFY_EACH_WEAK_END
@@ -1733,9 +1733,10 @@ void praat_Tiers_init () {
 	praat_addAction1 (classPitchTier, 1, U"View & Edit", nullptr, praat_ATTRACTIVE, WINDOW_PitchTier_viewAndEdit);
 	praat_addAction1 (classPitchTier, 1,   U"Edit", U"*View & Edit", praat_DEPRECATED_2011, WINDOW_PitchTier_viewAndEdit);
 	praat_addAction1 (classPitchTier, 0, U"View & Edit with Sound?", nullptr, 0, HINT_PitchTier_Sound_viewAndEdit);
-	praat_addAction1 (classPitchTier, 0, U"Play pulses", nullptr, 0, PLAY_PitchTier_play);
-	praat_addAction1 (classPitchTier, 0, U"Hum", nullptr, 0, PLAY_PitchTier_hum);
-	praat_addAction1 (classPitchTier, 0, U"Play sine", nullptr, 0, PLAY_PitchTier_playSine);
+	praat_addAction1 (classPitchTier, 0, U"Play -", nullptr, 0, nullptr);
+	praat_addAction1 (classPitchTier, 0, U"Play pulses", nullptr, 1, PLAY_PitchTier_play);
+	praat_addAction1 (classPitchTier, 0, U"Hum", nullptr, 1, PLAY_PitchTier_hum);
+	praat_addAction1 (classPitchTier, 0, U"Play sine", nullptr, 1, PLAY_PitchTier_playSine);
 	praat_addAction1 (classPitchTier, 0, U"Draw...", nullptr, 0, GRAPHICS_PitchTier_draw);
 	praat_addAction1 (classPitchTier, 0, U"& Manipulation: Replace?", nullptr, 0, INFO_PitchTier_Manipulation_replace);
 	praat_addAction1 (classPitchTier, 0, U"Query -", nullptr, 0, nullptr);
@@ -1775,10 +1776,6 @@ void praat_Tiers_init () {
 	praat_addAction1 (classPointProcess, 0, U"Play -", nullptr, 0, nullptr);
 		praat_addAction1 (classPointProcess, 0, U"Play as pulse train", nullptr, 1, PLAY_PointProcess_play);
 		praat_addAction1 (classPointProcess, 0, U"Hum", nullptr, 1, PLAY_PointProcess_hum);
-		praat_addAction1 (classPointProcess, 0, U"-- to sound --", nullptr, 1, nullptr);
-		praat_addAction1 (classPointProcess, 0, U"To Sound (pulse train)...", nullptr, 1, NEW_PointProcess_to_Sound_pulseTrain);
-		praat_addAction1 (classPointProcess, 0, U"To Sound (phonation)...", nullptr, 1, NEW_PointProcess_to_Sound_phonation);
-		praat_addAction1 (classPointProcess, 0, U"To Sound (hum)", nullptr, 1, NEW_PointProcess_to_Sound_hum);
 	praat_addAction1 (classPointProcess, 0, U"Draw...", nullptr, 0, GRAPHICS_PointProcess_draw);
 	praat_addAction1 (classPointProcess, 0, U"Query -", nullptr, 0, nullptr);
 		praat_TimeFunction_query_init (classPointProcess);
@@ -1816,6 +1813,10 @@ void praat_Tiers_init () {
 	praat_addAction1 (classPointProcess, 0, U"Analyse -", nullptr, 0, nullptr);
 		praat_addAction1 (classPointProcess, 0, U"To PitchTier...", nullptr, 1, NEW_PointProcess_to_PitchTier);
 		praat_addAction1 (classPointProcess, 0, U"To TextGrid (vuv)...", nullptr, 1, NEW_PointProcess_to_TextGrid_vuv);
+	praat_addAction1 (classPointProcess, 0, U"Synthesize -", nullptr, 0, nullptr);
+		praat_addAction1 (classPointProcess, 0, U"To Sound (pulse train)...", nullptr, 1, NEW_PointProcess_to_Sound_pulseTrain);
+		praat_addAction1 (classPointProcess, 0, U"To Sound (phonation)...", nullptr, 1, NEW_PointProcess_to_Sound_phonation);
+		praat_addAction1 (classPointProcess, 0, U"To Sound (hum)", nullptr, 1, NEW_PointProcess_to_Sound_hum);
 	praat_addAction1 (classPointProcess, 0, U"Convert -", nullptr, 0, nullptr);
 		praat_addAction1 (classPointProcess, 0, U"Hack", nullptr, 1, nullptr);
 			praat_addAction1 (classPointProcess, 0, U"To Matrix", nullptr, 2, NEW_PointProcess_to_Matrix);
diff --git a/fon/praat_TimeTier.cpp b/fon/praat_TimeTier.cpp
index 324ab43..3aac761 100644
--- a/fon/praat_TimeTier.cpp
+++ b/fon/praat_TimeTier.cpp
@@ -42,7 +42,7 @@ FORM (INTEGER_TimeTier_getHighIndexFromTime, U"Get high index", U"AnyTier: Get h
 	OK
 DO
 	FIND_ONE (AnyTier)
-		Melder_information (my points.size == 0 ? U"--undefined--" : Melder_integer (AnyTier_timeToHighIndex (me, GET_REAL (U"Time"))));
+		Melder_information (my points.size == 0 ? U"--undefined--" : Melder_integer (AnyTier_timeToHighIndex (me, time)));
 	END
 }
 
@@ -51,7 +51,7 @@ FORM (INTEGER_TimeTier_getNearestIndexFromTime, U"Get nearest index", U"AnyTier:
 	OK
 DO
 	FIND_ONE (AnyTier)
-		Melder_information (my points.size == 0 ? U"--undefined--" : Melder_integer (AnyTier_timeToNearestIndex (me, GET_REAL (U"Time"))));
+		Melder_information (my points.size == 0 ? U"--undefined--" : Melder_integer (AnyTier_timeToNearestIndex (me, time)));
 	END
 }
 
diff --git a/gram/Network.cpp b/gram/Network.cpp
index 1dbfe5a..53a6a9f 100644
--- a/gram/Network.cpp
+++ b/gram/Network.cpp
@@ -71,7 +71,7 @@ void structNetwork :: v_info ()
 
 Thing_implement (Network, Daata, 6);
 
-void Network_init (Network me, double spreadingRate, enum kNetwork_activityClippingRule activityClippingRule,
+void Network_init (Network me, double spreadingRate, kNetwork_activityClippingRule activityClippingRule,
 	double minimumActivity, double maximumActivity, double activityLeak,
 	double learningRate, double minimumWeight, double maximumWeight, double weightLeak,
 	double xmin, double xmax, double ymin, double ymax, long numberOfNodes, long numberOfConnections)
@@ -97,7 +97,7 @@ void Network_init (Network me, double spreadingRate, enum kNetwork_activityClipp
 	my connections = NUMvector <structNetworkConnection> (1, numberOfConnections);
 }
 
-autoNetwork Network_create (double spreadingRate, enum kNetwork_activityClippingRule activityClippingRule,
+autoNetwork Network_create (double spreadingRate, kNetwork_activityClippingRule activityClippingRule,
 	double minimumActivity, double maximumActivity, double activityLeak,
 	double learningRate, double minimumWeight, double maximumWeight, double weightLeak,
 	double xmin, double xmax, double ymin, double ymax, long numberOfNodes, long numberOfConnections)
@@ -184,11 +184,11 @@ void Network_spreadActivities (Network me, long numberOfSteps) {
 			NetworkNode node = & my nodes [inode];
 			if (! node -> clamped) {
 				switch (my activityClippingRule) {
-					case kNetwork_activityClippingRule_SIGMOID:
+					case kNetwork_activityClippingRule::SIGMOID:
 						node -> activity = my minimumActivity +
 							(my maximumActivity - my minimumActivity) * NUMsigmoid (node -> excitation - 0.5 * (my minimumActivity + my maximumActivity));
 					break;
-					case kNetwork_activityClippingRule_LINEAR:
+					case kNetwork_activityClippingRule::LINEAR:
 						if (node -> excitation < my minimumActivity) {
 							node -> activity = my minimumActivity;
 						} else if (node -> excitation > my maximumActivity) {
@@ -197,7 +197,7 @@ void Network_spreadActivities (Network me, long numberOfSteps) {
 							node -> activity = node -> excitation;
 						}
 					break;
-					case kNetwork_activityClippingRule_TOP_SIGMOID:
+					case kNetwork_activityClippingRule::TOP_SIGMOID:
 						if (node -> excitation <= my minimumActivity) {
 							node -> activity = my minimumActivity;
 						} else {
diff --git a/gram/Network.h b/gram/Network.h
index 7215ac1..c999e61 100644
--- a/gram/Network.h
+++ b/gram/Network.h
@@ -2,7 +2,7 @@
 #define _Network_h_
 /* Network.h
  *
- * Copyright (C) 2009-2012,2013,2014 Paul Boersma
+ * Copyright (C) 2009-2012,2013,2014,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
 #include "Network_def.h"
 
 void Network_init (Network me,
-	double spreadingRate, enum kNetwork_activityClippingRule activityClippingRule,
+	double spreadingRate, kNetwork_activityClippingRule activityClippingRule,
 	double minimumActivity, double maximumActivity, double activityLeak,
 	double learningRate, double minimumWeight, double maximumWeight, double weightLeak,
 	double xmin, double xmax, double ymin, double ymax, long numberOfNodes, long numberOfConnections);
diff --git a/gram/Network_enums.h b/gram/Network_enums.h
index e25ebb7..05cf03f 100644
--- a/gram/Network_enums.h
+++ b/gram/Network_enums.h
@@ -1,6 +1,6 @@
 /* Network_enums.h
  *
- * Copyright (C) 2012,2015 Paul Boersma
+ * Copyright (C) 2012,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,6 +20,9 @@ enums_begin (kNetwork_activityClippingRule, 0)
 	enums_add (kNetwork_activityClippingRule, 0, SIGMOID, U"sigmoid")
 	enums_add (kNetwork_activityClippingRule, 1, LINEAR, U"linear")
 	enums_add (kNetwork_activityClippingRule, 2, TOP_SIGMOID, U"top-sigmoid")
+	/*
+		As this enumerated type occurs in data, you should add new activity clipping rules only at the end.
+	*/
 enums_end (kNetwork_activityClippingRule, 2, LINEAR)
 
 /* End of file Network_enums.h */
diff --git a/gram/OTGrammar.cpp b/gram/OTGrammar.cpp
index 6cbf264..377c5ff 100644
--- a/gram/OTGrammar.cpp
+++ b/gram/OTGrammar.cpp
@@ -171,7 +171,7 @@ void structOTGrammar :: v_readText (MelderReadText text, int formatVersion) {
 	OTGrammar_Parent :: v_readText (text, formatVersion);
 	if (formatVersion >= 1) {
 		try {
-			decisionStrategy = texgete8 (text, kOTGrammar_decisionStrategy_getValue);
+			decisionStrategy = (kOTGrammar_decisionStrategy) texgete8 (text, (enum_generic_getValue) kOTGrammar_decisionStrategy_getValue);
 		} catch (MelderError) {
 			Melder_throw (U"Trying to read decision strategy.");
 		}
@@ -345,31 +345,31 @@ long OTGrammar_getTableau (OTGrammar me, const char32 *input) {
 }
 
 static void _OTGrammar_fillInHarmonies (OTGrammar me, long itab) noexcept {
-	if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) return;
+	if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) return;
 	OTGrammarTableau tableau = & my tableaus [itab];
 	for (long icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
 		OTGrammarCandidate candidate = & tableau -> candidates [icand];
 		int *marks = candidate -> marks;
 		double disharmony = 0.0;
-		if (my decisionStrategy == kOTGrammar_decisionStrategy_HARMONIC_GRAMMAR ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY)
+		if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
 		{
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				disharmony += my constraints [icons]. disharmony * marks [icons];
 			}
-		} else if (my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+		} else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 		{
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				disharmony += exp (my constraints [icons]. disharmony) * marks [icons];
 			}
-		} else if (my decisionStrategy == kOTGrammar_decisionStrategy_LINEAR_OT) {
+		} else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				if (my constraints [icons]. disharmony > 0.0) {
 					disharmony += my constraints [icons]. disharmony * marks [icons];
 				}
 			}
-		} else if (my decisionStrategy == kOTGrammar_decisionStrategy_POSITIVE_HG) {
+		} else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
 				disharmony += constraintDisharmony * marks [icons];
@@ -384,7 +384,7 @@ static void _OTGrammar_fillInHarmonies (OTGrammar me, long itab) noexcept {
 int OTGrammar_compareCandidates (OTGrammar me, long itab1, long icand1, long itab2, long icand2) noexcept {
 	int *marks1 = my tableaus [itab1]. candidates [icand1]. marks;
 	int *marks2 = my tableaus [itab2]. candidates [icand2]. marks;
-	if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+	if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			int numberOfMarks1 = marks1 [my index [icons]];
 			int numberOfMarks2 = marks2 [my index [icons]];
@@ -401,8 +401,8 @@ int OTGrammar_compareCandidates (OTGrammar me, long itab1, long icand1, long ita
 		}
 		/* If we arrive here, None of the comparisons found a difference between the two candidates. Hence, they are equally good. */
 		return 0;
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_HARMONIC_GRAMMAR ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY)
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
 	{
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -411,7 +411,7 @@ int OTGrammar_compareCandidates (OTGrammar me, long itab1, long icand1, long ita
 		}
 		if (disharmony1 < disharmony2) return -1;   // candidate 1 is better than candidate 2
 		if (disharmony1 > disharmony2) return +1;   // candidate 2 is better than candidate 1
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_LINEAR_OT) {
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			if (my constraints [icons]. disharmony > 0.0) {
@@ -421,8 +421,8 @@ int OTGrammar_compareCandidates (OTGrammar me, long itab1, long icand1, long ita
 		}
 		if (disharmony1 < disharmony2) return -1;   // candidate 1 is better than candidate 2
 		if (disharmony1 > disharmony2) return +1;   // candidate 2 is better than candidate 1
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 	{
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -431,7 +431,7 @@ int OTGrammar_compareCandidates (OTGrammar me, long itab1, long icand1, long ita
 		}
 		if (disharmony1 < disharmony2) return -1;   // candidate 1 is better than candidate 2
 		if (disharmony1 > disharmony2) return +1;   // candidate 2 is better than candidate 1
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_POSITIVE_HG) {
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
@@ -472,8 +472,8 @@ static void _OTGrammar_fillInProbabilities (OTGrammar me, long itab) noexcept {
 
 long OTGrammar_getWinner (OTGrammar me, long itab) noexcept {
 	long icand_best = 1;
-	if (my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+	if (my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 	{
 		_OTGrammar_fillInHarmonies (me, itab);
 		_OTGrammar_fillInProbabilities (me, itab);
@@ -512,8 +512,8 @@ long OTGrammar_getWinner (OTGrammar me, long itab) noexcept {
 }
 
 long OTGrammar_getNumberOfOptimalCandidates (OTGrammar me, long itab) {
-	if (my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY) return 1;
+	if (my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY) return 1;
 	long icand_best = 1, icand, numberOfBestCandidates = 1;
 	for (icand = 2; icand <= my tableaus [itab]. numberOfCandidates; icand ++) {
 		int comparison = OTGrammar_compareCandidates (me, itab, icand, itab, icand_best);
@@ -699,7 +699,7 @@ static int OTGrammar_crucialCell (OTGrammar me, long itab, long icand, long iwin
 	int icons;
 	OTGrammarTableau tableau = & my tableaus [itab];
 	if (tableau -> numberOfCandidates < 2) return 0;   // if there is only one candidate, all cells can be greyed
-	if (my decisionStrategy != kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) return my numberOfConstraints;   // nothing grey
+	if (my decisionStrategy != kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) return my numberOfConstraints;   // nothing grey
 	if (OTGrammar_compareCandidates (me, itab, icand, itab, iwinner) == 0) {   // candidate equally good as winner?
 		if (numberOfOptimalCandidates > 1) {
 			/* All cells are important. */
@@ -913,7 +913,7 @@ void OTGrammar_drawTableau (OTGrammar me, Graphics g, bool vertical, const char3
 				double width = vertical ? rowHeight / worldAspectRatio : OTGrammar_constraintWidth (g, constraint -> name) + margin * 2;
 				static MelderString markString;
 				MelderString_empty (& markString);
-				if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+				if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 					/*
 					 * An exclamation mark can be drawn in this cell only if all of the following conditions are met:
 					 * 1. the candidate is not optimal;
@@ -958,11 +958,11 @@ void OTGrammar_drawTableau (OTGrammar me, Graphics g, bool vertical, const char3
 			/*
 			 * Draw harmony.
 			 */
-			if (my decisionStrategy != kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+			if (my decisionStrategy != kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 				Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
 				double value = tableau -> candidates [icand]. harmony;
-				if (my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-					my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+				if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+					my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 				{
 					//value = value > 1e-308 ? 1000 : value < -1e308 ? -1000 : - log (- value);
 					Graphics_text (g, x, y + descent, Melder_float (Melder_half (value)));
@@ -1243,7 +1243,7 @@ static void OTGrammar_honourLocalRankings (OTGrammar me, double plasticity, doub
 }
 
 static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, long iadult,
-	int updateRule, int honourLocalRankings,
+	kOTGrammar_rerankingStrategy updateRule, int honourLocalRankings,
 	double plasticity, double relativePlasticityNoise, int warnIfStalled, bool *grammarHasChanged)
 {
 	try {
@@ -1251,12 +1251,12 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 		OTGrammarCandidate winner = & tableau -> candidates [iwinner], adult = & tableau -> candidates [iadult];
 		double step = learningStep (plasticity, relativePlasticityNoise);
 		bool multiplyStepByNumberOfViolations =
-			my decisionStrategy == kOTGrammar_decisionStrategy_HARMONIC_GRAMMAR ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_LINEAR_OT ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_POSITIVE_HG ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY;
+			my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY;
 		if (Melder_debug != 0) {
 			/*
 			 * Perhaps override the standard update rule.
@@ -1264,7 +1264,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 			if (Melder_debug == 26) multiplyStepByNumberOfViolations = false;   // OT-GLA
 			else if (Melder_debug == 27) multiplyStepByNumberOfViolations = true;   // HG-GLA
 		}
-		if (updateRule == kOTGrammar_rerankingStrategy_SYMMETRIC_ONE) {
+		if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ONE) {
 			long icons = NUMrandomInteger (1, my numberOfConstraints);
 			OTGrammarConstraint constraint = & my constraints [icons];
 			double constraintStep = step * constraint -> plasticity;
@@ -1280,7 +1280,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 				constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
 				if (grammarHasChanged) *grammarHasChanged = true;
 			}
-		} else if (updateRule == kOTGrammar_rerankingStrategy_SYMMETRIC_ALL) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL) {
 			bool changed = false;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				OTGrammarConstraint constraint = & my constraints [icons];
@@ -1298,7 +1298,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 					changed = true;
 				}
 			}
-			if (changed && my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG)
+			if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG)
 			{
 				double sumOfWeights = 0.0;
 				for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -1310,7 +1310,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 				}
 			}
 			if (grammarHasChanged) *grammarHasChanged = changed;
-		} else if (updateRule == kOTGrammar_rerankingStrategy_SYMMETRIC_ALL_SKIPPABLE) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL_SKIPPABLE) {
 			bool changed = false;
 			int winningConstraints = 0, adultConstraints = 0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -1335,7 +1335,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 					changed = true;
 				}
 			}
-			if (changed && my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG)
+			if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG)
 			{
 				double sumOfWeights = 0.0;
 				for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -1347,7 +1347,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 				}
 			}
 			if (grammarHasChanged) *grammarHasChanged = changed;
-		} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_UNCANCELLED) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_UNCANCELLED) {
 			int winningConstraints = 0, adultConstraints = 0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				int winnerMarks = winner -> marks [icons];
@@ -1373,7 +1373,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 					if (grammarHasChanged) *grammarHasChanged = true;
 				}
 			}
-		} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL) {
 			int winningConstraints = 0, adultConstraints = 0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				int winnerMarks = winner -> marks [icons];
@@ -1397,7 +1397,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 					if (grammarHasChanged) *grammarHasChanged = true;
 				}
 			}
-		} else if (updateRule == kOTGrammar_rerankingStrategy_EDCD || updateRule == kOTGrammar_rerankingStrategy_EDCD_WITH_VACATION) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::EDCD || updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
 			/*
 			 * Determine the crucial winner mark.
 			 */
@@ -1420,7 +1420,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 			 * Determine the stratum into which some constraints will be demoted.
 			 */
 			pivotRanking = my constraints [my index [icons]]. ranking;
-			if (updateRule == kOTGrammar_rerankingStrategy_EDCD_WITH_VACATION) {
+			if (updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
 				long numberOfConstraintsToDemote = 0;
 				for (icons = 1; icons <= my numberOfConstraints; icons ++) {
 					int winnerMarks = winner -> marks [icons];
@@ -1460,7 +1460,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 					}
 				}
 			}
-		} else if (updateRule == kOTGrammar_rerankingStrategy_DEMOTION_ONLY) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::DEMOTION_ONLY) {
 			/*
 			 * Determine the crucial adult mark.
 			 */
@@ -1486,7 +1486,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 			double constraintStep = step * offendingConstraint -> plasticity;
 			offendingConstraint -> ranking -= constraintStep;
 			if (grammarHasChanged) *grammarHasChanged = true;
-		} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGHEST_DOWN) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN) {
 			long numberOfUp = 0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				int winnerMarks = winner -> marks [icons];
@@ -1531,7 +1531,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 				offendingConstraint -> ranking -= /*numberOfUp **/ constraintStep * (1.0 - offendingConstraint -> ranking * my leak);
 				if (grammarHasChanged) *grammarHasChanged = true;
 			}
-		} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGHEST_DOWN_2012) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN_2012) {
 			long numberOfUp = 0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				int winnerMarks = winner -> marks [icons];
@@ -1576,7 +1576,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 				offendingConstraint -> ranking -= /*numberOfUp **/ constraintStep * (1.0 - offendingConstraint -> ranking * my leak);
 				if (grammarHasChanged) *grammarHasChanged = true;
 			}
-		} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGH_DOWN) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN) {
 			long numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				int winnerMarks = winner -> marks [my index [icons]];   // the order is important, therefore indirect
@@ -1616,7 +1616,7 @@ static void OTGrammar_modifyRankings (OTGrammar me, long itab, long iwinner, lon
 				}
 				if (grammarHasChanged) *grammarHasChanged = true;
 			}
-		} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGH_DOWN_2012) {
+		} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN_2012) {
 			long numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				int winnerMarks = winner -> marks [my index [icons]];   // the order is important, therefore indirect
@@ -1796,10 +1796,10 @@ bool OTGrammar_PairDistribution_findPositiveWeights_e (OTGrammar me, PairDistrib
 	NUMlinprog linprog = nullptr;
 	try {
 		bool result = false;
-		if (my decisionStrategy != kOTGrammar_decisionStrategy_HARMONIC_GRAMMAR &&
-			my decisionStrategy != kOTGrammar_decisionStrategy_LINEAR_OT &&
-			my decisionStrategy != kOTGrammar_decisionStrategy_POSITIVE_HG &&
-			my decisionStrategy != kOTGrammar_decisionStrategy_EXPONENTIAL_HG)
+		if (my decisionStrategy != kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR &&
+			my decisionStrategy != kOTGrammar_decisionStrategy::LINEAR_OT &&
+			my decisionStrategy != kOTGrammar_decisionStrategy::POSITIVE_HG &&
+			my decisionStrategy != kOTGrammar_decisionStrategy::EXPONENTIAL_HG)
 		{
 			Melder_throw (U"To find positive weights, the decision strategy has to be HarmonicGrammar, LinearOT, PositiveHG, or ExponentialHG.");
 		}
@@ -1852,7 +1852,7 @@ bool OTGrammar_PairDistribution_findPositiveWeights_e (OTGrammar me, PairDistrib
 			double weighting = NUMlinprog_getPrimalValue (linprog, icons);
 			Melder_assert (weighting >= weightFloor);
 			my constraints [icons]. ranking = my constraints [icons]. disharmony =
-				my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ? log (weighting) : weighting;
+				my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ? log (weighting) : weighting;
 		}
 		NUMlinprog_delete (linprog);
 		return result;
@@ -1960,7 +1960,7 @@ void OTGrammar_learnOneFromPartialOutput (OTGrammar me, const char32 *partialAdu
 {
 	try {
 		OTGrammar_newDisharmonies (me, evaluationNoise);
-		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy_EDCD) {
+		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD) {
 			OTGrammar_save (me);
 		}
 		long ichew = 1;
@@ -1975,7 +1975,7 @@ void OTGrammar_learnOneFromPartialOutput (OTGrammar me, const char32 *partialAdu
 				plasticity, relativePlasticityNoise, Melder_debug == 47, warnIfStalled, & grammarHasChanged);
 			if (! grammarHasChanged) return;
 		}
-		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy_EDCD && ichew > numberOfChews) {
+		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD && ichew > numberOfChews) {
 			/*
 			 * Is the partial output form grammatical by now?
 			 */
@@ -2003,7 +2003,7 @@ static void OTGrammar_learnOneFromPartialOutput_opt (OTGrammar me, const char32
 {
 	try {
 		OTGrammar_newDisharmonies (me, evaluationNoise);
-		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy_EDCD) {
+		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD) {
 			OTGrammar_save (me);
 		}
 		long ichew = 1;
@@ -2053,7 +2053,7 @@ static void OTGrammar_learnOneFromPartialOutput_opt (OTGrammar me, const char32
 				plasticity, relativePlasticityNoise, warnIfStalled, & grammarHasChanged);
 			if (! grammarHasChanged) return;
 		}
-		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy_EDCD && ichew > numberOfChews) {
+		if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD && ichew > numberOfChews) {
 			/*
 			 * Is the partial output form grammatical by now?
 			 */
@@ -2476,7 +2476,7 @@ static bool OTGrammarTableau_candidateIsPossibleWinner (OTGrammar me, long itab,
 	for (;;) {
 		bool grammarHasChanged = false;
 		OTGrammar_learnOne (me, my tableaus [itab]. input, my tableaus [itab]. candidates [icand]. output,
-			1e-3, kOTGrammar_rerankingStrategy_EDCD, false, 1.0, 0.0, true, true, & grammarHasChanged);
+			1e-3, kOTGrammar_rerankingStrategy::EDCD, false, 1.0, 0.0, true, true, & grammarHasChanged);
 		if (! grammarHasChanged) {
 			OTGrammar_restore (me);
 			return true;
@@ -2580,7 +2580,7 @@ void OTGrammar_PairDistribution_listObligatoryRankings (OTGrammar me, PairDistri
 				if (prob -> weight > 0.0) {
 					bool grammarHasChanged = false;
 					OTGrammar_learnOne (me, prob -> string1, prob -> string2,
-						evaluationNoise, kOTGrammar_rerankingStrategy_EDCD, true /* honour fixed rankings; very important */,
+						evaluationNoise, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
 						1.0, 0.0, false, true, & grammarHasChanged);
 					if (grammarHasChanged) {
 						OTGrammar_newDisharmonies (me, evaluationNoise);
@@ -2618,7 +2618,7 @@ void OTGrammar_PairDistribution_listObligatoryRankings (OTGrammar me, PairDistri
 						if (prob -> weight > 0.0) {
 							bool grammarHasChanged = false;
 							OTGrammar_learnOne (me, prob -> string1, prob -> string2,
-								evaluationNoise, kOTGrammar_rerankingStrategy_EDCD, true /* honour fixed rankings; very important */,
+								evaluationNoise, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
 								1.0, 0.0, false, true, & grammarHasChanged);
 							if (grammarHasChanged) {
 								OTGrammar_newDisharmonies (me, evaluationNoise);
@@ -2664,7 +2664,7 @@ void OTGrammar_PairDistribution_listObligatoryRankings (OTGrammar me, PairDistri
 								if (prob -> weight > 0.0) {
 									bool grammarHasChanged = false;
 									OTGrammar_learnOne (me, prob -> string1, prob -> string2,
-										evaluationNoise, kOTGrammar_rerankingStrategy_EDCD, true /* honour fixed rankings; very important */,
+										evaluationNoise, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
 										1.0, 0.0, false, true, & grammarHasChanged);
 									if (grammarHasChanged) {
 										OTGrammar_newDisharmonies (me, evaluationNoise);
@@ -2798,7 +2798,7 @@ void OTGrammar_Distributions_listObligatoryRankings (OTGrammar me, Distributions
 				ipair ++;
 				Melder_progressOff ();
 				OTGrammar_Distributions_learnFromPartialOutputs (me, thee, columnNumber,
-					1e-9, kOTGrammar_rerankingStrategy_EDCD, true /* honour fixed rankings; very important */,
+					1e-9, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
 					1.0, 1000, 0.0, 1, 0.0, 1, 0, nullptr, false, false, 0);
 				Melder_progressOn ();
 				for (kcons = 1; kcons <= my numberOfConstraints; kcons ++) {
diff --git a/gram/OTGrammarEditor.cpp b/gram/OTGrammarEditor.cpp
index 5fcb761..4510962 100644
--- a/gram/OTGrammarEditor.cpp
+++ b/gram/OTGrammarEditor.cpp
@@ -1,6 +1,6 @@
 /* OTGrammarEditor.cpp
  *
- * Copyright (C) 1997-2011,2012,2013,2015,2016 Paul Boersma
+ * Copyright (C) 1997-2011,2012,2013,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -96,7 +96,7 @@ static void menu_cb_learnOne (OTGrammarEditor me, EDITOR_ARGS_FORM) {
 		LABEL (U"", U"Adult surface form:")
 		SENTENCE (U"Output string", U"")
 		REAL (U"Evaluation noise", U"2.0")
-		OPTIONMENU_ENUM (U"Update rule", kOTGrammar_rerankingStrategy, kOTGrammar_rerankingStrategy_SYMMETRIC_ALL)
+		OPTIONMENU_ENUM (U"Update rule", kOTGrammar_rerankingStrategy, kOTGrammar_rerankingStrategy::SYMMETRIC_ALL)
 		REAL (U"Plasticity", U"0.1")
 		REAL (U"Rel. plasticity spreading", U"0.1")
 		BOOLEAN (U"Honour local rankings", 1)
@@ -113,11 +113,11 @@ static void menu_cb_learnOne (OTGrammarEditor me, EDITOR_ARGS_FORM) {
 }
 
 static void menu_cb_learnOneFromPartialOutput (OTGrammarEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Learn one from partial adult output", 0)
+	EDITOR_FORM (U"Learn one from partial adult output", nullptr)
 		LABEL (U"", U"Partial adult surface form (e.g. overt form):")
 		SENTENCE (U"Partial output", U"")
 		REAL (U"Evaluation noise", U"2.0")
-		OPTIONMENU_ENUM (U"Update rule", kOTGrammar_rerankingStrategy, kOTGrammar_rerankingStrategy_SYMMETRIC_ALL)
+		OPTIONMENU_ENUM (U"Update rule", kOTGrammar_rerankingStrategy, kOTGrammar_rerankingStrategy::SYMMETRIC_ALL)
 		REAL (U"Plasticity", U"0.1")
 		REAL (U"Rel. plasticity spreading", U"0.1")
 		BOOLEAN (U"Honour local rankings", 1)
@@ -147,7 +147,7 @@ static void menu_cb_removeConstraint (OTGrammarEditor me, EDITOR_ARGS_DIRECT) {
 }
 
 static void menu_cb_resetAllRankings (OTGrammarEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Reset all rankings", 0)
+	EDITOR_FORM (U"Reset all rankings", nullptr)
 		REAL (U"Ranking", U"100.0")
 	EDITOR_OK
 	EDITOR_DO
@@ -195,8 +195,8 @@ void structOTGrammarEditor :: v_draw () {
 	OTGrammar ot = (OTGrammar) data;
 	static char32 text [1000];
 	Graphics_clearWs (graphics.get());
-	if (ot -> decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-		ot -> decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+	if (ot -> decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+		ot -> decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 	{
 		HyperPage_listItem (this, U"\t\t      %%ranking value\t      %disharmony\t      %plasticity\t   %%e^^disharmony");
 	} else {
@@ -204,8 +204,8 @@ void structOTGrammarEditor :: v_draw () {
 	}
 	for (long icons = 1; icons <= ot -> numberOfConstraints; icons ++) {
 		OTGrammarConstraint constraint = & ot -> constraints [ot -> index [icons]];
-		if (ot -> decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-			ot -> decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+		if (ot -> decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+			ot -> decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 		{
 			Melder_sprint (text,1000,
 				U"\t", icons == selected ? U"♠︎ " : U"   ",
diff --git a/gram/OTGrammar_enums.h b/gram/OTGrammar_enums.h
index 7d33f61..cbfcedd 100644
--- a/gram/OTGrammar_enums.h
+++ b/gram/OTGrammar_enums.h
@@ -1,6 +1,6 @@
 /* OTGrammar_enums.h
  *
- * Copyright (C) 2006-2011,2013,2014,2015 Paul Boersma
+ * Copyright (C) 2006-2011,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,6 +24,9 @@ enums_begin (kOTGrammar_decisionStrategy, 0)
 	enums_add (kOTGrammar_decisionStrategy, 4, MAXIMUM_ENTROPY, U"MaximumEntropy")
 	enums_add (kOTGrammar_decisionStrategy, 5, POSITIVE_HG, U"PositiveHG")
 	enums_add (kOTGrammar_decisionStrategy, 6, EXPONENTIAL_MAXIMUM_ENTROPY, U"ExponentialMaximumEntropy")
+	/*
+		As this enumerated type occurs in data, you should add new decision strategies only at the end.
+	*/
 enums_end (kOTGrammar_decisionStrategy, 6, OPTIMALITY_THEORY)
 
 enums_begin (kOTGrammar_rerankingStrategy, 0)
diff --git a/gram/OTMulti.cpp b/gram/OTMulti.cpp
index a69d7fa..d3d0612 100644
--- a/gram/OTMulti.cpp
+++ b/gram/OTMulti.cpp
@@ -105,7 +105,7 @@ void structOTMulti :: v_readText (MelderReadText text, int formatVersion) {
 	OTMulti_Parent :: v_readText (text, formatVersion);
 	if (formatVersion >= 1) {
 		try {
-			decisionStrategy = texgete8 (text, kOTGrammar_decisionStrategy_getValue);
+			decisionStrategy = (kOTGrammar_decisionStrategy) texgete8 (text, (enum_generic_getValue) kOTGrammar_decisionStrategy_getValue);
 		} catch (MelderError) {
 			Melder_throw (U"Decision strategy not read.");
 		}
@@ -199,7 +199,7 @@ void OTMulti_newDisharmonies (OTMulti me, double evaluationNoise) {
 int OTMulti_compareCandidates (OTMulti me, long icand1, long icand2) {
 	int *marks1 = my candidates [icand1]. marks;
 	int *marks2 = my candidates [icand2]. marks;
-	if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+	if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			int numberOfMarks1 = marks1 [my index [icons]];
 			int numberOfMarks2 = marks2 [my index [icons]];
@@ -214,8 +214,8 @@ int OTMulti_compareCandidates (OTMulti me, long icand1, long icand2) {
 			if (numberOfMarks1 < numberOfMarks2) return -1;   /* Candidate 1 is better than candidate 2. */
 			if (numberOfMarks1 > numberOfMarks2) return +1;   /* Candidate 2 is better than candidate 1. */
 		}
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_HARMONIC_GRAMMAR ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY)
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
 	{
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -224,7 +224,7 @@ int OTMulti_compareCandidates (OTMulti me, long icand1, long icand2) {
 		}
 		if (disharmony1 < disharmony2) return -1;   /* Candidate 1 is better than candidate 2. */
 		if (disharmony1 > disharmony2) return +1;   /* Candidate 2 is better than candidate 1. */
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_LINEAR_OT) {
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			if (my constraints [icons]. disharmony > 0.0) {
@@ -234,8 +234,8 @@ int OTMulti_compareCandidates (OTMulti me, long icand1, long icand2) {
 		}
 		if (disharmony1 < disharmony2) return -1;   /* Candidate 1 is better than candidate 2. */
 		if (disharmony1 > disharmony2) return +1;   /* Candidate 2 is better than candidate 1. */
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 	{
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -244,7 +244,7 @@ int OTMulti_compareCandidates (OTMulti me, long icand1, long icand2) {
 		}
 		if (disharmony1 < disharmony2) return -1;   /* Candidate 1 is better than candidate 2. */
 		if (disharmony1 > disharmony2) return +1;   /* Candidate 2 is better than candidate 1. */
-	} else if (my decisionStrategy == kOTGrammar_decisionStrategy_POSITIVE_HG) {
+	} else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
 		double disharmony1 = 0.0, disharmony2 = 0.0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
@@ -265,30 +265,30 @@ int OTMulti_candidateMatches (OTMulti me, long icand, const char32 *form1, const
 }
 
 static void _OTMulti_fillInHarmonies (OTMulti me, const char32 *form1, const char32 *form2) {
-	if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) return;
+	if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) return;
 	for (long icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
 		OTCandidate candidate = & my candidates [icand];
 		int *marks = candidate -> marks;
 		double disharmony = 0.0;
-		if (my decisionStrategy == kOTGrammar_decisionStrategy_HARMONIC_GRAMMAR ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY)
+		if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
 		{
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				disharmony += my constraints [icons]. disharmony * marks [icons];
 			}
-		} else if (my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+		} else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 		{
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				disharmony += exp (my constraints [icons]. disharmony) * marks [icons];
 			}
-		} else if (my decisionStrategy == kOTGrammar_decisionStrategy_LINEAR_OT) {
+		} else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				if (my constraints [icons]. disharmony > 0.0) {
 					disharmony += my constraints [icons]. disharmony * marks [icons];
 				}
 			}
-		} else if (my decisionStrategy == kOTGrammar_decisionStrategy_POSITIVE_HG) {
+		} else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
 				disharmony += constraintDisharmony * marks [icons];
@@ -330,8 +330,8 @@ class MelderError_OTMulti_NoMatchingCandidate: public MelderError {};
 long OTMulti_getWinner (OTMulti me, const char32 *form1, const char32 *form2) {
 	try {
 		long icand_best = 0;
-		if (my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY ||
-			my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY)
+		if (my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
+			my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
 		{
 			_OTMulti_fillInHarmonies (me, form1, form2);
 			_OTMulti_fillInProbabilities (me, form1, form2);
@@ -382,7 +382,7 @@ long OTMulti_getWinner (OTMulti me, const char32 *form1, const char32 *form2) {
 }
 
 static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
-	enum kOTGrammar_rerankingStrategy updateRule,
+	kOTGrammar_rerankingStrategy updateRule,
 	double plasticity, double relativePlasticityNoise)
 {
 	bool *grammarHasChanged = nullptr;   // to be implemented
@@ -391,12 +391,12 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 	OTCandidate winner = & my candidates [iwinner], loser = & my candidates [iloser];
 	double step = relativePlasticityNoise == 0.0 ? plasticity : NUMrandomGauss (plasticity, relativePlasticityNoise * plasticity);
 	bool multiplyStepByNumberOfViolations =
-		my decisionStrategy == kOTGrammar_decisionStrategy_HARMONIC_GRAMMAR ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_LINEAR_OT ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_MAXIMUM_ENTROPY ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_POSITIVE_HG ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG ||
-		my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_MAXIMUM_ENTROPY;
+		my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
+		my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY;
 	if (Melder_debug != 0) {
 		/*
 		 * Perhaps override the standard update rule.
@@ -404,7 +404,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 		if (Melder_debug == 26) multiplyStepByNumberOfViolations = false;   // OT-GLA
 		else if (Melder_debug == 27) multiplyStepByNumberOfViolations = true;   // HG-GLA
 	}
-	if (updateRule == kOTGrammar_rerankingStrategy_SYMMETRIC_ONE) {
+	if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ONE) {
 		long icons = NUMrandomInteger (1, my numberOfConstraints);
 		OTConstraint constraint = & my constraints [icons];
 		double constraintStep = step * constraint -> plasticity;
@@ -420,7 +420,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
 			if (grammarHasChanged) *grammarHasChanged = true;
 		}
-	} else if (updateRule == kOTGrammar_rerankingStrategy_SYMMETRIC_ALL) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL) {
 		bool changed = false;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			OTConstraint constraint = & my constraints [icons];
@@ -438,7 +438,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				changed = true;
 			}
 		}
-		if (changed && my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG) {
+		if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG) {
 			double sumOfWeights = 0.0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				sumOfWeights += my constraints [icons]. ranking;
@@ -449,7 +449,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			}
 		}
 		if (grammarHasChanged) *grammarHasChanged = changed;
-	} else if (updateRule == kOTGrammar_rerankingStrategy_SYMMETRIC_ALL_SKIPPABLE) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL_SKIPPABLE) {
 		bool changed = false;
 		int winningConstraints = 0, losingConstraints = 0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -474,7 +474,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				changed = true;
 			}
 		}
-		if (changed && my decisionStrategy == kOTGrammar_decisionStrategy_EXPONENTIAL_HG) {
+		if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG) {
 			double sumOfWeights = 0.0;
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 				sumOfWeights += my constraints [icons]. ranking;
@@ -485,7 +485,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			}
 		}
 		if (grammarHasChanged) *grammarHasChanged = changed;
-	} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_UNCANCELLED) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_UNCANCELLED) {
 		int winningConstraints = 0, losingConstraints = 0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			int winnerMarks = winner -> marks [icons];
@@ -513,7 +513,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				}
 			}
 		}
-	} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL) {
 		int winningConstraints = 0, losingConstraints = 0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			int winnerMarks = winner -> marks [icons];
@@ -537,7 +537,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				if (grammarHasChanged) *grammarHasChanged = true;
 			}
 		}
-	} else if (updateRule == kOTGrammar_rerankingStrategy_EDCD || updateRule == kOTGrammar_rerankingStrategy_EDCD_WITH_VACATION) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::EDCD || updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
 		/*
 		 * Determine the crucial winner mark.
 		 */
@@ -560,7 +560,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 		 * Determine the stratum into which some constraints will be demoted.
 		 */
 		pivotRanking = my constraints [my index [icons]]. ranking;
-		if (updateRule == kOTGrammar_rerankingStrategy_EDCD_WITH_VACATION) {
+		if (updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
 			long numberOfConstraintsToDemote = 0;
 			for (icons = 1; icons <= my numberOfConstraints; icons ++) {
 				int winnerMarks = winner -> marks [icons];
@@ -600,7 +600,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				}
 			}
 		}
-	} else if (updateRule == kOTGrammar_rerankingStrategy_DEMOTION_ONLY) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::DEMOTION_ONLY) {
 		/*
 		 * Determine the crucial loser mark.
 		 */
@@ -613,7 +613,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			if (my constraints [my index [icons]]. tiedToTheRight)
 				Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
 			if (loserMarks < winnerMarks) {
-				if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+				if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 					Melder_throw (U"Demotion-only learning step: Loser wins! Should never happen.");
 				} else {
 					// do nothing; the whole demotion-only idea does not really apply very well to non-OT decision strategies
@@ -622,7 +622,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			if (loserMarks > winnerMarks) break;
 		}
 		if (icons > my numberOfConstraints) {   // completed the loop?
-			if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+			if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 				Melder_throw (U"(OTGrammar_step:) Loser equals correct candidate: loser \"", loser -> string, U"\", winner \"", winner -> string, U"\".");
 			} else {
 				// do nothing
@@ -637,7 +637,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			offendingConstraint -> ranking -= constraintStep;
 			if (grammarHasChanged) *grammarHasChanged = true;
 		}
-	} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGHEST_DOWN) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN) {
 		bool changed = false;
 		long numberOfUp = 0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -667,7 +667,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				if (my constraints [my index [icons]]. tiedToTheRight)
 					Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
 				if (loserMarks < winnerMarks) {
-					if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+					if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 						Melder_throw (U"Demotion-only learning step: Loser wins! Should never happen.");
 					} else {
 						// do nothing; the whole demotion-only idea does not really apply very well to non-OT decision strategies
@@ -676,7 +676,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				if (loserMarks > winnerMarks) break;
 			}
 			if (icons > my numberOfConstraints) {   // completed the loop?
-				if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+				if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 					Melder_throw (U"(OTGrammar_step:) Loser equals correct candidate: loser \"", loser -> string, U"\", winner \"", winner -> string, U"\".");
 				} else {
 					// do nothing
@@ -693,7 +693,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			}
 		}
 		if (grammarHasChanged) *grammarHasChanged = changed;
-	} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGHEST_DOWN_2012) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN_2012) {
 		bool changed = false;
 		long numberOfUp = 0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -723,7 +723,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				if (my constraints [my index [icons]]. tiedToTheRight)
 					Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
 				if (loserMarks < winnerMarks) {
-					if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+					if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 						Melder_throw (U"Demotion-only learning step: Loser wins! Should never happen.");
 					} else {
 						// do nothing; the whole demotion-only idea does not really apply very well to non-OT decision strategies
@@ -732,7 +732,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 				if (loserMarks > winnerMarks) break;
 			}
 			if (icons > my numberOfConstraints) {   // completed the loop?
-				if (my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+				if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 					Melder_throw (U"(OTGrammar_step:) Loser equals correct candidate: loser \"", loser -> string, U"\", winner \"", winner -> string, U"\".");
 				} else {
 					// do nothing
@@ -749,7 +749,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			}
 		}
 		if (grammarHasChanged) *grammarHasChanged = changed;
-	} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGH_DOWN) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN) {
 		long numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			int winnerMarks = winner -> marks [my index [icons]];   // the order is important, therefore indirect
@@ -784,7 +784,7 @@ static void OTMulti_modifyRankings (OTMulti me, long iwinner, long iloser,
 			}
 			if (grammarHasChanged) *grammarHasChanged = true;
 		}
-	} else if (updateRule == kOTGrammar_rerankingStrategy_WEIGHTED_ALL_UP_HIGH_DOWN_2012) {
+	} else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN_2012) {
 		long numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
 		for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
 			int winnerMarks = winner -> marks [my index [icons]];   // the order is important, therefore indirect
@@ -1202,7 +1202,7 @@ void OTMulti_drawTableau (OTMulti me, Graphics g, const char32 *form1, const cha
 		/*
 		 * Draw grey cell backgrounds.
 		 */
-		if (! bidirectional && my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY) {
+		if (! bidirectional && my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
 			x = candWidth + 2 * doubleLineDx;
 			Graphics_setGrey (g, 0.9);
 			for (long icons = 1; icons <= my numberOfConstraints; icons ++) {
@@ -1245,7 +1245,7 @@ void OTMulti_drawTableau (OTMulti me, Graphics g, const char32 *form1, const cha
 			 * 2. this is the crucial cell, i.e. the cells after it are drawn in grey.
 			 */
 			if (! bidirectional && icons == crucialCell && ! candidateIsOptimal &&
-			    my decisionStrategy == kOTGrammar_decisionStrategy_OPTIMALITY_THEORY)
+			    my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY)
 			{
 				int winnerMarks = my candidates [winner]. marks [index];
 				if (winnerMarks + 1 > 5) {
diff --git a/gram/OTMultiEditor.cpp b/gram/OTMultiEditor.cpp
index 25f6598..ea028a3 100644
--- a/gram/OTMultiEditor.cpp
+++ b/gram/OTMultiEditor.cpp
@@ -83,7 +83,7 @@ static void menu_cb_editRanking (OTMultiEditor me, EDITOR_ARGS_FORM) {
 
 static void menu_cb_learnOne (OTMultiEditor me, EDITOR_ARGS_FORM) {
 	EDITOR_FORM (U"Learn one", U"OTGrammar: Learn one...")
-		OPTIONMENU_ENUM (U"Update rule", kOTGrammar_rerankingStrategy, kOTGrammar_rerankingStrategy_SYMMETRIC_ALL)
+		OPTIONMENU_ENUM (U"Update rule", kOTGrammar_rerankingStrategy, kOTGrammar_rerankingStrategy::SYMMETRIC_ALL)
 		OPTIONMENU (U"Direction", 3)
 			OPTION (U"forward")
 			OPTION (U"backward")
diff --git a/main/main_Praat.cpp b/main/main_Praat.cpp
index 04efa12..6115e13 100644
--- a/main/main_Praat.cpp
+++ b/main/main_Praat.cpp
@@ -1,6 +1,6 @@
 /* main_Praat.cpp
  *
- * Copyright (C) 1992-2012,2013,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2012,2013,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
 static void logo (Graphics graphics) {
 	Graphics_setWindow (graphics, 0.0, 1.0, 0.0, 0.8);
 	Graphics_setTextAlignment (graphics, Graphics_CENTRE, Graphics_HALF);
-	Graphics_setFont (graphics, kGraphics_font_TIMES);
+	Graphics_setFont (graphics, kGraphics_font::TIMES);
 	Graphics_setFontSize (graphics, 45);
 	Graphics_setColour (graphics, Graphics_MAROON);
 	Graphics_text (graphics, 0.385, 0.66, U"P");
@@ -38,7 +38,7 @@ static void logo (Graphics graphics) {
 	Graphics_setColour (graphics, Graphics_BLACK);
 	Graphics_setFontSize (graphics, 14);
 	Graphics_text (graphics, 0.5, 0.33, U"www.praat.org");
-	Graphics_setFont (graphics, kGraphics_font_HELVETICA);
+	Graphics_setFont (graphics, kGraphics_font::HELVETICA);
 	Graphics_setFontSize (graphics, 10);
 	Graphics_text (graphics, 0.5, 0.16, U"Copyright © 1992–" xstr(PRAAT_YEAR) " by Paul Boersma and David Weenink");
 }
diff --git a/num/NUMarrays.cpp b/num/NUMarrays.cpp
index cdf6f07..36cffff 100644
--- a/num/NUMarrays.cpp
+++ b/num/NUMarrays.cpp
@@ -306,7 +306,7 @@ FUNCTION (unsigned int, u16)
 FUNCTION (unsigned long, u32)
 FUNCTION (double, r32)
 FUNCTION (double, r64)
-FUNCTION (fcomplex, c64)
+FUNCTION (dcomplex, c64)
 FUNCTION (dcomplex, c128)
 #undef FUNCTION
 
diff --git a/num/NUMsort.cpp b/num/NUMsort.cpp
index 205c0da..f8b2ab1 100644
--- a/num/NUMsort.cpp
+++ b/num/NUMsort.cpp
@@ -16,13 +16,6 @@
  * along with this work. If not, see <http://www.gnu.org/licenses/>.
  */
 
-/*
- * pb 2002/06/24 removed NUMselect
- * pb 2007/08/10 NUMsort_strW
- * pb 2008/01/21 double
- * pb 2011/03/29 C++
- */
-
 #include "melder.h"
 
 /*
@@ -38,63 +31,67 @@
 	Modification: there is no distinction between record and key.
 */
 
-#define MACRO_NUMsort(TYPE)  { \
-	long l, r, j, i; \
-	TYPE k; \
-	if (n < 2) return;   /* Already sorted. */ \
-	/* This n<2 step is absent from Press et al.'s implementation, */ \
-	/* which will therefore not terminate on if(--ir==1). */ \
-	/* Knuth's initial assumption is now fulfilled: n >= 2. */ \
-if (n == 2) { \
-	if (a [1] > a [2]) { TYPE min = a [2]; a [2] = a [1]; a [1] = min; } \
-	return; \
-} \
-if (n <= 12) { \
-	for (i = 1; i < n; i ++) { \
-		TYPE min = a [i]; \
-		long pmin = i; \
-		for (j = i + 1; j <= n; j ++) if (a [j] < min) { \
-			min = a [j]; \
-			pmin = j; \
-		} \
-		a [pmin] = a [i]; \
-		a [i] = min; \
-	} \
-	return; \
-} \
-	l = (n >> 1) + 1; \
-	r = n; \
-	for (;;) { \
-		if (l > 1) { \
-			l --; \
-			k = a [l]; \
-		} else /* l == 1 */ { \
-			k = a [r]; \
-			a [r] = a [1]; \
-			r --; \
-			if (r == 1) { a [1] = k; return; } \
-		} \
-		j = l; \
-		for (;;) { \
-			i = j; \
-			j = j << 1; \
-			if (j > r) break; \
-			if (j < r && a [j] < a [j + 1]) j ++; \
-			if (k >= a [j]) break; \
-			a [i] = a [j]; \
+#define MACRO_NUMsort(DataType, dataExpression, CounterType, sizeExpression) \
+	{/* scope */ \
+		DataType *_x = dataExpression; \
+		CounterType _n = sizeExpression; \
+		if (_n < 2) return;   /* Already sorted. */ \
+		/* This `n < 2` step is absent from Press et al.'s implementation,      */ \
+		/* which will therefore not terminate on `if (_r == 1)`.                */ \
+		/* Knuth's initial assumption is now fulfilled: n >= 2.                 */ \
+		if (_n == 2) { \
+			if (_x [1] > _x [2]) { DataType _min = _x [2]; _x [2] = _x [1]; _x [1] = _min; } \
+		} else if (_n <= 44) { \
+			for (CounterType _i = 1; _i < _n; _i ++) { \
+				DataType _min = _x [_i]; \
+				CounterType _pmin = _i; \
+				for (CounterType _j = _i + 1; _j <= _n; _j ++) if (_x [_j] < _min) { \
+					_min = _x [_j]; \
+					_pmin = _j; \
+				} \
+				_x [_pmin] = _x [_i]; \
+				_x [_i] = _min; \
+			} \
+		} else { \
+			CounterType _l = (_n >> 1) + 1; \
+			CounterType _r = _n; \
+			for (;;) { \
+				DataType _k; \
+				if (_l > 1) { \
+					_l --; \
+					_k = _x [_l]; \
+				} else /* _l == 1 */ { \
+					_k = _x [_r]; \
+					_x [_r] = _x [1]; \
+					_r --; \
+					if (_r == 1) { _x [1] = _k; return; } \
+				} \
+				CounterType _j = _l; \
+				CounterType _i; \
+				for (;;) { \
+					_i = _j; \
+					_j = _j << 1; \
+					if (_j > _r) break; \
+					if (_j < _r && _x [_j] < _x [_j + 1]) _j ++; \
+					if (_k >= _x [_j]) break; \
+					_x [_i] = _x [_j]; \
+				} \
+				_x [_i] = _k; \
+			} \
 		} \
-		a [i] = k; \
-	} \
-}
+	}
 
-void NUMsort_d (long n, double a [])
-	MACRO_NUMsort (double)
+void NUMsort_d (long n, double a []) {
+	MACRO_NUMsort (double, a, integer, n)
+}
 
-void NUMsort_i (long n, int a [])
-	MACRO_NUMsort (int)
+void NUMsort_i (long n, int a []) {
+	MACRO_NUMsort (int, a, integer, n)
+}
 
-void NUMsort_l (long n, long a [])
-	MACRO_NUMsort (long)
+void NUMsort_l (long n, long a []) {
+	MACRO_NUMsort (long, a, integer, n)
+}
 
 void NUMsort_str (long n, char32 *a []) {
 	long l, r, j, i;
diff --git a/stat/Table.cpp b/stat/Table.cpp
index 4eaa6d8..2cfbb76 100644
--- a/stat/Table.cpp
+++ b/stat/Table.cpp
@@ -390,7 +390,7 @@ bool Table_isCellNumeric_ErrorFalse (Table me, integer rowNumber, integer column
 		while (*cell == U' ' || *cell == U'\t' || *cell == U'\n' || *cell == U'\r') cell ++;
 		return *cell == U'\0';   // only white space after the "?" or "--undefined--"
 	}
-	return Melder_isStringNumeric_nothrow (cell);
+	return Melder_isStringNumeric (cell);
 }
 
 bool Table_isColumnNumeric_ErrorFalse (Table me, integer columnNumber) {
@@ -627,7 +627,7 @@ integer Table_drawRowFromDistribution (Table me, integer columnNumber) {
 	}
 }
 
-autoTable Table_extractRowsWhereColumn_number (Table me, integer columnNumber, int which_Melder_NUMBER, double criterion) {
+autoTable Table_extractRowsWhereColumn_number (Table me, integer columnNumber, kMelder_number which, double criterion) {
 	try {
 		Table_checkSpecifiedColumnNumberWithinRange (me, columnNumber);
 		Table_numericize_Assert (me, columnNumber);   // extraction should work even if cells are not defined
@@ -637,7 +637,7 @@ autoTable Table_extractRowsWhereColumn_number (Table me, integer columnNumber, i
 		}
 		for (integer irow = 1; irow <= my rows.size; irow ++) {
 			TableRow row = my rows.at [irow];
-			if (Melder_numberMatchesCriterion (row -> cells [columnNumber]. number, which_Melder_NUMBER, criterion)) {
+			if (Melder_numberMatchesCriterion (row -> cells [columnNumber]. number, which, criterion)) {
 				autoTableRow newRow = Data_copy (row);
 				thy rows. addItem_move (newRow.move());
 			}
@@ -651,7 +651,7 @@ autoTable Table_extractRowsWhereColumn_number (Table me, integer columnNumber, i
 	}
 }
 
-autoTable Table_extractRowsWhereColumn_string (Table me, integer columnNumber, int which_Melder_STRING, const char32 *criterion) {
+autoTable Table_extractRowsWhereColumn_string (Table me, integer columnNumber, kMelder_string which, const char32 *criterion) {
 	try {
 		Table_checkSpecifiedColumnNumberWithinRange (me, columnNumber);
 		autoTable thee = Table_create (0, my numberOfColumns);
@@ -661,7 +661,7 @@ autoTable Table_extractRowsWhereColumn_string (Table me, integer columnNumber, i
 		}
 		for (integer irow = 1; irow <= my rows.size; irow ++) {
 			TableRow row = my rows.at [irow];
-			if (Melder_stringMatchesCriterion (row -> cells [columnNumber]. string, which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (row -> cells [columnNumber]. string, which, criterion)) {
 				autoTableRow newRow = Data_copy (row);
 				thy rows. addItem_move (newRow.move());
 			}
@@ -1332,10 +1332,10 @@ void Table_formula_columnRange (Table me, integer fromColumn, integer toColumn,
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
 				if (result. expressionType == kFormula_EXPRESSION_TYPE_STRING) {
-					Table_setStringValue (me, irow, icol, result. result.stringResult);
-					Melder_free (result. result.stringResult);
+					Table_setStringValue (me, irow, icol, result. stringResult);
+					Melder_free (result. stringResult);
 				} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
-					Table_setNumericValue (me, irow, icol, result. result.numericResult);
+					Table_setNumericValue (me, irow, icol, result. numericResult);
 				} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
 					Melder_throw (me, U": cannot put vectors into cells.");
 				} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
diff --git a/stat/Table.h b/stat/Table.h
index 790de83..9a46d71 100644
--- a/stat/Table.h
+++ b/stat/Table.h
@@ -2,7 +2,7 @@
 #define _Table_h_
 /* Table.h
  *
- * Copyright (C) 2002-2011,2012,2014,2015 Paul Boersma
+ * Copyright (C) 2002-2011,2012,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -110,8 +110,8 @@ void Table_writeToCommaSeparatedFile (Table me, MelderFile file);
 autoTable Table_readFromTableFile (MelderFile file);
 autoTable Table_readFromCharacterSeparatedTextFile (MelderFile file, char32 separator);
 
-autoTable Table_extractRowsWhereColumn_number (Table me, integer column, int which_Melder_NUMBER, double criterion);
-autoTable Table_extractRowsWhereColumn_string (Table me, integer column, int which_Melder_STRING, const char32 *criterion);
+autoTable Table_extractRowsWhereColumn_number (Table me, integer column, kMelder_number which, double criterion);
+autoTable Table_extractRowsWhereColumn_string (Table me, integer column, kMelder_string which, const char32 *criterion);
 autoTable Table_collapseRows (Table me, const char32 *factors_string, const char32 *columnsToSum_string,
 	const char32 *columnsToAverage_string, const char32 *columnsToMedianize_string,
 	const char32 *columnsToAverageLogarithmically_string, const char32 *columnsToMedianizeLogarithmically_string);
diff --git a/stat/TableEditor.cpp b/stat/TableEditor.cpp
index 7d1380b..3d26e03 100644
--- a/stat/TableEditor.cpp
+++ b/stat/TableEditor.cpp
@@ -305,7 +305,7 @@ autoTableEditor TableEditor_create (const char32 *title, Table table) {
 		Graphics_setWsViewport (my graphics.get(), 0.0, size_pixels, 0.0, size_pixels);
 		Graphics_setWsWindow (my graphics.get(), 0.0, size_pixels, 0.0, size_pixels);
 		Graphics_setViewport (my graphics.get(), 0.0, size_pixels, 0.0, size_pixels);
-		Graphics_setFont (my graphics.get(), kGraphics_font_COURIER);
+		Graphics_setFont (my graphics.get(), kGraphics_font::COURIER);
 		Graphics_setFontSize (my graphics.get(), 12);
 		Graphics_setUnderscoreIsSubscript (my graphics.get(), false);
 		Graphics_setAtSignIsLink (my graphics.get(), true);
diff --git a/stat/TableOfReal.cpp b/stat/TableOfReal.cpp
index 12cc54f..81046e5 100644
--- a/stat/TableOfReal.cpp
+++ b/stat/TableOfReal.cpp
@@ -318,7 +318,7 @@ void TableOfReal_formula (TableOfReal me, const char32 *expression, Interpreter
 			for (integer icol = 1; icol <= my numberOfColumns; icol ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				thy data [irow] [icol] = result. result.numericResult;
+				thy data [irow] [icol] = result. numericResult;
 			}
 		}
 	} catch (MelderError) {
@@ -362,13 +362,13 @@ static void copyColumn (TableOfReal me, integer myCol, TableOfReal thee, integer
 	}
 }
 
-autoTableOfReal TableOfReal_extractRowsWhereColumn (TableOfReal me, integer column, int which_Melder_NUMBER, double criterion) {
+autoTableOfReal TableOfReal_extractRowsWhereColumn (TableOfReal me, integer column, kMelder_number which, double criterion) {
 	try {
 		if (column < 1 || column > my numberOfColumns)
 			Melder_throw (U"No such column: ", column, U".");
 		integer n = 0;
 		for (integer irow = 1; irow <= my numberOfRows; irow ++) {
-			if (Melder_numberMatchesCriterion (my data [irow] [column], which_Melder_NUMBER, criterion)) {
+			if (Melder_numberMatchesCriterion (my data [irow] [column], which, criterion)) {
 				n ++;
 			}
 		}
@@ -377,7 +377,7 @@ autoTableOfReal TableOfReal_extractRowsWhereColumn (TableOfReal me, integer colu
 		copyColumnLabels (me, thee.get());
 		n = 0;
 		for (integer irow = 1; irow <= my numberOfRows; irow ++)
-			if (Melder_numberMatchesCriterion (my data [irow] [column], which_Melder_NUMBER, criterion))
+			if (Melder_numberMatchesCriterion (my data [irow] [column], which, criterion))
 				copyRow (me, irow, thee.get(), ++ n);
 		return thee;
 	} catch (MelderError) {
@@ -385,11 +385,11 @@ autoTableOfReal TableOfReal_extractRowsWhereColumn (TableOfReal me, integer colu
 	}
 }
 
-autoTableOfReal TableOfReal_extractRowsWhereLabel (TableOfReal me, int which_Melder_STRING, const char32 *criterion) {
+autoTableOfReal TableOfReal_extractRowsWhereLabel (TableOfReal me, kMelder_string which, const char32 *criterion) {
 	try {
 		integer n = 0;
 		for (integer irow = 1; irow <= my numberOfRows; irow ++) {
-			if (Melder_stringMatchesCriterion (my rowLabels [irow], which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (my rowLabels [irow], which, criterion)) {
 				n ++;
 			}
 		}
@@ -399,7 +399,7 @@ autoTableOfReal TableOfReal_extractRowsWhereLabel (TableOfReal me, int which_Mel
 		copyColumnLabels (me, thee.get());
 		n = 0;
 		for (integer irow = 1; irow <= my numberOfRows; irow ++)
-			if (Melder_stringMatchesCriterion (my rowLabels [irow], which_Melder_STRING, criterion))
+			if (Melder_stringMatchesCriterion (my rowLabels [irow], which, criterion))
 				copyRow (me, irow, thee.get(), ++ n);
 		return thee;
 	} catch (MelderError) {
@@ -407,13 +407,13 @@ autoTableOfReal TableOfReal_extractRowsWhereLabel (TableOfReal me, int which_Mel
 	}
 }
 
-autoTableOfReal TableOfReal_extractColumnsWhereRow (TableOfReal me, integer row, int which_Melder_NUMBER, double criterion) {
+autoTableOfReal TableOfReal_extractColumnsWhereRow (TableOfReal me, integer row, kMelder_number which, double criterion) {
 	try {
 		if (row < 1 || row > my numberOfRows)
 			Melder_throw (U"No such row: ", row, U".");
 		integer n = 0;
 		for (integer icol = 1; icol <= my numberOfColumns; icol ++) {
-			if (Melder_numberMatchesCriterion (my data [row] [icol], which_Melder_NUMBER, criterion)) {
+			if (Melder_numberMatchesCriterion (my data [row] [icol], which, criterion)) {
 				n ++;
 			}
 		}
@@ -423,7 +423,7 @@ autoTableOfReal TableOfReal_extractColumnsWhereRow (TableOfReal me, integer row,
 		copyRowLabels (me, thee.get());
 		n = 0;
 		for (integer icol = 1; icol <= my numberOfColumns; icol ++)
-			if (Melder_numberMatchesCriterion (my data [row] [icol], which_Melder_NUMBER, criterion))
+			if (Melder_numberMatchesCriterion (my data [row] [icol], which, criterion))
 				copyColumn (me, icol, thee.get(), ++ n);
 		return thee;
 	} catch (MelderError) {
@@ -431,11 +431,11 @@ autoTableOfReal TableOfReal_extractColumnsWhereRow (TableOfReal me, integer row,
 	}
 }
 
-autoTableOfReal TableOfReal_extractColumnsWhereLabel (TableOfReal me, int which_Melder_STRING, const char32 *criterion) {
+autoTableOfReal TableOfReal_extractColumnsWhereLabel (TableOfReal me, kMelder_string which, const char32 *criterion) {
 	try {
 		integer n = 0;
 		for (integer icol = 1; icol <= my numberOfColumns; icol ++) {
-			if (Melder_stringMatchesCriterion (my columnLabels [icol], which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (my columnLabels [icol], which, criterion)) {
 				n ++;
 			}
 		}
@@ -445,7 +445,7 @@ autoTableOfReal TableOfReal_extractColumnsWhereLabel (TableOfReal me, int which_
 		copyRowLabels (me, thee.get());
 		n = 0;
 		for (integer icol = 1; icol <= my numberOfColumns; icol ++) {
-			if (Melder_stringMatchesCriterion (my columnLabels [icol], which_Melder_STRING, criterion)) {
+			if (Melder_stringMatchesCriterion (my columnLabels [icol], which, criterion)) {
 				copyColumn (me, icol, thee.get(), ++ n);
 			}
 		}
@@ -579,7 +579,7 @@ autoTableOfReal TableOfReal_extractRowsWhere (TableOfReal me, const char32 *cond
 			for (integer icol = 1; icol <= my numberOfColumns; icol ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				if (result. result.numericResult != 0.0) {
+				if (result. numericResult != 0.0) {
 					numberOfElements ++;
 					break;
 				}
@@ -600,7 +600,7 @@ autoTableOfReal TableOfReal_extractRowsWhere (TableOfReal me, const char32 *cond
 			for (integer icol = 1; icol <= my numberOfColumns; icol ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				if (result. result.numericResult != 0.0) {
+				if (result. numericResult != 0.0) {
 					copyRow (me, irow, thee.get(), ++ numberOfElements);
 					break;
 				}
@@ -623,7 +623,7 @@ autoTableOfReal TableOfReal_extractColumnsWhere (TableOfReal me, const char32 *c
 			for (integer irow = 1; irow <= my numberOfRows; irow ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				if (result. result.numericResult != 0.0) {
+				if (result. numericResult != 0.0) {
 					numberOfElements ++;
 					break;
 				}
@@ -644,7 +644,7 @@ autoTableOfReal TableOfReal_extractColumnsWhere (TableOfReal me, const char32 *c
 			for (integer irow = 1; irow <= my numberOfRows; irow ++) {
 				Formula_Result result;
 				Formula_run (irow, icol, & result);
-				if (result. result.numericResult != 0.0) {
+				if (result. numericResult != 0.0) {
 					copyColumn (me, icol, thee.get(), ++ numberOfElements);
 					break;
 				}
diff --git a/stat/TableOfReal.h b/stat/TableOfReal.h
index 20821c7..f54b668 100644
--- a/stat/TableOfReal.h
+++ b/stat/TableOfReal.h
@@ -63,11 +63,11 @@ autoTableOfReal TableOfReal_readFromHeaderlessSpreadsheetFile (MelderFile file);
 autoTableOfReal TableOfReal_extractRowRanges (TableOfReal me, const char32 *ranges);
 autoTableOfReal TableOfReal_extractColumnRanges (TableOfReal me, const char32 *ranges);
 
-autoTableOfReal TableOfReal_extractRowsWhereColumn (TableOfReal me, integer icol, int which_Melder_NUMBER, double criterion);
-autoTableOfReal TableOfReal_extractColumnsWhereRow (TableOfReal me, integer icol, int which_Melder_NUMBER, double criterion);
+autoTableOfReal TableOfReal_extractRowsWhereColumn (TableOfReal me, integer icol, kMelder_number which, double criterion);
+autoTableOfReal TableOfReal_extractColumnsWhereRow (TableOfReal me, integer icol, kMelder_number which, double criterion);
 
-autoTableOfReal TableOfReal_extractRowsWhereLabel (TableOfReal me, int which_Melder_STRING, const char32 *criterion);
-autoTableOfReal TableOfReal_extractColumnsWhereLabel (TableOfReal me, int which_Melder_STRING, const char32 *criterion);
+autoTableOfReal TableOfReal_extractRowsWhereLabel (TableOfReal me, kMelder_string which, const char32 *criterion);
+autoTableOfReal TableOfReal_extractColumnsWhereLabel (TableOfReal me, kMelder_string which, const char32 *criterion);
 
 autoTableOfReal TableOfReal_extractRowsWhere (TableOfReal me, const char32 *condition, Interpreter interpreter);
 autoTableOfReal TableOfReal_extractColumnsWhere (TableOfReal me, const char32 *condition, Interpreter interpreter);
diff --git a/stat/praat_Stat.cpp b/stat/praat_Stat.cpp
index 67bf634..e3c2498 100644
--- a/stat/praat_Stat.cpp
+++ b/stat/praat_Stat.cpp
@@ -100,10 +100,10 @@ DO
 // MARK: Draw
 
 FORM (GRAPHICS_LogisticRegression_drawBoundary, U"LogisticRegression: Draw boundary", nullptr) {
-	WORD4 (horizontalFactor, U"Horizontal factor", U"")
+	SENTENCE4 (horizontalFactor, U"Horizontal factor", U"")
 	REAL4 (fromHorizontal, U"left Horizontal range", U"0.0")
 	REAL4 (toHorizontal, U"right Horizontal range", U"0.0 (= auto)")
-	WORD4 (verticalFactor, U"Vertical factor", U"")
+	SENTENCE4 (verticalFactor, U"Vertical factor", U"")
 	REAL4 (fromVertical, U"left Vertical range", U"0.0")
 	REAL4 (toVertical, U"right Vertical range", U"0.0 (= auto)")
 	BOOLEAN4 (garnish, U"Garnish", true)
@@ -313,13 +313,13 @@ DO
 // MARK: Draw
 
 FORM (GRAPHICS_Table_scatterPlot, U"Scatter plot", nullptr) {
-	WORD4 (horizontalColumn, U"Horizontal column", U"")
+	SENTENCE4 (horizontalColumn, U"Horizontal column", U"")
 	REAL4 (fromHorizontal, U"left Horizontal range", U"0.0")
 	REAL4 (toHorizontal, U"right Horizontal range", U"0.0 (= auto)")
-	WORD4 (verticalColumn, U"Vertical column", U"")
+	SENTENCE4 (verticalColumn, U"Vertical column", U"")
 	REAL4 (fromVertical, U"left Vertical range", U"0.0")
 	REAL4 (toVertical, U"right Vertical range", U"0.0 (= auto)")
-	WORD4 (columnWithMarks, U"Column with marks", U"")
+	SENTENCE4 (columnWithMarks, U"Column with marks", U"")
 	NATURAL4 (fontSize, U"Font size", U"12")
 	BOOLEAN4 (garnish, U"Garnish", true)
 	OK
@@ -334,10 +334,10 @@ DO
 }
 
 FORM (GRAPHICS_Table_scatterPlot_mark, U"Scatter plot (marks)", nullptr) {
-	WORD4 (horizontalColumn, U"Horizontal column", U"")
+	SENTENCE4 (horizontalColumn, U"Horizontal column", U"")
 	REAL4 (fromHorizontal, U"left Horizontal range", U"0.0")
 	REAL4 (toHorizontal, U"right Horizontal range", U"0.0 (= auto)")
-	WORD4 (verticalColumn, U"Vertical column", U"")
+	SENTENCE4 (verticalColumn, U"Vertical column", U"")
 	REAL4 (fromVertical, U"left Vertical range", U"0.0")
 	REAL4 (toVertical, U"right Vertical range", U"0.0 (= auto)")
 	POSITIVE4 (markSize, U"Mark size (mm)", U"1.0")
@@ -355,10 +355,10 @@ DO
 }
 
 FORM (GRAPHICS_Table_drawEllipse, U"Draw ellipse (standard deviation)", nullptr) {
-	WORD4 (horizontalColumn, U"Horizontal column", U"")
+	SENTENCE4 (horizontalColumn, U"Horizontal column", U"")
 	REAL4 (fromHorizontal, U"left Horizontal range", U"0.0")
 	REAL4 (toHorizontal, U"right Horizontal range", U"0.0 (= auto)")
-	WORD4 (verticalColumn, U"Vertical column", U"")
+	SENTENCE4 (verticalColumn, U"Vertical column", U"")
 	REAL4 (fromVertical, U"left Vertical range", U"0.0")
 	REAL4 (toVertical, U"right Vertical range", U"0.0 (= auto)")
 	POSITIVE4 (numberOfSigmas, U"Number of sigmas", U"2.0")
@@ -376,7 +376,7 @@ DO
 // MARK: Query
 
 FORM (INTEGER_Table_drawRowFromDistribution, U"Table: Draw row from distribution", nullptr) {
-	WORD4 (columnWithDistribution, U"Column with distribution", U"")
+	SENTENCE4 (columnWithDistribution, U"Column with distribution", U"")
 	OK
 DO
 	NUMBER_ONE (Table)
@@ -406,8 +406,8 @@ DO
 }
 
 FORM (REAL_Table_getGroupMean, U"Table: Get group mean", nullptr) {
-	WORD4 (columnLabel, U"Column label", U"salary")
-	WORD4 (groupColumnLabel, U"Group column label", U"gender")
+	SENTENCE4 (columnLabel, U"Column label", U"salary")
+	SENTENCE4 (groupColumnLabel, U"Group column label", U"gender")
 	SENTENCE4 (group, U"Group", U"F")
 	OK
 DO
@@ -419,7 +419,7 @@ DO
 }
 
 FORM (REAL_Table_getMaximum, U"Table: Get maximum", nullptr) {
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	OK
 DO
 	NUMBER_ONE (Table)
@@ -429,7 +429,7 @@ DO
 }
 
 FORM (REAL_Table_getMean, U"Table: Get mean", nullptr) {
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	OK
 DO
 	NUMBER_ONE (Table)
@@ -439,7 +439,7 @@ DO
 }
 
 FORM (REAL_Table_getMinimum, U"Table: Get minimum", nullptr) {
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	OK
 DO
 	NUMBER_ONE (Table)
@@ -449,7 +449,7 @@ DO
 }
 
 FORM (REAL_Table_getQuantile, U"Table: Get quantile", nullptr) {
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	POSITIVE4 (quantile, U"Quantile", U"0.50 (= median)")
 	OK
 DO
@@ -460,7 +460,7 @@ DO
 }
 
 FORM (REAL_Table_getStandardDeviation, U"Table: Get standard deviation", nullptr) {
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	OK
 DO
 	NUMBER_ONE (Table)
@@ -507,8 +507,8 @@ DO
 // MARK: Statistics
 
 FORM (INFO_Table_reportCorrelation_kendallTau, U"Report correlation (Kendall tau)", nullptr) {
-	WORD4 (column1, U"left Columns", U"")
-	WORD4 (column2, U"right Columns", U"")
+	SENTENCE4 (column1, U"left Columns", U"")
+	SENTENCE4 (column2, U"right Columns", U"")
 	POSITIVE4 (oneTailedUnconfidence, U"One-tailed unconfidence", U"0.025")
 	OK
 DO
@@ -533,8 +533,8 @@ DO
 }
 
 FORM (INFO_Table_reportCorrelation_pearsonR, U"Report correlation (Pearson r)", nullptr) {
-	WORD4 (column1, U"left Columns", U"")
-	WORD4 (column2, U"right Columns", U"")
+	SENTENCE4 (column1, U"left Columns", U"")
+	SENTENCE4 (column2, U"right Columns", U"")
 	POSITIVE4 (oneTailedUnconfidence, U"One-tailed unconfidence", U"0.025")
 	OK
 DO
@@ -560,8 +560,8 @@ DO
 }
 
 FORM (INFO_Table_reportDifference_studentT, U"Report difference (Student t)", nullptr) {
-	WORD4 (column1, U"left Columns", U"")
-	WORD4 (column2, U"right Columns", U"")
+	SENTENCE4 (column1, U"left Columns", U"")
+	SENTENCE4 (column2, U"right Columns", U"")
 	POSITIVE4 (oneTailedUnconfidence, U"One-tailed unconfidence", U"0.025")
 	OK
 DO
@@ -588,8 +588,8 @@ DO
 }
 
 FORM (INFO_Table_reportGroupDifference_studentT, U"Report group difference (Student t)", nullptr) {
-	WORD4 (column, U"Column", U"salary")
-	WORD4 (groupColumn, U"Group column", U"gender")
+	SENTENCE4 (column, U"Column", U"salary")
+	SENTENCE4 (groupColumn, U"Group column", U"gender")
 	SENTENCE4 (group1, U"Group 1", U"F")
 	SENTENCE4 (group2, U"Group 2", U"M")
 	POSITIVE4 (oneTailedUnconfidence, U"One-tailed unconfidence", U"0.025")
@@ -618,8 +618,8 @@ DO
 }
 
 FORM (INFO_Table_reportGroupDifference_wilcoxonRankSum, U"Report group difference (Wilcoxon rank sum)", nullptr) {
-	WORD4 (column, U"Column", U"salary")
-	WORD4 (groupColumn, U"Group column", U"gender")
+	SENTENCE4 (column, U"Column", U"salary")
+	SENTENCE4 (groupColumn, U"Group column", U"gender")
 	SENTENCE4 (group1, U"Group 1", U"F")
 	SENTENCE4 (group2, U"Group 2", U"M")
 	OK
@@ -642,8 +642,8 @@ DO
 }
 
 FORM (INFO_Table_reportGroupMean_studentT, U"Report group mean (Student t)", nullptr) {
-	WORD4 (column, U"Column", U"salary")
-	WORD4 (groupColumn, U"Group column", U"gender")
+	SENTENCE4 (column, U"Column", U"salary")
+	SENTENCE4 (groupColumn, U"Group column", U"gender")
 	SENTENCE4 (group, U"Group", U"F")
 	POSITIVE4 (oneTailedUnconfidence, U"One-tailed unconfidence", U"0.025")
 	OK
@@ -671,7 +671,7 @@ DO
 }
 
 FORM (INFO_Table_reportMean_studentT, U"Report mean (Student t)", nullptr) {
-	WORD4 (column, U"Column", U"")
+	SENTENCE4 (column, U"Column", U"")
 	POSITIVE4 (oneTailedUnconfidence, U"One-tailed unconfidence", U"0.025")
 	OK
 DO
@@ -698,7 +698,7 @@ DO
 // MARK: Modify
 
 FORM (MODIFY_Table_appendColumn, U"Table: Append column", nullptr) {
-	WORD4 (label, U"Label", U"newcolumn")
+	SENTENCE4 (label, U"Label", U"newcolumn")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -707,9 +707,9 @@ DO
 }
 
 FORM (MODIFY_Table_appendDifferenceColumn, U"Table: Append difference column", nullptr) {
-	WORD4 (column1, U"left Columns", U"")
-	WORD4 (column2, U"right Columns", U"")
-	WORD4 (label, U"Label", U"diff")
+	SENTENCE4 (column1, U"left Columns", U"")
+	SENTENCE4 (column2, U"right Columns", U"")
+	SENTENCE4 (label, U"Label", U"diff")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -720,9 +720,9 @@ DO
 }
 
 FORM (MODIFY_Table_appendProductColumn, U"Table: Append product column", nullptr) {
-	WORD4 (column1, U"left Columns", U"")
-	WORD4 (column2, U"right Columns", U"")
-	WORD4 (label, U"Label", U"prod")
+	SENTENCE4 (column1, U"left Columns", U"")
+	SENTENCE4 (column2, U"right Columns", U"")
+	SENTENCE4 (label, U"Label", U"prod")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -733,9 +733,9 @@ DO
 }
 
 FORM (MODIFY_Table_appendQuotientColumn, U"Table: Append quotient column", nullptr) {
-	WORD4 (column1, U"left Columns", U"")
-	WORD4 (column2, U"right Columns", U"")
-	WORD4 (label, U"Label", U"quot")
+	SENTENCE4 (column1, U"left Columns", U"")
+	SENTENCE4 (column2, U"right Columns", U"")
+	SENTENCE4 (label, U"Label", U"quot")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -746,9 +746,9 @@ DO
 }
 
 FORM (MODIFY_Table_appendSumColumn, U"Table: Append sum column", nullptr) {
-	WORD4 (column1, U"left Columns", U"")
-	WORD4 (column2, U"right Columns", U"")
-	WORD4 (label, U"Label", U"sum")
+	SENTENCE4 (column1, U"left Columns", U"")
+	SENTENCE4 (column2, U"right Columns", U"")
+	SENTENCE4 (label, U"Label", U"sum")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -765,7 +765,7 @@ DIRECT (MODIFY_Table_appendRow) {
 }
 
 FORM (MODIFY_Table_formula, U"Table: Formula", U"Table: Formula...") {
-	WORD4 (columnLabel, U"Column (label)", U"")
+	SENTENCE4 (columnLabel, U"Column (label)", U"")
 	TEXTFIELD4 (formula, U"formula", U"abs (self)")
 	OK
 DO
@@ -776,8 +776,8 @@ DO
 }
 
 FORM (MODIFY_Table_formula_columnRange, U"Table: Formula (column range)", U"Table: Formula...") {
-	WORD4 (fromColumn, U"From column (label)", U"")
-	WORD4 (toColumn, U"To column (label)", U"")
+	SENTENCE4 (fromColumn, U"From column (label)", U"")
+	SENTENCE4 (toColumn, U"To column (label)", U"")
 	TEXTFIELD4 (formula, U"formula", U"log10 (self)")
 	OK
 DO
@@ -790,7 +790,7 @@ DO
 
 FORM (MODIFY_Table_insertColumn, U"Table: Insert column", nullptr) {
 	NATURAL4 (position, U"Position", U"1")
-	WORD4 (label, U"Label", U"newcolumn")
+	SENTENCE4 (label, U"Label", U"newcolumn")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -808,7 +808,7 @@ DO
 }
 
 FORM (MODIFY_Table_removeColumn, U"Table: Remove column", nullptr) {
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -838,7 +838,7 @@ DO
 
 FORM (MODIFY_Table_setColumnLabel_label, U"Set column label", nullptr) {
 	SENTENCE4 (oldLabel, U"Old label", U"")
-	WORD4 (newLabel, U"New label", U"")
+	SENTENCE4 (newLabel, U"New label", U"")
 	OK
 DO
 	MODIFY_EACH (Table)
@@ -849,7 +849,7 @@ DO
 
 FORM (MODIFY_Table_setNumericValue, U"Table: Set numeric value", nullptr) {
 	NATURAL4 (rowNumber, U"Row number", U"1")
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	REAL_OR_UNDEFINED4 (numericValue, U"Numeric value", U"1.5")
 	OK
 DO
@@ -861,7 +861,7 @@ DO
 
 FORM (MODIFY_Table_setStringValue, U"Table: Set string value", nullptr) {
 	NATURAL4 (rowNumber, U"Row number", U"1")
-	WORD4 (columnLabel, U"Column label", U"")
+	SENTENCE4 (columnLabel, U"Column label", U"")
 	SENTENCE4 (stringValue, U"String value", U"xx")
 	OK
 DO
@@ -924,27 +924,27 @@ DIRECT (NEW1_Tables_append) {
 }
 
 FORM (NEW_Table_extractRowsWhereColumn_number, U"Table: Extract rows where column (number)", nullptr) {
-	WORD4 (extractAllRowsWhereColumn___, U"Extract all rows where column...", U"")
+	SENTENCE4 (extractAllRowsWhereColumn___, U"Extract all rows where column...", U"")
 	RADIO_ENUM4 (___is___, U"...is...", kMelder_number, DEFAULT)
 	REAL4 (___theNumber, U"...the number", U"0.0")
 	OK
 DO
 	CONVERT_EACH (Table)
 		long columnNumber = Table_getColumnIndexFromColumnLabel (me, extractAllRowsWhereColumn___);
-		autoTable result = Table_extractRowsWhereColumn_number (me, columnNumber, ___is___, ___theNumber);
+		autoTable result = Table_extractRowsWhereColumn_number (me, columnNumber, (kMelder_number) ___is___, ___theNumber);
 	CONVERT_EACH_END (my name, U"_", Table_messageColumn (me, columnNumber), U"_",
 		isdefined (___theNumber) ? Melder_integer (lround (___theNumber)) : U"undefined")
 }
 
 FORM (NEW_Table_extractRowsWhereColumn_text, U"Table: Extract rows where column (text)", nullptr) {
-	WORD4 (extractAllRowsWhereColumn___, U"Extract all rows where column...", U"")
+	SENTENCE4 (extractAllRowsWhereColumn___, U"Extract all rows where column...", U"")
 	OPTIONMENU_ENUM4 (___, U"...", kMelder_string, DEFAULT)
 	SENTENCE4 (___theText, U"...the text", U"hi")
 	OK
 DO
 	CONVERT_EACH (Table)
 		long columnNumber = Table_getColumnIndexFromColumnLabel (me, extractAllRowsWhereColumn___);
-		autoTable result = Table_extractRowsWhereColumn_string (me, columnNumber, ___, ___theText);
+		autoTable result = Table_extractRowsWhereColumn_string (me, columnNumber, (kMelder_string) ___, ___theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
@@ -957,7 +957,7 @@ DIRECT (NEW_Table_transpose) {
 FORM (NEW_Table_rowsToColumns, U"Table: Rows to columns", nullptr) {
 	LABEL (U"", U"Columns with factors (independent variables):")
 	TEXTFIELD4 (factors, U"factors", U"dialect gender speaker")
-	WORD4 (columnToTranspose, U"Column to transpose", U"vowel")
+	SENTENCE4 (columnToTranspose, U"Column to transpose", U"vowel")
 	LABEL (U"", U"Columns to expand:")
 	TEXTFIELD4 (columnsToExpand, U"columnsToExpand", U"duration F0 F1 F2 F3")
 	LABEL (U"", U"Columns not mentioned above will be ignored.")
@@ -978,8 +978,8 @@ DIRECT (NEW_Table_to_LinearRegression) {
 FORM (NEW_Table_to_LogisticRegression, U"Table: To LogisticRegression", nullptr) {
 	LABEL (U"", U"Factors (column names):")
 	TEXTFIELD4 (factors, U"factors", U"F0 F1 duration")
-	WORD4 (dependent1, U"Dependent 1 (column name)", U"e")
-	WORD4 (dependent2, U"Dependent 2 (column name)", U"i")
+	SENTENCE4 (dependent1, U"Dependent 1 (column name)", U"e")
+	SENTENCE4 (dependent2, U"Dependent 2 (column name)", U"i")
 	OK
 DO
 	CONVERT_EACH (Table)
@@ -988,7 +988,7 @@ DO
 }
 
 FORM (NEW_Table_to_TableOfReal, U"Table: Down to TableOfReal", nullptr) {
-	WORD4 (columnForRowLabels, U"Column for row labels", U"")
+	SENTENCE4 (columnForRowLabels, U"Column for row labels", U"")
 	OK
 DO
 	CONVERT_EACH (Table)
diff --git a/stat/praat_TableOfReal.cpp b/stat/praat_TableOfReal.cpp
index da8c553..55abb0d 100644
--- a/stat/praat_TableOfReal.cpp
+++ b/stat/praat_TableOfReal.cpp
@@ -392,7 +392,7 @@ FORM (NEW_TableOfReal_extractColumnsWhereLabel, U"Extract column where label", n
 	OK
 DO
 	CONVERT_EACH (TableOfReal)
-		autoTableOfReal result = TableOfReal_extractColumnsWhereLabel (me, extractAllColumnsWhoseLabel, ___theText);
+		autoTableOfReal result = TableOfReal_extractColumnsWhereLabel (me, (kMelder_string) extractAllColumnsWhoseLabel, ___theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
@@ -403,7 +403,7 @@ FORM (NEW_TableOfReal_extractColumnsWhereRow, U"Extract columns where row", null
 	OK
 DO
 	CONVERT_EACH (TableOfReal)
-		autoTableOfReal result = TableOfReal_extractColumnsWhereRow (me, extractAllColumnsWhereRow, ___is___, ___theValue);
+		autoTableOfReal result = TableOfReal_extractColumnsWhereRow (me, extractAllColumnsWhereRow, (kMelder_number) ___is___, ___theValue);
 	CONVERT_EACH_END (my name, U"_", extractAllColumnsWhereRow, U"_", lround (___theValue))
 }
 
@@ -441,7 +441,7 @@ FORM (NEW_TableOfReal_extractRowsWhereColumn, U"Extract rows where column", null
 	OK
 DO
 	CONVERT_EACH (TableOfReal)
-		autoTableOfReal result = TableOfReal_extractRowsWhereColumn (me, extractAllRowsWhereColumn, ___is___, ___theValue);
+		autoTableOfReal result = TableOfReal_extractRowsWhereColumn (me, extractAllRowsWhereColumn, (kMelder_number) ___is___, ___theValue);
 	CONVERT_EACH_END (my name, U"_", extractAllRowsWhereColumn, U"_", lround (___theValue))
 }
 
@@ -451,7 +451,7 @@ FORM (NEW_TableOfReal_extractRowsWhereLabel, U"Extract rows where label", nullpt
 	OK
 DO
 	CONVERT_EACH (TableOfReal)
-		autoTableOfReal result = TableOfReal_extractRowsWhereLabel (me, extractAllRowsWhoseLabel, ___theText);
+		autoTableOfReal result = TableOfReal_extractRowsWhereLabel (me, (kMelder_string) extractAllRowsWhoseLabel, ___theText);
 	CONVERT_EACH_END (my name, U"_", ___theText)
 }
 
diff --git a/sys/Data.cpp b/sys/Data.cpp
index e63be1b..62ae708 100644
--- a/sys/Data.cpp
+++ b/sys/Data.cpp
@@ -85,12 +85,14 @@ MelderFile Data_createTextFile (Daata me, MelderFile file, bool verbose) {
 		file -> requiresCRLF = true;
 	#endif
 	file -> verbose = verbose;
-	file -> outputEncoding = Melder_getOutputEncoding ();
-	if (file -> outputEncoding == kMelder_textOutputEncoding_ASCII_THEN_UTF16)
-		file -> outputEncoding = Data_canWriteAsEncoding (me, kMelder_textOutputEncoding_ASCII) ? kMelder_textOutputEncoding_ASCII : kMelder_textOutputEncoding_UTF16;
-	else if (file -> outputEncoding == kMelder_textOutputEncoding_ISO_LATIN1_THEN_UTF16)
-		file -> outputEncoding = Data_canWriteAsEncoding (me, kMelder_textOutputEncoding_ISO_LATIN1) ? kMelder_textOutputEncoding_ISO_LATIN1 : kMelder_textOutputEncoding_UTF16;
-	if (file -> outputEncoding == kMelder_textOutputEncoding_UTF16) {
+	file -> outputEncoding = (int) Melder_getOutputEncoding ();
+	if (file -> outputEncoding == (int) kMelder_textOutputEncoding::ASCII_THEN_UTF16)
+		file -> outputEncoding = Data_canWriteAsEncoding (me, kMelder_textOutputEncoding_ASCII) ?
+			kMelder_textOutputEncoding_ASCII : (int) kMelder_textOutputEncoding::UTF16;
+	else if (file -> outputEncoding == (int) kMelder_textOutputEncoding::ISO_LATIN1_THEN_UTF16)
+		file -> outputEncoding = Data_canWriteAsEncoding (me, kMelder_textOutputEncoding_ISO_LATIN1) ?
+			kMelder_textOutputEncoding_ISO_LATIN1 : (int) kMelder_textOutputEncoding::UTF16;
+	if (file -> outputEncoding == (int) kMelder_textOutputEncoding::UTF16) {
 		binputu16 (0xfeff, file -> filePointer);
 	}
 	return mfile.transfer();
@@ -186,6 +188,13 @@ autoDaata Data_readFromTextFile (MelderFile file) {
 		char32 *line = MelderReadText_readLine (text.peek());
 		if (! line)
 			Melder_throw (U"No lines.");
+		/*
+			Allow for a future version of text files (we have no plans).
+			This check was written on 2017-09-10.
+			See below at `Data_readFromBinaryFile` for a more serious proposal.
+		*/
+		if (str32str (line, U"ooText2File"))
+			Melder_throw (U"This Praat version cannot read this Praat file. Please download a newer version of Praat.");
 		char32 *end = str32str (line, U"ooTextFile");   // oo format?
 		autoDaata me;
 		int formatVersion;
@@ -231,6 +240,15 @@ autoDaata Data_readFromBinaryFile (MelderFile file) {
 		autofile f = Melder_fopen (file, "rb");
 		char line [200];
 		int n = fread (line, 1, 199, f); line [n] = '\0';
+		/*
+			Allow for a future version of binary files, which can handle 64-bit integers
+			and are perhaps written in little-endian format.
+			This check was written on 2017-09-10, and should stay for at least a year;
+			ooBinary2 files can therefore be implemented from some moment after 2018-09-10.
+			Please compare with `Data_readFromTextFile` above.
+		*/
+		if (strstr (line, "ooBinary2File"))
+			Melder_throw (U"This Praat version cannot read this Praat file. Please download a newer version of Praat.");
 		char *end = strstr (line, "ooBinaryFile");
 		autoDaata me;
 		int formatVersion;
@@ -247,7 +265,7 @@ autoDaata Data_readFromBinaryFile (MelderFile file) {
 			me = Thing_newFromClassName (Melder_peek8to32 (line), nullptr).static_cast_move <structDaata> ();
 			formatVersion = -1;   // old version: override version number, which was set to 0 by newFromClassName
 			rewind (f);
-			fread (line, 1, end - line + strlen ("BinaryFile"), f);
+			fread (line, 1, (size_t) (end - line) + strlen ("BinaryFile"), f);
 		}
 		MelderFile_getParentDir (file, & Data_directoryBeingRead);
 		Data_readBinary (me.get(), f, formatVersion);
@@ -283,25 +301,31 @@ void Data_recognizeFileType (Data_FileTypeRecognizer recognizer) {
 }
 
 autoDaata Data_readFromFile (MelderFile file) {
-	int nread, i;
 	char header [513];
 	autofile f = Melder_fopen (file, "rb");
-	nread = fread (& header [0], 1, 512, f);
+	int nread = fread (& header [0], 1, 512, f);
 	f.close (file);
 	header [nread] = 0;
 
 	/***** 1. Is this file a text file as defined in Data.cpp? *****/
 
 	if (nread > 11) {
+		int numberOfBytesInFileType = 0;
 		char *p = strstr (header, "TextFile");
-		if (p && p - header < nread - 8 && p - header < 40)
+		if (p) {
+			numberOfBytesInFileType = 8;
+		} else {
+			p = strstr (header, "Text2File");   // future version?
+			numberOfBytesInFileType = 9;
+		}
+		if (p && p - header < nread - numberOfBytesInFileType && p - header < 40)
 			return Data_readFromTextFile (file);
 	}
 	if (nread > 22) {
 		char headerCopy [101];
 		memcpy (headerCopy, header, 100);
 		headerCopy [100] = '\0';
-		for (i = 0; i < 100; i ++)
+		for (int i = 0; i < 100; i ++)
 			if (headerCopy [i] == '\0') headerCopy [i] = '\001';
 		char *p = strstr (headerCopy, "T\001e\001x\001t\001F\001i\001l\001e");
 		if (p && p - headerCopy < nread - 15 && p - headerCopy < 80)
@@ -311,15 +335,22 @@ autoDaata Data_readFromFile (MelderFile file) {
 	/***** 2. Is this file a binary file as defined in Data.cpp? *****/
 
 	if (nread > 13) {
+		int numberOfBytesInFileType = 0;
 		char *p = strstr (header, "BinaryFile");
-		if (p && p - header < nread - 10 && p - header < 40)
+		if (p) {
+			numberOfBytesInFileType = 10;
+		} else {
+			p = strstr (header, "Binary2File");   // future version
+			numberOfBytesInFileType = 11;
+		}
+		if (p && p - header < nread - numberOfBytesInFileType && p - header < 40)
 			return Data_readFromBinaryFile (file);
 	}
 
 	/***** 3. Is this file of a type for which a recognizer has been installed? *****/
 
 	MelderFile_getParentDir (file, & Data_directoryBeingRead);
-	for (i = 1; i <= numFileTypeRecognizers; i ++) {
+	for (int i = 1; i <= numFileTypeRecognizers; i ++) {
 		autoDaata object = fileTypeRecognizers [i] (nread, header, file);
 		if (object) {
 			if (object -> classInfo == classDaata)   // dummy object? the recognizer could have had a side effect, such as drawing a picture
@@ -330,8 +361,9 @@ autoDaata Data_readFromFile (MelderFile file) {
 
 	/***** 4. Is this a common text file? *****/
 
-	for (i = 0; i < nread; i ++)
-		if (header [i] < 32 || header [i] > 126)   // not ASCII?
+	int i = 0;
+	for (; i < nread; i ++)
+		if (header [i] < 32 || header [i] > 126)   // not ASCII? (note: this expression happens to work correctly for both signed and unsigned char)
 			break;
 	if (i >= nread) return Data_readFromTextFile (file);
 
@@ -386,7 +418,7 @@ int64 Data_Description_integer (void *address, Data_Description description) {
 		case longwa:           return * (long *)           ((char *) address + description -> offset);
 		case ubytewa:          return * (unsigned char *)  ((char *) address + description -> offset);
 		case uintwa:           return * (unsigned int *)   ((char *) address + description -> offset);
-		case ulongwa:          return * (unsigned long *)  ((char *) address + description -> offset);
+		case ulongwa:  return (int64) * (unsigned long *)  ((char *) address + description -> offset);   // ignore numbers above 2^63 - 1
 		case boolwa:           return * (bool *)           ((char *) address + description -> offset);
 		case objectwa:         return (* (Collection *)    ((char *) address + description -> offset))->size;
 		case autoobjectwa:     return (* (Collection *)    ((char *) address + description -> offset))->size;   // FIXME: alignment not guaranteed
diff --git a/sys/Data.h b/sys/Data.h
index 970c9a0..920576b 100644
--- a/sys/Data.h
+++ b/sys/Data.h
@@ -331,28 +331,29 @@ void Data_setPublishProc (int (*publish) (autoDaata));
 #define int16wa  2
 #define intwa  3
 #define longwa  4
-#define ubytewa  5
-#define uintwa  6
-#define ulongwa  7
-#define boolwa 8
-#define floatwa  9
-#define doublewa  10
-#define fcomplexwa  11
-#define dcomplexwa  12
-#define enumwa  13
-#define lenumwa  14
-#define booleanwa  15
-#define questionwa  16
-#define stringwa  17
-#define lstringwa  18
+#define integerwa  5
+#define ubytewa  6
+#define uintwa  7
+#define ulongwa  8
+#define boolwa 9
+#define floatwa  10
+#define doublewa  11
+#define fcomplexwa  12
+#define dcomplexwa  13
+#define enumwa  14
+#define lenumwa  15
+#define booleanwa  16
+#define questionwa  17
+#define stringwa  18
+#define lstringwa  19
 #define maxsingletypewa lstringwa
-#define structwa  19
-#define widgetwa  20
-#define objectwa  21
-#define autoobjectwa  22
-#define collectionofwa  23
-#define autocollectionwa  24
-#define inheritwa  25
+#define structwa  20
+#define widgetwa  21
+#define objectwa  22
+#define autoobjectwa  23
+#define collectionofwa  24
+#define autocollectionwa  25
+#define inheritwa  26
 
 /* Recursive routines for working with struct members. */
 
diff --git a/sys/DataEditor.cpp b/sys/DataEditor.cpp
index fab971b..ae70b08 100644
--- a/sys/DataEditor.cpp
+++ b/sys/DataEditor.cpp
@@ -167,14 +167,26 @@ static void gui_button_cb_change (DataSubEditor me, GuiButtonEvent /* event */)
 						}
 					}
 				} break;
+				case integerwa: {
+					integer oldValue = * (integer *) my d_fieldData [irow]. address, newValue = Melder_atoi (text);
+					if (newValue != oldValue) {
+						Data_Description numberUse = DataSubEditor_findNumberUse (me, my d_fieldData [irow]. description -> name);
+						if (numberUse) {
+							Melder_flushError (U"Changing field \"", strip_d (my d_fieldData [irow]. description -> name),
+								U"\" would damage the array \"", strip_d (numberUse -> name), U"\".");
+						} else {
+							* (integer *) my d_fieldData [irow]. address = newValue;
+						}
+					}
+				} break;
 				case ubytewa: { * (unsigned char *) my d_fieldData [irow]. address = (uint8) Melder_atoi (text); } break;
 				case uintwa: { * (unsigned int *) my d_fieldData [irow]. address = Melder_atoi (text); } break;
 				case ulongwa: { * (unsigned long *) my d_fieldData [irow]. address = (unsigned long) Melder_atoi (text); } break;
 				case boolwa: { * (bool *) my d_fieldData [irow]. address = Melder_atoi (text); } break;
 				case floatwa: { * (double *) my d_fieldData [irow]. address = Melder_atof (text); } break;
 				case doublewa: { * (double *) my d_fieldData [irow]. address = Melder_atof (text); } break;
-				case fcomplexwa: { fcomplex *x = (fcomplex *) my d_fieldData [irow]. address;
-					sscanf (Melder_peek32to8 (text), "%f + %f i", & x -> re, & x -> im); } break;
+				case fcomplexwa: { dcomplex *x = (dcomplex *) my d_fieldData [irow]. address;
+					sscanf (Melder_peek32to8 (text), "%lf + %lf i", & x -> re, & x -> im); } break;
 				case dcomplexwa: { dcomplex *x = (dcomplex *) my d_fieldData [irow]. address;
 					sscanf (Melder_peek32to8 (text), "%lf + %lf i", & x -> re, & x -> im); } break;
 				case enumwa: {
@@ -358,20 +370,19 @@ long structStructEditor :: v_countFields () {
 
 static const char32 * singleTypeToText (void *address, int type, void *tagType, MelderString *buffer) {
 	switch (type) {
-		case bytewa:   MelderString_append (buffer, Melder_integer (* (signed char *)    address)); break;
-		case int16wa:  MelderString_append (buffer, Melder_integer (* (int16 *)          address)); break;
-		case intwa:    MelderString_append (buffer, Melder_integer (* (int *)            address)); break;
-		case longwa:   MelderString_append (buffer, Melder_integer (* (long *)           address)); break;
-		case ubytewa:  MelderString_append (buffer, Melder_integer (* (unsigned char *)  address)); break;
-		case uintwa:   MelderString_append (buffer, Melder_integer (* (unsigned int *)   address)); break;
-		case ulongwa:  MelderString_append (buffer, Melder_integer (* (unsigned long *)  address)); break;
-		case boolwa:   MelderString_append (buffer, Melder_integer (* (bool *)           address)); break;
-		case floatwa:  MelderString_append (buffer, Melder_single  (* (double *)         address)); break;
-		case doublewa: MelderString_append (buffer, Melder_double  (* (double *)         address)); break;
-		case fcomplexwa: { fcomplex value = * (fcomplex *) address;
-			MelderString_append (buffer, Melder_single (value. re), U" + ", Melder_single (value. im), U" i"); } break;
-		case dcomplexwa: { dcomplex value = * (dcomplex *) address;
-			MelderString_append (buffer, Melder_double (value. re), U" + ", Melder_double (value. im), U" i"); } break;
+		case bytewa:     MelderString_append (buffer, Melder_integer  (* (signed char *)    address)); break;
+		case int16wa:    MelderString_append (buffer, Melder_integer  (* (int16 *)          address)); break;
+		case intwa:      MelderString_append (buffer, Melder_integer  (* (int *)            address)); break;
+		case longwa:     MelderString_append (buffer, Melder_integer  (* (long *)           address)); break;
+		case integerwa:  MelderString_append (buffer, Melder_integer  (* (integer *)        address)); break;
+		case ubytewa:    MelderString_append (buffer, Melder_integer  (* (unsigned char *)  address)); break;
+		case uintwa:     MelderString_append (buffer, Melder_integer  (* (unsigned int *)   address)); break;
+		case ulongwa:    MelderString_append (buffer, Melder_integer  (* (unsigned long *)  address)); break;
+		case boolwa:     MelderString_append (buffer, Melder_integer  (* (bool *)           address)); break;
+		case floatwa:    MelderString_append (buffer, Melder_single   (* (double *)         address)); break;
+		case doublewa:   MelderString_append (buffer, Melder_double   (* (double *)         address)); break;
+		case fcomplexwa: MelderString_append (buffer, Melder_scomplex (* (dcomplex *)       address)); break;
+		case dcomplexwa: MelderString_append (buffer, Melder_dcomplex (* (dcomplex *)       address)); break;
 		case enumwa:  MelderString_append (buffer, U"<", ((const char32 * (*) (int)) tagType) (* (signed char *)  address), U">"); break;
 		case lenumwa: MelderString_append (buffer, U"<", ((const char32 * (*) (int)) tagType) (* (signed short *) address), U">"); break;
 		case booleanwa:  MelderString_append (buffer, * (bool *) address ? U"<true>" : U"<false>"); break;
diff --git a/sys/DemoEditor.cpp b/sys/DemoEditor.cpp
index 46a5f49..f705f8c 100644
--- a/sys/DemoEditor.cpp
+++ b/sys/DemoEditor.cpp
@@ -36,7 +36,7 @@ void structDemoEditor :: v_destroy () noexcept {
 void structDemoEditor :: v_info () {
 	DemoEditor_Parent :: v_info ();
 	MelderInfo_writeLine (U"Colour: ", Graphics_Colour_name (((PraatPicture) praatPicture) -> colour));
-	MelderInfo_writeLine (U"Font: ", kGraphics_font_getText (((PraatPicture) praatPicture) -> font));
+	MelderInfo_writeLine (U"Font: ", kGraphics_font_getText ((kGraphics_font) ((PraatPicture) praatPicture) -> font));
 	MelderInfo_writeLine (U"Font size: ", ((PraatPicture) praatPicture) -> fontSize);
 }
 
@@ -146,7 +146,7 @@ void Demo_open () {
 		editor -> praatPicture = Melder_calloc_f (structPraatPicture, 1);
 		theCurrentPraatPicture = (PraatPicture) editor -> praatPicture;
 		theCurrentPraatPicture -> graphics = editor -> graphics.get();
-		theCurrentPraatPicture -> font = kGraphics_font_HELVETICA;
+		theCurrentPraatPicture -> font = (int) kGraphics_font::HELVETICA;
 		theCurrentPraatPicture -> fontSize = 10;
 		theCurrentPraatPicture -> lineType = Graphics_DRAWN;
 		theCurrentPraatPicture -> colour = Graphics_BLACK;
diff --git a/sys/Editor.cpp b/sys/Editor.cpp
index ced210a..34ceda1 100644
--- a/sys/Editor.cpp
+++ b/sys/Editor.cpp
@@ -366,9 +366,9 @@ void structEditor :: v_do_pictureWindow (EditorCommand cmd) {
 }
 
 void structEditor :: v_form_pictureMargins (EditorCommand cmd) {
-	UiField radio;
+	UiField _radio_;
 	LABEL (U"", U"Margins:")
-	OPTIONMENU_ENUM (U"Write name at top", kEditor_writeNameAtTop, kEditor_writeNameAtTop_DEFAULT);
+	OPTIONMENU_ENUM (U"Write name at top", kEditor_writeNameAtTop, kEditor_writeNameAtTop::DEFAULT);
 }
 void structEditor :: v_ok_pictureMargins (EditorCommand cmd) {
 	SET_ENUM (U"Write name at top", kEditor_writeNameAtTop, pref_picture_writeNameAtTop ());
@@ -505,13 +505,13 @@ void Editor_openPraatPicture (Editor me) {
 	my pictureGraphics = praat_picture_editor_open (my pref_picture_eraseFirst ());
 }
 void Editor_closePraatPicture (Editor me) {
-	if (my data && my pref_picture_writeNameAtTop () != kEditor_writeNameAtTop_NO) {
+	if (my data && my pref_picture_writeNameAtTop () != kEditor_writeNameAtTop::NO_) {
 		Graphics_setNumberSignIsBold (my pictureGraphics, false);
 		Graphics_setPercentSignIsItalic (my pictureGraphics, false);
 		Graphics_setCircumflexIsSuperscript (my pictureGraphics, false);
 		Graphics_setUnderscoreIsSubscript (my pictureGraphics, false);
 		Graphics_textTop (my pictureGraphics,
-			my pref_picture_writeNameAtTop () == kEditor_writeNameAtTop_FAR,
+			my pref_picture_writeNameAtTop () == kEditor_writeNameAtTop::FAR_,
 			my data -> name);
 		Graphics_setNumberSignIsBold (my pictureGraphics, true);
 		Graphics_setPercentSignIsItalic (my pictureGraphics, true);
diff --git a/sys/EditorM.h b/sys/EditorM.h
index 626e641..5b629d0 100644
--- a/sys/EditorM.h
+++ b/sys/EditorM.h
@@ -40,53 +40,113 @@
 #undef SET_REAL
 #undef SET_INTEGER
 #undef SET_STRING
+#undef SET_ENUM
 #undef GET_REAL
 #undef GET_INTEGER
 #undef GET_STRING
 #undef GET_FILE
 
-#define REAL(label,def)		UiForm_addReal (cmd -> d_uiform.get(), label, def);
-#define REAL_OR_UNDEFINED(label,def)  UiForm_addRealOrUndefined (cmd -> d_uiform.get(), label, def);
-#define POSITIVE(label,def)	UiForm_addPositive (cmd -> d_uiform.get(), label, def);
-#define INTEGER(label,def)	UiForm_addInteger (cmd -> d_uiform.get(), label, def);
-#define NATURAL(label,def)	UiForm_addNatural (cmd -> d_uiform.get(), label, def);
-#define WORD(label,def)		UiForm_addWord (cmd -> d_uiform.get(), label, def);
-#define SENTENCE(label,def)	UiForm_addSentence (cmd -> d_uiform.get(), label, def);
-#define COLOUR(label,def)	UiForm_addColour (cmd -> d_uiform.get(), label, def);
-#define CHANNEL(label,def)	UiForm_addChannel (cmd -> d_uiform.get(), label, def);
-#define BOOLEAN(label,def)	UiForm_addBoolean (cmd -> d_uiform.get(), label, def);
-#define LABEL(name,label)	UiForm_addLabel (cmd -> d_uiform.get(), name, label);
-#define TEXTFIELD(name,def)	UiForm_addText (cmd -> d_uiform.get(), name, def);
-#define RADIO(label,def)	radio = UiForm_addRadio (cmd -> d_uiform.get(), label, def);
-#define RADIOBUTTON(label)	UiRadio_addButton (radio, label);
-#define OPTIONMENU(label,def)	radio = UiForm_addOptionMenu (cmd -> d_uiform.get(), label, def);
-#define OPTION(label)	UiOptionMenu_addButton (radio, label);
-#define RADIO_ENUM(label,enum,def) \
-	RADIO (label, def - enum##_MIN + 1) \
-	for (int ienum = enum##_MIN; ienum <= enum##_MAX; ienum ++) \
-		OPTION (enum##_getText (ienum))
-#define OPTIONMENU_ENUM(label,enum,def) \
-	OPTIONMENU (label, def - enum##_MIN + 1) \
-	for (int ienum = enum##_MIN; ienum <= enum##_MAX; ienum ++) \
-		OPTION (enum##_getText (ienum))
-#define LIST(label,n,str,def)	UiForm_addList (cmd -> d_uiform.get(), label, n, str, def);
-#define SET_REAL(name,value)	UiForm_setReal (cmd -> d_uiform.get(), name, value);
-#define SET_INTEGER(name,value)	UiForm_setInteger (cmd -> d_uiform.get(), name, value);
-#define SET_STRING(name,value)	UiForm_setString (cmd -> d_uiform.get(), name, value);
-#define SET_ENUM(name,enum,value)  SET_STRING (name, enum##_getText (value))
-
-#define DIALOG  cmd -> d_uiform
-
 #define EDITOR_ARGS_FORM  EditorCommand cmd, UiForm sendingForm, int narg, Stackel args, const char32 *sendingString, Interpreter interpreter
 #define EDITOR_ARGS_CMD  EditorCommand cmd, UiForm, int, Stackel, const char32 *, Interpreter
 #define EDITOR_ARGS_DIRECT  EditorCommand, UiForm, int, Stackel, const char32 *, Interpreter
-#define EDITOR_FORM(title,helpTitle)  if (! cmd -> d_uiform) { UiField radio = nullptr; (void) radio; \
+#define EDITOR_FORM(title,helpTitle)  if (! cmd -> d_uiform) { UiField _radio_ = nullptr; (void) _radio_; \
 	cmd -> d_uiform = UiForm_createE (cmd, title, cmd -> itemTitle, helpTitle);
 #define EDITOR_OK  UiForm_finish (cmd -> d_uiform.get()); } if (! sendingForm && ! args && ! sendingString) {
 #define EDITOR_DO  UiForm_do (cmd -> d_uiform.get(), false); } else if (! sendingForm) { \
 	UiForm_parseStringE (cmd, narg, args, sendingString, interpreter); } else {
 #define EDITOR_END  }
 
+/*
+	Functions to define the fields in a form on the basis of the label text
+	(or an invisible name) and factory default value.
+	They are to be called between EDITOR_FORM and EDITOR_OK.
+*/
+
+#define REAL(fieldLabel, defaultValue) \
+	UiForm_addReal (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define REAL_OR_UNDEFINED(fieldLabel, defaultValue) \
+	UiForm_addRealOrUndefined (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define POSITIVE(fieldLabel, defaultValue) \
+	UiForm_addPositive (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define INTEGER(fieldLabel, defaultValue) \
+	UiForm_addInteger (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define NATURAL(fieldLabel, defaultValue) \
+	UiForm_addNatural (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define WORD(fieldLabel, defaultValue) \
+	UiForm_addWord (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define SENTENCE(fieldLabel, defaultValue) \
+	UiForm_addSentence (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define COLOUR(fieldLabel, defaultValue) \
+	UiForm_addColour (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define CHANNEL(fieldLabel, defaultValue) \
+	UiForm_addChannel (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define BOOLEAN(fieldLabel, defaultValue) \
+	UiForm_addBoolean (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define LABEL(invisibleName, labelText) \
+	UiForm_addLabel (cmd -> d_uiform.get(), invisibleName, labelText);
+
+#define TEXTFIELD(invisibleName, defaultValue) \
+	UiForm_addText (cmd -> d_uiform.get(), invisibleName, defaultValue);
+
+#define RADIO(fieldLabel, defaultValue) \
+	_radio_ = UiForm_addRadio (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define RADIOBUTTON(fieldLabel) \
+	UiRadio_addButton (_radio_, fieldLabel);
+
+#define OPTIONMENU(fieldLabel, defaultValue) \
+	_radio_ = UiForm_addOptionMenu (cmd -> d_uiform.get(), fieldLabel, defaultValue);
+
+#define OPTION(fieldLabel) \
+	UiOptionMenu_addButton (_radio_, fieldLabel);
+
+#define RADIO_ENUM(fieldLabel, EnumeratedType, defaultValue) \
+	{ enum EnumeratedType _compilerTypeCheckDummy = defaultValue; (void) _compilerTypeCheckDummy; } \
+	_radio_ = UiForm_addRadio (cmd -> d_uiform.get(), fieldLabel, (int) defaultValue - (int) EnumeratedType::MIN + 1); \
+	for (int _ienum = (int) EnumeratedType::MIN; _ienum <= (int) EnumeratedType::MAX; _ienum ++) \
+		UiRadio_addButton (_radio_, EnumeratedType##_getText ((enum EnumeratedType) _ienum));
+
+#define OPTIONMENU_ENUM(fieldLabel, EnumeratedType, defaultValue) \
+	{ enum EnumeratedType _compilerTypeCheckDummy = defaultValue; (void) _compilerTypeCheckDummy; } \
+	_radio_ = UiForm_addOptionMenu (cmd -> d_uiform.get(), fieldLabel, (int) defaultValue - (int) EnumeratedType::MIN + 1); \
+	for (int _ienum = (int) EnumeratedType::MIN; _ienum <= (int) EnumeratedType::MAX; _ienum ++) \
+		UiOptionMenu_addButton (_radio_, EnumeratedType##_getText ((enum EnumeratedType) _ienum));
+
+#define LIST(fieldLabel, numberOfStrings, strings, defaultValue) \
+	UiForm_addList (cmd -> d_uiform.get(), fieldLabel, numberOfStrings, strings, defaultValue);
+
+/*
+	Four optional functions to change the content of a field on the basis of the current
+	editor setting rather than on the basis of the factory default.
+	They are to be called between EDITOR_OK and EDITOR_DO.
+*/
+
+#define SET_REAL(fieldLabel, newValue) \
+	UiForm_setReal (cmd -> d_uiform.get(), fieldLabel, newValue);
+
+#define SET_INTEGER(fieldLabel, newValue) \
+	UiForm_setInteger (cmd -> d_uiform.get(), fieldLabel, newValue);
+
+#define SET_STRING(fieldLabelOrInvisibleName, newValue) \
+	UiForm_setString (cmd -> d_uiform.get(), fieldLabelOrInvisibleName, newValue);
+
+#define SET_ENUM(fieldLabel, EnumeratedType, newValue) \
+	{ enum EnumeratedType _compilerTypeCheckDummy = newValue; (void) _compilerTypeCheckDummy; } \
+	UiForm_setString (cmd -> d_uiform.get(), fieldLabel, EnumeratedType##_getText (newValue));
+
+
+#define DIALOG  cmd -> d_uiform
+
 #define EDITOR_FORM_SAVE(title,helpTitle) \
 	if (! cmd -> d_uiform) { \
 		cmd -> d_uiform = autoUiForm (UiOutfile_createE (cmd, title, cmd -> itemTitle, helpTitle)); \
diff --git a/sys/Editor_enums.h b/sys/Editor_enums.h
index bfb82f8..9aa0ea0 100644
--- a/sys/Editor_enums.h
+++ b/sys/Editor_enums.h
@@ -1,6 +1,6 @@
 /* Editor_enums.h
  *
- * Copyright (C) 1992-2007,2013,2015 Paul Boersma
+ * Copyright (C) 1992-2007,2013,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,9 +17,9 @@
  */
 
 enums_begin (kEditor_writeNameAtTop, 0)
-	enums_add (kEditor_writeNameAtTop, 0, NO, U"no")
-	enums_add (kEditor_writeNameAtTop, 1, FAR, U"far")
-	enums_add (kEditor_writeNameAtTop, 2, NEAR, U"near")
-enums_end (kEditor_writeNameAtTop, 2, FAR)
+	enums_add (kEditor_writeNameAtTop, 0, NO_, U"no")
+	enums_add (kEditor_writeNameAtTop, 1, FAR_, U"far")
+	enums_add (kEditor_writeNameAtTop, 2, NEAR_, U"near")
+enums_end (kEditor_writeNameAtTop, 2, FAR_)
 
 /* End of file Editor_enums.h */
diff --git a/sys/Formula.cpp b/sys/Formula.cpp
index 125de2b..0352c84 100644
--- a/sys/Formula.cpp
+++ b/sys/Formula.cpp
@@ -105,6 +105,7 @@ enum { GEENSYMBOOL_,
 		HERTZ_TO_MEL_, MEL_TO_HERTZ_, HERTZ_TO_SEMITONES_, SEMITONES_TO_HERTZ_,
 		ERB_, HERTZ_TO_ERB_, ERB_TO_HERTZ_,
 		SUM_, MEAN_, STDEV_, CENTER_,
+		EVALUATE_, EVALUATE_NOCHECK_, EVALUATE_STR_, EVALUATE_NOCHECK_STR_,
 		STRINGSTR_, SLEEP_,
 	#define HIGH_FUNCTION_1  SLEEP_
 
@@ -231,6 +232,7 @@ static const char32 *Formula_instructionNames [1 + hoogsteSymbool] = { U"",
 	U"hertzToMel", U"melToHertz", U"hertzToSemitones", U"semitonesToHertz",
 	U"erb", U"hertzToErb", U"erbToHertz",
 	U"sum", U"mean", U"stdev", U"center",
+	U"evaluate", U"evaluate_nocheck", U"evaluate$", U"evaluate_nocheck$",
 	U"string$", U"sleep",
 	U"arctan2", U"randomUniform", U"randomInteger", U"randomGauss", U"randomBinomial",
 	U"chiSquareP", U"chiSquareQ", U"incompleteGammaP", U"invChiSquareQ", U"studentP", U"studentQ", U"invStudentQ",
@@ -666,7 +668,7 @@ static void Formula_lexan () {
 					U_RIGHT_GUILLEMET U" in formula "
 					U"(variables start with lower case; object names contain an underscore).");
 			} else if (str32nequ (token.string, U"Object_", 7)) {
-				long uniqueID = a32tol (token.string + 7);
+				long uniqueID = Melder_atoi (token.string + 7);
 				int i = theCurrentPraatObjects -> n;
 				while (i > 0 && uniqueID != theCurrentPraatObjects -> list [i]. id)
 					i --;
@@ -786,6 +788,23 @@ static void Formula_lexan () {
 			nieuwtok (STRING_)
 			lexan [itok]. content.string = Melder_dup_f (token.string);
 			numberOfStringConstants ++;
+		} else if (kar == U'~') {
+			/*
+				The content of the remainder of the line,
+				including any leading and trailing space,
+				will become a string constant (this is good for formulas).
+			*/
+			nieuwkar;
+			stokaan;
+			for (;;) {
+				if (kar == U'\0') break;
+				stokkar
+			}
+			stokuit;
+			oudkar;
+			nieuwtok (STRING_)
+			lexan [itok]. content.string = Melder_dup_f (token.string);
+			numberOfStringConstants ++;
 		} else if (kar == U'|') {
 			nieuwtok (OR_)   /* "|" = "or" */
 			nieuwkar;
@@ -820,12 +839,12 @@ static void pas (int symbol) {
 	} else {
 		const char32 *symbolName1 = Formula_instructionNames [symbol];
 		const char32 *symbolName2 = Formula_instructionNames [lexan [ilexan]. symbol];
-		bool needQuotes1 = ( str32chr (symbolName1, U' ') == nullptr );
-		bool needQuotes2 = ( str32chr (symbolName2, U' ') == nullptr );
+		bool needQuotes1 = ! str32chr (symbolName1, U' ');
+		bool needQuotes2 = ! str32chr (symbolName2, U' ');
 		static MelderString melding { };
 		MelderString_copy (& melding,
-			U"Expected ", needQuotes1 ? U"\"" : nullptr, symbolName1, needQuotes1 ? U"\"" : nullptr,
-			U", but found ", needQuotes2 ? U"\"" : nullptr, symbolName2, needQuotes2 ? U"\"" : nullptr);
+			U"Expected ", ( needQuotes1 ? U"\"" : nullptr ), symbolName1, ( needQuotes1 ? U"\"" : nullptr ),
+			U", but found ", ( needQuotes2 ? U"\"" : nullptr ), symbolName2, ( needQuotes2 ? U"\"" : nullptr ));
 		formulefout (melding.string, lexan [ilexan]. position);
 	}
 }
@@ -835,10 +854,10 @@ static bool pasArguments () {
     if (symbol == HAAKJEOPENEN_) return true;   // success: a function call like: myFunction (...)
     if (symbol == COLON_) return false;   // success: a function call like: myFunction: ...
     const char32 *symbolName2 = Formula_instructionNames [lexan [ilexan]. symbol];
-    bool needQuotes2 = ( str32chr (symbolName2, U' ') == nullptr );
+    bool needQuotes2 = ! str32chr (symbolName2, U' ');
     static MelderString melding { };
     MelderString_copy (& melding,
-		U"Expected \"(\" or \":\", but found ", needQuotes2 ? U"\"" : nullptr, symbolName2, needQuotes2 ? U"\"" : nullptr);
+		U"Expected \"(\" or \":\", but found ", ( needQuotes2 ? U"\"" : nullptr ), symbolName2, ( needQuotes2 ? U"\"" : nullptr ));
     formulefout (melding.string, lexan [ilexan]. position);
     return false;   // will never occur
 }
@@ -2005,8 +2024,9 @@ static void Formula_removeLabels () {
 		}
 	}
 	/*
-	 * Then remove the labels, which have become superfluous.
-	 */
+		Then remove the labels,
+		which have become superfluous.
+	*/
 	if (theOptimize) {
 		int i = 1;
 		while (i <= numberOfInstructions) {
@@ -2018,7 +2038,7 @@ static void Formula_removeLabels () {
 					if ((symbolj == GOTO_ || symbolj == IFTRUE_ || symbolj == IFFALSE_ || symbolj == INCREMENT_GREATER_GOTO_) && parse [j]. content.label > i)
 						parse [j]. content.label --;  /* Pas een label aan. */
 				}
-				i --;   /* Voorkom ophogen i (overbodig?). */
+				i --;   // voorkom ophogen i (overbodig?)
 			}
 			i ++;
 		}
@@ -2126,20 +2146,25 @@ static void Stackel_cleanUp (Stackel me) {
 	if (my which == Stackel_STRING) {
 		Melder_free (my string);
 	} else if (my which == Stackel_NUMERIC_VECTOR) {
-		NUMvector_free (my numericVector.at, 1);
+		if (my owned) {
+			NUMvector_free (my numericVector.at, 1);
+		}
 		my numericVector = empty_numvec;
 	} else if (my which == Stackel_NUMERIC_MATRIX) {
-		NUMmatrix_free (my numericMatrix.at, 1, 1);
+		if (my owned) {
+			NUMmatrix_free (my numericMatrix.at, 1, 1);
+		}
 		my numericMatrix = empty_nummat;
 	}
 }
 static Stackel theStack;
 static int w, wmax;   /* w = stack pointer; */
 #define pop  & theStack [w --]
+#define topOfStack  & theStack [w]
 inline static void pushNumber (double x) {
 	/* inline runs 10 to 20 percent faster; here's the test script:
 		stopwatch
-		Create Sound from formula: "test", 1, 0.0, 1000.0, 44100, "x + 1 + 2 + 3 + 4 + 5 + 6"
+		Create Sound from formula: "test", 1, 0.0, 1000.0, 44100, ~ x + 1 + 2 + 3 + 4 + 5 + 6
 		writeInfoLine: stopwatch
 		Remove
 	 * Mac: 3.76 -> 3.20 seconds
@@ -2150,6 +2175,7 @@ inline static void pushNumber (double x) {
 	stackel -> which = Stackel_NUMBER;
 	stackel -> number = isdefined (x) ? x : undefined;
 	//stackel -> number = x;   // this one would be 2 percent faster
+	//stackel -> owned = true;
 }
 static void pushNumericVector (autonumvec x) {
 	Stackel stackel = & theStack [++ w];
@@ -2157,6 +2183,15 @@ static void pushNumericVector (autonumvec x) {
 	if (w > wmax) wmax ++;
 	stackel -> which = Stackel_NUMERIC_VECTOR;
 	stackel -> numericVector = x.releaseToAmbiguousOwner();
+	stackel -> owned = true;
+}
+static void pushNumericVectorReference (numvec x) {
+	Stackel stackel = & theStack [++ w];
+	if (stackel -> which > Stackel_NUMBER) Stackel_cleanUp (stackel);
+	if (w > wmax) wmax ++;
+	stackel -> which = Stackel_NUMERIC_VECTOR;
+	stackel -> numericVector = x;
+	stackel -> owned = false;
 }
 static void pushNumericMatrix (autonummat x) {
 	Stackel stackel = & theStack [++ w];
@@ -2164,6 +2199,15 @@ static void pushNumericMatrix (autonummat x) {
 	if (w > wmax) wmax ++;
 	stackel -> which = Stackel_NUMERIC_MATRIX;
 	stackel -> numericMatrix = x.releaseToAmbiguousOwner();
+	stackel -> owned = true;
+}
+static void pushNumericMatrixReference (nummat x) {
+	Stackel stackel = & theStack [++ w];
+	if (stackel -> which > Stackel_NUMBER) Stackel_cleanUp (stackel);
+	if (w > wmax) wmax ++;
+	stackel -> which = Stackel_NUMERIC_MATRIX;
+	stackel -> numericMatrix = x;
+	stackel -> owned = false;
 }
 static void pushString (char32 *x) {
 	Stackel stackel = & theStack [++ w];
@@ -2171,6 +2215,7 @@ static void pushString (char32 *x) {
 	if (w > wmax) wmax ++;
 	stackel -> which = Stackel_STRING;
 	stackel -> string = x;
+	//stackel -> owned = true;
 }
 static void pushObject (Daata object) {
 	Stackel stackel = & theStack [++ w];
@@ -2178,6 +2223,7 @@ static void pushObject (Daata object) {
 	if (w > wmax) wmax ++;
 	stackel -> which = Stackel_OBJECT;
 	stackel -> object = object;
+	//stackel -> owned = false;
 }
 static void pushVariable (InterpreterVariable var) {
 	Stackel stackel = & theStack [++ w];
@@ -2185,6 +2231,7 @@ static void pushVariable (InterpreterVariable var) {
 	if (w > wmax) wmax ++;
 	stackel -> which = Stackel_VARIABLE;
 	stackel -> variable = var;
+	//stackel -> owned = false;
 }
 const char32 *Stackel_whichText (Stackel me) {
 	return
@@ -2360,189 +2407,471 @@ static void do_gt () {
 		Melder_throw (U"Cannot compare (>) ", Stackel_whichText (x), U" to ", Stackel_whichText (y), U".");
 	}
 }
+inline static void moveNumericVector (Stackel from, Stackel to) {
+	//Melder_assert (from -> owned);
+	//Melder_assert (to -> which == Stackel_NUMERIC_VECTOR);
+	from -> owned = false;
+	to -> numericVector = from -> numericVector;
+	to -> owned = true;
+}
+inline static void moveNumericMatrix (Stackel from, Stackel to) {
+	//Melder_assert (from -> owned);
+	//Melder_assert (to -> which == Stackel_NUMERIC_MATRIX);
+	from -> owned = false;
+	to -> numericMatrix = from -> numericMatrix;
+	to -> owned = true;
+}
+inline static void numvec_addScalar (numvec x, real number) {
+	for (integer i = 1; i <= x.size; i ++) {
+		x [i] += number;
+	}
+}
+inline static void nummat_addScalar (nummat x, real number) {
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			x [irow] [icol] += number;
+		}
+	}
+}
+inline static void numvec_addNumvec (numvec x, numvec y) {
+	for (integer i = 1; i <= x.size; i ++) {
+		x [i] += y [i];
+	}
+}
+inline static void nummat_addNummat (nummat x, nummat y) {
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			x [irow] [icol] += y [irow] [icol];
+		}
+	}
+}
+inline static void numvec_multiplyByScalar (numvec x, real factor) {
+	for (integer i = 1; i <= x.size; i ++) {
+		x [i] *= factor;
+	}
+}
+inline static void nummat_multiplyByScalar (nummat x, real factor) {
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			x [irow] [icol] *= factor;
+		}
+	}
+}
+inline static autonumvec add_numvec (numvec x, real addend) {
+	autonumvec result (x.size, false);
+	for (long i = 1; i <= x.size; i ++) {
+		result [i] = x [i] + addend;
+	}
+	return result;
+}
+inline static autonummat add_nummat (nummat x, real addend) {
+	autonummat result (x.nrow, x.ncol, false);
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			result [irow] [icol] = x [irow] [icol] + addend;
+		}
+	}
+	return result;
+}
+/**
+	result.. = x.. + y..
+*/
 static void do_add () {
-	/*
-		result.. = x.. + y..
-	*/
-	Stackel y = pop, x = pop;
+	Stackel y = pop, x = topOfStack;
 	if (x->which == Stackel_NUMBER) {
-		double xvalue = x->number;
 		if (y->which == Stackel_NUMBER) {
-			/*
+			/*@praat
+				#
+				# result = x + y
+				#
+				x = 5
+				y = 6
 				result = x + y
-			*/
-			double yvalue = y->number;
-			pushNumber (xvalue + yvalue);
+				assert result = 11
+			@*/
+			x->number += y->number;
+			//x->which = Stackel_NUMBER;   // superfluous, as is cleaning up
 			return;
 		}
 		if (y->which == Stackel_NUMERIC_VECTOR) {
 			/*
 				result# = x + y#
 			*/
-			long ny = y->numericVector.size;
-			autonumvec result (ny, false);
-			for (long i = 1; i <= ny; i ++) {
-				double yvalue = y->numericVector [i];
-				result [i] = xvalue + yvalue;
+			if (y->owned) {
+				/*@praat
+					#
+					# result# = x + owned y#
+					#
+					result# = 5 + { 11, 13, 31 }   ; numeric vector literals are owned
+					assert result# = { 16, 18, 36 }
+				@*/
+				numvec_addScalar (y->numericVector, x->number);
+				// x does not have to be cleaned up, because it was a number
+				y->owned = false, x->numericVector = y->numericVector, x->owned = true;   // move
+			} else {
+				/*@praat
+					#
+					# result# = x + unowned y#
+					#
+					y# = { 17, -11, 29 }
+					result# = 30 + y#   ; numeric vector variables are not owned
+					assert result# = { 47, 19, 59 }
+				@*/
+				// x does not have to be cleaned up, because it was a number
+				x->numericVector = add_numvec (y->numericVector, x->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
-			pushNumericVector (result.move());
+			x->which = Stackel_NUMERIC_VECTOR;
 			return;
 		}
 		if (y->which == Stackel_NUMERIC_MATRIX) {
 			/*
 				result## = x + y##
 			*/
-			long nrow = y->numericMatrix.nrow, ncol = y->numericMatrix.ncol;
-			autonummat result (nrow, ncol, false);
-			for (long irow = 1; irow <= nrow; irow ++) {
-				for (long icol = 1; icol <= ncol; icol ++) {
-					double yvalue = y->numericMatrix [irow] [icol];
-					result [irow] [icol] = xvalue + yvalue;
-				}
+			if (y->owned) {
+				nummat_addScalar (y->numericMatrix, x->number);
+				// x does not have to be cleaned up, because it was a number
+				y->owned = false, x->numericMatrix = y->numericMatrix, x->owned = true;   // move
+			} else {
+				// x does not have to be cleaned up, because it was a number
+				x->numericMatrix = add_nummat (y->numericMatrix, x->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
-			pushNumericMatrix (result.move());
+			x->which = Stackel_NUMERIC_MATRIX;
 			return;
 		}
 	}
 	if (x->which == Stackel_NUMERIC_VECTOR) {
+		if (y->which == Stackel_NUMERIC_VECTOR) {
+			/*
+				result# = x# + y#
+				i.e.
+				result# [i] = x# [i] + y# [i]
+			*/
+			long nx = x->numericVector.size, ny = y->numericVector.size;
+			if (nx != ny) {
+				/*@praat
+					#
+					# Error: unequal sizes.
+					#
+					x# = { 11, 13, 17 }
+					y# = { 8, 90 }
+					asserterror When adding vectors, their numbers of elements should be equal, instead of 3 and 2.
+					result# = x# + y#
+				@*/
+				Melder_throw (U"When adding vectors, their numbers of elements should be equal, instead of ", nx, U" and ", ny, U".");
+			}
+			if (x -> owned) {
+				/*@praat
+					#
+					# result# = owned x# + y#
+					#
+					x# = { 11, 13, 17 }
+					result# = x# + { 44, 56, 67 }   ; owned + unowned
+					assert result# = { 55, 69, 84 }
+					y# = { 3, 2, 89.5 }
+					result# = x# + y#   ; owned + owned
+					assert result# = { 14, 15, 106.5 }
+				@*/
+				numvec_addNumvec (x->numericVector, y->numericVector);
+			} else if (y -> owned) {
+				/*@praat
+					#
+					# result# = unowned x# + owned y#
+					#
+					x# = { 14, -3, 6.25 }
+					result# = x# + { 55, 1, -89 }
+					assert result# = { 69, -2, -82.75 }
+				@*/
+				numvec_addNumvec (y->numericVector, x->numericVector);
+				// x does not have to be cleaned up, because it was not owned
+				y->owned = false, x->numericVector = y->numericVector, x->owned = true;   // move
+			} else {
+				/*@praat
+					#
+					# result# = unowned x# + unowned y#
+					#
+					x# = { 14, -33, 6.25 }
+					y# = { -33, 17, 9 }
+					result# = x# + y#
+					assert result# = { -19, -16, 15.25 }
+				@*/
+				// x does not have to be cleaned up, because it was not owned
+				x->numericVector = add_numvec (x->numericVector, y->numericVector). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_VECTOR;   // superfluous
+			return;
+		}
 		if (y->which == Stackel_NUMBER) {
 			/*
 				result# = x# + y
 				i.e.
 				result# [i] = x# [i] + y
 			*/
-			integer size = x->numericVector.size;
-			autonumvec result (size, false);
-			real yvalue = y->number;
-			for (integer i = 1; i <= size; i ++) {
-				double xvalue = x->numericVector [i];
-				result [i] = xvalue + yvalue;
+			if (x->owned) {
+				numvec_addScalar (x->numericVector, y->number);
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericVector = add_numvec (x->numericVector, y->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
-			pushNumericVector (result.move());
+			//x->which = Stackel_NUMERIC_VECTOR;   // superfluous
 			return;
 		}
-		if (y->which == Stackel_NUMERIC_VECTOR) {
-			long nx = x->numericVector.size, ny = y->numericVector.size;
-			if (nx != ny)
-				Melder_throw (U"When adding vectors, their numbers of elements should be equal, instead of ", nx, U" and ", ny, U".");
-			pushNumericVector (add_numvec (x->numericVector, y->numericVector));
+	}
+	if (x->which == Stackel_NUMERIC_MATRIX) {
+		if (y->which == Stackel_NUMERIC_MATRIX) {
+			/*
+				result## = x## + y##
+				i.e.
+				result## [i, j] = x## [i, j] + y## [i, j]
+			*/
+			long xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol;
+			long ynrow = y->numericMatrix.nrow, yncol = y->numericMatrix.ncol;
+			if (xnrow != ynrow)
+				Melder_throw (U"When adding matrices, their numbers of rows should be equal, instead of ", xnrow, U" and ", ynrow, U".");
+			if (xncol != yncol)
+				Melder_throw (U"When adding matrices, their numbers of columns should be equal, instead of ", xncol, U" and ", yncol, U".");
+			if (x->owned) {
+				nummat_addNummat (x->numericMatrix, y->numericMatrix);
+			} else if (y->owned) {
+				nummat_addNummat (y->numericMatrix, x->numericMatrix);
+				// x does not have to be cleaned up, because it was not owned
+				y->owned = false, x->numericMatrix = y->numericMatrix, x->owned = true;   // move
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericMatrix = add_nummat (x->numericMatrix, y->numericMatrix). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_MATRIX;
 			return;
 		}
-	}
-	if (x->which == Stackel_NUMERIC_MATRIX && y->which == Stackel_NUMERIC_MATRIX) {
-		long xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol;
-		long ynrow = y->numericMatrix.nrow, yncol = y->numericMatrix.ncol;
-		if (xnrow != ynrow)
-			Melder_throw (U"When adding matrices, their numbers of rows should be equal, instead of ", xnrow, U" and ", ynrow, U".");
-		if (xncol != yncol)
-			Melder_throw (U"When adding matrices, their numbers of columns should be equal, instead of ", xncol, U" and ", yncol, U".");
-		autonummat result (xnrow, xncol, false);
-		for (long irow = 1; irow <= xnrow; irow ++) {
-			for (long icol = 1; icol <= xncol; icol ++) {
-				double xvalue = x->numericMatrix [irow] [icol];
-				double yvalue = y->numericMatrix [irow] [icol];
-				result [irow] [icol] = xvalue + yvalue;
+		if (y->which == Stackel_NUMBER) {
+			/*
+				result## = x## + y
+				i.e.
+				result## [i, j] = x## [i, j] + y
+			*/
+			if (x->owned) {
+				nummat_addScalar (x->numericMatrix, y->number);
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericMatrix = add_nummat (x->numericMatrix, y->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
+			//x->which = Stackel_NUMERIC_MATRIX;   // superfluous
+			return;
 		}
-		pushNumericMatrix (result.move());
-		return;
 	}
 	if (x->which == Stackel_STRING && y->which == Stackel_STRING) {
+		/*
+			result$ = x$ + y$
+		*/
 		long length1 = str32len (x->string), length2 = str32len (y->string);
 		char32 *result = Melder_malloc (char32, length1 + length2 + 1);
 		str32cpy (result, x->string);
 		str32cpy (result + length1, y->string);
-		pushString (result);
+		Melder_free (x->string);   // clean up
+		x->string = result;
+		//x->which = Stackel_STRING;   // superfluous
 		return;
 	}
 	Melder_throw (U"Cannot add ", Stackel_whichText (y), U" to ", Stackel_whichText (x), U".");
 }
+inline static void numvec_subtractScalar (numvec x, real number) {
+	for (integer i = 1; i <= x.size; i ++) {
+		x [i] -= number;
+	}
+}
+inline static void numvec_subtractScalarReversed (numvec x, real number) {
+	for (integer i = 1; i <= x.size; i ++) {
+		x [i] = number - x [i];
+	}
+}
+inline static void nummat_subtractScalar (nummat x, real number) {
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			x [irow] [icol] -= number;
+		}
+	}
+}
+inline static void nummat_subtractScalarReversed (nummat x, real number) {
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			x [irow] [icol] = number - x [irow] [icol];
+		}
+	}
+}
+inline static void numvec_subtractNumvec (numvec x, numvec y) {
+	for (integer i = 1; i <= x.size; i ++) {
+		x [i] -= y [i];
+	}
+}
+inline static void numvec_subtractNumvecReversed (numvec x, numvec y) {
+	for (integer i = 1; i <= x.size; i ++) {
+		x [i] = y [i] - x [i];
+	}
+}
+inline static void nummat_subtractNummat (nummat x, nummat y) {
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			x [irow] [icol] -= y [irow] [icol];
+		}
+	}
+}
+inline static void nummat_subtractNummatReversed (nummat x, nummat y) {
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			x [irow] [icol] = y [irow] [icol] - x [irow] [icol];
+		}
+	}
+}
+inline static autonumvec sub_numvec (numvec x, real y) {
+	autonumvec result (x.size, false);
+	for (integer i = 1; i <= x.size; i ++) {
+		result [i] = x [i] - y;
+	}
+	return result;
+}
+inline static autonumvec sub_numvec (real x, numvec y) {
+	autonumvec result (y.size, false);
+	for (integer i = 1; i <= y.size; i ++) {
+		result [i] = x - y [i];
+	}
+	return result;
+}
+inline static autonummat sub_nummat (nummat x, real y) {
+	autonummat result (x.nrow, x.ncol, false);
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			result [irow] [icol] = x [irow] [icol] - y;
+		}
+	}
+	return result;
+}
+inline static autonummat sub_nummat (real x, nummat y) {
+	autonummat result (y.nrow, y.ncol, false);
+	for (integer irow = 1; irow <= y.nrow; irow ++) {
+		for (integer icol = 1; icol <= y.ncol; icol ++) {
+			result [irow] [icol] = x - y [irow] [icol];
+		}
+	}
+	return result;
+}
 static void do_sub () {
 	/*
 		result.. = x.. - y..
 	*/
-	Stackel y = pop, x = pop;
+	Stackel y = pop, x = topOfStack;
 	if (x->which == Stackel_NUMBER) {
-		double xvalue = x->number;
 		if (y->which == Stackel_NUMBER) {
 			/*
 				result = x - y
 			*/
-			double yvalue = y->number;
-			pushNumber (xvalue - yvalue);
+			x->number -= y->number;
+			//x->which = Stackel_NUMBER;   // superfluous
 			return;
 		}
 		if (y->which == Stackel_NUMERIC_VECTOR) {
 			/*
 				result# = x - y#
 			*/
-			long ny = y->numericVector.size;
-			autonumvec result (ny, false);
-			for (long i = 1; i <= ny; i ++) {
-				double yvalue = y->numericVector [i];
-				result [i] = xvalue - yvalue;
+			if (y->owned) {
+				numvec_subtractScalarReversed (y->numericVector, x->number);
+				y->owned = false, x->numericVector = y->numericVector, x->owned = true;   // move
+			} else {
+				x->numericVector = sub_numvec (x->number, y->numericVector). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
-			pushNumericVector (result.move());
+			x->which = Stackel_NUMERIC_VECTOR;
 			return;
 		}
 		if (y->which == Stackel_NUMERIC_MATRIX) {
 			/*
 				result## = x - y##
 			*/
-			long nrow = y->numericMatrix.nrow, ncol = y->numericMatrix.ncol;
-			autonummat result (nrow, ncol, false);
-			for (long irow = 1; irow <= nrow; irow ++) {
-				for (long icol = 1; icol <= ncol; icol ++) {
-					double yvalue = y->numericMatrix [irow] [icol];
-					result [irow] [icol] = xvalue - yvalue;
-				}
+			if (y->owned) {
+				nummat_subtractScalarReversed (y->numericMatrix, x->number);
+				y->owned = false, x->numericMatrix = y->numericMatrix, x->owned = true;   // move
+			} else {
+				x->numericMatrix = sub_nummat (x->number, y->numericMatrix). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
-			pushNumericMatrix (result.move());
+			x->which = Stackel_NUMERIC_MATRIX;
 			return;
 		}
 	}
 	if (x->which == Stackel_NUMERIC_VECTOR) {
+		if (y->which == Stackel_NUMERIC_VECTOR) {
+			/*
+				result# = x# - y#
+				i.e.
+				result# [i] = x# [i] - y# [i]
+			*/
+			long nx = x->numericVector.size, ny = y->numericVector.size;
+			if (nx != ny)
+				Melder_throw (U"When subtracting vectors, their numbers of elements should be equal, instead of ", nx, U" and ", ny, U".");
+			if (x -> owned) {
+				numvec_subtractNumvec (x->numericVector, y->numericVector);
+			} else if (y -> owned) {
+				numvec_subtractNumvecReversed (y->numericVector, x->numericVector);
+				moveNumericVector (y, x);
+			} else {
+				// no clean-up of x required, because x is not owned and has the right type
+				x->numericVector = sub_numvec (x->numericVector, y->numericVector). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_VECTOR;   // superfluous
+			return;
+		}
 		if (y->which == Stackel_NUMBER) {
 			/*
 				result# = x# - y
 				i.e.
 				result# [i] = x# [i] - y
 			*/
-			integer size = x->numericVector.size;
-			autonumvec result (size, false);
-			real yvalue = y->number;
-			for (integer i = 1; i <= size; i ++) {
-				double xvalue = x->numericVector [i];
-				result [i] = xvalue - yvalue;
+			if (x->owned) {
+				numvec_subtractScalar(x->numericVector, y->number);
+			} else {
+				x->numericVector = sub_numvec (x->numericVector, y->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
-			pushNumericVector (result.move());
+			//x->which = Stackel_NUMERIC_VECTOR;   // superfluous
 			return;
 		}
-		if (y->which == Stackel_NUMERIC_VECTOR) {
-			long nx = x->numericVector.size, ny = y->numericVector.size;
-			if (nx != ny)
-				Melder_throw (U"When subtracting vectors, their numbers of elements should be equal, instead of ", nx, U" and ", ny, U".");
-			pushNumericVector (sub_numvec (x->numericVector, y->numericVector));
+	}
+	if (x->which == Stackel_NUMERIC_MATRIX) {
+		if (y->which == Stackel_NUMERIC_MATRIX) {
+			long xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol;
+			long ynrow = y->numericMatrix.nrow, yncol = y->numericMatrix.ncol;
+			if (xnrow != ynrow)
+				Melder_throw (U"When subtracting matrices, their numbers of rows should be equal, instead of ", xnrow, U" and ", ynrow, U".");
+			if (xncol != yncol)
+				Melder_throw (U"When subtracting matrices, their numbers of columns should be equal, instead of ", xncol, U" and ", yncol, U".");
+			if (x->owned) {
+				nummat_subtractNummat (x->numericMatrix, y->numericMatrix);
+			} else if (y->owned) {
+				nummat_subtractNummatReversed (y->numericMatrix, x->numericMatrix);
+				moveNumericMatrix (y, x);
+			} else {
+				// no clean-up of x required, because x is not owned and has the right type
+				x->numericMatrix = sub_nummat (x->numericMatrix, y->numericMatrix). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_MATRIX;   // superfluous
 			return;
 		}
-	}
-	if (x->which == Stackel_NUMERIC_MATRIX && y->which == Stackel_NUMERIC_MATRIX) {
-		long xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol;
-		long ynrow = y->numericMatrix.nrow, yncol = y->numericMatrix.ncol;
-		if (xnrow != ynrow)
-			Melder_throw (U"When subtracting matrices, their numbers of rows should be equal, instead of ", xnrow, U" and ", ynrow, U".");
-		if (xncol != yncol)
-			Melder_throw (U"When subtracting matrices, their numbers of columns should be equal, instead of ", xncol, U" and ", yncol, U".");
-		autonummat result (xnrow, xncol, false);
-		for (long irow = 1; irow <= xnrow; irow ++) {
-			for (long icol = 1; icol <= xncol; icol ++) {
-				double xvalue = x->numericMatrix [irow] [icol];
-				double yvalue = y->numericMatrix [irow] [icol];
-				result [irow] [icol] = xvalue - yvalue;
+		if (y->which == Stackel_NUMBER) {
+			if (x->owned) {
+				nummat_subtractScalar (x->numericMatrix, y->number);
+			} else {
+				x->numericMatrix = sub_nummat (x->numericMatrix, y->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
+			//x->which = Stackel_NUMERIC_MATRIX;   // superfluous
+			return;
 		}
-		pushNumericMatrix (result.move());
-		return;
 	}
 	if (x->which == Stackel_STRING && y->which == Stackel_STRING) {
 		int64 length1 = str32len (x->string), length2 = str32len (y->string), newlength = length1 - length2;
@@ -2554,7 +2883,9 @@ static void do_sub () {
 		} else {
 			result = Melder_dup (x->string);
 		}
-		pushString (result);
+		Melder_free (x->string);
+		x->string = result;
+		//x->which = Stackel_STRING;   // superfluous
 		return;
 	}
 	Melder_throw (U"Cannot subtract (-) ", Stackel_whichText (y), U" from ", Stackel_whichText (x), U".");
@@ -2578,28 +2909,42 @@ static void do_mul () {
 			/*
 				result# = x * y#
 			*/
-			long ny = y->numericVector.size;
-			autonumvec result { ny, false };
-			for (long i = 1; i <= ny; i ++) {
-				double yvalue = y->numericVector [i];
-				result [i] = xvalue * yvalue;
+			if (y->owned) {
+				numvec_multiplyByScalar (y->numericVector, xvalue);
+				x->which = Stackel_NUMERIC_VECTOR;
+				moveNumericVector (y, x);
+				w ++;
+			} else {
+				long ny = y->numericVector.size;
+				autonumvec result { ny, false };
+				for (long i = 1; i <= ny; i ++) {
+					double yvalue = y->numericVector [i];
+					result [i] = xvalue * yvalue;
+				}
+				pushNumericVector (result.move());
 			}
-			pushNumericVector (result.move());
 			return;
 		}
 		if (y->which == Stackel_NUMERIC_MATRIX) {
 			/*
 				result## = x * y##
 			*/
-			long nrow = y->numericMatrix.nrow, ncol = y->numericMatrix.ncol;
-			autonummat result (nrow, ncol, false);
-			for (long irow = 1; irow <= nrow; irow ++) {
-				for (long icol = 1; icol <= ncol; icol ++) {
-					double yvalue = y->numericMatrix [irow] [icol];
-					result [irow] [icol] = xvalue * yvalue;
+			if (y->owned) {
+				nummat_multiplyByScalar (y->numericMatrix, xvalue);
+				x->which = Stackel_NUMERIC_MATRIX;
+				moveNumericMatrix (y, x);
+				w ++;
+			} else {
+				long nrow = y->numericMatrix.nrow, ncol = y->numericMatrix.ncol;
+				autonummat result (nrow, ncol, false);
+				for (long irow = 1; irow <= nrow; irow ++) {
+					for (long icol = 1; icol <= ncol; icol ++) {
+						double yvalue = y->numericMatrix [irow] [icol];
+						result [irow] [icol] = xvalue * yvalue;
+					}
 				}
+				pushNumericMatrix (result.move());
 			}
-			pushNumericMatrix (result.move());
 			return;
 		}
 	}
@@ -2709,35 +3054,34 @@ static void do_function_n_n (double (*f) (double)) {
 	}
 }
 static void do_functionvec_n_n (double (*f) (double)) {
-	#if 0
-	Stackel x = pop;
-	if (x->which == Stackel_NUMERIC_VECTOR) {
-		long nelm = x->numericVector.numberOfElements;
-		double *result = NUMvector<double> (1, nelm);
-		for (long i = 1; i <= nelm; i ++) {
-			result [i] = f (x->numericVector.data [i]);
-		}
-		pushNumericVector (nelm, result);
-	} else {
-		Melder_throw (U"The function ", Formula_instructionNames [parse [programPointer]. symbol],
-			U" requires a numeric vector argument, not ", Stackel_whichText (x), U".");
-	}
-	#else
 	Stackel x = & theStack [w];
 	if (x->which == Stackel_NUMERIC_VECTOR) {
-		long nelm = x->numericVector.size;
-		for (long i = 1; i <= nelm; i ++) {
-			x->numericVector [i] = f (x->numericVector [i]);
+		integer n = x->numericVector.size;
+		real *at = x->numericVector.at;
+		if (x->owned) {
+			for (integer i = 1; i <= n; i ++) {
+				at [i] = f (at [i]);
+			}
+		} else {
+			autonumvec result { n, false };
+			for (integer i = 1; i <= n; i ++) {
+				result [i] = f (at [i]);
+			}
+			x->numericVector = result. releaseToAmbiguousOwner();
+			x->owned = true;
 		}
 	} else {
 		Melder_throw (U"The function ", Formula_instructionNames [parse [programPointer]. symbol],
 			U" requires a numeric vector argument, not ", Stackel_whichText (x), U".");
 	}
-	#endif
 }
 static void do_softmax () {
 	Stackel x = & theStack [w];
 	if (x->which == Stackel_NUMERIC_VECTOR) {
+		if (! x->owned) {
+			x->numericVector = copy_numvec (x->numericVector). releaseToAmbiguousOwner();
+			x->owned = true;
+		}
 		long nelm = x->numericVector.size;
 		double maximum = -1e308;
 		for (long i = 1; i <= nelm; i ++) {
@@ -3201,6 +3545,48 @@ static void do_do () {
 	praat_updateSelection ();   // BUG: superfluous? flickering?
 	pushNumber (1);
 }
+static void do_evaluate () {
+	Stackel expression = pop;
+	if (expression->which == Stackel_STRING) {
+		double result;
+		Interpreter_numericExpression (theInterpreter, expression->string, & result);
+		pushNumber (result);
+	} else Melder_throw (U"The argument of the function \"evaluate\" should be a string with a numeric expression, not ", Stackel_whichText (expression));
+}
+static void do_evaluate_nocheck () {
+	Stackel expression = pop;
+	if (expression->which == Stackel_STRING) {
+		try {
+			double result;
+			Interpreter_numericExpression (theInterpreter, expression->string, & result);
+			pushNumber (result);
+		} catch (MelderError) {
+			Melder_clearError ();
+			pushNumber (undefined);
+		}
+	} else Melder_throw (U"The argument of the function \"evaluate_nocheck\" should be a string with a numeric expression, not ", Stackel_whichText (expression));
+}
+static void do_evaluateStr () {
+	Stackel expression = pop;
+	if (expression->which == Stackel_STRING) {
+		char32 *result;
+		Interpreter_stringExpression (theInterpreter, expression->string, & result);
+		pushString (result);
+	} else Melder_throw (U"The argument of the function \"evaluate$\" should be a string with a string expression, not ", Stackel_whichText (expression));
+}
+static void do_evaluate_nocheckStr () {
+	Stackel expression = pop;
+	if (expression->which == Stackel_STRING) {
+		try {
+			char32 *result;
+			Interpreter_stringExpression (theInterpreter, expression->string, & result);
+			pushString (result);
+		} catch (MelderError) {
+			Melder_clearError ();
+			pushString (Melder_dup (U""));
+		}
+	} else Melder_throw (U"The argument of the function \"evaluate_nocheck$\" should be a string with a string expression, not ", Stackel_whichText (expression));
+}
 static void do_doStr () {
 	Stackel narg = pop;
 	Melder_assert (narg->which == Stackel_NUMBER);
@@ -4037,7 +4423,7 @@ static void do_midStr () {
 			if (newlength > 0) {
 				result.reset (Melder_malloc (char32, newlength + 1));
 				str32ncpy (result.peek(), s->string + start - 1, newlength);
-				result [newlength] = '\0';
+				result [newlength] = U'\0';
 			} else {
 				result.reset (Melder_dup (U""));
 			}
@@ -4118,7 +4504,7 @@ static void do_rindex () {
 			Stackel_whichText (whole), U" and ", Stackel_whichText (part), U".");
 	}
 }
-static void do_stringMatchesCriterion (int criterion) {
+static void do_stringMatchesCriterion (kMelder_string criterion) {
 	Stackel t = pop, s = pop;
 	if (s->which == Stackel_STRING && t->which == Stackel_STRING) {
 		int result = Melder_stringMatchesCriterion (s->string, criterion, t->string);
@@ -4194,8 +4580,8 @@ static void do_extractNumber () {
 				pushNumber (undefined);
 			} else {
 				char32 buffer [101], *slash;
-				int i;
-				for (i = 0; i < 100; i ++) {
+				int i = 0;
+				for (; i < 100; i ++) {
 					buffer [i] = *substring;
 					substring ++;
 					if (*substring == U'\0' || *substring == U' ' || *substring == U'\t' || *substring == U'\n' || *substring == U'\r') break;
@@ -4372,8 +4758,14 @@ static void do_selectObject () {
 		} else if (object -> which == Stackel_STRING) {
 			int IOBJECT = praat_findObjectFromString (object -> string);
 			praat_select (IOBJECT);
+		} else if (object -> which == Stackel_NUMERIC_VECTOR) {
+			numvec vec = object -> numericVector;
+			for (int ielm = 1; ielm <= vec.size; ielm ++) {
+				int IOBJECT = praat_findObjectById (lround (vec [ielm]));
+				praat_select (IOBJECT);
+			}
 		} else {
-			Melder_throw (U"The function \"selectObject\" takes numbers and strings, not ", Stackel_whichText (object));
+			Melder_throw (U"The function \"selectObject\" takes numbers, strings, or numeric vectors, not ", Stackel_whichText (object));
 		}
 	}
 	praat_show ();
@@ -4389,8 +4781,14 @@ static void do_plusObject () {
 		} else if (object -> which == Stackel_STRING) {
 			int IOBJECT = praat_findObjectFromString (object -> string);
 			praat_select (IOBJECT);
+		} else if (object -> which == Stackel_NUMERIC_VECTOR) {
+			numvec vec = object -> numericVector;
+			for (int ielm = 1; ielm <= vec.size; ielm ++) {
+				int IOBJECT = praat_findObjectById (lround (vec [ielm]));
+				praat_select (IOBJECT);
+			}
 		} else {
-			Melder_throw (U"The function \"plusObject\" takes numbers and strings, not ", Stackel_whichText (object), U".");
+			Melder_throw (U"The function \"plusObject\" takes numbers, strings, or numeric vectors, not ", Stackel_whichText (object), U".");
 		}
 	}
 	praat_show ();
@@ -4406,8 +4804,14 @@ static void do_minusObject () {
 		} else if (object -> which == Stackel_STRING) {
 			int IOBJECT = praat_findObjectFromString (object -> string);
 			praat_deselect (IOBJECT);
+		} else if (object -> which == Stackel_NUMERIC_VECTOR) {
+			numvec vec = object -> numericVector;
+			for (int ielm = 1; ielm <= vec.size; ielm ++) {
+				int IOBJECT = praat_findObjectById (lround (vec [ielm]));
+				praat_deselect (IOBJECT);
+			}
 		} else {
-			Melder_throw (U"The function \"minusObject\" takes numbers and strings, not ", Stackel_whichText (object), U".");
+			Melder_throw (U"The function \"minusObject\" takes numbers, strings, or numeric vectors, not ", Stackel_whichText (object), U".");
 		}
 	}
 	praat_show ();
@@ -4423,8 +4827,14 @@ static void do_removeObject () {
 		} else if (object -> which == Stackel_STRING) {
 			int IOBJECT = praat_findObjectFromString (object -> string);
 			praat_removeObject (IOBJECT);
+		} else if (object -> which == Stackel_NUMERIC_VECTOR) {
+			numvec vec = object -> numericVector;
+			for (int ielm = 1; ielm <= vec.size; ielm ++) {
+				int IOBJECT = praat_findObjectById (lround (vec [ielm]));
+				praat_removeObject (IOBJECT);
+			}
 		} else {
-			Melder_throw (U"The function \"removeObject\" takes numbers and strings, not ", Stackel_whichText (object), U".");
+			Melder_throw (U"The function \"removeObject\" takes numbers, strings, or numeric vectors, not ", Stackel_whichText (object), U".");
 		}
 	}
 	praat_show ();
@@ -4781,37 +5191,21 @@ static void do_mulNumvec () {
 		/*
 			result# = mul# (x#, y##)
 		*/
-		long xn = x->numericVector.size, ynrow = y->numericMatrix.nrow, yncol = y->numericMatrix.ncol;
-		if (ynrow != xn)
+		integer xSize = x->numericVector.size, yNrow = y->numericMatrix.nrow;
+		if (yNrow != xSize)
 			Melder_throw (U"In the function \"mul#\", the dimension of the vector and the number of rows of the matrix should be equal, "
-				"not ", xn, U" and ", ynrow);
-		autonumvec result { yncol, false };
-		for (long j = 1; j <= yncol; j ++) {
-			result [j] = 0.0;
-			for (long i = 1; i <= ynrow; i ++) {
-				double xvalue = x->numericVector [i];
-				double yvalue = y->numericMatrix [i] [j];
-				result [j] += xvalue * yvalue;
-			}
-		}
+				"not ", xSize, U" and ", yNrow);
+		autonumvec result = mul_numvec (x->numericVector, y->numericMatrix);
 		pushNumericVector (result.move());
 	} else if (x->which == Stackel_NUMERIC_MATRIX && y->which == Stackel_NUMERIC_VECTOR) {
 		/*
 			result# = mul# (x##, y#)
 		*/
-		long xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol, yn = y->numericVector.size;
-		if (yn != xncol)
+		long xNcol = x->numericMatrix.ncol, ySize = y->numericVector.size;
+		if (ySize != xNcol)
 			Melder_throw (U"In the function \"mul#\", the number of columns of the matrix and the dimension of the vector should be equal, "
-				"not ", xncol, U" and ", yn);
-		autonumvec result { xnrow, false };
-		for (long i = 1; i <= xnrow; i ++) {
-			result [i] = 0.0;
-			for (long j = 1; j <= xncol; j ++) {
-				double xvalue = x->numericMatrix [i] [j];
-				double yvalue = y->numericVector [j];
-				result [i] += xvalue * yvalue;
-			}
-		}
+				"not ", xNcol, U" and ", ySize);
+		autonumvec result = mul_numvec (x->numericMatrix, y->numericVector);
 		pushNumericVector (result.move());
 	} else {
 		Melder_throw (U"The function \"mul#\" requires a vector and a matrix, not ", Stackel_whichText (x), U" and ", Stackel_whichText (y), U".");
@@ -5986,6 +6380,10 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case MEAN_: { do_mean ();
 } break; case STDEV_: { do_stdev ();
 } break; case CENTER_: { do_center ();
+} break; case EVALUATE_: { do_evaluate ();
+} break; case EVALUATE_NOCHECK_: { do_evaluate_nocheck ();
+} break; case EVALUATE_STR_: { do_evaluateStr ();
+} break; case EVALUATE_NOCHECK_STR_: { do_evaluate_nocheckStr ();
 /********** Functions of 2 numerical variables: **********/
 } break; case ARCTAN2_: { do_function_dd_d (atan2);
 } break; case RANDOM_UNIFORM_: { do_function_dd_d (NUMrandomUniform);
@@ -6066,8 +6464,8 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case ENVIRONMENTSTR_: { do_environmentStr ();
 } break; case INDEX_: { do_index ();
 } break; case RINDEX_: { do_rindex ();
-} break; case STARTS_WITH_: { do_stringMatchesCriterion (kMelder_string_STARTS_WITH);
-} break; case ENDS_WITH_: { do_stringMatchesCriterion (kMelder_string_ENDS_WITH);
+} break; case STARTS_WITH_: { do_stringMatchesCriterion (kMelder_string::STARTS_WITH);
+} break; case ENDS_WITH_: { do_stringMatchesCriterion (kMelder_string::ENDS_WITH);
 } break; case REPLACESTR_: { do_replaceStr ();
 } break; case INDEX_REGEX_: { do_index_regex (false);
 } break; case RINDEX_REGEX_: { do_index_regex (true);
@@ -6245,12 +6643,10 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 	pushNumber (var -> numericValue);
 } break; case NUMERIC_VECTOR_VARIABLE_: {
 	InterpreterVariable var = f [programPointer]. content.variable;
-	autonumvec vec = copy_numvec (var -> numericVectorValue);
-	pushNumericVector (vec.move());
+	pushNumericVectorReference (var -> numericVectorValue);
 } break; case NUMERIC_MATRIX_VARIABLE_: {
 	InterpreterVariable var = f [programPointer]. content.variable;
-	autonummat mat = copy_nummat (var -> numericMatrixValue);
-	pushNumericMatrix (mat.move());
+	pushNumericMatrixReference (var -> numericMatrixValue);
 } break; case STRING_VARIABLE_: {
 	InterpreterVariable var = f [programPointer]. content.variable;
 	autostring32 string = Melder_dup (var -> stringValue);
@@ -6265,46 +6661,50 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 			if (theStack [1]. which == Stackel_NUMERIC_VECTOR) Melder_throw (U"Found a vector expression instead of a numeric expression.");
 			if (theStack [1]. which == Stackel_NUMERIC_MATRIX) Melder_throw (U"Found a matrix expression instead of a numeric expression.");
 			result -> expressionType = kFormula_EXPRESSION_TYPE_NUMERIC;
-			result -> result.numericResult = theStack [1]. number;
+			result -> numericResult = theStack [1]. number;
 		} else if (theExpressionType [theLevel] == kFormula_EXPRESSION_TYPE_STRING) {
 			if (theStack [1]. which == Stackel_NUMBER)
 				Melder_throw (U"Found a numeric expression (value ", theStack [1]. number, U") instead of a string expression.");
 			if (theStack [1]. which == Stackel_NUMERIC_VECTOR) Melder_throw (U"Found a vector expression instead of a string expression.");
 			if (theStack [1]. which == Stackel_NUMERIC_MATRIX) Melder_throw (U"Found a matrix expression instead of a string expression.");
 			result -> expressionType = kFormula_EXPRESSION_TYPE_STRING;
-			result -> result.stringResult = theStack [1]. string;   // dangle...
+			result -> stringResult = theStack [1]. string;   // dangle...
 			theStack [1]. string = nullptr;   // ...undangle (and disown)
 		} else if (theExpressionType [theLevel] == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
 			if (theStack [1]. which == Stackel_NUMBER) Melder_throw (U"Found a numeric expression instead of a vector expression.");
 			if (theStack [1]. which == Stackel_STRING) Melder_throw (U"Found a string expression instead of a vector expression.");
 			if (theStack [1]. which == Stackel_NUMERIC_MATRIX) Melder_throw (U"Found a matrix expression instead of a vector expression.");
 			result -> expressionType = kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR;
-			result -> result.numericVectorResult = theStack [1]. numericVector;   // dangle
-			theStack [1]. numericVector = empty_numvec;   // ...undangle (and disown)
+			result -> numericVectorResult = theStack [1]. numericVector;
+			result -> owned = theStack [1]. owned;
+			theStack [1]. owned = false;   // optionally undangle
 		} else if (theExpressionType [theLevel] == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
 			if (theStack [1]. which == Stackel_NUMBER) Melder_throw (U"Found a numeric expression instead of a matrix expression.");
 			if (theStack [1]. which == Stackel_STRING) Melder_throw (U"Found a string expression instead of a matrix expression.");
 			if (theStack [1]. which == Stackel_NUMERIC_VECTOR) Melder_throw (U"Found a vector expression instead of a matrix expression.");
 			result -> expressionType = kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX;
-			result -> result.numericMatrixResult = theStack [1]. numericMatrix;   // dangle
-			theStack [1]. numericMatrix = empty_nummat;   // ...undangle (and disown)
+			result -> numericMatrixResult = theStack [1]. numericMatrix;
+			result -> owned = theStack [1]. owned;
+			theStack [1]. owned = false;   // optionally undangle
 		} else {
 			Melder_assert (theExpressionType [theLevel] == kFormula_EXPRESSION_TYPE_UNKNOWN);
 			if (theStack [1]. which == Stackel_NUMBER) {
 				result -> expressionType = kFormula_EXPRESSION_TYPE_NUMERIC;
-				result -> result.numericResult = theStack [1]. number;
+				result -> numericResult = theStack [1]. number;
 			} else if (theStack [1]. which == Stackel_STRING) {
 				result -> expressionType = kFormula_EXPRESSION_TYPE_STRING;
-				result -> result.stringResult = theStack [1]. string;   // dangle...
+				result -> stringResult = theStack [1]. string;   // dangle...
 				theStack [1]. string = nullptr;   // ...undangle (and disown)
 			} else if (theStack [1]. which == Stackel_NUMERIC_VECTOR) {
 				result -> expressionType = kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR;
-				result -> result.numericVectorResult = theStack [1]. numericVector;   // dangle...
-				theStack [1]. numericVector = empty_numvec;   // ...undangle (and disown)
+				result -> numericVectorResult = theStack [1]. numericVector;
+				result -> owned = theStack [1]. owned;
+				theStack [1]. owned = false;   // optionally undangle
 			} else if (theStack [1]. which == Stackel_NUMERIC_MATRIX) {
 				result -> expressionType = kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX;
-				result -> result.numericMatrixResult = theStack [1]. numericMatrix;   // dangle...
-				theStack [1]. numericMatrix = empty_nummat;   // ...undangle (and disown)
+				result -> numericMatrixResult = theStack [1]. numericMatrix;
+				result -> owned = theStack [1]. owned;
+				theStack [1]. owned = false;   // optionally undangle
 			} else {
 				Melder_throw (U"Don't know yet how to write ", Stackel_whichText (& theStack [1]), U".");
 			}
diff --git a/sys/Formula.h b/sys/Formula.h
index b61dd36..25d1829 100644
--- a/sys/Formula.h
+++ b/sys/Formula.h
@@ -42,7 +42,7 @@ typedef struct structStackel {
 	#define Stackel_STRING_ARRAY  6
 	#define Stackel_VARIABLE  -1
 	#define Stackel_OBJECT  -2
-	int which;   /* 0 or negative = no clean-up required, positive = requires clean-up */
+	int which;   // 0 or negative = no clean-up required, positive = requires clean-up
 	union {
 		double number;
 		char32 *string;
@@ -51,6 +51,7 @@ typedef struct structStackel {
 		nummat numericMatrix;
 		InterpreterVariable variable;
 	};
+	bool owned;
 } *Stackel;
 const char32 *Stackel_whichText (Stackel me);
 
@@ -61,7 +62,8 @@ struct Formula_Result {
 		char32 *stringResult;
 		numvec numericVectorResult;
 		nummat numericMatrixResult;
-	} result;
+	};
+	bool owned;
 };
 
 Thing_declare (Interpreter);
diff --git a/sys/Graphics.cpp b/sys/Graphics.cpp
index d70479a..dc9273f 100644
--- a/sys/Graphics.cpp
+++ b/sys/Graphics.cpp
@@ -34,7 +34,7 @@ Thing_implement (Graphics, Thing, 0);
 enum kGraphics_cjkFontStyle theGraphicsCjkFontStyle;
 
 void Graphics_prefs () {
-	Preferences_addEnum (U"Graphics.cjkFontStyle", & theGraphicsCjkFontStyle, kGraphics_cjkFontStyle, kGraphics_cjkFontStyle_DEFAULT);
+	Preferences_addEnum (U"Graphics.cjkFontStyle", & theGraphicsCjkFontStyle, kGraphics_cjkFontStyle, (int) kGraphics_cjkFontStyle::DEFAULT);
 }
 
 void structGraphics :: v_destroy () noexcept {
@@ -70,25 +70,25 @@ static void computeTrafo (Graphics me) {
 void Graphics_init (Graphics me, int resolution) {
 	my resolution = resolution;
 	if (resolution == 96) {
-		my resolutionNumber = kGraphics_resolution_96;
+		my resolutionNumber = kGraphics_resolution::DPI_96;
 	} else if (resolution == 100) {
-		my resolutionNumber = kGraphics_resolution_100;
+		my resolutionNumber = kGraphics_resolution::DPI_100;
 	} else if (resolution == 180) {
-		my resolutionNumber = kGraphics_resolution_180;
+		my resolutionNumber = kGraphics_resolution::DPI_180;
 	} else if (resolution == 200) {
-		my resolutionNumber = kGraphics_resolution_200;
+		my resolutionNumber = kGraphics_resolution::DPI_200;
 	} else if (resolution == 300) {
-		my resolutionNumber = kGraphics_resolution_300;
+		my resolutionNumber = kGraphics_resolution::DPI_300;
 	} else if (resolution == 360) {
-		my resolutionNumber = kGraphics_resolution_360;
+		my resolutionNumber = kGraphics_resolution::DPI_360;
 	} else if (resolution == 600) {
-		my resolutionNumber = kGraphics_resolution_600;
+		my resolutionNumber = kGraphics_resolution::DPI_600;
 	} else if (resolution == 720) {
-		my resolutionNumber = kGraphics_resolution_720;
+		my resolutionNumber = kGraphics_resolution::DPI_720;
 	} else if (resolution == 900) {
-		my resolutionNumber = kGraphics_resolution_900;
+		my resolutionNumber = kGraphics_resolution::DPI_900;
 	} else if (resolution == 1200) {
-		my resolutionNumber = kGraphics_resolution_1200;
+		my resolutionNumber = kGraphics_resolution::DPI_1200;
 	} else {
 		Melder_fatal (U"Unsupported resolution ", resolution, U" dpi.");
 	}
@@ -102,7 +102,7 @@ void Graphics_init (Graphics me, int resolution) {
 	my lineWidth = 1.0;
 	my arrowSize = 1.0;
 	my speckleSize = 1.0;
-	my font = kGraphics_font_HELVETICA;
+	my font = kGraphics_font::HELVETICA;
 	my fontSize = 10;
 	my fontStyle = Graphics_NORMAL;
 	my record = nullptr;
diff --git a/sys/Graphics.h b/sys/Graphics.h
index eec6fb4..238359a 100644
--- a/sys/Graphics.h
+++ b/sys/Graphics.h
@@ -100,10 +100,10 @@ Thing_define (Graphics, Thing) {
 	int lineType;
 	Graphics_Colour colour;
 	double lineWidth, arrowSize, speckleSize;
-	enum kGraphics_colourScale colourScale;
+	kGraphics_colourScale colourScale;
 	int horizontalTextAlignment, verticalTextAlignment;
 	double textRotation, wrapWidth, secondIndent, textX, textY;
-	enum kGraphics_font font;
+	kGraphics_font font;
 	int fontSize, fontStyle;
 	int percentSignIsItalic, numberSignIsBold, circumflexIsSuperscript, underscoreIsSubscript;
 	int dollarSignIsCode, atSignIsLink;
@@ -141,8 +141,8 @@ Thing_define (Graphics, Thing) {
 };
 
 autoGraphics Graphics_create (int resolution);
-autoGraphics Graphics_create_postscriptjob (MelderFile file, int resolution, enum kGraphicsPostscript_spots spots,
-	enum kGraphicsPostscript_paperSize paperSize, enum kGraphicsPostscript_orientation rotation, double magnification);
+autoGraphics Graphics_create_postscriptjob (MelderFile file, int resolution, kGraphicsPostscript_spots spots,
+	kGraphicsPostscript_paperSize paperSize, kGraphicsPostscript_orientation rotation, double magnification);
 autoGraphics Graphics_create_epsfile (MelderFile file, int resolution, enum kGraphicsPostscript_spots spots,
 	double xmin, double xmax, double ymin, double ymax, bool includeFonts, bool useSilipaPS);
 autoGraphics Graphics_create_pdffile (MelderFile file, int resolution,
@@ -255,14 +255,14 @@ void Graphics_unhighlight2 (Graphics me, double x1, double x2, double y1, double
 	double innerX1, double innerX2, double innerY1, double innerY2);
 
 #define Graphics_NOCHANGE  -1
-#define Graphics_LEFT  kGraphics_horizontalAlignment_LEFT
-#define Graphics_CENTRE  kGraphics_horizontalAlignment_CENTRE
-#define Graphics_RIGHT  kGraphics_horizontalAlignment_RIGHT
+#define Graphics_LEFT  kGraphics_horizontalAlignment::LEFT
+#define Graphics_CENTRE  kGraphics_horizontalAlignment::CENTRE
+#define Graphics_RIGHT  kGraphics_horizontalAlignment::RIGHT
 #define Graphics_BOTTOM  0
 #define Graphics_HALF  1
 #define Graphics_TOP  2
 #define Graphics_BASELINE  3
-void Graphics_setTextAlignment (Graphics me, int horizontal, int vertical);
+void Graphics_setTextAlignment (Graphics me, enum kGraphics_horizontalAlignment horizontal, int vertical);
 
 void Graphics_setFont (Graphics me, enum kGraphics_font font);
 void Graphics_setFontSize (Graphics me, int height);
diff --git a/sys/GraphicsP.h b/sys/GraphicsP.h
index 3e5fb10..8750175 100644
--- a/sys/GraphicsP.h
+++ b/sys/GraphicsP.h
@@ -34,12 +34,12 @@ void Graphics_init (Graphics me, int resolution);
 		my fontStyle == Graphics_NORMAL;
 */ 
 
-#define kGraphics_font_SYMBOL  (kGraphics_font_MAX + 1)
-#define kGraphics_font_IPATIMES  (kGraphics_font_MAX + 2)
-#define kGraphics_font_IPAPALATINO  (kGraphics_font_MAX + 3)
-#define kGraphics_font_DINGBATS  (kGraphics_font_MAX + 4)
-#define kGraphics_font_CHINESE  (kGraphics_font_MAX + 5)
-#define kGraphics_font_JAPANESE  (kGraphics_font_MAX + 6)
+#define kGraphics_font_SYMBOL  ((int) kGraphics_font::MAX + 1)
+#define kGraphics_font_IPATIMES  ((int) kGraphics_font::MAX + 2)
+#define kGraphics_font_IPAPALATINO  ((int) kGraphics_font::MAX + 3)
+#define kGraphics_font_DINGBATS  ((int) kGraphics_font::MAX + 4)
+#define kGraphics_font_CHINESE  ((int) kGraphics_font::MAX + 5)
+#define kGraphics_font_JAPANESE  ((int) kGraphics_font::MAX + 6)
 
 /*
 	Honour the NO_GRAPHICS compiler switch.
diff --git a/sys/GraphicsPostscript.cpp b/sys/GraphicsPostscript.cpp
index c54c773..ea3fad0 100644
--- a/sys/GraphicsPostscript.cpp
+++ b/sys/GraphicsPostscript.cpp
@@ -1,6 +1,6 @@
 /* GraphicsPostscript.cpp
  *
- * Copyright (C) 1992-2011,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2011,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -127,8 +127,8 @@ void structGraphicsPostscript :: v_destroy () noexcept {
 	GraphicsPostscript_Parent :: v_destroy ();
 }
 
-autoGraphics Graphics_create_postscriptjob (MelderFile file, int resolution, enum kGraphicsPostscript_spots spots,
-	enum kGraphicsPostscript_paperSize paperSize, enum kGraphicsPostscript_orientation rotation, double magnification)
+autoGraphics Graphics_create_postscriptjob (MelderFile file, int resolution, kGraphicsPostscript_spots spots,
+	kGraphicsPostscript_paperSize paperSize, kGraphicsPostscript_orientation rotation, double magnification)
 {
 	autoGraphicsPostscript me = Thing_new (GraphicsPostscript);
 	time_t today;
@@ -136,13 +136,13 @@ autoGraphics Graphics_create_postscriptjob (MelderFile file, int resolution, enu
 	my job = true, my eps = false, my printer = false;
 	my d_printf = (int (*)(void *, const char*, ...)) fprintf;
 	Graphics_init (me.get(), resolution);   // virtual resolution; may differ from that of the printer; OK if always 600 dpi
-	my photocopyable = spots == kGraphicsPostscript_spots_PHOTOCOPYABLE;
+	my photocopyable = spots == kGraphicsPostscript_spots::PHOTOCOPYABLE;
 	if (my photocopyable) { my spotsDensity = 85; my spotsAngle = 35; }
 	else { my spotsDensity = 106; my spotsAngle = 46; }
- 	if (paperSize == kGraphicsPostscript_paperSize_A3) my paperWidth = 842 / 72.0, my paperHeight = 1191 / 72.0;
-	else if (paperSize == kGraphicsPostscript_paperSize_US_LETTER) my paperWidth = 612 / 72.0, my paperHeight = 792 / 72.0;
+ 	if (paperSize == kGraphicsPostscript_paperSize::A3) my paperWidth = 842 / 72.0, my paperHeight = 1191 / 72.0;
+	else if (paperSize == kGraphicsPostscript_paperSize::US_LETTER) my paperWidth = 612 / 72.0, my paperHeight = 792 / 72.0;
 	else my paperWidth = 595 / 72.0, my paperHeight = 842 / 72.0;
-	my landscape = rotation == kGraphicsPostscript_orientation_LANDSCAPE;
+	my landscape = rotation == kGraphicsPostscript_orientation::LANDSCAPE;
 	my magnification = magnification;
 	my includeFonts = true;
 	my d_file = Melder_fopen (file, "w");
@@ -204,7 +204,7 @@ autoGraphics Graphics_create_epsfile (MelderFile file, int resolution, enum kGra
 		my d_printf = (int (*)(void *, const char*, ...)) fprintf;
 	#endif
 	Graphics_init (me.get(), resolution);   // virtual resolution; may differ from that of the printer; OK if always 600 dpi
-	my photocopyable = spots == kGraphicsPostscript_spots_PHOTOCOPYABLE;
+	my photocopyable = spots == kGraphicsPostscript_spots::PHOTOCOPYABLE;
 	if (my photocopyable) { my spotsDensity = 85; my spotsAngle = 35; }
 	else { my spotsDensity = 106; my spotsAngle = 46; }
 	my paperWidth = 7.5, my paperHeight = 11.0;
@@ -249,12 +249,12 @@ autoGraphics Graphics_create_postscriptprinter () {
 	my job = false, my eps = false, my printer = true;
 	my d_printf = Printer_postScript_printf;
 	Graphics_init (me.get(), thePrinter. resolution);   // virtual resolution
-	my photocopyable = thePrinter. spots == kGraphicsPostscript_spots_PHOTOCOPYABLE;
+	my photocopyable = thePrinter. spots == kGraphicsPostscript_spots::PHOTOCOPYABLE;
 	if (my photocopyable) { my spotsDensity = 85; my spotsAngle = 35; }
 	else { my spotsDensity = 106; my spotsAngle = 46; }
 	my paperWidth = (double) thePrinter. paperWidth / my resolution;
 	my paperHeight = (double) thePrinter. paperHeight / my resolution;
-	my landscape = thePrinter. orientation == kGraphicsPostscript_orientation_LANDSCAPE;
+	my landscape = thePrinter. orientation == kGraphicsPostscript_orientation::LANDSCAPE;
 	my magnification = thePrinter. magnification;
 	my includeFonts = true;
 	my d_x1DC = my d_x1DCmin = my resolution / 2;
diff --git a/sys/Graphics_enums.h b/sys/Graphics_enums.h
index 8dba7b6..d1485e6 100644
--- a/sys/Graphics_enums.h
+++ b/sys/Graphics_enums.h
@@ -1,6 +1,6 @@
 /* Graphics_enums.h
  *
- * Copyright (C) 1992-2007,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2007,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -31,17 +31,17 @@ enums_begin (kGraphics_horizontalAlignment, 0)
 enums_end (kGraphics_horizontalAlignment, 2, CENTRE)
 
 enums_begin (kGraphics_resolution, 0)
-	enums_add (kGraphics_resolution, 0,  96, U"96 dpi")
-	enums_add (kGraphics_resolution, 1, 100, U"100 dpi")
-	enums_add (kGraphics_resolution, 2, 180, U"180 dpi")
-	enums_add (kGraphics_resolution, 3, 200, U"200 dpi")
-	enums_add (kGraphics_resolution, 4, 300, U"300 dpi")
-	enums_add (kGraphics_resolution, 5, 360, U"360 dpi")
-	enums_add (kGraphics_resolution, 6, 600, U"600 dpi")
-	enums_add (kGraphics_resolution, 7, 720, U"720 dpi")
-	enums_add (kGraphics_resolution, 8, 900, U"900 dpi")
-	enums_add (kGraphics_resolution, 9, 1200, U"1200 dpi")
-enums_end (kGraphics_resolution, 9, 100)
+	enums_add (kGraphics_resolution, 0, DPI_96, U"96 dpi")
+	enums_add (kGraphics_resolution, 1, DPI_100, U"100 dpi")
+	enums_add (kGraphics_resolution, 2, DPI_180, U"180 dpi")
+	enums_add (kGraphics_resolution, 3, DPI_200, U"200 dpi")
+	enums_add (kGraphics_resolution, 4, DPI_300, U"300 dpi")
+	enums_add (kGraphics_resolution, 5, DPI_360, U"360 dpi")
+	enums_add (kGraphics_resolution, 6, DPI_600, U"600 dpi")
+	enums_add (kGraphics_resolution, 7, DPI_720, U"720 dpi")
+	enums_add (kGraphics_resolution, 8, DPI_900, U"900 dpi")
+	enums_add (kGraphics_resolution, 9, DPI_1200, U"1200 dpi")
+enums_end (kGraphics_resolution, 9, DPI_100)
 
 enums_begin (kGraphics_colourScale, 0)
 	enums_add (kGraphics_colourScale, 0, GREY, U"grey")
diff --git a/sys/Graphics_image.cpp b/sys/Graphics_image.cpp
index abf26de..94c7269 100644
--- a/sys/Graphics_image.cpp
+++ b/sys/Graphics_image.cpp
@@ -221,13 +221,13 @@ static void _GraphicsScreen_cellArrayOrImage (GraphicsScreen me, double **z_floa
 		#elif quartz
 			#define ROW_START_ADDRESS  (imageData + (clipy1 - 1 - yDC) * bytesPerRow)
 			#define PUT_PIXEL \
-				if (my colourScale == kGraphics_colourScale_GREY) { \
+				if (my colourScale == kGraphics_colourScale::GREY) { \
 					unsigned char kar = value <= 0 ? 0 : value >= 255 ? 255 : (int) value; \
 					*pixelAddress ++ = kar; \
 					*pixelAddress ++ = kar; \
 					*pixelAddress ++ = kar; \
 					*pixelAddress ++ = 0; \
-				} else if (my colourScale == kGraphics_colourScale_BLUE_TO_RED) { \
+				} else if (my colourScale == kGraphics_colourScale::BLUE_TO_RED) { \
 					if (value < 0.0) { \
 						*pixelAddress ++ = 0; \
 						*pixelAddress ++ = 0; \
diff --git a/sys/Graphics_record.cpp b/sys/Graphics_record.cpp
index 04b52aa..14fc4bb 100644
--- a/sys/Graphics_record.cpp
+++ b/sys/Graphics_record.cpp
@@ -180,7 +180,7 @@ void Graphics_play (Graphics me, Graphics thee) {
 			case SET_FONT_STYLE: Graphics_setFontStyle (thee, (int) get); break;
 			case SET_TEXT_ALIGNMENT:
 			{  int hor = get, vert = get;
-				Graphics_setTextAlignment (thee, hor, vert);
+				Graphics_setTextAlignment (thee, (kGraphics_horizontalAlignment) hor, vert);
 			}  break;
 			case SET_TEXT_ROTATION: Graphics_setTextRotation (thee, get); break;
 			case SET_LINE_TYPE: Graphics_setLineType (thee, (int) get); break;
diff --git a/sys/Graphics_text.cpp b/sys/Graphics_text.cpp
index d3d759c..0d33f44 100644
--- a/sys/Graphics_text.cpp
+++ b/sys/Graphics_text.cpp
@@ -41,7 +41,7 @@ extern const char * ipaSerifRegularPS [];
 	static bool hasTimes, hasHelvetica, hasCourier, hasSymbol, hasPalatino, hasDoulos, hasCharis, hasIpaSerif;
 #elif gdi
 	#define win_MAXIMUM_FONT_SIZE  500
-	static HFONT fonts [1 + kGraphics_resolution_MAX] [1 + kGraphics_font_JAPANESE] [1+win_MAXIMUM_FONT_SIZE] [1 + Graphics_BOLD_ITALIC];
+	static HFONT fonts [1 + (int) kGraphics_resolution::MAX] [1 + kGraphics_font_JAPANESE] [1+win_MAXIMUM_FONT_SIZE] [1 + Graphics_BOLD_ITALIC];
 	static int win_size2isize (int size) { return size > win_MAXIMUM_FONT_SIZE ? win_MAXIMUM_FONT_SIZE : size; }
 	static int win_isize2size (int isize) { return isize; }
 #elif quartz
@@ -89,8 +89,8 @@ extern const char * ipaSerifRegularPS [];
 			ANSI_CHARSET;
 		spec. lfOutPrecision = spec. lfClipPrecision = spec. lfQuality = 0;
 		spec. lfPitchAndFamily =
-			( font == kGraphics_font_COURIER ? FIXED_PITCH : font == kGraphics_font_IPATIMES ? DEFAULT_PITCH : VARIABLE_PITCH ) |
-			( font == kGraphics_font_HELVETICA ? FF_SWISS : font == kGraphics_font_COURIER ? FF_MODERN :
+			( font == (int) kGraphics_font::COURIER ? FIXED_PITCH : font == kGraphics_font_IPATIMES ? DEFAULT_PITCH : VARIABLE_PITCH ) |
+			( font == (int) kGraphics_font::HELVETICA ? FF_SWISS : font == (int) kGraphics_font::COURIER ? FF_MODERN :
 			  font == kGraphics_font_CHINESE ? FF_DONTCARE :
 			  font == kGraphics_font_JAPANESE ? FF_DONTCARE :
 			  font >= kGraphics_font_IPATIMES ? FF_DONTCARE : FF_ROMAN );
@@ -109,10 +109,10 @@ extern const char * ipaSerifRegularPS [];
 			}
 		}
 		wcscpy (spec. lfFaceName,
-			font == kGraphics_font_HELVETICA ? L"Arial" :
-			font == kGraphics_font_TIMES     ? L"Times New Roman" :
-			font == kGraphics_font_COURIER   ? L"Courier New" :
-			font == kGraphics_font_PALATINO  ? L"Book Antiqua" :
+			font == (int) kGraphics_font::HELVETICA ? L"Arial" :
+			font == (int) kGraphics_font::TIMES     ? L"Times New Roman" :
+			font == (int) kGraphics_font::COURIER   ? L"Courier New" :
+			font == (int) kGraphics_font::PALATINO  ? L"Book Antiqua" :
 			font == kGraphics_font_SYMBOL    ? L"Symbol" :
 			font == kGraphics_font_IPATIMES  ? ( doulosAvailable && style == 0 ? L"Doulos SIL" : charisAvailable ? L"Charis SIL" : L"Times New Roman" ) :
 			font == kGraphics_font_DINGBATS  ? L"Wingdings" :
@@ -129,10 +129,10 @@ extern const char * ipaSerifRegularPS [];
 		Melder_assert (font >= 0 && font <= kGraphics_font_DINGBATS);
 		if (! fontDescriptions [font]) {
 			const char *fontFace =
-				font == kGraphics_font_HELVETICA ? "Helvetica" :
-				font == kGraphics_font_TIMES ? "Times" :
-				font == kGraphics_font_COURIER ? "Courier" : 
-				font == kGraphics_font_PALATINO ? "Palatino" : 
+				font == (int) kGraphics_font::HELVETICA ? "Helvetica" :
+				font == (int) kGraphics_font::TIMES ? "Times" :
+				font == (int) kGraphics_font::COURIER ? "Courier" :
+				font == (int) kGraphics_font::PALATINO ? "Palatino" :
 				font == kGraphics_font_IPATIMES ? "Doulos SIL" :
 				font == kGraphics_font_IPAPALATINO ? "Charis SIL" :
 				font == kGraphics_font_DINGBATS ? "Dingbats" : "Serif";
@@ -198,17 +198,17 @@ inline static int chooseFont (Graphics me, _Graphics_widechar *lc) {
 	Longchar_Info info = lc -> karInfo;
 	int alphabet = info -> alphabet;
 
-	if (font == kGraphics_font_COURIER) {
+	if (font == (int) kGraphics_font::COURIER) {
 		constexpr bool systemSubstitutesMonospacedSerifFontForIpaCourier = (quartz);
 		if (systemSubstitutesMonospacedSerifFontForIpaCourier) {
 			/*
 				No need to check whether the character is phonetic or not.
 			*/
-			return kGraphics_font_COURIER;
+			return (int) kGraphics_font::COURIER;
 		}
 		if (alphabet == Longchar_SYMBOL ||
 			alphabet == Longchar_PHONETIC ||
-			lc [1]. karInfo -> isDiacritic)   // inspect next character to ensure diacritic continuity
+			lc [1]. kar > U'\t' && lc [1]. karInfo -> isDiacritic)   // inspect next character to ensure diacritic continuity
 		{
 			/*
 				Serif is more important than monospaced,
@@ -217,58 +217,58 @@ inline static int chooseFont (Graphics me, _Graphics_widechar *lc) {
 			if (hasCharis) return kGraphics_font_IPAPALATINO;
 			if (hasDoulos) return kGraphics_font_IPATIMES;
 		}
-		return kGraphics_font_COURIER;
+		return (int) kGraphics_font::COURIER;
 	}
 	font =
 		alphabet == Longchar_SYMBOL || // ? kGraphics_font_SYMBOL :
 		alphabet == Longchar_PHONETIC ?
-			( my font == kGraphics_font_TIMES ?
+			( my font == kGraphics_font::TIMES ?
 				( hasDoulos ?
 					( lc -> style == 0 ?
 						kGraphics_font_IPATIMES :
 					  hasCharis ?
 						kGraphics_font_IPAPALATINO :   // other styles in Charis, because Doulos has no bold or italic
-						kGraphics_font_TIMES
+						(int) kGraphics_font::TIMES
 					) :
 				  hasCharis ?
 					kGraphics_font_IPAPALATINO :
-					kGraphics_font_TIMES   // on newer systems, Times and Times New Roman have a lot of phonetic characters
+					(int) kGraphics_font::TIMES   // on newer systems, Times and Times New Roman have a lot of phonetic characters
 				) :
-			  my font == kGraphics_font_HELVETICA ?
-				kGraphics_font_HELVETICA :   // sans serif, so fall back on Lucida Grande or so for phonetic characters
+			  my font == kGraphics_font::HELVETICA ?
+				(int) kGraphics_font::HELVETICA :   // sans serif, so fall back on Lucida Grande or so for phonetic characters
 			  /* my font must be kGraphics_font_PALATINO */
 			  hasCharis && Melder_debug != 900 ?
 				kGraphics_font_IPAPALATINO :
 			  hasDoulos && Melder_debug != 900 ?
 				( lc -> style == 0 ?
 					kGraphics_font_IPATIMES :
-					kGraphics_font_TIMES
+					(int) kGraphics_font::TIMES
 				) :
-				kGraphics_font_PALATINO
+				(int) kGraphics_font::PALATINO
 			) :
 		alphabet == Longchar_DINGBATS ?
 			kGraphics_font_DINGBATS :
-		my font == kGraphics_font_TIMES ?
+		my font == kGraphics_font::TIMES ?
 			( hasDoulos ?
 				( lc -> style == 0 ?
 					kGraphics_font_IPATIMES :
 				  lc -> style == Graphics_ITALIC ?
-					( lc [1]. karInfo -> isDiacritic && hasCharis ?
-						kGraphics_font_IPAPALATINO : kGraphics_font_TIMES ) :   // correct placement of diacritics
+					( lc [1]. kar > U'\t' && lc [1]. karInfo -> isDiacritic && hasCharis ?
+						kGraphics_font_IPAPALATINO : (int) kGraphics_font::TIMES ) :   // correct placement of diacritics
 				  hasCharis ?
 					kGraphics_font_IPAPALATINO :
-					kGraphics_font_TIMES 
+					(int) kGraphics_font::TIMES
 				) :
-				kGraphics_font_TIMES
+				(int) kGraphics_font::TIMES
 			) :
-		my font == kGraphics_font_HELVETICA ?
-			kGraphics_font_HELVETICA :
-		my font == kGraphics_font_PALATINO ?
+		my font == kGraphics_font::HELVETICA ?
+			(int) kGraphics_font::HELVETICA :
+		my font == kGraphics_font::PALATINO ?
 			( hasCharis && Melder_debug != 900 ?
 				kGraphics_font_IPAPALATINO :
-				kGraphics_font_PALATINO
+				(int) kGraphics_font::PALATINO
 			) :
-		my font;   // why not lc -> font.integer?
+		(int) my font;   // why not lc -> font.integer?
 	Melder_assert (font >= 0 && font <= kGraphics_font_DINGBATS);
 	return font;
 }
@@ -300,23 +300,23 @@ static void charSize (void *void_me, _Graphics_widechar *lc) {
 			       info -> alphabet == Longchar_PHONETIC ? kGraphics_font_IPATIMES :
 			       info -> alphabet == Longchar_DINGBATS ? kGraphics_font_DINGBATS : lc -> font.integer;
 			if ((unsigned int) lc -> kar >= 0x2E80 && (unsigned int) lc -> kar <= 0x9FFF)
-				font = ( theGraphicsCjkFontStyle == kGraphics_cjkFontStyle_CHINESE ? kGraphics_font_CHINESE : kGraphics_font_JAPANESE );
+				font = ( theGraphicsCjkFontStyle == kGraphics_cjkFontStyle::CHINESE ? kGraphics_font_CHINESE : kGraphics_font_JAPANESE );
 			size = lc -> size < 100 ? smallSize : normalSize;
 			style = lc -> style & (Graphics_ITALIC | Graphics_BOLD);   // take out Graphics_CODE
-			fontInfo = fonts [my resolutionNumber] [font] [size] [style];
+			fontInfo = fonts [(int) my resolutionNumber] [font] [size] [style];
 			if (! fontInfo) {
 				fontInfo = loadFont (me, font, size, style);
 				if (! fontInfo) return;
-				fonts [my resolutionNumber] [font] [size] [style] = fontInfo;
+				fonts [(int) my resolutionNumber] [font] [size] [style] = fontInfo;
 			}
 			SIZE extent;
 			lc -> code =
 				font == kGraphics_font_IPATIMES ||
-				font == kGraphics_font_TIMES ||
-				font == kGraphics_font_HELVETICA ||
+				font == (int) kGraphics_font::TIMES ||
+				font == (int) kGraphics_font::HELVETICA ||
 				font == kGraphics_font_CHINESE ||
 				font == kGraphics_font_JAPANESE ||
-				font == kGraphics_font_COURIER ? lc -> kar :
+				font == (int) kGraphics_font::COURIER ? lc -> kar :
 				info -> winEncoding;
 			if (lc -> code == 0) {
 				_Graphics_widechar *lc2;
@@ -366,7 +366,7 @@ static void charSize (void *void_me, _Graphics_widechar *lc) {
 			lc -> style == Graphics_BOLD_ITALIC ? Graphics_BOLD_ITALIC : 0;
 		if (! my fontInfos [font] [style]) {
 			const char *fontInfo, *secondaryFontInfo = nullptr, *tertiaryFontInfo = nullptr;
-			if (font == kGraphics_font_COURIER) {
+			if (font == (int) kGraphics_font::COURIER) {
 				fontInfo = style == Graphics_BOLD ? "Courier-Bold" :
 					style == Graphics_ITALIC ? "Courier-Oblique" :
 					style == Graphics_BOLD_ITALIC ? "Courier-BoldOblique" : "Courier";
@@ -376,7 +376,7 @@ static void charSize (void *void_me, _Graphics_widechar *lc) {
 				tertiaryFontInfo = style == Graphics_BOLD ? "CourierNew-Bold" :
 					style == Graphics_ITALIC ? "CourierNew-Italic" :
 					style == Graphics_BOLD_ITALIC ? "CourierNew-BoldItalic" : "CourierNew";
-			} else if (font == kGraphics_font_TIMES) {
+			} else if (font == (int) kGraphics_font::TIMES) {
 				fontInfo = style == Graphics_BOLD ? "Times-Bold" :
 					style == Graphics_ITALIC ? "Times-Italic" :
 					style == Graphics_BOLD_ITALIC ? "Times-BoldItalic" : "Times-Roman";
@@ -386,7 +386,7 @@ static void charSize (void *void_me, _Graphics_widechar *lc) {
 				tertiaryFontInfo = style == Graphics_BOLD ? "TimesNewRoman-Bold" :
 					style == Graphics_ITALIC ? "TimesNewRoman-Italic" :
 					style == Graphics_BOLD_ITALIC ? "TimesNewRoman-BoldItalic" : "TimesNewRoman";
-			} else if (font == kGraphics_font_PALATINO) {
+			} else if (font == (int) kGraphics_font::PALATINO) {
 				fontInfo = style == Graphics_BOLD ? "Palatino-Bold" :
 					style == Graphics_ITALIC ? "Palatino-Italic" :
 					style == Graphics_BOLD_ITALIC ? "Palatino-BoldItalic" : "Palatino-Roman";
@@ -426,11 +426,11 @@ static void charSize (void *void_me, _Graphics_widechar *lc) {
 				strcpy (my fontInfos [font] [style], fontInfo);
 			} else {
 				sprintf (my fontInfos [font] [style], "%s-Praat", fontInfo);
-				if (thePrinter. fontChoiceStrategy == kGraphicsPostscript_fontChoiceStrategy_LINOTYPE) {
+				if (thePrinter. fontChoiceStrategy == kGraphicsPostscript_fontChoiceStrategy::LINOTYPE) {
 					my d_printf (my d_file, "/%s /%s-Praat PraatEncode\n", fontInfo, fontInfo);
-				} else if (thePrinter. fontChoiceStrategy == kGraphicsPostscript_fontChoiceStrategy_MONOTYPE) {
+				} else if (thePrinter. fontChoiceStrategy == kGraphicsPostscript_fontChoiceStrategy::MONOTYPE) {
 					my d_printf (my d_file, "/%s /%s-Praat PraatEncode\n", tertiaryFontInfo, fontInfo);
-				} else if (thePrinter. fontChoiceStrategy == kGraphicsPostscript_fontChoiceStrategy_PS_MONOTYPE) {
+				} else if (thePrinter. fontChoiceStrategy == kGraphicsPostscript_fontChoiceStrategy::PS_MONOTYPE) {
 					my d_printf (my d_file, "/%s /%s-Praat PraatEncode\n", secondaryFontInfo, fontInfo);
 				} else {
 					/* Automatic font choice strategy. */
@@ -456,33 +456,33 @@ static void charSize (void *void_me, _Graphics_widechar *lc) {
 		lc -> size *= normalSize * 0.01;
 		lc -> baseline *= normalSize * 0.01;
 
-		if (font == kGraphics_font_COURIER) {
+		if (font == (int) kGraphics_font::COURIER) {
 			lc -> width = 600;   // Courier
 		} else if (style == 0) {
-			if (font == kGraphics_font_TIMES) lc -> width = info -> ps.times;
-			else if (font == kGraphics_font_HELVETICA) lc -> width = info -> ps.helvetica;
-			else if (font == kGraphics_font_PALATINO) lc -> width = info -> ps.palatino;
+			if (font == (int) kGraphics_font::TIMES) lc -> width = info -> ps.times;
+			else if (font == (int) kGraphics_font::HELVETICA) lc -> width = info -> ps.helvetica;
+			else if (font == (int) kGraphics_font::PALATINO) lc -> width = info -> ps.palatino;
 			else if (font == kGraphics_font_SYMBOL) lc -> width = info -> ps.times;
 			else if (my useSilipaPS) lc -> width = info -> ps.timesItalic;
 			else lc -> width = info -> ps.times;   // XIPA
 		} else if (style == Graphics_BOLD) {
-			if (font == kGraphics_font_TIMES) lc -> width = info -> ps.timesBold;
-			else if (font == kGraphics_font_HELVETICA) lc -> width = info -> ps.helveticaBold;
-			else if (font == kGraphics_font_PALATINO) lc -> width = info -> ps.palatinoBold;
+			if (font == (int) kGraphics_font::TIMES) lc -> width = info -> ps.timesBold;
+			else if (font == (int) kGraphics_font::HELVETICA) lc -> width = info -> ps.helveticaBold;
+			else if (font == (int) kGraphics_font::PALATINO) lc -> width = info -> ps.palatinoBold;
 			else if (font == kGraphics_font_SYMBOL) lc -> width = info -> ps.times;
 			else if (my useSilipaPS) lc -> width = info -> ps.timesBoldItalic;
 			else lc -> width = info -> ps.times;   // Symbol, IPA
 		} else if (style == Graphics_ITALIC) {
-			if (font == kGraphics_font_TIMES) lc -> width = info -> ps.timesItalic;
-			else if (font == kGraphics_font_HELVETICA) lc -> width = info -> ps.helvetica;
-			else if (font == kGraphics_font_PALATINO) lc -> width = info -> ps.palatinoItalic;
+			if (font == (int) kGraphics_font::TIMES) lc -> width = info -> ps.timesItalic;
+			else if (font == (int) kGraphics_font::HELVETICA) lc -> width = info -> ps.helvetica;
+			else if (font == (int) kGraphics_font::PALATINO) lc -> width = info -> ps.palatinoItalic;
 			else if (font == kGraphics_font_SYMBOL) lc -> width = info -> ps.times;
 			else if (my useSilipaPS) lc -> width = info -> ps.timesItalic;
 			else lc -> width = info -> ps.times;   // Symbol, IPA
 		} else if (style == Graphics_BOLD_ITALIC) {
-			if (font == kGraphics_font_TIMES) lc -> width = info -> ps.timesBoldItalic;
-			else if (font == kGraphics_font_HELVETICA) lc -> width = info -> ps.helveticaBold;
-			else if (font == kGraphics_font_PALATINO) lc -> width = info -> ps.palatinoBoldItalic;
+			if (font == (int) kGraphics_font::TIMES) lc -> width = info -> ps.timesBoldItalic;
+			else if (font == (int) kGraphics_font::HELVETICA) lc -> width = info -> ps.helveticaBold;
+			else if (font == (int) kGraphics_font::PALATINO) lc -> width = info -> ps.palatinoBoldItalic;
 			else if (font == kGraphics_font_SYMBOL) lc -> width = info -> ps.times;
 			else if (my useSilipaPS) lc -> width = info -> ps.timesBoldItalic;
 			else lc -> width = info -> ps.times;   // Symbol, IPA
@@ -620,7 +620,7 @@ static void charDraw (void *void_me, int xDC, int yDC, _Graphics_widechar *lc,
 				}
 				width += 4;   // for slant
 				Rectangle (dc, 0, top, width, bottom);
-				SelectFont (dc, fonts [my resolutionNumber] [font] [lc -> size] [lc -> style]);
+				SelectFont (dc, fonts [(int) my resolutionNumber] [font] [lc -> size] [lc -> style]);
 				SetTextColor (dc, my d_winForegroundColour);
 				TextOutW (dc, 0, baseline, codesW, str16len ((const char16 *) codesW));
 				BitBlt (my d_gdiGraphicsContext, xDC, yDC - ascent, width, bottom - top, dc, 0, top, SRCINVERT);
@@ -628,7 +628,7 @@ static void charDraw (void *void_me, int xDC, int yDC, _Graphics_widechar *lc,
 			}
 			SelectPen (my d_gdiGraphicsContext, my d_winPen), SelectBrush (my d_gdiGraphicsContext, my d_winBrush);
 			if (lc -> link) SetTextColor (my d_gdiGraphicsContext, RGB (0, 0, 255)); else SetTextColor (my d_gdiGraphicsContext, my d_winForegroundColour);
-			SelectFont (my d_gdiGraphicsContext, fonts [my resolutionNumber] [font] [lc -> size] [lc -> style]);
+			SelectFont (my d_gdiGraphicsContext, fonts [(int) my resolutionNumber] [font] [lc -> size] [lc -> style]);
 			if (my textRotation == 0.0) {
 				TextOutW (my d_gdiGraphicsContext, xDC, yDC, codesW, str16len ((const char16 *) codesW));
 			} else {
@@ -674,10 +674,10 @@ static void charDraw (void *void_me, int xDC, int yDC, _Graphics_widechar *lc,
 				keys [1] = kCTFontNameAttribute;
 				CFStringRef cfFont;
 				switch (font) {
-					case kGraphics_font_TIMES:       { cfFont = (CFStringRef) Melder_peek32toCfstring (U"Times New Roman"); } break;
-					case kGraphics_font_HELVETICA:   { cfFont = (CFStringRef) Melder_peek32toCfstring (U"Arial"          ); } break;
-					case kGraphics_font_COURIER:     { cfFont = (CFStringRef) Melder_peek32toCfstring (U"Courier New"    ); } break;
-					case kGraphics_font_PALATINO:    { if (Melder_debug == 900)
+					case (int) kGraphics_font::TIMES:       { cfFont = (CFStringRef) Melder_peek32toCfstring (U"Times New Roman"); } break;
+					case (int) kGraphics_font::HELVETICA:   { cfFont = (CFStringRef) Melder_peek32toCfstring (U"Arial"          ); } break;
+					case (int) kGraphics_font::COURIER:     { cfFont = (CFStringRef) Melder_peek32toCfstring (U"Courier New"    ); } break;
+					case (int) kGraphics_font::PALATINO:    { if (Melder_debug == 900)
 															cfFont = (CFStringRef) Melder_peek32toCfstring (U"DG Meta Serif Science");
 													   else
 														    cfFont = (CFStringRef) Melder_peek32toCfstring (U"Palatino");
@@ -699,10 +699,10 @@ static void charDraw (void *void_me, int xDC, int yDC, _Graphics_widechar *lc,
 				NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithCapacity: 2];
 				[attributes   setObject: styleDict   forKey: (id) kCTFontTraitsAttribute];
 				switch (font) {
-					case kGraphics_font_TIMES:       { [attributes   setObject: @"Times New Roman"   forKey: (id) kCTFontNameAttribute]; } break;
-					case kGraphics_font_HELVETICA:   { [attributes   setObject: @"Arial"             forKey: (id) kCTFontNameAttribute]; } break;
-					case kGraphics_font_COURIER:     { [attributes   setObject: @"Courier New"       forKey: (id) kCTFontNameAttribute]; } break;
-					case kGraphics_font_PALATINO:    { if (Melder_debug == 900)
+					case (int) kGraphics_font::TIMES:       { [attributes   setObject: @"Times New Roman"   forKey: (id) kCTFontNameAttribute]; } break;
+					case (int) kGraphics_font::HELVETICA:   { [attributes   setObject: @"Arial"             forKey: (id) kCTFontNameAttribute]; } break;
+					case (int) kGraphics_font::COURIER:     { [attributes   setObject: @"Courier New"       forKey: (id) kCTFontNameAttribute]; } break;
+					case (int) kGraphics_font::PALATINO:    { if (Melder_debug == 900)
 															[attributes   setObject: @"DG Meta Serif Science" forKey: (id) kCTFontNameAttribute];
 													   else
 														    [attributes   setObject: @"Palatino"              forKey: (id) kCTFontNameAttribute];
@@ -867,6 +867,7 @@ static void charSizes (Graphics me, _Graphics_widechar string [], bool measureEa
 			 * Determine the font family.
 			 */
 			Longchar_Info info = lc -> karInfo;
+			Melder_assert (info);
 			int font = chooseFont (me, lc);
 			lc -> font.string = nullptr;   // this erases font.integer!
 
@@ -888,10 +889,10 @@ static void charSizes (Graphics me, _Graphics_widechar string [], bool measureEa
 					NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithCapacity: 2];
 					[attributes   setObject: styleDict   forKey: (id) kCTFontTraitsAttribute];
 					switch (font) {
-						case kGraphics_font_TIMES:       { [attributes   setObject: @"Times"           forKey: (id) kCTFontNameAttribute]; } break;
-						case kGraphics_font_HELVETICA:   { [attributes   setObject: @"Arial"           forKey: (id) kCTFontNameAttribute]; } break;
-						case kGraphics_font_COURIER:     { [attributes   setObject: @"Courier New"     forKey: (id) kCTFontNameAttribute]; } break;
-						case kGraphics_font_PALATINO:    { if (Melder_debug == 900)
+						case (int) kGraphics_font::TIMES:       { [attributes   setObject: @"Times"           forKey: (id) kCTFontNameAttribute]; } break;
+						case (int) kGraphics_font::HELVETICA:   { [attributes   setObject: @"Arial"           forKey: (id) kCTFontNameAttribute]; } break;
+						case (int) kGraphics_font::COURIER:     { [attributes   setObject: @"Courier New"     forKey: (id) kCTFontNameAttribute]; } break;
+						case (int) kGraphics_font::PALATINO:    { if (Melder_debug == 900)
 																[attributes   setObject: @"DG Meta Serif Science" forKey: (id) kCTFontNameAttribute];
 														   else
 																[attributes   setObject: @"Palatino"              forKey: (id) kCTFontNameAttribute];
@@ -1059,9 +1060,9 @@ static void drawOneCell (Graphics me, int xDC, int yDC, _Graphics_widechar lc []
 	_Graphics_widechar *plc, *lastlc;
 	bool inLink = false;
 	switch (my horizontalTextAlignment) {
-		case Graphics_LEFT:      dx = 1 + (0.1/72) * my fontSize * my resolution; break;
-		case Graphics_CENTRE:    dx = - width / 2; break;
-		case Graphics_RIGHT:     dx = width != 0.0 ? - width - (0.1/72) * my fontSize * my resolution : 0; break;   // if width is zero, do not step left
+		case (int) Graphics_LEFT:      dx = 1 + (0.1/72) * my fontSize * my resolution; break;
+		case (int) Graphics_CENTRE:    dx = - width / 2; break;
+		case (int) Graphics_RIGHT:     dx = width != 0.0 ? - width - (0.1/72) * my fontSize * my resolution : 0; break;   // if width is zero, do not step left
 		default:                 dx = 1 + (0.1/72) * my fontSize * my resolution; break;
 	}
 	switch (my verticalTextAlignment) {
@@ -1198,7 +1199,7 @@ static void drawOneCell (Graphics me, int xDC, int yDC, _Graphics_widechar lc []
 	#endif
 }
 
-static struct { double width; short alignment; } tabs [1 + 20] = { { 0, Graphics_CENTRE },
+static struct { double width; kGraphics_horizontalAlignment alignment; } tabs [1 + 20] = { { 0, Graphics_CENTRE },
 	{ 1, Graphics_CENTRE }, { 1, Graphics_CENTRE }, { 1, Graphics_CENTRE }, { 1, Graphics_CENTRE },
 	{ 1, Graphics_CENTRE }, { 1, Graphics_CENTRE }, { 1, Graphics_CENTRE }, { 1, Graphics_CENTRE } };
 
@@ -1221,7 +1222,7 @@ static void drawCells (Graphics me, double xWC, double yWC, _Graphics_widechar l
 			itab ++;
 			xWC += ( tabs [itab]. alignment == Graphics_LEFT ? 0 :
 			       tabs [itab]. alignment == Graphics_CENTRE ? 0.5 * tabs [itab]. width : tabs [itab]. width ) * my fontSize / 12.0;
-			my horizontalTextAlignment = tabs [itab]. alignment;
+			my horizontalTextAlignment = (int) tabs [itab]. alignment;
 			my wrapWidth = tabs [itab]. width * my fontSize / 12.0;
 		}
 	}
@@ -1367,7 +1368,7 @@ static void parseTextIntoCellsLinesRuns (Graphics me, const char32 *txt /* catta
 				in += 2;
 			}
 		} else if (kar == U'\"') {
-			if (! (my font == kGraphics_font_COURIER || my fontStyle == Graphics_CODE || wordCode || globalCode))
+			if (! (my font == kGraphics_font::COURIER || my fontStyle == Graphics_CODE || wordCode || globalCode))
 				kar = ++nquote & 1 ? UNICODE_LEFT_DOUBLE_QUOTATION_MARK : UNICODE_RIGHT_DOUBLE_QUOTATION_MARK;
 		} else if (kar == U'\'') {
 			kar = UNICODE_RIGHT_SINGLE_QUOTATION_MARK;
@@ -1375,10 +1376,10 @@ static void parseTextIntoCellsLinesRuns (Graphics me, const char32 *txt /* catta
 			kar = UNICODE_LEFT_SINGLE_QUOTATION_MARK;
 		} else if (kar >= 32 && kar <= 126) {
 			if (kar == U'f') {
-				if (in [0] == U'i' && HAS_FI_AND_FL_LIGATURES && ! (my font == kGraphics_font_COURIER || my fontStyle == Graphics_CODE || wordCode || globalCode)) {
+				if (in [0] == U'i' && HAS_FI_AND_FL_LIGATURES && ! (my font == kGraphics_font::COURIER || my fontStyle == Graphics_CODE || wordCode || globalCode)) {
 					kar = UNICODE_LATIN_SMALL_LIGATURE_FI;
 					in ++;
-				} else if (in [0] == U'l' && HAS_FI_AND_FL_LIGATURES && ! (my font == kGraphics_font_COURIER || my fontStyle == Graphics_CODE || wordCode || globalCode)) {
+				} else if (in [0] == U'l' && HAS_FI_AND_FL_LIGATURES && ! (my font == kGraphics_font::COURIER || my fontStyle == Graphics_CODE || wordCode || globalCode)) {
 					kar = UNICODE_LATIN_SMALL_LIGATURE_FL;
 					in ++;
 				}
@@ -1393,8 +1394,8 @@ static void parseTextIntoCellsLinesRuns (Graphics me, const char32 *txt /* catta
 			charItalic = charBold = charSuperscript = charSubscript = 0;
 			out ++;
 			continue;   // do not draw
-		} else if (kar == '\n') {
-			kar = ' ';
+		} else if (kar == U'\n') {
+			kar = U' ';
 		}
 		if (wordItalic | wordBold | wordCode | wordLink) {
 			if (! isalnum ((int) kar) && kar != U'_')   // FIXME: this test could be more precise.
@@ -1406,18 +1407,20 @@ static void parseTextIntoCellsLinesRuns (Graphics me, const char32 *txt /* catta
 			((my fontStyle & Graphics_BOLD) | charBold | wordBold | globalBold ? Graphics_BOLD : 0);
 		out -> font.string = nullptr;
 		out -> font.integer = my fontStyle == Graphics_CODE || wordCode || globalCode ||
-			kar == U'/' || kar == U'|' ? kGraphics_font_COURIER : my font;
+			kar == U'/' || kar == U'|' ? (int) kGraphics_font::COURIER : (int) my font;
 		out -> link = wordLink | globalLink;
 		out -> baseline = charSuperscript | globalSuperscript ? 34 : charSubscript | globalSubscript ? -25 : 0;
 		out -> size = globalSmall || out -> baseline != 0 ? 80 : 100;
 		if (kar == U'/') {
 			out -> baseline -= out -> size / 12;
 			out -> size += out -> size / 10;
-			if (my screen) out -> font.integer = kGraphics_font_PALATINO;
+			if (my screen) out -> font.integer = (int) kGraphics_font::PALATINO;
 		}
 		out -> code = U'?';   // does this have any meaning?
+		Melder_assert (kar != U'\0');
 		out -> kar = kar;
 		out -> karInfo = Longchar_getInfoFromNative (kar);
+		Melder_assert (out -> karInfo);
 		out -> rightToLeft =
 			(kar >= 0x0590 && kar <= 0x06FF) ||
 			(kar >= 0xFE70 && kar <= 0xFEFF) ||
@@ -1427,6 +1430,7 @@ static void parseTextIntoCellsLinesRuns (Graphics me, const char32 *txt /* catta
 	}
 	out -> kar = U'\0';   // end of text
 	out -> karInfo = Longchar_getInfoFromNative (kar);
+	Melder_assert (out -> karInfo);
 	out -> rightToLeft = false;
 }
 
@@ -1479,8 +1483,8 @@ void Graphics_textRect (Graphics me, double x1, double x2, double y1, double y2,
 			if (flush) {
 				char32 saveKar = plc -> kar;
 				int direction = my yIsZeroAtTheTop ? -1 : 1;
-				int x = my horizontalTextAlignment == Graphics_LEFT ? x1DC :
-					my horizontalTextAlignment == Graphics_RIGHT ? x2DC :
+				int x = my horizontalTextAlignment == (int) Graphics_LEFT ? x1DC :
+					my horizontalTextAlignment == (int) Graphics_RIGHT ? x2DC :
 					0.5 * (x1 + x2) * my scaleX + my deltaX;
 				int y = my verticalTextAlignment == Graphics_BOTTOM ?
 					y1DC + direction * (lines - iline) * lineHeight :
@@ -1616,30 +1620,30 @@ static double psTextWidth (_Graphics_widechar string [], bool useSilipaPS) {
 			character -> style == Graphics_BOLD_ITALIC ? Graphics_BOLD_ITALIC : 0;
 		double size = character -> size * 0.01;
 		double charWidth = 600;   // Courier
-		if (font == kGraphics_font_COURIER) {
+		if (font == (int) kGraphics_font::COURIER) {
 			charWidth = 600;
 		} else if (style == 0) {
-			if (font == kGraphics_font_TIMES) charWidth = info -> ps.times;
-			else if (font == kGraphics_font_HELVETICA) charWidth = info -> ps.helvetica;
-			else if (font == kGraphics_font_PALATINO) charWidth = info -> ps.palatino;
+			if (font == (int) kGraphics_font::TIMES) charWidth = info -> ps.times;
+			else if (font == (int) kGraphics_font::HELVETICA) charWidth = info -> ps.helvetica;
+			else if (font == (int) kGraphics_font::PALATINO) charWidth = info -> ps.palatino;
 			else if (font == kGraphics_font_IPATIMES && useSilipaPS) charWidth = info -> ps.timesItalic;
 			else charWidth = info -> ps.times;   // Symbol, IPA
 		} else if (style == Graphics_BOLD) {
-			if (font == kGraphics_font_TIMES) charWidth = info -> ps.timesBold;
-			else if (font == kGraphics_font_HELVETICA) charWidth = info -> ps.helveticaBold;
-			else if (font == kGraphics_font_PALATINO) charWidth = info -> ps.palatinoBold;
+			if (font == (int) kGraphics_font::TIMES) charWidth = info -> ps.timesBold;
+			else if (font == (int) kGraphics_font::HELVETICA) charWidth = info -> ps.helveticaBold;
+			else if (font == (int) kGraphics_font::PALATINO) charWidth = info -> ps.palatinoBold;
 			else if (font == kGraphics_font_IPATIMES && useSilipaPS) charWidth = info -> ps.timesBoldItalic;
 			else charWidth = info -> ps.times;
 		} else if (style == Graphics_ITALIC) {
-			if (font == kGraphics_font_TIMES) charWidth = info -> ps.timesItalic;
-			else if (font == kGraphics_font_HELVETICA) charWidth = info -> ps.helvetica;
-			else if (font == kGraphics_font_PALATINO) charWidth = info -> ps.palatinoItalic;
+			if (font == (int) kGraphics_font::TIMES) charWidth = info -> ps.timesItalic;
+			else if (font == (int) kGraphics_font::HELVETICA) charWidth = info -> ps.helvetica;
+			else if (font == (int) kGraphics_font::PALATINO) charWidth = info -> ps.palatinoItalic;
 			else if (font == kGraphics_font_IPATIMES && useSilipaPS) charWidth = info -> ps.timesItalic;
 			else charWidth = info -> ps.times;
 		} else if (style == Graphics_BOLD_ITALIC) {
-			if (font == kGraphics_font_TIMES) charWidth = info -> ps.timesBoldItalic;
-			else if (font == kGraphics_font_HELVETICA) charWidth = info -> ps.helveticaBold;
-			else if (font == kGraphics_font_PALATINO) charWidth = info -> ps.palatinoBoldItalic;
+			if (font == (int) kGraphics_font::TIMES) charWidth = info -> ps.timesBoldItalic;
+			else if (font == (int) kGraphics_font::HELVETICA) charWidth = info -> ps.helveticaBold;
+			else if (font == (int) kGraphics_font::PALATINO) charWidth = info -> ps.palatinoBoldItalic;
 			else if (font == kGraphics_font_IPATIMES && useSilipaPS) charWidth = info -> ps.timesBoldItalic;
 			else charWidth = info -> ps.times;
 		}
@@ -1754,10 +1758,10 @@ void _GraphicsScreen_text_init (GraphicsScreen me) {   // BUG: should be done as
 	#elif gdi
 		int font, size, style;
 		if (my printer || my metafile)
-			for (font = kGraphics_font_MIN; font <= kGraphics_font_DINGBATS; font ++)
+			for (font = (int) kGraphics_font::MIN; font <= kGraphics_font_DINGBATS; font ++)
 				for (size = 0; size <= 4; size ++)
 					for (style = 0; style <= Graphics_BOLD_ITALIC; style ++)
-						if (fonts [my resolutionNumber] [font] [size] [style]) {
+						if (fonts [(int) my resolutionNumber] [font] [size] [style]) {
 							//DeleteObject (fonts [my resolutionNumber] [font] [size] [style]);
 							//fonts [my resolutionNumber] [font] [size] [style] = 0;
 						}
@@ -1769,8 +1773,8 @@ void _GraphicsScreen_text_init (GraphicsScreen me) {   // BUG: should be done as
 
 /* Output attributes. */
 
-void Graphics_setTextAlignment (Graphics me, int hor, int vert) {
-	if (hor != Graphics_NOCHANGE) my horizontalTextAlignment = hor;
+void Graphics_setTextAlignment (Graphics me, kGraphics_horizontalAlignment hor, int vert) {
+	if ((int) hor != Graphics_NOCHANGE) my horizontalTextAlignment = (int) hor;
 	if (vert != Graphics_NOCHANGE) my verticalTextAlignment = vert;
 	if (my recording) { op (SET_TEXT_ALIGNMENT, 2); put (hor); put (vert); }
 }
diff --git a/sys/Graphics_utils.cpp b/sys/Graphics_utils.cpp
index f6a8e53..a2740c3 100644
--- a/sys/Graphics_utils.cpp
+++ b/sys/Graphics_utils.cpp
@@ -830,7 +830,7 @@ void Graphics_mark (Graphics me, double x, double y, double size_mm, const char3
 		Graphics_setTextAlignment (me, Graphics_CENTRE, Graphics_HALF);
 		Graphics_text (me, x, y, markString);
 		Graphics_setFontSize (me, oldSize);
-		Graphics_setTextAlignment (me, oldHorizontalAlignment, oldVerticalAlignment);
+		Graphics_setTextAlignment (me, (kGraphics_horizontalAlignment) oldHorizontalAlignment, oldVerticalAlignment);
 	} else if (mark == 0) {
 		Graphics_fillCircle_mm (me, x, y, size_mm);
 	} else if (mark == 1) {
diff --git a/sys/GuiText.cpp b/sys/GuiText.cpp
index 22197f2..5e72e25 100644
--- a/sys/GuiText.cpp
+++ b/sys/GuiText.cpp
@@ -746,7 +746,7 @@ char32 * GuiText_getSelection (GuiText me) {
 			memmove (bufferW, bufferW + startW, lengthW * sizeof (WCHAR));   // not because of realloc, but because of free!
 			bufferW [lengthW] = U'\0';
 			char32 *result = Melder_dup_f (Melder_peekWto32 (bufferW));
-			Melder_killReturns_inline (result);   // AFTER zooming!
+			(void) Melder_killReturns_inline (result);   // AFTER zooming!
 			return result;
 		}
 	#elif cocoa
@@ -757,7 +757,7 @@ char32 * GuiText_getSelection (GuiText me) {
 			char32 *result = Melder_malloc_f (char32, length + 1);
 			memcpy (result, & selection [start], length * sizeof (char32));
 			result [length] = '\0';
-			Melder_killReturns_inline (result);
+			(void) Melder_killReturns_inline (result);
 			return result;
 		}
 	#endif
@@ -814,7 +814,7 @@ char32 * GuiText_getStringAndSelectionPosition (GuiText me, long *first, long *l
 
 		char32 *result = Melder_dup_f (Melder_peekWto32 (bufferW));
 		Melder_free (bufferW);
-		Melder_killReturns_inline (result);
+		(void) Melder_killReturns_inline (result);
 		return result;
 	#elif cocoa
 		if (my d_cocoaTextView) {
diff --git a/sys/HyperPage.cpp b/sys/HyperPage.cpp
index 8f865c7..0eb41ca 100644
--- a/sys/HyperPage.cpp
+++ b/sys/HyperPage.cpp
@@ -110,7 +110,7 @@ void HyperPage_initSheetOfPaper (HyperPage me) {
 	my d_y = PAPER_TOP - TOP_MARGIN;
 	my d_x = 0;
 	my previousBottomSpacing = 0.0;
-	Graphics_setFont (my ps, kGraphics_font_TIMES);
+	Graphics_setFont (my ps, kGraphics_font::TIMES);
 	Graphics_setFontSize (my ps, 12);
 	Graphics_setFontStyle (my ps, Graphics_ITALIC);
 	if (leftHeader) {
@@ -145,15 +145,11 @@ void HyperPage_initSheetOfPaper (HyperPage me) {
 
 static void updateVerticalScrollBar (HyperPage me);
 
-int HyperPage_any (HyperPage me, const char32 *text, enum kGraphics_font font, int size, int style, double minFooterDistance,
+void HyperPage_any (HyperPage me, const char32 *text, kGraphics_font font, int size, int style, double minFooterDistance,
 	double x, double secondIndent, double topSpacing, double bottomSpacing, unsigned long method)
 {
-	double heightGuess;
-
-	if (my rightMargin == 0) return 0;
-	// Melder_assert (my rightMargin != 0);
-
-	heightGuess = size * (1.2/72) * ((long) size * str32len (text) / (int) (my rightMargin * 150));
+	if (my rightMargin == 0) return;   // no infinite heights please
+	double heightGuess = size * (1.2/72) * ((long) size * str32len (text) / (int) (my rightMargin * 150));
 
 if (! my printing) {
 	Graphics_Link *paragraphLinks;
@@ -219,80 +215,79 @@ if (! my printing) {
 	my d_y = Graphics_inqTextY (my ps);
 }
 	my previousBottomSpacing = bottomSpacing;
-	return 1;
 }
 
-int HyperPage_pageTitle (HyperPage me, const char32 *title) {
-	return HyperPage_any (me, title, my p_font, my p_fontSize * 2, 0,
+void HyperPage_pageTitle (HyperPage me, const char32 *title) {
+	HyperPage_any (me, title, my p_font, my p_fontSize * 2, 0,
 		2.0, 0.0, 0.0, my printing ? 0.4/2 : 0.2/2, 0.3/2, HyperPage_ADD_BORDER);
 }
-int HyperPage_intro (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.03, 0.0, 0.1, 0.1, 0);
+void HyperPage_intro (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.03, 0.0, 0.1, 0.1, 0);
 }
-int HyperPage_entry (HyperPage me, const char32 *title) {
-	return HyperPage_any (me, title, my p_font, my p_fontSize * 1.4, Graphics_BOLD, 0.5, 0.0, 0.0, 0.25/1.4, 0.1/1.4, HyperPage_USE_ENTRY_HINT);
+void HyperPage_entry (HyperPage me, const char32 *title) {
+	HyperPage_any (me, title, my p_font, my p_fontSize * 1.4, Graphics_BOLD, 0.5, 0.0, 0.0, 0.25/1.4, 0.1/1.4, HyperPage_USE_ENTRY_HINT);
 }
-int HyperPage_paragraph (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.03, 0.0, 0.1, 0.1, 0);
+void HyperPage_paragraph (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.03, 0.0, 0.1, 0.1, 0);
 }
-int HyperPage_listItem (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.30, 0.2, 0.0, 0.0, 0);
+void HyperPage_listItem (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.30, 0.2, 0.0, 0.0, 0);
 }
-int HyperPage_listItem1 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.57, 0.2, 0.0, 0.0, 0);
+void HyperPage_listItem1 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.57, 0.2, 0.0, 0.0, 0);
 }
-int HyperPage_listItem2 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.84, 0.2, 0.0, 0.0, 0);
+void HyperPage_listItem2 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.84, 0.2, 0.0, 0.0, 0);
 }
-int HyperPage_listItem3 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 1.11, 0.2, 0.0, 0.0, 0);
+void HyperPage_listItem3 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 1.11, 0.2, 0.0, 0.0, 0);
 }
-int HyperPage_listTag (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 0.03, 0.0, 0.1, 0.03, 0);
+void HyperPage_listTag (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 0.03, 0.0, 0.1, 0.03, 0);
 }
-int HyperPage_listTag1 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 0.50, 0.0, 0.05, 0.03, 0);
+void HyperPage_listTag1 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 0.50, 0.0, 0.05, 0.03, 0);
 }
-int HyperPage_listTag2 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 0.97, 0.0, 0.03, 0.03, 0);
+void HyperPage_listTag2 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 0.97, 0.0, 0.03, 0.03, 0);
 }
-int HyperPage_listTag3 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 1.44, 0.0, 0.03, 0.03, 0);
+void HyperPage_listTag3 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.2, 1.44, 0.0, 0.03, 0.03, 0);
 }
-int HyperPage_definition (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.5, 0.0, 0.03, 0.1, 0);
+void HyperPage_definition (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.5, 0.0, 0.03, 0.1, 0);
 }
-int HyperPage_definition1 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.97, 0.0, 0.03, 0.05, 0);
+void HyperPage_definition1 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.97, 0.0, 0.03, 0.05, 0);
 }
-int HyperPage_definition2 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 1.44, 0.0, 0.03, 0.03, 0);
+void HyperPage_definition2 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 1.44, 0.0, 0.03, 0.03, 0);
 }
-int HyperPage_definition3 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 1.93, 0.0, 0.03, 0.03, 0);
+void HyperPage_definition3 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 1.93, 0.0, 0.03, 0.03, 0);
 }
-int HyperPage_code (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, kGraphics_font_COURIER, my p_fontSize * 0.86, 0, 0.0, 0.3, 0.5, 0.0, 0.0, 0);
+void HyperPage_code (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, kGraphics_font::COURIER, my p_fontSize * 0.86, 0, 0.0, 0.3, 0.5, 0.0, 0.0, 0);
 }
-int HyperPage_code1 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, kGraphics_font_COURIER, my p_fontSize * 0.86, 0, 0.0, 0.6, 0.5, 0.0, 0.0, 0);
+void HyperPage_code1 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, kGraphics_font::COURIER, my p_fontSize * 0.86, 0, 0.0, 0.6, 0.5, 0.0, 0.0, 0);
 }
-int HyperPage_code2 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, kGraphics_font_COURIER, my p_fontSize * 0.86, 0, 0.0, 0.9, 0.5, 0.0, 0.0, 0);
+void HyperPage_code2 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, kGraphics_font::COURIER, my p_fontSize * 0.86, 0, 0.0, 0.9, 0.5, 0.0, 0.0, 0);
 }
-int HyperPage_code3 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, kGraphics_font_COURIER, my p_fontSize * 0.86, 0, 0.0, 1.2, 0.5, 0.0, 0.0, 0);
+void HyperPage_code3 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, kGraphics_font::COURIER, my p_fontSize * 0.86, 0, 0.0, 1.2, 0.5, 0.0, 0.0, 0);
 }
-int HyperPage_code4 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, kGraphics_font_COURIER, my p_fontSize * 0.86, 0, 0.0, 1.5, 0.5, 0.0, 0.0, 0);
+void HyperPage_code4 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, kGraphics_font::COURIER, my p_fontSize * 0.86, 0, 0.0, 1.5, 0.5, 0.0, 0.0, 0);
 }
-int HyperPage_code5 (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, kGraphics_font_COURIER, my p_fontSize * 0.86, 0, 0.0, 1.8, 0.5, 0.0, 0.0, 0);
+void HyperPage_code5 (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, kGraphics_font::COURIER, my p_fontSize * 0.86, 0, 0.0, 1.8, 0.5, 0.0, 0.0, 0);
 }
-int HyperPage_prototype (HyperPage me, const char32 *text) {
-	return HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.03, 0.5, 0.0, 0.0, 0);
+void HyperPage_prototype (HyperPage me, const char32 *text) {
+	HyperPage_any (me, text, my p_font, my p_fontSize, 0, 0.0, 0.03, 0.5, 0.0, 0.0, 0);
 }
-int HyperPage_formula (HyperPage me, const char32 *formula) {
+void HyperPage_formula (HyperPage me, const char32 *formula) {
 	double topSpacing = 0.2, bottomSpacing = 0.2, minFooterDistance = 0.0;
 	kGraphics_font font = my p_font;
 	int size = my p_fontSize;
@@ -329,10 +324,9 @@ if (! my printing) {
 	Graphics_setTextAlignment (my ps, Graphics_LEFT, Graphics_BOTTOM);
 }
 	my previousBottomSpacing = bottomSpacing;
-	return 1;
 }
 
-int HyperPage_picture (HyperPage me, double width_inches, double height_inches, void (*draw) (Graphics g)) {
+void HyperPage_picture (HyperPage me, double width_inches, double height_inches, void (*draw) (Graphics g)) {
 	double topSpacing = 0.1, bottomSpacing = 0.1, minFooterDistance = 0.0;
 	kGraphics_font font = my p_font;
 	int size = my p_fontSize;
@@ -379,10 +373,9 @@ if (! my printing) {
 	Graphics_setTextAlignment (my ps, Graphics_LEFT, Graphics_BOTTOM);
 }
 	my previousBottomSpacing = bottomSpacing;
-	return 1;
 }
 
-int HyperPage_script (HyperPage me, double width_inches, double height_inches, const char32 *script) {
+void HyperPage_script (HyperPage me, double width_inches, double height_inches, const char32 *script) {
 	char32 *text = Melder_dup (script);
 	autoInterpreter interpreter = Interpreter_createFromEnvironment (nullptr);
 	double topSpacing = 0.1, bottomSpacing = 0.1, minFooterDistance = 0.0;
@@ -415,7 +408,7 @@ if (! my printing) {
 			theCurrentPraatObjects = (PraatObjects) my praatObjects;
 			theCurrentPraatPicture = (PraatPicture) my praatPicture;
 			theCurrentPraatPicture -> graphics = my graphics.get();   // has to draw into HyperPage rather than Picture window
-			theCurrentPraatPicture -> font = font;
+			theCurrentPraatPicture -> font = (int) font;
 			theCurrentPraatPicture -> fontSize = size;
 			theCurrentPraatPicture -> lineType = Graphics_DRAWN;
 			theCurrentPraatPicture -> colour = Graphics_BLACK;
@@ -512,7 +505,7 @@ if (! my printing) {
 		theCurrentPraatObjects = (PraatObjects) my praatObjects;
 		theCurrentPraatPicture = (PraatPicture) my praatPicture;
 		theCurrentPraatPicture -> graphics = my ps;
-		theCurrentPraatPicture -> font = font;
+		theCurrentPraatPicture -> font = (int) font;
 		theCurrentPraatPicture -> fontSize = size;
 		theCurrentPraatPicture -> lineType = Graphics_DRAWN;
 		theCurrentPraatPicture -> colour = Graphics_BLACK;
@@ -571,7 +564,6 @@ if (! my printing) {
 }
 	my previousBottomSpacing = bottomSpacing;
 	Melder_free (text);
-	return 1;
 }
 
 static void print (void *void_me, Graphics graphics) {
@@ -681,11 +673,11 @@ static void menu_cb_font (HyperPage me, EDITOR_ARGS_FORM) {
 			RADIOBUTTON (U"Times")
 			RADIOBUTTON (U"Helvetica")
 	EDITOR_OK
-		SET_INTEGER (U"Font", my p_font == kGraphics_font_TIMES ? 1 :
-				my p_font == kGraphics_font_HELVETICA ? 2 : my p_font == kGraphics_font_PALATINO ? 3 : 1);
+		SET_INTEGER (U"Font", my p_font == kGraphics_font::TIMES ? 1 :
+				my p_font == kGraphics_font::HELVETICA ? 2 : my p_font == kGraphics_font::PALATINO ? 3 : 1);
 	EDITOR_DO
 		int font = GET_INTEGER (U"Font");
-		my pref_font () = my p_font = font == 1 ? kGraphics_font_TIMES : kGraphics_font_HELVETICA;
+		my pref_font () = my p_font = font == 1 ? kGraphics_font::TIMES : kGraphics_font::HELVETICA;
 		if (my graphics) Graphics_updateWs (my graphics.get());
 	EDITOR_END
 }
@@ -710,7 +702,7 @@ static void menu_cb_18 (HyperPage me, EDITOR_ARGS_DIRECT) { setFontSize (me, 18)
 static void menu_cb_24 (HyperPage me, EDITOR_ARGS_DIRECT) { setFontSize (me, 24); }
 
 static void menu_cb_fontSize (HyperPage me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Font size", 0)
+	EDITOR_FORM (U"Font size", nullptr)
 		NATURAL (U"Font size (points)", my default_fontSize ())
 	EDITOR_OK
 		SET_INTEGER (U"Font size", my p_fontSize)
@@ -720,7 +712,7 @@ static void menu_cb_fontSize (HyperPage me, EDITOR_ARGS_FORM) {
 }
 
 static void menu_cb_searchForPage (HyperPage me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Search for page", 0)
+	EDITOR_FORM (U"Search for page", nullptr)
 		TEXTFIELD (U"Page", U"a")
 	EDITOR_OK
 	EDITOR_DO
@@ -935,12 +927,12 @@ void HyperPage_init (HyperPage me, const char32 *title, Daata data) {
 	my graphics = Graphics_create_xmdrawingarea (my drawingArea);
 	Graphics_setAtSignIsLink (my graphics.get(), true);
 	Graphics_setDollarSignIsCode (my graphics.get(), true);
-	Graphics_setFont (my graphics.get(), kGraphics_font_TIMES);
-	if (my p_font != kGraphics_font_TIMES && my p_font != kGraphics_font_HELVETICA)
-		my pref_font () = my p_font = kGraphics_font_TIMES;   // ensure Unicode compatibility
+	Graphics_setFont (my graphics.get(), kGraphics_font::TIMES);
+	if (my p_font != kGraphics_font::TIMES && my p_font != kGraphics_font::HELVETICA)
+		my pref_font () = my p_font = kGraphics_font::TIMES;   // ensure Unicode compatibility
 	setFontSize (me, my p_fontSize);
 
-struct structGuiDrawingArea_ResizeEvent event { my drawingArea, 0 };
+struct structGuiDrawingArea_ResizeEvent event { my drawingArea, 0, 0 };
 event. width  = GuiControl_getWidth  (my drawingArea);
 event. height = GuiControl_getHeight (my drawingArea);
 gui_drawingarea_cb_resize (me, & event);
diff --git a/sys/HyperPage.h b/sys/HyperPage.h
index 1f55e23..357faa5 100644
--- a/sys/HyperPage.h
+++ b/sys/HyperPage.h
@@ -79,34 +79,34 @@ void HyperPage_clear (HyperPage me);
 #define HyperPage_ADD_BORDER  1
 #define HyperPage_USE_ENTRY_HINT  2
 
-int HyperPage_any (HyperPage me, const char32 *text, enum kGraphics_font font, int size, int style, double minFooterDistance,
+void HyperPage_any (HyperPage me, const char32 *text, kGraphics_font font, int size, int style, double minFooterDistance,
 	double x, double secondIndent, double topSpacing, double bottomSpacing, unsigned long method);
-int HyperPage_pageTitle (HyperPage me, const char32 *title);
-int HyperPage_intro (HyperPage me, const char32 *text);
-int HyperPage_entry (HyperPage me, const char32 *title);
-int HyperPage_paragraph (HyperPage me, const char32 *text);
-int HyperPage_listItem (HyperPage me, const char32 *text);
-int HyperPage_listItem1 (HyperPage me, const char32 *text);
-int HyperPage_listItem2 (HyperPage me, const char32 *text);
-int HyperPage_listItem3 (HyperPage me, const char32 *text);
-int HyperPage_listTag (HyperPage me, const char32 *text);
-int HyperPage_listTag1 (HyperPage me, const char32 *text);
-int HyperPage_listTag2 (HyperPage me, const char32 *text);
-int HyperPage_listTag3 (HyperPage me, const char32 *text);
-int HyperPage_definition (HyperPage me, const char32 *text);
-int HyperPage_definition1 (HyperPage me, const char32 *text);
-int HyperPage_definition2 (HyperPage me, const char32 *text);
-int HyperPage_definition3 (HyperPage me, const char32 *text);
-int HyperPage_code (HyperPage me, const char32 *text);
-int HyperPage_code1 (HyperPage me, const char32 *text);
-int HyperPage_code2 (HyperPage me, const char32 *text);
-int HyperPage_code3 (HyperPage me, const char32 *text);
-int HyperPage_code4 (HyperPage me, const char32 *text);
-int HyperPage_code5 (HyperPage me, const char32 *text);
-int HyperPage_prototype (HyperPage me, const char32 *text);
-int HyperPage_formula (HyperPage me, const char32 *formula);
-int HyperPage_picture (HyperPage me, double width_inches, double height_inches, void (*draw) (Graphics g));
-int HyperPage_script (HyperPage me, double width_inches, double height_inches, const char32 *script);
+void HyperPage_pageTitle (HyperPage me, const char32 *title);
+void HyperPage_intro (HyperPage me, const char32 *text);
+void HyperPage_entry (HyperPage me, const char32 *title);
+void HyperPage_paragraph (HyperPage me, const char32 *text);
+void HyperPage_listItem (HyperPage me, const char32 *text);
+void HyperPage_listItem1 (HyperPage me, const char32 *text);
+void HyperPage_listItem2 (HyperPage me, const char32 *text);
+void HyperPage_listItem3 (HyperPage me, const char32 *text);
+void HyperPage_listTag (HyperPage me, const char32 *text);
+void HyperPage_listTag1 (HyperPage me, const char32 *text);
+void HyperPage_listTag2 (HyperPage me, const char32 *text);
+void HyperPage_listTag3 (HyperPage me, const char32 *text);
+void HyperPage_definition (HyperPage me, const char32 *text);
+void HyperPage_definition1 (HyperPage me, const char32 *text);
+void HyperPage_definition2 (HyperPage me, const char32 *text);
+void HyperPage_definition3 (HyperPage me, const char32 *text);
+void HyperPage_code (HyperPage me, const char32 *text);
+void HyperPage_code1 (HyperPage me, const char32 *text);
+void HyperPage_code2 (HyperPage me, const char32 *text);
+void HyperPage_code3 (HyperPage me, const char32 *text);
+void HyperPage_code4 (HyperPage me, const char32 *text);
+void HyperPage_code5 (HyperPage me, const char32 *text);
+void HyperPage_prototype (HyperPage me, const char32 *text);
+void HyperPage_formula (HyperPage me, const char32 *formula);
+void HyperPage_picture (HyperPage me, double width_inches, double height_inches, void (*draw) (Graphics g));
+void HyperPage_script (HyperPage me, double width_inches, double height_inches, const char32 *script);
 
 int HyperPage_goToPage (HyperPage me, const char32 *title);
 void HyperPage_goToPage_i (HyperPage me, long i);
diff --git a/sys/Interpreter.cpp b/sys/Interpreter.cpp
index b09876b..7188a1d 100644
--- a/sys/Interpreter.cpp
+++ b/sys/Interpreter.cpp
@@ -25,6 +25,8 @@ extern structMelderDir praatDir;
 #include "praat_version.h"
 #include "UnicodeData.h"
 
+#include "../fon/Vector.h"
+
 #define Interpreter_WORD 1
 #define Interpreter_REAL 2
 #define Interpreter_POSITIVE 3
@@ -316,9 +318,9 @@ UiForm Interpreter_createForm (Interpreter me, GuiWindow parent, const char32 *p
 			case Interpreter_TEXT:
 				UiForm_addText (form, parameter, my arguments [ipar]); break;
 			case Interpreter_CHOICE:
-				radio = UiForm_addRadio (form, parameter, a32tol (my arguments [ipar])); break;
+				radio = UiForm_addRadio (form, parameter, Melder_atoi (my arguments [ipar])); break;
 			case Interpreter_OPTIONMENU:
-				radio = UiForm_addOptionMenu (form, parameter, a32tol (my arguments [ipar])); break;
+				radio = UiForm_addOptionMenu (form, parameter, Melder_atoi (my arguments [ipar])); break;
 			case Interpreter_BUTTON:
 				if (radio) UiRadio_addButton (radio, my arguments [ipar]); break;
 			case Interpreter_OPTION:
@@ -690,6 +692,655 @@ static void parameterToVariable (Interpreter me, int type, const char32 *in_para
 	}
 }
 
+inline static void NumericVectorVariable_move (InterpreterVariable variable, numvec movedVector, bool owned) {
+	numvec variableVector = variable -> numericVectorValue;
+	if (owned) {
+		/*
+			Statement like: a# = b# + c#
+		*/
+		NUMvector_free (variableVector.at, 1);
+		variable -> numericVectorValue = movedVector;
+	} else if (variableVector.size == movedVector.size) {
+		if (variableVector.at == movedVector.at) {
+			/*
+				Statement like: a# = a#
+			*/
+			(void) 0;   // assigning a variable to itself: do nothing
+		} else {
+			/*
+				Statement like: a# = b#   // with matching sizes
+			*/
+			numvec_copyElements_nocheck (movedVector, variableVector);
+		}
+	} else {
+		/*
+			Statement like: a# = b#   // with non-matching sizes
+		*/
+		autonumvec copiedVector = copy_numvec (movedVector);
+		NUMvector_free (variableVector.at, 1);
+		variable -> numericVectorValue = copiedVector. releaseToAmbiguousOwner();
+	}
+}
+
+inline static void NumericMatrixVariable_move (InterpreterVariable variable, nummat movedMatrix, bool owned) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	if (owned) {
+		/*
+			Statement like: a## = b## + c##
+		*/
+		NUMmatrix_free (variableMatrix.at, 1, 1);
+		variable -> numericMatrixValue = movedMatrix;
+	} else if (variableMatrix.nrow == movedMatrix.nrow && variableMatrix.ncol == movedMatrix.ncol) {
+		if (variableMatrix.at == movedMatrix.at) {
+			/*
+				Statement like: a## = a##
+			*/
+			(void) 0;   // assigning a variable to itself: do nothing
+		} else {
+			/*
+				Statement like: a## = b##   // with matching sizes
+			*/
+			nummat_copyElements_nocheck (movedMatrix, variableMatrix);
+		}
+	} else {
+		/*
+			Statement like: a## = b##   // with non-matching sizes
+		*/
+		autonummat copiedMatrix = copy_nummat (movedMatrix);
+		NUMmatrix_free (variableMatrix.at, 1, 1);
+		variable -> numericMatrixValue = copiedMatrix. releaseToAmbiguousOwner();
+	}
+}
+
+inline static void NumericVectorVariable_add (InterpreterVariable variable, real scalar) {
+	numvec variableVector = variable -> numericVectorValue;
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] += scalar;
+}
+inline static void NumericVectorVariable_add (InterpreterVariable variable, numvec vector, bool owned) {
+	numvec variableVector = variable -> numericVectorValue;
+	if (vector.size != variableVector.size)
+		Melder_throw (U"You cannot add a vector with size ", vector.size,
+		              U" to a vector with a different size (", variableVector.size, U").");
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] += vector [i];
+	if (owned) {
+		/*
+			Statement like: a# += b# + c#
+		*/
+		NUMvector_free (vector.at, 1);
+	} else {
+		/*
+			Statement like: a# += b#
+		*/
+		(void) 0;
+	}
+}
+inline static void NumericVectorVariable_subtract (InterpreterVariable variable, real scalar) {
+	numvec variableVector = variable -> numericVectorValue;
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] -= scalar;
+}
+inline static void NumericVectorVariable_subtract (InterpreterVariable variable, numvec vector, bool owned) {
+	numvec variableVector = variable -> numericVectorValue;
+	if (vector.size != variableVector.size)
+		Melder_throw (U"You cannot subtract a vector with size ", vector.size,
+		              U" from a vector with a different size (", variableVector.size, U").");
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] -= vector [i];
+	if (owned) {
+		/*
+			Statement like: a# -= b# + c#
+		*/
+		NUMvector_free (vector.at, 1);
+	} else {
+		/*
+			Statement like: a# -= b#
+		*/
+		(void) 0;
+	}
+}
+inline static void NumericVectorVariable_multiply (InterpreterVariable variable, real scalar) {
+	numvec variableVector = variable -> numericVectorValue;
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] *= scalar;
+}
+inline static void NumericVectorVariable_multiply (InterpreterVariable variable, numvec vector, bool owned) {
+	numvec variableVector = variable -> numericVectorValue;
+	if (vector.size != variableVector.size)
+		Melder_throw (U"You cannot multiply a vector with size ", variableVector.size,
+		              U" with a vector with a different size (", vector.size, U").");
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] *= vector [i];
+	if (owned) {
+		/*
+			Statement like: a# *= b# + c#
+		*/
+		NUMvector_free (vector.at, 1);
+	} else {
+		/*
+			Statement like: a# *= b#
+		*/
+		(void) 0;
+	}
+}
+inline static void NumericVectorVariable_divide (InterpreterVariable variable, real scalar) {
+	numvec variableVector = variable -> numericVectorValue;
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] /= scalar;
+}
+inline static void NumericVectorVariable_divide (InterpreterVariable variable, numvec vector, bool owned) {
+	numvec variableVector = variable -> numericVectorValue;
+	if (vector.size != variableVector.size)
+		Melder_throw (U"You cannot divide a vector with size ", variableVector.size,
+		              U" by a vector with a different size (", vector.size, U").");
+	for (integer i = 1; i <= variableVector.size; i ++)
+		variableVector [i] /= vector [i];
+	if (owned) {
+		/*
+			Statement like: a# /= b# + c#
+		*/
+		NUMvector_free (vector.at, 1);
+	} else {
+		/*
+			Statement like: a# /= b#
+		*/
+		(void) 0;
+	}
+}
+inline static void NumericMatrixVariable_add (InterpreterVariable variable, real scalar) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] += scalar;
+}
+inline static void NumericMatrixVariable_add (InterpreterVariable variable, nummat matrix, bool owned) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
+		Melder_throw (U"You cannot add a matrix with size ", matrix.nrow, U"x", matrix.ncol,
+		              U" to a matrix with a different size (", variableMatrix.nrow, U"x", variableMatrix.ncol, U").");
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] += matrix [irow] [icol];
+	if (owned)
+		NUMmatrix_free (matrix.at, 1, 1);
+}
+inline static void NumericMatrixVariable_subtract (InterpreterVariable variable, real scalar) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] -= scalar;
+}
+inline static void NumericMatrixVariable_subtract (InterpreterVariable variable, nummat matrix, bool owned) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
+		Melder_throw (U"You cannot subtract a matrix with size ", matrix.nrow, U"x", matrix.ncol,
+		              U" from a matrix with a different size (", variableMatrix.nrow, U"x", variableMatrix.ncol, U").");
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] -= matrix [irow] [icol];
+	if (owned)
+		NUMmatrix_free (matrix.at, 1, 1);
+}
+inline static void NumericMatrixVariable_multiply (InterpreterVariable variable, real scalar) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] *= scalar;
+}
+inline static void NumericMatrixVariable_multiply (InterpreterVariable variable, nummat matrix, bool owned) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
+		Melder_throw (U"You cannot multiply a matrix with size ", variableMatrix.nrow, U"x", variableMatrix.ncol,
+		              U" from a matrix with a different size (", matrix.nrow, U"x", matrix.ncol, U").");
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] *= matrix [irow] [icol];
+	if (owned)
+		NUMmatrix_free (matrix.at, 1, 1);
+}
+inline static void NumericMatrixVariable_divide (InterpreterVariable variable, real scalar) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] /= scalar;
+}
+inline static void NumericMatrixVariable_divide (InterpreterVariable variable, nummat matrix, bool owned) {
+	nummat variableMatrix = variable -> numericMatrixValue;
+	if (matrix.nrow != variableMatrix.nrow || matrix.ncol != variableMatrix.ncol)
+		Melder_throw (U"You cannot divide a matrix with size ", variableMatrix.nrow, U"x", variableMatrix.ncol,
+		              U" by a matrix with a different size (", matrix.nrow, U"x", matrix.ncol, U").");
+	for (integer irow = 1; irow <= variableMatrix.nrow; irow ++)
+		for (integer icol = 1; icol <= variableMatrix.ncol; icol ++)
+			variableMatrix [irow] [icol] /= matrix [irow] [icol];
+	if (owned)
+		NUMmatrix_free (matrix.at, 1, 1);
+}
+
+static void Interpreter_do_procedureCall (Interpreter me, char32 *command,
+	char32 **lines, integer numberOfLines, long& lineNumber, long callStack [], int& callDepth)
+{
+	/*
+		Modern type of procedure calls, with comma separation, quoted strings, and array support.
+
+		We just passed the `@` sign, so we continue by looking for a procedure name at the call site.
+	*/
+	char32 *p = command;
+	while (Melder_isblank (*p)) p ++;   // skip whitespace
+	char32 *callName = p;
+	while (*p != U'\0' && *p != U' ' && *p != U'\t' && *p != U'(' && *p != U':') p ++;
+	if (p == callName) Melder_throw (U"Missing procedure name after \"@\".");
+	bool hasArguments = ( *p != U'\0' );
+	if (hasArguments) {
+		bool parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
+		*p = U'\0';   // close procedure name
+		if (! parenthesisOrColonFound) {
+			p ++;   // step over first white space
+			while (Melder_isblank (*p)) p ++;   // skip more whitespace
+			hasArguments = ( *p != U'\0' );
+			parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
+			if (hasArguments && ! parenthesisOrColonFound)
+				Melder_throw (U"Missing parenthesis or colon after procedure name \"", callName, U"\".");
+		}
+		p ++;   // step over parenthesis or colon
+	}
+	integer callLength = str32len (callName);
+	integer iline = 1;
+	for (; iline <= numberOfLines; iline ++) {
+		if (! str32nequ (lines [iline], U"procedure ", 10)) continue;
+		char32 *q = lines [iline] + 10;
+		while (Melder_isblank (*q)) q ++;   // skip whitespace before procedure name
+		char32 *procName = q;
+		while (*q != U'\0' && ! Melder_isblank (*q) && *q != U'(' && *q != U':') q ++;
+		if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
+		if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
+			/*
+			 * We found the procedure definition.
+			 */
+			if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
+				Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
+			str32cpy (my procedureNames [my callDepth], callName);
+			bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
+			if (*q) q ++;   // step over parenthesis or colon or first white space
+			if (! parenthesisOrColonFound) {
+				while (Melder_isblank (*q)) q ++;   // skip more whitespace
+				if (*q == U'(' || *q == U':') q ++;   // step over parenthesis or colon
+			}
+			while (*q && *q != U')') {
+				static MelderString argument { };
+				MelderString_empty (& argument);
+				while (Melder_isblank (*p)) p ++;
+				while (Melder_isblank (*q)) q ++;
+				char32 *parameterName = q;
+				while (*q != U'\0' && ! Melder_isblank (*q) && *q != U',' && *q != U')') q ++;   // collect parameter name
+				int expressionDepth = 0;
+				for (; *p; p ++) {
+					if (*p == U',') {
+						if (expressionDepth == 0) break;   // depth-0 comma ends expression
+						MelderString_appendCharacter (& argument, U',');
+					} else if (*p == U')') {
+						if (expressionDepth == 0) break;   // depth-0 closing parenthesis ends expression
+						expressionDepth --;
+						MelderString_appendCharacter (& argument, U')');
+					} else if (*p == U'(') {
+						expressionDepth ++;
+						MelderString_appendCharacter (& argument, U'(');
+					} else if (*p == U'\"') {
+						/*
+						 * Enter a string literal.
+						 */
+						MelderString_appendCharacter (& argument, U'\"');
+						p ++;
+						for (;; p ++) {
+							if (*p == U'\0') {
+								Melder_throw (U"Incomplete string literal: the quotes don't match.");
+							} else if (*p == U'\"') {
+								MelderString_appendCharacter (& argument, U'\"');
+								if (p [1] == '\"') {
+									p ++;   // stay in the string literal
+									MelderString_appendCharacter (& argument, U'\"');
+								} else {
+									break;
+								}
+							} else {
+								MelderString_appendCharacter (& argument, *p);
+							}
+						}
+					} else {
+						MelderString_appendCharacter (& argument, *p);
+					}
+				}
+				if (q == parameterName) break;
+				if (*p) { *p = U'\0'; p ++; }
+				if (q [-1] == U'$') {
+					char32 *value;
+					my callDepth --;
+					Interpreter_stringExpression (me, argument.string, & value);
+					my callDepth ++;
+					char32 save = *q; *q = U'\0';
+					InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
+					Melder_free (var -> stringValue);
+					var -> stringValue = value;
+				} else if (q [-1] == U'#') {
+					if (q [-2] == U'#') {
+						nummat value;
+						bool owned;
+						my callDepth --;
+						Interpreter_numericMatrixExpression (me, argument.string, & value, & owned);
+						my callDepth ++;
+						char32 save = *q; *q = U'\0';
+						InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
+						NumericMatrixVariable_move (var, value, owned);
+					} else {
+						numvec value;
+						bool owned;
+						my callDepth --;
+						Interpreter_numericVectorExpression (me, argument.string, & value, & owned);
+						my callDepth ++;
+						char32 save = *q; *q = U'\0';
+						InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
+						NumericVectorVariable_move (var, value, owned);
+					}
+				} else {
+					double value;
+					my callDepth --;
+					Interpreter_numericExpression (me, argument.string, & value);
+					my callDepth ++;
+					char32 save = *q; *q = U'\0';
+					InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
+					var -> numericValue = value;
+				}
+				if (*q) q ++;   // skip comma
+			}
+			if (callDepth == Interpreter_MAX_CALL_DEPTH)
+				Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
+			callStack [++ callDepth] = lineNumber;
+			lineNumber = iline;
+			break;
+		}
+	}
+	if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
+}
+static void Interpreter_do_oldProcedureCall (Interpreter me, char32 *command,
+	char32 **lines, integer numberOfLines, long& lineNumber, long callStack [], int& callDepth)
+{
+	/*
+		Old type of procedure calls, with space separation, unquoted strings, and no array support.
+	*/
+	char32 *p = command;
+	while (Melder_isblank (*p)) p ++;   // skip whitespace
+	char32 *callName = p;
+	while (*p != U'\0' && *p != U' ' && *p != U'\t' && *p != U'(' && *p != U':') p ++;
+	if (p == callName) Melder_throw (U"Missing procedure name after 'call'.");
+	bool hasArguments = *p != U'\0';
+	*p = U'\0';   // close procedure name
+	integer callLength = str32len (callName);
+	integer iline = 1;
+	for (; iline <= numberOfLines; iline ++) {
+		if (! str32nequ (lines [iline], U"procedure ", 10)) continue;
+		char32 *q = lines [iline] + 10;
+		while (Melder_isblank (*q)) q ++;
+		char32 *procName = q;
+		while (*q != U'\0' && *q != U' ' && *q != U'\t' && *q != U'(' && *q != U':') q ++;
+		if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
+		bool hasParameters = ( *q != U'\0' );
+		if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
+			if (hasArguments && ! hasParameters)
+				Melder_throw (U"Call to procedure \"", callName, U"\" has too many arguments.");
+			if (hasParameters && ! hasArguments)
+				Melder_throw (U"Call to procedure \"", callName, U"\" has too few arguments.");
+			if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
+				Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
+			str32cpy (my procedureNames [my callDepth], callName);
+			if (hasParameters) {
+				bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
+				q ++;   // step over parenthesis or colon or first white space
+				if (! parenthesisOrColonFound) {
+					while (Melder_isblank (*q)) q ++;   // skip more whitespace
+					if (*q == U'(' || *q == U':') q ++;   // step over parenthesis or colon
+				}
+				++ p;   // first argument
+				while (*q && *q != ')') {
+					char32 *par, save;
+					static MelderString arg { };
+					MelderString_empty (& arg);
+					while (Melder_isblank (*p)) p ++;
+					while (*q == U' ' || *q == U'\t' || *q == U',' || *q == U')') q ++;
+					par = q;
+					while (*q != U'\0' && *q != U' ' && *q != U'\t' && *q != U',' && *q != U')') q ++;   // collect parameter name
+					if (*q) {   // does anything follow the parameter name?
+						if (*p == U'\"') {
+							p ++;   // skip initial quote
+							while (*p != U'\0') {
+								if (*p == U'\"') {   // quote signals end-of-string or string-internal quote
+									if (p [1] == U'\"') {   // double quote signals string-internal quote
+										MelderString_appendCharacter (& arg, U'\"');
+										p += 2;   // skip second quote
+									} else {   // single quote signals end-of-string
+										break;
+									}
+								} else {
+									MelderString_appendCharacter (& arg, *p ++);
+								}
+							}
+						} else {
+							while (*p != U'\0' && *p != U' ' && *p != U'\t')
+								MelderString_appendCharacter (& arg, *p ++);   // white space separates
+						}
+						if (*p) { *p = U'\0'; p ++; }
+					} else {   // else rest of line
+						while (*p != '\0')
+							MelderString_appendCharacter (& arg, *p ++);
+					}
+					if (q [-1] == '$') {
+						save = *q; *q = U'\0';
+						InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
+						Melder_free (var -> stringValue);
+						var -> stringValue = Melder_dup_f (arg.string);
+					} else {
+						double value;
+						my callDepth --;
+						Interpreter_numericExpression (me, arg.string, & value);
+						my callDepth ++;
+						save = *q; *q = U'\0';
+						InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
+						var -> numericValue = value;
+					}
+				}
+			}
+			if (callDepth == Interpreter_MAX_CALL_DEPTH)
+				Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
+			callStack [++ callDepth] = lineNumber;
+			lineNumber = iline;
+			break;
+		}
+	}
+	if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
+}
+
+static void assignToNumericVectorElement (Interpreter me, char32 *& p, const char32* vectorName, MelderString& valueString) {
+	long indexValue = 0;
+	static MelderString index { };
+	MelderString_empty (& index);
+	int depth = 0;
+	bool inString = false;
+	while ((depth > 0 || *p != U']' || inString) && *p != U'\n' && *p != U'\0') {
+		MelderString_appendCharacter (& index, *p);
+		if (*p == U'[') {
+			if (! inString) depth ++;
+		} else if (*p == U']') {
+			if (! inString) depth --;
+		}
+		if (*p == U'"') inString = ! inString;
+		p ++;
+	}
+	if (*p == U'\n' || *p == U'\0')
+		Melder_throw (U"Missing closing bracket (]) in array element.");
+	Formula_Result result;
+	Interpreter_anyExpression (me, index.string, & result);
+	if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+		indexValue = lround (result. numericResult);
+	} else {
+		Melder_throw (U"Element index should be numeric.");
+	}
+	p ++;   // step over closing bracket
+	while (Melder_isblank (*p)) p ++;
+	if (*p != U'=')
+		Melder_throw (U"Missing '=' after vector element ", vectorName, U" [", index.string, U"].");
+	p ++;   // step over equals sign
+	while (Melder_isblank (*p)) p ++;   // go to first token after assignment
+	if (*p == U'\0') {
+		Melder_throw (U"Missing expression after vector element ", vectorName, U" [", index.string, U"].");
+	}
+	double value;
+	if (isCommand (p)) {
+		/*
+			Get the value of the query.
+		*/
+		MelderString_empty (& valueString);
+		autoMelderDivertInfo divert (& valueString);
+		MelderString_appendCharacter (& valueString, 1);   // will be overwritten by something totally different if any MelderInfo function is called...
+		int status = praat_executeCommand (me, p);
+		if (status == 0) {
+			value = undefined;
+		} else if (valueString.string [0] == 1) {   // ...not overwritten by any MelderInfo function? then the return value will be the selected object
+			int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
+			WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
+			if (numberOfSelectedObjects > 1) {
+				Melder_throw (U"Multiple objects selected. Cannot assign object ID to vector element.");
+			} else if (numberOfSelectedObjects == 0) {
+				Melder_throw (U"No objects selected. Cannot assign object ID to vector element.");
+			} else {
+				value = theCurrentPraatObjects -> list [selectedObject]. id;
+			}
+		} else {
+			value = Melder_atof (valueString.string);   // including --undefined--
+		}
+	} else {
+		/*
+			Get the value of the formula.
+		*/
+		Interpreter_numericExpression (me, p, & value);
+	}
+	InterpreterVariable var = Interpreter_hasVariable (me, vectorName);
+	if (! var)
+		Melder_throw (U"Vector ", vectorName, U" does not exist.");
+	if (indexValue < 1)
+		Melder_throw (U"A vector index cannot be less than 1 (the index you supplied is ", indexValue, U").");
+	if (indexValue > var -> numericVectorValue.size)
+		Melder_throw (U"A vector index cannot be greater than the number of elements (here ",
+			var -> numericVectorValue.size, U"). The index you supplied is ", indexValue, U".");
+	var -> numericVectorValue.at [indexValue] = value;
+}
+
+static void assignToNumericMatrixElement (Interpreter me, char32 *& p, const char32* matrixName, MelderString& valueString) {
+	long rowNumber = 0, columnNumber = 0;
+	/*
+		Get the row number.
+	*/
+	static MelderString rowFormula { };
+	MelderString_empty (& rowFormula);
+	int depth = 0;
+	bool inString = false;
+	while ((depth > 0 || *p != U',' || inString) && *p != U'\n' && *p != U'\0') {
+		MelderString_appendCharacter (& rowFormula, *p);
+		if (*p == U'[' || *p == U'(') {
+			if (! inString) depth ++;
+		} else if (*p == U']' || *p == U')') {
+			if (! inString) depth --;
+		}
+		if (*p == U'"') inString = ! inString;
+		p ++;
+	}
+	if (*p == U'\n' || *p == U'\0')
+		Melder_throw (U"Missing comma in matrix indexing.");
+	Formula_Result result;
+	Interpreter_anyExpression (me, rowFormula.string, & result);
+	if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+		rowNumber = lround (result. numericResult);
+	} else {
+		Melder_throw (U"Row number should be numeric.");
+	}
+	p ++;   // step over comma
+	/*
+		Get the column number.
+	*/
+	static MelderString columnFormula { };
+	MelderString_empty (& columnFormula);
+	depth = 0;
+	inString = false;
+	while ((depth > 0 || *p != U']' || inString) && *p != U'\n' && *p != U'\0') {
+		MelderString_appendCharacter (& columnFormula, *p);
+		if (*p == U'[') {
+			if (! inString) depth ++;
+		} else if (*p == U']') {
+			if (! inString) depth --;
+		}
+		if (*p == U'"') inString = ! inString;
+		p ++;
+	}
+	if (*p == U'\n' || *p == U'\0')
+		Melder_throw (U"Missing closing bracket (]) in matrix indexing.");
+	Interpreter_anyExpression (me, columnFormula.string, & result);
+	if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+		columnNumber = lround (result. numericResult);
+	} else {
+		Melder_throw (U"Column number should be numeric.");
+	}
+	p ++;   // step over closing bracket
+	while (Melder_isblank (*p)) p ++;
+	if (*p != U'=')
+		Melder_throw (U"Missing '=' after matrix element ", matrixName, U" [",
+			rowFormula.string, U",", columnFormula.string, U"].");
+	p ++;   // step over equals sign
+	while (Melder_isblank (*p)) p ++;   // go to first token after assignment
+	if (*p == U'\0') {
+		Melder_throw (U"Missing expression after matrix element ", matrixName, U" [",
+			rowFormula.string, U",", columnFormula.string, U"].");
+	}
+	double value;
+	if (isCommand (p)) {
+		/*
+			Get the value of the query.
+		*/
+		MelderString_empty (& valueString);
+		autoMelderDivertInfo divert (& valueString);
+		MelderString_appendCharacter (& valueString, 1);   // will be overwritten by something totally different if any MelderInfo function is called...
+		int status = praat_executeCommand (me, p);
+		if (status == 0) {
+			value = undefined;
+		} else if (valueString.string [0] == 1) {   // ...not overwritten by any MelderInfo function? then the return value will be the selected object
+			int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
+			WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
+			if (numberOfSelectedObjects > 1) {
+				Melder_throw (U"Multiple objects selected. Cannot assign object ID to matrix element.");
+			} else if (numberOfSelectedObjects == 0) {
+				Melder_throw (U"No objects selected. Cannot assign object ID to matrix element.");
+			} else {
+				value = theCurrentPraatObjects -> list [selectedObject]. id;
+			}
+		} else {
+			value = Melder_atof (valueString.string);   // including --undefined--
+		}
+	} else {
+		Interpreter_numericExpression (me, p, & value);
+	}
+	InterpreterVariable var = Interpreter_hasVariable (me, matrixName);
+	if (! var)
+		Melder_throw (U"Matrix ", matrixName, U" does not exist.");
+	if (rowNumber < 1)
+		Melder_throw (U"A row number cannot be less than 1 (the row number you supplied is ", rowNumber, U").");
+	if (rowNumber > var -> numericMatrixValue. nrow)
+		Melder_throw (U"A row number cannot be greater than the number of rows (here ",
+			var -> numericMatrixValue. nrow, U"). The row number you supplied is ", rowNumber, U".");
+	if (columnNumber < 1)
+		Melder_throw (U"A column number cannot be less than 1 (the column number you supplied is ", columnNumber, U").");
+	if (columnNumber > var -> numericMatrixValue. ncol)
+		Melder_throw (U"A column number cannot be greater than the number of columns (here ",
+			var -> numericMatrixValue. ncol, U"). The column number you supplied is ", columnNumber, U".");
+	var -> numericMatrixValue.at [rowNumber] [columnNumber] = value;
+}
+
 void Interpreter_run (Interpreter me, char32 *text) {
 	autoNUMvector <char32 *> lines;   // not autostringvector, because the elements are reference copies
 	long lineNumber = 0;
@@ -732,18 +1383,18 @@ void Interpreter_run (Interpreter me, char32 *text) {
 		 */
 		lines.reset (1, numberOfLines);
 		for (lineNumber = 1, command = text; lineNumber <= numberOfLines; lineNumber ++, command += str32len (command) + 1 + chopped) {
-			int length;
 			while (Melder_isblank (*command) || *command == UNICODE_NO_BREAK_SPACE) command ++;   // nbsp can occur for scripts copied from the manual
-			length = str32len (command);
 			/*
 			 * Chop trailing spaces?
 			 */
-			/*chopped = 0;
-			while (length > 0) { char kar = command [-- length]; if (kar != ' ' && kar != '\t') break; command [length] = '\0'; chopped ++; }*/
+			#if 0
+				chopped = 0;
+				int length = str32len (command);
+				while (length > 0) { char kar = command [-- length]; if (kar != ' ' && kar != '\t') break; command [length] = '\0'; chopped ++; }
+			#endif
 			lines [lineNumber] = command;
 			if (str32nequ (command, U"label ", 6)) {
-				int ilabel;
-				for (ilabel = 1; ilabel <= my numberOfLabels; ilabel ++)
+				for (integer ilabel = 1; ilabel <= my numberOfLabels; ilabel ++)
 					if (str32equ (command + 6, my labelNames [ilabel]))
 						Melder_throw (U"Duplicate label \"", command + 6, U"\".");
 				if (my numberOfLabels >= Interpreter_MAXNUM_LABELS)
@@ -869,7 +1520,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 					*s = U'\0';   // trailing null byte
 					colon = str32chr (varName, U':');
 					if (colon) {
-						precision = a32tol (colon + 1);
+						precision = Melder_atoi (colon + 1);
 						if (str32chr (colon + 1, U'%')) percent = true;
 						*colon = '\0';
 					}
@@ -904,151 +1555,8 @@ void Interpreter_run (Interpreter me, char32 *text) {
 						fail = true;
 						break;
 					case U'@':
-					{
-						/*
-						 * This is a function call.
-						 * Look for a function name.
-						 */
-						char32 *p = command2.string + 1;
-						while (Melder_isblank (*p)) p ++;   // skip whitespace
-						char32 *callName = p;
-						while (*p != U'\0' && *p != U' ' && *p != U'\t' && *p != U'(' && *p != U':') p ++;
-						if (p == callName) Melder_throw (U"Missing procedure name after \"@\".");
-						bool hasArguments = ( *p != U'\0' );
-						if (hasArguments) {
-							bool parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
-							*p = U'\0';   // close procedure name
-							if (! parenthesisOrColonFound) {
-								p ++;   // step over first white space
-								while (Melder_isblank (*p)) p ++;   // skip more whitespace
-								hasArguments = ( *p != U'\0' );
-								parenthesisOrColonFound = ( *p == U'(' || *p == U':' );
-								if (hasArguments && ! parenthesisOrColonFound)
-									Melder_throw (U"Missing parenthesis or colon after procedure name \"", callName, U"\".");
-							}
-							p ++;   // step over parenthesis or colon
-						}
-						int64 callLength = str32len (callName);
-						long iline = 1;
-						for (; iline <= numberOfLines; iline ++) {
-							char32 *linei = lines [iline], *q;
-							if (linei [0] != U'p' || linei [1] != U'r' || linei [2] != U'o' || linei [3] != U'c' ||
-								linei [4] != U'e' || linei [5] != U'd' || linei [6] != U'u' || linei [7] != U'r' ||
-								linei [8] != U'e' || linei [9] != U' ') continue;
-							q = lines [iline] + 10;
-							while (Melder_isblank (*q)) q ++;   // skip whitespace before procedure name
-							char32 *procName = q;
-							while (*q != U'\0' && ! Melder_isblank (*q) && *q != U'(' && *q != U':') q ++;
-							if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
-							if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
-								/*
-								 * We found the procedure definition.
-								 */
-								if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
-									Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
-								str32cpy (my procedureNames [my callDepth], callName);
-								bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
-								if (*q) q ++;   // step over parenthesis or colon or first white space
-								if (! parenthesisOrColonFound) {
-									while (Melder_isblank (*q)) q ++;   // skip more whitespace
-									if (*q == U'(' || *q == U':') q ++;   // step over parenthesis or colon
-								}
-								while (*q && *q != U')') {
-									static MelderString argument { };
-									MelderString_empty (& argument);
-									while (Melder_isblank (*p)) p ++;
-									while (Melder_isblank (*q)) q ++;
-									char32 *parameterName = q;
-									while (*q != U'\0' && ! Melder_isblank (*q) && *q != U',' && *q != U')') q ++;   // collect parameter name
-									int expressionDepth = 0;
-									for (; *p; p ++) {
-										if (*p == U',') {
-											if (expressionDepth == 0) break;   // depth-0 comma ends expression
-											MelderString_appendCharacter (& argument, U',');
-										} else if (*p == U')') {
-											if (expressionDepth == 0) break;   // depth-0 closing parenthesis ends expression
-											expressionDepth --;
-											MelderString_appendCharacter (& argument, U')');
-										} else if (*p == U'(') {
-											expressionDepth ++;
-											MelderString_appendCharacter (& argument, U'(');
-										} else if (*p == U'\"') {
-											/*
-											 * Enter a string literal.
-											 */
-											MelderString_appendCharacter (& argument, U'\"');
-											p ++;
-											for (;; p ++) {
-												if (*p == U'\0') {
-													Melder_throw (U"Incomplete string literal: the quotes don't match.");
-												} else if (*p == U'\"') {
-													MelderString_appendCharacter (& argument, U'\"');
-													if (p [1] == '\"') {
-														p ++;   // stay in the string literal
-														MelderString_appendCharacter (& argument, U'\"');
-													} else {
-														break;
-													}
-												} else {
-													MelderString_appendCharacter (& argument, *p);
-												}
-											}
-										} else {
-											MelderString_appendCharacter (& argument, *p);
-										}
-									}
-									if (q == parameterName) break;
-									if (*p) { *p = U'\0'; p ++; }
-									if (q [-1] == U'$') {
-										char32 *value;
-										my callDepth --;
-										Interpreter_stringExpression (me, argument.string, & value);
-										my callDepth ++;
-										char32 save = *q; *q = U'\0';
-										InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
-										Melder_free (var -> stringValue);
-										var -> stringValue = value;
-									} else if (q [-1] == U'#') {
-										if (q [-2] == U'#') {
-											nummat value;
-											my callDepth --;
-											Interpreter_numericMatrixExpression (me, argument.string, & value);
-											my callDepth ++;
-											char32 save = *q; *q = U'\0';
-											InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
-											var -> numericMatrixValue. reset();
-											var -> numericMatrixValue = value;
-										} else {
-											numvec value;
-											my callDepth --;
-											Interpreter_numericVectorExpression (me, argument.string, & value);
-											my callDepth ++;
-											char32 save = *q; *q = U'\0';
-											InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
-											var -> numericVectorValue. reset();
-											var -> numericVectorValue = value;
-										}
-									} else {
-										double value;
-										my callDepth --;
-										Interpreter_numericExpression (me, argument.string, & value);
-										my callDepth ++;
-										char32 save = *q; *q = U'\0';
-										InterpreterVariable var = Interpreter_lookUpVariable (me, parameterName); *q = save;
-										var -> numericValue = value;
-									}
-									if (*q) q ++;   // skip comma
-								}
-								if (callDepth == Interpreter_MAX_CALL_DEPTH)
-									Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
-								callStack [++ callDepth] = lineNumber;
-								lineNumber = iline;
-								break;
-							}
-						}
-						if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
+						Interpreter_do_procedureCall (me, command2.string + 1, lines.peek(), numberOfLines, lineNumber, callStack, callDepth);
 						break;
-					}
 					case U'a':
 						if (str32nequ (command2.string, U"assert ", 7)) {
 							double value;
@@ -1068,101 +1576,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 						break;
 					case U'c':
 						if (str32nequ (command2.string, U"call ", 5)) {
-							char32 *p = command2.string + 5, *callName, *procName;
-							long iline;
-							bool hasArguments;
-							int64 callLength;
-							while (Melder_isblank (*p)) p ++;   // skip whitespace
-							callName = p;
-							while (*p != U'\0' && *p != U' ' && *p != U'\t' && *p != U'(' && *p != U':') p ++;
-							if (p == callName) Melder_throw (U"Missing procedure name after 'call'.");
-							hasArguments = *p != U'\0';
-							*p = U'\0';   // close procedure name
-							callLength = str32len (callName);
-							for (iline = 1; iline <= numberOfLines; iline ++) {
-								char32 *linei = lines [iline], *q;
-								int hasParameters;
-								if (linei [0] != U'p' || linei [1] != U'r' || linei [2] != U'o' || linei [3] != U'c' ||
-									linei [4] != U'e' || linei [5] != U'd' || linei [6] != U'u' || linei [7] != U'r' ||
-									linei [8] != U'e' || linei [9] != U' ') continue;
-								q = lines [iline] + 10;
-								while (Melder_isblank (*q)) q ++;
-								procName = q;
-								while (*q != U'\0' && *q != U' ' && *q != U'\t' && *q != U'(' && *q != U':') q ++;
-								if (q == procName) Melder_throw (U"Missing procedure name after 'procedure'.");
-								hasParameters = *q != U'\0';
-								if (q - procName == callLength && str32nequ (procName, callName, callLength)) {
-									if (hasArguments && ! hasParameters)
-										Melder_throw (U"Call to procedure \"", callName, U"\" has too many arguments.");
-									if (hasParameters && ! hasArguments)
-										Melder_throw (U"Call to procedure \"", callName, U"\" has too few arguments.");
-									if (++ my callDepth > Interpreter_MAX_CALL_DEPTH)
-										Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
-									str32cpy (my procedureNames [my callDepth], callName);
-									if (hasParameters) {
-										bool parenthesisOrColonFound = ( *q == U'(' || *q == U':' );
-										q ++;   // step over parenthesis or colon or first white space
-										if (! parenthesisOrColonFound) {
-											while (Melder_isblank (*q)) q ++;   // skip more whitespace
-											if (*q == U'(' || *q == U':') q ++;   // step over parenthesis or colon
-										}
-										++ p;   // first argument
-										while (*q && *q != ')') {
-											char32 *par, save;
-											static MelderString arg { };
-											MelderString_empty (& arg);
-											while (Melder_isblank (*p)) p ++;
-											while (*q == U' ' || *q == U'\t' || *q == U',' || *q == U')') q ++;
-											par = q;
-											while (*q != U'\0' && *q != U' ' && *q != U'\t' && *q != U',' && *q != U')') q ++;   // collect parameter name
-											if (*q) {   // does anything follow the parameter name?
-												if (*p == U'\"') {
-													p ++;   // skip initial quote
-													while (*p != U'\0') {
-														if (*p == U'\"') {   // quote signals end-of-string or string-internal quote
-															if (p [1] == U'\"') {   // double quote signals string-internal quote
-																MelderString_appendCharacter (& arg, U'\"');
-																p += 2;   // skip second quote
-															} else {   // single quote signals end-of-string
-																break;
-															}
-														} else {
-															MelderString_appendCharacter (& arg, *p ++);
-														}
-													}
-												} else {
-													while (*p != U'\0' && *p != U' ' && *p != U'\t')
-														MelderString_appendCharacter (& arg, *p ++);   // white space separates
-												}
-												if (*p) { *p = U'\0'; p ++; }
-											} else {   // else rest of line
-												while (*p != '\0')
-													MelderString_appendCharacter (& arg, *p ++);
-											}
-											if (q [-1] == '$') {
-												save = *q; *q = U'\0';
-												InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
-												Melder_free (var -> stringValue);
-												var -> stringValue = Melder_dup_f (arg.string);
-											} else {
-												double value;
-												my callDepth --;
-												Interpreter_numericExpression (me, arg.string, & value);
-												my callDepth ++;
-												save = *q; *q = U'\0';
-												InterpreterVariable var = Interpreter_lookUpVariable (me, par); *q = save;
-												var -> numericValue = value;
-											}
-										}
-									}
-									if (callDepth == Interpreter_MAX_CALL_DEPTH)
-										Melder_throw (U"Call depth greater than ", Interpreter_MAX_CALL_DEPTH, U".");
-									callStack [++ callDepth] = lineNumber;
-									lineNumber = iline;
-									break;
-								}
-							}
-							if (iline > numberOfLines) Melder_throw (U"Procedure \"", callName, U"\" not found.");
+							Interpreter_do_oldProcedureCall (me, command2.string + 5, lines.peek(), numberOfLines, lineNumber, callStack, callDepth);
 						} else fail = true;
 						break;
 					case U'd':
@@ -1210,7 +1624,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							long iline;
 							for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
 								if (str32nequ (lines [iline], U"endif", 5) && wordEnd (lines [iline] [5])) {
-									if (depth == 0) { lineNumber = iline; break; }   // go after 'endif'
+									if (depth == 0) { lineNumber = iline; break; }   // go after `endif`
 									else depth --;
 								} else if (str32nequ (lines [iline], U"if ", 3)) {
 									depth ++;
@@ -1227,10 +1641,10 @@ void Interpreter_run (Interpreter me, char32 *text) {
 									long iline;
 									for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
 										if (str32nequ (lines [iline], U"endif", 5) && wordEnd (lines [iline] [5])) {
-											if (depth == 0) { lineNumber = iline; break; }   // go after 'endif'
+											if (depth == 0) { lineNumber = iline; break; }   // go after `endif`
 											else depth --;
 										} else if (str32nequ (lines [iline], U"else", 4) && wordEnd (lines [iline] [4])) {
-											if (depth == 0) { lineNumber = iline; break; }   // go after 'else'
+											if (depth == 0) { lineNumber = iline; break; }   // go after `else`
 										} else if ((str32nequ (lines [iline], U"elsif", 5) && wordEnd (lines [iline] [5]))
 											|| (str32nequ (lines [iline], U"elif", 4) && wordEnd (lines [iline] [4]))) {
 											if (depth == 0) { lineNumber = iline - 1; fromif = true; break; }   // go at next 'elsif' or 'elif'
@@ -1245,7 +1659,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								long iline;
 								for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
 									if (str32nequ (lines [iline], U"endif", 5) && wordEnd (lines [iline] [5])) {
-										if (depth == 0) { lineNumber = iline; break; }   /* Go after 'endif'. */
+										if (depth == 0) { lineNumber = iline; break; }   // go after `endif`
 										else depth --;
 									} else if (str32nequ (lines [iline], U"if ", 3)) {
 										depth ++;
@@ -1255,7 +1669,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							}
 						} else if (str32nequ (command2.string, U"exit", 4)) {
 							if (command2.string [4] == U'\0') {
-								lineNumber = numberOfLines;   /* Go after end. */
+								lineNumber = numberOfLines;   // go after end
 							} else if (command2.string [4] == U' ') {
 								Melder_throw (command2.string + 5);
 							} else fail = true;
@@ -1382,14 +1796,14 @@ void Interpreter_run (Interpreter me, char32 *text) {
 						break;
 					case U'p':
 						if (str32nequ (command2.string, U"procedure ", 10)) {
-							long iline = lineNumber + 1;
+							integer iline = lineNumber + 1;
 							for (; iline <= numberOfLines; iline ++) {
 								if (str32nequ (lines [iline], U"endproc", 7) && wordEnd (lines [iline] [7])) {
 									lineNumber = iline;
 									break;
-								}   // go after 'endproc'
+								}   // go after `endproc`
 							}
-							if (iline > numberOfLines) Melder_throw (U"Unmatched 'proc'.");
+							if (iline > numberOfLines) Melder_throw (U"Unmatched 'procedure'.");
 						} else if (str32nequ (command2.string, U"print", 5)) {
 							/*
 							 * Make sure that lines like "print = 3" will not be regarded as assignments.
@@ -1409,7 +1823,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 						break;
 					case U's':
 						if (str32nequ (command2.string, U"stopwatch", 9) && wordEnd (command2.string [9])) {
-							(void) Melder_stopwatch ();   /* Reset stopwatch. */
+							(void) Melder_stopwatch ();   // reset stopwatch
 						} else fail = true;
 						break;
 					case U't':
@@ -1421,10 +1835,10 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							Interpreter_numericExpression (me, command2.string + 6, & value);
 							if (value == 0.0) {
 								int depth = 0;
-								long iline;
-								for (iline = lineNumber - 1; iline > 0; iline --) {
+								integer iline = lineNumber - 1;
+								for (; iline > 0; iline --) {
 									if (str32nequ (lines [iline], U"repeat", 6) && wordEnd (lines [iline] [6])) {
-										if (depth == 0) { lineNumber = iline; break; }   // go after 'repeat'
+										if (depth == 0) { lineNumber = iline; break; }   // go after `repeat`
 										else depth --;
 									} else if (str32nequ (lines [iline], U"until ", 6)) {
 										depth ++;
@@ -1443,10 +1857,10 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							Interpreter_numericExpression (me, command2.string + 6, & value);
 							if (value == 0.0) {
 								int depth = 0;
-								long iline;
-								for (iline = lineNumber + 1; iline <= numberOfLines; iline ++) {
+								integer iline = lineNumber + 1;
+								for (; iline <= numberOfLines; iline ++) {
 									if (str32nequ (lines [iline], U"endwhile", 8) && wordEnd (lines [iline] [8])) {
-										if (depth == 0) { lineNumber = iline; break; }   // go after 'endwhile'
+										if (depth == 0) { lineNumber = iline; break; }   // go after `endwhile`
 										else depth --;
 									} else if (str32nequ (lines [iline], U"while ", 6)) {
 										depth ++;
@@ -1469,30 +1883,29 @@ void Interpreter_run (Interpreter me, char32 *text) {
 				}
 				if (fail) {
 					/*
-					 * Found an unknown word starting with a lower-case letter, optionally preceded by a period.
-					 * See whether the word is a variable name.
-					 */
+						Found an unknown word starting with a lower-case letter, optionally preceded by a period.
+						See whether the word is a variable name.
+					*/
 					trace (U"found an unknown word starting with a lower-case letter, optionally preceded by a period");
 					char32 *p = & command2.string [0];
 					/*
-					 * Variable names consist of a sequence of letters, digits, and underscores,
-					 * optionally preceded by a period and optionally followed by a $ and/or #.
-					 */
+						Variable names consist of a sequence of letters, digits, and underscores,
+						optionally preceded by a period and optionally followed by a $ and/or #.
+					*/
 					if (*p == U'.') p ++;
 					while (isalnum ((int) *p) || *p == U'_' || *p == U'.')  p ++;
 					if (*p == U'$') {
 						/*
-						 * Assign to a string variable.
-						 */
+							Assign to a string variable.
+						*/
 						trace (U"detected an assignment to a string variable");
 						char32 *endOfVariable = ++ p;
 						char32 *variableName = command2.string;
-						int withFile;
 						while (Melder_isblank (*p)) p ++;   // go to first token after variable name
 						if (*p == U'[') {
 							/*
-							 * This must be an assignment to an indexed string variable.
-							 */
+								This must be an assignment to an indexed string variable.
+							*/
 							*endOfVariable = U'\0';
 							static MelderString indexedVariableName { };
 							MelderString_copy (& indexedVariableName, command2.string, U"[");
@@ -1517,11 +1930,11 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								Formula_Result result;
 								Interpreter_anyExpression (me, index.string, & result);
 								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
-									double numericIndexValue = result.result.numericResult;
+									double numericIndexValue = result. numericResult;
 									MelderString_append (& indexedVariableName, numericIndexValue);
 								} else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
-									MelderString_append (& indexedVariableName, U"\"", result.result.stringResult, U"\"");
-									Melder_free (result.result.stringResult);
+									MelderString_append (& indexedVariableName, U"\"", result. stringResult, U"\"");
+									Melder_free (result. stringResult);
 								}
 								MelderString_appendCharacter (& indexedVariableName, *p);
 								if (*p == U']') {
@@ -1532,6 +1945,7 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							p ++;   // skip closing bracket
 						}
 						while (Melder_isblank (*p)) p ++;   // go to first token after (perhaps indexed) variable name
+						int withFile;   // 0, 1, 2 or 3
 						if (*p == U'=') {
 							withFile = 0;   // assignment
 						} else if (*p == U'<') {
@@ -1572,8 +1986,8 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							}
 						} else if (isCommand (p)) {
 							/*
-							 * Example: name$ = Get name
-							 */
+								Statement like: name$ = Get name
+							*/
 							MelderString_empty (& valueString);   // empty because command may print nothing; also makes sure that valueString.string exists
 							autoMelderDivertInfo divert (& valueString);
 							int status = praat_executeCommand (me, p);
@@ -1582,13 +1996,13 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							var -> stringValue = Melder_dup (status ? valueString.string : U"");
 						} else {
 							/*
-							 * Evaluate a string expression and assign the result to the variable.
-							 * Examples:
-							 *    sentence$ = subject$ + verb$ + object$
-							 *    extension$ = if index (file$, ".") <> 0
-							 *       ... then right$ (file$, length (file$) - rindex (file$, "."))
-							 *       ... else "" fi
-							 */
+								Evaluate a string expression and assign the result to the variable.
+								Statements like:
+									sentence$ = subject$ + verb$ + object$
+									extension$ = if index (file$, ".") <> 0
+									... then right$ (file$, length (file$) - rindex (file$, "."))
+									... else "" fi
+							*/
 							char32 *stringValue;
 							trace (U"evaluating string expression");
 							Interpreter_stringExpression (me, p, & stringValue);
@@ -1618,96 +2032,91 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								if (*p == U'\0')
 									Melder_throw (U"Missing right-hand expression in assignment to matrix ", matrixName.string, U".");
 								nummat value;
-								Interpreter_numericMatrixExpression (me, p, & value);
+								bool owned;
+								Interpreter_numericMatrixExpression (me, p, & value, & owned);
 								InterpreterVariable var = Interpreter_lookUpVariable (me, matrixName.string);
-								NUMmatrix_free (var -> numericMatrixValue.at, 1, 1);
-								var -> numericMatrixValue = value;
+								NumericMatrixVariable_move (var, value, owned);
 							} else if (*p == U'[') {
-								/*
-								 * This must be an assignment to an element of the matrix variable.
-								 */
-								long rowNumber = 0, columnNumber = 0;
-								p ++;   // step over opening bracket
-								/*
-									Get the row number.
-								*/
-								static MelderString rowFormula { };
-								MelderString_empty (& rowFormula);
-								int depth = 0;
-								bool inString = false;
-								while ((depth > 0 || *p != U',' || inString) && *p != U'\n' && *p != U'\0') {
-									MelderString_appendCharacter (& rowFormula, *p);
-									if (*p == U'[' || *p == U'(') {
-										if (! inString) depth ++;
-									} else if (*p == U']' || *p == U')') {
-										if (! inString) depth --;
-									}
-									if (*p == U'"') inString = ! inString;
-									p ++;
+								assignToNumericMatrixElement (me, ++ p, matrixName.string, valueString);
+							} else if (*p == U'+' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
+								if (! var)
+									Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
+									              U"You can increment (+=) only existing matrices.");
+								Formula_Result result;
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
+									NumericMatrixVariable_add (var, result. numericMatrixResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericMatrixVariable_add (var, result. numericResult);
+								} else {
+									Melder_throw (U"You can increment (+=) a numeric matrix only with a number or another numeric matrix.");
 								}
-								if (*p == U'\n' || *p == U'\0')
-									Melder_throw (U"Missing comma in matrix indexing.");
+							} else if (*p == U'-' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
+								if (! var)
+									Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
+									              U"You can decrement (-=) only existing matrices.");
 								Formula_Result result;
-								Interpreter_anyExpression (me, rowFormula.string, & result);
-								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
-									rowNumber = lround (result.result.numericResult);
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
+									NumericMatrixVariable_subtract (var, result. numericMatrixResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericMatrixVariable_subtract (var, result. numericResult);
 								} else {
-									Melder_throw (U"Row number should be numeric.");
+									Melder_throw (U"You can decrement (-=) a numeric matrix only with a number or another numeric matrix.");
 								}
-
-								p ++;   // step over comma
-								/*
-									Get the column number.
-								*/
-								static MelderString columnFormula { };
-								MelderString_empty (& columnFormula);
-								depth = 0;
-								inString = false;
-								while ((depth > 0 || *p != U']' || inString) && *p != U'\n' && *p != U'\0') {
-									MelderString_appendCharacter (& columnFormula, *p);
-									if (*p == U'[') {
-										if (! inString) depth ++;
-									} else if (*p == U']') {
-										if (! inString) depth --;
-									}
-									if (*p == U'"') inString = ! inString;
-									p ++;
+							} else if (*p == U'*' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
+								if (! var)
+									Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
+									              U"You can multiply (*=) only existing matrices.");
+								Formula_Result result;
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
+									NumericMatrixVariable_multiply (var, result. numericMatrixResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericMatrixVariable_multiply (var, result. numericResult);
+								} else {
+									Melder_throw (U"You can multiply (*=) a numeric matrix only with a number or another numeric matrix.");
 								}
-								if (*p == U'\n' || *p == U'\0')
-									Melder_throw (U"Missing closing bracket (]) in matrix indexing.");
-								Interpreter_anyExpression (me, columnFormula.string, & result);
-								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
-									columnNumber = lround (result.result.numericResult);
+							} else if (*p == U'/' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
+								if (! var)
+									Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
+									              U"You can divide (/=) only existing matrices.");
+								Formula_Result result;
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX) {
+									NumericMatrixVariable_divide (var, result. numericMatrixResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericMatrixVariable_divide (var, result. numericResult);
 								} else {
-									Melder_throw (U"Column number should be numeric.");
+									Melder_throw (U"You can divide (/=) a numeric matrix only with a number or another numeric matrix.");
 								}
-								p ++;   // step over closing bracket
-								while (Melder_isblank (*p)) p ++;
-								if (*p != U'=')
-									Melder_throw (U"Missing '=' after matrix element ", matrixName.string, U" [",
-										rowFormula.string, U",", columnFormula.string, U"].");
-								p ++;   // step over equals sign
+							} else if (*p == U'~') {
+								/*
+									This must be a formula assignment to a matrix variable.
+								*/
+								p ++;   // step over tilde
 								while (Melder_isblank (*p)) p ++;   // go to first token after assignment
-								if (*p == U'\0') {
-									Melder_throw (U"Missing expression after matrix element ", matrixName.string, U" [",
-										rowFormula.string, U",", columnFormula.string, U"].");
-								}
-								double value;
-								Interpreter_numericExpression (me, p, & value);
+								if (*p == U'\0')
+									Melder_throw (U"Missing formula expression for matrix ", matrixName.string, U".");
 								InterpreterVariable var = Interpreter_hasVariable (me, matrixName.string);
 								if (! var)
-									Melder_throw (U"Matrix ", matrixName.string, U" does not exist.");
-								if (rowNumber < 1)
-									Melder_throw (U"A row number cannot be less than 1 (the row number you supplied is ", rowNumber, U").");
-								if (rowNumber > var -> numericMatrixValue. nrow)
-									Melder_throw (U"A row number cannot be greater than the number of rows (here ",
-										var -> numericMatrixValue. nrow, U"). The row number you supplied is ", rowNumber, U".");
-								if (columnNumber < 1)
-									Melder_throw (U"A column number cannot be less than 1 (the column number you supplied is ", columnNumber, U").");
-								if (columnNumber > var -> numericMatrixValue. ncol)
-									Melder_throw (U"A column number cannot be greater than the number of columns (here ",
-										var -> numericMatrixValue. ncol, U"). The column number you supplied is ", columnNumber, U".");
-								var -> numericMatrixValue.at [rowNumber] [columnNumber] = value;
+									Melder_throw (U"The matrix ", matrixName.string, U" does not exist.\n"
+										"You can assign a formula only to an existing matrix.");
+								static Matrix matrixObject;
+								if (! matrixObject) {
+									matrixObject = Matrix_createSimple (1, 1). releaseToAmbiguousOwner();   // prevent destruction when program ends
+								}
+								nummat mat = var -> numericMatrixValue;
+								matrixObject -> xmax = mat.ncol + 0.5;
+								matrixObject -> nx = mat.ncol;
+								matrixObject -> ymax = mat.nrow + 0.5;
+								matrixObject -> ny = mat.nrow;
+								matrixObject -> z = mat.at;
+								Matrix_formula (matrixObject, p, me, nullptr);
 							} else Melder_throw (U"Missing '=' after matrix variable ", matrixName.string, U".");
 						} else {
 							/*
@@ -1728,60 +2137,90 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								if (*p == U'\0')
 									Melder_throw (U"Missing right-hand expression in assignment to vector ", vectorName.string, U".");
 								numvec value;
-								Interpreter_numericVectorExpression (me, p, & value);
+								bool owned;
+								Interpreter_numericVectorExpression (me, p, & value, & owned);
 								InterpreterVariable var = Interpreter_lookUpVariable (me, vectorName.string);
-								NUMvector_free (var -> numericVectorValue.at, 1);
-								var -> numericVectorValue = value;
+								NumericVectorVariable_move (var, value, owned);
 							} else if (*p == U'[') {
-								/*
-								 * This must be an assignment to an element of the vector variable.
-								 */
-								long indexValue = 0;
-								p ++;   // step over opening bracket
-								static MelderString index { };
-								MelderString_empty (& index);
-								int depth = 0;
-								bool inString = false;
-								while ((depth > 0 || *p != U']' || inString) && *p != U'\n' && *p != U'\0') {
-									MelderString_appendCharacter (& index, *p);
-									if (*p == U'[') {
-										if (! inString) depth ++;
-									} else if (*p == U']') {
-										if (! inString) depth --;
-									}
-									if (*p == U'"') inString = ! inString;
-									p ++;
+								assignToNumericVectorElement (me, ++ p, vectorName.string, valueString);
+							} else if (*p == U'+' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
+								if (! var)
+									Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
+									              U"You can increment (+=) only existing vectors.");
+								Formula_Result result;
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
+									NumericVectorVariable_add (var, result. numericVectorResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericVectorVariable_add (var, result. numericResult);
+								} else {
+									Melder_throw (U"You can increment (+=) a numeric vector only with a number or another numeric vector.");
 								}
-								if (*p == U'\n' || *p == U'\0')
-									Melder_throw (U"Missing closing bracket (]) in array element.");
+							} else if (*p == U'-' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
+								if (! var)
+									Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
+									              U"You can decrement (-=) only existing vectors.");
 								Formula_Result result;
-								Interpreter_anyExpression (me, index.string, & result);
-								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
-									indexValue = lround (result.result.numericResult);
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
+									NumericVectorVariable_subtract (var, result. numericVectorResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericVectorVariable_subtract (var, result. numericResult);
 								} else {
-									Melder_throw (U"Element index should be numeric.");
+									Melder_throw (U"You can decrement (-=) a numeric vector only with a number or another numeric vector.");
 								}
-								p ++;   // step over closing bracket
-								while (Melder_isblank (*p)) p ++;
-								if (*p != U'=')
-									Melder_throw (U"Missing '=' after vector element ", vectorName.string, U" [", index.string, U"].");
-								p ++;   // step over equals sign
-								while (Melder_isblank (*p)) p ++;   // go to first token after assignment
-								if (*p == U'\0') {
-									Melder_throw (U"Missing expression after vector element ", vectorName.string, U" [", index.string, U"].");
+							} else if (*p == U'*' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
+								if (! var)
+									Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
+									              U"You can multiply (*=) only existing vectors.");
+								Formula_Result result;
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
+									NumericVectorVariable_multiply (var, result. numericVectorResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericVectorVariable_multiply (var, result. numericResult);
+								} else {
+									Melder_throw (U"You can multiply (*=) a numeric vector only with a number or another numeric vector.");
 								}
-								double value;
-								Interpreter_numericExpression (me, p, & value);
+							} else if (*p == U'/' && p [1] == U'=') {
+								InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
+								if (! var)
+									Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
+									              U"You can divide (/=) only existing vectors.");
+								Formula_Result result;
+								Interpreter_anyExpression (me, p += 2, & result);
+								if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR) {
+									NumericVectorVariable_divide (var, result. numericVectorResult, result. owned);
+								} else if (result. expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									NumericVectorVariable_divide (var, result. numericResult);
+								} else {
+									Melder_throw (U"You can divide (/=) a numeric vector only with a number or another numeric vector.");
+								}
+							} else if (*p == U'~') {
+								/*
+									This must be a formula assignment to a vector variable.
+								*/
+								p ++;   // step over tilde
+								while (Melder_isblank (*p)) p ++;   // go to first token after assignment
+								if (*p == U'\0')
+									Melder_throw (U"Missing formula expression for vector ", vectorName.string, U".");
 								InterpreterVariable var = Interpreter_hasVariable (me, vectorName.string);
 								if (! var)
-									Melder_throw (U"Vector ", vectorName.string, U" does not exist.");
-								if (indexValue < 1)
-									Melder_throw (U"A vector index cannot be less than 1 (the index you supplied is ", indexValue, U").");
-								if (indexValue > var -> numericVectorValue.size)
-									Melder_throw (U"A vector index cannot be greater than the number of elements (here ",
-										var -> numericVectorValue.size, U"). The index you supplied is ", indexValue, U".");
-								var -> numericVectorValue.at [indexValue] = value;
-							} else Melder_throw (U"Missing '=' after vector variable ", vectorName.string, U".");
+									Melder_throw (U"The vector ", vectorName.string, U" does not exist.\n"
+										"You can assign a formula only to an existing vector.");
+								static Matrix vectorObject;
+								if (! vectorObject) {
+									vectorObject = Matrix_createSimple (1, 1). releaseToAmbiguousOwner();   // prevent destruction when program ends
+								}
+								numvec vec = var -> numericVectorValue;
+								vectorObject -> xmax = vec.size + 0.5;
+								vectorObject -> nx = vec.size;
+								vectorObject -> z [1] = vec.at;
+								Matrix_formula (vectorObject, p, me, nullptr);
+							} else Melder_throw (U"Missing '=' or '+=' or '[' or '~' after vector variable ", vectorName.string, U".");
 						}
 					} else {
 						/*
@@ -1801,14 +2240,14 @@ void Interpreter_run (Interpreter me, char32 *text) {
 						while (Melder_isblank (*p)) p ++;
 						if (*p == U'=' || ((*p == U'+' || *p == U'-' || *p == U'*' || *p == U'/') && p [1] == U'=')) {
 							/*
-							 * This must be an assignment (though: "echo = ..." ???)
-							 */
+								This must be an assignment (though: "echo = ..." ???)
+							*/
 							typeOfAssignment = *p == U'+' ? 1 : *p == U'-' ? 2 : *p == U'*' ? 3 : *p == U'/' ? 4 : 0;
 							*endOfVariable = U'\0';   // close variable name; FIXME: this can be any weird character, e.g. hallo&
 						} else if (*p == U'[') {
 							/*
-							 * This must be an assignment to an indexed numeric variable.
-							 */
+								This must be an assignment to an indexed numeric variable.
+							*/
 							*endOfVariable = U'\0';
 							static MelderString indexedVariableName { };
 							MelderString_copy (& indexedVariableName, command2.string, U"[");
@@ -1833,11 +2272,11 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								Formula_Result result;
 								Interpreter_anyExpression (me, index.string, & result);
 								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
-									double numericIndexValue = result.result.numericResult;
+									double numericIndexValue = result. numericResult;
 									MelderString_append (& indexedVariableName, numericIndexValue);
 								} else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
-									MelderString_append (& indexedVariableName, U"\"", result.result.stringResult, U"\"");
-									Melder_free (result.result.stringResult);
+									MelderString_append (& indexedVariableName, U"\"", result. stringResult, U"\"");
+									Melder_free (result. stringResult);
 								}
 								MelderString_appendCharacter (& indexedVariableName, *p);
 								if (*p == U']') {
@@ -1852,8 +2291,8 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							}
 						} else {
 							/*
-							 * Not an assignment: perhaps a PraatShell command (select, echo, execute, pause ...).
-							 */
+								Not an assignment: perhaps a PraatShell command (select, echo, execute, pause ...).
+							*/
 							praat_executeCommand (me, variableName);
 							continue;   // next line
 						}
@@ -1861,15 +2300,15 @@ void Interpreter_run (Interpreter me, char32 *text) {
 						while (*p == U' ' || *p == U'\t') p ++;
 						if (*p == U'\0') Melder_throw (U"Missing expression after variable ", variableName, U".");
 						/*
-						 * Three classes of assignments:
-						 *    var = formula
-						 *    var = Query
-						 *    var = Object creation
-						 */
+							Three classes of assignments:
+								var = formula
+								var = Query
+								var = Object creation
+						*/
 						if (isCommand (p)) {
 							/*
-							 * Get the value of the query.
-							 */
+								Get the value of the query.
+							*/
 							MelderString_empty (& valueString);
 							autoMelderDivertInfo divert (& valueString);
 							MelderString_appendCharacter (& valueString, 1);   // will be overwritten by something totally different if any MelderInfo function is called...
@@ -1877,22 +2316,22 @@ void Interpreter_run (Interpreter me, char32 *text) {
 							if (status == 0) {
 								value = undefined;
 							} else if (valueString.string [0] == 1) {   // ...not overwritten by any MelderInfo function? then the return value will be the selected object
-								int IOBJECT, result = 0, found = 0;
-								WHERE (SELECTED) { result = IOBJECT; found += 1; }
-								if (found > 1) {
-									Melder_throw (U"Multiple objects selected. Cannot assign ID to variable.");
-								} else if (found == 0) {
-									Melder_throw (U"No objects selected. Cannot assign ID to variable.");
+								int IOBJECT, selectedObject = 0, numberOfSelectedObjects = 0;
+								WHERE (SELECTED) { selectedObject = IOBJECT; numberOfSelectedObjects += 1; }
+								if (numberOfSelectedObjects > 1) {
+									Melder_throw (U"Multiple objects selected. Cannot assign object ID to variable.");
+								} else if (numberOfSelectedObjects == 0) {
+									Melder_throw (U"No objects selected. Cannot assign object ID to variable.");
 								} else {
-									value = theCurrentPraatObjects -> list [result]. id;
+									value = theCurrentPraatObjects -> list [selectedObject]. id;
 								}
 							} else {
 								value = Melder_atof (valueString.string);   // including --undefined--
 							}
 						} else {
 							/*
-							 * Get the value of the formula.
-							 */
+								Get the value of the formula.
+							*/
 							Interpreter_numericExpression (me, p, & value);
 						}
 						/*
@@ -1900,17 +2339,17 @@ void Interpreter_run (Interpreter me, char32 *text) {
 						 */
 						if (typeOfAssignment == 0) {
 							/*
-							 * Use an existing variable, or create a new one.
-							 */
+								Use an existing variable, or create a new one.
+							*/
 							//Melder_casual (U"looking up variable ", variableName);
 							InterpreterVariable var = Interpreter_lookUpVariable (me, variableName);
 							var -> numericValue = value;
 						} else {
 							/*
-							 * Modify an existing variable.
-							 */
+								Modify an existing variable.
+							*/
 							InterpreterVariable var = Interpreter_hasVariable (me, variableName);
-							if (! var) Melder_throw (U"Unknown variable ", variableName, U".");
+							if (! var) Melder_throw (U"The variable ", variableName, U" does not exist. You can modify only existing variables.");
 							if (isundef (var -> numericValue)) {
 								/* Keep it that way. */
 							} else {
@@ -1997,42 +2436,44 @@ void Interpreter_voidExpression (Interpreter me, const char32 *expression) {
 	Formula_run (0, 0, & result);
 }
 
-void Interpreter_numericExpression (Interpreter me, const char32 *expression, double *value) {
-	Melder_assert (value);
+void Interpreter_numericExpression (Interpreter me, const char32 *expression, double *p_value) {
+	Melder_assert (p_value);
 	if (str32str (expression, U"(=")) {
-		*value = Melder_atof (expression);
+		*p_value = Melder_atof (expression);
 	} else {
 		Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC, false);
 		Formula_Result result;
 		Formula_run (0, 0, & result);
-		*value = result. result.numericResult;
+		*p_value = result. numericResult;
 	}
 }
 
-void Interpreter_numericVectorExpression (Interpreter me, const char32 *expression, numvec *value) {
+void Interpreter_numericVectorExpression (Interpreter me, const char32 *expression, numvec *p_value, bool *p_owned) {
 	Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR, false);
 	Formula_Result result;
 	Formula_run (0, 0, & result);
-	*value = result. result.numericVectorResult;
+	*p_value = result. numericVectorResult;
+	*p_owned = result. owned;
 }
 
-void Interpreter_numericMatrixExpression (Interpreter me, const char32 *expression, nummat *value) {
+void Interpreter_numericMatrixExpression (Interpreter me, const char32 *expression, nummat *p_value, bool *p_owned) {
 	Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX, false);
 	Formula_Result result;
 	Formula_run (0, 0, & result);
-	*value = result. result.numericMatrixResult;
+	*p_value = result. numericMatrixResult;
+	*p_owned = result. owned;
 }
 
-void Interpreter_stringExpression (Interpreter me, const char32 *expression, char32 **value) {
+void Interpreter_stringExpression (Interpreter me, const char32 *expression, char32 **p_value) {
 	Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_STRING, false);
 	Formula_Result result;
 	Formula_run (0, 0, & result);
-	*value = result. result.stringResult;
+	*p_value = result. stringResult;
 }
 
-void Interpreter_anyExpression (Interpreter me, const char32 *expression, Formula_Result *result) {
+void Interpreter_anyExpression (Interpreter me, const char32 *expression, Formula_Result *p_result) {
 	Formula_compile (me, nullptr, expression, kFormula_EXPRESSION_TYPE_UNKNOWN, false);
-	Formula_run (0, 0, result);
+	Formula_run (0, 0, p_result);
 }
 
 /* End of file Interpreter.cpp */
diff --git a/sys/Interpreter.h b/sys/Interpreter.h
index 9a2ace7..f751f91 100644
--- a/sys/Interpreter.h
+++ b/sys/Interpreter.h
@@ -78,11 +78,11 @@ void Interpreter_run (Interpreter me, char32 *text);   // destroys 'text'
 void Interpreter_stop (Interpreter me);   // can be called from any procedure called deep-down by the interpreter; will stop before next line
 
 void Interpreter_voidExpression (Interpreter me, const char32 *expression);
-void Interpreter_numericExpression (Interpreter me, const char32 *expression, double *value);
-void Interpreter_numericVectorExpression (Interpreter me, const char32 *expression, numvec *value);
-void Interpreter_numericMatrixExpression (Interpreter me, const char32 *expression, nummat *value);
-void Interpreter_stringExpression (Interpreter me, const char32 *expression, char32 **value);
-void Interpreter_anyExpression (Interpreter me, const char32 *expression, Formula_Result *result);
+void Interpreter_numericExpression (Interpreter me, const char32 *expression, double *p_value);
+void Interpreter_numericVectorExpression (Interpreter me, const char32 *expression, numvec *p_value, bool *p_owned);
+void Interpreter_numericMatrixExpression (Interpreter me, const char32 *expression, nummat *p_value, bool *p_owned);
+void Interpreter_stringExpression (Interpreter me, const char32 *expression, char32 **p_value);
+void Interpreter_anyExpression (Interpreter me, const char32 *expression, Formula_Result *p_result);
 
 InterpreterVariable Interpreter_hasVariable (Interpreter me, const char32 *key);
 InterpreterVariable Interpreter_lookUpVariable (Interpreter me, const char32 *key);
diff --git a/sys/ManPage.h b/sys/ManPage.h
index d4e040c..ea19b14 100644
--- a/sys/ManPage.h
+++ b/sys/ManPage.h
@@ -2,7 +2,7 @@
 #define _ManPage_h_
 /* ManPage.h
  *
- * Copyright (C) 1996-2011,2015 Paul Boersma
+ * Copyright (C) 1996-2011,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,7 +23,7 @@
 #include "ManPage_enums.h"
 
 typedef struct structManPage_Paragraph {
-	short type;
+	enum kManPage_type type;
 	const char32 *text;
 	double width, height;
 	void (*draw) (Graphics g);
diff --git a/sys/ManPages.cpp b/sys/ManPages.cpp
index b2d32e2..0fcfdc5 100644
--- a/sys/ManPages.cpp
+++ b/sys/ManPages.cpp
@@ -43,7 +43,7 @@ void structManPages :: v_destroy () noexcept {
 			Melder_free (page -> author);
 			if (page -> paragraphs) {
 				ManPage_Paragraph par;
-				for (par = page -> paragraphs; par -> type; par ++)
+				for (par = page -> paragraphs; (int) par -> type != 0; par ++)
 					Melder_free (par -> text);
 				NUMvector_free <struct structManPage_Paragraph> (page -> paragraphs, 0);
 			}
@@ -144,7 +144,7 @@ static void readOnePage (ManPages me, MelderReadText text) {
 	for (;; par ++) {
 		char32 link [501], fileName [256];
 		try {
-			par -> type = texgete8 (text, kManPage_type_getValue);
+			par -> type = (kManPage_type) texgete8 (text, (enum_generic_getValue) kManPage_type_getValue);
 		} catch (MelderError) {
 			if (Melder_hasError (U"end of text")) {
 				Melder_clearError ();
@@ -153,7 +153,7 @@ static void readOnePage (ManPages me, MelderReadText text) {
 				throw;
 			}
 		}
-		if (par -> type == kManPage_type_SCRIPT) {
+		if (par -> type == kManPage_type::SCRIPT) {
 			par -> width = texgetr64 (text);
 			par -> height = texgetr64 (text);
 		}
@@ -315,7 +315,7 @@ static long lookUp_sorted (ManPages me, const char32 *title) {
 }
 
 static void grind (ManPages me) {
-	long ipage, ndangle = 0, jpage, grandNlinks, ilinkHither, ilinkThither;
+	long ndangle = 0, jpage, grandNlinks, ilinkHither, ilinkThither;
 	long *grandLinksHither, *grandLinksThither;
 
 	qsort (& my pages.at [1], my pages.size, sizeof (ManPage), pageCompare);
@@ -324,10 +324,9 @@ static void grind (ManPages me) {
 	 * First pass: count and check links: fill in nlinksHither and nlinksThither.
 	 */
 	grandNlinks = 0;
-	for (ipage = 1; ipage <= my pages.size; ipage ++) {
+	for (integer ipage = 1; ipage <= my pages.size; ipage ++) {
 		ManPage page = my pages.at [ipage];
-		int ipar;
-		for (ipar = 0; page -> paragraphs [ipar]. type; ipar ++) {
+		for (integer ipar = 0; (int) page -> paragraphs [ipar]. type != 0; ipar ++) {
 			const char32 *text = page -> paragraphs [ipar]. text, *p;
 			char32 link [301];
 			if (text) for (p = extractLink (text, nullptr, link); p != nullptr; p = extractLink (text, p, link)) {
@@ -360,7 +359,7 @@ static void grind (ManPages me) {
 		return;
 	}
 	ilinkHither = ilinkThither = 0;
-	for (ipage = 1; ipage <= my pages.size; ipage ++) {
+	for (integer ipage = 1; ipage <= my pages.size; ipage ++) {
 		ManPage page = my pages.at [ipage];
 		page -> linksHither = grandLinksHither + ilinkHither;
 		page -> linksThither = grandLinksThither + ilinkThither;
@@ -375,9 +374,9 @@ static void grind (ManPages me) {
 	 * Third pass: remember the links: fill in linksThither [1..nlinksThither] and linksHither [1..nlinksHither].
 	 * Rebuild nlinksHither and nlinksThither.
 	 */
-	for (ipage = 1; ipage <= my pages.size; ipage ++) {
+	for (integer ipage = 1; ipage <= my pages.size; ipage ++) {
 		ManPage page = my pages.at [ipage];
-		for (int ipar = 0; page -> paragraphs [ipar]. type; ipar ++) {
+		for (int ipar = 0; (int) page -> paragraphs [ipar]. type != 0; ipar ++) {
 			const char32 *text = page -> paragraphs [ipar]. text, *p;
 			char32 link [301];
 			if (text) for (p = extractLink (text, nullptr, link); p != nullptr; p = extractLink (text, p, link)) {
@@ -478,19 +477,19 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 	bool inList = false, inItalic = false, inBold = false;
 	bool inSub = false, inCode = false, inSuper = false, ul = false, inSmall = false;
 	bool wordItalic = false, wordBold = false, wordCode = false, letterSuper = false;
-	for (ManPage_Paragraph paragraph = paragraphs; paragraph -> type != 0; paragraph ++) {
+	for (ManPage_Paragraph paragraph = paragraphs; (int) paragraph -> type != 0; paragraph ++) {
 		const char32 *p = paragraph -> text;
-		int type = paragraph -> type, inTable;
-		bool isListItem = type == kManPage_type_LIST_ITEM ||
-			(type >= kManPage_type_LIST_ITEM1 && type <= kManPage_type_LIST_ITEM3);
-		bool isTag = type == kManPage_type_TAG ||
-			(type >= kManPage_type_TAG1 && type <= kManPage_type_TAG3);
-		bool isDefinition = type == kManPage_type_DEFINITION ||
-			(type >= kManPage_type_DEFINITION1 && type <= kManPage_type_DEFINITION3);
-		/*bool isCode = type == kManPage_type_CODE ||
-			(type >= kManPage_type_CODE1 && type <= kManPage_type_CODE5);*/
-
-		if (type == kManPage_type_PICTURE) {
+		int inTable;
+		bool isListItem = paragraph -> type == kManPage_type::LIST_ITEM ||
+			(paragraph -> type >= kManPage_type::LIST_ITEM1 && paragraph -> type <= kManPage_type::LIST_ITEM3);
+		bool isTag = paragraph -> type == kManPage_type::TAG ||
+			(paragraph -> type >= kManPage_type::TAG1 && paragraph -> type <= kManPage_type::TAG3);
+		bool isDefinition = paragraph -> type == kManPage_type::DEFINITION ||
+			(paragraph -> type >= kManPage_type::DEFINITION1 && paragraph -> type <= kManPage_type::DEFINITION3);
+		/*bool isCode = paragraph -> type == kManPage_type::CODE ||
+			(paragraph -> type >= kManPage_type::CODE1 && paragraph -> type <= kManPage_type::CODE5);*/
+
+		if (paragraph -> type == kManPage_type::PICTURE) {
 			numberOfPictures ++;
 			structMelderFile pdfFile;
 			MelderFile_copy (file, & pdfFile);
@@ -499,7 +498,7 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 				Melder_cat (U"_", numberOfPictures, U".pdf"));
 			{// scope
 				autoGraphics graphics = Graphics_create_pdffile (& pdfFile, 100, 0.0, paragraph -> width, 0.0, paragraph -> height);
-				Graphics_setFont (graphics.get(), kGraphics_font_TIMES);
+				Graphics_setFont (graphics.get(), kGraphics_font::TIMES);
 				Graphics_setFontStyle (graphics.get(), 0);
 				Graphics_setFontSize (graphics.get(), 12);
 				Graphics_setWrapWidth (graphics.get(), 0);
@@ -522,7 +521,7 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 				U" width=", paragraph -> width * 100, U" src=", MelderFile_name (& tiffFile), U"></p>"));
 			continue;
 		}
-		if (type == kManPage_type_SCRIPT) {
+		if (paragraph -> type == kManPage_type::SCRIPT) {
 			autoInterpreter interpreter = Interpreter_createFromEnvironment (nullptr);
 			numberOfPictures ++;
 			structMelderFile pdfFile;
@@ -532,7 +531,7 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 				Melder_cat (U"_", numberOfPictures, U".pdf"));
 			{// scope
 				autoGraphics graphics = Graphics_create_pdffile (& pdfFile, 100, 0.0, paragraph -> width, 0.0, paragraph -> height);
-				Graphics_setFont (graphics.get(), kGraphics_font_TIMES);
+				Graphics_setFont (graphics.get(), kGraphics_font::TIMES);
 				Graphics_setFontStyle (graphics.get(), 0);
 				Graphics_setFontSize (graphics.get(), 12);
 				Graphics_setWrapWidth (graphics.get(), 0);
@@ -545,7 +544,7 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 				theCurrentPraatObjects = (PraatObjects) & praatObjects;
 				theCurrentPraatPicture = (PraatPicture) & praatPicture;
 				theCurrentPraatPicture -> graphics = graphics.get();   // FIXME: should be move()?
-				theCurrentPraatPicture -> font = kGraphics_font_TIMES;
+				theCurrentPraatPicture -> font = (int) kGraphics_font::TIMES;
 				theCurrentPraatPicture -> fontSize = 12;
 				theCurrentPraatPicture -> lineType = Graphics_DRAWN;
 				theCurrentPraatPicture -> colour = Graphics_BLACK;
@@ -614,13 +613,13 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 				if (p [0] == U'•'  && p [1] == U' ') p += 1;
 				if (p [0] == U'\\' && p [1] == U'b' && p [2] == U'u' && p [3] == U' ') p += 3;
 			}
-			MelderString_append (buffer, ul ? U"<li>" : stylesInfo [paragraph -> type]. htmlIn, U"\n");
+			MelderString_append (buffer, ul ? U"<li>" : stylesInfo [(int) paragraph -> type]. htmlIn, U"\n");
 		} else {
 			if (inList) {
 				MelderString_append (buffer, ul ? U"</ul>\n" : U"</dl>\n");
 				inList = ul = false;
 			}
-			MelderString_append (buffer, stylesInfo [paragraph -> type]. htmlIn, U"\n");
+			MelderString_append (buffer, stylesInfo [(int) paragraph -> type]. htmlIn, U"\n");
 		}
 		inTable = *p == U'\t';
 		if (inTable) {
@@ -770,7 +769,7 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 		if (inSub) { MelderString_append (buffer, U"</sub>"); inSub = false; }
 		if (inSuper || letterSuper) { MelderString_append (buffer, U"</sup>"); inSuper = letterSuper = false; }
 		if (inTable) { MelderString_append (buffer, U"</table>"); inTable = false; }
-		MelderString_append (buffer, stylesInfo [paragraph -> type]. htmlOut, U"\n");
+		MelderString_append (buffer, stylesInfo [(int) paragraph -> type]. htmlOut, U"\n");
 	}
 	if (inList) { MelderString_append (buffer, ul ? U"</ul>\n" : U"</dl>\n"); inList = false; }
 }
@@ -792,7 +791,7 @@ static void writePageAsHtml (ManPages me, MelderFile file, long ipage, MelderStr
 	writeParagraphsAsHtml (me, file, paragraphs, buffer);
 	if (ManPages_uniqueLinksHither (me, ipage)) {
 		long ilink, jlink, lastParagraph = 0;
-		while (page -> paragraphs [lastParagraph]. type != 0) lastParagraph ++;
+		while ((int) page -> paragraphs [lastParagraph]. type != 0) lastParagraph ++;
 		if (lastParagraph > 0) {
 			const char32 *text = page -> paragraphs [lastParagraph - 1]. text;
 			if (text && text [0] && text [str32len (text) - 1] != U':')
@@ -834,7 +833,7 @@ void ManPages_writeOneToHtmlFile (ManPages me, long ipage, MelderFile file) {
 	static MelderString buffer { };
 	MelderString_empty (& buffer);
 	writePageAsHtml (me, file, ipage, & buffer);
-	MelderFile_writeText (file, buffer.string, kMelder_textOutputEncoding_UTF8);
+	MelderFile_writeText (file, buffer.string, kMelder_textOutputEncoding::UTF8);
 }
 
 void ManPages_writeAllToHtmlDir (ManPages me, const char32 *dirPath) {
@@ -871,7 +870,7 @@ void ManPages_writeAllToHtmlDir (ManPages me, const char32 *dirPath) {
 		if (! oldText.peek()   // doesn't the file exist yet?
 			|| str32cmp (buffer.string, oldText.peek()))   // isn't the old file identical to the new text?
 		{
-			MelderFile_writeText (& file, buffer.string, kMelder_textOutputEncoding_UTF8);   // then write the new text
+			MelderFile_writeText (& file, buffer.string, kMelder_textOutputEncoding::UTF8);   // then write the new text
 		}
 	}
 }
diff --git a/sys/ManPagesM.h b/sys/ManPagesM.h
index 3a4c766..eb3971c 100644
--- a/sys/ManPagesM.h
+++ b/sys/ManPagesM.h
@@ -26,31 +26,31 @@
 
 #define MAN_BEGIN(t,a,d)  { const char32 *title = t, *author = a; long date = d; \
 	static struct structManPage_Paragraph page [] = {
-#define INTRO(text)  { kManPage_type_INTRO, text, 0.0, 0.0, nullptr },
-#define ENTRY(text)  { kManPage_type_ENTRY, text, 0.0, 0.0, nullptr },
-#define NORMAL(text)  { kManPage_type_NORMAL, text, 0.0, 0.0, nullptr },
-#define LIST_ITEM(text)  { kManPage_type_LIST_ITEM, text, 0.0, 0.0, nullptr },
-#define LIST_ITEM1(text)  { kManPage_type_LIST_ITEM1, text, 0.0, 0.0, nullptr },
-#define LIST_ITEM2(text)  { kManPage_type_LIST_ITEM2, text, 0.0, 0.0, nullptr },
-#define LIST_ITEM3(text)  { kManPage_type_LIST_ITEM3, text, 0.0, 0.0, nullptr },
-#define TAG(text)  { kManPage_type_TAG, text, 0.0, 0.0, nullptr },
-#define TAG1(text)  { kManPage_type_TAG1, text, 0.0, 0.0, nullptr },
-#define TAG2(text)  { kManPage_type_TAG2, text, 0.0, 0.0, nullptr },
-#define TAG3(text)  { kManPage_type_TAG3, text, 0.0, 0.0, nullptr },
-#define DEFINITION(text)  { kManPage_type_DEFINITION, text, 0.0, 0.0, nullptr },
-#define DEFINITION1(text)  { kManPage_type_DEFINITION1, text, 0.0, 0.0, nullptr },
-#define DEFINITION2(text)  { kManPage_type_DEFINITION2, text, 0.0, 0.0, nullptr },
-#define DEFINITION3(text)  { kManPage_type_DEFINITION3, text, 0.0, 0.0, nullptr },
-#define CODE(text)  { kManPage_type_CODE, text, 0.0, 0.0, nullptr },
-#define CODE1(text)  { kManPage_type_CODE1, text, 0.0, 0.0, nullptr },
-#define CODE2(text)  { kManPage_type_CODE2, text, 0.0, 0.0, nullptr },
-#define CODE3(text)  { kManPage_type_CODE3, text, 0.0, 0.0, nullptr },
-#define CODE4(text)  { kManPage_type_CODE4, text, 0.0, 0.0, nullptr },
-#define CODE5(text)  { kManPage_type_CODE5, text, 0.0, 0.0, nullptr },
-#define PROTOTYPE(text)  { kManPage_type_PROTOTYPE, text, 0.0, 0.0, nullptr },
-#define FORMULA(text)  { kManPage_type_FORMULA, text, 0.0, 0.0, nullptr },
-#define PICTURE(width,height,draw)  { kManPage_type_PICTURE, nullptr, width, height, draw },
-#define SCRIPT(width,height,text)  { kManPage_type_SCRIPT, text, width, height },
+#define INTRO(text)  { kManPage_type::INTRO, text, 0.0, 0.0, nullptr },
+#define ENTRY(text)  { kManPage_type::ENTRY, text, 0.0, 0.0, nullptr },
+#define NORMAL(text)  { kManPage_type::NORMAL, text, 0.0, 0.0, nullptr },
+#define LIST_ITEM(text)  { kManPage_type::LIST_ITEM, text, 0.0, 0.0, nullptr },
+#define LIST_ITEM1(text)  { kManPage_type::LIST_ITEM1, text, 0.0, 0.0, nullptr },
+#define LIST_ITEM2(text)  { kManPage_type::LIST_ITEM2, text, 0.0, 0.0, nullptr },
+#define LIST_ITEM3(text)  { kManPage_type::LIST_ITEM3, text, 0.0, 0.0, nullptr },
+#define TAG(text)  { kManPage_type::TAG, text, 0.0, 0.0, nullptr },
+#define TAG1(text)  { kManPage_type::TAG1, text, 0.0, 0.0, nullptr },
+#define TAG2(text)  { kManPage_type::TAG2, text, 0.0, 0.0, nullptr },
+#define TAG3(text)  { kManPage_type::TAG3, text, 0.0, 0.0, nullptr },
+#define DEFINITION(text)  { kManPage_type::DEFINITION, text, 0.0, 0.0, nullptr },
+#define DEFINITION1(text)  { kManPage_type::DEFINITION1, text, 0.0, 0.0, nullptr },
+#define DEFINITION2(text)  { kManPage_type::DEFINITION2, text, 0.0, 0.0, nullptr },
+#define DEFINITION3(text)  { kManPage_type::DEFINITION3, text, 0.0, 0.0, nullptr },
+#define CODE(text)  { kManPage_type::CODE, text, 0.0, 0.0, nullptr },
+#define CODE1(text)  { kManPage_type::CODE1, text, 0.0, 0.0, nullptr },
+#define CODE2(text)  { kManPage_type::CODE2, text, 0.0, 0.0, nullptr },
+#define CODE3(text)  { kManPage_type::CODE3, text, 0.0, 0.0, nullptr },
+#define CODE4(text)  { kManPage_type::CODE4, text, 0.0, 0.0, nullptr },
+#define CODE5(text)  { kManPage_type::CODE5, text, 0.0, 0.0, nullptr },
+#define PROTOTYPE(text)  { kManPage_type::PROTOTYPE, text, 0.0, 0.0, nullptr },
+#define FORMULA(text)  { kManPage_type::FORMULA, text, 0.0, 0.0, nullptr },
+#define PICTURE(width,height,draw)  { kManPage_type::PICTURE, nullptr, width, height, draw },
+#define SCRIPT(width,height,text)  { kManPage_type::SCRIPT, text, width, height, nullptr },
 #define MAN_END  { } }; ManPages_addPage (me, title, author, date, page); }
 
 #define Manual_DRAW_WINDOW(height,title,menu) \
diff --git a/sys/Manual.cpp b/sys/Manual.cpp
index 1734c40..a47038d 100644
--- a/sys/Manual.cpp
+++ b/sys/Manual.cpp
@@ -79,7 +79,6 @@ static void menu_cb_searchForPageList (Manual me, EDITOR_ARGS_FORM) {
 void structManual :: v_draw () {
 	ManPages manPages = (ManPages) our data;
 	ManPage page;
-	ManPage_Paragraph paragraph;
 	#if motif
 	Graphics_clearWs (our graphics.get());
 	#endif
@@ -97,42 +96,42 @@ void structManual :: v_draw () {
 	page = manPages -> pages.at [path];
 	if (! our paragraphs) return;
 	HyperPage_pageTitle (this, page -> title);
-	for (paragraph = & page -> paragraphs [0]; paragraph -> type != 0; paragraph ++) {
+	for (ManPage_Paragraph paragraph = & page -> paragraphs [0]; (int) paragraph -> type != 0; paragraph ++) {
 		switch (paragraph -> type) {
-			case  kManPage_type_INTRO: HyperPage_intro (this, paragraph -> text); break;
-			case  kManPage_type_ENTRY: HyperPage_entry (this, paragraph -> text); break;
-			case  kManPage_type_NORMAL: HyperPage_paragraph (this, paragraph -> text); break;
-			case  kManPage_type_LIST_ITEM: HyperPage_listItem (this, paragraph -> text); break;
-			case  kManPage_type_TAG: HyperPage_listTag (this, paragraph -> text); break;
-			case  kManPage_type_DEFINITION: HyperPage_definition (this, paragraph -> text); break;
-			case  kManPage_type_CODE: HyperPage_code (this, paragraph -> text); break;
-			case  kManPage_type_PROTOTYPE: HyperPage_prototype (this, paragraph -> text); break;
-			case  kManPage_type_FORMULA: HyperPage_formula (this, paragraph -> text); break;
-			case  kManPage_type_PICTURE: HyperPage_picture (this, paragraph -> width,
+			case  kManPage_type::INTRO: HyperPage_intro (this, paragraph -> text); break;
+			case  kManPage_type::ENTRY: HyperPage_entry (this, paragraph -> text); break;
+			case  kManPage_type::NORMAL: HyperPage_paragraph (this, paragraph -> text); break;
+			case  kManPage_type::LIST_ITEM: HyperPage_listItem (this, paragraph -> text); break;
+			case  kManPage_type::TAG: HyperPage_listTag (this, paragraph -> text); break;
+			case  kManPage_type::DEFINITION: HyperPage_definition (this, paragraph -> text); break;
+			case  kManPage_type::CODE: HyperPage_code (this, paragraph -> text); break;
+			case  kManPage_type::PROTOTYPE: HyperPage_prototype (this, paragraph -> text); break;
+			case  kManPage_type::FORMULA: HyperPage_formula (this, paragraph -> text); break;
+			case  kManPage_type::PICTURE: HyperPage_picture (this, paragraph -> width,
 				paragraph -> height, paragraph -> draw); break;
-			case  kManPage_type_SCRIPT: HyperPage_script (this, paragraph -> width,
+			case  kManPage_type::SCRIPT: HyperPage_script (this, paragraph -> width,
 				paragraph -> height, paragraph -> text); break;
-			case  kManPage_type_LIST_ITEM1: HyperPage_listItem1 (this, paragraph -> text); break;
-			case  kManPage_type_LIST_ITEM2: HyperPage_listItem2 (this, paragraph -> text); break;
-			case  kManPage_type_LIST_ITEM3: HyperPage_listItem3 (this, paragraph -> text); break;
-			case  kManPage_type_TAG1: HyperPage_listTag1 (this, paragraph -> text); break;
-			case  kManPage_type_TAG2: HyperPage_listTag2 (this, paragraph -> text); break;
-			case  kManPage_type_TAG3: HyperPage_listTag3 (this, paragraph -> text); break;
-			case  kManPage_type_DEFINITION1: HyperPage_definition1 (this, paragraph -> text); break;
-			case  kManPage_type_DEFINITION2: HyperPage_definition2 (this, paragraph -> text); break;
-			case  kManPage_type_DEFINITION3: HyperPage_definition3 (this, paragraph -> text); break;
-			case  kManPage_type_CODE1: HyperPage_code1 (this, paragraph -> text); break;
-			case  kManPage_type_CODE2: HyperPage_code2 (this, paragraph -> text); break;
-			case  kManPage_type_CODE3: HyperPage_code3 (this, paragraph -> text); break;
-			case  kManPage_type_CODE4: HyperPage_code4 (this, paragraph -> text); break;
-			case  kManPage_type_CODE5: HyperPage_code5 (this, paragraph -> text); break;
+			case  kManPage_type::LIST_ITEM1: HyperPage_listItem1 (this, paragraph -> text); break;
+			case  kManPage_type::LIST_ITEM2: HyperPage_listItem2 (this, paragraph -> text); break;
+			case  kManPage_type::LIST_ITEM3: HyperPage_listItem3 (this, paragraph -> text); break;
+			case  kManPage_type::TAG1: HyperPage_listTag1 (this, paragraph -> text); break;
+			case  kManPage_type::TAG2: HyperPage_listTag2 (this, paragraph -> text); break;
+			case  kManPage_type::TAG3: HyperPage_listTag3 (this, paragraph -> text); break;
+			case  kManPage_type::DEFINITION1: HyperPage_definition1 (this, paragraph -> text); break;
+			case  kManPage_type::DEFINITION2: HyperPage_definition2 (this, paragraph -> text); break;
+			case  kManPage_type::DEFINITION3: HyperPage_definition3 (this, paragraph -> text); break;
+			case  kManPage_type::CODE1: HyperPage_code1 (this, paragraph -> text); break;
+			case  kManPage_type::CODE2: HyperPage_code2 (this, paragraph -> text); break;
+			case  kManPage_type::CODE3: HyperPage_code3 (this, paragraph -> text); break;
+			case  kManPage_type::CODE4: HyperPage_code4 (this, paragraph -> text); break;
+			case  kManPage_type::CODE5: HyperPage_code5 (this, paragraph -> text); break;
 			default: break;
 		}
 	}
 	if (ManPages_uniqueLinksHither (manPages, our path)) {
 		long ilink, jlink, lastParagraph = 0;
 		bool goAhead = true;
-		while (page -> paragraphs [lastParagraph]. type != 0) lastParagraph ++;
+		while ((int) page -> paragraphs [lastParagraph]. type != 0) lastParagraph ++;
 		if (lastParagraph > 0) {
 			const char32 *text = page -> paragraphs [lastParagraph - 1]. text;
 			if (! text || text [0] == U'\0' || text [str32len (text) - 1] != U':') {
@@ -188,14 +187,14 @@ static void print (void *void_me, Graphics graphics) {
 	for (long ipage = 1; ipage <= numberOfPages; ipage ++) {
 		ManPage page = manPages -> pages.at [ipage];
 		if (my printPagesStartingWith == nullptr ||
-		    Melder_stringMatchesCriterion (page -> title, kMelder_string_STARTS_WITH, my printPagesStartingWith))
+		    Melder_stringMatchesCriterion (page -> title, kMelder_string::STARTS_WITH, my printPagesStartingWith))
 		{
 			ManPage_Paragraph par;
 			my path = ipage;
 			my paragraphs = page -> paragraphs;
 			my numberOfParagraphs = 0;
 			par = my paragraphs;
-			while ((par ++) -> type) my numberOfParagraphs ++;
+			while ((int) (par ++) -> type != 0) my numberOfParagraphs ++;
 			Melder_free (my currentPageTitle);
 			my currentPageTitle = Melder_dup_f (page -> title);
 			my v_goToPage_i (ipage);
@@ -208,7 +207,7 @@ static void print (void *void_me, Graphics graphics) {
 }
 
 static void menu_cb_printRange (Manual me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Print range", 0)
+	EDITOR_FORM (U"Print range", nullptr)
 		SENTENCE (U"Left or inside header", U"")
 		SENTENCE (U"Middle header", U"")
 		SENTENCE (U"Right or outside header", U"Manual")
@@ -277,7 +276,7 @@ static double searchToken (ManPages me, long ipage, char32 *token) {
 	/*
 	 * Try to find a match in the paragraphs, case-insensitively.
 	 */
-	while (par -> type) {
+	while ((int) par -> type != 0) {
 		if (par -> text) {
 			char32 *ptoken;
 			MelderString_copy (& buffer, par -> text);
@@ -475,7 +474,7 @@ void structManual :: v_goToPage_i (long pageNumber) {
 	our paragraphs = page -> paragraphs;
 	our numberOfParagraphs = 0;
 	ManPage_Paragraph par = paragraphs;
-	while ((par ++) -> type) our numberOfParagraphs ++;
+	while ((int) (par ++) -> type != 0) our numberOfParagraphs ++;
 	Melder_free (our currentPageTitle);
 	our currentPageTitle = Melder_dup_f (page -> title);
 }
@@ -519,7 +518,7 @@ void Manual_init (Manual me, const char32 *title, Daata data, bool ownData) {
 	my paragraphs = page -> paragraphs;
 	my numberOfParagraphs = 0;
 	par = my paragraphs;
-	while ((par ++) -> type) my numberOfParagraphs ++;
+	while ((int) (par ++) -> type != 0) my numberOfParagraphs ++;
 
 	if (manPages -> pages.at [1] -> title [0] == U'-') {
 		Melder_sprint (windowTitle,101, & manPages -> pages.at [1] -> title [1]);
diff --git a/sys/PAIRWISE_SUM.h b/sys/PAIRWISE_SUM.h
new file mode 100644
index 0000000..73d99d6
--- /dev/null
+++ b/sys/PAIRWISE_SUM.h
@@ -0,0 +1,542 @@
+#ifndef _PAIRWISE_SUM_h_
+#define _PAIRWISE_SUM_h_
+/* PAIRWISE_SUM.h
+ *
+ * Copyright (C) 2017 Paul Boersma <paul.boersma at uva.nl>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (This is the two-clause BSD license, which is approximately identical to CC-BY.)
+ */
+
+/*
+	# PAIRWISE SUMMATION FOR C AND C++
+
+	The pairwise summation algorithm as implemented here is approximately 1.5 times
+	faster than using the usual naive sequential summation loop,
+	and is also hundreds of times more precise than sequential summation.
+	Using pairwise summation instead of sequential summation will improve all your
+	code for sums, means, inner products, norms, matrix multiplications,
+	and CPU-based neural networks.
+	
+	This is not necessarily a small thing in terms of CO2.
+
+	The macro you will call is
+
+		PAIRWISE_SUM (
+			AccumulatorType,     // the type of the accumulator: long double, double, float;
+			sumVariableName,     // the variable name of the accumulator, e.g. "sum";
+			CounterType,         // the type of the term counters: long, int, int64_t, intptr_t;
+			sizeExpression,      // the total number of terms to sum;
+			initializeStatement, // the statement that declares and initializes the loop pointer(s),
+			                     // which is/are typically a pointer or pointers to the data;
+			incrementStatement,  // the statement that increments the loop pointer(s)
+			                     // before the next piece(s) of data is/are fetched;
+			termExpression       // the expression that computes a "term" (thing to sum)
+			                     // from a piece or pieces of data.
+		)
+
+	We explain this in detail below, pedagocically starting with an analogous macro
+	for the more usual "sequential" summation algorithm.
+
+	## 1. SEQUENTIAL SUMMATION: A SINGLE ACCUMULATOR
+
+	The simplest use of summation of multiple values is if you have a single array of
+	floating-point data to sum. If this array is x [1..size],
+	then the usual straightforward algorithm for adding the terms is
+
+		long double sum = 0.0;
+		for (long i = 1; i <= size; i ++)
+			sum += x [i];
+		printf ("%.17g", (double) sum);
+		
+	This is *sequential summation*: a single *accumulator*, namely the variable `sum`,
+	accumulates every element sequentially. Note that even though the array `x` will
+	typically of type `double`, the accumulator is of type `long double` in this example:
+	using 80 bits for summing is much better than using 64 bits (1000 times more precise),
+	and on modern computers it is usually equally fast.
+
+	The formulation above, which uses array indexing, is equivalent to a formulation
+	in terms of a "looping pointer":
+
+		long double sum = 0.0;
+		double *xx = x;   // the looping pointer
+		for (long i = 1; i <= size; i ++)
+			sum += * ++ xx;
+		printf ("%.17g", (double) sum);
+
+	The looping-pointer formulation consists of defining (declaring and initializing)
+	the pointer before the loop, and then inside the loop
+	first pre-incrementing the pointer and then dereferencing it.
+	The "*++xx" formulation has been idiomatic C from the 1980s on,
+	but modern compilers produce equally efficient code if you separate the two steps:
+
+		long double sum = 0.0;
+		double *xx = x;   // decalere and initialize
+		for (long i = 1; i <= size; i ++) {
+			xx += 1;   // first increment...
+			sum += *xx;   // ... then dereference
+		}
+		printf ("%.17g", (double) sum);
+
+	In this little algorithm we can discern the following seven ingredients:
+	- AccumulatorType: long double
+	- sumVariableName: "sum"
+	- CounterType: long
+	- sizeExpression: "size"
+	- initializeStatement: "double *xx = x"
+	- incrementStatement: "xx += 1"
+	- termExpression: "*xx"
+
+	The algorithm can therefore be replaced with
+	
+		SEQUENTIAL_SUM (long double, sum, long, size, double *xx = x, xx += 1, *xx)
+		printf ("%.17g", (double) sum);
+
+	where the SEQUENTIAL_SUM macro is defined as:
+*/
+#define SEQUENTIAL_SUM(AccumulatorType, sumVariableName, CounterType, sizeExpression, \
+	initializeStatement, incrementStatement, termExpression) \
+\
+	AccumulatorType sumVariableName = 0.0; \
+	{/* scope */ \
+		initializeStatement; \
+		CounterType _n = sizeExpression;   /* to ensure that the size expression is evaluated only once */ \
+		for (CounterType _i = 1; _i <= _n; _i ++) { \
+			incrementStatement; \
+			sumVariableName += termExpression; \
+		} \
+	}
+
+/*
+	## 2. SEQUENTIAL SUMMATION IN MACHINE CODE
+
+	How would you sum the four numbers a, b, c and d,
+	if all that your computer can do is adding two numbers at a time?
+	
+	You could write the sum in one stroke in C, without parentheses:
+
+		sum = a + b + c + d;
+
+	Evaluation of pluses in C proceeds from left to right, so this formulation means the same as
+
+		sum = ((a + b) + c) + d;
+
+	In machine-like C, using the "registers" r1, r2, r3 and r4, you could implement this as
+
+		r1 = a;
+		r2 = b;
+		r1 += r2;
+		r3 = c;
+		r1 += r3;
+		r4 = d;
+		r1 += r4;
+		sum = r1;
+
+	All these three formulations lead to identical true machine code,
+	at least with Clang or GCC, and with optimization option -O3.
+
+
+	## 3. SPEED OF SEQUENTIAL SUMMATION
+	
+	If your processor has good prefetching, then the four loads from RAM (moving a, b, c,
+	and d into the registers r1, r2, r3 and r4) will take up no time at all.
+	The three additions, however, will require three clock cycles in total:
+	as all additions are performed on r1, each addition will have to wait
+	until the previous addition has finished. Adding up 64 numbers will require 63 cycles.
+
+
+	## 4. PRECISION OF SEQUENTIAL SUMMATION
+	
+	The worst case floating-point rounding error for sequential summation is N * epsilon,
+	where N is the number of numbers to add, and epsilon is the precision of one number.
+	The expected random-walk rounding error is epsilon * sqrt (N).
+
+
+	## 5. PAIRWISE SUMMATION: MULTIPLE ACCUMULATORS
+
+	An alternative way to add the four numbers, next to sequential summation,
+	is to proceed in a *pairwise* manner, dividing the four numbers into two groups,
+	then summing these separately, then summing the two results. In short C this would be:
+
+		sum = (a + b) + (c + d);
+
+	This pairwise summation continues recursively. For instance, you add 8 numbers this way:
+
+		sum = ((a + b) + (c + d)) + ((e + f) + (g + h));
+
+
+	## 6. PAIRWISE SUMMATION IN MACHINE CODE
+
+	In machine-like C, using the "registers" r1, r2, r3 and r4, you can implement this as
+
+		r1 = a;
+		r2 = b;
+		r1 += r2;
+		r3 = c;
+		r4 = d;
+		r3 += r4;
+		r1 += r3;
+		sum = r1;
+
+	The number of loads, stores and additions is identical to those in the sequential case.
+	
+
+	## 7. SPEED OF PAIRWISE SUMMATION
+	
+	Pairwise summation can be faster than sequential summation,
+	because the addition of r2 to r1 can be performed in parallel to the addition of r4 to r3.
+	The three additions in section 5 could therefore be performed in two clock cycles.
+	Indeed, both compilers that I am using in 2017 (Clang on the Mac, and GCC on Windows and Linux)
+	take only 0.30 nanoseconds per addition, which is just over two-thirds of the clock period
+	of the 2.3 GHz processor (which is 43.5 nanoseconds) of my 2014 Macbook Pro.
+	On a processor far, far away, which has more than 64 registers, perfect prefetching,
+	and perfectly parallel operations on independent registers, 64 terms may be added
+	within 6 clock cycles: six is the number of times we need to add something to r1,
+	and each of these additions has to wait for the result of the previous addition.
+	Theoretically, then, but not yet in practice, the execution time has fallen from order N
+	to order log(N), at least for low N (and for high N it will be of order N,
+	but with a much lower multiplication factor than sequential summation has).
+
+
+	## 8. PRECISION OF PAIRWISE SUMMATION
+	
+	The worst-case rounding error is only epsilon * log (N),
+	and the expected random-walk rounding error is only epsilon * sqrt (log (N)).
+	Both are much better than in the sequential summation case.
+
+
+	## 9. HOW TO USE PAIRWISE SUMMATION
+
+	For pairwise summation we use the exact same macro arguments as for sequential summation:
+	
+		PAIRWISE_SUM (long double, sum, long, size, double *xx = x, xx += 1, *xx)
+		printf ("%.17g", (double) sum);
+
+	The macro starts by declaring a variable of type `AccumulatorType` and name `sumVariableName`.
+	Thus, the expansion of the macro effectively starts with the declaration `long double sum;`
+	and the variable `sum` will be available after the macro ends,
+	so that you can use it in your subsequent code, as is done here by using `(double) sum`.
+	This variable is used to accumulate the resulting sum and should for best results
+	therefore be capable of accumulating partial sums with little rounding error.
+	For this reason, you will usually want AccumulatorType to be `long double`.
+	If long doubles are not available on your platform, or if they are slow on your platform,
+	you can use `double` instead, or even `float`.
+
+	The size of the array to sum comes in in the argument `sizeExpression`, and in the expansion
+	of the macro this number and all macro-internal counters are of type `CounterType`.
+	In the example above I used `long` for simplicity, but you should note that on 64-bit Windows
+	a `long` is only 32 bits, whereas the natural type for array indexing on a 64-bit machine
+	would be `int64_t`; I therefore tend to use a CounterType of `intptr_t` myself,
+	because this is 32 bits on 32-bit machines and 64 bits on 64-bit machines.
+	If your `size` is an `int` (always 32 bits nowadays), you would use a CounterType of `int`;
+	it is not beneficial to use a wider CounterType than the width of your `size`.
+
+	The fifth argument of the macro declares and initializes the loop pointer,
+	as in `double *xx = x` above. This pointer starts out pointing just below the elements of the array.
+
+	The sixth argument of the macro is the formula you use for incrementing the loop pointer(s),
+	as in `xx += 1` above. The macro uses this formula to prepare for the retrieval of
+	every term in the summation.
+	
+	The seventh argument is an expression that should evaluate to the value of a term,
+	given the current (incremented) value of the loop pointer(s), as in `*xx` above.
+
+	More complicated use cases than a single array also exist. For instance, if you have to compute
+	the inner product of the two arrays x [1..n] and y [1..n], you can do
+	
+		PAIRWISE_SUM (long double, inner, long, n,
+			double *xx = x;   // the semicolon ensures that this line and the next form a single argument
+			double *yy = y,
+			(++ xx, ++ yy),
+			(long double) *xx * (long double) *yy)
+		printf ("%.17g", (double) inner);
+
+	Note for the sixth argument: you can see here that you can do the two increments simultaneously
+	by using parentheses and a comma; fortunately, the C macro preprocessor understands enough
+	about parentheses to see that you mean the fifth argument to be a single argument.
+
+	Note for the seventh argument: as 64-bit multiplication loses a lot of the precision of its
+	two 64-bit operands, it is advisable to convert both operands to `long double`
+	*before* the multiplication, as is done here. This usually costs no extra computation
+	time (it can actually be faster). If you do
+	
+		PAIRWISE_SUM (long double, inner, long, n, double *xx = x; double *yy = y, (++ xx, ++ yy), *xx * *yy)
+		printf ("%.17g", (double) inner);
+
+	instead, the conversion to `long double` is done (by the macro) *after* the multiplication,
+	which is less precise.
+
+	Other use cases include array multiplication with strides...
+
+		PAIRWISE_SUM (long double, inner, long, n,
+			double *xx = & x [1 - xstride];   // note the funny semicolon again
+			double *yy = & y [1 - ystride],
+			(xx += xstride, yy += ystride),
+			(long double) *xx * (long double) *yy)
+		printf ("%.17g", (double) inner);
+
+	... and small-lag convolution...
+
+	for (long i = 1; i <= xSize - kernelSize + 1; i ++) {
+		PAIRWISE_SUM (long double, conv, long, kernelSize,
+			double *xx = & x [i - 1];
+			double *filter = & kernel [kernelSize + 1],
+			(xx += 1, filter -= 1),
+			(long double) *xx * (long double) *filter)
+		result [i] = conv;
+	}
+
+	... and matrix multiplication, and computing norms.
+
+
+	## 10. IMPLEMENTATION OF PAIRWISE SUMMATION: LOW POWERS OF 2
+	
+	We have to implement pairwise summation with a macro,
+	because the sixth and seventh arguments to PAIRWISE_SUM() have to be formulas for getting
+	the next element to add. We use fixed formulas for adding 2, 4, 8, 16 or 32 terms,
+	and a stack for the next 57 higher powers of 2, up to 2^62,
+	so that our summation will work for N up to 2^63 - 1.
+	The fixed formulas for the low powers of 2 are recursively defined macros:
+*/
+
+#define PAIRWISE_SUM_1_TERM(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	incrementStatement; \
+	AccumulatorType accumulator = termExpression;
+
+#define PAIRWISE_SUM_2_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_1_TERM (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_1_TERM (AccumulatorType, _r2, incrementStatement, termExpression) \
+		accumulator += _r2; \
+	}
+
+#define PAIRWISE_SUM_4_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_2_TERMS (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_2_TERMS (AccumulatorType, _r3, incrementStatement, termExpression) \
+		accumulator += _r3; \
+	}
+
+#define PAIRWISE_SUM_8_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_4_TERMS (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_4_TERMS (AccumulatorType, _r4, incrementStatement, termExpression) \
+		accumulator += _r4; \
+	}
+
+#define PAIRWISE_SUM_16_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_8_TERMS (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_8_TERMS (AccumulatorType, _r5, incrementStatement, termExpression) \
+		accumulator += _r5; \
+	}
+
+#define PAIRWISE_SUM_32_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_16_TERMS (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_16_TERMS (AccumulatorType, _r6, incrementStatement, termExpression) \
+		accumulator += _r6; \
+	}
+/*
+	(The difference between the variable names "_r2", "_r3" and so on is not strictly needed,
+	but making them the same would lead the compiler to issue warnings about shadowed variables.)
+*/
+
+/*
+	## 11. IMPLEMENTATION OF PAIRWISE ADDITION: HIGH POWERS OF 2
+	
+	Higher powers of 2 than 32 go on a stack. We sum 64 values at each stroke,
+	and this requires a fixed formula for these 64 terms:
+*/
+#define PAIRWISE_SUM_64_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_32_TERMS (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_32_TERMS (AccumulatorType, _r7, incrementStatement, termExpression) \
+		accumulator += _r7; \
+	}
+#define PAIRWISE_SUM_128_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_64_TERMS (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_64_TERMS (AccumulatorType, _r8, incrementStatement, termExpression) \
+		accumulator += _r8; \
+	}
+#define PAIRWISE_SUM_256_TERMS(AccumulatorType, accumulator, incrementStatement, termExpression) \
+	PAIRWISE_SUM_128_TERMS (AccumulatorType, accumulator, incrementStatement, termExpression) \
+	{ \
+		PAIRWISE_SUM_128_TERMS (AccumulatorType, _r9, incrementStatement, termExpression) \
+		accumulator += _r9; \
+	}
+
+/*
+	A generalization about the timing of the summations in all the above macros
+	is that r(i+1) is added to r(i) precisely when r(i+1) contains the same
+	number of terms as r(i). This criterion for collapsing the partial sums
+	is also used in the stack logic below.
+*/
+
+#define PAIRWISE_SUM(AccumulatorType, sumVariableName, CounterType, sizeExpression, \
+	initializeStatement, incrementStatement, termExpression) \
+\
+	AccumulatorType sumVariableName = 0.0; \
+	{/* scope */ \
+		initializeStatement; \
+		CounterType _n = sizeExpression; \
+		if (_n & 1) { \
+			PAIRWISE_SUM_1_TERM (AccumulatorType, _partialSum, incrementStatement, termExpression) \
+			sumVariableName += _partialSum; \
+		} \
+		if (_n & 2) { \
+			PAIRWISE_SUM_2_TERMS (AccumulatorType, _partialSum, incrementStatement, termExpression) \
+			sumVariableName += _partialSum; \
+		} \
+		if (_n & 4) { \
+			PAIRWISE_SUM_4_TERMS (AccumulatorType, _partialSum, incrementStatement, termExpression) \
+			sumVariableName += _partialSum; \
+		} \
+		if (_n & 8) { \
+			PAIRWISE_SUM_8_TERMS (AccumulatorType, _partialSum, incrementStatement, termExpression) \
+			sumVariableName += _partialSum; \
+		} \
+		if (_n & 16) { \
+			PAIRWISE_SUM_16_TERMS (AccumulatorType, _partialSum, incrementStatement, termExpression) \
+			sumVariableName += _partialSum; \
+		} \
+		if (_n & 32) { \
+			PAIRWISE_SUM_32_TERMS (AccumulatorType, _partialSum, incrementStatement, termExpression) \
+			sumVariableName += _partialSum; \
+		} \
+		const int _baseCasePower = 6;   /* because the base case is 64 = 2^6 terms */ \
+		CounterType _numberOfBaseCases = _n >> _baseCasePower; \
+		if (_numberOfBaseCases != 0) { \
+			/*                                                                                  */ \
+			/*  The value of _powers [0] stays at 0, to denote the bottom of the stack.         */ \
+			/*  The maximum value of _powers [1] should be 62, which denotes that 2^62 terms    */ \
+			/*  have been accumulated into _partialSumStack [1]. This the maximum,              */ \
+			/*  because _n can be at most 2^63-1 (assuming CounterType is 64 bits).             */ \
+			/*  The maximum value of _powers [2] should then be 61.                             */ \
+			/*  The maximum value of _powers [3] should be 60.                                  */ \
+			/*  ...                                                                             */ \
+			/*  The maximum value of _powers [57] should be 6. It ends there, because           */ \
+			/*  2^6 is the granularity with which base case sums are put on the stack.          */ \
+			/*  The maximum value of _powers [58] should also be 6,                             */ \
+			/*  because this can be the situation just before collapsing the top of the stack.  */ \
+			/*  However, if the whole stack is filled up like this, the actual number of        */ \
+			/*  terms is already 2^63 (2^62 + 2^61 + 2^60 + ... 2^6 + 2^6). Therefore, we       */ \
+			/*  need one element less, so the highest index of _powers [] should be 57.         */ \
+			/*  For 32-bit counters, this highest index is 25.                                  */ \
+			/*                                                                                  */ \
+			const int _numberOfBitsInCounterType = 8 * sizeof (CounterType);   /* 64 or 32 */ \
+			const int _highestIndex = _numberOfBitsInCounterType - 1 - _baseCasePower; \
+			AccumulatorType _partialSumStack [1 + _highestIndex];   /* 8 bytes too many, but better code */ \
+			unsigned char _powers [1 + _highestIndex]; \
+			_powers [0] = 0; \
+			int _stackPointer = 0; \
+			for (CounterType _ipart = 1; _ipart <= _numberOfBaseCases; _ipart ++) { \
+				/*                                                                              */ \
+				/*  Compute the sum of the next 64 data points.                                 */ \
+				/*                                                                              */ \
+				PAIRWISE_SUM_64_TERMS (AccumulatorType, _partialSum, incrementStatement, termExpression) \
+				/*                                                                              */ \
+				/*  Put this sum on top of the stack.                                           */ \
+				/*                                                                              */ \
+				_partialSumStack [++ _stackPointer] = _partialSum; \
+				_powers [_stackPointer] = _baseCasePower; \
+				/*                                                                              */ \
+				/*  The collapse criterion:                                                     */ \
+				/*                                                                              */ \
+				while (_powers [_stackPointer] == _powers [_stackPointer - 1]) { \
+					_partialSumStack [_stackPointer - 1] += _partialSumStack [_stackPointer]; \
+					_powers [-- _stackPointer] += 1; \
+				} \
+			} \
+			/*                                                                                  */ \
+			/*  Add all the elements of the stack, starting at the top.                         */ \
+			/*                                                                                  */ \
+			while (_stackPointer > 0) { \
+				sumVariableName += _partialSumStack [_stackPointer --]; \
+			} \
+		} \
+	}
+/*
+	Note that we don't do the usual trick with `do {...} while (0)` that would allow you to add
+	a semicolon after the `PAIRWISE_SUM()` call. This is to prevent the suggestion that the macro
+	constitutes a single statement. The macro contains a sequence of two things: the definitions
+	of `sum` and the pointer(s), and a long block that usually changes the value of `sum`. Hence,
+	the macro cannot be used as a single statement
+	and e.g. has to be bracketed if used in an `else` clause.
+	You are therefore advised to call `PAIRWISE_SUM()` without appending the misleading semicolon.
+*/
+
+/*
+	## 12. SOME LESS GOOD SUMMATION ALGORITHMS
+	
+	The summation algorithm (at least for computing the mean) in the statistics software R
+	is two-loop summation. This is fairly precise, but very slow:
+*/
+#define TWO_LOOP_SUM(AccumulatorType, sumVariableName, CounterType, sizeExpression, \
+	initializeStatement, incrementStatement, termExpression) \
+\
+	AccumulatorType sumVariableName = 0.0; \
+	{/* scope */ \
+		CounterType _n = sizeExpression; \
+		{/* scope */ \
+			initializeStatement; \
+			for (CounterType _i = 1; _i <= _n; _i ++) { \
+				incrementStatement; \
+				sumVariableName += termExpression; \
+			} \
+		} \
+		AccumulatorType _mean = sumVariableName / _n; \
+		{/* scope */ \
+			sumVariableName = 0.0; \
+			initializeStatement; \
+			for (CounterType _i = 1; _i <= _n; _i ++) { \
+				incrementStatement; \
+				sumVariableName += (termExpression) - _mean; \
+			} \
+			sumVariableName += _mean * _n; \
+		} \
+	}
+/*
+	Another one is the Kahan algorithm. Its precision is comparable to that of pairwise summation,
+	but it is extremely slow:
+*/
+#define KAHAN_SUM(AccumulatorType, sumVariableName, CounterType, sizeExpression, \
+	initializeStatement, incrementStatement, termExpression) \
+\
+	AccumulatorType sumVariableName = 0.0; \
+	{/* scope */ \
+		initializeStatement; \
+		CounterType _n = sizeExpression; \
+		AccumulatorType _correction = 0.0; \
+		for (CounterType _i = 1; _i <= _n; _i ++) { \
+			incrementStatement; \
+			AccumulatorType _correctedTerm = (termExpression) - _correction; \
+			AccumulatorType _newSum = sumVariableName + _correctedTerm; \
+			_correction = (_newSum - sumVariableName) - _correctedTerm; \
+			sumVariableName = _newSum; \
+		} \
+	}
+
+/* End of file PAIRWISE_SUM.h */
+#endif
diff --git a/sys/Preferences.cpp b/sys/Preferences.cpp
index 6e03b65..19b4d51 100644
--- a/sys/Preferences.cpp
+++ b/sys/Preferences.cpp
@@ -82,8 +82,8 @@ void Preferences_addDouble (const char32 *string, double *value, double defaultV
 void Preferences_addString (const char32 *string, char32 *value, const char32 *defaultValue)
 	{ str32cpy (value, defaultValue); Preferences_add (string, stringwa, value, 0, 0, nullptr, nullptr); }
 
-void _Preferences_addEnum (const char32 *string, enum kPreferences_dummy *value, int min, int max,
-	const char32 *(*getText) (int value), int (*getValue) (const char32 *text), enum kPreferences_dummy defaultValue)
+void _Preferences_addEnum (const char32 *string, int *value, int min, int max,
+	const char32 *(*getText) (int value), int (*getValue) (const char32 *text), int defaultValue)
 {
 	{ *value = defaultValue; Preferences_add (string, enumwa, value, min, max, getText, getValue); }
 }
@@ -144,7 +144,7 @@ void Preferences_read (MelderFile file) {
 					int intValue = pref -> getValue (value);
 					if (intValue < 0)
 						intValue = pref -> getValue (U"\t");   // look for the default
-					* (enum kPreferences_dummy *) pref -> value = (enum kPreferences_dummy) intValue; break;
+					* (int *) pref -> value = intValue; break;
 				}
 			}
 		}
@@ -170,12 +170,12 @@ void Preferences_write (MelderFile file) {
 			case boolwa:   MelderString_append (& buffer,       (* (bool *)           pref -> value)); break;
 			case doublewa: MelderString_append (& buffer,       (* (double *)         pref -> value)); break;
 			case stringwa: MelderString_append (& buffer,         ((const char32 *)   pref -> value)); break;
-			case enumwa:   MelderString_append (& buffer, pref -> getText (* (enum kPreferences_dummy *) pref -> value)); break;
+			case enumwa:   MelderString_append (& buffer, pref -> getText (* (int *) pref -> value)); break;
 		}
 		MelderString_appendCharacter (& buffer, U'\n');
 	}
 	try {
-		MelderFile_writeText (file, buffer.string, kMelder_textOutputEncoding_ASCII_THEN_UTF16);
+		MelderFile_writeText (file, buffer.string, kMelder_textOutputEncoding::ASCII_THEN_UTF16);
 	} catch (MelderError) {
 		Melder_clearError ();
 	}
diff --git a/sys/Preferences.h b/sys/Preferences.h
index 88a1cca..6878ca0 100644
--- a/sys/Preferences.h
+++ b/sys/Preferences.h
@@ -27,17 +27,15 @@
  */
 #define Preferences_STRING_BUFFER_SIZE 1+kMelder_MAXPATH
 
-#define pref_str32cpy(to,from) \
+#define pref_str32cpy(to, from) \
 	str32ncpy (to, from, Preferences_STRING_BUFFER_SIZE); \
 	to [Preferences_STRING_BUFFER_SIZE - 1] = U'\0';
 
-#define pref_str32cpy2(to2,to1,from) \
+#define pref_str32cpy2(to2, to1, from) \
 	str32ncpy (to1, from, Preferences_STRING_BUFFER_SIZE); \
 	to1 [Preferences_STRING_BUFFER_SIZE - 1] = U'\0'; \
 	str32cpy (to2, to1);
 
-enum kPreferences_dummy { dummy1 = 1, dummy2 = 2 };
-
 void Preferences_addByte   (const char32 *string /* cattable */, signed char *value, signed char defaultValue);
 void Preferences_addShort  (const char32 *string /* cattable */, short *value, short defaultValue);
 void Preferences_addInt16  (const char32 *string /* cattable */, int *value, int defaultValue);
@@ -50,11 +48,11 @@ void Preferences_addUlong  (const char32 *string /* cattable */, unsigned long *
 void Preferences_addBool   (const char32 *string /* cattable */, bool *value, bool defaultValue);
 void Preferences_addDouble (const char32 *string /* cattable */, double *value, double defaultValue);
 void Preferences_addString (const char32 *string /* cattable */, char32 *value, const char32 *defaultValue);
-void _Preferences_addEnum  (const char32 *string /* cattable */, enum kPreferences_dummy *value, int min, int max,
-	const char32 *(*getText) (int value), int (*getValue) (const char32 *text), enum kPreferences_dummy defaultValue);
-#define Preferences_addEnum(string,value,enumerated,defaultValue) \
-	_Preferences_addEnum (string, (enum kPreferences_dummy *) value, enumerated##_MIN, enumerated##_MAX, \
-	enumerated##_getText, enumerated##_getValue, (enum kPreferences_dummy) defaultValue)
+void _Preferences_addEnum  (const char32 *string /* cattable */, int *value, int min, int max,
+	const char32 *(*getText) (int value), int (*getValue) (const char32 *text), int defaultValue);
+#define Preferences_addEnum(string, value, enumerated, defaultValue) \
+	_Preferences_addEnum (string, (int *) value, (int) enumerated::MIN, (int) enumerated::MAX, \
+	(const char32 *(*) (int)) enumerated##_getText, (enum_generic_getValue) enumerated##_getValue, (int) defaultValue)
 
 void Preferences_read (MelderFile file);
 void Preferences_write (MelderFile file);
diff --git a/sys/Printer.cpp b/sys/Printer.cpp
index cfd9f25..777a7b0 100644
--- a/sys/Printer.cpp
+++ b/sys/Printer.cpp
@@ -40,17 +40,17 @@
  */
 
 /* exported */ struct Printer thePrinter = {
-	kGraphicsPostscript_spots_DEFAULT, kGraphicsPostscript_paperSize_DEFAULT, kGraphicsPostscript_orientation_DEFAULT, false,
-	true, kGraphicsPostscript_fontChoiceStrategy_DEFAULT,
+	kGraphicsPostscript_spots::DEFAULT, kGraphicsPostscript_paperSize::DEFAULT, kGraphicsPostscript_orientation::DEFAULT, false,
+	true, kGraphicsPostscript_fontChoiceStrategy::DEFAULT,
 	600, 5100, 6600,
 	1.0
 };
 
 void Printer_prefs () {
-	Preferences_addEnum (U"Printer.spots", & thePrinter. spots, kGraphicsPostscript_spots, kGraphicsPostscript_spots_DEFAULT);
-	Preferences_addEnum (U"Printer.paperSize", & thePrinter. paperSize, kGraphicsPostscript_paperSize, kGraphicsPostscript_paperSize_DEFAULT);
+	Preferences_addEnum (U"Printer.spots", & thePrinter. spots, kGraphicsPostscript_spots, kGraphicsPostscript_spots::DEFAULT);
+	Preferences_addEnum (U"Printer.paperSize", & thePrinter. paperSize, kGraphicsPostscript_paperSize, kGraphicsPostscript_paperSize::DEFAULT);
 	Preferences_addBool (U"Printer.allowDirectPostScript", & thePrinter. allowDirectPostScript, true);
-	Preferences_addEnum (U"Printer.fontChoiceStrategy", & thePrinter. fontChoiceStrategy, kGraphicsPostscript_fontChoiceStrategy, kGraphicsPostscript_fontChoiceStrategy_DEFAULT);
+	Preferences_addEnum (U"Printer.fontChoiceStrategy", & thePrinter. fontChoiceStrategy, kGraphicsPostscript_fontChoiceStrategy, kGraphicsPostscript_fontChoiceStrategy::DEFAULT);
 }
 
 #if cocoa
@@ -157,10 +157,10 @@ static void DO_Printer_postScriptSettings (UiForm dia, int /* narg */, Stackel /
 	thePrinter. spots = GET_ENUM (kGraphicsPostscript_spots, U"Grey resolution");
 	#if defined (UNIX)
 		thePrinter. paperSize = GET_ENUM (kGraphicsPostscript_paperSize, U"Paper size");
-	 	if (thePrinter. paperSize == kGraphicsPostscript_paperSize_A3) {
+	 	if (thePrinter. paperSize == kGraphicsPostscript_paperSize::A3) {
 	 		thePrinter. paperWidth = 842 * thePrinter. resolution / 72;
 	 		thePrinter. paperHeight = 1191 * thePrinter. resolution / 72;
-		} else if (thePrinter. paperSize == kGraphicsPostscript_paperSize_US_LETTER) {
+		} else if (thePrinter. paperSize == kGraphicsPostscript_paperSize::US_LETTER) {
 			thePrinter. paperWidth = 612 * thePrinter. resolution / 72;
 			thePrinter. paperHeight = 792 * thePrinter. resolution / 72;
 		} else {
diff --git a/sys/Printer.h b/sys/Printer.h
index 217242d..483cd0f 100644
--- a/sys/Printer.h
+++ b/sys/Printer.h
@@ -2,7 +2,7 @@
 #define _Printer_h_
 /* Printer.h
  *
- * Copyright (C) 1992-2011,2012,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2012,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,11 +22,11 @@
 
 /* When changing the following structure, update its initialization in Printer.cpp */
 struct Printer {
-	enum kGraphicsPostscript_spots spots;
-	enum kGraphicsPostscript_paperSize paperSize;
-	enum kGraphicsPostscript_orientation orientation;
+	kGraphicsPostscript_spots spots;
+	kGraphicsPostscript_paperSize paperSize;
+	kGraphicsPostscript_orientation orientation;
 	bool postScript, allowDirectPostScript;
-	enum kGraphicsPostscript_fontChoiceStrategy fontChoiceStrategy;
+	kGraphicsPostscript_fontChoiceStrategy fontChoiceStrategy;
 	long resolution, paperWidth, paperHeight;
 	double magnification;
 };
diff --git a/sys/TextEditor.cpp b/sys/TextEditor.cpp
index c31d6d6..39c5963 100644
--- a/sys/TextEditor.cpp
+++ b/sys/TextEditor.cpp
@@ -527,7 +527,7 @@ static void menu_cb_whereAmI (TextEditor me, EDITOR_ARGS_DIRECT) {
 }
 
 static void menu_cb_goToLine (TextEditor me, EDITOR_ARGS_FORM) {
-	EDITOR_FORM (U"Go to line", 0)
+	EDITOR_FORM (U"Go to line", nullptr)
 		NATURAL (U"Line", U"1")
 	EDITOR_OK
 		long firstLine, lastLine;
diff --git a/sys/Ui.cpp b/sys/Ui.cpp
index 09a2c0f..fba3cb7 100644
--- a/sys/Ui.cpp
+++ b/sys/Ui.cpp
@@ -163,34 +163,37 @@ static int colourToValue (UiField me, char32 *string) {
 	return 1;
 }
 
+#define EVALUATE_WIDGET_REPRESENTATIONS  0
 static void UiField_widgetToValue (UiField me) {
 	switch (my type) {
 		case UI_REAL: case UI_REAL_OR_UNDEFINED: case UI_POSITIVE: {
 			autostring32 dirty = GuiText_getString (my text);   // the text as typed by the user
 			Interpreter_numericExpression (nullptr, dirty.peek(), & my realValue);
-			/*
-			 * Put a clean version of the new value in the form.
-			 * If the value is equal to the default value, make sure that any default comments are included.
-			 */
-			if (my realValue == Melder_atof (my stringDefaultValue)) {
-				GuiText_setString (my text, my stringDefaultValue);
-			} else {
-				char32 clean [40];
-				str32cpy (clean, Melder_double (my realValue));
+			#if EVALUATE_WIDGET_REPRESENTATIONS
 				/*
-				 * If the default value is overtly real (rather than integer), the shown value must be overtly real as well.
-				 */
-				if ((str32chr (my stringDefaultValue, U'.') || str32chr (my stringDefaultValue, U'e')) &&
-					! (str32chr (clean, U'.') || str32chr (clean, U'e')))
-				{
-					str32cpy (clean + str32len (clean), U".0");
+					Put a clean version of the new value in the form.
+					If the value is equal to the default value, make sure that any default comments are included.
+				*/
+				if (my realValue == Melder_atof (my stringDefaultValue)) {
+					GuiText_setString (my text, my stringDefaultValue);
+				} else {
+					char32 clean [40];
+					str32cpy (clean, Melder_double (my realValue));
+					/*
+						If the default value is overtly real (rather than integer), the shown value must be overtly real as well.
+					*/
+					if ((str32chr (my stringDefaultValue, U'.') || str32chr (my stringDefaultValue, U'e')) &&
+						! (str32chr (clean, U'.') || str32chr (clean, U'e')))
+					{
+						str32cpy (clean + str32len (clean), U".0");
+					}
+					GuiText_setString (my text, clean);
 				}
-				GuiText_setString (my text, clean);
-			}
+			#endif
 			if (isundef (my realValue) && my type != UI_REAL_OR_UNDEFINED)
 				Melder_throw (U_LEFT_DOUBLE_QUOTE, my name, U_RIGHT_DOUBLE_QUOTE U" has the value \"undefined\".");
 			if (my type == UI_POSITIVE && my realValue <= 0.0)
-				Melder_throw (U_LEFT_DOUBLE_QUOTE, my name, U_RIGHT_DOUBLE_QUOTE U" must be greater than 0.0.");
+				Melder_throw (U_LEFT_DOUBLE_QUOTE, my name, U_RIGHT_DOUBLE_QUOTE U" should be greater than 0.0.");
 			if (my realVariable) *my realVariable = my realValue;
 		} break; case UI_INTEGER: case UI_NATURAL: case UI_CHANNEL: {
 			autostring32 dirty = GuiText_getString (my text);
@@ -203,21 +206,29 @@ static void UiField_widgetToValue (UiField me) {
 				Interpreter_numericExpression (nullptr, dirty.peek(), & realValue);
 				my integerValue = lround (realValue);
 			}
-			if (my integerValue == Melder_atoi (my stringDefaultValue)) {
-				GuiText_setString (my text, my stringDefaultValue);
-			} else {
-				GuiText_setString (my text, Melder_integer (my integerValue));
-			}
+			#if EVALUATE_WIDGET_REPRESENTATIONS
+				if (my integerValue == Melder_atoi (my stringDefaultValue)) {
+					GuiText_setString (my text, my stringDefaultValue);
+				} else {
+					GuiText_setString (my text, Melder_integer (my integerValue));
+				}
+			#endif
 			if ((my type == UI_NATURAL || my type == UI_CHANNEL) && my integerValue < 1) {
-				Melder_throw (U_LEFT_DOUBLE_QUOTE, my name, U_RIGHT_DOUBLE_QUOTE U" must be a positive whole number.");
+				Melder_throw (U_LEFT_DOUBLE_QUOTE, my name, U_RIGHT_DOUBLE_QUOTE U" should be a positive whole number.");
 			}
 			if (my longVariable) *my longVariable = my integerValue;
 		} break; case UI_WORD: {
 			Melder_free (my stringValue);
 			my stringValue = GuiText_getString (my text);
-			char32 *p = my stringValue;
-			while (*p != '\0') { if (*p == U' ' || *p == U'\t') *p = U'\0'; p ++; }
-			GuiText_setString (my text, my stringValue);
+			#if EVALUATE_WIDGET_REPRESENTATIONS
+				char32 *p = my stringValue;
+				while (*p != '\0') { if (*p == U' ' || *p == U'\t') *p = U'\0'; p ++; }
+				GuiText_setString (my text, my stringValue);
+			#else
+				if (str32chr (my stringValue, U' ') || str32chr (my stringValue, U'\t')) {
+					Melder_throw (U_LEFT_DOUBLE_QUOTE, my name, U_RIGHT_DOUBLE_QUOTE U" should be a single word and cannot contain a space.");
+				}
+			#endif
 			if (my stringVariable) *my stringVariable = my stringValue;
 		} break; case UI_SENTENCE: case UI_TEXT: {
 			Melder_free (my stringValue);
@@ -314,8 +325,8 @@ static void UiField_stringToValue (UiField me, const char32 *string, Interpreter
 			}
 			if (my integerValue == 0) {
 				/*
-				 * Retry with different case.
-				 */
+					Retry with different case.
+				*/
 				for (int i = 1; i <= my options.size; i ++) {
 					UiOption b = my options.at [i];
 					char32 name2 [100];
@@ -1663,7 +1674,7 @@ Graphics_Colour UiForm_getColour_check (UiForm me, const char32 *fieldName) {
 }
 
 void UiForm_Interpreter_addVariables (UiForm me, Interpreter interpreter) {
-	static MelderString lowerCaseFieldName { 0 };
+	static MelderString lowerCaseFieldName { };
 	for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
 		UiField field = my field [ifield];
 		MelderString_copy (& lowerCaseFieldName, field -> name);
diff --git a/sys/abcio.cpp b/sys/abcio.cpp
index a0d7667..e193b85 100644
--- a/sys/abcio.cpp
+++ b/sys/abcio.cpp
@@ -161,14 +161,14 @@ static double getReal (MelderReadText me) {
 		double numerator, denominator;
 		*slash = '\0';
 		numerator = Melder_a8tof (buffer), denominator = Melder_a8tof (slash + 1);
-		if (numerator == HUGE_VAL || denominator == HUGE_VAL || denominator == 0.0)
-			return HUGE_VAL;
+		if (isundef (numerator) || isundef (denominator) || denominator == 0.0)
+			return undefined;
 		return numerator / denominator;
 	}
 	return Melder_a8tof (buffer);
 }
 
-static short getEnum (MelderReadText me, int (*getValue) (const char32 *)) {
+static int getEnum (MelderReadText me, int (*getValue) (const char32 *)) {
 	char32 buffer [41], c;
 	for (c = MelderReadText_getChar (me); c != U'<'; c = MelderReadText_getChar (me)) {
 		if (c == U'\0')
@@ -294,6 +294,17 @@ int32 texgeti32 (MelderReadText text) {
 	}
 }
 
+integer texgetinteger (MelderReadText text) {
+	try {
+		int64 externalValue = getInteger (text);
+		if (externalValue < INT32_MIN || externalValue > INT32_MAX)
+			Melder_throw (U"Value (", externalValue, U") out of range (-2147483648 .. +2147483647).");   // this will change
+		return (integer) externalValue;
+	} catch (MelderError) {
+		Melder_throw (U"Signed integer not read from text file.");
+	}
+}
+
 unsigned int texgetu8 (MelderReadText text) {
 	try {
 		uint64 externalValue = getUnsigned (text);
@@ -330,14 +341,14 @@ uint32 texgetu32 (MelderReadText text) {
 double texgetr32 (MelderReadText text) { return getReal (text); }
 double texgetr64 (MelderReadText text) { return getReal (text); }
 double texgetr80 (MelderReadText text) { return getReal (text); }
-fcomplex texgetc64 (MelderReadText text) { fcomplex z; z.re = getReal (text); z.im = getReal (text); return z; }
+dcomplex texgetc64  (MelderReadText text) { dcomplex z; z.re = getReal (text); z.im = getReal (text); return z; }
 dcomplex texgetc128 (MelderReadText text) { dcomplex z; z.re = getReal (text); z.im = getReal (text); return z; }
 
-short texgete8 (MelderReadText text, int (*getValue) (const char32 *)) { return getEnum (text, getValue); }
-short texgete16 (MelderReadText text, int (*getValue) (const char32 *)) { return getEnum (text, getValue); }
-bool texgeteb (MelderReadText text) { return getEnum (text, kBoolean_getValue); }
-bool texgeteq (MelderReadText text) { return getEnum (text, kQuestion_getValue); }
-bool texgetex (MelderReadText text) { return getEnum (text, kExistence_getValue); }
+int texgete8 (MelderReadText text, enum_generic_getValue getValue) { return getEnum (text, getValue); }
+int texgete16 (MelderReadText text, enum_generic_getValue getValue) { return getEnum (text, getValue); }
+bool texgeteb (MelderReadText text) { return getEnum (text, (enum_generic_getValue) kBoolean_getValue); }
+bool texgeteq (MelderReadText text) { return getEnum (text, (enum_generic_getValue) kQuestion_getValue); }
+bool texgetex (MelderReadText text) { return getEnum (text, (enum_generic_getValue) kExistence_getValue); }
 char *texgets16 (MelderReadText text) { return (char *) Melder_32to8 (getString (text)); }
 char *texgets32 (MelderReadText text) { return (char *) Melder_32to8 (getString (text)); }
 char32 *texgetw16 (MelderReadText text) { return Melder_dup   (getString (text)); }
@@ -391,6 +402,10 @@ void texputi32 (MelderFile file, long i, const char32 *s1, const char32 *s2, con
 	PUTLEADER
 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, i, file -> verbose ? U" " : nullptr);
 }
+void texputinteger (MelderFile file, integer number, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6) {
+	PUTLEADER
+	MelderFile_write (file, file -> verbose ? U" = " : nullptr, number, file -> verbose ? U" " : nullptr);
+}
 void texputu8 (MelderFile file, unsigned int u, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6) {
 	PUTLEADER
 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, u, file -> verbose ? U" " : nullptr);
@@ -411,15 +426,13 @@ void texputr64 (MelderFile file, double x, const char32 *s1, const char32 *s2, c
 	PUTLEADER
 	MelderFile_write (file, file -> verbose ? U" = " : nullptr, x, file -> verbose ? U" " : nullptr);
 }
-void texputc64 (MelderFile file, fcomplex z, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6) {
+void texputc64 (MelderFile file, dcomplex z, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6) {
 	PUTLEADER
-	MelderFile_write (file, file -> verbose ? U" = " : nullptr, Melder_single (z.re),
-		file -> verbose ? U" + " : U" ", Melder_single (z.im), file -> verbose ? U" i " : nullptr);
+	MelderFile_write (file, file -> verbose ? U" = " : nullptr, z, file -> verbose ? U" i " : nullptr);
 }
 void texputc128 (MelderFile file, dcomplex z, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6) {
 	PUTLEADER
-	MelderFile_write (file, file -> verbose ? U" = " : nullptr, z.re,
-		file -> verbose ? U" + " : U" ", z.im, file -> verbose ? U" i " : nullptr);
+	MelderFile_write (file, file -> verbose ? U" = " : nullptr, z, file -> verbose ? U" i " : nullptr);
 }
 void texpute8 (MelderFile file, int i, const char32 * (*getText) (int), const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6) {
 	PUTLEADER
@@ -593,7 +606,6 @@ void texputw32 (MelderFile file, const char32 *s, const char32 *s1, const char32
  *    Cray X/MP and Y/MP
  *    Digital Equipment VAX
  *
- *
  * Implemented by Malcolm Slaney and Ken Turkowski.
  *
  * Malcolm Slaney contributions during 1988-1990 include big- and little-
@@ -905,6 +917,26 @@ int32 bingeti32LE (FILE *f) {
 	}
 }
 
+integer bingetinteger (FILE *f) {
+	try {
+		if (binario_32bitBE && Melder_debug != 18) {
+			int32 l;
+			if (fread (& l, sizeof (int32), 1, f) != 1) readError (f, U"a signed 32-bit integer.");
+			return (integer) l;
+		} else {
+			uint8 bytes [4];
+			if (fread (bytes, sizeof (uint8), 4, f) != 4) readError (f, U"four bytes.");
+			return (integer) (int32)
+				((uint32) ((uint32) bytes [0] << 24) |
+				 (uint32) ((uint32) bytes [1] << 16) |
+				 (uint32) ((uint32) bytes [2] << 8) |
+						   (uint32) bytes [3]);
+		}
+	} catch (MelderError) {
+		Melder_throw (U"Signed integer not read from 4 bytes in binary file.");
+	}
+}
+
 uint32 bingetu32 (FILE *f) {
 	try {
 		if (binario_32bitBE && Melder_debug != 18) {
@@ -966,7 +998,7 @@ double bingetr32 (FILE *f) {
 				if (mantissa == 0) x = 0.0;
 				else x = ldexp ((double) mantissa, exponent - 149);   // denormalized
 			else if (exponent == 0x000000FF)   // Infinity or Not-a-Number
-				x = HUGE_VAL;
+				return undefined;
 			else   // finite
 				x = ldexp ((double) (mantissa | 0x00800000), exponent - 150);
 			return bytes [0] & 0x80 ? - x : x;
@@ -997,7 +1029,7 @@ double bingetr32LE (FILE *f) {
 				if (mantissa == 0) x = 0.0;
 				else x = ldexp ((double) mantissa, exponent - 149);   // denormalized
 			else if (exponent == 0x000000FF)   // Infinity or Not-a-Number
-				x = HUGE_VAL;
+				return undefined;
 			else   // finite
 				x = ldexp ((double) (mantissa | 0x00800000), exponent - 150);
 			return bytes [3] & 0x80 ? - x : x;
@@ -1009,7 +1041,7 @@ double bingetr32LE (FILE *f) {
 
 double bingetr64 (FILE *f) {
 	try {
-		if (binario_doubleIEEE8msb && Melder_debug != 18) {
+		if (binario_doubleIEEE8msb && Melder_debug != 18 || Melder_debug == 181) {
 			double x;
 			if (fread (& x, sizeof (double), 1, f) != 1) readError (f, U"a 64-bit floating-point number.");
 			return x;
@@ -1034,7 +1066,7 @@ double bingetr64 (FILE *f) {
 				else x = ldexp ((double) highMantissa, exponent - 1042) +
 					ldexp ((double) lowMantissa, exponent - 1074);   // denormalized
 			else if (exponent == 0x000007FF)   // Infinity or Not-a-Number
-				x = HUGE_VAL;
+				return undefined;
 			else
 				x = ldexp ((double) (highMantissa | 0x00100000), exponent - 1043) +
 					ldexp ((double) lowMantissa, exponent - 1075);
@@ -1063,8 +1095,8 @@ double bingetr80 (FILE *f) {
 			(uint32) ((uint32) bytes [8] << 8) |
 					  (uint32) bytes [9];
 		double x;
-		if (exponent == 0 && highMantissa == 0 && lowMantissa == 0) x = 0;
-		else if (exponent == 0x00007FFF) x = HUGE_VAL;   // Infinity or NaN
+		if (exponent == 0 && highMantissa == 0 && lowMantissa == 0) x = 0.0;
+		else if (exponent == 0x00007FFF) return undefined;   // Infinity or NaN
 		else {
 			exponent -= 16383;   // between -16382 and +16383
 			x = ldexp ((double) highMantissa, exponent - 31);
@@ -1237,6 +1269,21 @@ void binputi32LE (int32 i, FILE *f) {
 	}
 }
 
+void binputinteger (integer i, FILE *f) {
+	try {
+		if (i < INT32_MIN || i > INT32_MAX)
+			Melder_throw (U"The number ", i, U" is too big to fit into 32 bits.");   // this will change in the future
+		char bytes [4];
+		bytes [0] = (char) (i >> 24);   // truncate
+		bytes [1] = (char) (i >> 16);   // truncate
+		bytes [2] = (char) (i >> 8);   // truncate
+		bytes [3] = (char) i;   // truncate
+		if (fwrite (bytes, sizeof (char), 4, f) != 4) writeError (U"a signed 32-bit integer.");
+	} catch (MelderError) {
+		Melder_throw (U"Signed integer not written to 4 bytes in binary file.");
+	}
+}
+
 void binputu32 (uint32 u, FILE *f) {
 	try {
 		if (binario_32bitBE && Melder_debug != 18) {
@@ -1274,8 +1321,8 @@ void binputu32LE (uint32 u, FILE *f) {
 void binputr32 (double x, FILE *f) {
 	try {
 		if (binario_floatIEEE4msb && Melder_debug != 18) {
-			float x4 = (float) x;   // convert down, with loss of precision
-			if (fwrite (& x4, sizeof (float), 1, f) != 1) writeError (U"a 32-bit floating-point number.");
+			float x32 = (float) x;   // convert down, with loss of precision
+			if (fwrite (& x32, sizeof (float), 1, f) != 1) writeError (U"a 32-bit floating-point number.");
 		} else {
 			uint8 bytes [4];
 			int sign, exponent;
@@ -1286,7 +1333,7 @@ void binputr32 (double x, FILE *f) {
 			if (x == 0.0) { exponent = 0; mantissa = 0; }
 			else {
 				fMantissa = frexp (x, & exponent);
-				if ((exponent > 128) || ! (fMantissa < 1))   // Infinity or Not-a-Number
+				if ((exponent > 128) || ! (fMantissa < 1.0))   // Infinity or Not-a-Number
 					{ exponent = sign | 0x00FF; mantissa = 0; }   // Infinity
 				else {   // finite
 					exponent += 126;   // add bias
@@ -1314,8 +1361,8 @@ void binputr32 (double x, FILE *f) {
 void binputr32LE (double x, FILE *f) {
 	try {
 		if (binario_floatIEEE4lsb && Melder_debug != 18) {
-			float x4 = (float) x;   // convert down, with loss of precision
-			if (fwrite (& x4, sizeof (float), 1, f) != 1) writeError (U"a 32-bit floating-point number.");
+			float x32 = (float) x;   // convert down, with loss of precision
+			if (fwrite (& x32, sizeof (float), 1, f) != 1) writeError (U"a 32-bit floating-point number.");
 		} else {
 			uint8 bytes [4];
 			int sign, exponent;
@@ -1326,7 +1373,7 @@ void binputr32LE (double x, FILE *f) {
 			if (x == 0.0) { exponent = 0; mantissa = 0; }
 			else {
 				fMantissa = frexp (x, & exponent);
-				if ((exponent > 128) || ! (fMantissa < 1))   // Infinity or Not-a-Number
+				if ((exponent > 128) || ! (fMantissa < 1.0))   // Infinity or Not-a-Number
 					{ exponent = sign | 0x00FF; mantissa = 0; }   // Infinity
 				else {   // finite
 					exponent += 126;   // add bias
@@ -1336,7 +1383,7 @@ void binputr32LE (double x, FILE *f) {
 					}
 					exponent |= sign;
 					fMantissa = ldexp (fMantissa, 24);          
-					fsMantissa = floor (fMantissa); 
+					fsMantissa = floor (fMantissa);
 					mantissa = (uint32) fsMantissa & 0x007FFFFF;
 				}
 			}
@@ -1353,8 +1400,17 @@ void binputr32LE (double x, FILE *f) {
 
 void binputr64 (double x, FILE *f) {
 	try {
-		if (binario_doubleIEEE8msb && Melder_debug != 18) {
+		if (binario_doubleIEEE8msb && Melder_debug != 18 || Melder_debug == 181) {
 			if (fwrite (& x, sizeof (double), 1, f) != 1) writeError (U"a 64-bit floating-point number.");
+		} else if (binario_doubleIEEE8lsb && Melder_debug != 18) {
+			union { double xx; uint8 bytes [8]; };
+			xx = x;
+			uint8 tmp;
+			tmp = bytes [0], bytes [0] = bytes [7], bytes [7] = tmp;
+			tmp = bytes [1], bytes [1] = bytes [6], bytes [6] = tmp;
+			tmp = bytes [2], bytes [2] = bytes [5], bytes [5] = tmp;
+			tmp = bytes [3], bytes [3] = bytes [4], bytes [4] = tmp;
+			if (fwrite (& xx, sizeof (double), 1, f) != 1) writeError (U"a 64-bit floating-point number.");
 		} else {
 			uint8 bytes [8];
 			int sign, exponent;
@@ -1365,7 +1421,7 @@ void binputr64 (double x, FILE *f) {
 			if (x == 0.0) { exponent = 0; highMantissa = 0; lowMantissa = 0; }
 			else {
 				fMantissa = frexp (x, & exponent);
-				if ((exponent > 1024) || ! (fMantissa < 1))   // Infinity or Not-a-Number
+				if (/*(exponent > 1024) ||*/ ! (fMantissa < 1.0))   // Infinity or Not-a-Number
 					{ exponent = sign | 0x07FF; highMantissa = 0; lowMantissa = 0; }   // Infinity
 				else { // finite
 					exponent += 1022;   // add bias
@@ -1375,7 +1431,7 @@ void binputr64 (double x, FILE *f) {
 					}
 					exponent |= sign;
 					fMantissa = ldexp (fMantissa, 21);          
-					fsMantissa = floor (fMantissa); 
+					fsMantissa = floor (fMantissa);
 					highMantissa = (uint32) fsMantissa & 0x000FFFFF;
 					fMantissa = ldexp (fMantissa - fsMantissa, 32); 
 					fsMantissa = floor (fMantissa); 
@@ -1409,7 +1465,7 @@ void binputr80 (double x, FILE *f) {
 		if (x == 0.0) { exponent = 0; highMantissa = 0; lowMantissa = 0; }
 		else {
 			fMantissa = frexp (x, & exponent);
-			if ((exponent > 16384) || ! (fMantissa < 1))   // Infinity or Not-a-Number
+			if ((exponent > 16384) || ! (fMantissa < 1.0))   // Infinity or Not-a-Number
 				{ exponent = sign | 0x7FFF; highMantissa = 0; lowMantissa = 0; }   // Infinity
 			else {   // finite
 				exponent += 16382;   // add bias
@@ -1419,7 +1475,7 @@ void binputr80 (double x, FILE *f) {
 				}
 				exponent |= sign;
 				fMantissa = ldexp (fMantissa, 32);          
-				fsMantissa = floor (fMantissa); 
+				fsMantissa = floor (fMantissa);
 				highMantissa = (uint32) fsMantissa;
 				fMantissa = ldexp (fMantissa - fsMantissa, 32); 
 				fsMantissa = floor (fMantissa); 
@@ -1442,15 +1498,15 @@ void binputr80 (double x, FILE *f) {
 	}
 }
 
-fcomplex bingetc64 (FILE *f) {
+dcomplex bingetc64 (FILE *f) {
 	try {
-		fcomplex result;
-		result. re = bingetr32 (f);
-		result. im = bingetr32 (f);
+		dcomplex result;
+		result.re = bingetr32 (f);
+		result.im = bingetr32 (f);
 		return result;
 	} catch (MelderError) {
 		Melder_throw (U"Complex number not read from 8 bytes in binary file.");
-		fcomplex result { };
+		dcomplex result { };
 		return result;
 	}
 }
@@ -1458,8 +1514,8 @@ fcomplex bingetc64 (FILE *f) {
 dcomplex bingetc128 (FILE *f) {
 	try {
 		dcomplex result;
-		result. re = bingetr64 (f);
-		result. im = bingetr64 (f);
+		result.re = bingetr64 (f);
+		result.im = bingetr64 (f);
 		return result;
 	} catch (MelderError) {
 		Melder_throw (U"Complex number not read from 16 bytes in binary file.");
@@ -1468,10 +1524,10 @@ dcomplex bingetc128 (FILE *f) {
 	}
 }
 
-void binputc64 (fcomplex z, FILE *f) {
+void binputc64 (dcomplex z, FILE *f) {
 	try {
-		binputr32 (z. re, f);
-		binputr32 (z. im, f);
+		binputr32 (z.re, f);
+		binputr32 (z.im, f);
 	} catch (MelderError) {
 		Melder_throw (U"Complex number not written to 8 bytes in binary file.");
 	}
@@ -1479,8 +1535,8 @@ void binputc64 (fcomplex z, FILE *f) {
 
 void binputc128 (dcomplex z, FILE *f) {
 	try {
-		binputr64 (z. re, f);
-		binputr64 (z. im, f);
+		binputr64 (z.re, f);
+		binputr64 (z.im, f);
 	} catch (MelderError) {
 		Melder_throw (U"Complex number not written to 16 bytes in binary file.");
 	}
@@ -1528,14 +1584,14 @@ char * bingets32 (FILE *f) {
 char32 * bingetw8 (FILE *f) {
 	try {
 		autostring32 result;
-		unsigned short length = bingetu8 (f);
+		unsigned int length = bingetu8 (f);
 		if (length == 0xFF) {   // an escape for encoding
 			/*
 			 * UTF-16
 			 */
 			length = bingetu8 (f);
 			result.reset (Melder_malloc (char32, (int64) length + 1));
-			for (unsigned short i = 0; i < length; i ++) {
+			for (unsigned int i = 0; i < length; i ++) {
 				char32 kar = bingetu16 (f);
 				if ((kar & 0x00F800) == 0x00D800) {
 					if (kar > 0x00DBFF)
@@ -1553,7 +1609,7 @@ char32 * bingetw8 (FILE *f) {
 			 * ASCII
 			 */
 			result.reset (Melder_malloc (char32, (int64) length + 1));
-			for (unsigned short i = 0; i < length; i ++) {
+			for (unsigned int i = 0; i < length; i ++) {
 				result [i] = bingetu8 (f);
 			}
 		}
diff --git a/sys/abcio.h b/sys/abcio.h
index 788981a..a2f2dbb 100644
--- a/sys/abcio.h
+++ b/sys/abcio.h
@@ -28,16 +28,17 @@
 int texgeti8 (MelderReadText text);
 int16 texgeti16 (MelderReadText text);
 int32 texgeti32 (MelderReadText text);
+integer texgetinteger (MelderReadText text);
 unsigned int texgetu8 (MelderReadText text);
 uint16 texgetu16 (MelderReadText text);
 uint32 texgetu32 (MelderReadText text);
 double texgetr32 (MelderReadText text);
 double texgetr64 (MelderReadText text);
 double texgetr80 (MelderReadText text);
-fcomplex texgetc64 (MelderReadText text);
+dcomplex texgetc64 (MelderReadText text);
 dcomplex texgetc128 (MelderReadText text);
-short texgete8 (MelderReadText text, int (*getValue) (const char32 *));
-short texgete16 (MelderReadText text, int (*getValue) (const char32 *));
+int texgete8 (MelderReadText text, enum_generic_getValue getValue);
+int texgete16 (MelderReadText text, enum_generic_getValue getValue);
 bool texgeteb (MelderReadText text);
 bool texgeteq (MelderReadText text);
 bool texgetex (MelderReadText text);
@@ -54,12 +55,13 @@ void texputintro (MelderFile file, const char32 *s1, const char32 *s2, const cha
 void texputi8 (MelderFile file, int i, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputi16 (MelderFile file, int i, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputi32 (MelderFile file, long i, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
+void texputinteger (MelderFile file, integer i, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputu8 (MelderFile file, unsigned int u, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputu16 (MelderFile file, unsigned int u, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputu32 (MelderFile file, unsigned long u, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputr32 (MelderFile file, double x, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputr64 (MelderFile file, double x, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
-void texputc64 (MelderFile file, fcomplex z, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
+void texputc64 (MelderFile file, dcomplex z, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texputc128 (MelderFile file, dcomplex z, const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texpute8 (MelderFile file, int i, const char32 * (*getText) (int), const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
 void texpute16 (MelderFile file, int i, const char32 * (*getText) (int), const char32 *s1, const char32 *s2, const char32 *s3, const char32 *s4, const char32 *s5, const char32 *s6);
@@ -87,6 +89,7 @@ int bingeti8 (FILE *f);   void binputi8 (int i, FILE *f);   /* -128..127 */
 int16 bingeti16 (FILE *f);   void binputi16 (int16 i, FILE *f);   // -32768..32767
 int32 bingeti24 (FILE *f);   void binputi24 (int32 i, FILE *f);   // -8388608..8388607
 int32 bingeti32 (FILE *f);   void binputi32 (int32 i, FILE *f);   // -2147483648..2147483647
+integer bingetinteger (FILE *f);   void binputinteger (integer i, FILE *f);
 /*
 	Read or write signed or unsigned integers from or to 2 or 4 bytes in the stream 'f',
 	in big-endian byte order (most significant byte first).
@@ -173,9 +176,9 @@ double bingetr80 (FILE *f);   void binputr80 (double x, FILE *f);
 	and is the native format of a `double` on 68k Macintosh.
 */
 
-fcomplex bingetc64 (FILE *f);
+dcomplex bingetc64 (FILE *f);
 dcomplex bingetc128 (FILE *f);
-void binputc64 (fcomplex z, FILE *f);
+void binputc64 (dcomplex z, FILE *f);
 void binputc128 (dcomplex z, FILE *f);
 
 char * bingets8 (FILE *f);   void binputs8 (const char *s, FILE *f);   // 0..255 characters
diff --git a/sys/abcio_enums.h b/sys/abcio_enums.h
index 359a407..3bc1947 100644
--- a/sys/abcio_enums.h
+++ b/sys/abcio_enums.h
@@ -1,6 +1,6 @@
 /* abcio_enums.h
  *
- * Copyright (C) 1992-2009,2015 Paul Boersma
+ * Copyright (C) 1992-2009,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,9 +17,9 @@
  */
 
 enums_begin (kBoolean, 0)
-	enums_add (kBoolean, 0, FALSE, U"false")
-	enums_add (kBoolean, 1, TRUE, U"true")
-enums_end (kBoolean, 1, FALSE)
+	enums_add (kBoolean, 0, FALSE_, U"false")
+	enums_add (kBoolean, 1, TRUE_, U"true")
+enums_end (kBoolean, 1, FALSE_)
 
 enums_begin (kQuestion, 0)
 	enums_add (kQuestion, 0, NO, U"no")
diff --git a/sys/complex.cpp b/sys/complex.cpp
index de6aff5..aa4b3d6 100644
--- a/sys/complex.cpp
+++ b/sys/complex.cpp
@@ -1,6 +1,6 @@
 /* complex.cpp
  *
- * Copyright (C) 1992-2011 Paul Boersma
+ * Copyright (C) 1992-2011,2017 Paul Boersma
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,167 +19,6 @@
 #include <math.h>
 #include "complex.h"
 
-fcomplex fcomplex_add (fcomplex a, fcomplex b) {
-	fcomplex result;
-	result.re = a.re + b.re;
-	result.im = a.im + b.im;
-	return result;
-}
-
-dcomplex dcomplex_add (dcomplex a, dcomplex b) {
-	dcomplex result;
-	result.re = a.re + b.re;
-	result.im = a.im + b.im;
-	return result;
-}
-
-fcomplex fcomplex_sub (fcomplex a, fcomplex b) {
-	fcomplex result;
-	result.re = a.re - b.re;
-	result.im = a.im - b.im;
-	return result;
-}
-
-dcomplex dcomplex_sub (dcomplex a, dcomplex b) {
-	dcomplex result;
-	result.re = a.re - b.re;
-	result.im = a.im - b.im;
-	return result;
-}
-
-fcomplex fcomplex_mul (fcomplex a, fcomplex b) {
-	fcomplex result;
-	result.re = a.re * b.re - a.im * b.im;
-	result.im = a.im * b.re + a.re * b.im;
-	return result;
-}
-
-dcomplex dcomplex_mul (dcomplex a, dcomplex b) {
-	dcomplex result;
-	result.re = a.re * b.re - a.im * b.im;
-	result.im = a.im * b.re + a.re * b.im;
-	return result;
-}
-
-fcomplex fcomplex_create (float re, float im) {
-	fcomplex result;
-	result.re = re;
-	result.im = im;
-	return result;
-}
-
-dcomplex dcomplex_create (double re, double im) {
-	dcomplex result;
-	result.re = re;
-	result.im = im;
-	return result;
-}
-
-fcomplex fcomplex_conjugate (fcomplex z) {
-	fcomplex result;
-	result.re = z.re;
-	result.im = - z.im;
-	return result;
-}
-
-dcomplex dcomplex_conjugate (dcomplex z) {
-	dcomplex result;
-	result.re = z.re;
-	result.im = - z.im;
-	return result;
-}
-
-fcomplex fcomplex_div (fcomplex a, fcomplex b) {
-	fcomplex result;
-	float r, den;
-	if (fabs (b.re) >= fabs (b.im)) {
-		r = b.im / b.re;
-		den = b.re + r * b.im;
-		result.re = (a.re + r * a.im) / den;
-		result.im = (a.im - r * a.re) / den;
-	} else {
-		r = b.re / b.im;
-		den = b.im + r * b.re;
-		result.re = (a.re * r + a.im) / den;
-		result.im = (a.im * r - a.re) / den;
-	}
-	return result;
-}
-
-dcomplex dcomplex_div (dcomplex a, dcomplex b) {
-	dcomplex result;
-	double r, den;
-	if (fabs (b.re) >= fabs (b.im)) {
-		r = b.im / b.re;
-		den = b.re + r * b.im;
-		result.re = (a.re + r * a.im) / den;
-		result.im = (a.im - r * a.re) / den;
-	} else {
-		r = b.re / b.im;
-		den = b.im + r * b.re;
-		result.re = (a.re * r + a.im) / den;
-		result.im = (a.im * r - a.re) / den;
-	}
-	return result;
-}
-
-float fcomplex_abs (fcomplex z) {
-	float x, y, temp;
-	x = fabs (z.re);
-	y = fabs (z.im);
-	if (x == 0.0) return y;
-	if (y == 0.0) return x;
-	if (x > y) {
-		temp = y / x;
-		return x * sqrt (1.0 + temp * temp);
-	} else {
-		temp = x / y;
-		return y * sqrt (1.0 + temp * temp);
-	}
-}
-
-double dcomplex_abs (dcomplex z) {
-	double x, y, temp;
-	x = fabs (z.re);
-	y = fabs (z.im);
-	if (x == 0.0) return y;
-	if (y == 0.0) return x;
-	if (x > y) {
-		temp = y / x;
-		return x * sqrt (1.0 + temp * temp);
-	} else {
-		temp = x / y;
-		return y * sqrt (1.0 + temp * temp);
-	}
-}
-
-fcomplex fcomplex_sqrt (fcomplex z) {
-	fcomplex result;
-	float x, y, w, r;
-	if (z.re == 0 && z.im == 0) {
-		result.re = 0;
-		result.im = 0;
-		return result;
-	}
-	x = fabs (z.re);
-	y = fabs (z.im);
-	if (x >= y) {
-		r = y / x;
-		w = sqrt (x) * sqrt (0.5 * (1.0 + sqrt (1.0 + r * r)));
-	} else {
-		r = x / y;
-		w = sqrt (y) * sqrt (0.5 * (r + sqrt (1.0 + r * r)));
-	}
-	if (z.re >= 0.0) {
-		result.re = w;
-		result.im = z.im / (2.0 * w);
-	} else {
-		result.im = (z.im >= 0) ? w : -w;
-		result.re = z.im / (2.0 * result.im);
-	}
-	return result;
-}
-
 dcomplex dcomplex_sqrt (dcomplex z) {
 	dcomplex result;
 	double x, y, w, r;
@@ -207,34 +46,4 @@ dcomplex dcomplex_sqrt (dcomplex z) {
 	return result;
 }
 
-fcomplex fcomplex_rmul (float x, fcomplex a) {
-	fcomplex result;
-	result.re = x * a.re;
-	result.im = x * a.im;
-	return result;
-}
-
-dcomplex dcomplex_rmul (double x, dcomplex a) {
-	dcomplex result;
-	result.re = x * a.re;
-	result.im = x * a.im;
-	return result;
-}
-
-fcomplex fcomplex_exp (fcomplex z) {
-	fcomplex result;
-	double size = exp (z.re);
-	result.re = size * cos (z.im);
-	result.im = size * sin (z.im);
-	return result;
-}
-
-dcomplex dcomplex_exp (dcomplex z) {
-	dcomplex result;
-	double size = exp (z.re);
-	result.re = size * cos (z.im);
-	result.im = size * sin (z.im);
-	return result;
-}
-
 /* End of file complex.cpp */
diff --git a/sys/complex.h b/sys/complex.h
index bc7f966..eeb5e33 100644
--- a/sys/complex.h
+++ b/sys/complex.h
@@ -18,45 +18,86 @@
  * along with this work. If not, see <http://www.gnu.org/licenses/>.
  */
 
-struct fcomplex { float re, im; };
+#include <math.h>
+
 struct dcomplex { double re, im; };
 
-/*
- * Stack-based complex arithmetic.
- * Some compilers will issue warnings about returning structs larger than 8 bytes,
- * but will still work as expected.
- */
+inline static dcomplex dcomplex_add (dcomplex a, dcomplex b) {
+	dcomplex result;
+	result.re = a.re + b.re;
+	result.im = a.im + b.im;
+	return result;
+}
+
+inline static dcomplex dcomplex_sub (dcomplex a, dcomplex b) {
+	dcomplex result;
+	result.re = a.re - b.re;
+	result.im = a.im - b.im;
+	return result;
+}
+
+inline static dcomplex dcomplex_mul (dcomplex a, dcomplex b) {
+	dcomplex result;
+	result.re = a.re * b.re - a.im * b.im;
+	result.im = a.im * b.re + a.re * b.im;
+	return result;
+}
+
+inline static dcomplex dcomplex_conjugate (dcomplex z) {
+	dcomplex result;
+	result.re = z.re;
+	result.im = - z.im;
+	return result;
+}
+
+inline static dcomplex dcomplex_div (dcomplex a, dcomplex b) {
+	dcomplex result;
+	double r, den;
+	if (fabs (b.re) >= fabs (b.im)) {
+		r = b.im / b.re;
+		den = b.re + r * b.im;
+		result.re = (a.re + r * a.im) / den;
+		result.im = (a.im - r * a.re) / den;
+	} else {
+		r = b.re / b.im;
+		den = b.im + r * b.re;
+		result.re = (a.re * r + a.im) / den;
+		result.im = (a.im * r - a.re) / den;
+	}
+	return result;
+}
+
+inline static double dcomplex_abs (dcomplex z) {
+	double x, y, temp;
+	x = fabs (z.re);
+	y = fabs (z.im);
+	if (x == 0.0) return y;
+	if (y == 0.0) return x;
+	if (x > y) {
+		temp = y / x;
+		return x * sqrt (1.0 + temp * temp);
+	} else {
+		temp = x / y;
+		return y * sqrt (1.0 + temp * temp);
+	}
+}
+
+inline static dcomplex dcomplex_rmul (double x, dcomplex a) {
+	dcomplex result;
+	result.re = x * a.re;
+	result.im = x * a.im;
+	return result;
+}
+
+inline static dcomplex dcomplex_exp (dcomplex z) {
+	dcomplex result;
+	double size = exp (z.re);
+	result.re = size * cos (z.im);
+	result.im = size * sin (z.im);
+	return result;
+}
 
-fcomplex fcomplex_add (fcomplex a, fcomplex b);
-dcomplex dcomplex_add (dcomplex a, dcomplex b);
-	/* Addition: a + b */
-fcomplex fcomplex_sub (fcomplex a, fcomplex b);
-dcomplex dcomplex_sub (dcomplex a, dcomplex b);
-	/* Subtraction: a - b */
-fcomplex fcomplex_mul (fcomplex a, fcomplex b);
-dcomplex dcomplex_mul (dcomplex a, dcomplex b);
-	/* Multiplication: a * b */
-fcomplex fcomplex_create (float re, float im);
-dcomplex dcomplex_create (double re, double im);
-	/* Create a complex number: { re, im } */
-fcomplex fcomplex_conjugate (fcomplex z);
-dcomplex dcomplex_conjugate (dcomplex z);
-	/* Conjugation: { z.re, - z.im } */
-fcomplex fcomplex_div (fcomplex a, fcomplex b);
-dcomplex dcomplex_div (dcomplex a, dcomplex b);
-	/* Division: a / b */
-float fcomplex_abs (fcomplex z);
-double dcomplex_abs (dcomplex z);
-	/* Absolute value: | z | */
-fcomplex fcomplex_sqrt (fcomplex z);
 dcomplex dcomplex_sqrt (dcomplex z);
-	/* Square root: sqrt (z) */
-fcomplex fcomplex_rmul (float x, fcomplex a);
-dcomplex dcomplex_rmul (double x, dcomplex a);
-	/* Multiplication by a real number: x * a */
-fcomplex fcomplex_exp (fcomplex z);
-dcomplex dcomplex_exp (dcomplex z);
-	/* Exponentiation: e^z */
 
 /* End of file complex.h */
 #endif
diff --git a/sys/enums.h b/sys/enums.h
index 31d69a2..c1d8bdc 100644
--- a/sys/enums.h
+++ b/sys/enums.h
@@ -2,7 +2,7 @@
 #define _enums_h_
 /* enums.h
  *
- * Copyright (C) 2007,2013,2015 Paul Boersma
+ * Copyright (C) 2007,2013,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -18,13 +18,15 @@
  * along with this work. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#define enums_begin(type,minimum)  enum type { type##_MIN = minimum,
-#define enums_add(type,value,which,text)  type##_##which = value,
-#define enums_alt(type,which,text)
-#define enums_end(type,maximum,def) type##_MAX = maximum, \
-	type##_DEFAULT = type##_##def }; \
-	const char32 * type##_getText (int value); \
-	int type##_getValue (const char32 *text);
+typedef int (*enum_generic_getValue) (const char32 *text);
+
+#define enums_begin(kType,minimum)  enum class kType { MIN = minimum,
+#define enums_add(kType,value,which,text)  which = value,
+#define enums_alt(kType,which,text)
+#define enums_end(kType,maximum,default) MAX = maximum, \
+	DEFAULT = default }; \
+	const char32 * kType##_getText (kType value); \
+	kType kType##_getValue (const char32 *text);
 
 /* End of file enums.h */
 #endif
diff --git a/sys/enums_getText.h b/sys/enums_getText.h
index 23581b1..cdec5d2 100644
--- a/sys/enums_getText.h
+++ b/sys/enums_getText.h
@@ -1,6 +1,6 @@
 /* enums_getText.h
  *
- * Copyright (C) 2007,2015 Paul Boersma
+ * Copyright (C) 2007,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,9 +24,9 @@
 #undef enums_add
 #undef enums_alt
 #undef enums_end
-#define enums_begin(type,minimum)  const char32 * type##_getText (int testValue) { return
-#define enums_add(type,value,which,text)  testValue == type##_##which ? text :
-#define enums_alt(type,which,text)
-#define enums_end(type,maximum,def) type##_getText (type##_DEFAULT); }
+#define enums_begin(kType,minimum)  const char32 * kType##_getText (kType testValue) { return
+#define enums_add(kType,value,which,text)  testValue == kType::which ? text :
+#define enums_alt(kType,which,text)
+#define enums_end(kType,maximum,def) kType##_getText (kType::DEFAULT); }
 
 /* End of file enums_getText.h */
diff --git a/sys/enums_getValue.h b/sys/enums_getValue.h
index fb079dd..ab4f763 100644
--- a/sys/enums_getValue.h
+++ b/sys/enums_getValue.h
@@ -24,12 +24,12 @@
 #undef enums_add
 #undef enums_alt
 #undef enums_end
-#define enums_begin(type,minimum)  int type##_getValue (const char32 *testText) {
-#define enums_add(type,value,which,text)  if (Melder_equ_firstCharacterCaseInsensitive (testText, text)) return type##_##which;
-#define enums_alt(type,which,text)  if (Melder_equ_firstCharacterCaseInsensitive (testText, text)) return type##_##which;
-#define enums_end(type,maximum,def) \
-	if (str32equ (testText, U"\t")) return type##_DEFAULT; \
-	if (str32equ (testText, U"\n")) return maximum; \
-	return -1; }
+#define enums_begin(kType,minimum)  kType kType##_getValue (const char32 *testText) {
+#define enums_add(kType,value,which,text)  if (Melder_equ_firstCharacterCaseInsensitive (testText, text)) return kType::which;
+#define enums_alt(kType,which,text)  if (Melder_equ_firstCharacterCaseInsensitive (testText, text)) return kType::which;
+#define enums_end(kType,maximum,def) \
+	if (str32equ (testText, U"\t")) return kType::DEFAULT; \
+	if (str32equ (testText, U"\n")) return (kType) maximum; \
+	return (kType) -1; }
 
 /* End of file enums_getValue.h */
diff --git a/sys/melder.cpp b/sys/melder.cpp
index c8b25f7..3426af2 100644
--- a/sys/melder.cpp
+++ b/sys/melder.cpp
@@ -1,6 +1,6 @@
 /* melder.cpp
  *
- * Copyright (C) 1992-2012,2013,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2012,2013,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -257,41 +257,41 @@ void * Melder_monitor (double progress, Melder_19_ARGS) {
 
 /********** NUMBER AND STRING COMPARISONS **********/
 
-bool Melder_numberMatchesCriterion (double value, int which_kMelder_number, double criterion) {
+bool Melder_numberMatchesCriterion (double value, kMelder_number which, double criterion) {
 	return
-		(which_kMelder_number == kMelder_number_EQUAL_TO && value == criterion) ||
-		(which_kMelder_number == kMelder_number_NOT_EQUAL_TO && value != criterion) ||
-		(which_kMelder_number == kMelder_number_LESS_THAN && value < criterion) ||
-		(which_kMelder_number == kMelder_number_LESS_THAN_OR_EQUAL_TO && value <= criterion) ||
-		(which_kMelder_number == kMelder_number_GREATER_THAN && value > criterion) ||
-		(which_kMelder_number == kMelder_number_GREATER_THAN_OR_EQUAL_TO && value >= criterion);
+		(which == kMelder_number::EQUAL_TO && value == criterion) ||
+		(which == kMelder_number::NOT_EQUAL_TO && value != criterion) ||
+		(which == kMelder_number::LESS_THAN && value < criterion) ||
+		(which == kMelder_number::LESS_THAN_OR_EQUAL_TO && value <= criterion) ||
+		(which == kMelder_number::GREATER_THAN && value > criterion) ||
+		(which == kMelder_number::GREATER_THAN_OR_EQUAL_TO && value >= criterion);
 }
 
-bool Melder_stringMatchesCriterion (const char32 *value, int which_kMelder_string, const char32 *criterion) {
+bool Melder_stringMatchesCriterion (const char32 *value, kMelder_string which, const char32 *criterion) {
 	if (! value) {
 		value = U"";   // regard null strings as empty strings, as is usual in Praat
 	}
 	if (! criterion) {
 		criterion = U"";   // regard null strings as empty strings, as is usual in Praat
 	}
-	if (which_kMelder_string <= kMelder_string_NOT_EQUAL_TO) {
+	if (which <= kMelder_string::NOT_EQUAL_TO) {
 		bool matchPositiveCriterion = str32equ (value, criterion);
-		return ( which_kMelder_string == kMelder_string_EQUAL_TO ) == matchPositiveCriterion;
+		return ( which == kMelder_string::EQUAL_TO ) == matchPositiveCriterion;
 	}
-	if (which_kMelder_string <= kMelder_string_DOES_NOT_CONTAIN) {
+	if (which <= kMelder_string::DOES_NOT_CONTAIN) {
 		bool matchPositiveCriterion = !! str32str (value, criterion);
-		return ( which_kMelder_string == kMelder_string_CONTAINS ) == matchPositiveCriterion;
+		return ( which == kMelder_string::CONTAINS ) == matchPositiveCriterion;
 	}
-	if (which_kMelder_string <= kMelder_string_DOES_NOT_START_WITH) {
+	if (which <= kMelder_string::DOES_NOT_START_WITH) {
 		bool matchPositiveCriterion = str32nequ (value, criterion, str32len (criterion));
-		return ( which_kMelder_string == kMelder_string_STARTS_WITH ) == matchPositiveCriterion;
+		return ( which == kMelder_string::STARTS_WITH ) == matchPositiveCriterion;
 	}
-	if (which_kMelder_string <= kMelder_string_DOES_NOT_END_WITH) {
+	if (which <= kMelder_string::DOES_NOT_END_WITH) {
 		int criterionLength = str32len (criterion), valueLength = str32len (value);
 		bool matchPositiveCriterion = ( criterionLength <= valueLength && str32equ (value + valueLength - criterionLength, criterion) );
-		return (which_kMelder_string == kMelder_string_ENDS_WITH) == matchPositiveCriterion;
+		return (which == kMelder_string::ENDS_WITH) == matchPositiveCriterion;
 	}
-	if (which_kMelder_string == kMelder_string_MATCH_REGEXP) {
+	if (which == kMelder_string::MATCH_REGEXP) {
 		char32 *place = nullptr;
 		regexp *compiled_regexp = CompileRE_throwable (criterion, 0);
 		if (ExecRE (compiled_regexp, nullptr, value, nullptr, 0, '\0', '\0', nullptr, nullptr, nullptr))
@@ -771,11 +771,11 @@ void Melder_assert_ (const char *fileName, int lineNumber, const char *condition
 	 */
 	MelderThread_LOCK (theMelder_fatal_mutex);
 	static char32 fileNameBuffer [1000], conditionBuffer [1000], lineNumberBuffer [40];
-	Melder_8to32_inline (fileName, fileNameBuffer, kMelder_textInputEncoding_UTF8);
-	Melder_8to32_inline (condition, conditionBuffer, kMelder_textInputEncoding_UTF8);
+	Melder_8to32_inline (fileName, fileNameBuffer, (int) kMelder_textInputEncoding::UTF8);
+	Melder_8to32_inline (condition, conditionBuffer, (int) kMelder_textInputEncoding::UTF8);
 	static char lineNumberBuffer8 [40];
 	sprintf (lineNumberBuffer8, "%d", lineNumber);
-	Melder_8to32_inline (lineNumberBuffer8, lineNumberBuffer, kMelder_textInputEncoding_UTF8);
+	Melder_8to32_inline (lineNumberBuffer8, lineNumberBuffer, (int) kMelder_textInputEncoding::UTF8);
 	str32cpy (theFatalBuffer, theCrashMessage);
 	str32cpy (theFatalBuffer + str32len (theFatalBuffer), U"Assertion failed in file \"");
 	str32cpy (theFatalBuffer + str32len (theFatalBuffer), fileNameBuffer);
diff --git a/sys/melder.h b/sys/melder.h
index 980b6af..2e05f0d 100644
--- a/sys/melder.h
+++ b/sys/melder.h
@@ -90,6 +90,7 @@ typedef float real32;
 typedef double real64;
 typedef long double real80;   // at least 80 bits ("extended") precision, but stored in 96 or 128 bits
 typedef double real;
+#include "complex.h"
 
 #pragma mark - LAW OF DEMETER FOR CLASS FUNCTIONS DEFINED OUTSIDE CLASS DEFINITION
 
@@ -120,10 +121,10 @@ typedef char32_t char32;
 #define strequ  ! strcmp
 #define strnequ  ! strncmp
 
-inline static int64 str16len (const char16 *string) noexcept {
+inline static integer str16len (const char16 *string) noexcept {
 	const char16 *p = string;
 	while (*p != u'\0') ++ p;
-	return (int64) (p - string);
+	return p - string;
 }
 inline static char16 * str16cpy (char16 *target, const char16 *source) noexcept {
 	char16 *p = target;
@@ -132,10 +133,10 @@ inline static char16 * str16cpy (char16 *target, const char16 *source) noexcept
 	return target;
 }
 
-inline static int64 str32len (const char32 *string) noexcept {
+inline static integer str32len (const char32 *string) noexcept {
 	const char32 *p = string;
 	while (*p != U'\0') ++ p;
-	return (int64) (p - string);
+	return p - string;
 }
 inline static char32 * str32cpy (char32 *target, const char32 *source) noexcept {
 	char32 *p = target;
@@ -143,7 +144,7 @@ inline static char32 * str32cpy (char32 *target, const char32 *source) noexcept
 	*p = U'\0';
 	return target;
 }
-inline static char32 * str32ncpy (char32 *target, const char32 *source, int64 n) noexcept {
+inline static char32 * str32ncpy (char32 *target, const char32 *source, integer n) noexcept {
 	char32 *p = target;
 	for (; n > 0 && *source != U'\0'; -- n) * p ++ = * source ++;
 	for (; n > 0; -- n) * p ++ = U'\0';
@@ -157,7 +158,7 @@ inline static int str32cmp (const char32 *string1, const char32 *string2) noexce
 		if (*string1 == U'\0') return 0;
 	}
 }
-inline static int str32ncmp (const char32 *string1, const char32 *string2, int64 n) noexcept {
+inline static int str32ncmp (const char32 *string1, const char32 *string2, integer n) noexcept {
 	for (; n > 0; -- n, ++ string1, ++ string2) {
 		int32 diff = (int32) *string1 - (int32) *string2;
 		if (diff) return (int) diff;
@@ -166,7 +167,7 @@ inline static int str32ncmp (const char32 *string1, const char32 *string2, int64
 	return 0;
 }
 int Melder_cmp (const char32 *string1, const char32 *string2);   // regards null string as empty string
-int Melder_ncmp (const char32 *string1, const char32 *string2, int64 n);
+int Melder_ncmp (const char32 *string1, const char32 *string2, integer n);
 
 #define str32equ  ! str32cmp
 #define str32nequ  ! str32ncmp
@@ -189,7 +190,7 @@ inline static char32 * str32rchr (const char32 *string, char32 kar) noexcept {
 	return result;
 }
 inline static char32 * str32str (const char32 *string, const char32 *find) noexcept {
-	int64 length = str32len (find);
+	integer length = str32len (find);
 	if (length == 0) return (char32 *) string;
 	char32 firstCharacter = * find ++;   // optimization
 	do {
@@ -201,7 +202,7 @@ inline static char32 * str32str (const char32 *string, const char32 *find) noexc
 	} while (str32ncmp (string, find, length - 1));
 	return (char32 *) (string - 1);
 }
-inline static int64 str32spn (const char32 *string1, const char32 *string2) noexcept {
+inline static integer str32spn (const char32 *string1, const char32 *string2) noexcept {
 	const char32 *p = string1;
 	char32 kar1, kar2;
 cont:
@@ -238,51 +239,65 @@ typedef struct { double red, green, blue, transparency; } double_rgbt;
 	You can call at most 32 of them in one Melder_casual call, for instance.
 */
 
-const  char32 * Melder_integer  (int64 value) noexcept;
-const  char   * Melder8_integer (int64 value) noexcept;
+const char32 * Melder_integer  (int64 value) noexcept;
+const char   * Melder8_integer (int64 value) noexcept;
 
-const  char32 * Melder_bigInteger  (int64 value) noexcept;
-const  char   * Melder8_bigInteger (int64 value) noexcept;
+const char32 * Melder_bigInteger  (int64 value) noexcept;
+const char   * Melder8_bigInteger (int64 value) noexcept;
 
-const  char32 * Melder_boolean  (bool value) noexcept;
-const  char   * Melder8_boolean (bool value) noexcept;
+const char32 * Melder_boolean  (bool value) noexcept;
+const char   * Melder8_boolean (bool value) noexcept;
 	// "yes" or "no"
 
 /**
 	Format a double value as "--undefined--" or something in the "%.15g", "%.16g", or "%.17g" formats.
 */
-const  char32 * Melder_double  (double value) noexcept;
-const  char   * Melder8_double (double value) noexcept;
+const char32 * Melder_double  (double value) noexcept;
+const char   * Melder8_double (double value) noexcept;
 
 /**
 	Format a double value as "--undefined--" or something in the "%.9g" format.
 */
-const  char32 * Melder_single  (double value) noexcept;
-const  char   * Melder8_single (double value) noexcept;
+const char32 * Melder_single  (double value) noexcept;
+const char   * Melder8_single (double value) noexcept;
 
 /**
 	Format a double value as "--undefined--" or something in the "%.4g" format.
 */
-const  char32 * Melder_half  (double value) noexcept;
-const  char   * Melder8_half (double value) noexcept;
+const char32 * Melder_half  (double value) noexcept;
+const char   * Melder8_half (double value) noexcept;
 
 /**
 	Format a double value as "--undefined--" or something in the "%.*f" format.
 */
-const  char32 * Melder_fixed  (double value, int precision) noexcept;
-const  char   * Melder8_fixed (double value, int precision) noexcept;
+const char32 * Melder_fixed  (double value, int precision) noexcept;
+const char   * Melder8_fixed (double value, int precision) noexcept;
 
 /**
 	Format a double value with a specified precision. If exponent is -2 and precision is 2, you get things like 67E-2 or 0.00024E-2.
 */
-const  char32 * Melder_fixedExponent  (double value, int exponent, int precision) noexcept;
-const  char   * Melder8_fixedExponent (double value, int exponent, int precision) noexcept;
+const char32 * Melder_fixedExponent  (double value, int exponent, int precision) noexcept;
+const char   * Melder8_fixedExponent (double value, int exponent, int precision) noexcept;
 
 /**
 	Format a double value as a percentage. If precision is 3, you get things like "0" or "34.400%" or "0.014%" or "0.001%" or "0.0000007%".
 */
-const  char32 * Melder_percent  (double value, int precision) noexcept;
-const  char   * Melder8_percent (double value, int precision) noexcept;
+const char32 * Melder_percent  (double value, int precision) noexcept;
+const char   * Melder8_percent (double value, int precision) noexcept;
+
+/**
+	Format a dcomplex value as "--undefined--" or something in the "%.15g", "%.16g", or "%.17g" formats,
+	separated without spaces by "+" or "-" and followed by "i".
+*/
+const char32 * Melder_dcomplex  (dcomplex value) noexcept;
+const char   * Melder8_dcomplex (dcomplex value) noexcept;
+
+/**
+	Format a dcomplex value as "--undefined--" or something in the "%.9g" format,
+	separated without spaces by "+" or "-" and followed by "i".
+*/
+const char32 * Melder_scomplex  (dcomplex value) noexcept;
+const char   * Melder8_scomplex (dcomplex value) noexcept;
 
 /**
 	Convert a formatted floating-point string to something suitable for visualization with the Graphics library.
@@ -372,10 +387,10 @@ int64 Melder_movingReallocationsCount ();
  * Text encodings.
  */
 void Melder_textEncoding_prefs ();
-void Melder_setInputEncoding (enum kMelder_textInputEncoding encoding);
-int Melder_getInputEncoding ();
-void Melder_setOutputEncoding (enum kMelder_textOutputEncoding encoding);
-enum kMelder_textOutputEncoding Melder_getOutputEncoding ();
+void Melder_setInputEncoding (kMelder_textInputEncoding encoding);
+kMelder_textInputEncoding Melder_getInputEncoding ();
+void Melder_setOutputEncoding (kMelder_textOutputEncoding encoding);
+kMelder_textOutputEncoding Melder_getOutputEncoding ();
 
 /*
  * Some other encodings. Although not used in the above set/get functions,
@@ -393,8 +408,8 @@ bool Melder_isEncodable (const char32 *string, int outputEncoding);
 extern char32 Melder_decodeMacRoman [256];
 extern char32 Melder_decodeWindowsLatin1 [256];
 
-long Melder_killReturns_inline (char32 *text);
-long Melder_killReturns_inline (char *text);
+integer Melder_killReturns_inline (char32 *text);
+integer Melder_killReturns_inline (char *text);
 /*
 	 Replaces all bare returns (old Mac) or return-plus-linefeed sequences (Win) with bare linefeeds
 	 (generic: Unix and modern Mac).
@@ -438,22 +453,15 @@ void Melder_fwrite32to8 (const char32 *ptr, FILE *f);
 
 #pragma mark - STRING TO NUMBER CONVERSION
 
-bool Melder_isStringNumeric_nothrow (const char32 *string);
-double Melder_a8tof (const char *string);
-double Melder_atof (const char32 *string);
-int64 Melder_atoi (const char32 *string);
+bool Melder_isStringNumeric (const char32 *string) noexcept;
+double Melder_a8tof (const char *string) noexcept;
+double Melder_atof (const char32 *string) noexcept;
+int64 Melder_atoi (const char32 *string) noexcept;
 	/*
 	 * "3.14e-3" -> 3.14e-3
 	 * "15.6%" -> 0.156
 	 * "fghfghj" -> undefined
 	 */
-inline static long a32tol (const char32 *string) {
-	if (sizeof (wchar_t) == 4) {
-		return wcstol ((const wchar_t *) string, nullptr, 10);
-	} else {
-		return atol (Melder_peek32to8 (string));
-	}
-}
 
 /********** FILES **********/
 
@@ -514,7 +522,7 @@ void Melder_getTempDir (MelderDir tempDir);
 
 bool MelderFile_exists (MelderFile file);
 bool MelderFile_readable (MelderFile file);
-long MelderFile_length (MelderFile file);
+integer MelderFile_length (MelderFile file);
 void MelderFile_delete (MelderFile file);
 
 /* The following two should be combined with each other and with Windows extension setting: */
@@ -928,7 +936,7 @@ FUNCTION (unsigned int, u16)
 FUNCTION (unsigned long, u32)
 FUNCTION (double, r32)
 FUNCTION (double, r64)
-FUNCTION (fcomplex, c64)
+FUNCTION (dcomplex, c64)
 FUNCTION (dcomplex, c128)
 #undef FUNCTION
 
@@ -1470,6 +1478,7 @@ struct MelderArg {
 	MelderArg (const unsigned int        arg) : _arg (Melder_integer         (arg)) { }
 	MelderArg (const          short      arg) : _arg (Melder_integer         (arg)) { }
 	MelderArg (const unsigned short      arg) : _arg (Melder_integer         (arg)) { }
+	MelderArg (const dcomplex            arg) : _arg (Melder_dcomplex        (arg)) { }
 	MelderArg (const char32_t            arg) : _arg (Melder_character       (arg)) { }
 	MelderArg (void *                    arg) : _arg (Melder_integer         ((int64) arg)) { }
 	/*
@@ -1625,14 +1634,14 @@ void MelderFile_write (MelderFile file, Melder_12_OR_13_ARGS);
 void MelderFile_write (MelderFile file, Melder_14_OR_15_ARGS);
 void MelderFile_write (MelderFile file, Melder_16_TO_19_ARGS);
 void MelderFile_rewind (MelderFile file);
-void MelderFile_seek (MelderFile file, long position, int direction);
-long MelderFile_tell (MelderFile file);
+void MelderFile_seek (MelderFile file, integer position, int direction);
+integer MelderFile_tell (MelderFile file);
 void MelderFile_close (MelderFile file);
 void MelderFile_close_nothrow (MelderFile file);
 
 /* Read and write whole text files. */
 char32 * MelderFile_readText (MelderFile file);
-void MelderFile_writeText (MelderFile file, const char32 *text, enum kMelder_textOutputEncoding outputEncoding);
+void MelderFile_writeText (MelderFile file, const char32 *text, kMelder_textOutputEncoding outputEncoding);
 void MelderFile_appendText (MelderFile file, const char32 *text);
 
 void Melder_createDirectory (MelderDir parent, const char32 *subdirName, int mode);
@@ -1694,8 +1703,8 @@ void MelderString_copy (MelderString *me, Melder_16_TO_19_ARGS);
 void MelderString_ncopy (MelderString *me, const char32 *source, int64 n);
 
 inline static void MelderString_append (MelderString *me, Melder_1_ARG) {
-	const char32 *s1  = arg1._arg  ? arg1._arg  : U"";  int64 length1  = str32len (s1);
-	int64 sizeNeeded = me -> length + length1 + 1;
+	const char32 *s1  = arg1._arg  ? arg1._arg  : U"";  integer length1  = str32len (s1);
+	integer sizeNeeded = me -> length + length1 + 1;
 	if (sizeNeeded > me -> bufferSize) MelderString_expand (me, sizeNeeded);
 	str32cpy (me -> string + me -> length, s1);   me -> length += length1;
 }
@@ -1755,8 +1764,8 @@ void Melder_sprint (char32 *buffer, int64 bufferSize, Melder_16_TO_19_ARGS);
 
 /********** NUMBER AND STRING COMPARISON **********/
 
-bool Melder_numberMatchesCriterion (double value, int which_kMelder_number, double criterion);
-bool Melder_stringMatchesCriterion (const char32 *value, int which_kMelder_string, const char32 *criterion);
+bool Melder_numberMatchesCriterion (double value, kMelder_number which, double criterion);
+bool Melder_stringMatchesCriterion (const char32 *value, kMelder_string which, const char32 *criterion);
 
 /********** STRING PARSING **********/
 
@@ -1882,6 +1891,23 @@ class MelderError { };
 
 void Melder_appendError_noLine (const MelderArg& arg1);
 
+/**
+	The usual error reporting function is Melder_throw. However,
+	you may sometimes want to prepend other messages before Melder_throw,
+	without jumping away from the error-generating location.
+	In such a special case you can use Melder_appendError.
+
+	Melder_appendError() has to be followed by one of these:
+	- Melder_throw() (or just `throw`) to prepend the error to a normal exception;
+	- Melder_flushError() to show the error in the GUI
+	  (this is where a trail of Melder_throw will usually end up as well);
+	- Melder_clearError() to ignore the error.
+	
+	If you don't do this, the error will linger in your error buffer until
+	the next, probably unrelated, error is generated,
+	and your prepended error text will be shown to the user out of context,
+	which is wrong.
+*/
 void Melder_appendError (Melder_1_ARG);
 void Melder_appendError (Melder_2_ARGS);
 void Melder_appendError (Melder_3_ARGS);
@@ -2246,7 +2272,7 @@ void MelderFile_writeAudioFileTrailer (MelderFile file, int audioFileType, long
 void MelderFile_writeAudioFile (MelderFile file, int audioFileType, const short *buffer, long sampleRate, long numberOfSamples, int numberOfChannels, int numberOfBitsPerSamplePoint);
 
 int MelderFile_checkSoundFile (MelderFile file, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples);
+	double *sampleRate, long *startOfData, integer *numberOfSamples);
 /* Returns information about a just opened audio file.
  * The return value is the audio file type, or 0 if it is not a sound file or in case of error.
  * The data start at 'startOfData' bytes from the start of the file.
@@ -2506,12 +2532,12 @@ class autoMelderAudioSaveMaximumAsynchronicity {
 public:
 	autoMelderAudioSaveMaximumAsynchronicity () {
 		our _savedAsynchronicity = MelderAudio_getOutputMaximumAsynchronicity ();
-		trace (U"value was ", our _savedAsynchronicity);
+		trace (U"value was ", (int) our _savedAsynchronicity);
 		our _disowned = false;
 	}
 	~autoMelderAudioSaveMaximumAsynchronicity () {
 		MelderAudio_setOutputMaximumAsynchronicity (our _savedAsynchronicity);
-		trace (U"value set to ", our _savedAsynchronicity);
+		trace (U"value set to ", (int) our _savedAsynchronicity);
 	}
 	/*
 		Disable copying.
diff --git a/sys/melder_alloc.cpp b/sys/melder_alloc.cpp
index 7e6818f..eb44d25 100644
--- a/sys/melder_alloc.cpp
+++ b/sys/melder_alloc.cpp
@@ -48,6 +48,12 @@ void Melder_alloc_init () {
 	assert (theRainyDayFund);
 }
 
+/*
+	The following functions take int64 arguments even on 32-bit machines.
+	This is because it is easy for the user to request objects that do not fit in memory
+	on 32-bit machines, in which case an appropriate error message is required.
+*/
+
 void * _Melder_malloc (int64 size) {
 	if (size <= 0)
 		Melder_throw (U"Can never allocate ", Melder_bigInteger (size), U" bytes.");
@@ -114,12 +120,11 @@ void * Melder_realloc (void *ptr, int64 size) {
 }
 
 void * Melder_realloc_f (void *ptr, int64 size) {
-	void *result;
 	if (size <= 0)
 		Melder_fatal (U"(Melder_realloc_f:) Can never allocate ", Melder_bigInteger (size), U" bytes.");
 	if (sizeof (size_t) < 8 && size > SIZE_MAX)
 		Melder_fatal (U"(Melder_realloc_f:) Can never allocate ", Melder_bigInteger (size), U" bytes.");
-	result = realloc (ptr, (size_t) size);   // will not show in the statistics...
+	void *result = realloc (ptr, (size_t) size);   // will not show in the statistics...
 	if (! result) {
 		if (theRainyDayFund) { free (theRainyDayFund); theRainyDayFund = nullptr; }
 		result = realloc (ptr, (size_t) size);
@@ -281,7 +286,7 @@ int Melder_cmp (const char32 *string1, const char32 *string2) {
 	return str32cmp (string1, string2);
 }
 
-int Melder_ncmp (const char32 *string1, const char32 *string2, int64 n) {
+int Melder_ncmp (const char32 *string1, const char32 *string2, integer n) {
 	if (! string1) string1 = U"";
 	if (! string2) string2 = U"";
 	return str32ncmp (string1, string2, n);
diff --git a/sys/melder_atof.cpp b/sys/melder_atof.cpp
index 16cdf3f..35699e4 100644
--- a/sys/melder_atof.cpp
+++ b/sys/melder_atof.cpp
@@ -18,8 +18,9 @@
 
 #include "melder.h"
 
-static const char32 *findEndOfNumericString_nothrow (const char32 *string) {
-	const char32 *p = & string [0];
+template <typename T>
+static const T *findEndOfNumericString (const T *string) noexcept {
+	const T *p = & string [0];
 	/*
 	 * Leading white space is OK.
 	 */
@@ -79,70 +80,9 @@ static const char32 *findEndOfNumericString_nothrow (const char32 *string) {
 	return p;
 }
 
-static const char *findEndOfNumericString_nothrow (const char *string) {
-	const char *p = & string [0];
-	/*
-	 * Leading white space is OK.
-	 */
-	while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
-		p ++;
-	/*
-	 * Next we accept an optional leading plus or minus.
-	 */
-	if (*p == '+' || *p == '-') p ++;
-	/*
-	 * The next character must be a decimal digit.
-	 * So we don't allow things like ".5".
-	 */
-	if (*p < '0' || *p > '9') return nullptr;   // string is not numeric
-	p ++;
-	/*
-	 * Then we accept any number of decimal digits.
-	 */
-	while (*p >= '0' && *p <= '9') p ++;
-	/*
-	 * Next we accept an optional decimal point.
-	 */
-	if (*p == '.') {
-		p ++;
-		/*
-		 * We accept any number of (even zero) decimal digits after the decimal point.
-		 */
-		while (*p >= '0' && *p <= '9') p ++;
-	}
-	/*
-	 * Next we accept an optional exponential E or e.
-	 */
-	if (*p == 'e' || *p == 'E') {
-		p ++;
-		/*
-		 * In the exponent we accept an optional leading plus or minus.
-		 */
-		if (*p == '+' || *p == '-') p ++;
-		/*
-		 * The exponent must contain a decimal digit.
-		 * So we don't allow things like "+2.1E".
-		 */
-		if (*p < '0' || *p > '9') return nullptr;   // string is not numeric
-		p ++;
-		/*
-		 * Then we accept any number of decimal digits.
-		 */
-		while (*p >= '0' && *p <= '9') p ++;
-	}
-	/*
-	 * Next we accept an optional percent sign.
-	 */
-	if (*p == '%') p ++;
-	/*
-	 * We have found the end of the numeric string.
-	 */
-	return p;
-}
-
-bool Melder_isStringNumeric_nothrow (const char32 *string) {
+bool Melder_isStringNumeric (const char32 *string) noexcept {
 	if (! string) return false;
-	const char32 *p = findEndOfNumericString_nothrow (string);
+	const char32 *p = findEndOfNumericString (string);
 	if (! p) return false;
 	/*
 	 * We accept only white space after the numeric string.
@@ -152,19 +92,19 @@ bool Melder_isStringNumeric_nothrow (const char32 *string) {
 	return *p == U'\0';
 }
 
-double Melder_a8tof (const char *string) {
+double Melder_a8tof (const char *string) noexcept {
 	if (! string) return undefined;
-	const char *p = findEndOfNumericString_nothrow (string);
+	const char *p = findEndOfNumericString (string);
 	if (! p) return undefined;
 	Melder_assert (p - string > 0);
 	return p [-1] == '%' ? 0.01 * strtod (string, nullptr) : strtod (string, nullptr);
 }
 
-double Melder_atof (const char32 *string) {
+double Melder_atof (const char32 *string) noexcept {
 	return Melder_a8tof (Melder_peek32to8 (string));
 }
 
-int64 Melder_atoi (const char32 *string) {
+int64 Melder_atoi (const char32 *string) noexcept {
 	return strtoll (Melder_peek32to8 (string), nullptr, 10);
 }
 
diff --git a/sys/melder_audio.cpp b/sys/melder_audio.cpp
index 6853826..bd52002 100644
--- a/sys/melder_audio.cpp
+++ b/sys/melder_audio.cpp
@@ -71,9 +71,9 @@ static struct {
 } preferences;
 
 void Melder_audio_prefs () {
-	Preferences_addEnum (U"Audio.maximumAsynchronicity", & preferences. maximumAsynchronicity, kMelder_asynchronicityLevel, kMelder_asynchronicityLevel_DEFAULT);
-	Preferences_addEnum (U"Audio.inputSoundSystem", & preferences. inputSoundSystem, kMelder_inputSoundSystem, kMelder_inputSoundSystem_DEFAULT);
-	Preferences_addEnum (U"Audio.outputSoundSystem", & preferences. outputSoundSystem, kMelder_outputSoundSystem, kMelder_outputSoundSystem_DEFAULT);
+	Preferences_addEnum (U"Audio.maximumAsynchronicity", & preferences. maximumAsynchronicity, kMelder_asynchronicityLevel, kMelder_asynchronicityLevel::DEFAULT);
+	Preferences_addEnum (U"Audio.inputSoundSystem", & preferences. inputSoundSystem, kMelder_inputSoundSystem, kMelder_inputSoundSystem::DEFAULT);
+	Preferences_addEnum (U"Audio.outputSoundSystem", & preferences. outputSoundSystem, kMelder_outputSoundSystem, kMelder_outputSoundSystem::DEFAULT);
 	Preferences_addBool (U"Audio.useInternalSpeaker", & preferences. useInternalSpeaker, true);
 	Preferences_addDouble (U"Audio.silenceBefore2", & preferences. silenceBefore, kMelderAudio_outputSilenceBefore_DEFAULT);
 	Preferences_addDouble (U"Audio.silenceAfter2", & preferences. silenceAfter, kMelderAudio_outputSilenceAfter_DEFAULT);
@@ -174,7 +174,7 @@ typedef struct pulseAudio {
 static struct MelderPlay {
 	int16_t *buffer;
 	long sampleRate, numberOfSamples, samplesLeft, samplesSent, samplesPlayed;
-	unsigned int asynchronicity;
+	kMelder_asynchronicityLevel asynchronicity;
 	int numberOfChannels;
 	bool explicitStop, fakeMono;
 	volatile int volatile_interrupted;
@@ -387,7 +387,7 @@ bool MelderAudio_stopPlaying (bool explicitStop) {
 	struct MelderPlay *me = & thePlay;
 	my explicitStop = explicitStop;
 	trace (U"playing = ", MelderAudio_isPlaying);
-	if (! MelderAudio_isPlaying || my asynchronicity < kMelder_asynchronicityLevel_ASYNCHRONOUS) return false;
+	if (! MelderAudio_isPlaying || my asynchronicity < kMelder_asynchronicityLevel::ASYNCHRONOUS) return false;
 	#if gtk
 		if (thePlay.workProcId_gtk && ! my usePulseAudio) {
 			g_source_remove (thePlay.workProcId_gtk);
@@ -680,9 +680,9 @@ void pulseAudio_serverReport () {
 			}
 			// Now it is save to unref because the server info operation has completed
 			pa_operation_unref (operation);
-			my pulseAudio.occupation &= ~PA_GETTINGINFO_DONE;
+			my pulseAudio.occupation &= ~ PA_GETTINGINFO_DONE;
 		}
-		my pulseAudio.occupation &= ~PA_GETTINGINFO;
+		my pulseAudio.occupation &= ~ PA_GETTINGINFO;
 		pa_threaded_mainloop_unlock (my pulseAudio.mainloop);
 	} else {
 		my pulseAudio.occupation |= PA_GETTINGINFO;
@@ -699,8 +699,8 @@ void pulseAudio_serverReport () {
 		// Now we know that the operation to get server info has succeeded!
 		pa_operation_unref (my pulseAudio.operation_info);
 		my pulseAudio.operation_info = nullptr;
-		my pulseAudio.occupation &= ~PA_GETTINGINFO;
-		my pulseAudio.occupation &= ~PA_GETTINGINFO_DONE;
+		my pulseAudio.occupation &= ~ PA_GETTINGINFO;
+		my pulseAudio.occupation &= ~ PA_GETTINGINFO_DONE;
 		pa_threaded_mainloop_unlock (my pulseAudio.mainloop);
 		if (! MelderAudio_isPlaying) {
 			my pulseAudio.occupation = 0;
@@ -734,7 +734,7 @@ void stream_drain_complete_cb (pa_stream *stream, int success, void *userdata) {
 		pa_stream_disconnect (my pulseAudio.stream);
 		pa_stream_unref (my pulseAudio.stream);
 		my pulseAudio.stream = nullptr;
-		my pulseAudio.occupation &= ~PA_WRITING;
+		my pulseAudio.occupation &= ~ PA_WRITING;
 		my pulseAudio.occupation |= PA_WRITING_DONE;
 		MelderAudio_isPlaying = false;
 		if (my pulseAudio.timer_event) {
@@ -786,7 +786,7 @@ void stream_write_cb2 (pa_stream *stream, size_t length, void *userdata) {
 						pa_stream_unref (my pulseAudio.stream);
 						my pulseAudio.stream = nullptr;
 						trace (U"stream exists");
-						my pulseAudio.occupation &= ~PA_WRITING;
+						my pulseAudio.occupation &= ~ PA_WRITING;
 						my pulseAudio.occupation |= PA_WRITING_DONE;
 						pa_threaded_mainloop_signal (my pulseAudio.mainloop, 0);
 						if (my pulseAudio.timer_event) {
@@ -850,7 +850,7 @@ void stream_write_cb (pa_stream *stream, size_t length, void *userdata) {
 						pa_stream_unref (my pulseAudio.stream);
 						my pulseAudio.stream = nullptr;
 						trace (U"stream exists");
-						my pulseAudio.occupation &= ~PA_WRITING;
+						my pulseAudio.occupation &= ~ PA_WRITING;
 						my pulseAudio.occupation |= PA_WRITING_DONE;
 						pa_threaded_mainloop_signal (my pulseAudio.mainloop, 0);
 						if (my pulseAudio.timer_event) {
@@ -926,7 +926,7 @@ void prepare_and_play (struct MelderPlay *me) {
 
 	//my pulseAudio.stream_flags = PA_STREAM_NOFLAGS;
 	my pulseAudio.stream_flags = (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
-	my pulseAudio.latency = pa_usec_to_bytes (1000 * my pulseAudio.latency_msec, &(my pulseAudio.sample_spec));
+	my pulseAudio.latency = pa_usec_to_bytes (1000 * my pulseAudio.latency_msec, & my pulseAudio.sample_spec);
 	
 	my pulseAudio.buffer_attr.maxlength = (uint32_t) -1;
 	my pulseAudio.buffer_attr.prebuf = (uint32_t) -1;
@@ -946,7 +946,7 @@ void prepare_and_play (struct MelderPlay *me) {
 	// first pa_stream_begin_write followed by pa_stream_write.
 	//
 	my pulseAudio.stream_flags = PA_STREAM_NOFLAGS;
-	if (pa_stream_connect_playback (my pulseAudio.stream, nullptr, &(my pulseAudio.buffer_attr), my pulseAudio.stream_flags, nullptr, nullptr) < 0) {
+	if (pa_stream_connect_playback (my pulseAudio.stream, nullptr, & my pulseAudio.buffer_attr, my pulseAudio.stream_flags, nullptr, nullptr) < 0) {
 		Melder_throw (U"pa_stream_connect_playback() failed: ", Melder_peek8to32 (pa_strerror (pa_context_errno (my pulseAudio.context))));
 	}
 	trace (U"tlength = ", my pulseAudio.buffer_attr.tlength, U", channels = ", my numberOfChannels);
@@ -1017,19 +1017,19 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 	my callback = playCallback;
 	my closure = playClosure;
 	my asynchronicity =
-		Melder_batch ? kMelder_asynchronicityLevel_SYNCHRONOUS :
-		(Melder_backgrounding && ! Melder_asynchronous) ? kMelder_asynchronicityLevel_INTERRUPTABLE :
-		kMelder_asynchronicityLevel_ASYNCHRONOUS;
+		Melder_batch ? kMelder_asynchronicityLevel::SYNCHRONOUS :
+		(Melder_backgrounding && ! Melder_asynchronous) ? kMelder_asynchronicityLevel::INTERRUPTABLE :
+		kMelder_asynchronicityLevel::ASYNCHRONOUS;
 	if (my asynchronicity > preferences. maximumAsynchronicity)
 		my asynchronicity = preferences. maximumAsynchronicity;
-	trace (U"asynchronicity ", my asynchronicity);
+	trace (U"asynchronicity ", (int) my asynchronicity);
 	my usePortAudio =
 		#if defined (_WIN32)
-			preferences. outputSoundSystem == kMelder_outputSoundSystem_MME_VIA_PORTAUDIO;
+			preferences. outputSoundSystem == kMelder_outputSoundSystem::MME_VIA_PORTAUDIO;
 		#elif defined (macintosh)
-			preferences. outputSoundSystem == kMelder_outputSoundSystem_COREAUDIO_VIA_PORTAUDIO;
+			preferences. outputSoundSystem == kMelder_outputSoundSystem::COREAUDIO_VIA_PORTAUDIO;
 		#else
-			preferences. outputSoundSystem == kMelder_outputSoundSystem_ALSA_VIA_PORTAUDIO;
+			preferences. outputSoundSystem == kMelder_outputSoundSystem::ALSA_VIA_PORTAUDIO;
 		#endif
 	my usePulseAudio = ! my usePortAudio;
 
@@ -1095,7 +1095,7 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 		err = Pa_StartStream (my stream);
 		if (err) Melder_throw (U"PortAudio cannot start sound output: ", Melder_peek8to32 (Pa_GetErrorText (err)), U".");
 		my paStartingTime = Pa_GetStreamTime (my stream);
-		if (my asynchronicity <= kMelder_asynchronicityLevel_INTERRUPTABLE) {
+		if (my asynchronicity <= kMelder_asynchronicityLevel::INTERRUPTABLE) {
 			for (;;) {
 				#if defined (linux)
 					/*
@@ -1117,14 +1117,14 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 					}
 				#endif
 				bool interrupted = false;
-				if (my asynchronicity != kMelder_asynchronicityLevel_SYNCHRONOUS && my callback &&
+				if (my asynchronicity != kMelder_asynchronicityLevel::SYNCHRONOUS && my callback &&
 					! my callback (my closure, my samplesPlayed))
 					interrupted = true;
 				/*
 				 * Safe operation: only listen to key-down events.
 				 * Do this on the lowest level that will work.
 				 */
-				if (my asynchronicity == kMelder_asynchronicityLevel_INTERRUPTABLE && ! interrupted) {
+				if (my asynchronicity == kMelder_asynchronicityLevel::INTERRUPTABLE && ! interrupted) {
 					#if gtk
 						// TODO: implement a reaction to the Escape key
 					#elif cocoa
@@ -1228,7 +1228,7 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 			prepare_and_play (me);
 		}
 
-		if (my asynchronicity < kMelder_asynchronicityLevel_ASYNCHRONOUS) {
+		if (my asynchronicity < kMelder_asynchronicityLevel::ASYNCHRONOUS) {
 			pa_threaded_mainloop_lock (my pulseAudio.mainloop);
 			trace (U"occupation ", my pulseAudio.occupation);
 			while ((my pulseAudio.occupation & PA_WRITING_DONE) != PA_WRITING_DONE) {
@@ -1307,12 +1307,12 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 				}
 
 				theStartingTime = Melder_clock ();
-				if (my asynchronicity == kMelder_asynchronicityLevel_SYNCHRONOUS) {
+				if (my asynchronicity == kMelder_asynchronicityLevel::SYNCHRONOUS) {
 					if (write (my audio_fd, & my buffer [0], 2 * numberOfChannels * numberOfSamples) == -1)
 						Melder_throw (U"Cannot write audio output.");
 					close (my audio_fd), my audio_fd = 0;   // drain; set to zero in order to notify flush ()
 					my samplesPlayed = my numberOfSamples;
-				} else if (my asynchronicity <= kMelder_asynchronicityLevel_INTERRUPTABLE) {
+				} else if (my asynchronicity <= kMelder_asynchronicityLevel::INTERRUPTABLE) {
 					bool interrupted = false;
 					while (my samplesLeft && ! interrupted) {
 						int dsamples = my samplesLeft > 500 ? 500 : my samplesLeft;
@@ -1447,19 +1447,19 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 				}
 
 				theStartingTime = Melder_clock ();
-				if (my asynchronicity == kMelder_asynchronicityLevel_SYNCHRONOUS) {
+				if (my asynchronicity == kMelder_asynchronicityLevel::SYNCHRONOUS) {
 					while (! (my waveHeader. dwFlags & WHDR_DONE)) {
 						Sleep (10);
 					}
 					my samplesPlayed = my numberOfSamples;
-				} else if (my asynchronicity <= kMelder_asynchronicityLevel_INTERRUPTABLE) {
+				} else if (my asynchronicity <= kMelder_asynchronicityLevel::INTERRUPTABLE) {
 					while (! (my waveHeader. dwFlags & WHDR_DONE)) {
 						MSG event;
 						Sleep (10);
 						my samplesPlayed = (Melder_clock () - theStartingTime) * my sampleRate;
 						if (my callback && ! my callback (my closure, my samplesPlayed))
 							break;
-						if (my asynchronicity == kMelder_asynchronicityLevel_INTERRUPTABLE &&
+						if (my asynchronicity == kMelder_asynchronicityLevel::INTERRUPTABLE &&
 							PeekMessage (& event, 0, 0, 0, PM_REMOVE) && event. message == WM_KEYDOWN)
 						{
 							if (LOWORD (event. wParam) == VK_ESCAPE) {
diff --git a/sys/melder_audiofiles.cpp b/sys/melder_audiofiles.cpp
index a103a00..ccb9951 100644
--- a/sys/melder_audiofiles.cpp
+++ b/sys/melder_audiofiles.cpp
@@ -344,7 +344,7 @@ static short alaw2linear[] =
 };
 
 static void Melder_checkAiffFile (FILE *f, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples)
+	double *sampleRate, long *startOfData, integer *numberOfSamples)
 {
 	char data [8], chunkID [4];
 	bool commonChunkPresent = false, dataChunkPresent = false, isAifc = true;
@@ -431,7 +431,7 @@ static void Melder_checkAiffFile (FILE *f, int *numberOfChannels, int *encoding,
 }
 
 static void Melder_checkWavFile (FILE *f, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples)
+	double *sampleRate, long *startOfData, integer *numberOfSamples)
 {
 	char data [14], chunkID [4];
 	bool formatChunkPresent = false, dataChunkPresent = false;
@@ -570,7 +570,7 @@ static void Melder_checkWavFile (FILE *f, int *numberOfChannels, int *encoding,
 }
 
 static void Melder_checkNextSunFile (FILE *f, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples)
+	double *sampleRate, long *startOfData, integer *numberOfSamples)
 {
 	char tag [4];
 	fread (tag, 1, 4, f);
@@ -616,7 +616,7 @@ static int nistGetValue (const char *header, const char *object, double *rval, c
 	return 1;
 }
 static void Melder_checkNistFile (FILE *f, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples)
+	double *sampleRate, long *startOfData, integer *numberOfSamples)
 {
 	char header [1024], sval [100];
  	double rval = 0.0;
@@ -656,7 +656,7 @@ static void Melder_checkNistFile (FILE *f, int *numberOfChannels, int *encoding,
 }
 
 static void Melder_checkFlacFile (MelderFile file, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples)
+	double *sampleRate, long *startOfData, integer *numberOfSamples)
 {
 	FLAC__StreamMetadata metadata;
 	FLAC__StreamMetadata_StreamInfo *info;
@@ -673,7 +673,7 @@ static void Melder_checkFlacFile (MelderFile file, int *numberOfChannels, int *e
 }
 
 static void Melder_checkMp3File (FILE *f, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples)
+	double *sampleRate, long *startOfData, integer *numberOfSamples)
 {
 	MP3_FILE mp3f = mp3f_new ();
 	mp3f_set_file (mp3f, f);
@@ -692,7 +692,7 @@ static void Melder_checkMp3File (FILE *f, int *numberOfChannels, int *encoding,
 }
 
 int MelderFile_checkSoundFile (MelderFile file, int *numberOfChannels, int *encoding,
-	double *sampleRate, long *startOfData, int32 *numberOfSamples)
+	double *sampleRate, long *startOfData, integer *numberOfSamples)
 {
 	char data [16];
 	FILE *f = file -> filePointer;
diff --git a/sys/melder_debug.cpp b/sys/melder_debug.cpp
index 43f1427..a17311d 100644
--- a/sys/melder_debug.cpp
+++ b/sys/melder_debug.cpp
@@ -78,15 +78,12 @@ the behaviour of Praat will temporarily change in the following ways:
 45: tracing structMatrix :: read ()
 46: trace GTK parent sizes in _GuiObject_position ()
 47: force resampling in OTGrammar RIP
-48: compute sum, mean, stdev, inner, and so on, with naive implementation in real64
-49: compute sum, mean, stdev, inner, and so on, with naive implementation in real80
+48: compute sum, mean, stdev with naive implementation in real64
+49: compute sum, mean, stdev with naive implementation in real80
 50: compute sum, mean, stdev with first-element offset (80 bits)
-51: compute sum, mean, stdev with Chan, Golub & LeVeque's pairwise algorithm (80 bits)
-52: compute sum, mean, stdev, inner and so on with simple pairwise algorithm, base case 8 (80 bits)
-53: compute sum, mean, stdev, inner and so on with simple pairwise algorithm, base case 16 (80 bits)
-54: compute sum, mean, stdev, inner and so on with two cycles, as in R (80 bits)
-55: compute sum, mean, stdev, inner and so on with simple pairwise algorithm, base case 32 (80 bits)
-(other numbers than 48-55: compute sum, mean, stdev, inner and so on with simple pairwise algorithm, base case 64 [80 bits])
+51: compute sum, mean, stdev with two cycles, as in R (80 bits)
+(other numbers than 48-51: compute sum, mean, stdev with simple pairwise algorithm, base case 64 [80 bits])
+181: read and write native-endian real64
 900: use DG Meta Serif Science instead of Palatino
 1264: Mac: Sound_record_fixedTime uses microphone "FW Solo (1264)"
 
@@ -434,7 +431,7 @@ static void Melder_trace_close (FILE *f) {
 void Melder_trace (const char *fileName, int lineNumber, const char *functionName, Melder_1_ARG) {
 	if (! Melder_isTracing || MelderFile_isNull (& theTracingFile)) return;
 	FILE *f = Melder_trace_open (fileName, lineNumber, functionName);
-	fprintf (f, "%s", peek32to8 (arg1._arg));
+	fprintf (f, "%s", peek32to8 (arg1. _arg));
 	Melder_trace_close (f);
 }
 void Melder_trace (const char *fileName, int lineNumber, const char *functionName, Melder_2_ARGS) {
diff --git a/sys/melder_files.cpp b/sys/melder_files.cpp
index a668d75..24b5080 100644
--- a/sys/melder_files.cpp
+++ b/sys/melder_files.cpp
@@ -127,7 +127,13 @@ void Melder_str32To8bitFileRepresentation_inline (const char32 *string, char *ut
 void Melder_8bitFileRepresentationToStr32_inline (const char *path8, char32 *path32) {
 	#if defined (macintosh)
 		CFStringRef cfpath = CFStringCreateWithCString (nullptr, path8, kCFStringEncodingUTF8);
-		Melder_assert (cfpath != 0);
+		if (! cfpath) {
+			/*
+				Probably something wrong, like a disk was disconnected in the meantime.
+			*/
+			Melder_8to32_inline (path8, path32, (int) kMelder_textInputEncoding::UTF8);
+			Melder_throw (U"Unusual error finding or creating file ", path32, U".");
+		}
 		CFMutableStringRef cfpath2 = CFStringCreateMutableCopy (nullptr, 0, cfpath);
 		CFRelease (cfpath);
 		CFStringNormalize (cfpath2, kCFStringNormalizationFormC);   // Praat requires composed characters
@@ -148,7 +154,7 @@ void Melder_8bitFileRepresentationToStr32_inline (const char *path8, char32 *pat
 		path32 [n_utf32] = U'\0';
 		CFRelease (cfpath2);
 	#else
-		Melder_8to32_inline (path8, path32, kMelder_textInputEncoding_UTF8);
+		Melder_8to32_inline (path8, path32, (int) kMelder_textInputEncoding::UTF8);
 	#endif
 }
 #endif
@@ -703,7 +709,7 @@ bool MelderFile_readable (MelderFile file) {
 	}
 }
 
-long MelderFile_length (MelderFile file) {
+integer MelderFile_length (MelderFile file) {
 	#if defined (UNIX)
 		char utf8path [kMelder_MAXPATH+1];
 		Melder_str32To8bitFileRepresentation_inline (file -> path, utf8path);
@@ -714,7 +720,7 @@ long MelderFile_length (MelderFile file) {
 		try {
 			autofile f = Melder_fopen (file, "r");
 			fseek (f, 0, SEEK_END);
-			long length = ftell (f);
+			integer length = ftell (f);
 			f.close (file);
 			return length;
 		} catch (MelderError) {
@@ -868,7 +874,7 @@ MelderFile MelderFile_create (MelderFile me) {
 	return me;
 }
 
-void MelderFile_seek (MelderFile me, long position, int direction) {
+void MelderFile_seek (MelderFile me, integer position, int direction) {
 	if (! my filePointer) return;
 	if (fseek (my filePointer, position, direction)) {
 		fclose (my filePointer);
@@ -877,9 +883,9 @@ void MelderFile_seek (MelderFile me, long position, int direction) {
 	}
 }
 
-long MelderFile_tell (MelderFile me) {
+integer MelderFile_tell (MelderFile me) {
 	if (! my filePointer) return 0;
-	long result = ftell (my filePointer);
+	integer result = ftell (my filePointer);
 	if (result == -1) {
 		fclose (my filePointer);
 		my filePointer = nullptr;
diff --git a/sys/melder_ftoa.cpp b/sys/melder_ftoa.cpp
index 664322f..2f432a9 100644
--- a/sys/melder_ftoa.cpp
+++ b/sys/melder_ftoa.cpp
@@ -22,8 +22,8 @@
 
 #define NUMBER_OF_BUFFERS  32
 	/* = maximum number of arguments to a function call */
-#define MAXIMUM_NUMERIC_STRING_LENGTH  400
-	/* = sign + 324 + point + 60 + e + sign + 3 + null byte + ("·10^^" - "e") + 4 extra */
+#define MAXIMUM_NUMERIC_STRING_LENGTH  800
+	/* = sign + 324 + point + 60 + e + sign + 3 + null byte + ("·10^^" - "e"), times 2, + i, + 7 extra */
 
 static char   buffers8  [NUMBER_OF_BUFFERS] [MAXIMUM_NUMERIC_STRING_LENGTH + 1];
 static char32 buffers32 [NUMBER_OF_BUFFERS] [MAXIMUM_NUMERIC_STRING_LENGTH + 1];
@@ -231,6 +231,50 @@ const char32 * Melder_percent (double value, int precision) noexcept {
 	CONVERT_BUFFER_TO_CHAR32
 }
 
+const char * Melder8_dcomplex (dcomplex value) noexcept {
+	if (isundef (value.re) || isundef (value.im)) return "--undefined--";
+	if (++ ibuffer == NUMBER_OF_BUFFERS) ibuffer = 0;
+	sprintf (buffers8 [ibuffer], "%.15g", value.re);
+	if (strtod (buffers8 [ibuffer], nullptr) != value.re) {
+		sprintf (buffers8 [ibuffer], "%.16g", value.re);
+		if (strtod (buffers8 [ibuffer], nullptr) != value.re) {
+			sprintf (buffers8 [ibuffer], "%.17g", value.re);
+		}
+	}
+	char *p = buffers8 [ibuffer] + strlen (buffers8 [ibuffer]);
+	*p = value.im < 0.0 ? '-' : '+';
+	value.im = fabs (value.im);
+	++ p;
+	sprintf (p, "%.15g", value.im);
+	if (strtod (p, nullptr) != value.im) {
+		sprintf (p, "%.16g", value.im);
+		if (strtod (p, nullptr) != value.im) {
+			sprintf (p, "%.17g", value.im);
+		}
+	}
+	strcat (buffers8 [ibuffer], "i");
+	return buffers8 [ibuffer];
+}
+const char32 * Melder_dcomplex (dcomplex value) noexcept {
+	const char *p = Melder8_dcomplex (value);
+	CONVERT_BUFFER_TO_CHAR32
+}
+
+const char * Melder8_scomplex (dcomplex value) noexcept {
+	if (isundef (value.re) || isundef (value.im)) return "--undefined--";
+	if (++ ibuffer == NUMBER_OF_BUFFERS) ibuffer = 0;
+	sprintf (buffers8 [ibuffer], "%.9g", value.re);
+	char *p = buffers8 [ibuffer] + strlen (buffers8 [ibuffer]);
+	*p = value.im < 0.0 ? '-' : '+';
+	sprintf (++ p, "%.9g", fabs (value.im));
+	strcat (buffers8 [ibuffer], "i");
+	return buffers8 [ibuffer];
+}
+const char32 * Melder_scomplex (dcomplex value) noexcept {
+	const char *p = Melder8_scomplex (value);
+	CONVERT_BUFFER_TO_CHAR32
+}
+
 const char32 * Melder_float (const char32 *number) noexcept {
 	if (++ ibuffer == NUMBER_OF_BUFFERS) ibuffer = 0;
 	if (! str32chr (number, 'e')) {
diff --git a/sys/melder_readtext.cpp b/sys/melder_readtext.cpp
index f7b5c14..2f025ee 100644
--- a/sys/melder_readtext.cpp
+++ b/sys/melder_readtext.cpp
@@ -26,7 +26,7 @@ char32 MelderReadText_getChar (MelderReadText me) {
 		return * my readPointer32 ++;
 	} else {
 		if (* my readPointer8 == '\0') return U'\0';
-		if (my input8Encoding == kMelder_textInputEncoding_UTF8) {
+		if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8) {
 			char32 kar1 = (char32) (char8) * my readPointer8 ++;
 			if (kar1 <= 0x00007F) {
 				return kar1;
@@ -45,9 +45,9 @@ char32 MelderReadText_getChar (MelderReadText me) {
 			} else {
 				return UNICODE_REPLACEMENT_CHARACTER;
 			}
-		} else if (my input8Encoding == kMelder_textInputEncoding_MACROMAN) {
+		} else if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::MACROMAN) {
 			return Melder_decodeMacRoman [(char8) * my readPointer8 ++];
-		} else if (my input8Encoding == kMelder_textInputEncoding_WINDOWS_LATIN1) {
+		} else if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::WINDOWS_LATIN1) {
 			return Melder_decodeWindowsLatin1 [(char8) * my readPointer8 ++];
 		} else {
 			/* Unknown encoding. */
@@ -286,22 +286,22 @@ MelderReadText MelderReadText_createFromFile (MelderFile file) {
 	} else {
 		Melder_assert (my string8);
 		my readPointer8 = & my string8 [0];
-		my input8Encoding = Melder_getInputEncoding ();
-		if (my input8Encoding == kMelder_textInputEncoding_UTF8 ||
-			my input8Encoding == kMelder_textInputEncoding_UTF8_THEN_ISO_LATIN1 ||
-			my input8Encoding == kMelder_textInputEncoding_UTF8_THEN_WINDOWS_LATIN1 ||
-			my input8Encoding == kMelder_textInputEncoding_UTF8_THEN_MACROMAN)
+		my input8Encoding = (unsigned long) Melder_getInputEncoding ();
+		if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8 ||
+			my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8_THEN_ISO_LATIN1 ||
+			my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8_THEN_WINDOWS_LATIN1 ||
+			my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8_THEN_MACROMAN)
 		{
 			if (Melder_str8IsValidUtf8 (my string8)) {
-				my input8Encoding = kMelder_textInputEncoding_UTF8;
-			} else if (my input8Encoding == kMelder_textInputEncoding_UTF8) {
+				my input8Encoding = (unsigned long) kMelder_textInputEncoding::UTF8;
+			} else if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8) {
 				Melder_throw (U"Text is not valid UTF-8; please try a different text input encoding.");
-			} else if (my input8Encoding == kMelder_textInputEncoding_UTF8_THEN_ISO_LATIN1) {
-				my input8Encoding = kMelder_textInputEncoding_ISO_LATIN1;
-			} else if (my input8Encoding == kMelder_textInputEncoding_UTF8_THEN_WINDOWS_LATIN1) {
-				my input8Encoding = kMelder_textInputEncoding_WINDOWS_LATIN1;
-			} else if (my input8Encoding == kMelder_textInputEncoding_UTF8_THEN_MACROMAN) {
-				my input8Encoding = kMelder_textInputEncoding_MACROMAN;
+			} else if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8_THEN_ISO_LATIN1) {
+				my input8Encoding = (unsigned long) kMelder_textInputEncoding::ISO_LATIN1;
+			} else if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8_THEN_WINDOWS_LATIN1) {
+				my input8Encoding = (unsigned long) kMelder_textInputEncoding::WINDOWS_LATIN1;
+			} else if (my input8Encoding == (unsigned long) kMelder_textInputEncoding::UTF8_THEN_MACROMAN) {
+				my input8Encoding = (unsigned long) kMelder_textInputEncoding::MACROMAN;
 			}
 		}
 	}
diff --git a/sys/melder_textencoding.cpp b/sys/melder_textencoding.cpp
index 3aeb484..1c6ace5 100644
--- a/sys/melder_textencoding.cpp
+++ b/sys/melder_textencoding.cpp
@@ -29,19 +29,19 @@
 #endif
 
 static struct {
-	enum kMelder_textInputEncoding inputEncoding;
-	enum kMelder_textOutputEncoding outputEncoding;
+	kMelder_textInputEncoding inputEncoding;
+	kMelder_textOutputEncoding outputEncoding;
 } preferences;
 
-void Melder_setInputEncoding (enum kMelder_textInputEncoding encoding) { preferences. inputEncoding = encoding; }
-int Melder_getInputEncoding () { return preferences. inputEncoding; }
+void Melder_setInputEncoding (kMelder_textInputEncoding encoding) { preferences. inputEncoding = encoding; }
+kMelder_textInputEncoding Melder_getInputEncoding () { return preferences. inputEncoding; }
 
-void Melder_setOutputEncoding (enum kMelder_textOutputEncoding encoding) { preferences. outputEncoding = encoding; }
-enum kMelder_textOutputEncoding Melder_getOutputEncoding () { return preferences. outputEncoding; }
+void Melder_setOutputEncoding (kMelder_textOutputEncoding encoding) { preferences. outputEncoding = encoding; }
+kMelder_textOutputEncoding Melder_getOutputEncoding () { return preferences. outputEncoding; }
 
 void Melder_textEncoding_prefs () {
-	Preferences_addEnum (U"TextEncoding.inputEncoding", & preferences. inputEncoding, kMelder_textInputEncoding, kMelder_textInputEncoding_DEFAULT);
-	Preferences_addEnum (U"TextEncoding.outputEncoding", & preferences. outputEncoding, kMelder_textOutputEncoding, kMelder_textOutputEncoding_DEFAULT);
+	Preferences_addEnum (U"TextEncoding.inputEncoding", & preferences. inputEncoding, kMelder_textInputEncoding, kMelder_textInputEncoding::DEFAULT);
+	Preferences_addEnum (U"TextEncoding.outputEncoding", & preferences. outputEncoding, kMelder_textOutputEncoding, kMelder_textOutputEncoding::DEFAULT);
 }
 
 bool Melder_isValidAscii (const char32 *text) {
@@ -65,10 +65,10 @@ bool Melder_isEncodable (const char32 *text, int outputEncoding) {
 			}
 			return true;
 		} break;
-		case kMelder_textOutputEncoding_UTF8:
-		case kMelder_textOutputEncoding_UTF16:
-		case kMelder_textOutputEncoding_ASCII_THEN_UTF16:
-		case kMelder_textOutputEncoding_ISO_LATIN1_THEN_UTF16: {
+		case (int) kMelder_textOutputEncoding::UTF8:
+		case (int) kMelder_textOutputEncoding::UTF16:
+		case (int) kMelder_textOutputEncoding::ASCII_THEN_UTF16:
+		case (int) kMelder_textOutputEncoding::ISO_LATIN1_THEN_UTF16: {
 			return true;
 		}
 	}
@@ -98,7 +98,7 @@ bool Melder_str8IsValidUtf8 (const char *string) {
 	return true;
 }
 
-long Melder_killReturns_inline (char *text) {
+integer Melder_killReturns_inline (char *text) {
 	const char *from;
 	char *to;
 	for (from = text, to = text; *from != '\0'; from ++, to ++) {
@@ -118,7 +118,7 @@ long Melder_killReturns_inline (char *text) {
 }
 
 template <class CHAR>
-long Melder_killReturns_inlineCHAR (CHAR *text) {
+integer Melder_killReturns_inlineCHAR (CHAR *text) {
 	const CHAR *from;
 	CHAR *to;
 	for (from = text, to = text; *from != '\0'; from ++, to ++) {
@@ -140,7 +140,7 @@ long Melder_killReturns_inlineCHAR (CHAR *text) {
 	*to = '\0';   // closing null character
 	return to - text;
 }
-long Melder_killReturns_inline (char32 *text) {
+integer Melder_killReturns_inline (char32 *text) {
 	return Melder_killReturns_inlineCHAR <char32> (text);
 }
 
@@ -331,40 +331,40 @@ char32 Melder_decodeWindowsLatin1 [256] = {
 void Melder_8to32_inline (const char *string8, char32 *string32, int inputEncoding) {
 	char32 *q = & string32 [0];
 	if (inputEncoding == 0) {
-		inputEncoding = preferences. inputEncoding;
+		inputEncoding = (int) preferences. inputEncoding;
 		/*
 		 * In case the preferences weren't initialized yet, use the platform defaults:
 		 */
 		if (inputEncoding == 0) {
 			#if defined (macintosh)
-				inputEncoding = kMelder_textInputEncoding_UTF8_THEN_MACROMAN;
+				inputEncoding = (int) kMelder_textInputEncoding::UTF8_THEN_MACROMAN;
 			#elif defined (_WIN32)
-				inputEncoding = kMelder_textInputEncoding_UTF8_THEN_WINDOWS_LATIN1;
+				inputEncoding = (int) kMelder_textInputEncoding::UTF8_THEN_WINDOWS_LATIN1;
 			#else
-				inputEncoding = kMelder_textInputEncoding_UTF8_THEN_ISO_LATIN1;
+				inputEncoding = (int) kMelder_textInputEncoding::UTF8_THEN_ISO_LATIN1;
 			#endif
 		}
 	}
-	if (inputEncoding == kMelder_textInputEncoding_UTF8 ||
-		inputEncoding == kMelder_textInputEncoding_UTF8_THEN_ISO_LATIN1 ||
-		inputEncoding == kMelder_textInputEncoding_UTF8_THEN_WINDOWS_LATIN1 ||
-		inputEncoding == kMelder_textInputEncoding_UTF8_THEN_MACROMAN)
+	if (inputEncoding == (int) kMelder_textInputEncoding::UTF8 ||
+		inputEncoding == (int) kMelder_textInputEncoding::UTF8_THEN_ISO_LATIN1 ||
+		inputEncoding == (int) kMelder_textInputEncoding::UTF8_THEN_WINDOWS_LATIN1 ||
+		inputEncoding == (int) kMelder_textInputEncoding::UTF8_THEN_MACROMAN)
 	{
 		if (Melder_str8IsValidUtf8 (string8)) {
-			inputEncoding = kMelder_textInputEncoding_UTF8;
-		} else if (inputEncoding == kMelder_textInputEncoding_UTF8_THEN_ISO_LATIN1) {
-			inputEncoding = kMelder_textInputEncoding_ISO_LATIN1;
-		} else if (inputEncoding == kMelder_textInputEncoding_UTF8_THEN_WINDOWS_LATIN1) {
-			inputEncoding = kMelder_textInputEncoding_WINDOWS_LATIN1;
-		} else if (inputEncoding == kMelder_textInputEncoding_UTF8_THEN_MACROMAN) {
-			inputEncoding = kMelder_textInputEncoding_MACROMAN;
+			inputEncoding = (int) kMelder_textInputEncoding::UTF8;
+		} else if (inputEncoding == (int) kMelder_textInputEncoding::UTF8_THEN_ISO_LATIN1) {
+			inputEncoding = (int) kMelder_textInputEncoding::ISO_LATIN1;
+		} else if (inputEncoding == (int) kMelder_textInputEncoding::UTF8_THEN_WINDOWS_LATIN1) {
+			inputEncoding = (int) kMelder_textInputEncoding::WINDOWS_LATIN1;
+		} else if (inputEncoding == (int) kMelder_textInputEncoding::UTF8_THEN_MACROMAN) {
+			inputEncoding = (int) kMelder_textInputEncoding::MACROMAN;
 		} else {
-			Melder_assert (inputEncoding == kMelder_textInputEncoding_UTF8);
+			Melder_assert (inputEncoding == (int) kMelder_textInputEncoding::UTF8);
 			Melder_throw (U"Text is not valid UTF-8; please try a different text input encoding.");
 		}
 	}
 	const char8 *p = (const char8 *) & string8 [0];
-	if (inputEncoding == kMelder_textInputEncoding_UTF8) {
+	if (inputEncoding == (int) kMelder_textInputEncoding::UTF8) {
 		while (*p != '\0') {
 			char32 kar1 = * p ++;   // convert up without sign extension
 			if (kar1 <= 0x00007F) {
@@ -381,19 +381,19 @@ void Melder_8to32_inline (const char *string8, char32 *string32, int inputEncodi
 				* q ++ = kar;
 			}
 		}
-	} else if (inputEncoding == kMelder_textInputEncoding_ISO_LATIN1) {
+	} else if (inputEncoding == (int) kMelder_textInputEncoding::ISO_LATIN1) {
 		while (*p != '\0') {
 			* q ++ = * p ++;
 		}
-	} else if (inputEncoding == kMelder_textInputEncoding_WINDOWS_LATIN1) {
+	} else if (inputEncoding == (int) kMelder_textInputEncoding::WINDOWS_LATIN1) {
 		while (*p != '\0') {
 			* q ++ = Melder_decodeWindowsLatin1 [* p ++];
 		}
-	} else if (inputEncoding == kMelder_textInputEncoding_MACROMAN) {
+	} else if (inputEncoding == (int) kMelder_textInputEncoding::MACROMAN) {
 		while (*p != '\0') {
 			* q ++ = Melder_decodeMacRoman [* p ++];
 		}
-	} else if (inputEncoding != kMelder_textInputEncoding_UTF8) {
+	} else if (inputEncoding != (int) kMelder_textInputEncoding::UTF8) {
 		Melder_fatal (U"Unknown text input encoding ", inputEncoding, U".");
 	}
 	* q = U'\0';   // closing null character
@@ -410,7 +410,7 @@ char32 * Melder_8to32 (const char *string, int inputEncoding) {
 char32 * Melder_8to32 (const char *string) {
 	if (! string) return nullptr;
 	autostring32 result = Melder_malloc (char32, (int64) strlen (string) + 1);
-	Melder_8to32_inline (string, result.peek(), kMelder_textInputEncoding_UTF8);
+	Melder_8to32_inline (string, result.peek(), (int) kMelder_textInputEncoding::UTF8);
 	return result.transfer();
 }
 
diff --git a/sys/melder_writetext.cpp b/sys/melder_writetext.cpp
index d4fec92..68b7747 100644
--- a/sys/melder_writetext.cpp
+++ b/sys/melder_writetext.cpp
@@ -52,13 +52,13 @@ void Melder_fwrite32to8 (const char32 *string, FILE *f) {
 	}
 }
 
-void MelderFile_writeText (MelderFile file, const char32 *text, enum kMelder_textOutputEncoding outputEncoding) {
+void MelderFile_writeText (MelderFile file, const char32 *text, kMelder_textOutputEncoding outputEncoding) {
 	if (! text) text = U"";
 	autofile f = Melder_fopen (file, "wb");
-	if (outputEncoding == kMelder_textOutputEncoding_UTF8) {
+	if (outputEncoding == kMelder_textOutputEncoding::UTF8) {
 		Melder_fwrite32to8 (text, f);
-	} else if ((outputEncoding == kMelder_textOutputEncoding_ASCII_THEN_UTF16 && Melder_isValidAscii (text)) ||
-		(outputEncoding == kMelder_textOutputEncoding_ISO_LATIN1_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ISO_LATIN1)))
+	} else if ((outputEncoding == kMelder_textOutputEncoding::ASCII_THEN_UTF16 && Melder_isValidAscii (text)) ||
+		(outputEncoding == kMelder_textOutputEncoding::ISO_LATIN1_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ISO_LATIN1)))
 	{
 		#ifdef _WIN32
 			#define flockfile(f)  (void) 0
@@ -119,13 +119,13 @@ void MelderFile_appendText (MelderFile file, const char32 *text) {
 		type = 2;   // little-endian 16-bit
 	}
 	if (type == 0) {
-		int outputEncoding = Melder_getOutputEncoding ();
-		if (outputEncoding == kMelder_textOutputEncoding_UTF8) {   // TODO: read as file's encoding
+		kMelder_textOutputEncoding outputEncoding = Melder_getOutputEncoding ();
+		if (outputEncoding == kMelder_textOutputEncoding::UTF8) {   // TODO: read as file's encoding
 			autofile f2 = Melder_fopen (file, "ab");
 			Melder_fwrite32to8 (text, f2);
 			f2.close (file);
-		} else if ((outputEncoding == kMelder_textOutputEncoding_ASCII_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ASCII))
-		    || (outputEncoding == kMelder_textOutputEncoding_ISO_LATIN1_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ISO_LATIN1)))
+		} else if ((outputEncoding == kMelder_textOutputEncoding::ASCII_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ASCII))
+		    || (outputEncoding == kMelder_textOutputEncoding::ISO_LATIN1_THEN_UTF16 && Melder_isEncodable (text, kMelder_textOutputEncoding_ISO_LATIN1)))
 		{
 			/*
 			 * Append ASCII or ISOLatin1 text to ASCII or ISOLatin1 file.
@@ -229,7 +229,7 @@ static void _MelderFile_write (MelderFile file, const char32 *string) {
 			if (kar == '\n' && file -> requiresCRLF) putc (13, f);
 			putc (kar, f);
 		}
-	} else if (file -> outputEncoding == kMelder_textOutputEncoding_UTF8) {
+	} else if (file -> outputEncoding == (unsigned long) kMelder_textOutputEncoding::UTF8) {
 		for (int64 i = 0; i < length; i ++) {
 			char32 kar = string [i];
 			if (kar <= 0x00007F) {
@@ -271,7 +271,7 @@ void MelderFile_writeCharacter (MelderFile file, char32 kar) {
 	if (file -> outputEncoding == kMelder_textOutputEncoding_ASCII || file -> outputEncoding == kMelder_textOutputEncoding_ISO_LATIN1) {
 		if (kar == U'\n' && file -> requiresCRLF) putc (13, f);
 		putc ((int) kar, f);
-	} else if (file -> outputEncoding == kMelder_textOutputEncoding_UTF8) {
+	} else if (file -> outputEncoding == (unsigned long) kMelder_textOutputEncoding::UTF8) {
 		if (kar <= 0x00007F) {
 			if (kar == U'\n' && file -> requiresCRLF) putc (13, f);
 			putc ((int) kar, f);   // guarded conversion down
diff --git a/sys/oo.h b/sys/oo.h
index f1776ff..f1ebb8c 100644
--- a/sys/oo.h
+++ b/sys/oo.h
@@ -43,6 +43,7 @@
 #define oo_BYTE(x)  oo_SIMPLE (signed char, i8, x)
 #define oo_INT(x)  oo_SIMPLE (int, i16, x)
 #define oo_LONG(x)  oo_SIMPLE (long, i32, x)
+#define oo_INTEGER(x)  oo_SIMPLE (integer, integer, x)
 #define oo_UBYTE(x)  oo_SIMPLE (unsigned char, u8, x)
 #define oo_UINT(x)  oo_SIMPLE (unsigned int, u16, x)
 #define oo_ULONG(x)  oo_SIMPLE (unsigned long, u32, x)
@@ -56,7 +57,7 @@
 #define oo_BOOL(x)  oo_SIMPLE (unsigned char, u8, x)
 #define oo_FLOAT(x)  oo_SIMPLE (double, r32, x)
 #define oo_DOUBLE(x)  oo_SIMPLE (double, r64, x)
-//#define oo_FCOMPLEX(x)  oo_SIMPLE (fcomplex, c64, x)
+//#define oo_FCOMPLEX(x)  oo_SIMPLE (dcomplex, c64, x)
 //#define oo_DCOMPLEX(x)  oo_SIMPLE (dcomplex, c128, x)
 #define oo_POINTER(x)  oo_SIMPLE (void *, dummy, x)
 
@@ -75,7 +76,7 @@
 //#define oo_BOOL_ARRAY(x,cap,n)  oo_ARRAY (unsigned char, u8, x, cap, n)
 //#define oo_FLOAT_ARRAY(x,cap,n)  oo_ARRAY (double, r32, x, cap, n)
 #define oo_DOUBLE_ARRAY(x,cap,n)  oo_ARRAY (double, r64, x, cap, n)
-//#define oo_FCOMPLEX_ARRAY(x,cap,n)  oo_ARRAY (fcomplex, c64, x, cap, n)
+//#define oo_FCOMPLEX_ARRAY(x,cap,n)  oo_ARRAY (dcomplex, c64, x, cap, n)
 //#define oo_DCOMPLEX_ARRAY(x,cap,n)  oo_ARRAY (dcomplex, c128, x, cap, n)
 #define oo_POINTER_ARRAY(x,cap,n)  oo_ARRAY (void *, dummy, x, cap, n)
 
@@ -91,7 +92,7 @@
 //#define oo_BOOL_SET(x,setType)  oo_SET (unsigned char, u8, x, setType)
 //#define oo_FLOAT_SET(x,setType)  oo_SET (double, r32, x, setType)
 #define oo_DOUBLE_SET(x,setType)  oo_SET (double, r64, x, setType)
-//#define oo_FCOMPLEX_SET(x,setType)  oo_SET (fcomplex, c64, x, setType)
+//#define oo_FCOMPLEX_SET(x,setType)  oo_SET (dcomplex, c64, x, setType)
 //#define oo_DCOMPLEX_SET(x,setType)  oo_SET (dcomplex, c128, x, setType)
 #define oo_POINTER_SET(x,setType)  oo_SET (void *, dummy, x, setType)
 
@@ -108,7 +109,7 @@
 //#define oo_BOOL_VECTOR_FROM(x,min,max)  oo_VECTOR (unsigned char, u8, x, min, max)
 #define oo_FLOAT_VECTOR_FROM(x,min,max)  oo_VECTOR (double, r32, x, min, max)
 #define oo_DOUBLE_VECTOR_FROM(x,min,max)  oo_VECTOR (double, r64, x, min, max)
-//#define oo_FCOMPLEX_VECTOR_FROM(x,min,max)  oo_VECTOR (fcomplex, c64, x, min, max)
+//#define oo_FCOMPLEX_VECTOR_FROM(x,min,max)  oo_VECTOR (dcomplex, c64, x, min, max)
 #define oo_DCOMPLEX_VECTOR_FROM(x,min,max)  oo_VECTOR (dcomplex, c128, x, min, max)
 #define oo_POINTER_VECTOR_FROM(x,min,max)  oo_VECTOR (void *, dummy, x, min, max)
 
@@ -121,7 +122,7 @@
 //#define oo_BOOL_MATRIX_FROM(x,row1,row2,col1,col2)  oo_MATRIX (unsigned char, u8, x, row1, row2, col1, col2)
 //#define oo_FLOAT_MATRIX_FROM(x,row1,row2,col1,col2)  oo_MATRIX (double, r32, x, row1, row2, col1, col2)
 #define oo_DOUBLE_MATRIX_FROM(x,row1,row2,col1,col2)  oo_MATRIX (double, r64, x, row1, row2, col1, col2)
-//#define oo_FCOMPLEX_MATRIX_FROM(x,row1,row2,col1,col2)  oo_MATRIX (fcomplex, c64, x, row1, row2, col1, col2)
+//#define oo_FCOMPLEX_MATRIX_FROM(x,row1,row2,col1,col2)  oo_MATRIX (dcomplex, c64, x, row1, row2, col1, col2)
 //#define oo_DCOMPLEX_MATRIX_FROM(x,row1,row2,col1,col2)  oo_MATRIX (dcomplex, c128, x, row1, row2, col1, col2)
 #define oo_POINTER_MATRIX_FROM(x,row1,row2,col1,col2)  oo_MATRIX (void *, dummy, x, row1, row2, col1, col2)
 
@@ -136,7 +137,7 @@
 //#define oo_BOOL_VECTOR(x,n)  oo_VECTOR (unsigned char, u8, x, 1, n)
 #define oo_FLOAT_VECTOR(x,n)  oo_VECTOR (double, r32, x, 1, n)
 #define oo_DOUBLE_VECTOR(x,n)  oo_VECTOR (double, r64, x, 1, n)
-//#define oo_FCOMPLEX_VECTOR(x,n)  oo_VECTOR (fcomplex, c64, x, 1, n)
+//#define oo_FCOMPLEX_VECTOR(x,n)  oo_VECTOR (dcomplex, c64, x, 1, n)
 //#define oo_DCOMPLEX_VECTOR(x,n)  oo_VECTOR (dcomplex, c128, x, 1, n)
 #define oo_POINTER_VECTOR(x,n)  oo_VECTOR (void *, dummy, x, 1, n)
 
@@ -149,7 +150,7 @@
 //#define oo_BOOL_MATRIX(x,nrow,ncol)  oo_MATRIX (unsigned char, u8, x, 1, nrow, 1, ncol)
 #define oo_FLOAT_MATRIX(x,nrow,ncol)  oo_MATRIX (double, r32, x, 1, nrow, 1, ncol)
 #define oo_DOUBLE_MATRIX(x,nrow,ncol)  oo_MATRIX (double, r64, x, 1, nrow, 1, ncol)
-//#define oo_FCOMPLEX_MATRIX(x,nrow,ncol)  oo_MATRIX (fcomplex, c64, x, 1, nrow, 1, ncol)
+//#define oo_FCOMPLEX_MATRIX(x,nrow,ncol)  oo_MATRIX (dcomplex, c64, x, 1, nrow, 1, ncol)
 //#define oo_DCOMPLEX_MATRIX(x,nrow,ncol)  oo_MATRIX (dcomplex, c128, x, 1, nrow, 1, ncol)
 #define oo_POINTER_MATRIX(x,nrow,ncol)  oo_MATRIX (void *, dummy, x, 1, nrow, 1, ncol)
 
@@ -164,7 +165,7 @@
 /*    ex: store as byte 226 (absent) or byte 241 (present). */
 /* For text format, the value is written as a string between '<' and '>'. */
 
-#define oo_ENUM(Type,x)  oo_ENUMx (signed char, e8, Type, x)
+#define oo_ENUM(kType,x)  oo_ENUMx (kType, e8, x)
 //#define oo_LENUM(Type,x)  oo_ENUMx (short, e16, Type, x)
 //#define oo_ENUM_ARRAY(Type,x,cap,n)  oo_ENUMx_ARRAY (signed char, e8, Type, x, cap, n)
 //#define oo_LENUM_ARRAY(Type,x,cap,n)  oo_ENUMx_ARRAY (short, e16, Type, x, cap, n)
@@ -221,23 +222,23 @@
 
 #define oo_SIMPLE(type,storage,x)  type x;
 #define oo_ARRAY(type,storage,x,cap,n)  type x [cap];
-#define oo_SET(type,storage,x,setType)  type x [1 + setType##_MAX];
+#define oo_SET(type,storage,x,setType)  type x [1 + (int) setType::MAX];
 #define oo_VECTOR(type,storage,x,min,max)  type *x;
 #define oo_MATRIX(type,storage,x,row1,row2,col1,col2)  type **x;
 
-#define oo_ENUMx(type,storage,Type,x)  type x;
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)  type x [cap];
-#define oo_ENUMx_SET(type,storage,Type,x,setType)  type x [1 + setType##_MAX];
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  type *x;
+#define oo_ENUMx(kType,storage,x)  kType x;
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)  kType x [cap];
+//#define oo_ENUMx_SET(kType,storage,x,setType)  kType x [1 + (int) setType::MAX];
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  kType *x;
 
 #define oo_STRINGx(storage,x)  char32 *x;
 #define oo_STRINGx_ARRAY(storage,x,cap,n)  char32 *x [cap];
-#define oo_STRINGx_SET(storage,x,setType)  char32 *x [1 + setType##_MAX];
+#define oo_STRINGx_SET(storage,x,setType)  char32 *x [1 + setType::MAX];
 #define oo_STRINGx_VECTOR(storage,x,min,max)  char32 **x;
 
 #define oo_STRUCT(Type,x)  struct struct##Type x;
 #define oo_STRUCT_ARRAY(Type,x,cap,n)  struct struct##Type x [cap];
-#define oo_STRUCT_SET(Type,x,setType)  struct struct##Type x [1 + setType##_MAX];
+#define oo_STRUCT_SET(Type,x,setType)  struct struct##Type x [1 + (int) setType::MAX];
 #define oo_STRUCT_VECTOR_FROM(Type,x,min,max)  Type x;
 #define oo_STRUCT_MATRIX_FROM(Type,x,row1,row2,col1,col2)  struct struct##Type **x;
 
diff --git a/sys/oo_CAN_WRITE_AS_ENCODING.h b/sys/oo_CAN_WRITE_AS_ENCODING.h
index cf40394..4653d47 100644
--- a/sys/oo_CAN_WRITE_AS_ENCODING.h
+++ b/sys/oo_CAN_WRITE_AS_ENCODING.h
@@ -1,6 +1,6 @@
 /* oo_CAN_WRITE_AS_ENCODING.h
  *
- * Copyright (C) 2007-2012,2013,2014,2015 Paul Boersma
+ * Copyright (C) 2007-2012,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,13 +28,13 @@
 
 #define oo_MATRIX(type,storage,x,row1,row2,col1,col2)
 
-#define oo_ENUMx(type,storage,Type,x)
+#define oo_ENUMx(kType,storage,x)
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)
+//#define oo_ENUMx_SET(kType,storage,x,setType)
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)
 
 #define oo_STRINGx(storage,x)  \
 	if (our x && ! Melder_isEncodable (our x, encoding)) return false;
@@ -49,10 +49,10 @@
 		if (our x [i] && ! Melder_isEncodable (our x [i], encoding)) return false;
 
 #define oo_STRINGx_SET(storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= setType::MAX; i ++) \
 		if (our x [i] && ! Melder_isEncodable (our x [i], encoding)) return false;
 #define oo_STRING32x_SET(storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= setType::MAX; i ++) \
 		if (our x [i] && ! Melder_isEncodable (our x [i], encoding)) return false;
 
 #define oo_STRINGx_VECTOR(storage,x,min,max)  \
@@ -74,7 +74,7 @@
 		if (! our x [i]. canWriteAsEncoding (encoding)) return false;
 
 #define oo_STRUCT_SET(Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		if (! our x [i]. canWriteAsEncoding (encoding)) return false;
 
 #define oo_STRUCT_VECTOR_FROM(Type,x,min,max)  \
diff --git a/sys/oo_COPY.h b/sys/oo_COPY.h
index 8a64376..1f58afa 100644
--- a/sys/oo_COPY.h
+++ b/sys/oo_COPY.h
@@ -1,6 +1,6 @@
 /* oo_COPY.h
  *
- * Copyright (C) 1994-2012,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1994-2012,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
 		thy x [i] = our x [i];
 
 #define oo_SET(type,storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		thy x [i] = our x [i];
 
 #define oo_VECTOR(type,storage,x,min,max)  \
@@ -35,17 +35,17 @@
 #define oo_MATRIX(type,storage,x,row1,row2,col1,col2)  \
 	if (our x) thy x = NUMmatrix_copy (our x, row1, row2, col1, col2);
 
-#define oo_ENUMx(type,storage,Type,x)  \
+#define oo_ENUMx(kType,storage,x)  \
 	thy x = our x;
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)  \
-	for (int i = 0; i < n; i ++) thy x [i] = our x [i];
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)  \
+//	for (int i = 0; i < n; i ++) thy x [i] = our x [i];
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) thy x [i] = our x [i];
+//#define oo_ENUMx_SET(kType,storage,x,setType)  \
+//	for (int i = 0; i <= (int) setType::MAX; i ++) thy x [i] = our x [i];
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  \
-	if (our x) thy x = NUMvector_copy (our x, min, max);
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  \
+//	if (our x) thy x = NUMvector_copy (our x, min, max);
 
 #define oo_STRINGx(storage,x)  \
 	if (our x) thy x = Melder_dup (our x);
@@ -55,7 +55,7 @@
 		if (our x [i]) thy x [i] = Melder_dup (our x [i]);
 
 #define oo_STRINGx_SET(storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= setType::MAX; i ++) \
 		if (our x [i]) thy x [i] = Melder_dup (our x [i]);
 
 #define oo_STRINGx_VECTOR(storage,x,min,max)  \
@@ -73,7 +73,7 @@
 		our x [i]. copy (& thy x [i]);
 
 #define oo_STRUCT_SET(Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		our x [i]. copy (& thy x [i]);
 
 #define oo_STRUCT_VECTOR_FROM(Type,x,min,max)  \
diff --git a/sys/oo_DESCRIPTION.h b/sys/oo_DESCRIPTION.h
index d672768..4cc24e2 100644
--- a/sys/oo_DESCRIPTION.h
+++ b/sys/oo_DESCRIPTION.h
@@ -28,6 +28,8 @@
 #define oo_INT32(x)  { U"" #x, intwa, Melder_offsetof (ooSTRUCT, x), sizeof (int32), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
 #undef oo_LONG
 #define oo_LONG(x)  { U"" #x, longwa, Melder_offsetof (ooSTRUCT, x), sizeof (long), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
+#undef oo_INTEGER
+#define oo_INTEGER(x)  { U"" #x, integerwa, Melder_offsetof (ooSTRUCT, x), sizeof (integer), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
 #undef oo_UBYTE
 #define oo_UBYTE(x)  { U"" #x, ubytewa, Melder_offsetof (ooSTRUCT, x), sizeof (unsigned char), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
 #undef oo_UINT
@@ -41,7 +43,7 @@
 #undef oo_DOUBLE
 #define oo_DOUBLE(x)  { U"" #x, doublewa, Melder_offsetof (ooSTRUCT, x), sizeof (double), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
 #undef oo_FCOMPLEX
-#define oo_FCOMPLEX(x)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (fcomplex), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
+#define oo_FCOMPLEX(x)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
 #undef oo_DCOMPLEX
 #define oo_DCOMPLEX(x)  { U"" #x, dcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 0, nullptr, nullptr, nullptr, nullptr },
 
@@ -64,7 +66,7 @@
 #undef oo_DOUBLE_ARRAY
 #define oo_DOUBLE_ARRAY(x,cap,n)  { U"" #x, doublewa, Melder_offsetof (ooSTRUCT, x), sizeof (double), 0, 0, - cap, nullptr, U"" #n, nullptr, nullptr },
 #undef oo_FCOMPLEX_ARRAY
-#define oo_FCOMPLEX_ARRAY(x,cap,n)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (fcomplex), 0, 0, - cap, nullptr, U"" #n, nullptr, nullptr },
+#define oo_FCOMPLEX_ARRAY(x,cap,n)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, - cap, nullptr, U"" #n, nullptr, nullptr },
 #undef oo_DCOMPLEX_ARRAY
 #define oo_DCOMPLEX_ARRAY(x,cap,n)  { U"" #x, dcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, - cap, nullptr, U"" #n, nullptr, nullptr },
 
@@ -87,7 +89,7 @@
 #undef oo_DOUBLE_SET
 #define oo_DOUBLE_SET(x,setType)  { U"" #x, doublewa, Melder_offsetof (ooSTRUCT, x), sizeof (double), 0, 0, 3, (const char32 *) setType##_getText, (const char32 *) setType##_getValue, nullptr, nullptr },
 #undef oo_FCOMPLEX_SET
-#define oo_FCOMPLEX_SET(x,setType)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (fcomplex), 0, 0, 3, (const char32 *) setType##_getText, (const char32 *) setType##_getValue, nullptr, nullptr },
+#define oo_FCOMPLEX_SET(x,setType)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 3, (const char32 *) setType##_getText, (const char32 *) setType##_getValue, nullptr, nullptr },
 #undef oo_DCOMPLEX_SET
 #define oo_DCOMPLEX_SET(x,setType)  { U"" #x, dcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 3, (const char32 *) setType##_getText, (const char32 *) setType##_getValue, nullptr, nullptr },
 
@@ -110,7 +112,7 @@
 #undef oo_DOUBLE_VECTOR_FROM
 #define oo_DOUBLE_VECTOR_FROM(x,min,max)  { U"" #x, doublewa, Melder_offsetof (ooSTRUCT, x), sizeof (double), 0, 0, 1, U"" #min, U"" #max, nullptr, nullptr },
 #undef oo_FCOMPLEX_VECTOR_FROM
-#define oo_FCOMPLEX_VECTOR_FROM(x,min,max)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (fcomplex), 0, 0, 1, U"" #min, U"" #max, nullptr, nullptr },
+#define oo_FCOMPLEX_VECTOR_FROM(x,min,max)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 1, U"" #min, U"" #max, nullptr, nullptr },
 #undef oo_DCOMPLEX_VECTOR_FROM
 #define oo_DCOMPLEX_VECTOR_FROM(x,min,max)  { U"" #x, dcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 1, U"" #min, U"" #max, nullptr, nullptr },
 
@@ -133,7 +135,7 @@
 #undef oo_DOUBLE_MATRIX_FROM
 #define oo_DOUBLE_MATRIX_FROM(x,r1,r2,c1,c2)  { U"" #x, doublewa, Melder_offsetof (ooSTRUCT, x), sizeof (double), 0, 0, 2, U"" #r1, U"" #r2, U"" #c1, U"" #c2 },
 #undef oo_FCOMPLEX_MATRIX_FROM
-#define oo_FCOMPLEX_MATRIX_FROM(x,r1,r2,c1,c2)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (fcomplex), 0, 0, 2, U"" #r1, U"" #r2, U"" #c1, U"" #c2 },
+#define oo_FCOMPLEX_MATRIX_FROM(x,r1,r2,c1,c2)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 2, U"" #r1, U"" #r2, U"" #c1, U"" #c2 },
 #undef oo_DCOMPLEX_MATRIX_FROM
 #define oo_DCOMPLEX_MATRIX_FROM(x,r1,r2,c1,c2)  { U"" #x, dcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 2, U"" #r1, U"" #r2, U"" #c1, U"" #c2 },
 
@@ -156,7 +158,7 @@
 #undef oo_DOUBLE_VECTOR
 #define oo_DOUBLE_VECTOR(x,n)  { U"" #x, doublewa, Melder_offsetof (ooSTRUCT, x), sizeof (double), 0, 0, 1, nullptr, U"" #n, nullptr, nullptr },
 #undef oo_FCOMPLEX_VECTOR
-#define oo_FCOMPLEX_VECTOR(x,n)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (fcomplex), 0, 0, 1, nullptr, U"" #n, nullptr, nullptr },
+#define oo_FCOMPLEX_VECTOR(x,n)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 1, nullptr, U"" #n, nullptr, nullptr },
 #undef oo_DCOMPLEX_VECTOR
 #define oo_DCOMPLEX_VECTOR(x,n)  { U"" #x, dcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 1, nullptr, U"" #n, nullptr, nullptr },
 
@@ -179,7 +181,7 @@
 #undef oo_DOUBLE_MATRIX
 #define oo_DOUBLE_MATRIX(x,nrow,ncol)  { U"" #x, doublewa, Melder_offsetof (ooSTRUCT, x), sizeof (double), 0, 0, 2, nullptr, U"" #nrow, (const char32 *) 0, U"" #ncol },
 #undef oo_FCOMPLEX_MATRIX
-#define oo_FCOMPLEX_MATRIX(x,nrow,ncol)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (fcomplex), 0, 0, 2, nullptr, U"" #nrow, (const char32 *) 0, U"" #ncol },
+#define oo_FCOMPLEX_MATRIX(x,nrow,ncol)  { U"" #x, fcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 2, nullptr, U"" #nrow, (const char32 *) 0, U"" #ncol },
 #undef oo_DCOMPLEX_MATRIX
 #define oo_DCOMPLEX_MATRIX(x,nrow,ncol)  { U"" #x, dcomplexwa, Melder_offsetof (ooSTRUCT, x), sizeof (dcomplex), 0, 0, 2, nullptr, U"" #nrow, (const char32 *) 0, U"" #ncol },
 
diff --git a/sys/oo_DESTROY.h b/sys/oo_DESTROY.h
index f7d463d..904cd59 100644
--- a/sys/oo_DESTROY.h
+++ b/sys/oo_DESTROY.h
@@ -1,6 +1,6 @@
 /* oo_DESTROY.h
  *
- * Copyright (C) 1994-2012,2013,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1994-2012,2013,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -31,14 +31,14 @@
 	NUMmatrix_free <type> (our x, row1, col1);
 
 
-#define oo_ENUMx(type,storage,Type,x)
+#define oo_ENUMx(kType,storage,x)
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)
+//#define oo_ENUMx_SET(kType,storage,x,setType)
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  \
-	NUMvector_free <type> (our x, min);
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  \
+//	NUMvector_free <type> (our x, min);
 
 #define oo_STRINGx(storage,x)  \
 	Melder_free (our x);
@@ -48,7 +48,7 @@
 		Melder_free (our x [i]);
 
 #define oo_STRINGx_SET(storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= setType::MAX; i ++) \
 		Melder_free (our x [i]);
 
 #define oo_STRINGx_VECTOR(storage,x,min,max)  \
@@ -66,7 +66,7 @@
 		our x [i]. destroy ();
 
 #define oo_STRUCT_SET(Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		our x [i]. destroy ();
 
 #define oo_STRUCT_VECTOR_FROM(Type,x,min,max)  \
diff --git a/sys/oo_EQUAL.h b/sys/oo_EQUAL.h
index 846b863..59279e4 100644
--- a/sys/oo_EQUAL.h
+++ b/sys/oo_EQUAL.h
@@ -1,6 +1,6 @@
 /* oo_EQUAL.h
  *
- * Copyright (C) 1994-2012,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1994-2012,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
 		if (our x [i] != thy x [i]) return false; \
 
 #define oo_SET(type,storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		if (our x [i] != thy x [i]) return false; \
 
 #define oo_VECTOR(type,storage,x,min,max)  \
@@ -37,18 +37,18 @@
 	if (! our x != ! thy x || \
 		(our x && ! NUMmatrix_equal <type> (our x, thy x, row1, row2, col1, col2))) return false;
 
-#define oo_ENUMx(type,storage,Type,x)  \
+#define oo_ENUMx(kType,storage,x)  \
 	if (our x != thy x) return false;
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)  \
-	for (int i = 0; i < n; i ++) if (our x [i] != thy x [i]) return false;
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)  \
+//	for (int i = 0; i < n; i ++) if (our x [i] != thy x [i]) return false;
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) if (our x [i] != thy x [i]) return false;
+//#define oo_ENUMx_SET(kType,storage,x,setType)  \
+//	for (int i = 0; i <= (int) setType::MAX; i ++) if (our x [i] != thy x [i]) return false;
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  \
-	if (! our x != ! thy x || \
-		(our x && ! NUMvector_equal <type> (our x, thy x, min, max))) return false;
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  \
+//	if (! our x != ! thy x || \
+//		(our x && ! NUMvector_equal <type> (our x, thy x, min, max))) return false;
 
 #define oo_STRINGx(storage,x)  \
 	if (! Melder_equ (our x, thy x)) return false;
@@ -58,7 +58,7 @@
 		if (! Melder_equ (our x [i], thy x [i])) return false;
 
 #define oo_STRINGx_SET(storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= setType::MAX; i ++) \
 		if (! Melder_equ (our x [i], thy x [i])) return false;
 
 #define oo_STRINGx_VECTOR(storage,x,min,max)  \
@@ -76,7 +76,7 @@
 		if (! our x [i]. equal (& thy x [i])) return false;
 
 #define oo_STRUCT_SET(Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		if (! our x [i]. equal (& thy x [i])) return false;
 
 #define oo_STRUCT_VECTOR_FROM(Type,x,min,max)  \
diff --git a/sys/oo_READ_BINARY.h b/sys/oo_READ_BINARY.h
index b14f4fa..baffc06 100644
--- a/sys/oo_READ_BINARY.h
+++ b/sys/oo_READ_BINARY.h
@@ -28,7 +28,7 @@
 	}
 
 #define oo_SET(type,storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) { \
+	for (int i = 0; i <= (int) setType::MAX; i ++) { \
 		our x [i] = binget##storage (f); \
 	}
 
@@ -42,26 +42,26 @@
 	    our x = NUMmatrix_readBinary_##storage (row1, row2, col1, col2, f); \
 	}
 
-#define oo_ENUMx(type,storage,Type,x)  \
-	our x = binget##storage (f, Type##_MIN, Type##_MAX, U"" #Type);
+#define oo_ENUMx(kType,storage,x)  \
+	our x = (kType) binget##storage (f, (int) kType::MIN, (int) kType::MAX, U"" #kType);
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)  \
-	if (n > cap) Melder_throw (U"Number of \"" #x U"\" (", n, U") greater than ", cap, U"."); \
-	for (int i = 0; i < n; i ++) { \
-		our x [i] = binget##storage (f, Type##_MIN, Type##_MAX, U"" #Type); \
-	}
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)  \
+//	if (n > cap) Melder_throw (U"Number of \"" #x U"\" (", n, U") greater than ", cap, U"."); \
+//	for (int i = 0; i < n; i ++) { \
+//		our x [i] = (kType) binget##storage (f, (int) kType::MIN, (int) kType::MAX, U"" #kType); \
+//	}
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) { \
-		our x [i] = binget##storage (f, Type##_MIN, Type##_MAX, U"" #Type); \
-	}
+//#define oo_ENUMx_SET(kType,storage,x,setType)  \
+//	for (int i = 0; i <= (int) setType::MAX; i ++) { \
+//		our x [i] = (kType) binget##storage (f, (int) kType::MIN, (int) kType::MAX, U"" #kType); \
+//	}
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  \
-	if (max >= min) { \
-		our x = NUMvector <type> (min, max); \
-		for (long i = min; i <= max; i ++) { \
-			our x [i] = binget##storage (f, Type##_MIN, Type##_MAX, U"" #Type); \
-	}
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  \
+//	if (max >= min) { \
+//		our x = NUMvector <type> (min, max); \
+//		for (integer i = min; i <= max; i ++) { \
+//			our x [i] = (kType) binget##storage (f, (int) kType::MIN, (int) kType::MAX, U"" #kType); \
+//	}
 
 #define oo_STRINGx(storage,x)  \
 	our x = binget##storage (f);
@@ -73,7 +73,7 @@
 	}
 
 #define oo_STRINGx_SET(storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) { \
+	for (int i = 0; i <= setType::MAX; i ++) { \
 		our x [i] = binget##storage (f); \
 	}
 
@@ -95,7 +95,7 @@
 	}
 
 #define oo_STRUCT_SET(Type,x,setType) \
-	for (int i = 0; i <= setType##_MAX; i ++) { \
+	for (int i = 0; i <= (int) setType::MAX; i ++) { \
 		our x [i]. readBinary (f, formatVersion); \
 	}
 
diff --git a/sys/oo_READ_TEXT.h b/sys/oo_READ_TEXT.h
index 752a263..80d6cba 100644
--- a/sys/oo_READ_TEXT.h
+++ b/sys/oo_READ_TEXT.h
@@ -36,7 +36,7 @@
 	}
 
 #define oo_SET(type,storage,x,setType)  \
-	for (long i = 0; i <= setType##_MAX; i ++) { \
+	for (long i = 0; i <= (int) setType::MAX; i ++) { \
 		try { \
 			our x [i] = texget##storage (a_text); \
 		} catch (MelderError) { \
@@ -54,27 +54,27 @@
 	    our x = NUMmatrix_readText_##storage (row1, row2, col1, col2, a_text, #x); \
 	}
 
-#define oo_ENUMx(type,storage,Type,x)  \
-	our x = texget##storage (a_text, Type##_getValue);
+#define oo_ENUMx(kType,storage,x)  \
+	our x = (kType) texget##storage (a_text, (enum_generic_getValue) kType##_getValue);
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)  \
-	if (n > cap) Melder_throw (U"Number of \"" #x U"\" (", n, U") greater than ", cap, U"."); \
-	for (long i = 0; i < n; i ++) { \
-		our x [i] = texget##storage (a_text, Type##_getValue); \
-	}
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)  \
+//	if (n > cap) Melder_throw (U"Number of \"" #x U"\" (", n, U") greater than ", cap, U"."); \
+//	for (integer i = 0; i < n; i ++) { \
+//		our x [i] = (kType) texget##storage (a_text, (enum_generic_getValue) kType##_getValue); \
+//	}
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)  \
-	for (long i = 0; i <= setType##_MAX; i ++) { \
-		our x [i] = texget##storage (a_text, & Type##_getValue); \
-	}
+//#define oo_ENUMx_SET(kType,storage,x,setType)  \
+//	for (int i = 0; i <= (int) setType::MAX; i ++) { \
+//		our x [i] = (kType) texget##storage (a_text, (enum_generic_getValue) kType##_getValue); \
+//	}
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  \
-	if (max >= min) { \
-		our x = NUMvector <type> (min, max); \
-		for (long i = min; i <= max; i ++) { \
-			our x [i] = texget##storage (a_text, & Type##_getValue); \
-		} \
-	}
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  \
+//	if (max >= min) { \
+//		our x = NUMvector <kType> (min, max); \
+//		for (integer i = min; i <= max; i ++) { \
+//			our x [i] = (kType) texget##storage (a_text, (enum_generic_getValue) kType##_getValue); \
+//		} \
+//	}
 
 #define oo_STRINGx(storage,x)  \
 	try { \
@@ -90,7 +90,7 @@
 	}
 
 #define oo_STRINGx_SET(storage,x,setType)  \
-	for (long i = 0; i <= setType##_MAX; i ++) { \
+	for (long i = 0; i <= setType::MAX; i ++) { \
 		our x [i] = texget##storage (a_text); \
 	}
 
@@ -116,7 +116,7 @@
 	}
 
 #define oo_STRUCT_SET(Type,x,setType) \
-	for (long i = 0; i <= setType##_MAX; i ++) { \
+	for (long i = 0; i <= (int) setType::MAX; i ++) { \
 		our x [i]. readText (a_text, formatVersion); \
 	}
 
diff --git a/sys/oo_WRITE_BINARY.h b/sys/oo_WRITE_BINARY.h
index a281e49..cfcd842 100644
--- a/sys/oo_WRITE_BINARY.h
+++ b/sys/oo_WRITE_BINARY.h
@@ -26,7 +26,7 @@
 		binput##storage (our x [i], f);
 
 #define oo_SET(type,storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		binput##storage (our x [i], f);
 
 #define oo_VECTOR(type,storage,x,min,max)  \
@@ -37,20 +37,20 @@
 	if (our x) \
 		NUMmatrix_writeBinary_##storage (our x, row1, row2, col1, col2, f);
 
-#define oo_ENUMx(type,storage,Type,x)  \
-	binput##storage (our x, f);
+#define oo_ENUMx(kType,storage,x)  \
+	binput##storage ((int) our x, f);
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)  \
-	for (int i = 0; i < n; i ++) \
-		binput##storage (our x [i], f);
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)  \
+//	for (int i = 0; i < n; i ++) \
+//		binput##storage ((int) our x [i], f);
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
-		binput##storage (our x [i], f);
+//#define oo_ENUMx_SET(kType,storage,x,setType)  \
+//	for (int i = 0; i <= setType::MAX; i ++) \
+//		binput##storage ((int) our x [i], f);
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  \
-	if (our x) \
-		NUMvector_writeBinary_##storage (our x, min, max, f);
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  \
+//	if (our x) \
+//		NUMvector_writeBinary_##storage ((int) our x, min, max, f);
 
 #define oo_STRINGx(storage,x)  \
 	binput##storage (our x, f);
@@ -60,7 +60,7 @@
 		binput##storage (our x [i], f);
 
 #define oo_STRINGx_SET(storage,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= setType::MAX; i ++) \
 		binput##storage (our x [i], f);
 
 #define oo_STRINGx_VECTOR(storage,x,min,max)  \
@@ -75,7 +75,7 @@
 		our x [i]. writeBinary (f);
 
 #define oo_STRUCT_SET(Type,x,setType)  \
-	for (int i = 0; i <= setType##_MAX; i ++) \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
 		our x [i]. writeBinary (f);
 
 #define oo_STRUCT_VECTOR_FROM(Type,x,min,max)  \
diff --git a/sys/oo_WRITE_TEXT.h b/sys/oo_WRITE_TEXT.h
index ce9d615..af56bf9 100644
--- a/sys/oo_WRITE_TEXT.h
+++ b/sys/oo_WRITE_TEXT.h
@@ -29,8 +29,8 @@
 
 #define oo_SET(type,storage,x,setType)  \
 	texputintro (file, U"" #x U" []:", 0,0,0,0,0); \
-	for (int i = 0; i <= setType##_MAX; i ++) \
-		texput##storage (file, our x [i], U"" #x U" [", setType##_getText (i), U"]", 0,0,0); \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
+		texput##storage (file, our x [i], U"" #x U" [", setType##_getText ((setType) i), U"]", 0,0,0); \
 	texexdent (file);
 
 #define oo_VECTOR(type,storage,x,min,max)  \
@@ -41,26 +41,26 @@
 	if (our x) \
 		NUMmatrix_writeText_##storage (our x, row1, row2, col1, col2, file, U"" #x);
 
-#define oo_ENUMx(type,storage,Type,x)  \
-	texput##storage (file, our x, Type##_getText, U"" #x, 0,0,0,0,0);
+#define oo_ENUMx(kType,storage,x)  \
+	texput##storage (file, (int) our x, (const char32* (*) (int)) kType##_getText, U"" #x, 0,0,0,0,0);
 
-#define oo_ENUMx_ARRAY(type,storage,Type,x,cap,n)  \
-	texputintro (file, U"" #x U" []:", 0,0,0,0,0); \
-	for (int i = 0; i < n; i ++) \
-		texput##storage (file, our x [i], Type##_getText, U"" #x U" [", Melder_integer (i), U"]", 0,0,0); \
-	texexdent (file);
+//#define oo_ENUMx_ARRAY(kType,storage,x,cap,n)  \
+//	texputintro (file, U"" #x U" []:", 0,0,0,0,0); \
+//	for (int i = 0; i < n; i ++) \
+//		texput##storage (file, (int) our x [i], (const char32* (*) (int)) kType##_getText, U"" #x U" [", Melder_integer (i), U"]", 0,0,0); \
+//	texexdent (file);
 
-#define oo_ENUMx_SET(type,storage,Type,x,setType)  \
-	texputintro (file, U"" #x U" []: ", n ? nullptr : U"(empty)", 0,0,0,0); \
-	for (int i = 0; i <= setType##_MAX; i ++) \
-		texput##storage (file, our x [i], Type##_getText, U"" #x U" [", setType##_getText (i), U"]", 0,0,0); \
-	texexdent (file);
+//#define oo_ENUMx_SET(kType,storage,x,setType)  \
+//	texputintro (file, U"" #x U" []: ", n ? nullptr : U"(empty)", 0,0,0,0); \
+//	for (int i = 0; i <= (int) setType::MAX; i ++) \
+//		texput##storage (file, (int) our x [i], (const char32* (*) (int)) kType##_getText, U"" #x U" [", setType##_getText ((setType) i), U"]", 0,0,0); \
+//	texexdent (file);
 
-#define oo_ENUMx_VECTOR(type,storage,Type,x,min,max)  \
-	texputintro (file, U"" #x U" []: ", max >= min ? nullptr : U"(empty)", 0,0,0,0); \
-	for (long i = min; i <= max; i ++) \
-		texput##storage (file, our x [i], Type##_getText, U"" #x U" [", Melder_integer (i), U"]", 0,0,0); \
-	texexdent (file);
+//#define oo_ENUMx_VECTOR(kType,storage,x,min,max)  \
+//	texputintro (file, U"" #x U" []: ", max >= min ? nullptr : U"(empty)", 0,0,0,0); \
+//	for (long i = min; i <= max; i ++) \
+//		texput##storage (file, (int) our x [i], (const char32* (*) (int)) kType##_getText, U"" #x U" [", Melder_integer (i), U"]", 0,0,0); \
+//	texexdent (file);
 
 #define oo_STRINGx(storage,x)  \
 	texput##storage (file, our x, U""#x, 0,0,0,0,0);
@@ -73,8 +73,8 @@
 
 #define oo_STRINGx_SET(storage,x,setType)  \
 	texputintro (file, U"" #x U" []:", 0,0,0,0,0); \
-	for (int i = 0; i <= setType##_MAX; i ++) \
-		texput##storage (file, our x [i], U"" #x U" [", setType##_getText (i), U"]", 0,0,0); \
+	for (int i = 0; i <= (int) setType::MAX; i ++) \
+		texput##storage (file, our x [i], U"" #x U" [", setType##_getText ((setType) i), U"]", 0,0,0); \
 	texexdent (file);
 
 #define oo_STRINGx_VECTOR(storage,x,min,max)  \
@@ -99,8 +99,8 @@
 
 #define oo_STRUCT_SET(Type,x,setType)  \
 	texputintro (file, U"" #x U" []:", 0,0,0,0,0); \
-	for (int i = 0; i <= setType##_MAX; i ++) { \
-		texputintro (file, U"" #x U" [", setType##_getText (i), U"]:", 0,0,0); \
+	for (int i = 0; i <= (int) setType::MAX; i ++) { \
+		texputintro (file, U"" #x U" [", setType##_getText ((setType) i), U"]:", 0,0,0); \
 		our x [i]. writeText (file); \
 		texexdent (file); \
 	} \
diff --git a/sys/oo_undef.h b/sys/oo_undef.h
index 0c76c33..001d51c 100644
--- a/sys/oo_undef.h
+++ b/sys/oo_undef.h
@@ -1,6 +1,6 @@
 /* oo_undef.h
  *
- * Copyright (C) 1994-2011,2013,2015 Paul Boersma
+ * Copyright (C) 1994-2011,2013,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,9 +23,9 @@
 #undef oo_MATRIX
 
 #undef oo_ENUMx
-#undef oo_ENUMx_ARRAY
-#undef oo_ENUMx_SET
-#undef oo_ENUMx_VECTOR
+//#undef oo_ENUMx_ARRAY
+//#undef oo_ENUMx_SET
+//#undef oo_ENUMx_VECTOR
 
 #undef oo_STRINGx
 #undef oo_STRINGx_ARRAY
diff --git a/sys/praat.cpp b/sys/praat.cpp
index 1cf3776..cac8b52 100644
--- a/sys/praat.cpp
+++ b/sys/praat.cpp
@@ -538,7 +538,7 @@ static void praat_exit (int exit_code) {
 				MelderString_append (& buffer, U"# and the buttons that you hid or showed.\n\n");
 				praat_saveMenuCommands (& buffer);
 				praat_saveAddedActions (& buffer);
-				MelderFile_writeText (& buttonsFile, buffer.string, kMelder_textOutputEncoding_ASCII_THEN_UTF16);
+				MelderFile_writeText (& buttonsFile, buffer.string, kMelder_textOutputEncoding::ASCII_THEN_UTF16);
 			} catch (MelderError) {
 				Melder_clearError ();
 			}
@@ -1574,6 +1574,28 @@ void praat_run () {
 	Melder_assert (isundef (0.0 / 0.0));
 	Melder_assert (isundef (1.0 / 0.0));
 	{
+		/*
+			Assumptions made in abcio.cpp:
+			`frexp()` returns an infinity if its argument is an infinity,
+			and not-a-number if its argument is not-a-number.
+		*/
+		int exponent;
+		Melder_assert (isundef (frexp (HUGE_VAL, & exponent)));
+		Melder_assert (isundef (frexp (0.0/0.0, & exponent)));
+		Melder_assert (isundef (frexp (undefined, & exponent)));
+		/*
+			The following relies on the facts that:
+			- positive infinity is not less than 1.0 (because it is greater than 1.0)
+			- NaN is not less than 1.0 (because it is not ordered)
+			
+			Note: we cannot replace `! (... < 1.0)` with `... >= 1.0`,
+			because `! (NaN < 1.0)` is true but `NaN >= 1.0` is false.
+		*/
+		Melder_assert (! (frexp (HUGE_VAL, & exponent) < 1.0));
+		Melder_assert (! (frexp (0.0/0.0, & exponent) < 1.0));
+		Melder_assert (! (frexp (undefined, & exponent) < 1.0));
+	}
+	{
 		numvec x { };
 		Melder_assert (! x.at);
 		Melder_assert (x.size == 0);
@@ -1590,7 +1612,7 @@ void praat_run () {
 	Melder_assert (sizeof (real32) == 4);
 	Melder_assert (sizeof (real64) == 8);
 	Melder_assert (sizeof (real80) >= 12);
-
+	Melder_assert (sizeof (integer) == sizeof (void *));
 	if (sizeof (off_t) < 8)
 		Melder_fatal (U"sizeof(off_t) is less than 8. Compile Praat with -D_FILE_OFFSET_BITS=64.");
 
diff --git a/sys/praat.h b/sys/praat.h
index 47fb4c9..c97cc5d 100644
--- a/sys/praat.h
+++ b/sys/praat.h
@@ -316,27 +316,28 @@ void praat_name2 (char32 *name, ClassInfo klas1, ClassInfo klas2);
 #define OPTIONMENUSTR4(variable,label,def)  static char32 *variable; radio = UiForm_addOptionMenu4 (dia, nullptr, & variable, U"" #variable, label, def, 1);
 #define OPTIONMENUSTRVAR OPTIONMENUSTR4
 #define OPTION(label)	UiOptionMenu_addButton (radio, label);
-#define RADIO_ENUM(label,enum,def)  \
-	RADIO (label, enum##_##def - enum##_MIN + 1) \
-	for (int ienum = enum##_MIN; ienum <= enum##_MAX; ienum ++) \
-		OPTION (enum##_getText (ienum))
-#define RADIO_ENUM4(variable,label,enum,def)  \
-	RADIO4x (variable, label, enum##_##def - enum##_MIN + 1, enum##_MIN) \
-	for (int ienum = enum##_MIN; ienum <= enum##_MAX; ienum ++) \
-		OPTION (enum##_getText (ienum))
-#define OPTIONMENU_ENUM(label,enum,def)  \
-	OPTIONMENU (label, enum##_##def - enum##_MIN + 1) \
-	for (int ienum = enum##_MIN; ienum <= enum##_MAX; ienum ++) \
-		OPTION (enum##_getText (ienum))
-#define OPTIONMENU_ENUM4(variable,label,enum,def)  \
-	OPTIONMENU4x (variable, label, enum##_##def - enum##_MIN + 1, enum##_MIN) \
-	for (int ienum = enum##_MIN; ienum <= enum##_MAX; ienum ++) \
-		OPTION (enum##_getText (ienum))
+#define RADIO_ENUM(label,kType,def)  \
+	RADIO (label, (int) kType::def - (int) kType::MIN + 1) \
+	for (int ienum = (int) kType::MIN; ienum <= (int) kType::MAX; ienum ++) \
+		OPTION (kType##_getText ((kType) ienum))
+#define RADIO_ENUM4(variable,label,kType,def)  \
+	static kType variable; \
+	radio = UiForm_addRadio4 (dia, (int *) & variable, nullptr, U"" #variable, label, (int) kType::def - (int) kType::MIN + 1, (int) kType::MIN); \
+	for (int ienum = (int) kType::MIN; ienum <= (int) kType::MAX; ienum ++) \
+		OPTION (kType##_getText ((kType) ienum))
+#define OPTIONMENU_ENUM(label,kType,def)  \
+	OPTIONMENU (label, (int) kType::def - (int) kType::MIN + 1) \
+	for (int ienum = (int) kType::MIN; ienum <= (int) kType::MAX; ienum ++) \
+		OPTION (kType##_getText ((kType) ienum))
+#define OPTIONMENU_ENUM4(variable,label,kType,def)  \
+	OPTIONMENU4x (variable, label, (int) kType::def - (int) kType::MIN + 1, (int) kType::MIN) \
+	for (int ienum = (int) kType::MIN; ienum <= (int) kType::MAX; ienum ++) \
+		OPTION (kType##_getText ((kType) ienum))
 #define OPTIONMENU_ENUMVAR OPTIONMENU_ENUM4
-#define OPTIONMENU_ENUMSTR4(variable,label,enum,def)  \
-	OPTIONMENUSTRVAR (variable, label, enum##_##def - enum##_MIN + 1) \
-	for (int ienum = enum##_MIN; ienum <= enum##_MAX; ienum ++) \
-		OPTION (enum##_getText (ienum))
+#define OPTIONMENU_ENUMSTR4(variable,label,kType,def)  \
+	OPTIONMENUSTRVAR (variable, label, (int) kType::def - (int) kType::MIN + 1) \
+	for (int ienum = (int) kType::MIN; ienum <= (int) kType::MAX; ienum ++) \
+		OPTION (kType##_getText ((kType) ienum))
 #define OPTIONMENU_ENUMSTRVAR OPTIONMENU_ENUMSTR4
 #define LIST(label,n,str,def)  UiForm_addList (dia, label, n, str, def);
 #define LIST4(variable,label,n,str,def)  static long variable; UiForm_addList4 (dia, & variable, nullptr, U"" #variable, label, n, str, def);
@@ -353,7 +354,7 @@ void praat_name2 (char32 *name, ClassInfo klas1, ClassInfo klas2);
 #define SET_REAL(name,value)	UiForm_setReal (dia, name, value);
 #define SET_INTEGER(name,value)	UiForm_setInteger (dia, name, value);
 #define SET_STRING(name,value)	UiForm_setString (dia, name, value);
-#define SET_ENUM(name,enum,value)  SET_STRING (name, enum##_getText (value))
+#define SET_ENUM(name,kType,value)  { kType typeCheckDummy = value; (void) typeCheckDummy; } SET_STRING (name, kType##_getText ((kType) value))
 
 #define DO  \
 			UiForm_do (dia, modified); \
diff --git a/sys/praat_logo.cpp b/sys/praat_logo.cpp
index 9ce5b4a..f48cb89 100644
--- a/sys/praat_logo.cpp
+++ b/sys/praat_logo.cpp
@@ -27,7 +27,7 @@ static void logo_defaultDraw (Graphics g) {
 	Graphics_fillRectangle (g, 0.05, 0.95, 0.1, 0.9);
 	Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
 	Graphics_setColour (g, Graphics_YELLOW);
-	Graphics_setFont (g, kGraphics_font_TIMES);
+	Graphics_setFont (g, kGraphics_font::TIMES);
 	Graphics_setFontSize (g, 24);
 	Graphics_setFontStyle (g, Graphics_ITALIC);
 	Graphics_setUnderscoreIsSubscript (g, false);   // because program names may contain underscores
diff --git a/sys/praat_objectMenus.cpp b/sys/praat_objectMenus.cpp
index 6e423ad..2299105 100644
--- a/sys/praat_objectMenus.cpp
+++ b/sys/praat_objectMenus.cpp
@@ -275,19 +275,19 @@ DO
 	}
 	switch (result. expressionType) {
 		case kFormula_EXPRESSION_TYPE_NUMERIC: {
-			Melder_information (result. result.numericResult);
+			Melder_information (result. numericResult);
 		} break;
 		case kFormula_EXPRESSION_TYPE_STRING: {
-			Melder_information (result. result.stringResult);
-			Melder_free (result. result.stringResult);
+			Melder_information (result. stringResult);
+			Melder_free (result. stringResult);
 		} break;
 		case kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR: {
-			Melder_information (result. result.numericVectorResult);
-			result. result.numericVectorResult. reset();
+			Melder_information (result. numericVectorResult);
+			if (result. owned) result. numericVectorResult. reset();
 		} break;
 		case kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX: {
-			Melder_information (result. result.numericMatrixResult);
-			result. result.numericMatrixResult. reset();
+			Melder_information (result. numericMatrixResult);
+			if (result. owned) result. numericMatrixResult. reset();
 		}
 	}
 END }
diff --git a/sys/praat_picture.cpp b/sys/praat_picture.cpp
index 1a160e7..67cdfcc 100644
--- a/sys/praat_picture.cpp
+++ b/sys/praat_picture.cpp
@@ -27,7 +27,7 @@
 static bool praat_mouseSelectsInnerViewport;
 
 void praat_picture_prefs () {
-	Preferences_addEnum (U"Picture.font", & theCurrentPraatPicture -> font, kGraphics_font, kGraphics_font_DEFAULT);
+	Preferences_addEnum (U"Picture.font", & theCurrentPraatPicture -> font, kGraphics_font, kGraphics_font::DEFAULT);
 	Preferences_addInt (U"Picture.fontSize", & theCurrentPraatPicture -> fontSize, 10);
 	Preferences_addBool (U"Picture.mouseSelectsInnerViewport", & praat_mouseSelectsInnerViewport, false);
 }
@@ -40,13 +40,13 @@ static autoPicture praat_picture;
 
 /***** "Font" MENU: font part *****/
 
-static GuiMenuItem praatButton_fonts [1 + kGraphics_font_MAX];
+static GuiMenuItem praatButton_fonts [1 + (int) kGraphics_font::MAX];
 
 static void updateFontMenu () {
 	if (! theCurrentPraatApplication -> batch) {
-		if (theCurrentPraatPicture -> font < kGraphics_font_MIN) theCurrentPraatPicture -> font = kGraphics_font_MIN;
-		if (theCurrentPraatPicture -> font > kGraphics_font_MAX) theCurrentPraatPicture -> font = kGraphics_font_MAX;
-		for (int i = kGraphics_font_MIN; i <= kGraphics_font_MAX; i ++) {
+		if (theCurrentPraatPicture -> font < (int) kGraphics_font::MIN) theCurrentPraatPicture -> font = (int) kGraphics_font::MIN;
+		if (theCurrentPraatPicture -> font > (int) kGraphics_font::MAX) theCurrentPraatPicture -> font = (int) kGraphics_font::MAX;
+		for (int i = (int) kGraphics_font::MIN; i <= (int) kGraphics_font::MAX; i ++) {
 			GuiMenuItem_check (praatButton_fonts [i], theCurrentPraatPicture -> font == i);
 		}
 	}
@@ -56,15 +56,15 @@ static void setFont (kGraphics_font font) {
 		autoPraatPicture picture;
 		Graphics_setFont (GRAPHICS, font);
 	}
-	theCurrentPraatPicture -> font = font;
+	theCurrentPraatPicture -> font = (int) font;
 	if (theCurrentPraatPicture == & theForegroundPraatPicture) {
 		updateFontMenu ();
 	}
 }
-DIRECT (GRAPHICS_Times)     { setFont (kGraphics_font_TIMES);     END }
-DIRECT (GRAPHICS_Helvetica) { setFont (kGraphics_font_HELVETICA); END }
-DIRECT (GRAPHICS_Palatino)  { setFont (kGraphics_font_PALATINO);  END }
-DIRECT (GRAPHICS_Courier)   { setFont (kGraphics_font_COURIER);   END }
+DIRECT (GRAPHICS_Times)     { setFont (kGraphics_font::TIMES);     END }
+DIRECT (GRAPHICS_Helvetica) { setFont (kGraphics_font::HELVETICA); END }
+DIRECT (GRAPHICS_Palatino)  { setFont (kGraphics_font::PALATINO);  END }
+DIRECT (GRAPHICS_Courier)   { setFont (kGraphics_font::COURIER);   END }
 
 /***** "Font" MENU: size part *****/
 
@@ -289,7 +289,7 @@ DO
 	autoPraatPicture picture;
 	Graphics_inqWindow (GRAPHICS, & x1WC, & x2WC, & y1WC, & y2WC);
 	Graphics_setWindow (GRAPHICS, 0, 1, 0, 1);
-	Graphics_setTextAlignment (GRAPHICS, horizontalAlignment, verticalAlignment);
+	Graphics_setTextAlignment (GRAPHICS, (kGraphics_horizontalAlignment) horizontalAlignment, verticalAlignment);
 	Graphics_setTextRotation (GRAPHICS, rotation);
 	Graphics_text (GRAPHICS, horizontalAlignment == 0 ? 0.0 : horizontalAlignment == 1 ? 0.5 : 1.0,
 		verticalAlignment == 0 ? 0.0 : verticalAlignment == 1 ? 0.5 : 1.0, text);
@@ -653,7 +653,7 @@ FORM (GRAPHICS_Text, U"Praat picture: Text", U"Text...") {
 	OK
 DO
 	GRAPHICS_NONE
-		Graphics_setTextAlignment (GRAPHICS, horizontalAlignment, verticalAlignment);
+		Graphics_setTextAlignment (GRAPHICS, (kGraphics_horizontalAlignment) horizontalAlignment, verticalAlignment);
 		Graphics_setInner (GRAPHICS);
 		Graphics_text (GRAPHICS, horizontalPosition, verticalPosition, text);
 		Graphics_unsetInner (GRAPHICS);
@@ -681,7 +681,7 @@ DO
 	kGraphics_font currentFont = Graphics_inqFont (GRAPHICS);
 	int currentSize = Graphics_inqFontSize (GRAPHICS);
 	GRAPHICS_NONE
-		Graphics_setTextAlignment (GRAPHICS, horizontalAlignment, verticalAlignment);
+		Graphics_setTextAlignment (GRAPHICS, (kGraphics_horizontalAlignment) horizontalAlignment, verticalAlignment);
 		Graphics_setInner (GRAPHICS);
 		Graphics_setFont (GRAPHICS, (kGraphics_font) font);
 		Graphics_setFontSize (GRAPHICS, fontSize);
@@ -779,7 +779,7 @@ DO
 	for (long i = 1; i <= numberOfHorizontalSteps; i ++) {
 		Formula_Result result;
 		Formula_run (1, i, & result);
-		y [i] = result. result.numericResult;
+		y [i] = result. numericResult;
 	}
 	GRAPHICS_NONE
 		Graphics_setInner (GRAPHICS);
@@ -1506,7 +1506,7 @@ DIRECT (GRAPHICS_Picture_settings_report) {
 		theCurrentPraatPicture != & theForegroundPraatPicture ?
 			theCurrentPraatPicture -> y2NDC - ymargin :
 			12 - theCurrentPraatPicture -> y1NDC - ymargin, units);
-	MelderInfo_writeLine (U"Font: ", kGraphics_font_getText (theCurrentPraatPicture -> font));
+	MelderInfo_writeLine (U"Font: ", kGraphics_font_getText ((kGraphics_font) theCurrentPraatPicture -> font));
 	MelderInfo_writeLine (U"Line type: ",
 		theCurrentPraatPicture -> lineType == Graphics_DRAWN ? U"Solid" :
 		theCurrentPraatPicture -> lineType == Graphics_DOTTED ? U"Dotted" :
@@ -1870,10 +1870,10 @@ void praat_picture_init () {
 	praatButton_18 = praat_addMenuCommand (U"Picture", U"Font", U"18", nullptr, praat_CHECKBUTTON | praat_NO_API, GRAPHICS_18);
 	praatButton_24 = praat_addMenuCommand (U"Picture", U"Font", U"24", nullptr, praat_CHECKBUTTON | praat_NO_API, GRAPHICS_24);
 	praat_addMenuCommand (U"Picture", U"Font", U"-- font ---", nullptr, 0, nullptr);
-	praatButton_fonts [kGraphics_font_TIMES] = praat_addMenuCommand (U"Picture", U"Font", U"Times", nullptr, praat_RADIO_FIRST, GRAPHICS_Times);
-	praatButton_fonts [kGraphics_font_HELVETICA] = praat_addMenuCommand (U"Picture", U"Font", U"Helvetica", nullptr, praat_RADIO_NEXT, GRAPHICS_Helvetica);
-	praatButton_fonts [kGraphics_font_PALATINO] = praat_addMenuCommand (U"Picture", U"Font", U"Palatino", nullptr, praat_RADIO_NEXT, GRAPHICS_Palatino);
-	praatButton_fonts [kGraphics_font_COURIER] = praat_addMenuCommand (U"Picture", U"Font", U"Courier", nullptr, praat_RADIO_NEXT, GRAPHICS_Courier);
+	praatButton_fonts [(int) kGraphics_font::TIMES] = praat_addMenuCommand (U"Picture", U"Font", U"Times", nullptr, praat_RADIO_FIRST, GRAPHICS_Times);
+	praatButton_fonts [(int) kGraphics_font::HELVETICA] = praat_addMenuCommand (U"Picture", U"Font", U"Helvetica", nullptr, praat_RADIO_NEXT, GRAPHICS_Helvetica);
+	praatButton_fonts [(int) kGraphics_font::PALATINO] = praat_addMenuCommand (U"Picture", U"Font", U"Palatino", nullptr, praat_RADIO_NEXT, GRAPHICS_Palatino);
+	praatButton_fonts [(int) kGraphics_font::COURIER] = praat_addMenuCommand (U"Picture", U"Font", U"Courier", nullptr, praat_RADIO_NEXT, GRAPHICS_Courier);
 
 	praat_addMenuCommand (U"Picture", U"Help", U"Picture window help", nullptr, '?', HELP_PictureWindowHelp);
 	praat_addMenuCommand (U"Picture", U"Help", U"About special symbols", nullptr, 0, HELP_AboutSpecialSymbols);
diff --git a/sys/praat_script.cpp b/sys/praat_script.cpp
index 8576d47..87bbf56 100644
--- a/sys/praat_script.cpp
+++ b/sys/praat_script.cpp
@@ -139,11 +139,11 @@ static int parseCommaSeparatedArguments (Interpreter interpreter, char32 *argume
 			switch (result. expressionType) {
 				case kFormula_EXPRESSION_TYPE_NUMERIC: {
 					args [narg]. which = Stackel_NUMBER;
-					args [narg]. number = result. result. numericResult;
+					args [narg]. number = result. numericResult;
 				} break;
 				case kFormula_EXPRESSION_TYPE_STRING: {
 					args [narg]. which = Stackel_STRING;
-					args [narg]. string = result. result. stringResult;
+					args [narg]. string = result. stringResult;
 				} break;
 			}
 			arguments = p + 1;
diff --git a/sys/praat_version.h b/sys/praat_version.h
index 294cdc3..6254488 100644
--- a/sys/praat_version.h
+++ b/sys/praat_version.h
@@ -1,5 +1,5 @@
-#define PRAAT_VERSION_STR 6.0.31
-#define PRAAT_VERSION_NUM 6031
+#define PRAAT_VERSION_STR 6.0.32
+#define PRAAT_VERSION_NUM 6032
 #define PRAAT_YEAR 2017
-#define PRAAT_MONTH August
-#define PRAAT_DAY 21
+#define PRAAT_MONTH September
+#define PRAAT_DAY 16
diff --git a/sys/prefs.h b/sys/prefs.h
index 6264011..e51161d 100644
--- a/sys/prefs.h
+++ b/sys/prefs.h
@@ -2,7 +2,7 @@
 #define _prefs_h_
 /* prefs.h
  *
- * Copyright (C) 2013,2015 Paul Boersma
+ * Copyright (C) 2013,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/sys/prefs_define.h b/sys/prefs_define.h
index ea10a60..0a075cf 100644
--- a/sys/prefs_define.h
+++ b/sys/prefs_define.h
@@ -1,6 +1,6 @@
 /* prefs_define.h
  *
- * Copyright (C) 2013,2015 Paul Boersma
+ * Copyright (C) 2013,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -75,11 +75,11 @@
 
 #define prefs_add_enum(Klas,name,version,enumerated,default) \
 	enum enumerated struct##Klas :: s_##name; \
-	enum enumerated struct##Klas :: sdefault_##name = enumerated##_##default;
+	enum enumerated struct##Klas :: sdefault_##name = enumerated :: default;
 #define prefs_add_enum_with_data(Klas,name,version,enumerated,default)  prefs_add_enum (Klas, name, version, enumerated, default)
 #define prefs_override_enum(Klas,name,version,enumerated,default) \
 	enum enumerated struct##Klas :: s_##name; \
-	enum enumerated struct##Klas :: sdefault_##name = enumerated##_##default;
+	enum enumerated struct##Klas :: sdefault_##name = enumerated :: default;
 
 #define prefs_add_string(Klas,name,version,default) \
 	char32 struct##Klas :: s_##name [Preferences_STRING_BUFFER_SIZE]; \
diff --git a/sys/prefs_install.h b/sys/prefs_install.h
index 2a64278..d3e1703 100644
--- a/sys/prefs_install.h
+++ b/sys/prefs_install.h
@@ -67,10 +67,10 @@
 	Preferences_addDouble (Melder_cat (U"" #Klas U"." #name, version >= 2 ? U"." #version : U""), & s_##name, Melder_atof (sdefault_##name));
 
 #define prefs_add_enum(Klas,name,version,enumerated,default) \
-	Preferences_addEnum (Melder_cat (U"" #Klas U"." #name, version >= 2 ? U"." #version : U""), & s_##name, enumerated, enumerated##_##default);
+	Preferences_addEnum (Melder_cat (U"" #Klas U"." #name, version >= 2 ? U"." #version : U""), & s_##name, enumerated, enumerated :: default);
 #define prefs_add_enum_with_data(Klas,name,version,enumerated,default)  prefs_add_enum (Klas, name, version, enumerated, default)
 #define prefs_override_enum(Klas,name,version,enumerated,default) \
-	Preferences_addEnum (Melder_cat (U"" #Klas U"." #name, version >= 2 ? U"." #version : U""), & s_##name, enumerated, enumerated##_##default);
+	Preferences_addEnum (Melder_cat (U"" #Klas U"." #name, version >= 2 ? U"." #version : U""), & s_##name, enumerated, enumerated :: default);
 
 #define prefs_add_string(Klas,name,version,default) \
 	Preferences_addString (Melder_cat (U"" #Klas U"." #name, version >= 2 ? U"." #version : U""), & s_##name [0], default);
diff --git a/sys/tensor.cpp b/sys/tensor.cpp
index 27ea28a..fc4ea48 100644
--- a/sys/tensor.cpp
+++ b/sys/tensor.cpp
@@ -17,7 +17,8 @@
  */
 
 #include "tensor.h"
-#include "NUM2.h"
+#include "NUM2.h"   /* for NUMsort2 */
+#include "PAIRWISE_SUM.h"
 
 void numvec :: _initAt (integer givenSize, bool zero) {
 	Melder_assert (givenSize >= 0);
@@ -46,221 +47,6 @@ void nummat :: _freeAt () noexcept {
 	if (our at) NUMmatrix_free (our at, 1, 1);
 }
 
-/*
-	Recursive ("pairwise") addition preserves precision.
-	Therefore, don't delete the parentheses!
-*/
-#define tensor_ADD_1(offset)  tensor_TERM ((1+offset))
-#define tensor_ADD_2(offset)  tensor_TERM ((1+offset)) + tensor_TERM ((2+offset))
-#define tensor_ADD_3(offset)  tensor_TERM ((1+offset)) + tensor_TERM ((2+offset)) + tensor_TERM ((3+offset))
-#define tensor_ADD_4(offset)  (tensor_ADD_2 (offset)) + (tensor_ADD_2 (2+offset))
-#define tensor_ADD_5(offset)  (tensor_ADD_3 (offset)) + (tensor_ADD_2 (3+offset))
-#define tensor_ADD_6(offset)  (tensor_ADD_3 (offset)) + (tensor_ADD_3 (3+offset))
-#define tensor_ADD_7(offset)  (tensor_ADD_4 (offset)) + (tensor_ADD_3 (4+offset))
-#define tensor_ADD_8(offset)  (tensor_ADD_4 (offset)) + (tensor_ADD_4 (4+offset))
-#define tensor_ADD_9(offset)  (tensor_ADD_5 (offset)) + (tensor_ADD_4 (5+offset))
-#define tensor_ADD_10(offset)  (tensor_ADD_5 (offset)) + (tensor_ADD_5 (5+offset))
-#define tensor_ADD_11(offset)  (tensor_ADD_6 (offset)) + (tensor_ADD_5 (6+offset))
-#define tensor_ADD_12(offset)  (tensor_ADD_6 (offset)) + (tensor_ADD_6 (6+offset))
-#define tensor_ADD_13(offset)  (tensor_ADD_7 (offset)) + (tensor_ADD_6 (7+offset))
-#define tensor_ADD_14(offset)  (tensor_ADD_7 (offset)) + (tensor_ADD_7 (7+offset))
-#define tensor_ADD_15(offset)  (tensor_ADD_8 (offset)) + (tensor_ADD_7 (8+offset))
-#define tensor_ADD_16(offset)  (tensor_ADD_8 (offset)) + (tensor_ADD_8 (8+offset))
-#define tensor_ADD_17(offset)  (tensor_ADD_9 (offset)) + (tensor_ADD_8 (9+offset))
-#define tensor_ADD_18(offset)  (tensor_ADD_9 (offset)) + (tensor_ADD_9 (9+offset))
-#define tensor_ADD_19(offset)  (tensor_ADD_10 (offset)) + (tensor_ADD_9 (10+offset))
-#define tensor_ADD_20(offset)  (tensor_ADD_10 (offset)) + (tensor_ADD_10 (10+offset))
-#define tensor_ADD_21(offset)  (tensor_ADD_11 (offset)) + (tensor_ADD_10 (11+offset))
-#define tensor_ADD_22(offset)  (tensor_ADD_11 (offset)) + (tensor_ADD_11 (11+offset))
-#define tensor_ADD_23(offset)  (tensor_ADD_12 (offset)) + (tensor_ADD_11 (12+offset))
-#define tensor_ADD_24(offset)  (tensor_ADD_12 (offset)) + (tensor_ADD_12 (12+offset))
-#define tensor_ADD_25(offset)  (tensor_ADD_13 (offset)) + (tensor_ADD_12 (13+offset))
-#define tensor_ADD_26(offset)  (tensor_ADD_13 (offset)) + (tensor_ADD_13 (13+offset))
-#define tensor_ADD_27(offset)  (tensor_ADD_14 (offset)) + (tensor_ADD_13 (14+offset))
-#define tensor_ADD_28(offset)  (tensor_ADD_14 (offset)) + (tensor_ADD_14 (14+offset))
-#define tensor_ADD_29(offset)  (tensor_ADD_15 (offset)) + (tensor_ADD_14 (15+offset))
-#define tensor_ADD_30(offset)  (tensor_ADD_15 (offset)) + (tensor_ADD_15 (15+offset))
-#define tensor_ADD_31(offset)  (tensor_ADD_16 (offset)) + (tensor_ADD_15 (16+offset))
-#define tensor_ADD_32(offset)  (tensor_ADD_16 (offset)) + (tensor_ADD_16 (16+offset))
-#define tensor_ADD_33(offset)  (tensor_ADD_17 (offset)) + (tensor_ADD_16 (17+offset))
-#define tensor_ADD_34(offset)  (tensor_ADD_17 (offset)) + (tensor_ADD_17 (17+offset))
-#define tensor_ADD_35(offset)  (tensor_ADD_18 (offset)) + (tensor_ADD_17 (18+offset))
-#define tensor_ADD_36(offset)  (tensor_ADD_18 (offset)) + (tensor_ADD_18 (18+offset))
-#define tensor_ADD_37(offset)  (tensor_ADD_19 (offset)) + (tensor_ADD_18 (19+offset))
-#define tensor_ADD_38(offset)  (tensor_ADD_19 (offset)) + (tensor_ADD_19 (19+offset))
-#define tensor_ADD_39(offset)  (tensor_ADD_20 (offset)) + (tensor_ADD_19 (20+offset))
-#define tensor_ADD_40(offset)  (tensor_ADD_20 (offset)) + (tensor_ADD_20 (20+offset))
-#define tensor_ADD_41(offset)  (tensor_ADD_21 (offset)) + (tensor_ADD_20 (21+offset))
-#define tensor_ADD_42(offset)  (tensor_ADD_21 (offset)) + (tensor_ADD_21 (21+offset))
-#define tensor_ADD_43(offset)  (tensor_ADD_22 (offset)) + (tensor_ADD_21 (22+offset))
-#define tensor_ADD_44(offset)  (tensor_ADD_22 (offset)) + (tensor_ADD_22 (22+offset))
-#define tensor_ADD_45(offset)  (tensor_ADD_23 (offset)) + (tensor_ADD_22 (23+offset))
-#define tensor_ADD_46(offset)  (tensor_ADD_23 (offset)) + (tensor_ADD_23 (23+offset))
-#define tensor_ADD_47(offset)  (tensor_ADD_24 (offset)) + (tensor_ADD_23 (24+offset))
-#define tensor_ADD_48(offset)  (tensor_ADD_24 (offset)) + (tensor_ADD_24 (24+offset))
-#define tensor_ADD_49(offset)  (tensor_ADD_25 (offset)) + (tensor_ADD_24 (25+offset))
-#define tensor_ADD_50(offset)  (tensor_ADD_25 (offset)) + (tensor_ADD_25 (25+offset))
-#define tensor_ADD_51(offset)  (tensor_ADD_26 (offset)) + (tensor_ADD_25 (26+offset))
-#define tensor_ADD_52(offset)  (tensor_ADD_26 (offset)) + (tensor_ADD_26 (26+offset))
-#define tensor_ADD_53(offset)  (tensor_ADD_27 (offset)) + (tensor_ADD_26 (27+offset))
-#define tensor_ADD_54(offset)  (tensor_ADD_27 (offset)) + (tensor_ADD_27 (27+offset))
-#define tensor_ADD_55(offset)  (tensor_ADD_28 (offset)) + (tensor_ADD_27 (28+offset))
-#define tensor_ADD_56(offset)  (tensor_ADD_28 (offset)) + (tensor_ADD_28 (28+offset))
-#define tensor_ADD_57(offset)  (tensor_ADD_29 (offset)) + (tensor_ADD_28 (29+offset))
-#define tensor_ADD_58(offset)  (tensor_ADD_29 (offset)) + (tensor_ADD_29 (29+offset))
-#define tensor_ADD_59(offset)  (tensor_ADD_30 (offset)) + (tensor_ADD_29 (30+offset))
-#define tensor_ADD_60(offset)  (tensor_ADD_30 (offset)) + (tensor_ADD_30 (30+offset))
-#define tensor_ADD_61(offset)  (tensor_ADD_31 (offset)) + (tensor_ADD_30 (31+offset))
-#define tensor_ADD_62(offset)  (tensor_ADD_31 (offset)) + (tensor_ADD_31 (31+offset))
-#define tensor_ADD_63(offset)  (tensor_ADD_32 (offset)) + (tensor_ADD_31 (32+offset))
-#define tensor_ADD_64(offset)  (tensor_ADD_32 (offset)) + (tensor_ADD_32 (32+offset))
-
-#define tensor_ADD_casesUpTo7(remainder,sum)  \
-	switch (remainder) {  \
-		case 0: sum = 0.0; break;  \
-		case 1: sum = tensor_ADD_1 (0); break;  \
-		case 2: sum = tensor_ADD_2 (0); break;  \
-		case 3: sum = tensor_ADD_3 (0); break;  \
-		case 4: sum = tensor_ADD_4 (0); break;  \
-		case 5: sum = tensor_ADD_5 (0); break;  \
-		case 6: sum = tensor_ADD_6 (0); break;  \
-		case 7: sum = tensor_ADD_7 (0); break;  \
-		default: sum = undefined;  \
-	}
-
-#define tensor_ADD_casesUpTo15(remainder,sum)  \
-	switch (remainder) {  \
-		case 0: sum = 0.0; break;  \
-		case 1: sum = tensor_ADD_1 (0); break;  \
-		case 2: sum = tensor_ADD_2 (0); break;  \
-		case 3: sum = tensor_ADD_3 (0); break;  \
-		case 4: sum = tensor_ADD_4 (0); break;  \
-		case 5: sum = tensor_ADD_5 (0); break;  \
-		case 6: sum = tensor_ADD_6 (0); break;  \
-		case 7: sum = tensor_ADD_7 (0); break;  \
-		case 8: sum = tensor_ADD_8 (0); break;  \
-		case 9: sum = tensor_ADD_9 (0); break;  \
-		case 10: sum = tensor_ADD_10 (0); break;  \
-		case 11: sum = tensor_ADD_11 (0); break;  \
-		case 12: sum = tensor_ADD_12 (0); break;  \
-		case 13: sum = tensor_ADD_13 (0); break;  \
-		case 14: sum = tensor_ADD_14 (0); break;  \
-		case 15: sum = tensor_ADD_15 (0); break;  \
-		default: sum = undefined;  \
-	}
-
-#define tensor_ADD_casesUpTo31(remainder,sum)  \
-	switch (remainder) {  \
-		case 0: sum = 0.0; break;  \
-		case 1: sum = tensor_ADD_1 (0); break;  \
-		case 2: sum = tensor_ADD_2 (0); break;  \
-		case 3: sum = tensor_ADD_3 (0); break;  \
-		case 4: sum = tensor_ADD_4 (0); break;  \
-		case 5: sum = tensor_ADD_5 (0); break;  \
-		case 6: sum = tensor_ADD_6 (0); break;  \
-		case 7: sum = tensor_ADD_7 (0); break;  \
-		case 8: sum = tensor_ADD_8 (0); break;  \
-		case 9: sum = tensor_ADD_9 (0); break;  \
-		case 10: sum = tensor_ADD_10 (0); break;  \
-		case 11: sum = tensor_ADD_11 (0); break;  \
-		case 12: sum = tensor_ADD_12 (0); break;  \
-		case 13: sum = tensor_ADD_13 (0); break;  \
-		case 14: sum = tensor_ADD_14 (0); break;  \
-		case 15: sum = tensor_ADD_15 (0); break;  \
-		case 16: sum = tensor_ADD_16 (0); break;  \
-		case 17: sum = tensor_ADD_17 (0); break;  \
-		case 18: sum = tensor_ADD_18 (0); break;  \
-		case 19: sum = tensor_ADD_19 (0); break;  \
-		case 20: sum = tensor_ADD_20 (0); break;  \
-		case 21: sum = tensor_ADD_21 (0); break;  \
-		case 22: sum = tensor_ADD_22 (0); break;  \
-		case 23: sum = tensor_ADD_23 (0); break;  \
-		case 24: sum = tensor_ADD_24 (0); break;  \
-		case 25: sum = tensor_ADD_25 (0); break;  \
-		case 26: sum = tensor_ADD_26 (0); break;  \
-		case 27: sum = tensor_ADD_27 (0); break;  \
-		case 28: sum = tensor_ADD_28 (0); break;  \
-		case 29: sum = tensor_ADD_29 (0); break;  \
-		case 30: sum = tensor_ADD_30 (0); break;  \
-		case 31: sum = tensor_ADD_31 (0); break;  \
-		default: sum = undefined;  \
-	}
-
-#define tensor_ADD_casesUpTo63(remainder,sum)  \
-	switch (remainder) {  \
-		case 0: sum = 0.0; break;  \
-		case 1: sum = tensor_ADD_1 (0); break;  \
-		case 2: sum = tensor_ADD_2 (0); break;  \
-		case 3: sum = tensor_ADD_3 (0); break;  \
-		case 4: sum = tensor_ADD_4 (0); break;  \
-		case 5: sum = tensor_ADD_5 (0); break;  \
-		case 6: sum = tensor_ADD_6 (0); break;  \
-		case 7: sum = tensor_ADD_7 (0); break;  \
-		case 8: sum = tensor_ADD_8 (0); break;  \
-		case 9: sum = tensor_ADD_9 (0); break;  \
-		case 10: sum = tensor_ADD_10 (0); break;  \
-		case 11: sum = tensor_ADD_11 (0); break;  \
-		case 12: sum = tensor_ADD_12 (0); break;  \
-		case 13: sum = tensor_ADD_13 (0); break;  \
-		case 14: sum = tensor_ADD_14 (0); break;  \
-		case 15: sum = tensor_ADD_15 (0); break;  \
-		case 16: sum = tensor_ADD_16 (0); break;  \
-		case 17: sum = tensor_ADD_17 (0); break;  \
-		case 18: sum = tensor_ADD_18 (0); break;  \
-		case 19: sum = tensor_ADD_19 (0); break;  \
-		case 20: sum = tensor_ADD_20 (0); break;  \
-		case 21: sum = tensor_ADD_21 (0); break;  \
-		case 22: sum = tensor_ADD_22 (0); break;  \
-		case 23: sum = tensor_ADD_23 (0); break;  \
-		case 24: sum = tensor_ADD_24 (0); break;  \
-		case 25: sum = tensor_ADD_25 (0); break;  \
-		case 26: sum = tensor_ADD_26 (0); break;  \
-		case 27: sum = tensor_ADD_27 (0); break;  \
-		case 28: sum = tensor_ADD_28 (0); break;  \
-		case 29: sum = tensor_ADD_29 (0); break;  \
-		case 30: sum = tensor_ADD_30 (0); break;  \
-		case 31: sum = tensor_ADD_31 (0); break;  \
-		case 32: sum = tensor_ADD_32 (0); break;  \
-		case 33: sum = tensor_ADD_33 (0); break;  \
-		case 34: sum = tensor_ADD_34 (0); break;  \
-		case 35: sum = tensor_ADD_35 (0); break;  \
-		case 36: sum = tensor_ADD_36 (0); break;  \
-		case 37: sum = tensor_ADD_37 (0); break;  \
-		case 38: sum = tensor_ADD_38 (0); break;  \
-		case 39: sum = tensor_ADD_39 (0); break;  \
-		case 40: sum = tensor_ADD_40 (0); break;  \
-		case 41: sum = tensor_ADD_41 (0); break;  \
-		case 42: sum = tensor_ADD_42 (0); break;  \
-		case 43: sum = tensor_ADD_43 (0); break;  \
-		case 44: sum = tensor_ADD_44 (0); break;  \
-		case 45: sum = tensor_ADD_45 (0); break;  \
-		case 46: sum = tensor_ADD_46 (0); break;  \
-		case 47: sum = tensor_ADD_47 (0); break;  \
-		case 48: sum = tensor_ADD_48 (0); break;  \
-		case 49: sum = tensor_ADD_49 (0); break;  \
-		case 50: sum = tensor_ADD_50 (0); break;  \
-		case 51: sum = tensor_ADD_51 (0); break;  \
-		case 52: sum = tensor_ADD_52 (0); break;  \
-		case 53: sum = tensor_ADD_53 (0); break;  \
-		case 54: sum = tensor_ADD_54 (0); break;  \
-		case 55: sum = tensor_ADD_55 (0); break;  \
-		case 56: sum = tensor_ADD_56 (0); break;  \
-		case 57: sum = tensor_ADD_57 (0); break;  \
-		case 58: sum = tensor_ADD_58 (0); break;  \
-		case 59: sum = tensor_ADD_59 (0); break;  \
-		case 60: sum = tensor_ADD_60 (0); break;  \
-		case 61: sum = tensor_ADD_61 (0); break;  \
-		case 62: sum = tensor_ADD_62 (0); break;  \
-		case 63: sum = tensor_ADD_63 (0); break;  \
-		default: sum = undefined;  \
-	}
-/*
-	For instance:
-		tensor_ADD_4 (0) =
-			(tensor_ADD_2 (0)) + (tensor_ADD_2 (2)) =
-			(tensor_TERM ((1+0)) + tensor_TERM ((2+0))) + (tensor_TERM ((1+2)) + tensor_TERM ((2+2)))
-*/
-
 void sum_mean_scalar (numvec x, real *p_sum, real *p_mean) noexcept {
 	if (x.size <= 4) {
 		switch (x.size) {
@@ -277,7 +63,7 @@ void sum_mean_scalar (numvec x, real *p_sum, real *p_mean) noexcept {
 			} break; case 3: {
 				real80 sum = (real80) x [1] + (real80) x [2] + (real80) x [3];
 				if (p_sum) *p_sum = (real) sum;
-				if (p_mean) *p_mean = real (sum / 3.0);
+				if (p_mean) *p_mean = real ((1.0 / (real80) 3.0) * sum);
 			} break; case 4: {
 				real80 sum = ((real80) x [1] + (real80) x [2]) + ((real80) x [3] + (real80) x [4]);
 				if (p_sum) *p_sum = (real) sum;
@@ -289,303 +75,26 @@ void sum_mean_scalar (numvec x, real *p_sum, real *p_mean) noexcept {
 		}
 		return;
 	}
-	if (Melder_debug != 0) {
-		if (Melder_debug == 48) {
-			/*
-				Naive implementation in real64.
-			*/
-			real sum = 0.0;   // -> sum in R (invariant)
-			for (integer i = 1; i <= x.size; i ++) {
-				sum += x [i];   // sum before in R, x [i] in R -> sum after in R
-			}
-			if (p_sum) *p_sum = sum;
-			real mean = sum / x.size;   // sum in R, x.size != 0 -> mean in R
-			if (p_mean) *p_mean = mean;
-			return;
-		}
-		if (Melder_debug == 49) {
-			/*
-				Naive implementation in real80.
-			*/
-			real80 sum = 0.0;   // -> sum in R (invariant)
-			for (integer i = 1; i <= x.size; i ++) {
-				sum += (real80) x [i];   // sum before in R, x [i] in R -> sum after in R
-			}
-			if (p_sum) *p_sum = (real) sum;
-			real80 mean = sum / x.size;   // sum in R, x.size != 0 -> mean in R
-			if (p_mean) *p_mean = (real) mean;
-			return;
-		}
-		if (Melder_debug == 50) {
-			/*
-				First-element offset corrects for large DC components.
-			*/
-			real80 offset = (real80) x [1];   // x.size != 0 -> offset in R
-			real80 sumOfDifferences = 0.0;   // sumOfDifferences in R (invariant)
-			for (integer i = 2; i <= x.size; i ++) {
-				sumOfDifferences += (real80) x [i] - offset;   // sumOfDifferences before in R, x [i] in R, offset in R -> sumOfDifferences after in R
-			}
-			if (p_sum) {
-				real80 sum = sumOfDifferences + offset * x.size;
-				*p_sum = (real) sum;
-			}
-			real80 mean = offset + sumOfDifferences / x.size;   // offset in R, sumOfDifferences in R, x.size != 0 -> mean in R
-			if (p_mean) *p_mean = (real) mean;
-			return;
-		}
-		if (Melder_debug == 51) {
-			/*
-				Chan, Golub & LeVeque's pairwise algorithm.
-			*/
-			#define REAL  real80
-			//real offset = x [1];
-			const real offset = 0.0;
-			long terms [65];
-			REAL suma [65];
-			terms [1] = 0;
-			int top = 2;
-			long n2 = x.size / 2;
-			for (long i = 1; i <= n2; i ++) {
-				/*
-					Compute the sum of the next two data points.
-					Put this sum on top of the stack.
-				*/
-				long start = 2 * i - 1;
-				suma [top] = (REAL) (x [start] - offset) + REAL (x [start + 1] - offset);
-				terms [top] = 2;
-				while (terms [top] == terms [top - 1]) {
-					top --;
-					terms [top] *= 2;
-					suma [top] += suma [top + 1];
-				}
-				top ++;
-			}
-			top --;
-			if (x.size & 1) {
-				/*
-					x.size is odd. Put the last point on the stack.
-				*/
-				top ++;
-				suma [top] = (REAL) (x [x.size] - offset);
-			}
-			REAL sum = suma [top];
-			/*
-				If the remaining stack contains more than one element, x.size is not a power of 2.
-				Add all the elements.
-			*/
-			for (long i = top - 1; i >= 2; i --) {
-				sum += suma [i];
-			}
-			REAL mean = offset + sum / x.size;
-			if (p_sum) {
-				sum += offset * x.size;
-				*p_sum = (real) sum;
-			}
-			if (p_mean) *p_mean = (real) mean;
-			#undef REAL
-			return;
-		}
-		if (Melder_debug == 52) {
-			/*
-				Pairwise algorithm with base case 8.
-				
-				For an explanation see the base case 32 case.
-			*/
-			constexpr integer baseCasePower = 3;
-			constexpr integer baseCaseSize = 1 << baseCasePower;
-			integer remainder = x.size % baseCaseSize;
-			real80 sum;
-			real *y = x.at;
-			#define tensor_TERM(i)  (real80) y [i]
-			tensor_ADD_casesUpTo7 (remainder, sum)
-			integer numberOfBaseCases = x.size / baseCaseSize;
-			if (numberOfBaseCases != 0) {
-				constexpr integer highestIndex = 63 - baseCasePower;
-				integer numbersOfTerms [1 + highestIndex];
-				real80 partialSums [1 + highestIndex];
-				numbersOfTerms [0] = 0;
-				integer stackPointer = 0;
-				y += remainder;
-				for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-					partialSums [++ stackPointer] = tensor_ADD_8 (0);
-					numbersOfTerms [stackPointer] = baseCaseSize;
-					while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-						numbersOfTerms [-- stackPointer] *= 2;
-						partialSums [stackPointer] += partialSums [stackPointer + 1];
-					}
-					y += baseCaseSize;
-				}
-				for (integer i = stackPointer; i > 0; i --) {
-					sum += partialSums [i];
-				}
-			}
-			#undef tensor_TERM
-			if (p_sum) *p_sum = (real) sum;
-			if (p_mean) {
-				real80 mean = sum / x.size;
-				*p_mean = (real) mean;
-			}
-			return;
-		}
-		if (Melder_debug == 53) {
-			/*
-				Pairwise algorithm with base case 16.
-				
-				For an explanation see the base case 32 case.
-			*/
-			constexpr integer baseCasePower = 4;
-			constexpr integer baseCaseSize = 1 << baseCasePower;
-			integer remainder = x.size % baseCaseSize;
-			real80 sum;
-			real *y = x.at;
-			#define tensor_TERM(i)  (real80) y [i]
-			tensor_ADD_casesUpTo15 (remainder, sum)
-			integer numberOfBaseCases = x.size / baseCaseSize;
-			if (numberOfBaseCases != 0) {
-				constexpr integer highestIndex = 63 - baseCasePower;
-				integer numbersOfTerms [1 + highestIndex];
-				real80 partialSums [1 + highestIndex];
-				numbersOfTerms [0] = 0;
-				integer stackPointer = 0;
-				y += remainder;
-				for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-					partialSums [++ stackPointer] = tensor_ADD_16 (0);
-					numbersOfTerms [stackPointer] = baseCaseSize;
-					while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-						numbersOfTerms [-- stackPointer] *= 2;
-						partialSums [stackPointer] += partialSums [stackPointer + 1];
-					}
-					y += baseCaseSize;
-				}
-				for (integer i = stackPointer; i > 0; i --) {
-					sum += partialSums [i];
-				}
-			}
-			#undef tensor_TERM
-			if (p_sum) *p_sum = (real) sum;
-			if (p_mean) {
-				real80 mean = sum / x.size;
-				*p_mean = (real) mean;
-			}
-			return;
-		}
-		if (Melder_debug == 54) {
-			real80 sum = 0.0;   // -> sum in R (invariant)
-			for (integer i = 1; i <= x.size; i ++) {
-				sum += x [i];   // sum before in R, x [i] in R -> sum after in R
-			}
-			if (p_sum) *p_sum = (real) sum;
-			real80 mean = sum / x.size;   // sum in R, x.size != 0 -> mean in R
-			if (p_mean) {
-				real80 sum2 = 0.0;
-				for (integer i = 1; i <= x.size; i ++) {
-					sum2 += (real80) x [i] - mean;
-				}
-				*p_mean = real (mean + sum2 / x.size);
-			}
-			return;
-		}
-		if (Melder_debug == 55) {
-			/*
-				Pairwise algorithm with base case 32.
-				
-				For an explanation see the base case 64 case.
-			*/
-			constexpr integer baseCasePower = 5;
-			constexpr integer baseCaseSize = 1 << baseCasePower;
-			integer remainder = x.size % baseCaseSize;
-			real80 sum;
-			real *y = x.at;
-			#define tensor_TERM(i)  (real80) y [i]
-			tensor_ADD_casesUpTo31 (remainder, sum)
-			integer numberOfBaseCases = x.size / baseCaseSize;
-			if (numberOfBaseCases != 0) {
-				constexpr integer highestIndex = 63 - baseCasePower;
-				integer numbersOfTerms [1 + highestIndex];
-				real80 partialSums [1 + highestIndex];
-				numbersOfTerms [0] = 0;
-				integer stackPointer = 0;
-				y += remainder;
-				for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-					partialSums [++ stackPointer] = tensor_ADD_32 (0);
-					numbersOfTerms [stackPointer] = baseCaseSize;
-					while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-						numbersOfTerms [-- stackPointer] *= 2;
-						partialSums [stackPointer] += partialSums [stackPointer + 1];
-					}
-					y += baseCaseSize;
-				}
-				for (integer i = stackPointer; i > 0; i --) {
-					sum += partialSums [i];
-				}
-			}
-			#undef tensor_TERM
-			if (p_sum) *p_sum = (real) sum;
-			if (p_mean) {
-				real80 mean = sum / x.size;
-				*p_mean = (real) mean;
-			}
-			return;
-		}
-	}
-	/*
-		Our standard: pairwise algorithm with base case size 64 (if baseCasePower is 6).
-
-		If you want to change the base case size, do the following three things:
-		1. Change the `constexpr integer baseCasePower = 6` assignment (e.g. to 7).
-		2. Change the number of cases in the switch statement (e.g. up to case 127).
-		3. Change the `partialSums [++ stackPointer] = tensor_ADD_64` assignment (e.g. to tensor_ADD_128).
-	*/
-	constexpr integer baseCasePower = 6;
-	constexpr integer baseCaseSize = 1 << baseCasePower;
-	integer numberOfBaseCases = x.size / baseCaseSize, remainder = x.size % baseCaseSize;
-	real80 sum;
-	real *y = x.at;
-	#define tensor_TERM(i)  (real80) y [i]
-	tensor_ADD_casesUpTo63 (remainder, sum)
-	if (numberOfBaseCases != 0) {
-		/*
-			The value of numbersOfTerms [0] stays at 0, to denote the bottom of the stack.
-			The maximum value of numbersOfTerms [1] should be 2^62, because x.size can be at most 2^63-1 (if sizeof integer is 64).
-			The maximum value of numbersOfTerms [2] should then be 2^61.
-			The maximum value of numbersOfTerms [3] should be 2^60.
-			...
-			The maximum value of numbersOfTerms [58] should be 2^5, which is the granularity with which base case sums are put on the stack.
-			The maximum value of numbersOfTerms [59] should also be 2^5, because this can be the situation just before collapsing the top of the stack.
-			However, if the whole stack is filled up like this, the actual number of terms is already 2^63. Therefore, we need one element less.
-			So the highest index of numbersOfTerms [] should be 58.
-		*/
-		constexpr integer highestIndex = 63 - baseCasePower;
-		integer numbersOfTerms [1 + highestIndex];
-		real80 partialSums [1 + highestIndex];
-		numbersOfTerms [0] = 0;   // the constant zero at the bottom of the stack
-		integer stackPointer = 0;
-		y += remainder;
-		for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-			/*
-				Compute the sum of the next 32 data points.
-				Put this sum on top of the stack.
-			*/
-			partialSums [++ stackPointer] = tensor_ADD_64 (0);
-			numbersOfTerms [stackPointer] = baseCaseSize;
-			while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-				numbersOfTerms [-- stackPointer] *= 2;
-				partialSums [stackPointer] += partialSums [stackPointer + 1];
-			}
-			y += baseCaseSize;
-		}
-		/*
-			Add all the elements of the stack, starting at the top (small sums to big sums).
-		*/
-		for (integer i = stackPointer; i > 0; i --) {
-			sum += partialSums [i];
-		}
-	}
-	#undef tensor_TERM
-	if (p_sum) *p_sum = (real) sum;
-	if (p_mean) {
-		real80 mean = sum / x.size;   // it helps a bit to perform this division while still in real80
-		*p_mean = (real) mean;
+	if (Melder_debug == 0 || Melder_debug < 48 || Melder_debug > 51) {
+		PAIRWISE_SUM (real80, sum, integer, x.size, real *xx = x.at, ++ xx, (real80) *xx)
+		if (p_sum) *p_sum = (real) sum;
+		if (p_mean) *p_mean = real (sum / x.size);   // it helps a bit to perform the division while still in real80
+	} else if (Melder_debug == 48) {
+		SEQUENTIAL_SUM (real, sum, integer, x.size, real *xx = x.at, ++ xx, *xx)
+		if (p_sum) *p_sum = (real) sum;
+		if (p_mean) *p_mean = real (sum / x.size);
+	} else if (Melder_debug == 49) {
+		SEQUENTIAL_SUM (real80, sum, integer, x.size, real *xx = x.at, ++ xx, *xx)
+		if (p_sum) *p_sum = (real) sum;
+		if (p_mean) *p_mean = real (sum / x.size);
+	} else if (Melder_debug == 50) {
+		KAHAN_SUM (real80, sum, integer, x.size, real *xx = x.at, ++ xx, *xx)
+		if (p_sum) *p_sum = (real) sum;
+		if (p_mean) *p_mean = real (sum / x.size);
+	} else if (Melder_debug == 51) {
+		TWO_LOOP_SUM (real80, sum, integer, x.size, real *xx = x.at, ++ xx, *xx)
+		if (p_sum) *p_sum = (real) sum;
+		if (p_mean) *p_mean = real (sum / x.size);
 	}
 }
 
@@ -662,252 +171,21 @@ void sum_mean_sumsq_variance_stdev_scalar (numvec x, real *p_sum, real *p_mean,
 			return;
 		}
 		if (Melder_debug == 50) {
-			/*
-				First-element offset corrects for large DC components.
-			*/
-			real80 offset = (real80) x [1];   // x.size != 0 -> offset in R
-			real80 sumOfDifferences = 0.0;   // sumOfDifferences in R (invariant)
-			for (integer i = 2; i <= x.size; i ++) {
-				sumOfDifferences += (real80) x [i] - offset;   // sumOfDifferences before in R, x [i] in R, offset in R -> sumOfDifferences after in R
-			}
-			if (p_sum) {
-				real80 sum = sumOfDifferences + offset * x.size;
-				*p_sum = (real) sum;
-			}
-			real80 mean = offset + sumOfDifferences / x.size;   // offset in R, sumOfDifferences in R, x.size != 0 -> mean in R
-			if (p_mean) *p_mean = (real) mean;
-			if (! p_sumsq && ! p_variance && ! p_stdev) return;
-			real80 sumOfSquaredResiduals = 0.0;   // -> sumOfSquares >= 0.0 (invariant)
-			for (integer i = 1; i <= x.size; i ++) {
-				real80 residual = (real80) x [i] - mean;   // x [i] in R, mean in R -> residual in R
-				real80 squaredResidual = residual * residual;   // residual in R -> squaredResidual >= 0.0
-				sumOfSquaredResiduals += squaredResidual;   // sumOfSquaredResiduals before >= 0.0, squaredResidual >= 0.0 -> sumOfSquaredResiduals after >= 0.0
-			}
-			if (p_sumsq) *p_sumsq = (real) sumOfSquaredResiduals;
-			integer degreesOfFreedom = x.size - 1;   // x.size >= 2 -> degreesOfFreedom >= 1 -> degreesOfFreedom > 0
-			real80 meanSquaredResidual = sumOfSquaredResiduals / degreesOfFreedom;   // sumOfSquaredResiduals >= 0.0, degreesOfFreedom > 0 -> meanSquaredResidual >= 0.0
-			if (p_variance) *p_variance = (real) meanSquaredResidual;
-			if (p_stdev) {
-				real80 rootMeanSquaredResidual = sqrtl (meanSquaredResidual);   // meanSquaredResidual >= 0.0 -> rootMeanSquaredResidual >= 0.0 (in particular, not NaN)
-				*p_stdev = (real) rootMeanSquaredResidual;
-			}
-			return;
-		}
-		if (Melder_debug == 51) {
-			/*
-				Chan, Golub & LeVeque's pairwise algorithm.
-			*/
-			#define REAL  real80
-			if (! p_sumsq && ! p_variance && ! p_stdev) {
-				//real offset = x [1];
-				const real offset = 0.0;
-				long terms [65];
-				REAL suma [65];
-				terms [1] = 0;
-				int top = 2;
-				long n2 = x.size / 2;
-				for (long i = 1; i <= n2; i ++) {
-					/*
-						Compute the sum of the next two data points.
-						Put this sum on top of the stack.
-					*/
-					long start = 2 * i - 1;
-					suma [top] = (REAL) (x [start] - offset) + REAL (x [start + 1] - offset);
-					terms [top] = 2;
-					while (terms [top] == terms [top - 1]) {
-						top --;
-						terms [top] *= 2;
-						suma [top] += suma [top + 1];
-					}
-					top ++;
-				}
-				top --;
-				if (x.size & 1) {
-					/*
-						x.size is odd. Put the last point on the stack.
-					*/
-					top ++;
-					suma [top] = (REAL) (x [x.size] - offset);
-				}
-				REAL sum = suma [top];
-				/*
-					If the remaining stack contains more than one element, x.size is not a power of 2.
-					Add all the elements.
-				*/
-				for (long i = top - 1; i >= 2; i --) {
-					sum += suma [i];
-				}
-				REAL mean = offset + sum / x.size;
-				if (p_sum) {
-					sum += offset * x.size;
-					*p_sum = (real) sum;
-				}
-				if (p_mean) *p_mean = (real) mean;
-				return;
-			}
-			int64 terms [65];
-			REAL suma [65], sa [65];
-			terms [1] = 0;
-			int top = 2;
-			long n2 = x.size / 2;
-			for (long i = 1; i <= n2; i ++) {
-				suma [top] = x [2*i-1] + x [2*i];
-				REAL diff = x [2*i] - x [2*i-1];
-				sa [top] = diff * diff / 2.0;
-				terms [top] = 2;
-				while (terms [top] == terms [top - 1]) {
-					top --;
-					terms [top] *= 2;
-					diff = suma [top] - suma [top + 1];
-					sa [top] += sa [top + 1] + diff * diff / terms [top];
-					suma [top] += suma [top + 1];
-				}
-				top ++;
-			}
-			top --;
-			if (x.size & 1) {
-				top ++;
-				terms [top] = 1;
-				suma [top] = x [x.size];
-				sa [top] = 0.0;
-			}
-			long t = terms [top];
-			REAL sum = suma [top];
-			REAL sumOfSquaredResiduals = sa [top];
-			for (long i = top - 1; i >= 2; i --) {
-				REAL diff = terms [i] * sum / t - suma [i];
-				sumOfSquaredResiduals += sa [i] + t * diff * diff / terms [i] / (terms [i] + t);
-				sum += suma [i];
-				t += terms [i];
-			}
-			REAL mean = sum / x.size;
-			REAL variance = sumOfSquaredResiduals / (x.size - 1);
-			if (p_sum) *p_sum = (real) sum;
-			if (p_mean) *p_mean = (real) mean;
-			if (p_sumsq) *p_sumsq = (real) sumOfSquaredResiduals;
-			if (p_variance) *p_variance = (real) variance;
-			if (p_stdev) *p_stdev = (real) sqrtl (variance);
-			#undef REAL
-			return;
-		}
-		if (Melder_debug == 52) {
-			/*
-				Pairwise algorithm with base case 8.
-			*/
-			#define REAL  real80
-			//real offset = x [1];
-			const real offset = 0.0;
-			integer numbersOfTerms [1+60];
-			REAL partialSums [1+60];
-			numbersOfTerms [0] = 0;
-			integer stackPointer = 0;
-			integer n8 = x.size / 8, remainder = x.size % 8;
-			#define tensor_TERM(i)  REAL (y [i] - offset)
-			for (integer ipart = 1; ipart <= n8; ipart ++) {
-				/*
-					Compute the sum of the next eight data points.
-					Put this sum on top of the stack.
-				*/
-				real *y = & x [8 * (ipart - 1)];
-				partialSums [++ stackPointer] = tensor_ADD_8 (0);
-				numbersOfTerms [stackPointer] = 8;
-				while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-					numbersOfTerms [-- stackPointer] *= 2;
-					partialSums [stackPointer] += partialSums [stackPointer + 1];
-				}
-			}
-			REAL sum = 0.0;
-			if (remainder != 0) {
-				real *y = & x [x.size - remainder];
-				tensor_ADD_casesUpTo7 (remainder, sum)
-			}
-			#undef tensor_TERM
-			/*
-				Add all the elements of the stack.
-			*/
-			for (integer i = stackPointer; i > 0; i --) {
-				sum += partialSums [i];
-			}
-			REAL mean = offset + sum / x.size;
-			if (p_sum) {
-				sum += offset * x.size;
-				*p_sum = (real) sum;
-			}
-			real mean64 = (real) mean;
-			if (p_mean) *p_mean = mean64;
-			if (! p_sumsq && ! p_variance && ! p_stdev) {
-				return;
-			}
-			stackPointer = 0;
-			#define tensor_TERM(i)  REAL (y [i] - mean64) * REAL (y [i] - mean64)
-			for (integer ipart = 1; ipart <= n8; ipart ++) {
-				real *y = & x [8 * (ipart - 1)];
-				partialSums [++ stackPointer] = tensor_ADD_8 (0);
-				numbersOfTerms [stackPointer] = 16;
-				while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-					numbersOfTerms [-- stackPointer] *= 2;
-					partialSums [stackPointer] += partialSums [stackPointer + 1];
-				}
-			}
-			REAL sumsq = 0.0;
-			if (remainder != 0) {
-				real *y = & x [x.size - remainder];
-				tensor_ADD_casesUpTo7 (remainder, sumsq)
-			}
-			#undef tensor_TERM
-			for (integer i = stackPointer; i > 0; i --) {
-				sumsq += partialSums [i];
-			}
-			real variance = (real) sumsq / (x.size - 1);
-			if (p_sumsq) *p_sumsq = (real) sumsq;
-			if (p_variance) *p_variance = variance;
-			if (p_stdev) *p_stdev = sqrt (variance);
-			#undef REAL
-			return;
-		}
-		if (Melder_debug == 53) {
 			real mean;
-			sum_mean_scalar (x, p_sum, & mean);   // compute the sum only if the user asks for it, but the mean always, because we need it here
+			sum_mean_scalar (x, p_sum, & mean);
 			if (p_mean) *p_mean = mean;
 			if (! p_sumsq && ! p_variance && ! p_stdev) {
 				return;
 			}
-			constexpr integer baseCasePower = 4;
-			constexpr integer baseCaseSize = 1 << baseCasePower;
-			integer remainder = x.size % baseCaseSize;
-			real80 sumsq;
-			real *y = x.at;
-			#define tensor_TERM(i)  real80 (y [i] - mean) * real80 (y [i] - mean)
-			tensor_ADD_casesUpTo15 (remainder, sumsq)
-			integer numberOfBaseCases = x.size / baseCaseSize;
-			if (numberOfBaseCases != 0) {
-				constexpr integer highestIndex = 63 - baseCasePower;
-				integer numbersOfTerms [1 + highestIndex];
-				real80 partialSums [1 + highestIndex];
-				numbersOfTerms [0] = 0;
-				integer stackPointer = 0;
-				y += remainder;
-				for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-					partialSums [++ stackPointer] = tensor_ADD_16 (0);
-					numbersOfTerms [stackPointer] = baseCaseSize;
-					while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-						numbersOfTerms [-- stackPointer] *= 2;
-						partialSums [stackPointer] += partialSums [stackPointer + 1];
-					}
-					y += baseCaseSize;
-				}
-				for (integer i = stackPointer; i > 0; i --) {
-					sumsq += partialSums [i];
-				}
-			}
-			#undef tensor_TERM
-			real variance = (real) sumsq / (x.size - 1);
+			real *y;
+			KAHAN_SUM (real80, sumsq, integer, x.size, y = x.at, ++ y, real80 (*y - mean) * real80 (*y - mean))
+			real variance = real (sumsq / (x.size - 1));
 			if (p_sumsq) *p_sumsq = (real) sumsq;
 			if (p_variance) *p_variance = variance;
 			if (p_stdev) *p_stdev = sqrt (variance);
 			return;
 		}
-		if (Melder_debug == 54) {
+		if (Melder_debug == 51) {
 			real sum, mean;
 			sum_mean_scalar (x, & sum, & mean);
 			if (p_sum) *p_sum = sum;
@@ -929,91 +207,20 @@ void sum_mean_sumsq_variance_stdev_scalar (numvec x, real *p_sum, real *p_mean,
 			}
 			return;
 		}
-		if (Melder_debug == 55) {
-			real mean;
-			sum_mean_scalar (x, p_sum, & mean);   // compute the sum only if the user asks for it, but the mean always, because we need it here
-			if (p_mean) *p_mean = mean;
-			if (! p_sumsq && ! p_variance && ! p_stdev) {
-				return;
-			}
-			constexpr integer baseCasePower = 5;
-			constexpr integer baseCaseSize = 1 << baseCasePower;
-			integer remainder = x.size % baseCaseSize;
-			real80 sumsq;
-			real *y = x.at;
-			#define tensor_TERM(i)  real80 (y [i] - mean) * real80 (y [i] - mean)
-			tensor_ADD_casesUpTo31 (remainder, sumsq)
-			integer numberOfBaseCases = x.size / baseCaseSize;
-			if (numberOfBaseCases != 0) {
-				constexpr integer highestIndex = 63 - baseCasePower;
-				integer numbersOfTerms [1 + highestIndex];
-				real80 partialSums [1 + highestIndex];
-				numbersOfTerms [0] = 0;
-				integer stackPointer = 0;
-				y += remainder;
-				for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-					partialSums [++ stackPointer] = tensor_ADD_32 (0);
-					numbersOfTerms [stackPointer] = baseCaseSize;
-					while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-						numbersOfTerms [-- stackPointer] *= 2;
-						partialSums [stackPointer] += partialSums [stackPointer + 1];
-					}
-					y += baseCaseSize;
-				}
-				for (integer i = stackPointer; i > 0; i --) {
-					sumsq += partialSums [i];
-				}
-			}
-			#undef tensor_TERM
-			real variance = (real) sumsq / (x.size - 1);
-			if (p_sumsq) *p_sumsq = (real) sumsq;
-			if (p_variance) *p_variance = variance;
-			if (p_stdev) *p_stdev = sqrt (variance);
-			return;
-		}
-	} else {
-		/*
-			Our standard: pairwise algorithm with base case 64.
-		*/
-		real mean;
-		sum_mean_scalar (x, p_sum, & mean);   // compute the sum only if the user asks for it, but the mean always, because we need it here
-		if (p_mean) *p_mean = mean;
-		if (! p_sumsq && ! p_variance && ! p_stdev) {
-			return;
-		}
-		constexpr integer baseCasePower = 6;
-		constexpr integer baseCaseSize = 1 << baseCasePower;
-		integer numberOfBaseCases = x.size / baseCaseSize, remainder = x.size % baseCaseSize;
-		real80 sumsq;
-		real *y = x.at;
-		#define tensor_TERM(i)  real80 (y [i] - mean) * real80 (y [i] - mean)
-		tensor_ADD_casesUpTo63 (remainder, sumsq)
-		if (numberOfBaseCases != 0) {
-			constexpr integer highestIndex = 63 - baseCasePower;
-			integer numbersOfTerms [1 + highestIndex];
-			real80 partialSums [1 + highestIndex];
-			numbersOfTerms [0] = 0;
-			integer stackPointer = 0;
-			y += remainder;
-			for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-				partialSums [++ stackPointer] = tensor_ADD_64 (0);
-				numbersOfTerms [stackPointer] = baseCaseSize;
-				while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-					numbersOfTerms [-- stackPointer] *= 2;
-					partialSums [stackPointer] += partialSums [stackPointer + 1];
-				}
-				y += baseCaseSize;
-			}
-			for (integer i = stackPointer; i > 0; i --) {
-				sumsq += partialSums [i];
-			}
-		}
-		#undef tensor_TERM
-		real variance = (real) sumsq / (x.size - 1);
-		if (p_sumsq) *p_sumsq = (real) sumsq;
-		if (p_variance) *p_variance = variance;
-		if (p_stdev) *p_stdev = sqrt (variance);
 	}
+	/*
+		Our standard: pairwise algorithm with base case 64.
+	*/
+	PAIRWISE_SUM (real80, sum, integer, x.size, real *xx = x.at, ++ xx, (real80) *xx)
+	real mean = real (sum / x.size);   // rounded to real64, because this guarantees that x[i] - mean will be zero for constant x[1..size]
+	if (p_sum) *p_sum = (real) sum;
+	if (p_mean) *p_mean = (real) mean;
+	if (! p_sumsq && ! p_variance && ! p_stdev) return;
+	PAIRWISE_SUM (real80, sumsq, integer, x.size, real *xx = x.at, ++ xx, real80 (*xx - mean) * real80 (*xx - mean))
+	real80 variance = sumsq / (x.size - 1);
+	if (p_sumsq) *p_sumsq = (real) sumsq;
+	if (p_variance) *p_variance = (real) variance;
+	if (p_stdev) *p_stdev = sqrt ((real) variance);
 }
 
 real sumsq_scalar (numvec x) noexcept {
@@ -1043,123 +250,16 @@ double center_scalar (numvec x) noexcept {
 	return weightedSumOfIndexes / sumOfWeights;
 }
 
-real _inner_scalar (numvec x, numvec y) {
-	if (x.size != y.size) return undefined;
-	constexpr integer baseCasePower = 6;
-	constexpr integer baseCaseSize = 1 << baseCasePower;
-	integer numberOfBaseCases = x.size / baseCaseSize, remainder = x.size % baseCaseSize;
-	real80 sum;
-	real *xx = x.at, *yy = y.at;
-	#define tensor_TERM(i)  ((real80) xx [i] * (real80) yy [i])
-	//#define tensor_TERM(i)  xx [i] * yy [i]
-	tensor_ADD_casesUpTo63 (remainder, sum)
-	if (numberOfBaseCases != 0) {
-		constexpr integer highestIndex = 63 - baseCasePower;
-		integer numbersOfTerms [1 + highestIndex];
-		real80 partialSums [1 + highestIndex];
-		numbersOfTerms [0] = 0;
-		integer stackPointer = 0;
-		xx += remainder;
-		yy += remainder;
-		for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-			partialSums [++ stackPointer] = tensor_ADD_64 (0);
-			numbersOfTerms [stackPointer] = baseCaseSize;
-			while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-				numbersOfTerms [-- stackPointer] *= 2;
-				partialSums [stackPointer] += partialSums [stackPointer + 1];
-			}
-			xx += baseCaseSize;
-			yy += baseCaseSize;
-		}
-		for (integer i = stackPointer; i > 0; i --) {
-			sum += partialSums [i];
-		}
-	}
-	#undef tensor_TERM
-	return (real) sum;
-}
-
 real norm_scalar (numvec x, real power) noexcept {
 	if (power < 0.0) return undefined;
-	constexpr integer baseCasePower = 5;
-	constexpr integer baseCaseSize = 1 << baseCasePower;
-	integer numberOfBaseCases = x.size / baseCaseSize, remainder = x.size % baseCaseSize;
-	real80 sum;
-	real *y = x.at;
 	if (power == 2.0) {
-		#define tensor_TERM(i)  ((real80) y [i] * (real80) y [i])
-		tensor_ADD_casesUpTo31 (remainder, sum)
-		if (numberOfBaseCases != 0) {
-			constexpr integer highestIndex = 63 - baseCasePower;
-			integer numbersOfTerms [1 + highestIndex];
-			real80 partialSums [1 + highestIndex];
-			numbersOfTerms [0] = 0;
-			integer stackPointer = 0;
-			y += remainder;
-			for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-				partialSums [++ stackPointer] = tensor_ADD_32 (0);
-				numbersOfTerms [stackPointer] = baseCaseSize;
-				while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-					numbersOfTerms [-- stackPointer] *= 2;
-					partialSums [stackPointer] += partialSums [stackPointer + 1];
-				}
-				y += baseCaseSize;
-			}
-			for (integer i = stackPointer; i > 0; i --) {
-				sum += partialSums [i];
-			}
-		}
-		#undef tensor_TERM
+		PAIRWISE_SUM (real80, sum, integer, x.size, real *y = x.at, ++ y, (real80) *y * (real80) *y)
 		return sqrt ((real) sum);
 	} else if (power == 1.0) {
-		#define tensor_TERM(i)  (real80) fabs (y [i])
-		tensor_ADD_casesUpTo31 (remainder, sum)
-		if (numberOfBaseCases != 0) {
-			constexpr integer highestIndex = 63 - baseCasePower;
-			integer numbersOfTerms [1 + highestIndex];
-			real80 partialSums [1 + highestIndex];
-			numbersOfTerms [0] = 0;
-			integer stackPointer = 0;
-			y += remainder;
-			for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-				partialSums [++ stackPointer] = tensor_ADD_32 (0);
-				numbersOfTerms [stackPointer] = baseCaseSize;
-				while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-					numbersOfTerms [-- stackPointer] *= 2;
-					partialSums [stackPointer] += partialSums [stackPointer + 1];
-				}
-				y += baseCaseSize;
-			}
-			for (integer i = stackPointer; i > 0; i --) {
-				sum += partialSums [i];
-			}
-		}
-		#undef tensor_TERM
+		PAIRWISE_SUM (real80, sum, integer, x.size, real *y = x.at, ++ y, (real80) fabs (*y))
 		return (real) sum;
 	} else {
-		#define tensor_TERM(i)  powl ((real80) fabs (y [i]), power)
-		tensor_ADD_casesUpTo31 (remainder, sum)
-		if (numberOfBaseCases != 0) {
-			constexpr integer highestIndex = 63 - baseCasePower;
-			integer numbersOfTerms [1 + highestIndex];
-			real80 partialSums [1 + highestIndex];
-			numbersOfTerms [0] = 0;
-			integer stackPointer = 0;
-			y += remainder;
-			for (integer ipart = 1; ipart <= numberOfBaseCases; ipart ++) {
-				partialSums [++ stackPointer] = tensor_ADD_32 (0);
-				numbersOfTerms [stackPointer] = baseCaseSize;
-				while (numbersOfTerms [stackPointer] == numbersOfTerms [stackPointer - 1]) {
-					numbersOfTerms [-- stackPointer] *= 2;
-					partialSums [stackPointer] += partialSums [stackPointer + 1];
-				}
-				y += baseCaseSize;
-			}
-			for (integer i = stackPointer; i > 0; i --) {
-				sum += partialSums [i];
-			}
-		}
-		#undef tensor_TERM
+		PAIRWISE_SUM (real80, sum, integer, x.size, real *y = x.at, ++ y, powl ((real80) fabs (*y), power))
 		return (real) powl (sum, (real80) 1.0 / power);
 	}
 }
@@ -1248,4 +348,68 @@ autonummat peaks_nummat (numvec x, bool includeEdges, int interpolate, bool sort
 	return result;
 }
 
+real _inner_scalar (numvec x, numvec y) {
+	if (x.size != y.size) return undefined;
+	PAIRWISE_SUM (real80, sum, integer, x.size,
+		real *xx = x.at;
+		real *yy = y.at,
+		(++ xx, ++ yy),
+		(real80) *xx * (real80) *yy)
+	return (real) sum;
+}
+
+inline static real _inner_stride_scalar (numvec x, numvec y, integer stride) {
+	if (x.size != y.size) return undefined;
+	PAIRWISE_SUM (real80, sum, integer, x.size,
+		real *xx = & x [0];
+		real *yy = & y [1 - stride],
+		(++ xx, yy += stride),
+		(real80) *xx * (real80) *yy)
+	return (real) sum;
+}
+
+inline static void mul_inline (numvec target, numvec vec, nummat mat) {
+	for (integer j = 1; j <= mat.ncol; j ++) {
+		if ((false)) {
+			target [j] = 0.0;
+			for (integer i = 1; i <= mat.nrow; i ++) {
+				target [j] += vec [i] * mat [i] [j];
+			}
+		} else {
+			target [j] = _inner_stride_scalar (vec, { & mat [1] [j] - 1, mat.nrow }, mat.ncol);
+		}
+	}
+}
+
+inline static void mul_inline (numvec target, nummat mat, numvec vec) {
+	for (integer i = 1; i <= mat.nrow; i ++) {
+		if ((false)) {
+			target [i] = 0.0;
+			for (integer j = 1; j <= vec.size; j ++) {
+				target [i] += mat [i] [j] * vec [j];
+			}
+		} else {
+			target [i] = inner_scalar ({ & mat [i] [1] - 1, mat.ncol }, vec);
+		}
+	}
+}
+
+autonumvec mul_numvec (numvec vec, nummat mat) {
+	if (mat.nrow != vec.size) return autonumvec { nullptr, 0 };
+	autonumvec result { mat.ncol, false };
+	mul_inline (result.get(), vec, mat);
+	return result;
+}
+
+autonumvec mul_numvec (nummat mat, numvec vec) {
+	if (vec.size != mat.ncol) return autonumvec { nullptr, 0 };
+	autonumvec result { mat.nrow, false };
+	mul_inline (result.get(), mat, vec);
+	return result;
+}
+
+void numvec_sort (numvec x) {
+	NUMsort_d (x.size, x.at);
+}
+
 /* End of file tensor.cpp */
diff --git a/sys/tensor.h b/sys/tensor.h
index d94aabb..698fb35 100644
--- a/sys/tensor.h
+++ b/sys/tensor.h
@@ -19,7 +19,6 @@
  */
 
 #include "melder.h"
-#include <math.h>
 
 inline static double sqrt_scalar (double x) {
 	#if defined (_WIN32)
@@ -107,6 +106,20 @@ inline static bool equal_numvec (numvec x, numvec y) {
 	return true;
 }
 
+inline static void numvec_copyElements_nocheck (numvec from, numvec to) {
+	for (integer i = 1; i <= from.size; i ++) {
+		to [i] = from [i];
+	}
+}
+
+inline static void nummat_copyElements_nocheck (nummat from, nummat to) {
+	for (integer irow = 1; irow <= from.nrow; irow ++) {
+		for (integer icol = 1; icol <= from.ncol; icol ++) {
+			to [irow] [icol] = from [irow] [icol];
+		}
+	}
+}
+
 inline static autonumvec add_numvec (numvec x, numvec y) {
 	if (x.size != y.size) return autonumvec { nullptr, 0 };
 	autonumvec result (x.size, false);
@@ -115,6 +128,16 @@ inline static autonumvec add_numvec (numvec x, numvec y) {
 	}
 	return result;
 }
+inline static autonummat add_nummat (nummat x, nummat y) {
+	if (x.nrow != y.nrow || x.ncol != y.ncol) return autonummat { nullptr, 0, 0 };
+	autonummat result (x.nrow, x.ncol, false);
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			result [irow] [icol] = x [irow] [icol] + y [irow] [icol];
+		}
+	}
+	return result;
+}
 inline static autonumvec sub_numvec (numvec x, numvec y) {
 	if (x.size != y.size) return autonumvec { nullptr, 0 };
 	autonumvec result (x.size, false);
@@ -123,11 +146,24 @@ inline static autonumvec sub_numvec (numvec x, numvec y) {
 	}
 	return result;
 }
+inline static autonummat sub_nummat (nummat x, nummat y) {
+	if (x.nrow != y.nrow || x.ncol != y.ncol) return autonummat { nullptr, 0, 0 };
+	autonummat result (x.nrow, x.ncol, false);
+	for (integer irow = 1; irow <= x.nrow; irow ++) {
+		for (integer icol = 1; icol <= x.ncol; icol ++) {
+			result [irow] [icol] = x [irow] [icol] - y [irow] [icol];
+		}
+	}
+	return result;
+}
+
+autonumvec mul_numvec (numvec x, nummat y);
+autonumvec mul_numvec (nummat x, numvec y);
 
 autonummat copy_nummat (nummat x);
 
 inline static numvec as_numvec (nummat x) {
-	return numvec (x.nrow * x.ncol, x [1]);
+	return numvec (x [1], x.nrow * x.ncol);
 }
 
 inline static real norm_scalar (nummat x, real power) noexcept {
@@ -138,5 +174,7 @@ autonummat outer_nummat (numvec x, numvec y);
 
 autonummat peaks_nummat (numvec x, bool includeEdges, int interpolate, bool sortByHeight);
 
+void numvec_sort (numvec x);
+
 /* End of file tensor.h */
 #endif
diff --git a/test/createPraatTests.praat b/test/createPraatTests.praat
new file mode 100644
index 0000000..0faee7a
--- /dev/null
+++ b/test/createPraatTests.praat
@@ -0,0 +1,83 @@
+# Praat script createTests.praat
+# Paul Boersma 2017-09-13
+
+# This script extracts tests from C++ source code files
+# in which Praat script snippets have been inserted
+# between a "/*@praat" line and a "@*/" line.
+#
+# For instance, the tests in the source code file sys/Formula.cpp
+# are put into the new file test/sys/Formula.cpp.praat.
+
+stopwatch
+
+writeInfoLine: "Creating tests..."
+numberOfTestFiles = 0
+totalNumberOfTests = 0
+
+ at createAllPraatTestsInFolder: "kar"
+ at createAllPraatTestsInFolder: "num"
+ at createAllPraatTestsInFolder: "sys"
+ at createAllPraatTestsInFolder: "stat"
+ at createAllPraatTestsInFolder: "fon"
+ at createAllPraatTestsInFolder: "gram"
+ at createAllPraatTestsInFolder: "artsynth"
+ at createAllPraatTestsInFolder: "EEG"
+ at createAllPraatTestsInFolder: "contrib/ola"
+ at createAllPraatTestsInFolder: "main"
+
+procedure createAllPraatTestsInFolder: .folder$
+	.files.Strings = Create Strings as file list: "files", "../" + .folder$ + "/*.cpp"
+	.numberOfFiles = Get number of strings
+	for .ifile to .numberOfFiles
+		selectObject: .files.Strings
+		.fileName$ = Get string: .ifile
+		@createTest: .folder$, .fileName$
+	endfor
+	removeObject: .files.Strings
+	.files.Strings = Create Strings as file list: "files", "../" + .folder$ + "/*.h"
+	.numberOfFiles = Get number of strings
+	for .ifile to .numberOfFiles
+		selectObject: .files.Strings
+		.fileName$ = Get string: .ifile
+		@createTest: .folder$, .fileName$
+	endfor
+	removeObject: .files.Strings
+endproc
+
+procedure createTest: .folder$, .file$
+	.sourceFile$ = "../" + .folder$ + "/" + .file$
+	.lines = Read Strings from raw text file: .sourceFile$
+	.numberOfLines = Get number of strings
+	.targetFile$ = .folder$ + "/" + .file$ + ".praat"
+	.numberOfTestsInThisFile = 0
+	for .iline to .numberOfLines - 2
+		.line$ = Get string: .iline
+		if index (.line$, "/*@praat")
+			if .numberOfTestsInThisFile = 0
+				writeFileLine: .targetFile$, "# File ", .folder$, "/", .file$, ".praat"
+				appendFileLine: .targetFile$, "# Generated by test/createTests.praat"
+				appendFileLine: .targetFile$, "# ", date$ ()
+			endif
+			.numberOfTestsInThisFile += 1
+			.numberOfLeadingTabs = index (.line$, "/*@praat")
+			appendFileLine: .targetFile$, ""
+			label again
+			.iline += 1
+			.line$ = Get string: .iline
+			goto finish index (.line$, "@*/")
+			appendFileLine: .targetFile$, mid$ (.line$, .numberOfLeadingTabs + 1, 1000)
+			goto again
+		endif
+		label finish
+	endfor
+	Remove
+	if .numberOfTestsInThisFile > 0
+		appendFileLine: .targetFile$, newline$, "appendInfoLine: """, .targetFile$, """", ", "" OK"""
+		appendInfoLine: "Written ", .numberOfTestsInThisFile, " tests into ", .targetFile$
+		numberOfTestFiles += 1
+		totalNumberOfTests += .numberOfTestsInThisFile
+	endif
+endproc
+
+appendInfoLine: newline$, "Written ", numberOfTestFiles, " files with ", totalNumberOfTests, " tests in ", fixed$ (stopwatch, 3) , " seconds"
+appendInfoLine: "OK"
\ No newline at end of file
diff --git a/test/dwtools/Discriminant.praat b/test/dwtools/Discriminant.praat
index c03d999..a5715e8 100644
--- a/test/dwtools/Discriminant.praat
+++ b/test/dwtools/Discriminant.praat
@@ -7,7 +7,7 @@
 writeInfoLine: "test discriminant analysis"
 
 table = Create TableOfReal (Pols 1973): "no"
-Formula: "if col <= 3 then log10 (self) else self fi"
+Formula: ~ if col <= 3 then log10 (self) else self fi
 Standardize columns
 Set column label (index): 1, "standardized log (%F__1_)"
 Set column label (index): 2, "standardized log (%F__2_)"
diff --git a/test/fon/Sound_to_Formant.praat b/test/fon/Sound_to_Formant.praat
index 23f5088..2ea0a92 100644
--- a/test/fon/Sound_to_Formant.praat
+++ b/test/fon/Sound_to_Formant.praat
@@ -1,12 +1,13 @@
 # test/fon/Sound_to_Formant.praat
 # Paul Boersma, 22 November 2011
+# 2017
 
 for i to 30
 	duration = randomUniform (0.001, 0.003)
 	windowDuration = randomUniform (0.002, 0.005)
 	samplingFrequency = randomUniform (16000, 96000)
-	sound = Create Sound from formula... test 1 0 duration samplingFrequency 1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)
-	noprogress To Formant (burg)... 0.005 5 5500 windowDuration 50
+	sound = Create Sound from formula: "test", 1, 0, duration, samplingFrequency, ~ 1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)
+	noprogress To Formant (burg): 0.005, 5, 5500, windowDuration, 50
 	plus sound
 	Remove
 endfor 
diff --git a/test/fon/Sound_to_LPC.praat b/test/fon/Sound_to_LPC.praat
index 5671aee..4881cce 100644
--- a/test/fon/Sound_to_LPC.praat
+++ b/test/fon/Sound_to_LPC.praat
@@ -5,8 +5,8 @@ for i to 30
 	duration = randomUniform (0.001, 0.003)
 	windowDuration = randomUniform (0.002, 0.005)
 	samplingFrequency = randomUniform (16000, 96000)
-	sound = Create Sound from formula... test 1 0 duration samplingFrequency 1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)
-	noprogress To LPC (burg)... 16 windowDuration 0.005 50
+	sound = Create Sound from formula: "test", 1, 0, duration, samplingFrequency, ~ 1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)
+	noprogress To LPC (burg): 16, windowDuration, 0.005, 50
 	plus sound
 	Remove
 endfor 
diff --git a/test/fon/Spectrum_draw.praat b/test/fon/Spectrum_draw.praat
index 33f8224..ffb9c51 100644
--- a/test/fon/Spectrum_draw.praat
+++ b/test/fon/Spectrum_draw.praat
@@ -1,12 +1,12 @@
 # test/fon/Spectrum_draw.praat
-# Paul Boersma 2016-01-15
+# Paul Boersma 2017-08-28
 
 # There used to be a bug that caused Praat to crash with an assert message
 # if you drew a Spectrum that contained undefined values.
 
 sound = Create Sound as pure tone: "tone", 1, 0, 0.4, 44100, 440, 0.2, 0.01, 0.01
 spec = To Spectrum: "yes"
-Formula: "self/0"
+Formula: ~ self / 0
 Draw: 0, 0, 0, 0, "yes"
 
 removeObject: sound, spec
diff --git a/test/fon/data.praat b/test/fon/data.praat
index e376fd7..eb157e7 100644
Binary files a/test/fon/data.praat and b/test/fon/data.praat differ
diff --git a/test/fon/endian.praat b/test/fon/endian.praat
index 21f7e8e..bb3b835 100644
--- a/test/fon/endian.praat
+++ b/test/fon/endian.praat
@@ -1,9 +1,13 @@
 echo Endian...
-procedure do
+
+keepFiles = 0
+
+procedure do testCorrect
 	Read from file... test.wav
 	energyInAir = Get energy in air
 	assert "'energyInAir:11'" = "0.00008397361"
 
+if testCorrect
 	Read from file... test.Sound
 	energyInAir2 = Get energy in air
 	Remove
@@ -49,23 +53,29 @@ procedure do
 	deleteFile ("test3.sdf")
 	energyInAir3 = Get energy in air
 	assert energyInAir3 = energyInAir2
+endif
 
 	Remove
 
-	Create Sound... test 0 100 22050 0.1 * randomGauss (0, 1)
+	duration = 100
+	Create Sound: "test", 0, duration, 22050, ~ 0.1 * randomGauss (0, 1)
 	for i to 2
 		stopwatch
-		Write to WAV file... kanweg.Sound
+		Save as WAV file: "kanweg.wav"
 		t1 = stopwatch
+		keepFiles or deleteFile: "kanweg.wav"
 		stopwatch
-		Write to AIFF file... kanweg.Sound
+		Save as AIFF file: "kanweg.aiff"
 		t2 = stopwatch
+		keepFiles or deleteFile: "kanweg.aiff"
 		stopwatch
-		Write to binary file... kanweg.Sound
+		Save as binary file: "kanweg.Sound"
 		t3 = stopwatch
+		keepFiles or deleteFile: "kanweg.Sound"
 		stopwatch
-		Write to FLAC file... kanweg.Sound
+		Save as FLAC file: "kanweg.flac"
 		t4 = stopwatch
+		keepFiles or deleteFile: "kanweg.flac"
 		printline 't1:2' 't2:2' 't3:2' 't4:2'
 	endfor
 	Remove
@@ -112,10 +122,13 @@ procedure do
 endproc
 printline Optimized:
 Debug... no 0
-call do
+call do 1
 printline Portable:
 Debug... no 18
-call do
+call do 1
+printline Native:
+Debug... no 181
+call do 0
 Debug... no 0
 
 printline OK
diff --git a/test/fon/resample16_8.praat b/test/fon/resample16_8.praat
index 006ca98..8a0e246 100644
--- a/test/fon/resample16_8.praat
+++ b/test/fon/resample16_8.praat
@@ -4,7 +4,7 @@ Times
 depth = 200
 
 sweep = Create Sound from formula: "sweep", 1, 0, 10, 16000,
-	... "sin (2 * pi * 400 * x^2)"
+	... ~ sin (2 * pi * 400 * x^2)
 To Spectrogram: 0.05, 8000, 0.002, 20, "Gaussian"
 Select outer viewport: 0, 6, 0, 3
 Paint: 0, 0, 0, 8000, 100, "yes", 90, 0, 0, "yes"
@@ -20,16 +20,16 @@ cutoff = 3800
 
 filter_mat = Create Matrix: "filter1", -depth / 16000, depth / 16000,
 ... depth*2, 1 / 16000, (-depth+0.5) / 16000,
-... 1, 1, 1, 1, 1, "if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
-... * (0.5 + 0.5 * cos (pi * x*16000 / depth)) fi"
+... 1, 1, 1, 1, 1, ~ if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
+... * (0.5 + 0.5 * cos (pi * x*16000 / depth)) fi
 sum = Get sum
-Formula: "self / sum"
+Formula: ~ self / sum
 filter = To Sound
 
 selectObject: sweep, filter
 sweep_low = Convolve: "sum", "zero"
 
-mooi = Create Sound from formula: "mooi", 1, 0, 10, 16000/2, "object [sweep_low, col*2]"
+mooi = Create Sound from formula: "mooi", 1, 0, 10, 16000/2, ~ object [sweep_low, col*2]
 To Spectrogram: 0.05, 8000, 0.002, 20, "Gaussian"
 Select outer viewport: 0, 6, 6, 9
 Paint: 0, 0, 0, 8000, 100, "yes", 90, 0, 0, "yes"
@@ -45,4 +45,4 @@ for i to depth*2
 endfor
 appendInfoLine: "};"
 
-removeObject: filter_mat, filter, sweep_low, sweep, sweep_8k, mooi
\ No newline at end of file
+removeObject: filter_mat, filter, sweep_low, sweep, sweep_8k, mooi
diff --git a/test/fon/resample22_8.praat b/test/fon/resample22_8.praat
index 9ab7262..bb2466e 100644
--- a/test/fon/resample22_8.praat
+++ b/test/fon/resample22_8.praat
@@ -4,7 +4,7 @@ Times
 depth = 200
 
 sweep = Create Sound from formula: "sweep", 1, 0, 10, 22050,
-	... "sin (2 * pi * 500 * x^2)"
+	... ~ sin (2 * pi * 500 * x^2)
 To Spectrogram: 0.05, 10000, 0.002, 20, "Gaussian"
 Select outer viewport: 0, 6, 0, 3
 Paint: 0, 0, 0, 10000, 100, "yes", 90, 0, 0, "yes"
@@ -21,17 +21,17 @@ cutoff = 3600
 for ifilter from 0 to 3
 	filter_mat [ifilter] = Create Matrix: "filter", -depth / 22050, depth / 22050,
 	... depth*2, 1 / 22050, (-depth+0.875-0.25*ifilter) / 22050,
-	... 1, 1, 1, 1, 1, "if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
-	... * (0.5 + 0.5 * cos (pi * x*22050 / depth)) fi"
+	... 1, 1, 1, 1, 1, ~ if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
+	... * (0.5 + 0.5 * cos (pi * x*22050 / depth)) fi
 	sum = Get sum
-	Formula: "self / sum"
+	Formula: ~ self / sum
 	filter [ifilter] = To Sound
 	plusObject: sweep
 	sweep_low [ifilter] = Convolve: "sum", "zero"
 endfor
 
 mooi = Create Sound from formula: "mooi", 1, 0, 10, 22050/2.75,
-	... "object [sweep_low [col mod 4], (col*11+(col mod 4))/4]"
+	... ~ object [sweep_low [col mod 4], (col*11+(col mod 4))/4]
 To Spectrogram: 0.05, 6000, 0.002, 20, "Gaussian"
 Select outer viewport: 0, 6, 6, 9
 Paint: 0, 0, 0, 6000, 100, "yes", 90, 0, 0, "yes"
diff --git a/test/fon/resample44_8.praat b/test/fon/resample44_8.praat
index d5966dc..92a4020 100644
--- a/test/fon/resample44_8.praat
+++ b/test/fon/resample44_8.praat
@@ -4,7 +4,7 @@ Times
 depth = 200
 
 sweep = Create Sound from formula: "sweep", 1, 0, 10, 44100,
-	... "sin (2 * pi * 1000 * x^2)"
+	... ~ sin (2 * pi * 1000 * x^2)
 To Spectrogram: 0.05, 20000, 0.002, 20, "Gaussian"
 Select outer viewport: 0, 6, 0, 3
 Paint: 0, 0, 0, 20000, 100, "yes", 90, 0, 0, "yes"
@@ -21,10 +21,10 @@ cutoff = 3600
 for ifilter from 0 to 1
 	filter_mat [ifilter] = Create Matrix: "filter", -depth / 44100, depth / 44100,
 	... depth*2, 1 / 44100, (-depth+0.75-0.5*ifilter) / 44100,
-	... 1, 1, 1, 1, 1, "if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
-	... * (0.5 + 0.5 * cos (pi * x*44100 / depth)) fi"
+	... 1, 1, 1, 1, 1, ~ if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
+	... * (0.5 + 0.5 * cos (pi * x*44100 / depth)) fi
 	sum = Get sum
-	Formula: "self / sum"
+	Formula: ~ self / sum
 	filter [ifilter] = To Sound
 	;Multiply by window: "Hanning"
 	plusObject: sweep
@@ -32,7 +32,7 @@ for ifilter from 0 to 1
 endfor
 
 mooi = Create Sound from formula: "mooi", 1, 0, 10, 44100/5.5,
-	... "object [sweep_low [col mod 2], (col*11+(col mod 2))/2]"
+	... ~ object [sweep_low [col mod 2], (col*11+(col mod 2))/2]
 To Spectrogram: 0.05, 6000, 0.002, 20, "Gaussian"
 Select outer viewport: 0, 6, 6, 9
 Paint: 0, 0, 0, 6000, 100, "yes", 90, 0, 0, "yes"
@@ -57,4 +57,4 @@ appendInfoLine: "};"
 for ifilter from 0 to 1
 	removeObject: filter_mat [ifilter], filter [ifilter], sweep_low [ifilter]
 endfor
-removeObject: sweep, sweep_8k, mooi
\ No newline at end of file
+removeObject: sweep, sweep_8k, mooi
diff --git a/test/fon/soundFiles.praat b/test/fon/soundFiles.praat
index 31b0344..168e7b7 100644
--- a/test/fon/soundFiles.praat
+++ b/test/fon/soundFiles.praat
@@ -4,7 +4,7 @@ procedure test .type$ .extension$ .duration
 	for numberOfChannels from 1 to 8
 		print 'numberOfChannels' channels:
 		sound = Create Sound from formula... sound numberOfChannels 0 .duration/numberOfChannels 44100 1/4 * sin(2*pi*377*x) + randomGauss(0,0.05)
-		Formula... round (self * 32768) / 32768
+		Formula: ~ round (self * 32768) / 32768
 		energy1 = Get energy in air
 		Save as '.type$' file... kanweg.'.extension$'
 		stopwatch
@@ -23,7 +23,7 @@ procedure test24 .type$ .extension$ .duration
 	for numberOfChannels from 1 to 8
 		print 'numberOfChannels' channels:
 		sound = Create Sound from formula... sound numberOfChannels 0 .duration/numberOfChannels 44100 1/4 * sin(2*pi*377*x) + randomGauss(0,0.05)
-		Formula... round (self * 32768*256) / (32768*256)
+		Formula: ~ round (self * 32768*256) / (32768*256)
 		energy1 = Get energy in air
 		Save as 24-bit '.type$' file... kanweg.'.extension$'
 		stopwatch
@@ -42,7 +42,7 @@ procedure test32 .type$ .extension$ .duration
 	for numberOfChannels from 1 to 8
 		print 'numberOfChannels' channels:
 		sound = Create Sound from formula... sound numberOfChannels 0 .duration/numberOfChannels 44100 1/4 * sin(2*pi*377*x) + randomGauss(0,0.05)
-		Formula... round (self * 32768*65536) / (32768*65536)
+		Formula: ~ round (self * 32768*65536) / (32768*65536)
 		energy1 = Get energy in air
 		Save as 32-bit '.type$' file... kanweg.'.extension$'
 		stopwatch
diff --git a/test/num/alloc.praat b/test/num/alloc.praat
new file mode 100644
index 0000000..18a3d97
--- /dev/null
+++ b/test/num/alloc.praat
@@ -0,0 +1,44 @@
+writeInfoLine: "alloc versus zero"
+
+numberOfChecks = 100
+for n from 1 to numberOfChecks
+	result$ = Praat test: "TimeAlloc", string$ (10^7 / n), string$ (n), "", ""
+	durationAlloc = extractNumber (result$, "")
+	result$ = Praat test: "TimeAlloc0", string$ (10^7 / n), string$ (n), "", ""
+	durationAlloc0 = extractNumber (result$, "")
+	result$ = Praat test: "TimeZero", string$ (10^7 / n), string$ (n), "", ""
+	durationZero = extractNumber (result$, newline$)
+	result$ = Praat test: "TimeMalloc", string$ (10^7 / n), string$ (n), "", ""
+	durationMalloc = extractNumber (result$, newline$)
+	result$ = Praat test: "TimeCalloc", string$ (10^7 / n), string$ (n), "", ""
+	durationCalloc = extractNumber (result$, newline$)
+	appendInfoLine (n, " ", durationAlloc, " ", durationAlloc0, " ", durationZero, " ", durationMalloc, " ", durationCalloc)
+endfor
+for i from 1 to 100
+	n = i*100
+	result$ = Praat test: "TimeAlloc", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationAlloc = extractNumber (result$, "")
+	result$ = Praat test: "TimeAlloc0", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationAlloc0 = extractNumber (result$, "")
+	result$ = Praat test: "TimeZero", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationZero = extractNumber (result$, newline$)
+	result$ = Praat test: "TimeMalloc", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationMalloc = extractNumber (result$, newline$)
+	result$ = Praat test: "TimeCalloc", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationCalloc = extractNumber (result$, newline$)
+	appendInfoLine (n, " ", durationAlloc, " ", durationAlloc0, " ", durationZero, " ", durationMalloc, " ", durationCalloc)
+endfor
+for i from 1 to 100
+	n = i*10000
+	result$ = Praat test: "TimeAlloc", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationAlloc = extractNumber (result$, "")
+	result$ = Praat test: "TimeAlloc0", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationAlloc0 = extractNumber (result$, "")
+	result$ = Praat test: "TimeZero", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationZero = extractNumber (result$, newline$)
+	result$ = Praat test: "TimeMalloc", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationMalloc = extractNumber (result$, newline$)
+	result$ = Praat test: "TimeCalloc", string$ (max (10^7 / n, 1)), string$ (n), "", ""
+	durationCalloc = extractNumber (result$, newline$)
+	appendInfoLine (n, " ", durationAlloc, " ", durationAlloc0, " ", durationZero, " ", durationMalloc, " ", durationCalloc)
+endfor
diff --git a/test/num/assign.praat b/test/num/assign.praat
new file mode 100644
index 0000000..b149a36
--- /dev/null
+++ b/test/num/assign.praat
@@ -0,0 +1,11 @@
+a# = repeat# ({42},42)
+stopwatch
+for i to 10^6
+	b# = a#
+endfor
+writeInfoLine: stopwatch
+stopwatch
+for i to 10^6
+	b# = a# + 0
+endfor
+appendInfoLine: stopwatch
\ No newline at end of file
diff --git a/test/num/mean.R b/test/num/mean.R
index b636490..9e33dbb 100644
--- a/test/num/mean.R
+++ b/test/num/mean.R
@@ -1,7 +1,7 @@
 n = 1e5+1
 n7 = 7 * n
 d = 0
-d = 0.23456
+d = 0.234567
 #d = 0.000547462463
 big0 = 1 + d
 sequenceA = seq (1, 7)
diff --git a/test/num/mean.praat b/test/num/mean.praat
index 6460324..9f611a8 100644
--- a/test/num/mean.praat
+++ b/test/num/mean.praat
@@ -12,7 +12,7 @@ appendInfoLine: "mean ", mean (durations#), " nanoseconds"
 n = 1e5+1
 n7 = 7 * n
 d = 0
-d = 0.23456
+d = 0.234567
 ;d = 0.000547462463
 big0 = 1 + d 
 sequenceA# = { 1, 2, 3, 4, 5, 6, 7 }
@@ -51,16 +51,12 @@ for power from 1 to 25
 endfor
 Debug: "no", 0
 
-debug# = { 48, 49, 50, 51, 52, 53, 54, 55, 0 }
+debug# = { 48, 49, 50, 51, 0 }
 debug$ [1] = "Naive 64-bits"
 debug$ [2] = "Naive 80-bits"
-debug$ [3] = "First-element offset"
-debug$ [4] = "Chan pairwise"
-debug$ [5] = "Pairwise base case 8"
-debug$ [6] = "Pairwise base case 16"
-debug$ [7] = "Two-loop"
-debug$ [8] = "Pairwise base case 32"
-debug$ [9] = "Pairwise base case 64"
+debug$ [3] = "Kahan"
+debug$ [4] = "Two-loop (as in R)"
+debug$ [5] = "Pairwise base case 64"
 
 appendInfoLine: newline$, "OFFSET"
 for idebug from 1 to size (debug#)
@@ -91,16 +87,16 @@ appendInfoLine: newline$, "TIMING"
 numberOfTrials = 100
 stopwatch
 for i to numberOfTrials
-	b# = a#
-	b# = a#
-	b# = a#
-	b# = a#
-	b# = a#
-	b# = a#
-	b# = a#
-	b# = a#
-	b# = a#
-	b# = a#
+	size: a#
+	size: a#
+	size: a#
+	size: a#
+	size: a#
+	size: a#
+	size: a#
+	size: a#
+	size: a#
+	size: a#
 endfor
 appendInfoLine: "Baseline: ", stopwatch / numberOfTrials / n7 * 1e9 / 10, " ns"
 for idebug from 1 to size (debug#)
@@ -148,10 +144,6 @@ endproc
 for idebug from 1 to size (debug#)
 	Debug: "no", debug# [idebug]
 	@do_single_peak: 1, 2
-	@do_single_peak: 2, 1
-	@do_single_peak: 2, 3
-	@do_single_peak: 1e6+2, 1e6+1
-	@do_single_peak: 1e6+1, 1e6+2
 endfor
 
 appendInfoLine: newline$, "OK"
\ No newline at end of file
diff --git a/test/num/mul.praat b/test/num/mul.praat
new file mode 100644
index 0000000..797facb
--- /dev/null
+++ b/test/num/mul.praat
@@ -0,0 +1,26 @@
+# mul.praat
+
+iterations = 1000
+size = 1000
+vec# = zero# (size)
+mat## = zero## (size, size)
+vecje# = zero# (1)
+matje## = zero## (1, 1)
+
+stopwatch
+for i to iterations
+	b## = mat##
+endfor
+writeInfoLine: stopwatch * 1e9 / size ^ 2 / iterations
+
+stopwatch
+for i to iterations
+	a# = mul# (vec#, mat##)
+endfor
+appendInfoLine: stopwatch * 1e9 / size ^ 2 / iterations
+
+stopwatch
+for i to iterations
+	a# = mul# (mat##, vec#)
+endfor
+appendInfoLine: stopwatch * 1e9 / size ^ 2 / iterations
diff --git a/test/num/plusgets.praat b/test/num/plusgets.praat
new file mode 100644
index 0000000..08110a1
--- /dev/null
+++ b/test/num/plusgets.praat
@@ -0,0 +1,17 @@
+a# = repeat# ({5},100)
+b# = a#
+stopwatch
+for i to 1000000
+	b# += a#
+endfor
+writeInfoLine: stopwatch, " ", b# [3]
+
+a# = repeat# ({5},100)
+for i to 5
+	a# += a#
+endfor
+a# [3] = 160
+
+a# = { 1, 3, 5 }
+a# += 10
+assert a# = { 11, 13, 15 }
diff --git a/test/num/sort.praat b/test/num/sort.praat
new file mode 100644
index 0000000..72854cf
--- /dev/null
+++ b/test/num/sort.praat
@@ -0,0 +1,21 @@
+writeInfoLine: "sort..."
+
+numberOfChecks = 70
+durations# = zero# (numberOfChecks)
+for icheck from 1 to numberOfChecks
+	n = icheck
+	result$ = Praat test: "TimeSort", string$ (10^7 / n), string$ (n), "", ""
+	durations# [icheck] = extractNumber (result$, "")
+	appendInfoLine (n, " ", durations# [n])
+endfor
+for icheck from 1 to 8
+	n = 10^icheck
+	result$ = Praat test: "TimeSort", string$ (max (1, 10^7 / n)), string$ (n), "", ""
+	duration = extractNumber (result$, "")
+	appendInfoLine (n, " ", duration)
+endfor
+
+appendInfoLine: "mean ", mean (durations#), " nanoseconds"
+
+;for n to 1000
+appendInfoLine: "OK"
diff --git a/test/num/sum.praat b/test/num/sum.praat
index d0c3011..e3d0d2e 100644
--- a/test/num/sum.praat
+++ b/test/num/sum.praat
@@ -1,6 +1,6 @@
 writeInfoLine: "sum..."
 
-for i from 2 to 100
+for i from 2 to 1000
 	assert sum (linear# (1, i, i, 0)) = sum (linear# (1, i - 1, i - 1, 0)) + i   ; 'i'
 endfor
 
@@ -9,8 +9,9 @@ for i from 1 to 10
 	assert sum (linear# (1, i, i, 0)) = i * mean (linear# (1, i, i, 0))   ; 'i'
 endfor
 
-durations# = zero# (100)
-for n from 1 to 100
+numberOfChecks = 100
+durations# = zero# (numberOfChecks)
+for n from 1 to numberOfChecks
 	result$ = Praat test: "TimeSum", string$ (10^8 / n), string$ (n), "", ""
 	durations# [n] = extractNumber (result$, newline$)
 	appendInfoLine (n, " ", durations# [n])
diff --git a/test/runAllTests.praat b/test/runAllPraatTests.praat
similarity index 98%
rename from test/runAllTests.praat
rename to test/runAllPraatTests.praat
index b0c19a5..f2003cc 100644
--- a/test/runAllTests.praat
+++ b/test/runAllPraatTests.praat
@@ -1,5 +1,5 @@
-# Praat script runAlltests.praat
-# Paul Boersma, 31 December 2014
+# Praat script runAllPraatTests.praat
+# Paul Boersma 2017-09-13
 #
 # This script runs all Praat scripts in its subdirectories.
 
diff --git a/test/runAllTests_leak.praat b/test/runAllPraatTests_leak.praat
similarity index 96%
rename from test/runAllTests_leak.praat
rename to test/runAllPraatTests_leak.praat
index d2dc890..69e0e6a 100644
--- a/test/runAllTests_leak.praat
+++ b/test/runAllPraatTests_leak.praat
@@ -1,5 +1,5 @@
-# Praat script runAlltests_leak.praat
-# Paul Boersma, 2016-02-02
+# Praat script runAllPraatTests_leak.praat
+# Paul Boersma 2017-09-13
 #
 # This script runs all Praat scripts in its subdirectories.
 
@@ -28,7 +28,7 @@ other_after   = 0
 
 leakReport$ = ""
 
-procedure runScript (.path$)
+procedure doScript: .path$
 	runScript: .path$
 	Erase all
 	writeInfo ()
@@ -74,7 +74,7 @@ for directory to numberOfDirectories
 			file$ = Get string: file
 			path$ = directory$ + "/" + file$
 			appendInfoLine: "### executing ", path$, ":"
-			@runScript: path$
+			@doScript: path$
 		endfor
 		removeObject: files
 	endif
@@ -99,7 +99,7 @@ for directory1 to numberOfDirectories1
 				file$ = Get string: file
 				path$ = directory1$ + "/" + directory2$ + "/" + file$
 				appendInfoLine: "### executing ", path$, ":"
-				@runScript: path$
+				@doScript: path$
 			endfor
 			removeObject: files
 		endfor
diff --git a/test/script/RBM.praat b/test/script/RBM.praat
index b0db6f8..dc23a93 100644
--- a/test/script/RBM.praat
+++ b/test/script/RBM.praat
@@ -5,13 +5,10 @@ numberOfInputNodes = 30
 numberOfMiddleNodes = 50
 numberOfOutputNodes = 20
 numberOfVowels = 3
-mean# = zero# (3)
-mean# [1] = 8
-mean# [2] = 16
-mean# [3] = 23
+mean# = { 8, 16, 23 }
 sigma = 1.8
-numberOfPatterns = 10000
-learningRate = 0.001
+numberOfPatterns = 100000
+learningRate = 0.0001
 
 #
 # Train first layer.
@@ -26,17 +23,12 @@ outrec1# = zero# (numberOfMiddleNodes)
 for idatum to numberOfPatterns
 	vowel = randomInteger (1, numberOfVowels)
 	formant = randomGauss (mean# [vowel], sigma)
-	for i to numberOfInputNodes
-		input1# [i] = 5 * exp (-0.5 * ((i - formant) / sigma) ^ 2) - 0.5
-	endfor
+	input1# ~ 5 * exp (-0.5/sigma^2 * (col - formant) ^ 2) - 0.5
 	#
 	# Spread up, with Bernoulli sampling.
 	#
 	output1# = sigmoid# (outbias1# + mul# (input1#, weight1##))
-	for j to numberOfMiddleNodes
-		output1# [j] = ( randomUniform (0, 1) < output1# [j] )
-		;output1# [j] = randomBernoulli (output1# [j])
-	endfor
+	output1# = randomBernoulli# (output1#)
 	#
 	# Spread down.
 	#
@@ -48,9 +40,9 @@ for idatum to numberOfPatterns
 	#
 	# Update.
 	#
-	inbias1# = inbias1# + learningRate * (input1# - inrec1#)
-	outbias1# = outbias1# + learningRate * (output1# - outrec1#)
-	weight1## = weight1## + learningRate * (outer## (input1#, output1#) - outer## (inrec1#, outrec1#))
+	inbias1# += learningRate * (input1# - inrec1#)
+	outbias1# += learningRate * (output1# - outrec1#)
+	weight1## += learningRate * (outer## (input1#, output1#) - outer## (inrec1#, outrec1#))
 endfor
 
 #
@@ -66,17 +58,16 @@ outrec2# = zero# (numberOfOutputNodes)
 for idatum to numberOfPatterns
 	vowel = randomInteger (1, numberOfVowels)
 	formant = randomGauss (mean# [vowel], sigma)
-	for i to numberOfInputNodes
-		input1# [i] = 5 * exp (-0.5 * ((i - formant) / sigma) ^ 2) - 0.5
-	endfor
+	input1# ~ 5 * exp (-0.5/sigma^2 * (col - formant) ^ 2) - 0.5
 	#
 	# Spread up through first layer, with Bernoulli sampling.
 	#
 	output1# = sigmoid# (outbias1# + mul# (input1#, weight1##))
-	for j to numberOfMiddleNodes
-		output1# [j] = ( randomUniform (0, 1) < output1# [j] )
-		;output1# [j] = randomBernoulli (output1# [j])
-	endfor
+	output1# = randomBernoulli# (output1#)
+	;; output1# owned and target
+	;; output1# by reference (and target)
+	;; randomBernoulli# into output1# (because no use of output1# further on)
+	;; assignment is no-op
 	#
 	# Copy output of first layer to input of second layer.
 	#
@@ -85,14 +76,19 @@ for idatum to numberOfPatterns
 	# Spread up through second layer, with Bernoulli sampling.
 	#
 	output2# = sigmoid# (outbias2# + mul# (input2#, weight2##))
-	for j to numberOfOutputNodes
-		output2# [j] = ( randomUniform (0, 1) < output2# [j] )
-		;output2# [j] = randomBernoulli (output2# [j])
-	endfor
+	output2# = randomBernoulli# (output2#)
 	#
 	# Spread down.
 	#
 	inrec2# = sigmoid# (inbias2# + mul# (weight2##, output2#))
+	; inrec2# owned and target TODO
+	; inbias2# by reference OK
+	; weight2## by reference OK
+	; output2# by reference OK
+	; mul# ref and ref into target (size matches, and inrec# does not occur further on) TODO
+	; + ref into target TODO
+	; sigmoid# into target TODO
+
 	#
 	# Spread up.
 	#
@@ -100,9 +96,9 @@ for idatum to numberOfPatterns
 	#
 	# Update.
 	#
-	inbias2# = inbias2# + learningRate * (input2# - inrec2#)
-	outbias2# = outbias2# + learningRate * (output2# - outrec2#)
-	weight2## = weight2## + learningRate * (outer## (input2#, output2#) - outer## (inrec2#, outrec2#))
+	inbias2# += learningRate * (input2# - inrec2#)
+	outbias2# += learningRate * (output2# - outrec2#)
+	weight2## += learningRate * (outer## (input2#, output2#) - outer## (inrec2#, outrec2#))
 endfor
 
 appendInfoLine: "Trained in ", stopwatch, " seconds"
@@ -114,14 +110,12 @@ for itest to numberOfTestPatterns
 	appendInfoLine: "Test pattern #", itest, ":"
 	vowel = randomInteger (1, numberOfVowels)
 	formant = randomGauss (mean# [vowel], sigma)
-	for i to numberOfInputNodes
-		input1# [i] = 5 * exp (-0.5 * ((i - formant) / sigma) ^ 2) - 0.5
-	endfor
+	input1# ~ 5 * exp (-0.5 * ((col - formant) / sigma) ^ 2) - 0.5
 	#
 	# Draw input.
 	#
 	Select outer viewport: 0, 3, (itest - 1) * 0.6, (itest - 1) * 0.6 + 1.0
-	Create simple Matrix: "input", 1, numberOfInputNodes, "5 * exp (-0.5 * ((col - formant) / sigma) ^ 2) - 0.5"
+	Create simple Matrix: "input", 1, numberOfInputNodes, ~ 5 * exp (-0.5 * ((col - formant) / sigma) ^ 2) - 0.5
 	stdev = Get standard deviation: 0, 0, 0, 0
 	appendInfoLine: "   Energy in input layer: ", stdev
 	Draw rows: 0, 0, 0, 0, -5, 5
@@ -130,9 +124,7 @@ for itest to numberOfTestPatterns
 	# Spread up through first layer, without Bernoulli sampling.
 	#
 	output1# = sigmoid# (outbias1# + mul# (input1#, weight1##))
-	mean = sumOver (i to numberOfMiddleNodes, output1# [i]) / numberOfMiddleNodes
-	stdev = sqrt (sumOver (i to numberOfMiddleNodes, (output1# [i] - mean) ^ 2) / (numberOfMiddleNodes - 1))
-	appendInfoLine: "   Energy in middle layer: ", stdev
+	appendInfoLine: "   Energy in middle layer: ", stdev (output1#)
 	#
 	# Copy output of first layer to input of second layer.
 	#
@@ -141,16 +133,12 @@ for itest to numberOfTestPatterns
 	# Spread up through second layer, without Bernoulli sampling.
 	#
 	output2# = sigmoid# (outbias2# + mul# (input2#, weight2##))
-	mean = sumOver (i to numberOfOutputNodes, output2# [i]) / numberOfOutputNodes
-	stdev = sqrt (sumOver (i to numberOfOutputNodes, (output2# [i] - mean) ^ 2) / (numberOfOutputNodes - 1))
-	appendInfoLine: "   Energy in output layer: ", stdev
+	appendInfoLine: "   Energy in output layer: ", stdev (output2#)
 	#
 	# Spread down through second layer.
 	#
 	inrec2# = sigmoid# (inbias2# + mul# (weight2##, output2#))
-	mean = sumOver (i to numberOfMiddleNodes, inrec2# [i]) / numberOfMiddleNodes
-	stdev = sqrt (sumOver (i to numberOfMiddleNodes, (inrec2# [i] - mean) ^ 2) / (numberOfMiddleNodes - 1))
-	appendInfoLine: "   Energy in middle layer: ", stdev
+	appendInfoLine: "   Energy in middle layer: ", stdev (inrec2#)
 	#
 	# Spread down through first layer.
 	#
diff --git a/test/script/script.praat b/test/script/script.praat
index ced4381..3d4c1ba 100644
Binary files a/test/script/script.praat and b/test/script/script.praat differ
diff --git a/test/sys/Formula.cpp.praat b/test/sys/Formula.cpp.praat
new file mode 100644
index 0000000..1184af1
--- /dev/null
+++ b/test/sys/Formula.cpp.praat
@@ -0,0 +1,59 @@
+# File sys/Formula.cpp.praat
+# Generated by test/createTests.praat
+# Wed Sep 13 22:42:24 2017
+
+#
+# result = x + y
+#
+x = 5
+y = 6
+result = x + y
+assert result = 11
+
+#
+# result# = x + owned y#
+#
+result# = 5 + { 11, 13, 31 }   ; numeric vector literals are owned
+assert result# = { 16, 18, 36 }
+
+#
+# result# = x + unowned y#
+#
+y# = { 17, -11, 29 }
+result# = 30 + y#   ; numeric vector variables are not owned
+assert result# = { 47, 19, 59 }
+
+#
+# Error: unequal sizes.
+#
+x# = { 11, 13, 17 }
+y# = { 8, 90 }
+asserterror When adding vectors, their numbers of elements should be equal, instead of 3 and 2.
+result# = x# + y#
+
+#
+# result# = owned x# + y#
+#
+x# = { 11, 13, 17 }
+result# = x# + { 44, 56, 67 }   ; owned + unowned
+assert result# = { 55, 69, 84 }
+y# = { 3, 2, 89.5 }
+result# = x# + y#   ; owned + owned
+assert result# = { 14, 15, 106.5 }
+
+#
+# result# = unowned x# + owned y#
+#
+x# = { 14, -3, 6.25 }
+result# = x# + { 55, 1, -89 }
+assert result# = { 69, -2, -82.75 }
+
+#
+# result# = unowned x# + unowned y#
+#
+x# = { 14, -33, 6.25 }
+y# = { -33, 17, 9 }
+result# = x# + y#
+assert result# = { -19, -16, 15.25 }
+
+appendInfoLine: "sys/Formula.cpp.praat", " OK"
diff --git a/test/sys/object.praat b/test/sys/object.praat
index f78c244..8345c70 100644
--- a/test/sys/object.praat
+++ b/test/sys/object.praat
@@ -1,6 +1,6 @@
 writeInfoLine: "object[]..."
 nsamp = 44100
-sound = Create Sound from formula: "sineWithNoise", 1, 0, 1, nsamp, "1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)"
+x.Sound = Create Sound from formula: "sineWithNoise", 1, 0, 1, nsamp, "1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)"
 niter = 1e3
 stopwatch
 
@@ -11,23 +11,25 @@ procedure do (formula$)
 	appendInfoLine: fixed$ (1e9 * stopwatch / niter / nsamp, 1), " ns: ", formula$
 endproc
 
- at do: "0"
- at do: "5"
- at do: "1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)"
- at do: "self + 5"
- at do: "Sound_sineWithNoise[] + 5"
- at do: "Object_'sound'[] + 5"
- at do: "self"
- at do: "self [col]"
- at do: "self [col] + 5"
- at do: "Sound_sineWithNoise [col] + 5"
- at do: "Object_'sound' [col] + 5"
- at do: "object [sound, col] + 5"
- at do: "self"
+ at do: ~ 0
+ at do: ~ 5
+ at do: ~ 1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)
+ at do: ~ self + 5
+ at do: ~ Sound_sineWithNoise[] + 5
+ at do: ~ Object_'x.Sound'[] + 5
+ at do: ~ self
+ at do: ~ self [col]
+ at do: ~ self [col] + 5
+ at do: ~ Sound_sineWithNoise [col] + 5
+ at do: ~ Object_'x.Sound' [col] + 5
+ at do: "object [x.Sound, col] + 5"
+;@do: ~ x.Sound [col] + 5
+ at do: ~ self
 @do: "Sound_sineWithNoise [row, col] + 5"
- at do: "Object_'sound' [row, col] + 5"
+ at do: "Object_'x.Sound' [row, col] + 5"
 @do: "self [row, col] + 5"
- at do: "object [sound, row, col] + 5"
+ at do: "object [x.Sound, row, col] + 5"
+;@do: ~ x.Sound [row, col] + 5
 @do: "object [""Sound sineWithNoise"", row, col] + 5"
 name$ = "Sound sineWithNoise"
 @do: "object [name$, row, col] + 5"
diff --git a/test/sys/script2.praat b/test/sys/script2.praat
index 1033bc9..1cef12b 100644
Binary files a/test/sys/script2.praat and b/test/sys/script2.praat differ

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/praat.git



More information about the debian-med-commit mailing list