anchor.diff:                   antidote_pb_codec:decode_response(ErrMsg)),
   
       %% Test encoding results
  -    Results = [1, [2]],
  +    Results = [1, [term_to_binary(2)]],
       ResEnc = antidote_pb_codec:encode(read_objects_response,
                                         {ok, lists:zip(Objects, Results)}
                                        ),
  
positive.code: -endif.
  
  -export([encode/2,
           decode/2,
           decode_response/1]).
  
  -define(TYPE_COUNTER, counter).
  -define(TYPE_SET, set).
  
  encode(start_transaction, {Clock, Properties}) ->
      case Clock of
        ignore ->
                #apbstarttransaction{
                           properties = encode(txn_properties, Properties)};
        _ ->
                #apbstarttransaction{timestamp=Clock,
                           properties = encode(txn_properties, Properties)}
      end;
  
  encode(txn_properties, _) ->
   %%TODO: Add more property paramaeters
   #apbtxnproperties{};
  
  encode(abort_transaction, TxId) ->
      #apbaborttransaction{transaction_descriptor = TxId};
  
  encode(commit_transaction, TxId) ->
      #apbcommittransaction{transaction_descriptor = TxId};
  
  encode(update_objects, {Updates, TxId}) ->
     EncUpdates = lists:map(fun(Update) ->
                                    encode(update_op, Update) end,
                            Updates),
     #apbupdateobjects{ updates = EncUpdates, transaction_descriptor = TxId};
  
  encode(update_op, {Object={_Key, Type, _Bucket}, Op, Param}) ->
      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;
  
  encode(static_update_objects, {Clock, Properties, Updates}) ->
      EncTransaction = encode(start_transaction, {Clock, Properties}),
      EncUpdates = lists:map(fun(Update) ->
                                    encode(update_op, Update) end,
                            Updates),
      #apbstaticupdateobjects{transaction = EncTransaction,
                              updates = EncUpdates};
  
  encode(bound_object, {Key, Type, Bucket}) ->
      #apbboundobject{key=Key, type=encode(type,Type), bucket=Bucket};
  
  encode(type, antidote_crdt_counter) -> 'COUNTER';
  encode(type, antidote_crdt_orset) -> 'ORSET';
  encode(type, antidote_crdt_lwwreg) -> 'LWWREG';
  
  encode(reg_update, {assign, Value}) ->
      #apbregupdate{value=Value};
  
  encode(counter_update, {increment, Amount}) ->
      #apbcounterupdate{inc = Amount};
  
  encode(counter_update, {decrement, Amount}) ->
      #apbcounterupdate{inc= -Amount};
  
  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};
  
  
  encode(read_objects, {Objects, TxId}) ->
      BoundObjects = lists:map(fun(Object) ->
                                       encode(bound_object, Object) end,
                               Objects),
      #apbreadobjects{boundobjects = BoundObjects, transaction_descriptor = TxId};
  
  
  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(start_transaction_response, {error, Reason}) ->
      #apbstarttransactionresp{success=false, errorcode = encode(error_code, Reason)};
  
  encode(start_transaction_response, {ok, TxId}) ->
      #apbstarttransactionresp{success=true, transaction_descriptor=term_to_binary(TxId)};
  
  encode(operation_response, {error, Reason}) ->
      #apboperationresp{success=false, errorcode = encode(error_code, Reason)};
  
  encode(operation_response, ok) ->
      #apboperationresp{success=true};
  
  encode(commit_response, {error, Reason}) ->
      #apbcommitresp{success=false, errorcode = encode(error_code, Reason)};
  
  encode(commit_response, {ok, CommitTime}) ->
      #apbcommitresp{success=true, commit_time= term_to_binary(CommitTime)};
  
  encode(read_objects_response, {error, Reason}) ->
      #apbreadobjectsresp{success=false, errorcode = encode(error_code, Reason)};
  
  encode(read_objects_response, {ok, Results}) ->
      EncResults = lists:map(fun(R) ->
                                     encode(read_object_resp, R) end,
                             Results),
      #apbreadobjectsresp{success=true, objects = EncResults};
  
  encode(read_object_resp, {{_Key, antidote_crdt_lwwreg, _Bucket}, Val}) ->
      #apbreadobjectresp{reg=#apbgetregresp{value=term_to_binary(Val)}};
  
  encode(read_object_resp, {{_Key, antidote_crdt_counter, _Bucket}, Val}) ->
      #apbreadobjectresp{counter=#apbgetcounterresp{value=Val}};
  
  encode(read_object_resp, {{_Key, antidote_crdt_orset, _Bucket}, Val}) ->
      #apbreadobjectresp{set=#apbgetsetresp{value=Val}};
  
  encode(static_read_objects_response, {ok, Results, CommitTime}) ->
      #apbstaticreadobjectsresp{
         objects = encode(read_objects_response, {ok, Results}),
         committime = encode(commit_response, {ok, CommitTime})};
  
  %% Add new error codes
  encode(error_code, unknown) -> 0;
  encode(error_code, timeout) -> 1;
  encode(error_code, _Other) -> 0;
  
  encode(_Other, _) ->
      erlang:error("Incorrect operation/Not yet implemented").
  
  decode(txn_properties, _Properties) ->
      {};
  decode(bound_object, #apbboundobject{key = Key, type=Type, bucket=Bucket}) ->
      {Key, decode(type, Type), Bucket};
  
  decode(type, 'COUNTER') -> antidote_crdt_counter;
  decode(type, 'ORSET') -> antidote_crdt_orset;
  decode(type, 'LWWREG') -> antidote_crdt_lwwreg;
  decode(error_code, 0) -> unknown;
  decode(error_code, 1) -> timeout;
  
  decode(update_object, #apbupdateop{boundobject = Object, optype = OpType, counterop = CounterOp, setop = SetOp,
                  regop = RegOp}) ->
      {Op, OpParam} = case OpType of
                   'COUNTER' ->
                       decode(counter_update, CounterOp);
                   'SET' ->
                       decode(set_update, SetOp);
                   'REG' ->
                       decode(reg_update, RegOp)
      end,
      {decode(bound_object, Object), Op, OpParam};
  decode(reg_update, #apbregupdate{value =Value}) ->
      {assign, Value};
  decode(counter_update, #apbcounterupdate{inc = I}) ->
      case I of
          undefined -> {increment, 1};
          I ->  {increment, I} % negative value for I indicates decrement
      end;
  
  decode(set_update, #apbsetupdate{optype = OpType, adds = A, rems = R}) ->
    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;
  
  decode(_Other, _) ->
      erlang:error("Unknown message").
  
  decode_response(#apboperationresp{success = true}) ->
      {opresponse, ok};
  decode_response(#apboperationresp{success = false, errorcode = Reason})->
      {error, decode(error_code, Reason)};
  decode_response(#apbstarttransactionresp{success=true,
                                           transaction_descriptor = TxId}) ->
      {start_transaction, TxId};
  decode_response(#apbstarttransactionresp{success=false, errorcode = Reason}) ->
      {error, decode(error_code, Reason)};
  decode_response(#apbcommitresp{success=true, commit_time = TimeStamp}) ->
      {commit_transaction, TimeStamp};
  decode_response(#apbcommitresp{success=false, errorcode = Reason}) ->
      {error, decode(error_code, Reason)};
  decode_response(#apbreadobjectsresp{success=false, errorcode=Reason}) ->
      {error, decode(error_code, Reason)};
  decode_response(#apbreadobjectsresp{success=true, objects = Objects}) ->
      Resps = lists:map(fun(O) ->
                                decode_response(O) end,
                        Objects),
      {read_objects, Resps};
  decode_response(#apbreadobjectresp{counter = #apbgetcounterresp{value = Val}}) ->
      {counter, Val};
  decode_response(#apbreadobjectresp{set = #apbgetsetresp{value = Val}}) ->
      Res = lists:map(fun(Elem) ->
                                binary_to_term(Elem)
                      end,
                      Val),
      {set, Res};
  decode_response(#apbreadobjectresp{reg = #apbgetregresp{value = Val}}) ->
      {reg, erlang:binary_to_term(Val)};
  decode_response(#apbstaticreadobjectsresp{objects = Objects,
                                            committime = CommitTime}) ->
      {read_objects, Values} = decode_response(Objects),
  
negative.code:     {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}.
  
  
  
  
  
  
  