MetamorphicTestCaseFactoryWithPreformer.java

package com.github.dakusui.thincrest.metamor;


import com.github.dakusui.thincrest.metamor.internals.InternalUtils;
import com.github.dakusui.thincrest_pcond.forms.Printables;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;

import static java.util.Objects.requireNonNull;

public interface MetamorphicTestCaseFactoryWithPreformer<X, I, O, P, R> extends MetamorphicTestCaseFactory<X, I, O, R> {
  Function<IoPair<I, O>, P> metamorphicPreformer();

  Function<Dataset<P>, R> metamorphicReducer();

  @Override
  default Function<Dataset<IoPair<I, O>>, R> metamorphicTransformer() {
    return metamorphicPreformerStage().andThen(metamorphicReducer());
  }

  default Function<Dataset<IoPair<I, O>>, Dataset<P>> metamorphicPreformerStage() {
    return InternalUtils.createObservableProcessingPipeline(
        "preform",
        metamorphicPreformerToIoContextCallback(metamorphicPreformer()),
        inputResolverSequenceFactory().count(),
        ioVariableNameFormatter(),
        ioVariableName());
  }

  default Function<IoContext<IoPair<I, O>, P>, Function<IoPair<I, O>, P>> metamorphicPreformerToIoContextCallback(Function<IoPair<I, O>, P> preformer) {
    return Printables.function(
        () -> "  " + preformer,
        c -> Printables.function(
            () -> String.format("preform[%s]:%s", c, preformer),
            preformer));
  }

  IntFunction<String> ioVariableNameFormatter();

  class Impl<X, I, O, P, R> implements MetamorphicTestCaseFactoryWithPreformer<X, I, O, P, R> {

    private final Function<I, O>                          fut;
    private final InputResolver.Sequence.Factory<X, I, O> inputResolverSequenceFactory;
    private final Function<IoPair<I, O>, P>               preformer;
    private final Function<Dataset<P>, R>                 reducer;
    private final Predicate<R>                            checker;
    private final String                                  ioVariableName;
    private final String                                  inputVariableName;

    public Impl(Function<I, O> fut, InputResolver.Sequence.Factory<X, I, O> inputResolverSequenceFactory, Function<IoPair<I, O>, P> preformer, Function<Dataset<P>, R> reducer, Predicate<R> checker, String inputVariableName, String ioVariableName) {
      this.fut = requireNonNull(fut);
      this.inputResolverSequenceFactory = inputResolverSequenceFactory;
      this.preformer = requireNonNull(preformer);
      this.reducer = requireNonNull(reducer);
      this.checker = requireNonNull(checker);
      this.inputVariableName = requireNonNull(inputVariableName);
      this.ioVariableName = requireNonNull(ioVariableName);
    }


    @Override
    public Function<I, O> fut() {
      return this.fut;
    }

    @Override
    public InputResolver.Sequence.Factory<X, I, O> inputResolverSequenceFactory() {
      return this.inputResolverSequenceFactory;
    }

    @Override
    public Function<IoPair<I, O>, P> metamorphicPreformer() {
      return this.preformer;
    }

    @Override
    public Function<Dataset<P>, R> metamorphicReducer() {
      return Printables.function(() -> "reduce:" + reducer, this.reducer);
    }

    @Override
    public Predicate<R> metamorphicChecker() {
      return this.checker;
    }

    @Override
    public String inputVariableName() {
      return this.inputVariableName;
    }

    @Override
    public IntFunction<String> inputVariableNameFormatter() {
      return i -> this.inputVariableName + "[" + i + "]";
    }

    @Override
    public String ioVariableName() {
      return this.ioVariableName;
    }

    @Override
    public IntFunction<String> ioVariableNameFormatter() {
      return i -> this.ioVariableName + "[" + i + "]";
    }
  }

  class Builder<X, I, O, P, R> extends BuilderBase<Builder<X, I, O, P, R>, X, I, O, R> {
    private Function<Dataset<P>, R>   reducer;
    private Function<IoPair<I, O>, P> preformer;

    public Builder() {
    }

    @SuppressWarnings("unchecked")
    @Override
    public <BB extends BuilderBase<BB, XX, I, O, R>, XX> BB sourceValueType(XX sourceType) {
      return (BB) this.<Builder<XX, I, O, P, R>, XX>newBuilderWithSpecifiedSourceType(Builder::new);
    }

    @Override
    public <PP> Builder<X, I, O, PP, R> preformer(Function<IoPair<I, O>, PP> preformer) {
      Builder<X, I, O, PP, R> ret = this.withPreformer();
      ret.preformer = preformer;
      return ret;
    }

    public <QQ> Builder<X, I, O, QQ, R> preform(Function<P, QQ> preformer) {
      Function<IoPair<I, O>, P> currentPreformer = this.preformer;
      if (currentPreformer == null)
        throw new IllegalStateException();
      Builder<X, I, O, QQ, R> ret = this.withPreformer();
      ret.preformer = currentPreformer.andThen(preformer);
      return ret;
    }

    public <QQ> Builder<X, I, O, QQ, R> preform(String name, Function<P, QQ> preformer) {
      return this.preform(Printables.function(name, preformer));
    }

    public Builder<X, I, O, P, R> reducer(Function<Dataset<P>, R> reducer) {
      this.reducer = requireNonNull(reducer);
      return this;
    }

    public Builder<X, I, O, P, R> reduce(Function<Dataset<P>, R> reducer) {
      return this.reducer(reducer);
    }

    public Builder<X, I, O, P, R> reduce(String reducerName, Function<Dataset<P>, R> reducer) {
      return this.reduce(Printables.function(reducerName, reducer));
    }

    public Builder<X, I, O, P, Proposition> propositionFactory(Function<Dataset<P>, Proposition> pf) {
      return this
          .<Builder<X, I, O, P, Proposition>, Proposition>newBuilderWithSpecifiedRelationType(Builder::new)
          .preformer(this.preformer)
          .reducer(pf)
          .checker(PropositionPredicate.INSTANCE);
    }

    public MetamorphicTestCaseFactory<X, I, O, Proposition> proposition(Function<Object[], String> propositionFormatter, Predicate<Dataset<P>> p) {
      return this.propositionFactory(
          Proposition.Factory.create(
              p,
              propositionFormatter,
              i -> this.outputVariableName + "[" + i + "]",
              this.inputResolverSequenceFactoryProvider.count()))
          .build();
    }

    public MetamorphicTestCaseFactory<X, I, O, Proposition> proposition(String propositionName, Predicate<Dataset<P>> p) {
      return this.proposition(args -> MessageFormat.format(propositionName, Arrays.stream(args).map(Objects::toString).toArray()), p);
    }

    public MetamorphicTestCaseFactoryWithPreformer<X, I, O, P, R> build() {
      return new Impl<>(fut, inputResolverSequenceFactoryProvider.get(), preformer, reducer, checker, inputVariableName, ioVariableName);
    }
  }
}