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

io.github.ericmedvet.jnb.buildable.Functions Maven / Gradle / Ivy

Go to download

Buildable components of jnb, a small framework for building objects from a named parameter map.

The newest version!
/*-
 * ========================LICENSE_START=================================
 * jnb-buildable
 * %%
 * Copyright (C) 2023 - 2024 Eric Medvet
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =========================LICENSE_END==================================
 */
package io.github.ericmedvet.jnb.buildable;

import io.github.ericmedvet.jnb.core.Cacheable;
import io.github.ericmedvet.jnb.core.Discoverable;
import io.github.ericmedvet.jnb.core.MathOp;
import io.github.ericmedvet.jnb.core.Param;
import io.github.ericmedvet.jnb.datastructure.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Discoverable(prefixTemplate = "function|f")
public class Functions {

  private static final Logger L = Logger.getLogger(Functions.class.getName());

  private Functions() {}

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction avg(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%.1f") String format) {
    Function, Double> f =
        vs -> vs.stream().mapToDouble(Number::doubleValue).average().orElseThrow();
    return FormattedNamedFunction.from(f, format, "avg").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction clip(
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param("range") DoubleRange range,
      @Param(value = "format", dS = "%.1f") String format) {

    Function f = range::clip;
    return FormattedNamedFunction.from(
            f, format, ("clip[" + format + ";" + format + "]").formatted(range.min(), range.max()))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction composition(
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param(value = "then", dNPM = "f.identity()") Function afterF) {
    return FormattedNamedFunction.from(afterF, FormattedFunction.format(afterF), NamedFunction.name(afterF))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  NamedFunction> distinct(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, Set> f = HashSet::new;
    return FormattedNamedFunction.from(f, format, "distinct").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  NamedFunction> each(
      @Param("mapF") Function mapF,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF) {
    Function, Collection> f = ts -> ts.stream().map(mapF).toList();
    return NamedFunction.from(f, "each[%s]".formatted(NamedFunction.name(mapF)))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  NamedFunction> filter(
      @Param(value = "condition", dNPM = "predicate.always()") Predicate condition,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, Collection> f =
        ts -> ts.stream().filter(condition).toList();
    return FormattedNamedFunction.from(f, format, "filter[%s]".formatted(condition))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  NamedFunction fromBase64(
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function f = s -> {
      try (ByteArrayInputStream bais =
              new ByteArrayInputStream(Base64.getDecoder().decode(s));
          ObjectInputStream ois = new ObjectInputStream(bais)) {
        return ois.readObject();
      } catch (Throwable t) {
        L.warning("Cannot deserialize due to %s".formatted(t));
        return null;
      }
    };
    return FormattedNamedFunction.from(f, format, "from.base64").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridCompactness(
      @Param(value = "predicate", dNPM = "f.nonNull()") Function predicate,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Double> f = g -> GridUtils.compactness(g, predicate::apply);
    return FormattedNamedFunction.from(f, format, "grid.compactness").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridCount(
      @Param(value = "predicate", dNPM = "f.nonNull()") Function predicate,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Integer> f = g -> GridUtils.count(g, predicate::apply);
    return FormattedNamedFunction.from(f, format, "grid.count").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridCoverage(
      @Param(value = "predicate", dNPM = "f.nonNull()") Function predicate,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Double> f = g -> (double) GridUtils.count(g, predicate::apply) / (double) (g.w() * g.h());
    return FormattedNamedFunction.from(f, format, "grid.coverage").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridElongation(
      @Param(value = "predicate", dNPM = "f.nonNull()") Function predicate,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Double> f = g -> GridUtils.elongation(g, predicate::apply);
    return FormattedNamedFunction.from(f, format, "grid.elongation").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridFitH(
      @Param(value = "predicate", dNPM = "f.nonNull()") Function predicate,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Integer> f = g -> GridUtils.fit(g, predicate::apply).h();
    return FormattedNamedFunction.from(f, format, "grid.fit.h").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridFitW(
      @Param(value = "predicate", dNPM = "f.nonNull()") Function predicate,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Integer> f = g -> GridUtils.fit(g, predicate::apply).w();
    return FormattedNamedFunction.from(f, format, "grid.fit.w").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridH(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Integer> f = Grid::h;
    return FormattedNamedFunction.from(f, format, "grid.h").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction gridW(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%2d") String format) {
    Function, Integer> f = Grid::w;
    return FormattedNamedFunction.from(f, format, "grid.w").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  Function identity() {
    Function f = x -> x;
    return NamedFunction.from(f, NamedFunction.IDENTITY_NAME);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction mathConst(
      @Param("v") double v, @Param(value = "format", dS = "%.1f") String format) {
    return FormattedNamedFunction.from(x -> v, format, format.formatted(v));
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction mathOp(
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param("args") List> args,
      @Param("op") MathOp op,
      @Param(value = "format", dS = "%.1f") String format) {
    Function f = y -> op.applyAsDouble(
        args.stream().mapToDouble(aF -> aF.apply(y).doubleValue()).toArray());
    return FormattedNamedFunction.from(
            f,
            format,
            "%s[%s]"
                .formatted(
                    op.toString().toLowerCase(),
                    args.stream().map(NamedFunction::name).collect(Collectors.joining(";"))))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static > FormattedNamedFunction max(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, C> f =
        cs -> cs.stream().max(Comparable::compareTo).orElseThrow();
    return FormattedNamedFunction.from(f, format, "max").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static > FormattedNamedFunction median(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, C> f =
        cs -> cs.stream().sorted().toList().get(Math.min(cs.size() - 1, Math.max(0, cs.size() / 2)));
    return FormattedNamedFunction.from(f, format, "median").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static > FormattedNamedFunction min(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, C> f =
        cs -> cs.stream().min(Comparable::compareTo).orElseThrow();
    return FormattedNamedFunction.from(f, format, "min").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  NamedFunction nTh(
      @Param("n") int n,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, T> f = ts -> n >= 0 ? ts.get(n) : ts.get(ts.size() + n);
    return FormattedNamedFunction.from(f, format, "[%d]".formatted(n)).compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction> nkTh(
      @Param("n") int n,
      @Param("k") int k,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, List> f = ts -> IntStream.range(0, ts.size())
        .filter(i -> (i % n) == k)
        .mapToObj(ts::get)
        .toList();
    return FormattedNamedFunction.from(f, format, "[%di+%d]".formatted(n, k))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction nonNull(
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function f = Objects::nonNull;
    return FormattedNamedFunction.from(f, format, "non.null").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction pairFirst(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, F> f = Pair::first;
    return FormattedNamedFunction.from(f, format, "first").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction pairSecond(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, S> f = Pair::second;
    return FormattedNamedFunction.from(f, format, "second").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static > FormattedNamedFunction percentile(
      @Param("p") double p,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, C> f = cs ->
        cs.stream().sorted().toList().get((int) Math.min(cs.size() - 1, Math.max(0, cs.size() * p / 100d)));
    return FormattedNamedFunction.from(f, format, "percentile[%02.0f]".formatted(p))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction quantized(
      @Param("q") double q,
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param(value = "format", dS = "%.1f") String format) {
    Function f = v -> q * Math.floor(v.doubleValue() / q + 0.5);
    return FormattedNamedFunction.from(f, format, "q[%s]".formatted(format.formatted(q)))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction sd(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%.1f") String format) {
    Function, Double> f = vs -> {
      double mean = vs.stream().mapToDouble(Number::doubleValue).average().orElseThrow();
      return Math.sqrt(vs.stream()
              .mapToDouble(v -> v.doubleValue() - mean)
              .map(v -> v * v)
              .sum()
          / ((double) vs.size()));
    };
    return FormattedNamedFunction.from(f, format, "sd").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction size(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%3d") String format) {
    Function, Integer> f = Collection::size;
    return FormattedNamedFunction.from(f, format, "size").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction> subList(
      @Param("from") double from,
      @Param("to") double to,
      @Param(value = "relative", dB = true) boolean relative,
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function, List> f =
        ts -> ts.subList((int) Math.min(Math.max(0, relative ? (from * ts.size()) : from), ts.size()), (int)
            Math.min(Math.max(0, relative ? (to * ts.size()) : to), ts.size()));
    return FormattedNamedFunction.from(f, format, "sub[%s%f-%f]".formatted(relative ? "%" : "", from, to))
        .compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  NamedFunction toBase64(
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function f = x -> {
      try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(baos)) {
        oos.writeObject(x);
        oos.flush();
        return Base64.getEncoder().encodeToString(baos.toByteArray());
      } catch (Throwable t) {
        L.warning("Cannot serialize due to %s".formatted(t));
        return "not-serializable";
      }
    };
    return FormattedNamedFunction.from(f, format, "to.base64").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  NamedFunction toString(
      @Param(value = "of", dNPM = "f.identity()") Function beforeF,
      @Param(value = "format", dS = "%s") String format) {
    Function f = Object::toString;
    return FormattedNamedFunction.from(f, format, "to.string").compose(beforeF);
  }

  @SuppressWarnings("unused")
  @Cacheable
  public static  FormattedNamedFunction uniqueness(
      @Param(value = "of", dNPM = "f.identity()") Function> beforeF,
      @Param(value = "format", dS = "%5.3f") String format) {
    Function, Double> f =
        ts -> (double) ts.stream().distinct().count() / (double) ts.size();
    return FormattedNamedFunction.from(f, format, "uniqueness").compose(beforeF);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy