:- [cvs, unit_test].
cvs_id('$Id: utf_8.pl,v 1.2 2004/04/22 17:50:25 sandro Exp $').

%% utf8_codes(+N, -A)
%%
%%      Convert a Unicode character number into a list of
%%      byte values for the UTF-8 encoding of that character
%%
%%      (Only in that direction for now, because that's all
%%      I need.)
%%
%%      See: http://www.rfc-editor.org/rfc/rfc3629.txt

utf8_codes(N, [_]) :-  N < 0, !, fail.

%     0000 0000-0000 007F | 0xxxxxxx
utf8_codes(N, [N]) :-  N =< 16'7F, !.

%     0000 0080-0000 07FF | 110xxxxx 10xxxxxx
utf8_codes(N, [B1, B2]) :- N =< 16'7FF, !,
	B1 is 2'11000000 \/  N >> 6,
	B2 is 2'10000000 \/  (N /\ 2'00111111).

%    "prohibits encoding ... U+D800 and U+DFFF, which are reserved for ..."
utf8_codes(N, _Error) :- N >= 16'D800, N =< 16'DFFF,
	%_Error = "***UTF-8 ENCODING ERROR IN SOURCE***".
	throw(utf_8_encoding_error(N)).


%    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
utf8_codes(N, [B1, B2, B3]) :- N =< 16'FFFF, !,
	B1 is 2'11100000 \/  N >> 12,
	B2 is 2'10000000 \/  ((N >> 6) /\ 2'00111111),
	B3 is 2'10000000 \/  (N /\ 2'00111111).
	

%    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
utf8_codes(N, [B1, B2, B3, B4]) :- N =< 16'10FFFF, !,
	B1 is 2'11110000 \/  N >> 24,
	B2 is 2'10000000 \/  ((N >> 12) /\ 2'00111111),
	B3 is 2'10000000 \/  ((N >> 6) /\ 2'00111111),
	B4 is 2'10000000 \/  (N /\ 2'00111111).

	
unit_test(utf8_codes(0, [0])).
unit_test( \+ utf8_codes(-1, [_])).
unit_test(utf8_codes(1, [1])).
unit_test(utf8_codes(127, [127])).
unit_test(utf8_codes(16'80, [16'C2, 16'80])).

% RFC 3629 Examples
%
% "A<NOT IDENTICAL TO><ALPHA>."
unit_test(utf8_codes(16'0041, [16'41])).
unit_test(utf8_codes(16'2262, [16'E2, 16'89, 16'A2])).
unit_test(utf8_codes(16'0391, [16'CE, 16'91])).

% Korean "hangugeo", meaning "the Korean language"
unit_test(utf8_codes(16'D55C, [16'ED, 16'95, 16'9C])).
unit_test(utf8_codes(16'AD6D, [16'EA, 16'B5, 16'AD])).
unit_test(utf8_codes(16'C5B4, [16'EC, 16'96, 16'B4])).

% Japanese "nihongo", meaning "the Japanese language"
unit_test(utf8_codes(16'65E5, [16'E6, 16'97, 16'A5])).
unit_test(utf8_codes(16'672C, [16'E6, 16'9C, 16'AC])).
unit_test(utf8_codes(16'8A9E, [16'E8, 16'AA, 16'9E])).

% a Chinese character meaning 'stump of tree'
unit_test(utf8_codes(16'233B4, [16'F0, 16'A3, 16'8E, 16'B4])).
