view src/main/java/fj/Semigroup.java @ 0:fe80c1edf1be

add getLoop
author tatsuki
date Fri, 20 Mar 2015 21:04:03 +0900
parents
children
line wrap: on
line source

package fj;

import static fj.Function.curry;

import fj.data.Array;
import fj.data.List;
import fj.data.Natural;
import fj.data.NonEmptyList;
import fj.data.Option;
import fj.data.Set;
import fj.data.Stream;

import java.math.BigInteger;
import java.math.BigDecimal;

/**
 * Implementations must satisfy the law of associativity:
 * <ul>
 * <li><em>Associativity</em>; forall x. forall y. forall z. sum(sum(x, y), z) == sum(x, sum(y, z))</li>
 * </ul>
 *
 * @version %build.number%
 */
public final class Semigroup<A> {
  private final F<A, F<A, A>> sum;

  private Semigroup(final F<A, F<A, A>> sum) {
    this.sum = sum;
  }

  /**
   * Sums the two given arguments.
   *
   * @param a1 A value to sum with another.
   * @param a2 A value to sum with another.
   * @return The of the two given arguments.
   */
  public A sum(final A a1, final A a2) {
    return sum.f(a1).f(a2);
  }

  /**
   * Returns a function that sums the given value according to this semigroup.
   *
   * @param a1 The value to sum.
   * @return A function that sums the given value according to this semigroup.
   */
  public F<A, A> sum(final A a1) {
    return sum.f(a1);
  }

  /**
   * Returns a function that sums according to this semigroup.
   *
   * @return A function that sums according to this semigroup.
   */
  public F<A, F<A, A>> sum() {
    return sum;
  }

  /**
   * Constructs a semigroup from the given function.
   *
   * @param sum The function to construct this semigroup with.
   * @return A semigroup from the given function.
   */
  public static <A> Semigroup<A> semigroup(final F<A, F<A, A>> sum) {
    return new Semigroup<A>(sum);
  }

  /**
   * Constructs a semigroup from the given function.
   *
   * @param sum The function to construct this semigroup with.
   * @return A semigroup from the given function.
   */
  public static <A> Semigroup<A> semigroup(final F2<A, A, A> sum) {
    return new Semigroup<A>(curry(sum));
  }

  /**
   * A semigroup that adds integers.
   */
  public static final Semigroup<Integer> intAdditionSemigroup = semigroup(new F2<Integer, Integer, Integer>() {
    public Integer f(final Integer i1, final Integer i2) {
      return i1 + i2;
    }
  });

  /**
   * A semigroup that adds doubles.
   */
  public static final Semigroup<Double> doubleAdditionSemigroup = semigroup(new F2<Double, Double, Double>() {
    public Double f(final Double d1, final Double d2) {
      return d1 + d2;
    }
  });

  /**
   * A semigroup that multiplies integers.
   */
  public static final Semigroup<Integer> intMultiplicationSemigroup = semigroup(new F2<Integer, Integer, Integer>() {
    public Integer f(final Integer i1, final Integer i2) {
      return i1 * i2;
    }
  });

  /**
   * A semigroup that multiplies doubles.
   */
  public static final Semigroup<Double> doubleMultiplicationSemigroup = semigroup(new F2<Double, Double, Double>() {
    public Double f(final Double d1, final Double d2) {
      return d1 * d2;
    }
  });

  /**
   * A semigroup that yields the maximum of integers.
   */
  public static final Semigroup<Integer> intMaximumSemigroup = semigroup(Ord.intOrd.max);

  /**
   * A semigroup that yields the minimum of integers.
   */
  public static final Semigroup<Integer> intMinimumSemigroup = semigroup(Ord.intOrd.min);

  /**
   * A semigroup that adds big integers.
   */
  public static final Semigroup<BigInteger> bigintAdditionSemigroup =
      semigroup(new F2<BigInteger, BigInteger, BigInteger>() {
        public BigInteger f(final BigInteger i1, final BigInteger i2) {
          return i1.add(i2);
        }
      });

  /**
   * A semigroup that multiplies big integers.
   */
  public static final Semigroup<BigInteger> bigintMultiplicationSemigroup =
      semigroup(new F2<BigInteger, BigInteger, BigInteger>() {
        public BigInteger f(final BigInteger i1, final BigInteger i2) {
          return i1.multiply(i2);
        }
      });

  /**
   * A semigroup that yields the maximum of big integers.
   */
  public static final Semigroup<BigInteger> bigintMaximumSemigroup = semigroup(Ord.bigintOrd.max);

  /**
   * A semigroup that yields the minimum of big integers.
   */
  public static final Semigroup<BigInteger> bigintMinimumSemigroup = semigroup(Ord.bigintOrd.min);

  /**
   * A semigroup that adds big decimals.
   */
  public static final Semigroup<BigDecimal> bigdecimalAdditionSemigroup =
      semigroup(new F2<BigDecimal, BigDecimal, BigDecimal>() {
        public BigDecimal f(final BigDecimal i1, final BigDecimal i2) {
          return i1.add(i2);
        }
      });

  /**
   * A semigroup that multiplies big decimals.
   */
  public static final Semigroup<BigDecimal> bigdecimalMultiplicationSemigroup =
      semigroup(new F2<BigDecimal, BigDecimal, BigDecimal>() {
        public BigDecimal f(final BigDecimal i1, final BigDecimal i2) {
          return i1.multiply(i2);
        }
      });

  /**
   * A semigroup that yields the maximum of big decimals.
   */
  public static final Semigroup<BigDecimal> bigDecimalMaximumSemigroup = semigroup(Ord.bigdecimalOrd.max);

  /**
   * A semigroup that yields the minimum of big decimals.
   */
  public static final Semigroup<BigDecimal> bigDecimalMinimumSemigroup = semigroup(Ord.bigdecimalOrd.min);

  /**
   * A semigroup that multiplies natural numbers.
   */
  public static final Semigroup<Natural> naturalMultiplicationSemigroup =
      semigroup(new F2<Natural, Natural, Natural>() {
        public Natural f(final Natural n1, final Natural n2) {
          return n1.multiply(n2);
        }
      });

  /**
   * A semigroup that multiplies natural numbers.
   */
  public static final Semigroup<Natural> naturalAdditionSemigroup =
      semigroup(new F2<Natural, Natural, Natural>() {
        public Natural f(final Natural n1, final Natural n2) {
          return n1.add(n2);
        }
      });

  /**
   * A semigroup that yields the maximum of natural numbers.
   */
  public static final Semigroup<Natural> naturalMaximumSemigroup = semigroup(Ord.naturalOrd.max);

  /**
   * A semigroup that yields the minimum of natural numbers.
   */
  public static final Semigroup<Natural> naturalMinimumSemigroup = semigroup(Ord.naturalOrd.min);

  /**
   * A semigroup that adds longs.
   */
  public static final Semigroup<Long> longAdditionSemigroup = semigroup(new F2<Long, Long, Long>() {
    public Long f(final Long x, final Long y) {
      return x + y;
    }
  });

  /**
   * A semigroup that multiplies longs.
   */
  public static final Semigroup<Long> longMultiplicationSemigroup = semigroup(new F2<Long, Long, Long>() {
    public Long f(final Long x, final Long y) {
      return x * y;
    }
  });

  /**
   * A semigroup that yields the maximum of longs.
   */
  public static final Semigroup<Long> longMaximumSemigroup = semigroup(Ord.longOrd.max);

  /**
   * A semigroup that yields the minimum of longs.
   */
  public static final Semigroup<Long> longMinimumSemigroup = semigroup(Ord.longOrd.min);

  /**
   * A semigroup that ORs booleans.
   */
  public static final Semigroup<Boolean> disjunctionSemigroup = semigroup(new F2<Boolean, Boolean, Boolean>() {
    public Boolean f(final Boolean b1, final Boolean b2) {
      return b1 || b2;
    }
  });

  /**
   * A semigroup that XORs booleans.
   */
  public static final Semigroup<Boolean> exclusiveDisjunctionSemiGroup = semigroup(new F2<Boolean, Boolean, Boolean>() {
    public Boolean f(final Boolean p, final Boolean q) {
      return p && !q || !p && q;
    }
  });

  /**
   * A semigroup that ANDs booleans.
   */
  public static final Semigroup<Boolean> conjunctionSemigroup = semigroup(new F2<Boolean, Boolean, Boolean>() {
    public Boolean f(final Boolean b1, final Boolean b2) {
      return b1 && b2;
    }
  });

  /**
   * A semigroup that appends strings.
   */
  public static final Semigroup<String> stringSemigroup = semigroup(new F2<String, String, String>() {
    public String f(final String s1, final String s2) {
      return s1 + s2;
    }
  });

  /**
   * A semigroup that appends string buffers.
   */
  public static final Semigroup<StringBuffer> stringBufferSemigroup =
      semigroup(new F2<StringBuffer, StringBuffer, StringBuffer>() {
        public StringBuffer f(final StringBuffer s1, final StringBuffer s2) {
          return new StringBuffer(s1).append(s2);
        }
      });

  /**
   * A semigroup that appends string builders.
   */
  public static final Semigroup<StringBuilder> stringBuilderSemigroup =
      semigroup(new F2<StringBuilder, StringBuilder, StringBuilder>() {
        public StringBuilder f(final StringBuilder s1, final StringBuilder s2) {
          return new StringBuilder(s1).append(s2);
        }
      });

  /**
   * A semigroup for functions.
   *
   * @param sb The smeigroup for the codomain.
   * @return A semigroup for functions.
   */
  public static <A, B> Semigroup<F<A, B>> functionSemigroup(final Semigroup<B> sb) {
    return semigroup(new F2<F<A, B>, F<A, B>, F<A, B>>() {
      public F<A, B> f(final F<A, B> a1, final F<A, B> a2) {
        return new F<A, B>() {
          public B f(final A a) {
            return sb.sum(a1.f(a), a2.f(a));
          }
        };
      }
    });
  }

  /**
   * A semigroup for lists.
   *
   * @return A semigroup for lists.
   */
  public static <A> Semigroup<List<A>> listSemigroup() {
    return semigroup(new F2<List<A>, List<A>, List<A>>() {
      public List<A> f(final List<A> a1, final List<A> a2) {
        return a1.append(a2);
      }
    });
  }

  /**
   * A semigroup for non-empty lists.
   *
   * @return A semigroup for non-empty lists.
   */
  public static <A> Semigroup<NonEmptyList<A>> nonEmptyListSemigroup() {
    return semigroup(new F2<NonEmptyList<A>, NonEmptyList<A>, NonEmptyList<A>>() {
      public NonEmptyList<A> f(final NonEmptyList<A> a1, final NonEmptyList<A> a2) {
        return a1.append(a2);
      }
    });
  }

  /**
   * A semigroup for optional values.
   ** @return A semigroup for optional values.
   */
  public static <A> Semigroup<Option<A>> optionSemigroup() {
    return semigroup(new F2<Option<A>, Option<A>, Option<A>>() {
      public Option<A> f(final Option<A> a1, final Option<A> a2) {
        return a1.isSome() ? a1 : a2;
      }
    });
  }

  /**
   * A semigroup for optional values that take the first available value.
   *
   * @return A semigroup for optional values that take the first available value.
   */
  public static <A> Semigroup<Option<A>> firstOptionSemigroup() {
    return semigroup(new F2<Option<A>, Option<A>, Option<A>>() {
      public Option<A> f(final Option<A> a1, final Option<A> a2) {
        return a1.orElse(a2);
      }
    });
  }

  /**
   * A semigroup for optional values that take the last available value.
   *
   * @return A semigroup for optional values that take the last available value.
   */
  public static <A> Semigroup<Option<A>> lastOptionSemigroup() {
    return semigroup(new F2<Option<A>, Option<A>, Option<A>>() {
      public Option<A> f(final Option<A> a1, final Option<A> a2) {
        return a2.orElse(a1);
      }
    });
  }

  /**
   * A semigroup for streams.
   *
   * @return A semigroup for streams.
   */
  public static <A> Semigroup<Stream<A>> streamSemigroup() {
    return semigroup(new F2<Stream<A>, Stream<A>, Stream<A>>() {
      public Stream<A> f(final Stream<A> a1, final Stream<A> a2) {
        return a1.append(a2);
      }
    });
  }

  /**
   * A semigroup for arrays.
   *
   * @return A semigroup for arrays.
   */
  public static <A> Semigroup<Array<A>> arraySemigroup() {
    return semigroup(new F2<Array<A>, Array<A>, Array<A>>() {
      public Array<A> f(final Array<A> a1, final Array<A> a2) {
        return a1.append(a2);
      }
    });
  }

  /**
   * A semigroup for unary products.
   *
   * @param sa A semigroup for the product's type.
   * @return A semigroup for unary products.
   */
  public static <A> Semigroup<P1<A>> p1Semigroup(final Semigroup<A> sa) {
    return semigroup(new F2<P1<A>, P1<A>, P1<A>>() {
      public P1<A> f(final P1<A> a1, final P1<A> a2) {
        return new P1<A>() {
          public A _1() {
            return sa.sum(a1._1(), a2._1());
          }
        };
      }
    });
  }

  /**
   * A semigroup for binary products.
   *
   * @param sa A semigroup for the product's first type.
   * @param sb A semigroup for the product's second type.
   * @return A semigroup for binary products.
   */
  public static <A, B> Semigroup<P2<A, B>> p2Semigroup(final Semigroup<A> sa, final Semigroup<B> sb) {
    return semigroup(new F2<P2<A, B>, P2<A, B>, P2<A, B>>() {
      public P2<A, B> f(final P2<A, B> a1, final P2<A, B> a2) {
        return new P2<A, B>() {
          public A _1() {
            return sa.sum(a1._1(), a2._1());
          }

          public B _2() {
            return sb.sum(a1._2(), a2._2());
          }
        };
      }
    });
  }

  /**
   * A semigroup for the Unit value.
   */
  public static final Semigroup<Unit> unitSemigroup = semigroup(new F2<Unit, Unit, Unit>() {
    public Unit f(final Unit u1, final Unit u2) {
      return Unit.unit();
    }
  });

  /**
   * A semigroup for sets.
   *
   * @return a semigroup for sets.
   */
  public static <A> Semigroup<Set<A>> setSemigroup() {
    return semigroup(new F2<Set<A>, Set<A>, Set<A>>() {
      public Set<A> f(final Set<A> a, final Set<A> b) {
        return a.union(b);
      }
    });
  }

}