def reshape(Fn, A, B): """ implement dyadic ⍴ """ assertNotArray(A) if A.isScalarLike(): Apy = confirmInteger(A.scalarToPy()) assertTrue(Apy >= 0, "DOMAIN ERROR") if Apy == 0: return makeEmptyVector(B.prototype()) Bpy = B.castToVectorPy() return makeVector(Fn(Apy, Bpy), Apy, B.prototype()) if A.isEmptyVector(): Bpy = B.castToVectorPy() return makeVector(Fn(1, Bpy), 1, B.prototype()) Apy = tuple([confirmInteger(I) for I in A.vectorToPy()]) assertTrue(Apy == (2, 2), "WIP - MATRIX ERROR") for I in Apy: assertTrue(I >= 0, "DOMAIN ERROR") Bpy = B.castToVectorPy() return makeArray(Fn(Apy, Bpy), Apy, 2 * B.prototype())
def enlist(Fn, B): """ implement monadic ∊ """ if B.isEmptyVector(): return B if B.isVectorLike(): return makeVector(Fn(B.vectorToPy()), -1, B.prototype()) return makeVector(Fn(B.arrayToPy()), -1, B.prototype())
def unravel(_, B): """ implement monadic , """ if B.isArray(): return makeVector(B.arrayToPy(), B.elementCount(), B.prototype()) if B.isVector(): return B return makeVector(B.vectorToPy(), 1, B.prototype())
def rho(_, B): """ implement monadic ⍴ """ if B.isArray(): return makeVector(B.dimension(), B.rank()) if B.isVector(): return makeVector((B.tally(), ), 1) return makeEmptyVector()
def maths(Fn, A, B): """ the basic recursive map for dyadic mathematical functions """ if B.isArray(): if A.isArray(): # Check ranks are equal Rpy = iterator.maths(maths, Fn, A, B) elif A.isVector(): # Check length is compatible Rpy = iterator.maths(maths, Fn, A.vectorIterator(), B) else: Rpy = iterator.maths(maths, Fn, A.scalarIterator(), B) return makeArray(Rpy, B.dimension(), B.prototype()) if A.isArray(): if B.isVector(): # Check length is compatible Rpy = iterator.maths(maths, Fn, A, B.vectorIterator()) else: Rpy = iterator.maths(maths, Fn, A, B.scalarIterator()) return makeArray(Rpy, A.dimension(), A.prototype()) if B.isVector(): if A.isVector(): if B.isEmptyVector(): assertTrue(A.isEmptyVector(), "LENGTH ERROR") Rpy = iterator.maths(maths, Fn, A, B) elif B.isEmptyVector(): return B else: Rpy = iterator.maths(maths, Fn, A.scalarIterator(), B) return makeVector(Rpy, B.dimension(), B.prototype()) if A.isVector(): if A.isEmptyVector(): return A else: Rpy = iterator.maths(maths, Fn, A, B.scalarIterator()) return makeVector(Rpy, A.dimension(), A.prototype()) return makeScalar(iterator.maths(maths, Fn, A, B))
def expandFirst(Fn, A, B): """ implement dyadic ⍀ """ assertNotArray(A) if B.isScalar() and A.isEmptyVector(): return makeEmptyVector(B.prototype()) if B.isVectorLike(): if A.isScalar(): Apy = confirmInteger(A.scalarToPy()) assertTrue(Apy >= 0, "LENGTH ERROR") if Apy == 0: return makeVector(B.prototype(), 1, B.prototype()) Rpy = Fn(A.promoteScalarToVectorPy(), B.vectorToPy(), B.padFill()) return makeVector(Rpy, -1, B.prototype()) if B.isArray(): assertTrue(B.dimension() == (2, 2), "WIP - MATRIX ERROR") Apy = [confirmInteger(I) for I in A.vectorToPy()] if A.isScalarLike(): if Apy[0] < B.dimension()[0]: assertError("LENGTH ERROR") else: assertError("DOMAIN ERROR") else: Lpy = sum(abs(I) if I != 0 else 1 for I in Apy) Dpy = list(B.dimension()) Dpy[-1] = Lpy assertTrue(tuple(Dpy) == (2, 2), "WIP - MATRIX ERROR") Rpy = [] for Bit in B.arrayByFirstAxis(): Rpy += Fn(A.promoteScalarToVectorPy(), Bit.vectorToPy(), B.padFill()[-1]) Rpy = monadicTranspose(Rpy, Dpy) Dpy[0], Dpy[-1] = Dpy[-1], Dpy[0] return makeArray(Rpy, Dpy, B.prototype())
def drop(Fn, A, B): """ implement dyadic ↓ """ assertNotArray(A) if B.isScalar(): if A.isEmptyVector(): return B assertScalarLike(A) Apy = confirmInteger(A.scalarToPy()) if Apy == 0: return makeVector(B.vectorToPy(), 1, B.prototype()) return makeEmptyVector(B.prototype()) if B.isVector(): if A.isEmptyVector(): Apy = 1 else: assertScalarLike(A) Apy = confirmInteger(A.scalarToPy()) if Apy == 0: return B Rpy = Fn(Apy, B.vectorToPy()) return makeVector(Rpy, -1, B.prototype()) Apy = [confirmInteger(I) for I in A.vectorToPy()] assertTrue(B.dimension() == (2, 2), "WIP - MATRIX ERROR") assertTrue(len(Apy) == B.rank(), "LENGTH ERROR") Rpy = [] for Bit in Fn(Apy[0], B.arrayByLastAxis()): Rpy += Fn(Apy[1], Bit.vectorToPy()) Dpy = [max(0, I[1] - abs(I[0])) for I in zip(Apy, B.dimension())] return makeArray(Rpy, Dpy, B.prototype())
def take(Fn, A, B): """ implement dyadic ↑ """ assertNotArray(A) if B.isScalar() or B.isEmptyVector(): if A.isEmptyVector(): return B if B.isVectorLike(): assertScalarLike(A) Apy = confirmInteger(A.scalarToPy()) if Apy == 0: return makeEmptyVector(B.prototype()) Rpy = Fn(Apy, B.vectorToPy(), B.padFill()) return makeVector(Rpy, abs(Apy), B.prototype()) Apy = [confirmInteger(I) for I in A.vectorToPy()] assertTrue(B.dimension() == (2, 2), "WIP - MATRIX ERROR") assertTrue(len(Apy) == B.rank(), "LENGTH ERROR") Rpy = [] for Bit in Fn(Apy[0], B.arrayByLastAxis(), B.padFill()): Rpy += Fn(Apy[1], Bit.vectorToPy(), Bit.padFill()) return makeArray(Rpy, [abs(I) for I in Apy], B.prototype())
def grade(descending, B): """ implement monadic ⍒ and ⍋ """ def _sortKey(Y): """ sort key depends on type as well value """ if isinstance(Y, aplQuantity): return 2 * Y.tally(), Y if isinstance(Y, str): return 0, ord(Y) return 1, Y assertNotScalar(B, "DOMAIN ERROR") if B.isArray(): assertTrue(B.dimension() == (2, 2), "WIP - MATRIX ERROR") Bpy = [Bit.vectorToPy() for Bit in B.arrayByLastAxis()] Key = lambda X: [_sortKey(Y) for Y in Bpy[X]] if B.isVector(): B.resolve() Bpy = B.vectorToPy() Key = lambda X: _sortKey(Bpy[X]) Len = B.tally() S = list(range(Len)) S.sort(key=Key, reverse=descending) return makeVector(iterator.grade(S), Len)
def index(Fn, A, B): """ implement dyadic ⍳ """ assertNotArray(A) if B.isEmptyVector(): return makeEmptyVector(B.prototype()) if A.isEmptyVector(): Rpy = scalarIterator(indexOrigin(), B.elementCount(), B.expressionToGo) elif B.isArray(): Rpy = Fn(A.vectorToPy(), B.arrayToPy()) else: Rpy = Fn(A.vectorToPy(), B.vectorToPy()) if B.isArray(): return makeArray(Rpy, B.dimension()) if B.isVector(): return makeVector(Rpy, B.dimension()) return makeScalar(Rpy)
def encode(Fn, A, B): """ implement dyadic ⊤ """ if B.isEmptyVector(): # Simplistic return makeEmptyVector() if A.isEmptyVector(): # Simplistic return makeEmptyVector() if B.isArray(): if A.isScalarLike(): Rpy = [] Apy = A.vectorToPy() for Bpy in B.arrayToPy(): Rpy.append(Fn(Apy, Bpy).__next__()) return makeArray(Rpy, B.dimension()) assertError("WIP - RANK ERROR") if B.isVector(): assertTrue(B.tally() == 2, "WIP - LENGTH ERROR") if A.isArray(): assertError("WIP - RANK ERROR") if A.isVector(): assertTrue(A.tally() == 2, "WIP - LENGTH ERROR") Rpy, Apy = [], A.vectorToPy() for Bpy in B.vectorToPy(): Rpy += Fn(Apy, Bpy) Rpy = monadicTranspose(Rpy, (A.tally(), B.tally())) return makeArray(Rpy, (A.tally(), B.tally())) Rpy, Apy = [], A.vectorToPy() for Bpy in B.vectorToPy(): Rpy += Fn(Apy, Bpy) if A.isArray(): Rpy, Bpy = [], B.scalarToPy() for Ait in A.arrayByFirstAxis(): Rpy += Fn(Ait.vectorToPy(), Bpy) return makeArray(Rpy, A.dimension(), B.prototype()) if A.isVector(): Rpy = Fn(A.vectorToPy(), B.scalarToPy()) return makeVector(Rpy, A.tally()) Rpy = Fn(A.vectorToPy(), B.scalarToPy()) return makeScalar(Rpy)
def decode(Fn, A, B): """ implement dyadic ⊥ """ if B.isArray(): if A.isScalarLike(): Rpy = [] for Bit in B.arrayByFirstAxis(): Rpy.append(Fn(A.promoteScalarToVectorPy(), Bit)) return makeVector(Rpy, B.dimension()[0], B.prototype()) if A.isVector(): assertNotTrue(A.isEmptyVector(), "DOMAIN ERROR") assertTrue(A.tally() == B.dimension()[0], "LENGTH ERROR") Rpy = [] for Bit in B.arrayByFirstAxis(): Rpy.append(Fn(A.vectorToPy(), Bit)) return makeVector(Rpy, A.dimension(), B.prototype()) assertTrue(A.rank() == B.rank(), "WIP - RANK ERROR") assertTrue(A.dimension()[-1] == B.dimension()[0], "LENGTH ERROR") Rpy = [] for Ait in A.arrayByLastAxis(): for Bit in B.arrayByFirstAxis(): Rpy.append(Fn(Ait, Bit)) return makeArray(Rpy, A.dimension(), B.prototype()) if B.isVector(): if A.isEmptyVector() or B.isEmptyVector(): return makeScalar(0) return makeScalar(Fn(A.promoteScalarToVectorPy(), B.vectorToPy())) if A.isScalar(): assertNumeric(A) assertNumeric(B) return B return makeScalar(Fn(A.vectorToPy(), B.scalarIterator()))
def deal(Fn, A, B): """ implement deal (dyadic ?) """ assertScalarLike(A, "RANK ERROR") assertScalarLike(B, "RANK ERROR") Apy = confirmInteger(A.scalarToPy()) return makeVector(Fn(Apy, B.scalarToPy()), Apy)
def unique(Fn, B): """ implement monadic ∪ """ assertNotArray(B, "RANK ERROR") if B.isScalarLike() or B.isEmptyVector(): return B return makeVector(Fn(B.vectorToPy()), -1, B.prototype())
def evaluate(expression, cio): """ evaluate an APL expression called from parse and routines parse calls so beware indirect recursion """ try: expr = expression.lstrip() if not expr: assertError("SYNTAX ERROR") lhs, expr = parse(expr, cio) if lhs == []: lhs = None elif len(lhs) > 1: lhs = makeVector(tuple(lhs), -1, None) elif isinstance(lhs[0], aplQuantity): lhs = lhs[0] else: lhs = makeScalar(lhs[0], None) if not expr: return lhs if cio.newStmt: if lhs is None and expr[0] == '⊣': cio.hushImplicit = True cio.newStmt = False operator = operatorFunction(expr[1:].lstrip()) if lhs is None else None if operator is None and lhs is None: function = monadicFunction(expr) else: identity, function = dyadicFunction(expr) if not operator is None: expr = expr[1:].lstrip() rhs = evaluate(expr[1:], cio) rhs.expressionToGo = expr if operator is None: result = function(rhs) if lhs is None else function(lhs, rhs) else: result = operator(function, identity, rhs) return result.resolve() if eagerEvaluation() else result except aplException as error: if not error.expr: error.expr = expr raise error
def maths(Fn, B): """ the basic recursive map for monadic mathematical functions """ if B.isArray(): return makeArray(iterator.maths(maths, Fn, B), B.dimension()) if B.isVector(): return makeVector(iterator.maths(maths, Fn, B), B.dimension(), B.prototype()) return makeScalar(iterator.maths(maths, Fn, B))
def _aplVector(self, Y): """ create, for return, a non-empty APL vector quantity else raise StopIteration) """ V = self._V if V == []: raise StopIteration self._V = [] if Y is None else [Y] return makeVector(tuple(V), len(V), (makePrototype(V[0]), ))
def tail(Fn, B): """ implement monadic ↓ """ assertNotArray(B, "RANK ERROR") if B.isScalarLike(): return makeEmptyVector(B.prototype()) if B.isEmptyVector(): return B return makeVector(Fn(B.vectorToPy()), B.dimension() - 1, B.prototype())
def set2set(Fn, A, B): """ implement dyadic ∪ and ∩ """ assertNotArray(A) assertNotArray(B) if A.isEmptyVector() and B.isEmptyVector(): return B Rpy = Fn(A.vectorToPy(), B.vectorToPy()) return makeVector(Rpy, -1, A.prototype())
def without(Fn, A, B): """ implement dyadic ~ """ assertNotArray(A) if B.isArray(): Rpy = Fn(A.vectorToPy(), B.arrayToPy()) else: Rpy = Fn(A.vectorToPy(), B.vectorToPy()) return makeVector(Rpy, -1, A.prototype())
def transpose(_, A, B): """ implement dyadic ⍉ """ assertNotArray(A) if B.isArray(): assertTrue(A.tally() == B.rank(), "LENGTH ERROR") IO = indexOrigin() Apy = [confirmInteger(I) - IO for I in A.vectorToPy()] high = B.rank() + 1 assertTrue(all(x >= 0 and x <= high for x in Apy), "DOMAIN ERROR") assertTrue(sum(Apy) <= sum(range(B.rank())), "DOMAIN ERROR") Rpy = dyadicTranspose(B.arrayToPy(), Apy, B.dimension()) def newDimensions(A, B): """ determine dimensions of the transposed result """ R = list(range(len(A))) for I in R: H = [B[X] for X in R if A[X] == I] if H: yield min(H) Dpy = tuple(newDimensions(Apy, B.dimension())) if len(Dpy) == 1: return makeVector(Rpy, Dpy[0], None) return makeArray(Rpy, Dpy, None) if B.isVector(): assertScalarLike(A) Apy = confirmInteger(A.scalarToPy()) assertTrue(Apy == 1, "DOMAIN ERROR") return B assertEmptyVector(A) return B
def __next__(self): V = [] # the vector (row) to return L = self._L try: while L: V.append(self._B.__next__()) L -= 1 except StopIteration: if L != self._L: assertError("lastAxisIterator: oops") raise StopIteration return makeQuantity.makeVector(V, self._L)
def iota(Fn, B): """ implement monadic ⍳ the operand is scalar: there is no map as such """ if B.isScalar(): Bpy = confirmInteger(B.scalarToPy()) assertTrue(Bpy >= 0, "DOMAIN ERROR") return makeVector(Fn(Bpy), Bpy) assertNotVector(B, "WIP - LENGTH ERROR") assertNotArray(B)
def reverselast(Fn, B): """ implement monadic ⌽ """ if B.isArray(): assertTrue(B.dimension() == (2, 2), "WIP - MATRIX ERROR") Rpy = [] for Bit in B.arrayByLastAxis(): Rpy += Fn(Bit.vectorToPy()) return makeArray(Rpy, B.dimension(), None) if B.isVector() and not B.isEmptyVector(): return makeVector(Fn(B.vectorToPy()), B.dimension(), None) return B
def evaluateBoxTickIO(expr, cio): """ evaluate, print (possibly) and return an intermediary result """ texpr = expr[1:].lstrip() if texpr and texpr[0] == '←': # output operator newStmt = cio.newStmt cio.newStmt = False rhs = evaluate(texpr[1:], cio) if newStmt else evaluate( texpr[1:], cio).resolve() if rhs.padFill() == ' ': cio.userPromptLength = rhs.dimension() cio.printExplicit(rhs, '') elif rhs.isVector(): cio.printExplicit(makeVector((rhs, ), 1), '') else: cio.printExplicit(rhs, '') if newStmt: cio.hushImplicit = True rhs = None return rhs, len(expr) else: # input operator lcio = shallowcopy(cio) lcio.startNewLine() lcio.prompt = "" try: expr = lcio.read(lcio.inFile) except EOFError: assertError("EOF ERROR") value = makeString(cio.userPromptLength * ' ' + expr, False) cio.endOfLine = '\n' return value, 1
def concatenate(Fn, A, B): """ implement dyadic , """ assertNotArray(A, "WIP - RANK ERROR") assertNotArray(B, "WIP - RANK ERROR") prototype = B.prototype() if A.isEmptyVector() else A.prototype() Apy = 1 if A.isScalar() else A.dimension() Bpy = 1 if B.isScalar() else B.dimension() dimension = -1 if (Apy < 0 or Bpy < 0) else (Apy + Bpy) Rpy = Fn(A.vectorToPy(), B.vectorToPy()) return makeVector(Rpy, dimension, prototype)
def find(Fn, A, B): """ implement dyadic ∊ """ A.resolve() Apy = A.arrayToPy() if A.isArray() else A.vectorToPy() Bpy = B.arrayToPy() if B.isArray() else B.vectorToPy() Rpy = Fn(Apy, Bpy) if B.isArray(): return makeArray(Rpy, B.dimension(), B.prototype()) if B.isVector(): return makeVector(Rpy, B.dimension(), B.prototype()) return makeScalar(Rpy, B.prototype())
def scanLast(Fn, _, B): """ the map for the \ (scan along last axis) operator """ if B.isScalar(): return B if B.isVector(): if B.isEmptyVector(): return B if B.isScalarLike(): return makeScalar(B.scalarToPy()) Rpy = iterator.scanVector(Fn, B) return makeVector(Rpy, B.dimension(), B.prototype()) assertNotArray(B, "WIP - RANK ERROR")
def membership(Fn, A, B): """ implement dyadic ∊ """ if A.isEmptyVector(): return A Apy = A.arrayToPy() if A.isArray() else A.vectorToPy() Bpy = B.arrayToPy() if B.isArray() else B.vectorToPy() Rpy = Fn(Apy, Bpy) if A.isArray(): return makeArray(Rpy, A.dimension(), A.prototype()) if A.isVector(): return makeVector(Rpy, A.dimension(), A.prototype()) return makeScalar(Rpy, A.prototype())
def compressFirst(Fn, A, B): """ implement dyadic ⌿ """ assertNotArray(A) if B.isScalar() and A.isEmptyVector(): return makeEmptyVector(B.prototype()) if B.isVectorLike(): Rpy = Fn(A.promoteScalarToVectorPy(), B.vectorToPy(), B.padFill()) return makeVector(Rpy, -1, B.prototype()) if B.isArray(): assertTrue(B.dimension() == (2, 2), "WIP - MATRIX ERROR") Apy = [confirmInteger(I) for I in A.vectorToPy()] if A.isScalarLike(): Lpy = abs(Apy[0]) * B.dimension()[0] else: Lpy = sum(abs(I) for I in Apy) Dpy = list(B.dimension()) Dpy[0] = Lpy Dpy[0], Dpy[-1] = Dpy[-1], Dpy[0] if Lpy == 0: return makeArray(B.padFill(), Dpy, B.prototype()) assertTrue(tuple(Dpy) == (2, 2), "WIP - MATRIX ERROR") Rpy = [] for Bit in B.arrayByFirstAxis(): Rpy += Fn(A.promoteScalarToVectorPy(), Bit.vectorToPy(), B.padFill()[-1]) Rpy = monadicTranspose(Rpy, B.dimension()) return makeArray(Rpy, Dpy, B.prototype())