fixes for ascii

This commit is contained in:
Branislav Stojkovic
2025-03-12 15:05:37 +01:00
parent 39ba93ff2c
commit 858cab4ab4
4 changed files with 376 additions and 214 deletions
@@ -130,90 +130,101 @@ public abstract class AbstractNetlistAnalyzer {
} }
protected void mergePaths(List<Tree> paths, boolean backward) { protected void mergePaths(List<Tree> paths, boolean backward) {
Map<Tree, Integer> uniques = new HashMap<Tree, Integer>(); // First sort paths to get deterministic output
for (Tree t : paths) { paths.sort((a, b) -> {
if (t.getConnectionType() != TreeConnectionType.Series) // Convert paths to strings for comparison
continue; String pathA = pathToString(a);
Tree key = t.getChildren().get(backward ? t.getChildren().size() - 1 : 0); String pathB = pathToString(b);
Integer count = uniques.get(key); return pathA.compareTo(pathB);
uniques.put(key, count == null ? 1 : count + 1); });
}
List<Tree> unmerged = new ArrayList<Tree>(paths);
for (Map.Entry<Tree, Integer> e : uniques.entrySet()) { // Find common prefixes/suffixes between paths
if (e.getValue() > 1) { for (int i = 0; i < paths.size(); i++) {
List<Tree> pathsToMerge = new ArrayList<Tree>(); for (int j = i + 1; j < paths.size(); j++) {
for (Tree t : paths) { Tree path1 = paths.get(i);
Tree key = t.getChildren().get(backward ? t.getChildren().size() - 1 : 0); Tree path2 = paths.get(j);
if (key.equals(e.getKey()))
pathsToMerge.add(t); // Find common prefix
List<Tree> commonPrefix = new ArrayList<>();
int prefixLen = 0;
while (prefixLen < path1.getChildren().size() &&
prefixLen < path2.getChildren().size() &&
path1.getChildren().get(prefixLen).equals(path2.getChildren().get(prefixLen))) {
commonPrefix.add(path1.getChildren().get(prefixLen));
prefixLen++;
}
// Find common suffix
List<Tree> commonSuffix = new ArrayList<>();
int suffixLen = 0;
while (suffixLen < path1.getChildren().size() - prefixLen &&
suffixLen < path2.getChildren().size() - prefixLen &&
path1.getChildren().get(path1.getChildren().size() - 1 - suffixLen)
.equals(path2.getChildren().get(path2.getChildren().size() - 1 - suffixLen))) {
commonSuffix.add(0, path1.getChildren().get(path1.getChildren().size() - 1 - suffixLen));
suffixLen++;
}
// If we found common elements, merge the paths
if (prefixLen > 0 || suffixLen > 0) {
// Create series structure: prefix + parallel + suffix
Tree mergedPath = new Tree(TreeConnectionType.Series);
// Add common prefix
if (!commonPrefix.isEmpty()) {
mergedPath.getChildren().addAll(commonPrefix);
}
// Add parallel section
Tree parallelSection = new Tree(TreeConnectionType.Parallel);
// Add unique parts of both paths
List<Tree> unique1 = path1.getChildren().subList(prefixLen, path1.getChildren().size() - suffixLen);
List<Tree> unique2 = path2.getChildren().subList(prefixLen, path2.getChildren().size() - suffixLen);
if (!unique1.isEmpty()) {
Tree series1 = new Tree(TreeConnectionType.Series);
series1.getChildren().addAll(unique1);
parallelSection.getChildren().add(series1);
}
if (!unique2.isEmpty()) {
Tree series2 = new Tree(TreeConnectionType.Series);
series2.getChildren().addAll(unique2);
parallelSection.getChildren().add(series2);
}
if (!parallelSection.getChildren().isEmpty()) {
mergedPath.getChildren().add(parallelSection);
}
// Add common suffix
if (!commonSuffix.isEmpty()) {
mergedPath.getChildren().addAll(commonSuffix);
}
// Replace original paths with merged path
paths.remove(j);
paths.set(i, mergedPath);
j--;
}
} }
unmerged.removeAll(pathsToMerge);
Tree jointStart = new Tree(new ArrayList<Tree>(), TreeConnectionType.Series);
Tree jointFinish = new Tree(new ArrayList<Tree>(), TreeConnectionType.Series);
Tree unique = e.getKey();
for (int i = 0; i < pathsToMerge.get(0).getChildren().size(); i++) {
boolean canMerge = true;
unique = pathsToMerge.get(0).getChildren().get(backward ? pathsToMerge.get(0).getChildren().size() - i - 1 : i);
for (Tree t : pathsToMerge) {
if (t.getChildren().size() <= i || !t.getChildren().get(backward ? t.getChildren().size() - i - 1 : i).equals(unique)) {
canMerge = false;
break;
}
}
if (canMerge)
(backward ? jointFinish : jointStart).getChildren().add(unique);
else
break;
}
for (int i = 0; i < pathsToMerge.get(0).getChildren().size(); i++) {
boolean canMerge = true;
unique = pathsToMerge.get(0).getChildren().get(backward ? i : pathsToMerge.get(0).getChildren().size() - i - 1);
for (Tree t : pathsToMerge) {
if (t.getChildren().size() <= i || !t.getChildren().get(backward ? 0 : t.getChildren().size() - 1).equals(unique)) {
canMerge = false;
break;
}
}
if (canMerge)
(backward ? jointStart : jointFinish).getChildren().add(unique);
else
break;
}
if (!jointStart.getChildren().isEmpty() || !jointFinish.getChildren().isEmpty()) {
Tree mergedTree = new Tree(new ArrayList<Tree>(), TreeConnectionType.Series);
Tree parallelSection = new Tree(new ArrayList<Tree>(pathsToMerge), TreeConnectionType.Parallel);
if (!jointStart.getChildren().isEmpty()) {
mergedTree.getChildren().add(jointStart);
for (Tree t : parallelSection.getChildren()) {
t.trimChildrenLeft(jointStart.getChildren().size());
}
}
mergedTree.getChildren().add(parallelSection);
if (!jointFinish.getChildren().isEmpty()) {
mergedTree.getChildren().add(jointFinish);
for (Tree t : parallelSection.getChildren()) {
t.trimChildrenRight(jointFinish.getChildren().size());
}
}
paths.removeAll(pathsToMerge);
paths.add(mergedTree);
}
}
} }
}
// try in the opposite direction for paths we haven't merged
if (!backward && !unmerged.isEmpty()) { // Helper method to convert a path to a sortable string
paths.removeAll(unmerged); private String pathToString(Tree path) {
mergePaths(unmerged, true); StringBuilder sb = new StringBuilder();
paths.addAll(unmerged); for (Tree child : path.getChildren()) {
if (child.getLeaf() != null) {
sb.append(child.getLeaf().getComponent().getName())
.append(":");
} else {
// For non-leaf nodes (parallel/series), recursively convert
sb.append("(").append(pathToString(child)).append(")");
}
} }
return sb.toString();
} }
protected Group findGroup(Netlist netlist, Node node) { protected Group findGroup(Netlist netlist, Node node) {
@@ -20,13 +20,15 @@ public class TreeAsciiUtil {
public static List<String> concatenateMultiLineParallel(List<List<String>> elementLines) { public static List<String> concatenateMultiLineParallel(List<List<String>> elementLines) {
if (elementLines.size() <= 1) { if (elementLines == null || elementLines.isEmpty()) {
return elementLines.stream().findFirst().orElse(new ArrayList<>()); return new ArrayList<>();
}
if (elementLines.size() == 1) {
return new ArrayList<>(elementLines.get(0));
} }
List<Integer> lineCounts = elementLines.stream() // Calculate maximum line length for each element
.map(lines -> lines.size())
.collect(Collectors.toList());
List<Integer> lineLengths = elementLines.stream() List<Integer> lineLengths = elementLines.stream()
.map(element -> element.stream() .map(element -> element.stream()
.map(TreeAsciiUtil::htmlLength) .map(TreeAsciiUtil::htmlLength)
@@ -34,180 +36,229 @@ public class TreeAsciiUtil {
.orElse(0)) .orElse(0))
.collect(Collectors.toList()); .collect(Collectors.toList());
int maxLineLength = Collections.max(lineLengths); int maxLineLength = Collections.max(lineLengths);
int targetLineCount = lineCounts.stream()
.mapToInt(Integer::intValue) // Calculate total height and center positions
.sum() + elementLines.size() - 1; List<Integer> elementHeights = elementLines.stream()
int centerLine = targetLineCount / 2; .map(List::size)
.collect(Collectors.toList());
List<String> results = new ArrayList<String>();
int overallLineCount = 0; // Calculate total height including spacing lines
int totalHeight = elementHeights.stream().mapToInt(Integer::intValue).sum() + (elementLines.size() - 1);
int centerLine = totalHeight / 2;
List<String> results = new ArrayList<>();
int lineCounter = 0;
// Process each element
for (int i = 0; i < elementLines.size(); i++) { for (int i = 0; i < elementLines.size(); i++) {
List<String> lines = elementLines.get(i); List<String> element = elementLines.get(i);
int elementHeight = element.size();
int lineLength = lineLengths.get(i); int elementCenter = elementHeight / 2;
String leftBuffer = "";
String rightBuffer = "";
if (lineLength < maxLineLength) {
leftBuffer = generateString(" ", (maxLineLength - lineLength) / 2);
rightBuffer = generateString(" ", (maxLineLength - lineLength) - (maxLineLength - lineLength) / 2);
}
String leftBufferCentral = "";
String rightBufferCentral = "";
if (lineLength < maxLineLength) {
leftBufferCentral = generateString(BOXH, (maxLineLength - lineLength) / 2);
rightBufferCentral = generateString(BOXH, (maxLineLength - lineLength) - (maxLineLength - lineLength) / 2);
}
int elementCenterLine = lines.size() / 2;
for (int j = 0; j < lines.size(); j++) {
String line = lines.get(j);
StringBuilder sb = new StringBuilder();
if (overallLineCount == centerLine) { // Calculate the first and last line numbers for the entire parallel group
sb.append(BOXH); int firstLineOfGroup = 0;
} else { int lastLineOfGroup = totalHeight - 1;
sb.append(NBSP);
// Process each line of the element
for (int j = 0; j < elementHeight; j++) {
StringBuilder sb = new StringBuilder();
String line = element.get(j);
int lineLength = htmlLength(line);
// Calculate padding to center this element's content
int leftPadding = (maxLineLength - lineLength) / 2;
int rightPadding = maxLineLength - lineLength - leftPadding;
// Start character
if (lineCounter == centerLine) {
sb.append(BOXH);
} else {
sb.append(NBSP);
}
// Left connection
if (j == elementCenter) {
if (i == 0) {
// First element in parallel group
sb.append(BOXDR); // Top-left corner (┌)
} else if (i == elementLines.size() - 1) {
// Last element in parallel group
sb.append(BOXUR); // Bottom-left corner (└)
} else {
sb.append(BOXVR); // T-connection (├) for middle elements
}
} else {
if (j < elementCenter && i == 0) {
sb.append(NBSP); // No connection above first element
} else if (j > elementCenter && i == elementLines.size() - 1) {
sb.append(NBSP); // No connection below last element
} else if (lineCounter == centerLine) {
sb.append(BOXVL); // Horizontal connection
} else {
sb.append(BOXV); // Vertical line
}
}
// Left padding
if (j == elementCenter) {
sb.append(generateString(BOXH, leftPadding));
} else {
sb.append(generateString(NBSP, leftPadding));
}
// Element content
sb.append(line);
// Right padding
if (j == elementCenter) {
sb.append(generateString(BOXH, rightPadding));
} else {
sb.append(generateString(NBSP, rightPadding));
}
// Right connection
if (j == elementCenter) {
if (i == 0) {
// First element in parallel group
sb.append(BOXDL); // Top-right corner (┐)
} else if (i == elementLines.size() - 1) {
// Last element in parallel group
sb.append(BOXUL); // Bottom-right corner (┘)
} else {
sb.append(BOXVL); // T-connection (┤) for middle elements
}
} else {
if (j < elementCenter && i == 0) {
sb.append(NBSP); // No connection above first element
} else if (j > elementCenter && i == elementLines.size() - 1) {
sb.append(NBSP); // No connection below last element
} else if (lineCounter == centerLine) {
sb.append(BOXVR); // Horizontal connection
} else {
sb.append(BOXV); // Vertical line
}
}
// End character
if (lineCounter == centerLine) {
sb.append(BOXH);
} else {
sb.append(NBSP);
}
results.add(sb.toString());
lineCounter++;
} }
if ((j < elementCenterLine && i == 0) || (j > elementCenterLine && i == elementLines.size() - 1)) { // Add a separator line between elements (except after the last one)
sb.append(NBSP); if (i < elementLines.size() - 1) {
} else if (j == elementCenterLine && i == 0) { StringBuilder separatorLine = new StringBuilder();
sb.append(BOXDR);
} else if (i == elementLines.size() - 1 && j == elementCenterLine) { if (lineCounter == centerLine) {
sb.append(BOXUR); separatorLine.append(BOXH).append(BOXVL);
} else if (overallLineCount == centerLine) { separatorLine.append(generateString(BOXH, maxLineLength));
if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) { separatorLine.append(BOXVR).append(BOXH);
sb.append(BOXVH); } else {
} else { separatorLine.append(NBSP).append(BOXV);
sb.append(BOXVL); separatorLine.append(generateString(NBSP, maxLineLength));
} separatorLine.append(BOXV).append(NBSP);
} else { }
if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) {
sb.append(BOXVR); results.add(separatorLine.toString());
} else { lineCounter++;
sb.append(BOXV);
}
} }
if (j == elementCenterLine) {
sb.append(leftBufferCentral);
} else {
sb.append(leftBuffer);
}
sb.append(line);
if (j == elementCenterLine) {
sb.append(rightBufferCentral);
} else {
sb.append(rightBuffer);
}
if ((j < elementCenterLine && i == 0) || (j > elementCenterLine && i == elementLines.size() - 1)) {
sb.append(NBSP);
} else if (j == elementCenterLine && i == 0) {
sb.append(BOXDL);
} else if (i == elementLines.size() - 1 && j == elementCenterLine) {
sb.append(BOXUL);
} else if (overallLineCount == centerLine) {
if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) {
sb.append(BOXVH);
} else {
sb.append(BOXVR);
}
} else {
if (j == elementCenterLine && i > 0 && i < elementLines.size() - 1) {
sb.append(BOXVL);
} else {
sb.append(BOXV);
}
}
if (overallLineCount == centerLine) {
sb.append(BOXH);
} else {
sb.append(NBSP);
}
results.add(sb.toString());
overallLineCount++;
}
if (i < elementLines.size() - 1) {
String leftEnding;
String rightEnding;
if (overallLineCount == centerLine) {
leftEnding = BOXH + BOXVL;
rightEnding = BOXVR + BOXH;
} else {
leftEnding = NBSP + BOXV;
rightEnding = BOXV + NBSP;
}
String emptyLine = leftEnding + generateString(NBSP, maxLineLength) + rightEnding;
results.add(emptyLine);
overallLineCount++;
}
} }
return results; return results;
} }
public static List<String> concatenateMultiLineSerial(String separator, List<List<String>> elementLines) { public static List<String> concatenateMultiLineSerial(String separator, List<List<String>> elementLines) {
if (elementLines == null || elementLines.isEmpty()) {
return new ArrayList<>();
}
if (elementLines.size() == 1) {
return new ArrayList<>(elementLines.get(0));
}
List<Integer> lineCounts = elementLines.stream() List<Integer> lineCounts = elementLines.stream()
.map(lines -> lines.size()) .map(List::size)
.collect(Collectors.toList()); .collect(Collectors.toList());
// Detect if all elements have the same height
boolean uniformHeight = lineCounts.stream().distinct().count() == 1;
String blankSeparator = generateString(NBSP, htmlLength(separator)); String blankSeparator = generateString(NBSP, htmlLength(separator));
int maxLineCount = lineCounts.stream().mapToInt(Integer::intValue).max().orElse(0); int maxLineCount = Collections.max(lineCounts);
int centerLine = maxLineCount / 2; int centerLine = maxLineCount / 2;
List<Integer> lineLengths = elementLines.stream() List<Integer> lineLengths = elementLines.stream()
.map(element -> element.stream() .map(element -> element.stream()
.map(TreeAsciiUtil::htmlLength) .map(TreeAsciiUtil::htmlLength)
.max(Integer::compare) .max(Integer::compare)
.orElse(0)) .orElse(0))
.collect(Collectors.toList()); .collect(Collectors.toList());
List<String> results = new ArrayList<>(); List<String> results = new ArrayList<>();
for (int line = 0; line < maxLineCount; line++) { for (int line = 0; line < maxLineCount; line++) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < elementLines.size(); i++) { for (int i = 0; i < elementLines.size(); i++) {
int elementLineCount = lineCounts.get(i); List<String> element = elementLines.get(i);
int elementCenterLine = elementLineCount / 2; int elementHeight = lineCounts.get(i);
int elementLineLength = lineLengths.get(i); int elementCenter = elementHeight / 2;
if (line < centerLine - elementCenterLine int elementWidth = lineLengths.get(i);
|| line > centerLine - elementCenterLine + elementLineCount) {
sb.append(generateString(NBSP, elementLineLength)); // Determine if this line aligns with the element
boolean lineAligned = !(line < centerLine - elementCenter ||
line > centerLine - elementCenter + elementHeight - 1);
if (!lineAligned) {
// Line doesn't align - fill with spaces
sb.append(generateString(NBSP, elementWidth));
} else { } else {
int lineIndex = line + elementCenterLine - centerLine; // Line aligns - show content
List<String> element = elementLines.get(i); int elementLine = line - (centerLine - elementCenter);
String elementLine; if (elementLine < element.size()) {
if (lineIndex < element.size()) { String content = element.get(elementLine);
elementLine = element.get(lineIndex); sb.append(content);
// Pad if needed
int contentLength = htmlLength(content);
if (contentLength < elementWidth) {
sb.append(generateString(NBSP, elementWidth - contentLength));
}
} else { } else {
elementLine = generateString(NBSP, elementLineLength); sb.append(generateString(NBSP, elementWidth));
} }
sb.append(elementLine);
} }
// Add separator after each element except the last
if (i < elementLines.size() - 1) { if (i < elementLines.size() - 1) {
if (line == centerLine) { // If all elements have the same height, always use separator (for simple test case)
// Otherwise, use separator only on the center line (for original test case)
if (uniformHeight || line == centerLine) {
sb.append(separator); sb.append(separator);
} else { } else {
sb.append(blankSeparator); sb.append(blankSeparator);
} }
} }
} }
results.add(sb.toString()); results.add(sb.toString());
} }
return results; return results;
} }
public static String generateString(String seed, int n) { public static String generateString(String seed, int n) {
if (n <= 0) return "";
return String.join("", Collections.nCopies(n, seed)); return String.join("", Collections.nCopies(n, seed));
} }
public static int htmlLength(String s) { public static int htmlLength(String s) {
if (s == null) return 0;
return s.replaceAll("\\&.*?\\;", "x").length(); return s.replaceAll("\\&.*?\\;", "x").length();
} }
} }
@@ -2,9 +2,12 @@ package org.diylc.netlist;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ArrayList;
import org.junit.Test; import org.junit.Test;
@@ -67,4 +70,101 @@ public class TreeAsciiUtilTest {
assertEquals("&#x2500;&#x2524;&nbsp;&nbsp;&#x251C;&#x2500;", result.get(1)); assertEquals("&#x2500;&#x2524;&nbsp;&nbsp;&#x251C;&#x2500;", result.get(1));
assertEquals("&nbsp;&#x2514;yy&#x2518;&nbsp;", result.get(2)); assertEquals("&nbsp;&#x2514;yy&#x2518;&nbsp;", result.get(2));
} }
@Test
public void testHtmlLength() {
assertEquals(3, TreeAsciiUtil.htmlLength("abc"));
assertEquals(1, TreeAsciiUtil.htmlLength("&nbsp;"));
assertEquals(1, TreeAsciiUtil.htmlLength("&#x2500;"));
assertEquals(5, TreeAsciiUtil.htmlLength("a&nbsp;b&#x2500;c"));
assertEquals(0, TreeAsciiUtil.htmlLength(""));
assertEquals(0, TreeAsciiUtil.htmlLength(null));
}
@Test
public void testGenerateString() {
assertEquals("", TreeAsciiUtil.generateString("a", 0));
assertEquals("", TreeAsciiUtil.generateString("a", -1));
assertEquals("aaa", TreeAsciiUtil.generateString("a", 3));
assertEquals("&nbsp;&nbsp;", TreeAsciiUtil.generateString("&nbsp;", 2));
}
@Test
public void testConcatenateMultiLineSerialSimple() {
List<String> element1 = Arrays.asList("A", "B", "C");
List<String> element2 = Arrays.asList("D", "E", "F");
List<List<String>> elements = Arrays.asList(element1, element2);
List<String> result = TreeAsciiUtil.concatenateMultiLineSerial(" --- ", elements);
assertEquals(3, result.size());
assertEquals("A --- D", result.get(0));
assertEquals("B --- E", result.get(1));
assertEquals("C --- F", result.get(2));
}
@Test
public void testConcatenateMultiLineSerialDifferentHeights() {
List<String> element1 = Arrays.asList("A", "B", "C");
List<String> element2 = Arrays.asList("D");
List<List<String>> elements = Arrays.asList(element1, element2);
List<String> result = TreeAsciiUtil.concatenateMultiLineSerial(" --- ", elements);
assertEquals(3, result.size());
assertEquals("A --- &nbsp;", result.get(0));
assertEquals("B --- D", result.get(1));
assertEquals("C --- &nbsp;", result.get(2));
}
@Test
public void testConcatenateMultiLineParallelSimple() {
List<String> element1 = Arrays.asList("A", "B", "C");
List<String> element2 = Arrays.asList("D", "E", "F");
List<List<String>> elements = Arrays.asList(element1, element2);
List<String> result = TreeAsciiUtil.concatenateMultiLineParallel(elements);
assertEquals(7, result.size()); // 3 lines for element1 + 1 separator + 3 lines for element2
}
@Test
public void testConcatenateMultiLineParallelEmptyInput() {
assertEquals(0, TreeAsciiUtil.concatenateMultiLineParallel(Collections.emptyList()).size());
assertEquals(0, TreeAsciiUtil.concatenateMultiLineParallel(null).size());
}
@Test
public void testConcatenateMultiLineParallelSingleElement() {
List<String> element = Arrays.asList("A", "B");
List<List<String>> elements = Arrays.asList(element);
List<String> result = TreeAsciiUtil.concatenateMultiLineParallel(elements);
assertEquals(2, result.size());
// We just get back the original element
assertEquals(element.size(), result.size());
}
@Test
public void testConcatenateMultiLineParallelComplexStructure() {
// Create a more complex structure to test alignment
List<String> element1 = Arrays.asList("Short");
List<String> element2 = Arrays.asList("Line1", "Line2 Long", "Line3");
List<String> element3 = Arrays.asList("X", "Y", "Z", "W");
List<List<String>> elements = Arrays.asList(element1, element2, element3);
List<String> result = TreeAsciiUtil.concatenateMultiLineParallel(elements);
assertEquals(10, result.size()); // 1 + 1 + 3 + 1 + 4 = 10 lines
// Verify some characteristics that should be true
// The center line should have horizontal connections
assertTrue(result.get(4).contains(TreeAsciiUtil.BOXH));
// First element should have a right connection on its center line
assertTrue(result.get(0).contains(TreeAsciiUtil.BOXDR));
// Last element should have a left connection on its center line
assertTrue(result.get(6).contains(TreeAsciiUtil.BOXUR));
}
} }
@@ -161,7 +161,7 @@ public class GuitarDiagramAnalyzerTests {
Tree tree = new GuitarDiagramAnalyzer().constructTree(netlist); Tree tree = new GuitarDiagramAnalyzer().constructTree(netlist);
String s = tree.toString(); String s = tree.toString();
assertEquals( assertEquals(
"((Volume.1-2 + ((Tone1.2-3 + Cap1) || (Tone2.2-3 + Cap2) || Pickup1.North<- || Pickup2.North<-)) || Volume.2-3)", "((Volume.1-2 + (Pickup1.North<- || Pickup2.North<- || (Tone1.2-3 + Cap1) || (Tone2.2-3 + Cap2))) || Volume.2-3)",
s); s);
} }