#![allow(clippy::float_cmp)]

// Exact BSD-3-Clause fixture provenance at LibreMetaverse
// 2aa70bb68513b39795da5d13c88f31b86e85a3ba (see LICENSE.md):
// AssetMaterialOverrideTests.cs 3ec3a8bca08da06c52de1eeea1428b2b83c27660af20580f0333ed67b01bd09b
// GltfDocumentTests.cs          e6e94d5b97a27b8cc4a345f08ab9a9a43b6ce587667edd89d8a4ebbc04eaa646
// Embedded 44-byte triangle buffer d3478e66e380d8a2b0c9d80f3459f75660efb111e7b801a7d1c1f8f48723d79b

use libremetaverse::assets::gltf::{GltfDocument, GltfPrimitive, GltfPrimitiveMode};
use libremetaverse::assets::{AssetMaterial, GltfAlphaMode};
use libremetaverse_structured_data::OSDMap;
use libremetaverse_types::{Color4, Quaternion, UUID, Vector2, Vector3};

fn color(r: f32, g: f32, b: f32, a: f32) -> Color4 {
    Color4::new_with_single_single_single_single(r, g, b, a).expect("Color4 constructor")
}

fn vector2(x: f32, y: f32) -> Vector2 {
    Vector2::new_with_single_single(x, y).expect("Vector2 constructor")
}

fn color_values(value: &Color4) -> [f32; 4] {
    [value.r, value.g, value.b, value.a]
}

fn vector2_values(value: &Vector2) -> [f32; 2] {
    [value.x, value.y]
}

fn vector3_values(value: &Vector3) -> [f32; 3] {
    [value.x, value.y, value.z]
}

fn quaternion_values(value: &Quaternion) -> [f32; 4] {
    [value.x, value.y, value.z, value.w]
}

fn uuid_eq(actual: &UUID, expected: &UUID) {
    assert_eq!(
        actual.get_bytes().expect("actual UUID bytes"),
        expected.get_bytes().expect("expected UUID bytes")
    );
}

fn minimal_gltf_json() -> String {
    r#"{
  "asset": { "version": "2.0", "generator": "Test" },
  "scene": 0,
  "scenes": [{ "name": "Scene", "nodes": [0] }],
  "nodes": [{ "name": "Mesh", "mesh": 0 }],
  "meshes": [{
    "name": "Triangle",
    "primitives": [{
      "attributes": { "POSITION": 0 },
      "indices": 1,
      "material": 0,
      "mode": 4
    }]
  }],
  "materials": [{
    "name": "Mat",
    "pbrMetallicRoughness": {
      "baseColorFactor": [1.0, 0.5, 0.0, 1.0],
      "metallicFactor": 0.0,
      "roughnessFactor": 1.0
    },
    "alphaMode": "OPAQUE",
    "alphaCutoff": 0.5,
    "doubleSided": false
  }],
  "accessors": [
    {
      "bufferView": 0,
      "byteOffset": 0,
      "componentType": 5126,
      "count": 3,
      "type": "VEC3",
      "max": [1.0, 1.0, 0.0],
      "min": [0.0, 0.0, 0.0]
    },
    {
      "bufferView": 1,
      "byteOffset": 0,
      "componentType": 5123,
      "count": 3,
      "type": "SCALAR"
    }
  ],
  "bufferViews": [
    { "buffer": 0, "byteOffset": 0,  "byteLength": 36, "target": 34962 },
    { "buffer": 0, "byteOffset": 36, "byteLength": 6,  "target": 34963 }
  ],
  "buffers": [{
    "byteLength": 44,
    "uri": "data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAABAAIAAAA="
  }]
}"#
    .into()
}

fn load_document() -> GltfDocument {
    GltfDocument::load_gltf(minimal_gltf_json(), None).expect("GltfDocument LoadGltf")
}

fn first_primitive(document: &GltfDocument) -> GltfPrimitive {
    document.meshes()[0]
        .primitives()
        .into_iter()
        .next()
        .expect("first primitive")
}

fn assert_close(actual: f32, expected: f32, tolerance: f32) {
    assert!((actual - expected).abs() <= tolerance);
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.SetBaseColorFactor_OverrideToDefault_IsNudgedOffDefault::test df553fd4df83e48d4dd74cdd8aa15a737ea3a416bcad802f7110bbfe79928a31 translated
#[test]
fn base_color_override_to_default_is_nudged() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    let default = AssetMaterial::default().base_color_factor();
    material
        .set_base_color_factor_with_color4_boolean(default, Some(true))
        .expect("SetBaseColorFactor");
    assert_ne!(
        color_values(&material.base_color_factor()),
        color_values(&AssetMaterial::default().base_color_factor())
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.SetBaseColorFactor_NotOverride_LeavesExactDefaultUnchanged::test fec94fcfac0f36cc5813ed087a30e04fcc9815bc02072ea3f5dd282b8698a89c translated
#[test]
fn base_color_non_override_keeps_default() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    let default = AssetMaterial::default().base_color_factor();
    material
        .set_base_color_factor_with_color4_boolean(default, None)
        .expect("SetBaseColorFactor");
    assert_eq!(
        color_values(&material.base_color_factor()),
        color_values(&AssetMaterial::default().base_color_factor())
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.SetMetallicFactor_OverrideToOne_ClampsBelowDefault::test 7fcee795160428082f6e579881867c7b6c83f31549077fc28a46d18c75c485c9 translated
#[test]
fn metallic_override_to_one_clamps_below_default() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    material
        .set_metallic_factor_with_single_boolean(1.0, Some(true))
        .expect("SetMetallicFactor");
    assert!(material.metallic_factor() < 1.0);
    assert_ne!(
        material.metallic_factor(),
        AssetMaterial::default().metallic_factor()
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.SetAlphaMode_OverrideToDefaultOpaque_SetsOverrideFlag::test 8af09d12a58083657e27e285802bb3a6a1d1c136397cea53865d964006343715 translated
#[test]
fn alpha_mode_override_sets_flag() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    material
        .set_alpha_mode_with_gltf_alpha_mode_boolean(GltfAlphaMode::Opaque, Some(true))
        .expect("SetAlphaMode");
    assert!(material.override_alpha_mode());
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.SetTextureId_OverrideToNull_UsesSentinelUuid::test d9ebf410d2a55c3619b99524a571396fb268e5edd41de955cb86e4ec8cc55c78 translated
#[test]
fn null_texture_override_uses_sentinel() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    material
        .set_texture_id(AssetMaterial::TEXTURE_BASE_COLOR, UUID::zero(), Some(true))
        .expect("SetTextureId");
    uuid_eq(
        &material.texture_ids()[AssetMaterial::TEXTURE_BASE_COLOR as usize],
        &AssetMaterial::gltf_override_null_uuid(),
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.ApplyOverride_SentinelTexture_ClearsBaseTexture::test 17fd8cdb12ed5d1d0aa1b0a05dbcc357f75aa350d5079b066c957da459d9f628 translated
#[test]
fn sentinel_override_clears_base_texture() {
    let mut base = AssetMaterial::new_with_constructor().expect("base material");
    base.set_texture_id(
        AssetMaterial::TEXTURE_BASE_COLOR,
        UUID::random().expect("UUID Random"),
        None,
    )
    .expect("base texture");
    let mut override_ = AssetMaterial::new_with_constructor().expect("override material");
    override_
        .set_texture_id(AssetMaterial::TEXTURE_BASE_COLOR, UUID::zero(), Some(true))
        .expect("override texture");
    base.apply_override(override_).expect("ApplyOverride");
    uuid_eq(
        &base.texture_ids()[AssetMaterial::TEXTURE_BASE_COLOR as usize],
        &UUID::zero(),
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.ApplyOverride_UntouchedTexture_LeavesBaseTextureAlone::test 80ec5e369e3682d92ec6996841b15bda38a3ef6edcd36a2fd9893d8c15459348 translated
#[test]
fn untouched_override_keeps_base_texture() {
    let original = UUID::random().expect("UUID Random");
    let original_copy = UUID::new_with_bytes_int32(original.get_bytes().expect("UUID bytes"), 0)
        .expect("UUID copy");
    let mut base = AssetMaterial::new_with_constructor().expect("base material");
    base.set_texture_id(AssetMaterial::TEXTURE_BASE_COLOR, original, None)
        .expect("base texture");
    let override_ = AssetMaterial::new_with_constructor().expect("override material");
    base.apply_override(override_).expect("ApplyOverride");
    uuid_eq(
        &base.texture_ids()[AssetMaterial::TEXTURE_BASE_COLOR as usize],
        &original_copy,
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.ApplyOverride_ChangedScalar_ReplacesBaseValue::test 4c305c54c4e451932fbd4a4719957f92f38a7f7d28259559b43cb95fcf9250d1 translated
#[test]
fn changed_scalar_override_replaces_base() {
    let mut base = AssetMaterial::new_with_constructor().expect("base material");
    let mut override_ = AssetMaterial::new_with_constructor().expect("override material");
    override_
        .set_roughness_factor_with_single_boolean(0.3, Some(true))
        .expect("SetRoughnessFactor");
    base.apply_override(override_).expect("ApplyOverride");
    assert_eq!(base.roughness_factor(), 0.3);
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.SetBaseMaterial_ResetsFieldsButKeepsTransforms::test 39312602e736fd8607a41d9e6b6348bfaf6e659cf3f63100ee15edd20b672306 translated
#[test]
fn set_base_material_keeps_texture_transforms() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    material.set_name("Foo".into());
    material
        .set_roughness_factor_with_single_boolean(0.2, Some(true))
        .expect("SetRoughnessFactor");
    material
        .set_texture_offset(AssetMaterial::TEXTURE_BASE_COLOR, vector2(0.5, 0.25))
        .expect("SetTextureOffset");
    material.set_base_material().expect("SetBaseMaterial");
    assert_eq!(material.name(), "");
    assert_eq!(
        material.roughness_factor(),
        AssetMaterial::default().roughness_factor()
    );
    assert_eq!(
        vector2_values(
            &material.texture_transforms()[AssetMaterial::TEXTURE_BASE_COLOR as usize].offset,
        ),
        vector2_values(&vector2(0.5, 0.25))
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.ToOverrideOsd_RoundTrip_PreservesOverriddenFields::test 8d542528a14e3b4dcb74ea8a940a44673aada51f98baf8abcdd59a06f36e3cb0 translated
#[test]
fn override_osd_round_trip_preserves_fields() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    material
        .set_roughness_factor_with_single_boolean(0.4, Some(true))
        .expect("SetRoughnessFactor");
    material
        .set_metallic_factor_with_single_boolean(0.6, Some(true))
        .expect("SetMetallicFactor");
    let texture = UUID::random().expect("UUID Random");
    let expected =
        UUID::new_with_bytes_int32(texture.get_bytes().expect("UUID bytes"), 0).expect("UUID copy");
    material
        .set_texture_id(AssetMaterial::TEXTURE_EMISSIVE, texture, Some(true))
        .expect("SetTextureId");
    material
        .set_texture_rotation(AssetMaterial::TEXTURE_EMISSIVE, 1.25)
        .expect("SetTextureRotation");
    let osd = material.to_override_osd().expect("ToOverrideOsd");
    let round_trip = AssetMaterial::from_override_osd(osd).expect("FromOverrideOsd");
    assert_eq!(round_trip.roughness_factor(), 0.4);
    assert_eq!(round_trip.metallic_factor(), 0.6);
    uuid_eq(
        &round_trip.texture_ids()[AssetMaterial::TEXTURE_EMISSIVE as usize],
        &expected,
    );
    assert_eq!(
        round_trip.texture_transforms()[AssetMaterial::TEXTURE_EMISSIVE as usize].rotation,
        1.25
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.ToOverrideOsd_UntouchedMaterial_ProducesEmptyMap::test c9e29f7ec0e6b28579e0499e3ec89841e01a313a9386ba60257e5e701129b1c2 translated
#[test]
fn untouched_material_override_osd_is_empty() {
    let material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    let osd: OSDMap = material.to_override_osd().expect("ToOverrideOsd");
    assert_eq!(osd.count(), 0);
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.ToOverrideOsd_OverrideToDefaultRoughness_StillEncodesField::test 4ea9920c6709bd29858a8da66313fcee5ebebd646743c5d822dd6c9e477bf3b4 translated
#[test]
fn default_roughness_override_is_encoded() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    material
        .set_roughness_factor_with_single_boolean(
            AssetMaterial::default().roughness_factor(),
            Some(true),
        )
        .expect("SetRoughnessFactor");
    let osd = material.to_override_osd().expect("ToOverrideOsd");
    assert!(osd.contains_key("rf".into()).expect("ContainsKey"));
    let round_trip = AssetMaterial::from_override_osd(osd).expect("FromOverrideOsd");
    assert_ne!(
        round_trip.roughness_factor(),
        AssetMaterial::default().roughness_factor()
    );
}

// parity-case: LibreMetaverse.Tests/AssetMaterialOverrideTests.cs::AssetMaterialOverrideTests.ToJson_RoundTrip_PreservesNameAndColor::test d7ae4270d82671a970fc1b6457292860d24481dd84e9fb919ae60fded5f1914e translated
#[test]
fn material_json_round_trip_preserves_name_and_color() {
    let mut material = AssetMaterial::new_with_constructor().expect("AssetMaterial constructor");
    material.set_name("Chrome".into());
    material
        .set_base_color_factor_with_color4_boolean(color(0.1, 0.2, 0.3, 1.0), None)
        .expect("SetBaseColorFactor");
    let json = material.to_json().expect("AssetMaterial ToJson");
    let decoded =
        AssetMaterial::new_with_uuid_bytes(UUID::random().expect("UUID Random"), json.into_bytes())
            .expect("AssetMaterial bytes constructor");
    assert_eq!(decoded.name(), "Chrome");
    assert_eq!(
        color_values(&decoded.base_color_factor()),
        color_values(&material.base_color_factor())
    );
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.LoadGltf_ParsesSceneGraph::test e20583d8de31f793d1dc49d1dfde89e1eb8c00186359e7df4184e862ec9ed649 translated
#[test]
fn gltf_parses_scene_graph() {
    let document = load_document();
    assert_eq!(document.version(), "2.0");
    assert_eq!(document.generator().as_deref(), Some("Test"));
    assert_eq!(document.default_scene(), 0);
    assert_eq!(document.scenes().len(), 1);
    assert_eq!(document.scenes()[0].nodes().len(), 1);
    assert_eq!(document.nodes().len(), 1);
    assert_eq!(document.nodes()[0].mesh(), 0);
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.LoadGltf_ParsesMeshAndPrimitive::test ef6a76308db1966423c7746f5414f3d0553587efc388bbedb962f38cc122ecaf translated
#[test]
fn gltf_parses_mesh_and_primitive() {
    let document = load_document();
    assert_eq!(document.meshes().len(), 1);
    assert_eq!(document.meshes()[0].name().as_deref(), Some("Triangle"));
    assert_eq!(document.meshes()[0].primitives().len(), 1);
    let primitive = first_primitive(&document);
    assert!(primitive.attributes().contains_key("POSITION"));
    assert_eq!(primitive.indices(), 1);
    assert_eq!(primitive.material(), 0);
    assert_eq!(primitive.mode(), GltfPrimitiveMode::Triangles);
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.LoadGltf_ParsesMaterial::test 0147b4d7102c5223f247f69fa4cf3daeb0aa6300ad46ef34d6a17163b694c13e translated
#[test]
fn gltf_parses_material() {
    let document = load_document();
    assert_eq!(document.materials().len(), 1);
    let material = &document.materials()[0];
    assert_eq!(material.name().as_deref(), Some("Mat"));
    assert_close(material.base_color_factor().r, 1.0, 1e-4);
    assert_close(material.base_color_factor().g, 0.5, 1e-4);
    assert_close(material.metallic_factor(), 0.0, 1e-4);
    assert_close(material.roughness_factor(), 1.0, 1e-4);
    assert_eq!(material.alpha_mode(), GltfAlphaMode::Opaque);
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.GetPositions_ReturnsCorrectTriangle::test c3d444122ab73794a011d5a5aafb0c2aa030975393f967e871dc88db200f5e96 translated
#[test]
fn gltf_positions_are_correct_triangle() {
    let document = load_document();
    let positions = document
        .get_positions(first_primitive(&document))
        .expect("GetPositions");
    assert_eq!(positions.len(), 3);
    assert_eq!(
        vector3_values(&positions[0]),
        vector3_values(&Vector3::zero())
    );
    assert_close(positions[1].x, 1.0, 1e-5);
    assert_close(positions[1].y, 0.0, 1e-5);
    assert_close(positions[2].x, 0.0, 1e-5);
    assert_close(positions[2].y, 1.0, 1e-5);
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.GetIndices_ReturnsCorrectValues::test fa39e87a3dfee7258d9a95907a97cbb107f5add7d6a6f9d9800249bb67e2fc58 translated
#[test]
fn gltf_indices_are_correct() {
    let document = load_document();
    let indices = document
        .get_indices(first_primitive(&document))
        .expect("GetIndices");
    assert_eq!(indices, [0, 1, 2]);
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.GetPositions_EmptyWhenNoAttribute::test 06c691911ee6933f1884f6540d69afafa1f272acfed827c628580438204d9b4b translated
#[test]
fn gltf_positions_empty_without_attribute() {
    let document = load_document();
    let primitive = GltfPrimitive::new().expect("GltfPrimitive constructor");
    assert_eq!(
        document
            .get_positions(primitive)
            .expect("GetPositions")
            .len(),
        0
    );
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.GetNormals_EmptyWhenNoAttribute::test e04480b0b9e23cb561cc55e744ebd85203c759f98ad69febeeb291919f0d91af translated
#[test]
fn gltf_normals_empty_without_attribute() {
    let document = load_document();
    assert_eq!(
        document
            .get_normals(first_primitive(&document))
            .expect("GetNormals")
            .len(),
        0
    );
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ToJson_RoundTrip_PreservesSceneGraph::test 41c385b7bde3f8e4f4ad98d4f1f737fc6ccc241c2613526fd745eb35430386b2 translated
#[test]
fn gltf_json_round_trip_preserves_scene_graph() {
    let original = load_document();
    let json = original.to_json(None).expect("GltfDocument ToJson");
    let round_trip = GltfDocument::load_gltf(json, None).expect("GltfDocument LoadGltf");
    assert_eq!(round_trip.scenes().len(), original.scenes().len());
    assert_eq!(round_trip.nodes().len(), original.nodes().len());
    assert_eq!(round_trip.meshes().len(), original.meshes().len());
    assert_eq!(round_trip.materials().len(), original.materials().len());
    assert_close(
        round_trip.materials()[0].base_color_factor().r,
        original.materials()[0].base_color_factor().r,
        1e-4,
    );
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ToGlb_RoundTrip_PreservesGeometry::test 1e97fe2d1492ac195f6a4386329c76ad131ab99a41b8e03216b1bab8b48fae36 translated
#[test]
fn glb_round_trip_preserves_geometry() {
    let original = load_document();
    let glb = original.to_glb().expect("GltfDocument ToGlb");
    assert_eq!(&glb[..4], &[0x67, 0x6c, 0x54, 0x46]);
    let round_trip = GltfDocument::load(glb, None).expect("GltfDocument Load");
    let positions = round_trip
        .get_positions(first_primitive(&round_trip))
        .expect("GetPositions");
    assert_eq!(positions.len(), 3);
    assert_close(positions[1].x, 1.0, 1e-5);
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.Load_AutoDetectsGlbVsJson::test 6d07930be2302e504ef9ece3fef10f47b855d33ae6c8bff152e82df0beb2a474 translated
#[test]
fn gltf_load_auto_detects_glb_and_json() {
    let original = load_document();
    let glb = original.to_glb().expect("GltfDocument ToGlb");
    let json = original.to_json(None).expect("GltfDocument ToJson");
    let from_glb = GltfDocument::load(glb, None).expect("load GLB");
    let from_json = GltfDocument::load(json.into_bytes(), None).expect("load JSON");
    assert_eq!(from_glb.meshes().len(), 1);
    assert_eq!(from_json.meshes().len(), 1);
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_TrsDefaults::test 0f0fbe116d20276c2aee917b940bded9de277ed0b3e3b659844c5b3532755259 translated
#[test]
fn gltf_node_trs_defaults() {
    let document = load_document();
    let node = &document.nodes()[0];
    assert!(node.matrix().is_none());
    assert_eq!(
        vector3_values(&node.translation()),
        vector3_values(&Vector3::zero())
    );
    assert_eq!(
        quaternion_values(&node.rotation()),
        quaternion_values(&Quaternion::identity())
    );
    assert_eq!(
        vector3_values(&node.scale()),
        vector3_values(&Vector3::one())
    );
}

// parity-case: LibreMetaverse.Tests/GltfDocumentTests.cs::GltfDocumentTests.ParseNode_ExplicitMatrix::test 197ba2ee3a66cbb7d927acea13ce9dc84e62af8a500d939957749039118755c8 translated
#[test]
fn gltf_node_explicit_matrix() {
    let document = GltfDocument::load_gltf(
        r#"{
  "asset": { "version": "2.0" },
  "nodes": [{
    "matrix": [1,0,0,0, 0,1,0,0, 0,0,1,0, 5,6,7,1]
  }]
}"#
        .into(),
        None,
    )
    .expect("GltfDocument LoadGltf");
    let matrix = document.nodes()[0]
        .matrix()
        .flatten()
        .expect("explicit matrix");
    assert_close(matrix.m14, 5.0, 1e-5);
    assert_close(matrix.m24, 6.0, 1e-5);
    assert_close(matrix.m34, 7.0, 1e-5);
}
