diff --git a/clients/apple/Sources/PunktfunkClient/Session/SessionModel.swift b/clients/apple/Sources/PunktfunkClient/Session/SessionModel.swift index 12db3065..4e998fcf 100644 --- a/clients/apple/Sources/PunktfunkClient/Session/SessionModel.swift +++ b/clients/apple/Sources/PunktfunkClient/Session/SessionModel.swift @@ -264,14 +264,11 @@ final class SessionModel: ObservableObject { // PyroWave (wired LAN) is a pure opt-in: picking it in the codec setting both // advertises the bit and prefers it — the host never auto-selects it, and the // picker only offers it when the Metal decode probe passed (simdgroup floor ≈ A13; - // every M-series Mac and the ATV 4K gen 3 pass). The codec is 8-bit 4:2:0 SDR - // BT.709 by contract, so the opt-in also drops the HDR/10-bit/4:4:4 caps for this - // session — HDR sessions stay HEVC/AV1 (plan §4.7). + // every M-series Mac and the ATV 4K gen 3 pass). The decoder self-configures from + // the per-frame sequence header (4:2:0/4:4:4, SDR/PQ — design/pyrowave-444-hdr.md), + // so the session keeps the user's HDR/10-bit/4:4:4 caps exactly like HEVC/AV1. if preferredCodec == PunktfunkConnection.codecPyroWave, MetalWaveletDecoder.supported { videoCodecs |= PunktfunkConnection.codecPyroWave - videoCaps &= ~(PunktfunkConnection.videoCap10Bit - | PunktfunkConnection.videoCapHDR - | PunktfunkConnection.videoCap444) } let result = Result { try PunktfunkConnection( host: host.address, port: host.port, diff --git a/clients/apple/Sources/PunktfunkKit/Video/MetalVideoPresenter.swift b/clients/apple/Sources/PunktfunkKit/Video/MetalVideoPresenter.swift index 8612743c..45683735 100644 --- a/clients/apple/Sources/PunktfunkKit/Video/MetalVideoPresenter.swift +++ b/clients/apple/Sources/PunktfunkKit/Video/MetalVideoPresenter.swift @@ -196,13 +196,11 @@ fragment float4 pf_frag_hdr(VOut in [[stage_in]], // display-referred SDR. (When the display IS in an HDR mode — requested per session via // AVDisplayManager, see StreamViewIOS — tvOS presents pf_frag_hdr's PQ passthrough instead: // in a genuine HDR10 output, PQ passthrough is the correct emission and the TV tone-maps.) -fragment float4 pf_frag_hdr_tv(VOut in [[stage_in]], - texture2d lumaTex [[texture(0)]], - texture2d chromaTex [[texture(1)]], - constant CscUniform& csc [[buffer(0)]]) { - // Y′CbCr → full-range PQ R′G′B′ via the per-frame rows (as pf_frag_hdr). - float3 pq = sampleRgb(lumaTex, chromaTex, in.uv, csc); - // ST 2084 EOTF: PQ code value → linear light, 1.0 = 10,000 nits. +// The shared PQ→display-referred-SDR tail (see pf_frag_hdr_tv's rationale above): ST 2084 +// EOTF → 203-nit-anchored scene light → BT.2020→709 primaries → extended-Reinhard rolloff → +// BT.709 OETF. Used by the tvOS biplanar tone-map and the planar (PyroWave) tone-map — the +// latter also on macOS windowed sessions, whose IOSurface present path is BGRA8-only. +static inline float3 pqToSdr(float3 pq) { const float m1 = 2610.0/16384.0; const float m2 = 78.84375; const float c1 = 3424.0/4096.0; @@ -210,20 +208,49 @@ fragment float4 pf_frag_hdr_tv(VOut in [[stage_in]], const float c3 = 18.6875; float3 p = pow(pq, 1.0/m2); float3 lin = pow(max(p - c1, 0.0) / (c2 - c3 * p), 1.0/m1); - // Scene-referred with diffuse white at 1.0 (the same 203-nit anchor the EDR path uses). float3 t = lin * (10000.0/203.0); - // BT.2020 → BT.709 primaries while still linear; negatives are out-of-gamut, floor them. float3 t709 = float3( dot(t, float3( 1.6605, -0.5876, -0.0728)), dot(t, float3(-0.1246, 1.1329, -0.0083)), dot(t, float3(-0.0182, -0.1006, 1.1187))); t709 = max(t709, 0.0); - // Extended Reinhard: 1.0 stays put, the 1000-nit knee lands at display white, above rolls off. const float w = 1000.0/203.0; float3 mapped = saturate(t709 * (1.0 + t709 / (w * w)) / (1.0 + t709)); - // BT.709 OETF — the same encoding the SDR stream arrives in, so both paths present alike. float3 e = select(1.099 * pow(mapped, 0.45) - 0.099, 4.5 * mapped, mapped < 0.018); - return float4(e, 1.0); + return e; +} + +fragment float4 pf_frag_hdr_tv(VOut in [[stage_in]], + texture2d lumaTex [[texture(0)]], + texture2d chromaTex [[texture(1)]], + constant CscUniform& csc [[buffer(0)]]) { + // Y′CbCr → full-range PQ R′G′B′ via the per-frame rows (as pf_frag_hdr), then the tail. + return float4(pqToSdr(sampleRgb(lumaTex, chromaTex, in.uv, csc)), 1.0); +} + +// PyroWave planar HDR tone-map: three separate R16 planes (P010-style studio codes; the rows +// fold in depth-10 MSB packing) → PQ R′G′B′ → the shared SDR tail. Used when a PQ pyrowave +// stream must land on an 8-bit surface: tvOS without HDR headroom, and macOS WINDOWED sessions +// (the IOSurface present path — the DCP-panic mitigation — is BGRA8). The passthrough planar +// HDR pipeline reuses pf_frag_planar itself on an rgba16Float drawable (identical math — the +// layer's itur_2100_PQ colour space + EDR metadata do the interpretation). +fragment float4 pf_frag_planar_tm(VOut in [[stage_in]], + texture2d lumaTex [[texture(0)]], + texture2d cbTex [[texture(1)]], + texture2d crTex [[texture(2)]], + constant CscUniform& csc [[buffer(0)]]) { + constexpr sampler s(filter::linear, address::clamp_to_edge); +#ifdef PF_BILINEAR_LUMA + float lumaY = lumaTex.sample(s, in.uv).r; +#else + float lumaY = catmullRomLuma(lumaTex, s, in.uv); +#endif + float2 cuv = chromaUV(lumaTex, cbTex, in.uv); + float3 yuv = float3(lumaY, cbTex.sample(s, cuv).r, crTex.sample(s, cuv).r); + float3 pq = saturate(float3(dot(csc.r0.xyz, yuv) + csc.r0.w, + dot(csc.r1.xyz, yuv) + csc.r1.w, + dot(csc.r2.xyz, yuv) + csc.r2.w)); + return float4(pqToSdr(pq), 1.0); } """ @@ -287,6 +314,11 @@ public final class MetalVideoPresenter { private let pipelineHDRToneMap: MTLRenderPipelineState? /// PyroWave's 3-plane SDR path (pf_frag_planar → bgra8) — see `renderPlanar`. private let pipelinePlanar: MTLRenderPipelineState + /// PyroWave planar HDR passthrough (pf_frag_planar → rgba16Float; the layer's PQ colour + /// space + EDR interpret the samples) and the planar PQ→SDR tone-map (pf_frag_planar_tm → + /// bgra8; tvOS without headroom + macOS windowed IOSurface presents). + private let pipelinePlanarHDR: MTLRenderPipelineState + private let pipelinePlanarToneMap: MTLRenderPipelineState private var textureCache: CVMetalTextureCache? /// The PyroWave Metal decoder records on the presenter's device + queue: one device means @@ -337,6 +369,8 @@ public final class MetalVideoPresenter { let pipelineHDR: MTLRenderPipelineState let pipelineHDRToneMap: MTLRenderPipelineState? let pipelinePlanar: MTLRenderPipelineState + let pipelinePlanarHDR: MTLRenderPipelineState + let pipelinePlanarToneMap: MTLRenderPipelineState do { // DEBUG A/B lever: PUNKTFUNK_BILINEAR_LUMA=1 compiles the shader with Catmull-Rom OFF // (plain bilinear luma) by prepending a #define ahead of the source. Default (unset) is @@ -374,8 +408,18 @@ public final class MetalVideoPresenter { let planar = MTLRenderPipelineDescriptor() planar.vertexFunction = vtx planar.fragmentFunction = library.makeFunction(name: "pf_frag_planar") - planar.colorAttachments[0].pixelFormat = .bgra8Unorm // PyroWave is 8-bit SDR + planar.colorAttachments[0].pixelFormat = .bgra8Unorm pipelinePlanar = try device.makeRenderPipelineState(descriptor: planar) + let planarHdr = MTLRenderPipelineDescriptor() + planarHdr.vertexFunction = vtx + planarHdr.fragmentFunction = library.makeFunction(name: "pf_frag_planar") + planarHdr.colorAttachments[0].pixelFormat = .rgba16Float // PQ passthrough + pipelinePlanarHDR = try device.makeRenderPipelineState(descriptor: planarHdr) + let planarTm = MTLRenderPipelineDescriptor() + planarTm.vertexFunction = vtx + planarTm.fragmentFunction = library.makeFunction(name: "pf_frag_planar_tm") + planarTm.colorAttachments[0].pixelFormat = .bgra8Unorm + pipelinePlanarToneMap = try device.makeRenderPipelineState(descriptor: planarTm) } catch { return nil } @@ -416,6 +460,7 @@ public final class MetalVideoPresenter { return MetalVideoPresenter( device: device, queue: queue, pipelineSDR: pipelineSDR, pipelineHDR: pipelineHDR, pipelineHDRToneMap: pipelineHDRToneMap, pipelinePlanar: pipelinePlanar, + pipelinePlanarHDR: pipelinePlanarHDR, pipelinePlanarToneMap: pipelinePlanarToneMap, textureCache: textureCache, layer: layer) } @@ -423,6 +468,8 @@ public final class MetalVideoPresenter { device: MTLDevice, queue: MTLCommandQueue, pipelineSDR: MTLRenderPipelineState, pipelineHDR: MTLRenderPipelineState, pipelineHDRToneMap: MTLRenderPipelineState?, pipelinePlanar: MTLRenderPipelineState, + pipelinePlanarHDR: MTLRenderPipelineState, + pipelinePlanarToneMap: MTLRenderPipelineState, textureCache: CVMetalTextureCache, layer: CAMetalLayer ) { self.device = device @@ -431,6 +478,8 @@ public final class MetalVideoPresenter { self.pipelineHDR = pipelineHDR self.pipelineHDRToneMap = pipelineHDRToneMap self.pipelinePlanar = pipelinePlanar + self.pipelinePlanarHDR = pipelinePlanarHDR + self.pipelinePlanarToneMap = pipelinePlanarToneMap self.textureCache = textureCache self.layer = layer } @@ -652,7 +701,13 @@ public final class MetalVideoPresenter { let surfaceMode = surfacePresentsStaged #endif stagingLock.unlock() - configure(hdr: false) + // A PQ (HDR) pyrowave stream drives the same layer/EDR machinery as the biplanar path; + // macOS WINDOWED sessions stay on the SDR layer (the IOSurface path tone-maps in-shader). + #if os(macOS) + configure(hdr: planes.pq && !surfaceMode) + #else + configure(hdr: planes.pq) + #endif var csc = planes.csc #if os(macOS) if surfaceMode != surfacePresentsActive { @@ -672,9 +727,21 @@ public final class MetalVideoPresenter { planes, targetFromLayout: targetFromLayout, csc: &csc, onPresented: onPresented) } #endif + // PQ passthrough needs the HDR drawable; a PQ frame while the drawable is (still) + // 8-bit — tvOS without display headroom, or a not-yet-flipped layer — tone-maps + // in-shader instead (the pipeline must match the drawable's pixel format). + #if os(tvOS) + let planarPassthrough = hdrActive && hdrPassthroughActive + #else + let planarPassthrough = hdrActive + #endif + let planarPipeline: MTLRenderPipelineState = + planes.pq + ? (planarPassthrough ? pipelinePlanarHDR : pipelinePlanarToneMap) + : pipelinePlanar return encodePresent( decodedSize: CGSize(width: planes.width, height: planes.height), - targetFromLayout: targetFromLayout, pipeline: pipelinePlanar, + targetFromLayout: targetFromLayout, pipeline: planarPipeline, presentAtMediaTime: presentAtMediaTime, onPresented: onPresented, // The ring textures stay valid by ring depth; retaining them here also pins the // slot's set until the sample completes (mirrors the biplanar keep-alive). @@ -716,7 +783,7 @@ public final class MetalVideoPresenter { guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: pass) else { return false } - encoder.setRenderPipelineState(pipelinePlanar) + encoder.setRenderPipelineState(planes.pq ? pipelinePlanarToneMap : pipelinePlanar) encoder.setFragmentTexture(planes.y, index: 0) encoder.setFragmentTexture(planes.cb, index: 1) encoder.setFragmentTexture(planes.cr, index: 2) diff --git a/clients/apple/Sources/PunktfunkKit/Video/MetalWaveletDecoder.swift b/clients/apple/Sources/PunktfunkKit/Video/MetalWaveletDecoder.swift index a39d6b58..3765fca6 100644 --- a/clients/apple/Sources/PunktfunkKit/Video/MetalWaveletDecoder.swift +++ b/clients/apple/Sources/PunktfunkKit/Video/MetalWaveletDecoder.swift @@ -47,6 +47,9 @@ struct WaveletLayout { let width: Int let height: Int + /// Full-res chroma (4:4:4): chroma components get the full band set including level 0, + /// exactly like luma — upstream `init_block_meta` with `Chroma444`. + let chroma444: Bool let alignedWidth: Int let alignedHeight: Int /// blockMeta[component][level][band] = (blockOffset32x32, blockStride32x32); -1 offset = @@ -59,9 +62,10 @@ struct WaveletLayout { func levelWidth(_ level: Int) -> Int { (alignedWidth / 2) >> level } func levelHeight(_ level: Int) -> Int { (alignedHeight / 2) >> level } - init(width: Int, height: Int) { + init(width: Int, height: Int, chroma444: Bool) { self.width = width self.height = height + self.chroma444 = chroma444 let align = { (v: Int) in max((v + Self.alignment - 1) & ~(Self.alignment - 1), Self.minimumImageSize) } @@ -78,7 +82,7 @@ struct WaveletLayout { let ah = alignedHeight for level in stride(from: Self.decompositionLevels - 1, through: 0, by: -1) { for component in 0..<3 { - if level == 0 && component != 0 { continue } // 4:2:0: no top-level chroma + if level == 0 && component != 0 && !chroma444 { continue } // 4:2:0: no top-level chroma for band in (level == Self.decompositionLevels - 1 ? 0 : 1)..<4 { let levelW = (aw / 2) >> level let levelH = (ah / 2) >> level @@ -108,6 +112,9 @@ struct ParsedWaveletFrame { var decodedBlocks: Int /// VUI bits from the sequence header (BitstreamSequenceHeader). var bt2020: Bool + /// PQ transfer ⇒ HDR session: 16-bit studio-code planes + EDR present (the host stamps + /// this bit iff the session negotiated 10-bit — the depth is coupled to the transfer). + var pq: Bool var fullRange: Bool /// The frame's Y′CbCr→RGB signal for the presenter's planar CSC. PyroWave today is always @@ -203,6 +210,7 @@ enum WaveletBitstream { var totalBlocks = 0 var decodedBlocks = 0 var bt2020 = false + var pq = false var fullRange = false var sawSOF = false @@ -220,22 +228,28 @@ enum WaveletBitstream { // siting[31]. let code = (word1 >> 24) & 0x3 guard code == 0 else { return false } // only START_OF_FRAME is defined - let chromaRes = (word1 >> 26) & 1 - guard chromaRes == 0 else { return false } // host contract: 4:2:0 + let chroma444 = (word1 >> 26) & 1 != 0 let w = Int(word0 & 0x3fff) + 1 let h = Int((word0 >> 14) & 0x3fff) + 1 - guard w >= 2, h >= 2, w % 2 == 0, h % 2 == 0 else { return false } + guard w >= 2, h >= 2, chroma444 || (w % 2 == 0 && h % 2 == 0) else { + return false + } if sawSOF { // One frame, one geometry — a second SOF must agree. - guard layout?.width == w, layout?.height == h else { return false } + guard layout?.width == w, layout?.height == h, + layout?.chroma444 == chroma444 + else { return false } } else { sawSOF = true - let l = WaveletLayout(width: w, height: h) + let l = WaveletLayout(width: w, height: h, chroma444: chroma444) layout = l offsets = [UInt32](repeating: .max, count: l.blockCount32) payload.reserveCapacity(64 * 1024 / 4) totalBlocks = Int(word1 & 0xff_ffff) bt2020 = (word1 >> 29) & 1 != 0 + // transfer_function bit: PQ ⇒ an HDR session (16-bit studio-code + // planes by the negotiated coupling — design/pyrowave-444-hdr.md). + pq = (word1 >> 28) & 1 != 0 fullRange = (word1 >> 30) & 1 == 0 // YCBCR_RANGE_FULL = 0 } pos += 8 @@ -280,7 +294,7 @@ enum WaveletBitstream { return ParsedWaveletFrame( layout: layout, offsets: offsets, payload: payload, totalBlocks: totalBlocks, decodedBlocks: decodedBlocks, - bt2020: bt2020, fullRange: fullRange) + bt2020: bt2020, pq: pq, fullRange: fullRange) } } } @@ -293,6 +307,8 @@ public struct WaveletPlanes: @unchecked Sendable { public let cb: MTLTexture public let cr: MTLTexture public let csc: CscUniform + /// PQ (HDR) stream: the presenter picks the HDR/tone-map planar pipeline + EDR config. + public let pq: Bool public var width: Int { y.width } public var height: Int { y.height } } @@ -351,6 +367,8 @@ public final class MetalWaveletDecoder { private var slots: [Slot] = [] private var nextSlot = 0 + /// The ring's plane format facts (from the last SOF): PQ ⇒ 16-bit UNORM planes. + private var hdr16 = false /// The current geometry (from the last SOF that built the resources) — the pump reports /// decoded-size changes to the resize overlay from this. PUMP THREAD. @@ -409,8 +427,9 @@ public final class MetalWaveletDecoder { au: au, chunkAligned: chunkAligned, windowSize: windowSize) else { return false } - if layout?.width != frame.layout.width || layout?.height != frame.layout.height { - guard rebuild(layout: frame.layout) else { return false } + if layout?.width != frame.layout.width || layout?.height != frame.layout.height + || layout?.chroma444 != frame.layout.chroma444 || hdr16 != frame.pq { + guard rebuild(layout: frame.layout, hdr16: frame.pq) else { return false } } guard let layout, !slots.isEmpty else { return false } @@ -450,7 +469,7 @@ public final class MetalWaveletDecoder { dequant.setBuffer(slot.payload, offset: 0, index: 1) for level in 0.. Bool { + private func rebuild(layout newLayout: WaveletLayout, hdr16 newHdr16: Bool) -> Bool { waveletLog.info( - "pyrowave: building decoder \(newLayout.width)x\(newLayout.height) (aligned \(newLayout.alignedWidth)x\(newLayout.alignedHeight), \(newLayout.blockCount32) blocks)") + "pyrowave: building decoder \(newLayout.width)x\(newLayout.height) (aligned \(newLayout.alignedWidth)x\(newLayout.alignedHeight), \(newLayout.blockCount32) blocks, \(newLayout.chroma444 ? "4:4:4" : "4:2:0", privacy: .public)\(newHdr16 ? " HDR16" : "", privacy: .public))") var coeff: [[MTLTexture]] = [] var lls: [[MTLTexture]] = [] for component in 0..<3 { @@ -560,19 +586,22 @@ public final class MetalWaveletDecoder { var newSlots: [Slot] = [] for i in 0.. MTLTexture? in let desc = MTLTextureDescriptor.texture2DDescriptor( - pixelFormat: .r8Unorm, width: w, height: h, mipmapped: false) + pixelFormat: planeFormat, width: w, height: h, mipmapped: false) desc.usage = [.shaderRead, .shaderWrite] desc.storageMode = .private let t = self.device.makeTexture(descriptor: desc) t?.label = name return t } + let cw = newLayout.chroma444 ? newLayout.width : newLayout.width / 2 + let ch = newLayout.chroma444 ? newLayout.height : newLayout.height / 2 guard let y = plane(newLayout.width, newLayout.height, "pyrowave Y[\(i)]"), - let cb = plane(newLayout.width / 2, newLayout.height / 2, "pyrowave Cb[\(i)]"), - let cr = plane(newLayout.width / 2, newLayout.height / 2, "pyrowave Cr[\(i)]"), + let cb = plane(cw, ch, "pyrowave Cb[\(i)]"), + let cr = plane(cw, ch, "pyrowave Cr[\(i)]"), let offsets = device.makeBuffer( length: max(newLayout.blockCount32 * 4, 4), options: .storageModeShared), let payload = device.makeBuffer(length: 64 * 1024, options: .storageModeShared) @@ -585,6 +614,7 @@ public final class MetalWaveletDecoder { slots = newSlots nextSlot = 0 layout = newLayout + hdr16 = newHdr16 return true } diff --git a/clients/apple/Tests/PunktfunkKitTests/PyroWaveDecoderTests.swift b/clients/apple/Tests/PunktfunkKitTests/PyroWaveDecoderTests.swift index 8c28cd15..5aca89e2 100644 --- a/clients/apple/Tests/PunktfunkKitTests/PyroWaveDecoderTests.swift +++ b/clients/apple/Tests/PunktfunkKitTests/PyroWaveDecoderTests.swift @@ -57,7 +57,7 @@ final class PyroWaveParserTests: XCTestCase { func testLayoutMatchesUpstreamBlockSpace() { // init_block_meta's walk for 256x144 (aligned 256x160): level extents halve from // 128x80; per (comp,level,band) count32 = ceil(ceil(w/8)/4) * ceil(ceil(h/8)/4). - let layout = WaveletLayout(width: width, height: height) + let layout = WaveletLayout(width: width, height: height, chroma444: false) XCTAssertEqual(layout.alignedWidth, 256) XCTAssertEqual(layout.alignedHeight, 160) XCTAssertEqual(layout.levelWidth(0), 128) @@ -85,7 +85,7 @@ final class PyroWaveParserTests: XCTestCase { } func testDenseParseFillsOffsetsAndCountsBlocks() throws { - let layout = WaveletLayout(width: width, height: height) + let layout = WaveletLayout(width: width, height: height, chroma444: false) var au = sof(totalBlocks: 4) au += packet(blockIndex: 0) au += packet(blockIndex: 3) @@ -273,6 +273,17 @@ final class PyroWaveGoldenTests: XCTestCase { try assertMatchesReference(decoded, prefix: "ref-chunked") } + /// 4:4:4: the chroma components run the full pyramid like luma (no level-0 skip, no + /// early half-res emit) — the layout + dispatch structure Phase 4 added + /// (design/pyrowave-444-hdr.md). The fixture comes from the 4:4:4 host encoder; the + /// reference is upstream's own 4:4:4 decode (full-res chroma planes). + func testDense444GoldenFrame() throws { + try XCTSkipIf(!MetalWaveletDecoder.supported, "no capable Metal device") + let au = try fixture("au-dense444") + let decoded = try decode(au: au, chunkAligned: false, windowSize: 0) + try assertMatchesReference(decoded, prefix: "ref-dense444") + } + /// Phase-4 partial delivery: zero a mid-AU window (a lost shard) — the frame must still /// decode (blocks > half) and stay recognizably the same picture (holes reconstruct as /// localized blur, not garbage). diff --git a/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/au-dense444.bin b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/au-dense444.bin new file mode 100644 index 00000000..6506e5ae Binary files /dev/null and b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/au-dense444.bin differ diff --git a/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-cb.bin b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-cb.bin new file mode 100644 index 00000000..7b2338b8 --- /dev/null +++ b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-cb.bin @@ -0,0 +1 @@ +ssxsqt|xsvzxwy~|~ȷľľttxysrxt|wswywxu~~¾˾¾vutzqruy{{yytw}u{~~~~º¬͹ľopvwrryxuvuywvxt}|||ó˹¿wsvruxpuvxuyvssw}~}}ìʶ¿unmwuqvwxwttvzvt}y|{¯Ƕtonmtsssuvwrt{tu~z{|{~÷lpspnptrxvsuwzuxw}~yy}ʺotsrlrovproroqxp|~yx|}~z|{ʶpojjurwsosrtyspwwzy}wzz}yýɿʿȳnomnlqvnputnvsqpuv|~z~}}y}ȿûprmkpsmrsupquwwnxvy{|y}{{z}y}Dzlrmrllrlproupomn{xuw{uy{{y|ÿȱkgpksrlkorotrqptsy}{w{xy~z}þþDzԾ¿njlnjsmnpsmllntr{yrv|~z~vy~}zx}þļҺþgoiniljstqkilpumv{z}x|}}uzy|zw{нÿzx{x|x}||tmnmmoyopuwwuvsz|~~||}}ƿɽzwy{}}}||||~pqutpptprrnpnxpuy|z|{~}žǽzzyw{wx||polrtvstssrspuxx{x|yx~}y}ſɸsw|wzsx~zx||x~rsrqnnmoporxouxv{wwxzy}{z{ʺuvwyvxtxy~v}{ywmoonkmmqoqvrqtwwvz}}v}yx|¾ɿ²оptyyyvyxxxv|~|lpnnqkomsoslnpro|wu~~uw}}z{y|{}źƿѾtstt|t}{~|~}|{{ulpjipqjojtqmstsvytytz{x}y}{~}yſųοwtqtxzx{|zyyyv}}njokmrkjmnljotls}~}vtxzzxzuvz~yyÿºǵϾzxvyzw|{uwxzuu~lllimjnrjlrqolrl}rus{stw}u~|zw}z¼üĿļòsrozqqsxxwsz{uxyhmlohhhgmoqlnjmr{}~vvtv}z{wv{}yyy}v~¾ijκqsvuuyyzz{x}{yyugjehlpijgkiqmlpk~~tvuywtvzusvu||xµθwuoytxxy{xvxwwwujififjjopkmnqlos}}xtrs{trv|y{vz}}|̼strorvyssqvut{vvleejeiopjlhhrorm}uuws{vsryvytx}w}}ȼpwmnsmz|rtzusv|zddhnkgknokkgfjll~y|{~~vwuytzyvtquqwwu}}}~¸¬̻vnlmvtwppooor{r{jlfjifjkkpgpkpgl||~z{~|svsrpzzztqvt{uww~}½̺tonvntqqpvxurpvsgbfclchjdjkhmekh{z}~|puvwqwuswuuquyxz}~~}||ºîĸ_]\_d\]bcebffhcdputxx{t|v{~}z|gmimqmnrpslnsuku~r|{vz|uu{xzwwxÿſdZb`^][b`a`be`hh{zrswysw|zyx}{xoqioqqqqooknsmrr}}ryuz|w|yyyz|{|y¹\][\\`[ea__]`db_tysrwzzr{{{x}}}~jngjhjjllnpsrmso}}~v}w|uyzyvxxv{x|¾Ŀeaa``cb_bf^fcda^prqsyyvsy{}y|{{{mjninmlgrrrkkpol~}y{s{}|wwvvuvyy|~~³XY`ab`]b^]e`dfgdpopptssxyyvuyywwlmnigiikimlpknjqy~rywwyzxsru~|twx|~~Z]]`\][_\d_a^^]csrnsrtxutsttt{|wdninnmionpjkjkns{}}pwwyytqvr|zzvvwy|Z`Y_Z_]`^[\d__^cmpmvpqysvrqvv{{yhmemjlofjkhjnlsq~{|twsspq{swtzvsu|x|}}~^`XZVY]\[\Y^_]a`uuqsqorryqwto{sqdlhhgmhlchnklkpg{~}{|orrqryyo{yxwvwt~y{[XVY_Z^Z\`\\b^cdkosmpnqrvzru{xxsghbfcflfleghjkipy~z~z||ortswqusrvwvtywvz~|}|}`\``XZ^`][YY`e^^mprnpwovrrrsvsrsgiabcekgihofjjlh|y{y{~{~}}usqvqqvqmzuspzr{{~~~~]T^V`a]a\Z_\^[^\pnnnqoxpwovsssrs`gdgjeeeeldledffy{z{|}|{sppotruqtpsuzvzu{}~{}~YU[VWXZ]Z\`\^]^\llpsropsttoqsonxifahddcimidmikkhvwz{xzy~y{|pnpoqotxpmppuvwvvy~{|z~~VVW[ZVVWZ[Z[a_^\ilprmmouqnvstprxebffcfailbdiekin}~{u{xx}|~~}llponmvrwooqvxtxvyw{z{~}¾VVTX[S_ZW[\ZVY`\qpjpoqrttmkrpssuai^digjafkkjehkn~wvy|z|zy|qrnnrtvmrnsvptvtux{}x~||}RXWYZWVY]YW]\Y\Vqtilinsostvnttnofg```dehaccbblhl|w~u}yxv~zx~}}msmmotqlvmnvnpurzzy~zz{z{{XR[[U\WW[[VZZ[^`pgjprpnlnnmnpumsdbd_ggfdhajidcfhzw|t~u}wxxx}osskttsprppssswq{~|zx{{}x|{|igbfabjgmcogchll_\XY[[[]^`bd^c^euotwnppspzuzur|xlhgmkjnejhflklpk|}}|}}}vyurpuxvtwvv|zx}}~z~fcdfcigcdlmblggiYVWX`bbaZ^]e_``[puslmswqrtrtvssvjjhgffkkmmpqnmpkyyy}{}snqonrxxvzyurpsxz{~ighd`iadfiddjhllVVZU\^`X[b`[[a\^rtpnnrrwuuvrwyqvhifbfijmeemmmnpoxwy{y}~~~~rvlqpquyxywxtrv{||}~cdhgafffd`fgeejkXWZZXZW\Z^`Z`\c_tqlqtvvoswvnqprtjcefhgdgeijjohnp}zz|}x~{zx}|~kswnwspvxvqwvspsy~zgb^gb_ehhjejacejYXXVZ]XZ[XZ^\a_Ynjjpmjksomporwupejehbbcafnjfgkgk{yzx~~{y}pqtnovvtnoqxuxq{}~w|}}~~|_^__cabdcakhchkeZ[\XXVW]UXX_`bb]pmorqnqkmovmtpwtcgdbfdejchfgeeel}|u|vxz||zzmmoptkpuusvpsuwqx}{~`_^dbc]cbijcgjhiVZYVYT]]Y^U\`^_^qljlojmmnpqslpvw_``ggehiidciidhl{wyxx|yxkoptsqmsqmvyqrwoxw~y{~y|}~\^bdf`hf_`bhchagXTUZZV^U^Y]YW[a\jmolksirltlllorndf`bb`_dbhailghi{w|v~{{v~z~}knkknnruntumtsvxxy{}zxz|yz}|dbc`b_bddccb`jfjUZVX\UUY\[X[`^YYikghokpjmnmlutpog]faa_^igfggjhjcsux|w|v{wx}w|xkqpmskmqqltsvqrpwvwyu|}}z}{{\^c`b]^eebdacd`g[ZZWZWWVX\XWXX\Vikqnoprkpksqkprp`cbadde`dgkjkfghssrvu{xvxw|v~opnjtnqoqnqvlurq~x|xxyv|yzx^]\^a][da^b^gaadWZTPUYYSRS\WT^[^hlhjkpohokrpsist^\]^cabfdffaabi`txvvtztzyw{x{}zhlrjojkktqrqtottt{uxwwvwx}y|[^\`]\^b]ad`_b^fXYXUSUXZRUV[\[V]momjqjplqpprmprmZ\`[c_a^dedcigeduvsrxv~ttuzy}}{iipkikkkntmkmmnsruwwtx{z{zz}|X^Yca_a`[^_a^]bfOUNVTZPUYU\RYUWZhginnlpjhpnjikntba_b^]bd^ef`dd`guwwqxrry~{tu}{{gemohqosrkkippntvr{xuttx{wv|}xzY`]`\\Z^`cZae^deNTUOSRWWTXSZUTX\lgjjhefnhgirkqnjY]b`]\^cefd_bdfertpwyy{xvz}s~zztljijiklmlnqmlmll~~sqxz{{w|~wzxu|z{W`ba_c\^c]bbcd`cWSUQTTUTSSQTUV\Tmfllkmogghimisqlc`^\`bbbb_a`e``aszutsvxurw||y||{elgioijhglmqqonm~}zvrtuysz{t~{xw{~`Y_]]Z`]]Y[\Z`a_NVVWWURPXOTVP[PUijfidnhnhoonkhjjaaa_``_a`]^_db_apnvrqvyy{u{t|uv|ljmlklqnmjomknikx}}ttsuwys{{|xvx{}}PNILLQQPOQRQJLQPb_^`_fg_ba_hhhibV]RZWRT[^WXW_^^Zikknsqotrtvrmurxbgebbec`bgecehkew|v|xyyxw~y{yrkpruksuuqrqoqpqvy}xyzy{|~~LNLIEJIKLKHPJJKK`e]_`^fa^cegdajhZXZXSRSXYY]XT][\gkjkqioqqnpurlmqbb_`c^gedaigdcdewytzzyyu~{ww{w||mkklkttsqrrpsuut{w~v~yzz{{GMLIHPKLLMNIOQQPc[b_ca`e_b^h_`dhYXXTU\\\WVWZWWV\gpoihmloqkkrltplbfff`gdf`gbigffjuvuvxzzxy~z||mpiljmspqupltrpr{}}{vyv|{zy~|DJMLKHKJHIHMQJQO_\bdd[fa_g]fad^hXTRXZYXXWUTZ^U_^hlkmopimmmnrpltt_a_ff^hg_c`edcejvywz{~}vyy{~y}hgnolpprksmonpsut~uywy{|~}{z{y}~JJGFFJGKIMHMGMMNY]d`dfadbec]gddgOPRYRZRVZZWUT[^[fhjiilfnlmklnrskca]decfd]efd_adhuzyzv|vyuxu}uwmojkkqriosnouuqt~sxw}xuyw~}y{|KDMJKFJMMLDJLIQK]bZ[[Z[_]ba[\affOSUXUPXYY[ZWWY]Vmjdoeijppqoksplk\_\`]e`c`dedf`iczxwvxqv{uwtzy}w|ingnhporjsjlsntt~qy{}zvzszyxv{}~DFCDKCMKJLHILMFQaa]a[aZcbc`efeadVUNOURWZVUVX\ZYZlljkhjmkhihqmrtha`[`aaefbcc`_e`fvrspvxr{tuuwyz{vkgnlipnqljslmtnuvvwx{uuzzxzx@FJHGHGNCKLILIJK\^W_`a^`[e]][`a_OTUXMOVTWXYSRWYUelfihkgijilnlponb_Y_`\`be`^cdg^^pwo{zqr|tuyz}w}|lneioojpjlrpsnslz|uys}{{}|yzw|xxJHDIIDJGHFFDNGOH[UZ]`Z\\[bb``bbcUUVOTWUXRWXQQXWVickjimehkgolopip_^]a`\c]_\^b^gg_noqruyur{yzvsyz}ngmhhfhokqopnjllzsqysqt{uy|y{}wy|}DDGCJHKFELHGLGHEZY`ZZ[X^b^bdbad\PMRPQQPRNSPWSQRRbjcddlkinknphmno[\_`ZY[c]Z\aba`_orprquvrtyyyvyyyhdklnhjoplikromo|}{~|}}tr{p|yz|stvz{y}GCCGADDCJAIEILLL]W[W\V__^[\a\Za`NLWTMLOTSWNQPUQUbffkgjldmjfemhgi[[[__b`\a`ddc^`anroxvwxyx{rusttscicfdmffhmglgoql|~~qovrvrrwwvzwzwxt|~DDGFB?EHBDBMGKHFWWV\`]W\`bb[a^\ZRPOPQQQVWOWNXYYRihfchekbkekhnhjiW[^\\]\d[]_`a`ebnqsnrutxywqtutwtjhceojghljhkkqnn|{||}xuupvowrrsvytv|y|}}}}BEGC?IDDJBKIKKKG\UW_^^_^[_\\\aZ_QMSOOOLQTTSVTQOQgdcjfblejngfmgjk[_^VZX\\\a`a_[^]rpqrkutvuqqqptysigklmlegifkggmlnzx}{}uutopvrypuxtzvwx~}~}}FEBEH@CDAAJGCJFCYSVWZ\WYX_a_a\\\KIMSSQPSKUWVWWVShbbjjgldfieagfko\_^YbZZ\____[a[[rqrrqtonrsqtxoxudcdigdefmlmmmjjjw{{{~}{}~mtqxrstqwwuv{tuqz|~z||FEABCBAIABEDD@IFWVW[T\X[W\^\\[[]QNJQJJQNPPVLLRVWhffibbcaijfekjjiV[\V_Y^Z[W[^`\aatsroqssorwpprtquagkegejlfmlmjemnxzy{}}}mrmpwsussuuwyt|v~x~}~~BA?BDFAEDFBCC@DAVWZWUYYZ\WW\V]ZaLHOINLLQUTMSMRWLg_hcgg`gbfadfhbhW\]YZY_\\\_]`]\alstnknmopwqruqypbbhiiibjeffiifmnyw|~~~~|||sroqwsposyxywwuxy}|zWSONMUOMSYRYWXSSBJBLLHHLGMKKJJPLW[[Z_a\_[ca_cff_PTOTQVQPRYSVR\[Yeddifegiloniqpli]a_\Z_]b]ebdbffgyvwxt|vuxsuyzzvwjiomhjjmknpomjlo~tuwwusuxy{{|{|wyTRKPVRMTOSWPPSWPDGALHDCDFIIHGHEPW_Z\^_]^Z^[f[bbaOMUTPYQUYPVXR[[Tekefnjmiqglhlign]\bbacbZ^`afe]cfusqrvvqv{truw}|slgdhikohmjllkshl{~~~squxru}{u{zzxuuOSMLPLVMMSRVQRVTBFI@IHJCEGJGHOIJ^X_aZZ^_[[[`^ecZNQMRVORUVTWVPVZSlhejfdgojjjlnkmk``\`aab`d_cb\]e`uwtnwsuuvrsw{}ttchgklhimjgkmlqll}z~upuzrs{zs{ywwvvv}OOTPKNSTPXMUUVST@CIEHECCFHKDIEMNZXZ^Wa^Wb`a[[`]`PROQTOMSXRUWYXWXdegbjdjdijllohnp]\XX\ac^`dcdbdeapsqtqtyvxuxxyzutdfjkhfjlkokgrmrp}{~}}~}zsupuvruv}||~xx|}}IOPRJTKTPSRUSVQUAAADBBBJKJHDMLEMZ]Y^Y^XY[WX[^dc^OOMMSUPSWPQOQNQQhjicakfjimojkmgq[]^XZba^][]]^a]cttplsuvryqrtyutwlfikjknkgpfhfllrz}|}}~tsyvs{qwzxrtvsw{LILKPMSMOKPPQUWR@BEB>BHJEBDFJIDGWW[X\]WZ`]Zbaa_]MNQVOSOTTQONMPOThfdgaijcimcfmpmoXY_]]Y_Wbaaa]`bbpolqqvwpqmpzpywykgcglhjjhkhgjjgm|}{z}z|nnwsopxuovzv|||v||PKQPKQSOJSVOLMVMEFGFFBDEB>DHFJJI\ZTVY[YY`V^Z\^baLLQJKNLPKQPSNTORbchfhdccdfjideik^[_`Y\X`ZZX`\Z]^rtumttqrxpmuztzvficdicfiigohmlhm|x{{}|tmnsqpptqusyr{|r|}}z~~~PIHPISINKOPQPQOSG@EC?=CFFGABEIHDY\X[^V\]^Z`]^V__NNKIONPUULPRNTPR`adefkh_hgkffekfUXW^]V_\\]^Z_Z]drtpqqutrrpptxzsrffegilldlhlknmfhyz}zzy~y~pnpvutsprwxpqssz|{|~|}}NMNNOGIRONMRNJUP@F>DD@DEH@GBGJHAUS[YX]\TZZ]]_YYWIPMMRMOPQRNQMQRS`b`b_g`fkfclgfmi[XZ\T^_Y^\Y`YW\Yipmtlvutqtwswsvwifheakcgjjjidief}{~~x{~||{~pqsuprpxqtutpqv{~yyz|}|||{IHKJIJOPJNSKRSOOE>ECC>DBCCGCGHGCVUWVW[[Z[[U^W[^^OQMJNMLOTILQUQNQfifg`b_di_ghfbeeXZ^W\U^WZZ[\X[^Xqsppllttqqqvqqurhggcfacgghifglfey}{v}}~~~llnotqotopqrsrwqx||y~{{}zOHILOKGHKJJORRRN@?A?CAFEBEABGGDARXXXZXQYY^[^UZWZMMNGJQKLNNILSOPQdfad``bfcfjefgkhTUU\T[[WXYYXW^Ybknsomrkoporqwqnv^fe`ee`hdhkbflhi|ww}z|x|zkntltnpqmquwvrsux||y|y}|~NGFPHLLLLOQMKOPOA<==A>=?;?;BA?CGAC;<>=DBADMONMOQRTPPRUWVV\KEMJELEGHNJQNINM]_^]Zb\_e`fd__aaUQYOXWVZUW^Z]XYWhhnefqkomqjqorrq]abed``bea_`cbilrv{r{yz}zzxuv{kkomkrikprplpkor~t~||}~}xzyz{|y9644999<;67=?=<8LHPPQQMKOQSNSNUM@?FD>CEAFECGDGAGS[[UY^W^UWWY_[a^NRNSKQNUPKPMLPSPca_cfjcgfgchiefkTT]W]_Z]_`Xa^_b[oprsvsqlqqssrrzufcbabbihjnggjhhjzvy}{{|~pptrprvpwrussp{{{zzz|}|852::53368<<<575GGFGJNLPMNNTMMSM<>@GDA59IHNHGKOGRMHRONKNAA=EFBB>D@EEA@CFTSTWY\YYTU]ZZ^`^FHOPNLKQMNLRNUQM^^cbaaaf`fjfcdidZ\UXYYY_``XZ\a`Ymqtnntmoovkwwqttibjeiiiebbedjgify~{w~zzz}|tsttmwsnxuwqtspr}|}{{~{4362700499889:5=NKGOJJIMHGINMKNN;@A?>=B@CA@EIFBBUSWU][V[]]ZY^_V_OONPKMLPNKTSNOSLdcb`b]_b^jciibjbUS\YWWWY^^X[a``]sokkornqnpuuvrtoab`cdedhjijhdekf{|v~{|y~}~kmmrqunluvssttovz|y~{|z0187:308842588;>CCA>BNVRXVSZRXTZTZS[XDMJOLGFQKIQIOJGOc[[^_[ebbdac`_hcWWXWSXTZTW[ZY\[Zhphninjqoptmpstsc]d\bgedhaddedfjsxzzw~wxyy{{w~niojojqqlqnjsvstu{}yy{y~{z{}|~.2-27-48--/33361DILEIGDJMLENHLKL=6=>;@=A==<@;>ADRROUPWQRRVURQZUYGEDMJGIINPLIKJKLY]c`bb__bd^ab_g_YXYSRY[TVXXTYZ[Xdijpoppmnionmrqmd`d`a```dgbgdhjcxytu{v|tzx|y{{{{jipklqimkkkmonlus~uxvuzxw|zz||~02-135-50442.712A@JGEKJMKJHNGGIL=:;?;>>AA?BAMTLQPWYNOUWUSWXRHEFFFNEJMHLIGPHJ_bb^^d\`e_c\f``_USRQQTSSUUSSYZZXhkljngpoqhkpmoqn]^a`f_bcf^bgdbchtzxwwwy}utvxxwwxkkohfnppoqtuslnq}rzszy{{{wz|yzv}~}2403345505138558ABGCJLEIJHKLFOND@>:AADDTKRNLOQUTYQWVVUVCGKMCIEHMLKLHEERY[\^a\`]`d\e^aadSSPSXWOWWUX\VZYWhfiekhofgqlmlkipZ\]\eZb]da`cdfecouoqwqqtx{yxwzxjehmnqmiipiqsktp|}txzxxv{zyz{{vw~*)-.24.-170-.167>AIDGDDAJDBJHILK9667<7?@@<@==D?BUNRQNTRWNWQWTXUTAGJDKCJELJLMJKODWVZZ[a\_Zab_ade`SVRMRTPXWQRYQUUTccehfmhmieklgmkqZ`Y_Zd`^]`]f_^afvnupypusw{xqsx~xjodjhmflqpjkiooqz{}~xwxu|qysxwt|y{~y}~~~-*,,,,0+-*3..035DA>AEGDFGDFAHIHM596956=6AA?>@;?>MNMILLQLMPOUSXTPBAAAIEGBGLLKJHIJ[[W[\`ZZ_a^`_`]^PNPTONPRSRWXSWOZefihfgimfhkohopq_^_\ca\__`_]^a_arqxqswowttvuyvsxjmheenjolollkmnk}~|}~nqyxzxzvz{wxtx{x0+(./*1)*,++1.-.ACCED?EBJGECLBGB7=4@5=9:8@=;:=8=MKJNJMMSUUVUOVSTCABEIJIDCLFCJGJDU\U\Z_Y\[bacZaa_KQOOTOVPPPWTXPSScaecelejellphlngX_``YZ_dabdb\c]]nmoottwr{txtrts|igiciohmhnopqnpj}z|~~}vvxwqxtvzysuwyxv~|}}(+/1,*,-20)4.-3+>F=AGCEIFAIB@GCK7:87;6;<9=89?>?@=>=>2/41556//1011433@DHIFJMKFLKENGOJ7=?:?B?D<:F=C?B@UUVRPPQSYVQSWYYXKLGENIINGHPHOHPI_\b_da\ec[cfg_faQYRTXVZPR]TZVWY^heopinnhqnppoknl\^^de_]f^d_bgehiyyyprzrz}x{xy}ioliqqrqnrqrtmou}vst|zzy}}{|}:6;:7<>>A=><:B>B0/41/0446650/332CFDCDBGEDKHKIEHG7<?A;?@@7=>@<<<-).//540--657659DGFFFBCDHJKBGIEQ;>:@8@>LNRMNTQSSQTSQUUTGKCHMFGKKHGMMOMPX\][]_dd\aecb]]]QRWXXTVYVTYUWWVYflcilfmjllphrlpkZ]_aa`fcdgdhfefcyrpxw{qv|wt|}z|jhglmjllpollqmpq}qwy{yyuuvx~}z}{y~8<48>?7:9><=;?;9-+2/,5231411402.IGDHIFADJDILLEEH;?<7;=@9CENQSSTVNUSOUTRXVZHKEFHFKMFLKDKLGF^`a^aab_`]^]_be`RPXRPPWWTTS[QVXVejidfdepnjlignkha[]_b[cb`fdhcga`yuvqzx{rx{{|zy||khhmoprhmokirjkt}}~~~}uzwvuzwx|z}x~x~1678899859A99<>;(/.,.*/20+0...0-?DEDBGHCDIIIGHFH8<9;@==>A:@<:9@;TNTRUPMTSQRWUYWU@LDIHDCHGIECKFMGXXW`\\ZcY][e\ac_MQQNRPNTRPRUTZUSbggdcjmkfffohkmhYa_\b`^`bbc]egc`wppsywpyzysust}skjknknmgmfrnmrnt|qvuuwxvuwuxyyxz|~;78985;864?8?=AB/23*+335,,/11533AGHGEFCBJHJHDMJJ9;7=A:6><<::@>A=LSLMRKPQPRPVTXZWIBAECGJJFGFEKOKH[ZZXa`]][b^cdb]]QMTNOTSXUTXPUZTTehiljnjhofjgkqgl]XX`ZZbce\\faba_unnrpwtrutrzzvxyhignllhiokjrqlrr{z}xxut{z}ztu|z}y||~:766496;=<<886=A./)*231256/152./AC@C@BCAEHFKIJBL6<:9>9<9=??@D8DAQMSJRMQQQRORSUVREBBEDJKKGGHNDNOL[ZV]W``aZ_``\_`bKOTNRUVWVVTZXXTScekfabifkfiejgqo\Y^Yc`[^_^c\]_a`nuotsyuwzwstvt{vhllleqmjihigrljl}|qstrwswx{zvx|yzz;:75327;;;5>>76>1+1-0/10-0503572BD@FADHC@FAJJJHG9==6=:@>7=@B>>CANSQULPRMLRRQUSSMCABGFJEHDEIJJKFI]\ZYX_X[XY`][Z_\VUMXSUVUVVQQTSZZjcihhieejkngfomob_c\`Zbb_^a^cc`doqvvuyvuwutsvrusmoolhenmlifjlonl}{~~}~xwuqrszuwxsy{|xu|~~21246567<;6:69;=(0/0)/44+-,/0/45@>GD@?AI?>AHGEGG688657@>==@8:=?EJOPJMMOQWRUNOMTPEKDAKIHIJGGKMMIKW[SXYVX\_Z\[\Y]^STOUONNRWWUXZVXYjgeebggmbdjcgilo]ZXb^_]`_]^daa^hpmprvwnsvwsxyr{tkkmojggjhrhjmpjqx~y}~~rvtuvzwzywstx|vv~634679;4999>7:5=.1/,*/4*,02,.3..?EE@A??BDGAGF@DG856;69@:=9:<8AB?LJIOPNQNOTKLTPUPEADDABLDCEKLKHGO\V[[U]Y\^Y][^Y_aOTWTNUWVOS[WRQSSd_dhcdchdcgnkddl[_`Z`^aa`^d\`beftmqloupqotpupwurjklogifpnpqmlnklw{}{svqtszq{yx}wy}uw|}7496769162<79:56/0.-.20+04/6../1=@>CFE>FADEGEH?B4=7;769=<7?><@?AKPKLLPJMLPKNVNLUEIJGBCJECNHIHDFKYTWYYX^ZVZW\W[Y`OWNSLUUQXVUQRSVUibhfgfbjheegeidkXb`_b_a_]\c^^afflkquovstqwpwouzpmkfiljpfjlmoppplyyxzz{}{}|~ytqtuws{zvywx|ww~~219222834:5:9;9<+))1,+3+0/62,448=A@EADA=@BGEEDBB779<><<8>@<<><8@MGPKNJPRKOMOTPOM@IKJGDBHKHEHLHJHUW[YU[Y\^V]V_X``NPUONWSUQSVQWWWSee`gef`fdfhijkfl\[^]ZaZ^a\^__]^cmotnoutpmvvovtuwfmhcepkkhmkljksny~|{|{}~||yszyyvvtrt}{ty|y}~}442/1358:3:4355=*(,/1,-3/..64116C@@?BAC;F@EA@A?C::;;=@=<9C<@@?E@BCD>B=9756<:@@9::9@B:LFIFNMLGLJHOMKNSCJFHECDFJGLMFGJNZW[Z[TTW[]XUX^\]VNUVLONVWQUQRWVVfab```bgbghkhibd_Z\_Z]]Z\ea_\a\_qiknoponlouutvnteiggflogiqhjnkimuwz~}z|vvpqsxyttuwyzv}{~24/0635505752871/0.*++.+0+425,3.>>?;=A>E@=><:88:;@?:7<@8B9LIMNEIGLHLPHPHKMHJGIEGIHFIDGNKMGXUTTZY\YWXUZZTYVQSMTTNQYWUQSWVOYf]^bdfab`aacffcjXba\^e`Zfa\[]c^^qoqpkpqimlotuvqndggiimiggnllnmmkxy}vv|z{yy~|uwvtuxtstwvvttz|~+'##&+%(&/(.'-+,956<=3;:899;;>>>3601128251282:43BB?@C@HHFFBGJCCIC9;;@C?=EC@AHBGISQPRLRUPOOVNTSWRLOMKPOPQQOLROPPRW\_^[_Wa\b\Y[`_aWOSYZVYZ][ZY^[`^gjhdeckhlneinjoh_cgf_cajhd_fkhgitxtrnwuyuvvwxvsypkjtkksqulnmnwrq~|~}~~~|z{x~x||#$*#"*-)'-*&&0(.339193<;9689<5;=68647586880789<;CBACCFADEFEHBFFI:?@ACBAB>E??B?@JNOKJPQJLSTRPQSPGHMGMHINOLQRSJJQXXXWUUVZYW\^ZXc_TYSTYYU\V\WY_WY^fggaabejlfffhdfeb`ebd`heecgahkgiqunvptwvsxpwqruuonopknkmntosorus~y}}~|z|z}y|||~~%"'&$,)&%(%*+)(-5334484446=;987:5648/068:7;544:=>F?B@A?AHADDCHEE==?ADD@>G@BCDC?DKOMOJOSRQRQNMMVMMGFGLMNQQOSRRJJRZWXUUWT]X]Y__[_bRZ[\]ZUYZ[YWYY\`dc_bfecklkgimige`ageiea_dfkegjghqqoqppsvtsosqvzxqonqrvtmtuuvpstr|}x{~~}z{{}y|}{~~% #%#%++(,&-,+'.00/68399:797836:5626/3975166;257>>>>BA@@EFIHMRMIOOMLTLTRLGHKKIKJNQMOQRQPPSRYTT\ZZ_[[^`[\ZVVTVWXZZ^Y[ZZ\\\_edd`aafbliecfhhdfhbdjdeigcadhddnmqlonqwtpsrswvsmqrokoktnntuouvpv}~~~{|xxv{{}{~z#'+-)*'(-,-.%--014916116;573:9=363321475267586:>;?ADC:?AGBDAHDCC@;BBD?;C==CHEFGEFLLIPLRGJOLTJNNUOLEKNOOKHSNNIOPS\XUR]\V\^U\VWZ[[ST\[ZXXW[V]][Z[Waecfcfgdaeejiicfbded`ffdkbjddgdissmotruwtmorwqwvnmlnsjssmqvorrpnw~|~||~|zxxyzzy{{})+%$%,'#,$.0%,'+//13/478617294353/5.88763:436542<=<=;?ED>BFB??@H?A@;=C=CB>CD??@@JDLKHMLNLLOJKKLJFLJNNNRPPORQKTRNWWSWTY]^SZ^X[`\bYTZVWXZY\V__\X[Uddafdegdicbihhbjgc`ideebefa`ejfgktlqqqusppsrmvqvommosqmqqtnwnoopyzzwz|v{z~{~}|ww|{}{~z{%%&-#!()%&'%/&+-254310156370:76754850385451<<3389??EABDC@>;;CCC@A>B>AEDEDNMOKHPJGOIQHPRRLGJIJGMPLLMINIOLJUQSUYXX[^\XYZ\\[TW\YWXXW]W\\\Z^W`_bgca__hechdfggb`f_ddfefeddghhkslsoqlrorsssovsunpoqssunwoltwsprwxvwx{y{}{}tw~z~~}|zx~|%",$("&&+,+&'+(+03146359851:66241272437542445967>B=:=:DA>:E@CE@G>=?=C??@BCFBGCC@HJJGHKIMPOKIIQSTGJNOIQMHGOMQKNJLVQXSWZYU][U]\Z]YWZWUSWWU^[^\[XZ[f`ceaffacfficbgfeage_adff`adedehpnmismrlprruquutnojrsqnvslutwqstuyx{y|{y{z{}{~xxyuy~zz}~~}{'%)*"$$(+&,+-)0--50-510379240884-630064.833734=;9@><@@B?CE>?BAA?;@@CDD=?FEFIDEKJJJLHLGOQNNFKNMNGQOKPJOQKTTWUYWURXUVZYZTZXYVWSSV\\\[WWYX[\Y`^`b`caaac`fe`bebcgceegegedgfedhpjjjqqrrottjqnrqskukmlkqsntqqynsxz|}|{y}{zz|~~}}uvxx}~}}~|{|$%''*#%'%),.*)*.5.17354..3-2370154325446844<388=<>96;>;:<:@DDE=?::;?@EAA?B>BHNEGGKOHHKLGJKJKJHLKGKLOOHNMJMNJQVYUYOYUYTXY\^YZ[UX[VUYZ\UX^]V^_Z__`bccafee_cfehi[`c_faeaghfebhkknlnmkmhoiqstoksnonisnroqwqqtwtport{yvvwy}x|}~{y|||~~~{wx~y~{~''#$+()-++'*)/)*--340.30/7562083.0527985736552;9;99B<<;9A?@EAB;@>AC>B;@CC;E?=FHFICEEKKNHMOFIPIIPEGJIIMNNGQHRJQORMSVSSWWRRSSYZXY]XSSSVVXZ[YYYY[V[`]]a_`bdefabdecibefa^gcaigiighejkgiilnnpqqoqnnssrmkqpluomruspuvuzxxzyzxyuv|ywz~}~{y|z{z|}}} ''$,)&,)-,+++0//..14+243423.112/2802639127;68;4:8@=;B9B=:5;>:87;?;A<<=A>?@A>EABBDDGEEHJDDKMDHKKNJFMIFJHIFPKJKMLOPQPRVRNQUTQTPZSUXZYYWXYUYUX\W[a[WXZY_`\[`\]ae`_cedbaeedaedfdhfeieknjhdgmnjmkjoqiqjromnmmooosqsorsvyvttswzyvz}yx}~|y|yz~z||}}~}~#)'',(#*$%'*/((*.0,*,.0/40512410/2655480037674=75<885:>>9?9@@9;BB:==EC@DEBAEFJHEGMHMFDGMGLJKIHNHMONPLKPNQMQMNQOWNWXTUWXUSYVXYTVTW]TX\W_Z[Z`^`]cbcbd^`^cdfde`cbhiceedchddgfdfgnilkoomionokmkmojqqpomqoopsrqtxy{|rtuxv|}xwvyvxwz{x~{~}( *)(+,+((*&+,/*(+*)0-/14,0,-43134637360123=34:75=:<=;5:=A;?@;>@=;@ACB<=;E?=EB?CEEFGF@IJICLFKJGNGJFIILQMPKLQRLMKPSTOMSVRWUSSPRRTQTWUZYWUXZUX\U\Z]]a_\Z^]\_d`da_b_egeebgibhcdheeelihigkfmjmijjqpnllkkrosrqnmrssuswttxsvur{yut{yzyx~z~z~~|{|||~ \ No newline at end of file diff --git a/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-cr.bin b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-cr.bin new file mode 100644 index 00000000..0613a5e5 --- /dev/null +++ b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-cr.bin @@ -0,0 +1 @@ +qqvpnqzupsxvst{x~zw|~~}xx{zĺÿqsxwqouq{touvttqxww||{wzzƼǼ¿ſɿttrypprwzyuxpuzquwwxzz}vy}||~~~ǹmotvqpwvstqwtsupvzw{v{uy}}|~~y|z}Źwqtqswossxsuuqpty}wwz}x{ww{v~|vz{|{~~¾ºunkvspvwyrstqwtq|xt}v~tz|z}|~yxy{{¸¾Ƽſtnkluqrqtwwlszpqxv{wvu~}{~}zw~|~Ĺjospmpsqxupwtysx{rzvt~{{s}yv}z||~úottrkrntppmplowlwzqv}z|vw{ysuv}y}}Ƽpnjjvqvvmqtoysovsuszqtuxyzzsy{y}|~}½ÿnpmnirvlqusmuromqrxz{zuzx|}wztx|y~|ø¿qtpjrumqtvnpuwwmtrvwxuywwuw|z~swy{y~z{}|}¹lsmsklsmpsnwonlnxtqsxp}uxvz~~szwx~|zxy}}~~~~Žigrjuslkqsnurqorpw|ysws}t|{u}w{}~||z}|{÷olmpjunnqrnljotqzworyzv{r~uzytsw{|y{}|}}|~÷½hqjpjnjstvjhkpvlsyx{uy{{pwuxuqw~|v~~~zy|~~ÿ||}vknljoxnptuuutqz}xy}yzx{v|zxwu}}}~||~~~{~}~pputopsorpkomvms{ux{{ysvsvy}~v~zz|}~{||}pnjruuussqrsosxvvsxsqx|yt{}y~v~z{{~~~w}}x}~|}rusrkqjnpnpwmtwtxsr}susyv{uyu|~}|}}}|}~~|z{{~z}z}}z|}lopokknqopwqptvwsux~{yq|x{ss}|v~|{{~}tz~~{~}}|zoroorkoltntjloqmyssx|rryxvw{svuwx}}y}{{|yyz{y~{nrjjqsiokuqlsssvwptquytz}u}xwzyswzz||}}yv{}~}ypkqkmtljmnljmwks~{rotvxtvqqvy|szt|}xz|xz}{|{{}yznmniojotklsrokskpqpxpqsyq{zvsx|ux{{z}{~y~zyx|xwx}}yx~jporjiihooqlojmr~stru{wxsrxzuutypx{~|}y|{z|z~~~wz}~|zhlfinrklhlkrnlqiqsrwspsxqnqqyy}rxyz~|~{|~}vz~}}|||{mkhkgklrslpprlptwrnqxrosxvyswyw}y~zwx|x}y~z}ywy~zyv|{z|}pghlgjqslkihuosm~ssvqzupnwsvpuzu}|w{zxx{xyz|z||wttzsxz|y}gfjqninooqjhgjlm{~}tvsxtzwtrmsnuspz|uzu||vzyww~uts|~wvutwwxoojomhmnnrismrgm}}~pusnnzyyrnuqyqtu~z|xzw}yt|~|{x{~~~~}xu}t}xxw~x{w{}icjeoekmglmkoflh~pttzlutrusrmrvvyv|{xwxuvzvyz{||db`ghbagjhfnikhev|{}y|injormnsqulnsvjw~ozzswyprvtu|qrrz}y~{|~~{i`jbg``jajcdifmkw{}{y~y~}pskqssrrppjotmsrnzpzxt{twrxxvw}s}z~ywz~x}bb_e`gajhbdbchgc||wxkqilhkjknopuslun~u{vysvwuqusqwtw}|{}}|}~x}|kijegkibikcmgiecwyw|~|yokqjqpmittslkpnowzqy{ytwrrpsxs{xxywx~x{zz~^_fkhgbhdclbimkjvuvvz{y~|z}|ooqkhjjjjonqlojo}oxvvuyvnqr{ypstx~x{wyw|{wyz_eeed`agbkcjc_ef|zvx{x|zyy~z{frkqqpmroqllklos}~mvuxxopspzvwqvqx{y}u}ww~}~yw~~`h_i_fbgcc`jdccjuxtuzz}zwz}kpepoorhlmikpltt}~tyrpmozpunysoqys|vu}zu|vzw}{y~|}hfa_^`dcbaacfffe~~zy{tzzw{vyvgplkiplnfjpmolrh~}}nnqpowwlxzuuuqsz}ut}|tzw~{yx`b]bf`faaj`eiakjtwzvwvxzz}yjlfiehoiogiilkir|}}~nsrqvorrptutqxtss|x}vtv|v}{~|}~hdhi_dcjd_a]glcfuy~txvw|xz}zxzknddefniljrgkkni~~|~~~upqvpowmmysqmxn{}uxzy{~xyzww|}}}e[f_ghfhcbgdd`eayvuxywxu~zzzxzbkhinhihioeogfhh~~~}qqnntotqrkrtysxsvy~v|t|~z|v{u|~}~b]e\`abebcgccfbbvtw|zvx{}{vx{wumkdlggepplfplnmk{|}~{~}{}}qkrkqntwmllmstvqptyxu|t|u|}zxzz{~}|}{^^^fa]^_ba_cidddrry}uuw~xv~{zwxjgijfkekpdgkgmjsy||kmookkuqwlmouvqyrssv}~tuwy~{~z}||}{^_]_d[ga^be\__h`{}svyz{~sszyx~{hndimkodkopngmqo{|~}}qqmnruwlrlrtnrvozqsu}xs|{t~s{|{x{y|~{Za`db^`afb]gc^b_}}rvqw~v|w|uxjnddehildfefdpjq|z~|z~{ntmlotrkwllulntqw}vuzv{s{xzszvuz~y|}}bYee]fa_dd]aafef{qs{}zuytuuwz}x{ihjcmlkglgmmffim|xz{}||qutmuutpqppssrvow{{yvry{uut}uuu}~ttnqmlxryoypnrvygb_`abaafgikckblw|txxyx|{wpkipnmpflifnnmql~~vxtolwvsqvstyxuz{|wvsz|w|zv|{}}~~rpototrqnwzowrqu`\_^gijibdbjegf`x~{stz~{x{y{}yz{mmljhjonpprtqnrm~}}rlnmlnxxvrzppnovttx|wy~~}z}}yw|~~vtwnmvnorvoourwx_\c\eei_ajga`gbc{~wvvy{~~}~yw}mnjdilnpggooppsr}{|}{qvlpnptxvxtwqor{}v|uyv}}z||y{xwz{ppvtnssrnnqoqpww_a`c_`^cafhagdke}zsz}w}~sxvzznfhiljfjglnmqjpr~{|||jrwnwrotvtnvunnr{sxty}v~z}wz{z~}|}uokwpkrvtupyllrtc^b]ce`bb_`eche_wsryurs|wtwtz}vioileeeeirmijpjn~~|}|~ppumnvvqlnmvtynyxyrvxx|{zy|u{{y}}{~kjnkqmoqomxunwwqbcdb^^_e\`^fhijdzvw|zwystvu{v{glidjghofkiigggozz|~}}kpnotjnxunvqpsto}|zs||x{xuxxyzw}~z|}nmksopkoovwntuvu`dc]a]eeah\eiege{vsuxruvvw{ztxdddjkilnnhfmlfjp|~||}{jnqssqmrpluwoqwmrsztwz~|s}vzvv{y|y}~|ilqrsntvlmnuptmsb]^cb^f^g`fa]bicswyut}r{u|tstvztjkefgcbheldlpilkyy~koilllqumsulsqvwur}wyu}ruusuxwzxyzyy{z~qqrnqlqosppolwrv^b_bf^\aed`bhg_`supq{syuuvvv}yvm`lfdcankkkkokneyy}{z{|}{jtqkukkrpksruoqnrtru{qx|xwtxt}s~xz~~~miropjkvrornpplsegc`dbb``f_^`ab`qt|xyz}uysvty{yfihfihhdhkonpjkkyyw{y~{}|}{ronluorlrnquktspvvuttryx~|r}uy~r{~yz}lljlokiqokoismnpad_Y_cc\[Zf^\fderwstu{yq{u{z|q|}d`adjffljkkedembx~|{yx~~{~~}hpsipikktqsqsotqpyqtrtrqs{zq|{xvxz|zy}}|yzinkpkilojotolojtdbc_\_acZ\_dfc^fyzxt|t{v|{|~uy|u_af`icebkkhglkhh{}zv|xxz~~lhtijlklouljmkltoqtsqpxzuzzxty~tv{zzyzzx}{~~fnhtpoqmhlmpjjnuWaWa^dY^c_e[c]`csqsyyu{tqxysrsw~jhehcbgjbjjehicl|~}vwwy{~hgmshsotskjjpomrtnysqprryqqx}zqv{}}}}|gokqlhhpmriprktqY``Y\\ab_a\c^]bfxrvuroqvptr}u|xr^cggcbbhkkicgjkkxyv~|xzmljimimnmpsjmmkk~pmuwxys{yuupsvwt}yx~}~|}}z{}fqqrmsilskqprtmra_a\_^_`][Z^^_e^yrywvyzsqrtxr~|tjgcaeihhicecjedf||{y}{x|~fnhiphjihmmrsqonysoqpxpvznywq}rw~u|w|}|y}z~x~phnnliqjmghnhkqmXacdc`\[bX^`YdY\wvqupyszrzzyurttiihegedgecddifceuv~yw}y{{plpqkospokoqjmji{qqrovtqyy{yvsrxyx{z|z}www{|_]VYY]_^[_`\UX]Zplknkuulomkutvuo_eZc^[\ch_`_gfeasstx~yw}{}zu~zilkfekffekifhmpg|||~|z}~{skprvjttuqrpmomp{|rs||xsrtsuvxy~yx{z}zz~{{|X\ZVQUT\YSU\VUVXlvjllkunjprtqmxucada[ZZ`baf`\ebcqutu{qxz{wx|us|efbehcnhidkleehh}~v}}~zzy|~nkjkjutsqrqnrutrys|p|{vpwwu{{|y{y||z~|~z~V\YVU]XWY[[T\]`Zshqmqomslrkuklqucbb]^efe_]_b__^dr{yrpxvx|ss|t~ythnjmcmilbnfilkll|}|z~}~}npilintppupktrprxyyxrtp{uutzz~wvz}}QX[YYVYVTVTY^V]^mkrsshupntltoqjub][adcbb`^\bf]hgsvuwz{qytyvzyv~|hefjmcnldecnfegq|~}|~~}hkoqmqpslulnlorup|qvsuwyzyvuusyy|zz}|y}y}WXVSSXTXV\TYR[ZYhlsosuprouqhvrquYZ[c[dZ_dd`][dfcppssswqxwwutw{|uii`mkklgbkkfddik}{|y~zy|oqkllrthotmouvquoutztqvq||ytv}xz|}{~}yZRZZXTW\\XRWXT_XmrhjjihmipnkhottX]_b_Zbdcdda`cf_ytnyptt{{|xt~zvtbbcdbjdmbmhhkemfxx{z|z|iphqjspsiuikuosrovwzvqvsttsqwy}y~{~v{~zz~~QUPP[Q\ZX[TVZ[R]qqlpipgrqrnruunra`YW^[be`^``ecbdyxuvstxvrsq{w|}qgh_ifgmjijhdckdj}wyx|}yyyz|~{mhpnjsntmjtkmuotssuxysszwvuv}s|{yzyxyzy~PS[WUXU\RZZVZUW[jmfnoqmoitljjnolX_`bVX`^abc[Z`b^pxsurxqstsvxuzyukf`cgafglbcilicbx~uww{}~oqfkqqlsjlsqtmto}}qxsw{y{xvvsx~|st{y||~x~x|w|WZRYXQYTVUSQ^U]RmejlpjjkiqrnnorpaaaY_b_b\`cZZb`_vnwvuwqsupywxys{fbbieajccbbgblmcuvxx{{x|x}phokjfhrmqpqpgnk}qnwqnqzqvyvyzsuxyvz}ywy{}TQYR[WZWRZVU[UVQjhpjjignrmsspsqlZW]Z\[Z]X]Za]Z\[mxnpnxxp{vx{pyv}^eeh`^`jb`afejcbvzvzzwy{|zmfpkrjkpqqhlsslo~~rowrxvy{opsv{wu}}v{|~}w}}VSRXPSTPYPXUV[YZngkfkenomjkqjgqmZWb_XVY^]bX[Z^Z^nrrxrwvoytoqxorrc`ahfhfahhljidefvzuy{yyxzekeheqhgjngngpsl}onumvooutsyswtuozv|~uzw{|{y~~~}}TTWVPMSVPRQ\UZVRhhflpnflpqrjqmjg^\[[\\]bcXbWbbc[vusmvnxmxpxpzutq_cg`edckadcficlkuyzuxy~x{{z}|nkdjpmjjnlimlspo}~xttmvluooptvosytswxw|xyv}v}|}y~SUXSNZSTYQZWZ[\Tofioonpojoljkpgo_Xa\ZYW\__]b_ZYZtqnwrmxpwytrzpuvdfd^a]ccaiehdabb{zzxt{~~|xxxwzyllomqoiijhnhgonp}{}uuskoupwmtvqxstuxwxvzxw~~{{{{~x}~}~~VVRVZORTOPYYPXTSjdfkilgignospkjkVWV_`^[`Waabbba]uonxvvuroxmnpowzdge`jc_bgahcaia^{zyxzwuyzx|u|ggfnifihooooplkk{~~~}}~lspxopptsustzqrmv|{susv{wv}zz}}~VXQRRTPYPRURSPZShfhkemhkhmoknkil_YW^UT\Y[[aVV]bbvsswonoluwqoxuut^bd^h`f`a^`ffafk|}|uz|{w{vxyyw|dlphkiophpopkgpo|||}~mrkovquoqttvwq{sy~sx{|y}yu}xwwz|~|~UOQSUTRVUUQRRPROfglhfikknffkfnjrYS\UZWV^a`X]Y]cWumvovrnspomoqtnr_hdad]gf`fedgdedv|}uswuwxzu~zweflmmmengihmleps{}~}rrnrvqnmrxuwuvqw||t|zww|}}~uwuv{{~~~kica`ja_gkelileeQZP\\UW[UZZYWV^YfijhnqknisopruunZ_W`Yb[X[d\_Zedbpnptopqswyysz{usdhfb_fbhbkglglil}z||~xy~zzniqqiklokoqqmiknqrtuqpruuxvxu|qq}z{yz}y}gf_dkg`g`iicdblbRWO[YTPQQ\UUTUR]fpjkmnklhmisipqpYWb^[b[_dX`b\ed]qwopxvxs{ovqurpxdcijigk`bdfmiafm|yxy~x|zx{}wmleijmsgokmmlujj~snsvoq{xqxxxuzsrz{}|wy{{|y}{x~cia`d_k``ffjcgigQWXPZSZRTTZUU]VXohprkjonjiipltriY^U^_Y[`a_`aX`d\ytpvqoqyutuwxuyuggcffjhdkehh`bld}}u~z|{}xy~yzejilniknkinlmrlm}snsxopywpxvtrsrrzw{}yxwx{{x~~ccie_bgidl_ijkfgQP[VVXOQVT[SXS[]khjofrmhqpqjjolo\[\Za\WZd^b`dcabnqsmvouotvwvzry{db_^bijchkjjhjkfx{x{x{}{|{hfmqihkqlokisptq~}xqtmttoqs{{z{vuy|z||zv~v|~ywx}}\deh^i_hcgfigjchQOQUQQPZY[UR]]Q[knhpinggkfghmtrkZZVY^aW``YZZYXZXvvtplxrvs{ztvyq|bde^ajicd_cadhbi}}vs{}|ywyz{y}oklmmnpjjrgihnnt}~~srxuqzmtywoqspsw}|x}y{{~}}yz|~~~~a]a^eai`e_dcejlePUVOOOZXVORSZTRUgglilmgipmjrqrnlW^\b]Z\_`[YYWVY`sssonuvnwvprx{xzb]ifd`f^igfj`fgixvtxyxytvx}~okfgqklnimkgjjgn}|~klvrnnvsmtyszyyq{zu|v{}x|y~x|~~e`ie`fic_hkb_`j`XVXXVSSUPNRVUZXYnkdgjljjpeojkorqVW^TVZU\T[Z]WaY[ootttpnlotuuopsufdfh_b_gaa_gc_ad{|t||yyvs}z|jmfilfjklirjpnio{~}}skmqonlrmrpwny{nwwws{xx{|~vu{|~}~~f^\f_i]b`ddfeeajVRVTNMRVWVPPSXXOloiloemnnkqnoeop\WXTY]Z``V[]X_Z\knqssvrnrsxrspvq]a^fe]fccdf_g`dk|}xyy~{z{wv}yyjjijmpohojpnqqfh|}}|}|qmousrqmqvwmnqpxwuwx}{yv|zz~vyyu~~}cddde\]gddbhb^jcRXMUTPTVYOXQVZXPfclkionejlnnphheT]XZ_V\[^^[[W[\^mplnksmqypoysqytb`bd\fg`fc`i^]c_rzv|s}y{{{~mklgdoghnmnleleg~}|}oqstnqoxostrlpsyytruvxyv}}vuu~{_^b`^`ff^di`fgddWMVUTNUOUSWSVXWRighghmnlmldofkon[_ZS]YV^_VU_`\W\txtumolounpsqmpo`bgad\f^abcaabe^z}zytu}|yyy~zy|znhmgiccliklijlihzjjmoupmsmnopqqvmsxw~uz{ut}vsyy~|}}}d^`cga\]``^fhhgbQOSPUQXXSUPRXWTOcikjmicjkokoekfiZYZTU_WW[ZUV`[[\ssnrllqrqqwrqsxs\^^f]bc`````]d`isx}wvzrvxwzyyu}bkjdihcjgknfhojm||z|kntkumpqjotvtprtr}wvtxzszwxwy{{~d]\h^bbbaega_dfdRPMMSSUMTKYTRVXSdbckehjlhlgeokjjX]YV^TYZS]XU[]UYrjntuokrvrxroptqb_Za`cZbc_a`bdcjxsrsu{x|yz{x{yyhbmcffekjkmbenjgzy}}}~rqnssqpspuunvvvpt{{xr|sv|{{yvv~z{v}|zc`a\_`]\[ag`_`]^PMTPMRKRPPSTUYUSdlhibbfgicnfmojhVR]X\^[V]W\V`X^Znssmjjjtvmnqrnolfe^[\\f_d_f`]f]e{uszzxs}|{tx}x{jifbhhgjlmnmefok~~{{}}}{pqnlrrkrsvlrqxrvwwwysx{vyyvvx}x}zy}|z~{z]_`]`]ff[[\^egc\QOQLOMTRPWWMXQSObmigfgmjgkemdfooY\\[WXY]S___[VX[ijmqlnrwoplqnuqnaZec_^Z`c`]cgc]guuywtty|t|y}|wvjh`fbcfllceildmj}{~~~{}lligkttkmutsurnqtspttyrv|uu}ut{x}}{y|~]bb]_]]]fb]cdf_]JOUOSTPLORWRRXSTjdichefaccnfflkkXX[]RZS^T\^S]Y\_sihgsjjtjtjqnjoo`b]c^[]e]d]b`ecfrrxswrz{s}x}{}|~cjgkffjnjifjonnmy|x}y|||~impkinouinmjwsvurwsqsoz~}qvtryz{xx}}|Z[YWZc\cZ]e^g_cgLQOLOOIOJROMVVQUa^a``ccfbabgjhhoYS]WQ]QSX[W^[U[Ylnmlgqintntrlmnn_[cYaa_c_`ecgaa_qryopzuyv{s{x{{xdekilffgjfacefnny|y~zyllpmksijqsplqjnqqw{zz{zsttvvu}t}zz|{||}yOLIGNNMQOHITRRPIa^ffggb]efhbjai_PPUVMUTOTVQVRWOVemmejngodffjokqm[`Z_V^YaZU[XV[]Zpnkpswosrrmstorx[[e^egadeh^idfj_u{y|~{yswyz{yy|iiecfclkmqjimhjk}{|~pptroptovnsqonxyuuswxyvx}uy}zz|z{~MIHOPJGGHMRLSHJH]\[\`cbgbdbjb`g`LNRVWOMWYNTTVRSWfhhgnpipjqemhhkpTTR[[`_V]Y]]Y`\Vlmrpolsrywtqnrqwefbg]ccff\_]jfbfxu{xz}ww|w{u{zzkhkoljmfnmfqlhhm~}{}|}}rququpvltsnytppusy~rvv|vu|wyu}x|~KKOENINKORNOKRHM`]d]\_e^fb]gca^bRQMXUTRMRQSVOPPUfefiknikeeokjpqnRR[]ZWV]XZV]Ya\Wkjpommmtlrxspovqce[_baaghi__dhh_u|}ww~uwxs~x}{mfnilnmiddgfmikg}{|{}ussvjvrlwrvosqno{yvztu||w}xuz~|~}||~IHLGMDCINNMNMMHQea\e`__a^\]ba_bbKTQPOKTPSQOVYWQQgchgomemonkinpgp\\[\WZX]ZVa_Y[^Vrqqopikojwnuuowm][fb`_abff]gefhc~ytuw|wyvw~~y}wffcghjglnmnkfgnizz~kmlsqvljvvrqrrluvxt{z}}tvt~|{yy{~~zzz}|~}}EFNMOHENMFHJLMPQ^c`ebbdaa^eacgbgTNOKPRROPRLMQWVPhdlhafgjiflcipfpY[T\W[TZXW[[]V\Zjnpmolssnuomvvpqb_[Zbdc\bdb^a_f`zwtwuxuytyxt~uwuillnbgibmklogflpz~zz|}}|kqmmlrmotujqtvmoqzvxrzwu}t}z~~x~~|~~|~OKNMLJKLMSLNHHLNb^Z_^\\f^gc_^]b^MURNUMVQPRPPWTZPficfekmodfemnmkh\[X_\[V_YUSWZUZ]sjkppmnjvqsjttwo`\[`bd^^b``eegf]ot{vtst}stzw{wy|lanndieimdllhoggz~|~}pvkmujrtvsmtvomxsvyxx{sx{vuw{zv{~~}zy|z}~ECLNOGFFPGGJEHKKd^_^]Z[eg`bcd\f]QOJRQKOPTTRMRKYOfhmkbegfdlcheien]ZYZTUZ_SWV[ZXX]thntkpmpqspwsoqu`]Zb^c^cf``d]`__ru{rsttw}t{|zw}fkdjlcamhgfgkfgdyz|}z~|miskpkmqqmpspxrryu{uvwryr|wwt|vyxz{~{~~z|ENEFEGCMPHBPDEEJ[[^[^^``cbaf_ga]LQNUUNUSWWSUWOSM`ielhfndjflfkdmiR\ZY\UR^WU^U[UT[shilmhtppqoqlkvpa`aa\b]d\_cb^iabs|pysys{x{~vz}}{iahdemkgnehikdkoy}~{{|}}nipjoirrmmpirursrwyutw|}vuxsuyxsv}|}}~z}GFDINBKOBCDHHHMC\`d]`]YdcbZe_abbOJOONQNTLPLPLMRTeeahckccdhfbblgjVSQ[XTVV]^YWYVWWfkrnpqmmqqkookukdcc\[ce\`a`[c`f_nts{y{{wwr{vvzzvjejdffdfilfkhnofz|zx|}|ljrlmshljljlomktp{qurqwsp}us}wt|x~{xz}{}}GHDGIKCLEMLBFNFEW[`^]cbada^e^\_bOMKTKONQRLOVRNTR`h_ebjkabhjhdijdVQSWQ\RWZTZVS^UVorqmmrknrqphunmm_]\[Z]\]`][Zcbc`tvvtyqzz|ruyu}ywbdgfldfgjbflhfel{y~xzy{z{klqigprporuutkmqpxovtwyyurwyrpx{uwz}~~y{y}JLGIKKLOFLFLNKKOYY`Zbd]aa_bc\gfZTSLSPSUKJLTNQKVR`fjha``lejfligeoXQSY\UWYVXV\^US^rqkkolpmhjriptsvY]cd`^Z\XY^`a_[]wsrvrvutp|xx}sw{abjfcjl`ieglckdbz~yw{yx~{|{nonjhhiiqkkprltonvnvvxqwstor|sxt}{wyv}}}{K@IHFHDIGLCFJJKH_Y\V^`^]c]^d`b\ePNQNIQSOMQLKOVWSj_ea_achglckiiehRV[[PXRV\ZYZURR_gjllqjqlppkvjoos^][^b`Xba^af_da_sqtpws{qp|vwvsry`^dbk_jahffiilihuyvwwwz|zymgjoosnjjqhrtkuo~}sr{wurxwvwwwss~x}~{{~{zxv|~yxA?BIIKEFFOGABHMNVZc\_[\Vb[Ya_`cbMIGHQGRTRNQPNUQUhafcahfk`kejglgfPVYRZQYRZXZ\XZ^Qgehgjrkohqqmosto^b]W[^[da[[cYa^]nmosrvsysouvpyu{`i^c_kgcbfblcbel|w{tw|x}}wy{jqflhogmrrklgpoq~~v{tr{ovrtspyuw|rvwvv~zx|y|{FCF?ECGBD@JCDGIK]YVY^_[]`\^W`a_dHMIPGHPGTURNTKQPaa``^_e__bahghhbPPONXTUPV[ZYYUVWlkgjlpjgmplonnjl\YZ`YXY\]\`c_\Ydqrusovrwprvzqy{|feedigadefdacfcgzu{y}x}|x}|{x{noiffpkolpmnknok~~~ljyxyxzmyxrupsuy{zzy~z{x~yx{~|IC>HF@H@AABAGEBDZ\[^^V_Zc^]ZdY_YKQGQJQLNITMOJNINa`_b^`afjijhajffRPQTXZXRPZTPWUXQekeljnhljqqrhqqmU^YY_YbZZ[b]aZ]\onpnqwqtpww{ruzq_fhg``fkgikgaiabtsvu|{w|yxyxlikdkqhoipqrsoqk|~uwuxmwpvvyoqtuupwtuy~||zx|~|@CHJEACDJG?MDDJAUaVYaZ^c_XaYV`ZcKOLLNJMPMMLJOSNWb]g`__``hflffejiOMOTTUVV[RW]QT]SofohkgrqqjqunmpqUa[XXaWZ[c[]^[ZZsnrtqrqxorturrwpfic_efccb_fc`chaxw}x~x{ww}~vwhhlkmkopjolljhru~}~mnsmoutuwoppsww}wxsz{x|}}w~xVSTWXZUUV]Z`[Y^UJFIDNHJBHOIOPMKN_[Yb_aZ_]d`c^hecTSNUVJOQMSPLXRUSjhjkdcjfgjkkkkfjTXV[VUXTTVV]Y_]Trhrjlsopjiotnupobc[Xc\`]b`c`g^_hwsxo|swtx{twwr|xlhkifjgigcchhkek{}y~|}z{kpjqoplkmrkktmplyv|rst|s|qq~{rx{|{~|}UQV[\]WVYZY[WXWWJBLHLKMDDGDFFIGGW\^`]aec\ca\e]d^IPSLQSSUNLWOVLSSgljbbeahkidcjlkjZZTQ\VX\SU_T[U[Tnkrnspkrsiqtukrn]a]]b_dXZg]c_^bgrmz{syzr{wxyvrvuabcjkdambhcfmfon}xwx}y~grlhtousnrqrvlov{roov}tswyzs}xx~yw{~VQVVSXYY^YXUS\W[GGLHEGKJLMJECGIGZ_\[[Z^\Ya^b_Z^\JPNRPQTMQKQMQQSNka`bihfbfhehgmnbT[VXXU\XVS[TTTWUmomhnkqiqusqvmjl]aa_\^eZadZ\b``fqpovqtzyv{w{xxru_fee_fmdfffkgbleyyy{~z||}~ghlqjjnssolnnjvm~vrqruqw||tzvzutq|y{|v}WYWTSZZ[\PYZ[UVVD?DEELKFBALKKPHO]`^_^ZY[_acY]_[hNONTJORRLPRKMRPM``fa`fdcgcfecehgW[QYZSVZZSU[Z\Z`ggnilntqkortokki[[abb^`d_]c^`__aownuwoxuws|r}tys_bfgfckhjmiljlifzw}}w}{xklgnnllkqommqnls~otvxvuqprt|zuywsv~yz|}}|~SYPT\\RVV[WXVZUSD@JGCLIKHJGGKEIBb_\``^X[b[`cd\Z^MTNJMPSJPPQSMNUWadhhgi`mfahhcmhmWZSSWTZ[T^WRZYSRnqpoqopnnmljlqsm]Yc^ZYaa]][d[`a`qvtorno|xuurpyuqg_cdh`kgdlkmhkge{~wv~miioqsrhnokishlt~syutqwtt|yu{s|zrw{z~~|yyzxzyNSSSTVUSPT\TSWYU?FECEBEHDDECCDFAV\]\Y``Z[a``]`[^KPMLUPNTRKTNJIQKiehfieaegegeljkgO[SXXRPZUUTPYT[TggfojngshijtkmsmW\\Y[YX^[Y[^]c^\nssnmtxvopozruvq_febiedfhghbkmhc}yuz|vywzxxnjnpmkmkmgtomtot}otrruurrsrvowvuw~|uz~y}{z{xvzYTVVTRXTRO\S[Z]^GJKBCJLMCCFGGLJJXb``^_[Zd`a`Zfa`LMIRUMJROPLKTQSMbh``f_cebgbkfkmlYPPURWZXUUTSY\YUljihrommirmrsrjj\Z]XZ`^c__bZ`d\]qsvxuzwszptru}pve_^f__gjk`alggecuuzuzw{zyyjohqmohiskjssmsv~z~vwsr{y|zopyyyvzy|wy}zz|{zXTSRPWRX[YXSSQX]GGABKJJJPKHILIDF]XZ\WZ[Y]`_dabYbIRKKRKQJSSRVUIVVg`i_g^feecbiehlbUUOTS[]WVUW[R\][ljflfpqphqooknmrVVbY\`ac`a_ebb]Zltuqllurxqtpso{yc`e`kgbdediaadfdu}xy{}}yzz|ynmophtokjkhhtmkj}|nprpwrxq|xsuyvww~|||z~{}{}~{YXURPNTXXXPZ[RPYKCJEIGKIDHMHKMOH\`W`Z^`\Y]Xbcb__ORRJRNUQJPVUQQXSdhgi_fg`_gecjggaRPSWVXTXSRW\Y[SYmmkigpgjifplijojbbWd`aaa`a\[^]edvnvuuupqvuysq{y{jfkcg`ijfegchhfjwy~}}}{zx~w{zqsrpifppnihlnqnn~xvsnopwvtvpsyyuquz{zx~{{{~|ONOQSRSTYVRWRVXZBJHIBGNMCDDGGGLMYXa^XWYbVZW`_]_^KMMJIJSTPPUJLOSW]gd^bbcikhj`b_gdV[TQ\YYYXYT[]]YXikciifeomkjjlhlm_a[aZYY_cdacd`bdwtqrnssylnvnqsw{ea`hehcfdbejhgcnxtx{~wt{yxylrptnikikwiloqks}|qtrtuyuyxtnvsysr{~y~zz{|~}x{||SPSSUXZQWSX\SVQZHJIFCGMBEIKBDKDEY__Y\WW\`_Z`]X[_MKIPJNVMRMLPLUWRc^^decgcdi_`hcheVQTSRQ\RQT[Z\WU^nemldmhkqhnjohos\ad_Y`ebZ_hc\[]]qkpvmpnuporzwnnxcfh`ieijgekbgjln}t{tv~wxtvzw}|xonqrjkhsqrspoolm{}~rvpsqyoyxw}rx}ruu~~w}|~{{||URYUUUWNQSXSXWQPHNGEGMJDINFPEEGIUZY^`^W`Z]``]`VYKRNNLKNRRKSQOVOW`h``ag^b`e_aib`gW\\WSS[WT\W[WQS]ihhgkjokfkfkfmil]dY`Waa]dba\]]`_vnutssmvtqpsqtow`lgjkejgdckddhnmtrz}xzx}xuvroilpnsjmooqrvrm~zz|~~~ytpstvq{{txtvztty~|~~y~|}}OOXQOPVRRVSXVXTYEADLEDMCIJOLCMMQW[YaZ_ZUX[a^_^\[MMMTSPRMSVPPTMLSe\gae]fg`cbchfb`P\]ZYUSY[XUW]V[Vggmkeljnpenfphpq[\b\[e_a^_b]cdc]rsmuqrltpqtuwvrwfcicaiaeideffefjuy~vx~}wt~v}y}ksmhgtpokqnonlwp~~~zt{{zuvtqq~{rw{wxz~|wzzy}{~RSQMPRSWYNXQPRP[DBGJLFGOJCJMNKIO^[\T^[]TaY`YXZX[PQQPUVSRQWLNOSUM\fb\]__heb]dg^`iSYRUWXY[ZUV[]_^WbdjgeddfhfgnkhiodZ\abcZ][daf`gfatsrrpsvkusruwowvdhieifiicjmcdjijytxvzs|zz{yu|~|ipnjqpiqqqsuwnmp}y|~~|ytvutsuuw~x}zuts{~}~~}}~KSONTVOSPNQPYZXPIFNCGEPDMOHPCLEM_VXT^TZZY_Z\\]V[SPLMJROVVMNPLTXNc]^\ecaZa_\da_dgU]XZWTUX[X^_XV\_kkmknedhloiehnmnd[deZZZde^b]]eabsnpmmlouosuxttnojdehcefadmiecide|rtuxzxwsu~{}u}jmkljouknulmqompy{|zurqtyyuusxzyu{|{{|z|~|RTNOWQSTOTTROVTNKLIDEEIEKDOMOELEZYZVVYRZUYU[W^ZVVPTLOPRVSSLNVNWMd^eg\\^b__g\e_`a\]Z\WY[ZY[TX^^]Vldfikkmkijejjdif`a[bbY_heb^`g_\etkkortoollmpsrpvamkefnjcojcbdjff{zz|syzruvu~|~ytjkknmtlkkrppsqpo|~zz~|}r}vtvzttsvwusq{wz||~|{}}JEA@DIBDBLEIAJFIVQTX\PYVTVUWV[[YNOJKHLQJMKIQIPLK\[VZ\W`b_\Y`aYZ`XPNPSXRQZYSU]R]]ieeg`gjdbbjaidje\``Yaaaca][c]a_agmpnlmhrkskgiqmle]^dgcefifedjclftysppowrwxotztzqemqkgjgrpgelroon}~}{u}||}|}}y~upmymmxuympppzuo~xywvz~z||?BH@?HJFDJFGBJGGROUSUOYXUQTVYOXXPRQOPNPOSMJONVTT]\Z\\`X\]^\aY^_`NUVQ\YVWSQURXVYT]ahadhghcajgbdkeX\U_]`b\Y[_]^Ybfcjnjfhnioirnislmg`_a^^iicgfekf`mtoqoustnoquturzvognpjhlnlmigkmint}t{v}w~qlyqyxzrrqqrovtr|||zwvxyyx~|z~|}BDKDFJJCDIKIFGBERQYSUQZZZTWTRXUWNLKIOMQRRPQLMMLU]][\]X[[[Z[bX`XbWPTPONXQYRZSRVQS`cd__dg]^fhgdefbXX`X\XWa`YcdbZZ_iihgecgihfkligtlbh_^fgahahdcnafhrxqkmnpvxqrosqpokjojmgrloiohntlpz{x~v}~|{vwv|ysussorpnqwrvrvxt}}z{~x}v|~{z{}DAECCGHDBEBGIHEKRPQPQVPPQQ[XVUTUPQPOIJPRTQUMMLRVY]\\YZXYa[^]\`\]SSUX[ZVS]TWXZYSX`ebe_digggfa_`k__WVW^]b`bbcddZY`kijefgdngnhoojor_ihjjgafgfcedchlrklnsrnxxvqyxpsoikqmqqfjkmrmpprmz|uxys{{zvvyvpr{w{xtwxyyswxu}{~{~y|x~}{~w~D@BC?FIIFJCKJGEKMNMUVPWWXTWTTPSUQQKQHNSQOJPPUKMQVYVXT_]Y_XbY`[XZXZQQPZQQ\SQUUUV[[^\chb^dea`i`hf^YY]\Z_Zac]_bdaa^ccjcfokjplkoojljebafbgggldhefgihjtqpkmlrnxxootrrmrrjntomspjikpjnut{vvuz~}wy{z~wrywspspxsqxxqy{tz~xww{|}yyAFKNGJEFKKMLBJLOOSYOTNNTZSUNXV[NRNNMKOROMOONQOVTUX\_]RY[aZ]X`^^VYSYXZVQZSQX_[]\Z\bb_gbh\]c_i^bbib^W^b`dZ[d`\[`agnhbflnfnpelffklk`djfjbfdhakighfcnsosostpjvpuuvnqnkorhopnskqllpmo~}vt}|}uvzu~ssqrzow{ptywuvspy{z{xw|zx|yy|JKDDDKFDJCNMDLDINMOQMRVWSOVLXQNQNKPJSRQNNUMOPLMPWTXUTY_`X[a[WWWcUYYRSYRZXSZZSTVUaYaa^cbcabc^__`^X]\b`adccbcf[fc[kjfhdknpbkohkplri`kdckffjcmmkbhasrouqrtqvnowtunsqphromolmpihnrmqr}r|zy~xx|xu~xsstr{wtsxzr{stst{|}x|~yz}{|xyDEFM@DFDIAEGMDIMQTSROOOTTPSQVTRTQRVLNORONPMRWQMMS_T_US\\WYa[[^]ZVRR[[[VXTYSW\Z[Yecf`]g_]g]i]ghh`Z`\YZ`c_^_\^[`\]gbdflkilnnhikmljbgikf`gkidjjkglcnlpupnkkvqoupqqwmfsgorpkskomoppt|v{y|qr}|zw~}zuutzyyzu{spy}vsu{y{||~vv{|y{x|{DBNCJBFFKLKCEKEJNSPTURSXWSPVUUOQLPRNOQSPOKOOQQRSY\YSVS`[VU`Y\`WaVTWU[UUWY[^Y`XYU^`a]^b`dff`[]ghjZ\cd\d`ZZa^f[^\[jckejmkgolenmjnjgghbaifamjmjgghhvnqsoutmqssuoovnrkrpijnrokjmqmnqzvurzwzvw|z|y}}}tuqw{wt{xs{y}vx{z|}|{~|}x|yv{z|}~~{HEINB?EHJKJJOFPLKVOJTQOQVXPQMVVRJOQMMOOLRMOPLQWVS[ZW[Y]ZYV\Z]V\_VVZZZXQWWZZ[RT\[]`Y\ba``b\`^beddYba`a[ea[f[^e^eiihkigcjggjkkelhidhbadllkjdedgfjgnlopopmslomtrlltiqroqltppontmqlp{sup}{{{v}}szvzy{r|qrrqw{q}xv~tu{~~}}~wxyx~~~{|EFHFJCCGFJLNILHMULQVQTTMLSLOQUMOQRPLPRQNVPOWQQSXWYTOVXVTUTX^__VXSQQVX^TZUWXVXTY_d\]^bg]^aa\^`_`_]_`Z`acb[b_^^_\cilgl`likeilmojjmgglfciimcfmncnlkkmoospqstrkqrrwtgjnjooojrtplnquuxuzzutrxqz|}wt|vxspzu{vw}zv{~ztvyvy|{{~||~}xy{~}IHEEMJHNLKEJKLKIMLQTPMSONWTTQLWQJNRNTVSUTPPSLPUTWTU]VXWT\ZZa\\UZWY[VZRX\\R]WT_`^_Z\\abe_cf\_g^]fYZ^a]^dc[a[g]dbe`fiefikddedkkikogdbdgckglhghihffqlhshrnqsvpksspunrthkppktsussrosstrrt|wx{{xzxr||zxqxxr}vuv}{v|}{}||~{z~{zyz~{|{CHIFOKGNJOLKKNQNPNMRUJSSSTPRLOPPLPUMPRPVNOUWSUWNVS]WUX]TZUR]Y^X]TX[P\TSW][[[^WZXbabad\eZd`]hb]g_d_dbZaZ]_]d^_a\caddg`cl`ggcnjgholaffdfkgkkhfoqdlkkqtmkskisqqkqktssqspiqjpluuwvnvtoyswv{q||{zszu~{x}xr{uvr|vxy|zy{y}yz}}{|}zzz~|{~|z}ILGNEIMFNOLNNHJIPSPMKSNLNNTSRUVVRPLJQQTSMNRSROQQ[VPWZVSQUZV]VUWT]UTWZUVWYV^Y[Y\\`]\_bZZceZ^aae^ZaaZ^\^[e`_^c^acibekf`dhfcfbgfgjkjmfglfjdgldopjdjhgnnkhnjmjuonkuspjrulqmotltvpqstzxqnqwxswtr}yszqyusysuyrz{v}txz~~}x|zz|~~~|~x~x~~FJJINJEKFFFLPGIHPRMIMMPOVOVPRTOMLRTRTPVMLPTTSRYTPZSTPUYZT[S\ZST]\SUV^ZYYU`VU^[X]^ZW\^b^[^e]e\[\c]a`a]\b]afcca_d`eac_`d`k`jkhfhkfdkfmibfdfodemfpgkjqnolspqrskmnpqssrkpluumtrmnvmoronqqytwtyywsyvvsutsxqw|wutvuuv|xwyyyx}{}{z}{{x~}z|GELKJOONJILHMMQKHLJIQLPQTLOKLTSQRTTPVQUMMORWQRWSQ[VYYXPVX^V[[UZ[WUZ\][UVT_WT^[X\^]^`]W`a`Zd]bb^d]_[^\afcf]afh`a^dhgc_fjdkheeaccdbbkdjmheigfhldmjmlqnjhmkknsnrolplsursntvmtmovmrqxtruqvpxtvrtt{zwvuss}x||yutxzz~w|zx~{x{y}}{}~y~~} \ No newline at end of file diff --git a/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-y.bin b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-y.bin new file mode 100644 index 00000000..53a13e47 --- /dev/null +++ b/clients/apple/Tests/PunktfunkKitTests/PyroWaveFixtures/ref-dense444-y.bin @@ -0,0 +1 @@ +0030.171114423759658:99::9<6<577!!! ?>?@:A>>@CA>A>CA$" ""$ # #%&"&!#ACCABAGB?CABHCIB(*)%+'&,-)+(,-'*GDGFHFLJJGMMIIII021+,,-/0+-,,30-ONLLRRQQORPLOTSR1417076383779342PQSSVSUVXTWWSTVS<<:<<=9<:8>9>88<]UXUZ^\\[YZZZXYX@@=@?><@@BC@A??>12551/3362034231666<<<99;868@?=?A?==?@D=?@"$!$!$!#%&&%$&"$FDBAECHBBBFBBFHF,-+&)&'*+*.+,)*(KHMELHFIMNIKIKNJ./2.0,.1-20.0-21MMNNQOMQLPQONOON0350264169335926VWWWXUUSSSUTUTWU:<=:;;9<8=98=<::ZW\XZ\]\\YZ[\^_\AC@>BB@>?A@B@5448214588662593687=8<:9><7>=9;?  !""!$ !@;==B=@BA<=?A=@B#&!%"&&"&'$$&&$"ABFEHFIEGIDFHEII-+((*+/,+++,*.'+HGJJGKHILGKONMOK2.304-1-1/-5../5MPTSOPQQUSRTQTVS4598489573:596::SWYXWXWWYYWU[V[Y8;=8>;:@=?><;@=;\Y\\_]]\]\_a_`]`>BAAC@BCAA?DBBEC2366368756586674! 8;9;8;8:==<=>;<; !# " "# $!>>BE?ACCAACA@?FE)%*'"#$+&+*)))()GFEDFCFDGEHJLJHG,*)+***).-/*.*1.IJNIONJMPOMMLMPR1416244340350245TTSROPRTRVPSVSWW88887:<:4=<7:77:UWXXYYVWWYVX\YZ[;;9=::;;=><>;:=:?>A:!#$%!$ "'&%#!&&D?>>DF?@BEFCABGA%'()*(*,++&'))(+EHGGHFKJHKHHGJJI.,,/+,,+1/22/-22OPNNNPMLJMKMPOOL3//27/1225113668QRQRRRQUPRTRUXQT5779767=89;699:;TUVXZ[Y[Y]ZZWXX\<@A><;=>A@D\\[b[^\^]`aaa]_cFDCDADCAEAEDDDCG943::4;:;:878;:7! >;9@:A9>>=@?@><>$##!$$'#'&$#!"#"@B?BGCFCDDEGGFBC+(%'*&%()+$((())MEGGIFJKJHLJLKKI-,+**1-1+100.31/LJQMPKOQPRQRQQQN3564/20445364649SUUURPVVVTVUXWYX8;<=:;=6=:89;><:W[X[[[\[X\^_[[Z\;?AAAAD>CA?>CDCC__`a^^_ac\ccc``bEACDDGHCBBHFFDEF:755;9:7;;<89>98! "!>=?==<?A@A==@@AD@ACF@``c`a_c``cec`_beHBDCEBEHJGEDEDJI69;989;<>=;=>?<> !" ! !!"# A;A>>@EA??B@@BDA(()"()(%)%&#%%)(HGEAIGIIDIICFHFF-,+,.)*./0).***-KJNLNJJJLNOKPMIM30.3320/3//5122.RPSONPMPQTNRRSTU863:585744;98974VZWUXTZWWWZZZ[YV;<<@><>;??@]`]W][^^^]`Y^^^_@C?BB?BDDEGBDBD@`ab`c^_becbbbbddFEHGJGKHHGHJGHKI:>>;8=;=;;9<9:?:#! # """&" @A=>CDC@ABB=?ACG('#'#&(%$&+%%+(&CJFGFFJFFKIKJJIH.*),0-.0+0.0---,MJOJLLPNQMLOLQPQ3/2021/311520566OSOQQTURRQTTRTVW:6524:7;59567::;[TU\YWX\\TZY[Z]X@=9@?=;A=?:=?==A]]_^[\\^^^`b`a_aF?BDB@FEAADBGBBCcedcfb`deehfccdbEFHFHFJHJHGKHIHG=;7:>=@>;>>>A>=? #%#$#$!% !!>@?C=@@BCCBBFBGE%&'&'&+')&'(*++%CFEGIJHHFJKKLLLE++-.+++/..-/00,,LNKPLNKMRMNQLMMM1.56200076511056VUNRVTPRVTSTWWWX;5<85498;96<7=8:;>>>BB>=;:@@==AA;A?=<$##"%"$#!&'!%%(??DDEEBECFGGCEAD*)+%+%*','&,-)+)LKLGELIMJIMJKLNH.+*-,1/00/1--/-/RPOQQLRPQNSLTOTO6042836537868393RVTQSVWVTTSRUVUW8:7:9:7>>:?>?;8@XW^^W\Z\]Z[]_Y][;??CBC?@?a_`_a`]_``c]_aa_@FIBCEEDHGFEEJGJccaacdeddgeccegfMMLHJIKKJLIKMMLL@A>;BA??BC?@CCB@$#'#%$% $#&&'"("CACDECEEDCEIGIBE)-&)(')*-.(-*+-,FMIJMHKMHMKJJIHJ21-..../.-0001.4PNSMRMRPPOSOTSMS6463393444245549\Y\\Y^[Z^[[[][_]>>=B>?>@CA@BBBCD_aac`aaa_abbcdecDGGEHFCIDEGEFGFHeffehghefihhhihgKLNMONLLMNKMLMNM?B@C>@B@@B@E@A??%"%&)&'(()*&*(%+GDBCFCIFFEGLKDIG'(-*..*+(0*0//*/HJNKIHIOPKMLKLKM030.1/342/244652RSNOONTSSRUOQOQQ6767:79647994844YWZYUVZXXXUX[]UZ<=<=@;:=@=A@AB??[ZZZY\Y`[]]\[\^]D@B??DEDAEAFECAG_fbe_bec`dbfbaaaCFGEGDFHIIIHGGJKjjjgighfijihgiilOMNKMNNLMNNRNOPO=?B@DE@@BEAFDCCD&)+($,+$$'%('),(DHJIFFHJGKJGMHLL*(0-+-///*+/-/-1PNNNMLLNMPKMOLMP/1.201431/253351PPRTTUUWURURSWSW49:;;6=985898;9?YYY[WWY^VXXYWW\^>=;=@<:A=@@^^][a_]\abbabaaaGDGEFGAFDEHGCHFCgghaddgebgggebcdHJIIKGKOINHMGIHMfijlimlkjkniikljMLOPSLNOQMQORNPQAEAF@CBIHGB@AGIA*,(,+&-('')(),)-GMKJJLLMFJIKIIJM0/-/-./.+3,0/-2/NPLNQPSPQNLPSTTR2522655546652288SRWQVSSTVRUVVUWZ;6<:<9=;;<9<;>:;Y\]XX]X[]^\\X[]_A@@>@AB@=>CDDC>Bb`d``a]c`ccaacc_BFCFHAEHJFDFDDDFbhgeddhecgjhghgcMILGOJMHIKLOPONPhiikimjonjhoinkiSOMNRORRRQTRSQRS)&*%&$($*(''*,+-KDFEDHLEFKJKJJIL)-.-/.1/.+-0/,1/PMNPNNLOLPNONKPP5702614176443462TPTUSPQVVVRTVVSV66;:97<688<<7<89UWYVWZWVXVW[W\Z^?<>>?><=9<\]\ZY[[]W^Z[[[Z^>=>A<>BB=<>@D>CF\^`bdb^aecc^^`e^FFFFEFDFECJFHFDHdheiighfgffieiilNHLIJKOLMOPOMNKMnknniijlnlplnnpoPRSPNRPOQPSNTNQStqnospqoppqrsqqq)*(.'((-'.--**-+JIGKLNLLKKKLIKON1004,,-00/-/04/1OLOLLPRPMSRPSUTN6583435476674:89UXTVQXRRYXTU[SWV=:9=9;>>:99;<>?>XXW[[Y[[^X_\`]_`B?C>D>>A=?D@?C@Aa^]b`b`^b^dafcbeDGGHGCEFDIKDIIDKbiadhejhjiggfkhiIMNKPLJKOMHKJOKOlkmnimljjqkjkkpmTTONPUPSQQRUTTRTsststrtooqstssrq&)+)))(,)*,,*/--LNMLJIHKKJMNLNPO0.0/40.0101235/1PNNTNPOQPTORPUUU6497876689888<83XVVXZTVYWVVUUVVY==;=8@9EEBC??BCEFCEECG^`bd`ae`dbg`cbcdIDJEJJEGIELJJIGLdefiighfijjgglnkKJLJKKLKPPQPQQORpopkqlpkmrpootnrNRQPRPUWVPSRPUWTtrqrurtsssqtuvuu**+,**)++0)-1,+*JLMLKJKNLLQOMPQR302312443/125612PRTWUTOTSUPPVUQW74:98;9:9;768=<=ZUYVYYZU[YZ]]U]\9===<;@;??@@A@@@^]]_][^^[Z\]^_\^A?CB@BEA@DAAHGHE`acfd`faffhcfhhbFIKJIFHJMIKGNIKFlekjfhjfgijimlnhQMMNLNRNPNRNTMMNlmokmsspoppnlnnrSTSSWWWWTXVVTWSXptvusqtvtvsusuuu'*...+.-.*-040/2MPNMNNMKQPOKLOPL3124634232248424UQRTWQQUUSTWRTST6<9:9996:<9;=::9V[ZWXXZY]^^W\^VZ>?>>@<;A:]^b]b_ca^^a`a^cbDGDB@GDDGCDFCIHBhfh`hehfghgcdhifIJJIKLNMLOHKOLKIkfmijokkmmjlijloQOMPOMQOSNPTQPSNsqnlmmqtssorqtuoSUUWXZRRUYWUTYWVxwsstuvuruwusuvx,++,1+1021111//,NPLLPQLOMQPMQRQR/367847587338375UPSQSVSWXSXVUVUR899<=99=788\W\XZZY\__Y\]]YY=@B>>@AA@>AD=b`ca_c_acbdaced`AAIGGGGCBDFEHDDCgdgiefdecijheiigHMNIIIMMOJNPNMKLgioiojokilnjnookSQMQTOSPSPTRVTRToqmrptntpuqsoqttWXTWWWVUZZUUY[UXxxuvvvsswtwsuvvy0-+-/3/13111/-13RMQOPPNONOOMPUMT11668635333:87:5XSQUVWTVRSVXZTYT:==9:::<::<==;?>9:>;@?@@A?@><<==?@?AA><=\_^_^a__]^]b\_]_ABABABFBBCGFCEEEddd`ababfhegdgceHJEFIFFKHLMKNINIgjjkjkmjhjmljlikOQOQKSRMOLLNMQPSnnlmmnpqrprqorqpRRUTXYTTSWURVTWWuqqwrqyvtyxsytttW[Y^ZYWX][[XZZ^]wxyyzzxz}|~}z{}{1355466777588663QTPRUWSRRRSYUTWS:::;87877<;?=;9;?BC=BCDBAC]a_a^b_aabd_be`aFGFDFBGEEGFFGIFDegegcdibdidfghfhJLIHMGNKNNJNMLIMigijnmlknkmjhplnPNMNSPNTNRSNUQOPmmpopottspststssWSRUVZZXWWRUTSUYxsxxvtuvzvtuvuz|[_X^Z[Y_[[`[_\^`xyxz|~}|~z|{||6518477785565554UTSTRTTXXWVVXUVZ;98;=7>=<=8;:=>@\YWY^YX[]\]Z\^^]@@CCA<@A@>?AB?>@^`aa]b^babda`baaDFEDBIECCGGGKBGEfgfdchjdgcfgggggJIOMKLOILNNLJJPJkhmikkmjjjppkmjmNPQQPQVNURTUPUSUnnpqosooouoqqvqs[WVWTVXUTWZXWVVXuuwywvtzuvtw{x{|_YZ]Y[Z\^Z^^\`\_{~~~~}||{}}}4743489653765976XRSVSUYZVUUS[X\V=>>=??;=9=9??>:;[\]Z`\YX^[]Z]`]bB@>=B?DC@CBB@@CCb^a`_e_db_`bbabbFGFHEDIHIKHFIKIJhffdekhfkhdeegmkJMLOKPJQOPQPRNQPljpmomnpqlkqppppOPPTTRSOQVRTRWPUoqpsqptsutttvrutZVTWWXY[WVXW[ZYXuwuwyyxyx}{yxx~x[Z]``^[ZbZ]b``^^|}~~}}|~~4:3363:;68;969;=<8=::;<><@@AC?__]`]a`^]Z][_]\dDDA@D=@CEEDAGBECd`cdg_dd`hcc`chgEEGJHGHFIIJJGJIFigkjkffkfjlgnlolPIQOMQLPORORLLLQolkmnmmkkmnqqqsuPPQRQPQTTXWQUTTTvruuqrsxquwvwryx[\WVWZ\VX[]WWY^`uzyz}}}x}|}~|y|Za^]__bbbabbaccb~~}~<644;:;666676>8<[\XZZXZZ[]Y]Z^WY==?;B;=<>??@@>CB^`_\\ccb^\`^b_a`AFFCFBGDCCEHBHCEfdeccbhecafffeceLIIMLKHHMGHNMOJHlhigfihmgkhhkjinLPLTLRQQOTPONSNOopnpmmnnqsmouqntWTSRTRSQWVTZRZXTtxrttsstuvuw{t{tX]ZY][X^Z_]Y[ZaZv}~z{yy{}x{{~{]``]c^_b_cc^`bab:86;6:787<>8;6;<@B=@BC?AAD=?]a`d\a`_a`^\`abcBCAGECBHBDFJBDBKbfecbidbbfaeidfeEGMGKGHIMIIKOMMQlfljllnijnjmljloPPRLQSRQROPNOONRnsstsonounqnpponTUTXVRYUTVXWYT[Wrvwtrvyzyzwuwvwx[YYX][]Y`^Y`Z^Zb{}~xz{||}||~|c^c]c_^dbfb^`aba~VUSXYTUYZYY\[[YW9=<?BZ^Z^`]^a`b[`ab\cCDCCDEC?EGEDDFGI_efbedaacdcgabafGJFGJIHHMFHJKNNHkiekigmkiloimjhlNPNMOKNLMPNLRSOQnnqsromlprqmqpptRUVTUTTSSXRSTUWUpwwtwwvvvwtvsssvXXZWWZ\ZX]]XXYYZyw}yyw{ywy{}}|{}a^c^\^]_`^`^dc^^~}|~}~~ffgcfffdfhcghfgd\U]VYXVZW]WZ[\\]BB<<@@=>BA@>B@C@`b]abbbbaa^abaabCEAHACDBCFHIFGFE`ibeeggcgcffegkcJIJLMMMHFFJKLKJPjkjjignlkllimlmnNOPONULOQRONSTSQlplrnpsqqpqquqptWVSTUSTVY[WXUWXXvvrzsuwvyxww{wxxZ]Z_\_[[Z]\\]\Y_~zyz}}|{}~}z|}~_`c_`a`acceba``e~ghgfhgeghgeffgigXXXZY[W^\YZYZ]\Z@B?>ACC=CCDAEDDE`b]_^__`abcee_f`CGBDGDDIJFDHDEDCeifieehhcgedhfhkLGMMHHMNNNNMOLMNkmnlnlpmmmmkjlolMOTRMQRTQQRRUPOStqopuqpstsurpqqwSRUUSUWWWVUZWZZYxvvw|wwuzz|uyw|w]`ZY[_[]^_^^_``bw}}~}|z|~|}||cfb_bdeffb`dcbfb~ghfhjjghffjjhjii_]^]\^][]`Z`^^\Z=>?@CDA?CDECDDDDbcbbcba]eef_`dabEHEGIIGDDDFHKJHFhidjkjggeeeehgijJLJJNMNMMNPNKLMPkiiimpnmnlnnomkoQSPPNRRSTSSSSRUUvrpursqutqttttqvX[UX[U[X[Y[ZWY[]tu{zzx|y{zu|vvwZ^^Za[][^``b]\^b{{}~~~}zz~a_daefbgadefdcgeeedfeggigikhjkijXY^_`^[_\[`]`ab`>=?@@C?DECBBDDCCceea`a`c`ccgbcadCFHILKHEKKJFKJIFekiiijifefmlffikKIQINNOKKLPOJNLQnlmklrjnrlklrpmrOTPVWTQQUTUVTTVStqssvwqvsruwxwrqXV\XX\\WZVYX[Z\Y|wyz{y|wx~y|~}|}_\_`]^b\]_b^d^]b~}}~|}fdahdhfggfbbeceghffjijijhmmghjll[^^`]]\_\b^`]]]aCB?DC@FDCBCDCHHDaecgffefghdecceiKDLLFGKJKLLIJMNKekjlkififoklhlijMPJPJLNQQONOSLPOnmrrqjpomqqmoorsPVWWVUUUVTTYYZUUvrttvwqttvvuswwvVUVY\]\]][\Z\[Y[zz{{{|}|{}||}\db^`_b^^^b`e`bd}}}~~gggigfgjbefeefielknlkjkjklijkkll]b]a^`^a`]^d`__cACAGADHDFDAEFJIGei`igfiddfdfhfjjIILINJNPLLNMIKLPjojjhhoiliokiiolPNRRNPQTMPSORTRPrmnqpnqrotopvqqqRTXWYWXXVUTWV[UXwtvrswuvzzvyxywyX\Z][YZ_[\[\_`Z`z~|{{~zx|z}}~d]^cb^`ab_a`d_`aeegeeieeghijjfeglkmjlkojljjomklkbd^`]^a`_`^bc`dbHHFFEDFFKDJGDKEDdjgefkfhcfkhhhleOLKJMKINMLJLLLMOjilkkooiqponlmlsTMUVSQOTQQRTUWSUtqmssnqqrtussxqsTUTWXUUWXXWVX[Z[uwwwv|x{xx{|x{xyYa_Z_\`^^]]_aa][}}~~~}}|~{|a`ab`d`ef`cgbcdaieifeimkhighhiggmkllompnqnnrpoqp`__`c`b`adabfaffCEGDEEFGILGILKJGfgdgdejfidgfhhfmIMOMILILKOPPOOQQimllpknllonnmqomPQPRSROUTSRVQUPTotrvtopptpvuwtuvWZW[WUXYZ[ZVZY[\v|xxt{yy|v{{z~{zZ_Z^_\`\ca_ccb`c||}|~edbcebahhaeechgblekkjilkilgfhmkhqljloonoqpqpmmpngdffabefdb``ehdcEGKCIKFKIHHIKIGIijeefgkhijlfjjlfMNPLJLNQLTPPNONNqlnpllqmjsonkrltPSSTTSRTRTVWSVQXwupswvruwrwusrxuYYX[VVYXYXZZ_\][u|xyxyw{~y||{}~}\^\^abaada``aabc~~~~}gbgdbehifjibicgfghhmghkjkmijikmoonnrqronnqrpnopue`gahifiechdfdedIGGHIHNINHMKLKJJflikliiiilhnhhiiNMRPTMOSROQOMQNUopnmqoqnpnpqurtpVVVUUTVURUWWZYTTtuxuwxrwyuwyzswt]ZY^\[ZY\YZ[^Z\\{~xy{yx|zz}|}~^`a_ac^d`^a`ebbc~bfhfcehfdhjkefjgkhmnonjhklkooklltrpspurqqtqpqttsebfbdefhefifhgffIIKMLJKMNMJKMIJPnmhklkinpniomommMMOQRNNUPPOPRRPSooqpposuomoossssUTUVSYSUXR\ZWWXWrswtuytyzvxywwxu\\\\[]]]][]]`^^\y||z|zz~}{{yaaba_acbecfb`ecchdjiiljjihijkmhgjkkllnmlnololnoootrrottsssrtwutsddegfdceggefkjhhGILOIJJOLJPMNJLQljlnklioojkmkonsSSQTOPPNTVQQUSRRonqpnntruppquvrvVUWZUUZYXWW[YVYYtrtvy{uvxv{{y|y{^^ZZ^a__\^`^_`__}~{}}~{~{{~`aaeb__fcaed`hbbfkkhfkihihmkmklekoorprpnnpolstloptopsuursrtuurvwffdgjclhfhihfgmhPOKKOOOQQKJNMOPOlqjnopqkposqlqrqUQQQVUTSUSVQRUUSttqrtuvptqtvrtwqVWZ\YZ[[YY\\Y]W_yvtv|yu}yw|w{{{y``_[_`_a\]__^a^_~{{|}~}}acaeeggfbbfhebjcikjhgngkfhjhnlhmlnpmopnnrroprooprstuwwuvxxuusxsteiijjggjlihmlhjhPRJNJNQNRRTNQRMOppllmnqqlnmnmrruVRWPVSSWYQXRRVVTsxrrtwvrysrxrswvTXYZXYWZWVXY^Y[Z{{zx|y|x}y}v}yy_]^\a^^b_aceae_b}~|~bhhdchhccdfjidggkliloihhlnimikilokorpttoqrptovvvvswtruwwuyuwxvuukgmmjnkjlnilknooRKNRSROQONOPRSPSqpqmsrqrrpttoopsWSVTXPZXZRSUXZRYwwztyyyvxwvxwyyvX[^^\\XZZZZW[Z`Z{}}|{y~{{|~|~|zybbbaa`ba`cdd`cafjfeccghgefhiiejlokhkjjmlkmnnnpmrnrnvrtrtppqqwtuwszxyyxtvxwvwyyxyPOKNKJRNQLSNLNPSpnllmnnlpqrtnsntWQVVPRSSSXUYURYWwtswvvxqtsruvuyt\YXZWXZ\Y]\WZXXZ|}{xw{|{y{zz~}|_^^ca\]^_cbc_ada}~|~}bbeceeddceggddddlglhigjhlnonininonpqqusprvtotnrprvywzvsyvzxuywxvNMNONPOMOQULROOPmkllrsrtnqprrpslSWSRPUWVSVTVVTUUwwutstwwxyz{yxzwW^W\X[XYZZ[Y]\ZY{vxwvz~~|~~{yxy}[]ac`bdaaa`^ae`c|}dbdddhkhfdehgfgkjjjklijkonlmppjlroopprppsrqsuvpsvxxvxutuuwwuyzyxSQROLSNOOTOOSQSTmmqlrstnpuspoupqWYUSTVVYXYYVY[UXxyusvwxztuyzz{|{YWY^Y^XZa]][\\[\|~w{zz}~|z}^_bb`a``fc`b_d`ehdfieghhjgfiifnhmoillkmlmnkkpnlptpprorqsuqryutwszzxwxx|wvwxwxzx{RQTTOSSRPNQRQQUUqpqsoqpqqttstrwtZXTXZ[[WYZ\UWVYY{uwwyxvwvyzz}w|~^\\]]Y^[Z[`]\aa^y~z~||}{}e_ebbebbeffdceefjhggggjjnmklfkhhnljlnmrlplqomosntpqsqsqvxurqvvwsu{{{|y{|}zz{z~||VROVRPTUUVSVQQTUtrrptuqutrsuuwvrXUUXVTVZXWXVZ][Xz}y{vwwvy|yy|y|^\][```cb`^]^bab}}}||}~acc`bfcdeiiefafdmnhfkikjmmiligpmnmsrsormstosqnoswqqvuwuxwytwvyzuz}yy|z|{z~{OOQQSQSTSRYVRYWUstvsrrsursrwyyyvZXX[[YYXVY^VZ[^[y{zvzxy~x|yzzyy}a_[c^]]]ba]`]aac|}~{~~hcaciidcfcfedfjihlfogjnmjjjlmjomppqrmnrppopqrrunvrssxvsssvvuvwvyx{~|~|y}y~}SSRWTTRTTYZTXXYXvvuttryyvysxzxyx]YXY[XZX[Z]]YZ`_yyx}|{}~}|y~~z}ab^__be^a`]fdccf}}cbeghjjdcffigkhgimilkkjlilmlokloottnrqpurpuqusuwuttzwwzy{{{uwwx}}|~z|z|{{|}|RTWWWWYXTTUZVYUYwwtwyvzt{wzwvx|xY[][[`Y_[_ZYZ[]]|z{{yy}||{}~acc_ddaadcaegcdf~fgfgejfkfkfjiifimppqnqnknpporqqquosqrsrvsruwqvswvx{{wuzw|yxy~zz{~~||}YXZXYUYYYXYXV]Z]wyxz{wvy{{yz~|yx[[Y[`[_\^[^]cb_]y}|{{}__bfadadabdadbhfijfiifijlimnnmmnpopmjrpjproqstqpwspsturtustuyrtyywwy|z~w}{~|}y~z~~~UVZXXUV[[YZXZZW[|}{y{yyyx~xxy{zyZ^`_`_b]_]b^^^b_}~~~}~aa`baedahcbchdffhefjnjgikijlnmlnnmmjlpnpskopnrqnrsyuquyxtyrsxtuv~v}{}y}~z~zy}|}~XXWXZXU\ZWZV]YZ[{}yvz|}yww~zx~~~^_]_`aa\b_cbb^dc~||~bedcbebefdfeggiflhnlmnlllkilmmljoqrntrqumqpusurvwsvuxwxxxxvtywuvx||}xzz|z}}}~~XZY\ZYZ]Y[^[Y[Z_~}{z|}}{y~~{cdc_dac`eddgaaec|~}~eecbfgiecdhgjilhkpnnmniojkkooqmrrqowoqvprpqqrrur{zyxuvuwyzv{uz{y{z|{~{~~~~~}~X\Y`^\^]Y[\^[Z]ax~y~~z~~~||}a`adebfa`de``bchghgeicehmjfflkmilmmmmpomllnolompuspwvqqvvsurxsvqvywzvzvxz{xxz}}z~|}}}[`^`]\[]_a[_b]b`{~||~}~~eaddcaafacaichgaghghlkmjjknfnkkikqpnoolllprpopmrtutussvvuuwyrtwu||y|}{x{{}||{{}~~~Zaba_a]^b^a`bc_b}}}~gagfeggcbbdfdiieiljjgkkihjnoloolssmlponrsupostrsqrqtxvtwwvss{{sz|zxz~y~|}~{~zb]a``^b`_\]b]_ca~}~~eicebjffekiifefffiljhmnnnmohplkqknsqqtsqosvttttrxywtux{yxzyuuy}x|{~z}z~~}}dbacafhbcbbghghdgghjnkjmlnolholomslpnnnnmrnosnrtwusswwvxvttwztuvwx|w|{~y~x}}}}~|~}~dibdcehdcffkegkigjjknhlmnlmpnjioqomspppmtrnnprrouvtsuzzvtyvtvtxt{|y}|~{|y}|}~||gbgfgefhehcjdehkiomihllnojjojpnloqopqsruprvurqutwswutz{vzzw{z{|w}}{|}||~{~|{gfjildljejgjjgflkmmnpqknmmnpomsorssswuwqtruxvrvwyu{tvyz||xxyyz{}~}}~dhkjlmjkimjgmlkmklmmmokqnpnoprrotuvxrwsurvryxryt|w|xvw||}|xxxz}~}~}}~jkghgifiglkhhloopoksmoosstqpurpovywtwttxtuswwyuwz~{{~~yz~{|}y||lnimilhnmojopolnrtprposqopntqtvnvstsvwsztuuvxxyv|{y{~zz{}{}mkinnnmnkolkjmnlpqrrpvoqrqstsvttuxt{{uu{wwzz|y{|}xy}}||~}milnpklmlqqoopqptqtttuqrtqwuvxsxtwuxy}yx}||yw{{{|~~~~nnrnmokprrrtsrsnpxprqwwuywyysxyywxy{yx|y{|}~{~~{}~~~~~qnpmqmssroqtpotpsuvyuxxszvttyuuvx{x~~~~{}z{z{~pmoruporuuvpurppxwwrxvysyuzx|wxwy||z{|{{~z}upquuuuusutssuquzwv|xu|v{|yx}wy{~}~{z|}}|}soptsursrvyxxwtrzww~}{}xz|xvzy}}~}~~uruvswswtvxxwuvw~|{~yyzw}zz}}~~tvvwruxuxvtuuyu|yy~xzyz{}{z}~wwsrpxrquxuxxwvtvxyx{|y{x~||}|}{}}}~~wvruywsvszysuvyvxyz{|{{y|z}{|~~~xyuuvs{usyx|wx{x~y||~}|{{}}uyyxtvz{xzv{{|zz}z|{~}{~vxz}v{v|x}z}{}z}~~}|{}|}þĿzvyw|z~x{w|{||}~ġ¿~z~z~|y|z|{~~{{}|z}|~~~¿šŀ~~{{~~}ĽĿ~}~}}¿Ƀ~~ƿƧ˄¾Ŀìʄ¿ħíς¿ĿŮ˄¾ƤͰυªϭΧǧɮѳĿƪ̯Ӵ¾ħˬԶ¿ɧϯӸž¾ŤȭҳӺľſ̫̱׺¥ͭѴ׺¾½ɦΫηּÿľ¿Ƭѳֵ׼¥˩ʹָٺ¤Ŀ©ƬϳӹݺáĨ˪ұټܾý¾±¾ĽƦϰձռõ¦ʬαжܻ¹¸ÿǩ̭Թ׻»½½ýÿ´¾áªȮαٶ޿ĽřžŨŮҲշ׽䛙ÿĨǭѲٺ߽û睚ëȮձշ¼ý矟ţŦѫгԾ߽ýĿĿ螡¾˨ʱԵؿſƿ睠¿Ư˰Ҵؿ½ꤡľĥƬβӶ׿쥢ΰѴѶټ¾ž먧ë˰η׾ܾĿ좢ǩͭζ駦ſ˳ӻӽ¼驨ȿª̳ѷ½맧ſǮʶι¿몫dzȲ͸ľ먭ƿƴǹӿ¾뭮ĿǾƺľ÷ѿԿĿؽ½˹˹¾տپÿÿ˻˽¿ܿõ˹ͽ¾žݿÿIJǹϽſĿݽľƴ̺ξǾþžĿöżĿľƾĹȻ½üſ½¿ùÿ¿Žĸ˿Ŀþ½¿ƿ¼ÿĿĿ¿¿Ŀýÿ \ No newline at end of file diff --git a/docs-site/content/docs/pyrowave.md b/docs-site/content/docs/pyrowave.md index 4299eff4..b1cadca9 100644 --- a/docs-site/content/docs/pyrowave.md +++ b/docs-site/content/docs/pyrowave.md @@ -36,11 +36,14 @@ Bandwidth. At the codec's ~1.6 bits-per-pixel operating point (4:2:0, 60 fps): | 2560×1440 @ 60 | ≈ 355 Mbps | | 3840×2160 @ 60 | ≈ 800 Mbps | -120 Hz doubles these. Gigabit Ethernet tops out around 940 Mbps of payload, so 4K60 wants -2.5GbE (or a lower rate). **Do not run this over Wi-Fi** — that's what HEVC/AV1 are for. +120 Hz doubles these, 4:4:4 multiplies them by ~1.6, and an HDR (10-bit) session adds ~15 %. +Gigabit Ethernet tops out around 940 Mbps of payload, so 4K60 wants 2.5GbE (or a lower +rate). **Do not run this over Wi-Fi** — that's what HEVC/AV1 are for. -Also: PyroWave is **8-bit SDR only**. An HDR session stays on HEVC/AV1 regardless of this -setting. +PyroWave follows the same 4:4:4 and HDR settings as HEVC/AV1: a session negotiates +full-chroma 4:4:4 when your client's 4:4:4 setting is on, and HDR (BT.2020 PQ, carried in +16-bit planes) when the host's display pipeline is HDR — currently the Windows host only +(the Linux host's capture path has no HDR source yet, so Linux-hosted sessions are SDR). ## Turning it on @@ -53,10 +56,10 @@ setting. gamepad console, or launch with `PUNKTFUNK_PREFER_PYROWAVE=1`. - Apple (Mac, Apple TV 4K, iPad — wired networking strongly recommended): set **Settings → Codec → PyroWave (wired LAN)**. The option appears only on devices whose - GPU passes the decode probe (Apple Silicon and A13-class or newer); picking it forces - the session SDR. + GPU passes the decode probe (Apple Silicon and A13-class or newer). The decoder follows + the stream: 4:2:0 or 4:4:4, SDR or HDR, per what the session negotiated. 3. Leave the bitrate on Automatic: a PyroWave session pins itself to the ~1.6 bpp rate for - your mode (≈200 Mbps at 1080p60). An explicit bitrate is honored if you set one, but the + your mode (≈200 Mbps at 1080p60; ~2.6 bpp for 4:4:4, +15 % for HDR). An explicit bitrate is honored if you set one, but the adaptive-bitrate controller stays off either way — this codec has no useful low-rate regime, so under sustained loss the right move is switching back to HEVC, not degrading. The pin follows the resolution: a mid-stream resize (e.g. Match window) re-pins the rate @@ -66,7 +69,6 @@ The stats overlay shows `pyrowave` as the decode path when the mode is active. ## Current limits -- Linux host + Linux client (including docked Deck) and Apple clients (native Metal decode - on Mac / Apple TV 4K / iPad) today; the Windows host is tracked on the - [roadmap](/docs/roadmap). -- 8-bit SDR, 4:2:0 only. +- Linux and Windows hosts; Linux clients (including docked Deck) and Apple clients (native + Metal decode on Mac / Apple TV 4K / iPad) today. +- HDR needs a Windows host (the Linux host's capture path has no HDR source yet).