Skip to main content

Actor-Like cP Systems

  • Conference paper
  • First Online:

Part of the book series: Lecture Notes in Computer Science ((LNTCS,volume 11399))

Abstract

We propose a new version of our cP systems, extended to match the Actor model, thereby solving an earlier open problem. In the new version, top-cells have control upon the input message flow, to decide which message types are acceptable and at what time. We assess its capabilities by proposing a revised version of our previous best models for the Byzantine agreement problem – a famous problem in distributed algorithms, with non-trivial data structures and algorithms. The new actor-based solution uses a substantially shorter fixed sized alphabet and ruleset, independent of the problem size. Moreover, in contrast to our previous models, additional helper/firewall cells are not anymore needed to ensure protection against Sybil attacks. Also, as any standard distributed algorithm, the novel actor-based cP model uses exactly one top-level cell for each process in Byzantine agreement, thus solving another open problem.

This is a preview of subscription content, log in via an institution.

Buying options

Chapter
USD   29.95
Price excludes VAT (USA)
  • Available as PDF
  • Read on any device
  • Instant download
  • Own it forever
eBook
USD   39.99
Price excludes VAT (USA)
  • Available as EPUB and PDF
  • Read on any device
  • Instant download
  • Own it forever
Softcover Book
USD   54.99
Price excludes VAT (USA)
  • Compact, lightweight edition
  • Dispatched in 3 to 5 business days
  • Free shipping worldwide - see info

Tax calculation will be finalised at checkout

Purchases are for personal use only

Learn about institutional subscriptions

References

  1. Abd-El-Malek, M., Ganger, G.R., Goodson, G.R., Reiter, M.K., Wylie, J.J.: Fault-scalable Byzantine fault-tolerant services. In: Herbert, A., Birman, K.P. (eds.) SOSP, pp. 59–74. ACM (2005)

    Google Scholar 

  2. Cachin, C., Kursawe, K., Shoup, V.: Random oracles in Constantinople: practical asynchronous Byzantine agreement using cryptography. J. Cryptol. 18(3), 219–246 (2005)

    Article  MathSciNet  Google Scholar 

  3. Castro, M., Liskov, B.: Practical Byzantine fault tolerance and proactive recovery. ACM Trans. Comput. Syst. 20(4), 398–461 (2002)

    Article  Google Scholar 

  4. Cooper, J., Nicolescu, R.: The travelling salesman problem in cP systems. In: Zhang, G., Wang, J., Pan, L., Qiang, Zeng, Y. (eds.) Asian Conference on Membrane Computing, pp. 9–21 (2017)

    Google Scholar 

  5. Dinneen, M.J., Kim, Y.-B., Nicolescu, R.: A faster P solution for the Byzantine agreement problem. In: Gheorghe, M., Hinze, T., Păun, G., Rozenberg, G., Salomaa, A. (eds.) CMC 2010. LNCS, vol. 6501, pp. 175–197. Springer, Heidelberg (2010). https://doi.org/10.1007/978-3-642-18123-8_15

    Chapter  Google Scholar 

  6. Dinneen, M.J., Kim, Y.B., Nicolescu, R.: A faster P solution for the Byzantine agreement problem. Report CDMTCS-388, Centre for Discrete Mathematics and Theoretical Computer Science, The University of Auckland, Auckland, New Zealand, July 2010. http://www.cs.auckland.ac.nz/CDMTCS/researchreports/388-DKN.pdf

  7. Gilad, Y., Hemo, R., Micali, S., Vlachos, G., Zeldovich, N.: Algorand: Scaling Byzantine agreements for cryptocurrencies. In: Proceedings of the 26th Symposium on Operating Systems Principles, pp. 51–68. ACM, New York (2017)

    Google Scholar 

  8. Hewitt, C.: What is computation? Actor model versus Turing’s model. In: A Computable Universe: Understanding and Exploring Nature as Computation, pp. 159–185. World Scientific (2013)

    Google Scholar 

  9. Hewitt, C., Bishop, P., Steiger, R.: A universal modular actor formalism for artificial intelligence. In: Proceedings of the 3rd International Joint Conference on Artificial Intelligence, IJCAI 1973, pp. 235–245. Morgan Kaufmann Publishers Inc., San Francisco (1973). http://dl.acm.org/citation.cfm?id=1624775.1624804

  10. Lamport, L., Shostak, R.E., Pease, M.C.: The Byzantine generals problem. ACM Trans. Program. Lang. Syst. 4(3), 382–401 (1982)

    Article  Google Scholar 

  11. Lynch, N.A.: Distributed Algorithms. Morgan Kaufmann Publishers Inc., San Francisco (1996)

    MATH  Google Scholar 

  12. Martin, J.P., Alvisi, L.: Fast Byzantine consensus. IEEE Trans. Dependable Sec. Comput. 3(3), 202–215 (2006)

    Article  Google Scholar 

  13. Nicolescu, R.: Parallel and distributed algorithms in P systems. In: Gheorghe, M., Păun, G., Rozenberg, G., Salomaa, A., Verlan, S. (eds.) CMC 2011. LNCS, vol. 7184, pp. 35–50. Springer, Heidelberg (2012). https://doi.org/10.1007/978-3-642-28024-5_4

    Chapter  Google Scholar 

  14. Nicolescu, R.: Revising the membrane computing model for Byzantine agreement. In: Leporati, A., Rozenberg, G., Salomaa, A., Zandron, C. (eds.) CMC 2016. LNCS, vol. 10105, pp. 317–339. Springer, Cham (2017). https://doi.org/10.1007/978-3-319-54072-6_20

    Chapter  MATH  Google Scholar 

  15. Nicolescu, R., Wu, H.: Complex objects for complex applications. Roman. J. Inf. Sci. Technol. 17(1), 46–62 (2014)

    Google Scholar 

  16. Pease, M.C., Shostak, R.E., Lamport, L.: Reaching agreement in the presence of faults. J. ACM 27(2), 228–234 (1980)

    Article  MathSciNet  Google Scholar 

Download references

Acknowledgments

We are deeply indebted to the co-authors of our former studies on the Byzantine agreement, for their earlier contributions.

Author information

Authors and Affiliations

Authors

Corresponding author

Correspondence to Radu Nicolescu .

Editor information

Editors and Affiliations

Appendix: P Systems with Compound Terms

Appendix: P Systems with Compound Terms

In the interests of self-containment, we present here new and improved material describing the background of cP Systems, for the benefit of readers as yet unfamiliar with the topic. Much of this appeared in a similar form, most recently in [4], though it has now undergone several significant changes.

Besides some minor terminology and notation changes, here we start with an extended grammar, that includes three notable additions: (i) numbers; (ii) lists; and (iii) actor-like control on receiving messages. The first two items, numbers and lists, that were already possible as constructs derivable from the earlier basic grammar, have been now included, because these seem needed in almost any non trivial application. The last item, however, is a critical new addition, that would have previously been possible only by interfering with the internal cell logic and adding channel-like intermediary cells; the net effect would been loss in clarity, productivity, and efficiency.

Fig. 19.
figure 19

Bird’s eye view of a sample cP system, with top-level cells and subcells.

1.1 7.1 cP Systems

As seen in Fig. 19, cP systems share and extend some of the fundamental features of both traditional cell-like (tree-based) and tissue (graph-based) P systems:

  • Top-level cells are organised in digraph networks and are usually differentiated by unique IDs. Top-level cells represent nodes in a distributed computation (aka inter-cell parallelism).

  • Each arc represents a unidirectional communication channel and has two labels: one label at the source cell and another at the target cell (these two labels may, but need not, be different). Where graphs are used, each edge is considered a pair of opposite arcs.

  • Top-level cells contain nested (and labelled) sub-cells. Subcells represent local data that can be processed either sequentially or in the max-parallel mode (aka intra-cell parallelism).

  • The evolution is governed by multiset rewriting+communication rules, running in exactly-once or max-parallel modes.

  • Only top-level cells have evolution rules – nested subcells are just passive data repositories. This seems a severe limitation; however, it is more than compensated by the provision of powerful higher-level rules.

  • In a synchronous evolution, each internal step takes zero time units (sic!), and each message takes exactly one time unit to transit from source to target. This model is equivalent to the traditional model, where internal steps take one time unit and messages are instantaneous.

  • In an asynchronous evolution, each message may take any finite real time (in \(\mathcal {R}_{\ge 0}\)) to transit from source to target. This model closely matches the standard runtime model of asynchronous distributed algorithms, and includes the synchronous model as a particular case.

  • Top-level cells send messages – via rhs of rewriting rules, using symbol ‘!’ – over outgoing arcs, to their structural neighbours.

  • Top-level cells receive messages – via lhs of rewriting rules, using symbol ‘?’ – over incoming arcs, from their structural neighbours.

  • Messages which arrive at the target cell are not immediately inserted among the target’s contents; instead, these messages are conceptually “enqueued” and there is one message “queue” – read “multiset” – for each incoming arc.

  • Receiving cells have full control over the time and format of messages accepted from the message “queues”. This model matches similar facilities of Actor models [8, 9], such as Receive/Scan (F#) and receive/stash (Akka).

  • Messages not yet accepted remain in their message “queues”.

The last four items of the above list are brand new extensions, designed to match the capabilities of Actor models.

Note: although not strictly necessary – but also shared with other versions of the traditional P systems – our typical rulesets are state based and run in a weak priority mode.

Note: subcells – aka compound terms – play the roles of cellular micro-compartments or substructures, such as organelles, vesicles or cytoophidium assemblies (“snakes”), which are embedded in cells or travel between cells, but without having the full processing power of a complete cell. In our proposal, subcells represent nested labelled data compartments which have no own processing power: they are acted upon by the rules of their enclosing cells.

1.2 7.2 Extended Grammars for Terms and Rules

Our basic vocabulary consists of atoms and variables, collectively known as simple terms, plus a dedicated symbol, ‘\( 1 \)’, and a few delimiters: ‘(’, ‘)’, ‘[’, ‘]’, ‘’.

Compound terms are similar to Prolog-like first-order terms, but recursively built from multisets of atoms and variables. Roughly, our compound terms play the role of hierarchically nested subcells in traditional P systems. Numbers and lists are now explicit in this new extended grammar, although they are ad-hoc derivable as compound terms, by the other rules (as also shown below).

Together, simple terms, compound terms, numbers, and lists are collectively called terms and can be defined by the formal grammar in Fig. 20, where ‘...’ denote arbitrary multiset repetitions, including zero times.

Fig. 20.
figure 20

Term grammar

Symbol \(\lambda \) is an emphatic designation of the empty multiset. Numbers are represented in base 1, with \( 1 \) as unity symbol. Atoms are typically denoted by lower case letters, such as a, b, c. Variables are typically denoted by uppercase letters, such as X, Y, Z. If useful, atoms and variables may include subscripts or primes. Functors are subcell labels, not necessarily distinct; here functors can only be atoms (not variables).

For improved readability, we also consider anonymous variables, which are denoted by underscores (‘\(\_\)’). Each underscore occurrence represents a new unnamed variable and indicates that something, in which we are not interested, must fill that slot.

Terms that do not contain variables are called ground, e.g.:

  • Ground terms: a, \(a()=a(\lambda )\), a(b), a(bc), \(a(b^2 c)\), a(b(c)), \(a(bc(\lambda ))\), a(b(c)d(e)), a(b(c)d(e)), \(a(b(c)d(e(\lambda )))\), \(a(bc^2 d)\).

  • Terms which are not ground: X, a(X), a(bX), a(b(X)), a(XY), \(a(X^2)\), a(XdY), a(Xc()), a(b(X)d(e)), a(b(c)d(Y)), \(a(b(X^2)d(e(Xf^2)))\); also, using anonymous variables: \(\_\), \(a(b\_)\), \(a(X\_)\), \(a(b(X)d(e(\_)))\).

  • This term-like construct which starts with a variable is not a term (this grammar defines first-order terms only): X(aY).

In concrete models, cells may contain ground terms only (no variables). Rules may however contain any kind of terms, atoms, variables and terms (whether ground and not).

Unification. All terms which appear in rules (ground or not) can be (asymmetrically) matched against ground terms, using an ad-hoc version of pattern matching, more precisely, a one-way first-order syntactic unification (one-way, because cells may not contain variables). An atom can only match another copy of itself, but a variable can match any multiset of ground terms (including \(\lambda \)). This may create a combinatorial non-determinism, when a combination of two or more variables are matched against the same multiset, in which case an arbitrary matching is chosen. For example:

  • Matching \(a(b(X)fY) = a(b(cd(e))f^2g)\) deterministically creates a single set of unifiers: \(X, Y = cd(e), fg\).

  • Matching \(a(XY^2) = a(de^2f)\) deterministically creates a single set of unifiers: \(X, Y = df, e\).

  • Matching \(a(b(X)c( 1 X)) = a(b( 1 ^2)c( 1 ^3))\) deterministically creates one single unifier: \(X = 1 ^2\).

  • Matching \(a(b(X)c( 1 X)) = a(b( 1 ^2)c( 1 ^2))\) fails.

  • Matching \(a(XY) = a(df)\) non-deterministically creates one of the following four sets of unifiers: \(X, Y = \lambda , df\); \(X, Y = df, \lambda \); \(X, Y = d, f\); \(X, Y = f, d\).

Performance Note. If the rules avoid any matching non-determinism, then this proposal should not affect the performance of P simulators running on existing machines. Assuming that multisets are efficiently represented, e.g. via hash-tables, our proposed unification probably adds an almost linear factor. Let us recall that, in similar contexts (no occurs check needed), Prolog unification algorithms can run in O(ng(n)) steps, where g is the inverse Ackermann function. Our conjecture must be proven though, as the novel presence of multisets may affect the performance.

1.3 7.3 High-Level or Generic Rewriting Rules

Typically, our rewriting rules use states and are applied top-down, in the so-called weak priority order.

Pattern Matching. Rewriting rules are matched against cell contents using the above discussed pattern matching, which involves the rule’s left-hand side, promoters and inhibitors. Generally, variables have global rule scope; these are assumed to be introduced by existential quantifiers preceding the rule – with the exception of inhibitors, which may introduce local variables, as further discussed below.

Intuitively, the matching is valid only if, after substituting variables by their values, the rule’s right-hand side contains ground terms only (so no free variables are injected in the cell or sent to its neighbours), as illustrated by the following sample scenario:

  • The cell’s current content includes the ground term: \(n(a \, \phi (b \, \phi (c) \, \psi (d)) \, \psi (e))\)

  • The following (state-less) rewriting rule fragment is considered: \(n(X \, \phi (Y \, \phi (Y_1) \, \psi (Y_2)) \, \psi (Z)) ~ \rightarrow ~ v(X) \, n(Y \, \phi (Y_2) \, \psi (Y_1)) \, v(Z)\)

  • Our pattern matching determines the following unifiers: \(X = a\), \(Y = b\), \(Y_1 = c\), \( Y_2 = d\), \(Z = e\).

  • This is a valid matching and, after substitutions, the rule’s right-hand side gives the new content: \(v(a) ~ n(b \, \phi (d) \, \psi (c)) ~ v(e)\)

Generic Rules Format. More precisely, the rewriting rules are defined by the formal grammar in Fig. 21, where ‘\(\dots \)’ denote arbitrary repetitions (including zero times). We call this format generic, because it actually defines templates involving variables.

Fig. 21.
figure 21

Rule grammar

The rewriting rule is unified according to the following rules; ensuring that all states and cell contents are ground:

  • Lhs local terms must be unifiable to the cell’s ground contents.

  • Promoter local terms must be unifiable to the cell’s ground contents; but inhibitors must not be unifiable to the cell’s ground contents; see also note below.

  • Lhs local terms are consumed as in traditional P systems.

  • Lhs input terms designated by the receive symbol, ‘\(?_\delta \)’, are received over incoming arcs (from cell’s structural neighbours):

    • \(\delta \) is the label of an incoming arc, a set of such labels, or a variable (including the ‘_’ wildcard).

    • \((a)?_i\) indicates that a is received over incoming arc i;

    • \((a)?_{i,j}\) indicates that a is received over incoming arc i or j;

    • \((a)!_X\) indicates that a is received over an arbitrary incoming arc, whose label will be unified with X;

    • Any queued messages that do not match the template ‘a’ are not accepted in the cell, but kept in the corresponding queue.

  • Rhs must contain only ground terms!

  • Rhs local terms become available after the end of the current step only, as in traditional P systems (we can also imagine that these are sent via an ad-hoc fast loopback arc).

  • Rhs output terms designated by the send symbol, ‘\(!_\delta \)’, are sent over outgoing arcs (to cell’s structural neighbours):

    • \(\delta \) is the label of an outgoing arc, a set of such labels, a variable, or the special symbol \(\forall \).

    • \((a)!_i\) indicates that a is sent over outgoing arc i (unicast);

    • \((a)!_{i,j}\) indicates that a is sent over outgoing arcs i and j (multicast);

    • \((a)!_\forall \) indicates that a is sent over all outgoing arcs (broadcast).

  • Application mode \(\alpha \) \(\in \) \(\{\scriptstyle {1}\displaystyle \), \(\scriptstyle {+}\displaystyle \}\), indicates the exactly-once (aka min) or max-parallel mode, as further discussed below.

  • All terms sent out to the same destination and in the same step form one single message and travel together as one single block (regardless of the application modes used).

Application Modes: Exactly-Once and Max-Parallel. To explain our two rule application modes, exactly-once and max-parallel, let us consider a cell, \(\sigma \), containing three counter-like compound terms, \(c( 1 ^2)\), \(c( 1 ^2)\), \(c( 1 ^3)\), and the two possible application modes of the following high-level “decrementing” rule:

figure c

The left-hand side of rule \(\rho _\alpha \), \(c( 1 \, X)\), can be unified in three different ways, to each one of the three c terms extant in cell \(\sigma \). Conceptually, we instantiate this rule in three different ways, each one tied and applicable to a distinct term:

figure d
  1. 1.

    If \(\alpha = \, \scriptstyle {1}\displaystyle \), rule \(\rho _1\) non-deterministically selects and applies exactly one of these virtual rules \(\rho _1\), \(\rho _2\), \(\rho _3\). Using \(\rho _1\) or \(\rho _2\), cell \(\sigma \) ends with counters \(c( 1 )\), \(c( 1 ^2)\), \(c( 1 ^3)\). Using \(\rho _3\), cell \(\sigma \) ends with counters \(c( 1 ^2)\), \(c( 1 ^2)\), \(c( 1 ^2)\).

  2. 2.

    If \(\alpha = \, \scriptstyle {+}\displaystyle \), rule \(\rho _+\) applies all these virtual rules \(\rho _1\), \(\rho _2\), \(\rho _3\), in max-parallel mode. Cell \(\sigma \) ends with counters \(c( 1 )\), \(c( 1 )\), \(c( 1 ^2)\).

Semantically, the max-parallel mode is equivalent to a virtual sequential “while” loop around the same rule in the exactly-once mode, which would be then repeated until it is no more applicable.

Special Cases. Simple scenarios involving generic rules are sometimes semantically equivalent to sets of non-generic rules defined via bounded loops. For example, consider the rule

figure e

where the cell’s contents guarantee that I and J only match integers in ranges [1, n] and [1, m], respectively. Under these assumptions, this rule is essentially equivalent to the following set of \(n \times m\) non-generic rules:

figure f

However, unification is a much more powerful concept, which cannot be generally reduced to simple bounded loops.

Promoters and Inhibitors. To expressively define additional useful matchings, our promoters and inhibitors may also use virtual “equality” terms, written in infix format, with the ‘\(=\)’ operator. For example, including the term \((ab = XY)\) indicates the following additional matching constraints on variables X and Y: either \(X, Y = ab, \lambda \); or \(X, Y = a, b\); or \(X, Y = b, a\); or \(X, Y = \lambda , ab\).

To usefully define inhibitors as full logical negations, variables which only appear in the scope of an inhibitor are assumed to have local scope. These variables are assumed to be defined by existential quantifiers, immediately after the negation. Semantically, this is equivalent as introducing these variables at the global rule level, but by universal quantifiers, after all other global variables, which are introduced by existential quantifiers.

As an illustration, consider a cell containing two terms, \(a( 1 ) ~ a( 1 1 1 )\), and contrast the following two sample rule fragments (for brevity, other rule details are here omitted).

figure g

These two rules appear quite similar and their inhibitor tests seem to model the same intuitive expression: no a(X) must be present in the cell. In fact, these rule fragments could be explicited with the following quantifiers:

figure h

Rule (1) uses two global variables, X and Y, that can be matched in four different ways: (i) \(X, Y = \lambda , \lambda \); (ii) \(X, Y = 1 1 , \lambda \); (iii) \(X, Y = \lambda , 1 1 \); (iv) \(X, Y = 1 , 1 \). The first three unifications, (i-iii), pass the inhibitor test, as there are no terms a(), \(a( 1 1 )\), a(), respectively. However, unification (iv) fails the inhibitor test, because there is one cell term \(a( 1 )\).

Rules (2, 2’) use one global variable, Z, and two local inhibitor variables, X and Y. Variable Z can be matched in two different ways: (i) \(Z = \lambda \); (ii) \(Z = 1 1 \). Unification (i) passes the inhibitor test, because it only checks one unification, \(X, Y = \lambda , \lambda \), and there is no cell term a(). However, unification (ii) fails the inhibitor test, because it checks three local unifications: \(X, Y = 1 1 , \lambda \); \(X, Y = \lambda , 1 1 \); \(X, Y = 1 , 1 \); and the last check fails, because of extant term \(a( 1 )\).

The pattern of rule (2) has been further used, in [4], to define a one-step minimum finding ruleset.

Benefits. This type of generic rules allow algorithm descriptions with fixed-size alphabets and fixed-sized rulesets, independent of the size of the problem and number of cells in the system (often impossible with only atomic terms).

Synchronous vs Asynchronous. In our models, we do not make any syntactic difference between the synchronous and asynchronous scenarios; this is strictly a runtime assumption [13]. Any model is able to run on both the synchronous and asynchronous runtime “engines”, albeit the results may differ. Our asynchronous model matches closely the standard definition for asynchronicity used in distributed algorithms; however, this is not needed in this paper so we don’t follow this topic here.

1.4 7.4 Data Structures in cP Systems

In this section we discuss a few basic operations with our new extended structures, numbers and lists.

Natural Numbers. Natural numbers can be represented via multisets containing repeated occurrences of the same atom. The new extended cP terms grammar has a built-in symbol, \( 1 \), for the unary digit. Thus, the following compound terms can be used to describe the contents of an integer container a: (i) \(a () = a(\lambda )\) \(\equiv \) the value of a is 0; (ii) \(a( 1 ^3)\) \(\equiv \) the value of a is 3.

For concise expressions, we may alias multisets of \( 1 \) by their corresponding multiplicity, e.g. \(a() \equiv a(0), b( 1 ^3) \equiv b(3)\). Nicolescu et al. [15] show how the basic arithmetic operations can be efficiently modelled by P systems with compound terms.

Figure 22 shows a few simple arithmetic assignments, expressions, and comparisons. Note that “strictly less than" (<) requires the extra \(1\), because \(Y\) can match on \(\lambda \).

Fig. 22.
figure 22

Numbers: simple assignments, expressions, and comparisons

Lists. Consider the list y, containing the following sequence of values: [uvw] (using a standard programming notation). List [uvw] can be represented as a compound term: .(u .(v .(w .()))), where the ad-hoc functor ‘.’ represents the standard list constructor cons and ‘.()’ the empty list. However, to avoid ambiguities, different lists may need to use different cons functors.

To simplify list processing, the new extended grammar has an unambiguous built-in list, starting with the empty list ‘\([\,]\)’, and using operator \(\mid \) to separate the head and the tail of a non-empty list.

Thus, list .(u .(v .(w .()))) can now be represented as \([u\,|\,[\,v\,|\,[\,w\,|\,[\,]\,]\,]\,]\). As a notational convenience, we can also use the standard list notation, [uvw]. Our named list y can be now be represented as a compound term, y([uvw]), and, as the round parentheses are now redundant, directly as y[uvw].

Our lists work as lists in functional programming, i.e. essentially as stacks. Figure 23 shows rule fragments for a few simple list/stack operations.

Fig. 23.
figure 23

Lists: stack operations

Rights and permissions

Reprints and permissions

Copyright information

© 2019 Springer Nature Switzerland AG

About this paper

Check for updates. Verify currency and authenticity via CrossMark

Cite this paper

Henderson, A., Nicolescu, R. (2019). Actor-Like cP Systems. In: Hinze, T., Rozenberg, G., Salomaa, A., Zandron, C. (eds) Membrane Computing. CMC 2018. Lecture Notes in Computer Science(), vol 11399. Springer, Cham. https://doi.org/10.1007/978-3-030-12797-8_12

Download citation

  • DOI: https://doi.org/10.1007/978-3-030-12797-8_12

  • Published:

  • Publisher Name: Springer, Cham

  • Print ISBN: 978-3-030-12796-1

  • Online ISBN: 978-3-030-12797-8

  • eBook Packages: Computer ScienceComputer Science (R0)

Publish with us

Policies and ethics