SIMD

Μεταγραφή RNA
Μεταγραφή RNA στη διαδρομή Zig
const std = @import("std");
const mem = std.mem;

const block_len = std.simd.suggestVectorLength(u8) orelse 8;
const Block = @Vector(block_len, u8);

/// Transcribes a whole block of DNA nucleotides at once.
fn transcribeBlock(block: Block) Block {
    // Every base is replaced; the seed only survives for bytes that are not
    // a base, which valid DNA never contains.
    var rna: Block = block;
    rna = @select(u8, block == @as(Block, @splat('A')), @as(Block, @splat('U')), rna);
    rna = @select(u8, block == @as(Block, @splat('C')), @as(Block, @splat('G')), rna);
    rna = @select(u8, block == @as(Block, @splat('G')), @as(Block, @splat('C')), rna);
    rna = @select(u8, block == @as(Block, @splat('T')), @as(Block, @splat('A')), rna);
    return rna;
}

fn transcribe(nucleotide: u8) u8 {
    return switch (nucleotide) {
        'A' => 'U',
        'C' => 'G',
        'G' => 'C',
        'T' => 'A',
        else => unreachable,
    };
}

pub fn toRna(allocator: mem.Allocator, dna: []const u8) mem.Allocator.Error![]const u8 {
    const rna = try allocator.alloc(u8, dna.len);
    var i: usize = 0;
    while (i + block_len <= dna.len) : (i += block_len) {
        rna[i..][0..block_len].* = transcribeBlock(dna[i..][0..block_len].*);
    }
    // Transcribe the remaining nucleotides one at a time.
    for (dna[i..], rna[i..]) |nucleotide, *out| {
        out.* = transcribe(nucleotide);
    }
    return rna;
}

Zig's @Vector type provides portable SIMD: an operation on a vector is applied to all of its elements at once, compiling to the target's vector instructions where available. Rather than transcribe one nucleotide per iteration, this approach loads a whole block of them into a vector and transcribes the block in one pass, following the pattern in Everyone Should Know SIMD.

std.simd.suggestVectorLength picks a block length the target's vector registers can hold — for example 32 on x86-64 with AVX2 — and returns null on targets without SIMD, so a fallback of 8 is supplied.

transcribeBlock handles all four bases the same way: one @select per base, each comparing the whole block against that base and, in the lanes that match, replacing the running result with its complement ('A'→'U', 'C'→'G', 'G'→'C', 'T'→'A'). A lane matches at most one base, so the order of the selects does not matter. The seed value is the input block itself; for valid DNA every lane matches exactly one base and is overwritten, so the seed only shows through for bytes that are not a base.

The main loop copies block_len bytes out of dna, transcribes them, and stores the vector straight into the matching slice of rna — dna[i..][0..block_len].* dereferences a slice as a fixed-size array, which coerces to the vector, and the store on the left does the reverse.

Any nucleotides past the last full block are handled by a scalar switch, reusing the same mapping as the switch approach. Unlike the isogram-style problems, transcription has no notion of an "invalid" block, so the tail needs no special padding — just a short remainder loop.

The loop body is branch-free and consumes many nucleotides at once, so this is the fastest approach on long sequences. The trade-off is the most code and the need to reason about lane-wise selection and the scalar tail; for short input the simpler switch or lookup table approaches are preferable.

Translation missing: el.number.nth.ordinalized Sep 2026 · Σου φάνηκε χρήσιμη;