{-# LANGUAGE TypeOperators, ScopedTypeVariables #-}-- |-- Module : Data.Array.Accelerate.Prelude-- Copyright : [2010..2011] Manuel M T Chakravarty, Gabriele Keller, Ben Lever-- [2009..2012] Manuel M T Chakravarty, Gabriele Keller, Trevor L. McDonell-- License : BSD3---- Maintainer : Manuel M T Chakravarty <chak@cse.unsw.edu.au>-- Stability : experimental-- Portability : non-portable (GHC extensions)---- Standard functions that are not part of the core set (directly represented in the AST), but are-- instead implemented in terms of the core set.--moduleData.Array.Accelerate.Prelude(-- * ZippingzipWith3,zipWith4,zip,zip3,zip4,-- * Unzippingunzip,unzip3,unzip4,-- * ReductionsfoldAll,fold1All,-- ** Specialised foldsall,any,and,or,sum,product,minimum,maximum,-- * Scansprescanl,postscanl,prescanr,postscanr,-- ** Segmented scansscanlSeg,scanl'Seg,scanl1Seg,prescanlSeg,postscanlSeg,scanrSeg,scanr'Seg,scanr1Seg,prescanrSeg,postscanrSeg,-- * Shape manipulationflatten,-- * Enumeration and fillingfill,enumFromN,enumFromStepN,-- * Working with predicates-- ** Filteringfilter,-- ** Scatter / Gatherscatter,scatterIf,gather,gatherIf,-- * Permutationsreverse,transpose,-- * Extracting sub-vectorsinit,tail,take,drop,slit)where-- avoid clashes with Prelude functions--importData.BitsimportData.BoolimportPrelude((.),($),(+),(-),(*),const,subtract,id)importqualifiedPreludeasP-- friendsimportData.Array.Accelerate.Array.Sugarhiding((!),ignore,shape,size)importData.Array.Accelerate.LanguageimportData.Array.Accelerate.SmartimportData.Array.Accelerate.Type-- Map-like composites-- --------------------- | Zip three arrays with the given function--zipWith3::(Shapesh,Elta,Eltb,Eltc,Eltd)=>(Expa->Expb->Expc->Expd)->Acc(Arraysha)->Acc(Arrayshb)->Acc(Arrayshc)->Acc(Arrayshd)zipWith3fasbscs=map(\x->let(a,b,c)=unliftxinfabc)$zip3asbscs-- | Zip four arrays with the given function--zipWith4::(Shapesh,Elta,Eltb,Eltc,Eltd,Elte)=>(Expa->Expb->Expc->Expd->Expe)->Acc(Arraysha)->Acc(Arrayshb)->Acc(Arrayshc)->Acc(Arrayshd)->Acc(Arrayshe)zipWith4fasbscsds=map(\x->let(a,b,c,d)=unliftxinfabcd)$zip4asbscsds-- | Combine the elements of two arrays pairwise. The shape of the result is-- the intersection of the two argument shapes.--zip::(Shapesh,Elta,Eltb)=>Acc(Arraysha)->Acc(Arrayshb)->Acc(Arraysh(a,b))zip=zipWith(currylift)-- | Take three arrays and return an array of triples, analogous to zip.--zip3::forallsh.foralla.forallb.forallc.(Shapesh,Elta,Eltb,Eltc)=>Acc(Arraysha)->Acc(Arrayshb)->Acc(Arrayshc)->Acc(Arraysh(a,b,c))zip3asbscs=zipWith(\abc->let(b,c)=unliftbc::(Expb,Expc)inlift(a,b,c))as$zipbscs-- | Take four arrays and return an array of quadruples, analogous to zip.--zip4::forallsh.foralla.forallb.forallc.foralld.(Shapesh,Elta,Eltb,Eltc,Eltd)=>Acc(Arraysha)->Acc(Arrayshb)->Acc(Arrayshc)->Acc(Arrayshd)->Acc(Arraysh(a,b,c,d))zip4asbscsds=zipWith(\abcd->let(b,c,d)=unliftbcd::(Expb,Expc,Expd)inlift(a,b,c,d))as$zip3bscsds-- | The converse of 'zip', but the shape of the two results is identical to the-- shape of the argument.--unzip::(Shapesh,Elta,Eltb)=>Acc(Arraysh(a,b))->(Acc(Arraysha),Acc(Arrayshb))unziparr=(mapfstarr,mapsndarr)-- | Take an array of triples and return three arrays, analogous to unzip.--unzip3::(Shapesh,Elta,Eltb,Eltc)=>Acc(Arraysh(a,b,c))->(Acc(Arraysha),Acc(Arrayshb),Acc(Arrayshc))unzip3xs=(mapget1xs,mapget2xs,mapget3xs)whereget1::forallabc.(Elta,Eltb,Eltc)=>Exp(a,b,c)->Expaget1x=let(a,_::Expb,_::Expc)=unliftxinaget2::forallabc.(Elta,Eltb,Eltc)=>Exp(a,b,c)->Expbget2x=let(_::Expa,b,_::Expc)=unliftxinbget3::forallabc.(Elta,Eltb,Eltc)=>Exp(a,b,c)->Expcget3x=let(_::Expa,_::Expb,c)=unliftxinc-- | Take an array of quadruples and return four arrays, analogous to unzip.--unzip4::(Shapesh,Elta,Eltb,Eltc,Eltd)=>Acc(Arraysh(a,b,c,d))->(Acc(Arraysha),Acc(Arrayshb),Acc(Arrayshc),Acc(Arrayshd))unzip4xs=(mapget1xs,mapget2xs,mapget3xs,mapget4xs)whereget1::forallabcd.(Elta,Eltb,Eltc,Eltd)=>Exp(a,b,c,d)->Expaget1x=let(a,_::Expb,_::Expc,_::Expd)=unliftxinaget2::forallabcd.(Elta,Eltb,Eltc,Eltd)=>Exp(a,b,c,d)->Expbget2x=let(_::Expa,b,_::Expc,_::Expd)=unliftxinbget3::forallabcd.(Elta,Eltb,Eltc,Eltd)=>Exp(a,b,c,d)->Expcget3x=let(_::Expa,_::Expb,c,_::Expd)=unliftxincget4::forallabcd.(Elta,Eltb,Eltc,Eltd)=>Exp(a,b,c,d)->Expdget4x=let(_::Expa,_::Expb,_::Expc,d)=unliftxind-- Reductions-- ------------ | Reduction of an array of arbitrary rank to a single scalar value.--foldAll::(Shapesh,Elta)=>(Expa->Expa->Expa)->Expa->Acc(Arraysha)->Acc(Scalara)foldAllfearr=foldfe(flattenarr)-- | Variant of 'foldAll' that requires the reduced array to be non-empty and-- doesn't need an default value.--fold1All::(Shapesh,Elta)=>(Expa->Expa->Expa)->Acc(Arraysha)->Acc(Scalara)fold1Allfarr=fold1f(flattenarr)-- Specialised reductions-- -------------------------- Leave the results of these as scalar arrays to make it clear that these are-- array computations, and thus can not be nested.-- | Check if all elements satisfy a predicate--all::(Shapesh,Elte)=>(Expe->ExpBool)->Acc(Arrayshe)->Acc(ScalarBool)allf=and.mapf-- | Check if any element satisfies the predicate--any::(Shapesh,Elte)=>(Expe->ExpBool)->Acc(Arrayshe)->Acc(ScalarBool)anyf=or.mapf-- | Check if all elements are 'True'--and::Shapesh=>Acc(ArrayshBool)->Acc(ScalarBool)and=foldAll(&&*)(constantTrue)-- | Check if any element is 'True'--or::Shapesh=>Acc(ArrayshBool)->Acc(ScalarBool)or=foldAll(||*)(constantFalse)-- | Compute the sum of elements--sum::(Shapesh,Elte,IsNume)=>Acc(Arrayshe)->Acc(Scalare)sum=foldAll(+)0-- | Compute the product of the elements--product::(Shapesh,Elte,IsNume)=>Acc(Arrayshe)->Acc(Scalare)product=foldAll(*)1-- | Yield the minimum element of an array. The array must not be empty.--minimum::(Shapesh,Elte,IsScalare)=>Acc(Arrayshe)->Acc(Scalare)minimum=fold1Allmin-- | Yield the maximum element of an array. The array must not be empty.--maximum::(Shapesh,Elte,IsScalare)=>Acc(Arrayshe)->Acc(Scalare)maximum=fold1Allmax-- Composite scans-- ----------------- |Left-to-right prescan (aka exclusive scan). As for 'scan', the first argument must be an-- /associative/ function. Denotationally, we have---- > prescanl f e = Prelude.fst . scanl' f e--prescanl::Elta=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Vectora)prescanlfe=P.fst.scanl'fe-- |Left-to-right postscan, a variant of 'scanl1' with an initial value. Denotationally, we have---- > postscanl f e = map (e `f`) . scanl1 f--postscanl::Elta=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Vectora)postscanlfe=map(e`f`).scanl1f-- |Right-to-left prescan (aka exclusive scan). As for 'scan', the first argument must be an-- /associative/ function. Denotationally, we have---- > prescanr f e = Prelude.fst . scanr' f e--prescanr::Elta=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Vectora)prescanrfe=P.fst.scanr'fe-- |Right-to-left postscan, a variant of 'scanr1' with an initial value. Denotationally, we have---- > postscanr f e = map (e `f`) . scanr1 f--postscanr::Elta=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Vectora)postscanrfe=map(`f`e).scanr1f-- Segmented scans-- ----------------- |Segmented version of 'scanl'--scanlSeg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)scanlSegfzvecseg=scanl1Segfvec'seg'where-- Segmented exclusive scan is implemented by first injecting the seed-- element at the head of each segment, and then performing a segmented-- inclusive scan.---- This is done by creating a creating a vector entirely of the seed-- element, and overlaying the input data in all places other than at the-- start of a segment.--seg'=map(+1)segvec'=permuteconst(fill(index1$sizevec+sizeseg)z)(\ix->index1'$unindex1'ix+inc!ix)vec-- Each element in the segments must be shifted to the right one additional-- place for each successive segment, to make room for the seed element.-- Here, we make use of the fact that the vector returned by 'mkHeadFlags'-- contains non-unit entries, which indicate zero length segments.--flags=mkHeadFlagsseginc=scanl1(+)flags-- |Segmented version of 'scanl''---- The first element of the resulting tuple is a vector of scanned values. The-- second element is a vector of segment scan totals and has the same size as-- the segment vector.--scanl'Seg::forallai.(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora,Vectora)scanl'Segfzvecseg=resultwhere-- Returned the result combined, so that the sub-calculations are shared-- should the user require both results.--result=lift(body,sums)-- Segmented scan' is implemented by deconstructing a segmented exclusive-- scan, to separate the final value and scan body.---- TLM: Segmented scans, and this version in particular, expend a lot of-- effort scanning flag arrays. On inspection it appears that several-- of these operations are duplicated, but this will not be picked up-- by sharing _observation_. Perhaps a global CSE-style pass would be-- beneficial.--vec'=scanlSegfzvecseg-- Extract the final reduction value for each segment, which is at the last-- index of each segment.--seg'=map(+1)segtails=zipWith(+)seg.P.fst$scanl'(+)0seg'sums=backpermute(shapeseg)(\ix->index1'$tails!ix)vec'-- Slice out the body of each segment.---- Build a head-flags representation based on the original segment-- descriptor. This contains the target length of each of the body segments,-- which is one fewer element than the actual bodies stored in vec'. Thus,-- the flags align with the last element of each body section, and when-- scanned, this element will be incremented over.--offset=scanl1(+)seginc=scanl1(+)$permute(+)(fill(index1$sizevec+1)0)(\ix->index1'$offset!ix)(fill(shapeseg)(1::Expi))body=backpermute(shapevec)(\ix->index1'$unindex1'ix+inc!ix)vec'-- |Segmented version of 'scanl1'.--scanl1Seg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)scanl1Segfvecseg=P.snd.unzip.scanl1(segmentedf)$zip(mkHeadFlagsseg)vec-- |Segmented version of 'prescanl'.--prescanlSeg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)prescanlSegfevecseg=P.fst.unatup2$scanl'Segfevecseg-- |Segmented version of 'postscanl'.--postscanlSeg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)postscanlSegfevecseg=map(fe)$scanl1Segfvecseg-- |Segmented version of 'scanr'.--scanrSeg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)scanrSegfzvecseg=scanr1Segfvec'seg'where-- Using technique described for 'scanlSeg', where we intersperse the array-- with the seed element at the start of each segment, and then perform an-- inclusive segmented scan.--inc=scanl1(+)(mkHeadFlagsseg)seg'=map(+1)segvec'=permuteconst(fill(index1$sizevec+sizeseg)z)(\ix->index1'$unindex1'ix+inc!ix-1)vec-- | Segmented version of 'scanr''.--scanr'Seg::forallai.(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora,Vectora)scanr'Segfzvecseg=resultwhere-- Using technique described for scanl'Seg--result=lift(body,sums)vec'=scanrSegfzvecseg-- reduction valuesseg'=map(+1)segheads=P.fst$scanl'(+)0seg'sums=backpermute(shapeseg)(\ix->index1'$heads!ix)vec'-- body segmentsinc=scanl1(+)$mkHeadFlagssegbody=backpermute(shapevec)(\ix->index1'$unindex1'ix+inc!ix)vec'-- |Segmented version of 'scanr1'.--scanr1Seg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)scanr1Segfvecseg=P.snd.unzip.scanr1(segmentedf)$zip(mkTailFlagsseg)vec-- |Segmented version of 'prescanr'.--prescanrSeg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)prescanrSegfevecseg=P.fst.unatup2$scanr'Segfevecseg-- |Segmented version of 'postscanr'.--postscanrSeg::(Elta,Elti,IsIntegrali)=>(Expa->Expa->Expa)->Expa->Acc(Vectora)->Acc(Segmentsi)->Acc(Vectora)postscanrSegfevecseg=map(fe)$scanr1Segfvecseg-- Segmented scan helpers-- ------------------------ |Compute head flags vector from segment vector for left-scans.---- The vector will be full of zeros in the body of a segment, and non-zero-- otherwise. The "flag" value, if greater than one, indicates that several-- empty segments are represented by this single flag entry. This is additional-- data is used by exclusive segmented scan.--mkHeadFlags::(Elti,IsIntegrali)=>Acc(Segmentsi)->Acc(Segmentsi)mkHeadFlagsseg=init$permute(+)zeros(\ix->index1'(offset!ix))oneswhere(offset,len)=scanl'(+)0segzeros=fill(index1'$thelen+1)0ones=fill(index1$sizeoffset)1-- |Compute tail flags vector from segment vector for right-scans. That is, the-- flag is placed at the last place in each segment.--mkTailFlags::(Elti,IsIntegrali)=>Acc(Segmentsi)->Acc(Segmentsi)mkTailFlagsseg=init$permute(+)zeros(\ix->index1'(thelen-1-offset!ix))oneswhere(offset,len)=scanr'(+)0segzeros=fill(index1'$thelen+1)0ones=fill(index1$sizeoffset)1-- |Construct a segmented version of a function from a non-segmented version.-- The segmented apply operates on a head-flag value tuple, and follows the-- procedure of Sengupta et. al.--segmented::(Elte,Elti,IsIntegrali)=>(Expe->Expe->Expe)->Exp(i,e)->Exp(i,e)->Exp(i,e)segmentedfab=let(aF,aV)=unlifta(bF,bV)=unliftbinlift(aF.|.bF,bF/=*0?(bV,faVbV))-- |Index construction and destruction generalised to integral types.---- We generalise the segment descriptor to integral types because some-- architectures, such as GPUs, have poor performance for 64-bit types. So,-- there is a tension between performance and requiring 64-bit indices for some-- applications, and we would not like to restrict ourselves to either one.---- As we don't yet support non-Int dimensions in shapes, we will need to convert-- back to concrete Int. However, don't put these generalised forms into the-- base library, because it results in too many ambiguity errors.--index1'::(Elti,IsIntegrali)=>Expi->ExpDIM1index1'i=lift(Z:.fromIntegrali)unindex1'::(Elti,IsIntegrali)=>ExpDIM1->Expiunindex1'ix=letZ:.i=unliftixinfromIntegrali-- Reshaping of arrays-- --------------------- | Flattens a given array of arbitrary dimension.--flatten::(Shapeix,Elta)=>Acc(Arrayixa)->Acc(Vectora)flattena=reshape(index1$sizea)a-- Enumeration and filling-- ------------------------- | Create an array where all elements are the same value.--fill::(Shapesh,Elte)=>Expsh->Expe->Acc(Arrayshe)fillshc=generatesh(constc)-- | Create an array of the given shape containing the values x, x+1, etc (in-- row-major order).--enumFromN::(Shapesh,Elte,IsNume)=>Expsh->Expe->Acc(Arrayshe)enumFromNshx=enumFromStepNshx1-- | Create an array of the given shape containing the values @x@, @x+y@,-- @x+y+y@ etc. (in row-major order).--enumFromStepN::(Shapesh,Elte,IsNume)=>Expsh->Expe-- ^ x: start->Expe-- ^ y: step->Acc(Arrayshe)enumFromStepNshxy=reshapesh$generate(index1$shapeSizesh)(\ix->(fromIntegral(unindex1ix::ExpInt)*y)+x)-- Filtering-- ----------- | Drop elements that do not satisfy the predicate--filter::Elta=>(Expa->ExpBool)->Acc(Vectora)->Acc(Vectora)filterparr=letflags=map(boolToInt.p)arr(targetIdx,len)=scanl'(+)0flagsarr'=backpermute(index1$thelen)idarrinpermuteconstarr'(\ix->flags!ix==*0?(ignore,index1$targetIdx!ix))arr-- FIXME: This is abusing 'permute' in that the first two arguments are-- only justified because we know the permutation function will-- write to each location in the target exactly once.-- Instead, we should have a primitive that directly encodes the-- compaction pattern of the permutation function.{-# RULES
"ACC filter/filter" forall f g arr.
filter f (filter g arr) = filter (\x -> g x &&* f x) arr
#-}-- Gather operations-- ------------------- | Copy elements from source array to destination array according to a map. This-- is a backpermute operation where a 'map' vector encodes the output to input-- index mapping.---- For example:---- > input = [1, 9, 6, 4, 4, 2, 0, 1, 2]-- > map = [1, 3, 7, 2, 5, 3]-- >-- > output = [9, 4, 1, 6, 2, 4]--gather::(Elte)=>Acc(VectorInt)-- ^map->Acc(Vectore)-- ^input->Acc(Vectore)-- ^outputgathermapVinputV=backpermute(shapemapV)bpFinputVwherebpFix=lift(Z:.(mapV!ix))-- | Conditionally copy elements from source array to destination array according-- to a map. This is a backpermute operation where a 'map' vector encodes the-- output to input index mapping. In addition, there is a 'mask' vector, and an-- associated predication function, that specifies whether an element will be-- copied. If not copied, the output array assumes the default vector's value.---- For example:---- > default = [6, 6, 6, 6, 6, 6]-- > map = [1, 3, 7, 2, 5, 3]-- > mask = [3, 4, 9, 2, 7, 5]-- > pred = (> 4)-- > input = [1, 9, 6, 4, 4, 2, 0, 1, 2]-- >-- > output = [6, 6, 1, 6, 2, 4]--gatherIf::(Elte,Elte')=>Acc(VectorInt)-- ^map->Acc(Vectore)-- ^mask->(Expe->ExpBool)-- ^predicate->Acc(Vectore')-- ^default->Acc(Vectore')-- ^input->Acc(Vectore')-- ^outputgatherIfmapVmaskVpreddefaultVinputV=zipWithzwFpredVgatheredVwherezwFpg=p?(unliftg)gatheredV=zip(gathermapVinputV)defaultVpredV=mappredmaskV-- Scatter operations-- -------------------- | Copy elements from source array to destination array according to a map. This-- is a forward-permute operation where a 'map' vector encodes an input to output-- index mapping. Output elements for indices that are not mapped assume the-- default vector's value.---- For example:---- > default = [0, 0, 0, 0, 0, 0, 0, 0, 0]-- > map = [1, 3, 7, 2, 5, 8]-- > input = [1, 9, 6, 4, 4, 2, 5]-- >-- > output = [0, 1, 4, 9, 0, 4, 0, 6, 2]---- Note if the same index appears in the map more than once, the result is-- undefined. The map vector cannot be larger than the input vector.--scatter::(Elte)=>Acc(VectorInt)-- ^map->Acc(Vectore)-- ^default->Acc(Vectore)-- ^input->Acc(Vectore)-- ^outputscattermapVdefaultVinputV=permute(const)defaultVpFinputVwherepFix=lift(Z:.(mapV!ix))-- | Conditionally copy elements from source array to destination array according-- to a map. This is a forward-permute operation where a 'map' vector encodes an-- input to output index mapping. In addition, there is a 'mask' vector, and an-- associated predicate function, that specifies whether an elements will be-- copied. If not copied, the output array assumes the default vector's value.---- For example:---- > default = [0, 0, 0, 0, 0, 0, 0, 0, 0]-- > map = [1, 3, 7, 2, 5, 8]-- > mask = [3, 4, 9, 2, 7, 5]-- > pred = (> 4)-- > input = [1, 9, 6, 4, 4, 2]-- >-- > output = [0, 0, 0, 0, 0, 4, 0, 6, 2]---- Note if the same index appears in the map more than once, the result is-- undefined. The map and input vector must be of the same length.--scatterIf::(Elte,Elte')=>Acc(VectorInt)-- ^map->Acc(Vectore)-- ^mask->(Expe->ExpBool)-- ^predicate->Acc(Vectore')-- ^default->Acc(Vectore')-- ^input->Acc(Vectore')-- ^outputscatterIfmapVmaskVpreddefaultVinputV=permuteconstdefaultVpFinputVwherepFix=(pred(maskV!ix))?(lift(Z:.(mapV!ix)),ignore)-- Permutations-- -------------- | Reverse the elements of a vector.--reverse::Elte=>Acc(Vectore)->Acc(Vectore)reversexs=letlen=unindex1(shapexs)pfi=len-i-1inbackpermute(shapexs)(ilift1pf)xs-- | Transpose the rows and columns of a matrix.--transpose::Elte=>Acc(ArrayDIM2e)->Acc(ArrayDIM2e)transposemat=letswap=lift1$\(Z:.x:.y)->Z:.y:.x::Z:.ExpInt:.ExpIntinbackpermute(swap$shapemat)swapmat-- Extracting sub-vectors-- ------------------------ | Yield the first @n@ elements of the input vector. The vector must contain-- no more than @n@ elements.--take::Elte=>ExpInt->Acc(Vectore)->Acc(Vectore)taken=letn'=the(unitn)inbackpermute(index1n')id-- | Yield all but the first @n@ elements of the input vector. The vector must-- contain no fewer than @n@ elements.--drop::Elte=>ExpInt->Acc(Vectore)->Acc(Vectore)dropnarr=letn'=the(unitn)inbackpermute(ilift1(subtractn')(shapearr))(ilift1(+n'))arr-- | Yield all but the last element of the input vector. The vector must not be-- empty.--init::Elte=>Acc(Vectore)->Acc(Vectore)initarr=take((unindex1$shapearr)-1)arr-- | Yield all but the first element of the input vector. The vector must not be-- empty.--tail::Elte=>Acc(Vectore)->Acc(Vectore)tailarr=backpermute(ilift1(subtract1)(shapearr))(ilift1(+1))arr-- | Yield a slit (slice) from the vector. The vector must contain at least-- @i + n@ elements. Denotationally, we have:---- > slit i n = take n . drop i--slit::Elte=>ExpInt->ExpInt->Acc(Vectore)->Acc(Vectore)slitin=leti'=the(uniti)n'=the(unitn)inbackpermute(index1n')(ilift1(+i'))