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
//! 配列上の区間に関する関数。

use std::fmt::Debug;
use std::ops::Bound::{Excluded, Included, Unbounded};
use std::ops::{Range, RangeBounds};

/// 区間を配列サイズに収まるように丸める。
///
/// 与えられた区間 `r` と `0..len` の共通部分を、有界な半開区間として返す。
///
/// # Notes
///
/// 終端が陽に与えられたとき(有限のとき)は out of bounds でもそのまま返す。
/// 潜在的なバグの原因を見逃すのを防ぎたいので。
///
/// # Examples
/// ```
/// use nekolib::utils::bounds_within;
///
/// assert_eq!(bounds_within(.., 7), 0..7);
/// assert_eq!(bounds_within(..=4, 7), 0..5);
/// ```
pub fn bounds_within<R: RangeBounds<usize>>(r: R, len: usize) -> Range<usize> {
    let e_ex = match r.end_bound() {
        Included(&e) => e + 1,
        Excluded(&e) => e,
        Unbounded => len,
    };
    let s_in = match r.start_bound() {
        Included(&s) => s,
        Excluded(&s) => s + 1,
        Unbounded => 0,
    }
    .min(e_ex);
    s_in..e_ex
}

/// 境界チェックを行う。
///
/// # Examples
/// ```
/// use nekolib::utils::check_bounds;
///
/// let a = [0, 1, 2];
/// check_bounds(2, a.len());
/// ```
///
/// ```should_panic
/// use nekolib::utils::check_bounds;
///
/// let a = [0, 1, 2];
/// // panicked at 'index out of bounds: the len is 3 but the index is 3'
/// check_bounds(3, a.len());
/// ```
pub fn check_bounds(i: usize, len: usize) {
    assert!(
        i < len,
        "index out of bounds: the len is {} but the index is {}",
        len,
        i
    );
}

/// 境界チェックを行う。
///
/// # Examples
/// ```
/// use nekolib::utils::check_bounds_range;
///
/// let a = [0, 1, 2];
/// check_bounds_range(2, 0..a.len());
/// check_bounds_range(3, 0..=a.len());
/// ```
///
/// ```should_panic
/// use nekolib::utils::check_bounds_range;
///
/// let a = [0, 1, 2];
/// // panicked at 'index out of bounds: the range is 0..=3 but the index is 4'
/// check_bounds_range(4, 0..=a.len());
/// ```
pub fn check_bounds_range(i: usize, range: impl RangeBounds<usize> + Debug) {
    assert!(
        range.contains(&i),
        "index out of bounds: the range is {:?} but the index is {}",
        range,
        i
    );
}

#[test]
#[should_panic]
fn test_panic_bound_large() { check_bounds_range(4, 0..=3); }

#[test]
#[should_panic]
fn test_panic_bound_small() { check_bounds_range(0, 1..=3); }

#[test]
fn test_check() {
    check_bounds_range(0, 0..=3);
    check_bounds_range(3, 0..=3);
    check_bounds_range(2, 0..3);
}

#[test]
fn test_range() {
    assert_eq!(bounds_within(0..3, 2), 0..3);
    assert_eq!(bounds_within(0..3, 3), 0..3);
    assert_eq!(bounds_within(0..3, 4), 0..3);

    assert_eq!(bounds_within(0.., 2), 0..2);
    assert_eq!(bounds_within(0.., 3), 0..3);
    assert_eq!(bounds_within(0.., 4), 0..4);

    assert_eq!(bounds_within((Excluded(2), Included(5)), 8), 3..6);
    assert_eq!(bounds_within(.., 5), 0..5);
}