1#![cfg_attr(feature = "strict", deny(warnings))]
30#![deny(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
31 trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
32 unused_qualifications)]
33
34extern crate bincode;
35extern crate byteorder;
36extern crate rayon;
37extern crate serde;
38#[macro_use]
39extern crate serde_derive;
40
41pub mod legacy;
42pub mod rusty;
43
44pub use legacy::LegacyWebOfTrust;
45
46use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
47
48use std::io::prelude::*;
49use std::fs;
50use std::fs::File;
51
52#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
54pub struct NodeId(pub usize);
55
56#[derive(Debug, Copy, Clone, PartialEq, Eq)]
59pub enum NewLinkResult {
60 Ok(usize),
62 AlreadyCertified(usize),
64 AllCertificationsUsed(usize),
66 UnknownSource(),
68 UnknownTarget(),
70 SelfLinkingForbidden(),
72}
73
74#[derive(Debug, Copy, Clone, PartialEq, Eq)]
77pub enum RemLinkResult {
78 Removed(usize),
80 UnknownCert(usize),
82 UnknownSource(),
84 UnknownTarget(),
86}
87
88#[derive(Debug)]
90pub enum WotParseError {
91 FailToOpenFile(std::io::Error),
93
94 IOError(std::io::Error),
96}
97
98impl From<std::io::Error> for WotParseError {
99 fn from(e: std::io::Error) -> WotParseError {
100 WotParseError::IOError(e)
101 }
102}
103
104#[derive(Debug)]
106pub enum WotWriteError {
107 WrongWotSize(),
109
110 FailToCreateFile(std::io::Error),
112
113 FailToWriteInFile(std::io::Error),
115}
116
117impl From<std::io::Error> for WotWriteError {
118 fn from(e: std::io::Error) -> WotWriteError {
119 WotWriteError::FailToWriteInFile(e)
120 }
121}
122
123#[derive(Debug, Copy, Clone, PartialEq, Eq)]
125pub enum HasLinkResult {
126 Link(bool),
128 UnknownSource(),
130 UnknownTarget(),
132}
133
134#[derive(Debug, Copy, Clone, PartialEq)]
136pub struct WotDistanceParameters {
137 pub node: NodeId,
139 pub sentry_requirement: u32,
141 pub step_max: u32,
143 pub x_percent: f64,
145}
146
147#[derive(Debug, Copy, Clone, PartialEq, Eq)]
149pub struct WotDistance {
150 pub sentries: u32,
152 pub success: u32,
154 pub success_at_border: u32,
156 pub reached: u32,
158 pub reached_at_border: u32,
160 pub outdistanced: bool,
162}
163
164pub trait WebOfTrust {
168 fn get_max_link(&self) -> usize;
170
171 fn set_max_link(&mut self, max_link: usize);
173
174 fn add_node(&mut self) -> NodeId;
176
177 fn rem_node(&mut self) -> Option<NodeId>;
180
181 fn size(&self) -> usize;
183
184 fn is_enabled(&self, id: NodeId) -> Option<bool>;
187
188 fn set_enabled(&mut self, id: NodeId, enabled: bool) -> Option<bool>;
191
192 fn get_enabled(&self) -> Vec<NodeId>;
194
195 fn get_disabled(&self) -> Vec<NodeId>;
197
198 fn add_link(&mut self, source: NodeId, target: NodeId) -> NewLinkResult;
200
201 fn rem_link(&mut self, source: NodeId, target: NodeId) -> RemLinkResult;
203
204 fn has_link(&self, source: NodeId, target: NodeId) -> HasLinkResult;
206
207 fn get_links_source(&self, target: NodeId) -> Option<Vec<NodeId>>;
210
211 fn issued_count(&self, id: NodeId) -> Option<usize>;
214
215 fn get_sentries(&self, sentry_requirement: usize) -> Vec<NodeId>;
217
218 fn get_non_sentries(&self, sentry_requirement: usize) -> Vec<NodeId>;
220
221 fn get_paths(&self, from: NodeId, to: NodeId, k_max: u32) -> Vec<Vec<NodeId>>;
223
224 fn compute_distance(&self, params: WotDistanceParameters) -> Option<WotDistance>;
227
228 fn is_outdistanced(&self, params: WotDistanceParameters) -> Option<bool>;
231
232 fn from_file(&mut self, path: &str) -> Result<Vec<u8>, WotParseError> {
234 let file_size = fs::metadata(path).expect("fail to read wotb file !").len();
235 let mut file_pointing_to_blockstamp_size: Vec<u8> = vec![0; file_size as usize];
236 match File::open(path) {
237 Ok(mut file) => {
238 file.read_exact(&mut file_pointing_to_blockstamp_size.as_mut_slice())?;
239 }
240 Err(e) => return Err(WotParseError::FailToOpenFile(e)),
241 };
242 let mut file_pointing_to_blockstamp = file_pointing_to_blockstamp_size.split_off(4);
244 let mut buf = &file_pointing_to_blockstamp_size[..];
246 let blockstamp_size = buf.read_u32::<BigEndian>().unwrap();
247 let mut file_pointing_to_nodes_count =
249 file_pointing_to_blockstamp.split_off(blockstamp_size as usize);
250 let mut file_pointing_to_nodes_states = file_pointing_to_nodes_count.split_off(4);
252 let mut buf = &file_pointing_to_nodes_count[..];
254 let nodes_count = buf.read_u32::<BigEndian>().unwrap();
255 let nodes_states_size = match nodes_count % 8 {
257 0 => nodes_count / 8,
258 _ => (nodes_count / 8) + 1,
259 };
260 let file_pointing_to_links =
262 file_pointing_to_nodes_states.split_off(nodes_states_size as usize);
263 let mut count_remaining_nodes = nodes_count;
265 for byte in file_pointing_to_nodes_states {
266 let mut byte_integer = u8::from_be(byte);
267 let mut factor: u8 = 128;
268 for _i in 0..8 {
269 if count_remaining_nodes > 0 {
270 self.add_node();
271 if byte_integer >= factor {
272 byte_integer -= factor;
273 } else {
274 let _test = self.set_enabled(
275 NodeId((nodes_count - count_remaining_nodes) as usize),
276 false,
277 );
278 }
279 count_remaining_nodes -= 1;
280 }
281 factor /= 2;
282 }
283 }
284 let mut buffer_3b: Vec<u8> = Vec::with_capacity(3);
286 let mut count_bytes = 0;
287 let mut remaining_links: u8 = 0;
288 let mut target: u32 = 0;
289 for byte in file_pointing_to_links {
290 if remaining_links == 0 {
291 target += 1;
292 remaining_links = u8::from_be(byte);
293 count_bytes = 0;
294 } else {
295 buffer_3b.push(byte);
296 if count_bytes % 3 == 2 {
297 let mut buf = &buffer_3b.clone()[..];
298 let source = buf.read_u24::<BigEndian>().expect("fail to parse source");
299 self.add_link(NodeId(source as usize), NodeId((target - 1) as usize));
300 remaining_links -= 1;
301 buffer_3b.clear();
302 }
303 count_bytes += 1;
304 }
305 }
306 Ok(file_pointing_to_blockstamp)
307 }
308
309 fn to_file(&self, path: &str, blockstamp: &[u8]) -> Result<(), WotWriteError> {
311 let mut buffer: Vec<u8> = Vec::new();
312 let blockstamp_size = blockstamp.len() as u32;
314 let mut bytes: Vec<u8> = Vec::with_capacity(4);
315 bytes.write_u32::<BigEndian>(blockstamp_size).unwrap();
316 buffer.append(&mut bytes);
317 buffer.append(&mut blockstamp.to_vec());
319 let nodes_count = self.size() as u32;
321 let mut bytes: Vec<u8> = Vec::with_capacity(4);
322 bytes.write_u32::<BigEndian>(nodes_count).unwrap();
323 buffer.append(&mut bytes);
324 let mut enable_states: u8 = 0;
326 let mut factor: u8 = 128;
327 for n in 0..nodes_count {
328 match self.is_enabled(NodeId(n as usize)) {
329 Some(enable) => {
330 if enable {
331 enable_states += factor;
332 }
333 }
334 None => {
335 return Err(WotWriteError::WrongWotSize());
336 }
337 }
338 if n % 8 == 7 {
339 factor = 128;
340 let mut tmp_buf = Vec::with_capacity(1);
341 tmp_buf.write_u8(enable_states).unwrap();
342 buffer.append(&mut tmp_buf);
343 enable_states = 0;
344 } else {
345 factor /= 2;
346 }
347 }
348 if nodes_count % 8 != 7 {
350 let mut tmp_buf = Vec::with_capacity(1);
351 tmp_buf.write_u8(enable_states).unwrap();
352 buffer.append(&mut tmp_buf);
353 }
354 for n in 0..nodes_count {
356 if let Some(sources) = self.get_links_source(NodeId(n as usize)) {
357 let mut bytes = Vec::with_capacity(1);
359 bytes.write_u8(sources.len() as u8).unwrap();
360 buffer.append(&mut bytes);
361 for source in &sources {
362 let mut bytes: Vec<u8> = Vec::with_capacity(3);
364 bytes.write_u24::<BigEndian>(source.0 as u32).unwrap();
365 buffer.append(&mut bytes);
366 }
367 };
368 }
369 let mut file = match File::create(path) {
371 Ok(file) => file,
372 Err(e) => return Err(WotWriteError::FailToCreateFile(e)),
373 };
374 file.write_all(&buffer)?;
376
377 Ok(())
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384
385 pub fn generic_wot_test<T: WebOfTrust, F>(generator: F)
390 where
391 F: Fn(usize) -> T,
392 {
393 let mut wot = generator(3);
394
395 assert_eq!(wot.size(), 0);
397
398 assert_eq!(wot.is_enabled(NodeId(0)), None);
400 assert_eq!(wot.is_enabled(NodeId(23)), None);
401
402 assert_eq!(wot.add_node(), NodeId(0));
405 assert_eq!(wot.size(), 1);
406 assert_eq!(wot.get_disabled().len(), 0);
407
408 assert_eq!(wot.add_node(), NodeId(1));
410 assert_eq!(wot.size(), 2);
411 assert_eq!(wot.get_disabled().len(), 0);
412
413 for i in 0..10 {
415 assert_eq!(wot.add_node(), NodeId(i + 2));
416 }
417
418 assert_eq!(wot.size(), 12);
419
420 assert_eq!(
422 wot.add_link(NodeId(0), NodeId(0)),
423 NewLinkResult::SelfLinkingForbidden()
424 );
425
426 assert_eq!(wot.add_link(NodeId(0), NodeId(1)), NewLinkResult::Ok(1));
428 assert_eq!(wot.add_link(NodeId(0), NodeId(2)), NewLinkResult::Ok(1));
429 assert_eq!(wot.add_link(NodeId(0), NodeId(3)), NewLinkResult::Ok(1));
430 assert_eq!(
431 wot.add_link(NodeId(0), NodeId(4)),
432 NewLinkResult::AllCertificationsUsed(0)
433 );
434
435 assert_eq!(wot.get_max_link(), 3);
436 assert_eq!(
437 wot.has_link(NodeId(0), NodeId(1)),
438 HasLinkResult::Link(true)
439 );
440 assert_eq!(
441 wot.has_link(NodeId(0), NodeId(2)),
442 HasLinkResult::Link(true)
443 );
444 assert_eq!(
445 wot.has_link(NodeId(0), NodeId(3)),
446 HasLinkResult::Link(true)
447 );
448 assert_eq!(
449 wot.has_link(NodeId(0), NodeId(4)),
450 HasLinkResult::Link(false)
451 );
452
453 wot.set_max_link(4);
454 assert_eq!(wot.get_max_link(), 4);
455 assert_eq!(
456 wot.has_link(NodeId(0), NodeId(4)),
457 HasLinkResult::Link(false)
458 );
459 wot.add_link(NodeId(0), NodeId(4));
460 assert_eq!(
461 wot.has_link(NodeId(0), NodeId(4)),
462 HasLinkResult::Link(true)
463 );
464 wot.rem_link(NodeId(0), NodeId(1));
465 wot.rem_link(NodeId(0), NodeId(2));
466 wot.rem_link(NodeId(0), NodeId(3));
467 wot.rem_link(NodeId(0), NodeId(4));
468
469 assert_eq!(
471 wot.has_link(NodeId(0), NodeId(6)),
472 HasLinkResult::Link(false)
473 );
474 assert_eq!(
475 wot.has_link(NodeId(23), NodeId(0)),
476 HasLinkResult::UnknownSource()
477 );
478 assert_eq!(
479 wot.has_link(NodeId(2), NodeId(53)),
480 HasLinkResult::UnknownTarget()
481 );
482
483 assert_eq!(wot.is_enabled(NodeId(0)), Some(true));
485 assert_eq!(wot.is_enabled(NodeId(1)), Some(true));
486 assert_eq!(wot.is_enabled(NodeId(2)), Some(true));
487 assert_eq!(wot.is_enabled(NodeId(3)), Some(true));
488 assert_eq!(wot.is_enabled(NodeId(11)), Some(true));
489
490 assert_eq!(wot.set_enabled(NodeId(0), false), Some(false));
492 assert_eq!(wot.set_enabled(NodeId(1), false), Some(false));
493 assert_eq!(wot.set_enabled(NodeId(2), false), Some(false));
494 assert_eq!(wot.get_disabled().len(), 3);
495 assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));
496
497 assert_eq!(wot.is_enabled(NodeId(0)), Some(false));
499 assert_eq!(wot.is_enabled(NodeId(1)), Some(true));
500 assert_eq!(wot.is_enabled(NodeId(2)), Some(false));
501 assert_eq!(wot.is_enabled(NodeId(3)), Some(true));
502 assert_eq!(wot.set_enabled(NodeId(0), true), Some(true));
504 assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));
505 assert_eq!(wot.set_enabled(NodeId(2), true), Some(true));
506 assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));
507 assert_eq!(wot.get_disabled().len(), 0);
508
509 assert_eq!(
511 wot.has_link(NodeId(2), NodeId(0)),
512 HasLinkResult::Link(false)
513 );
514
515 assert_eq!(wot.add_link(NodeId(2), NodeId(0)), NewLinkResult::Ok(1));
517 assert_eq!(wot.add_link(NodeId(4), NodeId(0)), NewLinkResult::Ok(2));
518 assert_eq!(
519 wot.add_link(NodeId(4), NodeId(0)),
520 NewLinkResult::AlreadyCertified(2)
521 );
522 assert_eq!(
523 wot.add_link(NodeId(4), NodeId(0)),
524 NewLinkResult::AlreadyCertified(2)
525 );
526 assert_eq!(wot.add_link(NodeId(5), NodeId(0)), NewLinkResult::Ok(3));
527
528 assert_eq!(
537 wot.has_link(NodeId(2), NodeId(0)),
538 HasLinkResult::Link(true)
539 );
540 assert_eq!(
541 wot.has_link(NodeId(4), NodeId(0)),
542 HasLinkResult::Link(true)
543 );
544 assert_eq!(
545 wot.has_link(NodeId(5), NodeId(0)),
546 HasLinkResult::Link(true)
547 );
548 assert_eq!(
549 wot.has_link(NodeId(2), NodeId(1)),
550 HasLinkResult::Link(false)
551 );
552
553 assert_eq!(
555 wot.rem_link(NodeId(4), NodeId(0)),
556 RemLinkResult::Removed(2)
557 );
558 assert_eq!(
567 wot.has_link(NodeId(2), NodeId(0)),
568 HasLinkResult::Link(true)
569 );
570 assert_eq!(
571 wot.has_link(NodeId(4), NodeId(0)),
572 HasLinkResult::Link(false)
573 );
574 assert_eq!(
575 wot.has_link(NodeId(5), NodeId(0)),
576 HasLinkResult::Link(true)
577 );
578 assert_eq!(
579 wot.has_link(NodeId(2), NodeId(1)),
580 HasLinkResult::Link(false)
581 );
582
583 assert_eq!(
585 wot.is_outdistanced(WotDistanceParameters {
586 node: NodeId(0),
587 sentry_requirement: 1,
588 step_max: 1,
589 x_percent: 1.0,
590 },),
591 Some(false)
592 );
593 assert_eq!(
595 wot.is_outdistanced(WotDistanceParameters {
596 node: NodeId(0),
597 sentry_requirement: 2,
598 step_max: 1,
599 x_percent: 1.0,
600 },),
601 Some(false)
602 );
603 assert_eq!(
605 wot.is_outdistanced(WotDistanceParameters {
606 node: NodeId(0),
607 sentry_requirement: 3,
608 step_max: 1,
609 x_percent: 1.0,
610 },),
611 Some(false)
612 );
613 assert_eq!(wot.add_link(NodeId(3), NodeId(1)), NewLinkResult::Ok(1));
617 assert_eq!(wot.add_link(NodeId(3), NodeId(2)), NewLinkResult::Ok(1));
618 assert_eq!(wot.size(), 12);
627 assert_eq!(wot.get_sentries(1).len(), 1);
628 assert_eq!(wot.get_sentries(1)[0], NodeId(2));
629 assert_eq!(wot.get_sentries(2).len(), 0);
630 assert_eq!(wot.get_sentries(3).len(), 0);
631 assert_eq!(wot.get_non_sentries(1).len(), 11); assert_eq!(wot.get_non_sentries(2).len(), 12); assert_eq!(wot.get_non_sentries(3).len(), 12); assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 1).len(), 0); assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2).len(), 1); assert!(wot.get_paths(NodeId(3), NodeId(0), 2).contains(&vec![
637 NodeId(3),
638 NodeId(2),
639 NodeId(0),
640 ]));
641
642 assert_eq!(
643 wot.is_outdistanced(WotDistanceParameters {
644 node: NodeId(0),
645 sentry_requirement: 1,
646 step_max: 1,
647 x_percent: 1.0,
648 },),
649 Some(false)
650 ); assert_eq!(
652 wot.is_outdistanced(WotDistanceParameters {
653 node: NodeId(0),
654 sentry_requirement: 2,
655 step_max: 1,
656 x_percent: 1.0,
657 },),
658 Some(false)
659 ); assert_eq!(
661 wot.is_outdistanced(WotDistanceParameters {
662 node: NodeId(0),
663 sentry_requirement: 3,
664 step_max: 1,
665 x_percent: 1.0,
666 },),
667 Some(false)
668 ); assert_eq!(
670 wot.is_outdistanced(WotDistanceParameters {
671 node: NodeId(0),
672 sentry_requirement: 2,
673 step_max: 2,
674 x_percent: 1.0,
675 },),
676 Some(false)
677 ); wot.add_link(NodeId(1), NodeId(3));
680 wot.add_link(NodeId(2), NodeId(3));
681
682 assert_eq!(wot.size(), 12);
683 assert_eq!(wot.get_sentries(1).len(), 3);
684 assert_eq!(wot.get_sentries(1)[0], NodeId(1));
685 assert_eq!(wot.get_sentries(1)[1], NodeId(2));
686 assert_eq!(wot.get_sentries(1)[2], NodeId(3));
687
688 assert_eq!(wot.get_sentries(2).len(), 1);
689 assert_eq!(wot.get_sentries(2)[0], NodeId(3));
690 assert_eq!(wot.get_sentries(3).len(), 0);
691 assert_eq!(wot.get_non_sentries(1).len(), 9); assert_eq!(wot.get_non_sentries(2).len(), 11); assert_eq!(wot.get_non_sentries(3).len(), 12); assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 1).len(), 0); assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2).len(), 1); assert!(wot.get_paths(NodeId(3), NodeId(0), 2).contains(&vec![
697 NodeId(3),
698 NodeId(2),
699 NodeId(0),
700 ]));
701
702 assert_eq!(
703 wot.is_outdistanced(WotDistanceParameters {
704 node: NodeId(0),
705 sentry_requirement: 1,
706 step_max: 1,
707 x_percent: 1.0,
708 },),
709 Some(true)
710 ); assert_eq!(
712 wot.is_outdistanced(WotDistanceParameters {
713 node: NodeId(0),
714 sentry_requirement: 2,
715 step_max: 1,
716 x_percent: 1.0,
717 },),
718 Some(true)
719 ); assert_eq!(
721 wot.is_outdistanced(WotDistanceParameters {
722 node: NodeId(0),
723 sentry_requirement: 3,
724 step_max: 1,
725 x_percent: 1.0,
726 },),
727 Some(false)
728 ); assert_eq!(
730 wot.is_outdistanced(WotDistanceParameters {
731 node: NodeId(0),
732 sentry_requirement: 2,
733 step_max: 2,
734 x_percent: 1.0,
735 },),
736 Some(false)
737 ); assert_eq!(wot.size(), 12);
741
742 assert_eq!(wot.rem_node(), Some(NodeId(10)));
744
745 assert_eq!(wot.size(), 11);
747
748 assert_eq!(wot.set_enabled(NodeId(3), false), Some(false));
751 assert_eq!(wot.get_disabled().len(), 1);
752 assert_eq!(
753 wot.is_outdistanced(WotDistanceParameters {
754 node: NodeId(0),
755 sentry_requirement: 2,
756 step_max: 1,
757 x_percent: 1.0,
758 },),
759 Some(false)
760 ); assert_eq!(
764 wot.to_file(
765 "test.wot",
766 &[0b0000_0000, 0b0000_0001, 0b0000_0001, 0b0000_0000]
767 ).unwrap(),
768 ()
769 );
770
771 let mut wot2 = generator(3);
772
773 {
775 assert_eq!(
776 wot2.from_file("test.wot").unwrap(),
777 vec![0b0000_0000, 0b0000_0001, 0b0000_0001, 0b0000_0000]
778 );
779 assert_eq!(wot.size(), wot2.size());
780 assert_eq!(
781 wot.get_non_sentries(1).len(),
782 wot2.get_non_sentries(1).len()
783 );
784 assert_eq!(wot.get_disabled().len(), wot2.get_disabled().len());
785 assert_eq!(wot2.get_disabled().len(), 1);
786 assert_eq!(wot2.is_enabled(NodeId(3)), Some(false));
787 assert_eq!(
788 wot2.is_outdistanced(WotDistanceParameters {
789 node: NodeId(0),
790 sentry_requirement: 2,
791 step_max: 1,
792 x_percent: 1.0,
793 },),
794 Some(false)
795 );
796 }
797
798 let mut wot3 = generator(100);
800 assert_eq!(
801 wot3.from_file("tests/g1_genesis.bin").unwrap(),
802 vec![
803 57, 57, 45, 48, 48, 48, 48, 49, 50, 65, 68, 52, 57, 54, 69, 67, 65, 53, 54, 68, 69,
804 48, 66, 56, 69, 53, 68, 54, 70, 55, 52, 57, 66, 55, 67, 66, 69, 55, 56, 53, 53, 51,
805 69, 54, 51, 56, 53, 51, 51, 51, 65, 52, 52, 69, 48, 52, 51, 55, 55, 69, 70, 70, 67,
806 67, 65, 53, 51,
807 ]
808 );
809
810 let members_count = wot3.get_enabled().len() as u64;
812 assert_eq!(members_count, 59);
813
814 assert_eq!(
816 wot3.compute_distance(WotDistanceParameters {
817 node: NodeId(37),
818 sentry_requirement: 3,
819 step_max: 5,
820 x_percent: 0.8,
821 },),
822 Some(WotDistance {
823 sentries: 48,
824 success: 48,
825 success_at_border: 3,
826 reached: 52,
827 reached_at_border: 3,
828 outdistanced: false,
829 },)
830 );
831
832 let wot_size = wot3.size();
834 let members_count = wot3.get_enabled().len() as u64;
835 assert_eq!(members_count, 59);
836 let oriented_couples_count: u64 = members_count * (members_count - 1);
837 let mut centralities = vec![0; wot_size];
838 for i in 0..wot_size {
839 for j in 0..wot_size {
840 let paths = wot3.get_paths(NodeId(i), NodeId(j), 5);
841 let mut intermediate_members: Vec<NodeId> = Vec::new();
842 for path in paths {
843 if path.len() > 2 {
844 for node_id in &path[1..path.len() - 1] {
845 if !intermediate_members.contains(node_id) {
846 intermediate_members.push(*node_id);
847 }
848 }
849 }
850 }
851 let centralities_copy = centralities.clone();
852 for node_id in intermediate_members {
853 let centrality = ¢ralities_copy[node_id.0];
854 if let Some(tmp) = centralities.get_mut(node_id.0) {
855 *tmp = *centrality + 1;
856 }
857 }
858 }
859 }
860 let mut relative_centralities = Vec::with_capacity(wot_size);
861 for centrality in centralities {
862 relative_centralities.push((centrality * 100_000 / oriented_couples_count) as usize);
863 }
864 assert_eq!(relative_centralities.len(), 59);
865 }
866}