All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.data.fingertrees.Deep Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
package fj.data.fingertrees;

import fj.*;
import fj.data.Option;
import fj.data.vector.V2;
import fj.data.vector.V3;
import fj.data.vector.V4;

import static fj.Function.constant;
import static fj.data.List.list;
import static fj.Function.flip;

/**
 * A finger tree with 1-4-digits on the left and right, and a finger tree of 2-3-nodes in the middle.
 */
public final class Deep extends FingerTree {
  private final V v;
  private final Digit prefix;
  private final FingerTree> middle;
  private final Digit suffix;

  Deep(final Measured m, final V v, final Digit prefix,
       final FingerTree> middle,
       final Digit suffix) {
    super(m);
    this.v = v;
    this.prefix = prefix;
    this.middle = middle;
    this.suffix = suffix;
  }

  /**
   * Returns the first few elements of this tree.
   *
   * @return the first few elements of this tree.
   */
  public Digit prefix() {
    return prefix;
  }

  /**
   * Returns a finger tree of the inner nodes of this tree.
   *
   * @return a finger tree of the inner nodes of this tree.
   */
  public FingerTree> middle() {
    return middle;
  }

  /**
   * Returns the last few elements of this tree.
   *
   * @return the last few elements of this tree.
   */
  public Digit suffix() {
    return suffix;
  }

  @Override public  B foldRight(final F> aff, final B z) {
    return prefix.foldRight(aff, middle.foldRight(flip(Node.foldRight_(aff)), suffix.foldRight(aff, z)));
  }

  @Override public A reduceRight(final F> aff) {
    return prefix.foldRight(aff, middle.foldRight(flip(Node.foldRight_(aff)), suffix.reduceRight(aff)));
  }

  @Override public  B foldLeft(final F> bff, final B z) {
    return suffix.foldLeft(bff, middle.foldLeft(Node.foldLeft_(bff), prefix.foldLeft(bff, z)));
  }

  @Override public A reduceLeft(final F> aff) {
    return suffix.foldLeft(aff, middle.foldLeft(Node.foldLeft_(aff), prefix.reduceLeft(aff)));
  }

  @Override public  FingerTree map(final F abf, final Measured m) {
    return new Deep(m, v, prefix.map(abf, m), middle.map(Node.liftM(abf, m), m.nodeMeasured()),
                          suffix.map(abf, m));
  }

  /**
   * Returns the sum of the measurements of this tree's elements, according to the monoid.
   *
   * @return the sum of the measurements of this tree's elements, according to the monoid.
   */
  public V measure() {
    return v;
  }

  /**
   * Pattern matching on the tree. Matches the function on the Deep tree.
   */
  @Override public  B match(final F, B> empty, final F, B> single,
                               final F, B> deep) {
    return deep.f(this);
  }

  @Override public FingerTree cons(final A a) {
    final Measured m = measured();
    final V measure = m.sum(m.measure(a), v);
    final MakeTree mk = mkTree(m);

    return prefix.match(
      one -> new Deep<>(m, measure, mk.two(a, one.value()), middle, suffix),
      two -> new Deep<>(m, measure, mk.three(a, two.values()._1(), two.values()._2()), middle, suffix),
      three -> new Deep<>(m, measure, mk.four(a, three.values()._1(), three.values()._2(), three.values()._3()), middle, suffix),
      four -> new Deep<>(m, measure, mk.two(a, four.values()._1()), middle.cons(mk.node3(four.values()._2(), four.values()._3(), four.values()._4())), suffix));
  }

  public FingerTree snoc(final A a) {
    final Measured m = measured();
    final V measure = m.sum(m.measure(a), v);
    final MakeTree mk = mkTree(m);

    return suffix.match(
      one -> new Deep<>(m, measure, prefix, middle, mk.two(one.value(), a)),
      two -> new Deep<>(m, measure, prefix, middle, mk.three(two.values()._1(), two.values()._2(), a)),
      three -> new Deep<>(m, measure, prefix, middle, mk.four(three.values()._1(), three.values()._2(), three.values()._3(), a)),
      four -> new Deep<>(m, measure, prefix, middle.snoc(mk.node3(four.values()._1(), four.values()._2(), four.values()._3())), mk.two(four.values()._4(), a)));
  }

  @Override public A head() {
    return prefix.match(
      One::value,
      two -> two.values()._1(),
      three -> three.values()._1(),
      four -> four.values()._1());
  }

  @Override public A last() {
    return suffix.match(
      One::value,
      two -> two.values()._2(),
      three -> three.values()._3(),
      four -> four.values()._4());
  }

  private static final  FingerTree deepL(final Measured measured, final Option> lOpt, final FingerTree> m, final Digit r) {
    return lOpt.option(
      P.lazy(() -> m.isEmpty() ? r.toTree() : mkTree(measured).deep(m.head().toDigit(), m.tail(), r)),
      (F, FingerTree>) l -> mkTree(measured).deep(l, m, r)
    );
  }

  private static final  FingerTree deepR(final Measured measured, final Option> rOpt, final FingerTree> m, final Digit l) {
    return rOpt.option(
      P.lazy(() -> m.isEmpty() ? l.toTree() : mkTree(measured).deep(l, m.init(), m.last().toDigit())),
      (F, FingerTree>) r -> mkTree(measured).deep(l, m, r)
    );
  }

  @Override public FingerTree tail() { return deepL(measured(), prefix.tail(), middle, suffix); }

  @Override public FingerTree init() { return deepR(measured(), suffix.init(), middle, prefix); }

  @Override public FingerTree append(final FingerTree t) {
    final Measured m = measured();
    return t.match(
      constant(this),
      single -> snoc(single.value()),
      deep -> new Deep<>(m, m.sum(measure(), deep.measure()), prefix,
        addDigits0(m, middle, suffix, deep.prefix, deep.middle), deep.suffix));
  }

  @Override P3, A, FingerTree> split1(final F predicate, final V acc) {
    final Measured m = measured();
    final V accL = m.sum(acc, prefix.measure());
    if (predicate.f(accL)) {
      final P3>, A, Option>> lxr = prefix.split1(predicate, acc);
      return P.p(lxr._1().option(new Empty<>(m), Digit::toTree), lxr._2(), deepL(m, lxr._3(), middle, suffix));
    } else {
      final V accM = m.sum(accL, middle.measure());
      if (predicate.f(accM)) {
        final P3>, Node, FingerTree>> mlXsMr = middle.split1(predicate, accL);
        final P3>, A, Option>> lxr = mlXsMr._2().split1(predicate, m.sum(accL, mlXsMr._1().measure()));
        return P.p(deepR(m, lxr._1(), mlXsMr._1(), prefix), lxr._2(), deepL(m, lxr._3(), mlXsMr._3(), suffix));
      } else {
        final P3>, A, Option>> lxr = suffix.split1(predicate, accM);
        return P.p(deepR(m, lxr._1(), middle, prefix), lxr._2(), lxr._3().option(new Empty<>(m), Digit::toTree));
      }
    }
  }

  @Override public P2 lookup(final F o, final int i) {
    final int spr = o.f(prefix.measure());
    if (i < spr) {
      return prefix.lookup(o, i);
    } else {
      final int spm = spr + o.f(middle.measure());
      if (i < spm) {
        final P2> p = middle.lookup(o, i - spr);
        return p._2().lookup(o, p._1());
      } else {
        return suffix.lookup(o, i - spm);
      }
    }
  }

  private static  FingerTree> addDigits0(final Measured m, final FingerTree> m1,
                                                             final Digit s1, final Digit p2,
                                                             final FingerTree> m2) {
    final MakeTree mk = mkTree(m);
    return s1.match(new F, FingerTree>>() {
      public FingerTree> f(final One one1) {
        return p2.match(new F, FingerTree>>() {
          public FingerTree> f(final One one2) {
            return append1(m, m1, mk.node2(one1.value(), one2.value()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Two two2) {
            final V2 vs = two2.values();
            return append1(m, m1, mk.node3(one1.value(), vs._1(), vs._2()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Three three) {
            final V3 vs = three.values();
            return append2(m, m1, mk.node2(one1.value(), vs._1()), mk.node2(vs._2(), vs._3()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Four four) {
            final V4 vs = four.values();
            return append2(m, m1, mk.node3(one1.value(), vs._1(), vs._2()), mk.node2(vs._3(), vs._4()), m2);
          }
        });
      }
    }, new F, FingerTree>>() {
      public FingerTree> f(final Two two1) {
        final V2 v1 = two1.values();
        return p2.match(new F, FingerTree>>() {
          public FingerTree> f(final One one) {
            return append1(m, m1, mk.node3(v1._1(), v1._2(), one.value()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Two two2) {
            final V2 v2 = two2.values();
            return append2(m, m1, mk.node2(v1._1(), v1._2()), mk.node2(v2._1(), v2._2()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Three three) {
            final V3 v2 = three.values();
            return append2(m, m1, mk.node3(v1._1(), v1._2(), v2._1()), mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Four four) {
            final V4 v2 = four.values();
            return append2(m, m1, mk.node3(v1._1(), v1._2(), v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F, FingerTree>>() {
      public FingerTree> f(final Three three1) {
        final V3 v1 = three1.values();
        return p2.match(new F, FingerTree>>() {
          public FingerTree> f(final One one) {
            return append2(m, m1, mk.node2(v1._1(), v1._2()), mk.node2(v1._3(), one.value()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Two two) {
            final V2 v2 = two.values();
            return append2(m, m1, mk.node3(v1), mk.node2(v2), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Three three2) {
            return append2(m, m1, mk.node3(v1), mk.node3(three2.values()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Four four) {
            return append3(m, m1, mk.node3(v1), mk.node2(four.values()._1(), four.values()._2()),
                           mk.node2(four.values()._3(), four.values()._4()), m2);
          }
        });
      }
    }, new F, FingerTree>>() {
      public FingerTree> f(final Four four1) {
        final V4 v1 = four1.values();
        return p2.match(new F, FingerTree>>() {
          public FingerTree> f(final One one) {
            return append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), one.value()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Two two) {
            final V2 v2 = two.values();
            return append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), v2._1(), v2._2()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Three three) {
            final V3 v2 = three.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), v2._1()),
                           mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F, FingerTree>>() {
          public FingerTree> f(final Four four2) {
            final V4 v2 = four2.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), v2._1(), v2._2()),
                           mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    });
  }

  private static  FingerTree> append1(final Measured m, final FingerTree> xs,
                                                          final Node a, final FingerTree> ys) {
    return xs.match(new F>, FingerTree>>() {
      public FingerTree> f(final Empty> empty) {
        return ys.cons(a);
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Single> single) {
        return ys.cons(a).cons(single.value());
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Deep> deep1) {
        return ys.match(new F>, FingerTree>>() {
          public FingerTree> f(final Empty> empty) {
            return xs.snoc(a);
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Single> single) {
            return xs.snoc(a).snoc(single.value());
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Deep> deep2) {
            final Measured> nm = m.nodeMeasured();
            return new Deep>(nm, m.sum(m.sum(deep1.v, nm.measure(a)), deep2.v), deep1.prefix,
                                           addDigits1(nm, deep1.middle, deep1.suffix, a, deep2.prefix, deep2.middle),
                                           deep2.suffix);
          }
        });
      }
    });
  }

  private static  FingerTree>> addDigits1(final Measured> m,
                                                                      final FingerTree>> m1,
                                                                      final Digit> x, final Node n,
                                                                      final Digit> y,
                                                                      final FingerTree>> m2) {
    final MakeTree> mk = mkTree(m);
    return x.match(new F>, FingerTree>>>() {
      public FingerTree>> f(final One> one1) {
        return y.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one2) {
            return append1(m, m1, mk.node3(one1.value(), n, one2.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append2(m, m1, mk.node2(one1.value(), n), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append2(m, m1, mk.node3(one1.value(), n, v2._1()), mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append2(m, m1, mk.node3(one1.value(), n, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Two> two1) {
        final V2> v1 = two1.values();
        return y.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append2(m, m1, mk.node2(v1), mk.node2(n, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append2(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            return append2(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node3(three.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n), mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()),
                           m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Three> three) {
        final V3> v1 = three.values();
        return y.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append2(m, m1, mk.node3(v1), mk.node2(n, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            final V2> v2 = two.values();
            return append2(m, m1, mk.node3(v1), mk.node3(n, v2._1(), v2._2()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append3(m, m1, mk.node3(v1), mk.node2(n, v2._1()), mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(v1), mk.node3(n, v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Four> four) {
        final V4> v1 = four.values();
        return y.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append2(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), n), mk.node2(two.values()),
                           m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, v2._1()),
                           mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n, v2._1()),
                           mk.node3(v2._2(), v2._3(), v2._4()), m2);
          }
        });
      }
    });
  }

  private static  FingerTree> append2(final Measured m, final FingerTree> t1,
                                                          final Node n1, final Node n2,
                                                          final FingerTree> t2) {
    return t1.match(new F>, FingerTree>>() {
      public FingerTree> f(final Empty> empty) {
        return t2.cons(n2).cons(n1);
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Single> single) {
        return t2.cons(n2).cons(n1).cons(single.value());
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Deep> deep) {
        return t2.match(new F>, FingerTree>>() {
          public FingerTree> f(final Empty> empty) {
            return deep.snoc(n1).snoc(n2);
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Single> single) {
            return deep.snoc(n1).snoc(n2).snoc(single.value());
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Deep> deep2) {
            return new Deep>(m.nodeMeasured(),
                                           m.sum(m.sum(m.sum(deep.measure(), n1.measure()), n2.measure()),
                                                 deep2.measure()), deep.prefix,
                                           addDigits2(m.nodeMeasured(), deep.middle, deep.suffix, n1, n2, deep2.prefix,
                                                      deep2.middle), deep2.suffix);
          }
        });
      }
    });
  }

  private static  FingerTree>> addDigits2(final Measured> m,
                                                                      final FingerTree>> m1,
                                                                      final Digit> suffix,
                                                                      final Node n1, final Node n2,
                                                                      final Digit> prefix,
                                                                      final FingerTree>> m2) {
    final MakeTree> mk = mkTree(m);
    return suffix.match(new F>, FingerTree>>>() {
      public FingerTree>> f(final One> one) {
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one2) {
            return append2(m, m1, mk.node2(one.value(), n1), mk.node2(n2, one2.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(three.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()),
                           m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Two> two) {
        final V2> v1 = two.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two2) {
            final V2> v2 = two2.values();
            return append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, v2._1(), v2._2()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, v2._1()), mk.node2(v2._2(), v2._3()),
                           m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, v2._1(), v2._2()),
                           mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Three> three) {
        final V3> v1 = three.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append2(m, m1, mk.node3(v1), mk.node3(n1, n2, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append3(m, m1, mk.node3(v1), mk.node2(n1, n2), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three2) {
            final V3> v2 = three2.values();
            return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, v2._1()), mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Four> four) {
        final V4> v1 = four.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node2(v1._4(), n1), mk.node2(n2, one.value()),
                           m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node3(three.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four2) {
            final V4> v2 = four2.values();
            return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node2(v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    });
  }

  @SuppressWarnings("unchecked")
  private static  FingerTree> append3(final Measured m, final FingerTree> t1,
                                                          final Node n1, final Node n2, final Node n3,
                                                          final FingerTree> t2) {
    final Measured> nm = m.nodeMeasured();
    return t1.match(new F>, FingerTree>>() {
      public FingerTree> f(final Empty> empty) {
        return t2.cons(n3).cons(n2).cons(n1);
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Single> single) {
        return t2.cons(n3).cons(n2).cons(n1).cons(single.value());
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Deep> deep) {
        return t2.match(new F>, FingerTree>>() {
          public FingerTree> f(final Empty> empty) {
            return deep.snoc(n1).snoc(n2).snoc(n3);
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Single> single) {
            return deep.snoc(n1).snoc(n2).snoc(n3).snoc(single.value());
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Deep> deep2) {
            return new Deep>(nm, nm.monoid().sumLeft(
                list(deep.v, n1.measure(), n2.measure(), n3.measure(), deep2.v)), deep.prefix,
                                           addDigits3(nm, deep.middle, deep.suffix, n1, n2, n3, deep2.prefix,
                                                      deep2.middle), deep2.suffix);
          }
        });
      }
    });
  }

  private static  FingerTree>> addDigits3(final Measured> m,
                                                                      final FingerTree>> m1,
                                                                      final Digit> suffix,
                                                                      final Node n1, final Node n2,
                                                                      final Node n3,
                                                                      final Digit> prefix,
                                                                      final FingerTree>> m2) {
    final MakeTree> mk = mkTree(m);
    return suffix.match(new F>, FingerTree>>>() {
      public FingerTree>> f(final One> one) {
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one2) {
            return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, one2.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            final V2> v2 = two.values();
            return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, v2._1(), v2._2()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, v2._1()), mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, v2._1(), v2._2()),
                           mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Two> two) {
        final V2> v1 = two.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append2(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, n3), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, v2._1()), mk.node2(v2._2(), v2._3()),
                           m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, v2._1()),
                           mk.node3(v2._2(), v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Three> three) {
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append3(m, m1, mk.node3(three.values()), mk.node2(n1, n2), mk.node2(n3, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append3(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three2) {
            return append3(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node3(three2.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append4(m, m1, mk.node3(three.values()), mk.node3(n1, n2, n3), mk.node2(v2._1(), v2._2()),
                           mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Four> four) {
        final V4> v1 = four.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node2(n3, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            final V2> v2 = two.values();
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node3(n3, v2._1(), v2._2()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2), mk.node2(n3, v2._1()),
                           mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four2) {
            final V4> v2 = four2.values();
            return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node3(n3, v2._1(), v2._2()), mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    });
  }

  @SuppressWarnings("unchecked")  
  private static  FingerTree> append4(final Measured m,
                                                          final FingerTree> t1,
                                                          final Node n1,
                                                          final Node n2,
                                                          final Node n3,
                                                          final Node n4,
                                                          final FingerTree> t2) {
    final Measured> nm = m.nodeMeasured();
    return t1.match(new F>, FingerTree>>() {
      public FingerTree> f(final Empty> empty) {
        return t2.cons(n4).cons(n3).cons(n2).cons(n1);
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Single> single) {
        return t2.cons(n4).cons(n3).cons(n2).cons(n1).cons(single.value());
      }
    }, new F>, FingerTree>>() {
      public FingerTree> f(final Deep> deep) {
        return t2.match(new F>, FingerTree>>() {
          public FingerTree> f(final Empty> empty) {
            return t1.snoc(n1).snoc(n2).snoc(n3).snoc(n4);
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Single> single) {
            return t1.snoc(n1).snoc(n2).snoc(n3).snoc(n4).snoc(single.value());
          }
        }, new F>, FingerTree>>() {
          public FingerTree> f(final Deep> deep2) {
            return new Deep>(nm, m.monoid().sumLeft(
                list(deep.v, n1.measure(), n2.measure(), n3.measure(), n4.measure(), deep2.v)), deep.prefix,
                                           addDigits4(nm, deep.middle, deep.suffix, n1, n2, n3, n4, deep2.prefix,
                                                      deep2.middle), deep2.suffix);
          }
        });
      }
    });
  }

  private static  FingerTree>> addDigits4(final Measured> m,
                                                                      final FingerTree>> m1,
                                                                      final Digit> suffix,
                                                                      final Node n1, final Node n2,
                                                                      final Node n3, final Node n4,
                                                                      final Digit> prefix,
                                                                      final FingerTree>> m2) {
    final MakeTree> mk = mkTree(m);
    return suffix.match(new F>, FingerTree>>>() {
      public FingerTree>> f(final One> one) {
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one2) {
            return append2(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, one2.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node2(n3, n4), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, v2._1()), mk.node2(v2._2(), v2._3()),
                           m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append3(m, m1, mk.node3(one.value(), n1, n2), mk.node3(n3, n4, v2._1()),
                           mk.node3(v2._2(), v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Two> two) {
        final V2> v1 = two.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node2(n2, n3), mk.node2(n4, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two2) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node2(two2.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node3(three.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append4(m, m1, mk.node3(v1._1(), v1._2(), n1), mk.node3(n2, n3, n4), mk.node2(v2._1(), v2._2()),
                           mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Three> three) {
        final V3> v1 = three.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node2(n4, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            final V2> v2 = two.values();
            return append3(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node3(n4, v2._1(), v2._2()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append4(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node2(n4, v2._1()), mk.node2(v2._2(), v2._3()),
                           m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append4(m, m1, mk.node3(v1), mk.node3(n1, n2, n3), mk.node3(n4, v2._1(), v2._2()),
                           mk.node2(v2._3(), v2._4()), m2);
          }
        });
      }
    }, new F>, FingerTree>>>() {
      public FingerTree>> f(final Four> four) {
        final V4> v1 = four.values();
        return prefix.match(new F>, FingerTree>>>() {
          public FingerTree>> f(final One> one) {
            return append3(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node3(n3, n4, one.value()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Two> two) {
            return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node2(n3, n4), mk.node2(two.values()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Three> three) {
            final V3> v2 = three.values();
            return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node3(n3, n4, v2._1()), mk.node2(v2._2(), v2._3()), m2);
          }
        }, new F>, FingerTree>>>() {
          public FingerTree>> f(final Four> four) {
            final V4> v2 = four.values();
            return append4(m, m1, mk.node3(v1._1(), v1._2(), v1._3()), mk.node3(v1._4(), n1, n2),
                           mk.node3(n3, n4, v2._1()), mk.node3(v2._2(), v2._3(), v2._4()), m2);
          }
        });
      }
    });
  }
}