// codesnip-guard: levenshtein_distance #[cfg_attr(any(),rustfmt::skip)]pub use self::levenshtein_distance::{LevenshteinEdit,levenshtein_distance,levenshtein_distance_with_edits};#[cfg_attr(any(),rustfmt::skip)]mod levenshtein_distance{#[cfg(target_arch="x86_64")]use super::avx512_enabled;use std::{collections::HashMap,hash::Hash};#[doc=" Returns the edit distance with unit insertion, deletion, and substitution costs."]#[doc=" Uses O(m + n + n ceil(m / 64)) expected time and O(m + n) space, where m <= n."]pub fn levenshtein_distance(x:&[T],y:&[T])->usize where T:Eq+Hash{let(x,y)=if x.len()<=y.len(){(x,y)}else{(y,x)};if x.is_empty(){return y.len();}let prefix=levenshtein_common_prefix(x,y);let(x,y)=(&x[prefix..],&y[prefix..]);if x.is_empty(){return y.len();}let suffix=if x.last()==y.last(){let blocks=x.rchunks_exact(32).zip(y.rchunks_exact(32)).take_while(|(a,b)|a==b).count();let suffix=blocks*32;suffix+x[..x.len()-suffix].iter().rev().zip(y[..y.len()-suffix].iter().rev()).take_while(|(a,b)|a==b).count()}else{0};let(x,y)=(&x[..x.len()-suffix],&y[..y.len()-suffix]);if x.is_empty(){return y.len();}let work=y.len().saturating_mul(x.len().div_ceil(64));if work>=65_536&&let Some(distance)=levenshtein_wavefront(x,y,(work/64).clamp(2048,1<<22)){return distance;}levenshtein_distance_impl(x.iter(),y.iter(),|_|{})}#[derive(Debug,Clone,Copy,PartialEq,Eq)]pub enum LevenshteinEdit{Match,Substitute,Insert,Delete}#[doc=" Returns the unit-cost distance and one optimal edit sequence from `x` to `y`."]#[doc=" Edits run left to right: `Insert` consumes one element of `y`, `Delete` of `x`,"]#[doc=" and `Match`/`Substitute` of each."]pub fn levenshtein_distance_with_edits(x:&[T],y:&[T])->(usize,Vec)where T:Eq+Hash{let mut edits=Vec::with_capacity(x.len().max(y.len()));let distance=levenshtein_edits(x,y,false,&mut edits);(distance,edits)}fn levenshtein_common_prefix(x:&[T],y:&[T])->usize where T:Eq{if x.first()!=y.first(){return 0;}let blocks=x.as_chunks::<32>().0 .iter().zip(y.as_chunks().0).take_while(|(a,b)|a==b).count();let prefix=blocks*32;prefix+x[prefix..].iter().zip(&y[prefix..]).take_while(|(a,b)|a==b).count()}fn levenshtein_wavefront(x:&[T],y:&[T],mut budget:usize)->Optionwhere T:Eq{let limit=budget.isqrt().min(x.len().max(y.len()));if x.len().abs_diff(y.len())>limit{return None;}let first=levenshtein_common_prefix(x,y);if first==x.len()&&first==y.len(){return Some(0);}budget=budget.checked_sub(1+first/32)?;let base=limit+1;let mut previous=vec![-1isize;2*limit+3];let mut current=previous.clone();previous[base]=first as isize;for distance in 1..=limit{for diagonal in-(distance.min(y.len())as isize)..=distance.min(x.len())as isize{budget=budget.checked_sub(1)?;let index=(base as isize+diagonal)as usize;let delete=previous[index-1];let substitute=previous[index];let insert=previous[index+1];let mut position=-1;if delete>=0&&delete=0&&substitute=0&&insert-(diagonal+1)=0{let matched=levenshtein_common_prefix(&x[position as usize..],&y[(position-diagonal)as usize..]);position+=matched as isize;if position==x.len()as isize&&position-diagonal==y.len()as isize{return Some(distance);}budget=budget.checked_sub(1+matched/32)?;}current[index]=position;}std::mem::swap(&mut previous,&mut current);}None}fn levenshtein_edits(x:&[T],y:&[T],flipped:bool,edits:&mut Vec)->usize where T:Eq+Hash{if x.len()>y.len(){return levenshtein_edits(y,x,!flipped,edits);}if x.len()<=1{let insert=if flipped{LevenshteinEdit::Delete}else{LevenshteinEdit::Insert};if x.is_empty(){edits.extend(std::iter::repeat_n(insert,y.len()));return y.len();}let position=y.iter().position(|value|value==&x[0]);let index=position.unwrap_or(0);edits.extend(std::iter::repeat_n(insert,index));edits.push(if position.is_some(){LevenshteinEdit::Match}else{LevenshteinEdit::Substitute});edits.extend(std::iter::repeat_n(insert,y.len()-index-1));return y.len()-usize::from(position.is_some());}let middle=x.len()/2;let mut forward=Vec::with_capacity(y.len()+1);forward.push(middle);levenshtein_distance_impl(x[..middle].iter(),y.iter(),|score|forward.push(score));let mut split=y.len();let mut distance=forward[split]+x.len()-middle;let mut index=y.len();levenshtein_distance_impl(x[middle..].iter().rev(),y.iter().rev(),|score|{index-=1;let candidate=forward[index]+score;if candidate(x:I,y:I,row:F)->usize where I:ExactSizeIterator,I::Item:Eq+Hash,F:FnMut(usize){#[cfg(target_arch="x86_64")]if x.len()>=256&&is_x86_feature_detected!("avx2"){let avx512=avx512_enabled()&&is_x86_feature_detected!("avx512f");return unsafe{levenshtein_distance_simd(x,y,avx512,row)};}levenshtein_distance_scalar(x,y,row)}fn levenshtein_distance_scalar(x:I,y:I,mut row:F)->usize where I:ExactSizeIterator,I::Item:Eq+Hash,F:FnMut(usize){let m=x.len();let mut masks:HashMap>=HashMap::new();for(i,value)in x.enumerate(){let entries=masks.entry(value).or_default();let block=i/64;if let Some(last)=entries.last_mut().filter(|last|last.0==block){last.1|=1<<(i%64);}else{entries.push((block,1<<(i%64)));}}let mut y=y;let mut skipped=0;let first=y.find(|value|{if masks.contains_key(value){true}else{skipped+=1;row(m.max(skipped));false}});let Some(first)=first else{return m.max(skipped);};let mut state:Vec<_>=(0..m.div_ceil(64)).map(|block|{let shift=skipped.saturating_sub(block*64).min(64)as u32;(u64::MAX.checked_shl(shift).unwrap_or(0),0)}).collect();let top=1<<((m-1)%64);let mut distance=m.max(skipped);for value in std::iter::once(first).chain(y){let mut entries=masks.get(&value).map(Vec::as_slice).unwrap_or(&[]);let(mut carry,mut hp,mut hm)=(false,1,0);let mut last=(0,0);for(block,(pv,mv))in state.iter_mut().enumerate(){let eq=match entries.first(){Some(&(index,mask))if index==block=>{entries=&entries[1..];mask}_=>0,};let xv=eq|*mv;let(sum,overflow)=(eq&*pv).overflowing_add(*pv);let(sum,overflow_carry)=sum.overflowing_add(u64::from(carry));carry=overflow|overflow_carry;let xh=(sum^*pv)|eq;let ph=*mv|!(xh|*pv);let mh=*pv&xh;let ph_shifted=(ph<<1)|hp;let mh_shifted=(mh<<1)|hm;hp=ph>>63;hm=mh>>63;*pv=mh_shifted|!(xv|ph_shifted);*mv=ph_shifted&xv;last=(ph,mh);}distance+=usize::from(last.0&top!=0);distance-=usize::from(last.1&top!=0);row(distance);}distance}#[cfg(target_arch="x86_64")]unsafe fn levenshtein_distance_simd(x:I,y:I,avx512:bool,mut row:F)->usize where I:ExactSizeIterator,I::Item:Eq+Hash,F:FnMut(usize){let lanes=if avx512{8}else{4};let m=x.len();let n=y.len();let mut ids=HashMap::new();let mut pattern:Vec<_>=x.map(|value|{let id=ids.len();*ids.entry(value).or_insert(id)}).collect();let absent=ids.len();let mut text=vec![absent;n+14];for(id,value)in text[7..n+7].iter_mut().zip(y){*id=ids.get(&value).copied().unwrap_or(absent);}drop(ids);let mut offsets=vec![usize::MAX;absent+1];for&id in&text[7..n+7]{offsets[id]=0;}let shared=offsets[..absent].iter().filter(|&&offset|offset==0).count();if shared==0{for len in 1..=n{row(m.max(len));}return m.max(n);}let stride=(m.saturating_add(n)/(shared+4)).max(lanes)/lanes*lanes;let stride=stride.min(m.div_ceil(64).next_multiple_of(lanes));let dense=stride>=32;let stride=if dense{stride}else{lanes};let mut mask_len=0;for offset in&mut offsets[..absent]{if*offset==0{*offset=mask_len;mask_len+=stride;}}offsets[absent]=mask_len;for offset in&mut offsets[..absent]{if*offset==usize::MAX{*offset=mask_len+stride;}}for id in pattern.iter_mut().chain(&mut text){*id=offsets[*id];}drop(offsets);let mut horizontal=vec![0;n+14];horizontal[7..n+7].fill(1);if dense{let storage_len=mask_len+4*stride;let mut storage=vec![0u64;storage_len+7];let alignment=storage.as_ptr().align_offset(64);let storage=&mut storage[alignment..alignment+storage_len];let(masks,state)=storage.split_at_mut(mask_len+2*stride);let(positive,negative)=state.split_at_mut(stride);unsafe{if avx512{advance_dense_avx512(&pattern,&text[7..n+7],&mut horizontal[7..n+7],masks,positive,negative);}else{advance_dense_avx2(&pattern,&text[7..n+7],&mut horizontal[7..n+7],masks,positive,negative);}}}else{let mut masks=vec![0;mask_len+2*lanes];let mut start=0;if avx512{start=pattern.len()/512*512;unsafe{advance_avx512(&pattern[..start],&text,&mut horizontal,&mut masks);}}let end=pattern.len()/256*256;unsafe{advance_avx2(&pattern[start..end],&text,&mut horizontal,&mut masks);}for block in pattern[end..].chunks(64){for(i,&id)in block.iter().enumerate(){masks[id]|=1<0);let eq=masks[id];let xv=eq|mv;let eq=eq|neg;let xh=((eq&pv).wrapping_add(pv)^pv)|eq;let ph=mv|!(xh|pv);let mh=pv&xh;*h=((ph>>shift)&1)as i8-((mh>>shift)&1)as i8;let ph=(ph<<1)|pos;let mh=(mh<<1)|neg;pv=mh|!(xv|ph);mv=ph&xv;}for&id in block{masks[id]=0;}}}let mut delta=0isize;for&h in&horizontal[7..n+7]{delta+=h as isize;row((m as isize+delta)as usize);}(m as isize+delta)as usize}#[cfg(target_arch="x86_64")]#[target_feature(enable="avx2")]unsafe fn advance_dense_avx2(pattern:&[usize],text:&[usize],horizontal:&mut[i8],masks:&mut[u64],positive:&mut[u64],negative:&mut[u64]){use std::arch::x86_64::*;let stride=positive.len();let all=_mm256_set1_epi64x(-1);let one=_mm256_set1_epi64x(1);let positions=_mm256_set_epi64x(3,2,1,0);for group in pattern.chunks(stride*64){for(i,&offset)in group.iter().enumerate(){masks[offset+i/64]|=1<<(i%64);}let words=group.len().div_ceil(64).next_multiple_of(4);let positive=&mut positive[..words];let negative=&mut negative[..words];positive.fill(u64::MAX);negative.fill(0);let mut top=[0u64;4];top[((group.len()-1)/64)%4]=1<<((group.len()-1)%64);let top=unsafe{_mm256_loadu_si256(top.as_ptr().cast())};for(&offset,h)in text.iter().zip(horizontal.iter_mut()){let mut carry=u32::from(*h<0);let mut ph=_mm256_set1_epi64x(i64::from(*h>0)<<63);let mut mh=_mm256_set1_epi64x(i64::from(*h<0)<<63);for((p,m),mask)in positive.as_chunks_mut::<4>().0 .iter_mut().zip(negative.as_chunks_mut::<4>().0 .iter_mut()).zip(masks[offset..offset+words].as_chunks::<4>().0){let(eq,pv,mv)=unsafe{(_mm256_load_si256(mask.as_ptr().cast()),_mm256_load_si256(p.as_ptr().cast()),_mm256_load_si256(m.as_ptr().cast()))};let x=_mm256_and_si256(eq,pv);let sum=_mm256_add_epi64(x,pv);let generate=_mm256_movemask_pd(_mm256_castsi256_pd(_mm256_or_si256(x,_mm256_andnot_si256(sum,pv))))as u32;let propagate=_mm256_movemask_pd(_mm256_castsi256_pd(_mm256_cmpeq_epi64(sum,all)))as u32;let incoming=(propagate+(generate<<1)+carry)^propagate;carry=incoming>>4;let increments=_mm256_and_si256(_mm256_srlv_epi64(_mm256_set1_epi64x(incoming as i64),positions),one);let xv=_mm256_or_si256(eq,mv);let xh=_mm256_or_si256(_mm256_xor_si256(_mm256_add_epi64(sum,increments),pv),eq);let next_ph=_mm256_or_si256(mv,_mm256_andnot_si256(_mm256_or_si256(xh,pv),all));let next_mh=_mm256_and_si256(pv,xh);let in_p=_mm256_blend_epi32::<3>(_mm256_permute4x64_epi64::<0x90>(next_ph),_mm256_permute4x64_epi64::<0xff>(ph));let in_m=_mm256_blend_epi32::<3>(_mm256_permute4x64_epi64::<0x90>(next_mh),_mm256_permute4x64_epi64::<0xff>(mh));ph=next_ph;mh=next_mh;let shifted_ph=_mm256_or_si256(_mm256_slli_epi64::<1>(ph),_mm256_srli_epi64::<63>(in_p));let shifted_mh=_mm256_or_si256(_mm256_slli_epi64::<1>(mh),_mm256_srli_epi64::<63>(in_m));let pv=_mm256_or_si256(shifted_mh,_mm256_andnot_si256(_mm256_or_si256(xv,shifted_ph),all));let mv=_mm256_and_si256(shifted_ph,xv);unsafe{_mm256_store_si256(p.as_mut_ptr().cast(),pv);_mm256_store_si256(m.as_mut_ptr().cast(),mv);}}*h=(_mm256_testz_si256(mh,top)-_mm256_testz_si256(ph,top))as i8;}for(i,&offset)in group.iter().enumerate(){masks[offset+i/64]=0;}}}#[cfg(target_arch="x86_64")]#[target_feature(enable="avx512f")]unsafe fn advance_dense_avx512(pattern:&[usize],text:&[usize],horizontal:&mut[i8],masks:&mut[u64],positive:&mut[u64],negative:&mut[u64]){use std::arch::x86_64::*;let stride=positive.len();let all=_mm512_set1_epi64(-1);let one=_mm512_set1_epi64(1);for group in pattern.chunks(stride*64){for(i,&offset)in group.iter().enumerate(){masks[offset+i/64]|=1<<(i%64);}let words=group.len().div_ceil(64).next_multiple_of(8);let positive=&mut positive[..words];let negative=&mut negative[..words];positive.fill(u64::MAX);negative.fill(0);let mut top=[0u64;8];top[((group.len()-1)/64)%8]=1<<((group.len()-1)%64);let top=unsafe{_mm512_loadu_si512(top.as_ptr().cast())};for(&offset,h)in text.iter().zip(horizontal.iter_mut()){let mut carry=u32::from(*h<0);let mut ph=_mm512_set1_epi64(i64::from(*h>0)<<63);let mut mh=_mm512_set1_epi64(i64::from(*h<0)<<63);for((p,m),mask)in positive.as_chunks_mut::<8>().0 .iter_mut().zip(negative.as_chunks_mut::<8>().0 .iter_mut()).zip(masks[offset..offset+words].as_chunks::<8>().0){let(eq,pv,mv)=unsafe{(_mm512_load_si512(mask.as_ptr().cast()),_mm512_load_si512(p.as_ptr().cast()),_mm512_load_si512(m.as_ptr().cast()))};let x=_mm512_and_si512(eq,pv);let sum=_mm512_add_epi64(x,pv);let generate=_mm512_cmp_epu64_mask::<_MM_CMPINT_LT>(sum,x)as u32;let propagate=_mm512_cmpeq_epi64_mask(sum,all)as u32;let incoming=(propagate+(generate<<1)+carry)^propagate;carry=incoming>>8;let sum=_mm512_mask_add_epi64(sum,incoming as u8,sum,one);let xv=_mm512_or_si512(eq,mv);let xh=_mm512_ternarylogic_epi64::<0xbe>(sum,pv,eq);let next_ph=_mm512_ternarylogic_epi64::<0xf1>(mv,xh,pv);let next_mh=_mm512_and_si512(pv,xh);let in_p=_mm512_alignr_epi64::<7>(next_ph,ph);let in_m=_mm512_alignr_epi64::<7>(next_mh,mh);ph=next_ph;mh=next_mh;let shifted_ph=_mm512_or_si512(_mm512_slli_epi64::<1>(ph),_mm512_srli_epi64::<63>(in_p));let shifted_mh=_mm512_or_si512(_mm512_slli_epi64::<1>(mh),_mm512_srli_epi64::<63>(in_m));let pv=_mm512_ternarylogic_epi64::<0xf1>(shifted_mh,xv,shifted_ph);let mv=_mm512_and_si512(shifted_ph,xv);unsafe{_mm512_store_si512(p.as_mut_ptr().cast(),pv);_mm512_store_si512(m.as_mut_ptr().cast(),mv);}}*h=i8::from(_mm512_test_epi64_mask(ph,top)!=0)-i8::from(_mm512_test_epi64_mask(mh,top)!=0);}for(i,&offset)in group.iter().enumerate(){masks[offset+i/64]=0;}}}#[cfg(target_arch="x86_64")]#[target_feature(enable="avx2")]unsafe fn advance_avx2(pattern:&[usize],text:&[usize],horizontal:&mut[i8],masks:&mut[u64]){use std::arch::x86_64::*;let n=text.len()-14;let offsets=_mm256_set_epi64x(3,2,1,0);let all=_mm256_set1_epi64x(-1);for group in pattern.as_chunks::<256>().0{for(i,&id)in group.iter().enumerate(){masks[id+i/64]|=1<<(i%64);}let mut pv=all;let mut mv=_mm256_setzero_si256();let mut hp=_mm256_setzero_si256();let mut hm=_mm256_setzero_si256();for t in 0..n+3{let incoming=horizontal[t+7];let pos=_mm256_blend_epi32::<3>(_mm256_permute4x64_epi64::<0x93>(hp),_mm256_set1_epi64x(i64::from(incoming>0)));let neg=_mm256_blend_epi32::<3>(_mm256_permute4x64_epi64::<0x93>(hm),_mm256_set1_epi64x(i64::from(incoming<0)));let eq=unsafe{let ids=_mm256_loadu_si256(text.as_ptr().add(t+4).cast());let indices=_mm256_add_epi64(_mm256_permute4x64_epi64::<0x1b>(ids),offsets);_mm256_i64gather_epi64::<8>(masks.as_ptr().cast(),indices)};let xv=_mm256_or_si256(eq,mv);let eq=_mm256_or_si256(eq,neg);let xh=_mm256_or_si256(_mm256_xor_si256(_mm256_add_epi64(_mm256_and_si256(eq,pv),pv),pv),eq);let ph=_mm256_or_si256(mv,_mm256_xor_si256(_mm256_or_si256(xh,pv),all));let mh=_mm256_and_si256(pv,xh);hp=_mm256_srli_epi64::<63>(ph);hm=_mm256_srli_epi64::<63>(mh);horizontal[t+4]=_mm256_extract_epi64::<3>(_mm256_sub_epi64(hp,hm))as i8;let ph=_mm256_or_si256(_mm256_slli_epi64::<1>(ph),pos);let mh=_mm256_or_si256(_mm256_slli_epi64::<1>(mh),neg);pv=_mm256_or_si256(mh,_mm256_xor_si256(_mm256_or_si256(xv,ph),all));mv=_mm256_and_si256(ph,xv);}for(i,&id)in group.iter().enumerate(){masks[id+i/64]=0;}}}#[cfg(target_arch="x86_64")]#[target_feature(enable="avx512f")]unsafe fn advance_avx512(pattern:&[usize],text:&[usize],horizontal:&mut[i8],masks:&mut[u64]){use std::arch::x86_64::*;let n=text.len()-14;let offsets=_mm512_set_epi64(7,6,5,4,3,2,1,0);let reverse=_mm512_set_epi64(0,1,2,3,4,5,6,7);let rotate=_mm512_set_epi64(6,5,4,3,2,1,0,7);let all=_mm512_set1_epi64(-1);for group in pattern.as_chunks::<512>().0{for(i,&id)in group.iter().enumerate(){masks[id+i/64]|=1<<(i%64);}let mut pv=all;let mut mv=_mm512_setzero_si512();let mut hp=_mm512_setzero_si512();let mut hm=_mm512_setzero_si512();for t in 0..n+7{let incoming=horizontal[t+7];let pos=_mm512_mask_mov_epi64(_mm512_permutexvar_epi64(rotate,hp),1,_mm512_set1_epi64(i64::from(incoming>0)));let neg=_mm512_mask_mov_epi64(_mm512_permutexvar_epi64(rotate,hm),1,_mm512_set1_epi64(i64::from(incoming<0)));let eq=unsafe{let ids=_mm512_loadu_si512(text.as_ptr().add(t).cast());let indices=_mm512_add_epi64(_mm512_permutexvar_epi64(reverse,ids),offsets);_mm512_i64gather_epi64::<8>(indices,masks.as_ptr().cast())};let xv=_mm512_or_si512(eq,mv);let eq=_mm512_or_si512(eq,neg);let xh=_mm512_or_si512(_mm512_xor_si512(_mm512_add_epi64(_mm512_and_si512(eq,pv),pv),pv),eq);let ph=_mm512_or_si512(mv,_mm512_xor_si512(_mm512_or_si512(xh,pv),all));let mh=_mm512_and_si512(pv,xh);hp=_mm512_srli_epi64::<63>(ph);hm=_mm512_srli_epi64::<63>(mh);let out=_mm512_extracti32x4_epi32::<3>(_mm512_sub_epi64(hp,hm));horizontal[t]=_mm_cvtsi128_si64(_mm_srli_si128::<8>(out))as i8;let ph=_mm512_or_si512(_mm512_slli_epi64::<1>(ph),pos);let mh=_mm512_or_si512(_mm512_slli_epi64::<1>(mh),neg);pv=_mm512_or_si512(mh,_mm512_xor_si512(_mm512_or_si512(xv,ph),all));mv=_mm512_and_si512(ph,xv);}for(i,&id)in group.iter().enumerate(){masks[id+i/64]=0;}}}} // codesnip-guard: avx_helper #[cfg_attr(any(),rustfmt::skip)]pub use self::avx_helper::{SimdBackend,avx512_enabled,avx512_supported,disable_avx512,enable_avx512,simd_backend};#[cfg_attr(any(),rustfmt::skip)]mod avx_helper{use std::sync::atomic::{AtomicBool,Ordering};#[derive(Copy,Clone,Debug,Eq,PartialEq)]pub enum SimdBackend{Scalar,Avx2,Avx512}static AVX512_ENABLED:AtomicBool=AtomicBool::new(true);#[inline]pub fn disable_avx512(){AVX512_ENABLED.store(false,Ordering::Relaxed);}#[inline]pub fn enable_avx512(){AVX512_ENABLED.store(true,Ordering::Relaxed);}#[inline]pub fn avx512_enabled()->bool{AVX512_ENABLED.load(Ordering::Relaxed)}#[inline]pub fn avx512_supported()->bool{#[cfg(any(target_arch="x86",target_arch="x86_64"))]return is_x86_feature_detected!("avx512f")&&is_x86_feature_detected!("avx512dq")&&is_x86_feature_detected!("avx512cd")&&is_x86_feature_detected!("avx512bw")&&is_x86_feature_detected!("avx512vl");#[cfg(not(any(target_arch="x86",target_arch="x86_64")))]false}#[inline]pub fn simd_backend()->SimdBackend{#[cfg(any(target_arch="x86",target_arch="x86_64"))]{if avx512_enabled()&&avx512_supported(){return SimdBackend::Avx512;}if is_x86_feature_detected!("avx2"){return SimdBackend::Avx2;}}SimdBackend::Scalar}#[macro_export]macro_rules!avx_helper{(@dispatch$backend:path,$kind:ident;$avx512:expr,$avx2:expr,$scalar:expr)=>{{#[cfg(target_arch="x86_64")]{match$backend(){$kind::Avx512=>$avx512,$kind::Avx2=>$avx2,$kind::Scalar=>$scalar,}}#[cfg(not(target_arch="x86_64"))]$scalar}};(@dispatch_avx2_fma$avx2:expr,$scalar:expr)=>{{#[cfg(target_arch="x86_64")]{if is_x86_feature_detected!("avx2")&&is_x86_feature_detected!("fma"){$avx2}else{$scalar}}#[cfg(not(target_arch="x86_64"))]$scalar}};(@avx512$(#[$meta:meta])*$vis:vis fn$name:ident$(<$($T:ident),+>)?($($i:ident:$t:ty),*)->$ret:ty where[$($clauses:tt)*]$body:block)=>{$(#[$meta])*$vis fn$name$(<$($T)*>)?($($i:$t),*)->$ret where$($clauses)*{if$crate::avx512_supported(){$crate::avx_helper!(@def_avx512 fn avx512$(<$($T)*>)?($($i:$t),*)->$ret where[$($clauses)*]$body);unsafe{avx512$(::<$($T),*>)?($($i),*)}}else if is_x86_feature_detected!("avx2"){$crate::avx_helper!(@def_avx2 fn avx2$(<$($T)*>)?($($i:$t),*)->$ret where[$($clauses)*]$body);unsafe{avx2$(::<$($T),*>)?($($i),*)}}else{$body}}};(@avx2$(#[$meta:meta])*$vis:vis fn$name:ident$(<$($T:ident),+>)?($($i:ident:$t:ty),*)->$ret:ty where[$($clauses:tt)*]$body:block)=>{$(#[$meta])*$vis fn$name$(<$($T)*>)?($($i:$t),*)->$ret where$($clauses)*{if is_x86_feature_detected!("avx2"){$crate::avx_helper!(@def_avx2 fn avx2$(<$($T)*>)?($($i:$t),*)->$ret where[$($clauses)*]$body);unsafe{avx2$(::<$($T),*>)?($($i),*)}}else{$body}}};(@def_avx512 fn$name:ident$(<$($T:ident),+>)?($($args:tt)*)->$ret:ty where[$($clauses:tt)*]$body:block)=>{#[target_feature(enable="avx512f,avx512dq,avx512cd,avx512bw,avx512vl")]unsafe fn$name$(<$($T)*>)?($($args)*)->$ret where$($clauses)*$body};(@def_avx2 fn$name:ident$(<$($T:ident),+>)?($($args:tt)*)->$ret:ty where[$($clauses:tt)*]$body:block)=>{#[target_feature(enable="avx2")]unsafe fn$name$(<$($T)*>)?($($args)*)->$ret where$($clauses)*$body};(@$tag:ident$(#[$meta:meta])*$vis:vis fn$name:ident$(<$($T:ident),+>)?($($args:tt)*)->$ret:ty$body:block)=>{$crate::avx_helper!(@$tag$(#[$meta])*$vis fn$name$(<$($T)*>)?($($args)*)->$ret where[]$body);};(@$tag:ident$(#[$meta:meta])*$vis:vis fn$name:ident$(<$($T:ident),+>)?($($args:tt)*)$($t:tt)*)=>{$crate::avx_helper!(@$tag$(#[$meta])*$vis fn$name$(<$($T)*>)?($($args)*)->()$($t)*);};($($t:tt)*)=>{::std::compile_error!($($t)*);}}}