Rust の Itertools で個数可変の直積集合を列挙する

:::note
コード中では `import` や `use` 文（宣言？）を省略します。
:::

# ちょっとした困りごと
Rust の [`itertools`](https://docs.rs/itertools/latest/itertools/index.html) には、順列、組み合わせなどを列挙する関数があり大変便利なんですが、個数を指定できる直積集合を列挙する関数は直接用意されていません。

## Python の等価な関数
Python の `itertools` には、直積集合を列挙する [`product`](https://docs.python.org/ja/3/library/itertools.html#itertools.product) 関数があり、このように使えます。
```python
for a, b, c in itertools.product(range(1, 5), repeat=3):
    print(a, b, c)
```

## iproduct の弱点
Rust でも [`iproduct`](https://docs.rs/itertools/latest/itertools/macro.iproduct.html) マクロを使うことで同様なことを実現できます。
```rust
fn main() {
    for (a, b, c) in iproduct!(1..5, 1..5, 1..5) {
        println!("{} {} {}", a, b, c);
    }
}
```

しかし、 `iproduct` マクロには以下の弱点があります。
- 多くの要素を必要とするとき、記述が長くなる
- イテレータを直接書くため、個数を可変にできない

```rust
fn main() {
    let size = 10; // この値を個数として使いたい
    
    // iproduct!(1..5, size) のように使いたい
    for x in iproduct!(1..5, 1..5, 1..5, 1..5, 1..5, 1..5, 1..5, 1..5, 1..5, 1..5) {
        // 列挙された直積の値の総和を計算し、出力する
        // タプルはイテレータにできないので x.iter().sum() が使えない!
        println!("{}", x.0 + x.1 + x.2 + x.3 + x.4 + x.5 + x.6 + x.7 + x.8 + x.9);
    }
}
        
```

# 解決策
そこで、現状存在するもので作ろうと思います。 [`multi_cartesian_product`](https://docs.rs/itertools/latest/itertools/trait.Itertools.html#method.multi_cartesian_product) を使います。

```rust
fn main() {
    let size = 10; // 要素の個数
    
    for x in (0..size).map(|_| 0..5).multi_cartesian_product() {
        // 列挙された直積の値の総和を計算し、出力する
        println!("{}", x.iter().sum::<u64>());
    }
}
        
```

`multi_cartesian_product` は、イテレータ（にできるもの）のイテレータから、各要素の `Vec` からなる直積集合のイテレータを作ってくれます。今回は複数の集合としてすべて同じものを使いましたが、 `map` 関数の中身を作ることによって異なる集合の直積集合を作れます。

## デメリット
そんな `multi_cartesian_product` ですが、デメリットも存在します。

- 要素の型がすべて同じでなければならない
- パターンマッチングによる代入ができない

ただ、上記の状況の場合 `iproduct` で事足りることが多く、さほど気にならないと思われます。[要出典]

## 使用例

これを使って、 ABC157-C を解きます。

https://atcoder.jp/contests/abc157/tasks/abc157_c

### 問題概要
$N\ (1 \le N \le 3)$ 桁の整数で、各 $i\ (1 \le i \le M \le 5)$ に対し、「左から $s_i$ 番目の数字が $c_i$ である」という条件を満たす整数のうち最小のもの（存在しなければ $-1$ ）を出力してください。 

```rust
fn solve(n: usize, sc: Vec<(usize, i64)>) -> i64 {
    // 判定関数
    let ok = |num: &Vec<i64>| {
        sc.iter().all(|&(s, c)| num[s] == c)
    };
    // コーナーケース: 0
    if n == 1 && ok(&vec![0; n]) {
        return 0;
    }
    // 先頭のみ 1..=9, それ以外 0..=9 の長さ n の直積集合
    for num in (0..n).map(|i| if i == 0 { 1..=9 } else { 0..=9 }).multi_cartesian_product() {
        if ok(&num) {
            return num.iter().fold(0, |acc, &x| acc * 10 + x);
        }
    }
    // 見つからない場合 -1 を返す
    -1
}

fn main() {
    // proconio::input で入力
    input! {
        (n, m): (usize, usize),
        sc: [(Usize1, i64); m],
    }
    println!("{}", solve(n, sc));
}

```

# おわりに
ちょっとこの例微妙じゃないですかね...競プロにおいては、問題を直接解くよりも、ランダムテストで愚直解を書く用途のほうが使えそうです。

## おまけ
bit全探索で使えそうです。普通に整数でループ回したほうが早く書けそうですが、 bit 演算のことを考えなくて済みます。

https://atcoder.jp/contests/abc190/tasks/abc190_c

### 問題概要
人 $i\ (1 \le i \le K \le 16)$ は皿 $C_i$ または皿 $D_i$ のどちらか一方にボールを置きます。「皿 $A_j$ と皿 $B_j$ の両方にボールが置かれている」という条件を満たす数として考えられる最大値を出力してください。

```rust
fn solve(n: usize, ab: &[(usize, usize)], k: usize, cd: &[(usize, usize)]) -> usize {
    let mut ans = 0;
    // k-bit の組み合わせを全列挙
    for choose in (0..k).map(|_| [true, false]).multi_cartesian_product() {
        // 各箱にボールが置かれているか管理する
        let mut placed = vec![false; n];
        for (i, &c) in choose.iter().enumerate() {
            if c {
                placed[cd[i].0] = true;
            } else {
                placed[cd[i].1] = true;
            }
        }
        // 満たす条件の数を数える
        let mut cnt = 0;
        for &(a, b) in ab {
            if placed[a] && placed[b] {
                cnt += 1;
            }
        }
        ans = ans.max(cnt);
    }
    ans
}

fn main() {
    input! {
        (n, m): (usize, usize),
        ab: [(Usize1, Usize1); m],
        k: usize,
        cd: [(Usize1, Usize1); k],
    }
    println!("{}", solve(n, &ab, k, &cd));
}

```