├── .gitignore ├── README.md ├── rebar.config ├── src ├── ealgo.app.src ├── ealgo.erl ├── ealgo.hrl ├── ealgo_ntl.erl └── ealgo_shield.erl └── test ├── ealgo_ntl_tests.erl ├── ealgo_shield_tests.erl └── ealgo_tests.erl /.gitignore: -------------------------------------------------------------------------------- 1 | /.rebar/ 2 | /_build/ 3 | /ebin/ 4 | /rebar.lock 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # An Erlang Algorithm Library 2 | 3 | Although the standard library of Erlang has become more and more complete, I have found that the support for various common algorithms is far from enough. This is an Erlang algorithm library similar to the `` in C++/STL, which is intended to supplement the shortcomings of the standard library. 4 | 5 | 6 |

Index

7 | 8 | * [Number Theory](#number-theory) 9 | * [Combinatorial](#combinatorial) 10 | * [Random](#random) 11 | * [Shielding Words](#shielding-words) 12 | * [Miscs](#miscs) 13 | 14 | 15 |

Number Theory

16 | 17 | * `fun ealgo_ntl:prime_list/1`: Generates a prime list where any element is not greater than `N`. 18 | * `fun ealgo_ntl:prime_array/1`: Generates a prime array where any element is not greater than `N`. 19 | * `fun ealgo_ntl:divisible/2`: Gives true if `N` is divisible by `M`, and false otherwise. 20 | * `fun ealgo_ntl:is_odd/1`: Gives true if `N` is an odd integer, and false otherwise. 21 | * `fun ealgo_ntl:is_even/1`: Gives true if `N` is an even integer, and false otherwise. 22 | * `fun ealgo_ntl:multiplicity/2`: Gives the highest power of `A` that divides `N`. 23 | * `fun ealgo_ntl:quotient/2`: Gives the integer quotient of `N` and `M`. 24 | * `fun ealgo_ntl:remainder/2`: Gives the remainder on division of `N` by `M`. 25 | * `fun ealgo_ntl:quotient_remainder/2`: Gives the quotient and remainder from division of `N` by `M`. 26 | * `fun ealgo_ntl:gcd/2`: Gives the [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) of `N` and `M`. note: we define `gcd(0, 0) = 0` for convenience. 27 | * `fun ealgo_ntl:gcd/1`: Gives the [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) of a list of integers. If the list has only one integer `X`, it returns `gcd(X, 0)`; If the list is empty, for this corner case, it returns `0`. 28 | * `fun ealgo_ntl:lcm/2`: Gives the [least common multiple](https://en.wikipedia.org/wiki/Least_common_multiple) of `N` and `M`. note: we define `lcm(X, Y) = 0` when `X = 0` or `y = 0`. 29 | * `fun ealgo_ntl:lcm/1`: Gives the [least common multiple](https://en.wikipedia.org/wiki/Least_common_multiple) of a list of integers. If the list has only one integer `X`, it returns `lcm(X, 1)`; If the list is empty, for this corner case, it returns `1`. 30 | * `fun ealgo_ntl:power/2`: Gives `A` to the power `X` where `A` is an integer and `X` is a nonnegative integer. note: `power(0, 0)` is undefined. 31 | * `fun ealgo_ntl:power_mod/3`: Gives `A ^ X mod M` where `A` is an integer, `X` is a nonnegative integer and `M` is a nonzero integer. note: `power_mod(0, 0, M)` is undefined. 32 | * `fun ealgo_ntl:is_coprime/2`: Gives true if `N` and `M` are [relatively prime](https://en.wikipedia.org/wiki/Coprime_integers), and false otherwise. relation: `is_coprime(N, M) = true <=> gcd(N, M) = 1` 33 | * `fun ealgo_ntl:jacobi_symbol/2`: Gives the [Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol) `(A / N)`.. 34 | * `fun ealgo_ntl:is_prime/1`: Gives true if `N` is a [prime number](https://en.wikipedia.org/wiki/Prime_number), and false otherwise, mainly implemented with [miller rabin test](https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test) and [strong lucas test](https://en.wikipedia.org/wiki/Lucas_pseudoprime). 35 | * `fun ealgo_ntl:bit_length/1`: Gives the number of binary bits necessary to represent the integer `N`. In other words, the integer `S` satisfied that `2^(S-1) ≤ |N| < 2^S`, and we defined `S = 0` when `N = 0` for the corner case. 36 | * `fun ealgo_ntl:nthrootrem/2`: Gives the `N'th` root and remainder of integer `X`. 37 | * `fun ealgo_ntl:extended_gcd/2`: Gives the extended greatest common divisor of N and M. note: `extended_gcd(0, 0) = {0, {0, 0}}` for convenience. 38 | 39 | 40 | 41 |

Combinatorial

42 | 43 | * `fun ealgo:cartesian_product/1`: Gives the [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) of a list of list. 44 | * `fun ealgo:combinations/1`: Gives all combinations of `L`. 45 | * `fun ealgo:combinations/2`: Gives all combinations of `L` containing exactly `N` elements. 46 | * `fun ealgo:permutations/1`: Gives all permutations of `L`. 47 | * `fun ealgo:permutations/2`: Gives all permutations of `L` containing exactly `N` elements. 48 | * `fun ealgo:next_permutation/1`: Gives the next permutation of `L`. 49 | 50 | 51 |

Random

52 | 53 | * `fun ealgo:shuffle/1`: Shuffle the list `L` randomly. 54 | * `fun ealgo:select_by_weight/1`: Select one element from list `L` by weight. 55 | * `fun ealgo:select_amount_by_weight/2`: Select `N` element from list `L` by weight. 56 | * `fun ealgo:get_rand_elem/1`: Select one element from list `L` randomly. 57 | * `fun ealgo:get_rand_elems/2`: Select `N` element from list `L` randomly. 58 | 59 | 60 |

Shielding Words

61 | 62 | * `fun ealgo_shield:create/1`: Create a trie tree from a given list of shielding words. 63 | * `fun ealgo_shield:insert/2`: Insert a shielding word into the shielding trie. 64 | * `fun ealgo_shield:detect/2`: Detect whether the sentence contains a shielding word. 65 | * `fun ealgo_shield:lookup/2`: Judge whether the sentence has a prefix is a shielding word. 66 | 67 | 68 |

Miscs

69 | 70 | * `fun ealgo:rabin_karp/2`: [Rabin Karp](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) gives a list of the starting character positions at which `Pattern` appears as a substring of `String`. 71 | * `fun ealgo:sgn/1`: [Sign Function](https://en.wikipedia.org/wiki/Sign_function) gives `-1`, `0`, or `1` depending on whether `X` is negative, zero, or positive. 72 | * `fun ealgo:boole/1`: [Indicator Function](https://en.wikipedia.org/wiki/Indicator_function) yields `1` if `X` is true and `0` if it is false. 73 | * `fun ealgo:ustep/1`: [Heaviside Step Function](https://en.wikipedia.org/wiki/Heaviside_step_function) Represents the unit step function, equal to `0` for `X < 0` and `1` for `X >= 0`. 74 | * `fun ealgo:id/1`: [Identity Function](https://en.wikipedia.org/wiki/Identity_function) Gives `X` (the identity operation). 75 | 76 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, []}. 2 | {cover_enabled, true}. 3 | -------------------------------------------------------------------------------- /src/ealgo.app.src: -------------------------------------------------------------------------------- 1 | {application, ealgo, [ 2 | {description, "An Erlang Algorithm Library"} 3 | , {vsn, "1.0.0"} 4 | , {modules, [ 5 | ealgo 6 | , ealgo_ntl 7 | , ealgo_shield 8 | ]} 9 | , {registered, []} 10 | , {applications, [kernel, stdlib]} 11 | , {env, []} 12 | ]}. 13 | -------------------------------------------------------------------------------- /src/ealgo.erl: -------------------------------------------------------------------------------- 1 | -module(ealgo). 2 | -include("ealgo.hrl"). 3 | -export([ 4 | cartesian_product/1 5 | , combinations/1 6 | , combinations/2 7 | , permutations/1 8 | , permutations/2 9 | , next_permutation/1 10 | , rabin_karp/2 11 | , rabin_karp/3 12 | , sgn/1 13 | , boole/1 14 | , ustep/1 15 | , id/1 16 | , shuffle/1 17 | , select_by_weight/1 18 | , select_amount_by_weight/2 19 | , get_rand_elem/1 20 | , get_rand_elems/2 21 | ]). 22 | 23 | 24 | %% https://en.wikipedia.org/wiki/Cartesian_product 25 | %% Gives the cartesian product of a list of list. 26 | -spec cartesian_product(L :: [[term()]]) -> 27 | R :: [[term()]]. 28 | cartesian_product([ ]) -> [[]]; 29 | cartesian_product([A | T]) -> 30 | B = cartesian_product(T), 31 | [[X | Y] || X <- A, Y <- B]. 32 | 33 | 34 | %% Gives all combinations of L. 35 | -spec combinations(L :: [term()]) -> 36 | R :: [[term()]]. 37 | combinations([ ]) -> [[]]; 38 | combinations([H | T]) -> 39 | A = B = combinations(T), 40 | A ++ [[H | Y] || Y <- B]. 41 | 42 | 43 | %% Gives all combinations of L containing exactly N elements. 44 | -spec combinations(L :: [term()], N :: non_neg_integer()) -> 45 | R :: [[term()]]. 46 | combinations(L, N) when not is_list(L) 47 | ; not is_integer(N) 48 | ; not (N >= 0) -> 49 | erlang:error(badarg); 50 | combinations( _ , 0) -> [[]]; 51 | combinations([ ], _) -> [ ]; 52 | combinations([H | T], N) -> 53 | A = combinations(T, N - 1), 54 | B = combinations(T, N ), 55 | [[H | X] || X <- A] ++ B. 56 | 57 | 58 | %% Gives all permutations of L. 59 | -spec permutations(L :: [term()]) -> 60 | R :: [[term()]]. 61 | permutations([]) -> [[]]; 62 | permutations(L) when is_list(L) -> 63 | permutations_h1(L, []). 64 | 65 | permutations_h1([ ], _) -> []; 66 | permutations_h1([H | T], C) -> 67 | A = permutations(lists:reverse(C, T)), 68 | B = permutations_h1(T, [H | C]), 69 | [[H | X] || X <- A] ++ B. 70 | 71 | 72 | %% Gives all permutations of L containing exactly N elements. 73 | -spec permutations(L :: [term()], N :: non_neg_integer()) -> 74 | R :: [[term()]]. 75 | permutations(L, N) when not is_list(L) 76 | ; not is_integer(N) 77 | ; not (N >= 0) -> 78 | erlang:error(badarg); 79 | permutations( _ , 0) -> [[]]; 80 | permutations([ ], _) -> [ ]; 81 | permutations( L , N) -> 82 | permutations_h2(L, N, []). 83 | 84 | permutations_h2([ ], _, _) -> []; 85 | permutations_h2([H | T], N, C) -> 86 | A = permutations(lists:reverse(C, T), N - 1), 87 | B = permutations_h2(T, N, [H | C]), 88 | [[H | X] || X <- A] ++ B. 89 | 90 | 91 | %% Gives the next permutation of L. 92 | -spec next_permutation(L :: [term()]) -> 93 | {true, R :: [term()]} | false. 94 | next_permutation(L) when is_list(L) -> 95 | next_permutation_h1(lists:reverse(L), []). 96 | 97 | next_permutation_h1([J, I | A], B) when I < J -> 98 | S = next_permutation_h2([I | lists:reverse(B, [J])], []), 99 | {true, lists:reverse(A, S)}; 100 | next_permutation_h1([H | A], B) -> 101 | next_permutation_h1(A, [H | B]); 102 | next_permutation_h1(_, _) -> false. 103 | 104 | next_permutation_h2([I, J | A], B) when I < J -> 105 | [J | lists:reverse(B, [I | A])]; 106 | next_permutation_h2([I, H | A], B) -> 107 | next_permutation_h2([I | A], [H | B]). 108 | 109 | 110 | %% https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm 111 | %% Gives a list of the starting character positions at which 112 | %% Pattern appears as a substring of String. 113 | -spec rabin_karp(String :: string(), Pattern :: string()) -> 114 | PositionList :: [pos_integer()]. 115 | rabin_karp(String, Pattern) when is_list(String), is_list(Pattern) -> 116 | rabin_karp(String, Pattern, -1). 117 | 118 | 119 | %% Includes the first N occurrences of Pattern, all if N < 0. 120 | -spec rabin_karp(String :: string(), Pattern :: string(), N :: integer()) -> 121 | PositionList :: [pos_integer()]. 122 | rabin_karp(String, Pattern, N) when length(String) < length(Pattern), is_integer(N) -> 123 | []; 124 | rabin_karp(String, Pattern, N) when is_list(String), is_list(Pattern), is_integer(N) -> 125 | MSB = ealgo_ntl:power_mod(Base = 257, PatSize = length(Pattern), Modu = 1299709), 126 | Iterate = fun(Hash, Out, In) -> 127 | ealgo_ntl:remainder((Hash * Base) - (Out * MSB) + In, Modu) 128 | end, 129 | PatHash = lists:foldl(fun(X, Acc) -> Iterate(Acc, 0, X) end, 0, Pattern), 130 | Accept = fun(OutList, WinHash) -> 131 | (PatHash =:= WinHash) andalso lists:prefix(Pattern, OutList) 132 | end, 133 | {Window, InList} = lists:split(PatSize, OutList = String), 134 | WinHash = lists:foldl(fun(X, Acc) -> Iterate(Acc, 0, X) end, 0, Window), 135 | rabin_karp_h1(Iterate, Accept, 1, OutList, WinHash, Accept(String, WinHash), InList, N, []). 136 | 137 | rabin_karp_h1(_, _, _, _, _, _, _, 0, Acc) -> lists:reverse(Acc, [ ]); 138 | rabin_karp_h1(_, _, _, _, _, false, [], _, Acc) -> lists:reverse(Acc, [ ]); 139 | rabin_karp_h1(_, _, Pos, _, _, true, [], _, Acc) -> lists:reverse(Acc, [Pos]); 140 | rabin_karp_h1(Iterate, Accept, Pos, [Out | OutList2], WinHash, IsMatch, [In | InList2], N, Acc) -> 141 | WinHash2 = Iterate(WinHash, Out, In), IsMatch2 = Accept(OutList2, WinHash2), 142 | case IsMatch of 143 | false -> Pos2 = Pos + 1, N2 = N - 0, Acc2 = Acc; 144 | true -> Pos2 = Pos + 1, N2 = N - 1, Acc2 = [Pos | Acc] 145 | end, 146 | rabin_karp_h1(Iterate, Accept, Pos2, OutList2, WinHash2, IsMatch2, InList2, N2, Acc2). 147 | 148 | 149 | %% https://en.wikipedia.org/wiki/Sign_function 150 | %% Gives -1, 0, or 1 depending on whether X is negative, zero, or positive. 151 | -spec sgn(X :: number()) -> 152 | R :: -1 | 0 | 1. 153 | sgn(X) when not is_integer(X) 154 | , not is_float(X) -> 155 | erlang:error(badarg); 156 | sgn(X) when X > 0 -> 1; 157 | sgn(X) when X < 0 -> -1; 158 | sgn(_) -> 0. 159 | 160 | 161 | %% https://en.wikipedia.org/wiki/Indicator_function 162 | %% Yields 1 if X is true and 0 if it is false. 163 | -spec boole(X :: boolean()) -> 164 | R :: 0 | 1. 165 | boole( true) -> 1; 166 | boole(false) -> 0. 167 | 168 | 169 | %% https://en.wikipedia.org/wiki/Heaviside_step_function 170 | %% Represents the unit step function, equal to 0 171 | %% for X < 0 and 1 for X >= 0. 172 | -spec ustep(X :: number()) -> 173 | R :: 0 | 1. 174 | ustep(X) when not is_integer(X) 175 | , not is_float(X) -> 176 | erlang:error(badarg); 177 | ustep(X) when X >= 0 -> 1; 178 | ustep(_) -> 0. 179 | 180 | 181 | %% https://en.wikipedia.org/wiki/Identity_function 182 | %% Gives X (the identity operation). 183 | -spec id(X :: term()) -> 184 | R :: term(). 185 | id(X) -> X. 186 | 187 | 188 | -spec shuffle(L :: list()) -> 189 | R :: any(). 190 | shuffle(L) -> 191 | L2 = [{rand:uniform(100), Item} || Item <- L], 192 | [ShuffledItem || {_, ShuffledItem} <- lists:sort(L2)]. 193 | 194 | -type key() :: any(). 195 | -type weight() :: integer. 196 | -type weight_item() :: [{key(), weight()}]. 197 | -spec select_by_weight(List :: list(weight_item())) -> 198 | R :: {ok, key()} | {error, _}. 199 | select_by_weight(List) -> 200 | AllWeight = lists:sum([Weight || {_Item, Weight} <- List, Weight >= 0]), 201 | case AllWeight > 0 of 202 | true -> 203 | RandomWeight = rand:uniform(AllWeight), 204 | select_by_weight(List, RandomWeight); 205 | false -> 206 | {error, <<"AllWeightMustGreaterThanZero">>} 207 | end. 208 | 209 | select_by_weight(List, RandomWeight) -> 210 | [{Item, Weight} | RestList] = List, 211 | if 212 | RandomWeight =< Weight -> 213 | {ok, Item}; 214 | RandomWeight > Weight -> 215 | select_by_weight(RestList, RandomWeight - Weight) 216 | end. 217 | 218 | 219 | -spec select_amount_by_weight(List :: list(weight_item()), Amount :: integer) -> 220 | R :: {ok, list(any)} | {error, _}. 221 | select_amount_by_weight(_List, Amount) when Amount =< 0 -> 222 | {error, <<"AmountMustGreaterThanZero">>}; 223 | select_amount_by_weight(List, Amount) when length(List) == Amount -> 224 | SelectedKeys = [Key || {Key, _} <- List], 225 | {ok, SelectedKeys}; 226 | select_amount_by_weight(List, Amount) when length(List) < Amount -> 227 | {error, <<"ListLengthMustGreaterThanAmount">>}; 228 | select_amount_by_weight(List, Amount) -> 229 | select_amount_by_weight(List, Amount, []). 230 | 231 | select_amount_by_weight(_List, 0, SelectedKeys) -> 232 | SelectedKeys; 233 | select_amount_by_weight([], _Amount, SelectedKeys) -> 234 | SelectedKeys; 235 | select_amount_by_weight(List, Amount, SelectedKeys) -> 236 | {ok, SelectedKey} = select_by_weight(List), 237 | SelectedList = lists:keydelete(SelectedKey, 1, List), 238 | select_amount_by_weight(SelectedList, Amount-1, [SelectedKey | SelectedKeys]). 239 | 240 | 241 | -spec get_rand_elem(List :: list()) -> 242 | R :: undefined | any(). 243 | get_rand_elem([]) -> 244 | undefined; 245 | get_rand_elem(List) -> 246 | {ok, [Elem]} = get_rand_elems(List, 1), 247 | Elem. 248 | 249 | 250 | -spec get_rand_elems(List :: list(), Amount :: integer()) -> 251 | R :: {ok, list()} | {error, _}. 252 | get_rand_elems([], _Amount) -> 253 | {error, <<"ListEmpty">>}; 254 | get_rand_elems(_List, Amount) when Amount =< 0 -> 255 | {error, <<"ArgAmountIllegal">>}; 256 | get_rand_elems(List, Amount) when length(List) == Amount -> 257 | {ok, List}; 258 | get_rand_elems(List, Amount) when length(List) < Amount -> 259 | {error, <<"NoEnoughListElems">>}; 260 | get_rand_elems(List, Amount) -> 261 | get_rand_elems(Amount, List, length(List), [], 0). 262 | 263 | get_rand_elems(_Amount, [], _ListLen, Elems, _ElemsAmount) -> 264 | {ok, Elems}; 265 | get_rand_elems(Amount, _List, _ListLen, Elems, ElemsAmount) when ElemsAmount >= Amount -> 266 | {ok, Elems}; 267 | get_rand_elems(Amount, List, ListLen, Elems, ElemsAmount) -> 268 | Index = rand:uniform(ListLen), 269 | Elem = lists:nth(Index, List), 270 | RestList = lists:delete(Elem, List), 271 | NewElems = [Elem | Elems], 272 | get_rand_elems(Amount, RestList, ListLen-1, NewElems, ElemsAmount+1). 273 | -------------------------------------------------------------------------------- /src/ealgo.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(EALGO_H). 2 | -define(EALGO_H, true). 3 | 4 | 5 | -define(PRIME_LIST_1000, [ 6 | 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73 7 | ,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157 8 | ,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239 9 | ,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331 10 | ,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421 11 | ,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509 12 | ,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613 13 | ,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709 14 | ,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821 15 | ,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919 16 | ,929,937,941,947,953,967,971,977,983,991,997]). 17 | 18 | 19 | -endif. 20 | -------------------------------------------------------------------------------- /src/ealgo_ntl.erl: -------------------------------------------------------------------------------- 1 | -module(ealgo_ntl). 2 | -include("ealgo.hrl"). 3 | -import(ealgo, [sgn/1, boole/1, id/1]). 4 | -export([ 5 | prime_list/1 6 | , prime_array/1 7 | , divisible/2 8 | , is_odd/1 9 | , is_even/1 10 | , multiplicity/2 11 | , quotient/2 12 | , remainder/2 13 | , quotient_remainder/2 14 | , gcd/2 15 | , gcd/1 16 | , lcm/2 17 | , lcm/1 18 | , power/2 19 | , power_mod/3 20 | , is_coprime/2 21 | , jacobi_symbol/2 22 | , is_prime/1 23 | , bit_length/1 24 | , nthrootrem/2 25 | , extended_gcd/2 26 | ]). 27 | 28 | 29 | %% Generates a prime list where any element is not greater than N. 30 | -spec prime_list(N :: integer()) -> 31 | PL :: [pos_integer()]. 32 | prime_list(N) when is_integer(N) -> 33 | prime_list(N, lists:seq(2, erlang:max(1, N))). 34 | prime_list(N, [H | T]) when H * H =< N -> 35 | [H | prime_list(N, [X || X <- T, X rem H =/= 0])]; 36 | prime_list(_, L) -> L. 37 | 38 | 39 | %% Generates a prime array where any element is not greater than N. 40 | -spec prime_array(N :: integer()) -> 41 | PT :: array:array(pos_integer()). 42 | prime_array(N) when is_integer(N) -> 43 | array:from_list(prime_list(N)). 44 | 45 | 46 | %% Gives true if N is divisible by M, and false otherwise. 47 | -spec divisible(N :: integer(), M :: integer()) -> 48 | P :: boolean(). 49 | divisible(N, M) when is_integer(N) 50 | , is_integer(M) 51 | , M =/= 0 -> 52 | N rem M =:= 0. 53 | 54 | 55 | %% Gives true if N is an odd integer, and false otherwise. 56 | -spec is_odd(N :: integer()) -> 57 | P :: boolean(). 58 | is_odd(N) when is_integer(N) -> 59 | N rem 2 =/= 0. 60 | 61 | 62 | %% Gives true if N is an even integer, and false otherwise. 63 | -spec is_even(N :: integer()) -> 64 | P :: boolean(). 65 | is_even(N) when is_integer(N) -> 66 | N rem 2 =:= 0. 67 | 68 | 69 | %% Gives the highest power of A that divides N. 70 | -spec multiplicity(N :: integer(), A :: pos_integer()) -> 71 | Y :: non_neg_integer(). 72 | multiplicity(N, A) when is_integer(N) 73 | , is_integer(A) 74 | , N =/= 0 75 | , A > 1 -> 76 | multiplicity(N, A, 0). 77 | multiplicity(N, A, Y) when N rem A =/= 0 -> Y; 78 | multiplicity(N, A, Y) -> multiplicity(N, A, Y, 1, A). 79 | multiplicity(N, A, Y, X, M) when N rem (M * M) =/= 0 -> 80 | multiplicity(N div M, A, Y + X); 81 | multiplicity(N, A, Y, X, M) -> 82 | multiplicity(N, A, Y, X + X, M * M). 83 | 84 | 85 | %% Definition of integer division: 86 | %% N = M * Q + R (M ≠ 0) 87 | %% Q = floor(N / M) 88 | %% 0 ≤ R < M (when M > 0) 89 | %% M < R ≤ 0 (when M < 0) 90 | %% 91 | %% Gives the integer quotient of N and M. 92 | -spec quotient(N :: integer(), M :: integer()) -> 93 | Q :: integer(). 94 | quotient(N, M) when is_integer(N) 95 | , is_integer(M) 96 | , M =/= 0 -> 97 | (N - remainder(N, M)) div M. 98 | 99 | 100 | %% Gives the remainder on division of N by M. 101 | -spec remainder(N :: integer(), M :: integer()) -> 102 | R :: integer(). 103 | remainder(N, M) when is_integer(N) 104 | , is_integer(M) 105 | , M =/= 0 -> 106 | case N rem M of 107 | R when R > 0, M < 0 -> R + M; 108 | R when R < 0, M > 0 -> R + M; 109 | R -> R 110 | end. 111 | 112 | 113 | %% Gives the quotient and remainder from division of N by M. 114 | -spec quotient_remainder(N :: integer(), M :: integer()) -> 115 | {Q :: integer(), R :: integer()}. 116 | quotient_remainder(N, M) when is_integer(N) 117 | , is_integer(M) 118 | , M =/= 0 -> 119 | R = remainder(N, M), Q = (N - R) div M, 120 | {Q, R}. 121 | 122 | 123 | %% https://en.wikipedia.org/wiki/Greatest_common_divisor 124 | %% Gives the greatest common divisor of N and M. 125 | %% note: we define gcd(0, 0) = 0 for convenience. 126 | -spec gcd(N :: integer(), M :: integer()) -> 127 | GCD :: integer(). 128 | gcd(N, M) when is_integer(N), is_integer(M) -> 129 | euclid(abs(N), abs(M)). 130 | euclid(N, 0) -> N; 131 | euclid(N, M) -> euclid(M, N rem M). 132 | 133 | 134 | %% Gives the greatest common divisor of a list of integers. 135 | %% If the list has only one integer X, it returns gcd(X, 0); 136 | %% If the list is empty, for this corner case, it returns 0. 137 | -spec gcd(L :: [integer()]) -> 138 | GCD :: integer(). 139 | gcd(L) when is_list(L) -> lists:foldl(fun gcd/2, 0, L). 140 | 141 | 142 | %% https://en.wikipedia.org/wiki/Least_common_multiple 143 | %% Gives the least common multiple of N and M. 144 | %% note: we define lcm(X, Y) = 0 (when X = 0 or y = 0). 145 | -spec lcm(N :: integer(), M :: integer()) -> 146 | LCM :: integer(). 147 | lcm(N, M) when is_integer(N), is_integer(M) -> 148 | case abs(N * M) of 149 | 0 -> 0; 150 | P -> P div gcd(N, M) 151 | end. 152 | 153 | 154 | %% Gives the least common multiple of a list of integers. 155 | %% If the list has only one integer X, it returns lcm(X, 1); 156 | %% If the list is empty, for this corner case, it returns 1. 157 | -spec lcm(L :: [integer()]) -> 158 | LCM :: integer(). 159 | lcm(L) when is_list(L) -> lists:foldl(fun lcm/2, 1, L). 160 | 161 | 162 | %% Gives A to the power X where A is an integer and X is a 163 | %% nonnegative integer. 164 | %% note: power(0, 0) is undefined. 165 | -spec power(A :: integer(), X :: non_neg_integer()) -> 166 | POWER :: integer(). 167 | power(A, X) when is_integer(A) 168 | , is_integer(X) 169 | , X >= 0 -> 170 | if 171 | % A =:= 0, X =:= 0 -> undefined 172 | A =:= 0, X =/= 0 -> 0; 173 | A =/= 0, X =:= 0 -> 1; 174 | A =/= 0, X =/= 0 -> 175 | B = power(A, X div 2), 176 | case is_even(X) of 177 | true -> B * B; 178 | _ -> A * B * B 179 | end 180 | end. 181 | 182 | %% Gives A ^ X mod M where A is an integer, X is a 183 | %% nonnegative integer and M is a nonzero integer. 184 | %% note: power_mod(0, 0, M) is undefined. 185 | -spec power_mod(A :: integer(), X :: non_neg_integer(), M :: integer()) -> 186 | POWER_MOD :: integer(). 187 | power_mod(A, X, M) when is_integer(A) 188 | , is_integer(X) 189 | , is_integer(M) 190 | , X >= 0 191 | , M =/= 0 -> 192 | if 193 | % A =:= 0, X =:= 0 -> undefined 194 | A =:= 0, X =/= 0 -> 0; 195 | A =/= 0, X =:= 0 -> remainder(1, M); 196 | A =/= 0, X =/= 0 -> 197 | B = power_mod(A, X div 2, M), 198 | case is_even(X) of 199 | true -> remainder(B * B, M); 200 | _ -> remainder(A * B * B, M) 201 | end 202 | end. 203 | 204 | 205 | %% https://en.wikipedia.org/wiki/Coprime_integers 206 | %% Gives true if N and M are relatively prime, and false otherwise. 207 | %% relation: is_coprime(N, M) = true <=> gcd(N, M) = 1 208 | -spec is_coprime(N :: integer(), M :: integer()) -> 209 | P :: boolean(). 210 | is_coprime(N, M) when is_integer(N), is_integer(M) -> 211 | gcd(N, M) =:= 1. 212 | 213 | 214 | %% https://en.wikipedia.org/wiki/Jacobi_symbol 215 | %% Gives the Jacobi symbol (A / N). 216 | -spec jacobi_symbol(A :: integer(), N :: integer()) -> 217 | J :: -1 | 0 | 1. 218 | jacobi_symbol(A, 1) when is_integer(A) -> 219 | 1; 220 | jacobi_symbol(A, N) when is_integer(A) 221 | , is_integer(N) 222 | , (N rem 2) > 0 -> 223 | B = remainder(A, N), 224 | case is_coprime(B, N) of 225 | false -> 226 | 0; 227 | true -> 228 | jacobi_symbol(B, N, 1) 229 | end. 230 | jacobi_symbol(1, _N, C) -> 231 | C; 232 | jacobi_symbol(A, N, C) when (A rem 2) =:= 0 -> 233 | D = case is_even(S = multiplicity(A, 2)) of 234 | true -> C; false -> case (N rem 8) of 235 | R when R =:= 3; R =:= 5 -> (-C); _ -> C 236 | end end, 237 | jacobi_symbol(A bsr S, N, D); 238 | jacobi_symbol(A, N, C) -> 239 | D = case {A rem 4, N rem 4} of 240 | {3, 3} -> (-C); _ -> C 241 | end, 242 | jacobi_symbol(N rem A, A, D). 243 | 244 | 245 | %% https://en.wikipedia.org/wiki/Prime_number 246 | %% Gives true if N is a prime number, and false otherwise. 247 | -spec is_prime(N :: integer()) -> 248 | P :: boolean(). 249 | is_prime(N) when is_integer(N) -> 250 | is_prime_helper(N). 251 | 252 | is_prime_helper(N) when N < 2 -> false; 253 | is_prime_helper(N) when N =:= 2 -> true; 254 | is_prime_helper(N) when N rem 2 =:= 0 -> false; 255 | is_prime_helper(N) when N < 1000000 -> trial_division(N); 256 | is_prime_helper(N) -> 257 | trial_division(N) andalso miller_rabin(N, 2) 258 | andalso (not is_square(N)) andalso strong_lucas(N). 259 | 260 | 261 | trial_division(N) -> trial_division(N, ?PRIME_LIST_1000). 262 | trial_division(_, [ ]) -> true; 263 | trial_division(N, [H | _]) when H * H > N -> true; 264 | trial_division(N, [H | _]) when N rem H =:= 0 -> false; 265 | trial_division(N, [_ | T]) -> trial_division(N, T). 266 | 267 | %% https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test 268 | miller_rabin(N, A) -> 269 | S = multiplicity(M = N - 1, 2), 270 | case power_mod(A, M bsr S, N) of 271 | B when B =:= 1 -> true; 272 | B when B =:= N - 1 -> true; 273 | B -> miller_rabin(N, B, S - 1) 274 | end. 275 | miller_rabin(_, _, 0) -> false; 276 | miller_rabin(N, A, S) -> 277 | case remainder(A * A, N) of 278 | B when B =:= 1 -> false; 279 | B when B =:= N - 1 -> true; 280 | B -> miller_rabin(N, B, S - 1) 281 | end. 282 | 283 | %% https://en.wikipedia.org/wiki/Lucas_pseudoprime 284 | strong_lucas(N) -> 285 | A = fun SearchA(X) -> 286 | case jacobi_symbol(X, N) of 287 | -1 -> X; 288 | _ -> SearchA(-X-2*sgn(X)) 289 | end 290 | end (5), 291 | Q = (1 - A) div 4, 292 | S = multiplicity(M = N + 1, 2), 293 | case lucas_uv(D = M bsr S, A, Q, N) of 294 | {0, _} -> true; 295 | {_, 0} -> true; 296 | {_, V} -> 297 | Q2 = power_mod(Q, D, N), 298 | strong_lucas(N, V, Q2, S - 1) 299 | end. 300 | strong_lucas(_, _, _, 0) -> false; 301 | strong_lucas(N, V, Q, S) -> 302 | case remainder(V * V - 2 * Q, N) of 303 | 0 -> true; 304 | V2 -> 305 | Q2 = remainder(Q * Q, N), 306 | strong_lucas(N, V2, Q2, S - 1) 307 | end. 308 | 309 | lucas_uv(1, _, _, _) -> {1, 1}; 310 | lucas_uv(D, A, Q, N) -> 311 | case lucas_uv(K = D div 2, A, Q, N) of 312 | UV when D rem 2 =/= 0 -> 313 | TEMP = lucas_double(UV, K, Q, N), 314 | lucas_addone(TEMP, A, N); 315 | UV -> 316 | lucas_double(UV, K, Q, N) 317 | end. 318 | 319 | lucas_double({U, V}, K, Q, N) -> 320 | Q2 = power_mod(Q, K, N), 321 | {remainder(U * V, N), remainder(V*V - 2*Q2, N)}. 322 | 323 | lucas_addone({U, V}, A, N) -> 324 | UD = case is_odd(UT = U + V) of 325 | true -> N; _ -> 0 326 | end, 327 | VD = case is_odd(VT = A * U + V) of 328 | true -> N; _ -> 0 329 | end, 330 | {remainder((UT + UD) div 2, N), 331 | remainder((VT + VD) div 2, N)}. 332 | 333 | is_square(N) -> 334 | {_, R} = nthrootrem(N, 2), R =:= 0. 335 | 336 | 337 | %% Gives the number of binary bits necessary to represent the integer N. 338 | %% In other words, the integer S satisfied that 2^(S-1) ≤ |N| < 2^S, and 339 | %% we defined S = 0 when N = 0 for the corner case. 340 | -spec bit_length(N :: integer()) -> 341 | S :: non_neg_integer(). 342 | bit_length(N) when is_integer(N) -> 343 | bit_length(erlang:abs(N), 0). 344 | bit_length(N, R) when N >= 16#10000000000000000 -> 345 | E = erlang:external_size(N) * 8 - 64, bit_length(N bsr E, R + E); 346 | bit_length(N, R) when N >= 16#100000000 -> bit_length(N bsr 32, R + 32); 347 | bit_length(N, R) when N >= 16#10000 -> bit_length(N bsr 16, R + 16); 348 | bit_length(N, R) when N >= 16#100 -> bit_length(N bsr 8, R + 8); 349 | bit_length(N, R) when N >= 16#10 -> bit_length(N bsr 4, R + 4); 350 | bit_length(N, R) when N >= 16#4 -> bit_length(N bsr 2, R + 2); 351 | bit_length(N, R) when N >= 16#2 -> bit_length(N bsr 1, R + 1); 352 | bit_length(N, R) when N =:= 1; N =:= 0 -> N + R. 353 | 354 | 355 | %% Gives the N'th root and remainder of integer X. 356 | -spec nthrootrem(X :: non_neg_integer(), N :: pos_integer()) -> 357 | {Y :: non_neg_integer(), R :: non_neg_integer()}. 358 | nthrootrem(X, N) when is_integer(X) 359 | , is_integer(N) 360 | , X >= 0 361 | , N > 0 -> 362 | if 363 | X =:= 0; X =:= 1; N =:= 1 -> 364 | {X, 0}; 365 | true -> 366 | S = (bit_length(X) - 1) div N, 367 | nthrootrem(X, N, 1 bsl S, 2 bsl S) 368 | end. 369 | nthrootrem(X, N, L, H) when L > H -> 370 | {H, X - power(H, N)}; 371 | nthrootrem(X, N, L, H) -> 372 | M = (L + H) div 2, 373 | case X < power(M, N) of 374 | true -> 375 | nthrootrem(X, N, L, M - 1); 376 | false -> 377 | nthrootrem(X, N, M + 1, H) 378 | end. 379 | 380 | 381 | %% Gives the extended greatest common divisor of N and M. 382 | %% note: extended_gcd(0, 0) = {0, {0, 0}} for convenience. 383 | -spec extended_gcd(N :: integer(), M :: integer()) -> 384 | {G :: integer(), {A :: integer(), B :: integer()}}. 385 | extended_gcd(N, M) when is_integer(N), is_integer(M) -> 386 | {G, {A, B}} = extended_euclid(abs(N), abs(M)), 387 | {G, {A * sgn(N), B * sgn(M)}}. 388 | extended_euclid(N, 0) -> {N, {1, 0}}; 389 | extended_euclid(N, M) -> 390 | {Q, R} = quotient_remainder(N, M), 391 | {G, {A, B}} = extended_euclid(M, R), 392 | {G, {B, A - Q * B}}. 393 | -------------------------------------------------------------------------------- /src/ealgo_shield.erl: -------------------------------------------------------------------------------- 1 | -module(ealgo_shield). 2 | -export([ 3 | create/1 4 | , detect/2 5 | ]). 6 | 7 | 8 | %% Create a trie tree from a given list of shielding words. 9 | -spec create(ShieldingWordList) -> ShieldingTrie when 10 | ShieldingWordList :: [string()], 11 | ShieldingTrie :: map(). 12 | create(ShieldingWordList) -> 13 | lists:foldl(fun insert/2, #{}, ShieldingWordList). 14 | 15 | 16 | %% Insert a shielding word into the shielding trie. 17 | -spec insert(ShieldingWord, ShieldingTrie1) -> ShieldingTrie2 when 18 | ShieldingWord :: string(), 19 | ShieldingTrie1 :: map(), 20 | ShieldingTrie2 :: map(). 21 | insert([ ], ShieldingNode) -> 22 | ShieldingNode#{ending => true}; 23 | insert([H | T], ShieldingNode) -> 24 | Child = maps:get(H, ShieldingNode, #{}), 25 | ShieldingNode#{H => insert(T, Child)}. 26 | 27 | 28 | %% Detect whether the sentence contains a shielding word. 29 | -spec detect(Sentence, ShieldingTrie) -> boolean() when 30 | Sentence :: string(), 31 | ShieldingTrie :: map(). 32 | detect([ ] = S, ShieldingTrie) -> 33 | lookup(S, ShieldingTrie); 34 | detect([_H | T] = S, ShieldingTrie) -> 35 | case lookup(S, ShieldingTrie) of 36 | true -> true; 37 | false -> detect(T, ShieldingTrie) 38 | end. 39 | 40 | 41 | %% Judge whether the sentence has a prefix is a shielding word. 42 | lookup( _S, #{ending := true}) -> true; 43 | lookup([ ], _ShieldingNode) -> false; 44 | lookup([H | T], ShieldingNode) -> 45 | case ShieldingNode of 46 | #{H := Child} -> lookup(T, Child); 47 | _ -> false 48 | end. 49 | -------------------------------------------------------------------------------- /test/ealgo_ntl_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ealgo_ntl_tests). 2 | -include_lib("eunit/include/eunit.hrl"). 3 | -import(ealgo_ntl, [ 4 | quotient/2 5 | , remainder/2 6 | , quotient_remainder/2 7 | , divisible/2 8 | , is_odd/1 9 | , is_even/1 10 | ]). 11 | 12 | 13 | all_test_() -> 14 | {foreach, 15 | fun() -> error_logger:tty(false) end, 16 | fun(_) -> error_logger:tty(true) end, 17 | [ 18 | {<<"test quotient">>, fun test_quotient/0} 19 | , {<<"test remainder">>, fun test_remainder/0} 20 | , {<<"test quotient_remainder">>, fun test_quotient_remainder/0} 21 | , {<<"test is_divisible">>, fun test_is_divisible/0} 22 | , {<<"test is_odd">>, fun test_is_odd/0} 23 | , {<<"test is_even">>, fun test_is_even/0} 24 | ] 25 | }. 26 | 27 | 28 | test_quotient() -> 29 | ?assertEqual(quotient(10, 3), 3), 30 | ?assertEqual(quotient(-10, 3), -4), 31 | ?assertEqual(quotient(10, -3), -4), 32 | ?assertEqual(quotient(-10, -3), 3), 33 | ok. 34 | 35 | 36 | test_remainder() -> 37 | ?assertEqual(remainder(10, 3), 1), 38 | ?assertEqual(remainder(-10, 3), 2), 39 | ?assertEqual(remainder(10, -3), -2), 40 | ?assertEqual(remainder(-10, -3), -1), 41 | ok. 42 | 43 | 44 | test_quotient_remainder() -> 45 | ?assertEqual(quotient_remainder(10, 3), {3, 1}), 46 | ?assertEqual(quotient_remainder(-10, 3), {-4, 2}), 47 | ?assertEqual(quotient_remainder(10, -3), {-4, -2}), 48 | ?assertEqual(quotient_remainder(-10, -3), {3, -1}), 49 | ok. 50 | 51 | 52 | test_is_divisible() -> 53 | ?assertEqual(divisible(10, 3), false), 54 | ?assertEqual(divisible(10, 2), true), 55 | ?assertEqual(divisible(10, 5), true), 56 | ?assertEqual(divisible(10, 7), false), 57 | ok. 58 | 59 | 60 | test_is_odd() -> 61 | ?assertEqual(is_odd(0), false), 62 | ?assertEqual(is_odd(-1), true), 63 | ?assertEqual(is_odd(1), true), 64 | ?assertEqual(is_odd(-2), false), 65 | ?assertEqual(is_odd(2), false), 66 | ?assertEqual(is_odd(-223897189), true), 67 | ?assertEqual(is_odd(223897189), true), 68 | ?assertEqual(is_odd(-328978959790), false), 69 | ?assertEqual(is_odd(328978959790), false), 70 | ok. 71 | 72 | 73 | test_is_even() -> 74 | ?assertEqual(is_even(0), true), 75 | ?assertEqual(is_even(-1), false), 76 | ?assertEqual(is_even(1), false), 77 | ?assertEqual(is_even(-2), true), 78 | ?assertEqual(is_even(2), true), 79 | ?assertEqual(is_even(-223897189), false), 80 | ?assertEqual(is_even(223897189), false), 81 | ?assertEqual(is_even(-328978959790), true), 82 | ?assertEqual(is_even(328978959790), true), 83 | ok. 84 | 85 | 86 | -------------------------------------------------------------------------------- /test/ealgo_shield_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ealgo_shield_tests). 2 | -include_lib("eunit/include/eunit.hrl"). 3 | 4 | 5 | all_test_() -> 6 | {foreach, 7 | fun() -> error_logger:tty(false) end, 8 | fun(_) -> error_logger:tty(true) end, 9 | [ 10 | {<<"test detect">>, fun test_detect/0} 11 | ] 12 | }. 13 | 14 | 15 | test_detect() -> 16 | ShieldingWordList = ["abc", "xyz", "甲乙", "哈哈哈哈"], 17 | ShieldingTrie = ealgo_shield:create(ShieldingWordList), 18 | ?assertEqual(ealgo_shield:detect("", ShieldingTrie), false), 19 | ?assertEqual(ealgo_shield:detect("pqr", ShieldingTrie), false), 20 | ?assertEqual(ealgo_shield:detect("abcde", ShieldingTrie), true), 21 | ?assertEqual(ealgo_shield:detect("12abc", ShieldingTrie), true), 22 | ?assertEqual(ealgo_shield:detect("甲乙丙丁", ShieldingTrie), true), 23 | ?assertEqual(ealgo_shield:detect("哈哈哈", ShieldingTrie), false), 24 | ?assertEqual(ealgo_shield:detect("哈哈哈哈", ShieldingTrie), true), 25 | ?assertEqual(ealgo_shield:detect("哈哈哈哈哈", ShieldingTrie), true), 26 | ok. 27 | 28 | -------------------------------------------------------------------------------- /test/ealgo_tests.erl: -------------------------------------------------------------------------------- 1 | -module(ealgo_tests). 2 | -include_lib("eunit/include/eunit.hrl"). 3 | -import(ealgo, [ 4 | cartesian_product/1 5 | , combinations/1 6 | , combinations/2 7 | , permutations/1 8 | , permutations/2 9 | , next_permutation/1 10 | , rabin_karp/2 11 | , rabin_karp/3 12 | , sgn/1 13 | , boole/1 14 | , ustep/1 15 | , id/1 16 | ]). 17 | 18 | 19 | all_test_() -> 20 | {foreach, 21 | fun() -> error_logger:tty(false) end, 22 | fun(_) -> error_logger:tty(true) end, 23 | [ 24 | {<<"test cartesian_product">>, fun test_cartesian_product/0}, 25 | {<<"test combinations">>, fun test_combinations/0}, 26 | {<<"test permutations">>, fun test_permutations/0}, 27 | {<<"test next_permutation">>, fun test_next_permutation/0}, 28 | {<<"test rabin_karp">>, fun test_rabin_karp/0}, 29 | {<<"test sgn">>, fun test_sgn/0}, 30 | {<<"test boole">>, fun test_boole/0}, 31 | {<<"test ustep">>, fun test_ustep/0}, 32 | {<<"test id">>, fun test_id/0} 33 | ] 34 | }. 35 | 36 | 37 | test_cartesian_product() -> 38 | ?assertEqual(cartesian_product([]) 39 | , [[]]), 40 | ?assertEqual(cartesian_product([[]]) 41 | , []), 42 | ?assertEqual(cartesian_product([[a]]) 43 | , [[a]]), 44 | ?assertEqual(cartesian_product([[a, b, c]]) 45 | , [[a], [b], [c]]), 46 | ?assertEqual(cartesian_product([[a, b, c], []]) 47 | , []), 48 | ?assertEqual(cartesian_product([[], [a, b, c]]) 49 | , []), 50 | ?assertEqual(cartesian_product([[a, b, c], [x, y, z]]) 51 | , [[a,x], [a,y], [a,z], [b,x], [b,y], [b,z], [c,x], [c,y], [c,z]]), 52 | ?assertEqual(cartesian_product([[a, b], [p, q], [x, y]]) 53 | , [[a,p,x], [a,p,y], [a,q,x], [a,q,y], [b,p,x], [b,p,y], [b,q,x], [b,q,y]]), 54 | ok. 55 | 56 | 57 | test_combinations() -> 58 | ?assertEqual(combinations([]) 59 | , [[]]), 60 | ?assertEqual(combinations([a]) 61 | , [[], [a]]), 62 | ?assertEqual(combinations([a, b]) 63 | , [[], [b], [a], [a, b]]), 64 | ?assertEqual(combinations([a, b, c]) 65 | , [[], [c], [b], [b, c], [a], [a, c], [a, b], [a, b, c]]), 66 | ?assertEqual(combinations([], 0) 67 | , [[]]), 68 | ?assertEqual(combinations([], 1) 69 | , []), 70 | ?assertEqual(combinations([a, b, c], 0) 71 | , [[]]), 72 | ?assertEqual(combinations([a, b, c], 1) 73 | , [[a], [b], [c]]), 74 | ?assertEqual(combinations([a, b, c], 2) 75 | , [[a, b], [a, c], [b, c]]), 76 | ?assertEqual(combinations([a, b, c], 3) 77 | , [[a, b, c]]), 78 | ?assertEqual(combinations([a, b, c], 4) 79 | , []), 80 | ok. 81 | 82 | 83 | test_permutations() -> 84 | ?assertEqual(permutations([]) 85 | , [[]]), 86 | ?assertEqual(permutations([a]) 87 | , [[a]]), 88 | ?assertEqual(permutations([a, b]) 89 | , [[a, b], [b, a]]), 90 | ?assertEqual(permutations([a, b, c]) 91 | , [[a,b,c], [a,c,b], [b,a,c], [b,c,a], [c,a,b], [c,b,a]]), 92 | ?assertEqual(permutations([], 0) 93 | , [[]]), 94 | ?assertEqual(permutations([], 1) 95 | , []), 96 | ?assertEqual(permutations([a, b, c], 0) 97 | , [[]]), 98 | ?assertEqual(permutations([a, b, c], 1) 99 | , [[a], [b], [c]]), 100 | ?assertEqual(permutations([a, b, c], 2) 101 | , [[a, b], [a, c], [b, a], [b, c], [c, a], [c, b]]), 102 | ?assertEqual(permutations([a, b, c], 3) 103 | , [[a,b,c], [a,c,b], [b,a,c], [b,c,a], [c,a,b], [c,b,a]]), 104 | ?assertEqual(permutations([a, b, c], 4) 105 | , []), 106 | ok. 107 | 108 | 109 | test_next_permutation() -> 110 | ?assertEqual(next_permutation([]) 111 | , false), 112 | ?assertEqual(next_permutation([a]) 113 | , false), 114 | ?assertEqual(next_permutation([a, b]) 115 | , {true, [b, a]}), 116 | ?assertEqual(next_permutation([b, a]) 117 | , false), 118 | ?assertEqual(next_permutation([a, b, c]) 119 | , {true, [a, c, b]}), 120 | ?assertEqual(next_permutation([a, c, b]) 121 | , {true, [b, a, c]}), 122 | ?assertEqual(next_permutation([b, a, c]) 123 | , {true, [b, c, a]}), 124 | ?assertEqual(next_permutation([b, c, a]) 125 | , {true, [c, a, b]}), 126 | ?assertEqual(next_permutation([c, a, b]) 127 | , {true, [c, b, a]}), 128 | ?assertEqual(next_permutation([c, b, a]) 129 | , false), 130 | ok. 131 | 132 | 133 | test_rabin_karp() -> 134 | ?assertEqual(rabin_karp("", "") 135 | , [1]), 136 | ?assertEqual(rabin_karp("", "", 0) 137 | , []), 138 | ?assertEqual(rabin_karp("", "", -1) 139 | , [1]), 140 | ?assertEqual(rabin_karp("", "", 1) 141 | , [1]), 142 | ?assertEqual(rabin_karp("", "a") 143 | , []), 144 | ?assertEqual(rabin_karp("", "a", 0) 145 | , []), 146 | ?assertEqual(rabin_karp("", "a", -1) 147 | , []), 148 | ?assertEqual(rabin_karp("", "a", 1) 149 | , []), 150 | ?assertEqual(rabin_karp("a", "a") 151 | , [1]), 152 | ?assertEqual(rabin_karp("a", "a", 0) 153 | , []), 154 | ?assertEqual(rabin_karp("a", "a", -1) 155 | , [1]), 156 | ?assertEqual(rabin_karp("a", "a", 1) 157 | , [1]), 158 | ?assertEqual(rabin_karp("aba", "a") 159 | , [1, 3]), 160 | ?assertEqual(rabin_karp("aba", "a", 0) 161 | , []), 162 | ?assertEqual(rabin_karp("aba", "a", -1) 163 | , [1, 3]), 164 | ?assertEqual(rabin_karp("aba", "a", 1) 165 | , [1]), 166 | Text = "It was the best of times, " 167 | "it was the worst of times; " 168 | "it was the age of wisdom, " 169 | "it was the age of foolishness; " 170 | "it was the epoch of belief, " 171 | "it was the epoch of incredulity; " 172 | "it was the season of light, " 173 | "it was the season of darkness; " 174 | "it was the spring of hope, " 175 | "it was the winter of despair; " 176 | "we had everything before us, " 177 | "we had nothing before us; " 178 | "we were all going direct to Heaven, " 179 | "we were all going direct the other way.", 180 | ?assertEqual(rabin_karp(Text, "it") 181 | , [27, 54, 80, 111, 139, 167, 172, 200, 231, 258]), 182 | ok. 183 | 184 | 185 | test_sgn() -> 186 | ?assertEqual(sgn(0) 187 | , 0), 188 | ?assertEqual(sgn(1) 189 | , 1), 190 | ?assertEqual(sgn(2) 191 | , 1), 192 | ?assertEqual(sgn(-1) 193 | , -1), 194 | ?assertEqual(sgn(-2) 195 | , -1), 196 | ?assertEqual(sgn(0.000000000001) 197 | , 1), 198 | ?assertEqual(sgn(-0.000000000001) 199 | , -1), 200 | ?assertEqual(sgn(12345678901234567890) 201 | , 1), 202 | ?assertEqual(sgn(-12345678901234567890) 203 | , -1), 204 | ok. 205 | 206 | 207 | test_boole() -> 208 | ?assertEqual(boole(true) 209 | , 1), 210 | ?assertEqual(boole(false) 211 | , 0), 212 | ok. 213 | 214 | 215 | test_ustep() -> 216 | ?assertEqual(ustep(0) 217 | , 1), 218 | ?assertEqual(ustep(1) 219 | , 1), 220 | ?assertEqual(ustep(2) 221 | , 1), 222 | ?assertEqual(ustep(-1) 223 | , 0), 224 | ?assertEqual(ustep(-2) 225 | , 0), 226 | ?assertEqual(ustep(0.000000000001) 227 | , 1), 228 | ?assertEqual(ustep(-0.000000000001) 229 | , 0), 230 | ?assertEqual(ustep(12345678901234567890) 231 | , 1), 232 | ?assertEqual(ustep(-12345678901234567890) 233 | , 0), 234 | ok. 235 | 236 | 237 | test_id() -> 238 | ?assertEqual(id(233) 239 | , 233), 240 | ok. 241 | 242 | --------------------------------------------------------------------------------