<commit_before>:- module(ionic,[ionic//3,acid_base//1]).
:- set_prolog_flag(double_quotes,chars).
:- use_module(oxyanion).
:- use_module(covalent,[sub_general//2]).



%%% Different algorithm is used depending on convertion from name to formula or from formula to name %%% 

ionic(Elems,ElemsR,Formula) --> {var(Formula)}, fwd(Elems,ElemsR,Formula), !.
ionic(_,_,Formula) --> {nonvar(Formula)}, rev(Formula).


%%% The Algorithms %%% 

fwd(MElems,FinalRest,[[MSym,MSub],[NMSym,NMSub]|Appended]) --> compound(MElems,FinalRest,[MSym,MCharge,NMSym,NMCharge],Appended),
	{
		GCD is gcd(MCharge,NMCharge),
		MSub is abs(NMCharge / GCD),
		NMSub is abs(MCharge / GCD)
	}.

rev([[MSym,MSub],[NMSym,NMSub]|Appended]) -->
	{
		%  TODO: If the metal is monovalent, don't bother conjuring it up! Just use it.
		%  Also, no need to actually check these charges, is there?
	
		(MSym = "H"; charge_check(metal,MSym)),

		(charge(NMSym,NMCharge), !),

		NMTotal is abs(NMSub * NMCharge),
		MCharge is NMTotal / MSub
	},

	compound(_,_,[MSym,MCharge,NMSym,NMCharge],Appended),
	!,

	/* Corrector: remove if unecessary */
	(
		{GCD is gcd(MSub,NMSub)}, 
		(
			%			{GCD = 1} -> {true}; 
			%	syntax_stop(corrector_not_reduced)
			%
			{GCD = 1} xx corrector_not_reduced
		)
	).


%%% Ionic Compound Naming Rules %%% 

compound(Elems,Rest,Formula,[]) --> acid(Elems,Rest,Formula).
compound(MElems,FinalRest,[MSym,MCharge,NMSym,NMCharge],Hydrate) --> 
	cation(MElems,MRest,MSym,MCharge), 
	" ", 
	anion(MRest,NMRest,NMSym,NMCharge), 
	hydrate_part(NMRest,FinalRest,Hydrate).

cation(Elems,Rest,Formula,Charge) --> group(Elems,Rest,Formula,_),
		charge_check(metal,Formula,Charge) xx cation.

cation([Sym|Rest],Rest,Sym,Charge) --> metal(Sym,Charge).

anion(Elems,Rest,Formula,Charge) --> group(Elems,Rest,Formula,_), 
		{charge_check(nonmetal,Formula,Charge)} xx anion.

anion([Sym|Rest],Rest,Sym,Charge) --> nonmetal_ide(Sym,_,Charge) xx nonmetal, 
	{Charge < 0} xx noble_gas_q.


%%% Hydrates %%%

hydrate_part(["H","O"|ElemR],ElemR,[[[["H",2],["O",1]],Num]]) --> 
	" ", 
	(sub_general(Num,Suffix), Suffix), 
	"hydrate" xx hydrate_h2o.

hydrate_part(Pass,Pass,[]) --> [].


%%% Metals %%% 

metal(Sym,Charge) --> element(Sym,_), {charge(Sym,Charges)}, metal_valence(Charge,Charges).

metal_valence(Charge,Charges) --> 
	{is_list(Charges)},
	%	(
	%	multivalent_charge(Charge) -> {true}; 
	%	syntax_stop(charge)
	%	),
	
	multivalent_charge(Charge) xx charge,

	{member(Charge,Charges)} xx charge_invalid.

metal_valence(Charge,Charges) --> 
	{Charges > 0},
       	multivalent_corrector(Charge), /* CORRECTOR: remove if unecessary */	
	{Charge = Charges}.

/* CORRECTOR: remove if unnecessary */
multivalent_corrector(Charge) --> {var(Charge)} -> 
	( 
		(
			multivalent_charge(_), 
			syntax_stop(corrector_not_multivalent)
		); 
		{true}
	); {true}.


%%% Multivalent Charges %%% 

multivalent_charge(Charge) --> "(", num_roman(Charge), ")".

num_roman(8) --> "VIII".
num_roman(8) --> "IIX".

num_roman(7) --> "VII".
num_roman(6) --> "VI".
num_roman(5) --> "V".
num_roman(4) --> "IV".
num_roman(3) --> "III".
num_roman(2) --> "II".
num_roman(1) --> "I".

%%% Acids %%% 

acid(["H"|Elems],Rest,["H",1,ASym,ACharge]) --> acid_anion(Elems,Rest,ASym,ACharge), " acid" xx acid.

acid_anion(Elems,Rest,ASym,ACharge) --> hydro_acid(Elems,Rest,ASym,ACharge).
acid_anion(Elems,Rest,ASym,ACharge) --> oxyanion_acid(Elems,Rest,ASym,ACharge).
acid_anion(Elems,Rest,ASym,ACharge) --> polyatomic_oxy_acid(Elems,Rest,ASym,ACharge).
acid_anion(Elems,Rest,ASym,ACharge) --> polyatomic_hydro_acid(Elems,Rest,ASym,ACharge).

hydro_acid([ASym|Rest],Rest,ASym,ACharge) --> "hydro", acid_base(ASym), ic_suffix,
		{charge_check(nonmetal,ASym,ACharge)} xx nonmetal_acid. 

polyatomic_oxy_acid(Elems,Rest,ASym,ACharge) --> group_base(Elems,Rest,ASym,_), ic_suffix,
	(
	
		{member(["O",_],ASym), !} ; 
		({var(Elems)} -> syntax_stop(oxy_acid_rule))
		
	), 
		{charge_check(nonmetal,ASym,ACharge)} xx nonmetal_acid.


polyatomic_hydro_acid(Elems,Rest,ASym,ACharge) --> "hydro", group_base(Elems,Rest,ASym,_), ic_suffix, 
	 
		{ \+ member(["O",_],ASym), ! } xx hydro_acid_rule,
		{charge_check(nonmetal,ASym,ACharge)} xx nonnmetal_acid.

acid_base(Sym) --> element_base(Sym,_), 
%(acid_ion_suffix(Sym) -> {true} ; syntax_stop(acid_suffix)).
	acid_ion_suffix(Sym) xx acid_suffix.

ic_suffix --> ("ic" -> {true}; syntax_stop(ic_acid_suffix)).



%%%%% ERROR MESSAGE GUIDANCE %%%%% 



guidance_errcode(charge,punct,
	'The ionic charge you have entered (starting at parenthesis) is malformed. You must use capital roman numerals in parentheses.

	e.g copper<(II)>, not something like copper<(2)> or copper<(ii)>.'
 ).

guidance_errcode(charge,digit,
	'Ionic charges must be entered with parenthesized roman numerals.
	
	 e.g. copper<(II)> instead of copper<2>'
 ).<commit_msg>Safe simplification.<commit_after>:- module(ionic,[ionic//3,acid_base//1]).
:- set_prolog_flag(double_quotes,chars).
:- use_module(oxyanion).
:- use_module(covalent,[sub_general//2]).



%%% Different algorithm is used depending on convertion from name to formula or from formula to name %%% 

ionic(Elems,ElemsR,Formula) --> {var(Formula)}, fwd(Elems,ElemsR,Formula), !.
ionic(_,_,Formula) --> {nonvar(Formula)}, rev(Formula).


%%% The Algorithms %%% 

fwd(MElems,FinalRest,[[MSym,MSub],[NMSym,NMSub]|Appended]) --> compound(MElems,FinalRest,[MSym,MCharge,NMSym,NMCharge],Appended),
	{
		GCD is gcd(MCharge,NMCharge),
		MSub is abs(NMCharge / GCD),
		NMSub is abs(MCharge / GCD)
	}.

rev([[MSym,MSub],[NMSym,NMSub]|Appended]) -->
	{
		%  TODO: If the metal is monovalent, don't bother conjuring it up! Just use it.
		%  Also, no need to actually check these charges, is there?
	
		(MSym = "H"; charge_check(metal,MSym)),

		(charge(NMSym,NMCharge), !),

		NMTotal is abs(NMSub * NMCharge),
		MCharge is NMTotal / MSub
	},

	compound(_,_,[MSym,MCharge,NMSym,NMCharge],Appended),
	!,

	/* Corrector: remove if unecessary */
	(
		{GCD is gcd(MSub,NMSub)}, 
		(
			%			{GCD = 1} -> {true}; 
			%	syntax_stop(corrector_not_reduced)
			%
			{GCD = 1} xx corrector_not_reduced
		)
	).


%%% Ionic Compound Naming Rules %%% 

compound(Elems,Rest,Formula,[]) --> acid(Elems,Rest,Formula).
compound(MElems,FinalRest,[MSym,MCharge,NMSym,NMCharge],Hydrate) --> 
	cation(MElems,MRest,MSym,MCharge), 
	" ", 
	anion(MRest,NMRest,NMSym,NMCharge), 
	hydrate_part(NMRest,FinalRest,Hydrate).

cation(Elems,Rest,Formula,Charge) --> group(Elems,Rest,Formula,_),
		charge_check(metal,Formula,Charge) xx cation.

cation([Sym|Rest],Rest,Sym,Charge) --> metal(Sym,Charge).

anion(Elems,Rest,Formula,Charge) --> group(Elems,Rest,Formula,_), 
		{charge_check(nonmetal,Formula,Charge)} xx anion.

anion([Sym|Rest],Rest,Sym,Charge) --> nonmetal_ide(Sym,_,Charge) xx nonmetal, 
	{Charge < 0} xx noble_gas_q.


%%% Hydrates %%%

hydrate_part(["H","O"|ElemR],ElemR,[[[["H",2],["O",1]],Num]]) --> 
	" ", 
	(sub_general(Num,Suffix), Suffix), 
	"hydrate" xx hydrate_h2o.

hydrate_part(Pass,Pass,[]) --> [].


%%% Metals %%% 

metal(Sym,Charge) --> element(Sym,_), {charge(Sym,Charges)}, metal_valence(Charge,Charges).

metal_valence(Charge,Charges) --> 
	{is_list(Charges)},
	%	(
	%	multivalent_charge(Charge) -> {true}; 
	%	syntax_stop(charge)
	%	),
	
	multivalent_charge(Charge) xx charge,

	{member(Charge,Charges)} xx charge_invalid.

metal_valence(Charge,Charges) --> 
	{Charges > 0},
       	multivalent_corrector(Charge), /* CORRECTOR: remove if unecessary */	
	{Charge = Charges}.

/* CORRECTOR: remove if unnecessary */
multivalent_corrector(Charge) --> {var(Charge)} -> 
	( 
		(
			multivalent_charge(_), 
			syntax_stop(corrector_not_multivalent)
		); 
		{true}
	); {true}.


%%% Multivalent Charges %%% 

multivalent_charge(Charge) --> "(", num_roman(Charge), ")".

num_roman(8) --> "VIII".
num_roman(8) --> "IIX".

num_roman(7) --> "VII".
num_roman(6) --> "VI".
num_roman(5) --> "V".
num_roman(4) --> "IV".
num_roman(3) --> "III".
num_roman(2) --> "II".
num_roman(1) --> "I".

%%% Acids %%% 

acid(["H"|Elems],Rest,["H",1,ASym,ACharge]) --> acid_anion(Elems,Rest,ASym,ACharge), " acid" xx acid.

acid_anion(Elems,Rest,ASym,ACharge) --> hydro_acid(Elems,Rest,ASym,ACharge).
acid_anion(Elems,Rest,ASym,ACharge) --> oxyanion_acid(Elems,Rest,ASym,ACharge).
acid_anion(Elems,Rest,ASym,ACharge) --> polyatomic_oxy_acid(Elems,Rest,ASym,ACharge).
acid_anion(Elems,Rest,ASym,ACharge) --> polyatomic_hydro_acid(Elems,Rest,ASym,ACharge).

hydro_acid([ASym|Rest],Rest,ASym,ACharge) --> "hydro", acid_base(ASym), ic_suffix,
		{charge_check(nonmetal,ASym,ACharge)} xx nonmetal_acid. 

polyatomic_oxy_acid(Elems,Rest,ASym,ACharge) --> group_base(Elems,Rest,ASym,_), ic_suffix,
	(
	
		{member(["O",_],ASym), !} ; 
		({var(Elems)} -> syntax_stop(oxy_acid_rule))
		
	), 
		{charge_check(nonmetal,ASym,ACharge)} xx nonmetal_acid.


polyatomic_hydro_acid(Elems,Rest,ASym,ACharge) --> "hydro", group_base(Elems,Rest,ASym,_), ic_suffix, 
	 
		{ \+ member(["O",_],ASym), ! } xx hydro_acid_rule,
		{charge_check(nonmetal,ASym,ACharge)} xx nonnmetal_acid.

acid_base(Sym) --> element_base(Sym,_), 
%(acid_ion_suffix(Sym) -> {true} ; syntax_stop(acid_suffix)).
	acid_ion_suffix(Sym) xx acid_suffix.

%ic_suffix --> ("ic" -> {true}; syntax_stop(ic_acid_suffix)).
ic_suffix --> "ic" xx ic_acid_suffix.


%%%%% ERROR MESSAGE GUIDANCE %%%%% 



guidance_errcode(charge,punct,
	'The ionic charge you have entered (starting at parenthesis) is malformed. You must use capital roman numerals in parentheses.

	e.g copper<(II)>, not something like copper<(2)> or copper<(ii)>.'
 ).

guidance_errcode(charge,digit,
	'Ionic charges must be entered with parenthesized roman numerals.
	
	 e.g. copper<(II)> instead of copper<2>'
 ).
{'fraction_non_alphanumeric': 0.1902896608597052, 'fraction_numerical': 0.004535614885063395, 'mean_word_length': 5.259354838709678, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 100, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4122056', 'n_tokens_mistral': 4432, 'n_tokens_neox': 3964, 'n_words': 583}