1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    2
    3/*	Nan.System.Sources
    4	Nan.System.Sources/Prolog 1.2.0-beta
    5	Answer Sources in Prolog
    6	Copyright 2015-2017 Julio P. Di Egidio
    7	<mailto:julio@diegidio.name>
    8	<http://julio.diegidio.name/Projects/Nan.System.Sources/>
    9	
   10	This file is part of Nan.System.Sources.
   11	
   12	Nan.System.Sources is free software: you can redistribute it and/or modify
   13	it under the terms of the GNU General Public License as published by
   14	the Free Software Foundation, either version 3 of the License, or
   15	(at your option) any later version.
   16	
   17	Nan.System.Sources is distributed in the hope that it will be useful,
   18	but WITHOUT ANY WARRANTY; without even the implied warranty of
   19	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   20	GNU General Public License for more details.
   21	
   22	You should have received a copy of the GNU General Public License
   23	along with Nan.System.Sources.  If not, see <http://www.gnu.org/licenses/>.
   24*/

   25
   26% (SWI-Prolog 7.3.25)
   27%	Main SWI-Prolog specifics:
   28%	- Threads
   29%	- Thread message queues
   30%	- Global cuts
   31
   32:- module(sources,
   33	[	using_source/4,         % @TSol, @GExe, -Src, :GUse
   34		using_source_com/4,     % +Srcs, :GCom, -Src, :GUse
   35		source_create/3,        % @TSol, @GExe, -Src
   36		source_create_com/5,    % +Srcs, @GRst, @GNxt, @Sta0, -Src
   37		source_destroy/1,       % +Src
   38		source_destroy_all/0,   % 
   39		source_reset/1,         % +Src
   40		source_next/2,          % +Src, ?Ans
   41		source_next_begin/1,    % +Src
   42		source_next_end/2,      % +Src, ?Ans
   43		source_exists/1         % +Src
   44	]).
   45
   46:- use_module(sources_types).
   47:- use_module(sources_system).

Answer Sources

Part of Nan.System.Sources (nan/system/sources.pl)

Module sources (nan/system/sources.pl) provides the predicates that implement Answer Sources in Prolog.

For code docs syntax and meaning see sources_docs.txt.

NOTE:

author
- Julio P. Di Egidio
version
- 1.2.0-beta
license
- GNU GPLv3
To be done
- Extend from fluents to interactors (implement yield/1). */
   70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   71
   72:- use_module(library(apply)).
   73
   74%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   75%	PUBLIC interface: using
   76%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 using_source(@TSol:any, @GExe:callable, -Src:source, :GUse:callable) is nondet
Creates a source, calls a goal, finally destroys the source.

Ensures destruction of the source even if goal throws an error.

Example:

?- using_source(I, between(1, 2, I), _S,
   (   repeat,
       source_next(_S, answer(_Det, the(I))),
       (_Det == last -> !; true)
   )).
I = 1 ;
I = 2.
Arguments:
TSol- The source's solution template (copied).
GExe- The source's goal to execute (copied).
Src- The new source.
GUse- The goal to execute. */
  105:- meta_predicate
  106	using_source(+, 0, -, 0).
  107
  108using_source(TSol, GExe, Src, GUse) :-
  109	setup_call_cleanup(
  110		source_create(TSol, GExe, Src),
  111		call(GUse),
  112		source_destroy(Src)
  113	).
 using_source_com(+Srcs:list(source), :GCom:callable, -Src:source, :GUse:callable) is nondet
Creates a combined source, calls a goal, finally destroys the source.

Ensures destruction of the source even if goal throws an error.

Example:

?- [user].
parallel_com(Srcs, Src) :-
    GRst = parallel_com__rst,
    GNxt = parallel_com__nxt,
    source_create_com(Srcs, GRst, GNxt, _, Src).

parallel_com__rst(Srcs, _, _) :-
    maplist(source_reset, Srcs).

parallel_com__nxt(Srcs, _, _, Ans) :-
    maplist(source_next_begin, Srcs),
    foldl(parallel_com__nxt__do, Srcs, [], As),
    Ans = answer(more, the(As)).

parallel_com__nxt__do(Src, As0, As) :-
    source_next_end(Src, A),
    append(As0, [A], As).
^Z

?- using_source(1, sleep(1), _S1,
   using_source(2, sleep(1), _S2,
   using_source_com([_S1, _S2], parallel_com, _S,
   (   time(source_next(_S, answer(more, the(Anss))))
   )))).  % Warm run
% 188 inferences, 0.000 CPU in 1.000 seconds (0% CPU, Infinite Lips)
Anss = [answer(last, the(1)), answer(last, the(2))].
Arguments:
Srcs- The sources to combine.
GCom- The combination constructor.
Src- The new combined source.
GUse- The goal to execute. */
  161:- meta_predicate
  162	using_source_com(+, 2, -, 0).
  163
  164using_source_com(Srcs, GCom, Src, GUse) :-
  165	setup_call_cleanup(
  166		call(GCom, Srcs, Src),
  167		call(GUse),
  168		source_destroy(Src)
  169	).
  170
  171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  172%	PUBLIC interface
  173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 source_create(@TSol:any, @GExe:callable, -Src:source) is det
Creates a source.
Arguments:
TSol- The solution template (copied).
GExe- The goal to execute (copied).
Src- The new source.
  185:- meta_predicate
  186	source_create(+, 0, -).
  187
  188source_create(TSol, GExe, Src) :-
  189	source_create_(TSol, GExe, Sid),
  190	source_typ_sid(Src, t0, Sid).
 source_create_com(+Srcs:list(source), @GRst:callable, @GNxt:callable, @Sta0:any, -Src:source) is det
Creates a combined source.

Enforces determinism on GRst and GNxt.

For an example, see using_source_com/4.

Arguments:
Srcs- The sources to combine.
GRst- The goal to reset the combinator to its initial state (copied).
GNxt- The goal to get the next answer from the combinator (copied).
Sta0- The initial state of the combinator (copied).
Src- The new combined source.
  209:- meta_predicate
  210	source_create_com(+, 3, 4, +, -).
  211
  212source_create_com(Srcs, GRst, GNxt, Sta0, Src) :-
  213	source_create_com_(Srcs, GRst, GNxt, Sta0, Sid),
  214	source_typ_sid(Src, t1, Sid).
 source_destroy(+Src:source) is det
Destroys a source.
Arguments:
Src- The source.
throws
- source_error(does_not_exist(Src))
  224source_destroy(Src) :-
  225	source_typ_sid(Src, Typ, Sid),
  226	source_assert_(Src, Sid),
  227	source_destroy_(Typ, Sid).
 source_destroy_all is semidet
Destroys all existing sources.

Fails if no source exists.

  235source_destroy_all :-
  236	source_destroy_all_.
 source_reset(+Src:source) is det
Resets a source to its initial state.
Arguments:
Src- The source.
throws
- source_error(does_not_exist(Src))
  246source_reset(Src) :-
  247	source_typ_sid(Src, Typ, Sid),
  248	source_assert_(Src, Sid),
  249	source_reset_(Typ, Sid).
 source_next(+Src:source, ?Ans:answer) is det
Gets the next answer from a source.
Arguments:
Src- The source.
Ans- The answer.
throws
- source_error(does_not_exist(Src))
  260source_next(Src, Ans) :-
  261	source_typ_sid(Src, Typ, Sid),
  262	source_assert_(Src, Sid),
  263	source_next_(Typ, Sid, Ans).
 source_next_begin(+Src:source) is det
Begins getting the next answer from a source (async).
Arguments:
Src- The source.
throws
- source_error(does_not_exist(Src))
  273source_next_begin(Src) :-
  274	source_typ_sid(Src, Typ, Sid),
  275	source_assert_(Src, Sid),
  276	source_next_b_(Typ, Sid).
 source_next_end(+Src:source, ?Ans:answer) is det
Ends getting the next answer from a source (async).
Arguments:
Src- The source.
Ans- The answer.
throws
- source_error(does_not_exist(Src))
  287source_next_end(Src, Ans) :-
  288	source_typ_sid(Src, Typ, Sid),
  289	source_assert_(Src, Sid),
  290	source_next_e_(Typ, Sid, Ans).
 source_exists(+Src:source) is semidet
Tests that a source exists.
Arguments:
Src- The source to test for.
  298source_exists(Src) :-
  299	source_typ_sid(Src, _, Sid),
  300	source_db_exists(Sid).
  301
  302source_assert_(_, Sid) :-
  303	source_db_exists(Sid), !.
  304source_assert_(Src, _) :-
  305	source_throw(does_not_exist(Src)).
  306
  307%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  308%	PRIVATE implementation: create
  309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  310
  311%	source_create_com_ (+Srcs, @GRst, @GNxt, @Sta0, -Sid) is det
  312
  313:- meta_predicate
  314	source_create_com_(+, 3, 4, +, -).
  315
  316source_create_com_(Srcs, GRst, GNxt, Sta0, Sid) :-
  317	source_new_sid(t1, Sid),
  318	source_call_(Sid, 'CREATE_1',
  319	(	copy_term([GRst, GNxt, Sta0], [GRst1, GNxt1, Sta01]),
  320		source_db_add(Sid, 'CREATE_1', t1(Srcs, GRst1, GNxt1, Sta01))
  321	)).
  322
  323%	source_create_ (@TSol, @GExe, -Sid) is det
  324
  325:- meta_predicate
  326	source_create_(+, 0, -).
  327
  328source_create_(TSol, GExe, Sid) :-
  329	source_new_sid(t0, Sid),
  330	source_call_(Sid, 'CREATE_0',
  331	(	source_create__do(Sid, TSol, GExe, Ress)
  332	)), source_rethrow(Ress).
  333
  334:- meta_predicate
  335	source_create__do(+, +, 0, -).
  336
  337source_create__do(Sid, TSol, GExe, Ress) :-
  338	source_create__pre(Sid, TSol, GExe, [Pid, Tid, GEXEC]),
  339	source_create__all(Sid, Pid, Tid, GEXEC, Res1),
  340	(	source_has_err(Res1)
  341	->	source_create__abort(Sid, Res2),
  342		Ress = [Res1, Res2]
  343	;	Ress = [Res1]
  344	).
  345
  346:- meta_predicate
  347	source_create__pre(+, +, 0, -).
  348
  349source_create__pre(Sid, TSol, GExe, [Pid, Tid, GEXEC]) :-
  350	copy_term([TSol, GExe], [TSol1, GExe1]),
  351	atom_concat(Sid, '_p', Pid),
  352	atom_concat(Sid, '', Tid),
  353	GEXEC = source_exec_(Sid, TSol1, GExe1).
  354
  355:- meta_predicate
  356	source_create__all(+, +, +, 0, -).
  357
  358source_create__all(Sid, Pid, Tid, GEXEC, Res) :-
  359	source_catch(
  360	(	source_db_add(Sid, 'CREATE_0', t0(Pid, Tid)),
  361		message_queue_create(_, [alias(Pid)]),
  362		thread_create(GEXEC, _, [alias(Tid)])
  363	), Res).
  364
  365source_create__abort(Sid, Res) :-
  366	source_catch(
  367	(	source_destroy_(t0, Sid)
  368	), Res).
  369
  370%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  371%	PRIVATE implementation: destroy
  372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  373
  374%	source_destroy_all_ () is semidet
  375
  376source_destroy_all_ :-
  377	bagof(Sid, Term^source_db_enum(Sid, Term), Sids),
  378	foldl(source_destroy_all__do, Sids, [], Ress),
  379	source_rethrow(Ress).
  380
  381source_destroy_all__do(Sid, Ress0, Ress) :-
  382	source_catch(
  383	(	source_typ_sid(_, Typ, Sid),
  384		source_destroy_(Typ, Sid)
  385	), Res), append(Ress0, [Res], Ress).
  386
  387%	source_destroy_ (+Typ, +Sid) is det
  388
  389source_destroy_(t1, Sid) :-
  390	source_call_(Sid, 'DESTROY_1',
  391	(	source_db_del(Sid, 'DESTROY_1')
  392	)).
  393
  394source_destroy_(t0, Sid) :-
  395	source_db_get(Sid, 'DESTROY_0', t0(Pid, Tid)),
  396	source_call_(Sid, 'DESTROY_0',
  397	(	source_destroy__do(Sid, Pid, Tid, Ress)
  398	)), source_rethrow(Ress).
  399
  400source_destroy__do(Sid, Pid, Tid, Ress) :-
  401	source_destroy__db(Sid, Res1),
  402	source_destroy__queue(Pid, Res2),
  403	source_destroy__thread(Sid, Tid, Res3),
  404	Ress = [Res1, Res2, Res3].
  405
  406source_destroy__db(Sid, Res) :-
  407	source_catch(
  408	(	source_db_del(Sid, 'DESTROY_0')
  409	), Res).
  410
  411source_destroy__queue(Qid, Res) :-
  412	source_catch(
  413	(	message_queue_destroy(Qid)
  414	), Res).
  415
  416source_destroy__thread(Sid, Tid, Res) :-
  417	source_catch(
  418	(	(	thread_property(Tid, status(running))
  419		->	source_msg_send_(Sid, 'DESTROY_0', Tid, close)
  420		;	true
  421		), thread_join(Tid, _)
  422	), Res).
  423
  424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  425%	PRIVATE implementation: call
  426%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  427
  428%	source_reset_ (+Typ, +Sid) is det
  429
  430source_reset_(t1, Sid) :-
  431	source_db_get(Sid, 'RESET_1', t1(Srcs, GRst, _, Sta0)),
  432	source_call_(Sid, 'RESET_1',
  433	(	source_reset__t1(Srcs, GRst, Sta0, Sta1),
  434		source_next__t1_state(Sid, 'RESET_1', Sta1)
  435	)).
  436
  437source_reset_(t0, Sid) :-
  438	source_db_get(Sid, 'RESET_0', t0(_, Tid)),
  439	source_call_(Sid, 'RESET_0',
  440	(	source_reset__t0(Sid, 'RESET_0', Tid)
  441	)).
  442
  443%	source_next_ (+Typ, +Sid, ?Ans) is semidet
  444
  445source_next_(t1, Sid, Ans) :-
  446	source_db_get(Sid, 'NEXT_1', t1(Srcs, _, GNxt, Sta0)),
  447	source_call_(Sid, 'NEXT_1',
  448	(	source_next__t1(Srcs, GNxt, Sta0, Sta1, Ans),
  449		source_next__t1_state(Sid, 'NEXT_1', Sta1)
  450	)).
  451
  452source_next_(t0, Sid, Ans) :-
  453	source_db_get(Sid, 'NEXT_0', t0(Pid, Tid)),
  454	source_call_(Sid, 'NEXT_0',
  455	(	source_next__t0_b(Sid, 'NEXT_0', Tid),
  456		source_next__t0_e(Sid, 'NEXT_0', Pid, Ans)
  457	)).
  458
  459%	source_next_b_ (+Typ, +Sid) is det
  460
  461source_next_b_(t1, Sid) :-
  462	source_db_get(Sid, 'NEXT_1_B', _),
  463	source_call_(Sid, 'NEXT_1_B',
  464	(	true
  465	)).
  466
  467source_next_b_(t0, Sid) :-
  468	source_db_get(Sid, 'NEXT_0_B', t0(_, Tid)),
  469	source_call_(Sid, 'NEXT_0_B',
  470	(	source_next__t0_b(Sid, 'NEXT_0_B', Tid)
  471	)).
  472
  473%	source_next_e_ (+Typ, +Sid, ?Ans) is semidet
  474
  475source_next_e_(t1, Sid, Ans) :-
  476	source_db_get(Sid, 'NEXT_1_E', t1(Srcs, _, GNxt, Sta0)),
  477	source_call_(Sid, 'NEXT_1_E',
  478	(	source_next__t1(Srcs, GNxt, Sta0, Sta1, Ans),
  479		source_next__t1_state(Sid, 'NEXT_1_E', Sta1)
  480	)).
  481
  482source_next_e_(t0, Sid, Ans) :-
  483	source_db_get(Sid, 'NEXT_0_E', t0(Pid, _)),
  484	source_call_(Sid, 'NEXT_0_E',
  485	(	source_next__t0_e(Sid, 'NEXT_0_E', Pid, Ans)
  486	)).
  487
  488:- meta_predicate
  489	source_reset__t1(+, 3, +, -),
  490	source_next__t1(+, 4, +, -, ?).
  491
  492source_reset__t1(Srcs, GRst, Sta0, Sta1) :-
  493	call(GRst, Srcs, Sta0, Sta1), !.					% NOTE: Enforce determinism!
  494
  495source_next__t1(Srcs, GNxt, Sta0, Sta1, Ans) :-
  496	call(GNxt, Srcs, Sta0, Sta1, Ans_), !, Ans = Ans_.	% NOTE: Enforce determinism!
  497
  498source_next__t1_state(Sid, Cat, Sta1) :-
  499	source_db_get(Sid, Cat, t1(Srcs, GRst, GNxt, Sta0)),
  500	(	Sta1 \== Sta0
  501	->	source_db_del(Sid, Cat),
  502		source_db_add(Sid, Cat, t1(Srcs, GRst, GNxt, Sta1))
  503	;	true
  504	).
  505
  506source_reset__t0(Sid, Cat, Tid) :-
  507	source_msg_send_(Sid, Cat, Tid, reset).
  508
  509source_next__t0_b(Sid, Cat, Tid) :-
  510	source_msg_send_(Sid, Cat, Tid, next).
  511
  512source_next__t0_e(Sid, Cat, Pid, Ans) :-
  513	source_msg_recv_(Sid, Cat, Pid, Msg),
  514	(	\+ callable(Msg)  ->
  515			source_throw(invalid_message(Sid, Cat, Msg))
  516	;	Msg = fail        -> Ans = answer(fail, no)
  517	;	Msg = last(Sol)  -> Ans = answer(last, the(Sol))
  518	;	Msg = more(Sol)  -> Ans = answer(more, the(Sol))
  519	;	Msg = except(Err) ->
  520			source_throw(error_message(Sid, Cat, Err))
  521	;	source_throw(unknown_message(Sid, Cat, Msg))
  522	).
  523
  524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  525%	PRIVATE implementation: exec
  526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  527
  528%	source_exec_ (+Sid, ?TSol, :GExe) is det
  529
  530:- meta_predicate
  531	source_exec_(+, ?, 0).
  532
  533source_exec_(Sid, TSol, GExe) :-
  534	source_db_get(Sid, 'EXEC', t0(Pid, Tid)),
  535	source_call_(Sid, 'EXEC',
  536	(	call_cleanup(
  537			source_exec__loop_0(Sid, Pid, Tid, TSol, GExe),
  538			exception(Err),
  539			source_msg_send_(Sid, 'EXEC', Pid, except(Err))
  540		)
  541	)).
  542
  543:- meta_predicate
  544	source_exec__loop_0(+, +, +, ?, 0),
  545	source_exec__loop_1(+, +, +, ?, 0),
  546	source_exec__loop_2(+, +, ?, 0).
  547
  548source_exec__loop_0(Sid, Pid, Tid, TSol, GExe) :-
  549	repeat,
  550	source_msg_recv_(Sid, 'EXEC', Tid, Msg),
  551	(	\+ callable(Msg) ->
  552			source_throw(invalid_message(Sid, 'EXEC', Msg))
  553	;	Msg = reset      -> fail
  554	;	Msg = close      -> !
  555	;	Msg = next       -> !,
  556			source_exec__loop_1(Sid, Pid, Tid, TSol, GExe)
  557	;	source_throw(unknown_message(Sid, 'EXEC', Msg))
  558	).
  559
  560source_exec__loop_1(Sid, Pid, Tid, TSol, GExe) :-
  561	prolog_current_choice(Loop0),
  562	repeat,
  563	prolog_current_choice(Loop1),
  564	source_exec__loop_2(Sid, Pid, TSol, GExe),
  565	source_exec__recv(Sid, Tid, Loop0, Loop1).
  566
  567source_exec__loop_2(Sid, Pid, TSol, GExe) :-
  568	(	call_cleanup(GExe, Det = true),
  569		(	Det == true
  570		->	source_msg_send_(Sid, 'EXEC', Pid, last(TSol))
  571		;	source_msg_send_(Sid, 'EXEC', Pid, more(TSol))
  572		)
  573	;	repeat,
  574		source_msg_send_(Sid, 'EXEC', Pid, fail)
  575	).
  576
  577source_exec__recv(Sid, Tid, Loop0, Loop1) :-
  578	source_msg_recv_(Sid, 'EXEC', Tid, Msg),
  579	(	\+ callable(Msg) ->
  580			source_throw(invalid_message(Sid, 'EXEC', Msg))
  581	;	Msg = next       -> fail
  582	;	Msg = close      -> prolog_cut_to(Loop0)
  583	;	Msg = reset      -> prolog_cut_to(Loop1),
  584			source_exec__recv(Sid, Tid, Loop0, Loop1)
  585	;	source_throw(unknown_message(Sid, 'EXEC', Msg))
  586	).
  587
  588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  589%	PRIVATE helpers
  590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  591
  592%	source_call_ (+Sid, +Cat, :GAct) is det
  593
  594:- meta_predicate
  595	source_call_(+, +, 0).
  596
  597source_call_(Sid, Cat, GAct) :-
  598	setup_call_cleanup(
  599		source_log_call(Sid, Cat, start),
  600		call(GAct),
  601		source_log_call(Sid, Cat, end)
  602	).
  603
  604%	source_msg_send_ (+Sid, +Cat, +Qid, +Msg) is det
  605%	source_msg_recv_ (+Sid, +Cat, +Qid, -Msg) is det
  606
  607source_msg_send_(Sid, Cat, Qid, Msg) :-
  608	thread_send_message(Qid, Msg),
  609	source_log_msg(Sid, Cat, send, Msg).
  610
  611source_msg_recv_(Sid, Cat, Qid, Msg) :-
  612	thread_get_message(Qid, Msg),
  613	source_log_msg(Sid, Cat, recv, Msg).
  614
  615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%