/src/state_boolean.erl

https://github.com/lasp-lang/types · Erlang · 231 lines · 148 code · 38 blank · 45 comment · 6 complexity · 9877469cf86e6f28ec3c4b5c66f506b1 MD5 · raw file

  1. %%
  2. %% Copyright (c) 2015-2016 Christopher Meiklejohn. All Rights Reserved.
  3. %%
  4. %% This file is provided to you under the Apache License,
  5. %% Version 2.0 (the "License"); you may not use this file
  6. %% except in compliance with the License. You may obtain
  7. %% a copy of the License at
  8. %%
  9. %% http://www.apache.org/licenses/LICENSE-2.0
  10. %%
  11. %% Unless required by applicable law or agreed to in writing,
  12. %% software distributed under the License is distributed on an
  13. %% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. %% KIND, either express or implied. See the License for the
  15. %% specific language governing permissions and limitations
  16. %% under the License.
  17. %%
  18. %% -------------------------------------------------------------------
  19. %% @doc Boolean primitive CRDT.
  20. %%
  21. %% @reference Carlos Baquero, Paulo Sérgio Almeida, Alcino Cunha and Carla Ferreira
  22. %% Composition of State-based CRDTs (2015)
  23. %% [http://haslab.uminho.pt/cbm/files/crdtcompositionreport.pdf]
  24. -module(state_boolean).
  25. -author("Vitor Enes Duarte <vitorenesduarte@gmail.com>").
  26. -behaviour(type).
  27. -behaviour(state_type).
  28. -define(TYPE, ?MODULE).
  29. -ifdef(TEST).
  30. -include_lib("eunit/include/eunit.hrl").
  31. -endif.
  32. -export([new/0, new/1]).
  33. -export([mutate/3, delta_mutate/3, merge/2]).
  34. -export([query/1, equal/2, is_bottom/1,
  35. is_inflation/2, is_strict_inflation/2,
  36. irreducible_is_strict_inflation/2]).
  37. -export([join_decomposition/1, delta/2, digest/1]).
  38. -export([encode/2, decode/2]).
  39. -export_type([state_boolean/0, state_boolean_op/0]).
  40. -opaque state_boolean() :: {?TYPE, payload()}.
  41. -type payload() :: 0 | 1.
  42. -type state_boolean_op() :: true.
  43. %% @doc Create a new `state_boolean()'
  44. -spec new() -> state_boolean().
  45. new() ->
  46. {?TYPE, 0}.
  47. %% @doc Create a new `state_boolean()'
  48. -spec new([term()]) -> state_boolean().
  49. new([]) ->
  50. new().
  51. %% @doc Mutate a `state_boolean()'.
  52. -spec mutate(state_boolean_op(), type:id(), state_boolean()) ->
  53. {ok, state_boolean()}.
  54. mutate(Op, Actor, {?TYPE, _Boolean}=CRDT) ->
  55. state_type:mutate(Op, Actor, CRDT).
  56. %% @doc Delta-mutate a `state_boolean()'.
  57. %% The first argument can only be `true'.
  58. %% The second argument is the replica id.
  59. %% The third argument is the `state_boolean()' to be inflated.
  60. -spec delta_mutate(state_boolean_op(), type:id(), state_boolean()) ->
  61. {ok, state_boolean()}.
  62. delta_mutate(true, _Actor, {?TYPE, _Boolean}) ->
  63. {ok, {?TYPE, 1}}.
  64. %% @doc Returns the value of the `state_boolean()'.
  65. -spec query(state_boolean()) -> boolean().
  66. query({?TYPE, Boolean}) ->
  67. Boolean == 1.
  68. %% @doc Merge two `state_boolean()'.
  69. %% Join is the logical or.
  70. -spec merge(state_boolean(), state_boolean()) -> state_boolean().
  71. merge({?TYPE, Boolean1}, {?TYPE, Boolean2}) ->
  72. {?TYPE, max(Boolean1, Boolean2)}.
  73. %% @doc Equality for `state_boolean()'.
  74. -spec equal(state_boolean(), state_boolean()) -> boolean().
  75. equal({?TYPE, Boolean1}, {?TYPE, Boolean2}) ->
  76. Boolean1 == Boolean2.
  77. %% @doc Check if a Boolean is bottom.
  78. -spec is_bottom(state_boolean()) -> boolean().
  79. is_bottom({?TYPE, Boolean}) ->
  80. Boolean == 0.
  81. %% @doc Given two `state_boolean()', check if the second is an inflation
  82. %% of the first.
  83. -spec is_inflation(state_boolean(), state_boolean()) -> boolean().
  84. is_inflation({?TYPE, Boolean1}, {?TYPE, Boolean2}) ->
  85. Boolean1 == Boolean2 orelse
  86. (Boolean1 == 0 andalso Boolean2 == 1).
  87. %% @doc Check for strict inflation.
  88. -spec is_strict_inflation(state_boolean(), state_boolean()) -> boolean().
  89. is_strict_inflation({?TYPE, _}=CRDT1, {?TYPE, _}=CRDT2) ->
  90. state_type:is_strict_inflation(CRDT1, CRDT2).
  91. %% @doc Check for irreducible strict inflation.
  92. -spec irreducible_is_strict_inflation(state_boolean(),
  93. state_type:digest()) ->
  94. boolean().
  95. irreducible_is_strict_inflation({?TYPE, _}=A, B) ->
  96. state_type:irreducible_is_strict_inflation(A, B).
  97. -spec digest(state_boolean()) -> state_type:digest().
  98. digest({?TYPE, _}=CRDT) ->
  99. {state, CRDT}.
  100. %% @doc Join decomposition for `state_boolean()'.
  101. -spec join_decomposition(state_boolean()) -> [state_boolean()].
  102. join_decomposition({?TYPE, _}=Boolean) ->
  103. [Boolean].
  104. %% @doc Delta calculation for `state_boolean()'.
  105. -spec delta(state_boolean(), state_type:digest()) -> state_boolean().
  106. delta({?TYPE, _}=A, B) ->
  107. state_type:delta(A, B).
  108. -spec encode(state_type:format(), state_boolean()) -> binary().
  109. encode(erlang, {?TYPE, _}=CRDT) ->
  110. erlang:term_to_binary(CRDT).
  111. -spec decode(state_type:format(), binary()) -> state_boolean().
  112. decode(erlang, Binary) ->
  113. {?TYPE, _} = CRDT = erlang:binary_to_term(Binary),
  114. CRDT.
  115. %% ===================================================================
  116. %% EUnit tests
  117. %% ===================================================================
  118. -ifdef(TEST).
  119. new_test() ->
  120. ?assertEqual({?TYPE, 0}, new()).
  121. query_test() ->
  122. Boolean0 = new(),
  123. Boolean1 = {?TYPE, 1},
  124. ?assertEqual(false, query(Boolean0)),
  125. ?assertEqual(true, query(Boolean1)).
  126. delta_true_test() ->
  127. Boolean0 = new(),
  128. {ok, {?TYPE, Delta1}} = delta_mutate(true, 1, Boolean0),
  129. ?assertEqual({?TYPE, 1}, {?TYPE, Delta1}).
  130. true_test() ->
  131. Boolean0 = new(),
  132. {ok, Boolean1} = mutate(true, 1, Boolean0),
  133. ?assertEqual({?TYPE, 1}, Boolean1).
  134. merge_test() ->
  135. Boolean1 = {?TYPE, 0},
  136. Boolean2 = {?TYPE, 1},
  137. Boolean3 = merge(Boolean1, Boolean1),
  138. Boolean4 = merge(Boolean1, Boolean2),
  139. Boolean5 = merge(Boolean2, Boolean1),
  140. Boolean6 = merge(Boolean2, Boolean2),
  141. ?assertEqual({?TYPE, 0}, Boolean3),
  142. ?assertEqual({?TYPE, 1}, Boolean4),
  143. ?assertEqual({?TYPE, 1}, Boolean5),
  144. ?assertEqual({?TYPE, 1}, Boolean6).
  145. merge_deltas_test() ->
  146. Boolean1 = {?TYPE, 0},
  147. Delta1 = {?TYPE, 0},
  148. Delta2 = {?TYPE, 1},
  149. Boolean3 = merge(Delta1, Boolean1),
  150. Boolean4 = merge(Boolean1, Delta1),
  151. DeltaGroup = merge(Delta1, Delta2),
  152. ?assertEqual({?TYPE, 0}, Boolean3),
  153. ?assertEqual({?TYPE, 0}, Boolean4),
  154. ?assertEqual({?TYPE, 1}, DeltaGroup).
  155. equal_test() ->
  156. Boolean1 = {?TYPE, 0},
  157. Boolean2 = {?TYPE, 1},
  158. ?assert(equal(Boolean1, Boolean1)),
  159. ?assertNot(equal(Boolean1, Boolean2)).
  160. is_bottom_test() ->
  161. Boolean0 = new(),
  162. Boolean1 = {?TYPE, 1},
  163. ?assert(is_bottom(Boolean0)),
  164. ?assertNot(is_bottom(Boolean1)).
  165. is_inflation_test() ->
  166. Boolean1 = {?TYPE, 0},
  167. Boolean2 = {?TYPE, 1},
  168. ?assert(is_inflation(Boolean1, Boolean1)),
  169. ?assert(is_inflation(Boolean1, Boolean2)),
  170. ?assertNot(is_inflation(Boolean2, Boolean1)),
  171. ?assert(is_inflation(Boolean2, Boolean2)),
  172. %% check inflation with merge
  173. ?assert(state_type:is_inflation(Boolean1, Boolean1)),
  174. ?assert(state_type:is_inflation(Boolean1, Boolean2)),
  175. ?assertNot(state_type:is_inflation(Boolean2, Boolean1)),
  176. ?assert(state_type:is_inflation(Boolean2, Boolean2)).
  177. is_strict_inflation_test() ->
  178. Boolean1 = {?TYPE, 0},
  179. Boolean2 = {?TYPE, 1},
  180. ?assertNot(is_strict_inflation(Boolean1, Boolean1)),
  181. ?assert(is_strict_inflation(Boolean1, Boolean2)),
  182. ?assertNot(is_strict_inflation(Boolean2, Boolean1)),
  183. ?assertNot(is_strict_inflation(Boolean2, Boolean2)).
  184. join_decomposition_test() ->
  185. Boolean1 = {?TYPE, 1},
  186. Decomp1 = join_decomposition(Boolean1),
  187. ?assertEqual([Boolean1], Decomp1).
  188. encode_decode_test() ->
  189. Boolean = {?TYPE, 1},
  190. Binary = encode(erlang, Boolean),
  191. EBoolean = decode(erlang, Binary),
  192. ?assertEqual(Boolean, EBoolean).
  193. -endif.