Chapter 14. The Facet Algebra

The facet algebra is a collection of operations for composing facets from one or more domains. The facet product and sum operations define classical conjunctive and disjunctive composition operations. Taking the product of two facets defines a new facet that satisfies both of the original specifications. Similarly, taking the sum of two facets defines a new facet that satisfies either of the original specifications. Using facet product and sum provides direct language support for concurrent engineering where multiple specifications must be simultaneously valid.

The if, case and let forms define the mechanisms for selecting between models and defining local models respectively. Facet-typed applications of if and case allow selection of behavior based on boolean conditions. Facet typed applications of let allow definition of local symbols over facet-typed expressions. The only distinction between facet algebra instances and traditional instances of these constructs is that facet algebra instances will have facet types.

Unlike product and sum, Homomorphism and isomorphism do not form new facets, but define relationships between facets. A homomorphism exists between two facets when the properties of one are implied by the other. Homomorphisms in the domain semi-lattice are critical for moving information between domains. An isomorphism exists between two facets when a homomorphism exists both ways. When facets are isomorphic, they are considered equivalent.

Facet Products and Sums

Products and sums originate from category theory and are widely accepted mechanisms for specification composition. It is not necessary to understand these concepts deeply to understand their utility in Rosetta. It is sufficient to understand initially that products and sums combine two specifications into a single specification that comprises both original specifications. The product defines a new specification that is simultaneously both of the original specifications, while the sum defines a new specification that is either of the orignal specifications.

An excellent example of a product from programming languages is a record, class, or tuple structure. A tuple type is literally the Cartesian product of its constituent types. The tuple (1,''a'', 2.1) is an element of the type:

integer × string × real

The best way to distinguish a product is to note that a tuple is not a tuple without one of its elements. In other words, (1,''a''), (1), and (''a'', 2.1) are not elements of the product type. All three values must be present.

In contrast, a sum composes specifications into a single specification that is either of the original specifications. An excellent example of a sum from programming languages is a union type or a constructed type. A union is the tagged union of the original specifications. The sum type:

integer + string + real

contains values from the original three types. To assure value distinctness, programming languages will tag values to indicate the source type of the original value. Thus, (left 1), (middle ''a''), and (right 2.1) are all members of the sum type. In this case the tag indicates the source type of the value by indicating the position of the type in the sum type declaration.

Syntax for facet product and sum operations specifies two facets, the operator and an optional sharing list:

F * G [[ sharing { symbols} ]]
F + G [[ sharing { symbols} ]]

The facet product, F * G, states that both specifications F and G simultaneously define a system and must simultaneously hold. The facet sum, F + G, states that both specifications F and G separately define a system. The optional sharing keyword defines the vocabulary that both specifications share. In both cases, the sharing clause identifies items that are shared among the specifications and must satisfy properties from both specifications.

The Shared Domain

Any two facets defined from domains in the same domain semi-lattice must share at least one common domain. Even if that domain is static or null, the common domain will always exist. Graphically, the common domains are easily found by tracing the lattice structure upward from the domains of two facets, until a common domain is discovered. This relationship is defined mathematically by identifying a partial ordering relationship over the domains in the semi-lattice and the existence of a minimum element resulting in a join semi-lattice.

When a domain is defined, a superdomain must be identified as the supertype of the new domain. In this specification fragment:

domain Dsub(...) :: Dsuper is
  ...

Dsub is a new domain, with Dsuper as the domain it inherits from; Dsub is a subtype of Dsuper.

When one domain extends another in this manner, a partial ordering relationship exists. Specifically, the partial ordering is defined such that when Dsub :: Dsuper, the ordering relationship DsuperDsub holds. Because all domains extend null, nullD is true for all domains and Dnull is never true for any domain. Thus, null defines the minimum domain, or bottom of the semi-lattice.

Using ⊑ we define the meet, or greatest common supertype (D1D2), of domains D1 and D2 as an element of the set:

{D | DD1DD2}

such that every other member of the set satisfies:

D ⊑ (D1D2)

Thus, D1D2 must be the maximum value on the set with respect to the The Shared Domain partial order. Because all domains extend null, D1D2 exists for any two domains in the semi-lattice, even when D1D2 is null.

We care about the greatest common supertype because it defines the maximal set of specification objects shared by two domains. This shared specification defines the common items that both specifications observe. If two facets are written that extend the discrete_time domain, discrete_time becomes the greatest shared supertype and they both share the same definition of time. Most importantly, it is the same definition of time. Thus, any property asserted over the time value in one facet must be consistent with the other under composition. Any item that appears in the greatest common supertype is shared in the same manner. In this way, specifications under composition can share the same type.

If two specifications do not share the same type, then by definition they share their greatest common supertype. If a specification written in the state_based domain is composed with a specification in the infinite_state domain, the shared items are state and next. The infinite, discrete nature of state is not known to the specification written in the state_based domain. As a result, the type of any composition of these two specifications must be state_based, losing more detailed information about the infinite_state specification.

Example 14.1. Impact of Two Facet Specifications Sharing a Common Supertype

A simple example demonstrates the role of the shared specification in facet composition. The following two facets define parity checkers that differ only in when state is updated. The first, parity1, updates its state on the rising clock edge, while the second, parity2, updates on the falling edge. As defined, there is nothing wrong with either facet.

facet parity1 (clk:: input bit; o:: output boolean)::infinite_state (natural) is
  next(s::integer)::integer is s+1;

begin
  up: s' = 1 if rising(clk)then next(s) else s end if;
  out: o' = odd(s);
end facet parity1;

facet parity2(clk:: input bit; o:: output boolean)::infinite_state (natural) is

  next(s::integer)::integer is s+1;
begin
  up: s' = 1 if falling(clk) then next(s) else s end if;
  out: o' = odd(s);
end facet parity2;

It bears repeating that there is nothing wrong with either of these specifications in isolation. They simply provide two definitions for what parity checking is. What if we compose these facets using a product to specify a single system that must satisfy both requirements:

facet parity(clk:: input bit; o:: output boolean)::infinite_state(natural)is
  parity1(clk, o) + parity2(clk, o);

The parity facet composes the original facets using the product operation. The greatest shared supertype is:

infinite_state (natural)

All definitions including next, state, and s are shared among the two specifications. This implies that definitions over those items must be consistent across facet specifications. Simply changing the triggering event for state update causes an inconsistency in the composition. Specifically, the value of the current state will be out of sync in the two models due to differing update times. Discovering this inconsistency is exactly the desired result, as it allows us to see a system-level problem caused by a local specification decision.

The distance between specifications in the semi-lattice is referred to informally as the intellectual distance between the specifications. As the name implies, the greater the intellectual distance between specifications, the more difficult it is to compose specifications. Although any two domains must share some greatest common superdomain, the intellectual distance between them may make their composition virtually useless. If two specifications share only the null or static domain, the intellectual distance is maximized. In essence, they share no common items other than basic mathematical definitions. Such problems can be avoided by defining functions that move specifications from domain to domain in the semi-lattice.

Example 14.2. Facet Composition Identifying the Shared Domain of the Original Facets

The following table illustrates the calculation of shared specifications from several domains in the domain semi-lattice. In general, determining the greatest common supertype simply involves following the tree upward from the two domains and stopping at the first point where the two paths intersect.

Domain A

Domain B

Shared Specification

state_based

state_based

state_based

state_based

finite_state

state_based

finite_state

state_based

state_based

finite_state

infinite_state

state_based

finite_state

signal_based

static

Products and sums are calculated using the greatest common supertype of the composed facets. The product defines two facets that together define the composite specification. The facet definitions are linked by their shared parts. Specifically, both facets define properties over items from the greatest common supertype that must be satisfied under composition. Similarly, the sum defines two facets that define cases in the composite specification. Again, the facet definitions are linked by properties defined over their shared parts. Semantically, products and co-products are constructed using pullbacks and pushouts, respectively. Detailed knowledge of these constructions is not required to compose Rosetta specifications.

The Sharing Clause

In many cases, it is desirable for facets to share more than just those items defined in a standard domain. Types, functions, and elements of system state are all examples of items useful for sharing. There are two approaches to adding something to the shared specification for use in a product or sum. The first is to write a new, specialized domain that adds shared items to the domain involved in the product or sum. The second is to use a sharing clause to implicitly add items to the shared part. Both have their advantages; however, the sharing clause tends to be preferred due its general nature and simplicity of use.

Example 14.3. Facet Composition Using Facet Product

Composing specifications using the product operation defines a new specification that satisfies both original specifications. The following facets define a simple component and a constraint that the component must satisfy. For this system to be correct, both specifications must be satisfied, indicating the use of a product for composition.

facet component(i:: input integer; o:: output integer)::state_based is
begin
end facet component;

facet constraint(v:: design real)::static is
begin
end facet constraint;

The facet system defines the product of component and constraint. For system to be consistent, both component and constraint must be satisfied over the same shared specification. In this case, the shared part is static and consistency is trivial to assert as long as elements of static are not redefined.

facet system i::input integer; o::output integer)::static is
   component (i::input integer; o::output integer) * constraint(2.3);

The specification is much more interesting if a functor is used to generate a state_based specification from the constraint. The concretization function gamma takes an abstract specification and makes it more concrete. In this case, it takes the static constraint specification and asserts the constraint in every state. The result is a new system facet in the state_based domain. This new specification does not sacrifice details of the component specification to achieve composition.

facet system(i::input integer; o::output integer)::state_based is
   component (i::input integer; o::output integer)
   * gamma(constraint(2.3))::state_based;

The specifics of defining gamma and how it is used will be discussed in detail later in this chapter.

The sharing clause is used with facet sum and product operations to extend the shared domain by introducing new items that are shared among the two facets involved in the composition operation. The sharing clause does not declare new items, but simply adds existing items to the shared specification from facets under composition. Specifically:

F + G sharing S

forms the sum of facet’s F and G, with S extending the greatest common domain of F and G. Elements of S must be defined in both F and G, but need not have a common type. The format of S is a set of names shared between the specifications. Thus:

F + G sharing {H,I,J}

defines the sum F+G, where {H,I,J} name the new shared items in both specifications that are added to the shared specification. Similarly:

F * G sharing {H, I, J}

defines the product F*G, where {H, I, J} names shared items in both specifications that do not exist in the greatest common supertype. In both cases an error exists if the shared items are not declared in both specifications. Items from the domain can be listed in the sharing clause. However, both F and G inherit those definitions from the shared domain without the sharing clause.

The semantic definition of the sharing clause pushes shared definitions from the facets being composed into the domain. For example, in the following specification, the product of test1 and test2 is formed sharing the state variable x. Thus, x refers to the same object in both specifications under the product. Conversely, y is not shared, causing both specifications to maintain their local, independent definitions.

facet interface test1(a:: input bit; b:: output bit)::state_based(bit) is
  x::integer;
  y::real;
end facet interface test1;
facet interface test2(a:: input bit; b:: output bit)::state_based(bit)is
  x::integer;
  y::real;
end facet interface test2;
test3 :: state_based(bit) is test1*test2 sharing {x};

An alternative approach is to write a new domain to accomplish the same task by extending the greatest common supertype domain. Rather than use the sharing clause to push items into the domain implicitly, a new domain explicitly defines those items: The new domain test_domain defines a domain specific to this specification task where x is added to the state_based specification. The product no longer requires the sharing clause because x now appears in the domain and is shared by default. Although there is nothing wrong with this approach, using the sharing clause avoids writing specialized domains and defeating the idea of domain reuse. Semantically, the second definition is identical to the first.

domain test_domain()::state_based(bit) is
 x::integer
begin
end domain test_domain;

facet interface test1(a:: input bit; b:: output bit)::test_domain is
 y::real;
end facet interface test1;

facet interface test2(a:: input bit; b:: output bit)::test_domain is
 y::real;
end facet interface test2;

 test3 :: state_based(bit) is test1*test2;

The new domain test_domain defines a domain specific to this specification task where x is added to the state_based specification. The product no longer requires the sharing clause because x now appears in the domain and is shared by default. Although there is nothing wrong with this approach, using the sharing clause avoids writing specialized domains and defeating the idea of domain reuse. Semantically, the second definition is identical to the first.

Example 14.4. Facet Composition Using Facet Sum

In contrast to the product operation for facets, then sum operation combines specifications disjunctively. Specifically, the result of a sum is a new specification that exhibits properties of one or the other specification. In the following definitions, componentA and componentB define alternate component behaviors.

facet component A(i:: input integer;o:: outputinteger)::state_based is
begin end facet componentA;

facet componentB(i::  input integer;o:: output integer)::state_based is
begin end facet componentB;

The component facet, formed from the sum of facets componentA and componentB, can be observed as either of the original components.

facet component (i:: input integer; o::output integer)::state_based is
  componentA (i:: input integer; o:: output integer)
  + componentB (i::input integer; o:: output integer)

When a facet resulting from a sum appears in a specification, it may exhibit behaviors from either of its constituent facets. Thus, the environment of the specification must deal with both possibilities. The environment cannot assume that either behavior set will occur, and must be prepared for both. The facet sum does not allow refinement of behavior, but actually expands a definition by allowing multiple, correct behaviors.

Implicit Sharing

Parameters appearing in facet interfaces are implicitly shared if they have the same name. This simply asserts that unique parameters have unique names. From a physical design perspective, this makes sense. For example, the clock signal should be the clock signal in every model where it is applicable, regardless of modeling domain.

For example, the following models define intersecting parameter lists. Under composition, either sum or product, parameters with the same name are treated as a part of the shared domain. There is an implicit sharing clause indicating the parameters across specifications.

facet interface devFunction
   (x:: input integer; y:: output integer)::state_based is
end facet interface devFunction;

facet interface devPower
   (x:: input real; leakage,deltaP:: design real)::continuous is
end facet interface devPower;

dev(x:: input number; y:: output number; leakage,deltaP:: design number) ::
  state_based is devFunction(x, y) and devPower(x, leakage,deltaP);

The input parameter x appears in both specifications and is a part of the shared domain associated with the product. The output y only appears in the functional model and is thus not shared. Likewise, the design parameters leakage and delta do not appear in the functional model and are thus not shared. However, all parameters appear in the parameter list associated with dev.

The parameter list associated with the product specification, dev, uses the type number to constrain values in its input and output. Another type could be chosen, but the number type expresses only the desired requirement. Specifically, the values of connected parameters must be number, but need not specifically be integer or real. The parameters represent distinct observations of the product’s abstract state. However, those observations must be mutually consistent with any translation functions that may be defined. For example, the integer and real values associated with x must satisfy the translator defined between the state_based and continuous domains. If no such translator is defined, then any assignment is legal, thus it is important to consider translators during specification.

The following Rosetta interface specifications define two composite specifications that will be interconnected. The first, fandc0, defines a model that is the product of f0 and c0. The models for this component, c0 and f0, implicitly share the parameters x and y. Because c0 is static and f0 is state_based, the interaction between parameters in the two models is governed by the active interaction between static and state_based.

facet interface c0(x:: input integer; y:: output integer)::static is
end facet interface c;

facet interface f0(x:: input real;y::output::real)::state_based is
end facet interface f;

fandc0(x::integer; y::integer) :: static is f0(x,y) and c0(x,y);

The fandc1 facet is analogous to fandc0 except that the models are defined in the discrete_time and state_based domains. Again, the interaction between models is governed by the active interaction between state_based and discrete_time.

facet interface c1(x:: input integer; y:: output integer)::discrete_time is
end facet interface c1;

facet interface f1(x:: input real; y::output::real)::state_based is
end facet interface f1;

fandc1(x::integer; y::integer) :: static is f1(x,y) and c1(x,y);

The interconnect facet simply connects the two previously defined components in sequence, with the output of fandc0 used as the input to fandc1.

facet interconnect(x:: input integer;y:: output integer):: static is
 z :: integer;
begin
 cmp0: fandc0(x,z);
 cmp1: fandc1(z,y);
end facet interconnect;

Because both fandc0 and fandc1 have models in the state_based domain, any constraints on parameters in that domain must be mutually consistent. However, fandc0 does not have a discrete_time model, and fandc1 does not have a static model. Because an interaction is defined between state_based and the other two domains, information is implicitly shared across the parameter. Specifically, all the assertions made by all models must be mutually consistent in the presence of the interactions. Because an interaction exists between state_based and static in fandc0 and between state_based and discrete_time in fandc1, an implicit transitive interaction exists between static and discrete_time.

Example 14.5. Use of the sharing Clause to Push More Shared Items into the Domain When Applying a Facet Composition Operator

Revisiting our activity-based power modeling example, we would like to add the capability of including internal switching in the power calculation. The model presented in Chapter 12 only monitored the output signal for value changes. The system facet is a structural model of a simple system that sequences multiplication and division operations. Because the internal signal changes values as well as the external signal, there will be an associated power drain.

facet system(i:: input integer; o:: output integer)::state_based is
  x::integer;
begin
  c1: mult(i,3,x);
  c2: div(x, 4, o);
end facet system;

The new systemPower model defines a local variable, x, that parallels the local definition in system. However, this is a local variable and is not the same item that is defined in system.

facet systemPower[Ti,To::type](i:: input Ti; o::output To;
               leak,switch::static real)::state_based is
  export power::real;
  x::top;
  powerUpdate[T::type](o::T)::real is
    leakage + if event(o) then switch else 0 end if;
begin
  power' = power
         + powerUpdate(x,leak,switch)
         + powerUpdate(o,leak,switch);
end facet systemPower

To compose the specifications and make systemPower aware of changes in x from facet system, we use a sharing clause to push x into the shared part.

powerAware[Ti,To::type](i:: input Ti;o:: output To)::state_based is
   system(i, o) * systemPower(i, o) sharing {x};

Now x in powerAware and x in system refer to the same item. When system changes the value of x, that change will be observed by systemPower and power consumption updated as appropriate.

Facet Homomorphism and Isomorphism

A homomorphism exists between two facets f1 and f2 when all the properties of f2 are also properties of f1. This is denoted using the facet homomorphism or facet implication relation:

f1  => f2
f2  <= f1

An isomorphism exists between two facets f1 and f2 when they exhibit exactly the same properties or when f1=>f2 and f1<=f2 hold simultaneously. This is denoted using the facet isomorphism or facet equality relation:

f1 <=> f2

Homomorphism and isom orphism are used primarily to express desired relationships between facets rather than to construct new facets. For example, these relationships can be used to express correctness conditions in the implications section of a component structure.

Facet homomorphism plays a critical role in the formation of the domain semi-lattice. Whenever one domain is extended to form another domain, a homomorphism exits between the new domain and the original domain. It follows that everything expressed in the original domain can be expressed in the new domain. When functors are defined between domains, the existance of a homomorphism assures that information from the source domain is not lost in the destination domain when facets are transformed. When an isomorphism exists between two domains, fact can be moved back and forth between them without losing information.

Conditional Expressions

The if expression can be used with facet values in the same manner as they are used with any other value. In the following equation, the test value determines the value of the if expression:

if t then F else G end if;

If t is true, the expression evaluates to F. Otherwise, the expression evaluates to G. Like any Rosetta expression, if can have only one type requiring F and G to share a common type. The type of the if expression is this shared type. If the type cannot be inferred, ascription can be used to assist in the analysis process. However, if a facet’s type is abstracted away, it is gone and may not be reconstructible at a later point. Remember that ascription is not casting — if the assertion f::D cannot be verified, it is not considered correct.

Example 14.6. Uses of Homomorphism and Isomorphism Relationships

Some interesting relationship can be defined using isomorphisms and homomorphisms. The simplest is that any facet is isomorphic to itself. The forall quantifier is used to state that the self-isomorphism relationship holds for all facets:

forall(f::static | f <=> f)

Similarly, a homomorphism exists between a facet and its domain. Because the facet extends the domain, we know that any property present in the domain is also present in the facet. The notation may seem odd because the domain is used as both a type and a facet. This is perfectly acceptable:

forall(f::D | f => D)

This relationship is important, as it defines the domain semi-lattice. Specifically, it serves as the partial order giving the semi-lattice its structure an defining the greatest element. Correctness conditions can be expressed using both homomorphisms and isomorphisms. Assume that R is a facet defining the requirements of a component and S is a structural facet defining an implementation architecture. The following relationship states that the architecture must exhibit all properties of the requirements specification:

S => R

Alternatively, we can use an isomorphism in conjunction with an abstraction functor to define a similar requirements relationship:

alpha(S) <=> R

Here the abstraction functor alpha removes detail from the S facet. This less detailed facet is then equated with the requirements facet. The idea is that extraneous properties that are not germane to correctness are removed by the abstraction function.

The case expression works identically to the if expression. The case argument selects one case and the associated facet expression is returned. Again, the facets in the case statement must share a common type and the case statement is of that type.

Let Expressions

The let expression defines local items over expressions or collections of terms. These local items may have any type including a facet type. Thus, the let expression may declare new facets for use in definitions. This is technically not a facet algebra operation, but is worth mentioning, as it can have utility in complex specification definition.

Example 14.7. Conditional Specifications Using both if and case Expressions

The system facet below is a partial specification of a system where one of two system models may be selected using the parameter lowPower. The intent is to allow a user to select between low and normal power configurations of the same system. By instantiating lowPower with true, the specification includes a low-power CPU model. Alternatively, instantiating lowPower with false selects a normal, higher-power CPU.

facetsystem(lowPower:: design boolean)::discretetime is
begin
  c1 : component1(...);
  cpu: if lowPower
          then lp_arm(...)
          else cots_arm(...)
       end if;
  c2 : component2(...);
  ...
  cn : componentN(...);
end facet system;

Such conditional specifications are also useful for selecting between different vendor models of a component. The following specification allows this by using a case statement to select a model based on a single input parameter.

package multiVendor()::static is
   vendor :: type is data
      Mot() :: Motp
      |TI() :: TIp
      |ibm() :: ibmp
   end data;
end package multiVendor;

use multiVendor();
facet system(cpuModel:: design vendor)::discrete_time is
begin
   c1: component1(...);
   cpu: case cpuModel of
        {Mot()}-> mot-arm(...)
        {TI()}-> ti-arm(...)
        {ibm()}-> ibm-arm(...)
     end case;
   c2: component2(...);
   ...
   cN: componentN(...);
   end facet system;

The multiVendor package defines a single type used to specify the CPU vendor in the specification. The case statement then uses the defined vendor values to select from three different cpu implementations.

Example 14.8. Local Facet Definition Using a let Expression to Define a Half-adder

The facet adder2 defines a structural 2-bit adder using a half-adder to sum the first bit and a full adder to sum the second. The let clause is used to define a local half-adder by instantiating a full adder with a carry in of 0.

facet adder2(x0,y0,x1,y1,z0,z1,z2)::static is
  c0 :: bit;
begin
  let halfAdder(x,y:: input bit; z,cout:: output bit)
        be adder(x,y,0,z,cout) in
     a0: halfAdder(x0,y0,z0,c0);
     a1: adder(x1,y1,c0,z1,z2);
  end let;
end facet adder2;

There are numerous mechanisms for writing this adder, including using two full adders rather than creating a half-adder. This mechanism is a perfectly reasonable alternative.

Higher-Order Facets

Higher-order facets are facets having facet type parameters, in the same spirit as higher-order functions having function type parameters. Such facets are exceptionally useful for defining high-level architectures and other common system structures.

An exceptionally common structural architecture is sequential ordering of two or more operations. We will refer to this as the batch sequential architecture defined by facet batchSequential.

facet batchSequential[T0,T1,T2::type]
                     (f1(x::T0; y::T1)::state_based;
                     f2(y::T1; z::T2)::state_based;
                     i:: input T0; o:: output T2)::state_based is
  x::T1;
begin
  c1: f1(i,x);
  c2: f2(x,o);
end facet batchSequential;

The batchSequential facet signature defines three universally quantified parameters, two facet types and an input and output. The universally quantified parameters, T0, T1, and T2 define the input, exchange, and output types associated with the facet. These types are not restricted in any way and can take any type value desired. Making them universally quantified allows discovery or direct specification of the desired type.

The two sequentially connected components are f1 and f2. Facet f1 takes component input, performs a transformation, and generates output for f2. Facet f2 takes output from f1 and generates component output. The universally quantified parameters must be instantiated to satisfy type conditions.

Example 14.9. Using the Batch Sequential Architecture

Now that we have a batch sequential architecture, we can put it to work by instantiating it in other systems or defining new components. The partial facet, system, uses sequenced negate facets to implement a delay buffer. The buffer component instantiates batchSequential with two negation operations and specifies inputs and outputs to the new device.

facet negate(i:: input bit; o:: output bit)::state_based is
begin
  o'=not(i);
end facet negate;

 facet system(...)::state_based is
begin
  ...
  buffer: batchSequential(negate,negate, in, out);
  ...
end facet system;

Alternatively, we define a buffer facet that can be reused throughout a system specification. Here a new facet, buffer, is defined by specifying its value instantiating batchSequential.

buffer(i:: input bit,o:: output bit)::state_based is
  batchSequential(negate,negate, i, o);

The new buffer facet is defined over two parameters that serve as input and output for the batchSequential instantiation. The component parameters to batchSequential are again instantiated with copies of negate.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.53.119