### Question:
Node { static Node concat(Node rope1, Node rope2) { int rope1Height = rope1.getHeight(); int rope2Height = rope2.getHeight(); switch (compare(rope1Height, rope2Height)) { case LESS: { List<Node> rope2Children = rope2.getChildren(); if (rope1Height == rope2Height - 1 && rope1.isValidNode()) { return mergeNodes(Collections.singletonList(rope1), rope2Children); } Node newRope = concat(rope1, rope2Children.get(0)); List<Node> rope2ChildrenSubList = rope2Children.subList(1, rope2Children.size()); if (newRope.getHeight() == rope2Height - 1) { return mergeNodes(Collections.singletonList(newRope), rope2ChildrenSubList); } else { return mergeNodes(newRope.getChildren(), rope2ChildrenSubList); } } case EQUAL: { if (rope1.isValidNode() && rope2.isValidNode()) { return Node.fromPieces(Arrays.asList(rope1, rope2)); } if (rope1Height == 0) { return mergeLeaves(rope1, rope2); } return mergeNodes(rope1.getChildren(), rope2.getChildren()); } case GREATER: { List<Node> rope1Children = rope1.getChildren(); if (rope2Height == rope1Height - 1) { return Node.mergeNodes(rope1Children, Collections.singletonList(rope2)); } int lastChildIndex = rope1Children.size() - 1; Node newRope = Node.concat(rope1Children.get(lastChildIndex), rope2); List<Node> rope1ChildrenSubList = rope1Children.subList(1, lastChildIndex + 1); if (newRope.getHeight() == rope1Height - 1) { return mergeNodes(rope1ChildrenSubList, Collections.singletonList(newRope)); } else { return mergeNodes(rope1ChildrenSubList, newRope.getChildren()); } } default: throw new IllegalStateException("unreachable state"); } } Node(NodeBody nodeBody); @Override boolean equals(Object o); @Override int hashCode(); @Override String toString();  }### Answer:
@Test public void testConcat() { Node hello = Node.fromString("hello"); Node world = Node.fromString(" world"); Node helloWorld = Node.concat(hello, world); assertEquals(helloWorld.getString(), "hello world"); helloWorld = hello.concat(world); assertEquals(helloWorld.getString(), "hello world"); }