package utp;


public class Functional {

	static public <K, S, T> IFunction<K, T> compose(IFunction<S, T> f, IFunction<K, S> g)
	{
		return x -> f.call(g.call(x));
		
		/*
		return new IFunction<K, T>() {
			public T call(K x) {
				return f.call(g.call(x));
			}
		};
		*/
	}
	
	static public <S, T> ILazyList<T> map(IFunction<S, T> fun, ILazyList<S> list)
	{
		return () -> Pair.asPair(fun.call(list.uncons().fst()), map(fun, list.uncons().snd()));
	}
	
	
	static public <S, T> IActiveGenerator<T> map(IFunction<S, T> fun, IActiveGenerator<S> gen)
	{
		return new IActiveGenerator<T>() {

			@Override
			public T value() throws StopException {
				return fun.call(gen.value());
			}

			@Override
			public IActiveGenerator<T> next() {
				return map(fun, gen.next());
			}
		};
	}
	
	static public <A, B, C> IActiveGenerator<C> map2(IFunction<A, IFunction<B, C>> fun, IActiveGenerator<A> genA, IActiveGenerator<B> genB)
	{
		return new IActiveGenerator<C>() {
			public C value() throws StopException {
				return fun.call(genA.value()).call(genB.value());
			}

			public IActiveGenerator<C> next() {
				return map2(fun, genA.next(), genB.next());
			}
		};
	}
	
	static public <T> IActiveGenerator<T> unfold(T init, IFunction<T, T> fun)
	{
		return new IActiveGenerator<T>() {
			public T value() throws StopException {
				return init;
			}

			public IActiveGenerator<T> next() {
				return unfold(fun.call(init), fun);
			}
			
		};
	}
	
	static public <S, T> IActiveGenerator<T> unfoldAcc(IActiveGenerator<S> gen , T init, IFunction<S, IFunction<T, T>> fun)
	{
		return new IActiveGenerator<T>() {
			public T value() throws StopException {
				return init;
			}

			public IActiveGenerator<T> next() {
				try {
					return unfoldAcc(gen.next(), fun.call(gen.value()).call(init), fun);
				} catch (StopException e) {
					return new ActiveNullGenerator<T>();
				}
			}
			
		};
	}
	
	
	
	public static <S, T, K> IFunction<S, IFunction<T, K>> curry(IFunction<Pair<S, T>, K> f)
	{
		return x -> y -> f.call(new Pair<S, T>(x, y));
	}
	
	public static <S, T, K>  IFunction<Pair<S, T>, K> uncurry(IFunction<S, IFunction<T, K>> f)
	{
		return p -> f.call(p.fst()).call(p.snd());
	}
	
	static public <T extends Comparable<T>> IActiveGenerator<T> unique(IActiveGenerator<T> genA, IActiveGenerator<T> genB)
	{
		return new ActiveUniqueGenerator<T>(genA, genB);
	}
	
	static public <T extends Comparable<T>> IActiveGenerator<T> uniqueT(IActiveGenerator<T> genA, IActiveGenerator<T> genB)
	{
		return new ActiveGenerator<T>(yield -> {
			IActiveGenerator<T> A = genA;
			IActiveGenerator<T> B = genB;
			while(true)
			{
				T a = A.value();
				T b = B.value();
				int cmp = a.compareTo(b);
				
				if(cmp < 0) A = A.next();
				else if(cmp > 0) B = B.next();
				else {
					yield.execute(a);
					do {
						A = A.next();
					} while(a.compareTo(A.value()) == 0);
				}
			}
		});
	}
	
}