view ibin/brachylog/Brachylog-master/src/predicates.pl @ 11865:318de151d0ec draft

<b_jonas> python3 -cimport os,zipfile; os.chdir("ibin/brachylog"); zipfile.ZipFile("master.zip").extractall()
author HackEso <hackeso@esolangs.org>
date Tue, 16 Jul 2019 21:37:27 +0000
parents
children
line wrap: on
line source

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
____            ____
\   \          /   /
 \   \  ____  /   /
  \   \/    \/   /
   \     /\     /     BRACHYLOG
    \   /  \   /      A terse declarative logic programming language
    /   \  /   \
   /     \/     \     Written by Julien Cumin - 2017
  /   /\____/\   \    https://github.com/JCumin/Brachylog
 /   /  ___   \   \
/___/  /__/    \___\

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


:- module(predicates, [%Symbols                                         % Reversed version
                       brachylog_lessequal/3,                           brachylog_lessequal_reversed/3,
                       brachylog_greaterequal/3,                        brachylog_greaterequal_reversed/3,
                       brachylog_contains/3,                            brachylog_contains_reversed/3,
                       brachylog_in/3,                                  brachylog_in_reversed/3,
                       brachylog_superset/3,                            brachylog_superset_reversed/3,
                       brachylog_subset/3,                              brachylog_subset_reversed/3,
                       brachylog_reverse/3,                             brachylog_reverse_reversed/3,
                       brachylog_call_predicate/3,                      brachylog_call_predicate_reversed/3,
                       brachylog_circular_permute_counterclockwise/3,   brachylog_circular_permute_counterclockwise_reversed/3,
                       brachylog_circular_permute_clockwise/3,          brachylog_circular_permute_clockwise_reversed/3,
                       brachylog_root/3,                                brachylog_root_reversed/3,
                       brachylog_ceil/3,                                brachylog_ceil_reversed/3,
                       brachylog_floor/3,                               brachylog_floor_reversed/3,
                       brachylog_range_ascending/3,                     brachylog_range_ascending_reversed/3,
                       brachylog_range_descending/3,                    brachylog_range_descending_reversed/3,
                       brachylog_natural_integer/3,                     brachylog_natural_integer_reversed/3,
                       brachylog_integer/3,                             brachylog_integer_reversed/3,
                       brachylog_float/3,                               brachylog_float_reversed/3,
                       brachylog_different/3,                           brachylog_different_reversed/3,
                       brachylog_identity/3,                            brachylog_identity_reversed/3,
                       brachylog_integer_division/3,                    brachylog_integer_division_reversed/3,
                       brachylog_multiply/3,                            brachylog_multiply_reversed/3,
                       brachylog_modulo/3,                              brachylog_modulo_reversed/3,
                       brachylog_exp/3,                                 brachylog_exp_reversed/3,
                       brachylog_plus/3,                                brachylog_plus_reversed/3,
                       brachylog_minus/3,                               brachylog_minus_reversed/3,
                       brachylog_divide/3,                              brachylog_divide_reversed/3,
                       brachylog_less/3,                                brachylog_less_reversed/3,
                       brachylog_equal/3,                               brachylog_equal_reversed/3,
                       brachylog_greater/3,                             brachylog_greater_reversed/3,
                       brachylog_transpose/3,                           brachylog_transpose_reversed/3,
                       brachylog_power/3,                               brachylog_power_reversed/3,

                       %Lowercase letters                               % Reversed version
                       brachylog_adfix/3,                               brachylog_adfix_reversed/3,
                       brachylog_behead/3,                              brachylog_behead_reversed/3,
                       brachylog_concatenate/3,                         brachylog_concatenate_reversed/3,
                       brachylog_duplicates/3,                          brachylog_duplicates_reversed/3,
                       brachylog_factors/3,                             brachylog_factors_reversed/3,
                       brachylog_group/3,                               brachylog_group_reversed/3,
                       brachylog_head/3,                                brachylog_head_reversed/3,
                       brachylog_index/3,                               brachylog_index_reversed/3,
                       brachylog_juxtapose/3,                           brachylog_juxtapose_reversed/3,
                       brachylog_knife/3,                               brachylog_knife_reversed/3,
                       brachylog_length/3,                              brachylog_length_reversed/3,
                       brachylog_order/3,                               brachylog_order_reversed/3,
                       brachylog_permute/3,                             brachylog_permute_reversed/3,
                       brachylog_substring/3,                           brachylog_substring_reversed/3,
                       brachylog_tail/3,                                brachylog_tail_reversed/3,
                       brachylog_write/3,                               brachylog_write_reversed/3,
                       brachylog_xterminate/3,                          brachylog_xterminate_reversed/3,
                       brachylog_zip/3,                                 brachylog_zip_reversed/3,

                       %Lowercase letters with dot below                % Reversed version
                       brachylog_to_codes/3,                            brachylog_to_codes_reversed/3,
                       brachylog_blocks/3,                              brachylog_blocks_reversed/3,
                       brachylog_dichotomize/3,                         brachylog_dichotomize_reversed/3,
                       brachylog_elements/3,                            brachylog_elements_reversed/3,
                       brachylog_to_number/3,                           brachylog_to_number_reversed/3,
                       brachylog_lowercase/3,                           brachylog_lowercase_reversed/3,
                       brachylog_split_lines/3,                         brachylog_split_lines_reversed/3,
                       brachylog_occurences/3,                          brachylog_occurences_reversed/3,
                       brachylog_random_element/3,                      brachylog_random_element_reversed/3,
                       brachylog_shuffle/3,                             brachylog_shuffle_reversed/3,
                       brachylog_uppercase/3,                           brachylog_uppercase_reversed/3,
                       brachylog_writeln/3,                             brachylog_writeln_reversed/3,

                       %Lowercase letters with dot above                % Reversed version
                       brachylog_absolute_value/3,                      brachylog_absolute_value_reversed/3,
                       brachylog_base/3,                                brachylog_base_reversed/3,
                       brachylog_coerce/3,                              brachylog_coerce_reversed/3,
                       brachylog_prime_decomposition/3,                 brachylog_prime_decomposition_reversed/3,
                       brachylog_factorial/3,                           brachylog_factorial_reversed/3,
                       brachylog_groups/3,                              brachylog_groups_reversed/3,
                       brachylog_matrix/3,                              brachylog_matrix_reversed/3,
                       brachylog_negate/3,                              brachylog_negate_reversed/3,
                       brachylog_prime/3,                               brachylog_prime_reversed/3,
                       brachylog_random_number/3,                       brachylog_random_number_reversed/3,
                       brachylog_sign/3,                                brachylog_sign_reversed/3,
                       brachylog_to_string/3,                           brachylog_to_string_reversed/3,
                       brachylog_cartesian_product/3,                   brachylog_cartesian_product_reversed/3,

                       %Label                                           % Reversed version
                       brachylog_label/3,                               brachylog_label_reversed/3
                      ]).

:- use_module(library(clpfd)).
:- use_module(utils).

:- multifile clpfd:run_propagator/2.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_LESSEQUAL
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_lessequal_reversed(S, I, O) :-
    brachylog_lessequal(S, O, I).
brachylog_lessequal('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_lessequal('integer':I, Arg, Output).
brachylog_lessequal('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_lessequal('integer':I, Arg, Output).
brachylog_lessequal('default', Input, Output) :-
    brachylog_lessequal('integer':0, Input, Output).
brachylog_lessequal('integer':0, 'integer':I1, 'integer':I2) :-
    I1 #=< I2.
brachylog_lessequal('integer':0, 'float':I1, 'integer':I2) :-
    nonvar(I1),
    brachylog_label('default', 'integer':I2, _),
    I1 =< I2.
brachylog_lessequal('integer':0, 'integer':I1, 'float':I2) :-
    nonvar(I2),
    brachylog_label('default', 'integer':I1, _),
    I1 =< I2.
brachylog_lessequal('integer':0, 'float':I1, 'float':I2) :-
    nonvar(I1),
    nonvar(I2),
    I1 =< I2.
brachylog_lessequal('integer':1, [], []).
brachylog_lessequal('integer':1, [I], [I]).
brachylog_lessequal('integer':1, [I,J|T], [I,J|T]) :-
    brachylog_lessequal('integer':0, I, J),
    brachylog_lessequal('integer':1, [J|T], [J|T]).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_GREATEREQUAL
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_greaterequal_reversed(S, I, O) :-
    brachylog_greaterequal(S, O, I).
brachylog_greaterequal('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_greaterequal('integer':I, Arg, Output).
brachylog_greaterequal('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_greaterequal('integer':I, Arg, Output).
brachylog_greaterequal('default', Input, Output) :-
    brachylog_greaterequal('integer':0, Input, Output).
brachylog_greaterequal('integer':0, 'integer':I1, 'integer':I2) :-
    I1 #>= I2.
brachylog_greaterequal('integer':0, 'float':I1, 'integer':I2) :-
    nonvar(I1),
    brachylog_label('default', 'integer':I2, _),
    I1 >= I2.
brachylog_greaterequal('integer':0, 'integer':I1, 'float':I2) :-
    nonvar(I2),
    brachylog_label('default', 'integer':I1, _),
    I1 >= I2.
brachylog_greaterequal('integer':0, 'float':I1, 'float':I2) :-
    nonvar(I1),
    nonvar(I2),
    I1 >= I2.
brachylog_greaterequal('integer':1, [], []).
brachylog_greaterequal('integer':1, [I], [I]).
brachylog_greaterequal('integer':1, [I,J|T], [I,J|T]) :-
    brachylog_greaterequal('integer':0, I, J),
    brachylog_greaterequal('integer':1, [J|T], [J|T]).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_CONTAINS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_contains_reversed(S, I, O) :-
    brachylog_contains(S, O, I).
brachylog_contains(Sub, Input, Output) :-
    brachylog_in(Sub, Output, Input).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_IN
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_in_reversed(S, I, O) :-
    brachylog_in(S, O, I).
brachylog_in('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_in('integer':I, Arg, Output).
brachylog_in('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_in('integer':I, Arg, Output).
brachylog_in('default', 'string':L, 'string':[M]) :-
    nth0(_, L, M).
brachylog_in('integer':S, 'string':L, 'string':[M]) :-
    nth0(S, L, M).
brachylog_in('default', 'integer':0, 'integer':0).
brachylog_in('integer':0, 'integer':0, 'integer':0).
brachylog_in('default', 'integer':I, 'integer':J) :-
    H #\= 0,
    integer_value('integer':_:[H|T], I),
    nth0(_, [H|T], M),
    integer_value('integer':'positive':[M], J).
brachylog_in('integer':S, 'integer':I, 'integer':J) :-
    H #\= 0,
    integer_value('integer':_:[H|T], I),
    nth0(S, [H|T], M),
    integer_value('integer':'positive':[M], J).
brachylog_in('default', L, M) :-
    is_brachylog_list(L),
    nth0(_, L, M).
brachylog_in('integer':S, L, M) :-
    is_brachylog_list(L),
    nth0(S, L, M).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_SUPERSET
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_superset_reversed(S, I, O) :-
    brachylog_superset(S, O, I).
brachylog_superset(Sub, Input, Output) :-
    brachylog_subset(Sub, Output, Input).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_SUBSET
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_subset_reversed(S, I, O) :-
    brachylog_subset(S, O, I).
brachylog_subset('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_subset('integer':I, Arg, Output).
brachylog_subset('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_subset('integer':I, Arg, Output).
brachylog_subset('default', Input, Output) :-
    brachylog_subset('integer':0, Input, Output).
brachylog_subset('integer':0, 'string':S, 'string':T) :-
    brachylog_subset_recur(S, T).
brachylog_subset('integer':0, 'integer':0, 'integer':0).
brachylog_subset('integer':0, 'integer':I, 'integer':J) :-
    H #\= 0,
    dif(M, []),
    integer_value('integer':Sign:[H|L], I),
    brachylog_subset_recur([H|L], M),
    integer_value('integer':Sign:M, J).
brachylog_subset('integer':0, 'float':F, 'float':G) :-
    Sign is abs(F)/F,
    AF is abs(F),
    number_chars(AF, C),
    brachylog_subset_recur(C, D),
    dif(D, []),
    \+ (D = ['.'|_] ; reverse(D, ['.'|_])),
    number_chars(AG,D),
    G is Sign*AG.
brachylog_subset('integer':0, L, S) :-
    is_brachylog_list(L),
    brachylog_subset_recur(L, S).

brachylog_subset_recur(L, S) :-
    var(S),
    length(L, Length),
    between(0, Length, I),
    J #= Length - I,
    length(S, J),
    brachylog_subset_recur_(L, S).
brachylog_subset_recur(L, S) :-
    nonvar(S),
    length(S, Length),
    I #>= Length,
    length(L, I),
    brachylog_subset_recur_(L, S).

brachylog_subset_recur_([], []).
brachylog_subset_recur_([H|T], [H|T2]) :-
    brachylog_subset_recur_(T, T2).
brachylog_subset_recur_([_|T], T2) :-
    brachylog_subset_recur_(T, T2).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_REVERSE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_reverse_reversed(S, I, O) :-
    brachylog_reverse(S, O, I).
brachylog_reverse('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_reverse('integer':I, Arg, Output).
brachylog_reverse('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_reverse('integer':I, Arg, Output).
brachylog_reverse('default', Input, Output) :-
    brachylog_reverse('integer':0, Input, Output).
brachylog_reverse('integer':0, 'string':S, 'string':R) :-
    reverse(S, R).
brachylog_reverse('integer':0, 'integer':I, 'integer':R) :-
    nonvar(I),
    H #\= 0,
    A #\= 0,
    integer_value('integer':Sign:[H|T], I),
    reverse([H|T], B),
    append(Zeroes, [A|Rest], B),
    maplist(=(0), Zeroes),
    integer_value('integer':Sign:[A|Rest], R).
brachylog_reverse('integer':0, 'integer':0, 'integer':0).
brachylog_reverse('integer':0, 'integer':I, 'integer':R) :-
    var(I),
    H #\= 0,
    A #\= 0,
    integer_value('integer':Sign:[A|B], R),
    reverse(L, [A|B]),
    append(Zeroes, [H|T], L),
    maplist(=(0), Zeroes),
    integer_value('integer':Sign:[H|T], I).
brachylog_reverse('integer':0, List, R) :-
    reverse(List, R).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_CALL_PREDICATE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_call_predicate_reversed(S, I, O) :-
    brachylog_call_predicate(S, O, I).
brachylog_call_predicate('first'-GlobalVariables, ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_call_predicate(GlobalVariables, 'integer':I, Arg, Output).
brachylog_call_predicate('last'-GlobalVariables, Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_call_predicate(GlobalVariables, 'integer':I, Arg, Output).
brachylog_call_predicate(Name-GlobalVariables, Arg, Output) :-
    brachylog_call_predicate(GlobalVariables, Name, Arg, Output).
brachylog_call_predicate(GlobalVariables, CallingPredName, Input, Output) :-
    atom(CallingPredName),
    call(CallingPredName, GlobalVariables, 'integer':0, Input, Output).
brachylog_call_predicate(GlobalVariables, 'integer':I, Input, Output) :-
    (   I = 0,
        PredName = 'brachylog_main'
    ;   I #> 0,
        atomic_list_concat(['brachylog_predicate_',I], PredName)
    ),
    call(PredName, GlobalVariables, 'integer':0, Input, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_CIRCULAR_PERMUTE_COUNTERCLOCKWISE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_circular_permute_counterclockwise_reversed(S, I, O) :-
    brachylog_circular_permute_counterclockwise(S, O, I).
brachylog_circular_permute_counterclockwise('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_circular_permute_counterclockwise('integer':I, Arg, Output).
brachylog_circular_permute_counterclockwise('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_circular_permute_counterclockwise('integer':I, Arg, Output).
brachylog_circular_permute_counterclockwise('default', Input, Output) :-
    brachylog_circular_permute_counterclockwise('integer':1, Input, Output).
brachylog_circular_permute_counterclockwise('integer':0, Input, Input).
brachylog_circular_permute_counterclockwise('integer':1, 'string':[], 'string':[]).
brachylog_circular_permute_counterclockwise('integer':1, 'string':[H|T], 'string':S) :-
    append(T, [H], S).
brachylog_circular_permute_counterclockwise('integer':1, [], []).
brachylog_circular_permute_counterclockwise('integer':1, [H|T], S) :-
    append(T, [H], S).
brachylog_circular_permute_counterclockwise('integer':1, 'integer':0, 'integer':0).
brachylog_circular_permute_counterclockwise('integer':1, 'integer':I, 'integer':J) :-
    dif(H, 0),
    integer_value('integer':Sign:[H|T], I),
    append(T, [H], S),
    integer_value('integer':Sign:S, J).
brachylog_circular_permute_counterclockwise('integer':I, Input, Output) :-
    I #> 1,
    brachylog_meta_iterate(ignore, 'integer':I, brachylog_circular_permute_counterclockwise, 'default', Input, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_CIRCULAR_PERMUTE_CLOCKWISE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_circular_permute_clockwise_reversed(S, I, O) :-
    brachylog_circular_permute_clockwise(S, O, I).
brachylog_circular_permute_clockwise('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_circular_permute_clockwise('integer':I, Arg, Output).
brachylog_circular_permute_clockwise('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_circular_permute_clockwise('integer':I, Arg, Output).
brachylog_circular_permute_clockwise('default', Input, Output) :-
    brachylog_circular_permute_clockwise('integer':1, Input, Output).
brachylog_circular_permute_clockwise('integer':0, Input, Input).
brachylog_circular_permute_clockwise('integer':1, 'string':[], 'string':[]).
brachylog_circular_permute_clockwise('integer':1, 'string':L, 'string':S) :-
    append(T, [H], L),
    S = [H|T].
brachylog_circular_permute_clockwise('integer':1, [], []).
brachylog_circular_permute_clockwise('integer':1, [A|B], S) :-
    append(T, [H], [A|B]),
    S = [H|T].
brachylog_circular_permute_clockwise('integer':1, 'integer':0, 'integer':0).
brachylog_circular_permute_clockwise('integer':1, 'integer':I, 'integer':J) :-
    dif(H2, 0),
    integer_value('integer':Sign:[H2|T2], I),
    append(T, [H], [H2|T2]),
    S = [H|T],
    integer_value('integer':Sign:S, J).
brachylog_circular_permute_clockwise('integer':I, Input, Output) :-
    I #> 1,
    brachylog_meta_iterate(ignore, 'integer':I, brachylog_circular_permute_clockwise, 'default', Input, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_ROOT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_root_reversed(S, I, O) :-
    brachylog_root(S, O, I).
brachylog_root('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_root('integer':I, Arg, Output).
brachylog_root('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_root('integer':I, Arg, Output).
brachylog_root('default', Input, Output) :-
    brachylog_root('integer':2, Input, Output).
brachylog_root('integer':I,'integer':E, Type:R) :-
    (   E #= R^I ->
        Type = 'integer'
    ;   brachylog_label('default', ['integer':I, 'integer':E], _),
        R is E^(1/I),
        Type = 'float'
    ).
brachylog_root('integer':I,'float':E, 'float':R) :-
    nonvar(E),
    brachylog_label('default', 'integer':I, _),
    R is E^(1/I).
brachylog_root('float':I,'integer':E, 'float':R) :-
    brachylog_label('default', 'integer':E, _),
    R is E^(1/I).
brachylog_root('float':I,'float':E, 'float':R) :-
    nonvar(E),
    R is E^(1/I).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_CEIL
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_ceil_reversed(S, I, O) :-
    brachylog_ceil(S, O, I).
brachylog_ceil('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_ceil('integer':I, Arg, Output).
brachylog_ceil('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_ceil('integer':I, Arg, Output).
brachylog_ceil('default', Input, Output) :-
    brachylog_ceil('integer':0, Input, Output).
brachylog_ceil('integer':0, [H|T], Output) :-
    foldl(scompare(@>), [H|T], H, Output).
brachylog_ceil('integer':1, 'integer':I, 'integer':I).
brachylog_ceil('integer':1, 'float':I, 'integer':J) :-
    nonvar(I),
    J is ceil(I).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_FLOOR
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_floor_reversed(S, I, O) :-
    brachylog_floor(S, O, I).
brachylog_floor('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_floor('integer':I, Arg, Output).
brachylog_floor('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_floor('integer':I, Arg, Output).
brachylog_floor('default', Input, Output) :-
    brachylog_floor('integer':0, Input, Output).
brachylog_floor('integer':0, [H|T], Output) :-
    foldl(scompare(@<), [H|T], H, Output).
brachylog_floor('integer':1, 'integer':I, 'integer':I).
brachylog_floor('integer':1, 'float':I, 'integer':J) :-
    nonvar(I),
    J is floor(I).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_RANGE_ASCENDING
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_range_ascending_reversed(S, I, O) :-
    brachylog_range_ascending(S, O, I).
brachylog_range_ascending('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_range_ascending('integer':I, Arg, Output).
brachylog_range_ascending('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_range_ascending('integer':I, Arg, Output).
brachylog_range_ascending('default', Input, Output) :-
    brachylog_range_ascending('integer':0, Input, Output).
brachylog_range_ascending('integer':0, 'integer':Input, Output) :-
    (   0 #=< Input,
        brachylog_range_ascending_(0, Input, Output)
    ;   0 #> Input,
        brachylog_range_ascending_(Input, 0, Output)
    ).
brachylog_range_ascending('integer':1, 'integer':Input, Output) :-
    (   1 #=< Input,
        brachylog_range_ascending_(1, Input, Output)
    ;   1 #> Input,
        brachylog_range_ascending_(Input, 1, Output)
    ).
brachylog_range_ascending('integer':2, ['integer':X,'integer':Y], Output) :-
    (   X #=< Y,
        brachylog_range_ascending_(X, Y, Output)
    ;   X #> Y,
        brachylog_range_ascending_(Y, X, Output)
    ).
brachylog_range_ascending('integer':3, ['integer':X,'integer':Y], Output) :-
    (   X #=< Y,
        Y2 #= Y - 1,
        brachylog_range_ascending_(X, Y2, Output)
    ;   X #> Y,
        X2 #= X - 1,
        brachylog_range_ascending_(Y, X2, Output)
    ).
brachylog_range_ascending('integer':4, ['integer':X,'integer':Y], Output) :-
    (   X #=< Y,
        X2 #= X + 1,
        brachylog_range_ascending_(X2, Y, Output)
    ;   X #> Y,
        Y2 #= Y + 1,
        brachylog_range_ascending_(Y2, X, Output)
    ).
brachylog_range_ascending('integer':5, 'integer':Input, Output) :-
    (   0 #=< Input,
        I2 #= Input - 1,
        brachylog_range_ascending_(0, I2, Output)
    ;   0 #> Input,
        I2 #= Input + 1,
        brachylog_range_ascending_(I2, 0, Output)
    ).
brachylog_range_ascending('integer':6, 'integer':Input, Output) :-
    (   1 #=< Input,
        I2 #= Input - 1,
        brachylog_range_ascending_(1, I2, Output)
    ;   1 #> Input,
        I2 #= Input + 1,
        brachylog_range_ascending_(I2, 1, Output)
    ).

brachylog_range_ascending_(I, S, ['integer':I|R]) :-
    I #=< S,
    if_(I = S,
        R = [],
        (   J #= I + 1,
            predicates:brachylog_range_ascending_(J, S, R)
        )
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_RANGE_DESCENDING
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_range_descending_reversed(S, I, O) :-
    brachylog_range_descending(S, O, I).
brachylog_range_descending('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_range_descending('integer':I, Arg, Output).
brachylog_range_descending('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_range_descending('integer':I, Arg, Output).
brachylog_range_descending('default', Input, Output) :-
    brachylog_range_descending('integer':0, Input, Output).
brachylog_range_descending('integer':Sub, Input, Output) :-
    brachylog_range_ascending('integer':Sub, Input, ROutput),
    brachylog_reverse('default', ROutput, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_NATURAL_INTEGER
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_natural_integer_reversed(S, I, O) :-
    brachylog_natural_integer(S, O, I).
brachylog_natural_integer('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_natural_integer('integer':I, Arg, Output).
brachylog_natural_integer('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_natural_integer('integer':I, Arg, Output).
brachylog_natural_integer('default', Input, Output) :-
    brachylog_natural_integer('integer':0, Input, Output).
brachylog_natural_integer('integer':I, 'integer':Input, 'integer':Input) :-
    I #>= 0,
    Input #>= I.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_INTEGER
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_integer_reversed(S, I, O) :-
    brachylog_integer(S, O, I).
brachylog_integer('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_integer('integer':I, Arg, Output).
brachylog_integer('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_integer('integer':I, Arg, Output).
brachylog_integer('default', 'integer':Input, 'integer':Input) :-
    Input in inf..sup.
brachylog_integer('integer':I, 'integer':Input, 'integer':Input) :-
    I #>= 0,
    Input #=< -I.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_FLOAT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_float_reversed(S, I, O) :-
    brachylog_float(S, O, I).
brachylog_float('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_float('integer':I, Arg, Output).
brachylog_float('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_float('integer':I, Arg, Output).
brachylog_float('default', 'float':Input, 'float':Input).
brachylog_float('integer':_, 'integer':Input, 'float':Input).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_DIFFERENT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_different_reversed(S, I, O) :-
    brachylog_different(S, O, I).
brachylog_different('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_different('integer':I, Arg, Output).
brachylog_different('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_different('integer':I, Arg, Output).
brachylog_different('default', 'string':S, 'string':S) :-
    brachylog_different_(S).
brachylog_different('default', [], []).
brachylog_different('default', [H|T], [H|T]) :-
    (   maplist(prepend_integer, L, [H|T]),
        all_different(L)                        % More efficient on integers
    ;   maplist(prepend_string, _, [H|T]),
        brachylog_different_([H|T])
    ;   maplist(is_brachylog_list, [H|T]),
        brachylog_different_([H|T])
    ).
brachylog_different('default', 'integer':I, 'integer':I) :-
    (   integer_value('integer':_:[_], I) ->
        true
    ;   H #\= 0,
        integer_value('integer':_:[H,H2|T], I),
        all_different([H,H2|T])
    ).

brachylog_different_([]).
brachylog_different_([H|T]) :-
    maplist(dif(H), T),
    brachylog_different_(T).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_IDENTITY
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_identity_reversed(S, I, O) :-
    brachylog_identity(S, O, I).
brachylog_identity(_, Input, Input).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_INTEGER_DIVISION
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_integer_division_reversed(S, I, O) :-
    brachylog_integer_division(S, O, I).
brachylog_integer_division('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_integer_division('integer':I, Arg, Output).
brachylog_integer_division('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_integer_division('integer':I, Arg, Output).
brachylog_integer_division('default', Input, Output) :-
    brachylog_integer_division('integer':0, Input, Output).
brachylog_integer_division('integer':0, [], 'integer':1).
brachylog_integer_division('integer':0, ['integer':I1,'integer':I2], 'integer':Division) :-
    brachylog_label('default', ['integer':I1,'integer':I2], _),
    Division #= I1 // I2.
brachylog_integer_division('integer':0, ['float':I1,'integer':I2], 'integer':Division) :-
    brachylog_label('default', 'integer':I2, _),
    nonvar(I1),
    D is I1 / I2,
    brachylog_floor('integer':1, 'float':D, 'integer':Division).
brachylog_integer_division('integer':0, ['integer':I1,'float':I2], 'integer':Division) :-
    brachylog_label('default', 'integer':I1, _),
    nonvar(I2),
    D is I1 / I2,
    brachylog_floor('integer':1, 'float':D, 'integer':Division).
brachylog_integer_division('integer':0, ['float':I1,'float':I2], 'integer':Division) :-
    nonvar(I1),
    nonvar(I2),
    D is I1 / I2,
    brachylog_floor('integer':1, 'float':D, 'integer':Division).
brachylog_integer_division('integer':I, Input, Output) :-
    I #> 0,
    brachylog_integer_division('integer':0, [Input,'integer':I], Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_MULTIPLY
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_multiply_reversed(S, I, O) :-
    brachylog_multiply(S, O, I).
brachylog_multiply('first', ['integer':A,'integer':B], Output) :-
    brachylog_multiply('integer':A, 'integer':B, Output).
brachylog_multiply('last', ['integer':A,'integer':B], Output) :-
    brachylog_multiply('integer':B, 'integer':A, Output).
brachylog_multiply('integer':S, 'integer':I, 'integer':J) :-
    J #= S*I.
brachylog_multiply('integer':S, 'float':F, 'float':G) :-
    nonvar(F),
    G is S*F.
brachylog_multiply('default', [], 'integer':1).
brachylog_multiply('default', [TypeI:I|T], TypeS:Product) :-
    (   TypeI = 'integer',
        TypeF = 'integer',
        (   var(I) ->
            I #> 0,
            F #> 0
        ;   true
        ),
        Product #= I * F,
        TypeS = 'integer',
        brachylog_multiply('default', T, TypeF:F)
    ;   TypeS = 'float',
        brachylog_multiply('default', T, TypeF:F),
        (   TypeF = 'float',
            TypeI = 'integer',
            brachylog_label('default', 'integer':I, _)
        ;   TypeI = 'float',
            nonvar(I),
            TypeF = 'integer',
            brachylog_label('default', 'integer':F, _)
        ;   TypeF = 'float',
            TypeI = 'float',
            nonvar(I)
        ),
        Product is I * F
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_MODULO
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_modulo_reversed(S, I, O) :-
    brachylog_modulo(S, O, I).
brachylog_modulo('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_modulo('integer':I, Arg, Output).
brachylog_modulo('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_modulo('integer':I, Arg, Output).
brachylog_modulo('default', Input, Output) :-
    brachylog_modulo('integer':0, Input, Output).
brachylog_modulo('integer':0, ['integer':I1,'integer':I2], 'integer':Rem) :-
    Rem #= I1 mod I2.
brachylog_modulo('integer':I, 'integer':I1, 'integer':Rem) :-
    I #> 0,
    Rem #= I1 mod I.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_EXP
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_exp_reversed(S, I, O) :-
    brachylog_exp(S, O, I).
brachylog_exp('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_exp('integer':I, Arg, Output).
brachylog_exp('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_exp('integer':I, Arg, Output).
brachylog_exp('default', Input, Output) :-
    brachylog_exp('integer':0, Input, Output).
brachylog_exp('integer':0, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is exp(I).
brachylog_exp('integer':0, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is exp(F).
brachylog_exp('integer':1, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is log(I).
brachylog_exp('integer':1, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is log(F).
brachylog_exp('integer':2, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is cos(I).
brachylog_exp('integer':2, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is cos(F).
brachylog_exp('integer':3, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is sin(I).
brachylog_exp('integer':3, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is sin(F).
brachylog_exp('integer':4, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is tan(I).
brachylog_exp('integer':4, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is tan(F).
brachylog_exp('integer':5, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is acos(I).
brachylog_exp('integer':5, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is acos(F).
brachylog_exp('integer':6, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is asin(I).
brachylog_exp('integer':6, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is asin(F).
brachylog_exp('integer':7, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is atan(I).
brachylog_exp('integer':7, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is atan(F).
brachylog_exp('integer':8, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is cosh(I).
brachylog_exp('integer':8, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is cosh(F).
brachylog_exp('integer':9, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is sinh(I).
brachylog_exp('integer':9, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is sinh(F).
brachylog_exp('integer':10, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is tanh(I).
brachylog_exp('integer':10, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is tanh(F).
brachylog_exp('integer':11, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is acosh(I).
brachylog_exp('integer':11, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is acosh(F).
brachylog_exp('integer':12, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is asinh(I).
brachylog_exp('integer':12, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is asinh(F).
brachylog_exp('integer':13, 'integer':I, 'float':Exp) :-
    brachylog_label('default', 'integer':I, _),
    Exp is atanh(I).
brachylog_exp('integer':13, 'float':F, 'float':Exp) :-
    nonvar(F),
    Exp is atanh(F).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_PLUS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_plus_reversed(S, I, O) :-
    brachylog_plus(S, O, I).
brachylog_plus('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_plus('integer':I, Arg, Output).
brachylog_plus('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_plus('integer':I, Arg, Output).
brachylog_plus('default', Input, Output) :-
    brachylog_plus('integer':0, Input, Output).
brachylog_plus('integer':I, 'integer':Input, 'integer':Output) :-
    I #> 0,
    Output #= Input + I.
brachylog_plus('integer':I, 'float':Input, 'float':Output) :-
    I #> 0,
    nonvar(Input),
    Output is Input + I.
brachylog_plus('integer':0, [], 'integer':0).
brachylog_plus('integer':0, [TypeI:I|T], TypeS:Sum) :-
    brachylog_plus('integer':0, T, TypeF:F),
    (   TypeI = 'integer',
        TypeF = 'integer',
        Sum #= I + F,
        TypeS = 'integer'
    ;   TypeS = 'float',
        (   TypeF = 'float',
            TypeI = 'integer',
            brachylog_label('default', 'integer':I, _)
        ;   TypeI = 'float',
            nonvar(I),
            TypeF = 'integer',
            brachylog_label('default', 'integer':F, _)
        ;   TypeF = 'float',
            TypeI = 'float',
            nonvar(I)
        ),
        Sum is I + F
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_MINUS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_minus_reversed(S, I, O) :-
    brachylog_minus(S, O, I).
brachylog_minus('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_minus('integer':I, Arg, Output).
brachylog_minus('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_minus('integer':I, Arg, Output).
brachylog_minus('default', Input, Output) :-
    brachylog_minus('integer':0, Input, Output).
brachylog_minus('integer':I, 'integer':Input, 'integer':Output) :-
    I #> 0,
    Output #= Input - I.
brachylog_minus('integer':I, 'float':Input, 'float':Output) :-
    I #> 0,
    nonvar(Input),
    Output is Input - I.
brachylog_minus('integer':0, [], 'integer':0).
brachylog_minus('integer':0, [TypeI:I|T], TypeS:Sum) :-
    brachylog_minus('integer':0, T, TypeF:F),
    (   TypeI = 'integer',
        TypeF = 'integer',
        Sum #= I - F,
        TypeS = 'integer'
    ;   TypeS = 'float',
        (   TypeF = 'float',
            TypeI = 'integer',
            brachylog_label('default', 'integer':I, _)
        ;   TypeI = 'float',
            nonvar(I),
            TypeF = 'integer',
            brachylog_label('default', 'integer':F, _)
        ;   TypeF = 'float',
            TypeI = 'float',
            nonvar(I)
        ),
        Sum is I - F
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_DIVIDE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_divide_reversed(S, I, O) :-
    brachylog_divide(S, O, I).
brachylog_divide('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_divide('integer':I, Arg, Output).
brachylog_divide('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_divide('integer':I, Arg, Output).
brachylog_divide('default', [], 'integer':1).
brachylog_divide('default', ['integer':I1,'integer':I2], Type:Division) :-
    brachylog_label('default', ['integer':I1,'integer':I2], _),
    I2 #\= 0,
    Division is I1 / I2,
    (   integer(Division) ->
        Type = 'integer'
    ;   Type = 'float'
    ).
brachylog_divide('default', ['integer':0, 'integer':0], 'integer':_).
brachylog_divide('default', ['float':I1,'integer':I2], 'float':Division) :-
    brachylog_label('default', 'integer':I2, _),
    I2 #\= 0,
    nonvar(I1),
    Division is I1 / I2.
brachylog_divide('default', ['float':0.0, 'integer':0], 'float':_).
brachylog_divide('default', ['integer':I1,'float':I2], 'float':Division) :-
    brachylog_label('default', 'integer':I1, _),
    nonvar(I2),
    dif(I2, 0.0),
    Division is I1 / I2.
brachylog_divide('default', ['integer':0, 'float':0.0], 'float':_).
brachylog_divide('default', ['float':I1,'float':I2], 'float':Division) :-
    nonvar(I1),
    nonvar(I2),
    dif(I2, 0.0),
    Division is I1 / I2.
brachylog_divide('default', ['float':0.0, 'float':0.0], 'float':_).
brachylog_divide('integer':1, 'integer':I, 'float':J) :-
    brachylog_label('default', 'integer':I, _),
    J is 1/I.
brachylog_divide('integer':1, 'float':I, 'float':J) :-
    J is 1/I.
brachylog_divide('integer':I, Input, Output) :-
    I #> 1,
    brachylog_divide('default', [Input,'integer':I], Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_LESS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_less_reversed(S, I, O) :-
    brachylog_less(S, O, I).
brachylog_less('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_less('integer':I, Arg, Output).
brachylog_less('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_less('integer':I, Arg, Output).
brachylog_less('default', Input, Output) :-
    brachylog_less('integer':0, Input, Output).
brachylog_less('integer':0, 'integer':I1, 'integer':I2) :-
    I1 #< I2.
brachylog_less('integer':0, 'float':I1, 'integer':I2) :-
    nonvar(I1),
    brachylog_label('default', 'integer':I2, _),
    I1 < I2.
brachylog_less('integer':0, 'integer':I1, 'float':I2) :-
    nonvar(I2),
    brachylog_label('default', 'integer':I1, _),
    I1 < I2.
brachylog_less('integer':0, 'float':I1, 'float':I2) :-
    nonvar(I1),
    nonvar(I2),
    I1 < I2.
brachylog_less('integer':1, [], []).
brachylog_less('integer':1, [I], [I]).
brachylog_less('integer':1, [I,J|T], [I,J|T]) :-
    brachylog_less('integer':0, I, J),
    brachylog_less('integer':1, [J|T], [J|T]).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_EQUAL
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_equal_reversed(S, I, O) :-
    brachylog_equal(S, O, I).
brachylog_equal('first', [I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_equal(I, Arg, Output).
brachylog_equal('last', Input, Output) :-
    reverse(Input, [I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_equal(I, Arg, Output).
brachylog_equal('default', [], []).
brachylog_equal('default', [H|T], [H|T]) :-
    maplist(=(H), T).
brachylog_equal('default', 'string':L, 'string':L) :-
    brachylog_equal('integer':0, L, L).
brachylog_equal('default', 'integer':0, 'integer':0).
brachylog_equal('default', 'integer':I, 'integer':I) :-
    H #\= 0,
    integer_value('integer':_:[H|T], I),
    brachylog_equal('default', [H|T], [H|T]).
brachylog_equal(Type:I, [Type:I|T], [Type:I|T]) :-
    brachylog_equal('default', [Type:I|T], [Type:I|T]).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_GREATER
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_greater_reversed(S, I, O) :-
    brachylog_greater(S, O, I).
brachylog_greater('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_greater('integer':I, Arg, Output).
brachylog_greater('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_greater('integer':I, Arg, Output).
brachylog_greater('default', Input, Output) :-
    brachylog_greater('integer':0, Input, Output).
brachylog_greater('integer':0, 'integer':I1, 'integer':I2) :-
    I1 #> I2.
brachylog_greater('integer':0, 'float':I1, 'integer':I2) :-
    nonvar(I1),
    brachylog_label('default', 'integer':I2, _),
    I1 > I2.
brachylog_greater('integer':0, 'integer':I1, 'float':I2) :-
    nonvar(I2),
    brachylog_label('default', 'integer':I1, _),
    I1 > I2.
brachylog_greater('integer':0, 'float':I1, 'float':I2) :-
    nonvar(I1),
    nonvar(I2),
    I1 > I2.
brachylog_greater('integer':1, [], []).
brachylog_greater('integer':1, [I], [I]).
brachylog_greater('integer':1, [I,J|T], [I,J|T]) :-
    brachylog_greater('integer':0, I, J),
    brachylog_greater('integer':1, [J|T], [J|T]).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_TRANSPOSE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_transpose_reversed(S, I, O) :-
    brachylog_transpose(S, O, I).
brachylog_transpose('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_transpose('integer':I, Arg, Output).
brachylog_transpose('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_transpose('integer':I, Arg, Output).
brachylog_transpose('default', Input, Output) :-
    brachylog_transpose('integer':0, Input, Output).
brachylog_transpose('integer':0, 'string':Input, 'string':Output) :-
    brachylog_split_lines('default', Input, Ls),
    brachylog_elements('default', Ls, LLs),
    brachylog_transpose('integer':0, LLs, CCs),
    maplist(brachylog_concatenate('default'), CCs, Cs),
    brachylog_split_lines('default', Output, Cs).
brachylog_transpose('integer':0, Input, Output) :-
    is_brachylog_list(Input),
    member(X, Input),
    is_list(X),
    !,
    maplist(is_brachylog_list, Input),
    length(X, LX),
    length(Input, LI),
    brachylog_juxtapose('integer':LI, ['integer':LX], Lengths),
    maplist(brachylog_length('default'), Input, Lengths),
    brachylog_equal('default', Lengths, _),
    transpose(Input, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_POWER
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_power_reversed(S, I, O) :-
    brachylog_power(S, O, I).
brachylog_power('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_power('integer':I, Arg, Output).
brachylog_power('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_power('integer':I, Arg, Output).
brachylog_power('default', [], 'integer':1).
brachylog_power('default', ['integer':I1,'integer':I2], 'integer':Power) :-
    Power #= I1 ^ I2.
brachylog_power('default', ['float':I1,'integer':I2], 'float':Power) :-
    nonvar(I1),
    brachylog_label('default', 'integer':I2, _),
    Power is I1 ^ I2.
brachylog_power('default', ['integer':I1,'float':I2], 'float':Power) :-
    nonvar(I2),
    brachylog_label('default', 'integer':I1, _),
    Power is I1 ^ I2.
brachylog_power('default', ['float':I1,'float':I2], 'float':Power) :-
    nonvar(I1),
    nonvar(I2),
    Power is I1 ^ I2.
brachylog_power('integer':S, 'integer':I, 'integer':J) :-
    J #= I^S.
brachylog_power('integer':S, 'float':I, 'float':J) :-
    nonvar(I),
    brachylog_label('default', 'integer':S, _),
    J is I^S.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_ADFIX
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_adfix_reversed(S, I, O) :-
    brachylog_adfix(S, O, I).
brachylog_adfix('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_adfix('integer':I, Arg, Output).
brachylog_adfix('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_adfix('integer':I, Arg, Output).
brachylog_adfix('default', Input, Output) :-
    (   brachylog_adfix('integer':0, Input, Output)
    ;   brachylog_adfix('integer':1, Input, Output)
    ).
brachylog_adfix('integer':0, [], []).
brachylog_adfix('integer':0, [H|T], [H2|T2]) :-
    (   brachylog_concatenate('default', [[H2|T2],[_|_]], [H|T])
    ;   [H2|T2] = [H|T]
    ).
brachylog_adfix('integer':0, 'string':S, 'string':P) :-
    brachylog_adfix('integer':0, S, P).
brachylog_adfix('integer':0, 'integer':0, 'integer':0).
brachylog_adfix('integer':0, 'integer':I, 'integer':P) :-
    H #\= 0,
    H2 #\= 0,
    abs(P) #=< abs(I),
    integer_value('integer':Sign:[H|T], I),
    integer_value('integer':Sign:[H2|T2], P),
    brachylog_adfix('integer':0, [H|T], [H2|T2]).
brachylog_adfix('integer':1, [], []).
brachylog_adfix('integer':1, [H|T], [H2|T2]) :-
    brachylog_concatenate('default', [_,[H2|T2]], [H|T]).
brachylog_adfix('integer':1, 'string':S, 'string':P) :-
    brachylog_adfix('integer':1, S, P).
brachylog_adfix('integer':1, 'integer':0, 'integer':0).
brachylog_adfix('integer':1, 'integer':I, 'integer':P) :-
    H #\= 0,
    H2 #\= 0,
    abs(P) #=< abs(I),
    integer_value('integer':Sign:[H|T], I),
    integer_value('integer':Sign:[H2|T2], P),
    brachylog_adfix('integer':1, [H|T], [H2|T2]).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_BEHEAD
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_behead_reversed(S, I, O) :-
    brachylog_behead(S, O, I).
brachylog_behead('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_behead('integer':I, Arg, Output).
brachylog_behead('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_behead('integer':I, Arg, Output).
brachylog_behead('integer':0, Input, Input).
brachylog_behead('default', Input, Output) :-
    brachylog_behead('integer':1, Input, Output).
brachylog_behead('integer':1, 'string':[_|T], 'string':T).
brachylog_behead('integer':1, 'integer':0, 'integer':0).
brachylog_behead('integer':1, 'integer':I, 'integer':J) :-
    H #\= 0,
    integer_value('integer':Sign:[H|T], I),
    integer_value('integer':Sign:T, J).
brachylog_behead('integer':1, 'float':F,'float':G) :-
    number_codes(F,L),
    brachylog_behead_float(L,M),
    number_codes(G,M).
brachylog_behead('integer':1, [_|T],T).
brachylog_behead('integer':I, Input, Output) :-
    I #> 1,
    brachylog_meta_iterate(ignore, 'integer':I, brachylog_behead, 'integer':1, Input, Output).

brachylog_behead_float([], []).
brachylog_behead_float([48|T], [48|T2]) :-
    brachylog_behead_float(T, T2).
brachylog_behead_float([46|T], [46|T2]) :-
    brachylog_behead_float(T, T2).
brachylog_behead_float([H|T], [48|T2]) :-
    H \= 46,
    H \= 48,
    brachylog_behead_float_(T, T2).

brachylog_behead_float_([], []).
brachylog_behead_float_([H|T], [H|T2]) :-
    brachylog_behead_float_(T, T2).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_CONCATENATE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_concatenate_reversed(S, I, O) :-
    brachylog_concatenate(S, O, I).
brachylog_concatenate('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_concatenate('integer':I, Arg, Output).
brachylog_concatenate('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_concatenate('integer':I, Arg, Output).
brachylog_concatenate('default', Input, Output) :-
    brachylog_concatenate('integer':0, Input, Output).
brachylog_concatenate('integer':I, Input, Output) :-
    I #> 0,
    length(Input, I),
    brachylog_concatenate('integer':0, Input, Output).
brachylog_concatenate('integer':0, [], []).
brachylog_concatenate('integer':0, [H|T],L) :-
    var(L),
    (   maplist(is_brachylog_list, [H|T]),
        brachylog_coerce('default', L, L),
        List = L,
        ListOfLists = [H|T],
        Integers = 'no'
    ;   maplist(brachylog_concatenate_prepend_string_or_empty, ListOfLists, [H|T]),
        brachylog_coerce('integer':2, 'string':List, L),
        Integers = 'no'
    ;   maplist(brachylog_concatenate_integer_value, ListOfLists, [H|T]),
        Integers = 'yes'
    ),
    brachylog_concatenate_(ListOfLists, List),
    (   Integers = 'yes',
        List = [0],
        L = 'integer':0
    ;   Integers = 'yes',
        List = [J|TList],
        integer_value('integer':'positive':[J|TList], I),
        L = 'integer':I
    ;   Integers = 'no'
    ).
brachylog_concatenate('integer':0, [H|T], L) :-
    nonvar(L),
    brachylog_length('default', L, 'integer':Length),
    (   var(T) ->
        LengthList #> 0,
        LengthList #=< Length,
        indomain(LengthList),
        length([H|T], LengthList),
        CanContainEmpty = 'no'
    ;   CanContainEmpty = 'yes'
    ),
    (   is_brachylog_list(L),
        maplist(brachylog_coerce('default'), [H|T], [H|T]),
        List = L,
        ListOfLists = [H|T]
    ;   L = 'string':List,
        maplist(brachylog_concatenate_prepend_string_or_empty, ListOfLists, [H|T])
    ;   L = 'integer':I,
        (   I = 0,
            List = [0]
        ;   I #\= 0,
            J #\= 0,
            integer_value('integer':_:[J|TList], I),
            List = [J|TList]
        ),
        (
            CanContainEmpty = 'no' ->
            maplist(brachylog_concatenate_limit_length(1, Length), [H|T])
        ;   maplist(brachylog_concatenate_limit_length(0, Length), [H|T])
        ),
        maplist(brachylog_concatenate_integer_value, ListOfLists, [H|T])
    ),
    (
        CanContainEmpty = 'no' ->
        maplist(brachylog_concatenate_limit_length(1, Length), [H|T])
    ;   maplist(brachylog_concatenate_limit_length(0, Length), [H|T])
    ),
    brachylog_concatenate_(ListOfLists, List).

brachylog_concatenate_prepend_string_or_empty([], []).
brachylog_concatenate_prepend_string_or_empty(S, 'string':S).

brachylog_concatenate_limit_length(Min, Max, H) :-
    Length #>= Min,
    Length #=< Max,
    indomain(Length),
    brachylog_length('default', H, 'integer':Length).

brachylog_concatenate_integer_value([0], 'integer':0).
brachylog_concatenate_integer_value([], []).
brachylog_concatenate_integer_value([H|T], 'integer':I) :-
    H #\= 0,
    integer_value('integer':_:[H|T], I).

brachylog_concatenate_([L|T], L2) :-
    is_brachylog_list(L2),
    append([L|T], L2).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_DUPLICATES
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_duplicates_reversed(S, I, O) :-
    brachylog_duplicates(S, O, I).
brachylog_duplicates('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_duplicates('integer':I, Arg, Output).
brachylog_duplicates('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_duplicates('integer':I, Arg, Output).
brachylog_duplicates('default', Input, Output) :-
    brachylog_duplicates('integer':0, Input, Output).
brachylog_duplicates('integer':0, 'string':S, 'string':T) :-
    list_to_set(S, T).
brachylog_duplicates('integer':0, 'integer':I, 'integer':J) :-
    brachylog_label('default', 'integer':I, 'integer':I),
    number_codes(I, C),
    list_to_set(C, S),
    number_codes(J, S).
brachylog_duplicates('integer':0, 'float':F, 'float':G) :-
    number_codes(F, C),
    list_to_set(C, S),
    number_codes(G, S).
brachylog_duplicates('integer':0, L, M) :-
    is_brachylog_list(L),
    list_to_set(L, M).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_FACTORS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_factors_reversed(S, I, O) :-
    brachylog_factors(S, O, I).
brachylog_factors('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_factors('integer':I, Arg, Output).
brachylog_factors('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_factors('integer':I, Arg, Output).
brachylog_factors('default', 'integer':N, Z) :-
    brachylog_label('default', 'integer':N, _),
    (   N = 0 ,
        Z = []
    ;   N #> 0,
        findall('integer':X, (X #>= 1, X #=< N, I #>= 1, I #=< N, N #= X*I, indomain(X)), Z)
    ;   N #< 0,
        findall('integer':X, (X #=< -1, X #>= N, I #>= 1, I #=< abs(N), N #= X*I, labeling([max(X)], [X])), Z)
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_GROUP
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_group_reversed(S, I, O) :-
    brachylog_group(S, O, I).
brachylog_group('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_group('integer':I, Arg, Output).
brachylog_group('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_group('integer':I, Arg, Output).
brachylog_group('integer':0, Input, Input).
brachylog_group('default', Input, Output) :-
    brachylog_group('integer':1, Input, Output).
brachylog_group('integer':1, X, [X]).
brachylog_group('integer':I, Input, Output) :-
    I #> 1,
    brachylog_meta_iterate(ignore, 'integer':I, brachylog_group, 'integer':1, Input, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_HEAD
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_head_reversed(S, I, O) :-
    brachylog_head(S, O, I).
brachylog_head('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_head('integer':I, Arg, Output).
brachylog_head('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_head('integer':I, Arg, Output).
brachylog_head('integer':0, _, []).
brachylog_head('default', 'string':[H|_], 'string':[H]).
brachylog_head('default', 'integer':0, 'integer':0).
brachylog_head('default', 'integer':I, 'integer':J) :-
    J #\= 0,
    integer_value('integer':_:[J|_], I).
brachylog_head('default', 'float':F, 'integer':I) :-
    number_codes(F,L),
    brachylog_head_float(L, 'integer':I).
brachylog_head('default', [H|_], H).
brachylog_head('integer':I, Input, Output) :-
    I #> 0,
    brachylog_length('default', Output, 'integer':I),
    once(brachylog_concatenate('default', [Output, _], Input)).

brachylog_head_float([H|T], 'integer':I) :-
    (   (   H = 48
        ;   H = 46
        ) ->
        (   T = [],
            I = 0
        ;   T \= [],
            brachylog_head_float(T, 'integer':I)
        )
    ;   H \= 48,
        H \= 46,
        number_codes(I, [H])
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_INDEX
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_index_reversed(S, I, O) :-
    brachylog_index(S, I, O).
brachylog_index('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_index('integer':I, Arg, Output).
brachylog_index('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_index('integer':I, Arg, Output).
brachylog_index('default', Input, [E,'integer':I]) :-
    brachylog_in('integer':I, Input, E).
brachylog_index('integer':1, Input, [E,'integer':J]) :-
    J #= I + 1,
    brachylog_in('integer':I, Input, E).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_JUXTAPOSE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_juxtapose_reversed(S, I, O) :-
    brachylog_juxtapose(S, O, I).
brachylog_juxtapose('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_juxtapose('integer':I, Arg, Output).
brachylog_juxtapose('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_juxtapose('integer':I, Arg, Output).
brachylog_juxtapose('default', Input, Output) :-
    brachylog_juxtapose('integer':2, Input, Output).
brachylog_juxtapose('integer':_, [], []).
brachylog_juxtapose('integer':0, [_|_], []).
brachylog_juxtapose('integer':I, [H|T], Z) :-
    var(Z),
    Z = [HZ|TZ],
    I #> 0,
    length([H|T], L),
    LZ #= I*L,
    length([HZ|TZ], LZ),
    append([H|T], T2, [HZ|TZ]),
    J #= I-1,
    brachylog_juxtapose('integer':J, [H|T], T2).
brachylog_juxtapose('integer':I, [H|T], Z) :-
    nonvar(Z),
    Z = [HZ|TZ],
    I #> 0,
    length([HZ|TZ], LZ),
    LZ #= I*L,
    indomain(L),
    length([H|T], L),
    append([H|T], T2, [HZ|TZ]),
    J #= I-1,
    brachylog_juxtapose('integer':J, [H|T], T2).
brachylog_juxtapose('integer':I, 'string':S, 'string':Z) :-
    brachylog_juxtapose('integer':I, S, Z).
brachylog_juxtapose('integer':J, 'integer':I, 'integer':Z) :-
    var(Z),
    dif(H, 0),
    integer_value('integer':Sign:[H|T], I),
    brachylog_juxtapose('integer':J, [H|T], LZ),
    LZ = [HZ|_],
    dif(HZ, 0),
    integer_value('integer':Sign:LZ, Z).
brachylog_juxtapose('integer':J, 'integer':I, 'integer':Z) :-
    nonvar(Z),
    dif(H, 0),
    dif(HZ, 0),
    integer_value('integer':Sign:[HZ|TZ], Z),
    brachylog_juxtapose('integer':J, [H|T], [HZ|TZ]),
    integer_value('integer':Sign:[H|T], I).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_KNIFE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_knife_reversed(S, I, O) :-
    brachylog_knife(S, O, I).
brachylog_knife('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_knife('integer':I, Arg, Output).
brachylog_knife('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_knife('integer':I, Arg, Output).
brachylog_knife('integer':0, Input, Input).
brachylog_knife('default', Input, Output) :-
    brachylog_knife('integer':1, Input, Output).
brachylog_knife('integer':1, [_], []).
brachylog_knife('integer':1, [H,I|T], [H2|T2]) :-
    (   var(T) ->
        reverse(T3, [H2|T2]),
        reverse([H,I|T], [_|T3])
    ;   reverse([H,I|T], [_|T3]),
        reverse(T3, [H2|T2])
    ).
brachylog_knife('integer':1, 'string':S, 'string':T) :-
    brachylog_knife('integer':1, S, T).
brachylog_knife('integer':1, 'integer':I, 'integer':0) :-
    I in -9..9.
brachylog_knife('integer':1, 'integer':I, 'integer':J) :-
    H #\= 0,
    H2 #\= 0,
    abs(J) #=< abs(I),
    abs(I) #=< 10*(abs(J) + 1),
    integer_value('integer':Sign:[H|T], I),
    integer_value('integer':Sign:[H2|T2], J),
    brachylog_knife('integer':1, [H|T], [H2|T2]).
brachylog_knife('integer':I, Input, Output) :-
    I #> 1,
    brachylog_meta_iterate(ignore, 'integer':I, brachylog_knife, 'integer':1, Input, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_LENGTH
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_length_reversed(S, I, O) :-
    brachylog_length(S, O, I).
brachylog_length('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_length('integer':I, Arg, Output).
brachylog_length('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_length('integer':I, Arg, Output).
brachylog_length('integer':I, Input, Input) :-
    I #>= 0,
    brachylog_length('default', Input, 'integer':I).
brachylog_length('default', [], 'integer':0).
brachylog_length('default', [H|T], 'integer':Length) :-
    length([H|T], Length).
brachylog_length('default', 'string':S, 'integer':Length) :-
    length(S, Length).
brachylog_length('default', 'integer':0, 'integer':1).
brachylog_length('default', 'integer':I, 'integer':Length) :-
    nonvar(Length),
    (   Length = 1 ->
        I in 1..9
    ;   H #\= 0,
        abs(I) #< 10^Length,
        integer_value('integer':_:[H|T], I),
        length([H|T], Length)
    ).
brachylog_length('default', 'integer':I, 'integer':Length) :-
    var(Length),
    H #\= 0,
    Length #>= 0,
    integer_value('integer':_:[H|T], I),
    length([H|T], Length).
brachylog_length('default', 'float':F, 'integer':Length) :-
    nonvar(F),
    length(L, Length),
    catch(number_codes(F, L), E, (print_message(error, E), false)).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_ORDER
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_order_reversed(S, I, O) :-
    brachylog_order(S, O, I).
brachylog_order('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_order('integer':I, Arg, Output).
brachylog_order('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_order('integer':I, Arg, Output).
brachylog_order('default', Input, Output) :-
    brachylog_order('integer':0, Input, Output).
brachylog_order('integer':0, 'string':S, 'string':T) :-
    (   nonvar(S),
        msort(S, T)
    ;   var(S),
        msort(T, T),
        brachylog_permute('default', 'string':T, 'string':S)
    ).
brachylog_order('integer':0, 'integer':I, 'integer':J) :-
    brachylog_label('default', 'integer':I, _),
    number_codes(I, C),
    msort(C, D),
    number_codes(J, D).
brachylog_order('integer':0, [], []).
brachylog_order('integer':0, [H|T], [H2|T2]) :-
    (   nonvar(T),
        brachylog_order_mixed_sort_([H|T], [H2|T2])
    ;   var(T),
        brachylog_order_mixed_sort_([H2|T2], [H2|T2]),
        brachylog_permute('default', [H2|T2], [H|T])
    ).
brachylog_order('integer':1, Input, Output) :-
    brachylog_order('integer':0, Input, ROutput),
    brachylog_reverse('default', ROutput, Output).

% keysort sorts by the first element of a - pair, disregarding but preserving the second
brachylog_order_type_pair_(List, PairsList-list) :-
    maplist(brachylog_order_type_pair_, List, PairsList).
brachylog_order_type_pair_(Type:Value, Value-Type).
brachylog_order_mixed_sort_(List, Sorted) :-
    maplist(brachylog_order_type_pair_, List, IPairs),
    keysort(IPairs, OPairs), % keysort, like msort, doesn't remove duplicates
    maplist(brachylog_order_type_pair_, Sorted, OPairs).

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_PERMUTE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_permute_reversed(S, I, O) :-
    brachylog_permute(S, O, I).
brachylog_permute('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_permute('integer':I, Arg, Output).
brachylog_permute('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_permute('integer':I, Arg, Output).
brachylog_permute('default', Input, Output) :-
    brachylog_permute('integer':0, Input, Output).
brachylog_permute('integer':0, 'string':S, 'string':Permutation) :-
    permutation(S, Permutation).
brachylog_permute('integer':0, List, Permutation) :-
    is_brachylog_list(List),
    is_brachylog_list(Permutation),
    permutation(List, Permutation).
brachylog_permute('integer':0, 'integer':0, 'integer':0).
brachylog_permute('integer':0, 'integer':I, 'integer':J) :-
    H #\= 0,
    J #\= 0,
    integer_value('integer':Sign:[H|L], I),
    permutation([H|L], M),
    integer_value('integer':Sign:M, J).
brachylog_permute('integer':0, 'float':F, 'float':G) :-
    Sign is abs(F)/F,
    AF is abs(F),
    number_chars(AF, C),
    permutation(C, D),
    \+ ( D = ['.'|_] ; reverse(D, ['.'|_])),
    number_chars(AG, D),
    G is Sign*AG.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_SUBSTRING
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_substring_reversed(S, I, O) :-
    brachylog_substring(S, O, I).
brachylog_substring('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_substring('integer':I, Arg, Output).
brachylog_substring('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_substring('integer':I, Arg, Output).
brachylog_substring('default', 'integer':0, 'integer':0).
brachylog_substring('default', 'integer':I, 'integer':J) :-
    H #\= 0,
    integer_value('integer':Sign:[H|L], I),
    brachylog_substring_recur([H|L], [H2|L2]),
    integer_value('integer':Sign:[H2|L2], J).
brachylog_substring('default', 'string':[], 'string':[]).
brachylog_substring('default', 'string':[H|T], 'string':[H2|T2]) :-
    brachylog_substring_recur([H|T], [H2|T2]).
brachylog_substring('default', [], []).
brachylog_substring('default', [H|T], [H2|T2]) :-
    brachylog_substring_recur([H|T], [H2|T2]).
brachylog_substring('integer':I, Input, Output) :-
    brachylog_length('default', Output, 'integer':I),
    brachylog_substring('default', Input, Output).

brachylog_substring_recur([], []).
brachylog_substring_recur([H|T], [H|T2]) :-
    brachylog_substring_recur_(T, T2).
brachylog_substring_recur([_|T], T2) :-
    brachylog_substring_recur(T, T2).

brachylog_substring_recur_([], []).
brachylog_substring_recur_([H|T], [H|T2]) :-
    brachylog_substring_recur_(T, T2).
brachylog_substring_recur_([_|_], []).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_TAIL
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_tail_reversed(S, I, O) :-
    brachylog_tail(S, O, I).
brachylog_tail('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_tail('integer':I, Arg, Output).
brachylog_tail('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_tail('integer':I, Arg, Output).
brachylog_tail('integer':0, _, []).
brachylog_tail('default', 'string':L, 'string':[H]) :-
    reverse(L, [H|_]).
brachylog_tail('default', 'integer':0, 'integer':0).
brachylog_tail('default', 'integer':I, 'integer':Z) :-
    J #\= 0,
    integer_value('integer':_:[J|T], I),
    reverse([J|T], [Z|_]).
brachylog_tail('default', 'float':F, 'integer':I) :-
    number_codes(F, L),
    reverse(L, R),
    brachylog_tail_float(R, 'integer':I).
brachylog_tail('default', L, H) :-
    is_brachylog_list(L),
    reverse(L, [H|_]).
brachylog_tail('integer':I, Input, Output) :-
    I #> 0,
    brachylog_length('default', Output, 'integer':I),
    once(brachylog_concatenate('default', [_,Output], Input)).

brachylog_tail_float([H|T], 'integer':I) :-
    (   (   H = 48
        ;   H = 46
        ) ->
        (   T = [],
            I = 0
        ;   T \= [],
            brachylog_tail_float(T, 'integer':I)
        )
    ;   H \= 48,
        H \= 46,
        number_codes(I, [H])
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_WRITE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_write_reversed(S, I, O) :-
    brachylog_write(S, O, I).
brachylog_write('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_write('integer':I, Arg, Output).
brachylog_write('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_write('integer':I, Arg, Output).
brachylog_write('default', Input, Output) :-
    brachylog_write('integer':0, Input, Output).
brachylog_write('integer':Sub, I, O) :-
  S #= Sub mod 4,
  brachylog_write_('integer':S, I, O, T),
  (    Sub #< 4,                         % imperative write
       write(T)
  ;    Sub #>= 4,
       b_getval('declw', DOld),         % Declarative write
       append(DOld, [T], DNew),
       b_setval('declw', DNew)
  ).

brachylog_write_('integer':1, [List,'string':F], _, T) :-
    is_brachylog_list(List),
    atomic_list_concat(F, Format),
    maplist(brachylog_write_try_label, List),
    brachylog_prolog_variable(List, PrologList),
    format(string(T), Format, PrologList).    % Output formatted text
brachylog_write_('integer':1, Args, _, T) :-
    is_brachylog_list(Args),
    reverse(Args, ['string':F|R]),
    reverse(R, S),
    maplist(brachylog_write_try_label, S),
    brachylog_prolog_variable(S, PrologS),
    atomic_list_concat(F, Format),
    format(string(T), Format, PrologS).    % Output formatted text
brachylog_write_('integer':0, 'string':S, _, X) :-
    nonvar(S),
    atomic_list_concat(S, X).
brachylog_write_('integer':0, 'integer':I, _, I) :-
    brachylog_label('default', 'integer':I, _).
brachylog_write_('integer':0, 'float':F, _, F) :-
    nonvar(F).
brachylog_write_('integer':0, List, _, PrologList) :-
    is_brachylog_list(List),
    maplist(brachylog_write_try_label, List),
    brachylog_prolog_variable(List, PrologList).
brachylog_write_('integer':2, I, I, T) :-
    brachylog_write_('integer':0, I, _, T).
brachylog_write_('integer':3, I, I, T) :-
    brachylog_write_('integer':1, I, _, T).

brachylog_write_try_label(X) :-
    (   nonvar(X), X = 'float':_ -> true
    ;   nonvar(X), X = 'string':_ -> true
    ;   nonvar(X), X = [] -> true
    ;   nonvar(X),
        X = [H|T] ->
        maplist(brachylog_write_try_label, [H|T])
    ;   X = 'integer':I,
        brachylog_label('default', 'integer':I, _)
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_XTERMINATE

   TODO: Use sub to know what to remove instead
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_xterminate_reversed(S, I, O) :-
    brachylog_xterminate(S, O, I).
brachylog_xterminate('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_xterminate('integer':I, Arg, Output).
brachylog_xterminate('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_xterminate('integer':I, Arg, Output).
brachylog_xterminate('default', Input, Output) :-
    brachylog_xterminate('integer':0, Input, Output).
brachylog_xterminate('integer':0, [L,[]], L).
brachylog_xterminate('integer':0, ['string':S,X], 'string':T) :-
    \+ is_brachylog_list(X),
    brachylog_xterminate_(X, 'string':S, 'string':T).
brachylog_xterminate('integer':0, ['string':S,[H|T]], L3) :-
    brachylog_xterminate_(H,'string':S, L2),
    brachylog_xterminate('integer':0, [L2,T], L3).
brachylog_xterminate('integer':0, [L,H|T], L3) :-
    is_brachylog_list(L),
    \+ is_brachylog_list(H),
    brachylog_xterminate('integer':0, [L,[H|T]], L3).
brachylog_xterminate('integer':0, [L,[H|T]], L3) :-
    is_brachylog_list(L),
    delete(L, H, L2),
    brachylog_xterminate('integer':0, [L2,T], L3).

brachylog_xterminate_(X, 'string':S, 'string':T) :-
    brachylog_xterminate_single(X, 'string':S, 'string':T).
brachylog_xterminate_(_, [], []).
brachylog_xterminate_(X, [H|T], [H2|T2]) :-
    brachylog_xterminate_single(X, H, H2),
    brachylog_xterminate_(X, T, T2).

brachylog_xterminate_single('string':L, 'string':H, 'string':Z) :-
    (   append([A,L,B], H),
        append(A,B,H2),
        brachylog_xterminate_single('string':L, 'string':H2, 'string':Z)
    ;   \+ append([_,L,_], H),
        Z = H
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_ZIP
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_zip_reversed(S, I, O) :-
    brachylog_zip(S, O, I).
brachylog_zip('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_zip('integer':I, Arg, Output).
brachylog_zip('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_zip('integer':I, Arg, Output).
brachylog_zip('default', L,Z) :-
    is_brachylog_list(L),
    maplist(brachylog_length('default'), L, Lengths),
    brachylog_order('default', Lengths, OrderedLengths),
    reverse(OrderedLengths, ['integer':MaxLength|_]),
    maplist(brachylog_zip_listify_integer, L, L2),
    brachylog_zip_(L2, MaxLength, Z).
brachylog_zip('integer':0, L, Z) :-
    is_brachylog_list(L),
    maplist(brachylog_length('default'), L, Lengths),
    brachylog_order('default', Lengths, ['integer':MinLength|_]),
    maplist(brachylog_zip_listify_integer, L, L2),
    brachylog_zip_(L2, MinLength, Z).
brachylog_zip('integer':1, L, Z) :-
    is_brachylog_list(L),
    maplist(brachylog_zip_listify_integer, L, L2),
    brachylog_zip_no_cycling(L2, Z).
brachylog_zip('integer':2, L, Z) :-
    maplist(brachylog_length_reversed('default', _), L),    % fails if the lengths aren't all the same
    brachylog_zip('default', L, Z).

brachylog_zip_(_, 0, []).
brachylog_zip_(L, I, [Heads|Z]) :-
    I #> 0,
    maplist(brachylog_head('default'), L, Heads),
    maplist(brachylog_circular_permute_counterclockwise('default'), L, Tails),
    J #= I - 1,
    brachylog_zip_(Tails, J, Z).

brachylog_zip_no_cycling([H|T], Z) :-
    brachylog_meta_select(ignore, 'default', brachylog_head, 'default', [H|T], Heads),
    (   Heads = [] ->
        Z = []
    ;   Z = [Heads|Z2],
        brachylog_meta_select(ignore, 'default', brachylog_behead, 'default', [H|T], Tails),
        brachylog_zip_no_cycling(Tails, Z2)
    ).

brachylog_zip_listify_integer(L, L) :-
    L \= 'integer':_.
brachylog_zip_listify_integer('integer':I, L) :-
    brachylog_elements('default', 'integer':I, L).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_TO_CODES
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_to_codes_reversed(S, I, O) :-
    brachylog_to_codes(S, O, I).
brachylog_to_codes('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_to_codes('integer':I, Arg, Output).
brachylog_to_codes('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_to_codes('integer':I, Arg, Output).
brachylog_to_codes('default', 'string':[], []).
brachylog_to_codes('default', 'string':[H|T], ['integer':I|T2]) :-
    (   var(T) ->
        length(T2,Length),
        length(T,Length)
    ;   length(T,Length),
        length(T2,Length)
    ),
    maplist(prepend_integer, L, ['integer':I|T2]),
    maplist(single_atom_code, [H|T],L).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_BLOCKS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_blocks_reversed(S, I, O) :-
    brachylog_blocks(S, O, I).
brachylog_blocks('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_blocks('integer':I, Arg, Output).
brachylog_blocks('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_blocks('integer':I, Arg, Output).
brachylog_blocks('default', [], []).
brachylog_blocks('default', [H|T], Blocks) :-
    brachylog_blocks('default', [H|T], H, Blocks).
brachylog_blocks('default', 'string':[H|T], StringBlocks) :-
    maplist(prepend_string, Blocks, StringBlocks),
    brachylog_blocks('default', [H|T], H, Blocks),
    !.

brachylog_blocks('default', [], _, [[]]).
brachylog_blocks('default', [H|T], H, [[H|T2]|T3]) :-
    brachylog_blocks('default', T, H, [T2|T3]).
brachylog_blocks('default', [H|T], I, [[],[H|T2]|T3]) :-
    dif(H, I),
    brachylog_blocks('default', T, H, [T2|T3]).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_DICHOTOMIZE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_dichotomize_reversed(S, I, O) :-
    brachylog_dichotomize(S, O, I).
brachylog_dichotomize('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_dichotomize('integer':I, Arg, Output).
brachylog_dichotomize('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_dichotomize('integer':I, Arg, Output).
brachylog_dichotomize('default', Input, Output) :-
    brachylog_dichotomize('integer':2, Input, Output).
brachylog_dichotomize('integer':I, Input, Output) :-
    brachylog_label('default', 'integer':I, _),
    length(Output, I),
    brachylog_dichotomize(Input, Output).
brachylog_dichotomize('string':L, L2) :-
    maplist(prepend_string,M, L2),
    brachylog_dichotomize(L, M).
brachylog_dichotomize('integer':0, L2) :-
    length(L2, Length),
    length(M, Length),
    brachylog_dichotomize([0], M),
    maplist(maplist(prepend_integer), M, L2).
brachylog_dichotomize('integer':I, L2) :-
    H #\= 0,
    integer_value('integer':_:[H|T], I),
    length(L2, Length),
    length(M, Length),
    brachylog_dichotomize([H|T], M),
    maplist(maplist(prepend_integer), M, L2).
brachylog_dichotomize(L, L2) :-
    is_brachylog_list(L),
    maplist(is_brachylog_list, L2),
    Length #= LengthL//LengthL2,
    length(L2, LengthL2),
    length(L, LengthL),
    reverse(L2, [_|T]),
    maplist(length_(Length), T),
    append(L2, L),
    !.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_ELEMENTS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_elements_reversed(S, I, O) :-
    brachylog_elements(S, O, I).
brachylog_elements('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_elements('integer':I, Arg, Output).
brachylog_elements('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_elements('integer':I, Arg, Output).
brachylog_elements('default', [], []).
brachylog_elements('default', [H|T], L) :-
    maplist(brachylog_elements('default'), [H|T], L).
brachylog_elements('default', 'string':S, L) :-
    brachylog_meta_find(ignore, 'default', brachylog_in, 'default', 'string':S, L).
brachylog_elements('default', 'integer':I, L) :-
    brachylog_meta_find(ignore, 'default', brachylog_in, 'default', 'integer':I, L).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_TO_NUMBER
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_to_number_reversed(S, I, O) :-
    brachylog_to_number(S, O, I).
brachylog_to_number('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_to_number('integer':I, Arg, Output).
brachylog_to_number('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_to_number('integer':I, Arg, Output).
brachylog_to_number('default', 'string':S, Type:N) :-
    atomic_list_concat(S, A),
    atom_number(A,N),
    (   member('.', S) ->
        Type = 'float'
    ;   Type = 'integer'
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_LOWERCASE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_lowercase_reversed(S, I, O) :-
    brachylog_lowercase(S, O, I).
brachylog_lowercase('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_lowercase('integer':I, Arg, Output).
brachylog_lowercase('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_lowercase('integer':I, Arg, Output).
brachylog_lowercase('default', 'string':Ls0, 'string':Ls) :-
    maplist(downcase_atom, Ls0, Ls).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_SPLIT_LINES
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_split_lines_reversed(S, I, O) :-
    brachylog_split_lines(S, O, I).
brachylog_split_lines('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_split_lines('integer':I, Arg, Output).
brachylog_split_lines('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_split_lines('integer':I, Arg, Output).
brachylog_split_lines('default', 'string':[], ['string':[]]).
brachylog_split_lines('default', 'string':['\n'|T], ['string':[]|T3]) :-
    brachylog_split_lines('default', 'string':T, T3).
brachylog_split_lines('default', 'string':['\r','\r\n'|T], ['string':[]|T3]) :-
    brachylog_split_lines('default', 'string':T, T3).
brachylog_split_lines('default', 'string':[H|T], ['string':[H|T2]|T3]) :-
    dif(H, '\n'),
    dif(H, '\r\n'),
    brachylog_split_lines('default', 'string':T, ['string':T2|T3]).
brachylog_split_lines('integer':1, 'string':[], ['string':[]]).
brachylog_split_lines('integer':1, 'string':[' '|T], ['string':[]|T3]) :-
    brachylog_split_lines('integer':1, 'string':T, T3).
brachylog_split_lines('integer':1, 'string':[H|T], ['string':[H|T2]|T3]) :-
    dif(H, ' '),
    brachylog_split_lines('integer':1, 'string':T, ['string':T2|T3]).
brachylog_split_lines('integer':2, 'string':[], ['string':[]]).
brachylog_split_lines('integer':2, 'string':[' '|T], ['string':[]|T3]) :-
    brachylog_split_lines('integer':2, 'string':T, T3).
brachylog_split_lines('integer':2, 'string':['\n'|T], ['string':[]|T3]) :-
    brachylog_split_lines('integer':2, 'string':T, T3).
brachylog_split_lines('integer':2, 'string':['\r','\r\n'|T], ['string':[]|T3]) :-
    brachylog_split_lines('integer':2, 'string':T, T3).
brachylog_split_lines('integer':2, 'string':[H|T], ['string':[H|T2]|T3]) :-
    dif(H, '\n'),
    dif(H, '\r\n'),
    dif(H, ' '),
    brachylog_split_lines('integer':2, 'string':T, ['string':T2|T3]).
brachylog_split_lines('integer':3, Input, Output) :-
    brachylog_split_lines('default', Input, L1),
    brachylog_meta_map(ignore, 'default', brachylog_split_lines, 'integer':1, L1, Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_OCCURENCES
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_occurences_reversed(S, I, O) :-
    brachylog_occurences(S, O, I).
brachylog_occurences('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_occurences('integer':I, Arg, Output).
brachylog_occurences('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_occurences('integer':I, Arg, Output).
brachylog_occurences('default', 'string':[], []).
brachylog_occurences('default', 'string':[H|T], L) :-
    brachylog_elements('default', 'string':[H|T], E),
    brachylog_occurences('default', E, L).
brachylog_occurences('default', 'integer':0, [['integer':0,'integer':1]]).
brachylog_occurences('default', 'integer':I, L) :-
    H #\= 0,
    brachylog_elements('default', 'integer':I, ['integer':H|T]),
    brachylog_occurences('default', ['integer':H|T], L).
brachylog_occurences('default', [], []).
brachylog_occurences('default', [H|T], [[H,'integer':O]|T2]) :-
    brachylog_occurences_(H, [H|T], O, R),
    brachylog_occurences('default', R, T2).

brachylog_occurences_(_, [], 0, []).
brachylog_occurences_(H, [H|T], I, R) :-
    I #= J + 1,
    brachylog_occurences_(H, T, J, R).
brachylog_occurences_(H, [H2|T], I, [H2|R]) :-
    dif(H, H2),
    brachylog_occurences_(H, T, I, R).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_RANDOM_ELEMENT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_random_element_reversed(S, I, O) :-
    brachylog_random_element(S, O, I).
brachylog_random_element('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_random_element('integer':I, Arg, Output).
brachylog_random_element('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_random_element('integer':I, Arg, Output).
brachylog_random_element('default', [], []).
brachylog_random_element('default', [H|T], R) :-
    length([H|T], L),
    M #= L - 1,
    random_between(0, M, I),
    nth0(I, [H|T], R).
brachylog_random_element('default', 'string':S, 'string':[R]) :-
    brachylog_random_element('default', S, R).
brachylog_random_element('default', 'integer':0, 'integer':0).
brachylog_random_element('default', 'integer':I, 'integer':R) :-
    H #\= 0,
    integer_value('integer':_:[H|T], I),
    brachylog_random_element('default', [H|T], R).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_SHUFFLE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_shuffle_reversed(S, I, O) :-
    brachylog_shuffle(S, O, I).
brachylog_shuffle('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_shuffle('integer':I, Arg, Output).
brachylog_shuffle('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_shuffle('integer':I, Arg, Output).
brachylog_shuffle('default', [], []).
brachylog_shuffle('default', [H|T], [H2|T2]) :-
    random_permutation([H|T], [H2|T2]).
brachylog_shuffle('default', 'string':S, 'string':T) :-
    brachylog_shuffle('default', S, T).
brachylog_shuffle('default', 'integer':I, 'integer':J) :-
    H #\= 0,
    integer_value('integer':Sign:[H|T], I),
    (   H2 #\= 0,
        brachylog_shuffle('default', [H|T], [H2|T2]) ->
        integer_value('integer':Sign:[H2|T2], J)
    ;   brachylog_shuffle('default', 'integer':I, 'integer':J)
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_UPPERCASE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_uppercase_reversed(S, I, O) :-
    brachylog_uppercase(S, O, I).
brachylog_uppercase('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_uppercase('integer':I, Arg, Output).
brachylog_uppercase('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_uppercase('integer':I, Arg, Output).
brachylog_uppercase('default', 'string':Ls0, 'string':Ls) :-
    maplist(upcase_atom, Ls0, Ls).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_WRITELN
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_writeln_reversed(S, I, O) :-
    brachylog_writeln(S, O, I).
brachylog_writeln('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_writeln('integer':I, Arg, Output).
brachylog_writeln('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_writeln('integer':I, Arg, Output).
brachylog_writeln('default', I, O) :-
    brachylog_writeln('integer':0, I, O).
brachylog_writeln('integer':Sub, I, O) :-
    brachylog_write('integer':Sub, I, O),
    (    Sub #> 3,                                       % declarative write
         brachylog_write('integer':4, 'string':['\n'], _)
    ;    Sub #< 4,                                       % imperative write
         brachylog_write('default', 'string':['\n'], _)
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_ABSOLUTE_VALUE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_absolute_value_reversed(S, I, O) :-
    brachylog_absolute_value(S, O, I).
brachylog_absolute_value('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_absolute_value('integer':I, Arg, Output).
brachylog_absolute_value('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_absolute_value('integer':I, Arg, Output).
brachylog_absolute_value('default', 'integer':I, 'integer':J) :-
    J #= abs(I).
brachylog_absolute_value('default', 'float':I, 'float':J) :-
    nonvar(I),
    J is abs(I).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_BASE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_base_reversed(S, I, O) :-
    brachylog_base(S, O, I).
brachylog_base('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_base('integer':I, Arg, Output).
brachylog_base('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_base('integer':I, Arg, Output).
brachylog_base('default', Input, Output) :-
    brachylog_base('integer':2, Input, Output).
brachylog_base('integer':1, 'integer':I, Output) :-
    brachylog_label('default', 'integer':I, _),
    length(Output, I),
    (   Output = ['integer':1|_] ->
        brachylog_equal('default', Output, _)
    ;   true
    ).
brachylog_base('integer':B, 'integer':I, L) :-
    B #> 1,
    %brachylog_label('default', ['integer':I, 'integer':B], _),
    (   I #>= 0
    ;   I #< 0
    ),
    J #= abs(I),
    n_base_digits(J, B, L).

% Credits to @repeat
% See: http://stackoverflow.com/a/33543199/2554145
n_base_digits(Expr, Base, Ds) :-
    Base #> 1,
    Ds = [_|_],
    N #=  Expr,
    N #>= 0,
    n_base_ref_acc_digits(N, Base, Ds, [], Ds).

n_base_ref_acc_digits(N, Base, Ref, Ds0, Ds) :-
    zcompare(Order, N, Base),
    order_n_base_ref_acc_digits(Order, N, Base, Ref, Ds0, Ds).

order_n_base_ref_acc_digits(<, N, _, [_], Ds0, ['integer':N|Ds0]).
order_n_base_ref_acc_digits(=, _, _, [_,_], Ds0, ['integer':1,'integer':0|Ds0]).
order_n_base_ref_acc_digits(>, N, Base, [_|Rs], Ds0, Ds) :-
    N0 #= N //  Base,
    N1 #= N mod Base,
    n_base_ref_acc_digits(N0, Base, Rs, ['integer':N1|Ds0], Ds).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_COERCE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_coerce_reversed(S, I, O) :-
    brachylog_coerce(S, O, I).
brachylog_coerce('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_coerce('integer':I, Arg, Output).
brachylog_coerce('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_coerce('integer':I, Arg, Output).
brachylog_coerce('default', [], []).
brachylog_coerce('default', [H|T], [H|T]).
brachylog_coerce('integer':1, 'integer':I, 'integer':I).
brachylog_coerce('integer':2, 'string':S, 'string':S).



/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_PRIME_DECOMPOSITION

   Credits to RosettaCode
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_prime_decomposition_reversed(S, I, O) :-
    brachylog_prime_decomposition(S, O, I).
brachylog_prime_decomposition('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_prime_decomposition('integer':I, Arg, Output).
brachylog_prime_decomposition('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_prime_decomposition('integer':I, Arg, Output).
brachylog_prime_decomposition('default', 'integer':N, Z) :-
    N #> 0,
    brachylog_label('default', 'integer':N, _),
    ceiled_square_root(N, SN),
    brachylog_prime_decomposition_1(N, SN, 2, [], L),
    brachylog_prime_decomposition_append_integer(L, Z).

brachylog_prime_decomposition_1(1, _, _, L, L) :- !.
brachylog_prime_decomposition_1(N, SN, D, L, LF) :-
    (   0 #= N mod D ->
        Q #= N // D,
        ceiled_square_root(Q, SQ),
        brachylog_prime_decomposition_1(Q, SQ, D, [D|L], LF)
    ;   D1 #= D + 1,
        (   D1 #> SN ->
            LF = [N|L]
        ;   brachylog_prime_decomposition_2(N, SN, D1, L, LF)
        )
    ).

brachylog_prime_decomposition_2(1, _, _, L, L) :- !.
brachylog_prime_decomposition_2(N, SN, D, L, LF) :-
    (   0 #= N mod D ->
        Q #= N // D,
        ceiled_square_root(Q, SQ),
        brachylog_prime_decomposition_2(Q, SQ, D, [D|L], LF);
        D1 #= D + 2,
        (   D1 #> SN ->
            LF = [N|L]
        ;   brachylog_prime_decomposition_2(N, SN, D1, L, LF)
        )
    ).

brachylog_prime_decomposition_append_integer([], []).
brachylog_prime_decomposition_append_integer([H|T], ['integer':H|T2]) :-
    brachylog_prime_decomposition_append_integer(T, T2).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_FACTORIAL
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_factorial_reversed(S, I, O) :-
    brachylog_factorial(S, O, I).
brachylog_factorial('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_factorial('integer':I, Arg, Output).
brachylog_factorial('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_factorial('integer':I, Arg, Output).
brachylog_factorial('default', 'integer':N, 'integer':F) :-
    brachylog_factorial_(N, 0, 1, F).

brachylog_factorial_(N, I, N0, F) :-
    F #> 0,
    N #>= 0,
    I #>= 0,
    I #=< N,
    N0 #> 0,
    N0 #=< F,
    if_(I #> 2,
        (   F #> N,
            if_(===(N, I, N0, F, T1),
                if_(T1 = true,
                    N0 = F,
                    N = I
                ),
                (   J #= I + 1,
                    N1 #= N0*J,
                    predicates:brachylog_factorial_(N, J, N1, F)
                )
            )
        ),
        if_(N = I,
            N0 = F,
            (   J #= I + 1,
                N1 #= N0*J,
                predicates:brachylog_factorial_(N, J, N1, F)
            )
        )
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_GROUPS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_groups_reversed(S, I, O) :-
    brachylog_groups(S, O, I).
brachylog_groups('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_groups('integer':I, Arg, Output).
brachylog_groups('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_groups('integer':I, Arg, Output).
brachylog_groups('default', Input, Output) :-
    I in 1..sup,
    brachylog_groups('integer':I, Input, Output).
brachylog_groups('integer':I, 'string':Input, Output) :-
    brachylog_groups('integer':I, Input, S),
    maplist(prepend_string, S, Output).
brachylog_groups('integer':I, 'integer':Input, Output) :-
    H #\= 0,
    integer_value('integer':_:[H|T], Input),
    brachylog_groups('integer':I, [H|T], S),
    maplist(brachylog_groups_ints, S, Output).
brachylog_groups('integer':I, Input, Output) :-
    maplist(is_brachylog_list, [Input, Output]),
    brachylog_groups(I, [], Input, Output).

brachylog_groups(_, [H|T], [], [RL]) :-
    reverse([H|T], RL).
brachylog_groups(I, L, Input, [RL|T2]) :-
    length(L, I),
    reverse(L, RL),
    brachylog_groups(I, [], Input, T2).
brachylog_groups(I, L, [H|T], Output) :-
    J #< I,
    J #>= 0,
    length(L, J),
    brachylog_groups(I, [H|L], T, Output).

brachylog_groups_ints(I, 'integer':O) :-
    integer_value('integer':'positive':I, O).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_MATRIX
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_matrix_reversed(S, I, O) :-
    brachylog_matrix(S, O, I).
brachylog_matrix('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_matrix('integer':I, Arg, Output).
brachylog_matrix('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_matrix('integer':I, Arg, Output).
brachylog_matrix('default', M, M) :-
    L in 0..sup,
    brachylog_matrix('integer':L, M, M).
brachylog_matrix('integer':L, M, M) :-
    length(M, L),
    maplist(length_(L), M).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_NEGATE
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_negate_reversed(S, I, O) :-
    brachylog_negate(S, O, I).
brachylog_negate('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_negate('integer':I, Arg, Output).
brachylog_negate('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_negate('integer':I, Arg, Output).
brachylog_negate('default', 'integer':I, 'integer':J) :-
    J #= -I.
brachylog_negate('default', 'float':I, 'float':J) :-
    nonvar(I),
    J is -I.


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_PRIME
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_prime_reversed(S, I, O) :-
    brachylog_prime(S, O, I).
brachylog_prime('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_prime('integer':I, Arg, Output).
brachylog_prime('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_prime('integer':I, Arg, Output).
brachylog_prime('default', 'integer':N, 'integer':N) :-
    clpfd:make_propagator(prime(N), Prop),
    clpfd:init_propagator(N, Prop),
    clpfd:trigger_once(Prop).

clpfd:run_propagator(prime(N), MState) :-
    (   nonvar(N) ->
        clpfd:kill(MState),
        check_prime(N)
    ;   clpfd:fd_get(N, ND, NL, NU, NPs),
        clpfd:cis_max(NL, n(2), NNL),
        clpfd:update_bounds(N, ND, NPs, NL, NU, NNL, NU)
    ).

:- if(current_predicate(crypto_is_prime/2)).
probably_prime(P) :- crypto_is_prime(P, []).
:- else.
probably_prime(_).
:- endif.

check_prime(N) :-
    (   N = 2 ->
        true
    ;   N #> 2,
        probably_prime(N),
        ceiled_square_root(N, SN),
        check_prime_1(N, SN, 2, [], [_])
    ).

check_prime_1(1, _, _, L, L) :- !.
check_prime_1(N, SN, D, L, LF) :-
    (   0 #= N mod D ->
        false
    ;   D1 #= D+1,
        (   D1 #> SN ->
            LF = [N|L]
        ;   check_prime_2(N, SN, D1, L, LF)
        )
    ).

check_prime_2(1, _, _, L, L) :- !.
check_prime_2(N, SN, D, L, LF) :-
    (   0 #= N mod D -> false
    ;   D1 #= D+2,
        (   D1 #> SN ->
            LF = [N|L]
        ;   check_prime_2(N, SN, D1, L, LF)
        )
).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_RANDOM_NUMBER
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_random_number_reversed(S, I, O) :-
    brachylog_random_number(S, O, I).
brachylog_random_number('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_random_number('integer':I, Arg, Output).
brachylog_random_number('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_random_number('integer':I, Arg, Output).
brachylog_random_number('default', 'integer':I, 'integer':R) :-
    brachylog_random_number(['integer':0,'integer':I], 'integer':R).
brachylog_random_number('default', 'float':I, 'float':R) :-
    brachylog_random_number(['float':0.0,'float':I], 'float':R).
brachylog_random_number('integer':1, _, 'float':R) :-
    brachylog_random_number(['float':0, 'float':1], 'float':R).
brachylog_random_number('integer':2, [A,B], R) :-
    brachylog_random_number([A,B], R).

brachylog_random_number(['integer':I,'integer':J], 'integer':R) :-
    brachylog_label('default', ['integer':I,'integer':J], _),
    (   I #=< J ->
        random_between(I, J, R)
    ;   random_between(J, I, R)
    ).
brachylog_random_number(['float':I,'float':J], 'float':R) :-
    random(K),
    (   I =< J ->
        R is I + K*(J - I)
    ;   R is J + K*(I - J)
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_SIGN
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_sign_reversed(S, I, O) :-
    brachylog_sign(S, O, I).
brachylog_sign('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_sign('integer':I, Arg, Output).
brachylog_sign('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_sign('integer':I, Arg, Output).
brachylog_sign('default', 'integer':I, 'integer':S) :-
    (   I = 0,
        S = 0
    ;   I #\= 0,
        S in -1\/1,
        S #= abs(I) // I
    ).
brachylog_sign('default', 'float':F, 'integer':S) :-
    nonvar(F),
    (   F =:= 0.0 -> S = 0
    ;   F > 0.0 -> S = 1
    ;   F < 0.0 -> S = -1
    ).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_TO_STRING
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_to_string_reversed(S, I, O) :-
    brachylog_to_string(S, O, I).
brachylog_to_string('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_to_string('integer':I, Arg, Output).
brachylog_to_string('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_to_string('integer':I, Arg, Output).
brachylog_to_string('default', 'integer':I, 'string':S) :-
    brachylog_label('default', 'integer':I, 'integer':I),
    atom_number(A, I),
    atom_chars(A, S).
brachylog_to_string('default', 'float':F, 'string':S) :-
    atom_number(A, F),
    atom_chars(A, S).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_CARTESIAN_PRODUCT
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_cartesian_product_reversed(S, I, O) :-
    brachylog_cartesian_product(S, O, I).
brachylog_cartesian_product('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_cartesian_product('integer':I, Arg, Output).
brachylog_cartesian_product('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_cartesian_product('integer':I, Arg, Output).
brachylog_cartesian_product('default', Input, Output) :-
    findall(L, brachylog_meta_map('ignore', 'default', brachylog_in, 'default', Input, L), Output).


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   BRACHYLOG_LABEL

   Credits to Markus Triska
   See: http://codereview.stackexchange.com/questions/129924/clpfd-labeling-on-possibly-infinite-domains/129945#129945
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
brachylog_label_reversed(S, I, O) :-
    brachylog_label(S, O, I).
brachylog_label('first', ['integer':I|Input], Output) :-
    (   Input = [Arg] -> true
    ;   Input = Arg
    ),
    brachylog_label('integer':I, Arg, Output).
brachylog_label('last', Input, Output) :-
    reverse(Input, ['integer':I|T]),
    (   T = [Arg] -> true
    ;   reverse(T, Arg)
    ),
    brachylog_label('integer':I, Arg, Output).
brachylog_label('default', Input, Output) :-
    brachylog_label('integer':0, Input, Output).
brachylog_label('integer':0, 'integer':Z, 'integer':Z) :-
    unsafe_indomain(Z).
brachylog_label('integer':0, Z, Z) :-
    is_brachylog_list(Z),
    maplist(brachylog_label('integer':0), Z, _).
brachylog_label('integer':1, 'integer':Z, 'integer':Z) :-
    get_time(T),
    T2 is ceil(1000000 * T),
    labeling([random_value(T2)], [Z]).
brachylog_label('integer':1, Z, Z) :-
    is_brachylog_list(Z),
    get_time(T),
    T2 is ceil(1000000 * T),
    labeling([random_value(T2)], Z).

unsafe_indomain(X) :-
    fd_inf(X, Inf0),
    fd_sup(X, Sup0),
    maplist(limit_pure, [Inf0,Sup0], [Inf,Sup]),
    unsafe_indomain_(Inf, Sup, X).

limit_pure(inf, inf) :- !.
limit_pure(sup, sup) :- !.
limit_pure(N, n(N))  :- !.

unsafe_indomain_(inf, Sup, X) :-
    infinite_down(Sup, X).
unsafe_indomain_(n(Low), Sup, X) :-
    unsafe_up_(Sup, Low, X).

infinite_down(sup, X) :-
    (   X = 0
    ;   abs(X) #= abs(N),
        positive_integer(N),
        ( X #= N ; X #= -N )
    ).
infinite_down(n(Up), X ) :-
    (   between(0, Up, X)
    ;   abs(X) #= abs(N),
        positive_integer(N),
        X #= -N
    ).

unsafe_up_(sup, Low, X) :-
    (   between(Low, 0, X)
    ;   positive_integer(X)
    ).
unsafe_up_(n(Up), Low, X) :- between(Low, Up, X).

% See: http://stackoverflow.com/a/39259871/2554145
positive_integer(I) :-
    I #> 0,
    (   var(I) ->
        fd_inf(I, Inf),
        (   I #= Inf
        ;   I #\= Inf,
            positive_integer(I)
        )
    ;   true
    ).