# ---------------------------------------------------------------------
#
# Copyright (c) 2012 University of Oxford
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# 
# ---------------------------------------------------------------------

# $Id: Functions.py 1047 2009-01-15 14:48:58Z graham $
#
"""
Miscellaneous functions for use with Python code, 
mostly based on Haskell library elements.
"""

from operator import concat, and_, or_

def concatMap(f,vs):
    """
    Map function over list and concatenate results.
    """
    return reduce( concat, map (f, vs), "")

def fst((a,b)):
    """
    First element of a pair.
    """
    return a

def snd((a,b)):
    """
    Second element of a pair.
    """
    return b

def iterAll(c,sentinel=None):
    """
    Like the built-in 'iter' function, except that when the supplied container 
    has no more objects to return an indefinite sequence of 'sentinel' values is 
    returned. (This is almost the converse of built-in iter(c,sentinel).)
    """
    i = iter(c)
    try:
        while True: yield i.next()
    except StopIteration:
        while True: yield sentinel

def zipAll(*lists):
    """
    A zip-iterator that, unlike the built-in zip function, keeps on returning
    tuples until all elements of the supplied lists have been returned.  When
    the values from any list have been exhausted, None values are returned.
    The iterator stops when all lists have been exhausted.
    """
    iters = map(iterAll,lists)
    while True:
        result = [i.next() for i in iters]
        if allEq(None,result): break
        yield tuple(result)
    return

def isEq(v):
    """
    Return a function that tests for equality with the supplied value.
    (Curried equality function.)
    """
    return (lambda v2: v==v2)

def isNe(v):
    """
    Return a function that tests for inequality with the supplied value.
    (Curried inequality function.)
    """
    return (lambda v2: v!=v2)

def all_orig(f,c):
    """
    Do all members of c satisfy f?
    """
    for i in c: 
        if not f(i): return False
    return True

def all(p, *lsargs):
    """
    Test if all sets of members from supplied lists satisfy predicate p
    """
    return reduce(and_, map(p, *lsargs), True)

def any(p, *lsargs):
    """
    Test if all sets of members from supplied lists satisfy predicate p
    """
    return reduce(or_, map(p, *lsargs), False)

def allEq(v,c):
    """
    Are all members of c equal to v?
    """
    return all(isEq(v),c)

def allNe(v,c):
    """
    Are all members of c not equal to v?
    """
    return all(isNe(v),c)