At the heart of the blaze function system is the concept of kernel "lifting". 

This process is where one element kernel is used to build a higher-order element kernel.    Input element kernels can have scalar, pointer, Array_C, Array_F, or Array_S elements.

The arguments of the "lifted" kernel are of kind Array_C, Array_F, or Array_S depending on what is specified. 

Here are some examples: 

Suppose I have a kernel signature (scalar, scalar) -> scalar and I want a rank-1 output.  There are 3 possibilities for the generated "wrapper" caller: 

(scalar, r1) -> r1
(r1, scalar) -> r1
(r1, r1) -> r1

The r1 arrays could be Array_C/F or Array_S depending on what is needed by the caller.  

If a rank2 output is desired, there are even more possibilites: 

(scalar, r2) -> r2
(r2, scalar) -> r2
(r1, r2) -> r2
(r2, r1) -> r2

And so forth.   We will manage this complexity with the following rule for "lifting" :  All arguments in the "lifted" kernel must be of the same "kind" (i.e. all Array_C, or Array_F, or Array_S) and the amount of lifting must be identical

One can still call a lifted kernel with lower-rank units by creating a higher-rank "view" of the data as Array_S and placing a constant 0 for the strides.

The lifting algorithm is: 

def lift(self, outrank, outkind):

 * call the argument kind vector argkinds (output last)
 * call the input rank profile ranks (output last)

 * check to see that the argkinds are compatible with outkind (scalar, pointer, or same Array kind)
 * dr = outrank - ranks[-1]
 * ranks = [r + dr for r in ranks]
 * Construct new types for each arg
 * Construct new function signature 
 * Build dr-dimensional loop
 * Extract elements from each array using []
 * call the underlying function

 
