Tải bản đầy đủ - 0 (trang)
3 Slicers, Generalized Moore Machines and Tuple Builders

3 Slicers, Generalized Moore Machines and Tuple Builders

Tải bản đầy đủ - 0trang

When RV Meets CEP


Fig. 4. Processor graph for the “Auction Bidding” query (Color figure online)

Each transition in this Moore machine contains two parts: the top part is a

function to evaluate on the input event, to decide whether the transition should

fire. The bottom part contains instructions on how to modify the Context object

of the processor. For example, the top left transition fires if the first element of

the event is the string “Create Auction”. If so, the transition is taken, and the

processor’s context is updated with the associations Last Price → 0, Days → 0.

The values of Min. Price and Max. Days are set with the content of the third and

fourth element of the tuple, respectively. The remaining transitions take care of

updating the minimum price and the number of days elapsed according to the

events received.

Each state of the Moore machine is associated with an output value. For

three of these states, the value to output is the empty event, meaning that no

output should be produced. For the remaining two states, the value to output is

the current content of Days, as defined in the processor’s context.

According to the semantics of the Slicer, each output event will consist of

a set, formed by the last output of every instance of the Moore machine. Thus,

this set will contain the number of elapsed days of all items whose auction is

currently open (the Moore machine for the other items outputs no number).


S. Hall´e

This set is then passed to a function processor, which computes the average of

its values (sum divided by cardinality).

As a bonus, we show how to plot a graph of the evolution of this average over

time. We fork the previous output; one branch of this fork goes into a Mutator,

which turns the set into the value 1; this stream of 1 s is then sent to a cumulative

function processor Σ0+ that computes their sum. Both this and the second branch

of the fork are fed into a function processor, that creates a named tuple where x

is set to the value of the first input, and y is set to the value of the second input.

The result is a tuple where x is the number of input events, and y is the average

computed earlier. These tuples are then accumulated into a set with the means

of another cumulative function processor, this time performing the set addition

operation. The end result is a stream of sets of (x, y) pairs, which could then be

sent to a Scatterplot processor to be plotted with the help of Gnuplot.

One can see again that processors of multiple palettes are involved, and events

of various types are mixed: predicates (pink), sets of numbers (grey), numbers

(turquoise), and named tuples (yellow).



In this paper, we have presented a short introduction to the field of Complex

Event Processing, and highlighted the differences between classical CEP problems and properties typically considered in Runtime Verification. In particular,

we have seen how CEP problems involve intricate computations and transformations over data fields inside events, while runtime monitors are generally more

powerful for evaluating properties that relate to the sequencing of events.

These observations motivated the development of BeepBeep, an event stream

processing engine that attempts to reconcile these two fields. In BeepBeep’s

generic architecture, basic units of computation called processors can be freely

composed to evaluate a wide range of expressions. Given an appropriate toolbox

of processors, properties involving extended finite-state machines, temporal logic,

aggregation and various other concepts can be evaluated. Moreover, through the

modular mechanism of palettes, end users can easily create their own processors,

thereby extending the expressiveness of the tool.

Still, several elements of BeepBeep have not been discussed due to lack of

space. To start with, BeepBeep proposes its own declarative input language,

eSQL, which provides an alternative to creating processor chains through “glue”

code; the detailed exposition of this language, which is still under development,

is left for a future publication. Performance is also a key aspect of both RV and

CEP; benchmarks have been conducted to compare BeepBeep’s throughput with

a roster of CEP software on a set of queries; these results are, again, left for a

future research paper devoted to the question.

Several research problems around BeepBeep’s concepts of processors and

event streams are also left unexplored. For example, BeepBeep currently does not

support lazy evaluation; if the output of an n-ary processor can be determined by

looking at fewer than n inputs, all inputs must still be computed and consumed.

When RV Meets CEP


Implementing lazy evaluation in a stream processing environment could provide

some performance benefits, but is considered at the moment as a non-trivial task.

In addition, since each processor represents an independent unit of computation

communicating through message passing, chains of processors should be easily

amenable to parallelization; whether this would bring tangible improvements in

terms of throughput is currently unknown.

In time, it is hoped that BeepBeep will be adopted as a modular framework

under which multiple event processing techniques can be developed and coexist,

and that their potential for composition will make the sum greater than its parts.


1. Esper. http://espertech.com

2. LINQ (language-integrated query). http://msdn.microsoft.com/en-us/library/


3. Simple API for XML. http://docs.oracle.com/javaee/1.4/tutorial/doc/JAXPSAX.

html. Accessed 13 Dec 2013

4. StreamBase SQL. http://streambase.com

5. VoltDB. http://voltdb.com

6. Abadi, D.J., Ahmad, Y., Balazinska, M., C

¸ etintemel, U., Cherniack, M., Hwang,

J.H., Lindner, W., Maskey, A., Rasin, A., Ryvkina, E., Tatbul, N., Xing, Y.,

Zdonik, S.B.: The design of the Borealis stream processing engine. In: CIDR, pp.

277–289 (2005)

7. Arasu, A., Babcock, B., Babu, S., Cieslewicz, J., Datar, M., Ito, K., Motwani,R.,

Srivastava, U., Widom, J.: Stream: the stanford data stream management system. Technical report 2004-20, Stanford InfoLab (2004). http://ilpubs.stanford.


8. Barringer, H., Falcone, Y., Havelund, K., Reger, G., Rydeheard, D.: Quantified event automata: towards expressive and efficient runtime monitors. In:

Giannakopoulou, D., M´ery, D. (eds.) FM 2012. LNCS, vol. 7436, pp. 68–84.

Springer, Heidelberg (2012). doi:10.1007/978-3-642-32759-9 9

9. Barringer, H., Rydeheard, D.E., Havelund, K.: Rule systems for run-time monitoring: from Eagle to RuleR. J. Log. Comput. 20(3), 675–706 (2010)

10. Basin, D., Harvan, M., Klaedtke, F., Z˘

alinescu, E.: MONPOLY: monitoring usagecontrol policies. In: Khurshid, S., Sen, K. (eds.) RV 2011. LNCS, vol. 7186, pp.

360–364. Springer, Heidelberg (2012). doi:10.1007/978-3-642-29860-8 27

11. Basin, D.A., Klaedtke, F., Marinovic, S., Zalinescu, E.: Monitoring of temporal

first-order properties with aggregations. Formal Methods Syst. Des. 46(3), 262–

285 (2015). http://dx.doi.org/10.1007/s10703-015-0222-7

12. Berry, A., Milosevic, Z.: Real-time analytics for legacy data streams in health: Monitoring health data quality. In: Gasevic, D., Hatala, M., Nezhad, H.R.M., Reichert,

M. (eds.) EDOC, pp. 91–100. IEEE (2013)

13. Bodden, E., Hendren, L.J., Lam, P., Lhot´

ak, O., Naeem, N.A.: Collaborative runtime verification with Tracematches. J. Log. Comput. 20(3), 707–723 (2010)

14. Brenna, L., Gehrke, J., Hong, M., Johansen, D.: Distributed event stream processing with non-deterministic finite automata. In: Gokhale, A.S., Schmidt, D.C. (eds.)

DEBS. ACM (2009)


S. Hall´e

15. Chandrasekaran, S., Cooper, O., Deshpande, A., Franklin, M.J., Hellerstein, J.M.,

Hong, W., Krishnamurthy, S., Madden, S., Raman, V., Reiss, F., Shah, M.A.:

TelegraphCQ: continuous dataflow processing for an uncertain world. In: CIDR


16. Cheng, K., Krishnakumar, A.S.: Automatic functional test generation using theextended finite state machine model. In: DAC, pp. 86–91 (1993). http://doi.acm.


17. Colombo, C., Gauci, A., Pace, G.J.: LarvaStat: monitoring of statistical properties.

In: Barringer, H., Falcone, Y., Finkbeiner, B., Havelund, K., Lee, I., Pace, G., Ro¸su,

G., Sokolsky, O., Tillmann, N. (eds.) RV 2010. LNCS, vol. 6418, pp. 480–484.

Springer, Heidelberg (2010). doi:10.1007/978-3-642-16612-9 38

18. Colombo, C., Pace, G.J., Schneider, G.: LARVA - safer monitoring of real-time Java

programs (tool paper). In: Seventh IEEE International Conference on Software

Engineering and Formal Methods (SEFM), pp. 33–37. IEEE Computer Society,

November 2009

19. D’Angelo, B., Sankaranarayanan, S., S´

anchez, C., Robinson, W., Finkbeiner, B.,

Sipma, H.B., Mehrotra, S., Manna, Z.: LOLA: runtime monitoring of synchronous

systems. In: 12th International Symposium on Temporal Representation and Reasoning (TIME 2005), 23–25 June 2005, Burlington, Vermont, USA, pp. 166–174.

IEEE Computer Society (2005). http://dx.doi.org/10.1109/TIME.2005.26

´ Pistoia, M. (eds.) Proceedings of the 2008 Workshop on Program20. Erlingsson, U.,

ming Languages and Analysis for Security, PLAS 2008, Tucson, AZ, USA, June 8,

2008. ACM (2008)

´ Schneider, F.B.: IRM enforcement of Java stack inspection. In:

21. Erlingsson, U.,

IEEE Symposium on Security and Privacy, pp. 246–255 (2000)

22. Fegaras, L., Dash, R.K., Wang, Y.: A fully pipelined XQuery processor. In: XIMEP (2006)

23. Finkbeiner, B., Sankaranarayanan, S., Sipma, H.: Collecting statistics over runtime

executions. Formal Methods Syst. Des. 27(3), 253–274 (2005). http://dx.doi.org/


24. Fry, C., Sagar, D.: Streaming API for XML, JSR 173 specification (2003). https://


25. Goldsmith, S., O’Callahan, R., Aiken, A.: Relational queries over program traces.

In: OOPSLA, pp. 385–402 (2005)

26. Hall´e, S., Gaboury, S., Bouchard, B.: Towards user activity recognition through

energy usage analysis and complex event processing. In: PETRA. ACM (2016)

27. Hall´e, S., Varvaressos, S.: A formalization of complex event stream processing.

In: Reichert, M., Rinderle-Ma, S., Grossmann, G. (eds.) 18th IEEE International Enterprise Distributed Object Computing Conference, EDOC 2014, Ulm,

Germany, September 1–5, 2014, pp. 2–11. IEEE Computer Society (2014). http://


28. Hall´e, S., Villemaire, R.: Runtime monitoring of web service choreographies using

streaming XML. In: Shin, S.Y., Ossowski, S. (eds.) SAC, pp. 2118–2125. ACM


29. Hall´e, S., Villemaire, R.: Runtime enforcement of web service message contracts

with data. IEEE T. Serv. Comput. 5(2), 192–206 (2012)

30. Krishnan, R., Goldstein, J., Raizman, A.: A hitchhiker’s guide to StreamInsight

queries, version 2.1 (2012). http://support.sas.com/documentation/onlinedoc/

dfdmstudio/2.4/dfU ELRG.pdf

31. Luckham, D.C.: The power of events - An introduction to complex event processing

in distributed enterprise systems. ACM (2005)

When RV Meets CEP


32. Martin, M.C., Livshits, V.B., Lam, M.S.: Finding application errors and security

flaws using PQL: a program query language. In: OOPSLA, pp. 365–383 (2005)

33. Meredith, P.O., Jin, D., Griffith, D., Chen, F., Rosu, G.: An overview

of the MOP runtime verification framework. STTT 14(3), 249–289 (2012).


34. Perera, S., Suhothayan, S., Vivekanandalingam, M., Fremantle, P.,

Weerawarana, S.: Solving the grand challenge using an opensource CEP

engine. In: Bellur,U., Kothari, R. (eds.) The 8th ACM International Conference

on Distributed Event-Based Systems, DEBS 2014, Mumbai, India, May 26–29,

2014, pp.288–293. ACM (2014). http://doi.acm.org/10.1145/2611286.2611331

35. Reger, G., Cruz, H.C., Rydeheard, D.: MarQ: monitoring at runtime with QEA.

In: Baier, C., Tinelli, C. (eds.) TACAS 2015. LNCS, vol. 9035, pp. 596–610.

Springer, Heidelberg (2015). doi:10.1007/978-3-662-46681-0 55

36. Rodrigues, J.: Health Information Systems: Concepts, Methodologies, Tools, and

Applications, vol. 1. IGI Global, Hershey (2010)

37. Stolz, V., Bodden, E.: Temporal assertions using AspectJ. Electr. Notes Theor.

Comput. Sci. 144(4), 109–124 (2006)

38. Varvaressos, S., Lavoie, K., Gaboury, S., Hall´e, S.: Automated bug finding in video

games: A case study for runtime monitoring. ACM Computers in Entertainment

(2014, in press)

39. Wu, E., Diao, Y., Rizvi, S.: High-performance complex event processing over

streams. In: Chaudhuri, S., Hristidis, V., Polyzotis, N. (eds.) SIGMOD Conference, pp. 407–418. ACM (2006)

Frama-C, A Collaborative Framework for C

Code Verification: Tutorial Synopsis

Nikolai Kosmatov and Julien Signoles(B)

CEA, LIST, Software Reliability and Security Laboratory, PC 174,

91191 Gif-sur-Yvette, France


Abstract. Frama-C is a source code analysis platform that aims at

conducting verification of industrial-size C programs. It provides its users

with a collection of plug-ins that perform static and dynamic analysis

for safety- and security-critical software. Collaborative verification across

cooperating plug-ins is enabled by their integration on top of a shared

kernel, and their compliance to a common specification language, ACSL.

This paper presents a three-hour tutorial on Frama-C in which we

provide a comprehensive overview of its most important plug-ins: the

abstract-interpretation based plug-in Value, the deductive verification

tool WP, the runtime verification tool E-ACSL and the test generation

tool PathCrawler. We also emphasize different possible collaborations

between these plug-ins and a few others. The presentation is illustrated

on concrete examples of C programs.

Keywords: Frama-C · ACSL · Abstract interpretation · Deductive

verification · Runtime verification · Test generation · Combinations of




The last few decades have seen much of the groundwork of formal software analysis being laid. Several angles and theoretical avenues have been explored, from

deductive verification to abstract interpretation to program transformation to

monitoring to concolic testing. While much remains to be done from an academic standpoint, these techniques have become mature enough to have been

successfully implemented and used in industrial settings [1].

However, although verification of C programs is of paramount importance

because the C programming language is still the language of choice for developing safety-critical systems and is also routinely used for security-based applications, verifying large C programs remains a time-consuming and challenging

task. One of the reasons is related to the C programming language itself since it

combines high level features like arrays and low level features like user-controlled

memory allocations, bitfields and unions. Another reason comes from weaknesses

This work has received funding for the S3P project from French DGE and BPIFrance.

c Springer International Publishing AG 2016

Y. Falcone and C. Sanchez (Eds.): RV 2016, LNCS 10012, pp. 92–115, 2016.

DOI: 10.1007/978-3-319-46982-9 7

Frama-C, A Collaborative Framework for C Code Verification


of each verification technique: dynamic techniques are not bothered by C code

complexity but are not exhaustive, abstract interpretation is exhaustive and

almost automatic but may be imprecise and cannot verify complex functional

properties, while deductive methods may tackle a broad varieties of properties

but require formal specifications and may be less efficient in presence of low level

code. One effective way to circumvent this problem is to combine several analyses in order to reduce weaknesses of each one thanks to the others. For instance,

abstract interpretation can ensure the absence of most runtime errors, deductive

verification can prove most functional properties, while monitoring can check

at runtime the remaining properties. Such analysis combinations is the raison

d’ˆetre of Frama-C.

The Frama-C software analysis platform [2] provides a collection of scalable,

interoperable, and sound software analyses for the industrial analysis of ISO

C99 source code. The platform is based on a kernel which hosts analyzers as

collaborating plug-ins and uses the ACSL formal specification language [3] as

a lingua franca. Frama-C includes plug-ins based on abstract interpretation,

deductive verification, monitoring and test case generation, as well as a series of

derived plug-ins which build elaborate analyses upon the basic ones. This large

variety of analysis techniques and its unique collaboration capabilities make

Frama-C most suitable for developing new code analyzers and applying code

analysis techniques in many academic and industrial projects.

This article is a companion paper of a 3-h tutorial which brings participants to

a journey into the Frama-C world along its main plug-ins. It aims at providing the

essence of each technique and tool along with a few illustrating examples. While

several tutorials about some parts of Frama-C have already been presented in

previous conferences [4–8], none of them have already presented all these techniques altogether. Here, after a general overview of Frama-C (Sect. 2), we present

deductive verification tool WP [2,9] (Sect. 3), abstract interpretation based plugin Value [2,10] and its recent redesign Eva (Sect. 4), the runtime verification

tool E-ACSL [11,12] (Sect. 5) and the test generation tool PathCrawler [13,14]

(Sect. 6). A last section is dedicated to some of their possible collaborations

(Sect. 7).


Overview of FRAMA-C

Frama-C is a platform which aims at analyzing source code written in ISO

C99. This code may be annotated with formal specifications written in the ACSL

formal specification language [3] (presented in Sect. 3). Recently Frama-Clang

has been released as a prototype Frama-C extension to handle C++ code. The

platform is written in OCaml [15] and based on a plug-in architecture [16]: each

analyzer is a plug-in which is linked against the Frama-C kernel.

The kernel provides a normalized representation of C programs and ACSL

specifications. In addition, the kernel provides several general services for supporting plug-in development and providing convenient features to Frama-C’s

end-users. For instance, messages, source code and annotations are uniformly


N. Kosmatov and J. Signoles





Abstract Interpretation


Specification Generation

Deductive Verification




Formal Methods

Code Transformation


Frama-C Plug-ins


Dynamic Analysis


Semantic constant folding

Browsing of unfamiliar code







Scope & Data-flow browsing

Fig. 1. Frama-C plug-in gallery

displayed, whereas parameters and command line options are homogeneously

handled. The kernel also allows plug-ins to collaborate with each other either

sequentially or in parallel. Sequential collaboration consists in a chain of analyses

that perform operations one after another, while parallel collaboration combines

partial analysis results from several analyzers to complete a full program verification. Examples of collaborations will be provided in Sect. 7. In particular, the

kernel consolidates analysis results to provide the users with a synthesis of what

is proven and ensure logical consistency when verifying dependent properties:

the proof of a property P by analyzer A may depend on the validity of another

property Q whose proof is done by analyzer B [17].

The Frama-C kernel is open source (under LGPL v2.1), as well as many of

its plug-ins. Several plug-ins are presented in Fig. 1. Many important plug-ins

are dedicated to program verification. First, Frama-C comes with a powerful

abstract interpretation framework based on Value, which aims at computing

over-approximations of possible values of program variables at each program

point. Value is presented in Sect. 4. Next, Frama-C provides two alternative

plug-ins for deductive verification: Jessie (which is now deprecated) and Wp.

These plug-ins aim at verifying that a given C code satisfies its specification

expressed as ACSL annotations. ACSL language and WP plug-in are presented

in Sect. 3. Finally, Frama-C provides dynamic verification through the E-Acsl

plug-in which aims at verifying annotations at runtime. This plug-in is presented in Sect. 5. Another dynamic tool, PathCrawler 1 is dedicated to test


unlike many other Frama-C analyzers, PathCrawler is currently not open source

but is available through the online test generation service http://pathcrawler-online.


Frama-C, A Collaborative Framework for C Code Verification


case generation and is presented in Sect. 6. Plug-ins Sante, StaDy and Ltest

implement different collaborations between static and dynamic analyses. They

are introduced in Sect. 7.

Other plug-ins are not directly program verifiers. Some of them aim at helping

the users to better understand a source code they are not familiar with: plug-in

Metrics computes some useful metrics about the code, plug-in Occurrences

displays in the Frama-C GUI all occurrences of a particular left-value (taking

into account aliasing), while a few other plug-ins compute scope and dataflow

dependency information. Plug-in Callgraph computes the callgraph, taking

into consideration function pointers as soon as Value has been executed.

Some plug-ins perform program transformations. Plug-in Semantic Constant Folding replaces constant variables by their numerical values and propagates them along the dataflow by taking into account aliasing. Plug-in Slicing simplifies the code by removing code fragments that are irrelevant with

respect to a given program property (e.g. preserve the effects of a particular

statement). Plug-in Sparecode can be seen as a particular case of Slicing

which removes dead code. Plug-in Impact computes the values and statements

impacted (directly or transitively) by the side effects of a given statement. It is

the forward counterpart of the usual (backward) slicing, but it does not necessarily generate a new program: by default it just highlights the impacted statement.

Frama-C also allows analyzers to generate new ACSL annotations which

encode specic properties. Plug-in Aoraă takes as input a Bă

uchi automaton or an

LTL formula and generates ACSL annotations which encode the corresponding

temporal property that can be verified by other means. In the same spirit, plugin Rte generates an ACSL annotation for every possible undefined behavior of

the source code. For instance, it generates a guard y = 0 before a division by y

in the source code.



Specification and Deductive Verification with


Specification of C Programs with ACSL

ACSL (ANSI/ISO C Specification Language) [3] is a formal behavioral specification language offered by Frama-C and shared by different Frama-C analyzers.

It allows its users to specify functional properties of C programs similarly to Eiffel

[18] and JML [19]. It is based on the notion of function contract. The contract of

a function f specifies the preconditions that are supposed to be true before a call

of f (i.e. ensured by the caller), and the postconditions that should be satisfied

after the call of f (and should be thus established during the verification of f ).

The preconditions are specified in requires clauses, while the postconditions

are stated in ensures clauses. An additional type of postconditions, specified in

an assigns clause in ACSL and used for the so-called frame rule, states a list of

locations of the global memory state that may have a different value before and

after the call. When the contract of f contains such a clause, all locations that


N. Kosmatov and J. Signoles

Fig. 2. Function all_zeros specified in ACSL (file all_zeros.c).

are not mentioned in it must have the same value before and after the call of f .

Function contracts can be also represented in the form of different behaviors.

Predicates used in annotations are written in typed first-order logic. Variables

have either a C type or a logical type (e.g. integer or real for mathematical

integer or real numbers). The user can define custom functions and predicates

and use them in annotations together with ACSL built-ins. Indeed, ACSL features its own functions and predicates to describe memory states. In particular,

regarding memory-related properties, \valid(p) expresses validity of a pointer

p (i.e. being a non-null pointer which can be safely accessed by the program);

\base addr(p), \block length(p), and \offset(p) express respectively the

base address, the size of the memory block containing p and the offset of p

inside it (in bytes), while \initialized(p) is true whenever the pointed location *p has been initialized. We refer the reader to [3] for detailed documentation

of all ACSL features.

Example of Specifications. Figure 2 illustrates a C function all_zeros specified

in ACSL. This function receives as arguments an array t and its size n and

checks whether all elements of the array are zeros. If yes, it returns a nonzero

value, and 0 otherwise. The function contract contains a precondition (line 1)

and postconditions (lines 2–4). The precondition states that the input array

contains n valid memory locations at indices 0..(n-1) that can be safely read

or written, and that the size n is non negative. This property must be ensured by

the caller and should be thus specified in the precondition. The assigns clause

at line 2 states that the function is not allowed to modify any non-local variable.

Without this clause, an erroneous implementation writing zeros in all elements

Frama-C, A Collaborative Framework for C Code Verification


Fig. 3. Function find_value specified in ACSL.

of the array and returning 1 would be considered correct with respect to the

contract. Finally, the clause at lines 3–4 states that the result is nonzero if and

only if all elements of the array are equal to zero. The loop contract at lines 8–12

will be discussed in the next section.

Figure 3 provides another example of a specified function. This function is

only declared and takes as arguments an array t of size n and some element elt.

It must return an index i such than t[i] = elt, or −1 if there is no such index.

The precondition (line 1) and the assigns clause (line 2) are similar to the ones

of the function all_zeros. The postcondition is expressed through two named

behaviors. They correspond to the two different cases of the contract. First, the

behavior present states that, if the searched element elt is present in the array

(line 4), the function’s result is an index with the expected property (line 5). The

behavior absent corresponds to the opposite case (line 7). In that case, the function returns −1 (line 8). Additionally the disjoint behaviors clause states that

these behaviors are mutually exclusive (line 9), while the complete behaviors

clause indicates that their cover all the possible cases of the function (line 10).

In other words, being both disjoint and complete guarantees that one and only

one behavior applies at each function call.


Deductive Verification with FRAMA-C/Wp

Among other formal software verification techniques, deductive program verification consists in establishing a rigorous mathematical proof that a given program

respects its specification. When no confusion is possible, one also says for short

that deductive verification consists in “proving a program”. The weakest precondition calculus proposed by Dijkstra [20] reduces any deductive verification

problem to establishing the validity of first-order formulas called verification conditions. The Wp plug-in [2,9] of Frama-C performs weakest precondition calculus for deductive verification of C programs. Various automatic SMT solvers,

such as Alt-Ergo, CVC4 and Z3, can be used to prove the verification conditions

generated by Wp.

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

3 Slicers, Generalized Moore Machines and Tuple Builders

Tải bản đầy đủ ngay(0 tr)