1/*****************************************************************************
    2 * This file is part of the Prolog Development Tool (PDT)
    3 * 
    4 * Author: G�nter Kniesel (among others)
    5 * WWW: http://sewiki.iai.uni-bonn.de/research/pdt/start
    6 * Mail: pdt@lists.iai.uni-bonn.de
    7 * Copyright (C): 2004-2012, CS Dept. III, University of Bonn
    8 * 
    9 * All rights reserved. This program is  made available under the terms
   10 * of the Eclipse Public License v1.0 which accompanies this distribution,
   11 * and is available at http://www.eclipse.org/legal/epl-v10.html
   12 * 
   13 ****************************************************************************/
   14
   15% Date: 25.06.2006
   16
   17/*
   18 * This file contains predicates for locating definitions (declarations and 
   19 * clauses) in SWI-Prolog modules and testing visibility of predicates in 
   20 * modules.
   21 *
   22 * Used in pdt/pl/{pdt_search,pdt_xref}.pl and in
   23 * ** pdt.runtime.builder/prolog-src/analyzer/metafile_referencer.pl
   24 */
   25:- module( utils4modules_visibility, 
   26         [
   27           module_of_file/2,               % File, Module
   28<<<<<<< HEAD
   29           defined_in_files/4,             % Module, Name, Arity, Locations
   30           defined_in_file/6,              % Module, Name, Arity, Nth,File,StartingLine **
   31           declared_in_file/4,             % Module, Name, Arity, Location=[File-[Line]] --     
   32           
   33           hidden_name/1,                  % (Name)
   34           
   35           defined_in/3,                   % Module, Name, Arity
   36           defined_in/4,                   % Module, Name, Arity, DefiningModule
   37           visible_in/3,	               % Module, Name, Arity
   38           undefined_in/3                  % Module, Name, Arity 
   39         ] ).
   40 
   41
   42        
   43%% module_of_file(?File,?Module)
   44%
   45% Module is the module defined in File. 
   46% If the file defines no explicit module or the  
   47% defined module is a hidden system module (whose 
   48% name starts with $) the Module is either 'user'
   49% .
   50module_of_file(File,Module):- 
   51    module_property(Module,file(File)),    % SWI: Fails if File defines no module
   52                                           % or if it defines a hidden module. 
   53                                           % Nondeterministic for mode (-,-).
   54=======
   55           
   56           empty_module/1,                 % (Module)           hidden_module/1,                % Module                      visible_in_module/3,	           % Module, Name, Arity           declared_in_module/2,           % Module, Head           declared_in_module/3,		   % Module, Head, DeclaringModule (not generating)           declared_in_module/4,           % Module, Name, Arity, DeclaringModule            referenced_but_undeclared/3,    % Module, Name, Arity            declared_but_undefined/3,       % Module, Name, Arity            defined_in_module/2,            % Module, Head           defined_in_module/3,            % Module, Name, Arity           defined_in_module/4,            % Module, Name, Arity, DefiningModule           defined_in_files/4,             % Module, Name, Arity, Locations           defined_in_file/6,              % Module, Name, Arity, Nth,File,StartingLine           declared_in_file/4              % Module, Name, Arity, Location=[File-[Line]]                     ] ).
 hidden_module(M)
   78hidden_module(M) :- starts_with_dollar(M).
   79
   80starts_with_dollar(Atom) :- sub_atom( Atom, 0, 1, _, '$'). 
 empty_module(?Module)
Generate or test empty modules (which contain no own predicate declaration, let alone any clauses).
   87empty_module(Module) :- 
   88    (  var(Module)                         % generating mode
   89    -> ( setof( M, empty_module__(M), Set),% avoid multiply returning
   90         member(Module,Set)                % ... the same module
   91       )
   92    ;  empty_module__(Module), !           % checking mode
   93    ). 
   94    
   95% A module is empty if all its predicates are imported
   96% (put differently: none of its predicates is not imported).    
   97empty_module__(M) :-                       % generator skips system Modules :-(
   98    current_predicate(M:_/_),
   99    \+ ( current_predicate(M:N/A),
  100         functor(H,N,A),
  101         \+ predicate_property(M:H, imported_from(_))
  102    ).
 module_of_file(?File, ?Module)
Module is the module defined in File (or 'user' if the file defines no explicit module or the defined module is a hidden system module.
  110module_of_file(File,Module):-
  111    module_property(Module,file(File)).    % Fails if File defines no module
  112                                           % or if it defines a hidden module
  113                                           % whose name starts with $
  114>>>>>>> 92defde0ffaad6ef7bd1d65d2c68e72011c7c32c
  115module_of_file(File,Module):-
  116    atom(File),                            % If File is provided as input
  117    \+ module_property(Module,file(File)), % and there is no module in that file
  118    ( Module=user                          % the default module is 'user'
  119    ; Module=system                        % but it could also be 'system'
  120    ).
  121
  122<<<<<<< HEAD
  123module_of_file_including_system(File,Module) :- % mode(?,?)
  124    module_property(Module,file(File)).    % Fails if File defines no module
  125                                           % or if it defines a hidden module
  126                                           % whose name starts with $
  127                                           
  128module_of_file_including_system(File,M):-  % mode(?,?)
  129    current_module_including_system(M),
  130    setof( F, file_of_module(M,F), Files ),
  131    member(File,Files).
  132	
  133
  134current_module_including_system(M) :-
  135    setof( M, N^A^defined_in(M,N,A), AllModules),
  136    member(M, AllModules).
  137    
  138file_of_module(M,File) :-
  139	defined_in(M,N,A),                     
  140    functor(P,N,A),                         
  141    predicate_property(M:P,file(File)).  
  142=======
  143%% visible_in_module(?Module,?Name,?Arity) is nondet.
  144%
  145% Suceed if the predicate Name/Arity is visible in Module
  146% either via a local declaration or import. 
  147% 
  148% The used predicate_property(Head,visible) is documented as:
  149%    True when predicate can  be called without raising a  predicate
  150%    existence  error.    This  means  that  the  predicate  is  (1)
  151%    defined, (2) can be  inherited from one of the  default modules
  152%    (see default_module/2) or (3) can be autoloaded.  The behaviour
  153%    is logically  consistent iff  the propery  visible is  provided
  154%    explicitly.   If  the property  is left  unbound, only  defined
  155%    predicates are enumerated.
  156          
  157visible_in_module(Module,Name,Arity) :-
  158%   (ground(functor(Head,Name,Arity),
  159%   predicate_property(Module:Head,visible).
  160% The above was created by Jan Wielemaker during his visit to our group 
  161% in Nov. 2011. It is intended as a better behaved alternative to the
  162% strangely inconsistent versions of current_predicate/1 and /2.
  163    
  164    current_predicate(Module:Name/Arity).
  165%  
  166% <-- Beware of current_predicate/2: It hides system modules! 
  167% Only current_predicate/1 returns ALL modules that see Name/Arity,
  168% including (hidden) system modules, such as '$syspreds'. 
  169% BUT it does it only if Name AND Arity are instantiated.
  170% If only Name is instantiated, current_predicate/1 only returns  
  171% non-system modules that actually CALL Name/_. 
  172
  173/*
  174Inconsistent behaviour of current_predicate(M:F/N):
  175
  1763 ?- F=visible,current_predicate(M:F/N).
  177F = visible,
  178M = system,
  179N = 1 ;
  180
  181F = visible,
  182M = gui_tracer,
  183N = 1 ;
  184
  185false.
  186
  1874 ?- F=visible,M='$syspreds', current_predicate(M:F/N).
  188M = '$syspreds',
  189F = visible,
  190N = 1 ;
  191
  192false.
  193
  1945 ?- F=visible,N=1,findall(M:F/N, current_predicate(M:F/N), All), length(All,L).
  195F = visible,
  196N = 1,
  197All = [emacs_buffer:visible/1, pce_hyper:visible/1, make:visible/1, 'condor.depend.expand.dnf':visible/1, targetModule:visible/1, prolog_break:visible/1,
  198utils4modules:visible/1, emacs_fundamental_mode:... / ..., ... : ...|...],
  199L = 203.
  200*/
declared_in_module(+Module, +Head) is det. declared_in_module(+Module, ?Head) is nondet. declared_in_module(?Module, ?Head) is nondet.

Determine whether the predicate indicated by Head has a local (non-imported) declaration in the module Module. The predicate succeeds if there is at least a declaration (but possibly no single clause) of the predicate in the module. If you want it to fail you must abolish the predicate declaration, not just retract all its clauses. See the built-ins Prolog predicates abolish/1, abolish/1, retractall/1, retract/1

  216declared_in_module(Module, Head) :-
  217   ( true ; Module = '$syspreds'),                     % Try also hidden module
  218   current_predicate(_, Module:Head),                  % Head is declared
  219   \+ predicate_property(Module:Head, imported_from(_)). % but not imported
 declared_in_module(?Module, ?Name, ?Arity, ?DeclaringModule) is nondet
Succeed if the predicate Name/Arity visible in Module is declared in DeclaringModule. Module = DeclaringModule holds if Module contains a local (non-imported) declaration. Otherwise, DeclaringModule is the module from which the declaration is imported. Note that predicate suceeds even if there is no defining clause for Name/Arity in DeclaringModule (definition implies declaration but not vice versa).
  232declared_in_module(Module,Name,Arity,DeclaringModule) :-
  233    visible_in_module(Module,Name,Arity),                % Name/arity is visible in Module    
  234	functor(Head,Name,Arity), 
  235    (  predicate_property(Module:Head, imported_from(M)) % by being imported
  236    -> DeclaringModule = M
  237    ;  DeclaringModule = Module                          % by being declared locally
  238    ).
 declared_in_module(?Module, +Head, ?DeclaringModule) is nondet
Unlike declared_in_module(Module,Name,Arity,DeclaringModule), functor(Head,Name,Arity) this call does not generate, it assumes Head is instantiated!
  248declared_in_module(Module,Head,DeclaringModule) :-   
  249    (  predicate_property(Module:Head, imported_from(M)) % imported
  250    -> DeclaringModule = M
  251    ;  DeclaringModule = Module                          % declared locally
  252    ),
  253    functor(Head,Name,Arity), 
  254    visible_in_module(Module,Name,Arity). % Name/arity is visible in Module   
 defined_in_module(?Module, +Head) is nondet
There is at least one clause for Head that is physically contained in Module (not just visible by import). The clause(es) in the module can come from different files (because of multifile predicates).
  263defined_in_module(Module,Head) :- 
  264    functor(Head,Name,Arity),
  265    defined_in_module(Module,Name,Arity).
 defined_in_module(?Module, ?Name, ?Arity) is nondet
There is at least one clause for Name/Arity that is physically contained in Module (not just visible by import). The clause(es) in the module can come from different files (because of multifile predicates).
  274defined_in_module(Module,Name,Arity) :- % <<< deleted 1 argument
  275    defined_in_module(Module,Name,Arity,Module).
 defined_in_module(?ReferencedModule, ?Name, ?Arity, ?DefiningModule) is nondet
The predicate Name/Arity visible in ReferencedModule is defined in DefiningModule. That is, there is at least one clause for Name/Arity that is physically contained in DefiningModule (not just visible by import). The clause(es) in DefiningModule can come from different files (because of multifile predicates).
  284defined_in_module(ReferencedModule,Name,Arity,DefiningModule) :- 
  285    declared_in_module(ReferencedModule,Name,Arity,DefiningModule),
  286    functor(Head,Name,Arity),
  287    \+ predicate_property(DefiningModule:Head, imported(_)).
 list_module(+Module)
List all predicates DEFINED in Module.
  291list_module(Module) :-
  292    nonvar(Module), 
  293    forall( defined_in_module(Module,F,A), listing(Module:F/A) ).
 declared_but_undefined(-Module, -Name, -Arity, ?DeclaringModule) is nondet
Succeed if the predicate Name/Arity visible in Module is declared in DeclaringModule but not defined by any clause.
  299declared_but_undefined(Module,Name,Arity) :- % <<< deleted 1 argument
  300    declared_in_module(Module,Name,Arity,Module),
  301    functor(Head,Name,Arity),
  302    \+ (predicate_property(Module:Head, number_of_clauses(X)), X>0).
 referenced_but_undeclared(?Module, ?Name, ?Arity) is nondet
Succeed if the predicate Name/Arity is called in Module but is not visible there.
  310referenced_but_undeclared(Module,Name,Arity) :-
  311    predicate_property(Module:Head,undefined),
  312    functor(Head,Name,Arity).   
 defined_in_file(-Module, -Name, -Arity, -N, ?File, ?Line) is nondet
defined_in_file(+Module,+Name,+Arity,+N,?File,?Line) is det

Get the source locations (File and Line) of all clauses that define Module:Name/Arity.

  320defined_in_file(Module,Name,Arity, N,File,Line) :-
  321	defined_in_file(Module,Name,Arity, _Ref, N,File,Line).
  322
  323defined_in_file(Module,Name,Arity, Ref, N,File,Line) :-
  324    declared_in_module(Module,Name,Arity,Module),
  325    functor(Head,Name,Arity),
  326    nth_clause(Module:Head,N,Ref),
  327    clause_property(Ref,file(File)),      
  328    clause_property(Ref,line_count(Line)).
  329
  330>>>>>>> 92defde0ffaad6ef7bd1d65d2c68e72011c7c32c
  331
  332%% declared_in_file(?Module, Head, ?File, ?Line) is nondet
  333%
  334% File is the file containing the declaration for the predicate Head, 
  335% which is visible in Module. 
  336% Line = 1 (approximating the line number information missing for declarations). 
  337
  338<<<<<<< HEAD
  339declared_in_file(Module,Name,Arity,[File-Lines]) :-
  340    functor(Head,Name,Arity),
  341    predicate_property(Module:Head,foreign),
  342    !,
  343    File=none,
  344    Lines=none.
  345%    File = 'No Prolog source code (only compiled external language code)',
  346%    Line = 0.
  347=======
  348declared_in_file(Module,Name,Arity,[File-[Line]]) :-
  349    functor(Head,Name,Arity),
  350    predicate_property(Module:Head,foreign),
  351    !,
  352    File = 'No Prolog source code (only compiled external language code)',
  353    Line = 0.
  354>>>>>>> 92defde0ffaad6ef7bd1d65d2c68e72011c7c32c
  355declared_in_file(Module,_Name,_Arity,[File-[Line]]) :-
  356    module_property(Module, file(File)),  % declaration in known file
  357    !,
  358    Line=1.                                        % guess the unknown line nr
  359<<<<<<< HEAD
  360declared_in_file(Module,Name,Arity,[File-Lines]) :-
  361    functor(Head,Name,Arity),
  362    predicate_property(Module:Head,dynamic),
  363    !,
  364    File=none,
  365    Lines=none.
  366%    File = 'Dynamic predicate, no Prolog source code (only dynamic code)' ,  
  367%    Line = 0.
  368=======
  369declared_in_file(Module,Name,Arity,[File-[Line]]) :-
  370    functor(Head,Name,Arity),
  371    predicate_property(Module:Head,foreign),
  372    File = 'Dynamic predicate, no Prolog source code (only dynamic code)' ,  
  373    Line = 0.
  374>>>>>>> 92defde0ffaad6ef7bd1d65d2c68e72011c7c32c
  375
  376%% 
  377% declared_or_defined_in_files(+Module,+Name,+Arity, Locations) is semidet
  378% 
  379% Locations is a list of File-Lines terms whose Lines
  380% is a list of numbers indicating the starting lines of
  381% the clauses for Module:Name/Arity contained in File. 
  382defined_in_files(Module,Name,Arity,Locations) :-
  383    ( var(Module)
  384    ; var(Name)
  385    ; var(Arity)
  386    ),
  387    throw( input_argument_free(defined_in_files(Module,Name,Arity,Locations)) ).
  388     
  389defined_in_files(Module,Name,Arity,Locations) :-
  390    findall( File-Lines,
  391             setof( Line, Module^Name^Arity^N^
  392                    defined_in_file(Module,Name,Arity, N,File,Line),
  393                    Lines
  394             ),
  395             SrcLocations
  396    ),
  397    (  SrcLocations == []
  398<<<<<<< HEAD
  399    -> ( defined_in(Module,Name,Arity,DeclaringModule),
  400=======
  401    -> ( declared_in_module(Module,Name,Arity,DeclaringModule),
  402>>>>>>> 92defde0ffaad6ef7bd1d65d2c68e72011c7c32c
  403         declared_in_file(DeclaringModule,Name,Arity,DeclLocation),
  404         Locations = DeclLocation
  405       ) 
  406    ;  Locations = SrcLocations
  407    ).
  408
  409<<<<<<< HEAD
  410%% defined_in_file(-Module,-Name,-Arity,-N,?File,?Line) is nondet
  411%  defined_in_file(+Module,+Name,+Arity,+N,?File,?Line) is det
  412%
  413%  The N-th clause of the predicate Module:Name/Arity starts at
  414%  line Line in File.
  415%   
  416%  @param File The full path of the file containing the N-th clause
  417
  418defined_in_file(Module,Name,Arity, N,File,Line) :-
  419    defined_in(Module,Name,Arity,Module),
  420    functor(Head,Name,Arity),
  421    nth_clause(Module:Head,N,Ref),
  422    clause_property(Ref,file(File)),      
  423    clause_property(Ref,line_count(Line)).
  424%    ( module_property(M, file(F))
  425%    -> DDDeclaringModule=M
  426%    ;  DDDeclaringModule=unknown
  427%    ).
  428
  429
  430      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  431      %
  432      %    UNDEFINED, DEFINED, IMPLEMENTED, IMPORTED, VISIBLE    %
  433      %
  434      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
declared_in_module(+Module, +Head) is det. declared_in_module(+Module, ?Head) is nondet. declared_in_module(?Module, ?Head) is nondet.

Determine whether the predicate indicated by Head has a local (non-imported) declaration in the module Module. The predicate succeeds if there is at least a declaration (but possibly no single clause) of the predicate in the module.

  446declared_in_module(Module, Head) :-
  447   defined_in(Module, Head).
  448   
  449defined_in(Module, Head) :-
  450   (  nonvar(Head)
  451   -> ( functor(Head,Name,Arity), defined_in(Module,Name,Arity) )
  452   ;  ( defined_in(Module,Name,Arity), functor(Head,Name,Arity) )
  453   ). 
 defined_in(?Module, ?Name, ?Arity, ?Category) is nondet
Succeed if the predicate Name/Arity visible in Module is declared in DeclaringModule. Module = DeclaringModule holds if Module contains a non-imported (local) declaration. Otherwise, DeclaringModule is the module from which the declaration is imported. Note that predicate suceeds even if there is no clause for Name/Arity in DeclaringModule.
  466defined_in(Module,Name,Arity) :-
  467	defined_in(SubModule,Name,Arity,Module), 
  468	( % Non-hidden module does not import from any other
  469	  SubModule == Module
  470	; % "system" imports from hidden system modules 
  471	  system == SubModule,
  472	  system \== Module,
  473	  hidden_name(Module) 
  474	).
  475
  476
  477% In SWI-Prolog names of hidden modules and predicates start with a $ sign:
  478hidden_name(Name) :- sub_atom(Name, 0, _, _, $).
 defined_in(?SubModule, ?Name, ?Arity, ?DeclaringModule) is nondet
Succeed if the predicate Name/Arity visible in Module is declared in DeclaringModule. Module = DeclaringModule holds if Module contains a non-imported (local) declaration. Otherwise, DeclaringModule is the module from which the declaration is imported. Note that predicate suceeds even if there is no clause for Name/Arity in DeclaringModule.
  491defined_in(Module,Name,Arity,DeclaringModule) :-
  492    visible_in(Module,Name,Arity),                % Name/arity is visible in Module    
  493	functor(Head,Name,Arity), 
  494    (  predicate_property(Module:Head, imported_from(M)) % by being imported
  495    -> DeclaringModule = M
  496    ;  DeclaringModule = Module                          % by being declared locally
  497    ).
  498
  499ddd(Module,Name,Arity) :- 
  500    visible_in(Module,Name,Arity),                % Name/arity is visible in Module    
  501	functor(Head,Name,Arity), 
  502    \+ predicate_property(Module:Head, imported_from(_)).
  503    
  504definition_category(Module,Name,Arity,Category) :-
  505	defined_in(Module,Name,Arity),
  506    functor(Head,Name,Arity),
  507    (  predicate_property(Module:Head, foreign)
  508    -> Category = foreign
  509    ;  (  predicate_property(Module:Head, number_of_clauses(0))
  510       -> Category = declared
  511       ;  Category = implemented
  512       )
  513    ).
 implemented_in(Module, Name, Arity) is nondet
Implemented = There is at least one clause in the declaring module. The clausees in the module can come from different files.
  522implemented_in(Module,Name,Arity) :- % <<< deleted 1 argument
  523    clause_location(Module,Name,Arity,_N,_File,_Line).
 undefined(?DeclaringModule, ?Name, ?Arity) is nondet
Succeed if the predicate Name/Arity called in Module is not defined in Module (neither by a clause nor by foreign code nor by a declaration).
  531undefined_in(Module,Name,Arity) :-
  532    (atom(Name), integer(Arity))
  533    -> % Checking mode
  534       ( functor(Head,Name,Arity),
  535         predicate_property(Module:Head,undefined)
  536       )
  537    ;  % Generating mode
  538       ( predicate_property(Module:Head,undefined),
  539         functor(Head,Name,Arity)
  540       )    .
 visible_in(?Module, ?Name, ?Arity) is nondet
Suceed if the predicate Name/Arity is visible in Module either via a local declaration or import.
  550visible_in(Module,Name,Arity) :-
  551    current_predicate(Module:Name/Arity).
  552%    ( atom(Name), integer(Arity) )
  553%    -> ( functor(Head,Name,Arity), 
  554%         current_predicate(_,Module:Head)
  555%       )
  556%    ;  ( current_predicate(_,Module:Head),
  557%         functor(Head,Name,Arity)
  558%       )
  559%    .
  560    
  561%   (ground(functor(Head,Name,Arity),
  562%   predicate_property(Module:Head,visible).
  563%
  564% predicate_property(Head,visible) was created by Jan Wielemaker  
  565% during his visit to our group in Nov. 2011. It is intended as 
  566% a better behaved alternative to the inconsistent versions of 
  567% current_predicate/1 and /2. It is documented as:
  568%    True when predicate can  be called without raising a  predicate
  569%    existence  error.    This  means  that  the  predicate  is  (1)
  570%    defined, (2) can be  inherited from one of the  default modules
  571%    (see default_module/2) or (3) can be autoloaded.  The behaviour
  572%    is logically  consistent iff  the propery  visible is  provided
  573%    explicitly.   If  the property  is left  unbound, only  defined
  574%    predicates are enumerated.
  575% However, the following does not seem really encouraging:
  576% 37 ?- utils4modules:is_inconsistent( A,B ) .
  577% A = 1547,    <- failed current_predicate when visible suceeds
  578% B = 933234.  <- failed visible when current_predicate succeeds
  579%
  580% 38 ?- count( predicate_property(Module:Head,visible), N).
  581% N = 1206199.
  582%
  583% 39 ?- count( current_predicate(Module:Name/Arity), N).
  584% N = 17356.
  585   
  586
  587% <-- Beware of current_predicate/2: It hides system modules! 
  588% Only current_predicate/1 returns ALL modules that see Name/Arity,
  589% including (hidden) system modules, such as '$syspreds'. 
  590% BUT it does it only if Name AND Arity are instantiated.
  591% If only Name is instantiated, current_predicate/1 only returns  
  592% non-system modules that actually CALL Name/_. 
  593
  594/*
  595Inconsistent behaviour of current_predicate(M:F/N):
  596
  5973 ?- F=visible,current_predicate(M:F/N).
  598F = visible,
  599M = system,
  600N = 1 ;
  601
  602F = visible,
  603M = gui_tracer,
  604N = 1 ;
  605
  606false.
  607
  6084 ?- F=visible,M='$syspreds', current_predicate(M:F/N).
  609M = '$syspreds',
  610F = visible,
  611N = 1 ;
  612
  613false.
  614
  6155 ?- F=visible,N=1,findall(M:F/N, current_predicate(M:F/N), All), length(All,L).
  616F = visible,
  617N = 1,
  618All = [emacs_buffer:visible/1, pce_hyper:visible/1, make:visible/1, 'condor.depend.expand.dnf':visible/1, targetModule:visible/1, prolog_break:visible/1,
  619utils4modules:visible/1, emacs_fundamental_mode:... / ..., ... : ...|...],
  620L = 203.
  621*/
  622=======
  623
  624
  625>>>>>>> 92defde0ffaad6ef7bd1d65d2c68e72011c7c32c