Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2235 (2023-08-27 19:19:28)

refactor: separate concerns in `TaskLayout.ApplyOptions()`
# Description

Separate management of `shownTaskLayoutComponents` & `hiddenTaskLayoutComponents` and the generation of hidden CSS classes in `taskListHiddenClasses`.

## Motivation and Context

Continue #2226 towards an abstraction in `TaskLayout`.

## How has this been tested?

Unit tests. Some commits tested by breaking code.

## Types of changes

- [x] **Refactor** (prefix: `refactor` - non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)

## Checklist

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

- [x] My contribution follow this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)


claremacrae: Hi @ilandikov 😄 

I realised that there is an easy way to get what we both want here, and that I would be very happy to accept as an addition to this PR...

Looking again at this code...

```ts
        this.hideComponent(this.options.hidePriority, 'priority');
        this.hideComponent(this.options.hideRecurrenceRule, 'recurrenceRule');
        this.hideComponent(this.options.hideCreatedDate, 'createdDate');
        this.hideComponent(this.options.hideStartDate, 'startDate');
        this.hideComponent(this.options.hideScheduledDate, 'scheduledDate');
        this.hideComponent(this.options.hideDueDate, 'dueDate');
        this.hideComponent(this.options.hideDoneDate, 'doneDate');

        this.generateHiddenClassForTaskList(this.options.hidePriority, 'priority');
        this.generateHiddenClassForTaskList(this.options.hideRecurrenceRule, 'recurrenceRule');
        this.generateHiddenClassForTaskList(this.options.hideCreatedDate, 'createdDate');
        this.generateHiddenClassForTaskList(this.options.hideStartDate, 'startDate');
        this.generateHiddenClassForTaskList(this.options.hideScheduledDate, 'scheduledDate');
        this.generateHiddenClassForTaskList(this.options.hideDueDate, 'dueDate');
        this.generateHiddenClassForTaskList(this.options.hideDoneDate, 'doneDate');
```

To be specific, my concern is the duplication of data (it's not [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)).

If there was an array/list/whatever created that contained the 7 options and their names, then you can iterate over it as many times as you want, in as many places as you want, because there will only be one single source of truth in the values.

I don't know the names (hence the nonsense names) or the syntax, but this is what I mean:

```ts
const applesauce1 = [
        (this.options.hidePriority, 'priority'),
        (this.options.hideRecurrenceRule, 'recurrenceRule'),
        (this.options.hideCreatedDate, 'createdDate'),
        (this.options.hideStartDate, 'startDate'),
        (this.options.hideScheduledDate, 'scheduledDate'),
        (this.options.hideDueDate, 'dueDate'),
        (this.options.hideDoneDate, 'doneDate'),
    ];

const applesauce2 = [
        (this.options.hideUrgency, 'urgency'),
        (this.options.hideBacklinks, 'backlinks'),
        (this.options.hideEditButton, 'edit-button'),
    ];
```

Now, when a new display option is added, all that needs to be done is:

- work out which list the new thing goes in
- pick a string name for it, and add it to the appropriate list

... and Bob's your Uncle... 🤣 

Am very happy for extra commits to be added to this PR to make it look something like the above... (This is what I would have said when we next paired, but I found time to say it now...)
ilandikov: Will try something like that =)

Thanks a lot for suggestion
ilandikov: Done =) This looks better indeed =)

Also rebased on main to resolve the conflict

Patch:
diff --git a/docs/Reference/Task Formats/Dataview Format.md b/docs/Reference/Task Formats/Dataview Format.md
index a0d21598..d9beb81f 100644
--- a/docs/Reference/Task Formats/Dataview Format.md
+++ b/docs/Reference/Task Formats/Dataview Format.md
@@ -130,7 +130,7 @@ For more information, see [[Recurring Tasks]].
 
 The Dataview format fully supports Tasks' [[Auto-Suggest]] feature, but requires users to manually type out surrounding brackets (`[]` or `()`).  This works best with `Settings > Editor > Autopair Brackets` enabled.
 
-Since Tasks X.Y.Z, the Auto-Suggest menu *only* appears between square brackets `[]` or parentheses `()`.
+Since Tasks 4.6.1, the Auto-Suggest menu *only* appears between square brackets `[]` or parentheses `()`.
 
 ## Limitations of Dataview Format
 
diff --git a/manifest.json b/manifest.json
index 82e81afc..3125317e 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
 {
   "id": "obsidian-tasks-plugin",
   "name": "Tasks",
-  "version": "4.6.0",
+  "version": "4.6.1",
   "minAppVersion": "1.1.1",
   "description": "Task management for Obsidian",
   "author": "Martin Schenck and Clare Macrae",
diff --git a/package.json b/package.json
index 727b2aa0..037290a0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "obsidian-tasks",
-  "version": "4.6.0",
+  "version": "4.6.1",
   "description": "Task management for the Obsidian knowledge base",
   "main": "main.js",
   "scripts": {
diff --git a/src/QueryRenderer.ts b/src/QueryRenderer.ts
index 4af0f4db..dfcc6551 100644
--- a/src/QueryRenderer.ts
+++ b/src/QueryRenderer.ts
@@ -218,7 +218,7 @@ class QueryRenderChild extends MarkdownRenderChild {
         const layout = new TaskLayout(this.query.layoutOptions);
         const taskList = content.createEl('ul');
         taskList.addClasses(['contains-task-list', 'plugin-tasks-query-result']);
-        taskList.addClasses(layout.taskListClasses);
+        taskList.addClasses(layout.taskListHiddenClasses);
         const groupingAttribute = this.getGroupingAttribute();
         if (groupingAttribute && groupingAttribute.length > 0) taskList.dataset.taskGroupBy = groupingAttribute;
         for (let i = 0; i < tasksCount; i++) {
diff --git a/src/Suggestor/EditorSuggestorPopup.ts b/src/Suggestor/EditorSuggestorPopup.ts
index 914b6b1f..c5853ee1 100644
--- a/src/Suggestor/EditorSuggestorPopup.ts
+++ b/src/Suggestor/EditorSuggestorPopup.ts
@@ -19,7 +19,7 @@ export class EditorSuggestor extends EditorSuggest<SuggestInfoWithContext> {
     onTrigger(cursor: EditorPosition, editor: Editor, _file: TFile): EditorSuggestTriggerInfo | null {
         if (!this.settings.autoSuggestInEditor) return null;
         const line = editor.getLine(cursor.line);
-        if (canSuggestForLine(line)) {
+        if (canSuggestForLine(line, cursor.ch)) {
             return {
                 start: { line: cursor.line, ch: 0 },
                 end: {
diff --git a/src/Suggestor/Suggestor.ts b/src/Suggestor/Suggestor.ts
index 8f53c6bd..b24d7936 100644
--- a/src/Suggestor/Suggestor.ts
+++ b/src/Suggestor/Suggestor.ts
@@ -3,8 +3,7 @@ import { DateParser } from '../Query/DateParser';
 import { doAutocomplete } from '../DateAbbreviations';
 import { Recurrence } from '../Recurrence';
 import type { DefaultTaskSerializerSymbols } from '../TaskSerializer/DefaultTaskSerializer';
-import * as task from '../Task';
-import { TaskRegularExpressions } from '../Task';
+import { Task, TaskRegularExpressions } from '../Task';
 import { GlobalFilter } from '../Config/GlobalFilter';
 import type { SuggestInfo, SuggestionBuilder } from '.';
 
@@ -411,7 +410,35 @@ export function onlySuggestIfBracketOpen(fn: SuggestionBuilder, brackets: [strin
  *  - Is the global filter (if set) in the line?
  *  - Is the line a task line (with a checkbox)
  * @param line
+ * @param cursorPosition - the cursor position, when 0 is presumed to mean 'at the start of the line'.
+ *                          See 'ch' in https://docs.obsidian.md/Reference/TypeScript+API/EditorPosition/EditorPosition
  */
-export function canSuggestForLine(line: string) {
-    return GlobalFilter.includedIn(line) && line.match(task.TaskRegularExpressions.taskRegex) !== null;
+export function canSuggestForLine(line: string, cursorPosition: number) {
+    return GlobalFilter.includedIn(line) && cursorIsInTaskLineDescription(line, cursorPosition);
+}
+
+/**
+ * Return true if:
+ * - line is a task line, that is, it is a list item with a checkbox.
+ * - the cursor is in a task line's description.
+ *
+ * Here, description includes any task signifiers, as well as the vanilla description.
+ * @param line
+ * @param cursorPosition
+ */
+function cursorIsInTaskLineDescription(line: string, cursorPosition: number) {
+    if (line.length === 0) {
+        return false;
+    }
+
+    const components = Task.extractTaskComponents(line);
+    if (!components) {
+        // It is not a task line, that is, it is not a list item with a checkbox:
+        return false;
+    }
+
+    // Reconstruct the contents of the line, up to the space after the closing ']' in the checkbox:
+    const beforeDescription = components.indentation + components.listMarker + ' [' + components.status.symbol + '] ';
+
+    return cursorPosition >= beforeDescription.length;
 }
diff --git a/src/Task.ts b/src/Task.ts
index 0e4c57f8..bc59eadc 100644
--- a/src/Task.ts
+++ b/src/Task.ts
@@ -297,7 +297,12 @@ export class Task {
         });
     }
 
-    private static extractTaskComponents(line: string): TaskComponents | null {
+    /**
+     * Extract the component parts of the task line.
+     * @param line
+     * @returns a {@link TaskComponents} object containing the component parts of the task line
+     */
+    static extractTaskComponents(line: string): TaskComponents | null {
         // Check the line to see if it is a markdown task.
         const regexMatch = line.match(TaskRegularExpressions.taskRegex);
         if (regexMatch === null) {
diff --git a/src/TaskLayout.ts b/src/TaskLayout.ts
index 5b3f9278..06c8b5e1 100644
--- a/src/TaskLayout.ts
+++ b/src/TaskLayout.ts
@@ -50,7 +50,7 @@ export class TaskLayout {
     public shownTaskLayoutComponents: TaskLayoutComponent[];
     public hiddenTaskLayoutComponents: TaskLayoutComponent[] = [];
     public options: LayoutOptions;
-    public taskListClasses: string[] = [];
+    public taskListHiddenClasses: string[] = [];
 
     constructor(options?: LayoutOptions) {
         if (options) {
@@ -58,59 +58,60 @@ export class TaskLayout {
         } else {
             this.options = new LayoutOptions();
         }
-
         this.shownTaskLayoutComponents = this.defaultLayout;
-        this.shownTaskLayoutComponents = this.applyOptions(this.options);
+        this.applyOptions();
     }
 
-    /**
-     * Return a new list of components with the given options applied.
-     */
-    applyOptions(layoutOptions: LayoutOptions): TaskLayoutComponent[] {
-        // Remove a component from the taskComponents array if the given layoutOption criteria is met,
-        // and add to the layout's specific classes list the class that denotes that this component
-        // isn't in the layout
-        const removeIf = (
-            taskComponents: TaskLayoutComponent[],
-            shouldRemove: boolean,
-            componentToRemove: TaskLayoutComponent,
-        ) => {
-            if (shouldRemove) {
-                this.taskListClasses.push(`tasks-layout-hide-${componentToRemove}`);
-                this.hiddenTaskLayoutComponents.push(componentToRemove);
-                return taskComponents.filter((element) => element != componentToRemove);
-            } else {
-                return taskComponents;
-            }
-        };
-        const markHiddenQueryComponent = (hidden: boolean, hiddenComponentName: string) => {
-            if (hidden) {
-                this.taskListClasses.push(`tasks-layout-hide-${hiddenComponentName}`);
-            }
-        };
+    private applyOptions() {
         // Remove components from the layout according to the task options. These represent the existing task options,
         // so some components (e.g. the description) are not here because there are no layout options to remove them.
-        let newComponents = this.shownTaskLayoutComponents;
-        newComponents = removeIf(newComponents, layoutOptions.hidePriority, 'priority');
-        newComponents = removeIf(newComponents, layoutOptions.hideRecurrenceRule, 'recurrenceRule');
-        newComponents = removeIf(newComponents, layoutOptions.hideCreatedDate, 'createdDate');
-        newComponents = removeIf(newComponents, layoutOptions.hideStartDate, 'startDate');
-        newComponents = removeIf(newComponents, layoutOptions.hideScheduledDate, 'scheduledDate');
-        newComponents = removeIf(newComponents, layoutOptions.hideDueDate, 'dueDate');
-        newComponents = removeIf(newComponents, layoutOptions.hideDoneDate, 'doneDate');
+        const componentsToHideAndGenerateClasses: [boolean, TaskLayoutComponent][] = [
+            [this.options.hidePriority, 'priority'],
+            [this.options.hideRecurrenceRule, 'recurrenceRule'],
+            [this.options.hideCreatedDate, 'createdDate'],
+            [this.options.hideStartDate, 'startDate'],
+            [this.options.hideScheduledDate, 'scheduledDate'],
+            [this.options.hideDueDate, 'dueDate'],
+            [this.options.hideDoneDate, 'doneDate'],
+        ];
+        for (const [hide, component] of componentsToHideAndGenerateClasses) {
+            this.hideComponent(hide, component);
+            this.generateHiddenClassForTaskList(hide, component);
+        }
+
+        const componentsToGenerateClassesOnly: [boolean, string][] = [
+            // Tags are hidden, rather than removed. See tasks-layout-hide-tags in styles.css.
+            [this.options.hideTags, 'tags'],
+
+            // The following components are handled in QueryRenderer.ts and thus are not part of the same flow that
+            // hides TaskLayoutComponent items. However, we still want to have 'tasks-layout-hide' items for them
+            // (see https://github.com/obsidian-tasks-group/obsidian-tasks/issues/1866).
+            // This can benefit from some refactoring, i.e. render these components in a similar flow rather than
+            // separately.
+            [this.options.hideUrgency, 'urgency'],
+            [this.options.hideBacklinks, 'backlinks'],
+            [this.options.hideEditButton, 'edit-button'],
+        ];
+        for (const [hide, component] of componentsToGenerateClassesOnly) {
+            this.generateHiddenClassForTaskList(hide, component);
+        }
 
-        // Tags are hidden, rather than removed. See tasks-layout-hide-tags in styles.css.
-        markHiddenQueryComponent(layoutOptions.hideTags, 'tags');
+        if (this.options.shortMode) this.taskListHiddenClasses.push('tasks-layout-short-mode');
+    }
+
+    private generateHiddenClassForTaskList(hide: boolean, component: string) {
+        if (hide) {
+            this.taskListHiddenClasses.push(`tasks-layout-hide-${component}`);
+        }
+    }
 
-        // The following components are handled in QueryRenderer.ts and thus are not part of the same flow that
-        // hides TaskLayoutComponent items. However, we still want to have 'tasks-layout-hide' items for them
-        // (see https://github.com/obsidian-tasks-group/obsidian-tasks/issues/1866).
-        // This can benefit from some refactoring, i.e. render these components in a similar flow rather than
-        // separately.
-        markHiddenQueryComponent(layoutOptions.hideUrgency, 'urgency');
-        markHiddenQueryComponent(layoutOptions.hideBacklinks, 'backlinks');
-        markHiddenQueryComponent(layoutOptions.hideEditButton, 'edit-button');
-        if (layoutOptions.shortMode) this.taskListClasses.push('tasks-layout-short-mode');
-        return newComponents;
+    /**
+     * Move a component from the shown to hidden if the given layoutOption criteria is met.
+     */
+    private hideComponent(hide: boolean, component: TaskLayoutComponent) {
+        if (hide) {
+            this.hiddenTaskLayoutComponents.push(component);
+            this.shownTaskLayoutComponents = this.shownTaskLayoutComponents.filter((element) => element != component);
+        }
     }
 }
diff --git a/versions.json b/versions.json
index 505cb2bb..3b2c7f8e 100644
--- a/versions.json
+++ b/versions.json
@@ -74,5 +74,6 @@
   "4.3.0": "1.1.1",
   "4.4.0": "1.1.1",
   "4.5.0": "1.1.1",
-  "4.6.0": "1.1.1"
+  "4.6.0": "1.1.1",
+  "4.6.1": "1.1.1"
 }


Test patch:
diff --git a/contributing/Testing/Approval Tests.md b/contributing/Testing/Approval Tests.md
index 1e9e05a6..21665ee3 100644
--- a/contributing/Testing/Approval Tests.md
+++ b/contributing/Testing/Approval Tests.md
@@ -44,6 +44,8 @@ There is a brief overview of Approval tests at [approvaltests.com](https://appro
 
 These trivial examples exist just to walk through the behaviour of Approval Tests.
 
+#### Verify strings and stringable things
+
 Example test in [tests/TestingTools/ApprovalTestsDemo.test.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/tests/TestingTools/ApprovalTestsDemo.test.ts), that saves its input in a text file:
 
 <!-- snippet: approval-test-as-text -->
@@ -62,6 +64,8 @@ Hello From Approvals
 ```
 <!-- endSnippet -->
 
+#### Verify an object as a JSON string
+
 <!-- snippet: approval-test-as-json -->
 ```ts
 test('JsonVerify', () => {
@@ -82,6 +86,64 @@ The corresponding `approved` file, named [tests/TestingTools/ApprovalTestsDemo.t
 ```
 <!-- endSnippet -->
 
+#### Verify the results of multiple input values
+
+Sometimes we want to test a piece of code under multiple different input values.
+
+A Tasks-specific example is when a piece of code is affected by multiple different user settings values. Testing all the possible combinations of user settings manually is prohibitively tedious.
+
+Approval Tests provides a facility to test with multiple input values: [Combinations Approvals](https://github.com/approvals/Approvals.NodeJS/blob/master/docs/reference/CombinationApprovals.md).
+
+However, the 'Combinations' code in Approvals.NodeJS does not work with async/await code.
+
+So instead, the Tasks project provides its own set of functions:
+
+- `verifyAllCombinations2Async()` function (for 2 input parameters)
+- and similarly named ones for numbers of parameters up to 9.
+
+Below is a contrived example of its use.
+
+> [!Important]
+> Note that we do not have an `it` section here. `verifyAllCombinations3Async()` creates the 'it' block.
+
+<!-- snippet: async-combination-approvals -->
+```ts
+describe('demonstrate async combination approvals', () => {
+    // Note that we do not have an 'it' section here.
+    // verifyAllCombinations3Async() creates the 'it' block.
+    verifyAllCombinations3Async(
+        'documentation example',
+        'sample outputs',
+        async (a, b, c) => {
+            return `(${a}, ${b}, ${c}) => ${a} '${b}' ${c}`;
+        },
+        [0, 1],
+        ['hello', 'world'],
+        [true, false],
+    );
+});
+```
+<!-- endSnippet -->
+
+The corresponding `approved` file, named [tests/TestingTools/CombinationApprovalsAsync.test.demonstrate_async_combination_approvals_documentation_example.approved.txt](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/tests/TestingTools/CombinationApprovalsAsync.test.demonstrate_async_combination_approvals_documentation_example.approved.txt):
+
+<!-- snippet: CombinationApprovalsAsync.test.demonstrate_async_combination_approvals_documentation_example.approved.txt -->
+```txt
+sample outputs
+(0, hello, true) => 0 'hello' true
+(0, hello, false) => 0 'hello' false
+(0, world, true) => 0 'world' true
+(0, world, false) => 0 'world' false
+(1, hello, true) => 1 'hello' true
+(1, hello, false) => 1 'hello' false
+(1, world, true) => 1 'world' true
+(1, world, false) => 1 'world' false
+```
+<!-- endSnippet -->
+
+> [!NOTE]
+> Note how all there is a line of output for each of the combinations of the `2 x 2 x 2` input values.
+
 ## Writing Approval Tests
 
 ### Summary of Approval Tests
@@ -97,6 +159,10 @@ We have these convenience helpers to make it easier to write Approvals-based tes
   - For saving various Tasks data types
 - [tests/TestingTools/VerifyMarkdownTable.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/tests/TestingTools/VerifyMarkdownTable.ts)
   - For saving approved data in Markdown tables
+- [tests/TestingTools/CombinationApprovalsAsync.ts](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/tests/TestingTools/CombinationApprovalsAsync.ts)
+  - For using Combination Approvals that needs to call async code
+  - See example above: [[#Verify the results of multiple input values]]
+  - If you are testing non-async code, you can use the combinations facilities provided by  Approvals.NodeJS: [Combinations Approvals](https://github.com/approvals/Approvals.NodeJS/blob/master/docs/reference/CombinationApprovals.md)
 
 ## Running Approval Tests
 
diff --git a/tests/Suggestor/Suggestor.test.ts b/tests/Suggestor/Suggestor.test.ts
index a589dea3..eb5647f7 100644
--- a/tests/Suggestor/Suggestor.test.ts
+++ b/tests/Suggestor/Suggestor.test.ts
@@ -28,6 +28,23 @@ afterAll(() => {
     chronoSpy.mockRestore();
 });
 
+/**
+ * Given a string with **exactly** one vertical bar (`|`), returns the string with the vertical bar removed
+ * and the index of the bar.
+ *
+ * This is used as a helper when writing tests, since it provides a less error prone
+ * and more readable way to represent a cursor's position, denoted by the vertical bar, and the string.
+ *
+ * @param line - A string that contains exactly one vertical bar (`|`)
+ * @returns A tuple of the line without the vertical bar, and the index of the vertical bar.
+ */
+function cursorPosition(line: string): [lineWithoutCursor: string, cursorIndex: number] {
+    const line_without_cursor = line.replace(/\|/g, '');
+    // Check that the cursor marker appears exactly once in each input string:
+    expect(line_without_cursor.length).toEqual(line.length - 1);
+    return [line_without_cursor, line.indexOf('|')];
+}
+
 const MAX_GENERIC_SUGGESTIONS_FOR_TESTS = 50;
 
 describe.each([
@@ -168,23 +185,6 @@ describe('onlySuggestIfBracketOpen', () => {
 
     const emptySuggestion = [] as unknown;
 
-    /**
-     * Given a string with **exactly** one vertical bar (`|`), returns the string with the vertical bar removed
-     * and the index of the bar.
-     *
-     * This is used as a helper when writing tests, since it provides a less error prone
-     * and more readable way to represent a cursor's position, denoted by the vertical bar, and the string.
-     *
-     * @param line - A string that contains exactly one vertical bar (`|`)
-     * @returns A tuple of the line without the vertical bar, and the index of the vertical bar.
-     */
-    function cursorPosition(line: string): [lineWithoutCursor: string, cursorIndex: number] {
-        const line_without_cursor = line.replace(/\|/g, '');
-        // Check that the cursor marker appears exactly once in each input string:
-        expect(line_without_cursor.length).toEqual(line.length - 1);
-        return [line_without_cursor, line.indexOf('|')];
-    }
-
     it('should suggest if cursor at end of line with an open pair', () => {
         const settings = getSettings();
         let suggestions = buildSuggestions(...cursorPosition('(hello world|'), settings);
@@ -233,18 +233,50 @@ describe('canSuggestForLine', () => {
         GlobalFilter.reset();
     });
 
+    function canSuggestForLineWithCursor(line: string) {
+        return canSuggestForLine(...cursorPosition(line));
+    }
+
+    it('should not suggest if there is no checkbox', () => {
+        GlobalFilter.reset();
+        expect(canSuggestForLineWithCursor('- not a task line|')).toEqual(false);
+    });
+
     it('should suggest if there is no global filter', () => {
         GlobalFilter.reset();
-        expect(canSuggestForLine('- [ ] global filter is not set')).toEqual(true);
+        expect(canSuggestForLineWithCursor('- [ ] global filter is not set|')).toEqual(true);
     });
 
     it('should suggest if global filter missing from line', () => {
         GlobalFilter.set('#todo');
-        expect(canSuggestForLine('- [ ] #todo has global filter')).toEqual(true);
+        expect(canSuggestForLineWithCursor('- [ ] #todo has global filter|')).toEqual(true);
     });
 
     it('should not suggest if global filter missing from line', () => {
         GlobalFilter.set('#todo');
-        expect(canSuggestForLine('- [ ] no global filter')).toEqual(false);
+        expect(canSuggestForLineWithCursor('- [ ] no global filter|')).toEqual(false);
+    });
+
+    it('should not suggest when cursor is in empty line', () => {
+        expect(canSuggestForLineWithCursor('|')).toEqual(false);
+    });
+
+    it('should not suggest when cursor is in the checkbox', () => {
+        expect(canSuggestForLineWithCursor('- [ |] ')).toEqual(false);
+        expect(canSuggestForLineWithCursor('- [ ]| ')).toEqual(false);
+    });
+
+    it('should suggest when the cursor is at least one character past the checkbox', () => {
+        expect(canSuggestForLineWithCursor('- [ ] |')).toEqual(true);
+    });
+
+    it('should suggest correctly when task is in a numbered list', () => {
+        expect(canSuggestForLineWithCursor('1. [ ]|')).toEqual(false);
+        expect(canSuggestForLineWithCursor('1. [ ] |')).toEqual(true);
+    });
+
+    it('should suggest correctly when task is indented', () => {
+        expect(canSuggestForLineWithCursor('    - [ ]|')).toEqual(false);
+        expect(canSuggestForLineWithCursor('    - [ ] |')).toEqual(true);
     });
 });
diff --git a/tests/TaskLayout.test.ts b/tests/TaskLayout.test.ts
index f205c2cf..2bab99f3 100644
--- a/tests/TaskLayout.test.ts
+++ b/tests/TaskLayout.test.ts
@@ -19,7 +19,7 @@ describe('TaskLayout tests', () => {
             blockLink"
         `);
         expect(taskLayout.hiddenTaskLayoutComponents.join('\n')).toMatchInlineSnapshot('""');
-        expect(taskLayout.taskListClasses.join('\n')).toMatchInlineSnapshot('"tasks-layout-hide-urgency"');
+        expect(taskLayout.taskListHiddenClasses.join('\n')).toMatchInlineSnapshot('"tasks-layout-hide-urgency"');
     });
 
     it('should generate expected CSS components with all default option reversed', () => {
@@ -46,7 +46,7 @@ describe('TaskLayout tests', () => {
             dueDate
             doneDate"
         `);
-        expect(taskLayout.taskListClasses.join('\n')).toMatchInlineSnapshot(`
+        expect(taskLayout.taskListHiddenClasses.join('\n')).toMatchInlineSnapshot(`
             "tasks-layout-hide-priority
             tasks-layout-hide-recurrenceRule
             tasks-layout-hide-createdDate
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify2.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify2.approved.txt
new file mode 100644
index 00000000..88340718
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify2.approved.txt
@@ -0,0 +1,5 @@
+title
+(0 + 2) => 2
+(0 + 3) => 3
+(1 + 2) => 3
+(1 + 3) => 4
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify3.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify3.approved.txt
new file mode 100644
index 00000000..d015791c
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify3.approved.txt
@@ -0,0 +1,9 @@
+title
+(0 + 2 + 4) => 6
+(0 + 2 + 5) => 7
+(0 + 3 + 4) => 7
+(0 + 3 + 5) => 8
+(1 + 2 + 4) => 7
+(1 + 2 + 5) => 8
+(1 + 3 + 4) => 8
+(1 + 3 + 5) => 9
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify4.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify4.approved.txt
new file mode 100644
index 00000000..cbcc3194
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify4.approved.txt
@@ -0,0 +1,17 @@
+title
+(0 + 2 + 4 + 6) => 12
+(0 + 2 + 4 + 7) => 13
+(0 + 2 + 5 + 6) => 13
+(0 + 2 + 5 + 7) => 14
+(0 + 3 + 4 + 6) => 13
+(0 + 3 + 4 + 7) => 14
+(0 + 3 + 5 + 6) => 14
+(0 + 3 + 5 + 7) => 15
+(1 + 2 + 4 + 6) => 13
+(1 + 2 + 4 + 7) => 14
+(1 + 2 + 5 + 6) => 14
+(1 + 2 + 5 + 7) => 15
+(1 + 3 + 4 + 6) => 14
+(1 + 3 + 4 + 7) => 15
+(1 + 3 + 5 + 6) => 15
+(1 + 3 + 5 + 7) => 16
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify5.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify5.approved.txt
new file mode 100644
index 00000000..e5f42111
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify5.approved.txt
@@ -0,0 +1,33 @@
+title
+(0 + 2 + 4 + 6 + 8) => 20
+(0 + 2 + 4 + 6 + 9) => 21
+(0 + 2 + 4 + 7 + 8) => 21
+(0 + 2 + 4 + 7 + 9) => 22
+(0 + 2 + 5 + 6 + 8) => 21
+(0 + 2 + 5 + 6 + 9) => 22
+(0 + 2 + 5 + 7 + 8) => 22
+(0 + 2 + 5 + 7 + 9) => 23
+(0 + 3 + 4 + 6 + 8) => 21
+(0 + 3 + 4 + 6 + 9) => 22
+(0 + 3 + 4 + 7 + 8) => 22
+(0 + 3 + 4 + 7 + 9) => 23
+(0 + 3 + 5 + 6 + 8) => 22
+(0 + 3 + 5 + 6 + 9) => 23
+(0 + 3 + 5 + 7 + 8) => 23
+(0 + 3 + 5 + 7 + 9) => 24
+(1 + 2 + 4 + 6 + 8) => 21
+(1 + 2 + 4 + 6 + 9) => 22
+(1 + 2 + 4 + 7 + 8) => 22
+(1 + 2 + 4 + 7 + 9) => 23
+(1 + 2 + 5 + 6 + 8) => 22
+(1 + 2 + 5 + 6 + 9) => 23
+(1 + 2 + 5 + 7 + 8) => 23
+(1 + 2 + 5 + 7 + 9) => 24
+(1 + 3 + 4 + 6 + 8) => 22
+(1 + 3 + 4 + 6 + 9) => 23
+(1 + 3 + 4 + 7 + 8) => 23
+(1 + 3 + 4 + 7 + 9) => 24
+(1 + 3 + 5 + 6 + 8) => 23
+(1 + 3 + 5 + 6 + 9) => 24
+(1 + 3 + 5 + 7 + 8) => 24
+(1 + 3 + 5 + 7 + 9) => 25
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify6.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify6.approved.txt
new file mode 100644
index 00000000..f93d3d63
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify6.approved.txt
@@ -0,0 +1,65 @@
+title
+(0 + 2 + 4 + 6 + 8 + 10) => 30
+(0 + 2 + 4 + 6 + 8 + 11) => 31
+(0 + 2 + 4 + 6 + 9 + 10) => 31
+(0 + 2 + 4 + 6 + 9 + 11) => 32
+(0 + 2 + 4 + 7 + 8 + 10) => 31
+(0 + 2 + 4 + 7 + 8 + 11) => 32
+(0 + 2 + 4 + 7 + 9 + 10) => 32
+(0 + 2 + 4 + 7 + 9 + 11) => 33
+(0 + 2 + 5 + 6 + 8 + 10) => 31
+(0 + 2 + 5 + 6 + 8 + 11) => 32
+(0 + 2 + 5 + 6 + 9 + 10) => 32
+(0 + 2 + 5 + 6 + 9 + 11) => 33
+(0 + 2 + 5 + 7 + 8 + 10) => 32
+(0 + 2 + 5 + 7 + 8 + 11) => 33
+(0 + 2 + 5 + 7 + 9 + 10) => 33
+(0 + 2 + 5 + 7 + 9 + 11) => 34
+(0 + 3 + 4 + 6 + 8 + 10) => 31
+(0 + 3 + 4 + 6 + 8 + 11) => 32
+(0 + 3 + 4 + 6 + 9 + 10) => 32
+(0 + 3 + 4 + 6 + 9 + 11) => 33
+(0 + 3 + 4 + 7 + 8 + 10) => 32
+(0 + 3 + 4 + 7 + 8 + 11) => 33
+(0 + 3 + 4 + 7 + 9 + 10) => 33
+(0 + 3 + 4 + 7 + 9 + 11) => 34
+(0 + 3 + 5 + 6 + 8 + 10) => 32
+(0 + 3 + 5 + 6 + 8 + 11) => 33
+(0 + 3 + 5 + 6 + 9 + 10) => 33
+(0 + 3 + 5 + 6 + 9 + 11) => 34
+(0 + 3 + 5 + 7 + 8 + 10) => 33
+(0 + 3 + 5 + 7 + 8 + 11) => 34
+(0 + 3 + 5 + 7 + 9 + 10) => 34
+(0 + 3 + 5 + 7 + 9 + 11) => 35
+(1 + 2 + 4 + 6 + 8 + 10) => 31
+(1 + 2 + 4 + 6 + 8 + 11) => 32
+(1 + 2 + 4 + 6 + 9 + 10) => 32
+(1 + 2 + 4 + 6 + 9 + 11) => 33
+(1 + 2 + 4 + 7 + 8 + 10) => 32
+(1 + 2 + 4 + 7 + 8 + 11) => 33
+(1 + 2 + 4 + 7 + 9 + 10) => 33
+(1 + 2 + 4 + 7 + 9 + 11) => 34
+(1 + 2 + 5 + 6 + 8 + 10) => 32
+(1 + 2 + 5 + 6 + 8 + 11) => 33
+(1 + 2 + 5 + 6 + 9 + 10) => 33
+(1 + 2 + 5 + 6 + 9 + 11) => 34
+(1 + 2 + 5 + 7 + 8 + 10) => 33
+(1 + 2 + 5 + 7 + 8 + 11) => 34
+(1 + 2 + 5 + 7 + 9 + 10) => 34
+(1 + 2 + 5 + 7 + 9 + 11) => 35
+(1 + 3 + 4 + 6 + 8 + 10) => 32
+(1 + 3 + 4 + 6 + 8 + 11) => 33
+(1 + 3 + 4 + 6 + 9 + 10) => 33
+(1 + 3 + 4 + 6 + 9 + 11) => 34
+(1 + 3 + 4 + 7 + 8 + 10) => 33
+(1 + 3 + 4 + 7 + 8 + 11) => 34
+(1 + 3 + 4 + 7 + 9 + 10) => 34
+(1 + 3 + 4 + 7 + 9 + 11) => 35
+(1 + 3 + 5 + 6 + 8 + 10) => 33
+(1 + 3 + 5 + 6 + 8 + 11) => 34
+(1 + 3 + 5 + 6 + 9 + 10) => 34
+(1 + 3 + 5 + 6 + 9 + 11) => 35
+(1 + 3 + 5 + 7 + 8 + 10) => 34
+(1 + 3 + 5 + 7 + 8 + 11) => 35
+(1 + 3 + 5 + 7 + 9 + 10) => 35
+(1 + 3 + 5 + 7 + 9 + 11) => 36
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify7.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify7.approved.txt
new file mode 100644
index 00000000..4693066a
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify7.approved.txt
@@ -0,0 +1,129 @@
+title
+(0 + 2 + 4 + 6 + 8 + 10 + 12) => 42
+(0 + 2 + 4 + 6 + 8 + 10 + 13) => 43
+(0 + 2 + 4 + 6 + 8 + 11 + 12) => 43
+(0 + 2 + 4 + 6 + 8 + 11 + 13) => 44
+(0 + 2 + 4 + 6 + 9 + 10 + 12) => 43
+(0 + 2 + 4 + 6 + 9 + 10 + 13) => 44
+(0 + 2 + 4 + 6 + 9 + 11 + 12) => 44
+(0 + 2 + 4 + 6 + 9 + 11 + 13) => 45
+(0 + 2 + 4 + 7 + 8 + 10 + 12) => 43
+(0 + 2 + 4 + 7 + 8 + 10 + 13) => 44
+(0 + 2 + 4 + 7 + 8 + 11 + 12) => 44
+(0 + 2 + 4 + 7 + 8 + 11 + 13) => 45
+(0 + 2 + 4 + 7 + 9 + 10 + 12) => 44
+(0 + 2 + 4 + 7 + 9 + 10 + 13) => 45
+(0 + 2 + 4 + 7 + 9 + 11 + 12) => 45
+(0 + 2 + 4 + 7 + 9 + 11 + 13) => 46
+(0 + 2 + 5 + 6 + 8 + 10 + 12) => 43
+(0 + 2 + 5 + 6 + 8 + 10 + 13) => 44
+(0 + 2 + 5 + 6 + 8 + 11 + 12) => 44
+(0 + 2 + 5 + 6 + 8 + 11 + 13) => 45
+(0 + 2 + 5 + 6 + 9 + 10 + 12) => 44
+(0 + 2 + 5 + 6 + 9 + 10 + 13) => 45
+(0 + 2 + 5 + 6 + 9 + 11 + 12) => 45
+(0 + 2 + 5 + 6 + 9 + 11 + 13) => 46
+(0 + 2 + 5 + 7 + 8 + 10 + 12) => 44
+(0 + 2 + 5 + 7 + 8 + 10 + 13) => 45
+(0 + 2 + 5 + 7 + 8 + 11 + 12) => 45
+(0 + 2 + 5 + 7 + 8 + 11 + 13) => 46
+(0 + 2 + 5 + 7 + 9 + 10 + 12) => 45
+(0 + 2 + 5 + 7 + 9 + 10 + 13) => 46
+(0 + 2 + 5 + 7 + 9 + 11 + 12) => 46
+(0 + 2 + 5 + 7 + 9 + 11 + 13) => 47
+(0 + 3 + 4 + 6 + 8 + 10 + 12) => 43
+(0 + 3 + 4 + 6 + 8 + 10 + 13) => 44
+(0 + 3 + 4 + 6 + 8 + 11 + 12) => 44
+(0 + 3 + 4 + 6 + 8 + 11 + 13) => 45
+(0 + 3 + 4 + 6 + 9 + 10 + 12) => 44
+(0 + 3 + 4 + 6 + 9 + 10 + 13) => 45
+(0 + 3 + 4 + 6 + 9 + 11 + 12) => 45
+(0 + 3 + 4 + 6 + 9 + 11 + 13) => 46
+(0 + 3 + 4 + 7 + 8 + 10 + 12) => 44
+(0 + 3 + 4 + 7 + 8 + 10 + 13) => 45
+(0 + 3 + 4 + 7 + 8 + 11 + 12) => 45
+(0 + 3 + 4 + 7 + 8 + 11 + 13) => 46
+(0 + 3 + 4 + 7 + 9 + 10 + 12) => 45
+(0 + 3 + 4 + 7 + 9 + 10 + 13) => 46
+(0 + 3 + 4 + 7 + 9 + 11 + 12) => 46
+(0 + 3 + 4 + 7 + 9 + 11 + 13) => 47
+(0 + 3 + 5 + 6 + 8 + 10 + 12) => 44
+(0 + 3 + 5 + 6 + 8 + 10 + 13) => 45
+(0 + 3 + 5 + 6 + 8 + 11 + 12) => 45
+(0 + 3 + 5 + 6 + 8 + 11 + 13) => 46
+(0 + 3 + 5 + 6 + 9 + 10 + 12) => 45
+(0 + 3 + 5 + 6 + 9 + 10 + 13) => 46
+(0 + 3 + 5 + 6 + 9 + 11 + 12) => 46
+(0 + 3 + 5 + 6 + 9 + 11 + 13) => 47
+(0 + 3 + 5 + 7 + 8 + 10 + 12) => 45
+(0 + 3 + 5 + 7 + 8 + 10 + 13) => 46
+(0 + 3 + 5 + 7 + 8 + 11 + 12) => 46
+(0 + 3 + 5 + 7 + 8 + 11 + 13) => 47
+(0 + 3 + 5 + 7 + 9 + 10 + 12) => 46
+(0 + 3 + 5 + 7 + 9 + 10 + 13) => 47
+(0 + 3 + 5 + 7 + 9 + 11 + 12) => 47
+(0 + 3 + 5 + 7 + 9 + 11 + 13) => 48
+(1 + 2 + 4 + 6 + 8 + 10 + 12) => 43
+(1 + 2 + 4 + 6 + 8 + 10 + 13) => 44
+(1 + 2 + 4 + 6 + 8 + 11 + 12) => 44
+(1 + 2 + 4 + 6 + 8 + 11 + 13) => 45
+(1 + 2 + 4 + 6 + 9 + 10 + 12) => 44
+(1 + 2 + 4 + 6 + 9 + 10 + 13) => 45
+(1 + 2 + 4 + 6 + 9 + 11 + 12) => 45
+(1 + 2 + 4 + 6 + 9 + 11 + 13) => 46
+(1 + 2 + 4 + 7 + 8 + 10 + 12) => 44
+(1 + 2 + 4 + 7 + 8 + 10 + 13) => 45
+(1 + 2 + 4 + 7 + 8 + 11 + 12) => 45
+(1 + 2 + 4 + 7 + 8 + 11 + 13) => 46
+(1 + 2 + 4 + 7 + 9 + 10 + 12) => 45
+(1 + 2 + 4 + 7 + 9 + 10 + 13) => 46
+(1 + 2 + 4 + 7 + 9 + 11 + 12) => 46
+(1 + 2 + 4 + 7 + 9 + 11 + 13) => 47
+(1 + 2 + 5 + 6 + 8 + 10 + 12) => 44
+(1 + 2 + 5 + 6 + 8 + 10 + 13) => 45
+(1 + 2 + 5 + 6 + 8 + 11 + 12) => 45
+(1 + 2 + 5 + 6 + 8 + 11 + 13) => 46
+(1 + 2 + 5 + 6 + 9 + 10 + 12) => 45
+(1 + 2 + 5 + 6 + 9 + 10 + 13) => 46
+(1 + 2 + 5 + 6 + 9 + 11 + 12) => 46
+(1 + 2 + 5 + 6 + 9 + 11 + 13) => 47
+(1 + 2 + 5 + 7 + 8 + 10 + 12) => 45
+(1 + 2 + 5 + 7 + 8 + 10 + 13) => 46
+(1 + 2 + 5 + 7 + 8 + 11 + 12) => 46
+(1 + 2 + 5 + 7 + 8 + 11 + 13) => 47
+(1 + 2 + 5 + 7 + 9 + 10 + 12) => 46
+(1 + 2 + 5 + 7 + 9 + 10 + 13) => 47
+(1 + 2 + 5 + 7 + 9 + 11 + 12) => 47
+(1 + 2 + 5 + 7 + 9 + 11 + 13) => 48
+(1 + 3 + 4 + 6 + 8 + 10 + 12) => 44
+(1 + 3 + 4 + 6 + 8 + 10 + 13) => 45
+(1 + 3 + 4 + 6 + 8 + 11 + 12) => 45
+(1 + 3 + 4 + 6 + 8 + 11 + 13) => 46
+(1 + 3 + 4 + 6 + 9 + 10 + 12) => 45
+(1 + 3 + 4 + 6 + 9 + 10 + 13) => 46
+(1 + 3 + 4 + 6 + 9 + 11 + 12) => 46
+(1 + 3 + 4 + 6 + 9 + 11 + 13) => 47
+(1 + 3 + 4 + 7 + 8 + 10 + 12) => 45
+(1 + 3 + 4 + 7 + 8 + 10 + 13) => 46
+(1 + 3 + 4 + 7 + 8 + 11 + 12) => 46
+(1 + 3 + 4 + 7 + 8 + 11 + 13) => 47
+(1 + 3 + 4 + 7 + 9 + 10 + 12) => 46
+(1 + 3 + 4 + 7 + 9 + 10 + 13) => 47
+(1 + 3 + 4 + 7 + 9 + 11 + 12) => 47
+(1 + 3 + 4 + 7 + 9 + 11 + 13) => 48
+(1 + 3 + 5 + 6 + 8 + 10 + 12) => 45
+(1 + 3 + 5 + 6 + 8 + 10 + 13) => 46
+(1 + 3 + 5 + 6 + 8 + 11 + 12) => 46
+(1 + 3 + 5 + 6 + 8 + 11 + 13) => 47
+(1 + 3 + 5 + 6 + 9 + 10 + 12) => 46
+(1 + 3 + 5 + 6 + 9 + 10 + 13) => 47
+(1 + 3 + 5 + 6 + 9 + 11 + 12) => 47
+(1 + 3 + 5 + 6 + 9 + 11 + 13) => 48
+(1 + 3 + 5 + 7 + 8 + 10 + 12) => 46
+(1 + 3 + 5 + 7 + 8 + 10 + 13) => 47
+(1 + 3 + 5 + 7 + 8 + 11 + 12) => 47
+(1 + 3 + 5 + 7 + 8 + 11 + 13) => 48
+(1 + 3 + 5 + 7 + 9 + 10 + 12) => 47
+(1 + 3 + 5 + 7 + 9 + 10 + 13) => 48
+(1 + 3 + 5 + 7 + 9 + 11 + 12) => 48
+(1 + 3 + 5 + 7 + 9 + 11 + 13) => 49
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify8.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify8.approved.txt
new file mode 100644
index 00000000..ac96a36d
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify8.approved.txt
@@ -0,0 +1,257 @@
+title
+(0 + 2 + 4 + 6 + 8 + 10 + 12 + 14) => 56
+(0 + 2 + 4 + 6 + 8 + 10 + 12 + 15) => 57
+(0 + 2 + 4 + 6 + 8 + 10 + 13 + 14) => 57
+(0 + 2 + 4 + 6 + 8 + 10 + 13 + 15) => 58
+(0 + 2 + 4 + 6 + 8 + 11 + 12 + 14) => 57
+(0 + 2 + 4 + 6 + 8 + 11 + 12 + 15) => 58
+(0 + 2 + 4 + 6 + 8 + 11 + 13 + 14) => 58
+(0 + 2 + 4 + 6 + 8 + 11 + 13 + 15) => 59
+(0 + 2 + 4 + 6 + 9 + 10 + 12 + 14) => 57
+(0 + 2 + 4 + 6 + 9 + 10 + 12 + 15) => 58
+(0 + 2 + 4 + 6 + 9 + 10 + 13 + 14) => 58
+(0 + 2 + 4 + 6 + 9 + 10 + 13 + 15) => 59
+(0 + 2 + 4 + 6 + 9 + 11 + 12 + 14) => 58
+(0 + 2 + 4 + 6 + 9 + 11 + 12 + 15) => 59
+(0 + 2 + 4 + 6 + 9 + 11 + 13 + 14) => 59
+(0 + 2 + 4 + 6 + 9 + 11 + 13 + 15) => 60
+(0 + 2 + 4 + 7 + 8 + 10 + 12 + 14) => 57
+(0 + 2 + 4 + 7 + 8 + 10 + 12 + 15) => 58
+(0 + 2 + 4 + 7 + 8 + 10 + 13 + 14) => 58
+(0 + 2 + 4 + 7 + 8 + 10 + 13 + 15) => 59
+(0 + 2 + 4 + 7 + 8 + 11 + 12 + 14) => 58
+(0 + 2 + 4 + 7 + 8 + 11 + 12 + 15) => 59
+(0 + 2 + 4 + 7 + 8 + 11 + 13 + 14) => 59
+(0 + 2 + 4 + 7 + 8 + 11 + 13 + 15) => 60
+(0 + 2 + 4 + 7 + 9 + 10 + 12 + 14) => 58
+(0 + 2 + 4 + 7 + 9 + 10 + 12 + 15) => 59
+(0 + 2 + 4 + 7 + 9 + 10 + 13 + 14) => 59
+(0 + 2 + 4 + 7 + 9 + 10 + 13 + 15) => 60
+(0 + 2 + 4 + 7 + 9 + 11 + 12 + 14) => 59
+(0 + 2 + 4 + 7 + 9 + 11 + 12 + 15) => 60
+(0 + 2 + 4 + 7 + 9 + 11 + 13 + 14) => 60
+(0 + 2 + 4 + 7 + 9 + 11 + 13 + 15) => 61
+(0 + 2 + 5 + 6 + 8 + 10 + 12 + 14) => 57
+(0 + 2 + 5 + 6 + 8 + 10 + 12 + 15) => 58
+(0 + 2 + 5 + 6 + 8 + 10 + 13 + 14) => 58
+(0 + 2 + 5 + 6 + 8 + 10 + 13 + 15) => 59
+(0 + 2 + 5 + 6 + 8 + 11 + 12 + 14) => 58
+(0 + 2 + 5 + 6 + 8 + 11 + 12 + 15) => 59
+(0 + 2 + 5 + 6 + 8 + 11 + 13 + 14) => 59
+(0 + 2 + 5 + 6 + 8 + 11 + 13 + 15) => 60
+(0 + 2 + 5 + 6 + 9 + 10 + 12 + 14) => 58
+(0 + 2 + 5 + 6 + 9 + 10 + 12 + 15) => 59
+(0 + 2 + 5 + 6 + 9 + 10 + 13 + 14) => 59
+(0 + 2 + 5 + 6 + 9 + 10 + 13 + 15) => 60
+(0 + 2 + 5 + 6 + 9 + 11 + 12 + 14) => 59
+(0 + 2 + 5 + 6 + 9 + 11 + 12 + 15) => 60
+(0 + 2 + 5 + 6 + 9 + 11 + 13 + 14) => 60
+(0 + 2 + 5 + 6 + 9 + 11 + 13 + 15) => 61
+(0 + 2 + 5 + 7 + 8 + 10 + 12 + 14) => 58
+(0 + 2 + 5 + 7 + 8 + 10 + 12 + 15) => 59
+(0 + 2 + 5 + 7 + 8 + 10 + 13 + 14) => 59
+(0 + 2 + 5 + 7 + 8 + 10 + 13 + 15) => 60
+(0 + 2 + 5 + 7 + 8 + 11 + 12 + 14) => 59
+(0 + 2 + 5 + 7 + 8 + 11 + 12 + 15) => 60
+(0 + 2 + 5 + 7 + 8 + 11 + 13 + 14) => 60
+(0 + 2 + 5 + 7 + 8 + 11 + 13 + 15) => 61
+(0 + 2 + 5 + 7 + 9 + 10 + 12 + 14) => 59
+(0 + 2 + 5 + 7 + 9 + 10 + 12 + 15) => 60
+(0 + 2 + 5 + 7 + 9 + 10 + 13 + 14) => 60
+(0 + 2 + 5 + 7 + 9 + 10 + 13 + 15) => 61
+(0 + 2 + 5 + 7 + 9 + 11 + 12 + 14) => 60
+(0 + 2 + 5 + 7 + 9 + 11 + 12 + 15) => 61
+(0 + 2 + 5 + 7 + 9 + 11 + 13 + 14) => 61
+(0 + 2 + 5 + 7 + 9 + 11 + 13 + 15) => 62
+(0 + 3 + 4 + 6 + 8 + 10 + 12 + 14) => 57
+(0 + 3 + 4 + 6 + 8 + 10 + 12 + 15) => 58
+(0 + 3 + 4 + 6 + 8 + 10 + 13 + 14) => 58
+(0 + 3 + 4 + 6 + 8 + 10 + 13 + 15) => 59
+(0 + 3 + 4 + 6 + 8 + 11 + 12 + 14) => 58
+(0 + 3 + 4 + 6 + 8 + 11 + 12 + 15) => 59
+(0 + 3 + 4 + 6 + 8 + 11 + 13 + 14) => 59
+(0 + 3 + 4 + 6 + 8 + 11 + 13 + 15) => 60
+(0 + 3 + 4 + 6 + 9 + 10 + 12 + 14) => 58
+(0 + 3 + 4 + 6 + 9 + 10 + 12 + 15) => 59
+(0 + 3 + 4 + 6 + 9 + 10 + 13 + 14) => 59
+(0 + 3 + 4 + 6 + 9 + 10 + 13 + 15) => 60
+(0 + 3 + 4 + 6 + 9 + 11 + 12 + 14) => 59
+(0 + 3 + 4 + 6 + 9 + 11 + 12 + 15) => 60
+(0 + 3 + 4 + 6 + 9 + 11 + 13 + 14) => 60
+(0 + 3 + 4 + 6 + 9 + 11 + 13 + 15) => 61
+(0 + 3 + 4 + 7 + 8 + 10 + 12 + 14) => 58
+(0 + 3 + 4 + 7 + 8 + 10 + 12 + 15) => 59
+(0 + 3 + 4 + 7 + 8 + 10 + 13 + 14) => 59
+(0 + 3 + 4 + 7 + 8 + 10 + 13 + 15) => 60
+(0 + 3 + 4 + 7 + 8 + 11 + 12 + 14) => 59
+(0 + 3 + 4 + 7 + 8 + 11 + 12 + 15) => 60
+(0 + 3 + 4 + 7 + 8 + 11 + 13 + 14) => 60
+(0 + 3 + 4 + 7 + 8 + 11 + 13 + 15) => 61
+(0 + 3 + 4 + 7 + 9 + 10 + 12 + 14) => 59
+(0 + 3 + 4 + 7 + 9 + 10 + 12 + 15) => 60
+(0 + 3 + 4 + 7 + 9 + 10 + 13 + 14) => 60
+(0 + 3 + 4 + 7 + 9 + 10 + 13 + 15) => 61
+(0 + 3 + 4 + 7 + 9 + 11 + 12 + 14) => 60
+(0 + 3 + 4 + 7 + 9 + 11 + 12 + 15) => 61
+(0 + 3 + 4 + 7 + 9 + 11 + 13 + 14) => 61
+(0 + 3 + 4 + 7 + 9 + 11 + 13 + 15) => 62
+(0 + 3 + 5 + 6 + 8 + 10 + 12 + 14) => 58
+(0 + 3 + 5 + 6 + 8 + 10 + 12 + 15) => 59
+(0 + 3 + 5 + 6 + 8 + 10 + 13 + 14) => 59
+(0 + 3 + 5 + 6 + 8 + 10 + 13 + 15) => 60
+(0 + 3 + 5 + 6 + 8 + 11 + 12 + 14) => 59
+(0 + 3 + 5 + 6 + 8 + 11 + 12 + 15) => 60
+(0 + 3 + 5 + 6 + 8 + 11 + 13 + 14) => 60
+(0 + 3 + 5 + 6 + 8 + 11 + 13 + 15) => 61
+(0 + 3 + 5 + 6 + 9 + 10 + 12 + 14) => 59
+(0 + 3 + 5 + 6 + 9 + 10 + 12 + 15) => 60
+(0 + 3 + 5 + 6 + 9 + 10 + 13 + 14) => 60
+(0 + 3 + 5 + 6 + 9 + 10 + 13 + 15) => 61
+(0 + 3 + 5 + 6 + 9 + 11 + 12 + 14) => 60
+(0 + 3 + 5 + 6 + 9 + 11 + 12 + 15) => 61
+(0 + 3 + 5 + 6 + 9 + 11 + 13 + 14) => 61
+(0 + 3 + 5 + 6 + 9 + 11 + 13 + 15) => 62
+(0 + 3 + 5 + 7 + 8 + 10 + 12 + 14) => 59
+(0 + 3 + 5 + 7 + 8 + 10 + 12 + 15) => 60
+(0 + 3 + 5 + 7 + 8 + 10 + 13 + 14) => 60
+(0 + 3 + 5 + 7 + 8 + 10 + 13 + 15) => 61
+(0 + 3 + 5 + 7 + 8 + 11 + 12 + 14) => 60
+(0 + 3 + 5 + 7 + 8 + 11 + 12 + 15) => 61
+(0 + 3 + 5 + 7 + 8 + 11 + 13 + 14) => 61
+(0 + 3 + 5 + 7 + 8 + 11 + 13 + 15) => 62
+(0 + 3 + 5 + 7 + 9 + 10 + 12 + 14) => 60
+(0 + 3 + 5 + 7 + 9 + 10 + 12 + 15) => 61
+(0 + 3 + 5 + 7 + 9 + 10 + 13 + 14) => 61
+(0 + 3 + 5 + 7 + 9 + 10 + 13 + 15) => 62
+(0 + 3 + 5 + 7 + 9 + 11 + 12 + 14) => 61
+(0 + 3 + 5 + 7 + 9 + 11 + 12 + 15) => 62
+(0 + 3 + 5 + 7 + 9 + 11 + 13 + 14) => 62
+(0 + 3 + 5 + 7 + 9 + 11 + 13 + 15) => 63
+(1 + 2 + 4 + 6 + 8 + 10 + 12 + 14) => 57
+(1 + 2 + 4 + 6 + 8 + 10 + 12 + 15) => 58
+(1 + 2 + 4 + 6 + 8 + 10 + 13 + 14) => 58
+(1 + 2 + 4 + 6 + 8 + 10 + 13 + 15) => 59
+(1 + 2 + 4 + 6 + 8 + 11 + 12 + 14) => 58
+(1 + 2 + 4 + 6 + 8 + 11 + 12 + 15) => 59
+(1 + 2 + 4 + 6 + 8 + 11 + 13 + 14) => 59
+(1 + 2 + 4 + 6 + 8 + 11 + 13 + 15) => 60
+(1 + 2 + 4 + 6 + 9 + 10 + 12 + 14) => 58
+(1 + 2 + 4 + 6 + 9 + 10 + 12 + 15) => 59
+(1 + 2 + 4 + 6 + 9 + 10 + 13 + 14) => 59
+(1 + 2 + 4 + 6 + 9 + 10 + 13 + 15) => 60
+(1 + 2 + 4 + 6 + 9 + 11 + 12 + 14) => 59
+(1 + 2 + 4 + 6 + 9 + 11 + 12 + 15) => 60
+(1 + 2 + 4 + 6 + 9 + 11 + 13 + 14) => 60
+(1 + 2 + 4 + 6 + 9 + 11 + 13 + 15) => 61
+(1 + 2 + 4 + 7 + 8 + 10 + 12 + 14) => 58
+(1 + 2 + 4 + 7 + 8 + 10 + 12 + 15) => 59
+(1 + 2 + 4 + 7 + 8 + 10 + 13 + 14) => 59
+(1 + 2 + 4 + 7 + 8 + 10 + 13 + 15) => 60
+(1 + 2 + 4 + 7 + 8 + 11 + 12 + 14) => 59
+(1 + 2 + 4 + 7 + 8 + 11 + 12 + 15) => 60
+(1 + 2 + 4 + 7 + 8 + 11 + 13 + 14) => 60
+(1 + 2 + 4 + 7 + 8 + 11 + 13 + 15) => 61
+(1 + 2 + 4 + 7 + 9 + 10 + 12 + 14) => 59
+(1 + 2 + 4 + 7 + 9 + 10 + 12 + 15) => 60
+(1 + 2 + 4 + 7 + 9 + 10 + 13 + 14) => 60
+(1 + 2 + 4 + 7 + 9 + 10 + 13 + 15) => 61
+(1 + 2 + 4 + 7 + 9 + 11 + 12 + 14) => 60
+(1 + 2 + 4 + 7 + 9 + 11 + 12 + 15) => 61
+(1 + 2 + 4 + 7 + 9 + 11 + 13 + 14) => 61
+(1 + 2 + 4 + 7 + 9 + 11 + 13 + 15) => 62
+(1 + 2 + 5 + 6 + 8 + 10 + 12 + 14) => 58
+(1 + 2 + 5 + 6 + 8 + 10 + 12 + 15) => 59
+(1 + 2 + 5 + 6 + 8 + 10 + 13 + 14) => 59
+(1 + 2 + 5 + 6 + 8 + 10 + 13 + 15) => 60
+(1 + 2 + 5 + 6 + 8 + 11 + 12 + 14) => 59
+(1 + 2 + 5 + 6 + 8 + 11 + 12 + 15) => 60
+(1 + 2 + 5 + 6 + 8 + 11 + 13 + 14) => 60
+(1 + 2 + 5 + 6 + 8 + 11 + 13 + 15) => 61
+(1 + 2 + 5 + 6 + 9 + 10 + 12 + 14) => 59
+(1 + 2 + 5 + 6 + 9 + 10 + 12 + 15) => 60
+(1 + 2 + 5 + 6 + 9 + 10 + 13 + 14) => 60
+(1 + 2 + 5 + 6 + 9 + 10 + 13 + 15) => 61
+(1 + 2 + 5 + 6 + 9 + 11 + 12 + 14) => 60
+(1 + 2 + 5 + 6 + 9 + 11 + 12 + 15) => 61
+(1 + 2 + 5 + 6 + 9 + 11 + 13 + 14) => 61
+(1 + 2 + 5 + 6 + 9 + 11 + 13 + 15) => 62
+(1 + 2 + 5 + 7 + 8 + 10 + 12 + 14) => 59
+(1 + 2 + 5 + 7 + 8 + 10 + 12 + 15) => 60
+(1 + 2 + 5 + 7 + 8 + 10 + 13 + 14) => 60
+(1 + 2 + 5 + 7 + 8 + 10 + 13 + 15) => 61
+(1 + 2 + 5 + 7 + 8 + 11 + 12 + 14) => 60
+(1 + 2 + 5 + 7 + 8 + 11 + 12 + 15) => 61
+(1 + 2 + 5 + 7 + 8 + 11 + 13 + 14) => 61
+(1 + 2 + 5 + 7 + 8 + 11 + 13 + 15) => 62
+(1 + 2 + 5 + 7 + 9 + 10 + 12 + 14) => 60
+(1 + 2 + 5 + 7 + 9 + 10 + 12 + 15) => 61
+(1 + 2 + 5 + 7 + 9 + 10 + 13 + 14) => 61
+(1 + 2 + 5 + 7 + 9 + 10 + 13 + 15) => 62
+(1 + 2 + 5 + 7 + 9 + 11 + 12 + 14) => 61
+(1 + 2 + 5 + 7 + 9 + 11 + 12 + 15) => 62
+(1 + 2 + 5 + 7 + 9 + 11 + 13 + 14) => 62
+(1 + 2 + 5 + 7 + 9 + 11 + 13 + 15) => 63
+(1 + 3 + 4 + 6 + 8 + 10 + 12 + 14) => 58
+(1 + 3 + 4 + 6 + 8 + 10 + 12 + 15) => 59
+(1 + 3 + 4 + 6 + 8 + 10 + 13 + 14) => 59
+(1 + 3 + 4 + 6 + 8 + 10 + 13 + 15) => 60
+(1 + 3 + 4 + 6 + 8 + 11 + 12 + 14) => 59
+(1 + 3 + 4 + 6 + 8 + 11 + 12 + 15) => 60
+(1 + 3 + 4 + 6 + 8 + 11 + 13 + 14) => 60
+(1 + 3 + 4 + 6 + 8 + 11 + 13 + 15) => 61
+(1 + 3 + 4 + 6 + 9 + 10 + 12 + 14) => 59
+(1 + 3 + 4 + 6 + 9 + 10 + 12 + 15) => 60
+(1 + 3 + 4 + 6 + 9 + 10 + 13 + 14) => 60
+(1 + 3 + 4 + 6 + 9 + 10 + 13 + 15) => 61
+(1 + 3 + 4 + 6 + 9 + 11 + 12 + 14) => 60
+(1 + 3 + 4 + 6 + 9 + 11 + 12 + 15) => 61
+(1 + 3 + 4 + 6 + 9 + 11 + 13 + 14) => 61
+(1 + 3 + 4 + 6 + 9 + 11 + 13 + 15) => 62
+(1 + 3 + 4 + 7 + 8 + 10 + 12 + 14) => 59
+(1 + 3 + 4 + 7 + 8 + 10 + 12 + 15) => 60
+(1 + 3 + 4 + 7 + 8 + 10 + 13 + 14) => 60
+(1 + 3 + 4 + 7 + 8 + 10 + 13 + 15) => 61
+(1 + 3 + 4 + 7 + 8 + 11 + 12 + 14) => 60
+(1 + 3 + 4 + 7 + 8 + 11 + 12 + 15) => 61
+(1 + 3 + 4 + 7 + 8 + 11 + 13 + 14) => 61
+(1 + 3 + 4 + 7 + 8 + 11 + 13 + 15) => 62
+(1 + 3 + 4 + 7 + 9 + 10 + 12 + 14) => 60
+(1 + 3 + 4 + 7 + 9 + 10 + 12 + 15) => 61
+(1 + 3 + 4 + 7 + 9 + 10 + 13 + 14) => 61
+(1 + 3 + 4 + 7 + 9 + 10 + 13 + 15) => 62
+(1 + 3 + 4 + 7 + 9 + 11 + 12 + 14) => 61
+(1 + 3 + 4 + 7 + 9 + 11 + 12 + 15) => 62
+(1 + 3 + 4 + 7 + 9 + 11 + 13 + 14) => 62
+(1 + 3 + 4 + 7 + 9 + 11 + 13 + 15) => 63
+(1 + 3 + 5 + 6 + 8 + 10 + 12 + 14) => 59
+(1 + 3 + 5 + 6 + 8 + 10 + 12 + 15) => 60
+(1 + 3 + 5 + 6 + 8 + 10 + 13 + 14) => 60
+(1 + 3 + 5 + 6 + 8 + 10 + 13 + 15) => 61
+(1 + 3 + 5 + 6 + 8 + 11 + 12 + 14) => 60
+(1 + 3 + 5 + 6 + 8 + 11 + 12 + 15) => 61
+(1 + 3 + 5 + 6 + 8 + 11 + 13 + 14) => 61
+(1 + 3 + 5 + 6 + 8 + 11 + 13 + 15) => 62
+(1 + 3 + 5 + 6 + 9 + 10 + 12 + 14) => 60
+(1 + 3 + 5 + 6 + 9 + 10 + 12 + 15) => 61
+(1 + 3 + 5 + 6 + 9 + 10 + 13 + 14) => 61
+(1 + 3 + 5 + 6 + 9 + 10 + 13 + 15) => 62
+(1 + 3 + 5 + 6 + 9 + 11 + 12 + 14) => 61
+(1 + 3 + 5 + 6 + 9 + 11 + 12 + 15) => 62
+(1 + 3 + 5 + 6 + 9 + 11 + 13 + 14) => 62
+(1 + 3 + 5 + 6 + 9 + 11 + 13 + 15) => 63
+(1 + 3 + 5 + 7 + 8 + 10 + 12 + 14) => 60
+(1 + 3 + 5 + 7 + 8 + 10 + 12 + 15) => 61
+(1 + 3 + 5 + 7 + 8 + 10 + 13 + 14) => 61
+(1 + 3 + 5 + 7 + 8 + 10 + 13 + 15) => 62
+(1 + 3 + 5 + 7 + 8 + 11 + 12 + 14) => 61
+(1 + 3 + 5 + 7 + 8 + 11 + 12 + 15) => 62
+(1 + 3 + 5 + 7 + 8 + 11 + 13 + 14) => 62
+(1 + 3 + 5 + 7 + 8 + 11 + 13 + 15) => 63
+(1 + 3 + 5 + 7 + 9 + 10 + 12 + 14) => 61
+(1 + 3 + 5 + 7 + 9 + 10 + 12 + 15) => 62
+(1 + 3 + 5 + 7 + 9 + 10 + 13 + 14) => 62
+(1 + 3 + 5 + 7 + 9 + 10 + 13 + 15) => 63
+(1 + 3 + 5 + 7 + 9 + 11 + 12 + 14) => 62
+(1 + 3 + 5 + 7 + 9 + 11 + 12 + 15) => 63
+(1 + 3 + 5 + 7 + 9 + 11 + 13 + 14) => 63
+(1 + 3 + 5 + 7 + 9 + 11 + 13 + 15) => 64
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify9.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify9.approved.txt
new file mode 100644
index 00000000..29dbee8b
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.CombinationApprovalsAsync_verify9.approved.txt
@@ -0,0 +1,513 @@
+title
+(0 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + 16) => 72
+(0 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + 17) => 73
+(0 + 2 + 4 + 6 + 8 + 10 + 12 + 15 + 16) => 73
+(0 + 2 + 4 + 6 + 8 + 10 + 12 + 15 + 17) => 74
+(0 + 2 + 4 + 6 + 8 + 10 + 13 + 14 + 16) => 73
+(0 + 2 + 4 + 6 + 8 + 10 + 13 + 14 + 17) => 74
+(0 + 2 + 4 + 6 + 8 + 10 + 13 + 15 + 16) => 74
+(0 + 2 + 4 + 6 + 8 + 10 + 13 + 15 + 17) => 75
+(0 + 2 + 4 + 6 + 8 + 11 + 12 + 14 + 16) => 73
+(0 + 2 + 4 + 6 + 8 + 11 + 12 + 14 + 17) => 74
+(0 + 2 + 4 + 6 + 8 + 11 + 12 + 15 + 16) => 74
+(0 + 2 + 4 + 6 + 8 + 11 + 12 + 15 + 17) => 75
+(0 + 2 + 4 + 6 + 8 + 11 + 13 + 14 + 16) => 74
+(0 + 2 + 4 + 6 + 8 + 11 + 13 + 14 + 17) => 75
+(0 + 2 + 4 + 6 + 8 + 11 + 13 + 15 + 16) => 75
+(0 + 2 + 4 + 6 + 8 + 11 + 13 + 15 + 17) => 76
+(0 + 2 + 4 + 6 + 9 + 10 + 12 + 14 + 16) => 73
+(0 + 2 + 4 + 6 + 9 + 10 + 12 + 14 + 17) => 74
+(0 + 2 + 4 + 6 + 9 + 10 + 12 + 15 + 16) => 74
+(0 + 2 + 4 + 6 + 9 + 10 + 12 + 15 + 17) => 75
+(0 + 2 + 4 + 6 + 9 + 10 + 13 + 14 + 16) => 74
+(0 + 2 + 4 + 6 + 9 + 10 + 13 + 14 + 17) => 75
+(0 + 2 + 4 + 6 + 9 + 10 + 13 + 15 + 16) => 75
+(0 + 2 + 4 + 6 + 9 + 10 + 13 + 15 + 17) => 76
+(0 + 2 + 4 + 6 + 9 + 11 + 12 + 14 + 16) => 74
+(0 + 2 + 4 + 6 + 9 + 11 + 12 + 14 + 17) => 75
+(0 + 2 + 4 + 6 + 9 + 11 + 12 + 15 + 16) => 75
+(0 + 2 + 4 + 6 + 9 + 11 + 12 + 15 + 17) => 76
+(0 + 2 + 4 + 6 + 9 + 11 + 13 + 14 + 16) => 75
+(0 + 2 + 4 + 6 + 9 + 11 + 13 + 14 + 17) => 76
+(0 + 2 + 4 + 6 + 9 + 11 + 13 + 15 + 16) => 76
+(0 + 2 + 4 + 6 + 9 + 11 + 13 + 15 + 17) => 77
+(0 + 2 + 4 + 7 + 8 + 10 + 12 + 14 + 16) => 73
+(0 + 2 + 4 + 7 + 8 + 10 + 12 + 14 + 17) => 74
+(0 + 2 + 4 + 7 + 8 + 10 + 12 + 15 + 16) => 74
+(0 + 2 + 4 + 7 + 8 + 10 + 12 + 15 + 17) => 75
+(0 + 2 + 4 + 7 + 8 + 10 + 13 + 14 + 16) => 74
+(0 + 2 + 4 + 7 + 8 + 10 + 13 + 14 + 17) => 75
+(0 + 2 + 4 + 7 + 8 + 10 + 13 + 15 + 16) => 75
+(0 + 2 + 4 + 7 + 8 + 10 + 13 + 15 + 17) => 76
+(0 + 2 + 4 + 7 + 8 + 11 + 12 + 14 + 16) => 74
+(0 + 2 + 4 + 7 + 8 + 11 + 12 + 14 + 17) => 75
+(0 + 2 + 4 + 7 + 8 + 11 + 12 + 15 + 16) => 75
+(0 + 2 + 4 + 7 + 8 + 11 + 12 + 15 + 17) => 76
+(0 + 2 + 4 + 7 + 8 + 11 + 13 + 14 + 16) => 75
+(0 + 2 + 4 + 7 + 8 + 11 + 13 + 14 + 17) => 76
+(0 + 2 + 4 + 7 + 8 + 11 + 13 + 15 + 16) => 76
+(0 + 2 + 4 + 7 + 8 + 11 + 13 + 15 + 17) => 77
+(0 + 2 + 4 + 7 + 9 + 10 + 12 + 14 + 16) => 74
+(0 + 2 + 4 + 7 + 9 + 10 + 12 + 14 + 17) => 75
+(0 + 2 + 4 + 7 + 9 + 10 + 12 + 15 + 16) => 75
+(0 + 2 + 4 + 7 + 9 + 10 + 12 + 15 + 17) => 76
+(0 + 2 + 4 + 7 + 9 + 10 + 13 + 14 + 16) => 75
+(0 + 2 + 4 + 7 + 9 + 10 + 13 + 14 + 17) => 76
+(0 + 2 + 4 + 7 + 9 + 10 + 13 + 15 + 16) => 76
+(0 + 2 + 4 + 7 + 9 + 10 + 13 + 15 + 17) => 77
+(0 + 2 + 4 + 7 + 9 + 11 + 12 + 14 + 16) => 75
+(0 + 2 + 4 + 7 + 9 + 11 + 12 + 14 + 17) => 76
+(0 + 2 + 4 + 7 + 9 + 11 + 12 + 15 + 16) => 76
+(0 + 2 + 4 + 7 + 9 + 11 + 12 + 15 + 17) => 77
+(0 + 2 + 4 + 7 + 9 + 11 + 13 + 14 + 16) => 76
+(0 + 2 + 4 + 7 + 9 + 11 + 13 + 14 + 17) => 77
+(0 + 2 + 4 + 7 + 9 + 11 + 13 + 15 + 16) => 77
+(0 + 2 + 4 + 7 + 9 + 11 + 13 + 15 + 17) => 78
+(0 + 2 + 5 + 6 + 8 + 10 + 12 + 14 + 16) => 73
+(0 + 2 + 5 + 6 + 8 + 10 + 12 + 14 + 17) => 74
+(0 + 2 + 5 + 6 + 8 + 10 + 12 + 15 + 16) => 74
+(0 + 2 + 5 + 6 + 8 + 10 + 12 + 15 + 17) => 75
+(0 + 2 + 5 + 6 + 8 + 10 + 13 + 14 + 16) => 74
+(0 + 2 + 5 + 6 + 8 + 10 + 13 + 14 + 17) => 75
+(0 + 2 + 5 + 6 + 8 + 10 + 13 + 15 + 16) => 75
+(0 + 2 + 5 + 6 + 8 + 10 + 13 + 15 + 17) => 76
+(0 + 2 + 5 + 6 + 8 + 11 + 12 + 14 + 16) => 74
+(0 + 2 + 5 + 6 + 8 + 11 + 12 + 14 + 17) => 75
+(0 + 2 + 5 + 6 + 8 + 11 + 12 + 15 + 16) => 75
+(0 + 2 + 5 + 6 + 8 + 11 + 12 + 15 + 17) => 76
+(0 + 2 + 5 + 6 + 8 + 11 + 13 + 14 + 16) => 75
+(0 + 2 + 5 + 6 + 8 + 11 + 13 + 14 + 17) => 76
+(0 + 2 + 5 + 6 + 8 + 11 + 13 + 15 + 16) => 76
+(0 + 2 + 5 + 6 + 8 + 11 + 13 + 15 + 17) => 77
+(0 + 2 + 5 + 6 + 9 + 10 + 12 + 14 + 16) => 74
+(0 + 2 + 5 + 6 + 9 + 10 + 12 + 14 + 17) => 75
+(0 + 2 + 5 + 6 + 9 + 10 + 12 + 15 + 16) => 75
+(0 + 2 + 5 + 6 + 9 + 10 + 12 + 15 + 17) => 76
+(0 + 2 + 5 + 6 + 9 + 10 + 13 + 14 + 16) => 75
+(0 + 2 + 5 + 6 + 9 + 10 + 13 + 14 + 17) => 76
+(0 + 2 + 5 + 6 + 9 + 10 + 13 + 15 + 16) => 76
+(0 + 2 + 5 + 6 + 9 + 10 + 13 + 15 + 17) => 77
+(0 + 2 + 5 + 6 + 9 + 11 + 12 + 14 + 16) => 75
+(0 + 2 + 5 + 6 + 9 + 11 + 12 + 14 + 17) => 76
+(0 + 2 + 5 + 6 + 9 + 11 + 12 + 15 + 16) => 76
+(0 + 2 + 5 + 6 + 9 + 11 + 12 + 15 + 17) => 77
+(0 + 2 + 5 + 6 + 9 + 11 + 13 + 14 + 16) => 76
+(0 + 2 + 5 + 6 + 9 + 11 + 13 + 14 + 17) => 77
+(0 + 2 + 5 + 6 + 9 + 11 + 13 + 15 + 16) => 77
+(0 + 2 + 5 + 6 + 9 + 11 + 13 + 15 + 17) => 78
+(0 + 2 + 5 + 7 + 8 + 10 + 12 + 14 + 16) => 74
+(0 + 2 + 5 + 7 + 8 + 10 + 12 + 14 + 17) => 75
+(0 + 2 + 5 + 7 + 8 + 10 + 12 + 15 + 16) => 75
+(0 + 2 + 5 + 7 + 8 + 10 + 12 + 15 + 17) => 76
+(0 + 2 + 5 + 7 + 8 + 10 + 13 + 14 + 16) => 75
+(0 + 2 + 5 + 7 + 8 + 10 + 13 + 14 + 17) => 76
+(0 + 2 + 5 + 7 + 8 + 10 + 13 + 15 + 16) => 76
+(0 + 2 + 5 + 7 + 8 + 10 + 13 + 15 + 17) => 77
+(0 + 2 + 5 + 7 + 8 + 11 + 12 + 14 + 16) => 75
+(0 + 2 + 5 + 7 + 8 + 11 + 12 + 14 + 17) => 76
+(0 + 2 + 5 + 7 + 8 + 11 + 12 + 15 + 16) => 76
+(0 + 2 + 5 + 7 + 8 + 11 + 12 + 15 + 17) => 77
+(0 + 2 + 5 + 7 + 8 + 11 + 13 + 14 + 16) => 76
+(0 + 2 + 5 + 7 + 8 + 11 + 13 + 14 + 17) => 77
+(0 + 2 + 5 + 7 + 8 + 11 + 13 + 15 + 16) => 77
+(0 + 2 + 5 + 7 + 8 + 11 + 13 + 15 + 17) => 78
+(0 + 2 + 5 + 7 + 9 + 10 + 12 + 14 + 16) => 75
+(0 + 2 + 5 + 7 + 9 + 10 + 12 + 14 + 17) => 76
+(0 + 2 + 5 + 7 + 9 + 10 + 12 + 15 + 16) => 76
+(0 + 2 + 5 + 7 + 9 + 10 + 12 + 15 + 17) => 77
+(0 + 2 + 5 + 7 + 9 + 10 + 13 + 14 + 16) => 76
+(0 + 2 + 5 + 7 + 9 + 10 + 13 + 14 + 17) => 77
+(0 + 2 + 5 + 7 + 9 + 10 + 13 + 15 + 16) => 77
+(0 + 2 + 5 + 7 + 9 + 10 + 13 + 15 + 17) => 78
+(0 + 2 + 5 + 7 + 9 + 11 + 12 + 14 + 16) => 76
+(0 + 2 + 5 + 7 + 9 + 11 + 12 + 14 + 17) => 77
+(0 + 2 + 5 + 7 + 9 + 11 + 12 + 15 + 16) => 77
+(0 + 2 + 5 + 7 + 9 + 11 + 12 + 15 + 17) => 78
+(0 + 2 + 5 + 7 + 9 + 11 + 13 + 14 + 16) => 77
+(0 + 2 + 5 + 7 + 9 + 11 + 13 + 14 + 17) => 78
+(0 + 2 + 5 + 7 + 9 + 11 + 13 + 15 + 16) => 78
+(0 + 2 + 5 + 7 + 9 + 11 + 13 + 15 + 17) => 79
+(0 + 3 + 4 + 6 + 8 + 10 + 12 + 14 + 16) => 73
+(0 + 3 + 4 + 6 + 8 + 10 + 12 + 14 + 17) => 74
+(0 + 3 + 4 + 6 + 8 + 10 + 12 + 15 + 16) => 74
+(0 + 3 + 4 + 6 + 8 + 10 + 12 + 15 + 17) => 75
+(0 + 3 + 4 + 6 + 8 + 10 + 13 + 14 + 16) => 74
+(0 + 3 + 4 + 6 + 8 + 10 + 13 + 14 + 17) => 75
+(0 + 3 + 4 + 6 + 8 + 10 + 13 + 15 + 16) => 75
+(0 + 3 + 4 + 6 + 8 + 10 + 13 + 15 + 17) => 76
+(0 + 3 + 4 + 6 + 8 + 11 + 12 + 14 + 16) => 74
+(0 + 3 + 4 + 6 + 8 + 11 + 12 + 14 + 17) => 75
+(0 + 3 + 4 + 6 + 8 + 11 + 12 + 15 + 16) => 75
+(0 + 3 + 4 + 6 + 8 + 11 + 12 + 15 + 17) => 76
+(0 + 3 + 4 + 6 + 8 + 11 + 13 + 14 + 16) => 75
+(0 + 3 + 4 + 6 + 8 + 11 + 13 + 14 + 17) => 76
+(0 + 3 + 4 + 6 + 8 + 11 + 13 + 15 + 16) => 76
+(0 + 3 + 4 + 6 + 8 + 11 + 13 + 15 + 17) => 77
+(0 + 3 + 4 + 6 + 9 + 10 + 12 + 14 + 16) => 74
+(0 + 3 + 4 + 6 + 9 + 10 + 12 + 14 + 17) => 75
+(0 + 3 + 4 + 6 + 9 + 10 + 12 + 15 + 16) => 75
+(0 + 3 + 4 + 6 + 9 + 10 + 12 + 15 + 17) => 76
+(0 + 3 + 4 + 6 + 9 + 10 + 13 + 14 + 16) => 75
+(0 + 3 + 4 + 6 + 9 + 10 + 13 + 14 + 17) => 76
+(0 + 3 + 4 + 6 + 9 + 10 + 13 + 15 + 16) => 76
+(0 + 3 + 4 + 6 + 9 + 10 + 13 + 15 + 17) => 77
+(0 + 3 + 4 + 6 + 9 + 11 + 12 + 14 + 16) => 75
+(0 + 3 + 4 + 6 + 9 + 11 + 12 + 14 + 17) => 76
+(0 + 3 + 4 + 6 + 9 + 11 + 12 + 15 + 16) => 76
+(0 + 3 + 4 + 6 + 9 + 11 + 12 + 15 + 17) => 77
+(0 + 3 + 4 + 6 + 9 + 11 + 13 + 14 + 16) => 76
+(0 + 3 + 4 + 6 + 9 + 11 + 13 + 14 + 17) => 77
+(0 + 3 + 4 + 6 + 9 + 11 + 13 + 15 + 16) => 77
+(0 + 3 + 4 + 6 + 9 + 11 + 13 + 15 + 17) => 78
+(0 + 3 + 4 + 7 + 8 + 10 + 12 + 14 + 16) => 74
+(0 + 3 + 4 + 7 + 8 + 10 + 12 + 14 + 17) => 75
+(0 + 3 + 4 + 7 + 8 + 10 + 12 + 15 + 16) => 75
+(0 + 3 + 4 + 7 + 8 + 10 + 12 + 15 + 17) => 76
+(0 + 3 + 4 + 7 + 8 + 10 + 13 + 14 + 16) => 75
+(0 + 3 + 4 + 7 + 8 + 10 + 13 + 14 + 17) => 76
+(0 + 3 + 4 + 7 + 8 + 10 + 13 + 15 + 16) => 76
+(0 + 3 + 4 + 7 + 8 + 10 + 13 + 15 + 17) => 77
+(0 + 3 + 4 + 7 + 8 + 11 + 12 + 14 + 16) => 75
+(0 + 3 + 4 + 7 + 8 + 11 + 12 + 14 + 17) => 76
+(0 + 3 + 4 + 7 + 8 + 11 + 12 + 15 + 16) => 76
+(0 + 3 + 4 + 7 + 8 + 11 + 12 + 15 + 17) => 77
+(0 + 3 + 4 + 7 + 8 + 11 + 13 + 14 + 16) => 76
+(0 + 3 + 4 + 7 + 8 + 11 + 13 + 14 + 17) => 77
+(0 + 3 + 4 + 7 + 8 + 11 + 13 + 15 + 16) => 77
+(0 + 3 + 4 + 7 + 8 + 11 + 13 + 15 + 17) => 78
+(0 + 3 + 4 + 7 + 9 + 10 + 12 + 14 + 16) => 75
+(0 + 3 + 4 + 7 + 9 + 10 + 12 + 14 + 17) => 76
+(0 + 3 + 4 + 7 + 9 + 10 + 12 + 15 + 16) => 76
+(0 + 3 + 4 + 7 + 9 + 10 + 12 + 15 + 17) => 77
+(0 + 3 + 4 + 7 + 9 + 10 + 13 + 14 + 16) => 76
+(0 + 3 + 4 + 7 + 9 + 10 + 13 + 14 + 17) => 77
+(0 + 3 + 4 + 7 + 9 + 10 + 13 + 15 + 16) => 77
+(0 + 3 + 4 + 7 + 9 + 10 + 13 + 15 + 17) => 78
+(0 + 3 + 4 + 7 + 9 + 11 + 12 + 14 + 16) => 76
+(0 + 3 + 4 + 7 + 9 + 11 + 12 + 14 + 17) => 77
+(0 + 3 + 4 + 7 + 9 + 11 + 12 + 15 + 16) => 77
+(0 + 3 + 4 + 7 + 9 + 11 + 12 + 15 + 17) => 78
+(0 + 3 + 4 + 7 + 9 + 11 + 13 + 14 + 16) => 77
+(0 + 3 + 4 + 7 + 9 + 11 + 13 + 14 + 17) => 78
+(0 + 3 + 4 + 7 + 9 + 11 + 13 + 15 + 16) => 78
+(0 + 3 + 4 + 7 + 9 + 11 + 13 + 15 + 17) => 79
+(0 + 3 + 5 + 6 + 8 + 10 + 12 + 14 + 16) => 74
+(0 + 3 + 5 + 6 + 8 + 10 + 12 + 14 + 17) => 75
+(0 + 3 + 5 + 6 + 8 + 10 + 12 + 15 + 16) => 75
+(0 + 3 + 5 + 6 + 8 + 10 + 12 + 15 + 17) => 76
+(0 + 3 + 5 + 6 + 8 + 10 + 13 + 14 + 16) => 75
+(0 + 3 + 5 + 6 + 8 + 10 + 13 + 14 + 17) => 76
+(0 + 3 + 5 + 6 + 8 + 10 + 13 + 15 + 16) => 76
+(0 + 3 + 5 + 6 + 8 + 10 + 13 + 15 + 17) => 77
+(0 + 3 + 5 + 6 + 8 + 11 + 12 + 14 + 16) => 75
+(0 + 3 + 5 + 6 + 8 + 11 + 12 + 14 + 17) => 76
+(0 + 3 + 5 + 6 + 8 + 11 + 12 + 15 + 16) => 76
+(0 + 3 + 5 + 6 + 8 + 11 + 12 + 15 + 17) => 77
+(0 + 3 + 5 + 6 + 8 + 11 + 13 + 14 + 16) => 76
+(0 + 3 + 5 + 6 + 8 + 11 + 13 + 14 + 17) => 77
+(0 + 3 + 5 + 6 + 8 + 11 + 13 + 15 + 16) => 77
+(0 + 3 + 5 + 6 + 8 + 11 + 13 + 15 + 17) => 78
+(0 + 3 + 5 + 6 + 9 + 10 + 12 + 14 + 16) => 75
+(0 + 3 + 5 + 6 + 9 + 10 + 12 + 14 + 17) => 76
+(0 + 3 + 5 + 6 + 9 + 10 + 12 + 15 + 16) => 76
+(0 + 3 + 5 + 6 + 9 + 10 + 12 + 15 + 17) => 77
+(0 + 3 + 5 + 6 + 9 + 10 + 13 + 14 + 16) => 76
+(0 + 3 + 5 + 6 + 9 + 10 + 13 + 14 + 17) => 77
+(0 + 3 + 5 + 6 + 9 + 10 + 13 + 15 + 16) => 77
+(0 + 3 + 5 + 6 + 9 + 10 + 13 + 15 + 17) => 78
+(0 + 3 + 5 + 6 + 9 + 11 + 12 + 14 + 16) => 76
+(0 + 3 + 5 + 6 + 9 + 11 + 12 + 14 + 17) => 77
+(0 + 3 + 5 + 6 + 9 + 11 + 12 + 15 + 16) => 77
+(0 + 3 + 5 + 6 + 9 + 11 + 12 + 15 + 17) => 78
+(0 + 3 + 5 + 6 + 9 + 11 + 13 + 14 + 16) => 77
+(0 + 3 + 5 + 6 + 9 + 11 + 13 + 14 + 17) => 78
+(0 + 3 + 5 + 6 + 9 + 11 + 13 + 15 + 16) => 78
+(0 + 3 + 5 + 6 + 9 + 11 + 13 + 15 + 17) => 79
+(0 + 3 + 5 + 7 + 8 + 10 + 12 + 14 + 16) => 75
+(0 + 3 + 5 + 7 + 8 + 10 + 12 + 14 + 17) => 76
+(0 + 3 + 5 + 7 + 8 + 10 + 12 + 15 + 16) => 76
+(0 + 3 + 5 + 7 + 8 + 10 + 12 + 15 + 17) => 77
+(0 + 3 + 5 + 7 + 8 + 10 + 13 + 14 + 16) => 76
+(0 + 3 + 5 + 7 + 8 + 10 + 13 + 14 + 17) => 77
+(0 + 3 + 5 + 7 + 8 + 10 + 13 + 15 + 16) => 77
+(0 + 3 + 5 + 7 + 8 + 10 + 13 + 15 + 17) => 78
+(0 + 3 + 5 + 7 + 8 + 11 + 12 + 14 + 16) => 76
+(0 + 3 + 5 + 7 + 8 + 11 + 12 + 14 + 17) => 77
+(0 + 3 + 5 + 7 + 8 + 11 + 12 + 15 + 16) => 77
+(0 + 3 + 5 + 7 + 8 + 11 + 12 + 15 + 17) => 78
+(0 + 3 + 5 + 7 + 8 + 11 + 13 + 14 + 16) => 77
+(0 + 3 + 5 + 7 + 8 + 11 + 13 + 14 + 17) => 78
+(0 + 3 + 5 + 7 + 8 + 11 + 13 + 15 + 16) => 78
+(0 + 3 + 5 + 7 + 8 + 11 + 13 + 15 + 17) => 79
+(0 + 3 + 5 + 7 + 9 + 10 + 12 + 14 + 16) => 76
+(0 + 3 + 5 + 7 + 9 + 10 + 12 + 14 + 17) => 77
+(0 + 3 + 5 + 7 + 9 + 10 + 12 + 15 + 16) => 77
+(0 + 3 + 5 + 7 + 9 + 10 + 12 + 15 + 17) => 78
+(0 + 3 + 5 + 7 + 9 + 10 + 13 + 14 + 16) => 77
+(0 + 3 + 5 + 7 + 9 + 10 + 13 + 14 + 17) => 78
+(0 + 3 + 5 + 7 + 9 + 10 + 13 + 15 + 16) => 78
+(0 + 3 + 5 + 7 + 9 + 10 + 13 + 15 + 17) => 79
+(0 + 3 + 5 + 7 + 9 + 11 + 12 + 14 + 16) => 77
+(0 + 3 + 5 + 7 + 9 + 11 + 12 + 14 + 17) => 78
+(0 + 3 + 5 + 7 + 9 + 11 + 12 + 15 + 16) => 78
+(0 + 3 + 5 + 7 + 9 + 11 + 12 + 15 + 17) => 79
+(0 + 3 + 5 + 7 + 9 + 11 + 13 + 14 + 16) => 78
+(0 + 3 + 5 + 7 + 9 + 11 + 13 + 14 + 17) => 79
+(0 + 3 + 5 + 7 + 9 + 11 + 13 + 15 + 16) => 79
+(0 + 3 + 5 + 7 + 9 + 11 + 13 + 15 + 17) => 80
+(1 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + 16) => 73
+(1 + 2 + 4 + 6 + 8 + 10 + 12 + 14 + 17) => 74
+(1 + 2 + 4 + 6 + 8 + 10 + 12 + 15 + 16) => 74
+(1 + 2 + 4 + 6 + 8 + 10 + 12 + 15 + 17) => 75
+(1 + 2 + 4 + 6 + 8 + 10 + 13 + 14 + 16) => 74
+(1 + 2 + 4 + 6 + 8 + 10 + 13 + 14 + 17) => 75
+(1 + 2 + 4 + 6 + 8 + 10 + 13 + 15 + 16) => 75
+(1 + 2 + 4 + 6 + 8 + 10 + 13 + 15 + 17) => 76
+(1 + 2 + 4 + 6 + 8 + 11 + 12 + 14 + 16) => 74
+(1 + 2 + 4 + 6 + 8 + 11 + 12 + 14 + 17) => 75
+(1 + 2 + 4 + 6 + 8 + 11 + 12 + 15 + 16) => 75
+(1 + 2 + 4 + 6 + 8 + 11 + 12 + 15 + 17) => 76
+(1 + 2 + 4 + 6 + 8 + 11 + 13 + 14 + 16) => 75
+(1 + 2 + 4 + 6 + 8 + 11 + 13 + 14 + 17) => 76
+(1 + 2 + 4 + 6 + 8 + 11 + 13 + 15 + 16) => 76
+(1 + 2 + 4 + 6 + 8 + 11 + 13 + 15 + 17) => 77
+(1 + 2 + 4 + 6 + 9 + 10 + 12 + 14 + 16) => 74
+(1 + 2 + 4 + 6 + 9 + 10 + 12 + 14 + 17) => 75
+(1 + 2 + 4 + 6 + 9 + 10 + 12 + 15 + 16) => 75
+(1 + 2 + 4 + 6 + 9 + 10 + 12 + 15 + 17) => 76
+(1 + 2 + 4 + 6 + 9 + 10 + 13 + 14 + 16) => 75
+(1 + 2 + 4 + 6 + 9 + 10 + 13 + 14 + 17) => 76
+(1 + 2 + 4 + 6 + 9 + 10 + 13 + 15 + 16) => 76
+(1 + 2 + 4 + 6 + 9 + 10 + 13 + 15 + 17) => 77
+(1 + 2 + 4 + 6 + 9 + 11 + 12 + 14 + 16) => 75
+(1 + 2 + 4 + 6 + 9 + 11 + 12 + 14 + 17) => 76
+(1 + 2 + 4 + 6 + 9 + 11 + 12 + 15 + 16) => 76
+(1 + 2 + 4 + 6 + 9 + 11 + 12 + 15 + 17) => 77
+(1 + 2 + 4 + 6 + 9 + 11 + 13 + 14 + 16) => 76
+(1 + 2 + 4 + 6 + 9 + 11 + 13 + 14 + 17) => 77
+(1 + 2 + 4 + 6 + 9 + 11 + 13 + 15 + 16) => 77
+(1 + 2 + 4 + 6 + 9 + 11 + 13 + 15 + 17) => 78
+(1 + 2 + 4 + 7 + 8 + 10 + 12 + 14 + 16) => 74
+(1 + 2 + 4 + 7 + 8 + 10 + 12 + 14 + 17) => 75
+(1 + 2 + 4 + 7 + 8 + 10 + 12 + 15 + 16) => 75
+(1 + 2 + 4 + 7 + 8 + 10 + 12 + 15 + 17) => 76
+(1 + 2 + 4 + 7 + 8 + 10 + 13 + 14 + 16) => 75
+(1 + 2 + 4 + 7 + 8 + 10 + 13 + 14 + 17) => 76
+(1 + 2 + 4 + 7 + 8 + 10 + 13 + 15 + 16) => 76
+(1 + 2 + 4 + 7 + 8 + 10 + 13 + 15 + 17) => 77
+(1 + 2 + 4 + 7 + 8 + 11 + 12 + 14 + 16) => 75
+(1 + 2 + 4 + 7 + 8 + 11 + 12 + 14 + 17) => 76
+(1 + 2 + 4 + 7 + 8 + 11 + 12 + 15 + 16) => 76
+(1 + 2 + 4 + 7 + 8 + 11 + 12 + 15 + 17) => 77
+(1 + 2 + 4 + 7 + 8 + 11 + 13 + 14 + 16) => 76
+(1 + 2 + 4 + 7 + 8 + 11 + 13 + 14 + 17) => 77
+(1 + 2 + 4 + 7 + 8 + 11 + 13 + 15 + 16) => 77
+(1 + 2 + 4 + 7 + 8 + 11 + 13 + 15 + 17) => 78
+(1 + 2 + 4 + 7 + 9 + 10 + 12 + 14 + 16) => 75
+(1 + 2 + 4 + 7 + 9 + 10 + 12 + 14 + 17) => 76
+(1 + 2 + 4 + 7 + 9 + 10 + 12 + 15 + 16) => 76
+(1 + 2 + 4 + 7 + 9 + 10 + 12 + 15 + 17) => 77
+(1 + 2 + 4 + 7 + 9 + 10 + 13 + 14 + 16) => 76
+(1 + 2 + 4 + 7 + 9 + 10 + 13 + 14 + 17) => 77
+(1 + 2 + 4 + 7 + 9 + 10 + 13 + 15 + 16) => 77
+(1 + 2 + 4 + 7 + 9 + 10 + 13 + 15 + 17) => 78
+(1 + 2 + 4 + 7 + 9 + 11 + 12 + 14 + 16) => 76
+(1 + 2 + 4 + 7 + 9 + 11 + 12 + 14 + 17) => 77
+(1 + 2 + 4 + 7 + 9 + 11 + 12 + 15 + 16) => 77
+(1 + 2 + 4 + 7 + 9 + 11 + 12 + 15 + 17) => 78
+(1 + 2 + 4 + 7 + 9 + 11 + 13 + 14 + 16) => 77
+(1 + 2 + 4 + 7 + 9 + 11 + 13 + 14 + 17) => 78
+(1 + 2 + 4 + 7 + 9 + 11 + 13 + 15 + 16) => 78
+(1 + 2 + 4 + 7 + 9 + 11 + 13 + 15 + 17) => 79
+(1 + 2 + 5 + 6 + 8 + 10 + 12 + 14 + 16) => 74
+(1 + 2 + 5 + 6 + 8 + 10 + 12 + 14 + 17) => 75
+(1 + 2 + 5 + 6 + 8 + 10 + 12 + 15 + 16) => 75
+(1 + 2 + 5 + 6 + 8 + 10 + 12 + 15 + 17) => 76
+(1 + 2 + 5 + 6 + 8 + 10 + 13 + 14 + 16) => 75
+(1 + 2 + 5 + 6 + 8 + 10 + 13 + 14 + 17) => 76
+(1 + 2 + 5 + 6 + 8 + 10 + 13 + 15 + 16) => 76
+(1 + 2 + 5 + 6 + 8 + 10 + 13 + 15 + 17) => 77
+(1 + 2 + 5 + 6 + 8 + 11 + 12 + 14 + 16) => 75
+(1 + 2 + 5 + 6 + 8 + 11 + 12 + 14 + 17) => 76
+(1 + 2 + 5 + 6 + 8 + 11 + 12 + 15 + 16) => 76
+(1 + 2 + 5 + 6 + 8 + 11 + 12 + 15 + 17) => 77
+(1 + 2 + 5 + 6 + 8 + 11 + 13 + 14 + 16) => 76
+(1 + 2 + 5 + 6 + 8 + 11 + 13 + 14 + 17) => 77
+(1 + 2 + 5 + 6 + 8 + 11 + 13 + 15 + 16) => 77
+(1 + 2 + 5 + 6 + 8 + 11 + 13 + 15 + 17) => 78
+(1 + 2 + 5 + 6 + 9 + 10 + 12 + 14 + 16) => 75
+(1 + 2 + 5 + 6 + 9 + 10 + 12 + 14 + 17) => 76
+(1 + 2 + 5 + 6 + 9 + 10 + 12 + 15 + 16) => 76
+(1 + 2 + 5 + 6 + 9 + 10 + 12 + 15 + 17) => 77
+(1 + 2 + 5 + 6 + 9 + 10 + 13 + 14 + 16) => 76
+(1 + 2 + 5 + 6 + 9 + 10 + 13 + 14 + 17) => 77
+(1 + 2 + 5 + 6 + 9 + 10 + 13 + 15 + 16) => 77
+(1 + 2 + 5 + 6 + 9 + 10 + 13 + 15 + 17) => 78
+(1 + 2 + 5 + 6 + 9 + 11 + 12 + 14 + 16) => 76
+(1 + 2 + 5 + 6 + 9 + 11 + 12 + 14 + 17) => 77
+(1 + 2 + 5 + 6 + 9 + 11 + 12 + 15 + 16) => 77
+(1 + 2 + 5 + 6 + 9 + 11 + 12 + 15 + 17) => 78
+(1 + 2 + 5 + 6 + 9 + 11 + 13 + 14 + 16) => 77
+(1 + 2 + 5 + 6 + 9 + 11 + 13 + 14 + 17) => 78
+(1 + 2 + 5 + 6 + 9 + 11 + 13 + 15 + 16) => 78
+(1 + 2 + 5 + 6 + 9 + 11 + 13 + 15 + 17) => 79
+(1 + 2 + 5 + 7 + 8 + 10 + 12 + 14 + 16) => 75
+(1 + 2 + 5 + 7 + 8 + 10 + 12 + 14 + 17) => 76
+(1 + 2 + 5 + 7 + 8 + 10 + 12 + 15 + 16) => 76
+(1 + 2 + 5 + 7 + 8 + 10 + 12 + 15 + 17) => 77
+(1 + 2 + 5 + 7 + 8 + 10 + 13 + 14 + 16) => 76
+(1 + 2 + 5 + 7 + 8 + 10 + 13 + 14 + 17) => 77
+(1 + 2 + 5 + 7 + 8 + 10 + 13 + 15 + 16) => 77
+(1 + 2 + 5 + 7 + 8 + 10 + 13 + 15 + 17) => 78
+(1 + 2 + 5 + 7 + 8 + 11 + 12 + 14 + 16) => 76
+(1 + 2 + 5 + 7 + 8 + 11 + 12 + 14 + 17) => 77
+(1 + 2 + 5 + 7 + 8 + 11 + 12 + 15 + 16) => 77
+(1 + 2 + 5 + 7 + 8 + 11 + 12 + 15 + 17) => 78
+(1 + 2 + 5 + 7 + 8 + 11 + 13 + 14 + 16) => 77
+(1 + 2 + 5 + 7 + 8 + 11 + 13 + 14 + 17) => 78
+(1 + 2 + 5 + 7 + 8 + 11 + 13 + 15 + 16) => 78
+(1 + 2 + 5 + 7 + 8 + 11 + 13 + 15 + 17) => 79
+(1 + 2 + 5 + 7 + 9 + 10 + 12 + 14 + 16) => 76
+(1 + 2 + 5 + 7 + 9 + 10 + 12 + 14 + 17) => 77
+(1 + 2 + 5 + 7 + 9 + 10 + 12 + 15 + 16) => 77
+(1 + 2 + 5 + 7 + 9 + 10 + 12 + 15 + 17) => 78
+(1 + 2 + 5 + 7 + 9 + 10 + 13 + 14 + 16) => 77
+(1 + 2 + 5 + 7 + 9 + 10 + 13 + 14 + 17) => 78
+(1 + 2 + 5 + 7 + 9 + 10 + 13 + 15 + 16) => 78
+(1 + 2 + 5 + 7 + 9 + 10 + 13 + 15 + 17) => 79
+(1 + 2 + 5 + 7 + 9 + 11 + 12 + 14 + 16) => 77
+(1 + 2 + 5 + 7 + 9 + 11 + 12 + 14 + 17) => 78
+(1 + 2 + 5 + 7 + 9 + 11 + 12 + 15 + 16) => 78
+(1 + 2 + 5 + 7 + 9 + 11 + 12 + 15 + 17) => 79
+(1 + 2 + 5 + 7 + 9 + 11 + 13 + 14 + 16) => 78
+(1 + 2 + 5 + 7 + 9 + 11 + 13 + 14 + 17) => 79
+(1 + 2 + 5 + 7 + 9 + 11 + 13 + 15 + 16) => 79
+(1 + 2 + 5 + 7 + 9 + 11 + 13 + 15 + 17) => 80
+(1 + 3 + 4 + 6 + 8 + 10 + 12 + 14 + 16) => 74
+(1 + 3 + 4 + 6 + 8 + 10 + 12 + 14 + 17) => 75
+(1 + 3 + 4 + 6 + 8 + 10 + 12 + 15 + 16) => 75
+(1 + 3 + 4 + 6 + 8 + 10 + 12 + 15 + 17) => 76
+(1 + 3 + 4 + 6 + 8 + 10 + 13 + 14 + 16) => 75
+(1 + 3 + 4 + 6 + 8 + 10 + 13 + 14 + 17) => 76
+(1 + 3 + 4 + 6 + 8 + 10 + 13 + 15 + 16) => 76
+(1 + 3 + 4 + 6 + 8 + 10 + 13 + 15 + 17) => 77
+(1 + 3 + 4 + 6 + 8 + 11 + 12 + 14 + 16) => 75
+(1 + 3 + 4 + 6 + 8 + 11 + 12 + 14 + 17) => 76
+(1 + 3 + 4 + 6 + 8 + 11 + 12 + 15 + 16) => 76
+(1 + 3 + 4 + 6 + 8 + 11 + 12 + 15 + 17) => 77
+(1 + 3 + 4 + 6 + 8 + 11 + 13 + 14 + 16) => 76
+(1 + 3 + 4 + 6 + 8 + 11 + 13 + 14 + 17) => 77
+(1 + 3 + 4 + 6 + 8 + 11 + 13 + 15 + 16) => 77
+(1 + 3 + 4 + 6 + 8 + 11 + 13 + 15 + 17) => 78
+(1 + 3 + 4 + 6 + 9 + 10 + 12 + 14 + 16) => 75
+(1 + 3 + 4 + 6 + 9 + 10 + 12 + 14 + 17) => 76
+(1 + 3 + 4 + 6 + 9 + 10 + 12 + 15 + 16) => 76
+(1 + 3 + 4 + 6 + 9 + 10 + 12 + 15 + 17) => 77
+(1 + 3 + 4 + 6 + 9 + 10 + 13 + 14 + 16) => 76
+(1 + 3 + 4 + 6 + 9 + 10 + 13 + 14 + 17) => 77
+(1 + 3 + 4 + 6 + 9 + 10 + 13 + 15 + 16) => 77
+(1 + 3 + 4 + 6 + 9 + 10 + 13 + 15 + 17) => 78
+(1 + 3 + 4 + 6 + 9 + 11 + 12 + 14 + 16) => 76
+(1 + 3 + 4 + 6 + 9 + 11 + 12 + 14 + 17) => 77
+(1 + 3 + 4 + 6 + 9 + 11 + 12 + 15 + 16) => 77
+(1 + 3 + 4 + 6 + 9 + 11 + 12 + 15 + 17) => 78
+(1 + 3 + 4 + 6 + 9 + 11 + 13 + 14 + 16) => 77
+(1 + 3 + 4 + 6 + 9 + 11 + 13 + 14 + 17) => 78
+(1 + 3 + 4 + 6 + 9 + 11 + 13 + 15 + 16) => 78
+(1 + 3 + 4 + 6 + 9 + 11 + 13 + 15 + 17) => 79
+(1 + 3 + 4 + 7 + 8 + 10 + 12 + 14 + 16) => 75
+(1 + 3 + 4 + 7 + 8 + 10 + 12 + 14 + 17) => 76
+(1 + 3 + 4 + 7 + 8 + 10 + 12 + 15 + 16) => 76
+(1 + 3 + 4 + 7 + 8 + 10 + 12 + 15 + 17) => 77
+(1 + 3 + 4 + 7 + 8 + 10 + 13 + 14 + 16) => 76
+(1 + 3 + 4 + 7 + 8 + 10 + 13 + 14 + 17) => 77
+(1 + 3 + 4 + 7 + 8 + 10 + 13 + 15 + 16) => 77
+(1 + 3 + 4 + 7 + 8 + 10 + 13 + 15 + 17) => 78
+(1 + 3 + 4 + 7 + 8 + 11 + 12 + 14 + 16) => 76
+(1 + 3 + 4 + 7 + 8 + 11 + 12 + 14 + 17) => 77
+(1 + 3 + 4 + 7 + 8 + 11 + 12 + 15 + 16) => 77
+(1 + 3 + 4 + 7 + 8 + 11 + 12 + 15 + 17) => 78
+(1 + 3 + 4 + 7 + 8 + 11 + 13 + 14 + 16) => 77
+(1 + 3 + 4 + 7 + 8 + 11 + 13 + 14 + 17) => 78
+(1 + 3 + 4 + 7 + 8 + 11 + 13 + 15 + 16) => 78
+(1 + 3 + 4 + 7 + 8 + 11 + 13 + 15 + 17) => 79
+(1 + 3 + 4 + 7 + 9 + 10 + 12 + 14 + 16) => 76
+(1 + 3 + 4 + 7 + 9 + 10 + 12 + 14 + 17) => 77
+(1 + 3 + 4 + 7 + 9 + 10 + 12 + 15 + 16) => 77
+(1 + 3 + 4 + 7 + 9 + 10 + 12 + 15 + 17) => 78
+(1 + 3 + 4 + 7 + 9 + 10 + 13 + 14 + 16) => 77
+(1 + 3 + 4 + 7 + 9 + 10 + 13 + 14 + 17) => 78
+(1 + 3 + 4 + 7 + 9 + 10 + 13 + 15 + 16) => 78
+(1 + 3 + 4 + 7 + 9 + 10 + 13 + 15 + 17) => 79
+(1 + 3 + 4 + 7 + 9 + 11 + 12 + 14 + 16) => 77
+(1 + 3 + 4 + 7 + 9 + 11 + 12 + 14 + 17) => 78
+(1 + 3 + 4 + 7 + 9 + 11 + 12 + 15 + 16) => 78
+(1 + 3 + 4 + 7 + 9 + 11 + 12 + 15 + 17) => 79
+(1 + 3 + 4 + 7 + 9 + 11 + 13 + 14 + 16) => 78
+(1 + 3 + 4 + 7 + 9 + 11 + 13 + 14 + 17) => 79
+(1 + 3 + 4 + 7 + 9 + 11 + 13 + 15 + 16) => 79
+(1 + 3 + 4 + 7 + 9 + 11 + 13 + 15 + 17) => 80
+(1 + 3 + 5 + 6 + 8 + 10 + 12 + 14 + 16) => 75
+(1 + 3 + 5 + 6 + 8 + 10 + 12 + 14 + 17) => 76
+(1 + 3 + 5 + 6 + 8 + 10 + 12 + 15 + 16) => 76
+(1 + 3 + 5 + 6 + 8 + 10 + 12 + 15 + 17) => 77
+(1 + 3 + 5 + 6 + 8 + 10 + 13 + 14 + 16) => 76
+(1 + 3 + 5 + 6 + 8 + 10 + 13 + 14 + 17) => 77
+(1 + 3 + 5 + 6 + 8 + 10 + 13 + 15 + 16) => 77
+(1 + 3 + 5 + 6 + 8 + 10 + 13 + 15 + 17) => 78
+(1 + 3 + 5 + 6 + 8 + 11 + 12 + 14 + 16) => 76
+(1 + 3 + 5 + 6 + 8 + 11 + 12 + 14 + 17) => 77
+(1 + 3 + 5 + 6 + 8 + 11 + 12 + 15 + 16) => 77
+(1 + 3 + 5 + 6 + 8 + 11 + 12 + 15 + 17) => 78
+(1 + 3 + 5 + 6 + 8 + 11 + 13 + 14 + 16) => 77
+(1 + 3 + 5 + 6 + 8 + 11 + 13 + 14 + 17) => 78
+(1 + 3 + 5 + 6 + 8 + 11 + 13 + 15 + 16) => 78
+(1 + 3 + 5 + 6 + 8 + 11 + 13 + 15 + 17) => 79
+(1 + 3 + 5 + 6 + 9 + 10 + 12 + 14 + 16) => 76
+(1 + 3 + 5 + 6 + 9 + 10 + 12 + 14 + 17) => 77
+(1 + 3 + 5 + 6 + 9 + 10 + 12 + 15 + 16) => 77
+(1 + 3 + 5 + 6 + 9 + 10 + 12 + 15 + 17) => 78
+(1 + 3 + 5 + 6 + 9 + 10 + 13 + 14 + 16) => 77
+(1 + 3 + 5 + 6 + 9 + 10 + 13 + 14 + 17) => 78
+(1 + 3 + 5 + 6 + 9 + 10 + 13 + 15 + 16) => 78
+(1 + 3 + 5 + 6 + 9 + 10 + 13 + 15 + 17) => 79
+(1 + 3 + 5 + 6 + 9 + 11 + 12 + 14 + 16) => 77
+(1 + 3 + 5 + 6 + 9 + 11 + 12 + 14 + 17) => 78
+(1 + 3 + 5 + 6 + 9 + 11 + 12 + 15 + 16) => 78
+(1 + 3 + 5 + 6 + 9 + 11 + 12 + 15 + 17) => 79
+(1 + 3 + 5 + 6 + 9 + 11 + 13 + 14 + 16) => 78
+(1 + 3 + 5 + 6 + 9 + 11 + 13 + 14 + 17) => 79
+(1 + 3 + 5 + 6 + 9 + 11 + 13 + 15 + 16) => 79
+(1 + 3 + 5 + 6 + 9 + 11 + 13 + 15 + 17) => 80
+(1 + 3 + 5 + 7 + 8 + 10 + 12 + 14 + 16) => 76
+(1 + 3 + 5 + 7 + 8 + 10 + 12 + 14 + 17) => 77
+(1 + 3 + 5 + 7 + 8 + 10 + 12 + 15 + 16) => 77
+(1 + 3 + 5 + 7 + 8 + 10 + 12 + 15 + 17) => 78
+(1 + 3 + 5 + 7 + 8 + 10 + 13 + 14 + 16) => 77
+(1 + 3 + 5 + 7 + 8 + 10 + 13 + 14 + 17) => 78
+(1 + 3 + 5 + 7 + 8 + 10 + 13 + 15 + 16) => 78
+(1 + 3 + 5 + 7 + 8 + 10 + 13 + 15 + 17) => 79
+(1 + 3 + 5 + 7 + 8 + 11 + 12 + 14 + 16) => 77
+(1 + 3 + 5 + 7 + 8 + 11 + 12 + 14 + 17) => 78
+(1 + 3 + 5 + 7 + 8 + 11 + 12 + 15 + 16) => 78
+(1 + 3 + 5 + 7 + 8 + 11 + 12 + 15 + 17) => 79
+(1 + 3 + 5 + 7 + 8 + 11 + 13 + 14 + 16) => 78
+(1 + 3 + 5 + 7 + 8 + 11 + 13 + 14 + 17) => 79
+(1 + 3 + 5 + 7 + 8 + 11 + 13 + 15 + 16) => 79
+(1 + 3 + 5 + 7 + 8 + 11 + 13 + 15 + 17) => 80
+(1 + 3 + 5 + 7 + 9 + 10 + 12 + 14 + 16) => 77
+(1 + 3 + 5 + 7 + 9 + 10 + 12 + 14 + 17) => 78
+(1 + 3 + 5 + 7 + 9 + 10 + 12 + 15 + 16) => 78
+(1 + 3 + 5 + 7 + 9 + 10 + 12 + 15 + 17) => 79
+(1 + 3 + 5 + 7 + 9 + 10 + 13 + 14 + 16) => 78
+(1 + 3 + 5 + 7 + 9 + 10 + 13 + 14 + 17) => 79
+(1 + 3 + 5 + 7 + 9 + 10 + 13 + 15 + 16) => 79
+(1 + 3 + 5 + 7 + 9 + 10 + 13 + 15 + 17) => 80
+(1 + 3 + 5 + 7 + 9 + 11 + 12 + 14 + 16) => 78
+(1 + 3 + 5 + 7 + 9 + 11 + 12 + 14 + 17) => 79
+(1 + 3 + 5 + 7 + 9 + 11 + 12 + 15 + 16) => 79
+(1 + 3 + 5 + 7 + 9 + 11 + 12 + 15 + 17) => 80
+(1 + 3 + 5 + 7 + 9 + 11 + 13 + 14 + 16) => 79
+(1 + 3 + 5 + 7 + 9 + 11 + 13 + 14 + 17) => 80
+(1 + 3 + 5 + 7 + 9 + 11 + 13 + 15 + 16) => 80
+(1 + 3 + 5 + 7 + 9 + 11 + 13 + 15 + 17) => 81
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.demonstrate_async_combination_approvals_documentation_example.approved.txt b/tests/TestingTools/CombinationApprovalsAsync.test.demonstrate_async_combination_approvals_documentation_example.approved.txt
new file mode 100644
index 00000000..6b638bda
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.demonstrate_async_combination_approvals_documentation_example.approved.txt
@@ -0,0 +1,9 @@
+sample outputs
+(0, hello, true) => 0 'hello' true
+(0, hello, false) => 0 'hello' false
+(0, world, true) => 0 'world' true
+(0, world, false) => 0 'world' false
+(1, hello, true) => 1 'hello' true
+(1, hello, false) => 1 'hello' false
+(1, world, true) => 1 'world' true
+(1, world, false) => 1 'world' false
diff --git a/tests/TestingTools/CombinationApprovalsAsync.test.ts b/tests/TestingTools/CombinationApprovalsAsync.test.ts
new file mode 100644
index 00000000..3efd5282
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.test.ts
@@ -0,0 +1,151 @@
+import {
+    verifyAllCombinations2Async,
+    verifyAllCombinations3Async,
+    verifyAllCombinations4Async,
+    verifyAllCombinations5Async,
+    verifyAllCombinations6Async,
+    verifyAllCombinations7Async,
+    verifyAllCombinations8Async,
+    verifyAllCombinations9Async,
+} from './CombinationApprovalsAsync';
+
+describe('CombinationApprovalsAsync', () => {
+    verifyAllCombinations2Async(
+        'verify2',
+        'title',
+        async (a, b) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2'.
+            return `(${a} + ${b}) => ${a + b}`;
+        },
+        [0, 1],
+        [2, 3],
+    );
+
+    verifyAllCombinations3Async(
+        'verify3',
+        'title',
+        async (a, b, c) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2' and 'T3'.
+            return `(${a} + ${b} + ${c}) => ${a + b + c}`;
+        },
+        [0, 1],
+        [2, 3],
+        [4, 5],
+    );
+
+    verifyAllCombinations4Async(
+        'verify4',
+        'title',
+        async (a, b, c, d) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2' and 'T3' + .....
+            const total = a + b + c + d;
+            return `(${a} + ${b} + ${c} + ${d}) => ${total}`;
+        },
+        [0, 1],
+        [2, 3],
+        [4, 5],
+        [6, 7],
+    );
+
+    verifyAllCombinations5Async(
+        'verify5',
+        'title',
+        async (a, b, c, d, e) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2' and 'T3' + .....
+            const total = a + b + c + d + e;
+            return `(${a} + ${b} + ${c} + ${d} + ${e}) => ${total}`;
+        },
+        [0, 1],
+        [2, 3],
+        [4, 5],
+        [6, 7],
+        [8, 9],
+    );
+
+    verifyAllCombinations6Async(
+        'verify6',
+        'title',
+        async (a, b, c, d, e, f) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2' and 'T3' + .....
+            const total = a + b + c + d + e + f;
+            return `(${a} + ${b} + ${c} + ${d} + ${e} + ${f}) => ${total}`;
+        },
+        [0, 1],
+        [2, 3],
+        [4, 5],
+        [6, 7],
+        [8, 9],
+        [10, 11],
+    );
+
+    verifyAllCombinations7Async(
+        'verify7',
+        'title',
+        async (a, b, c, d, e, f, g) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2' and 'T3' + .....
+            const total = a + b + c + d + e + f + g;
+            return `(${a} + ${b} + ${c} + ${d} + ${e} + ${f} + ${g}) => ${total}`;
+        },
+        [0, 1],
+        [2, 3],
+        [4, 5],
+        [6, 7],
+        [8, 9],
+        [10, 11],
+        [12, 13],
+    );
+
+    verifyAllCombinations8Async(
+        'verify8',
+        'title',
+        async (a, b, c, d, e, f, g, h) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2' and 'T3' + .....
+            const total = a + b + c + d + e + f + g + h;
+            return `(${a} + ${b} + ${c} + ${d} + ${e} + ${f} + ${g} + ${h}) => ${total}`;
+        },
+        [0, 1],
+        [2, 3],
+        [4, 5],
+        [6, 7],
+        [8, 9],
+        [10, 11],
+        [12, 13],
+        [14, 15],
+    );
+
+    verifyAllCombinations9Async(
+        'verify9',
+        'title',
+        async (a, b, c, d, e, f, g, h, i) => {
+            // @ts-expect-error: TS2365: Operator '+' cannot be applied to types 'T1' and 'T2' and 'T3' + .....
+            const total = a + b + c + d + e + f + g + h + i;
+            return `(${a} + ${b} + ${c} + ${d} + ${e} + ${f} + ${g} + ${h} + ${i}) => ${total}`;
+        },
+        [0, 1],
+        [2, 3],
+        [4, 5],
+        [6, 7],
+        [8, 9],
+        [10, 11],
+        [12, 13],
+        [14, 15],
+        [16, 17],
+    );
+});
+
+// begin-snippet: async-combination-approvals
+describe('demonstrate async combination approvals', () => {
+    // Note that we do not have an 'it' section here.
+    // verifyAllCombinations3Async() creates the 'it' block.
+    verifyAllCombinations3Async(
+        'documentation example',
+        'sample outputs',
+        async (a, b, c) => {
+            return `(${a}, ${b}, ${c}) => ${a} '${b}' ${c}`;
+        },
+        [0, 1],
+        ['hello', 'world'],
+        [true, false],
+    );
+});
+// end-snippet
diff --git a/tests/TestingTools/CombinationApprovalsAsync.ts b/tests/TestingTools/CombinationApprovalsAsync.ts
new file mode 100644
index 00000000..bc3c4d9e
--- /dev/null
+++ b/tests/TestingTools/CombinationApprovalsAsync.ts
@@ -0,0 +1,291 @@
+import { it } from '@jest/globals';
+import { verify } from 'approvals/lib/Providers/Jest/JestApprovals';
+import { EMPTY } from 'approvals/lib/Utilities/Printers';
+import { runCombinations9 } from './RunCombinations';
+
+export function verifyAllCombinations2Async<T1, T2>(
+    name: string,
+    title: string,
+    func: <T1, T2>(t1: T1, t2: T2) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, _t3, _t4, _t5, _t6, _t7, _t8, _t9) => {
+            it(`${name} ${t1} ${t2}`, async () => {
+                output += (await func(t1, t2)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
+
+export function verifyAllCombinations3Async<T1, T2, T3>(
+    name: string,
+    title: string,
+    func: <T1, T2, T3>(t1: T1, t2: T2, t3: T3) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, t3, _t4, _t5, _t6, _t7, _t8, _t9) => {
+            it(`${name} ${t1} ${t2} ${t3}`, async () => {
+                output += (await func(t1, t2, t3)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        params3,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
+
+export function verifyAllCombinations4Async<T1, T2, T3, T4>(
+    name: string,
+    title: string,
+    func: <T1, T2, T3, T4>(t1: T1, t2: T2, t3: T3, t4: T4) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+    params4: T4[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, t3, t4, _t5, _t6, _t7, _t8, _t9) => {
+            it(`${name} ${t1} ${t2} ${t3} ${t4}`, async () => {
+                output += (await func(t1, t2, t3, t4)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        params3,
+        params4,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
+
+export function verifyAllCombinations5Async<T1, T2, T3, T4, T5>(
+    name: string,
+    title: string,
+    func: <T1, T2, T3, T4, T5>(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+    params4: T4[],
+    params5: T5[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, t3, t4, t5, _t6, _t7, _t8, _t9) => {
+            it(`${name} ${t1} ${t2} ${t3} ${t4} ${t5}`, async () => {
+                output += (await func(t1, t2, t3, t4, t5)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        params3,
+        params4,
+        params5,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
+
+export function verifyAllCombinations6Async<T1, T2, T3, T4, T5, T6>(
+    name: string,
+    title: string,
+    func: <T1, T2, T3, T4, T5, T6>(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+    params4: T4[],
+    params5: T5[],
+    params6: T6[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, t3, t4, t5, t6, _t7, _t8, _t9) => {
+            it(`${name} ${t1} ${t2} ${t3} ${t4} ${t5} ${t6}`, async () => {
+                output += (await func(t1, t2, t3, t4, t5, t6)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        params3,
+        params4,
+        params5,
+        params6,
+        EMPTY,
+        EMPTY,
+        EMPTY,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
+
+export function verifyAllCombinations7Async<T1, T2, T3, T4, T5, T6, T7>(
+    name: string,
+    title: string,
+    func: <T1, T2, T3, T4, T5, T6, T7>(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+    params4: T4[],
+    params5: T5[],
+    params6: T6[],
+    params7: T7[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, t3, t4, t5, t6, t7, _t8, _t9) => {
+            it(`${name} ${t1} ${t2} ${t3} ${t4} ${t5} ${t6} ${t7}`, async () => {
+                output += (await func(t1, t2, t3, t4, t5, t6, t7)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        params3,
+        params4,
+        params5,
+        params6,
+        params7,
+        EMPTY,
+        EMPTY,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
+
+export function verifyAllCombinations8Async<T1, T2, T3, T4, T5, T6, T7, T8>(
+    name: string,
+    title: string,
+    func: <T1, T2, T3, T4, T5, T6, T7, T8>(
+        t1: T1,
+        t2: T2,
+        t3: T3,
+        t4: T4,
+        t5: T5,
+        t6: T6,
+        t7: T7,
+        t8: T8,
+    ) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+    params4: T4[],
+    params5: T5[],
+    params6: T6[],
+    params7: T7[],
+    params8: T8[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, t3, t4, t5, t6, t7, t8, _t9) => {
+            it(`${name} ${t1} ${t2} ${t3} ${t4} ${t5} ${t6} ${t7} ${t8}`, async () => {
+                output += (await func(t1, t2, t3, t4, t5, t6, t7, t8)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        params3,
+        params4,
+        params5,
+        params6,
+        params7,
+        params8,
+        EMPTY,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
+
+export function verifyAllCombinations9Async<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+    name: string,
+    title: string,
+    func: <T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+        t1: T1,
+        t2: T2,
+        t3: T3,
+        t4: T4,
+        t5: T5,
+        t6: T6,
+        t7: T7,
+        t8: T8,
+        t9: T9,
+    ) => Promise<any>,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+    params4: T4[],
+    params5: T5[],
+    params6: T6[],
+    params7: T7[],
+    params8: T8[],
+    params9: T9[],
+) {
+    let output = title + '\n';
+    runCombinations9(
+        (t1, t2, t3, t4, t5, t6, t7, t8, t9) => {
+            it(`${name} ${t1} ${t2} ${t3} ${t4} ${t5} ${t6} ${t7} ${t8} ${t9}`, async () => {
+                output += (await func(t1, t2, t3, t4, t5, t6, t7, t8, t9)) + '\n';
+            });
+        },
+        params1,
+        params2,
+        params3,
+        params4,
+        params5,
+        params6,
+        params7,
+        params8,
+        params9,
+    );
+
+    it(name, () => {
+        verify(output);
+    });
+}
diff --git a/tests/TestingTools/RunCombinations.ts b/tests/TestingTools/RunCombinations.ts
new file mode 100644
index 00000000..865738df
--- /dev/null
+++ b/tests/TestingTools/RunCombinations.ts
@@ -0,0 +1,40 @@
+import { printCombinations } from 'approvals/lib/Utilities/Printers';
+
+export function runCombinations9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+    func: <T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+        t1: T1,
+        t2: T2,
+        t3: T3,
+        t4: T4,
+        t5: T5,
+        t6: T6,
+        t7: T7,
+        t8: T8,
+        t9: T9,
+    ) => any,
+    params1: T1[],
+    params2: T2[],
+    params3: T3[],
+    params4: T4[],
+    params5: T5[],
+    params6: T6[],
+    params7: T7[],
+    params8: T8[],
+    params9: T9[],
+) {
+    printCombinations(
+        (p1, p2, p3, p4, p5, p6, p7, p8, p9) => {
+            func(p1, p2, p3, p4, p5, p6, p7, p8, p9);
+            return '';
+        },
+        params1,
+        params2,
+        params3,
+        params4,
+        params5,
+        params6,
+        params7,
+        params8,
+        params9,
+    );
+}