1#![allow(clippy::items_after_test_module)]
5
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
7use oxilean_kernel::{
8 BinderInfo as HmBI, Declaration as HmDecl, Expr as HmExpr, Level as HmLevel, Name as HmName,
9};
10
11use super::types::{
12 AssocMap, BiMap, FreqMap, IndexedMap, IntervalMap, LruMap, MultiMap, PersistentMap, StackMap,
13};
14
15pub fn build_hashmap_env(env: &mut Environment) -> Result<(), String> {
17 let type1 = Expr::Sort(Level::succ(Level::zero()));
18 let type2 = Expr::Sort(Level::succ(Level::succ(Level::zero())));
19 let hashmap_ty = Expr::Pi(
20 BinderInfo::Default,
21 Name::str("K"),
22 Box::new(type1.clone()),
23 Box::new(Expr::Pi(
24 BinderInfo::Default,
25 Name::str("V"),
26 Box::new(type1.clone()),
27 Box::new(type2.clone()),
28 )),
29 );
30 env.add(Declaration::Axiom {
31 name: Name::str("HashMap"),
32 univ_params: vec![],
33 ty: hashmap_ty,
34 })
35 .map_err(|e| e.to_string())?;
36 let empty_ty = Expr::Pi(
37 BinderInfo::Implicit,
38 Name::str("K"),
39 Box::new(type1.clone()),
40 Box::new(Expr::Pi(
41 BinderInfo::Implicit,
42 Name::str("V"),
43 Box::new(type1.clone()),
44 Box::new(Expr::App(
45 Box::new(Expr::App(
46 Box::new(Expr::Const(Name::str("HashMap"), vec![])),
47 Box::new(Expr::BVar(1)),
48 )),
49 Box::new(Expr::BVar(0)),
50 )),
51 )),
52 );
53 env.add(Declaration::Axiom {
54 name: Name::str("HashMap.empty"),
55 univ_params: vec![],
56 ty: empty_ty,
57 })
58 .map_err(|e| e.to_string())?;
59 let insert_ty = Expr::Pi(
60 BinderInfo::Implicit,
61 Name::str("K"),
62 Box::new(type1.clone()),
63 Box::new(Expr::Pi(
64 BinderInfo::Implicit,
65 Name::str("V"),
66 Box::new(type1.clone()),
67 Box::new(Expr::Pi(
68 BinderInfo::Default,
69 Name::str("k"),
70 Box::new(Expr::BVar(1)),
71 Box::new(Expr::Pi(
72 BinderInfo::Default,
73 Name::str("v"),
74 Box::new(Expr::BVar(1)),
75 Box::new(Expr::Pi(
76 BinderInfo::Default,
77 Name::str("m"),
78 Box::new(Expr::App(
79 Box::new(Expr::App(
80 Box::new(Expr::Const(Name::str("HashMap"), vec![])),
81 Box::new(Expr::BVar(3)),
82 )),
83 Box::new(Expr::BVar(2)),
84 )),
85 Box::new(Expr::App(
86 Box::new(Expr::App(
87 Box::new(Expr::Const(Name::str("HashMap"), vec![])),
88 Box::new(Expr::BVar(4)),
89 )),
90 Box::new(Expr::BVar(3)),
91 )),
92 )),
93 )),
94 )),
95 )),
96 );
97 env.add(Declaration::Axiom {
98 name: Name::str("HashMap.insert"),
99 univ_params: vec![],
100 ty: insert_ty,
101 })
102 .map_err(|e| e.to_string())?;
103 let lookup_ty = Expr::Pi(
104 BinderInfo::Implicit,
105 Name::str("K"),
106 Box::new(type1.clone()),
107 Box::new(Expr::Pi(
108 BinderInfo::Implicit,
109 Name::str("V"),
110 Box::new(type1.clone()),
111 Box::new(Expr::Pi(
112 BinderInfo::Default,
113 Name::str("k"),
114 Box::new(Expr::BVar(1)),
115 Box::new(Expr::Pi(
116 BinderInfo::Default,
117 Name::str("m"),
118 Box::new(Expr::App(
119 Box::new(Expr::App(
120 Box::new(Expr::Const(Name::str("HashMap"), vec![])),
121 Box::new(Expr::BVar(2)),
122 )),
123 Box::new(Expr::BVar(1)),
124 )),
125 Box::new(Expr::App(
126 Box::new(Expr::Const(Name::str("Option"), vec![])),
127 Box::new(Expr::BVar(2)),
128 )),
129 )),
130 )),
131 )),
132 );
133 env.add(Declaration::Axiom {
134 name: Name::str("HashMap.lookup"),
135 univ_params: vec![],
136 ty: lookup_ty,
137 })
138 .map_err(|e| e.to_string())?;
139 Ok(())
140}
141pub type NameExprMap = AssocMap<Name, Expr>;
143pub type StringMap<V> = AssocMap<String, V>;
145pub fn name_expr_map_from_iter(iter: impl IntoIterator<Item = (Name, Expr)>) -> NameExprMap {
147 let mut map = NameExprMap::new();
148 for (k, v) in iter {
149 map.insert(k, v);
150 }
151 map
152}
153pub fn build_hashmap_delete(env: &mut Environment) -> Result<(), String> {
155 let type1 = Expr::Sort(Level::succ(Level::zero()));
156 let delete_ty = Expr::Pi(
157 BinderInfo::Implicit,
158 Name::str("K"),
159 Box::new(type1.clone()),
160 Box::new(Expr::Pi(
161 BinderInfo::Implicit,
162 Name::str("V"),
163 Box::new(type1.clone()),
164 Box::new(Expr::Pi(
165 BinderInfo::Default,
166 Name::str("k"),
167 Box::new(Expr::BVar(1)),
168 Box::new(Expr::Pi(
169 BinderInfo::Default,
170 Name::str("m"),
171 Box::new(Expr::App(
172 Box::new(Expr::App(
173 Box::new(Expr::Const(Name::str("HashMap"), vec![])),
174 Box::new(Expr::BVar(2)),
175 )),
176 Box::new(Expr::BVar(1)),
177 )),
178 Box::new(Expr::App(
179 Box::new(Expr::App(
180 Box::new(Expr::Const(Name::str("HashMap"), vec![])),
181 Box::new(Expr::BVar(3)),
182 )),
183 Box::new(Expr::BVar(2)),
184 )),
185 )),
186 )),
187 )),
188 );
189 env.add(Declaration::Axiom {
190 name: Name::str("HashMap.delete"),
191 univ_params: vec![],
192 ty: delete_ty,
193 })
194 .map_err(|e| e.to_string())
195}
196pub fn build_hashmap_size(env: &mut Environment) -> Result<(), String> {
198 let type1 = Expr::Sort(Level::succ(Level::zero()));
199 let size_ty = Expr::Pi(
200 BinderInfo::Implicit,
201 Name::str("K"),
202 Box::new(type1.clone()),
203 Box::new(Expr::Pi(
204 BinderInfo::Implicit,
205 Name::str("V"),
206 Box::new(type1.clone()),
207 Box::new(Expr::Pi(
208 BinderInfo::Default,
209 Name::str("m"),
210 Box::new(Expr::App(
211 Box::new(Expr::App(
212 Box::new(Expr::Const(Name::str("HashMap"), vec![])),
213 Box::new(Expr::BVar(1)),
214 )),
215 Box::new(Expr::BVar(0)),
216 )),
217 Box::new(Expr::Const(Name::str("Nat"), vec![])),
218 )),
219 )),
220 );
221 env.add(Declaration::Axiom {
222 name: Name::str("HashMap.size"),
223 univ_params: vec![],
224 ty: size_ty,
225 })
226 .map_err(|e| e.to_string())
227}
228pub fn build_hashmap_extended(env: &mut Environment) -> Result<(), String> {
230 build_hashmap_env(env)?;
231 build_hashmap_delete(env)?;
232 build_hashmap_size(env)?;
233 Ok(())
234}
235pub fn count_matching<K: PartialEq + Clone, V: Clone>(
237 map: &AssocMap<K, V>,
238 pred: impl Fn(&K, &V) -> bool,
239) -> usize {
240 map.iter().filter(|(k, v)| pred(k, v)).count()
241}
242pub fn assoc_map_from_vec<K: PartialEq + Clone, V: Clone>(pairs: Vec<(K, V)>) -> AssocMap<K, V> {
244 let mut map = AssocMap::new();
245 for (k, v) in pairs {
246 map.insert(k, v);
247 }
248 map
249}
250#[cfg(test)]
251mod tests {
252 use super::*;
253 fn setup_env() -> Environment {
254 let mut env = Environment::new();
255 let type1 = Expr::Sort(Level::succ(Level::zero()));
256 let type2 = Expr::Sort(Level::succ(Level::succ(Level::zero())));
257 let option_ty = Expr::Pi(
258 BinderInfo::Default,
259 Name::str("α"),
260 Box::new(type1.clone()),
261 Box::new(type2),
262 );
263 env.add(Declaration::Axiom {
264 name: Name::str("Option"),
265 univ_params: vec![],
266 ty: option_ty,
267 })
268 .expect("operation should succeed");
269 env.add(Declaration::Axiom {
270 name: Name::str("Nat"),
271 univ_params: vec![],
272 ty: type1,
273 })
274 .expect("operation should succeed");
275 env
276 }
277 #[test]
278 fn test_build_hashmap_env() {
279 let mut env = setup_env();
280 assert!(build_hashmap_env(&mut env).is_ok());
281 assert!(env.get(&Name::str("HashMap")).is_some());
282 assert!(env.get(&Name::str("HashMap.empty")).is_some());
283 }
284 #[test]
285 fn test_hashmap_insert() {
286 let mut env = setup_env();
287 build_hashmap_env(&mut env).expect("build_hashmap_env should succeed");
288 let decl = env
289 .get(&Name::str("HashMap.insert"))
290 .expect("declaration 'HashMap.insert' should exist in env");
291 assert!(matches!(decl, Declaration::Axiom { .. }));
292 }
293 #[test]
294 fn test_hashmap_lookup() {
295 let mut env = setup_env();
296 build_hashmap_env(&mut env).expect("build_hashmap_env should succeed");
297 let decl = env
298 .get(&Name::str("HashMap.lookup"))
299 .expect("declaration 'HashMap.lookup' should exist in env");
300 assert!(matches!(decl, Declaration::Axiom { .. }));
301 }
302 #[test]
303 fn test_assoc_map_insert_and_get() {
304 let mut map: AssocMap<String, u32> = AssocMap::new();
305 map.insert("a".to_string(), 1);
306 map.insert("b".to_string(), 2);
307 assert_eq!(map.get(&"a".to_string()), Some(&1));
308 assert_eq!(map.get(&"b".to_string()), Some(&2));
309 assert_eq!(map.get(&"c".to_string()), None);
310 }
311 #[test]
312 fn test_assoc_map_overwrite() {
313 let mut map: AssocMap<String, u32> = AssocMap::new();
314 map.insert("x".to_string(), 10);
315 map.insert("x".to_string(), 20);
316 assert_eq!(map.get(&"x".to_string()), Some(&20));
317 assert_eq!(map.len(), 1);
318 }
319 #[test]
320 fn test_assoc_map_remove() {
321 let mut map: AssocMap<String, u32> = AssocMap::new();
322 map.insert("a".to_string(), 1);
323 let removed = map.remove(&"a".to_string());
324 assert_eq!(removed, Some(1));
325 assert!(map.is_empty());
326 }
327 #[test]
328 fn test_assoc_map_merge() {
329 let mut m1: AssocMap<String, u32> = AssocMap::new();
330 m1.insert("a".to_string(), 1);
331 let mut m2: AssocMap<String, u32> = AssocMap::new();
332 m2.insert("b".to_string(), 2);
333 m2.insert("a".to_string(), 99);
334 m1.merge(&m2);
335 assert_eq!(m1.get(&"a".to_string()), Some(&99));
336 assert_eq!(m1.get(&"b".to_string()), Some(&2));
337 }
338 #[test]
339 fn test_assoc_map_retain() {
340 let mut map: AssocMap<String, u32> = AssocMap::new();
341 map.insert("a".to_string(), 1);
342 map.insert("b".to_string(), 2);
343 map.insert("c".to_string(), 3);
344 map.retain(|_, v| *v > 1);
345 assert_eq!(map.len(), 2);
346 assert!(!map.contains_key(&"a".to_string()));
347 }
348 #[test]
349 fn test_persistent_map_insert_get() {
350 let m0: PersistentMap<i32, &str> = PersistentMap::empty();
351 let m1 = m0.insert(1, "one");
352 let m2 = m1.insert(2, "two");
353 assert_eq!(m1.get(&1), Some(&"one"));
354 assert_eq!(m1.get(&2), None);
355 assert_eq!(m2.get(&1), Some(&"one"));
356 assert_eq!(m2.get(&2), Some(&"two"));
357 }
358 #[test]
359 fn test_persistent_map_sorted_output() {
360 let m: PersistentMap<i32, i32> = PersistentMap::empty()
361 .insert(3, 30)
362 .insert(1, 10)
363 .insert(2, 20);
364 let sorted = m.to_sorted_vec();
365 assert_eq!(sorted, vec![(1, 10), (2, 20), (3, 30)]);
366 }
367 #[test]
368 fn test_persistent_map_overwrite() {
369 let m = PersistentMap::empty().insert(1, "old").insert(1, "new");
370 assert_eq!(m.get(&1), Some(&"new"));
371 assert_eq!(m.len(), 1);
372 }
373 #[test]
374 fn test_multi_map_insert_get() {
375 let mut mm: MultiMap<String, u32> = MultiMap::new();
376 mm.insert("k".to_string(), 1);
377 mm.insert("k".to_string(), 2);
378 mm.insert("k".to_string(), 3);
379 assert_eq!(mm.get(&"k".to_string()), &[1, 2, 3]);
380 assert_eq!(mm.total_count(), 3);
381 assert_eq!(mm.key_count(), 1);
382 }
383 #[test]
384 fn test_multi_map_remove() {
385 let mut mm: MultiMap<String, u32> = MultiMap::new();
386 mm.insert("a".to_string(), 10);
387 let vals = mm.remove(&"a".to_string());
388 assert_eq!(vals, vec![10]);
389 assert!(mm.is_empty());
390 }
391 #[test]
392 fn test_name_expr_map() {
393 let nat = Expr::Const(Name::str("Nat"), vec![]);
394 let map = name_expr_map_from_iter(vec![
395 (Name::str("x"), nat.clone()),
396 (Name::str("y"), nat.clone()),
397 ]);
398 assert_eq!(map.get(&Name::str("x")), Some(&nat));
399 assert_eq!(map.len(), 2);
400 }
401 #[test]
402 fn test_build_hashmap_delete() {
403 let mut env = setup_env();
404 build_hashmap_env(&mut env).expect("build_hashmap_env should succeed");
405 assert!(build_hashmap_delete(&mut env).is_ok());
406 assert!(env.get(&Name::str("HashMap.delete")).is_some());
407 }
408 #[test]
409 fn test_build_hashmap_extended() {
410 let mut env = setup_env();
411 assert!(build_hashmap_extended(&mut env).is_ok());
412 assert!(env.get(&Name::str("HashMap.delete")).is_some());
413 assert!(env.get(&Name::str("HashMap.size")).is_some());
414 }
415 #[test]
416 fn test_assoc_map_keys_values() {
417 let mut map: AssocMap<i32, i32> = AssocMap::new();
418 map.insert(1, 10);
419 map.insert(2, 20);
420 let keys: Vec<i32> = map.keys().copied().collect();
421 let vals: Vec<i32> = map.values().copied().collect();
422 assert_eq!(keys, vec![1, 2]);
423 assert_eq!(vals, vec![10, 20]);
424 }
425 #[test]
426 fn test_bimap_insert_lookup() {
427 let mut m: BiMap<String, u32> = BiMap::new();
428 m.insert("a".to_string(), 1);
429 m.insert("b".to_string(), 2);
430 assert_eq!(m.get_by_key(&"a".to_string()), Some(&1));
431 assert_eq!(m.get_by_val(&2), Some(&"b".to_string()));
432 }
433 #[test]
434 fn test_bimap_evicts_on_collision() {
435 let mut m: BiMap<String, u32> = BiMap::new();
436 m.insert("a".to_string(), 1);
437 m.insert("b".to_string(), 1);
438 assert!(m.get_by_key(&"a".to_string()).is_none());
439 }
440 #[test]
441 fn test_lrumap_eviction() {
442 let mut m: LruMap<u32, u32> = LruMap::new(2);
443 m.insert(1, 10);
444 m.insert(2, 20);
445 m.insert(3, 30);
446 assert!(m.peek(&1).is_none());
447 assert_eq!(m.peek(&3), Some(&30));
448 }
449 #[test]
450 fn test_stackmap_shadowing() {
451 let mut m: StackMap<String, u32> = StackMap::new();
452 m.insert("x".to_string(), 1);
453 m.push_scope();
454 m.insert("x".to_string(), 2);
455 assert_eq!(m.get(&"x".to_string()), Some(&2));
456 m.pop_scope();
457 assert_eq!(m.get(&"x".to_string()), Some(&1));
458 }
459 #[test]
460 fn test_freq_map_record() {
461 let mut f: FreqMap<String> = FreqMap::new();
462 f.record("a".to_string());
463 f.record("a".to_string());
464 f.record("b".to_string());
465 assert_eq!(f.count(&"a".to_string()), 2);
466 assert_eq!(f.count(&"b".to_string()), 1);
467 assert_eq!(f.total_count(), 3);
468 }
469 #[test]
470 fn test_freq_map_most_common() {
471 let mut f: FreqMap<u32> = FreqMap::new();
472 f.record(1);
473 f.record(2);
474 f.record(2);
475 f.record(2);
476 let (k, c) = f.most_common().expect("most_common should succeed");
477 assert_eq!(*k, 2);
478 assert_eq!(c, 3);
479 }
480 #[test]
481 fn test_interval_map_query() {
482 let mut m: IntervalMap<u32, &str> = IntervalMap::new();
483 m.insert(0, 10, "a");
484 m.insert(5, 15, "b");
485 m.insert(20, 30, "c");
486 let results = m.query(&7);
487 assert_eq!(results.len(), 2);
488 assert!(results.contains(&&"a"));
489 assert!(results.contains(&&"b"));
490 }
491 #[test]
492 fn test_interval_map_no_match() {
493 let mut m: IntervalMap<u32, &str> = IntervalMap::new();
494 m.insert(0, 5, "a");
495 assert!(m.query(&10).is_empty());
496 }
497 #[test]
498 fn test_indexed_map_insert_get() {
499 let mut m: IndexedMap<String> = IndexedMap::new();
500 m.insert(0, "hello".to_string());
501 m.insert(5, "world".to_string());
502 assert_eq!(m.get(0), Some(&"hello".to_string()));
503 assert_eq!(m.get(5), Some(&"world".to_string()));
504 assert!(m.get(3).is_none());
505 assert_eq!(m.len(), 2);
506 }
507 #[test]
508 fn test_indexed_map_remove() {
509 let mut m: IndexedMap<u32> = IndexedMap::new();
510 m.insert(0, 42);
511 let old = m.remove(0);
512 assert_eq!(old, Some(42));
513 assert!(m.is_empty());
514 }
515 #[test]
516 fn test_indexed_map_iter() {
517 let mut m: IndexedMap<u32> = IndexedMap::new();
518 m.insert(0, 10);
519 m.insert(2, 30);
520 let pairs: Vec<_> = m.iter().collect();
521 assert_eq!(pairs.len(), 2);
522 }
523 #[test]
524 fn test_count_matching() {
525 let mut m: AssocMap<u32, u32> = AssocMap::new();
526 m.insert(1, 10);
527 m.insert(2, 20);
528 m.insert(3, 5);
529 assert_eq!(count_matching(&m, |_, v| *v >= 10), 2);
530 }
531 #[test]
532 fn test_assoc_map_from_vec() {
533 let m = assoc_map_from_vec(vec![("a", 1u32), ("b", 2), ("a", 99)]);
534 assert_eq!(m.get(&"a"), Some(&99));
535 assert_eq!(m.len(), 2);
536 }
537}
538#[allow(dead_code)]
540pub fn hm_prop() -> HmExpr {
541 HmExpr::Sort(HmLevel::zero())
542}
543#[allow(dead_code)]
545pub fn hm_type1() -> HmExpr {
546 HmExpr::Sort(HmLevel::succ(HmLevel::zero()))
547}
548#[allow(dead_code)]
550pub fn hm_type2() -> HmExpr {
551 HmExpr::Sort(HmLevel::succ(HmLevel::succ(HmLevel::zero())))
552}
553#[allow(dead_code)]
555pub fn hm_cst(s: &str) -> HmExpr {
556 HmExpr::Const(HmName::str(s), vec![])
557}
558#[allow(dead_code)]
560pub fn hm_bvar(n: u32) -> HmExpr {
561 HmExpr::BVar(n)
562}
563#[allow(dead_code)]
565pub fn hm_app(f: HmExpr, a: HmExpr) -> HmExpr {
566 HmExpr::App(Box::new(f), Box::new(a))
567}
568#[allow(dead_code)]
570pub fn hm_app2(f: HmExpr, a: HmExpr, b: HmExpr) -> HmExpr {
571 hm_app(hm_app(f, a), b)
572}
573#[allow(dead_code)]
575pub fn hm_app3(f: HmExpr, a: HmExpr, b: HmExpr, c: HmExpr) -> HmExpr {
576 hm_app(hm_app2(f, a, b), c)
577}
578#[allow(dead_code)]
580pub fn hm_pi(bi: HmBI, name: &str, dom: HmExpr, body: HmExpr) -> HmExpr {
581 HmExpr::Pi(bi, HmName::str(name), Box::new(dom), Box::new(body))
582}
583#[allow(dead_code)]
585pub fn hm_arrow(a: HmExpr, b: HmExpr) -> HmExpr {
586 hm_pi(HmBI::Default, "_", a, b)
587}
588#[allow(dead_code)]
590pub fn hm_eq(ty: HmExpr, a: HmExpr, b: HmExpr) -> HmExpr {
591 hm_app3(hm_cst("Eq"), ty, a, b)
592}
593#[allow(dead_code)]
595pub fn hm_ty(k: HmExpr, v: HmExpr) -> HmExpr {
596 hm_app2(hm_cst("HashMap"), k, v)
597}
598#[allow(dead_code)]
600pub fn hm_option(v: HmExpr) -> HmExpr {
601 hm_app(hm_cst("Option"), v)
602}
603#[allow(dead_code)]
605pub fn hm_nat() -> HmExpr {
606 hm_cst("Nat")
607}
608#[allow(dead_code)]
610pub fn hm_bool() -> HmExpr {
611 hm_cst("Bool")
612}
613#[allow(dead_code)]
615pub fn hm_list(a: HmExpr) -> HmExpr {
616 hm_app(hm_cst("List"), a)
617}
618#[allow(dead_code)]
620pub fn hm_ext_forall_map(body: HmExpr) -> HmExpr {
621 hm_pi(
622 HmBI::Implicit,
623 "K",
624 hm_type1(),
625 hm_pi(
626 HmBI::Implicit,
627 "V",
628 hm_type1(),
629 hm_pi(HmBI::Default, "m", hm_ty(hm_bvar(1), hm_bvar(0)), body),
630 ),
631 )
632}
633#[allow(dead_code)]
635pub fn hm_ext_forall_two_maps(body: HmExpr) -> HmExpr {
636 hm_pi(
637 HmBI::Implicit,
638 "K",
639 hm_type1(),
640 hm_pi(
641 HmBI::Implicit,
642 "V",
643 hm_type1(),
644 hm_pi(
645 HmBI::Default,
646 "m1",
647 hm_ty(hm_bvar(1), hm_bvar(0)),
648 hm_pi(HmBI::Default, "m2", hm_ty(hm_bvar(2), hm_bvar(1)), body),
649 ),
650 ),
651 )
652}
653#[allow(dead_code)]
655pub fn hm_ext_map_eq(k: HmExpr, v: HmExpr, a: HmExpr, b: HmExpr) -> HmExpr {
656 hm_eq(hm_ty(k, v), a, b)
657}
658#[allow(dead_code)]
660pub fn hm_ext_get_eq_some(m: HmExpr, k: HmExpr, v: HmExpr, v_ty: HmExpr) -> HmExpr {
661 hm_eq(
662 hm_option(v_ty),
663 hm_app2(hm_cst("HashMap.get"), m, k),
664 hm_app(hm_cst("Option.some"), v),
665 )
666}
667#[allow(dead_code)]
669pub fn hm_ext_get_eq_none(m: HmExpr, k: HmExpr, v_ty: HmExpr) -> HmExpr {
670 hm_eq(
671 hm_option(v_ty),
672 hm_app2(hm_cst("HashMap.get"), m, k),
673 hm_cst("Option.none"),
674 )
675}
676#[allow(dead_code)]
678pub fn hm_ext_forall_map_k_v(body: HmExpr) -> HmExpr {
679 hm_pi(
680 HmBI::Implicit,
681 "K",
682 hm_type1(),
683 hm_pi(
684 HmBI::Implicit,
685 "V",
686 hm_type1(),
687 hm_pi(
688 HmBI::Default,
689 "m",
690 hm_ty(hm_bvar(1), hm_bvar(0)),
691 hm_pi(
692 HmBI::Default,
693 "k",
694 hm_bvar(2),
695 hm_pi(HmBI::Default, "v", hm_bvar(2), body),
696 ),
697 ),
698 ),
699 )
700}