anchor.diff:      {static_read_objects_resp, Values, TimeStamp};
   decode_response(Other) ->
       erlang:error("Unexpected message: ~p",[Other]).
  +
  +%%%%%%%%%%%%%%%%%%%%%%
  +%% Reading objects
  +
  +
  +encode_static_read_objects(Clock, Properties, Objects) ->
  +  EncTransaction = encode_start_transaction(Clock, Properties),
  +  EncObjects = lists:map(fun(Object) ->
  +    encode_bound_object(Object) end,
  +    Objects),
  +  #apbstaticreadobjects{transaction = EncTransaction,
  +    objects = EncObjects}.
  +
  +encode_read_objects(Objects, TxId) ->
  +  BoundObjects = lists:map(fun(Object) ->
  +    encode_bound_object(Object) end,
  +    Objects),
  +  #apbreadobjects{boundobjects = BoundObjects, transaction_descriptor = TxId}.
  +
  +%%%%%%%%%%%%%%%%%%%
  +%% Crdt types
  +
  +encode_type(antidote_crdt_counter) -> 'COUNTER';
  +encode_type(antidote_crdt_orset) -> 'ORSET';
  +encode_type(antidote_crdt_lwwreg) -> 'LWWREG'.
  +
  +decode_type('COUNTER') -> antidote_crdt_counter;
  +decode_type('ORSET') -> antidote_crdt_orset;
  +decode_type('LWWREG') -> antidote_crdt_lwwreg.
  +
  +
  +%%%%%%%%%%%%%%%%%%%%%%
  +% CRDT operations
  +
  +% general encoding of a CRDT operation
  +encode_update_op({Object, Op, Param}) ->
  +  encode_update_op(Object, Op, Param).
  +encode_update_op(Object, Op, Param) ->
  +  {_Key, Type, _Bucket} = Object,
  +  EncObject = encode_bound_object(Object),
  +  case Type of
  +    antidote_crdt_counter -> EncUp = encode_counter_update({Op, Param}),
  +      #apbupdateop{boundobject = EncObject, optype = 'COUNTER', counterop = EncUp};
  +    antidote_crdt_orset -> SetUp = encode_set_update({Op, Param}),
  +      #apbupdateop{boundobject = EncObject, optype = 'SET', setop = SetUp};
  +    antidote_crdt_lwwreg -> EncUp = encode_reg_update({Op, Param}),
  +      #apbupdateop{boundobject = EncObject, optype = 'REG', regop = EncUp}
  +
  +  end.
  +
  +% general encoding of CRDT responses
  +
  +encode_read_object_resp({{_Key, Type, _Bucket}, Val}) ->
  +  encode_read_object_resp(Type, Val).
  +
  +encode_read_object_resp(antidote_crdt_lwwreg, Val) ->
  +    #apbreadobjectresp{reg=#apbgetregresp{value=term_to_binary(Val)}};
  +encode_read_object_resp(antidote_crdt_counter, Val) ->
  +    #apbreadobjectresp{counter=#apbgetcounterresp{value=Val}};
  +encode_read_object_resp(antidote_crdt_orset, Val) ->
  +    #apbreadobjectresp{set=#apbgetsetresp{value=Val}}.
  +
  +
  +% TODO why does this use counter instead of antidote_crdt_counter etc.?
  +decode_read_object_resp(#apbreadobjectresp{counter = #apbgetcounterresp{value = Val}}) ->
  +    {counter, Val};
  +decode_read_object_resp(#apbreadobjectresp{set = #apbgetsetresp{value = Val}}) ->
  +    Res = lists:map(fun(Elem) ->
  +                              binary_to_term(Elem)
  +                    end,
  +                    Val),
  +    {set, Res};
  +decode_read_object_resp(#apbreadobjectresp{reg = #apbgetregresp{value = Val}}) ->
  +    {reg, erlang:binary_to_term(Val)}.
  +
  +
  +% set updates
  +
  +encode_set_update({add, Elem}) ->
  +    #apbsetupdate{optype = 'ADD', adds = [term_to_binary(Elem)]};
  +encode_set_update({add_all, Elems}) ->
  +    BinElems = lists:map(fun(Elem) ->
  +                                term_to_binary(Elem)
  +                        end,
  +                        Elems),
  +    #apbsetupdate{optype = 'ADD', adds = BinElems};
  +encode_set_update({remove, Elem}) ->
  +    #apbsetupdate{optype= 'REMOVE', rems = [term_to_binary(Elem)]};
  +encode_set_update({remove_all, Elems}) ->
  +    BinElems = lists:map(fun(Elem) ->
  +                                term_to_binary(Elem)
  +                        end,
  +                        Elems),
  +    #apbsetupdate{optype = 'REMOVE', rems = BinElems}.
  +
  +decode_set_update(Update) ->
  +  #apbsetupdate{optype = OpType, adds = A, rems = R} = Update,
  +  case OpType of
  +    'ADD' ->
  +      OpsAdd = case A of
  +                 undefined -> [];
  +                 AddElems when is_list(AddElems) -> {add_all, AddElems}
  +               end,
  +      OpsAdd;
  +    'REMOVE' ->
  +      OpsRem = case R of
  +                 undefined -> [];
  +                 Elems when is_list(Elems) -> {remove_all, Elems}
  +               end,
  +      OpsRem
  +  end.
  +
  +% counter updates
  +
  +encode_counter_update({increment, Amount}) ->
  +  #apbcounterupdate{inc = Amount};
  +encode_counter_update({decrement, Amount}) ->
  +  #apbcounterupdate{inc = -Amount}.
  +
  +
  +decode_counter_update(Update) ->
  +  #apbcounterupdate{inc = I} = Update,
  +  case I of
  +    undefined -> {increment, 1};
  +    I -> {increment, I} % negative value for I indicates decrement
  +  end.
  +
  +
  +% register updates
  +
  +encode_reg_update(Update) ->
  +  {assign, Value} = Update,
  +  #apbregupdate{value = Value}.
  +
  +
  +decode_reg_update(Update) ->
  +  #apbregupdate{value = Value} = Update,
  +  {assign, Value}.
  +
  +
  +
   
   
   
  
positive.code:               ],
      TxId = term_to_binary({12}),
           %% Dummy value, structure of TxId is opaque to client
      EncRecord = antidote_pb_codec:encode(update_objects, {Updates, TxId}),
      ?assertMatch(true, is_record(EncRecord, apbupdateobjects)),
      [MsgCode, MsgData] = riak_pb_codec:encode(EncRecord),
      Msg = riak_pb_codec:decode(MsgCode, list_to_binary(MsgData)),
      ?assertMatch(true, is_record(Msg, apbupdateobjects)),
      DecUpdates = lists:map(fun(O) ->
                                  antidote_pb_codec:decode(update_object, O) end,
                          Msg#apbupdateobjects.updates),
      ?assertMatch(Updates, DecUpdates).
  
  error_messages_test() ->
      EncRecord1 = antidote_pb_codec:encode(start_transaction_response,
  
negative.code:     Resp3 = antidote_pb_codec:decode_response(Msg3),
      ?assertMatch(Resp3, {error, unknown}).
  
  -define(TestCrdtOperationCodec(Type, Op, Param),
      ?assertEqual(
        {{<<"key">>, Type, <<"bucket">>}, Op, Param},
        decode_update_op(encode_update_op({<<"key">>, Type, <<"bucket">>}, Op, Param)))
  ).
  
  -define(TestCrdtResponseCodec(Type, ExpectedType, Val),
      ?assertEqual(
        {ExpectedType, Val},
        decode_read_object_resp(encode_read_object_resp(Type, Val)))
  ).
  
  crdt_encode_decode_test() ->
    %% encoding the following operations and decoding them again, should give the same result
  
    ?TestCrdtOperationCodec(antidote_crdt_counter, increment, 1),
    ?TestCrdtResponseCodec(antidote_crdt_counter, counter, 42),
  
    ok.
  
  
  
  -endif.
  
  
  