std: Adopt explicit generics
b3e2f8924fc671ea03a13f2f9b1a82e7b8a3e14fa44dcc8df5e451b2c98abdb3
Provide a typed vector interface and standard-library coverage using the completed generic contract, then advance the bootstrap seed to the self-hosted generic compiler. Assisted-by: Codex:gpt-5.6
1 parent
2034127d
lib/std/tests.rad
+81 -163
| 4 | 4 | use std::mem; |
|
| 5 | 5 | use std::vec; |
|
| 6 | 6 | use std::testing; |
|
| 7 | 7 | use std::sys::unix; |
|
| 8 | 8 | ||
| 9 | - | // Data types ////////////////////////////////////////////////////////////////// |
|
| 10 | - | ||
| 11 | - | record Point { |
|
| 12 | - | x: i32, |
|
| 13 | - | y: i32, |
|
| 14 | - | } |
|
| 15 | - | ||
| 16 | 9 | // fmt ///////////////////////////////////////////////////////////////////////// |
|
| 17 | 10 | ||
| 18 | 11 | @test fn testFormatU32Zero() throws (testing::TestError) { |
|
| 19 | 12 | let mut buffer: [u8; 11] = [0; 11]; |
|
| 20 | 13 | let result: *[u8] = fmt::formatU32(0, &mut buffer[..]); |
| 338 | 331 | try testing::expect(mem::cmp("abcd", "abc") > 0); |
|
| 339 | 332 | } |
|
| 340 | 333 | ||
| 341 | 334 | // vec ///////////////////////////////////////////////////////////////////////// |
|
| 342 | 335 | ||
| 343 | - | @test fn testVecInitialState() throws (testing::TestError) { |
|
| 344 | - | let mut arena: [u8; 16] align(4) = undefined; |
|
| 345 | - | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 346 | - | ||
| 347 | - | try testing::expect(vec::len(&v) == 0); |
|
| 348 | - | try testing::expect(vec::capacity(&v) == 4); |
|
| 349 | - | } |
|
| 350 | - | ||
| 351 | - | @test fn testVecPushAndGet() throws (testing::TestError) { |
|
| 352 | - | let mut arena: [u8; 16] align(4) = undefined; |
|
| 353 | - | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 354 | - | ||
| 355 | - | let val1: i32 = 42; |
|
| 356 | - | try testing::expect(vec::push(&mut v, &val1)); |
|
| 357 | - | try testing::expect(vec::len(&v) == 1); |
|
| 358 | - | ||
| 359 | - | let ptr = vec::get(&v, 0) else { |
|
| 360 | - | throw testing::TestError::Failed; |
|
| 361 | - | }; |
|
| 362 | - | let i32ptr: *i32 = ptr as *i32; |
|
| 363 | - | let value: i32 = *i32ptr; |
|
| 364 | - | try testing::expect(value == 42); |
|
| 365 | - | } |
|
| 366 | - | ||
| 367 | - | @test fn testVecPushPop() throws (testing::TestError) { |
|
| 368 | - | let mut arena: [u8; 16] align(4) = undefined; |
|
| 369 | - | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 370 | - | ||
| 371 | - | let val1: i32 = 42; |
|
| 372 | - | let val2: i32 = 100; |
|
| 373 | - | let val3: i32 = -5; |
|
| 374 | - | ||
| 375 | - | try testing::expect(vec::push(&mut v, &val1)); |
|
| 376 | - | try testing::expect(vec::len(&v) == 1); |
|
| 377 | - | ||
| 378 | - | try testing::expect(vec::push(&mut v, &val2)); |
|
| 379 | - | try testing::expect(vec::len(&v) == 2); |
|
| 380 | - | ||
| 381 | - | try testing::expect(vec::push(&mut v, &val3)); |
|
| 382 | - | try testing::expect(vec::len(&v) == 3); |
|
| 383 | - | ||
| 384 | - | let mut popped: i32 = 0; |
|
| 385 | - | ||
| 386 | - | try testing::expect(vec::pop(&mut v, &mut popped)); |
|
| 387 | - | try testing::expect(popped == -5); |
|
| 388 | - | try testing::expect(vec::len(&v) == 2); |
|
| 389 | - | ||
| 390 | - | try testing::expect(vec::pop(&mut v, &mut popped)); |
|
| 391 | - | try testing::expect(popped == 100); |
|
| 392 | - | try testing::expect(vec::len(&v) == 1); |
|
| 393 | - | ||
| 394 | - | try testing::expect(vec::pop(&mut v, &mut popped)); |
|
| 395 | - | try testing::expect(popped == 42); |
|
| 396 | - | try testing::expect(vec::len(&v) == 0); |
|
| 397 | - | ||
| 398 | - | try testing::expectNot(vec::pop(&mut v, &mut popped)); |
|
| 399 | - | } |
|
| 400 | - | ||
| 401 | - | @test fn testVecGetSet() throws (testing::TestError) { |
|
| 402 | - | let mut arena: [u8; 32] align(4) = undefined; |
|
| 403 | - | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 404 | - | ||
| 405 | - | let val1: i32 = 10; |
|
| 406 | - | let val2: i32 = 20; |
|
| 407 | - | let val3: i32 = 30; |
|
| 408 | - | ||
| 409 | - | vec::push(&mut v, &val1); |
|
| 410 | - | vec::push(&mut v, &val2); |
|
| 411 | - | vec::push(&mut v, &val3); |
|
| 412 | - | ||
| 413 | - | if let ptr = vec::get(&v, 0) { |
|
| 414 | - | let value: i32 = *(ptr as *i32); |
|
| 415 | - | try testing::expect(value == 10); |
|
| 416 | - | } else { |
|
| 417 | - | throw testing::TestError::Failed; |
|
| 418 | - | } |
|
| 419 | - | ||
| 420 | - | if let ptr = vec::get(&v, 1) { |
|
| 421 | - | let value: i32 = *(ptr as *i32); |
|
| 422 | - | try testing::expect(value == 20); |
|
| 423 | - | } else { |
|
| 424 | - | throw testing::TestError::Failed; |
|
| 425 | - | } |
|
| 426 | - | ||
| 427 | - | if let ptr = vec::get(&v, 2) { |
|
| 428 | - | let value: i32 = *(ptr as *i32); |
|
| 429 | - | try testing::expect(value == 30); |
|
| 430 | - | } else { |
|
| 431 | - | throw testing::TestError::Failed; |
|
| 432 | - | } |
|
| 433 | - | try testing::expect(vec::get(&v, 3) == nil); |
|
| 434 | - | ||
| 435 | - | let new: i32 = 999; |
|
| 436 | - | try testing::expect(vec::put(&mut v, 1, &new)); |
|
| 437 | - | ||
| 438 | - | if let ptr = vec::get(&v, 1) { |
|
| 439 | - | let value: i32 = *(ptr as *i32); |
|
| 440 | - | try testing::expect(value == 999); |
|
| 441 | - | } else { |
|
| 442 | - | throw testing::TestError::Failed; |
|
| 443 | - | } |
|
| 444 | - | try testing::expectNot(vec::put(&mut v, 5, &new)); |
|
| 445 | - | } |
|
| 446 | - | ||
| 447 | - | @test fn testVecCapacity() throws (testing::TestError) { |
|
| 448 | - | let mut arena: [u8; 16] align(4) = undefined; |
|
| 449 | - | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 450 | - | ||
| 451 | - | let val: i32 = 7; |
|
| 452 | - | try testing::expect(vec::push(&mut v, &val)); |
|
| 453 | - | try testing::expect(vec::push(&mut v, &val)); |
|
| 454 | - | try testing::expect(vec::push(&mut v, &val)); |
|
| 455 | - | try testing::expect(vec::push(&mut v, &val)); |
|
| 456 | - | try testing::expect(vec::len(&v) == 4); |
|
| 336 | + | /// Mixed-width value used by vector tests. |
|
| 337 | + | record Obj { |
|
| 338 | + | /// Wide field. |
|
| 339 | + | wide: u64, |
|
| 340 | + | /// Narrow field. |
|
| 341 | + | narrow: u8, |
|
| 342 | + | } |
|
| 343 | + | ||
| 344 | + | instantiate vec::Vec⟨i32⟩, |
|
| 345 | + | vec::init⟨i32⟩, |
|
| 346 | + | vec::len⟨i32⟩, |
|
| 347 | + | vec::capacity⟨i32⟩, |
|
| 348 | + | vec::reset⟨i32⟩, |
|
| 349 | + | vec::get⟨i32⟩, |
|
| 350 | + | vec::push⟨i32⟩, |
|
| 351 | + | vec::put⟨i32⟩, |
|
| 352 | + | vec::pop⟨i32⟩; |
|
| 353 | + | ||
| 354 | + | instantiate vec::Vec⟨Obj⟩, |
|
| 355 | + | vec::init⟨Obj⟩, |
|
| 356 | + | vec::get⟨Obj⟩, |
|
| 357 | + | vec::push⟨Obj⟩, |
|
| 358 | + | vec::put⟨Obj⟩; |
|
| 457 | 359 | ||
| 458 | - | try testing::expectNot(vec::push(&mut v, &val)); |
|
| 459 | - | try testing::expect(vec::len(&v) == 4); |
|
| 460 | - | } |
|
| 461 | - | ||
| 462 | - | @test fn testVecReset() throws (testing::TestError) { |
|
| 463 | - | let mut arena: [u8; 16] align(4) = undefined; |
|
| 464 | - | let mut v = vec::new(&mut arena[..], @sizeOf(i32), @alignOf(i32)); |
|
| 465 | - | ||
| 466 | - | let val: i32 = 123; |
|
| 467 | - | vec::push(&mut v, &val); |
|
| 468 | - | vec::push(&mut v, &val); |
|
| 469 | - | try testing::expect(vec::len(&v) == 2); |
|
| 470 | - | ||
| 471 | - | vec::reset(&mut v); |
|
| 472 | - | try testing::expect(vec::len(&v) == 0); |
|
| 473 | - | ||
| 474 | - | try testing::expect(vec::push(&mut v, &val)); |
|
| 475 | - | try testing::expect(vec::len(&v) == 1); |
|
| 476 | - | } |
|
| 477 | - | ||
| 478 | - | @test fn testVecStruct() throws (testing::TestError) { |
|
| 479 | - | let mut arena: [u8; 64] align(4) = undefined; |
|
| 480 | - | let mut v = vec::new(&mut arena[..], @sizeOf(Point), @alignOf(Point)); |
|
| 481 | - | ||
| 482 | - | let p1: Point = Point { x: 1, y: 2 }; |
|
| 483 | - | let p2: Point = Point { x: 3, y: 4 }; |
|
| 484 | - | ||
| 485 | - | try testing::expect(vec::push(&mut v, &p1)); |
|
| 486 | - | try testing::expect(vec::push(&mut v, &p2)); |
|
| 487 | - | ||
| 488 | - | if let ptr = vec::get(&v, 0) { |
|
| 489 | - | let p: *Point = ptr as *Point; |
|
| 490 | - | try testing::expect(p.x == 1); |
|
| 491 | - | try testing::expect(p.y == 2); |
|
| 492 | - | } else { |
|
| 493 | - | throw testing::TestError::Failed; |
|
| 494 | - | } |
|
| 495 | - | let mut popped: Point = Point { x: 0, y: 0 }; |
|
| 496 | - | ||
| 497 | - | try testing::expect(vec::pop(&mut v, &mut popped)); |
|
| 498 | - | try testing::expect(popped.x == 3); |
|
| 499 | - | try testing::expect(popped.y == 4); |
|
| 360 | + | @test fn testVecInitialState() throws (testing::TestError) { |
|
| 361 | + | let mut arena: [i32; 4] = undefined; |
|
| 362 | + | let mut v: vec::Vec⟨i32⟩ = undefined; |
|
| 363 | + | vec::init⟨i32⟩(&mut v, &mut arena[..]); |
|
| 364 | + | ||
| 365 | + | assert vec::len⟨i32⟩(&v) == 0; |
|
| 366 | + | assert vec::capacity⟨i32⟩(&v) == 4; |
|
| 367 | + | } |
|
| 368 | + | ||
| 369 | + | @test fn testVecGetPut() throws (testing::TestError) { |
|
| 370 | + | let mut arena: [i32; 3] = undefined; |
|
| 371 | + | let mut v: vec::Vec⟨i32⟩ = undefined; |
|
| 372 | + | vec::init⟨i32⟩(&mut v, &mut arena[..]); |
|
| 373 | + | ||
| 374 | + | assert vec::push⟨i32⟩(&mut v, 10); |
|
| 375 | + | assert vec::push⟨i32⟩(&mut v, 20); |
|
| 376 | + | assert vec::push⟨i32⟩(&mut v, 30); |
|
| 377 | + | assert not vec::push⟨i32⟩(&mut v, 40); |
|
| 378 | + | ||
| 379 | + | let first = vec::get⟨i32⟩(&v, 0) |
|
| 380 | + | else throw testing::TestError::Failed; |
|
| 381 | + | assert *first == 10; |
|
| 382 | + | assert vec::get⟨i32⟩(&v, 3) == nil; |
|
| 383 | + | ||
| 384 | + | assert vec::put⟨i32⟩(&mut v, 1, 999); |
|
| 385 | + | let replaced = vec::get⟨i32⟩(&v, 1) |
|
| 386 | + | else throw testing::TestError::Failed; |
|
| 387 | + | assert *replaced == 999; |
|
| 388 | + | assert not vec::put⟨i32⟩(&mut v, 3, 1); |
|
| 389 | + | } |
|
| 390 | + | ||
| 391 | + | @test fn testVecPopReset() throws (testing::TestError) { |
|
| 392 | + | let mut arena: [i32; 2] = undefined; |
|
| 393 | + | let mut v: vec::Vec⟨i32⟩ = undefined; |
|
| 394 | + | vec::init⟨i32⟩(&mut v, &mut arena[..]); |
|
| 395 | + | ||
| 396 | + | assert vec::push⟨i32⟩(&mut v, 42); |
|
| 397 | + | assert vec::push⟨i32⟩(&mut v, 100); |
|
| 398 | + | assert vec::pop⟨i32⟩(&mut v) == 100; |
|
| 399 | + | assert vec::pop⟨i32⟩(&mut v) == 42; |
|
| 400 | + | assert vec::pop⟨i32⟩(&mut v) == nil; |
|
| 401 | + | ||
| 402 | + | assert vec::push⟨i32⟩(&mut v, 7); |
|
| 403 | + | vec::reset⟨i32⟩(&mut v); |
|
| 404 | + | assert vec::len⟨i32⟩(&v) == 0; |
|
| 405 | + | } |
|
| 406 | + | ||
| 407 | + | @test fn testVecRecordElements() throws (testing::TestError) { |
|
| 408 | + | let mut arena: [Obj; 2] = undefined; |
|
| 409 | + | let mut v: vec::Vec⟨Obj⟩ = undefined; |
|
| 410 | + | vec::init⟨Obj⟩(&mut v, &mut arena[..]); |
|
| 411 | + | ||
| 412 | + | assert vec::push⟨Obj⟩(&mut v, Obj { wide: 9, narrow: 1 }); |
|
| 413 | + | assert vec::put⟨Obj⟩(&mut v, 0, Obj { wide: 12, narrow: 2 }); |
|
| 414 | + | let pair = vec::get⟨Obj⟩(&v, 0) |
|
| 415 | + | else throw testing::TestError::Failed; |
|
| 416 | + | assert pair.wide == 12; |
|
| 417 | + | assert pair.narrow == 2; |
|
| 500 | 418 | } |
lib/std/vec.rad
+33 -87
| 1 | - | //! Raw vector: type-unsafe dynamic array backed by static storage. |
|
| 2 | - | //! |
|
| 3 | - | //! Users provide their own arena (static array) and the vector manages |
|
| 4 | - | //! element count within that arena. The arena should be aligned according |
|
| 5 | - | //! to the element type's requirements. |
|
| 1 | + | //! Typed vector backed by caller-owned static storage. |
|
| 6 | 2 | ||
| 7 | - | /// Raw vector metadata structure. |
|
| 8 | - | /// |
|
| 9 | - | /// Does not own storage, points to user-provided arena. |
|
| 10 | - | export record RawVec { |
|
| 11 | - | /// Pointer to user-provided byte arena. |
|
| 12 | - | data: *mut [u8], |
|
| 13 | - | /// Current number of elements stored. |
|
| 3 | + | /// Typed vector metadata backed by caller-owned storage. |
|
| 4 | + | export record Vec⟨T⟩ { |
|
| 5 | + | /// Typed slice containing the vector's storage. |
|
| 6 | + | data: *mut [T], |
|
| 7 | + | /// Number of initialized elements. |
|
| 14 | 8 | len: u32, |
|
| 15 | - | /// Size of each element in bytes (stride between elements). |
|
| 16 | - | stride: u32, |
|
| 17 | - | /// Alignment in bytes required by element type (>= 1). |
|
| 18 | - | alignment: u32, |
|
| 19 | 9 | } |
|
| 20 | 10 | ||
| 21 | - | /// Create a new raw vector with external arena. |
|
| 22 | - | /// |
|
| 23 | - | /// * `arena` is a pointer to static array backing storage. |
|
| 24 | - | /// * `stride` is the size of each element. |
|
| 25 | - | /// * `alignment` is the required alignment for elements. |
|
| 26 | - | export fn new(arena: *mut [u8], stride: u32, alignment: u32) -> RawVec { |
|
| 27 | - | assert stride > 0; |
|
| 28 | - | assert alignment > 0; |
|
| 29 | - | assert (arena.ptr as u32) % alignment == 0; |
|
| 30 | - | assert (arena.len % stride) == 0; |
|
| 31 | - | ||
| 32 | - | return RawVec { data: arena, len: 0, stride, alignment }; |
|
| 11 | + | /// Initialize a vector over caller-owned storage. |
|
| 12 | + | export fn init⟨T⟩(vec: *mut Vec⟨T⟩, arena: *mut [T]) { |
|
| 13 | + | set vec.data = arena; |
|
| 14 | + | set vec.len = 0; |
|
| 33 | 15 | } |
|
| 34 | 16 | ||
| 35 | - | /// Get the current number of elements in the vector. |
|
| 36 | - | export fn len(vec: *RawVec) -> u32 { |
|
| 17 | + | /// Return the number of initialized elements. |
|
| 18 | + | export fn len⟨T⟩(vec: *Vec⟨T⟩) -> u32 { |
|
| 37 | 19 | return vec.len; |
|
| 38 | 20 | } |
|
| 39 | 21 | ||
| 40 | - | /// Get the maximum capacity of the vector. |
|
| 41 | - | export fn capacity(vec: *RawVec) -> u32 { |
|
| 42 | - | return vec.data.len / vec.stride; |
|
| 22 | + | /// Return the storage capacity in elements. |
|
| 23 | + | export fn capacity⟨T⟩(vec: *Vec⟨T⟩) -> u32 { |
|
| 24 | + | return vec.data.len; |
|
| 43 | 25 | } |
|
| 44 | 26 | ||
| 45 | - | /// Reset the vector to empty (does not clear memory). |
|
| 46 | - | export fn reset(vec: *mut RawVec) { |
|
| 27 | + | /// Reset a vector without clearing its storage. |
|
| 28 | + | export fn reset⟨T⟩(vec: *mut Vec⟨T⟩) { |
|
| 47 | 29 | set vec.len = 0; |
|
| 48 | 30 | } |
|
| 49 | 31 | ||
| 50 | - | /// Get a pointer to the element at the given index. |
|
| 51 | - | /// |
|
| 52 | - | /// Returns nil if index is out of bounds. |
|
| 53 | - | export fn get(vec: *RawVec, index: u32) -> ?*opaque { |
|
| 32 | + | /// Return a typed pointer to an element, or `nil` when out of bounds. |
|
| 33 | + | export fn get⟨T⟩(vec: *Vec⟨T⟩, index: u32) -> ?*T { |
|
| 54 | 34 | if index >= vec.len { |
|
| 55 | 35 | return nil; |
|
| 56 | 36 | } |
|
| 57 | - | let offset: u32 = index * vec.stride; |
|
| 58 | - | let ptr: *u8 = &vec.data[offset]; |
|
| 59 | - | ||
| 60 | - | return ptr as *opaque; |
|
| 37 | + | return &vec.data[index]; |
|
| 61 | 38 | } |
|
| 62 | 39 | ||
| 63 | - | /// Push an element onto the end of the vector. |
|
| 64 | - | /// |
|
| 65 | - | /// Returns false if the vector is at capacity. |
|
| 66 | - | export fn push(vec: *mut RawVec, elem: *opaque) -> bool { |
|
| 67 | - | if vec.len >= capacity(vec) { |
|
| 40 | + | /// Append an element, returning `false` when the vector is full. |
|
| 41 | + | export fn push⟨T⟩(vec: *mut Vec⟨T⟩, value: T) -> bool { |
|
| 42 | + | if vec.len >= vec.data.len { |
|
| 68 | 43 | return false; |
|
| 69 | 44 | } |
|
| 70 | - | let off: u32 = vec.len * vec.stride; |
|
| 71 | - | let dst: *mut u8 = &mut vec.data[off]; |
|
| 72 | - | let src: *u8 = elem as *u8; |
|
| 73 | - | ||
| 74 | - | copyBytes(dst, src, vec.stride); |
|
| 45 | + | set vec.data[vec.len] = value; |
|
| 75 | 46 | set vec.len += 1; |
|
| 76 | - | ||
| 77 | - | return true; |
|
| 78 | - | } |
|
| 79 | - | ||
| 80 | - | /// Pop an element from the end of the vector. |
|
| 81 | - | /// |
|
| 82 | - | /// Copies the element into the provided output pointer. |
|
| 83 | - | /// Returns false if the vector is empty. |
|
| 84 | - | export fn pop(vec: *mut RawVec, out: *mut opaque) -> bool { |
|
| 85 | - | if vec.len == 0 { |
|
| 86 | - | return false; |
|
| 87 | - | } |
|
| 88 | - | set vec.len -= 1; |
|
| 89 | - | ||
| 90 | - | let off: u32 = vec.len * vec.stride; |
|
| 91 | - | let src: *u8 = &vec.data[off]; |
|
| 92 | - | let dst: *mut u8 = out as *mut u8; |
|
| 93 | - | ||
| 94 | - | copyBytes(dst, src, vec.stride); |
|
| 95 | - | ||
| 96 | 47 | return true; |
|
| 97 | 48 | } |
|
| 98 | 49 | ||
| 99 | - | /// Set the element at the given index. |
|
| 100 | - | /// |
|
| 101 | - | /// Returns false if index is out of bounds. |
|
| 102 | - | export fn put(vec: *mut RawVec, index: u32, elem: *opaque) -> bool { |
|
| 50 | + | /// Replace an initialized element, returning `false` when out of bounds. |
|
| 51 | + | export fn put⟨T⟩(vec: *mut Vec⟨T⟩, index: u32, value: T) -> bool { |
|
| 103 | 52 | if index >= vec.len { |
|
| 104 | 53 | return false; |
|
| 105 | 54 | } |
|
| 106 | - | let off: u32 = index * vec.stride; |
|
| 107 | - | let dst: *mut u8 = &mut vec.data[off]; |
|
| 108 | - | let src: *u8 = elem as *u8; |
|
| 109 | - | ||
| 110 | - | copyBytes(dst, src, vec.stride); |
|
| 111 | - | ||
| 55 | + | set vec.data[index] = value; |
|
| 112 | 56 | return true; |
|
| 113 | 57 | } |
|
| 114 | 58 | ||
| 115 | - | /// Copy bytes from source to destination. |
|
| 116 | - | fn copyBytes(dst: *mut u8, src: *u8, count: u32) { |
|
| 117 | - | for i in 0..count { |
|
| 118 | - | set *(dst + i) = *(src + i); |
|
| 59 | + | /// Remove and return the last element, or `nil` when empty. |
|
| 60 | + | export fn pop⟨T⟩(vec: *mut Vec⟨T⟩) -> ?T { |
|
| 61 | + | if vec.len == 0 { |
|
| 62 | + | return nil; |
|
| 119 | 63 | } |
|
| 64 | + | set vec.len -= 1; |
|
| 65 | + | return vec.data[vec.len]; |
|
| 120 | 66 | } |
seed/radiance.rv64
+0 -0
Binary file changed.
seed/radiance.rv64.git
+1 -1
| 1 | - | b7b6da266e8ca28f893af0150567147d72c3bd9d4899c5e7a07e1a2309584aa2 |
|
| 1 | + | f102340ee2c496ef3ae997c4a449df1e805d63e30eef07fe7aa562bf2884eab3 |