1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2020 Graydon Hoare <graydon@pobox.com>
// Licensed under the MIT and Apache-2.0 licenses.

use crate::{CfgLE, StateLE, StateLEExt};
use pergola::{LatticeDef, LatticeElt};
use std::cmp::Ordering;
use im::OrdSet as ArcOrdSet;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};

/// `Participant`s store, exchange and update `Opinion`s about the values in the
/// Cfg and Obj lattices as they attempt to come to an agreement.
///
/// The term `Opinion` does not appear in the paper, but I've introduced it
/// (along with the field names in it) in an attempt to clarify the
/// presentation and bundle-together the 3 variables that are used as a group
/// in both local state and message bodies.
#[derive(Debug)]
pub struct Opinion<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash>
where
    ObjLD::T: Clone + Debug + Default,
{
    /// called vₚ in the paper
    pub estimated_commit: StateLE<ObjLD, Peer>,
    /// called Tₚ in the paper
    pub proposed_configs: ArcOrdSet<CfgLE<Peer>>,
    /// called objₚ in the paper
    pub candidate_object: LatticeElt<ObjLD>,
}

impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> Opinion<ObjLD, Peer>
where
    ObjLD::T: Clone + Debug + Default,
{
    pub fn same_estimated_commit_config(&self, other: &Self) -> bool {
        self.estimated_commit.config() == other.estimated_commit.config()
    }
    pub fn same_estimated_and_proposed_configs(&self, other: &Self) -> bool {
        self.same_estimated_commit_config(other) && self.proposed_configs == other.proposed_configs
    }
}

// Manually implement Default since #[derive(Default)] fails here,
// see bug https://github.com/rust-lang/rust/issues/26925
impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> std::default::Default
    for Opinion<ObjLD, Peer>
where
    ObjLD::T: Clone + Debug + Default,
{
    fn default() -> Self {
        Opinion {
            estimated_commit: StateLE::<ObjLD, Peer>::default(),
            proposed_configs: ArcOrdSet::default(),
            candidate_object: LatticeElt::<ObjLD>::default(),
        }
    }
}

// Manually implement Clone since #[derive(Clone)] fails here,
// see bug https://github.com/rust-lang/rust/issues/26925
impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> std::clone::Clone for Opinion<ObjLD, Peer>
where
    ObjLD::T: Clone + Debug + Default,
{
    fn clone(&self) -> Self {
        Opinion {
            estimated_commit: self.estimated_commit.clone(),
            proposed_configs: self.proposed_configs.clone(),
            candidate_object: self.candidate_object.clone(),
        }
    }
}

// Manually implement PartialEq since #[derive(PartialEq)] fails here,
// see bug https://github.com/rust-lang/rust/issues/26925
impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> std::cmp::PartialEq
    for Opinion<ObjLD, Peer>
where
    ObjLD::T: Clone + Debug + Default + Eq,
{
    fn eq(&self, other: &Self) -> bool {
        let tup1 = (
            &self.estimated_commit,
            &self.proposed_configs,
            &self.candidate_object,
        );
        let tup2 = (
            &other.estimated_commit,
            &other.proposed_configs,
            &other.candidate_object,
        );
        tup1.eq(&tup2)
    }
}

// Manually implement PartialOrd since #[derive(PartialOrd)] fails here,
// see bug https://github.com/rust-lang/rust/issues/26925
impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> std::cmp::PartialOrd
    for Opinion<ObjLD, Peer>
where
    ObjLD::T: Clone + Debug + Default + Eq,
{
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        let tup1 = (
            &self.estimated_commit,
            &self.proposed_configs,
            &self.candidate_object,
        );
        let tup2 = (
            &other.estimated_commit,
            &other.proposed_configs,
            &other.candidate_object,
        );
        tup1.partial_cmp(&tup2)
    }
}

// Conditionally implement Eq for Messages over lattices with equality.
impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> std::cmp::Eq for Opinion<ObjLD, Peer> where
    ObjLD::T: Clone + Debug + Default + Eq
{
}

// Conditionally implement Ord for Messages over ordered lattices.
impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> std::cmp::Ord for Opinion<ObjLD, Peer>
where
    ObjLD::T: Clone + Debug + Default + Ord,
{
    fn cmp(&self, other: &Self) -> Ordering {
        let tup1 = (
            &self.estimated_commit,
            &self.proposed_configs,
            &self.candidate_object,
        );
        let tup2 = (
            &other.estimated_commit,
            &other.proposed_configs,
            &other.candidate_object,
        );
        tup1.cmp(&tup2)
    }
}

impl<ObjLD: LatticeDef, Peer: Ord + Clone + Debug + Hash> Hash for Opinion<ObjLD, Peer>
where
    ObjLD::T: Clone + Debug + Default + Hash,
{
    fn hash<H: Hasher>(&self, hstate: &mut H) {
        self.estimated_commit.hash(hstate);
        self.proposed_configs.hash(hstate);
        self.candidate_object.hash(hstate);
    }
}