+ All Categories
Home > Documents > A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99...

A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99...

Date post: 04-Jan-2016
Category:
Upload: marshall-ball
View: 218 times
Download: 2 times
Share this document with a friend
36
A Logic Programming A Logic Programming Based Software Based Software Architecture for Architecture for Reactive Intelligent Reactive Intelligent Mobile Agents - Mobile Agents - DIPLCL’99 DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University of North Texas & BinNet Corporation
Transcript
Page 1: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

A Logic Programming A Logic Programming Based Software Based Software Architecture for Reactive Architecture for Reactive Intelligent Mobile Agents Intelligent Mobile Agents - - DIPLCL’99DIPLCL’99

Copyright © 1999, BinNet Corp.

Paul TarauUniversity of North Texas

&BinNet Corporation

Page 2: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

SummarySummary

– Introduction/MotivationIntroduction/Motivation

– Kernel Prolog = Horn Clauses + EnginesKernel Prolog = Horn Clauses + Engines

– Threads as First-Order ObjectsThreads as First-Order Objects

– Blackboard OperationsBlackboard Operations

– Mobile Threads and Remote ExecutionMobile Threads and Remote Execution

– Key Agent Programming PatternsKey Agent Programming Patterns

– Case Studies, Examples, ApplicationsCase Studies, Examples, Applications

– ConclusionConclusion

Page 3: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

MotivationMotivation

– paradigm shift towards networked, mobile, paradigm shift towards networked, mobile, ubiquitous computing with increasingly complex ubiquitous computing with increasingly complex patterns of interactionpatterns of interaction

– threads: needed for programming threads: needed for programming reactive/proactive mobile agentsreactive/proactive mobile agents

– back to pure LP - a good thing - but let’s give it back to pure LP - a good thing - but let’s give it an expressiveness lift:-)an expressiveness lift:-)

– Hilbert’s approach => reusable design patterns Hilbert’s approach => reusable design patterns through an through an axiomaticaxiomatic reconstruction of reconstruction of frequently used components!frequently used components!

Page 4: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

OntologyOntology• Places: Places: blackboards + a server thread blackboards + a server thread

listening on a portlistening on a port

• Things: Things: Prolog terms, in particular Prolog Prolog terms, in particular Prolog clausesclauses

• AgentsAgents: : – a a setset of mobile threads initiated by a unique goal of mobile threads initiated by a unique goal

at a given Placeat a given Place

– coordination: through blackboards - some local, coordination: through blackboards - some local, some remote some remote

Page 5: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Orthogonal Execution Orthogonal Execution MechanismsMechanisms

• enginesengines

• threadsthreads

• blackboard operationsblackboard operations– basic Linda coordination, associative searchbasic Linda coordination, associative search

– blackboard constraints - blackboard constraints - react when something becomes true

• mobile threads operations

Page 6: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Engines as Generalized Engines as Generalized Iterators (Fluents): Iterators (Fluents): keep keep state minimalstate minimal

Engine = an LD Resolution Interpreter with Engine = an LD Resolution Interpreter with first orderfirst order control: constructor+iterator control: constructor+iterator– new_engine(Goal,AnswerPattern, Handle):new_engine(Goal,AnswerPattern, Handle):

creates a new interpreter creates a new interpreter Handle Handle solving solving GoalGoal

– new_answer(Handle,AnswerInstance):new_answer(Handle,AnswerInstance): If If AnswerInstance is AnswerInstance is nono then stops the engine, then stops the engine, otherwise either returns a otherwise either returns a new answernew answer, of the , of the form form the(AnswerInstance)the(AnswerInstance) or returns or returns nono if if there are no more answers.there are no more answers.

Page 7: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

findall/3findall/3

– findall(X,G,Xs):- findall(X,G,Xs):- new_engine(G,X,E), new_engine(G,X,E), new_answer(E,Answer), new_answer(E,Answer), collect_all_answers(Answer,E,Xs).collect_all_answers(Answer,E,Xs).

– collect_all_answers(no,_,[]). collect_all_answers(no,_,[]). collect_all_answers(the(X),E,[X|Xs]):-collect_all_answers(the(X),E,[X|Xs]):-new_answer(E,Answer), new_answer(E,Answer), collect_all_answers(Answer,E,Xs).collect_all_answers(Answer,E,Xs).

Page 8: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Reflective Meta Reflective Meta InterpreterInterpreter

• solve(G):- solve(G):- once(reduce(G,NewG)), once(reduce(G,NewG)), call(NewG).call(NewG).

• reduce(G,G):-is_builtin(G). reduce(G,G):-is_builtin(G). reduce(','(A,B),','(solve(A),solve(B))). reduce(','(A,B),','(solve(A),solve(B))). reduce(G,','(clause(G,Gs),solve(Gs)))reduce(G,','(clause(G,Gs),solve(Gs)))..

Page 9: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Thread Operations: Thread Operations: minimal!minimal!• bg(Goal,ThreadHandle): bg(Goal,ThreadHandle): runs a runs a

thread initiated by Goalthread initiated by Goal and returns a and returns a handlehandle

• thread_clone(CloneThreadHandle)thread_clone(CloneThreadHandle)

• thread_join(T): thread_join(T): wait for wait for TT to finish to finish

• thread_sleep(Duration) , thread_sleep(Duration) , thread_this(ThisThreadHandle)thread_this(ThisThreadHandle)

• thread_resume/1, thread_resume/1, thread_suspend/1thread_suspend/1

Page 10: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Basic Linda OperationsBasic Linda Operations

• out(X):out(X): puts puts XX on the blackboard on the blackboard

• in(X):in(X): waits until it can take an object waits until it can take an object matchingmatching X X from the blackboard from the blackboard

• all(X,Xs):all(X,Xs): reads the list reads the list XsXs matching matching XX currently on the blackboard currently on the blackboard

• derived operations: derived operations: cin/1cin/1, , rd/1rd/1

Page 11: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Implementing Implementing in/1, out/1in/1, out/1 with with suspendsuspend and and resumeresume• in Jinni and BinProlog this is in the in Jinni and BinProlog this is in the

implementation language but, in fact, it implementation language but, in fact, it could becould be at source level at source level

• in(X):in(X): if X is on the blackboard take it, if if X is on the blackboard take it, if not add not add waiting(X,ThisThread)waiting(X,ThisThread) and do and do suspend(ThisThread)suspend(ThisThread)

• out(X):out(X): if if waiting(X,T)waiting(X,T) matches, take it matches, take it and call and call resume(T)resume(T)

Page 12: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Assertional Constraints vs. Assertional Constraints vs. Binding Constrains Binding Constrains

• From: From: when nonvar(X)when nonvar(X) ... ...

• => => when provable(X)when provable(X) … re-execution … re-execution

• => => when a_fact(X)…when a_fact(X)… more realistic more realistic

• constraints on constraints on variable bindingsvariable bindings are too are too fine-grained for distributed fine-grained for distributed programming!programming!

Page 13: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Beyond Linda: Blackboard Beyond Linda: Blackboard Constraint OperationsConstraint Operations

• wait_for(Pattern,Constraint)wait_for(Pattern,Constraint): waits for a : waits for a PatternPattern on the blackboard, such that on the blackboard, such that ConstraintConstraint holds, and when this happens, holds, and when this happens, it removes the result of the match from the it removes the result of the match from the blackboard blackboard

• notify_about(Pattern)notify_about(Pattern): notifies about this : notifies about this PatternPattern one of the blocked threads which one of the blocked threads which waits for it with a matching constraintwaits for it with a matching constraint

Page 14: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Coordination with Coordination with Blackboard Constraints Blackboard Constraints

• Two threads: T1 (prod) and T2 (cons)Two threads: T1 (prod) and T2 (cons)

• ?-notify_about(stock_offer(aol,91)). %T1?-notify_about(stock_offer(aol,91)). %T1

• ?-notify_about(stock_offer(aol,89)). %T1?-notify_about(stock_offer(aol,89)). %T1

• % action triggered in T2 => Price=89% action triggered in T2 => Price=89

• ?-wait_for(stock_offer(aol,Price), %T2 ?-wait_for(stock_offer(aol,Price), %T2 less(Price,90)).less(Price,90)).

Page 15: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

wait_for(Pattern,Constr)wait_for(Pattern,Constr)

wait_for(P,C):-wait_for(P,C):-

if(if(take_patterntake_pattern((available_foravailable_for(P),C),(P),C),

true,true,

and(out(and(out(waiting_forwaiting_for(P,C)),(P,C)),

in(in(holds_forholds_for(P,C))(P,C))

) ).) ).

Page 16: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

notify_about(Pattern,Consnotify_about(Pattern,Constr)tr)

notify_about(P):-notify_about(P):-

if(if(take_patterntake_pattern((waiting_forwaiting_for(P,C),C),(P,C),C),

out(out(holds_forholds_for(P,C)),(P,C)),

out(out(available_foravailable_for(P))(P))

).).

Page 17: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

take_pattern/2take_pattern/2

take_pattern(Pattern,Constraint):-take_pattern(Pattern,Constraint):-

all(Pattern,Ps),all(Pattern,Ps),

member(Pattern,Ps),member(Pattern,Ps),

Constraint,Constraint,

cin(Pattern,_). % non-blocking in/1cin(Pattern,_). % non-blocking in/1

Page 18: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

The Reactive Agent The Reactive Agent PatternPattern

• sell(Who,Stock,AskPrice) :-sell(Who,Stock,AskPrice) :-notify_about(notify_about(offeroffer(Who,Stock,AskPrice)).(Who,Stock,AskPrice)).

• buy(Who,Stock,SellingPrice) :- buy(Who,Stock,SellingPrice) :- bg(try_to_buy(Who,Stock,SellingPrice)).bg(try_to_buy(Who,Stock,SellingPrice)).

• try_to_buy(Me,Stock,LimitPrice) :- try_to_buy(Me,Stock,LimitPrice) :- wait_for(wait_for(offeroffer(You,Stock,YourPrice), (You,Stock,YourPrice), less(YourPrice,LimitPrice)),……….less(YourPrice,LimitPrice)),……….

Page 19: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

mobile threads with First mobile threads with First Order AND-continuationsOrder AND-continuations

• a:-b,c,d.a:-b,c,d.

• binarization: binarization: a(C)::-b(c(d(C))).a(C)::-b(c(d(C))).

• get_cont(C,C)::-true(C). get_cont(C,C)::-true(C). % binarized% binarized

• mobile threads algorithm:mobile threads algorithm:– move/0move/0: get continuation, send over the net, : get continuation, send over the net,

resume execution on target resume execution on target

– return/0return/0: send back new continuation, resume : send back new continuation, resume execution back homeexecution back home

Page 20: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

A Simple API for mobile A Simple API for mobile threadsthreads

• run_server/0, here/0, there/0, where/1run_server/0, here/0, there/0, where/1: : => local or remote focus=> local or remote focus

• set_this_host/1, set_this_port/1set_this_host/1, set_this_port/1, , set_that_host/1,set_that_port/1set_that_host/1,set_that_port/1

• move/0, return/0 vs. remote_run/1move/0, return/0 vs. remote_run/1

• the(Pattern,Goal,Answer):the(Pattern,Goal,Answer): runsruns GoalGoal toto produceproduce the(Answer) orthe(Answer) or no no => => here/therehere/there switchswitch => =>locallocal//remoteremote

Page 21: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Examples of mobile Examples of mobile threadsthreads

• Window 1Window 1: a mobile thread : a mobile thread

• ?-?- there, move, there, move, println(on_server), member(X,println(on_server), member(X,[1,2,3]), return, println(back).[1,2,3]), return, println(back).

• Window 2Window 2: a server : a server

• ?- run_server.?- run_server.

Page 22: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

mobile threads vs. RPCs: mobile threads vs. RPCs: move once, compute move once, compute many timesmany times

• ?-for(I,1,1000), ?-for(I,1,1000), remote_run(println(I)), remote_run(println(I)), eq(I,1000). eq(I,1000).

• ?-there, move, for(I,1,1000), ?-there, move, for(I,1,1000), println(I), eq(I,1000).println(I), eq(I,1000).

Page 23: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

mobile threads: WHY?mobile threads: WHY?

• Large database, small agent Large database, small agent

• Speed-up: move to a fast processor Speed-up: move to a fast processor and back, transparentlyand back, transparently

• here/there switch: same code can be here/there switch: same code can be run locally or remotelyrun locally or remotely

• fault tolerance - move->run->come fault tolerance - move->run->come backback

Page 24: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Emulating multiple Emulating multiple answer computationsanswer computations

• ?- there, move, ?- there, move, findall(X,for(I,1,3),Xs), return, findall(X,for(I,1,3),Xs), return, member(X,Xs).member(X,Xs).

• X=1;X=1;

• X=2;X=2;

• X=3X=3

Page 25: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

A Classic Design Pattern,A Classic Design Pattern, Publish/Subscribe: Publish/Subscribe: with with mobile threadsmobile threads + Linda+ Linda operationsoperations• a reactive channel listener: a reactive channel listener: in/1in/1 loop loop

• ?-?-listen(fun(_)).listen(fun(_)).

• selective channel publisher: selective channel publisher: out/1out/1 loop loop

• ?-?-talk(fun(jokes)).talk(fun(jokes)).

• will not match:will not match:

• ?-talk(stocks(quotes,nasdaq))?-talk(stocks(quotes,nasdaq))

Page 26: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

The (Pseudo) Server in The (Pseudo) Server in The Client PatternThe Client Pattern

• a client thread emulates server a client thread emulates server functionality (works behind a firewall!):functionality (works behind a firewall!):– pseudo_sever:-pseudo_sever:-

in(todo(X)),call(X),pseudo_server.in(todo(X)),call(X),pseudo_server.

– ?- there,pseudo_server.?- there,pseudo_server.

• the real client: there,out(todo(…))the real client: there,out(todo(…))

• communication: through a blackboard on a communication: through a blackboard on a (shared) real server(shared) real server

Page 27: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

The The OctopusOctopus Agent Agent PatternPattern

• branch out multiple branch out multiple Reactive AgentReactive Agent threads to various places using RPCs or threads to various places using RPCs or mobile threadsmobile threads

• watch for patterns containing returned watch for patterns containing returned results on the local blackboardresults on the local blackboard

• failure or non-termination of one thread failure or non-termination of one thread have minimal effect: fault tolerance have minimal effect: fault tolerance

Page 28: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Designing Jinni Agent Designing Jinni Agent ClassesClasses

• Java layer: Java layer: – code and type inheritancecode and type inheritance

– reaction to eventsreaction to events

– libraries: Java3D, XML, special devices etc.libraries: Java3D, XML, special devices etc.

• Prolog layerProlog layer– knowledge processingknowledge processing

– reaction to blackboard constraintsreaction to blackboard constraints

– mobile threadsmobile threads

Page 29: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Design Patterns in Visual Design Patterns in Visual JinniJinni

• drives Java objects through handlesdrives Java objects through handles

• meta-programming is essentialmeta-programming is essential

• Java events interoperate with Java events interoperate with blackboard coordinationblackboard coordination

• easy extension to incorporate access easy extension to incorporate access to Java2D and Java3d librariesto Java2D and Java3d libraries

Page 30: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

jdialog/2: a Jinni Visual jdialog/2: a Jinni Visual ComponentComponent

– jdialog(Q,A):-jdialog(Q,A):- new_frame('Jinni Dialog',2,1,F), new_frame('Jinni Dialog',2,1,F), set_layout(F,border), set_layout(F,border), new_label(F,Q,_),new_panel(F,flow,P), new_label(F,Q,_),new_panel(F,flow,P), new_button(P,'Ok', new_button(P,'Ok',out(bchoice(P,ok))out(bchoice(P,ok)),_),,_), new_button(P,'Cancel', new_button(P,'Cancel', out(bchoice(P,cancel))out(bchoice(P,cancel))),), show(F), show(F), in(bchoice(P,A)),in(bchoice(P,A)), remove_all(F), destroy(F). remove_all(F), destroy(F).

Page 31: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Shared Virtual WorldsShared Virtual Worlds

• set-up a set-up a registration mechanismregistration mechanism for agents for agents on server on server

• publish publish state changesstate changes to shared blackboard to shared blackboard on server with on server with out/1out/1

• one thread for each “ghost” (remote client) - one thread for each “ghost” (remote client) - too expensive!too expensive!

• => shared update loop: get each ghost’s => shared update loop: get each ghost’s state with state with in/1in/1 from server and update from server and update

Page 32: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Shared Virtual Reality Shared Virtual Reality AgentsAgents

• Web based: browser+EAI+Java+JinniWeb based: browser+EAI+Java+Jinni

• Jinni Server + Thin Jinni Applet ConnectorJinni Server + Thin Jinni Applet Connector

• NEXT: Java 3D virtual worlds connected in NEXT: Java 3D virtual worlds connected in a peer-to-peer Jinni networka peer-to-peer Jinni network

• new transport layers: Corba, RMI, HLAnew transport layers: Corba, RMI, HLA

Page 33: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Stock Market AgentsStock Market Agents

• reactive agents, user defined rulesreactive agents, user defined rules

• Internet data mining for stock quotesInternet data mining for stock quotes

• expressiveness far beyond the usual expressiveness far beyond the usual limit/stop/market transactionslimit/stop/market transactions

• Octopus Agent Pattern: wait for triggers Octopus Agent Pattern: wait for triggers on multiple stocks, analyst info, indexeson multiple stocks, analyst info, indexes

Page 34: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Educational AgentsEducational Agents

• chat, self organizing groupschat, self organizing groups

• alerts triggered by blackboard constraintsalerts triggered by blackboard constraints

• reusable agent hierarchiesreusable agent hierarchies

• student progress evaluationstudent progress evaluation

• Wizard of Oz help desk: combined human Wizard of Oz help desk: combined human and programmed agentsand programmed agents

Page 35: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

Mutual Agent/Host Mutual Agent/Host Security: the Security: the BRING YOUR BRING YOUR OWN WINEOWN WINE PRINCIPLE PRINCIPLE• reflective meta-interpreter: a few lines of reflective meta-interpreter: a few lines of

Prolog - can be Prolog - can be mobilemobile - bring your own! - bring your own!

• can act as a can act as a sandboxsandbox protecting the protecting the Host Host against the against the Agent -Agent -statically checkedstatically checked

• undecidability of a Turing equivalent undecidability of a Turing equivalent meta-interpreter protects the meta-interpreter protects the AgentAgent against theagainst the Host Host

Page 36: A Logic Programming Based Software Architecture for Reactive Intelligent Mobile Agents - DIPLCL’99 Copyright © 1999, BinNet Corp. Paul Tarau University.

ConclusionConclusion• expressiveness of LP + a few simple, expressiveness of LP + a few simple,

orthogonal concepts: orthogonal concepts: engines, engines, threads, blackboards, mobile threadsthreads, blackboards, mobile threads

• LP is ready for GUI-enabled, LP is ready for GUI-enabled, networked applications interoperating networked applications interoperating with mainstream software artifactswith mainstream software artifacts


Recommended