pax_global_header00006660000000000000000000000064142504132070014510gustar00rootroot0000000000000052 comment=16c64e5163cbd0502792ca20f1069fe40c929b0d abPOA-1.4.1/000077500000000000000000000000001425041320700124355ustar00rootroot00000000000000abPOA-1.4.1/.gitignore000066400000000000000000000012321425041320700144230ustar00rootroot00000000000000# Object files *.o *.ko *.obj *.elf .idea/* build/* # dot abpoa.dot abpoa.dot.pdf # readme README.html # Precompiled Headers *.gch *.pch # Libraries lib/* *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables bin/* example sub_example *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su #data data/* test_data/cons.fa *.dot *.png *.pdf #tags tags cscope.* # python dist/* pyabpoa.egg-info/* python/build/* python/dist/* python/example.png python/pyabpoa.c python/pyabpoa.egg-info/* python/src/* # eval file evaluation/msa_abPOA evaluation/msa_spoa #evaluation/racon_abPOA #evaluation/racon_spoa abPOA-1.4.1/.gitmodules000066400000000000000000000001461425041320700146130ustar00rootroot00000000000000[submodule "include/simde"] path = include/simde url = https://github.com/simd-everywhere/simde.git abPOA-1.4.1/.travis.yml000066400000000000000000000002071425041320700145450ustar00rootroot00000000000000matrix: include: - language: c compiler: gcc script: make - language: c compiler: clang script: make abPOA-1.4.1/BLOSUM62.mtx000066400000000000000000000045171425041320700143470ustar00rootroot00000000000000# Entries for the BLOSUM62 matrix at a scale of ln(2)/2.0. A R N D C Q E G H I L K M F P S T W Y V B J Z X * O U A 4 -1 -2 -2 0 -1 -1 0 -2 -1 -1 -1 -1 -2 -1 1 0 -3 -2 0 -2 -1 -1 -1 -4 -4 -4 R -1 5 0 -2 -3 1 0 -2 0 -3 -2 2 -1 -3 -2 -1 -1 -3 -2 -3 -1 -2 0 -1 -4 -4 -4 N -2 0 6 1 -3 0 0 0 1 -3 -3 0 -2 -3 -2 1 0 -4 -2 -3 4 -3 0 -1 -4 -4 -4 D -2 -2 1 6 -3 0 2 -1 -1 -3 -4 -1 -3 -3 -1 0 -1 -4 -3 -3 4 -3 1 -1 -4 -4 -4 C 0 -3 -3 -3 9 -3 -4 -3 -3 -1 -1 -3 -1 -2 -3 -1 -1 -2 -2 -1 -3 -1 -3 -1 -4 -4 -4 Q -1 1 0 0 -3 5 2 -2 0 -3 -2 1 0 -3 -1 0 -1 -2 -1 -2 0 -2 4 -1 -4 -4 -4 E -1 0 0 2 -4 2 5 -2 0 -3 -3 1 -2 -3 -1 0 -1 -3 -2 -2 1 -3 4 -1 -4 -4 -4 G 0 -2 0 -1 -3 -2 -2 6 -2 -4 -4 -2 -3 -3 -2 0 -2 -2 -3 -3 -1 -4 -2 -1 -4 -4 -4 H -2 0 1 -1 -3 0 0 -2 8 -3 -3 -1 -2 -1 -2 -1 -2 -2 2 -3 0 -3 0 -1 -4 -4 -4 I -1 -3 -3 -3 -1 -3 -3 -4 -3 4 2 -3 1 0 -3 -2 -1 -3 -1 3 -3 3 -3 -1 -4 -4 -4 L -1 -2 -3 -4 -1 -2 -3 -4 -3 2 4 -2 2 0 -3 -2 -1 -2 -1 1 -4 3 -3 -1 -4 -4 -4 K -1 2 0 -1 -3 1 1 -2 -1 -3 -2 5 -1 -3 -1 0 -1 -3 -2 -2 0 -3 1 -1 -4 -4 -4 M -1 -1 -2 -3 -1 0 -2 -3 -2 1 2 -1 5 0 -2 -1 -1 -1 -1 1 -3 2 -1 -1 -4 -4 -4 F -2 -3 -3 -3 -2 -3 -3 -3 -1 0 0 -3 0 6 -4 -2 -2 1 3 -1 -3 0 -3 -1 -4 -4 -4 P -1 -2 -2 -1 -3 -1 -1 -2 -2 -3 -3 -1 -2 -4 7 -1 -1 -4 -3 -2 -2 -3 -1 -1 -4 -4 -4 S 1 -1 1 0 -1 0 0 0 -1 -2 -2 0 -1 -2 -1 4 1 -3 -2 -2 0 -2 0 -1 -4 -4 -4 T 0 -1 0 -1 -1 -1 -1 -2 -2 -1 -1 -1 -1 -2 -1 1 5 -2 -2 0 -1 -1 -1 -1 -4 -4 -4 W -3 -3 -4 -4 -2 -2 -3 -2 -2 -3 -2 -3 -1 1 -4 -3 -2 11 2 -3 -4 -2 -2 -1 -4 -4 -4 Y -2 -2 -2 -3 -2 -1 -2 -3 2 -1 -1 -2 -1 3 -3 -2 -2 2 7 -1 -3 -1 -2 -1 -4 -4 -4 V 0 -3 -3 -3 -1 -2 -2 -3 -3 3 1 -2 1 -1 -2 -2 0 -3 -1 4 -3 2 -2 -1 -4 -4 -4 B -2 -1 4 4 -3 0 1 -1 0 -3 -4 0 -3 -3 -2 0 -1 -4 -3 -3 4 -3 0 -1 -4 -4 -4 J -1 -2 -3 -3 -1 -2 -3 -4 -3 3 3 -3 2 0 -3 -2 -1 -2 -1 2 -3 3 -3 -1 -4 -4 -4 Z -1 0 0 1 -3 4 4 -2 0 -3 -3 1 -1 -3 -1 0 -1 -2 -2 -2 0 -3 4 -1 -4 -4 -4 X -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -4 -4 -4 * -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 1 -4 -4 O -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 1 -4 U -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 -4 1 abPOA-1.4.1/CMakeLists.txt000066400000000000000000000031431425041320700151760ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) project(abpoa LANGUAGES C VERSION 3.0.0) include(GNUInstallDirs) find_package(ZLIB REQUIRED) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") # SIMD # build abPOA as a static library by default set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build all libraries as shared") SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") add_library(abpoa src/abpoa_align.c src/abpoa_graph.c src/abpoa_output.c src/abpoa_plot.c src/abpoa_seed.c src/abpoa_seq.c src/kalloc.c src/kstring.c src/simd_abpoa_align.c src/simd_check.c src/utils.c) add_executable(abpoa_bin src/abpoa.c) target_link_libraries(abpoa_bin abpoa z pthread m) set_target_properties(abpoa_bin PROPERTIES OUTPUT_NAME abpoa) target_include_directories(abpoa PUBLIC $ $) install(TARGETS abpoa DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS abpoa_bin DESTINATION ${CMAKE_INSTALL_BINDIR}) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h") # configure and install pkg-config file configure_file(${CMAKE_CURRENT_SOURCE_DIR}/abpoa.pc.in ${CMAKE_CURRENT_BINARY_DIR}/abpoa-1.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/abpoa-1.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) abPOA-1.4.1/HOXD70.mtx000066400000000000000000000005171425041320700141030ustar00rootroot00000000000000# all five kinds of bases need to be included # do not forget to set gap-open/extension penalty with -O/-E A C G T N A 91 -114 -31 -123 0 C -114 100 -125 -31 0 G -31 -125 100 -114 0 T -123 -31 -144 91 0 N 0 0 0 0 0 abPOA-1.4.1/LICENSE000066400000000000000000000020621425041320700134420ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2020 Yan Gao Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. abPOA-1.4.1/MANIFEST.in000066400000000000000000000007671425041320700142050ustar00rootroot00000000000000include src/abpoa_align.h include src/abpoa_graph.h include src/abpoa.h include src/abpoa_output.h include src/abpoa_seed.h include src/abpoa_seq.h include src/kalloc.h include src/kdq.h include src/khash.h include src/kseq.h include src/ksort.h include src/kstring.h include src/kvec.h include src/simd_abpoa_align.h include src/simd_instruction.h include src/utils.h recursive-include include/ *.h include python/cabpoa.pxd include python/pyabpoa.c include python/pyabpoa.pyx include python/README.md abPOA-1.4.1/Makefile000066400000000000000000000077671425041320700141160ustar00rootroot00000000000000#CC = gcc EXTRA_FLAGS = -Wno-unused-function -Wno-misleading-indentation -DUSE_SIMDE -DSIMDE_ENABLE_NATIVE_ALIASES CFLAGS = -Wall -O3 $(EXTRA_FLAGS) SIMD_FLAG = -march=native ifneq ($(armv7),) # for ARMv7 SIMD_FLAG = -march=armv7-a -mfpu=neon -D__AVX2__ else ifneq ($(armv8),) # for ARMv8 ifneq ($(aarch64),) # for Aarch64 SIMD_FLAG = -march=armv8-a+simd -D__AVX2__ else # for Aarch32 SIMD_FLAG = -march=armv8-a+simd -mfpu=auto -D__AVX2__ endif endif endif # for debug ifneq ($(debug),) DFLAGS = -D __DEBUG__ endif # for gdb ifneq ($(gdb),) CFLAGS = -Wall -g ${DFLAGS} $(EXTRA_FLAGS) else CFLAGS = -Wall -O3 ${DFLAGS} $(EXTRA_FLAGS) endif # for gprof ifneq ($(pg),) PG_FLAG = -pg CFLAGS += -pg endif LIB = -lm -lz -lpthread ifneq ($(PREFIX),) OUT_PRE_DIR = $(PREFIX) else OUT_PRE_DIR = . endif BIN_DIR = $(OUT_PRE_DIR)/bin LIB_DIR = $(OUT_PRE_DIR)/lib INC_DIR = ./include SRC_DIR = ./src SOURCE = $(SRC_DIR)/abpoa_align.c $(SRC_DIR)/abpoa.c $(SRC_DIR)/abpoa_graph.c $(SRC_DIR)/abpoa_plot.c $(SRC_DIR)/abpoa_seed.c $(SRC_DIR)/abpoa_seq.c $(SRC_DIR)/abpoa_output.c $(SRC_DIR)/kalloc.c $(SRC_DIR)/kstring.c $(SRC_DIR)/simd_abpoa_align.c $(SRC_DIR)/simd_check.c $(SRC_DIR)/utils.c HEADER = $(SRC_DIR)/abpoa_align.h $(SRC_DIR)/abpoa_graph.h $(SRC_DIR)/abpoa.h $(INC_DIR)/abpoa.h $(SRC_DIR)/abpoa_seed.h $(SRC_DIR)/abpoa_seq.h $(SRC_DIR)/abpoa_output.h $(SRC_DIR)/kalloc.h $(SRC_DIR)/kdq.h $(SRC_DIR)/khash.h $(SRC_DIR)/kseq.h $(SRC_DIR)/ksort.h $(SRC_DIR)/kstring.h $(SRC_DIR)/kvec.h $(SRC_DIR)/simd_instruction.h $(INC_DIR)/simd_instruction.h $(SRC_DIR)/simd_abpoa_align.h $(SRC_DIR)/utils.h OBJS = $(SRC_DIR)/abpoa_align.o $(SRC_DIR)/abpoa_graph.o $(SRC_DIR)/abpoa_plot.o $(SRC_DIR)/abpoa_seed.o $(SRC_DIR)/abpoa_seq.o $(SRC_DIR)/abpoa_output.o $(SRC_DIR)/kalloc.o $(SRC_DIR)/kstring.o $(SRC_DIR)/simd_abpoa_align.o $(SRC_DIR)/simd_check.o $(SRC_DIR)/utils.o # SIMD label SIMD_CHECK_D = -D __CHECK_SIMD_MAIN__ FLAG_SSE2 = -msse2 FLAG_SSE41 = -msse4.1 FLAG_AVX2 = -mavx2 # FLAG_AVX512F = -mavx512f # FLAG_AVX512BW = -mavx512bw ifneq ($(sse2),) SIMD_FLAG=$(FLAG_SSE2) py_SIMD_FLAG = SSE2=1 else ifneq ($(sse41),) SIMD_FLAG=$(FLAG_SSE41) py_SIMD_FLAG = SSE41=1 else ifneq ($(avx2),) SIMD_FLAG=$(FLAG_AVX2) py_SIMD_FLAG = AVX2=1 #else ifneq ($(avx512f),) # SIMD_FLAG=$(FLAG_AVX512F) # py_SIMD_FLAG = AVX512f=1 #else ifneq ($(avx512bw),) # SIMD_FLAG=$(FLAG_AVX512BW) # py_SIMD_FLAG = AVX512BW=1 endif .c.o: $(CC) -c $(CFLAGS) $< -I$(INC_DIR) -o $@ BIN = $(BIN_DIR)/abpoa ifneq ($(gdb),) BIN = $(BIN_DIR)/gdb_abpoa endif ABPOALIB = $(LIB_DIR)/libabpoa.a # TODO add example EXAMPLE = example all: $(BIN) abpoa: $(BIN) libabpoa: $(ABPOALIB) example: $(EXAMPLE) $(BIN):$(SRC_DIR)/abpoa.o $(ABPOALIB) if [ ! -d $(BIN_DIR) ]; then mkdir $(BIN_DIR); fi $(CC) $(CFLAGS) $< -I$(INC_DIR) -L$(LIB_DIR) -labpoa $(LIB) -o $@ $(PG_FLAG) $(EXAMPLE):example.c $(ABPOALIB) $(CC) $(CFLAGS) $< -o $@ -I$(INC_DIR) -L$(LIB_DIR) -labpoa $(LIB) $(ABPOALIB):$(OBJS) if [ ! -d $(LIB_DIR) ]; then mkdir $(LIB_DIR); fi $(AR) -csr $@ $(OBJS) $(SRC_DIR)/abpoa.o:$(SRC_DIR)/abpoa.c $(SRC_DIR)/abpoa.h $(SRC_DIR)/abpoa_graph.h $(SRC_DIR)/abpoa_align.h \ $(SRC_DIR)/abpoa_seq.h $(SRC_DIR)/utils.h $(SRC_DIR)/simd_instruction.h $(CC) -c $(CFLAGS) $(SIMD_FLAG) -I$(INC_DIR) $< -o $@ $(SRC_DIR)/simd_check.o:$(SRC_DIR)/simd_check.c $(SRC_DIR)/simd_instruction.h $(CC) -c $(CFLAGS) $(SIMD_FLAG) -I$(INC_DIR) $< -o $@ $(SRC_DIR)/simd_abpoa_align.o:$(SRC_DIR)/simd_abpoa_align.c $(SRC_DIR)/abpoa_graph.h $(SRC_DIR)/abpoa_align.h $(SRC_DIR)/simd_instruction.h $(SRC_DIR)/utils.h $(CC) -c $(CFLAGS) $(SIMD_FLAG) -I$(INC_DIR) $< -o $@ install_py: python/cabpoa.pxd python/pyabpoa.pyx python/README.md ${py_SIMD_FLAG} python setup.py install sdist: install_py ${py_SIMD_FLAG} python setup.py sdist #bdist_wheel publish_pypi: clean_py sdist twine upload dist/* clean: rm -f $(SRC_DIR)/*.[oa] $(LIB_DIR)/*.[oa] $(BIN) clean_py: rm -rf build/ dist/ pyabpoa.egg-info/ python/pyabpoa.c abPOA-1.4.1/PAM250.mtx000066400000000000000000000045161425041320700140410ustar00rootroot00000000000000# Entries for the PAM250 matrix at a scale of ln(2)/3.0. A R N D C Q E G H I L K M F P S T W Y V B J Z X * O U A 2 -2 0 0 -2 0 0 1 -1 -1 -2 -1 -1 -3 1 1 1 -6 -3 0 0 -1 0 -1 -8 -8 -8 R -2 6 0 -1 -4 1 -1 -3 2 -2 -3 3 0 -4 0 0 -1 2 -4 -2 -1 -3 0 -1 -8 -8 -8 N 0 0 2 2 -4 1 1 0 2 -2 -3 1 -2 -3 0 1 0 -4 -2 -2 2 -3 1 -1 -8 -8 -8 D 0 -1 2 4 -5 2 3 1 1 -2 -4 0 -3 -6 -1 0 0 -7 -4 -2 3 -3 3 -1 -8 -8 -8 C -2 -4 -4 -5 12 -5 -5 -3 -3 -2 -6 -5 -5 -4 -3 0 -2 -8 0 -2 -4 -5 -5 -1 -8 -8 -8 Q 0 1 1 2 -5 4 2 -1 3 -2 -2 1 -1 -5 0 -1 -1 -5 -4 -2 1 -2 3 -1 -8 -8 -8 E 0 -1 1 3 -5 2 4 0 1 -2 -3 0 -2 -5 -1 0 0 -7 -4 -2 3 -3 3 -1 -8 -8 -8 G 1 -3 0 1 -3 -1 0 5 -2 -3 -4 -2 -3 -5 0 1 0 -7 -5 -1 0 -4 0 -1 -8 -8 -8 H -1 2 2 1 -3 3 1 -2 6 -2 -2 0 -2 -2 0 -1 -1 -3 0 -2 1 -2 2 -1 -8 -8 -8 I -1 -2 -2 -2 -2 -2 -2 -3 -2 5 2 -2 2 1 -2 -1 0 -5 -1 4 -2 3 -2 -1 -8 -8 -8 L -2 -3 -3 -4 -6 -2 -3 -4 -2 2 6 -3 4 2 -3 -3 -2 -2 -1 2 -3 5 -3 -1 -8 -8 -8 K -1 3 1 0 -5 1 0 -2 0 -2 -3 5 0 -5 -1 0 0 -3 -4 -2 1 -3 0 -1 -8 -8 -8 M -1 0 -2 -3 -5 -1 -2 -3 -2 2 4 0 6 0 -2 -2 -1 -4 -2 2 -2 3 -2 -1 -8 -8 -8 F -3 -4 -3 -6 -4 -5 -5 -5 -2 1 2 -5 0 9 -5 -3 -3 0 7 -1 -4 2 -5 -1 -8 -8 -8 P 1 0 0 -1 -3 0 -1 0 0 -2 -3 -1 -2 -5 6 1 0 -6 -5 -1 -1 -2 0 -1 -8 -8 -8 S 1 0 1 0 0 -1 0 1 -1 -1 -3 0 -2 -3 1 2 1 -2 -3 -1 0 -2 0 -1 -8 -8 -8 T 1 -1 0 0 -2 -1 0 0 -1 0 -2 0 -1 -3 0 1 3 -5 -3 0 0 -1 -1 -1 -8 -8 -8 W -6 2 -4 -7 -8 -5 -7 -7 -3 -5 -2 -3 -4 0 -6 -2 -5 17 0 -6 -5 -3 -6 -1 -8 -8 -8 Y -3 -4 -2 -4 0 -4 -4 -5 0 -1 -1 -4 -2 7 -5 -3 -3 0 10 -2 -3 -1 -4 -1 -8 -8 -8 V 0 -2 -2 -2 -2 -2 -2 -1 -2 4 2 -2 2 -1 -1 -1 0 -6 -2 4 -2 2 -2 -1 -8 -8 -8 B 0 -1 2 3 -4 1 3 0 1 -2 -3 1 -2 -4 -1 0 0 -5 -3 -2 3 -3 2 -1 -8 -8 -8 J -1 -3 -3 -3 -5 -2 -3 -4 -2 3 5 -3 3 2 -2 -2 -1 -3 -1 2 -3 5 -2 -1 -8 -8 -8 Z 0 0 1 3 -5 3 3 0 2 -2 -3 0 -2 -5 0 0 -1 -6 -4 -2 2 -2 3 -1 -8 -8 -8 X -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -8 -8 -8 * -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 1 -8 -8 O -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 1 -8 U -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 -8 1 abPOA-1.4.1/README.md000066400000000000000000000340561425041320700137240ustar00rootroot00000000000000# abPOA: adaptive banded Partial Order Alignment [![Latest Release](https://img.shields.io/github/release/yangao07/abPOA.svg?label=Release)](https://github.com/yangao07/abPOA/releases/latest) [![Github All Releases](https://img.shields.io/github/downloads/yangao07/abPOA/total.svg?label=Download)](https://github.com/yangao07/abPOA/releases) [![BioConda Install](https://img.shields.io/conda/dn/bioconda/abpoa.svg?style=flag&label=BioConda%20install)](https://anaconda.org/bioconda/abpoa) [![PyPI](https://img.shields.io/pypi/dm/pyabpoa.svg?label=pip%20install)](https://pypi.python.org/pypi/pyabpoa) [![Published in Bioinformatics](https://img.shields.io/badge/Published%20in-Bioinformatics-blue.svg)](https://dx.doi.org/10.1093/bioinformatics/btaa963) [![GitHub Issues](https://img.shields.io/github/issues/yangao07/abPOA.svg?label=Issues)](https://github.com/yangao07/abPOA/issues) [![Build Status](https://img.shields.io/travis/yangao07/abPOA/master.svg?label=Master)](https://travis-ci.org/yangao07/abPOA) [![License](https://img.shields.io/badge/License-MIT-black.svg)](https://github.com/yangao07/abPOA/blob/master/LICENSE) ## Updates (v1.4.1) - Take quality score in FASTQ format file as weight (-Q) ## Getting started Download the [latest release](https://github.com/yangao07/abPOA/releases): ``` wget https://github.com/yangao07/abPOA/releases/download/v1.4.1/abPOA-v1.4.1.tar.gz tar -zxvf abPOA-v1.4.1.tar.gz && cd abPOA-v1.4.1 ``` Make from source and run with test data: ``` make; ./bin/abpoa ./test_data/seq.fa > cons.fa ``` Or, install via conda and run with test data: ``` conda install -c bioconda abpoa abpoa ./test_data/seq.fa > cons.fa ``` ## Table of Contents - [Introduction](#introduction) - [Installation](#install) - [Installing abPOA via conda](#conda) - [Building abPOA from source files](#build) - [Pre-built binary executable file for Linux/Unix](#binary) - [General usage](#usage) - [To generate one consensus sequence](#gen_1cons) - [To generate multiple consensus sequences](#gen_mcons) - [To generate row-column multiple sequence alignment](#gen_msa) - [To generate graph information in GFA format](#gen_gfa) - [To align sequence to an existing graph in GFA/MSA format](#aln_to_gfa) - [To generate a plot of the alignment graph](#gen_plot) - [Input](#input) - [Output](#output) - [Consensus sequence](#cons) - [Row-column multiple sequence alignment](#msa) - [Full graph information](#gfa) - [Plot of alignment graph](#plot) - [Algorithm description](#description) - [Adaptive banding](#banding) - [Minimizer-based seeding and partition](#seeding) - [Minimizer-based progressive tree](#tree) - [Multiple conensus sequences](#mcons) - [For development](#dev) - [Evaluation datasets](#eval) - [Contact](#contact) ## Introduction abPOA is an extended version of [Partial Order Alignment (POA](10.1093/bioinformatics/18.3.452)) that performs adaptive banded dynamic programming (DP) with an SIMD implementation. abPOA can perform multiple sequence alignment (MSA) on a set of input sequences and generate a consensus sequence by applying the [heaviest bundling algorithm](10.1093/bioinformatics/btg109) to the final alignment graph. abPOA can generate high-quality consensus sequences from error-prone long reads and offer significant speed improvement over existing tools. abPOA supports three alignment modes (global, local, extension) and flexible scoring schemes that allow linear, affine and convex gap penalties. It right now supports SSE2/SSE4.1/AVX2 vectorization. For more information, please refer to our [paper](https://dx.doi.org/10.1093/bioinformatics/btaa963) published in Bioinformatics. ## Installation ### Installing abPOA via conda On Linux/Unix and Mac OS, abPOA can be installed via ``` conda install -c bioconda abpoa # install abPOA program ``` ### Building abPOA from source files You can also build abPOA from source files. Make sure you have gcc (>=6.4.0) and zlib installed before compiling. It is recommended to download the [latest release](https://github.com/yangao07/abPOA/releases). ``` wget https://github.com/yangao07/abPOA/releases/download/v1.4.1/abPOA-v1.4.1.tar.gz tar -zxvf abPOA-v1.4.1.tar.gz cd abPOA-v1.4.1; make ``` Or, you can use `git clone` command to download the source code. This gives you the latest version of abPOA, which might be still under development. ``` git clone --recursive https://github.com/yangao07/abPOA.git cd abPOA; make ``` ### Pre-built binary executable file for Linux/Unix If you meet any compiling issue, please try the pre-built binary file: ``` wget https://github.com/yangao07/abPOA/releases/download/v1.4.1/abPOA-v1.4.1_x64-linux.tar.gz tar -zxvf abPOA-v1.4.1_x64-linux.tar.gz ``` ## General usage ### To generate consensus sequence ``` abpoa seq.fa > cons.fa ``` ### To generate multiple consensus sequences ``` abpoa heter.fa -d2 > 2cons.fa ``` ### To generate row-column multiple sequence alignment in FASTA format ``` abpoa seq.fa -r1 > out.msa abpoa seq.fa -r2 > out_cons.msa ``` ### To generate graph information in [GFA](https://github.com/GFA-spec/GFA-spec/blob/master/GFA1.md) format ``` abpoa seq.fa -r3 > out.gfa ``` To include the generated consensus sequence as a path in the GFA file: ``` abpoa seq.fa -r4 > out.gfa ``` ### To align sequence to an existing graph in GFA/MSA format ``` abpoa -i in.gfa seq.fa -r3 > out.gfa abpoa -i in.msa seq.fa -r1 > out.msa ``` For GFA input file, `S` and `P` lines are required and are used to reconstruct the alignment graph. For MSA input file, which is generally a FASTA format file, `-` in the sequence indicates the alignment gap. ``` abpoa seq1.fa -r1 > seq1.msa abpoa -i seq1.msa seq2.fa > cons.fa ``` ### To generate a plot of the alignment graph ``` abpoa seq.fa -g poa.png > cons.fa ``` See [Plot of alignment graph](#plot) for more details about the plot file. ## Input abPOA works with FASTA, FASTQ, gzip'd FASTA(.fa.gz) and gzip'd FASTQ(.fq.gz) formats. The input file is expected to contains multiple sequences which will be processed sequentially to perform the iterative sequence-to-graph (partial order) alignment. abPOA can also take a list of filenames as input with option `-l`, where each line is the path to one file containing multiple sequences. Each sequence file is then individually aligned by abPOA to generate a consensus sequence. ## Output ### Consensus sequence By default, abPOA only outputs the consensus sequence generated from the final alignment graph. It is in FASTA format with the name field set as "Consensus_sequence". For example: ``` >Consensus_sequence ACGTGTACACGTTGAC ``` For diploid input sequences, you may want to generate two or more consensus sequences, simply set `-d/--max-num-cons` as a desired value: ``` abpoa heter.fa -d2 ``` and this gives you two consensus sequences: ``` >Consensus_sequence_1 CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >Consensus_sequence_2 CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC ``` ### Row-column multiple sequence alignment abPOA can also output the row-column multiple sequence alignment (RC-MSA) of all the aligned sequences in FASTA format. For example: ``` >1 ACGTGTACA-GTTGAC >2 A-G-GTACACGTT-AC >3 A-GTGT-CACGTTGAC >4 ACGTGTACA--TTGAC ``` The `-` in the sequence stands for alignment gap. ### Full graph information abPOA can output the final alignment graph in GFA format. Each segment line (`S` line) represents one node and each link line (`L` line) represents one edge between two nodes. The original input sequences and the generated consensus sequence are described as paths in `P` lines. abPOA outputs two graph-related numbers in the header line (`H` line): `NS` and `NL`, which denote the total number of nodes and edges in the GFA file, respectively. Please refer to the [GFA specification](https://github.com/GFA-spec/GFA-spec/blob/master/GFA1.md) for more details of the GFA format. ### Plot of alignment graph abPOA can generate a plot of the final partial order alignment graph with the help of `graphviz dot`. For example: ![pog](https://github.com/yangao07/abPOA/blob/master/pog.png) The numbers inside the nodes are the node IDs. The numbers on the edges are the edge weights. `S` and `E` are the auxiliary start and end nodes that have no sequence bases. Make sure you have `dot` installed before using abPOA to generate the plot. For Linux/Unix systems: `sudo apt-get install graphviz`. ## Algorithm description ### Adaptive banding To understand how the adaptive banding working, please refer to our [Bioinformatics paper](https://dx.doi.org/10.1093/bioinformatics/btaa963). ### Minimizer-based seeding mode As abPOA always allocates quadratic size of memory, for very long input sequences (>10 kb), memory usage will be a challenge. To solve this issue, we develop a minimizer-based seeding and partition method to split the sequence and graph with a small window. The full POA DP matrix can be split into several smaller ones and adaptive banded POA can be performed within each small window separately. In more detail, abPOA extracts all the minimizers from all the input sequences, then all the minimizer hits between each pair of two sequences can be found. For each pair of sequences, the minimizer hits are first chained together using relatively stringent criteria to make sure that no big gap exists in the chain. This usually leads to several separated local chains of minimizer hits. A second round of chaining is then performed on all the local minimizer chains to generate a global chain going through the entire sequence. With this global chain, abPOA selects a series of minimizer hits as partition anchors which has at least a distance of 500 bp (by default, -n/--min-poa-win). Within each partitioned window, abPOA performs banded partial order alignment separately and combines all the alignment results at the end. ### Minimizer-based progressive tree Instead of aligning all the sequences in the original order, abPOA can alternatively build a progressive tree to guide the alignment order. The generation of the progressive tree is also based on minimizers. For each pair of sequences, abPOA calculates their similarity score which is the Jaccard similarity of the minimizers, i.e. the number of minimizer hits divided by the total number of all minimizers from the two sequences. With all the similarity scores (minimizer-based Jaccard similarity), abPOA builds the progressive tree in the following way: 1. Pick the first two sequences that have the highest scores. The progressive tree set is initialized as these first two sequences. 2. For each remaining sequence, sum the scores between the remaining sequence and all the sequences from the current progressive tree set. Pick the one with the highest sum score, and push it to the progressive tree set. 3. Repeat step 2, until no sequence remains. Then, abPOA performs partial order alignment following the order of sequences in this progressive tree set. ### Multiple consensus sequences Since v1.4.1, abPOA supports generating multiple consensus sequences from the final alignment graph (set -d/--max-num-cons as >1). The general underlying idea is to group input sequences into multiple clusters based on the heterozygous bases in the graph, Then, one consensus sequence is separately generated for each cluster of input sequences. The minimum allele frequency for each heterozygous base is 0.25 (by default, -q/--min-freq). ## For development abPOA is not only a stand-alone tool for MSA and consensus calling, it can also work as a programming library. [example.c](example.c) shows how to use the C APIs of abPOA to take a set of sequences as input and perform MSA and consensus calling. Basically, the library file `libabpoa.a` and two header files [abpoa.h](include/abpoa.h) and [simd_instruction.h](include/simd_instruction.h) are needed to make the abPOA library work in your program. abPOA also provides Python bindings to all the primary C APIs. Refer to [python/README.md](python/README.md) for more details. ## Evaluation datasets The evaluation datasets and scripts used in [abPOA paper](https://dx.doi.org/10.1093/bioinformatics/btaa963) can be found in [abPOA-v1.0.5](https://github.com/yangao07/abPOA/releases/tag/v1.0.5). ## Contact Yan Gao gaoy1@chop.edu Yi Xing xingyi@chop.edu Yadong Wang ydwang@hit.edu.cn [github issues](https://github.com/yangao07/abPOA/issues) abPOA-1.4.1/abpoa.pc.in000066400000000000000000000002731425041320700144520ustar00rootroot00000000000000libdir=@CMAKE_INSTALL_FULL_LIBDIR@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: abPOA Description: abPOA Version: @abPOA_VERSION@ Libs: -L${libdir} -labpoa Cflags: -I${includedir} abPOA-1.4.1/example.c000066400000000000000000000175111425041320700142410ustar00rootroot00000000000000/* example.c libabpoa usage example To compile: gcc -g example.c -I ./include -L ./lib -labpoa -lz -lm -o example or: gcc -g example.c -I ./include ./lib/libabpoa.a -lz -lm -o example */ #include #include #include #include #include "include/abpoa.h" // for nt // AaCcGgTtNn ==> 0,1,2,3,4 unsigned char nt4_table[256] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 /*'-'*/, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; // 65,97=>A, 67,99=>C, 71,103=>G, 84,85,116,117=>T, else=>N const char nt256_table[256] = { 'A', 'C', 'G', 'T', 'N', '-', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', '-', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'A', 'N', 'C', 'N', 'N', 'N', 'G', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'T', 'T', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'A', 'N', 'C', 'N', 'N', 'N', 'G', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'T', 'T', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N' }; int main(void) { int i, j, n_seqs = 10; // char seqs[10][100] = { // "CGTCAATCTATCGAAGCATACGCGGGCAGAGCCGAAGACCTCGGCAATCCA", // "CCACGTCAATCTATCGAAGCATACGCGGCAGCCGAACTCGACCTCGGCAATCAC", // "CGTCAATCTATCGAAGCATACGCGGCAGAGCCCGGAAGACCTCGGCAATCAC", // "CGTCAATGCTAGTCGAAGCAGCTGCGGCAGAGCCGAAGACCTCGGCAATCAC", // "CGTCAATCTATCGAAGCATTCTACGCGGCAGAGCCGACCTCGGCAATCAC", // "CGTCAATCTAGAAGCATACGCGGCAAGAGCCGAAGACCTCGGCCAATCAC", // "CGTCAATCTATCGGTAAAGCATACGCTCTGTAGCCGAAGACCTCGGCAATCAC", // "CGTCAATCTATCTTCAAGCATACGCGGCAGAGCCGAAGACCTCGGCAATC", // "CGTCAATGGATCGAGTACGCGGCAGAGCCGAAGACCTCGGCAATCAC", // "CGTCAATCTAATCGAAGCATACGCGGCAGAGCCGTCTACCTCGGCAATCACGT" // }; char seqs[10][100] = { "CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT", "CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT" }; // initialize variables abpoa_t *ab = abpoa_init(); abpoa_para_t *abpt = abpoa_init_para(); // alignment parameters // abpt->align_mode = 0; // 0:global 1:local, 2:extension // abpt->mat_fn = strdup("HOXD70.mtx"); abpt->use_score_matrix = 1; // score matrix instead of constant match/mismatch score // abpt->match = 2; // match score // abpt->mismatch = 4; // mismatch penalty // abpt->gap_mode = ABPOA_CONVEX_GAP; // gap penalty mode // abpt->gap_open1 = 4; // gap open penalty #1 // abpt->gap_ext1 = 2; // gap extension penalty #1 // abpt->gap_open2 = 24; // gap open penalty #2 // abpt->gap_ext2 = 1; // gap extension penalty #2 // gap_penalty = min{gap_open1 + gap_len * gap_ext1, gap_open2 + gap_len * gap_ext2} // abpt->bw = 10; // extra band used in adaptive banded DP // abpt->bf = 0.01; // output options abpt->out_msa = 1; // generate Row-Column multiple sequence alignment(RC-MSA), set 0 to disable abpt->out_cons = 1; // generate consensus sequence, set 0 to disable abpt->w = 6, abpt->k = 9; abpt->min_w = 10; // minimizer-based seeding and partition abpt->progressive_poa = 1; abpt->max_n_cons = 2; // to generate 2 consensus sequences abpoa_post_set_para(abpt); // collect sequence length, trasform ACGT to 0123 int *seq_lens = (int*)malloc(sizeof(int) * n_seqs); uint8_t **bseqs = (uint8_t**)malloc(sizeof(uint8_t*) * n_seqs); int **weights = (int**)malloc(sizeof(int*) * n_seqs); for (i = 0; i < n_seqs; ++i) { seq_lens[i] = strlen(seqs[i]); bseqs[i] = (uint8_t*)malloc(sizeof(uint8_t) * seq_lens[i]); weights[i] = (int*)malloc(sizeof(int) * seq_lens[i]); for (j = 0; j < seq_lens[i]; ++j) { bseqs[i][j] = nt4_table[(int)seqs[i][j]]; if (j >= 12) weights[i][j] = 2; else weights[i][j] = 0; } } // 1. directly output to stdout fprintf(stdout, "=== output to stdout ===\n"); abpt->use_qv = 1; // perform abpoa-msa // set weights as NULL if no quality score weights are used abpoa_msa(ab, abpt, n_seqs, NULL, seq_lens, bseqs, weights, stdout); // 2. output MSA alignment and consensus sequence stored in (abpoa_cons_t *) abpoa_cons_t *abc = ab->abc; fprintf(stdout, "=== stored in variables ===\n"); fprintf(stdout, ">Multiple_sequence_alignment\n"); for (i = 0; i < abc->n_seq; ++i) { for (j = 0; j < abc->msa_len; ++j) { fprintf(stdout, "%c", nt256_table[abc->msa_base[i][j]]); } fprintf(stdout, "\n"); } for (i = 0; i < abc->n_cons; ++i) { fprintf(stdout, ">Consensus_sequence"); if (abc->n_cons > 1) { fprintf(stdout, "_%d ", i+1); for (j = 0; j < abc->clu_n_seq[i]; ++j) { // output read ids for each cluster/group fprintf(stdout, "%d", abc->clu_read_ids[i][j]); if (j != abc->clu_n_seq[i]-1) fprintf(stdout, ","); } } fprintf(stdout, "\n"); for (j = 0; j < abc->cons_len[i]; ++j) fprintf(stdout, "%c", nt256_table[abc->cons_base[i][j]]); fprintf(stdout, "\n"); } /* generate DOT partial order graph plot */ abpt->out_pog = strdup("example.png"); // dump parital order graph to file if (abpt->out_pog != NULL) abpoa_dump_pog(ab, abpt); // free seq-related variables for (i = 0; i < n_seqs; ++i) { free(bseqs[i]); free(weights[i]); } free(bseqs); free(seq_lens); free(weights); // free abpoa-related variables abpoa_free(ab); abpoa_free_para(abpt); return 0; } abPOA-1.4.1/include/000077500000000000000000000000001425041320700140605ustar00rootroot00000000000000abPOA-1.4.1/include/abpoa.h000066400000000000000000000207561425041320700153250ustar00rootroot00000000000000#ifndef ABPOA_H #define ABPOA_H #include #include "simd_instruction.h" #define ABPOA_GLOBAL_MODE 0 #define ABPOA_LOCAL_MODE 1 #define ABPOA_EXTEND_MODE 2 //#define ABPOA_SEMI_MODE 3 // gap mode #define ABPOA_LINEAR_GAP 0 #define ABPOA_AFFINE_GAP 1 #define ABPOA_CONVEX_GAP 2 #define ABPOA_EXTRA_B 10 #define ABPOA_EXTRA_F 0.01 #define ABPOA_CIGAR_STR "MIDXSH" #define ABPOA_CMATCH 0 #define ABPOA_CINS 1 #define ABPOA_CDEL 2 #define ABPOA_CDIFF 3 #define ABPOA_CSOFT_CLIP 4 #define ABPOA_CHARD_CLIP 5 #define ABPOA_SRC_NODE_ID 0 #define ABPOA_SINK_NODE_ID 1 #define ABPOA_OUT_CONS 0 #define ABPOA_OUT_MSA 1 #define ABPOA_OUT_CONS_MSA 2 #define ABPOA_OUT_GFA 3 #define ABPOA_OUT_CONS_GFA 4 #define ABPOA_OUT_CONS_FQ 5 #define ABPOA_HB 0 #define ABPOA_HC 1 // NOTE: upper boundary of in_edge_n is pow(2,30) // for MATCH/MISMATCH: node_id << 34 | query_id << 4 | op // for INSERTION: query_id << 34 | op_len << 4 | op // for DELETION: node_id << 34 | op_len << 4 | op // op_len is always equal to 1 // for CLIP query_id << 34 | op_len << 4 | op #define abpoa_cigar_t uint64_t #ifdef __cplusplus extern "C" { #endif typedef struct { int n_cigar, m_cigar; abpoa_cigar_t *graph_cigar; int node_s, node_e, query_s, query_e; // for local and extension mode int n_aln_bases, n_matched_bases; int32_t best_score; // uint8_t is_rc:1; // is_rc: best_score is from the reverse complement // now is_rc is determined based on minimizer-based seeding and chaining } abpoa_res_t; typedef struct { int m; int *mat; char *mat_fn; // score matrix int use_score_matrix; // set _mat_ based on score matrix file, then _match_/_mismatch_ is not used. int match, max_mat, mismatch, min_mis, gap_open1, gap_open2, gap_ext1, gap_ext2; int inf_min; // minimizer seeding parameter int k, w, min_w; int wb; float wf; // extra band width int zdrop, end_bonus; // from minimap2 // int simd_flag; // available SIMD instruction // alignment mode uint8_t ret_cigar:1, rev_cigar:1, out_msa:1, out_cons:1, out_gfa:1, out_fq:1, use_read_ids:1, amb_strand:1; uint8_t use_qv:1, disable_seeding:1, progressive_poa:1; char *incr_fn, *out_pog; int align_mode, gap_mode, max_n_cons; double min_freq; // for multiploid data int verbose; // to control output msg // char LogTable65536[65536]; // char bit_table16[65536]; } abpoa_para_t; typedef struct { int node_id; int in_edge_n, in_edge_m, *in_id; int out_edge_n, out_edge_m, *out_id; int *out_weight; int *read_weight, n_read, m_read; // weight of each read, valid when use_qv=1 uint64_t **read_ids; int read_ids_n; // for each edge int aligned_node_n, aligned_node_m, *aligned_node_id; // mismatch; aligned node will have same rank // int heaviest_weight, heaviest_out_id; // for consensus uint8_t base; // 0~m // ID, pos ??? } abpoa_node_t; typedef struct { abpoa_node_t *node; int node_n, node_m, index_rank_m; int *index_to_node_id; int *node_id_to_index, *node_id_to_max_pos_left, *node_id_to_max_pos_right, *node_id_to_max_remain, *node_id_to_msa_rank; uint8_t is_topological_sorted:1, is_called_cons:1, is_set_msa_rank:1; } abpoa_graph_t; typedef struct { int n_cons, n_seq, msa_len; // # cons, # of total seq, length of row-column MSA (including gaps) int *clu_n_seq; // # of reads in each read cluster/group, size: n_cons int **clu_read_ids; // read ids for each cluster/group, size: n_cons * clu_n_seq[i] int *cons_len; // length of each consensus sequence, size: n_cons int **cons_node_ids; // node id of each consensus, size: n_cons * cons_len[i] uint8_t **cons_base; // sequence base of each consensus, size: n_cons * cons_len[i] uint8_t **msa_base; // sequence base of RC-MSA, size: (n_seq + n_cons) * msa_len int **cons_cov; // coverage of each consensus base, size: n_cons * cons_len[i] int **cons_phred_score; // phred score for each consensus base, size: n_cons * cons_len[i] } abpoa_cons_t; typedef struct { int l, m; char *s; } abpoa_str_t; typedef struct { int n_seq, m_seq; abpoa_str_t *seq, *name, *comment, *qual; uint8_t *is_rc; } abpoa_seq_t; typedef struct { SIMDi *s_mem; uint64_t s_msize; // qp, DP_HE, dp_f OR qp, DP_H, dp_f : based on (qlen, num_of_value, m, node_n) int *dp_beg, *dp_end, *dp_beg_sn, *dp_end_sn, rang_m; // if band : based on (node_m) } abpoa_simd_matrix_t; typedef struct { abpoa_graph_t *abg; abpoa_seq_t *abs; abpoa_simd_matrix_t *abm; abpoa_cons_t *abc; } abpoa_t; // init for abpoa parameters abpoa_para_t *abpoa_init_para(void); void abpoa_set_mat_from_file(abpoa_para_t *abpt, char *mat_fn); void abpoa_post_set_para(abpoa_para_t *abpt); void abpoa_free_para(abpoa_para_t *abpt); // init for alignment abpoa_t *abpoa_init(void); void abpoa_free(abpoa_t *ab); // perform msa int abpoa_msa(abpoa_t *ab, abpoa_para_t *abpt, int n_seqs, char **seq_names, int *seq_lens, uint8_t **seqs, int **qual_weights, FILE *out_fp); int abpoa_msa1(abpoa_t *ab, abpoa_para_t *abpt, char *read_fn, FILE *out_fp); // clean alignment graph void abpoa_reset(abpoa_t *ab, abpoa_para_t *abpt, int qlen); // restore graph from GFA/FASTA file abpoa_t *abpoa_restore_graph(abpoa_t *ab, abpoa_para_t *abpt); // for development: // align a sequence to a graph int abpoa_align_sequence_to_graph(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int qlen, abpoa_res_t *res); // align a sequence to a graph between beg_node_id and end_node_id (both are excluded) void abpoa_subgraph_nodes(abpoa_t *ab, abpoa_para_t *abpt, int inc_beg, int inc_end, int *exc_beg, int *exc_end); int abpoa_align_sequence_to_subgraph(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int qlen, abpoa_res_t *res); // add a node to a graph // para: // base: 0123 for ACGT int abpoa_add_graph_node(abpoa_graph_t *abg, uint8_t base); // add an edge to a graph // para: // from_id/to_id: ids of from and to nodes // check_edge: set as 1 if this edge maybe alread exist and only need to update weight, set as 0 if the edge is new // add_read_id: set as 1 if read_id is used (to use row-column algorithm/generate MSA result/multiple consensus) // read_id: is of sequence // read_ids_n: size of read_id array, each one is 64-bit (1+(tot_read_n-1)/64) int abpoa_add_graph_edge(abpoa_graph_t *abg, int from_id, int to_id, int check_edge, int w, uint8_t add_read_id, uint8_t add_read_weight, int read_id, int read_ids_n, int tot_read_n); // add an alignment to a graph // para: // query: 0123 for ACGT // qlen: query length // n_cigar/abpoa_cigar: from alignment result (abpoa_res_t) // read_id: id of sequence // tot_read_n: total number of sequence int abpoa_add_graph_alignment(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int *weight, int qlen, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends); int abpoa_add_subgraph_alignment(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int *weight, int qlen, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends); void abpoa_BFS_set_node_index(abpoa_graph_t *abg, int src_id, int sink_id); void abpoa_BFS_set_node_remain(abpoa_graph_t *abg, int src_id, int sink_id); // topological sortting of graph void abpoa_topological_sort(abpoa_graph_t *abg, abpoa_para_t *abpt); // generate consensus sequence from graph // para: // out_fp: consensus sequence output in FASTA format, set as NULL to disable // cons_seq, cons_l, cons_n: store consensus sequences in variables, set cons_n as NULL to disable. // cons_seq: store consensus sequences // cons_l: store consensus sequences length // cons_n: store number of consensus sequences // Note: cons_seq and cons_l need to be freed by user. void abpoa_generate_consensus(abpoa_t *ab, abpoa_para_t *abpt); void abpoa_output_fx_consensus(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // generate column multiple sequence alignment from graph void abpoa_generate_rc_msa(abpoa_t *ab, abpoa_para_t *abpt); void abpoa_output_rc_msa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // generate graph in GFA format to _out_fp_ void abpoa_generate_gfa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // output cons/msa void abpoa_output(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // generate DOT graph plot and dump graph into PDF/PNG format file void abpoa_dump_pog(abpoa_t *ab, abpoa_para_t *abpt); #ifdef __cplusplus } #endif #endif abPOA-1.4.1/include/simd_instruction.h000066400000000000000000000764601425041320700176430ustar00rootroot00000000000000// A header file to get you set going with Intel SIMD instrinsic programming. // is inlucded for SSE2, SSE41, AVX2 and AVX512F, AVX512BW // SSE4.1: floor and blend is available) // AVX2: double speed // do not support AVX512F/AVX512BW 12/20/2021 - Yan Gao // AVX512F: quardruple speed // AVX512BW: byte and word operation #include #include #pragma once #ifndef SIMD_INSTRUCTION_H #define SIMD_INSTRUCTION_H #undef __AVX512F__ #undef __AVX512BW__ #ifndef USE_SIMDE #include #else // use SIMDE #ifdef __AVX512F__ #include "simde/simde/x86/avx512.h" #else #ifdef __AVX2__ #include "simde/simde/x86/avx2.h" #else #ifdef __SSE4_1__ #include "simde/simde/x86/sse4.1.h" #else #include "simde/simde/x86/sse2.h" #endif // end of sse41 #endif // end of AVX2 #endif // end of 512F #endif // end of USE_SIMDE #include #include #include #include #define SIMD_SSE 0x1 #define SIMD_SSE2 0x2 #define SIMD_SSE3 0x4 #define SIMD_SSSE3 0x8 #define SIMD_SSE41 0x10 #define SIMD_SSE42 0x20 #define SIMD_AVX 0x40 #define SIMD_AVX2 0x80 #define SIMD_AVX512F 0x100 #define SIMD_AVX512BW 0x200 // #define SIMDFree(x) _mm_free(x) // posix_memalign and free #define SIMDFree(x) free(x) // Shift, Blend, ... for 8/16 and 32/64 #ifdef __AVX512BW__ // start of AVX512BW typedef __m512 SIMDf; typedef __m512i SIMDi; #define SIMDStore(x,y) _mm512_store_ps(x,y) #define SIMDStorei(x,y) _mm512_store_si512(x,y) #define SIMDLoad(x) _mm512_load_ps(x) #define SIMDLoadi(x) _mm512_load_si512(x) #define SIMDZero _mm512_setzero_si512() #define SIMDSetZero() _mm512_setzero_ps() #define SIMDSetZeroi() _mm512_setzero_si512() #define SIMDSetOne(x) _mm512_set1_ps(x) #define SIMDSetOnei8(x) _mm512_set1_epi8(x) #define SIMDSetOnei16(x) _mm512_set1_epi16(x) #define SIMDSetOnei32(x) _mm512_set1_epi32(x) #define SIMDSetOnei64(x) _mm512_set1_epi64(x) #define SIMDAdd(x,y) _mm512_add_ps(x,y) #define SIMDAddi8(x,y) _mm512_add_epi8(x,y) #define SIMDAddi16(x,y) _mm512_add_epi16(x,y) #define SIMDAddi32(x,y) _mm512_add_epi32(x,y) #define SIMDAddi64(x,y) _mm512_add_epi64(x,y) #define SIMDSub(x,y) _mm512_sub_ps(x,y) #define SIMDSubi8(x,y) _mm512_sub_epi8(x,y) #define SIMDSubi16(x,y) _mm512_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm512_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm512_sub_epi64(x,y) #define SIMDMul(x,y) _mm512_mul_ps(x,y) #define SIMDMuli32(x,y) _mm512_mul_epi32(x,y) #define SIMDAnd(x,y) _mm512_and_ps(x,y) #define SIMDAndi(x,y) _mm512_and_si512(x,y) #define SIMDAndNot(x,y) _mm512_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm512_andnot_si512(x,y) #define SIMDOr(x,y) _mm512_or_ps(x,y) #define SIMDOri(x,y) _mm512_or_si512(x,y) #define SIMDShiftLeft(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(x, _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), (16-(n))) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), (32-(n))) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), (48-(n))) : \ _mm512_bslli_epi128(_mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), ((n)-48)))) /* static inline SIMDi SIMDShiftLeft(SIMDi x, const int n) { // x=a|b|c|d SIMDi tmp1,tmp2; if (n < 16) { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)); // tmp1=0|0|c|d tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(2,1,0,2)); // tmp2=b|c|d|0 return _mm512_alignr_epi8(x, tmp2, 16 - n); } else if (n < 32) { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)); // tmp1=0|0|c|d tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(2,1,0,2)); // tmp2=b|c|d|0 tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)); // tmp1=c|d|0|0 return _mm512_alignr_epi8(tmp2, tmp1, 32 - n); } else if (n < 48) { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)); // tmp1=c|d|0|0 tmp2 = _mm512_shuffle_i64x2(SIMDZero, tmp1, _MM_SHUFFLE(2,0,0,0)); // tmp2=d|0|0|0 return _mm512_alignr_epi8(tmp1, tmp2, 48 - n); } else { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)); // tmp1=c|d|0|0 tmp2 = _mm512_shuffle_i64x2(SIMDZero, tmp1, _MM_SHUFFLE(2,0,0,0)); // tmp2=d|0|0|0 return _mm512_bslli_epi128(tmp2, n - 48); } }*/ #define SIMDShiftRight(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2( _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), x, (n)) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), ((n)-16)) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), ((n)-32)) : \ _mm512_bsrli_epi128(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), ((n)-48)))) /* static inline SIMDi SIMDShiftRight(SIMDi x, int n) { // x=a|b|c|d SIMDi tmp1, tmp2; if (n < 16) { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)); // tmp1=a|b|0|0 tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(0,3,2,1)); // tmp2=0|a|b|c return _mm512_alignr_epi8(tmp2, x, n); } else if (n < 32) { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)); // tmp1=a|b|0|0 tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(0,3,2,1)); // tmp2=0|a|b|c tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)); // tmp1=0|0|a|b return _mm512_alignr_epi8(tmp1, tmp2, n-16); } else if (n < 48) { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)); // tmp1=0|0|a|b tmp2 = _mm512_shuffle_i64x2(tmp1, SIMDZero, _MM_SHUFFLE(0,0,2,1)); // tmp2=0|0|0|a return _mm512_alignr_epi8(tmp2, tmp1, n-32); } else { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)); // tmp1=0|0|a|b tmp2 = _mm512_shuffle_i64x2(tmp1, SIMDZero, _MM_SHUFFLE(0,0,2,1)); // tmp2=0|0|0|a return _mm512_bsrli_epi128(tmp2, n - 48); } }*/ #define SIMDShiftLeftOnei16(x,y) _mm512_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm512_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm512_slli_epi64(x,y) #define SIMDShiftRightOnei16(x,y) _mm512_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm512_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm512_srli_epi64(x,y) #define SIMDEqualM(x,y) _mm512_cmpeq_ps_mask(x,y) #define SIMDEquali8M(x,y) _mm512_cmpeq_epi8_mask(x,y) #define SIMDEquali16M(x,y) _mm512_cmpeq_epi16_mask(x,y) #define SIMDEquali32M(x,y) _mm512_cmpeq_epi32_mask(x,y) #define SIMDEquali64M(x,y) _mm512_cmpeq_epi64_mask(x,y) #define SIMDNotEqualM(x,y) _mm512_cmpneq_ps_mask(x,y) #define SIMDNotEquali8M(x,y) _mm512_cmpneq_epi8_mask(x,y) #define SIMDNotEquali16M(x,y) _mm512_cmpneq_epi16_mask(x,y) #define SIMDNotEquali32M(x,y) _mm512_cmpneq_epi32_mask(x,y) #define SIMDNotEquali64M(x,y) _mm512_cmpneq_epi64_mask(x,y) #define SIMDGreaterThani8M(x,y) _mm512_cmpgt_epi8_mask(x,y) #define SIMDGreaterThani16M(x,y) _mm512_cmpgt_epi16_mask(x,y) #define SIMDGreaterThani32M(x,y) _mm512_cmpgt_epi32_mask(x,y) #define SIMDGreaterThani64M(x,y) _mm512_cmpgt_epi64_mask(x,y) #define SIMDGreaterThanOrEquali8M(x,y) _mm512_cmpge_epi8_mask(x,y) #define SIMDGreaterThanOrEquali16M(x,y) _mm512_cmpge_epi16_mask(x,y) #define SIMDGreaterThanOrEquali32M(x,y) _mm512_cmpge_epi32_mask(x,y) #define SIMDGreaterThanOrEquali64M(x,y) _mm512_cmpge_epi64_mask(x,y) #define SIMDLessThanM(x,y) _mm512_cmplt_ps_mask(x,y) #define SIMDLessThani8M(x,y) _mm512_cmplt_epi8_mask(x,y) #define SIMDLessThani16M(x,y) _mm512_cmplt_epi16_mask(x,y) #define SIMDLessThani32M(x,y) _mm512_cmplt_epi32_mask(x,y) #define SIMDLessThani64M(x,y) _mm512_cmplt_epi64_mask(x,y) #define SIMDLessThanOrEqualM(x,y) _mm512_cmple_ps_mask(x,y) #define SIMDLessThanOrEquali8M(x,y) _mm512_cmple_epi8_mask(x,y) #define SIMDLessThanOrEquali16M(x,y) _mm512_cmple_epi16_mask(x,y) #define SIMDLessThanOrEquali32M(x,y) _mm512_cmple_epi32_mask(x,y) #define SIMDLessThanOrEquali64M(x,y) _mm512_cmple_epi64_mask(x,y) #define SIMDMax(x,y) _mm512_max_ps(x,y) #define SIMDMaxi8(x,y) _mm512_max_epi8(x,y) #define SIMDMaxi16(x,y) _mm512_max_epi16(x,y) #define SIMDMaxi32(x,y) _mm512_max_epi32(x,y) #define SIMDMaxi64(x,y) _mm512_max_epi64(x,y) #define SIMDMin(x,y) _mm512_min_ps(x,y) #define SIMDMini8(x,y) _mm512_min_epi8(x,y) #define SIMDMini16(x,y) _mm512_min_epi16(x,y) #define SIMDMini32(x,y) _mm512_min_epi32(x,y) #define SIMDMini64(x,y) _mm512_min_epi64(x,y) #define SIMDBlend(x,y,z) _mm512_mask_blend_ps(z, x, y) #define SIMDBlendi8(x,y,z) _mm512_mask_blend_epi8(z, x, y) #define SIMDBlendi16(x,y,z) _mm512_mask_blend_epi16(z, x, y) #define SIMDBlendi32(x,y,z) _mm512_mask_blend_epi32(z, x, y) #define SIMDBlendi64(x,y,z) _mm512_mask_blend_epi64(z, x, y) // with AVX512BW #define Maski8 __mmask64 #define Maski16 __mmask32 #define Maski32 __mmask16 #define Maski64 __mmask8 /* x = a == b ? c : d */ #define SIMDSetIfEquali8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDEquali8M(a,b)); } #define SIMDSetIfEquali16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDEquali16M(a,b)); } #define SIMDSetIfEquali32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDEquali32M(a,b)); } #define SIMDSetIfEquali64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDEquali64M(a,b)); } /* x = a > b ? c : d */ #define SIMDSetIfGreateri8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDGreaterThani8M(a,b)); } #define SIMDSetIfGreateri16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16M(a,b)); } #define SIMDSetIfGreateri32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32M(a,b)); } #define SIMDSetIfGreateri64(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani64M(a,b)); } /* x = a < b ? c : d */ #define SIMDSetIfLessi8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDGreaterThani8M(b,a)); } #define SIMDSetIfLessi16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16M(b,a)); } #define SIMDSetIfLessi32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32M(b,a)); } #define SIMDSetIfLessi64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani64M(b,a)); } /* x = a > b ? c : d, y = a > b ? a : b */ #define SIMDGetIfGreateri8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(a,b); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } #define SIMDGetIfGreateri16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(a,b); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfGreateri32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(a,b); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfGreateri64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(a,b); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } /* x = a < b ? c : d, y = a < b ? a : b */ #define SIMDGetIfLessi8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(b,a); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } #define SIMDGetIfLessi16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(b,a); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfLessi32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(b,a); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfLessi64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(b,a); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } // end of AVX512BW #else #ifdef __AVX512F__ // start of AVX512F // XXX AVX512F has no following instructions (AVX512BW HAS), so AVX512F is not working for 8/16 bits tasks // addi8/16, subi8/16, alignri8, bslli_epi128, bslrli_epi128, // comeqi8/16, cmpneqi8/16, cmpgti8/16, cmpgei8/16, cmplti8/16, cmplei8 // maxi8/16, blendi8/i16, slli_epi16,srli_epi16 typedef __m512 SIMDf; typedef __m512i SIMDi; #define SIMDStore(x,y) _mm512_store_ps(x,y) #define SIMDStorei(x,y) _mm512_store_si512(x,y) #define SIMDLoad(x) _mm512_load_ps(x) #define SIMDLoadi(x) _mm512_load_si512(x) #define SIMDZero _mm512_setzero_si512() #define SIMDSetZero() _mm512_setzero_ps() #define SIMDSetZeroi() _mm512_setzero_si512() #define SIMDSetOne(x) _mm512_set1_ps(x) #define SIMDSetOnei8(x) _mm512_set1_epi8(x) #define SIMDSetOnei16(x) _mm512_set1_epi16(x) #define SIMDSetOnei32(x) _mm512_set1_epi32(x) #define SIMDSetOnei64(x) _mm512_set1_epi64(x) #define SIMDAdd(x,y) _mm512_add_ps(x,y) //#define SIMDAddi8(x,y) _mm512_add_epi8(x,y) //#define SIMDAddi16(x,y) _mm512_add_epi16(x,y) #define SIMDAddi32(x,y) _mm512_add_epi32(x,y) #define SIMDAddi64(x,y) _mm512_add_epi64(x,y) #define SIMDSub(x,y) _mm512_sub_ps(x,y) //#define SIMDSubi8(x,y) _mm512_sub_epi8(x,y) //#define SIMDSubi16(x,y) _mm512_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm512_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm512_sub_epi64(x,y) #define SIMDMul(x,y) _mm512_mul_ps(x,y) #define SIMDMuli32(x,y) _mm512_mul_epi32(x,y) #define SIMDAnd(x,y) _mm512_and_ps(x,y) #define SIMDAndi(x,y) _mm512_and_si512(x,y) #define SIMDAndNot(x,y) _mm512_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm512_andnot_si512(x,y) #define SIMDOr(x,y) _mm512_or_ps(x,y) #define SIMDOri(x,y) _mm512_or_si512(x,y) /*#define SIMDShiftLeft(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(x, _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), (16-(n))) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), (32-(n))) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), (48-(n))) : \ _mm512_bslli_epi128(_mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), ((n)-48)))) #define SIMDShiftRight(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2( _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), x, (n)) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), ((n)-16)) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), ((n)-32)) : \ _mm512_bsrli_epi128(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), ((n)-48))))*/ //#define SIMDShiftLeftOnei16(x,y) _mm512_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm512_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm512_slli_epi64(x,y) //#define SIMDShiftRightOnei16(x,y) _mm512_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm512_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm512_srli_epi64(x,y) #define SIMDEqualM(x,y) _mm512_cmpeq_ps_mask(x,y) //#define SIMDEquali8M(x,y) _mm512_cmpeq_epi8_mask(x,y) //#define SIMDEquali16M(x,y) _mm512_cmpeq_epi16_mask(x,y) #define SIMDEquali32M(x,y) _mm512_cmpeq_epi32_mask(x,y) #define SIMDEquali64M(x,y) _mm512_cmpeq_epi64_mask(x,y) #define SIMDNotEqualM(x,y) _mm512_cmpneq_ps_mask(x,y) //#define SIMDNotEquali8M(x,y) _mm512_cmpneq_epi8_mask(x,y) //#define SIMDNotEquali16M(x,y) _mm512_cmpneq_epi16_mask(x,y) #define SIMDNotEquali32M(x,y) _mm512_cmpneq_epi32_mask(x,y) #define SIMDNotEquali64M(x,y) _mm512_cmpneq_epi64_mask(x,y) //#define SIMDGreaterThani8M(x,y) _mm512_cmpgt_epi8_mask(x,y) //#define SIMDGreaterThani16M(x,y) _mm512_cmpgt_epi16_mask(x,y) #define SIMDGreaterThani32M(x,y) _mm512_cmpgt_epi32_mask(x,y) #define SIMDGreaterThani64M(x,y) _mm512_cmpgt_epi64_mask(x,y) //#define SIMDGreaterThanOrEquali8M(x,y) _mm512_cmpge_epi8_mask(x,y) //#define SIMDGreaterThanOrEquali16M(x,y) _mm512_cmpge_epi16_mask(x,y) #define SIMDGreaterThanOrEquali32M(x,y) _mm512_cmpge_epi32_mask(x,y) #define SIMDGreaterThanOrEquali64M(x,y) _mm512_cmpge_epi64_mask(x,y) #define SIMDLessThanM(x,y) _mm512_cmplt_ps_mask(x,y) //#define SIMDLessThani8M(x,y) _mm512_cmplt_epi8_mask(x,y) //#define SIMDLessThani16M(x,y) _mm512_cmplt_epi16_mask(x,y) #define SIMDLessThani32M(x,y) _mm512_cmplt_epi32_mask(x,y) #define SIMDLessThani64M(x,y) _mm512_cmplt_epi64_mask(x,y) #define SIMDLessThanOrEqualM(x,y) _mm512_cmple_ps_mask(x,y) //#define SIMDLessThanOrEquali8M(x,y) _mm512_cmple_epi8_mask(x,y) //#define SIMDLessThanOrEquali16M(x,y) _mm512_cmple_epi16_mask(x,y) #define SIMDLessThanOrEquali32M(x,y) _mm512_cmple_epi32_mask(x,y) #define SIMDLessThanOrEquali64M(x,y) _mm512_cmple_epi64_mask(x,y) #define SIMDMax(x,y) _mm512_max_ps(x,y) //#define SIMDMaxi8(x,y) _mm512_max_epi8(x,y) //#define SIMDMaxi16(x,y) _mm512_max_epi16(x,y) #define SIMDMaxi32(x,y) _mm512_max_epi32(x,y) #define SIMDMaxi64(x,y) _mm512_max_epi64(x,y) #define SIMDMin(x,y) _mm512_min_ps(x,y) //#define SIMDMini8(x,y) _mm512_min_epi8(x,y) //#define SIMDMini16(x,y) _mm512_min_epi16(x,y) #define SIMDMini32(x,y) _mm512_min_epi32(x,y) #define SIMDMini64(x,y) _mm512_min_epi64(x,y) #define SIMDBlend(x,y,z) _mm512_mask_blend_ps(z, x, y) //#define SIMDBlendi8(x,y,z) _mm512_mask_blend_epi8(z, x, y) //#define SIMDBlendi16(x,y,z) _mm512_mask_blend_epi16(z, x, y) #define SIMDBlendi32(x,y,z) _mm512_mask_blend_epi32(z, x, y) #define SIMDBlendi64(x,y,z) _mm512_mask_blend_epi64(z, x, y) // with AVX512F //#define Maski8 __mmask64 //#define Maski16 __mmask32 #define Maski32 __mmask16 #define Maski64 __mmask8 /* x = a == b ? c : d */ //#define SIMDSetIfEquali8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDEquali8M(a,b)); } //#define SIMDSetIfEquali16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDEquali16M(a,b)); } #define SIMDSetIfEquali32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDEquali32M(a,b)); } #define SIMDSetIfEquali64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDEquali64M(a,b)); } /* x = a > b ? c : d */ //#define SIMDSetIfGreateri8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDGreaterThani8M(a,b)); } //#define SIMDSetIfGreateri16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16M(a,b)); } #define SIMDSetIfGreateri32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32M(a,b)); } #define SIMDSetIfGreateri64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani64M(a,b)); } /* x = a < b ? c : d */ //#define SIMDSetIfLessi8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDGreaterThani8M(b,a)); } //#define SIMDSetIfLessi16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani8M(b,a)); } #define SIMDSetIfLessi32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani8M(b,a)); } #define SIMDSetIfLessi64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani8M(b,a)); } /* x = a > b ? c : d, y = a > b ? a : b */ //#define SIMDGetIfGreateri8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(a,b); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } //#define SIMDGetIfGreateri16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(a,b); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfGreateri32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(a,b); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfGreateri64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(a,b); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } /* x = a < b ? c : d, y = a < b ? a : b */ //#define SIMDGetIfLessi8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(b,a); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } //#define SIMDGetIfLessi16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(b,a); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfLessi32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(b,a); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfLessi64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(b,a); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } // end of AVX512F #else // AVX2 SSE4.1 SSE2 #ifdef __AVX2__ // start of AVX2 // m256 will be our base type typedef __m256 SIMDf; //for floats typedef __m256i SIMDi; //for integers //intrinsic functions #define SIMDStore(x,y) _mm256_store_ps(x,y) #define SIMDLoad(x) _mm256_load_ps(x) #define SIMDStorei(x,y) _mm256_store_si256(x,y) #define SIMDLoadi(x) _mm256_load_si256(x) #define SIMDSet(x,y,z,w,a,b,c,d) _mm256_set_ps(x,y,z,w,a,b,c,d) #define SIMDSeti8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32) __mm256_set_epi8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32) #define SIMDSeti16(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) __mm256_set_epi16(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) #define SIMDSeti32(x1,x2,x3,x4,x5,x6,x7,x8) __mm256_set_epi32(x1,x2,x3,x4,x5,x6,x7,x8) #define SIMDSeti64(x1,x2,x3,x4) __mm256_set_epi64x(x1,x2,x3,x4) #define SIMDSeti128(x,y) __mm256_set_m128(x,y) #define SIMDSetZero() _mm256_setzero_ps() #define SIMDSetZeroi() _mm256_setzero_si256() #define SIMDSetOne(x) _mm256_set1_ps(x) #define SIMDSetOnei8(x) _mm256_set1_epi8(x) #define SIMDSetOnei16(x) _mm256_set1_epi16(x) #define SIMDSetOnei32(x) _mm256_set1_epi32(x) #define SIMDSetOnei64(x) _mm256_set1_epi64x(x) #define SIMDAdd(x,y) _mm256_add_ps(x,y) #define SIMDAddi8(x,y) _mm256_add_epi8(x,y) #define SIMDAddi16(x,y) _mm256_add_epi16(x,y) #define SIMDAddi32(x,y) _mm256_add_epi32(x,y) #define SIMDAddi64(x,y) _mm256_add_epi64(x,y) #define SIMDSub(x,y) _mm256_sub_ps(x,y) #define SIMDSubi8(x,y) _mm256_sub_epi8(x,y) #define SIMDSubi16(x,y) _mm256_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm256_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm256_sub_epi64(x,y) #define SIMDMul(x,y) _mm256_mul_ps(x,y) #define SIMDMuli(x,y) _mm256_mul_epi32(x,y) #define SIMDAnd(x,y) _mm256_and_ps(x,y) #define SIMDAndi(x,y) _mm256_and_si256(x,y) #define SIMDAndNot(x,y) _mm256_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm256_andnot_si256(x,y) #define SIMDOr(x,y) _mm256_or_ps(x,y) #define SIMDOri(x,y) _mm256_or_si256(x,y) #define SIMDShiftLeft(a, n) (n) < 16 ? \ _mm256_alignr_epi8(a, _mm256_permute2x128_si256(a, a, _MM_SHUFFLE(0, 0, 2, 0)), (16-(n))) : \ _mm256_slli_si256(_mm256_permute2x128_si256(a, a, _MM_SHUFFLE(0, 0, 2, 0)), ((n)-16)) #define SIMDShiftRight(a, n) (n) < 16 ? \ _mm256_alignr_epi8(a, _mm256_permute2x128_si256(a, a, _MM_SHUFFLE(2, 0, 0, 1)), (n)) : \ _mm256_srli_si256(_mm256_permute2x128_si256(a, a, _MM_SHUFFLE(2, 0, 0, 1)), ((n)-16)) #define SIMDShiftLeftOnei16(x,y) _mm256_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm256_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm256_slli_epi64(x,y) #define SIMDShiftRightOnei16(x,y) _mm256_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm256_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm256_srli_epi64(x,y) #define SIMDEqual(x,y) _mm256_cmp_ps(x,y,_CMP_EQ_OQ) #define SIMDEquali16(x,y) _mm256_cmpeq_epi16(x,y) #define SIMDEquali8(x,y) _mm256_cmpeq_epi8(x,y) #define SIMDEquali32(x,y) _mm256_cmpeq_epi32(x,y) #define SIMDEquali64(x,y) _mm256_cmpeq_epi64(x,y) #define SIMDGreaterThan(x,y) _mm256_cmp_ps(x,y,_CMP_GT_OQ) #define SIMDGreaterThani16(x,y) _mm256_cmpgt_epi16(x,y) #define SIMDGreaterThani8(x,y) _mm256_cmpgt_epi8(x,y) #define SIMDGreaterThani32(x,y) _mm256_cmpgt_epi32(x,y) #define SIMDGreaterThani64(x,y) _mm256_cmpgt_epi64(x,y) #define SIMDFloor(x) _mm256_floor_ps(x) #define SIMDMax(x,y) _mm256_max_ps(x,y) #define SIMDMaxi8(x,y) _mm256_max_epi8(x,y) #define SIMDMaxi16(x,y) _mm256_max_epi16(x,y) #define SIMDMaxi32(x,y) _mm256_max_epi32(x,y) #define SIMDMaxi64(x,y) _mm256_max_epi64(x,y) #define SIMDMin(x,y) _mm256_min_ps(x,y) #define SIMDMini8(x,y) _mm256_min_epi8(x,y) #define SIMDMini16(x,y) _mm256_min_epi16(x,y) #define SIMDMini32(x,y) _mm256_min_epi32(x,y) #define SIMDBlendV(x,y,z) _mm256_blendv_ps(x,y,z) #define SIMDBlendVi8(x,y,z) _mm256_blendv_epi8(x,y,z) // end of AVX2 only #else // SSE4.1 SSE2 // start of SSE4.1 and SSE2 // m128 will be our base type typedef __m128 SIMDf; //for floats typedef __m128i SIMDi; //for integers #define SIMDStore(x,y) _mm_store_ps(x,y) #define SIMDLoad(x) _mm_load_ps(x) #define SIMDStorei(x,y) _mm_store_si128(x,y) #define SIMDLoadi(x) _mm_load_si128(x) #define SIMDSeti8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) __mm_set_epi8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) #define SIMDSeti16(x1,x2,x3,x4,x5,x6,x7,x8) __mm_set_epi16(x1,x2,x3,x4,x5,x6,x7,x8) #define SIMDSeti32(x1,x2,x3,x4) __mm_set_epi32(x1,x2,x3,x4) #define SIMDSeti64(x,y) __mm_set_epi64(x,y) #define SIMDSetOne(x) _mm_set1_ps(x) #define SIMDSetZero() _mm_setzero_ps() #define SIMDSetOnei8(x) _mm_set1_epi8(x) #define SIMDSetOnei16(x) _mm_set1_epi16(x) #define SIMDSetOnei32(x) _mm_set1_epi32(x) #define SIMDSetOnei64(x) _mm_set1_epi64(x) #define SIMDSetZeroi() _mm_setzero_si128() #define SIMDAdd(x,y) _mm_add_ps(x,y) #define SIMDAddi8(x,y) _mm_add_epi8(x,y) #define SIMDAddi16(x,y) _mm_add_epi16(x,y) #define SIMDAddi32(x,y) _mm_add_epi32(x,y) #define SIMDAddi64(x,y) _mm_add_epi64(x,y) #define SIMDSub(x,y) _mm_sub_ps(x,y) #define SIMDSubi8(x,y) _mm_sub_epi8(x,y) #define SIMDSubi16(x,y) _mm_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm_sub_epi64(x,y) #define SIMDMul(x,y) _mm_mul_ps(x,y) #define SIMDMuli(x,y) _mm_mul_epi32(x,y) #define SIMDAnd(x,y) _mm_and_ps(x,y) #define SIMDAndi(x,y) _mm_and_si128(x,y) #define SIMDAndNot(x,y) _mm_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm_andnot_si128(x,y) #define SIMDOr(x,y) _mm_or_ps(x,y) #define SIMDOri(x,y) _mm_or_si128(x,y) #define SIMDShiftLeft(x,y) _mm_slli_si128(x,y) // shift whole x by y bits #define SIMDShiftRight(x,y) _mm_srli_si128(x,y) #define SIMDShiftLeftOnei16(x,y) _mm_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm_slli_epi64(x,y) #define SIMDShiftRightOnei16(x,y) _mm_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm_srli_epi64(x,y) #define SIMDEqual(x,y) _mm_cmpeq_ps(x,y) #define SIMDEquali8(x,y) _mm_cmpeq_epi8(x,y) #define SIMDEquali16(x,y) _mm_cmpeq_epi16(x,y) #define SIMDEquali32(x,y) _mm_cmpeq_epi32(x,y) #define SIMDGreaterThan(x,y) _mm_cmpgt_ps(x,y) #define SIMDGreaterThani8(x,y) _mm_cmpgt_epi8(x,y) #define SIMDGreaterThani16(x,y) _mm_cmpgt_epi16(x,y) #define SIMDGreaterThani32(x,y) _mm_cmpgt_epi32(x,y) #define SIMDLessThan(x,y) _mm_cmplt_ps(x,y) #define SIMDLessThani8(x,y) _mm_cmplt_epi8(x,y) #define SIMDLessThani16(x,y) _mm_cmplt_epi16(x,y) #define SIMDLessThani32(x,y) _mm_cmplt_epi32(x,y) #define SIMDMax(x,y) _mm_max_ps(x,y) #define SIMDMaxi16(x,y) _mm_max_epi16(x,y) #define SIMDMin(x,y) _mm_min_ps(x,y) #define SIMDMini16(x,y) _mm_min_epi16(x,y) #define Maski16 __mmask8 #define Maski32 __mmask8 #ifdef __SSE4_1__ // start of SSE4.1 only #define SIMDBlendV(x,y,z) _mm_blendv_ps(x,y,z) // z is __mask #define SIMDBlendVi8(x,y,z) _mm_blendv_epi8(x,y,z) #define SIMDEquali64(x,y) _mm_cmpeq_epi64(x,y) #define SIMDFloor(x) _mm_floor_ps(x) #define SIMDMaxi8(x,y) _mm_max_epi8(x,y) #define SIMDMini8(x,y) _mm_min_epi8(x,y) #define SIMDMaxi32(x,y) _mm_max_epi32(x,y) #define SIMDMini32(x,y) _mm_min_epi32(x,y) // end of SSE4.1 only #else // SSE2 // start of SSE2 only #define SIMDBlendV(x,y,z) SIMDOr(SIMDAndNot(z,x), SIMDAnd(z,y)) //if we don't have sse4 #define SIMDBlendVi8(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) //if we don't have sse4 #define SIMDMaxi8(x,y) SIMDBlendVi8(y, x, SIMDGreaterThani8(x,y)) #define SIMDMini8(x,y) SIMDBlendVi8(x, y, SIMDGreaterThani8(x,y)) #define SIMDMaxi32(x,y) SIMDBlendi32(y, x, SIMDGreaterThani32(x,y)) #define SIMDMini32(x,y) SIMDBlendi32(x, y, SIMDGreaterThani32((x,y)) // end of SSE2 only // end of SSE4.1 and SSE2 #endif // SSE4.1 #endif // AVX2 // start of no AVX512F (AVX2/SSE4.1/SSE2) #define SIMDBlendi16(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) #define SIMDBlendi32(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) #define SIMDBlendi64(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) /* x = a == b ? c : d */ #define SIMDSetIfEquali8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDEquali8(a,b)); } #define SIMDSetIfEquali16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDEquali16(a,b)); } #define SIMDSetIfEquali32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDEquali32(a,b)); } #define SIMDSetIfEquali64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDEquali64(a,b)); } /* x = a > b ? c : d */ #define SIMDSetIfGreateri8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDGreaterThani8(a,b)); } #define SIMDSetIfGreateri16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16(a,b)); } #define SIMDSetIfGreateri32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32(a,b)); } #define SIMDSetIfGreateri64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani64(a,b)); } /* x = a < b ? c : d */ #define SIMDSetIfLessi8(x,a,b,c,d) { x = BlendVi8(d, c, SIMDGreaterThani8(b,a)); } #define SIMDSetIfLessi16(x,a,b,c,d) { x = Blendi16(d, c, SIMDGreaterThani16(b,a)); } #define SIMDSetIfLessi32(x,a,b,c,d) { x = Blendi32(d, c, SIMDGreaterThani32(b,a)); } #define SIMDSetIfLessi64(x,a,b,c,d) { x = Blendi64(d, c, SIMDGreaterThani64(b,a)); } /* x = a > b ? c : d, y = a > b ? a : b */ #define SIMDGetIfGreateri8(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani8(a,b); x = SIMDBlendVi8(d, c, cmp); y = SIMDBlendVi8(b, a, cmp); } #define SIMDGetIfGreateri16(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani16(a,b); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfGreateri32(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani32(a,b); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfGreateri64(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani64(a,b); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } /* x = a < b ? c : d, y = a < b ? a : b */ #define SIMDGetIfLessi8(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani8(b,a); x = SIMDBlendVi8(d, c, cmp); y = SIMDBlendVi8(b, a, cmp); } #define SIMDGetIfLessi16(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani16(b,a); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfLessi32(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani32(b,a); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfLessi64(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani64(b,a); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } // end of no AVX512F (AVX2/SSE4.1/SSE2) #endif // AVX512F #endif // AVX512BW #ifdef __cplusplus extern "C" { #endif // int simd_check(void); /* static void *SIMDMalloc(size_t size, size_t align) { void *ret = (void*)_mm_malloc(size, align); if (ret == NULL) { fprintf(stderr, "[%s] mm_Malloc fail!\nSize: %ld\n", __func__, size); exit(1); } else return ret; }*/ // use posix_memalign static void *SIMDMalloc(size_t size, size_t align) { void *ret; int res; res = posix_memalign(&ret, align, size); if (res != 0) { char error[10]; if (res == EINVAL) strcpy(error, "EINVAR"); else if (res == ENOMEM) strcpy(error, "ENOMEM"); else strcpy(error, "Unknown"); fprintf(stderr, "[%s] posix_memalign fail!\nSize: %ld, Error: %s\n", __func__, size, error); exit(1); } else return ret; } #ifdef __cplusplus } #endif #endif // SIMD_INSTRUCTION_H abPOA-1.4.1/include/simde/000077500000000000000000000000001425041320700151615ustar00rootroot00000000000000abPOA-1.4.1/pog.png000066400000000000000000002533061425041320700137410ustar00rootroot00000000000000‰PNG  IHDR Ýï”Ù‰8bKGDÿÿÿ ½§“ IDATxœìÝy|T…½þñÏÌ$°+¸â‚ˆ‚Š¢ÕªU¨Öªè­k­ZëϵV­^—[‹Òk[±µ·û½êµ½¢èTzEëj]ÜP°.€la‘E6²N2Ûùý1Q#‚HæÌ$Ÿ÷«yyNrΜ'K™dÎs¾'A€$I’$I’$I’$I’$I’¤ÍІ@’$I’$I’$I’$I’$IÊw–n%I’$I’$I’$I’$I’¤-°t+I’$I’$I’$I’$I’$mAQØ$I’$I’$I’ Bu5¬]›ýommömÃèÔ :w†®]¡Gìò®»†V’$I’$I-ÌÒ­$I’$I’$IRSñ8¼ý6|ð”•ÁìÙ0oTT4ÿ1JK¡o_88:† ]vi½Ü’$I’$IjU‘ ‚°CH’$I’$I’$…&“É–l'M‚—_†©S!™„’‚ìò¶ŠF¡¸‰ìz¿~pÊ)p ٷnÝZæs$I’$IR«³t+I’$I’$I’Ú§¹sá±Ç`Ô(X±"[ŽM¥²EÛÖÔ¡C¶„[\ '—\g•]—$I’$IRÞ²t+I’$I’$I’Ú†xè!øë_aæÌì4ÛO§Ð†¡¨([ôíÙ.¿n¸z÷/$I’$I’6ËÒ­$I’$I’$Ijûjká¾ûà·¿…òòì4Û|;Eòé¤ÛK/…ááoßPãH’$I’$é‹,ÝJ’$I’$I’¤¶«¡î¼~÷;¨©ÉN•ÍwÅÅNÃ÷¿Ÿ- ïµW؉$I’$I’„¥[I’$I’$I’ÔV½ü2\u,[–-±šâbˆÅ`ÄøÉO>Ÿ„+I’$I’¤PXº•$I’$I’$Im˪U0l<úh¶´Zˆ…Û¦ŠŠ`=`äH8ùä°ÓH’$I’$µ[–n%I’$I’$IRÛ1~<\tÄãL†¦åÄbÉÀu×Áÿ%%a'’$I’$Ijw,ÝJ’$I’$I’¤Â×Ð7ß ÿý߉d ªmQ,‡O> }ú„F’$I’$©]±t+I’$I’$I’ ÛâÅðÝï¬YJ…¦õAÇŽðÈ#pæ™a§‘$I’$Ij7¢a$I’$I’$IÚfÓ§Ã׿³g·Â-d?Ϻ:8ûl¸çž°ÓH’$I’$µ–n%I’$I’$IRazí5<*+!™ ;Mne2pÝu0|xvY’$I’$I­*¾ #I’$I’$I’ Ì“OÂ÷¿Ÿ-Ÿ¦Óa§ W$—_#GB,vI’$I’¤6ËÒ­$I’$I’$I*,Ï> gõù´We˶—]÷Ý—-áJ’$I’$©ÅEà I’$I’$I’ÔlÓ¦Á¹çZ¸ÝX: <¿üeØI$I’$I’Ú,'ÝJ’$I’$I’¤Â0{6s ÔÖfK¦!(&EÀZ èôކBIü×Á7†utI’$I’¤6ËÒ­$I’$I’$IÊkÖÀ!‡@y9¤R9?üBàÀc@sO¬ìü¸èßJ¹6)§Ÿ†3ÎÈåQ%I’$I’ÚüöÞ;—G–$I’$IjÓ,ÝJ’$I’$I’¤üvÇpÛmÙòmŽ=œd€R`,pÖöY œ¼Õ¸žóÒ-@q1 S§BII®.I’$I’Ô&Yº•$I’$I’$Iùëõ×á[ß ¥p; 8¨m\8µ™ûV‡‹ ©t PT×_þsG—$I’$Ijs,ÝJR¾Ù°æÏ‡yó ¬ æÎ…¥K¡¦jk¡²êë³ÛvìÝ»CçÎÙÛÅõé@ÿþÙÿî¿?ôèê§#I›²ªjKׯbéúU,®XÉ’Š•¬­Ù@]2N}²úT‚ºD=%EÅt,*¦cQºuìBç’Rúì¸;}vØ-ûßwcÏ»R+ ûS’$)|ñ¨­‡Úxö­& H¥!É•RiˆF ƒ¢(DzeœÎ¥Ÿ¿uéJ³ÛI’¶ÚZØo?X³ÒéœþxàµÆåK€‡¶rÿG€‹±t ‰À¤Ipüña%$I’$Ij3,ÝJRØÊËáÕWaòdxñEX¸0ûþh4{ ¸d²ùS<6µO¿~0t(œpwôìÙ*Ÿ†$}•Å+ùç²ÙL[6‹iËfQ]ŸT‰DH¦S›Ü/6®ûÄ¢Q¢‘(Ét(Š1p·}ùÆÞsä^9x·~–p%IíCM**a]%”WB²ñù4É>f¶â%Ÿ÷‰D¡GgèÕzv‡]-áJ’Â1|xvJkjÓ7¶¦×㚬OmåcÄ^@)!–nc1èÛfÏξv(I’$I’¤mféV’°z5üíoðÐCðá‡Ù÷e˲­¡¤äóÇ>ä¸ôR¸àØe—Ö9ž$s>ùˆ ³_çóÞb}]±hŒ€€L+Ü4X4F*¦¤¨˜cö>„3Ͼ‡YÀ•$µ-•5°b-¬\‰döI {¥JK‹}üL-Üöê{ì;ï`W’”óçÃA…R¸¸Ù¸¼?PJŠ‹ÁüÜ|sØI$I’$I’ š¥[IÊ•Dž|2[´8±ñäu¦ùSl[J$’ˆpÒIpÙepöÙÙb®$m§uµ?ûužž5™¥«(ŽmvŠmkŠEcd2i:•”rÚC8ã o2p·~9Ï!IR‹hHÀòµ°| ÔÆ³ShƒÿŸŸÅ ÷NÐ{gèÑ%÷9$IíÇqÇÁÛo·Þ…ê[°/ðQãò…À#¡¤hA:À¼yЧOØI$I’$I’ –¥[Ijm 0z4Üv|òI¶ðšN‡*«¨([úÝa6 ®»:u ;•¤´ªj£ß}–'fL$“ Hyòï|Vü=x·~üèè³9nßÃÃŽ$IRóÄ`ñJX¶:;É6Ÿ^‰6NÀíÑúí;ïv"IR[óÌ3pÖY¡¾hziɯŸ‡”¥ÅÃùçÃÇD’$I’$©`Yº•¤ÖR] ù üñPY™?EÛ͉Š{w¸å¸æèâÄ*I[¶¤b%÷O}ŠççN!‰’Ê„sËÏæˆF£d2Øe~|Ì÷,ßJ’òWm.‡•ë>¿CF¾ŠD²eàn]`Ë·’¤ôõ¯Ã„öšÚ`Ÿ&ëw×…’¤…E£PVý¼Œ$I’$IÒ¶°t+I­a¸òJX»6ÿ˶+*‚=àφ‹/;¤ øaHYZTq1\~9Ü{oØI$I’$I’ ’¥[IjIeeðãë¯æÿDª¯f³ë[Ùà÷ß?ìD’òÈËó§ñÛW¤2^SPeÛEcÀ¥_?+>›ŽÅÂŽ$IjÏV—Ãì ‘*¬²íÆ¢€¾½¡ß‹†H’Tˆ†iÓ ÞÝT–}𬷙I·-Þ.^ ½{‡D’$I’$©àxæC’ZB&¿ý-|0¼ùfö$y¡náóìo¾ Âï~WØ'þ%µˆŠº*~ü÷ß2lüTÔUtá²ÓyÓ™4ýs§=p#ï/Ÿv$IR{”HÂ?çÀûeÐ,üß»3AösX¼^{*ªÂN$I*4ï¼S¦„Z¸Øy£õÊPR´¢»ï;$I’$IRArÒ­$m¯5kà?€É“ »hûU¢Q8á;vÚ)ì4’BðîÇs6áNªêH¥Ã=ñÙ¢‘áÚcÏãò£Îl\—$©•UTÁeL·Í¿%"Soûï }÷Ÿ^%IÍqÍ50j$“a'a_à£Æå GBÌÒâzõ‚Õ«! ;‰$I’$IRA±t+IÛãµ×à¼ó`ýú¼8ÐªŠ‹¡[7ø¿ÿƒO ;¤ ûþ üiòÃD"n‹… &¢‘‡õîÏN¿‘^{„G’Ô–-Ys–d‹¨mý¥™°C78¬?t(;$)Ÿ%Ù ¾«òcRú5Àÿ4.÷ÚÜýQ^x† ;…$I’$IRA‰†@’ ÖÈ‘Ùé¯ååm¿p ÙÏqÆì ñ÷ßvI9ÐJpýßÿÀŸ&?L&È´ùÂ-@&˜±jçÎGåËÃŽ#Ij‹Òxw.Ì]m¿p Ùi·ë«áÍPSvIR>›0ª«ÃNñ™ š,—3·á1F½þ$ŒnL-¦¸FçU"I’$I’¤‚`éV’¶Å¯~?þqö°étØir'ÝxÛÛ+¯„;î;¤VTÝPËýš·–Î$´ý²mS©tšõñj.zôÌX9?ì8’¤¶$™‚i³aí†öQ¶m* ‘„·>Ìp%Iڔѣ! ;Åg†§6Yÿ¯­Ü?Œj\îœÓ¡ZJ2 O>™W%gI’$I’¤B`éV’¶FÀO~#F´¿“ä»í6¸öÚl WR›²®v—üm³?ùˆT¦]XÐD:“¦.ÙÀÇýš7>ú ì8’¤¶ ! SgAeMûý["²“~§Í‚5ëÃN#IÊ7‰¼ô¤Ra'ù‚;îË£—¶bß_+—‡]Z0W‹H$`òä°SH’$I’$K·’Ô\A_ wÝÕ~O’7•ÉÀ½÷Â¥—úõÚµ5ëùÁ#·²´b%évZ¸ýT&ÈJ§¸þ©?2iÁ?ÃŽ#I*dõ xk&ÔÆýÝ9 À{ó`uEØi$IùdêThh;Å—ì<p60~ ûd€ßn\? ¸©µnâbK·’$I’$I[ÉÒ­$5×O cÇ:Ùµ©L}† ;‰¤PÝPËÆÝAE݆v;ávcA7?{'ï-ŸvIR!J¦àÙÐÈ–M•ðATT…D’”/&M‚’’°SlÒ‰À³ÀŽ@p&p*ð(°ˆõÀBàAàHàçû <äåg–HÀ /„B’$I’$© D‚ ½X‘¤føÃà–[ÂN‘¿"‘ì×è§? ;‰¤mÔJð£qw0{õ" ·›D(‰•0ú·3`ç>aÇ‘$Št&[¸­¬±p») …o Ý:‡F’¶c…·ßÎë©ðK›ÉN¾ÝÒeùQàJà÷@·Vε]"Xµ vÙ%ì$’$I’$IÁÒ­$mɘ1p饡¾à_L&‹€µd§jtz“˜q0RF"xøa¸ð°HÚF™ àÆ§ÿÈ”Å3rR¸]òЛTÍ^Ñ¢yðo¿G¤(Ö¢¹±X4J·Ž]øÛÿûvëÖ«U%Ijཹ°vC^”‡6ÔÖ°ûÿ;ƒx¢®¥XõÈ:wìv¬ìß%EpÌ!PÚ!ì4’¤°$Ð¥ $“a'i–yÀ“|ñµº ÙI¸Ç}BÊ·U"xüq8眰“H’$I’$„hØ$)¯Í˜W\ÚIò…ÀÀÀ0àÅÆ÷UI`0x¸±q»¾do_W–ë°A—_~˜ë#KÚN£¦=ÅMwÂí¤3ªëëø·gþL2 ;Ž$)ß-Zž7…[€G&½H<Ñ@u¼Ž¿½örȉ$Rð~™Ó€%©=[¸°` ·½èýßId§ßÖõÀJ²EÜ_P …[€’˜3'ì’$I’$I£(ì’”·jj²B:Iþ,p!PÕ¸>´q}0° Ù«&*€ÙÀsÀƒ@5°ø p/ÙRnNe2pÆ0s&tíšë£KÚï-ŸË_ß|œ€Üÿ[·ï5ߦó>›ž;çWϪ® ïÕߢ˾;or»¹wŒ'YoµŒKeRÌ_³”;ßøÃŽ¿(gÇ•$˜Š*XðqÞnîqü×_ÏCO)ÍF‚ªj¡l)Ð'ì4’¤0”•e'®æÑsg»‘JÁ¼ya§$I’$I*Nº•¤Í¹újX¶,ûÂsŽ=œI¶p[ <¼@ö¶tû€ŽÀîÀIÀÀ|à˜œ'ÝH*Ë—g§KÊ{ëëªøéø;‰D"aG)(é ãï>Ϥ…ÿ ;Š$)%’ðAÎï;ñ•¦•Íaæâ…Ä¢Ÿ¿ ôÎü9ÌX¼0ÄT X¼>©;‰$) eeP\vŠö)†Y³ÂN!I’$I’T0,ÝJÒ¦Œ cdžr[»YÀ¥@¦qýïÀYÍØoWày²¥ÜP¥RðøãððÃa'‘´ÿþ¿PU_K&Èlyc}ÉÏŸÿ kkÖ‡C’”of,€d:¯&õÝÿÂ3üâ‚Ë()*nòþñ›Û%$‘ìׯ>vIR®••eï ¤p,XW¿»H’$I’$å3K·’´±òr¸ñÆÐÿ¯@mãò%À©[±owàW-žh]=T8¥JÊW/ÏŸÆÛ‹g’Êä~šw[ÐJò‡É£ÃŽ"IÊ'«Ëa톼* UÇëø¿×'Ò¡¸˜ëÏ8—3ŽüÙÇ™ü"ñDCˆé6d¿vs‡D’”kóç‡r·)5ŠÇaݺ°SH’$I’$K·’´±áá¶6”鯯5Yÿ·mxŒs€N-gÛAökøïÿvI›PŸlà÷“‚H$”ã÷¹t0‡üñ|:ïÓk»뀟ŸÁ!<ŸHQ¬’mT&ÍKeS™¶ÌÛpJ’€tæä_Ytì«/Q[_ÏYßø&;téÊå'öÙÇ*kk÷ƤÓmB&€UåP^vIR.­÷."¡«ª ;$I’$IRA°t+IMýóŸðÀL†rø±M–÷mÃc”’”úlŠdN ;‰¤üeÊ㬯«"òg _¡ŠF"Œxq$‰t8Ï’¤<24äßóÁý/Œø¬l;ôð#éÝs§Ï>~ß?ž %×WŠDàÃ…y51X’Ôʪ«ÃN ¿’$I’$IÍbéV’>ð£A,÷Ó?õr“寇–¢ÅbpÍ5a§ÔÄ’Š•<òÞó¤2é°£´ ™ `uu9cß!ì(’¤0ÕÆañªPî–ñU>X4Ÿ÷–±çN;sâaGD¹äÄS?Ûæ­¹2gÙ’nF@¼–¬ ;‰$)WjkÃN K·’$I’$IÍbéV’>õüó0c¤R¡¾ø¨Éú€PR´°T >øþñ°“HjtÿÔ§ˆEý°%e2˜ö4õɆ°£H’²py^¾Ârß Ù)¶—žø¢‘Ï^~ÒiD"‘/m—W`Ñ H;íV’Ú…ºº°ÈÒ­$I’$IR³äá)!I ɯ EE¡~íFëÝCIÑ b1øå/ÃN! XUµŽççN!™çâ‚¶¬¶!Îß?œv IRâ °bdòkÊm]C=c_}™H$Âe'}ç Ûw·Þ 9hÐgëOz†d2×·,•†? ;…$)òñy¨½©¯;$I’$IRA°t+I“&Á´i¡M¹¨Üh½S()ZA: ï¾ ¯½v©Ý5íbM¦Ú©åd‚€QSŸ"‘öD±$µ;‹V@4ÿž_ÿ﵉TÕÕrüÁ‡±Ï.»}éã—Ÿ|ÚgËÕU<ñæä\ÆkžL"œqÚ­$µy;†@]º„@’$I’$© Xº•$}Ê-@ÖÛÔMõŠŠàöÛÃN!µkëj7ðô‡“HfÒaGi“6Äk˜0ûõ°£H’r©!‘Äš‡¥Ðû_|±\ÛÔ¹ƒ¿E×ÒN_Ú>¯D€d Vl|_IR›Ó¹sØ ÔµkØ $I’$I’ ‚¥[Iúè£ìÖ§Üì¼ÑúÆ“o Z*¯¾ ‹‡Dj·ÆÏ~lsE­%›þrØ1$I¹´|m^>½ÎZúSçͦ{ç.œsìñ›Ü¦S‡ŽœÿÍo¶þÚ‡P¶|YŽn¥¥«ÃN IjmyVº}èCöiþøíxœ•À¯€Ãždïlµp9ðæv%l–n%I’$I’šÅÒ­$ú”[€R o“õyai-EEðÈ#a§Ú­gf½J:îÅm]@@Ùš%,®XvIR®,_™ ì_rß?žàûß<‘Ò’›Ýnã)¸£^œÐª¹¶I@U-ÔÄÃN"IjMyRøL?#[´]ºõ8ppð>PÄ…ÀÿC€«™òä{ I’$I’”ï,ÝJÒèÑÌ—·‡6Y~7´­$™„Q£²'Í%åÔìÕ±¤b%þ¿¯õG‹xvöëaÇ$åBe Ôæ_´>‘à‘É/0òOùα›};榫¾°ïèWž'‘Ê¿¾ …kÃN!IjM»îvæG¿ƒíþûyªW€£· üp;Õbzö ;$I’$IRA°t+©}{óMX²$쟹 Ér0scÙ[ßE€Ñ-©E-[Ó¦…Bjwžó:E±XØ1Ú…d&Å3³_%ã’Ôö­X ÑHØ)¾ä‰)“Y_S½Mû®­ÜÀÓo¿Ñ‰Z@ÉNöéU’Ú® ¤$´ÃÿøÙ‰´—¿ÝŽÇZ \¤b²%Û+.À ÀdàðÆíÆlÇñZD¯^Ð¥KØ)$I’$I’ ‚¥[IíÛßÿê úœÚdý¿¶rÿ80ªq¹pNK„jI%%ðøãa§ÚÌ{‹T:vŒvcmÍf­^v IRk[¹2ù×½ïÏpëù—<7e‹oÓïM$òyyø¾ž +úWkH@å¶•‰%I ÿÐ¸Ø˜<lÏÌ×_UËÿJ¶Ì»±R²EßOÝ 4lÇ1·Û€a]’$I’$© Xº•Ô¾ýãH„â îº7.^ÚŠ}¬h\NvzF^I$àÅÃN!µ+‹+V²¾®j˪ÅG‹xgéì°cH’ZSMɰS|IÙòe¼1{;väßÎ>¿Yû Ú§§yìgë“f¼Ç¢U+¾bD#P^v IRkéß’á<·‘-½Î¾µ• ;¹öSW~ŶGƒ——/lç±·Yq1 ÖÑ%I’$I’ Ž¥[Ií×Úµ0~Ø)¾dà ²/øÀÙÀø-ì“~ü¹qý(à¦Ö ¸½æÌuëÂN!µï,›E, ;F»’ ÒL]:3ì’¤ÖT^ M¦Ãæ‹û_ÌþåpÍw¾KϮݷ°õç~qÁeŸ-AÀ¨'´x¶í°nCØ)$I­¥ÿÐÝ ¸èØõ2ðé%"{[š{b“å'ZàøÛ$ õë/I’$I’Th,ÝJj¿&O;Áf< ìÔg§K€8P,$;ãçû <”ä4ñVzíµ°HíÆ´¥³È¿[_·iAÀô•e4¤òo¢$©…¬«„<{~M¤’Œyå”–tà¦ï^°Uû±ßN9üèÏÖšø<ÉTª¥#nŸ¨¨†L&ì$’¤Ö°ÇУGØ)¶Û´&ˇ7cû#š,¿ÓÂYš-‘€Ã ëè’$I’$I§(쒚ɓ³·OK$ÂN²IC÷›ÉNºx¯¾Í\”ì-ë~tkõtÛ¡¸&M‚sÎ ;‰Ôæ¼óñl2RN™{Çx’•ñ/½ÿ£{?¿Hbßk¾Mç}zå2Ö6I¦Ó|¸jGìy`ØQ$I­¡¢2o:·Ó?ZÀa×]ú…÷ízáéüï¿ÝÊ¥'þËf÷ÝP[Ãç ýÒûW¯/§äÌãøï«¿žž'¿»¬¯†žÍŸâ+I*‘|ûÛðôÓN‡f›Íj²¼w3¶ß«ÉòB²Ú—¶h¢fèÐŽ>zËÛI’$I’$ pÒ­¤öìwò¶pû©½Ç€ÙÀo€o‘}1¾èì|ø°øò¼p Ù¯ù;¡ÍîÚ•Oª+¨®¯ ;F»T1wÍ’°cH’ZC}$ól l{‰B•¿ÛHR›õíogË·ly“å]›±ýnM–3Àê–³e‘sL¶x+I’$I’¤fqÒ­¤ökÁ‚°4ÛàßßÚ„²²°HíÂ’Š•aGØ*üüŒ°#´œH„¥«ÂN!Ij 5õa'ø‚CûîGðÜ”mÚ·Gç.Û¼o8¨Í¯¯¿$©p¤ û–ê&ËÍ™XÛñ+öωâb8餜nýúõ<öØcŒ7ŽéÓ§ÇÙc=8òÈ#¹ùæ›4hPβH’¤ö%Ho ]?ŸL|éú22õsÉ4,%“©t-Aº’ “}Í!íH$Öb‰F»íЇhÇÄ:ö'Z:€XÇý‰Äz„üI’¤0Yº•Ô>­YÕ9[Ÿª®†òrèÙ3ì$R›¶¤bEÑ©LáÞš³P¥Ò)–v IRk¨g§ÂAØIÚŸ €êº°SH’ZKÿþ°óÎÙ×í TÓg©’fl¿ñ|Ùœ?Ë%Ù²sŽ 6ŒÑ£Gó§?ý‰±cÇÒ­[7Þ{ï=®¾új?üpžxâ Î:묜å‘$ImW*'Yõ*©êɤ*_$]¿°ñ#Q"Ñb‚ AfÓû¦kÒ5@önÔM'Bã>Ù÷ëØ¢îC)îvE]#Rä9OI’Ú“hØ$)óæ…@~¤V·dýJ ûÆœ…mqùа#H’ZCmŸ`CT;$©5]p”4§®šŸˆ"|q IDAT:5YN4cû†¯Ø?'vß¾þõœòòË/ç†n`×]w¥S§N 2„±cÇ’N§¹ùæ›sšE’$µ-™äjêWÿU³±áý¨]t.‰5÷5)Üd2 ›-ÜnR!ø´p ®_HbÍýÔ,üÞ߉ªY‡Ò°úN2ÉOZìó‘$IùËI·’Ú§ ƒ´ÓC‹Áüùpì±a'‘Ú´Ö-wÊmˆ6Ä«©MÄé\Òœ›ŠJ’ FMdœršDRi(Š…D’Ô.ºîº+ìÛ¬k“åæ\&Rÿû·º’¸ì2ˆæn6˨Q£6ùþAƒQZZÊ¢E‹‚€HÄ+œ$IR3 O’X÷ɪ‰@¤±P4þ/Ùz‡>¿Ì*]7“º ßDq·“(Ùé2Jv8"…{A™$IÚ<'ÝJjŸÊË¡ÈëB‹e¿’ZUE¼ +A᪪¯ ;‚$©¥5´ÞÉ5S2vIRk9üp0 ´t¹G“åÕÍØ~U“å(°kËÆùJ‹ žßk/‚ üWjkk‰Çã 8Э$Ijž †5÷Q9}oj?ú©Ê‰¤!HÑt"m5?Cªêjþ€Êv§~Õï 2u!ä‘$I­ÉÒ­¤ö©º:ìí[$â÷@ÊÚ„·_›ßIjƒRN‘ßIjÛ.»,{ÁvØdyi3¶_Öd¹³û¤D"üy§øÎUW±ÿþûs×]wQ[ÞE£?þ8·Þzkh$IRaÒÕÔ¯ú>ØøÒkÈ$WC?¯ÅßLªœúå·R5}OêWý ]v4I’ÔB,ÝJjŸjj ¦8´[A`éVÊx¢!ì힥[IjƒÒ%2K·’Ô¶]tQÁNº=ªÉò{ÍØþÝ&ËG¶p–¯²!atU .äÆo¤wïÞÜpà ,[¶l {·¬O>ù„áÇsÅWpÞyçåôØ#HI,#ŸKºö]’UI®:ûV5‘tí»¤ãsÉ$–A“Û\K’ÔÖ$7L jæþÄ—ÿœ UAäÿëA&“ª ¾üVªfìCbݘ°#I’¤à½Õ%µOÕÕ ãÖ"²_{K·R«‹'ëÃŽÐîÕ%üHR›“NCaö€ÚŽtþŸT“$m‡Ývƒþx’ɰÓl•î@%°(úÅö¯4Yþ^+æÚØ}:QSóÅIk•••Ü}÷Ýüå/áÔSOåg?ûÇsL«æ(//ç”SNáøãçÞ{ïmÕc†€t|6©ê·ÈÔÏ'ŸMº~6™ÄÊìíª›##Z²;±Ž‰•H´´?E]Ž!Vz þ+I*Téú2â‹L²úU²ÏgxŽ7H‘IWPûÑ%$Ö=DiŸ{‰uÜ?ìT’$iYº•Ô>ÕÖz¢6LétvÚ°¤VÕ.¬““mQÅgIj{2޹ “n%©íûÙÏ`Ô¨ÏV€™d'î~F~žÜè\ÜÓ¸~ðçÍlû0½qyà”Öö¹XŒ“¯¹†™+V0nÜ8’›Óé4Ï>û,Ï>û,Çs 7Þx#gŸ}6EE-û¯­­eèСxàŒ3†X,Ö¢_(2 ‘¬|™TÕ+$«&¤ÖC¤ˆnÛÔÚ M¦ác2 “¬š¤!H-Ú‘¢n'RÔíÛw?‰h‡}ZúS‘$©d¨_ù{âËok¼v$  o?dËÂÉê7I~8Ò=~EÇÝnÁ c$I*<ѰHR(:t€¨ÿ†&…ŽÃN!µyű|<Ù¾”ÄŠÃŽ Ijiž Q㉵˜ËIR[–J¥˜]]͘£æ†XŒÁ@àHààLò³pû©_Ý—ïÞßÄ6qàÚ&ë¿![ØÍ‰îÝ9tÄyä–-[Æm·ÝF¯^½6¹é[o½ÅyçÇ^{íň#¨¨¨h‘©TŠsÏ=—Þ½{3zôèvW¸ ÒH¬CÍÜã¨œÑø’kIV<™-Ü©m+Ü~é@ ÙÇ2© ’럤néµTÎèKÕ¬CiX}AjíöG’¤V$×P3ïdâË$!hCCF‚ìçÿøVjæìó±$Iȳ’Ú§.],݆)ƒ®]ÃN!µy‹,|†­sIiØ$I--Ú¾J!ù¥±ñÜΊ9’Ô–¥R)f̘Á<À5×\ÑGI—.]8p —¼ù&w§ÓLê08$ÜÈ[´0†ìÉ—ðmà~`-P L¾Evj/Àÿ.ÎU¸X n½J³«îºë®Œ1‚åË—3zôh¸ÉÝV­ZÅí·ßÎÞ{ïÍUW]Åܹs·+ÆUW]ECCãÆûÂÝ~ýú1uêÔízì|–ª~Ú…ßeÃû;S·ør’ÕoiZ’¤>+á¦ëf_vÞßÚß%UýF«_’¤æJU¿FÕ¬ƒIV¿dÂŽÓŠ2$«_£jæ$«&†F’$m…|¾ \’ZO×®QºÝŒ&’}!þ ì\ìR¾m‰Xº•r Sq)UõuaÇØfõ«6°àî— RÙÕú\>„nìrª­Ó¹Ä©Þ’ÔæE!vˆÍ{úí×9ûŽŸmóþÕOL¤Kiž_4RdéV’ Q:fîܹ¼û÷Þ{¼ûî»Ì˜1ƒx<Þ¬ýÿœÖJÙþ ÛÌÇ^㋃îÓ·ðxg®"ûúÞ•o»’ì4ÜœˆÅ`¯½àÚk¿ô¡:pñÅsñÅóæ›oòûßÿžçž{Ž øâí›kjj¸ï¾û5j'œp×_=§v‘Hóo0bÄfÏžÍĉéÐ!gó}C•¬|ú·“ª™ ‘"Rypcìà³¢obÃ럢¨Ë7èØû6Š» 9›$©=kX3’º¥×@\”º I&½š²¡tês/vúQ؉$IR3Xº•Ô>uév‚¯´‚ìmå6ññÕo“;€áÀ l|¹¥[©Õ•–tÌÞ…¹oƒ¤Ò,;õ³Âm¡êä¤[Ij{Úp᳸¨ˆâ¢x©¨ $)WÖ¯_Ïc=Ƹqã˜>}:ñxœ=ö؃#<’›o¾™AƒµÈqÆŽË;ï¼Ã»ï¾ËôéÓ©­­Ý¦Çùa$ÂO‚ðkŠ[ã<`0p0XBvbïîï¿¢ñ¿9“NèQ°…¢ëàÁƒ}:‡~8O?ýt‹ç7Þà®»îbÊ”)Û\¸=þ ƒøŸV.Üþ”lµ£9o[šrÛÔîd/”¨ê€…ÀCä¸p[\ ßÿ>œpB³wÙo¿ý¸ë®»X¹r%wÞy'{î¹ç&·›1cW]u}úôaøðá¬X±b³ùÄOluôB”I|L킳©);…t|Vã{ó°l»±ÆÂO:>‹š²¡Ô.8‡LbyÈ¡$IíC@|ÙOˆ¯A»+Ün$¾ü6ê–^ äïylI’déVR{Õ·/$“a§Ø¢R²Óloö:€¯‘-åŽl²í(à¥\ÜVÉ$ì»oØ)¤6oïv%-¼_÷ªËV³nÊ|J{ï@‡^…;ù¦cQzt*Üü’¤Íè\ [q å°Üzþ%ÏMÙâ[â™×ØmÇž\wÆ÷BNÝ ±(”@1X’ Àå—_Î 7ÜÀ®»îJ§N2dcÇŽ%NsóÍ7·È1~ó›ßгgÏmÞ¿_¿~<ñÚkŸzj¶8ª­Bi)üçnÓîÝ»wç†n`Ñ¢EŒ7Žo|ã›ÜnÍš5üþ÷¿§oß¾\|ñÅÌœ9óKÛ<ûì³Að•oG}ô6åÌ AІÕwQ5s‰ Ï5¾¯o‹Ý˜9¹aU3úe'‰p3I’Ú°€Ú.¦~õ]P`w6h>¹—Ú.¥½%IÊg…פ–пØ šå§À!_ññG6YÿßÖÓ² ä{ ²>;ìN4RX¿î¥jX>î¢ÅEìuá7ˆÄ +S{í° ‘B5,IڼΥ;I~Sƽ1‰Uå|mßþ|cÀÀ°ãlY'§ÈKRK5j#GŽüÒû Dii)‹-"hÒÃŽ;îÈüÇlÓ¾=zô`„ ÙÒî#@¯^sÚùV 3vÛm»¦¸¸˜sÏ=—·Þz‹wß}—‹.ºˆ¢¢/ß ‘HððÃ3hÐ Ìã?N:]€ÅÓ­”I,£zÎ7ˆ|A¦‚üø°%A$hXyU³"ÓðQØ‘$ImP|ÙOI¬‹“]›ÊX÷(ñ‡…D’$mFá¶$i{ôéSÓ1NÙÊmfmv«ûktØ©p§ÄF#úõÚ+ì’¤Öй4¯'¯ôìÖc<„½wÞµYÛß5~ןynkÆj‘tív IjÓjkk‰Çã 8H Mv¿âŠ+8âˆ#¶jŸ¢¢"{ì1 }ÇŽ;Âcåõsp^**‚ŸþÎ<³EöðÃg̘1ÌŸ?Ÿ[n¹…vØa“ÛM™2…óÎ;þýûs×]wQ[[Û¢9òErýSTÍ<ˆTÝ ‚Bœl»A&]?›ªY‡’\ÿtØq$ImHýª?P¿ú?±p»)êWý'õ«þvI’´ –n%µO±ì•¿e¨ÿ#{Ãcš±íŽM–Z'NËëÓÇÉ$RôÙq·‚ºùPÅÔETÍZAÃöb‡#ö ;Îv)Š±ÏŽ»‡C’ÔºtÌë»û 9hoþñøÑ)glqÛ·æ~È?çÏ¥W·î|ÿ›'æ ] èâ¤[IjM?þ8·Þzk‹=f$á´ÓNÛª}î¼óNN>ùä/¾sÈøõ¯!êif).†Ãƒßü¦Õ±Ï>ûð»ßýŽ¥K—2räÈÏKÒY´h7Þx#»ï¾;7ÜpK—.mµL9$©[z=5 Î!ÚÆtÛÍÊ$ Ò5Ô,8›ºe7¶íÏU’”‰ucˆ<<Ô ËWÃOÀ%Ãá¨óa¿¡°ó1°÷ pØÙpÞ¿Á¯þ ¯¾ ‰0Ä?¾™Dù£a\’$}_’Ô~vX›(~.o²<(´[!Ë~í%µº={ìJQôË·zÌG k«Y9a:%;v¦÷w·nR>JfRôíÙ;ì’¤ÖЩ"mãå”;ŸÎN¹½òÔ3éPw!,ÝJR+úä“O>|8W\qçw^‹<æ|À!C1bD³÷¹öÚk¹öÚk7ýÁáÃá´Ó²\µyEE°Ãð÷¿çän_]»våÊ+¯döìÙŒ?žOÜôźœt݇¹<¨$IÚ‚¶q–H’¶ÅqÇeoOZàf4Y¾$´[!ã;…Ô.ÇŠ¸Û¾aÇØ¢ aÙØ©©4{]ø b  ô³%|mÂN!Ij ÑôèvŠíöñÚ5<õökÄ¢Q®>õì°ã4ߎÝÂN ImRyy9§œr Ç<÷Þ{o‹<ÞÕW_ÍG±UÅÊ“N:‰;ï¼sóD£ðøã0xpNʤ)ƒॗ`Ï=szèh4Êé§ŸÎË/¿Ì|À•W^IÇŽ¿´]&“áÙgŸeðàÁqÄŒ3†d²p&§©õÔÌý6Éêɤ΂4ÉÊW¨™3„ µ.ì0’¤¤k¨]pAH…Û—ß‚ã.‚±Ïf ¶G „?ƒ·þ‹^†5oÁ—àùûà'—ŽݳûÅëáÉ—áœëá÷_2Ô,8ƒ ]ÛK’¤Í²t+©ý:áH¥ÂN±]¯4.lÝúB’Je¿ö’râ{Lq,¿'}òòlâË+Øå”ƒé´Wϰ㴈}vÜ;Y ’¤6«Wlù¶€ýåÙ¿“J§9û˜ãØs§ÃŽÓ<]:A‰+Ijiµµµ :”CÏyÔ/ Rd–S·äŠƒH’¤OYº•Ô~x læÅÞBqºÿr–fÛi'Øÿ°SHíÆ‘{ $™Îß“@µ‹×²fÒ\ºì· ;? ì8-¢8Zıû„{rS’ÔÊzv‡L8YZB]C=÷¿8€ëNÿ^Èiš)…z„B’ÚœT*ŹçžKïÞ½=zôvnß{ï=Ž=öX.¹äÖ®]û¥ŸvÚiÌ™3‡C9äKëÙ³'&L Gfþ[ß½;¼ò ì¶›ÅÛOE£Ù·q㲓€óÄ.»ìˆ#X¾|9£GæàƒÞäv«V­âöÛog=öàâ‹/fΜ99NºeAr Õs¿Iºa d g2o« ’¤–P=÷›É5a§‘$€ÄºÑ$Ö… ÷Ï£³Ào‡O¯ {è·pÞ©[Þ¯×ð·?þ¹½À&¤H”?NbÝÃa‘$IXº•ÔÞtlizFžú;ð ÙÈGQc-*‚¡CÃN!µ+ïÖ’¢ü<™®OòñߦQÔ©„=¿Tv"Oʤøú^…C’Ôšzt-èI·OzŠê*Ù§ßxhØqš'È@O§ÈKRK»êª«hhhøÒ„Ù~ýú1uêÔf=FEE7ÜpGyä&÷Ùo¿ýxî¹ç˜0aýúõãž{î!Òäï¿ââbžxâ úõë·uáwÛ ¦MË^ÜÝÞ‹·±Xvðøñpæ™a§Ù¤:pñÅ3sæLÞxã Î=÷ÜM–¼ëëëyøá‡9øàƒ9餓˜0aAþÅNAºšê²“É$V„RÊ[A’ ±‚겓½åµ$é+©rê–ÞÚñ‡ÿâõÙå ¾C·â¥N¥ð«Z'×Öª[z=Aª"ì’$µ{–n%åÌúõë¹÷Þ{9á„ØqÇ)--e¿ýöã /dÆŒá„:ýtH§Ã9öv˜\ܸ|ðݳl•t:û5Ï‘¼ü™S›S¦LáÚk¯eÿý÷§C‡ì¼óÎ <˜Gy$”“Dű"ŽÙûŠ¢Û>­¨µ¬xò=ëkÙãü#)îVvœS\TœÓÒm>þÜIRKÊËç¢èÕ£`‹·w( )·½8§g÷œ./î$©…1‚Ù³góÌ3ÏСC‡­Þ?“É0fÌößî¾ûn2ŸŽ kÔ©S'n»í6>üðCþå_þå³÷2„üàŸ­ÿ÷ÿ7Çü¶}»î S¦À‘Gì…õÛ­¨ºtW_…&_ç|6xð`ÆǼyó¸þúëéܹó—¶Éd2Lœ8‘3Î8ƒ8€»îº‹x<¾UÇ)++£²²rû jœE:>;´ÂíòÕðÀpÉp8ê|Øo(ì| ì}v6œ÷oð«¿Â«ï@C"·Ù‚ I&>›šùß !·—$ŒøÇÃ!S äþïé×ß…·>ø|ýšl~ÛÍz,ìú Tj‰/ÿ÷°ƒH’ÔîYº•”3Æ ãºë®ãÌ3ÏdΜ9”——óàƒ2}út?üpž~úé܇:ë,èÔ)÷ÇÝoÿÄ¿?7ÎÖ)-Íié6/æÔ¦•••1xð`æÏŸÏO~Úi§1gÎFŒ±ÉBïŸþô'ºwïÎM7ÝÄUW]µ}ŸL÷î0qb¶pmg§<Š‹a—]²:*ì4[­_¿~Üu×]¬X±‚;3½öÚk“Û•••qã7Ò§O†Ί+šõø¿øÅ/8á„X³fÍvå¬ýè2’Õ¯CÚ®ÇÙËVÁ¿þ¾ö]öG˜0,ò JCu-,] ß‚;GÃw¯ƒ¾'Á?‡ÉÓ>¿…vk ‚©š·©]ü£ÜP’TPRµÿ¤aí!]¼òäËŸ/÷ÛÚÊ,@ö×Ìo~½å2m« “¤aÍ}¤jšwW I’Ô:"£9$åÈW\A,cäÈ‘_xÿŒ38ôÐCÙo¿ý˜?~îƒ]~9<ú($>Ьzù4»ê5²…ÛàA>Ÿv[Š‹áâ‹aÔ¨œ2oæÔfÍ›7ƒ>˜5kÖ°Ã;|öþD"Áî»ïNMM •••Û4Ah[Åãq>^¾œsþz#ÕåHVÆIÇ$«ëI5.AÀžçI‡stËæ `ö/Ÿ"]¿}/°uî»ûþø„ Õ22ñ$9ÿg|sÿ#rvÌ|ü¹“¤–”ÿÎÅãqV­XÉÊ “XµöV–¯c}M5«*ÊYY‘]Îd29ËÕßñSžÿçÛ ;çüáòkÃŽÓ,jk(=vvß%gÇÌÇŸ;IjI§vÏ=÷ÜWnóöÛosôÑGá}«V­â–[nÙìÔïþýûs÷ÝwsòÉ[¾°ã•W^áøã'ÖRUÜ}7ÜtSv½ïnµU¢Q8î8øÛß²ÅÛ6 NóüóÏó»ßýŽ·Þzk³Û•””pæ™grÓM7qÔfÊÆË–-cß}÷%•JÑ¿^zé¥Í–z¿JÚû¨[r5aLå{ù-¸ò—PY]?b œ*û5Ø¥tí U50I¶tûÐSP±Ñ`ßçå2u„NûÜO‡~˜ËƒJ’òZ@Õ¬Ã'ÆçþÈN…_º2»|î)0òöPb´œHE¥Óuàûa'‘$©Ý²t+)/têôÿÙ»ïð(ª¶Ã¿™ÝM/é=ôÞ XQ,€]_Q‚"‚HDñRTBQ,À§"ŠH¤¼ ˆ€„¢ BI !¤'[çûcE²I6™-ç¾®\l™ò†=;ç<ç9~F, ’T=i®‹…ŒŒ ÒÓÓIKK###ƒ´´4Ò£¢ÈøñGR Èà\Udww£&Üþð˜¶áTØàØÔ©<öÖ[è`¹¿š¸æár={öäСCdgg\µ¥‘ÏŸ?Ï™3gÈÈÈ 55µô϶ôt222°X®Ý‰e¨åGȘ[ñ¾.°J1U„bSøgÒ÷U>Ž3&ÝžÛùÈY^xáÆO:u4Ç‘× ‚3rÖöµy½l}?’¶›U)&G;ž’D‡1O !qré÷´lÐHëì2þ³HVîþM´¯‚ 2›Í,Z´ˆ·Þz‹ÜÜÜ«Þ÷÷÷gâĉL™2/// "¼ÌÎð裕fmª©U«’j¾3fÀ«¯‚›ökEEEÉ·ß~{Íï^¡¡¡„‡‡óÐC]‘ÀýÚk¯1{öì‹Ï›7oΖ-[h×®Ý1X‹¢É‹îƒ¢+÷KTÁÚí0ò µR­·DNÇî¹ö>™çáÙ7`gÔ¥×j>é$É@`ç¿ÐùÕð‰A§dÎÞ@þñÁš¿°šÞzéù”10q¤fá8T@û‚Ëù‚ ‚ BµI·‚ h®  €€€ºuëÆáÇ+¼nn. W Œ§¦¦röìÙ+Æí]FL|<]áHªÏŸÀ Ô„ÛUÀPmé8Iâ~ÖѨQ#FMDDµjÕÒ$œª^s‚PQÙÙÙ4mÚ”víÚqà@ÅgÏž=›~øábÒÑè˜Á¯º„Œ¹¯Úþ9^u8>gÅéj™––#Ô±±Æ]C±Ø÷×a,* €aÆ1qâDÚ¶m[ãáTõºApvÎÚ¾¶nÔ„­ïE:eBëØO>báú¹¿ßÖLý@ëpì’UG‹§&¿°í« ‚þ÷¿ÿ1nÜ8bbb®zO’$žzê)fÍšEÆ 5ˆ® ™™0blܨ&©ÚlZGäz=4l+WÂM7iMHMMeÉ’%ÌŸ?Ÿ¬¬¬2· aôèÑŒ3½^O³fÍÈÎξb›úõë³iÓ&zöìYîy[yÑ=°ãk¼*_t ºøöCÔß¾} ‹à–áp2I}®EÒ-’W3»FÒÕÜ$oAzòäÿ IDATÁ9åÅôÃR¥Y•ÛÄ4èñÀ¥çL€Ñ®VÙ¨T:ôþ= ìü—Ö‚ ‚G’µ@aÕªU¼ñÆ•ÚÿäÉ“tëֻロ°°0^}õU>þøcV¬XÁ¶mÛˆŽŽ¶;áÖ€ZEöéJER=þB­pkÖRvÂí(@öÔP\+ˬ/V{‰ÓÒÒ˜>}:-Z´ <<œää䧪ל Ø+77—?þøƒ¡C‡Ò°aC–-[V©ãôîÝ›}ûö‘””ä°„ ïúA´~áv§N¸u5™Ä^L¸ÈÏÏgÉ’%tèÐ!C†°wïÞ‰ÃQ× ‚³ræöµCÓü>s¡S&Üæäóõ¶ŒòˆÆÑØoѺÕnA´¯‚ 5)%%…°°0n¿ýöRn{ôèÁŽ;X¶l™s%Ü\wlØ«W«IªN°êR• êï0y2?î1 ·7fÚ´i$$$ðé§ŸÒ±cÇR·;uê“'O¦E‹ 2䪄[€ŒŒ n»í6víÚUîy‹'b5&h’ 4ùÃK ·OÜgÂ-€Ÿ/¼^=qÙM±`5%Q”f³™ÈÈH:tèÀòå˯z¿V­ZÌ;—ýû÷Ó¿²µðàƒS¦¨I«ƒÖULI²ð7ÂáÃðÎ;àë™÷òŒ=šèèhÖ¬YÃm·ÝVêv¹¹¹üöÛoe'''‡AƒñË/¿”¹µ ãÙ% ˜«v…íØ»^zþâ°ŠcP(4¼Îq1UŠbÁ˜þ –‚}"‚ h©8å´üpåó‚"ûö{o1Ô¹¡ì{S­$=Å)ÓµŽBA<’Ë'ÝšL&ŠŠŠÈËË#;;›sçÎqöìYΟ?Onn.ùùùcµZµUp+CnœÏƒÌlH;çá\ää«ïk¾CÎÕœ;wŽ»ï¾›[o½•Å‹WéX'N¬Òþ¾¾¾üµ Kîo˜³7`ÊZ…9g –üÝX c3žB±æiªÓzóÍ71=z”:гgOÞyçJ¯ªŸs%ê´jHÛnGà6Ó¹B^ò9cù³õK*äõèуåË—c6;öû‚£¯;·¦˜°™±ÅZ°sîVÌçר?¹[±ìÇZt›)“ÖÑ n"³ ›Äì3I?Åþ¤#ì ~…Gï†O]¾xŒ_ËOð®?ºüMA·R”2 cêû(TŽÿ·ËÛ×ÂïUü‰iÐãKÏ“~So)´&I|šLŧñT­CAâ´I·V«•óçÏ“MVVEEjeY–Q¥JKJ:îEA’$‚‚‚¨]»6µk×&((È!ñ .HAM²=—s©†¢€,©ï)Šúg™ã>×xS’ÔŸ’$q?_¨_ ê«?zÏJþ³X, :ƒÁÀ?þ谤ʥK—2jÔ¨ íÌ/¿üriÀ`ñbxñEÍ;ÚûQ•Üש’neYÀø×¿‹Ñhdåʕ̘1ƒcÇŽ•¹{«V­ç¹çž+µJ®½ªëšsEŠ5Kî6̹ÿÃ’³ kq !I^(˜A©Bå É€$I(6’d@ÐCð]èƒnGÐ7(°_%_~ù%#GŽ$<<œ¹sçVx“ÉijÏ>ËŠ+*uþÛo¿µk×âïïÏÈï¦s8í8'\ å§œÛg×¶]Þ}Ù[ûÿÏ^zkG~L£ uíF“ÉÄwß}ǬY³ˆ‰‰±ë<óÌ3Lœ8‘fÍš9,¶ª^w®Êf<¥VæÎ݆9w+Šåü…eÌäªW­•¼+(d}ôAÑÝ!øNdïVŽ_pA¦"ö&D³/)†?N"ñü$IÂ ë±Ø,تðÝR/ë@’°X-t:º4jËM-»q}ó.tkÔÙÃÓœ©}eO´zOY‘ä¯rˤ—Ø}ˆˆûçãÑãµÇ>²·ô_o@´¯NáÔ)ز¶mS«Öž?¯.{.ËU¯Zëí­&½Y,P§ wÜwÞ ­Dûê±,VÈ̬ÈȆ •¯eùB?]YŸ·×ìÀSI’º‰MQ×TK`Õ †Z5¯ª¨¨ˆ™3g2sæLŠ‹¯ž€[§NÞzë-ÆŽë~˜Ïœ+`éR8v ¼¼´©Â­×«ŸAA0|8<ý4ôî]óq¸¨¨(úöí[¡ñ#I’˜1c“&MB±æ“s¨)Š5§£,[a4½õÒó)c`âHMBq(IW‹àžÉH²¿Ö¡‚ 5(÷p+¬Æx­ÃàåðõõqóFphMÅá¬I·²ws‚»ÇãQ72‚ ‚ 1§Ë~9þ<ÇŽc÷îÝÄÄÄššz1áÀf³U)á¸"iWQrrrHLLäàÁƒüù矜8q⪥³7–_qIð¿(øóõñùÜK÷¶Ë:ñ¯ù=õo*Ê¥„[P{ÏÏ@Ô1زöU—½sÎx‡3f F£‘ï¿ÿþŠäÇ6mÚ°gÏž /33“™3g2ujÅfðÕ®]›_ýõÊ £G«Ú'eÚ_cÔ‰éõн;Œ¼ºgØÛÛ›°°0bbbX»vm™URNŸ>MDD-Z´`òäÉ׬Ž{-޾æ\ sîV N=EÎäÇ=Œ)cñ…„[E1V-á@1£ØÔÁ)E1cÉßEQê;ä %ç`# ±ªÚ9\˜··š0’••U¡ýrss‰ŒŒ¤uëÖ•Nº÷Þ{Y¿~½šüwÐh­ ¶¸YÖ16ôñ‹ ·^^^„……ñÏ?ÿ°eË\îqòòò˜7omÚ´!,,Œèèh‡ÄWÙëÎ)ÖlL™ËÈ?z 9‡ÛPÿæ¬ÕjÂ-¨Ë˜U5á@1^\ÍfÉÂ|~5… /‘s8„ÜèÏD¢XÎVý<‚Ó³) {þáõ ¸uáh^ùùCVÚBâù3€z¯i²š«”p `±Y±XÕkÎlµr(9–%®fÄ7oqÇ'Ï3sûWÄfÄWõ×qYÎÔ¾ÒµµS¶¯‡Nű#ú’$ñÒà‡´Ç>ЮÅÅ„[í«f²³aÙ2¸åhÓ^z V¯VnAM’uD‚šÑ¨ +K=ÇK/AHôè‘‘pV´¯AAM´=[÷ÁÁc~)áÔ>¶k¶¯v 4+Ê¥IŠ¢öžHRû·ÿGN««_¹¹uëÖѹsg¦OŸ~U­,Ë >œØØXÂÃÃÝ/á aC˜8Ž…C‡`ìXõ5P«qW×ï,Ij‚/€¯/  kÖ¨Ÿsóç‹„Û*X¸pa…ÇEaòäɼþúë§‚bÕnl(ó_ °h‡£)¶|ŒK´CA¨A–¼]N“p ðÐ]—'¦ÁÞÃÚÅRlÆD,ù{µCA<ŠSTº5¤¤¤pæÌÌf³Z%OðJÎïïïO“&MhР²Xb̽Øl|VM|Í-P+èhYH’Ô~ƒšÕ‡–ÀÇK»xªÑ´iÓØ´i[·n% àÊ^Ã6mÚ°bÅ úõ³¯NëéÓ§™;w.Ÿþ9………Š£aÆlÙ²….]º\ýfT\ý•‰ÒBÅÉ2ìß={Úµù®]»˜7o«W¯ÆZFõMooo{ì1¦L™B‡ì:®#¯9Wc3%cLÄtö+l–L  å2>ί÷ëŽWƒ—ð¾.ìBÅH÷1qâDΜ9SjòÎðáÃY±bóæÍcܸqå+--O?ý”ÈÈH²³³Ëݾ,C† aÕªU“CJDîü–¯ö­ÃVÕdk&Ë2Í‚ë³ú™ÕJ”×pàÀæÎË·ß~‹¥$¡¤¡¡¡Lš4‰!å,'êÈëÎYòv`LŸ‹éüz$J&èiu]KHÈ(HxÕ‚w×ÑÐ(¡º¤çeñ6²6úw²‹òÑÉ:,6ûþ_;œz½‹ÕB»z-øOÏ»Òùf¼tmâ©&®Ô¾›§R=fBeµPné¡Þ+_ƒh_«ÑŽ0w.¬_)ÁQ«{dIRï/%I]fýå—a€h_ÝN± âS!9LäªO ­Š’¾Â@hÙšÔS¯C7GDD7n,õý¾}û²`Á®¿þúŽÌIœ8Û·«•½·l¹4Ñ 2Õ½ÿ½—ôë§Vò¾ývµÒƒWdr¤ôôtZ´hÑh¬ô1žy؇Ù‹5ûï7?uéù¼7à©¡ÚÄâh²¾.A=’d') (‚ T«ÂÄ—1¦/rLü<ì>¨>¾{|3§bû;s¥[Iö»þX|›¨u(‚ ‚à14Mº-**"))‰´´4ÍmK#]Ü1 4k֌Ƌä[WgµBb:œJQ;ðÁùCK›Õ‡&àç£m<ôÕW_ñÌ3Ï\s›?ÿü³ÜȽ{÷2gÎV¯^­ƒ~Íš5cëÖ­´k×®ì^z –,¹TiG¨½ž^­ÎQA'Nœ`þüù|öÙgWT:¿œ,ËÜ{ï½¼þúëÜtÓMeËQל«±ORœúÆÌ¯IÛDÛRI€„l¸ŸF¯ãU4’ì§uP1qâD>úè#¦M›FXX5"--O>ù„Y³fÑ»wovî܉¯oÙ=1ÿý7sæÌá»ï¾Ãl®Ú¿Ýþó–-[†ÁpuòU±ÙÈड़+ȉ·•$!ñùãSéÓ¬“ÝûTfÂH=xùå—6lØÕºK8âºsEæœM§LÇ’¿$ýÅê³NãBLú€ñiò_ Áƒ´ŽH¨¢¤ìt¾Ø÷3?GÿŽ$¥ŒIBZ‘$ ‰Z¾<{Ãý<Òí| î1¹Å•ÚW¬6øýÍÎw¯éJúu:Avo.ÚWÚ´ ¦O‡={Ôû:g»'.‰éÆá¿ÿ…A¢}uy…Åp2EM¶çûì,é§óÒCHShÞt®Û?\XXȬY³øàƒJMLlذ!3gÎdøðáûÆ 9Ž‡ØØK?©©ŸêO~¾:iÄÇüý¡V-õ§}{h×Ný³Cµ‚wiß!„*›>}:Ó¦M«òqº>™ r¡ÿÌ3ãóxùû½·>ü²ì÷!)H’tø¶X€wýçµ DA¨¹ÿtÀZ«uWˆ9w= ExˆœÃï·gNºÐùv&¨«cVøA¡|š$ÝOFF’$U*i®¦I’„N§£yóæ4mÚTt:º› N§©É¶‹S.ùyIRãl\Ú_¹¬¦« ÃArí*ß¹¹¹¬ZµŠU«VKjj*^^^´oßžGy„ððð23¶lÙÂìٳٲe‹CbyöÙgY²dÉ5' H>Ƴ+ßI·• “u<Ùû&ÜòTù—"33“¥K—IZZš]û´lÙ’ˆˆFui)sªvݹ"sÎfŠ’&a-< è'o§%(Vt~Ýñm6 Cð]åï#8•´ÜLæíüŽMÇv£“eÌV'¿æY’ñ÷öeÔ ðd¯{0è\»z𫵝dåÂÞµB¢è:¨I‚V CËJí.Ú×*ؼ&MRïãœ1Ùößt:ubu÷î0kÜ%ÚW—Sd„㉚©>w¶dÛÒH¨ÿ?Z7–Õj¸.dݺuŒ7Ž„„„«ÞÓëõ¼øâ‹¼óÎ;Ù?éAœ…Ñh¤E‹¤§§;äxwÞ_Íß®‡QXMo½ô|òhxíÙò÷s…¤[½šÜ#ñ%YÁ½)–³dh€3ȯÝ#ßPSdÞcþSþ¼:£ >_Sç]zÍ9Ú×ËIÔꕤ¿Në@AÁ#ÔhÒ­¢($%%ï2ɶ¥ñõõ¥mÛ¶Ô®][ëP{dfCô)uÚšó}·/Ÿ$ƒ¤@ÛæÒ¸Üå5ÝÉdâ»ï¾cÖ¬YÄÄÄ”¹]Æ 3f ‚é_K½µoßž­[·Ò´iSûN }ú@–$óHüõ—:øéùùù,]º”?þ¸Ô¡mÚ´aìØ±Œ=Ú½¿í¡˜(>óÅÉÓŠK$Û^I’tÈ^-ðkµ}ÐZ‡Scl66làÝwßeß¾}víãïïÏàÁƒYµjU™ß¥žþy.\hW…þÏ÷¬aÁ+nÅgfu´©×œåÃÞ©r"›Ñhdåʕ̘1ƒcÇŽÙµOpp0#FŒ`Ò¤I4nܸJçw%6SE ã1_s1‘Õ¥\ˆÙ«öCø¶ˆDö²óûˆ ³ÕÂòýøäϰ) H¶½‚²N¦qP=Þºë9nhÞEëˆjŒ3´¯œL†ØÄŠ„-ÈúÁݪœÈ&Ú× HJ‚ñãaÍšK‰¬®¤$懂ÈH°÷~_ÐŽMÓ©—¨öÓ¹â}ˆ„ZÍ´[k¨¬u4åŠeüøñüú믥¾Ë-·°`Áºtñœï ‚û),,äàÁƒœ;wŽsçΑ™™IFFÆÏ/ÿÓ7ö€o?„ €jþ_z> ©êãÂïUüÎ\‰/°ãïèoÖ: A¡™²¾§àÄpÖù-»á¹©›¯>oß †…›ûB“è¯N„IÏT«ãþoü²2Ï«ÛK<2¾z]Ùç©q’„ëUxÕyXëHAÁ#ÔXÒmvv6qqq¹|R‡$I(ŠB:uh×®ÞÞ®_Ô-Mp,RΪá®}Ù©ßà}½ Kk¸®–ÖÑT»ÜÜ\¾üòKæÌ™Crrr™ÛuëÖ &ðÄO\±¼ëÓO?Í×_}ñy×®]Ù²e 4¨X Ÿ£G»æ V¾øžyÆá‡µ7y¢~ýú¼ð Œ?ž:uê8<gcÉûÂÓ£±O¢¸ZÚ¿]HH3Ôº¿–‹‘½šiQµ)I&ÿè£HL´/!§äÚ7nuëÖåà矾j»×^{™3gÚ‹MQxþ‡÷‰J>âtK¥;#I’ð7øðÃÓ³iä¸ã%Ÿq|ð»wï¶k///üq^ýu:vìè°XœŽbÁ˜¾¢ä)ꤜXp9I22Þ'ãÛxŠËWøvWû“Žðö–ÏH:ŸîòÕÀeIƦØèÒ“©wŽ¢a`]­Cª6ÎÔ¾¢ÅÀ¹\q/a/½ôpè*/¢}½‹.„)SÀlV\™Á –)š²rà\64ms_¯ÙÄÛ—gÀ×kÔÇÍÁ¡5?†Ó&ÝJ¼¯ ïÕçZG"‚ T£Âø0ýE¹ú;¨³HH…–À›Ájgן¯Üw Œ]ÚVo|•!I^xÕ…_Ë…Z‡"‚ ¡Ú“nEáôéÓ$%%]LVu²,#IíÛ·§^½zZ‡#\.= Ç©kCØÜçš»˜<ÜòÂr›.¶Œ=ÒÒÒøôÓO‰ŒŒ$;;»ÌíBCC™4iƒ.u`àèÑ£tîÜEQèÕ«›7oæºë*™õÔS°r¥ó/¯©5½ž|¾úªÚOµk×.fΜɆ ÊlW6l'N¤m['¼û­*ÅBQò§ÍdÀ}’%%É $~!_âUçQ­Ãq¨ôôt>ùäæÏŸOVV–]û´nÝšqãÆ]UÅù?þ ÿþWl;iÒ$>øàƒ Ç•U˜Ë£_¿Æù¢<¬6÷¹–NQ“nç=ø*7·îUm§ÙµkóæÍcõêÕXíH„–e™{ï½—É“'ZmqiÁfJ¤ îa¬…]bÁ¿H’Ù·+mDöÑ:á«ÍÊü]+ùjß:$ÙuWg)A§G/ëyûî繫}?­Ãq(gm_1™açaõO7ê ©’½;@ýê[QH´¯—IL„‡†ƒ]¯²myt:èÚ~üBDûê4Ž'ÂÉ÷˜9IVoÉ»µ…FÎ1±EQ–/_ΤI“8sæÌUï ^xáÞ}÷]5ˆPœ‹b+"ç@=[Ö¡°3 îñÒó_–À \PÌi“nIö#¸W&’ì$ ‚ —ÓKÁ­Ã°KB*lÚ ;þ‚ãñꤛ¼ð÷ƒàhÖºw€>á®þÎÓž–Eï߇ÀÎi† ‚ x„jMº-..&&&†‚‚·J¶-MÓ¦M U´f³©ÕmãÓܯÿ ûA¯­¤¥Ã‡óá‡òÝwßa.£ÂNÉ çÔ©S¹þúëË=æ½÷ÞKff&›6mªZÅS³î¹~ÿ]$Þ–Å`€›n‚Í›¡«ÿý÷ßÌ™3Ç®ëæÍ7ßä†n¨±Øª“Íxš‚¸‡±ýŠ»^“2`ça¾Ífº|%È'N0þ|>ûì3ŠŠŠìÚ§wïÞŒ?ž'Ÿ|®ô5ŠBCC/Vm{ûí·™:uj¥cL<†'ÿï òMEn•dæH¯Ý>‚a½î®‘óU×uã*Ìç¢àdŠbtùê¶e’ H’þ!Ë0Ô~ üí…j•’“Á+?D\f’ÛN@$ x²÷=DÜ< ƒN¯uHUâ í+Űûoõ>ÂmïO S+uri ðôö•Ÿ~‚°00]¿ºmY ðñeËàѾj®°ÄB^¡ûN@(©zÛª1´o¡é$ùC‡1vìXþøãRß¿ãŽ;˜7o:uªáÈÁy™Î}CÁÉá€óô… ~vTß=¾™S±ý9é$üÛ|‹WǵDA¨&ÙQA(Ö<­ÃðH’.Z½sµCA>>Z‡ã™<¡ÿrj—îm¡AJ5foÅÒ‘#G2aš7on÷±9BóæÍ pÀú_……pë­pèûLV–Á:Á®]àˆ¿ëJ8sæ ‹/®r…dWàIh—“tè}»áßö'dïZGSa%Õ~üñG»Y+ZQí§Ÿ~âá‡æã?&<<¼ÊñF§à™•Ó1[-ñý­"$IbÌóÂMÔø¹322X´hQ¥*8>÷ÜsøùùUs„¦˜)Lœ€1}Á…çø¬êL1ï†áø5› ’Aë€<Òö¸¿xcãBLV37M¸½œN’i[¿sïŸ@£ J®¡!Wk_É·=ÿ¨÷©¢y½ZÛfêO ó¸öÕl† `Á…öÕÝ'Y•$A†‡ÃìÙê}«PóÎdÁßn¸U™´›$ŸÍÿû_.\Xj5ï¦M›òÞ{ïV£q ‚+(8ùæ¬Pœhb{Ì ¸ëY(*VŸGNá÷Û¿¿S'ÝJz¼ê<ŽëZG"‚ TÅœAöÁZ‡áÑjõÊDÒ;Ç*‚ ‚àΪ%é6%%…'N8ú°NO’$ôz=Ý»wÇßß_ëpë Ⱥ`:lCç×MëhÊe³Ùذa3fÌàÏ?ÿ´kooo{ì1^ýu:vìX¡sýüóÏ<øàƒ• ÷*;OdüO³QEd K2v½•·î­iùùù,]º”?þ˜„„»ö©W¯/¾ø"ãÆ£n]çïhSlsÞvPÜ?ññJ: Áño»Iv±D.÷~aöÿ¾Eò¨Ï=½¬#ÀÛÏ›J»zöOlÓŠ«·¯dœ‡ýÇ.<ñœëìš$ šÕ‡.­5 ÃÚW àÁaûv(%Ï­ét0p ¬^ ®–(íêâÓàèiõ±'}ìÉèõpCg¬þkNQ–/_Ϋ¯¾JFFÆUïûúú2~üxÞ|óMÇLJ·£s 6Ë9­¹ÊÚí0ò uÞ‚,ÃÛã`Ì@'_{?£ >_Sç]zÍ©’nÙPàžWf ‚ ®Ï’·ƒ¼£·h†G ì´ }@ù“ÎAA¨‡'ÝÆÇÇÛ=HáŽ$IB–eºvíJpp°Öáx†¬\øëˆZ1Ó’Ðþ­uu ;'V2˜ùÑG‘˜˜XævmÚ´aìØ±Œ=__'ê HJRìNŸ‰·´jÛ¶©‰·NÄd2ñÝwß1kÖ,bbbÊÜ®aÆŒ3†ˆˆjÕªUƒV”BQÊ4ŠSÞÖ:ÍH’$_Úo@x³Öá”Êh4²råJÞÿ}bccíÚ'88˜#F0iÒ$7n\ÍÚo{Ü_¼¶~.VE±«‚ ;“$‰‡ºÜÆ›wB–ÊÙª!%‰gï¾û.ûöí³kŸ’ij7ß|“víÚUs„•£XΓ{–Â(p¢ C5KÞ¯6#é]¯ú¨«QPød÷,Ù½Ú£’m/§“d¼ô^,|x½›ÚŸ”Z“Ü©}åL¼ð;xò½k‰æ  sȅɤÚs×ö•óçáž{ * ,Ú¾êõЭlÞ ×‰öµFÄ%©?žJ’Ô¬¸>¡NPµæÀŒ;¶ÌÉ(ƒ&22’j‹A\µè¹ÿtÖ:Œ2mÙ ÏM…Ü|õyûV0|(ÜÜš4€@(,‚ôLµ:îÿöÁ/; 󼺽$Á#ƒ`á[ ×i÷{”&¨Û1t>íµCAp0ãÙ¥ÆñÀ‚ ÎA’tøµü ¯zÏhŠ ‚ ¸=‡%Ý*ŠB\\iiiŽ8œK+Y²¼S§N\':ó«×™spð8 –é Yèâ<ƒ–%ÒÓÓùä“OÊ]¶344”ððpzè!t:'뼜´T-»w‡M›œzÐRQ¶mÛFdd$ëׯ/s»ÀÀ@žyæ&NœH³f5¿´í5)V ãÇ`<û%àÙÉH2:üÛ|¡öåo_CΞ=ËÂ… Y¸p!™™™víÓªU+ÂÃÃ5j”ÓVÇߟt„±«ga²š°zhâ­„Ä3×!üæaZ‡R¦]»v1sæL6lØ€=_ëK–Xã7èׯ_ Dh›)‰ücw`5ŃÍÃ'µHtÞ- è° ÙËÉÚ$7bSl¼ýëg¬‰þÍ®ÿ;îL–dt²Ä¬!ÜÞ¦¯Öá\ä®í«˜4z“Ou—ö•¤$¸ãˆ“F hÙR4êl÷|îDQ ú$¥k‰öJúæzµ‡Ž]½)++‹éÓ§³páB¬¥T¯nÓ¦ sçÎå¾ûîsèyÁÓ¨«J9ñÄÏ„Tø` ü°¬vvÏøúÀ}·ÀøáÐ¥mõÆW)’¿óð®ÿ‚Ö‘‚ Vœ6‹¢ä·@1jŠg’¼ðmú>&j‰ ‚ ¸=‡%Ý?~\$Ü^¦$ñ¶K—.Ô©ãØŽUႌóuìB²­V^N’ i}èªíòœ%âââX°`K–,¡¸¸¸ÔmJ'§L™Â7ÞXÃVAa!<ü0lÙâ™Ës?üÎV‰ø<ÈÇÌ·ß~‹¥Œdi///üq^{í5ºtéRÖF¡ðô(L™_£ˆYÁH É´]‹¡Ö½šFrêÔ)"##ùüóÏ),,´kŸž={Á°aÃÐëõÕaÕ?›Èsß¿C¾±‹Ís®AI’@)GòX;µÇ.ÿý7 .ä믿Æh´¯C344”I“&1xðà‹ß]í±nÝ: „——Weý‚bÎ ÷È ØL) xxBP É€ìÕ„ N{‘ õµŽÆí((Lß´„Ÿc~Ǧx椂“%‰È_e@HOMcñ„ö•¼BØ£Nà³yؽ¬$AçVм¡Ö‘ØÅ•ÛW22à† %E$Ü–0 IØ»ê‹öµZüs’3<{RÁ.|ôíõjWùh6›+V0a„R'¤øùùñꫯ2yòd|||ª|>Að'Á”µpþ>„TØ´vüÇãá\6䀿@³Fнôé wõgî¶•txÕ~ÿ6ßk‰ ‚à`EÉS1ž™bI·Z$o¼O·Ét­CA·ç¤ÛÓ§O“””äñ‚J#IÝ»w'88XëPÜKvì‰ö¼J{µi íškvz{ª•,ÃùÆoо½‹.#e±À«¯Bd¤:xìî• eYý_yfÎT«Ýº Ó§O3wî\–.]JAAA™Û• –2Äîc›L&Ž9B=*EÉS(Nû@ X^EB’ tÜŽ> ´ÆÏ~àÀæÎË7ß|SjU¡“$‰;ñãÇWèzri¹™¼üó‡ÏHÀêÉizY‡Þ‹÷ãæÖ½´§ÂΜ9ÃâÅ‹‰ŒŒ$;;Û®}Ú¶mËK/½Ä˜1cÊœÏËË£yóæôë×Õ«Wã[ÅÉŠ5¼£° ·ÿ"IdßNv܉¤ Ô:·2oçw,Ýû3bâÞ•$$ô:Ÿ?6•Mjþû¹§µ¯áÀ1È-ôŒïz²¤ÞOôhõ«žxVÓ\­}%/ €#GDÂí¿ ЩìÜ ¢}u¨ØD8™‚h_ÿE’ÔÜÛº@íÊ_sýõcÇŽeß¾}¥¾?xð`,X@‹Î[E\œQîßm°ŸÔ: $û¶#¸k¬Öa‚ V˜ø2¦ôE(ŠIëP<’$yáÝà%|›¤u(‚ ‚àöªœt›ššJ\\œ£â©›ÍÆž={رcqqqdddPXXˆuëÖ¥]»vtíÚ•ÐÐP®Ópùs½^OÏž=ñóóÓ,·’_»ÿQ×RÒxp2» ŸÆO ¥Èd$Ð×´ëðw–J[A«F5v:›ÍƆ xÿý÷Ù³gO™ÛÕ«W_|‘±cÇjúÿÒ¡~þ†£Lnzi0¨UmW¬WLj(ENN_}õ³fÍ"55µÌízôèÁË/¿lWå´/¿ü’—_~™7rÓM7U)>cÆ" ã_ªÒ1ª"ù lÞ;öñӕ 9yêòtu‚¡mKuyº›ûÀ=ÀÛA¹ì§CÒØéOt¾«ýlŠ¢°~ýzæÍ›ÇÖ­[íÚ§¤rò¤I“èܹs5GX½ÌV ‘;¾aEÔ/ á¶­dI¦cƒV|8ôe¹v•——Ç_|Á‡~HRR’]û4hЀ矞ððpj×.=!ê£>b„ Ü|óͬ_¿žÀÊ&¬(&òcïÁœ·C³¥<ý³N’ôèn$°Ã¼köänjå¡_™±õK”HŠÿj¹1)=f× éu=æådIÆÏˇåÃÞ!¤n“j;O Oo_±)›§SÕ¤,7m_‘€ u‰u_×þ,s‰öÕd‚{î;Ô‰¢ˆÖ[“ÀY š]>À  ƒêõpãêÊ5Þ®}M:„3sJë(œ·ŸN’@/ÃÝ  bIõgΜáµ×^cÅŠ¥Þ‡µk׎yóæ1hÐ GE+žC±r~¿¯˜ªIò¢VŸBªïþFA¨y—VoÔæ~ÔÓI’¯ëžÆ¯ÕgZ‡"‚ n¯JI·™™™ÄÄÄ82»ÅÅÅ1cÆ N*¿CW’$:wîÌ!C¸í¶Û·TŸ$IÂ`0ЧO CžÛí˜Ì°ó˜,N1 ¹`ÝŒ[|i¦Øgã'3j“$$J@¯РNµžÆh4²råJÞÿ}bcËž™Âøñãyî¹çÜ3=) ~¢¢Ü¯â­NݺÁ?B«VZGãp%×ð|ÀÑ£GËÜ®eË–DDD0jÔ(üýý¯z_QºvíJLL þþþ¬Y³†V*&óù5äÇ=„‚Ó`Öçðý/`±se=_¸g<9né«1«zdC=‚ºþ¤¯žI“ÉÄwß}ÇÌ™39räˆ]ûòÌ3ÏðꫯҴiÓj‰K+¿ŸŒbʆ[LXlοô¢½dIFQžè5ˆWny ƒÎ5+y—¦äž={6ÑÑÑvíSr O˜0æÍ/UÎ7›Í´iÓ†ÄÄÄ‹¯õíÛ—M›6Q§NÅ¿oœ|SÖ÷š$ܺÔg¤Ç«îø‡,«¡º¯í'þ╟?¬±ÛWLºÐÉ:êøñÈYÔò­ž*¢}ý—Œ,8§ÞG¸Ój.%‰Ä-A‡–jµ[7áÌí+O> ߯IÂí `*°ûïdZÀá@ÖØÖëá‰'`™h_«,=K­Üí_ÎÝO'·úw¯òû‡- .ä­·Þ"77÷ª÷ýýý™8q"S¦L©ñ¾náJ7n$""‚S§NaÑh²ƒP96ãIr·Ñ: Üý4²wK­ÃA¨àä˜ÎUä®Pp, ¯ºãßú[­A·Wé¤ÛââböïßoײŽvòäIÆqYðÐÐP† Bûöí Âl6söìY:ĪU«®¸xå•W:thÇ,IµjÕ¢[·n5~n·¡ûbà|®Ó Dv;‚¿OŸ¸øüúvØû±³Ì»PEc@j©&töìY.\È‚ 8wî\™ÛõêÕ‹ððpž|òIt:7Ÿµn±ÀÂ…0eŠúØÕ«Þzy©E“&©¿“›â”Tkž9s&üñG™Û3bÄ&MšDãÆ/¾¾qãFî»ï¾‹Ï}}}Y½z5wß}wÅâ0ž&÷Ÿn(JAO.زF¿¥VyèÓ¿B{Aƒë Ðróáx¹’’˜}æÎ…ËK=ETT‘‘‘|óÍ7eN.ñööæ±Çãõ×_§cÇŽÜvÛmüöÛoWlãååÅÊ•+yàì;±b&ïÈMXŠƒ­f—¶[»F¾¡Xóö‚È)ðØ=×Þ'ó<<û쌺ôZÍ'¢Èø6{ŸF“ª|¤øøx/^ÌâÅ‹ÉÉÉ) [·nL˜0'žx£*ÚïMŒæ_?#%÷,6L¼ÕË:t’Žû?ÊS½ïE/»ùÄË©&²9A[…I’úÓ®´j¬>öZ·¯DGCŸ>`¬ùöõàqÀøßåÝv_x®IÒ-€ÁýÝEûZaŠþ95?a´4.ÑOj²m»кÉUo¥¦¦2yòdV¬XQjŸ{÷îÝ™?> ¨@…ò 6Œnݺ1qâDZ¶lÉ™3gDÒ­‹1¦/ 0ñPj¶N¸@2à×b.Þõ_Ô:AÁ ÆbÌX"ÚW­H^x׃_‹yZG"‚ n¯R%áNž<©YÂmzzúń۠  FŒqÍí “'OÆÇǧ&Â+×éÓ§íd.“• qIZGq…Ï6ý ÀÔ'žÁKo¸ìõµZ…t5›y…›PåCEEEF»ví˜7o^© ·^^^ >œèèhÖ­[ç™ ·MšÀ?À¯¿^¸Ó»ÀRå%•ˆ»w‡­[Õ%I=0áÔªRË–-#66–ñãÇãëë{Õ6F£‘åË—Ó¥K pUÂ-¨ËÎ>úè£|û­}˸&MÄRx°Æn£ãà…éjÀW3ÊOB¸®6|û!´Ö|.‰¢ä)XòvUú‡&,,Œ¶mÛ2sæL»ÚêÐÐPÖ®]Ë¡C‡ ó¬„ à†æ]øé™‰0Œ`t²éZåšœ„^Ö!K2÷teݨ¹<ÝwˆG%ÜôìÙóâgÜØ±cñ÷÷/wŸ?þø£Ì„ €£Grûí·“œœ|Íã(¶ âD¡æ'¤¸úg‚¢þÝYóÊßX¸Â‡¿­àXúéM¸u6EaþÎï8˜r¬ÒÇík%Ô †=¡}K0èÕä,ísÙÊ'Ë€MêÁ­½ ¤‰G%Ü‚¶í+ðàƒšLøŒžFM¸ø‘ònVÕ–ýEý»Ëík…wš„[p‘~:P?Ó'ª+y]`6›‰ŒŒ¤C‡,_¾üª>÷Zµj1wî\öïß/nÈÒ¥K™}X¾|y©U?ƒ‚‚?~<§NbÙ²etêÔÉ‘»¾;(ؼ®¿^}Í;âKbê×OMŽŠ‚;îÐ6&'Ѻuk"##9uêS¦L¡N:Wmc³Ùص«ì»i‹ÅÂðáÃùâ‹/®y.KîvŒgæk²äÎä¡H-²Ç÷Á þ×Þþr~¾ðvxõÄU1'ÿƒb+½wYvíÚÅ!CèÙ³'Ë—//·*,Ë <˜½{÷^ÜWò°„’ËtzFô̯c1áÖ§¨í„,ÉN™|«×éÐË:èz;FEòî½/Ò ðêÿÓž$$$„ùóç“’’Âܹsí«¢w ±±±ôïߟ“'O–¹MQâD¬ÆPj¾”ËÖ)¬¦$Š’'kˆkÙ—÷6aUÄýWeHÀkëçQl®Øýh_«H– ¤1ÜÞ:¶Ã¥ ²Î¦$®¦õá¶^Э øxi•¦´h_™8@ƒ ‹c‚ GvÌg¹(xÛáUÅII0Y´¯r.âÓœ&áÖ¥úéJ8VÛ¶m£GDDD÷¯äoI’>|8±±±„‡‡‹äN'SÚmÁµ(Ö|­CðlbR© ‚’t8Ãð€Ùo/Rm“Î8öØFŒ~KýÓé(ˆ¤[AA¨!JºU…ãÇk:øuyÂ_vv¶ÝûuìØ±:©“ÉDBBÕ+zŒÉ`4;SÎ-ßüö+ÅÅC–µ¹mùtºÍ~ÿVU¿p:¾9”n³GÒ×len«bã\AŸï]Sî¶¢}­:Z6‚ÛzCçôS_×ò>Ô¤`/´j¬Æ×%|½µËÉÔTûJT,Yf ÚWà÷Ëž¿\‰c< ø9&œÊ³Xà“O`Ÿh_íbS ú¤SMp¹~:E“™Y¯¿ÉÀ9räÈU›ôéÓ‡?ÿü“eË–Q¿~} ‚÷§Øò@LÎÓŽ¢ˆ¤[A7$é‚5o_ãâᮑ0÷ëê™'8m½Æ¼XmÙtAZ!‚ ¡B#5IIIiVå Q£F§¦¦râÄ »ö{ôÑGùí·ß:thu…f7EQHLL¤°°bÕø`ÍÈyÃ4P«Éêe]LÜ2è H€A§ã¶6}˜÷à«la17£^@íj?¿+óöö&,,ŒèèhÖ®]Khhh¥Ž“––ÆÀ‰ŽŽ¾ìU…ñ£Ñji1·ú¬“tžÃ¥…¼…²,Û¿žäì ¬b•‘*±Ú¬|±o-§³J¿—ík ehÞúw‡›{Bë&às!ÁU–ª±‚Ìe÷ᲬžG’ AèÓîèZx|eÛòTkûj³ÁèÑš%bsÙãv@÷JõRn¦C"ªÆŒQÿN…k;…F§ê«s¹~:Eá?]®ÇßÏÿŠ—ëÔ©ÃܹsÙ³gO…úA¨8µÒ­øÜ׌bI·‚ nHöѤèB‰¥?À-ap8V]íì­{üí{`É÷н=´næØc;„bFön­u‚ ‚àìî•7NQµY³f«iÚl6¦OŸÎ©S§4Žªâ$I"..Në0œß?ÎU9ààÉãDˆ¥Y½ú ìÙY’1ðÒ"Š»þÑÄx",ƒO„¢«—Ô‹gòäÉ4oÞœˆˆRRRJ=Dhhë/±k IDAT(k×®åàÁƒ„……‰eí*+8ÂÂà·ßàôiX¼{ êÖUß×éÔj¸U¹ö%éRE]PýÈ#ê¹âãáÐ!‡ë´.ÙçšdYfÈ!lܸ±ÂË N:•É—-]j3%Rœò6Š ·¿_VÌ©gŠÂ/žY{¡_eFÚJ¢èô W¼’žžÎ´iÓhÑ¢$%%•{”6mÚ0wî\âãã™6muþŸ½û¢Z8þm齑‚Ò!¨ˆ¨€¨×‚ˆ—f# ""R,ü.XËïUŠŠ Š‚X(ŠŠ1¡§‘²©»›-óûcI¤„d“ìîìnÎçyöaË”7a2gçÌ{Þ骀}ÖÕ‘ <ÚëlÿV™G{ÝKׯmО¯~«Uk*+ÑÖ•J%¡Uk$ ‰«#¸¯S?Ü5…žø/¯™ÌM-»¢®ç~šŠsÜÎ;Ù¼y3ª:$òdeeqóÍ7óÛo¿`ÊY‚¥tÈîŸö|ì\'[°–ýŽ)ç]ƒð|™E¹,ùñl¢r•SHÀœ­s¢}UHp´jj¯.Û«ƒýyDèßÕgU’ƒ ˜Õ$ËIç·S‘Í`OúíÚú_]’ 6Òã®Õ=+ÚW–,}ûì•ZpÁ˜ú(/¼ð`¯¾;fÌzõêÅm·ÝF÷îÝÑé<¿ÊŠ,ËèõzòòòˆªH´.–ùEJGq™¥[ìÕ1éwª ¦‚}ë þõÑÊÊJÐK·l`þ¸Eb¼¢ŠÄÛN­HMMåÍ7ßäÃ?Är…›tZ­–¡C‡òì³ÏÒ­[77Û@4kcÆØü»wßÂáÃpàœ9V2ÕjhÒ:t€k®¤$èÙÚ¶uÝÏЀ½ýöÛ †Z¯7oÞ<æÎ‹áô4@™„Û2œ¼ h]+_(2'›1‡Y¿™çZ³hÑ"–.]ŠÑèX'~rr2)))Ü}÷Ýâ&§“¨$‰Žñ­èߊ1×¥Üjæ÷ŒcÎ9Á‰ü ÒòΞ{†Bc‰ÃÛô×øqUD-£›Ò"²1-¢ÓµÉ5DŠi“œmûöíØêXñ-77—[n¹…Í›>¡­ÿóT›àåB>y®Ã†áôTtÑÑTA5/Þ-ܱ›%y;‹Íʯ§³#}¬Á¢}õb´lb¯Î©/ÂûŒ1Å(1€ùJÕeªH˜U« 0B 8Оà :½‹äŜѾ~ñÉ'ô|þyÅ’K ‡À·Q$ '³Ù`êT>‚DûZ¥?O)õ•»ŸNæÙÛÿÁás™<;óºté¢tD‚àu~øáV¯^M¯^½èÙ³'­Zµrx]I„$©‘ÚÐI’I¬t‚ ‚“©tÍ‘$-²ÕnÕjxz<3ü\.2éÿ ;Í€–W9ûÎ I:T:O,Á+‚ ¾Ç¡¤[£ÑHNNNe'¥Òz÷îÍsÏ=Çüùó1Øl6vîÜÉÎ;ñ÷÷§C‡têÔ‰Î;Ó¶mÛ:UqI’8yò¤Hº½’´3öû€žqØPf2²ú»­H’Ĩ[ï¸è³–ñ¹¡]'~8˜ ÀÊí[˜7ê1üß-ÚWO¤RÙd#/i#­V0™Áj‹ÕþÚlÊ~7J­­4j‘\ë&Îj_o½í6Ö[,ôsR\µuî’×N,¤¬’Xº&‹öõ2eFÈÈõ¨*·^ßOøûéøð™YÐ¥£Ò¡‚WÒjµ¼óÎ;¼óÎ;ÄÆÆÒ«W/’““éÙ³'ݺuÃÏϯê•Õ!Ôb2HÁéTHꥃAœMR£ò» «1Íí»Ž ‡é\³íåëaó÷pOxpköá ’sÄ€wAAp‡’nO:…$I“t 0pà@ºvíÊêÕ«Ù²eKe¥A£ÑÈ/¿üÂ/¿ü@XX}ûöåž{î¡I“&J†|Y–)..F¯×®t8ž%Wo¯ÒãaÖ|¿¢²Rnîx-WÇÅ_öùèþƒ*;óó‹‹X·ó[†ßÜßÝaVËd1sßÈadžË©òó¸¸8&L˜@JJ nŽN¨’Vk¯^+x”+V“Sõß‘£½½‚³*ÞzÎÁY€¬è’Ól ¿ûcp +=ÚeÖx/ØÏÏûî»éÓ§“”$¦ÛQZ€ÖÆa±J‡!\`É’%Ö{;¥eå {V΃[z:!°ZòÙslÁ”1¿Ø Hª¥£ñ(ÿݳµ$aóœËWŸ`³Ù0Äjjìí«‡Q«!PÜlñ$Nk_ËË| ¬÷ÖjïÒŸ P\Âb9s`ÂíëEÒÎ*Áe|¡Ÿ{•ò¼Bˆò™ôuAp›K‹Eäää°~ýzÖ¯_€F£¡S§N$''ÓµkWúôéÃUWÙKÓIª`$)úñc*L˜§³ ùZظس¶ç|ÒùÄgAÁר»`5ž@©Y-íLŸÍàÍ甎¦’M ˜=CAܥƓÉDVVV§½s¥˜˜RRRX¿~=/¿ü2C† ©ì0©PXXÈgŸ}ÆÈ‘#y÷Ýw=îç$‰'N(†ç9vTULw©°e_}Ø;í«roï› ¼lyOâ§ÖðømC/{¿U«VÌŸ?Ÿ'N0{öl‘p+Õ°Ùl¼ñÆNÙÖÊ 6}Ñ^xÌÝ.­öXjpl½9K ²Ç•ŽnÇ•úôÐÐñšªËYÆÄÄ0uêTŽ?Ί+DB T¡¼¼œ… :m{F<ô,|ñƒÓ6é0_>×ÉV=å¹Ë•ãdçóùÁï1Û|£SßÓ„%%Õ¼Q•Ÿ‰öUjæìöÕ 68m‹Ž»tèx™1¸Œ^ËEûzc9œÉñ¨*·àýt€=éïØi¥£¯FsåÚ2‹…½{÷²páBFŽI³fÍhÕª#GŽä¿«ÿàÐ1¥o™-ðÒÛ0øQ{‚¬§mÏUdd$•HºAðEš›ìßq}€ÙãfBy9¼û „)Qu$4!}”BAŒ“nÏžõ¼*—òóóã†nà©§žbÅŠ|öÙg<÷ÜsôìÙµÚ^ÑÅjµ²jÕªzMî ²,SXXHqq±Ò¡0dÈ$Iâ•W^Q6} ãi¥©žLç§#‡ æžä>U.èçÏý7ÞRùúûû8zæ”›"tÜcƒî!8Ð~UœœÌçŸÎÑ£GIIIÁßßWÊ¿ ‚ëȲ̆ øá‡X»v- ,à…^`ôèÑ 4ˆnݺѤIt:CÛûx üs:”›]ø%¢/¹S^èyÆëN¶ðøÿ@-Z´¨\0wî\âã/¯„$‚ÝþýûiÑ¢III„„8ç&˜©FMƒO·:esóås,Û0füžPšÉS®%>øí |£Kß3YeA=›_ôžh_Áq.i_{5NÙšã.Ÿ þµ{=ˆÍÿ÷‘`ê)í+'2ÀÃZX_ê§C–!¿È·¾¨ ‚›¨ÕêZÿü믿X±bONý”Þ–ÓâV¸w’}Êhw;vú†ùËÓì8{{®$aE¥‰R: AÁ4¡}A¶(†SÌ[ûÃóã¡k;¥£©l±ÿîAAp‹+ÆžT”••Uãô‘ž&""‚2pà@222xýõ×ùí·ßX¿~=wÝu×eq•¤R©ÈÎÎvÚ —ºX±b7nTlÿ9“’ dϪJ¼ôK{í˜nìG€ÎïŠËî?ˆw¿úûwùîWyퟻ<¾Úˆ æ?OL¡Ý½wе[7¥Ã¯£V«iÓ¦ mÚ´©qÙ¼¼<²³³ÉÉÉ!##ƒœœ²²28uh!çòMdçBN>|ù<<–Ï?Çruë-0À>ÎÉ ûë#鎭7}‚ýQáT&t¾¼€¶âîê¯cÎR?¢ãZ‘’’ƒ>XmõAþÖ½{w~øáï²´ƒììl233ÉÉÉ!33³Šs[YYY”•]¹ÖÙãg‚Áë.Hæt¾}®“±•ŸÆR¼MÈŠEá)×V›• ¢Ê­ËEumAÑöc´hÜL´¯‚PK.k_‡°W¾åòŸÂ.hT4«GÜ´_·e8}vì€Eûjÿ}äˆ~:WSIpæ„U=c‹ày6mÚÄàÁƒ/zO:_ÑmÙ²eŒ3F‰°|šÉd"##ƒ³gÏröìY2228}ú4Cý¦!Il£ï½¨ƒþ»f,´Ï 3ìhÝ^üçlÏÕdÙŠ*@ÌŽ!‚à‹ÔmQi£±™s•¥^~L…ù+à¦î0q„ÒÑÔL¥AíßZé0A¡Á¨öÎXAAf³›Ëî9YBB/¿ü2?ü0yyyȲÌO?ýäQI·6›ììlZ¶lYÙ1çNLš4‰#F°råJ·ïÿ"62Ïy\G¾±¼œUß~À;_®ç/×;¼îòo¾`ÎÈqè4ZW…W'ßt+4KT: AðyQQQDEEѶmÛÊ÷Ìú/(ù󵋖“eÈ-‹®|»Ðùú\ËÏŸÒ~;䯻FUÎÖ÷üiÝïGÜ”É,>* €æÍ›Ó¼yó—---%##ƒ“©)dßJÖ9 y•9yösݫˆ®qsNáËç:$-å¹+Kºõ¤k‰Ý'~§Ð *ĹšU¶Òjâ­ìšº­Z$Û B}Ô¥}ÍNI!gëV2,Ι@px{íóÑ® ú€ÅçŸÿê¦}ºV +V(–tëIí+¹zûÈ)â‹ýtØdÈÈkšÛp7hÐ ¯+VâÉ ™™™¤§§“‘‘Afffå¿ïeggc³9ï¾ÁM7ö"åÞÝô¹Îi›tXNLy Z6…7§Á ]aåÏÙž»¨ýEÒ­ ‚¯Ò„ÞŠ9ïcd<ëZÂQE%0aD†Á’Ù ªqþh…I4a”ŽBA”jïegg#I’Gu™Ífú÷ïObb"Ë–-sh   :wîÌ7ß|@n®çª²X,äççåþétÆŽ˽÷ÞË 7Ü |G~N¾=ãÌìÛõ-%ÅuZ÷\¡žõ?îà¾ÙOö>Ñs§@ç€ ¸Hiiie%÷ «ÓfffræÌ™ÊʵF£Ñm1%''óâ‹/rË-· ßŠl­Ûù¤>Ôjxz<3Ú93^9{{î ©C4î¿&‚à‹dYf÷îݬ^½š­[·ròäIÂÂÂhݺ5&L`øðán/ü¥ LyÞ·îÓ™žyNgÁš7!.Zéh [ц»©ÂžyÌ ‚ ‚»]1éÖjµrîÜ9J¸{.Ë2§OŸÆl6£Õ:V• 8øï©¹]^I’DVV–Û“nÿ÷¿ÿqèÐ!Ö¬YÆ 0ôùL a¯Íâ9*¦¬›~ÿH^yx\Ëï?þ]ž|¤òïgé– ž×™o“!+,-@£V:Ah0dkæüõ%Ü*醮Ы ìÞg½`%¬ö•D4IMyî{"éVÜÌ\ð²­~S|:“¯Ÿëd›³þst‘÷»u¿žt-QZn`û_¿ˆ„[7Q©Ôl8øHºwûì3¨çÚÎtpðåù×oïÕb}ðîùç¡À=Î Í9 øüs¸¿á¶¯X¬öé <(á|´ŸÉÞ7*’n/PPPpQEÚ‚‚‚‹ªÓ^ø^}èt:¢¢¢HHH >>ž„„Z´hQù<>>žÍ›73uêÔj·#IwÜq3fÌàºëþ.m«öo…¥ô·zÅXQá0}‚çnÏD•[Aç9zô(½{÷¦_¿~¬[·ŽÖ­[“‘‘Á /¼Àˆ#HMMåõ×_wkLÚˆ¡Hª@d[©[÷ë ë¾²?&Üý“•ŽÆ1’*À­I·žxÌ ‚ ‚»]1é¶  ÀqÔšÑhdÇŽôíëXéÁƒ+Ÿ;2mŸ»É²L~~>²,»mÔÏ™3gxúé§ùä“O qË>«e“íSÖyX¢÷Ñ3§Øqh?AþþL¾Ë±-®NdðuÉ|¾g'Û÷ï%-ó,-ã»2ÔÚ“eÈ+ù‚àF–¢oñŒ„Û óžþÿƒ¶ì°O?7âN¥£rÙ‚¹ð+{’³äaÓ‡ ‚3ë7"I*dÙySÖ—OŸë$5æ‚nMºõ´k‰='b¶Š„[w±Ú¬ìJßÅfE£øÁm6n´ÏiéÄ©µëk>°(–c¯~ÛßÁuggÏ?®fYE¨Õöß¹“n=­}%·<çp@?ݹó}£¢*” ƒÁpÅÄÙ ßËÉÉÁê„ïÞþþþU&Ñ^ø^£FPÕ0ŸóÑ£G¯ø™Z­æ`Ú´i´k×îòσz`5D¶•×ûç'IZÔÁ=”CÁ§h4>úè#"""hÑ¢ï¿ÿ>_ý5‹-bΜ9øùù¹%ƒÁ@AAÇ3oáÔ±/ÐYÈεׂªxÃÃáÁAn Éa6›½Ê-À’µö‡#†=}ñëäkaã⪗u:I‹^J¸›gAó¤cNA”pŤ[½^$IWéöB‹-¢U«V4mÚ´ÚåV¯^MZZ¡¡¡ôèá™ó6›ââbBCCݲ¿1cÆðàƒ:œ¸ìrúb«œ°ì«Ïx쎻‰ sx½ÃFUvæË²Ì»_mä_xØpsII·‚àfæ¢í€ðœÎüv‰°xŒžnïP˜<ŠKaü ®þ¾¦rظÝ=qÖ•l3`)ýMp/¥C„BÆR¸YöŒ©¯+øô¹N¶`)úÚ­»ô´k‰ŸOD«Vc¶zÖqçËLÖrf¥Ñ9¡µÒ¡Bà ˰u+X<ë<×X‡½â­¸ ø¨nž ð/àó¯{O_yqåX,ðuÃn_É+•äQýu>ÝOö/ªúˆð€¤kÁ«™L&òòòÈÍÍ%''‡sçΑ››[ù¨ê=g$ÒJ’Dll,±±±ÄÇÇG\\ 4jÔˆ&Mš@ãÆñ÷÷wÂOJ•÷‡´Z-<ðÓ§O')éÊU5¡7cÊYâ”8ÇÉXІzH['‚àÚ´iƒÙ|y±NGÓ¦MIMMÅh4Ö+Ò`0Mff&999dff’MNNNå œ¬¬,²²²(++«v[jÌŸîy ·`¿ô)*Q:ŠÚù)ÕÌÄСfž}öYºuëæò}ºã˜AOwŤÛüü|lT9£*ùùùŒ;–;3޽{Ó¬Y3‚‚‚°Z­äååqøða¾øâ ~ýõWT*“'O& À½£|¥R©(((pKÒí²eË8vìŸ|ò‰Ë÷å0ìÈ/·˜YñÍ—èüxúîaµZ·[«6 ìz=[öþÀû۾ेƠÕ\ñÏÎýlç+h‚à6–Â- {NÂm…!}áÃ7`ì {‡Â `åç0bÜØÇAH” ;ýßþ _þ¹ç‹ãKücøé”ýY.#é°mI·‚à&VÃal–<¥Ã¨’/ŸëlæsXGÝ2E§'^Kì<¾_$ܺ™V­æçSEÒ­ ¸ËáÃç™ík?`ð Ü ’8@Î?o{ϯÛø ð°fõoçÎÁÑ£PM–³xbûÊ9½GUVöù~:°÷æФ[á"6›¼¼<òòòÈÏϯ|ž››KvvöE‰³ï;5†ÐÐPªL¦½ð½ØØX4nþ»º0é6 €1cÆ0eÊ”‹µ"ñS)2hBntÏ®d™Ý»w³zõj¶nÝÊÉ“' £uëÖL˜0áÇ»möIAwÓëõ;vŒ.]ºæø€5€_~ù…§Ÿ~º2©ÖYß-´X<î¾Õ)›s:ò÷8¶l¯ap$ÝþüÃ7`@o×ÅueÿþÀ³ÙÀÇÌÇLrr2S§NeРAnoãêsÌ ‚ ‚·©²÷Ãl6c0Ü‹Ct:‹-bݺuìܹ£ÑÈÚµkY»¶úÚþÑÑÑLš4‰Þ½ù¶ã›ÍFAAÍš5sé~N:Å”)SذaAAAõÞÞ·ß~KBBBµ#Æ’«÷˜„ÛÔôctyò‘‹Þk4|0ïMžÎ#ýn¿âºúÒ"îpÙûYyèî¼ €OxŠ'ßã¼€ë£Ô&3ø‰i×ÁÕdsVcšÒa\Ñ­½àû•0w)¬û Ž·'¤Õ$Àî¸ &Ž€ö­\g­ÉfÌ…[ñOxAéH¡A°mIVé¶‚Ïžë$ –¢í.OºuöµÄêÕ«4hP½æ—qFŸ]ïXÜåð+Ÿc.¼üz;}É·•Ï[>v AWG»3¬Z³ØlüxâwÆ]·Ò¡Bð};h4Wé¶Âà7àYì•o·œ\‰ ÌÜ3ßSi4öß½‹“n=±}¥Ül‰äT?M¶÷‘&6Q:ÁEJJJ*gsss/K¦½0©6??ŸÜÜ\ \KttôEFG£Fˆ¿(™ÖS ™ÄÅÅÅðáÙ:u* ¯+i¢P$a5qa„Â¥Ôí4î¹Þ9zô(½{÷¦_¿~¬[·ŽÖ­[“‘‘Á /¼Àˆ#HMMåõ×_wK,‚ îRTTĘ6m5bÅŠµÞF§NHOOçìÙ³N‹ËOÿ·»gÜEƒð×)_í0^ôÞ®]»2d;wfòäÉ<øàƒ.åŒcNA¼M•­«^ïÙ•/Û·oOûöí)))áÇ$55•´´4²²²(--ì#šccciÑ¢×_=ÉÉÉ^Q¾¾¨¨Y–]:êhãÆÒ§OŸ*?Ÿ1c3fÌàØ±c$&&V»½¯¿þšyóæqË-·0qâĺš²É /­Ý:‚óA£(¥£Ÿg.þ{'Ï`P•f öQÆÏƒ-;à‡_àϧ·OÃaÁÐ4:µní oòÜ{/€Œµä'Í ‰‚àj–âïìS`{0_=×YоÅ/öQ—îÅÙ×3gÎd„ 6ŒÉ“'Ó¦M›ZÇôëé?<½yõI²,s ó›J­t8‚àû¾ûÎóÛW`-pøØ¤ç ´nF͉²–d¾ýmxí+yEµ_GpŽ‚bû±'*/z¬²²2ôz=ÔøúˆˆˆZ´hÁûï¿Ï×_Í¢E‹˜3gŽWÜ?ApÄ+¯¼RyíЧO>ûì3Ú·o_ëíèt:&NœÈÔ©S—¿¬œ·ôtÊæ„ó} Åf3VùYjj*#GŽdÖ¬YL˜0 &¸¤ú¬³Ž9AAð6’,_ÞcâÄ NŸ>̓¦kHºwïN`` Û÷»jÕ*FŒÁË/¿Ì /8^ pÚ´iÌ;·òu«V­xüñÇ;v¬ã?GIüZÛgPIØTTÐð`¬]»–>úˆÔÔT Mš4áºë®ãÙgŸ¥S§NJ‡(8Èpv6¦Œ¹È²kn°Õ íxÄ-Ó® õ÷Å_0iÒ$ÒÓÓ±xh57áÊŠ~OôèªÞ¾LК°GÙw]¯%IK³/*•о}ûÖz ßâÝëøßžõ”[ÅùB F¿IóHÇ«‰ Êí«—KL„4Ѿ*¢uk8ÚðÚWކ´33+UƒsSOñåõ /JŒ­M­«g+Edd$QQQ•I²¾Wñ~LL 111„„„¸4&_f)ÙMñÉJ‡ÁÊ òöçÉ×ÂÆÅžµ=g1[ ¼ÃèB®W:ºtéBjj*z½^L]…ÜR=ef#%¦2ÊÊÌ& f!þAhýÐú¤ " „ h³„ú+-7P`(¦´ÜPy¼K+·@?!µÓßJ IDAT~öã/:(\ép=Zyy9ééé,X°€eË–1k֬ʤÈÚ(**⪫®¢°°°^ñêXõª…›º»?÷Ä•ía¯ap$ÝþüÃ7`€›'\¶Êú<á#™-É£>Ê“O>I\\œScqÖ1× ˜Ì`µÚ¿Y¬`µÙZ5¨Õ VF :­ý_A¨¯âb8wÎþoi©ý¡×C` AH„‡ÛŸ7j¤t´‚e™òòr¬V+V«‹Å‚Õj@­V£ÑhP«Õ¨Õjt:K‹’ºZ••n Uäâ nb0Iºu–cÇŽ1iÒ$fÍšÅÈ‘#yæ™ghÚ´iõ+•Eu*¥È2”xÆtBÕ¦L™ÂòåËyýõ×Y½z5¡¡¡ìÝ»— &еkWÖ­[ÇСC•Sp€Íx³Òa4X6ÃQ‘tëáÒÒÒ˜sGÁNGxxxe¢ì¥ÿ^˜P{ág5þ} N¥ î…Ú¯9VÓ ¥CiVlŠdÉÃ1vìXþùÏ­Hz½žcÇŽÑ¥K—Ÿp›WVÈÞÓ‡IË;Ãñü³¤åžá”>‹r‹ãýØaþA\ՄĨ&4‹ŒçšØ«é˜Ð?˜uL¸œÉRÎþŒcÉ9ΉüLû±—w†B£ã3¢ú©u4ˆ£etSZD&Ð2º)×6iCT`Ãþ{® ÓéhÓ¦ ‹/&;;›™3gÒ³gOúõëW«í„††2fÌÞxã:ÇÎæMŸÐÖÿd«g϶ìm4Úöø“/¾ü–yóæ±k×®j—ÏÏÏgΜ9¼öÚkÜÿý<÷Üs´mÛÖ)±8ë˜ó)&3äÙ‹Ï•ìSì•k7°U«à ´_†AD¨T®‹[ð^üø#ìÛg<~è9ùùŽo# Z´€öíášk ];¸ápr¢¾à;JKK)**¢¬¬Œ²²2JKK)//w8çT’$t:AAAHhh(AAA.ŽÜ9ªLº-))I· ‘$‰²²2¢¢¢”¥Þ Y¸p!‹/fèСLš4‰^½zU½p©Á>eš8îÜOŠË”ŽB¨ÁèÑ£III©|}à 7°zõj:wî̳Ï>+’n½„µìÈ¢Š¼$I‡ÕxÑÍëÙf̘A¯^½øôÓOiÞ¼9¥¥Žw´ žÁV~d1¸@)²\Ž­ü4*¿æJ‡Roû÷ïgüøñ̘1ƒQ£Fñä“OÒ¸qã*—ýëÜ)lâ:BZµšùв«Ò¡Õí«8qÌ¢}ULy9œ> Í›+I½Õ¦}¥¨TôÓ)BIeï+õaƒ‚‚ŒFcåóKWú¬âý¬¬,·ÜÃð÷÷'""â²G@@À?«xÄÇÇ{uÅ–†Dý†Œ9âzÖÅ$IËòÏ´¤¥¥ñÜsÏ1kÖ,† ¸qãÜ–”STTĘ6m5bÅŠnÙ¯')-7°çäA~>}ˆ]ÇS9U…$IhU,6K®¯ ¥¤ž=ÊÁÌ¿@’°X-hÕjÚÇ·¢WóŽ\wU{:Æ·B%Ή ’M–ù=ãO~>uˆÝ'ç`æ1ÌV+µd¹N©MÖrþÊ=Mzî4j f›dhGòÕ¹îªvô¸ª½¨Â <˜Ï>ûŒM›6Õé\Û·o_Þzë­: XŠŽŽæ«¯¾âÚk¯Å”ó/ÊN<†«Gö-úf.¬ú³]¿Ad¿_·o?¬r|ÛϾﮫú³aO_üúôw®C§" é«¨5Á <˜Áƒ³wï^,XÀêÕ«++ V¥¼¼œ•+WòÁpûí·“’’âÔv¸¾Çœ×²X!·ò !Geç¯éT*ûµõÛ×ó/«b¶@A1èKì‹Ød{NMDD‡CT„‡\quÁÇÙlö$ÛíÛaëVøé'{ÿ¡Ng?ÞêÒ—h0Ø“u­ÖÞ'ö¸„¾}íÐPçþ,‚×0õÑX,$IB’¤:}We“É„ÉdB¯×#Ë2²,£Õj ¯ì_ñ÷÷wÁOS’\EÏÔŽ;Ü2Ò[¸œ$IÄÅÅ‘”ä¾J|&LàwÞ¹ìý°eË–ן6msçÎuh_½zõbÒ¤IÜu×]h4ä|ÿþdœSÖ)E­‚ÊOç$Ô^`` &“©²1<™Œþ× d›oß8óTt1# ¼ú]¥Cªa0*+ú4iÒ„¬¬,1ýµ—1ë7Sòç ¥ÃhЂ“¾D6Ðmû«ïµÄ…Ó_WÇÏÏx€I“&ѹsçÊ÷edz¼5“µ¼v N¡V©ÒîFf¯t(B5Dûê6o†A¢}UÔ—_Úo.¸‰Òí+[~²ßÀÜOšÄA‡–JGR¥êaIžÍÍÍÅìæ5%Ç^éé±7vç²™Ò)ÜŸˆ’%¾=ýµ+§Ó®«]¿I ~´êßq×®]7nÇwYU£W^y¥rªë>}ú°páB:tèà’}y›,óó©ƒl8ø=Ûþ܃ÙjF­R»|Ö ZÙj!20Œmz2´}’b›»t¿‚gHÏ;ËWGäÓß·“S’F¥Æ*[]>®K«Öb±YШT\߬#CÚÝDßVÝѨ¼sæŠúZ½z5ÇgĈ4°ÙllÞ¼Ù¡ê©WÒ¨Q#¶nÝJûöí+¶Jñ¡XÊRAv]ŸDuI·—rfÒí¥\št+iÐt ¤ý¯ÀåOÓÒÒX¸p!Ë–-Ã`pìžäµ×^KJJ ÇG]ÏY^êrÌy-È+„39•{>±VåúLöä[› ~Zˆ†&±öJ¸‚ï;|Ö®…wß…³gíɱ‹ëNûùÙ“pµZ¸õV9†µ¿|šÅb!//¬¬,ôz{ÕzI’\>º"ÿI–e‚ƒƒiÔ¨±±±h=蘻,é¶¼¼œüQ©x ,,ìòŽnV›¤Û ñññŒ7Ž'Ÿ|Ò^Õ÷ÇöQ:‚rúu眜„š•––LÇŽÙ¿¿Òá5°™³)Ü×Hé04MH2!×ìT: ÁA")È;™²Qvê)QH)’–Àfóñ‹}LéHæhRÐ…’““IIIᮻ¼”¾o‹„O%unœÄòa/*†à Ѿz©E‹à©§Dµ[¥hµ0><ÖpÚWU†o~qQt‚C"B¡gûš—s€Ñh¤¤¤„¢¢"ŠŠŠ())¡¤¤„ââb +_—””PPP@iiiåk½^_¹^QQ‘Ã7í-<<œ°°0BCC/z„‡‡_Tù$""¢Ê×*1ýªà€’#}1ïpi"Pu|>éVÒ0vVŸ|™]íbaaaÜÿýLœ8‘víÚ9=ŒòòrÒÓÓY°`Ë–-cÖ¬Y•‰¸¾(»8Ÿ~û‚Ï~ÞPr>ÑV¡k4 «…Ö1Íx K·»ZÜ—ò%åV3Ÿüžµ©[ùóÜI´j f«rןj•›ÍF¨wu¸™á]o#68R±x\å™gž!++‹U«.Ï 1b«V­báÂ…<ùä“ÕnÇd2±víZæÎËáÇëO||<Û¶m£mÛ¶½o-ÝKÑ׉!ëKRÚîWÔ]ª],''‡·ß~›E‹‘——çЦ[´hÁĉ;v,W\ÎYÇœ×2–É {²m¹Å>[‰’ǵê|nH4ocìvßa2ÁûïÃÛoÃï¿Û«Ù–+XˆD£±'úFEÁèÑ’WšIIðZ………œ9s†¼¼<·Ì4äI’ˆŠŠ¢I“&„……)šKߨ®Ô¼àî鯄ÌÌL^|ñEæÍ›Ç½÷ÞËsýî¤mt¼Òa5l«Hºõ2ü1Ó§OW8Á²µPé<ÙªW:Aðy²µð|“Ò‘4T*dk‘ÒA¸Ü®]»ØµkW_}5÷=ü Vu9êÒa5X%¦2¥CßWX(n–(I¥‚¢†Õ¾Ž5šqI݉Q:¬«¸HOÁ©S•ɯEEEV&Ä£×ë)))©|¯   rùÒÒRôz=ÅÅÅŠ´¸(I6""â²ÄÙŠdÚª>«ø\ÜÁ?aæ#}•Ãg•”ZØò}Íý£………,]º”eË–që­·òè£2xðàzWÝ« ÓéhÓ¦ ‹/&;;›™3gÒ³gOŸ›þú´>›ÿý¼ ¿G’ÀrþÞ¯b ·XÎ'_Ë=Å+[ÿË¢ñÏwòŽ·à¯õS.6¡Þ fë~ÿ†ÿîYO¡¡ù|ç ’ ·ÖóÕœ %¬úí Víý‚;ÛßÄèCi«hlζzõjZ·nÍÃ?L||<™™™,^¼˜U«VѵkWÆŒsÅu yÿý÷yõÕWÉÈȨWÍš5ã›o¾¡eËËgmPuÅ/v¦œ¥Š rñz’¿Ø 5&ÜÄÆÆ2{öl¦L™Â|Ào¼ÁŸþYí:éééLš4‰9sæðØcñÄO]å²õ9æ¼V™ÒÎÚ“máïÊ¢J'’WÌ&]RÓáÏSТ \gŸñXð^¥¥°t)üë_—÷÷1§dÂ-ØnÁÓüùöÇ#ÀsÏA‹І&Ô_~~>'Ož¤¨¨È-mkC–eòòòÈÍÍ%44”fÍš©Ü€ªË*Ý–””°wï^¥â€+Vð¿ÿýOé0ÜJ%©Ø­“~»U–‰ܨw'1å€ÉÎΦS§N <˜eË–)Žàkéoêªt šÊï*Â:T: ÁA¢Ÿw2œž†1û-°™”¥aRùÑsX0Gÿr¬r¯Pûk‰è~5ÑÉ­ÐE+NƒÓ(4НÆýGé0‰öÕKM›o½e¯j!¸ŸŸ×sÄÁÊ@¾"40ˆQ·ÞÁ“ƒÿAËxQ­ÄÝn9™m{VlÿZ­–àà`ÂÃà ¹, öJ•g/MžÕéÄÀ(Á»ÿÑK鯊$ùv¥[5š k)úœåË—óöÛosêÔ)‡×Žçá‡æñǧiÓ¦N‹ê½÷ÞcôèѤ¤¤0þ|§mWI™E¹,ܱ†-Gv£V©Oxt„JRäÀ˜C~ímhÕ—Õ‹´ê„BoRTTÄÇÌÇÌÑ£GÉÈÈ@§Ó‘””Ä?þñRRR¸l½ôôtÞzë-Þ{ï=JKKëGRRÛ¶m£I“&W\F¶•R|°3VÓ ‘x[[’µ®)!í÷#©k?@Òf³±yófæÌ™Ãž={ZÇÏÏûî»éÓ§“””Tù~]9¯e0ÙY3rí¯=(íŠ$ìÕH[6†æ öj¸‚÷0™ì‰¬sçBIÉßI®žL««xÀž$|ÕUJG$ÔR~~>ǧ¤¤Äã’m«Rcpp0-Z´ ""Âý1\št«×ëÅ4å [¾|9ï½÷žÒa(¦UBSt7c!ÐÏ_épŽëÛCd¨ÒQÈËË£_¿~$%%ñÁ8m”¿àZ–âï)>ÜGé04•&’°kÖro&’‚¼SÙÉ'ÎW*ðý™<’¤ãúaAü™V t$Ê$‚c‰îÝšÐkì‹‚Ë…ùóÃï*†à Ѿz©'ž°Wµh3#y$Žk‚‚8RÐ0ÛW•¤¢o§®Lr/ƒ®ë%Ê»É]sžgýîïkµŽ¿¿?øûûQù¸ô=G^‡‡‡‹ÿk¡A2~IÉÑÛÙ·o'ÝBpÒ´a{ÒÏöíÛY°`›7ovøf®N§ãÎ;ïdܸqÜrË-õ>O­^½šáÇ3bÄV¬XQ¯m)Ílµ°ò×Í,þq6Y®¬(ë5dP©U$„Æ0³ÿXz\Õ^éˆüxò/½Œ¬¢\¬JWy¬JJ¥âÑ^÷òp·;Шν¶}ûöñÖ[oñá‡:Ü?œœLVViiiU~~Í5×°mÛ6jÜ–ÕpâƒÝe1°´6$IKH»_Pvª÷¶vîÜɼyón‡U*·ß~;Ï?ÿ<={ö¬÷þ½†M†ãpì”}v?O@«’øûCÇ–%fñ [·Âøñpê”=‰ÕÛhµ VÃìÙðÔSöׂG3™Lüõ×_äææzE²í•DGG“˜˜ˆŸŸûfиl¸ Õÿh}Œ·ÀÎr,ã4“–.à•5ï3î¶;y|Ð=$Dzÿ(CÏ%’w6Ø Pii)  mÛ¶¬X±B$ܺ٫¯¾J^^}úôá†n 8Øñjz²µØ…‘ Ž­búkAp5ÙZx_'»Ï­4èß¿,Sr,›’cÙ$„}CkÂ;7CÒˆ)´\E qƒD\®¤¼ b”ϲZôïß&ÛØ–ú ÛR¡s‹V¤ÜyÃnº?qÓÄ¥]׋×w%88˜àà` ©|]Qq6((ˆàà`BBj_éJ„Ëiâ ì‚Åp@TßsIƒ&°SeÂ-ØwúõëG¿~ý8vìÿýïy÷ÝwÉ«¡ª|yyyeU½¤¤$FŸq㪭hôÌ3Ï••ŪU«.ûìË/¿ {÷îuüá<ï§ÿ७Ë8]Í ìÉØ…ç÷Ñ+ônÑ…·Ž¡QH”Ò‘ UÈ-Õóæw«Ø|x'*•Êk;‹Í 6+‹v¬áãý[™yëXz6ï¨tX.U‘h¹iÓ&‡–×jµ :”)S¦Ð½{w>øàzè¡Ë–ëÚµ+_}õQQŽýͪÚÐ|eÇÇaï]Øü§$Üôîݛ޽{sàÀ-ZÄŠ+0W\Þf³±iÓ&6mÚDrr2S§NeРA¾=P/¿¤A™Ñ;“m+È€Ñ{AL8th þîKHj!3¦L>°'­zkþŽÙl¼ð,Yï¼ýû+•PY–ÉÈÈ ==ý¢÷¼U~~>{öì᪫®¢Y³fni£.«t›››Ë¡C‡\¾cáÊÞ{ï=–/_®t£ÿµ×±âéÄ…G*Šo»6 ‰ Of±X2dZ­–O>ùFL³änÏ>û,¯½öjµšÎ;“œœLïÞ½éß¿?aaW!h.ø”’c÷¸+T¡*’šˆî↉·•ø”³oß>ž|òIn¾ùfúôéC¯^½ž ª4måyk¥J‘¸þÁþL+R: Òª‰¾)‰Fý;ˆª·.¤’Tì{zµÒaí«rêÓ¾2l¬]ëÝ7y¼™$qMHGŠDû  óã黇ñÒCc|û«Ò$ nk@¤ÁƒXK¥èàâ$²EÀÌ…Ž-Û¾üpyΨK·ç<!í~Bt]µKF6nÜÈüùóÙ½{·Ã[ aذa<öØctêty"Ò3Ï<Ûo¾ÉìÙ³yøá‡‰'33“Å‹óꫯҵkWvìØá•S`[mVþ½s-ïÿ¼I%aó¡ABZµJÃK'Ð?éz¥Ã.ð]Ú^žß¼“¥Üž´ê#Ô*6›Ì°kðÔM¡UûÎý·òòrÖ¬YÃk¯½ÆÁƒZ'$$„Q£FñôÓOsÕÓƒ[,9yòdå{Ý»wgË–-DFÖþ>~iúCö¾d1Ð¥z’]ôp‚®~ße»ÈÊÊbÉ’%,\¸gyILLä‰'ž`üøñøûûÐìŲ ž‚´³ö>e_ꊑT :¶‚x‘âQ>ÿFŒƒÁ·fºR«íÙŸ|^{ t:¥#Î3™L:tˆâbß+'IAAA´k×ÎåíÓe%T*QHi¢Ãü´Zéw;ûþý>_½ü–H¸u¨˜êéÆÉdâ£>º(á611‘Ÿ~úIÁÈޏ¸¸ÊçV«•½{÷²páBî»ï>bbbèÝ»73fÌà›o¾¡¬ì’ªªª 7G+\J’|è¢_\(**Š]»vñÊ+¯Ð¯_?"""¸é¦›˜={6ß}÷]µ#Þ%U’$¾S(Åþ»×sÚ° ì@›éƒi4@$ܺšŸFT:GÔ§}%(ÈÞI-(C­Ñ_Jã¨æ<<žSË?ãåcEÿ¥«©ÄïW”¢ê†_Ì$I|Ï­/IÒâ÷h ·þþþÜ{ï½ìÚµ‹_ý•qãÆXãzÅÅÅ,]º”Î;Ó­[7–.]ŠÁ`¨ü|æÌ™,[¶ŒÝ»wsóÍ7J‡øæ›oø×¿þåµ ·g sxpÕtVüºÙ§nÌV F‹‰g7.àµoW`¶Š„<¥•[ÍÌÛþ>“>{C¹Ñ§n¬622k÷}ÍC«gQxNéê­¸¸˜ вeKFŽéPÂm\\³fÍâäÉ“,X°à¢„[FCJJJåëo¼‘o¾ù¦N ·AW¿‡6ä&ª˜œY8O’´h‚“ jþŽK÷Ó¨Q#fÏžÍÉ“'™?>M›6­q¿þú‹I“&ѼysfÏžM~~¾Kct‹2#ìúÒ3ì¯})áìƒÊl2ì; ‡OØŸ Ê2™ %†µÏtåK ·`¯Ö+ËðŸÿÀõ×ÉJG$`/ÆúË/¿PRR¢t(.!Ë2¥¥¥üúë¯äææºt_—Uº-,,$55Õ¥;ª·|ùrÞ{ï=¥ÃPDlx£úÝÁ“CþA㨥ÃiXzv€1§š={6[¶laÛ¶m_ôYbb"«V­âúëňoW[¹r%?ü°CËj4:uêT9UZN*,Çoqq„BuTÚXºd+†à Q‰O9F£±ÚN—žßz÷î]9R°ìÔdʳßF–ËÝ®pIÒqý°`ަù@g4Ž êú–DtkŽ$”¹Md`(ß>¶Té0‰öU9õi_™<Þ~ÊEûªŽk‚ƒ9â 7ëàÚ–IŒ¿ýNî{þ¢"‰ûè´ÐÏ»§;o&[ò)ÜŸˆlÕã{Ùî"!i 똆¤‰¨Óôz=Ë—/gáÂ…MyZ“ØØXFÅ„ hÞ¼yöíɶû…é_ü‡r«Ùç«¢–T´ŠmÆü;Ÿ&>4Zép¤³…9<µá ŽåžÁÚŽ9J…N£ãÿn‚›»)N­UT,]°`z½Þ¡uZµjÅã?îPÅÒÒÒRš5kF÷îÝùôÓOë=pA¶•Qr¸–²T},Ù¬¾$-ꀶ„\³I\óòNd6›ùðÃkU!988˜Ñ£G_V!ÙkdåÃïÇìU9D2ªapmðS:˜†éøq¸ûn8xB_­Fþþ°jÜy§ÒÑ4H²,“––ÆÙ³g•Å-$IB–eš4iB‹-\R@ತۊl_o’››ËæÍ›ÙµkYYY˜L&¢££éر#·ß~;:tP:ÄZÙµk—W%>8p€#GŽÔk[3iÈ}}èÙöâ× Q™\aÝíü †øíK^ûv9Èr:65*5Á~,»o­c¼ç;™/8šs‚qÏ¡ÄTÖ ’¼+Ø“2d¦öŰ.u»'än¿ÿþ;¯¿þ:kÖ¬Áì`¥Äääd¦NÊ Aƒj•ˆ²qãF €ÎIƒòdsEôÀV~V$ÞV´¨uMi·I£l¡²;w2oÞ<6mÚäÐò*•ŠÛo¿™3gÒ½»—\‡È„ÃÇíÏNójŸaE£í ¤æ'JM…~ý ¨È÷ªÛVG¥²W¾]¸žxBéh«ÕÊ¡C‡Ðëõ\’&êó$I"<<œöíÛ£ròlf—%ÝFöìÙãÔ¸Òwß}Ç믿^mGò!C˜8qâEÓ±{²àà`ºvõžNÕiÓ¦1wîÜZ¯WÑá1qâD…5E*)«y%a¶X˜¹ê]^]÷6Ù>m7'Ýþòça®›<¦Úeüýý‰‰‰¡Q£FÄÅÅ]ô<**ŠèèèÊ$ݨ¨(‡¦~3hÐ 6oÞ\í2"éÖ5ŠŠŠ8wî¹¹¹äååñÛo¿1cÆ §lÛßzt„›{Àøû='÷ÀŸðÆ{°ù;°Ö0+YX¼õ íç–ÐêíÅ%±,x/‡ˆˆâã㉈ˆ !!áŠÏ›6mŠÖ‹‚ø‚M›61xðà*?§¼ó IDAT[¶lcÆTßV Γ””ÄŸþY§uàúNÐûZ¸©;tiëäàêÉ—“n‘t4™ƒü3JGâ°ÄÄDÒÒÒj½^hh(<òŽɸ-¯º 2‘er¾=BζCØÌ5߬jóü tAn¬îLßžàè{DûêÁDûê9êÓ¾½>À-€'Þ¶ú  Ô4·„x˜ 8¿¾€ èt0g<ÓpÚ×§}œfé®~ÍYÒ³2øÇÿMg_Zõ[w÷º‰åOÍ Ø[¦ ‚Þ”ŽB¼Òô”ç­Y XªIƒ.ò‚Z®qú¦Ïž=˲eËX¼x1999¯×²eKÆŽË?ÿùO¢£½gÀf™Å»×±t÷§ *ÙöBjÉ^}ô?÷L¥k“k”§Aøõô<ñé«”[Í ¢ÂmU$$F]7˜‰7CòЫ—Š„ÈÍ›7;”DS‘9}útº¯h+?MÉ‘~X˃­% UEÒ¢ö»šà6ß Ò5Q:šJ©©©¼ùæ›|øá‡€©kb·[;m4T’jt»"=¿„Oøþ{¸ã0™F…Û+™:þõ/û1(¸”ÅbáÀ7¸„Û ’$DÇŽzê²,Toºùµk×.^zé%l6ÁÁÁŒ?¾rú½Ã‡³lÙ2>Ì矎ÑhäùçŸW:d‡xÓÿA]„„„0lØ0&OžL›6mìoþ|ê^€Ã­Žœ9ÉðW_ä·´£žûå°–²õ5O‘h49}ú4§O;ö¥300èèhbbbˆ‰‰!::ú¢Gll,ÑÑÑDFFIDDDS–4TŽŽªW\\Lnn.çÎ#//¯2‘¶âù…ɵï9:¸.ütÐù¸w ç$Ü<ðdž³?ï˜O‚ë;CX0dçÙ“r®„Ÿ‡Âbøç  }®S6nGœ+°í)((    Æå%Iª<‡ÅÆÆVž·ª:—U<œ5²»¡4hPƒý²ï.F£±Æs_^^uÞG™Á~ŽHjё΋]¨™„•Ʋƒë.11‘'žx‚1cÆDi¹¶(•ƒd™Ók¦`ï üÚfDvmŽãÔ~ZÌ…eLFæ½_6¢7”0£ÿT’s«£Õ•ÍfcóæÍ¼òÊ+üüóÏ­ãççÇ}÷ÝÇ /¼@ëÖ­]aí©tM iû%GoÃR¶··»4 NÚ‚¤ñ¬A";wfÅŠ¼ôÒK,Y²„%K–Ô8›Ï®]»2d:uâ©§žbذaž“ #Ëp0N×4tØÇɲ½ ÓžCpmĉ›:.õé§ðÀ`³Ùû›²W_…Ü\xçût»‚K˜L&öïßÉdjÐ÷dY¦¬¬Œ}ûöÑ©S'üüüœ²ÝË*ÝìÞ½Û¥‰FΠ×ë>|8ÿÏÞy‡GQ®}øžÙšž„4BI¨AºT°Öˆæ (È'r°ŽØ{ @°MQ8b‰ „„ Òë¶ùþX²éɦíÌ&s_×^LygæIØÌ;ó¾¿ç÷””” Õjyçwê< VTT0wî\‡‹È£>Úâ²à®B"""ˆŽŽ–;§qÖé6::š¹sç2cÆ |}keÉ:i™`Söù;›¿âáUoQfª`ÚØkн'¬Y¸·Óíªÿ}ˬמ•; <<<pˆp+?­ûùùáíí···Ü᫸€ÒÒRòóóÉËË#??¿ÑOíIn“É$wø„ÁôáÞÛíN±Jcà»èv@4üoÝ‘·66ܵ¾ÿ;~Ñøn…kãl.‚ gŦKømŸ/999dff’MiiÛº¬ûúúÖ Xïý¬òÓQ’8T\KEEEû\õÄ‚êŸêÛ[Sfؘuó¦ß¤Ì{T9ݾºfÜ,w4mO̴ޗʆÓ8ãÄ'ãÆcþüù\sÍ5uÊ¿\þÖ?((/iÏ0Û„Ì-Èþß!Q û]—à78²Þv%)9$¿û#HÊwºÕi´„'KV¤ö¯*Eö¯AAÌ;s†Ù€»^©å\ l?·| ðEí>&Ÿ[Ö'°ö ­mر.í\ý+[÷€É¢X;bI’8ÿÁðgòæMŠeéìùuÚ•”—sù?ç8Ú-¹ë_ ˆÊ¯ÐQXXÈš5kxùå—9}ú´SÇ„……Çüùóñ÷w~„ãàÁƒèt:úõë×Òpër NeÛE§*8^þ/èÁò†ÒQÙ´ n¼Ñ>Ñ®~ïìh40c¬X¡:Þ¶f³™?ÿü³Ó n«#Š"z½žáÇ·IH½¢Û}ûöQXXØê“·'K—.å믿 66–ûï¿¿Þv‰‰‰ÜwŸ½~lpp0Ÿ~ú©r²gêAz÷îMxx¸Ü¡8MS¢Û‘#GòàƒrÓM7¡ÕÖ1W¶s"Ó.¼UðzfÞYÂîžDŸðHþ3÷ŸŒ<œUßodÖ2ûÏî΢ÛÅì6ç“••ENNYYYdff:–³²²()Q¾! ¼½½ñññÁÇÇǺ··7~~~øúúâããƒÑhÄßß½^——žžž |}}ÑjµÍzØW©K~~>‹…ÂÂB***(--¥¤¤“ÉD~~>ååå”””ŸŸOQQÅÅÅ”””PPP@aa!%%%SXXèø(E8Û†Îw1adZ'kUŠn׿—h¸ÝÏÀMØ— zHÿEéÏÂZ<{¾‰¡ë½5¶;B999ddd8–333÷Åììlrs›vo)µEBÕÅCþþþøùùÕ¸¿U¿—©‰î‰ÍfsÜï*ï………N%T~ÚZÔV‰··7‚ PTTäô1}úôáþûï'îž›)OìÞ.qµ]të?ü ‚¹Ý6& ªtàX¸p!ƒ jðS?ý7¥·¬\»«(Ï, é¬6‚¯èOØ„ÆËE'-ÝJÙ©\Å‹n5¢†GÇN'vÈøÛÕþUE.:jÿ:ûæ›1vWnÿj @¥ äwà¢FÚ÷*§‡?îh·ÈÚ3gÜÊí¶-úWv€<翯®æë]?só3öªfA¾~¤}ð5úú]*v&`äÃöwA£^Ïɾ!È×Ïe±6Q ×¬ÉˆzQQQÄÄÄ0pà@bbb:t¨Ú?©¨È€éì'”$ONì2ê^ÑŸ¢¼]–«²víZÞzë-8àôq~~~Lž<™yóæ1p ëÄÂΰì×µ¼·{=êw¯&Z†U·-fhD °T8qŒ{Ö>‰ÙjéÔÎÊõ!³.º‰ûGÝæòkgggóÎ;ïðæ›o:=ŽRi†‡‡‡òœ5,”ž\HEæRìBÀžt ˆ Ù0†>„Gä‹ 4 §P(&“‰µk×òâ‹/rèÐ!§Žñõõeúôé,\¸nݺ5ÙþŽ;îàÇdëÖ­ <¸µ!Ñ4H>Ú¿ÖBìr uUqWvï†Ñ£¡¢BÑš(Yxüqxúi¹#éPX­VöïßOii)¶N\µ >AÀËË‹¡C‡¢i¥Ër½=¶——EEEŠU:›Íf~øáÇúĉl;`À¢££INN&''‡={ö0räHW„Ù"$IÂÓÓSî0Z^¯ç†n`Á‚\tQcS-çðòP|ç¢Õhy|ò4þuûtŒ©¼© УOozœÕh³’’‡·¶0·Òá§rùÌ™3TTT¸è¨ÂÙ’¶Í¡1Ansöùøø8Dçþþþ‚€(ŠøùùÕ¸N%•ǵ†J¡k%•‚W€‚‚l6’$‘ŸŸ€Åb¡¨¨¨^lcâÙ†öu¼¼¼ "88Øá¨¶eËΞ=ëÔñ‚ píµ×²`Á®¼òJJÇQq&$e»É pQï¬!Õæ›Ýã9Í‚ÆXwÖÛÛ›Þ½{Ó»wï¦Ï`±Ôq6ËÎήá‚væÌ²³³ËåååNE×÷¯†<<<ðððÀÛÛN‡¿¿?:Α|àì¾ÎHõ{\yy9eeec6›ÞW)ø)..v$”””´› §6^^^Ö¹—Un«Ü^¹ˆÁ`à©§žâ‰'žhòü#GŽdÑ¢EL˜0Áá(Y¡ñA²*W Ñ‘4>n%¸mˆî½÷^xà‚‚š.£Ö;(’„Ìd,6åº_dÿxÉjC‚G7=!ØçÁñM¶QV›•žuFÕþUí_Cí_[Ö¿âãÍ캒"jN6%îN•è6¿]"jc||ÜJpÛÍí_ñö„übŎ׭ûu»cùÆK.oPp pé€AD‡Eœqšr“‰µ?o㉷¸"ÌQX\̉ӧ$‰”””:eÓÃÂÂ8p` AîyçGhh¨L«¨t|ôw!Yò(=1WîPŒ€G÷WdÜ‚]ÀG\\ñññ,]º”µk×6YÙ³  €+V°bÅ çLd`ÇŽDEEÖ6>þŸíÿÕ.ܦ¾ÿ… ι:Ë çoEh'÷ «ÍÆý_½ÈGw>MT`D»\§³‘|æ÷®{«ÍÚî‚[wûÎ}åï_ãçáÍÝ#®k·ëTçØ±c¼ù曬\¹’²²2§Ž1bóæÍã®»îjµD6-žÝ_Gç3š’”)HRØ:Ƽc‚èWôÇèüÖ¼(½^ÏÔ©S¹ûî»Ù¼y3Ë–-«óS›ÂÂB–-[Æ»ï¾ËäÉ“Y´hQƒ‰/©©©|ñÅX,FÍwß}çœþ¤!NdBò©–ßFä—~÷$ÊLøxx’ññF¼ŒFyƒ’$@€½‡à’Áàíf‚}¥’W]f³lc,G€À6 ÈJ? œ\ ôwup’Ϩt¬kˆÒÒÒ‰•÷ÅêÇ™ÍfGyÁ²²2ÊËË÷( üýýë|ù¶ø¹2$$¤Á}:ŽØØX~øa† «[ÆOc샥äÏ]W¥uÔ—\àN >œÙ³g3uêÔzûà†èÕ%\Ñ‘µÜLÁûD’gÏ ´nðîÓztiÝ„²Ú¿ªý«œ¸SÿJŸ>ð§2ûW ¨LML»3œ¬¶Ü´4_´eùJhiÿŠ·‡}ÌH¡ï¯{ŽV9( ‹nú]ïü>ýIΰ÷Ç÷ü¦lÑmi ®»ŽC‡“ššŠÕZ3±(##ƒŒŒŒ:Ç…„„Cÿþýÿ0 yc´*** by›)òŒWéðŽ{ÍFĶcèCrâ`Ĉ|øá‡¼ôÒK|ðÁ,_¾Ü©Rì;vì`ÇŽ„……1uêTî¿ÿ~"##ºæâÅ‹9~ü8ß}÷ýû·N®ðã±?xþ«•Ú +›d£Ì\AܺgøbÚKø{¨Ž|­!·´Y랦ÜbÂ*©÷¸†xå§éæÂèèFJ¶’ß~ûeË–ñÕW_Õy¬Q¹îºëxä‘G¯ƒhº€ð”@IÒ-XJâéhý¯ hÐxœ‡WŸ/ ½ä§Õˆ¢Èĉ™8q¢#ùåÓO?mô;l2™øè£øøã;v,óæÍ«c¸÷úë¯c±Xû8ÝÕW_Í·ß~Ë¥—^Úü ³ráPJók>þñ{ÊLö±·¢²RþûóVf^­áµ$Å{`ÔÐwNÓ€6#;ÆŽ…ÒRpâ~ÞÖŸQÿðΙsŸ¿€ÏmëÜ L\:"öÐC“&¹òª’#GŽPPP êˆA’$ 8zôh«ÞÝêÝz{{+ú—_Ý–¾oߦUûU?|øp»ÄÔVètºV»[ÊÁ!C˜3gS¦LiÙä“Ñ:-˜-mœJãØ$ðkû’µ•.PΊtÁ>y^ßäxcÛ )**r™£‘$IQ"`WR{RÜÏϯÎye¹à€€€îRm•ÐÐØDy@@÷Þ{/sçέ×Ñ@ë9 ©dãæÁ +íË›-o<Î j» êë:ñµ7•¥©{öìÙ¬ãl6›ãžWY¹¨¨ˆââbŠ‹‹),,$??ŸââbÇöÊRÊÕ۵形Ò;''§ÍÎÙðôôt|ðòòr¬ûûû;–½¼¼¼¯ùûû»üù°¾{··7÷Üs ,h4éMãuÖ²ƒnq¿ûi¬ù þ8góÁÃ]á¢ApóU0¦ ó®Fth¼Ý(àsˆ¢È„ xðÁ7n\‹ÎÑ¿kO,Vå¾GËB²ØÔ<"¨È)âìŽ$ŠŽdb.(EЈèü<ñŠîJà%ÑC\òº~Fo‚½\~]µUéŒý+]‚«Š@ðü¹õW€u ´]¤ž[îŒi×ÈÚÎþ»w3Ú¢Å×Ë>f¤P²ó«îÅA¾ 'Tâßű¼?%©]bj+º……³a“=#¶¢¢‚Çsøða:Dbb"‡æÈ‘#uª UV¨Ú¾}{í~~~!n¿~ýèׯ **ªÓº®«¨´È±™³0ù˜Ž&üi9"úà)xD>ßtS eÑ¢E,\¸ü‘¥K—²yóæ&ç@322xñÅyýõ׹ᆈ‹‹cìØ±UUj‘˜˜ÈO?ý„$IŒ9’õë×3jÔ¨Å|º ›Ç7¿$CöKôœ±xõªßÿÐSë±Ù«ŽDÝ;ïè®õ¶K|fæç9[‹Õf%¯¬G6-ã[CTpB®’±I‹6/£°¬ØåU„Üí;€$ðèæ7ùjú+„ù:QAÂIl6›7oæ…^`çÎNc0¸í¶ÛxôÑG0`@›Å¢$D}$>1;©Èz›²S!I”÷^ÜAˆÂáþ¨Âî9*“_–,YÂÒ¥KYµjU£sù’$±mÛ6¶mÛÆ°aØ?>wÞy'EEE¬^½ºFÛ‚‚®¾új6lØÀ˜1ÍU(-‡¿”ó.¸òû 5×·lP†èìÂÛ 3ìO‚ bì?*ÍÇfƒ;ï„Ü\°¸~þbpPxnýês룀@r`3°{5«ãÀ³À»Ø¹.åî»áÀèÑÃÕWî0ddd••å²ë 4ˆŸ~ú °¨.X°°ëf¾þúk§Î±eË^xá…ö ±Q²²²ð÷÷oqõ¨zE·¾¾¾çœø”9°züøqDz3?x×®Uá§OŸ¦¢¢B‘ÂVApý„eky衇xþù6P ô³g)ô{×að÷–;  jòÜÙÌñêT:.Ô˜$ÏÏÏw”­ÜV9¹^^^îø·¬¬Œ¢¢", yyyX,ŠZºÓ]¨tÜ @«Õâãããpäªtòª,Ï[}’<  Î$¹¾¾¾Š¸w×7QÞ«W/æÏŸÏ=÷܃·wÃOïK’¤,‡¯¦$()ƒÔÓðóX¾Ò³!À–>#‡Ëaã‚­ßxÜé­PE‡«Z[Pé>WVVF^^žc¹ö¿­ÝçnF÷¦Úÿ¶vŸ——z½{˜U¿×EDD0oÞˆ®Ë­”xSÞ—€PæX`ƒ¬ènÀ£û²Ÿš¨¨(–.]Ê¿þõ/Þ~ûmÞzë­&ûÞ}ûö1mÚ4–,YBLLŒ£ÊRuŠ‹‹¹öÚkùüóÏ™äŒ+¦$Áþ£`µ)¢šËî#‡øûø14¢ˆÕfOäÚsô?Æ^ © $Iv§’”Ó­VOiÏ=Û·ÛÅ·.æ `2ö4AàSàÆzÚ…ŸûŒnœKûh$ ÊËᦛà÷ßÁMÇ夤¤„¤$å$¸ Gu蓚K½O¢(âëëÛ¢Ò‹® ºL—.]ii'00б\él£ÔÃÆÊ@*•VâWtNt«âB»jNå¤Õˆ¢H@@@› ×k—B­.È­½/??³Ù\ツ‚l6’$9ܬª {+**jdø•””Ôq,i.ÕËÏBU)Y¨Y‚ÖßßAE???t:ÞÞÞŽÉm___´Z­£ômõ}>>>ŽÒ·µ÷uTª'rT϶¬ü}6† Ñx_Œ¥è7ñVç­‡Ÿ«¹íÂÁpÿpÇðWàem$IBë;Vî0dÅÃÃÚm®©rÓ@²Ò……… –ª<‡Á`ÀÓÓ³Áëúùù!VëÇ*ûFÓd9n•† aðàÁÜÿýÍ.Eì.ÂÏ”S0| ̽Ë.®õ÷…¼øe/¼¼Ú.¼ÝµÆÍ€߇¶3®h$Ðú\.wͦ-Þ% ZƒÂú°ïÔ‘f p\AyfÕ{uá¡Ó”¤ä õ22i(¾1h½ ˜ Ë(ØŸFÖ¶CØLr~:ŒÍd%â&%g¶H\Øc ÜAÈŠÚ¿ªýksiMÿªtá§»‹í·À*àìöÕÑÃÛ€ÙØÅ¸nÁå³EíÂÛ¼"EŠ~‚|ý).³;š)Ìo²}nQM!j~I±2E·’d7&h­VKTTQQQuÜŒóòòjq:DJJ ))5˨Z,ÇömÛ¶ÕØPGŒ[é”ëÌø‡ŠJÇFÀ£û+ÚÊN-Æ]ÆøÚÈç0†="wͦOŸ>¼ð ,Y²„7òÆo8å*yäÈyäž}öYî¸ãæÌ™Ã!C())áÃ?¬Ñ¶¼¼œ;¼¼ûŒo¬ONXÄT((QÌ»íÊ-ëX|Ç žûìCLó¹íxë¾‡ä ­&p4 ºøØ5%*ÎóË/ðIJnÓ©ªËñ%p­Ç…b׆=y^Ìf»Óí£Â«j’cs°Z­$$$ȆےÀù矦ZÒ¾348B@QQ6nMQ]˜D;œ IDATæŒëam¥–¡—$É-E·mF Ÿbt: "v±³JƒF‡«’R¨>ñ^)˜Uq¡¡¡Œ7Žyóæ1qbóË|èüÆc-þÝíÜn«³?´ðƒÛ®qݾÕmD€îLeòC%Õê*îG¯^½Ø¿‹úAˆÆ£Ö2çœäbÆÍðòš÷° ˆ½®»nzö„Œ˜ý|ó¶|±:ƒÆs ‚VéÊàöã’ƒù;ý›òÙ­¥Žå’”Dƒ–è9WbèZ5P©ð"xÌ<{‘òîv$›ÄÙIø Ç»oËÊÚ´7VÉÆ…ÝÏ“;ŒÚ¿v,ZÓ¿ýú“ÎGr ù@1P_ ©( €2ÜDt;p ´‘;¶[äùEŠÔ“ êCj–]œðç±#M¶ÿëø±ëE¥%íW«‘pJtÛŒ5ªNYó¼¼<‡È¶º7!!¡Ž³z^^ñññÄÇÇר®Ó鈌Œ¬#È>ž+VðñÇ79‡XTTÄŠ+X±b#FŒ`À€õšY,âââ8}ú4K–,i2ž=i ü÷Ï-ŠL&uàŸ›–±ñž×1ê䯘ç”™+X´i™Üa¸-V›•÷nftôpÎŒqú¸””–.]ʪU«œÖ, :”ÿû¿ÿsÚ¦££óÎo<æ‚(?ý$–âØe/J{ž°Ç¤ñºcÄèüj§¦v>¼¼¼xà˜3g›7oæ¹çžã÷ßoñùL&·Þz+«W¯fêÔ©õ7:[©Î Û]AQY)kÙ†A§cÞ¤XžHá‹ß¶ðñöïyù÷ã¡WX?öçQ=4ŠŸV%%pûí²U±{¨ñ˜†s‚ÛJü€§€)mTs°Xàõ×aâD=ZÎHÜŠ””ÊËËÝ2‘êšk®ášk®‘íú’$QQQAJJ }úôiÖ± Þ)¸{ÖJ%ÎFçw-‚ ÌÒ/£†Ûïk¯.j8iÀË–?Qµÿ—½öûžR=:?ù^H•À…=ÎS¤àÀZ^³ß¾¢ Ámu¼zpA/ÇzövåŠë‚¼ü‰ô‘; ·¢µý+×^«ØÒjg+€»€­À•ÀO@!P$ÿŽOýÎíW4z=È8à«ýìcG äú .u,¯ÿýWÊL ÷9•ƾä£5¶é”*V0èÁ³}œÃ1b±±±,Y²„Ï?ÿœ½{÷RXXHbb"_}õÏ?ÿ<Ó§Oçâ‹/®7Ýl6;œq—-[ÆìÙ³?~ɬY³—›­žÞºQ”g¼¿çôQ ~y2^½ZŸl4à_“üòdmó\¢Z‹U²q¶¤€U»¿qéuÝ™»¾älI6œ•;Âw@žüa%[Ó‰þù'S§N¥_¿~,[¶Ì)ÁíÈ‘#Ù°aûöícêÔ©ªà¶:¿«ð‰ÙÏ€_Ðw™„ è H`ÚA@DA‡>`>~Å'æ7Up[ Q™8q"»víâ×_e„ -/±Z­ÜsÏ=¬^½ºîN›“e?Öǧ?ý@Iy97^r9Þ>Ü3~‚c_AI1Ÿÿú£ŒÑÕƒ$É É§äŽÄ}xúiÈÉ*‘µ'¿?W[ÿ¿œã(/Šgw¾Ui’¢¢"222ÜJpûÙgŸ1zôh>ýôS¹CìÂÛôôtG¥rgið‰Ã××·ŽC¬R¨înëL´¹Ö¢3FEÕ çÖ­[;v,›6mªa“Qx†ww}©ŠÛxvë*VMþ·Ü¡(š'¿_Agù{no¬6ïì\ÇÕý.!ÜÏ.œ3™L¬]»–—^z‰„„§ÎãããÃŒ3xøá‡‰Œl}ú°téR{ì1.¼ðBÒÒÒšu¼$I<ðÀ”––òðÃCY$T”àv_òQâ!2¸+ㆀ(ˆLw-Ï}ö!;p(-•˜î=eŒ´’á¢óäŽBÙÌš%«ÉàÖjËn?¢eµÂ¿ÿ ·ÝfU©—–¸³ªÔ$I“‘‘áô˜T£¢Ûfwf® 88˜Ã‡í^¹¹¹M¶?{ö¬cYEºtéÒn±µOOO¼¼¼äC~ü¼íõ|KËéRÕ¡lÜt;•6G4•òŒä£Uh5póxxýûú—ß+St«1öEã9Xî0TT:%ú é”¥?ëÖ]«–Ï4ýÚ! ‚ Ã<u¢&Æ\ÎêÝÊs=ª5ú&Ý4F‚FD²Ú'\-%Ê*É,=ÂèÜ]îPTT:'ӧóÏ*ª¼Ú—Õ–'Mù¢Þ‚]t ð-P†ÝECQèt0s¦Z ì"ÐäÓrGQ/c‡žÏÆ'^æ®——W\ÄÚ_¶±ö—muÚÝ2r4ÏO¿—¾³n :,½ò*¡áå>Ê3‹¨ƒÁÀyçÇyçÕpÍËË#%%…””8tè)))9r„âââmM&‰‰‰$&ÖÍp3 DGG3pà@¢¢¢Ÿª‚\7§£ n«0†ý}ÀM”¦ÎÁ\¸ »ó­» )@Bç=Ïžï {Ël„‡‡³dÉ{ì1Ö¯_ÏŠ+ض­nã üñ—]v[¶l¡W¯^,ûu-6 ‚Ü‹ÍÊÞ“‰üš²Ë¢†5}@'dû±½üyê0’:ÛfH6‰·w|΢QSX³f /¿ü2§O;÷ ÊìÙ³™?>þþþíiçA4ôÀ<óܸ)XËa)Þ‰­ü(ÖòDl¥°šNÙųΠhÐè»!zBc€hì‡Öû41íøStNŽ?Þb’$I,\¸ÒÒRþ}Êӛ¬Øb»ž>îzD¡ªBÂ=ã'ðüç9ÌWlYÏqÊcƒHœ-„ì<èÐtûÎÈúõðË/²]¾H©¶Þ_®@Ú› /†>’;EbµZ9~ü¸ÜaÔK^^£G–;Œ‘’’B×®]ë­òT›FE·žžžx{{× “›^½zñë¯ö¢u™™™M¶¯žyÁ ¬AUA u¿RãíFdW8’†âž‚:~^àÝQ8UT”ÆcÏ!XKÿÆïu}{U-'¦4ÜN6-úàYrG¡¢ÒiÑM¡ìô“r‡Ñ*j˜Ì(Ts#Iôwˆ"ˆ Œ _×ÍNSÔÄ‘1؇â£ö÷ÖJ!m“TŸpU˜àK#j¸yðX¹ÃPQé¼L™O*«=Rm9ʉöÕÛT'PàD€Åw«ý+`;òõ‚¢RE9Uríù“²ú –oþšÍìäÈ©”–ìëÏ}pÏU˜tÑ(~?\å06<º¯Œ7€ @dǨ‚VÝ766Ö±]’$ÒÒÒHJJ"))‰£G:ÜqSSSë”Z®¨¨àСC:t¨Î5‚‚‚ˆŽŽ¦wïÞŽ+—«»Gª¨Èhìƒwÿ­˜ó7Ršz/6s–ó‚¥ hµ]ðˆ|}ÐT¹£Q z½žØØXbcc9|ø0ÿøÇ?عsg³ÏsôèQ.½ôR6oÞL×ènl9¼Su¹mcQàퟫ¢ÛXùû×¢€¤°JîŒÙfáóï¾aém‹())qê˜ÁƒóðÃsûí·£ÓéÚ9BGL]¬dÆfÎB²•€µ›5¬çt1oDMh¼DoD]WÔÿ'WðÒK/µúO<ñ%’xqÆ}mQÛPZQΧ?mEfŒ¿¾Æ¾è°.8„_îࣷðâŒ9”vo°»«¢ÛúyæÐhì­2SkÝO–(Ú³>ýžxzwÞ$À†HOOÇ*Ó÷­#cµZÉÈÈ [·nM¶mTt ö Τ¤$GV…ˆ‰©z :zôh“í©þïß_qÃø€ÝUXåÁçD·*í† @wUè­¢"'†®s(M½%‰n{ Þý 6,‡QÛn¯¯öQZÞ~qµ}йÃPQé´ˆ†(t¾£1ý ’¥é\ÀÿvAì|xà.xj^Óí³«¹Û+qIТó¹ÑÐSîHÃmC¯âÙ­ï)êýÕQõ展›‘,6­Ø`{k¹¹ÆÄ—Ö[iI£0aàer‡¡¢Òy‰Š‚Ñ£á×_íÂPÐÚÅIL´Z¸ürµt]uº‡B‚3-íø{yóèmSxô¶†ßÿö%W!_Á¥®«ùDtìŠT‚ УGzôèÁ¸qãjì3›Íœ¤§§·øøÌÌLFÍý^@Õ>¶-6›Ä¬ãìIKàÂîåGQì:q€„Ìd¹ÃèøD‘eÐÚ­aäÈ‘,Z´ˆ & (,ѺÓ!èõU‚š¦ýìTÚ›cÇŽ±aÆ69×K_|L¹©‚7âTÄßÚÚŸ·QXZ˜ÁÃéR·rÇ=WMpˆns‹ ùâ·íÜ5æ*W‡Ù8PP g °CH:ÛŽ­[aï^YC(¨µîutœ@£W^wß•;Ea³Ù8yò¤¢æÂªÀ×_ÝàþÏ>ûŒåË—7¸_N*“ÆÃÃÛCjRtBjj*&“©Íl-#FŒÀËË‹’’rrr8yò$‘‘‘ ¶w,+;XÂÃÃÑë›*¾×‰0èíΧ²PGÚ ½Ö^PEEE6 ÁÓ(?½›9»éÆ.æÀçD·Ç«UGêÚ¥ýâi‚}×8DšÔ¢¢"'ÆðŘ_)wuH8æ\»øƒUËÔX)L²`ŒxBî(Ť—óöoŸ“WZ ˜©lßþaÉ&Qv:Ï ¶/;çX´<•SZP«ÑpËàqzªƒª**²²x1\©œþ5¨ì2™¾¯-ÝT\J°ÅbwÐP©¢[0$¥A…YîHZÌ×»~ÀÇÓ[FŽ‘9šZT&ÇæbäBt:C[›ÒÒRŽ=ZÃ!7))‰cÇŽÕ¨pWIyy¹C¼»iÓ¦ûŒFc wÜêÿvïÞ­¶Éé•f!ˆ^ÃÆ:ÓÙµ”^Œ­â$  q$´ Ùu!Ã¡ï‡ ªòšâÛo¿%55µUç(**âãÇ—qËùt¹Ð™z*ÍA#ˆ¼»s*º­Åv~‰FÔ`µ)äÔ°Š>õ¤è»¿êìE‘ë®»Žýë_\tÑE2D§¢â¼öÚkmêÚ¸lÃ:J+ÊùÏÜ" ò&ß­üÞ.&¾çª õî5†¹Ë_£¨¬ÔÑ^q¢[°¿¿&TE·µyòI{·Œ òµgJe‰¢0›aõjûxhD„ÜÑ(†ÌÌÌ:ƒTÚ‹ÅBff&ááá¶k²gE±QA«èt:®ºªªƒÙ¸qcƒm9vÌ>«Ì…^Øîñ5g,‰;ÑJNøvo ºˆògt©¨tjưGååÎþwsÓUCm6øê‡ªõK†¶oLÍdž1t¡ÜA¨¨tz´¾cÐz_lŸÄS;þ„ŒÚµvj‘œ;÷W­_EûÆÔ|4h½.@ës¹Ü( ½FÇ?.ºAAfZ#Þ}«$eyžh´}þ¾ªý¾1áZå<+H6‰éL”; •1càâ‹íƒù  º÷õf )Yæ—Õ–cEåïi4pÁv§[•*D¢ºµÌʸµð>„ëGr,ýT£íö&æûí¦ Ýt;^F£+Âs ˆj|¿3ãééÉСC‰å±Çãý÷ßgÇŽdeeQ^^Nrr2[·nå7Þ ..ŽqãÆ…FS÷ª¼¼œƒ²~ýz^}õUæÌ™ÃUW]Ett4žžžDGG3~üxfϞ͋/¾ÈºuëˆwºL´ŠJƒzôASñœ„WïÏÐù]‚æÜ»²ï.¹ëkÐùŽÇ«÷gø =!ôAUpë$ï¶‘Ó–d“8õÅäüt¸MΧR…U²ê0 ™ò»õOš4 Axæ™gdã`Æ1ö>¬ nÛ‘àQ}0xT=kz{{ÇáÇٸq£*¸UQi‹ÅBzz:ƒ "$$¤ÍÜiW}¿‘©¯>EÆìO¤ðûáü¼¼¹eäèzÛxŒL¾|¬cýçû8rJÕ¡% r 펷2£”þ•={`ÇÙ+RÕ¶Ü«í|ëö,[&w€r¾wiiiŠu¹íTºÝ6…S£óáááœ8qBQ*éiÓ¦ñÃ?PRRÂ×_͸qãèÛ·o6¼ñÆŽõ™3g¢Ó)Ç1@BBB0*m W xì%ÕÒsT·Û¶F§µ; «¨¨È޾ke§Ÿk¾Ü¡Ôà`<÷xüÞ†Û<½³±˜~“kbs A‡!ènDC¹#QQQŒÿ¦øÈur‡Q“|>yÙþhT›’2¸ïI{‚Àe#àÒa®±i¬»=-wŠäÖÁcyw×—••(FvýŠf"Ù$rw'0¢žÝëºÝ–Ï!ïã‘qÊqäÑŠ&Ä\F˜oÜ¡¨¨¨üûßp2ú×éÀs@< ¼Ò@Û_€UÕÖy嫞Vû×zéÇN‚Y9ãÕ¼òÕy÷ú“.OŸÍáΗ–`“lôëÖGo›êâèš@ìÕ¨< rGâ– ‡Cî¸qãjì3™Lœ:uÊáz[ý“@yyyöf³Ù±¿>תýéÑ£G½"_•:zô]bÑw‰E²ä`:û_Lg>ÀRºß.`u`k§ª“¢lf´žÃÐMCx;‚6¸}®×IIIaË–-mwB 26ÿ…¹ ŒðIC탭*m‚V£ecÂÏ •ÏIøÃ?lÔ<Ê•¬Oø­¨ÅbSÞó\GAÒkº(ó¡î»ï>æÍ›G—.ŠJ3TQQ,Z­–o¾ùƱnµZÉÎÎ&;;›ôôt²³³ÉÌÌ$33³Æ¶¬¬,Ξ=Ûè¹?Ùþå&Ÿþs z­ëõB+¾[Àí—ÃCßð»ß=WM`Õ÷U}ƪï7òò?îo÷øš(À©ðó–-%õ¯¼ÿ>ètvGVñ¢¨ª0Õ¡Rº*ÝnŸ{Ξ0/JùÞåççSQQ!w­bòäÉLž–ÿ~ÚSÕvþT¸p°|±ÖEÂþ¸ÜA¨¨¨œCçw ZÏaXÊ€¤œAým;áš™0š]Pëç¹ùðóðÊHJµ· †ÿ<%k¨u´h=‡ óSÖûR0ê ̼èF–þú_l•Êi™1†úvýÒ7îG²Ú8¾ògBÆÄoH$Zo#–¢rò÷§‘µ-é\ÒaøÃ1†)§L—̼XIY6**œk®aÃàÀÙ4º/ž[Hþ°ø¯^¤Ê ÷ &ºÕjaÈPØø¡bЈö±º#'Wê?ß}CiE9ó&ÅÒ¯[wDAäxV:~ÿW¾ú”¼â"zt eËS¯aP!`ÿ]F«åÛ½^ïÅÖÆb±––V¯ ÷ðáÃõ:ÛæååO|||}:ŽÈÈÈBܰ°0ÂÃÃ‰ŠŠ¢W¯^mæÔ¥Òq´ÁBæa™‡dÉÃRô3æÂíX ¾ÃZ~ û BDtH’pöÝFDuH¶Êc4Æ>hý®Aç{%Zß+4µ‹Ïª4‡O>ùQÛü}óÌoG1–ÑýŽ‹´êN[`±ZØxèWŒž‚Nãú*éééÌŸ?Ÿ)S¦ðÑG¹üúÕ1[-|›¸Cܺ€€+ûñÖÖpEß äEEÅ­Ñh4„……Æ!Cmk2™ì¢ÜÄ£dîˆ';?ôÜ3dçç‘•ŸKFîYNgúkϲzþcõzýPn2ññöïû»ë¾û¦‰#ªøàßòì´8Y„Âb“ =ô”¥º²’úWL&øäÙ·•\ ,?·¼WÎ@Úƒ3g`Û6ÙÆí”ô½ËÊÊj—÷•š‚@VVVëE·Ýºu###ƒòòrÅX9’Å‹óꫯR\\Ì+¯¼Â+¯ÔõÒ˜8q">ø`=gAˆŒŒÄÃC-ÕÓ ^öÒjÇÓ›®sî"^ùêS¾÷v½û~>°áú‘Žõ!½z³ÿ­\ZÓ‚Ý5£GhÓmUTT\†1ôAL9+°V™…·ÍV eåð¿]öOCxyÀã÷Á½ŠJBÒb û'¢!Zî@TTTxöZAá!ù˧]~¼þ(¼¶Nf¾D˜öHÃíG‡OA¨ÒŒ=%+=ß‘; Es×ðkùòïÿq:?«¤Œ‡ ËûF$só_XËͤoÜOúÆýuÚiŒ:"n9ÿ¡ÊIÐÔf\0‘Hµb†ŠŠbX±Ržt`J€oÎ}âNà]@QÓFV+¼£ö¯Ò3NfAi¹â„·ý¸…~¬ßupâE#ygÎÃt ª]lQf¢"ÀS­‚æj´Zmƒ‚\›ÍÆ©S§HNNæØ±c$''“œœÌñãÇ9~ü8¹¹¹uŽiÊ%×××—^½z5øñôôlóŸQŽ´ènDp#°ÉZˆµü¶ò£X˱•A,Î IDATÁV‘ŠÍVÖb$k!’­Ì~¬è ñ¢èhè‰hìÆ£?c?Dc_û~•6cñâÅ,^¼˜ÒÒR )**¢°°¼¼< kl+,,$??Ÿ‚‚‚ÛNdž¢¸¨k¹¹ÆüWÁß'I).§çôËÐx(êIÉm)©(eGê_ŒŽáòkÏš5‹ØØX.»ì2ÙÅ?'ÇSRQ*k ½—[Žþ®ŠnUT\ˆ^¯§[·nt;S {(ª’ò;¶“W\Ô¢cs òùfׯÜvÙ•mU`±BN„¸ÞÍ[Iý+7BQËþÛƒ;¨Ýþšë›µxòÜòûÀ´¶¬-ÐéàƒdÝ*å{g³ÙÈÉÉQ·.@’$²²²èÝ»7¢XR¤Ó¢[AèÛ·/ýõW›ØŒ3†Aƒ±iÓ&vìØAff&&“‰ÀÀ@Ìõ×_Ï Aƒä³:Nu¹u†>‘p:Ç^X!Â[·E’`P44p3PQQ‘ Ag¯U%Ž‘;‚àéy°h&lÜnw³ýë0d…âð0ÚÛ ì W\7‡.Ê1àDD]0ÆðÇäDEE¥¯ó1ÏÄ”³æœC<è´0íF˜2 ~Ù ›~‚½!-ŠKÁÛº…ƒàæ«à2×ÏÇ4‰ èÐw…ÖëB¹CQ4:–'®ŠcægÊ*4²¾1áäîI¡(1S^ ¶ =Æ_|ú…Òåâh4®s[h Qèâ嫺ܪ¨(‘óχ™3aÍE8jÄ7[@.`ü°—¸»û`ýP™blfÍ‚ ÕþµQDõ†ßÊ ß,~ÿíßËÖ}{ØŸ’DFîYÎàëéIx`0Wœ7”Û.˨Š*Ír :è­ºÜ*ʪÝ»wg̘ºc5n}Ÿ²²²:Çò×_58·R¯·GDFFb04\‚V¥c"h|Ñz]^ª`KÉxzzâééIhhóŒNJLe\ñö,ÌV»ã¨­Â‚µÂŒ­ÜŒµÂ‚­ÜŒ9¯‡êH܈¢†õr¹èvõêÕ$$$°víZÖ¯_ïÒk×dž„ŸÑˆ,6eUÜëˆXl¶ÛÿLexéUó-—a±BæYE nV|gïŸ9§âVøùù1tèP†­?} //Ïáz[û“––†¥žÉج¬,²²²øý÷ßë=g@@QQQ„……îpé­\ïÕ«‚àú²®-eÓ¦MLœ8±Æ¶ÊøW®\ÉÌ™3åKEÅeì>qsµ‰rÑ E4hÁWçµV›•)a±YÑŠ®éwO:Å‚ øòË/ñq±(¤>ÌV »NP·.Äl1óÇÉC²8,«¨tZ΀ŒœJãׄ¿ð2ù¿›œ+:¤Wo&^8’ »àÇ¿âIÎ8Mt˜Â6% rÎE¹è]Diý+&üðƒb·•¼ì €°»ß^å䱋Óç–¼Û<ºVb2Áöí0i’Ë.©´ïÝÙ³gAQšÍÖ’™™Éí·ßNß¾}Y±b…ÜáÔ@„¶ÝDGGsöìYU9ÝBA K—.)­F­‚ „`ûƒRºq¸1½äŽBEE¥<"_Çœ¿ÉjEqo…Cçw5º€äŽDEE¥m<{,¥$E1ÅhÜ Ïo"häÄmøç˜©ì<¾‹¹¼C B¸ ­FËȃÓû|¹CQQQiˆ.]`éR˜¦ö¯-F’àÍ7!@í_f@O{IɦŽÕµQ€ YÊpª´?Œ1‚#êŠl, ÙÙÙdddÔ+ÊMMM­·Dd^^ñññ ^Ó`0Ñ 0·W¯^xzz¶éÏÙ&L˜ >›«tjö¤D§Ñ8œnUÚŸ «‰ƒ™É ïë’ëÍœ9“;3+¯T†3áßI˜,ò›}t&4-¤%¨¢[Wr¶Àþ®¥ §Û•ßo`Îõ7èã¼9Úâ;f8D·’$±êûh æÂ0] ’:¹Ô,-ú.· ¼KîHÜŠ`ï^˜0¹_½$w(n‡(ø½xêÚûäEEE¥)¦L±;k¬]«8w Å£ÕÂ-·À]jÿÚ,ŒzûXÝÞD¹#q?Áþ½Ü[îHTd@«ÕNxxx½¢ÜÒÒRŽ?îøœ8q‚“'O’––Ɖ'ÈÌ̬wr­¢¢Â!Ü­Q ¥GtïÞîÝ»IÏž=Ë]º¨"pWñÛñ¿TÁ­‹Ñi4ìI;èÑíÊ•+IJJâË/¿l÷k9Ëž´t­ú½s!«…Ç÷³pÌT¹CQQé<ääÛE  Ád1óáÿ¾ÃCo`ÁÍw4ëØóûôçš³%Þ^ ãýmßòÔÝ3Ñi›-ïj_DÁ.vvèV‰ý+?þz½]ä0Æ›€;\àààn`$À)àà 2Ísð5 Hu‘É[¶À믻ärJûÞ•––b6«‰T®Æl6SZZZo2s‹îÊtëÖÓ§O« êfƒN§“; ÷C§…a}awÂ9‹z¹rz…«Î**n‚Îÿz ¡R‘ù& :Ê7¯>_"hUywÀ³ç»X‹ÇZ‘ª oEТÑGâÙs¥Ü‘¸%—E ã®×òß}[°*hàUù¼6é!ü=ä/™¤¢¢âï¾kwØHMU…·Î¢ÕBd$¬Tû×Ñ5z†Á‰ 5A¾¹Œèzu|X¥.žžž 8Ö»ßd29D¸'Ož$55Õ±\)Ì-++«sœÍf#==ôôtvíÚUï¹½½½¢ÜˆˆÇ'<<œˆˆÂ iÓŸWE¥3’[ZÈ©ü,¹ÃpšÄg6`.¨{_Iyw»c9zÎX¼z){\Öb³±+õoâ.¾¹]¯“––ÆÂ… Y¿~=^^^­>ßöíÛ oµ™Ó®Ô¿1ÛÜc¾¡£|çRs38[Z@ §óî–***-Äd†Òº÷9ØŸ’ݹÓkl ½Ën¸´æÿgúÿ³wßñmÕ÷þÇ_ßs´lY²ä=bDZgO„„@!!$fÃ(4p„=ÚBém¡@K{ ¥å^Fi{i)-«—ÐRF)£üHi€,²Èpâïmkœß²åÄ[G’?ÏÇCIÇçèû±£èHGïóùÎ?û˜ÛÖ46à½ðÌ£–—VWb[<€Ÿ_w+7.:à î 5P8lP‡‰Ôý+o½€<ø¸“PçÛ7Ú/Ç¢+€‡÷ W×_~ ee0ÈŸOòyW\\Ìúõë9묳Ð4­ÏS]]R*¢rš6l`åÊ•G-¯®®fΜ9á/h(¥¨©©¸Ð-@~~>555466FÔ?h$1b‰‰òƺϒÜP”[÷ÈÁüžP 0*×ìJ„½Ÿó0º÷ñ7o#rߨG¸abI8ÅìB„=¤4'Η©ß4MÞÒõBÇYôJ—ðc_Ý|Ú¥ü»x3ÛìÁ%_4™I)ŪY39[fi"j8ðòË0mšÙ•D]‡×^—ì_ûltT×A}SDMáÑFå†mêM{l6sêêjöïßOIIIg÷ÛCïïÚµ‹`'¢544°iÓ&6mÚtÜñ“““ÉÊÊ"33³Ëë‚‚<Ï€ü¾BÄ¢ïý"ÔTFv›aeJ¶á°hú óÚk¯Q[[{Ì€Á½÷Þ˽÷Þ À¶mÛ(,<~çû7ß|“‡~˜yóæ±jÕ*.\ˆR½ëJä øÙTº3ÔÐH„—‚ÏŠ·0¿h†Ù•û*ëÌ®`誮ooš7x]ó"qÿJ[|òIÄï_‡/[€—·À $cÙÀr Ï”*ûàÃC3W ¢|Þ•••qÎ9çPPPÀÊ•+¹úê«IHHèuMµµµ½ÞF Œšš²²²ŽZ®Œ~$f[ZZøôÓOñûý¼=¥ÉÉÉÇ"Û’éNᙋ¾Ëå¿¿—f_ ÒŽbÑt&dòóWJàVˆhuÙeP] ]Lë%Ú)<"Ûg‡ãàÿm}v•ýëÑ”ž˜\dv%B`³ÙÈÏÏ'??ÿ˜ëTVVRRRÂÞ½{)--eïÞ½”””°oß>öíÛÇþýû)++;f#––––Î0ï±èºNzz:ÙÙÙdff’““CZZ©©©dff’ššJjj*¸Ý=¹©½òUÕ~™}Å4Š]Õ%¦„nʶmÛ¸ù曹ï¾û¸âŠ+¸ýöÛÉÉÉ9î6»ªJ¤¹²IF¯ªö›]†èÆŽ;¸å–[ؽ{7eeef—#úª±Y^èLa šM Ý”¾ì_ùòËÐñ%9~~?lÙbvýRWWÇc=Æã?ÎÙgŸÍM7ÝÄüùó»a´¶¶†©Bq¤–– Ã8ª+v¿O%v:L˜0uëÖHÇÛC(¥ˆg„ hšfv9±CSpÂøh=4¶ÈŽìPªýrâXp;Í®FÑzÜxŠVÓ°å ü„&yJYÑE$½ŽÒf—#„è{úÛöÐRòäuîHŽÌ;pdÜjv!1¥0%‡Ÿ/½‹kÿt?FÐÀ[:Y4áIYüâü»±[¬f—#„èo„={à'?.¦Ò4 î¸n•ýë€rÅô1°f2gö” ŽÐßGŽ‹(‘œœLrr2ãÇ?îzÕÕÕ‡uÉ=òzçÎTWWw¹m `ÿþýìßß}Én·“””„×ëíìœëõzºŸ••ENNV«¼—‘kû=r¨I¬ºÎ®ªýPp‚Ù¥ô[mm-=öO<ñçw7ß|33gÎìrÝ]Uû±è|Ò]9ì‚Á Û+ö˜]†èƽ÷ÞËÌ™3yùå—ÉËË£±±Ñì’D_Ô5JfÄ*ô™·±ÙìBDoö¯|ù%X­ÐÖÞ"°q£ÙU ˆ`0ÈêÕ«Y½z5S§NåÚk¯åòË/Çá8:ÿÐúæè=ùo£ŒúW©¨¨à‹/¾èl¨SJa·Û™2e 6›ÍìrbSk|´ZÚäM´ŸIœ0 Ò“Ì®F1@|Õ¯Ò°ý‚ö×9ùÂ\)+Ê–…kìÇhÖ ³ËB ƒÆWÒVñò:×AÖºçˆg‘nÞƒãïÛ?áö?ÿA ùüJ(p›–Äÿ^öRœ³ËB À+¯„çž“àmMƒåËáÙgCÇPÄÀ+«‚µ_¶çneÿŠÒÀa…™Á.!@14577wÈ=4˜»wï^|>߀ép8Ž ã/¨+Do½øâ‹L™2…‚‚‚^mg`0ã¿® 5 á 3èšÎ¹ãNã{g^¶1¯»î:þçþç¨ågžy&o¼ñF·Û9ýõñÌœ9“›o¾™%K–`±ì¹õÝ7ž`õ¦ ÒaÙ ‹oþµÌ&Á𛛉‹ uè6l¥¥¥øýR:o|,Ç>Ì¢€aé0¡wï‹ú#ö¯\uU蘛¼^˜#.Ãz|¯?Ï»µk×2mÚ´“••Å 7ÜÀŠ+HIIé\^YYÉÆ G« &”txoÀB·:³yãÆÆÐþâR)E\\'NÄn·›]NlkõÁ¿6…ZÖáçJ…ÞМ0R½fW#„`¾º·iÜz.†ác¿yWVt{> £ßA³e›]b@4﹃–ÒŸ˜]HPØÓ¯'~øc€tCLïÞÀM¯<‚/è#0„ÊZ4ažtžºðÒää=!bŠa„ººþDö¯(×_=&ÝF[E-¬Ý ACŽÕ9pâ8pHC!Ž' RVVÆ(--¥¼¼œPVVÖ¹¼¼¼œÒÒR8@KKË€íp8:»èﺫeGN+)†ŽÓO?wß}—±cDzlÙ2®¸â FŒÑív•MµœþËð>ÅÑ&gâ7—ü§ÙeôXoBA233Y±b+W®$99™+ž¿Ï÷}9HŠžøÇõOâw›]†è ÝF©V¼ó‰ÙU m^7œ|üY2"É@ì_9åøè£AªPôHy9¤¦š]Eô&tÛÁn·sá…rÇw0aÂöíÛÇŽ;†tÓLJ) :qw@C·uuulذ@ 0$ÿ±•R¸Ýn&L˜€®ëf—34øðÉPÓ04æk t¦Ëìj„ƒÄßð1 _ž…lcàºD ×É$­Fér€JˆXÕ²ÿš‹ïe(OI—ó ŽÌ»Í.cÈX_²ë_|f_+þàÐëú¢k:“²FòøÒ»pÚâÌ.G1Xxî½wh3éðàƒp·ì_æ¦>t¬. …o‡¥Àë‚icÀ"LJ…hõõõ”””pàÀnƒº•••ƒV‡Çã!99¯×‹ÇãÁãñàv»q¹\¸\.Ün7‰‰‰$&&vÞï¸îXW¾CŠNóæÍãïÿ{ç}MÓ˜={6—\r çŸþQÝ:ì®.áÜgn W™¢ …)9¼tåÍ.£Çú êàp8X¶lûFiTØZd"%­¾æQr<éf—!z@B·Qª±ÞûÌì*†6WÌšbv=6û×»ÿùOÆîÜ9À•‰^Ù¾z9ó„Yúº=Ô)§œÂe—]ÆØ±c°*ÑJ)òòòÈÍÍ=|ù@‡nšššX·n~¿Ÿàë”’’˜1cФkFxƒðÙ—P^=´2JÍ '§|I.D¬ 4o¦aË<‚þŠ!¼Õ°%-ÁYð;PÒA^ˆX×zàišv]º3d¦¾ÓA3ï)l©W™]̳³r+þt?ÕMuC*x«”bÞÈé¿ý¾bHø×¿`éÒÐôk¾ß¿Z­”/¾§ÊþÕ4m>ø|TÔ˜]I´ïOGƒÂùÆMˆ(ÖÐÐpX÷ÈÛ]ý¬¶¶–ššÈy­ëëz<”R׉‰‰hšÖyÝÑm·ãÚåra±X:¯°Z­×šæ4>> ó±;k½X3þ|Þyçn׋gÑ¢E\rÉ%œuÖYl(ßÁÕ/|? Šc©ùûVöüuèvC´§¸HžYHÒIhVé´.Ï^ü=¦mv¢$t¥ªêàãfW1¤Ý÷ü¯øþsϘ]†iF7ßbóÝo䪡ÛN§“³Î:‹eË–‘‘‘av9C‚¦idffRXXxØòA Ývسg»víˆÉ//•RX­VƇÛ-Ó]G„ªºP×[Ÿ?6§°S€¡`t.äg›]Â-%Ó\|OûÝìT¥¬h–dœ#_Â’0Óìj„&0ü4n¿ _ÝÛÄîIÖÄ8 žCY’Í.fÈ30xvÍÿñó_@#öžwMÇçâ'‹oerV‘Ùå!ÌPQ—]o¿š5(i,XÏ=ɲ;öÁÖ=¡Û1x|M…‚ÞSG×ev5BÕÖÖR__O}}=uuuÔ××S]]}Ô²Žî‘Ë;–ÅZØçxá\›Í†Óéìü™×ëí¼ít:±Ùl]®t†…»sèãOG7áî<úè£|ñÅÝ®w(ÇÃÌ3f³Å[‹³ ¥ÉÙáf•oÁþ¿m0»ÓYœv’f|J!V·tIl/½‹YùÑ3íúP&¡Û(U^ ÿÞlvCÚ}¿{†ïÿþWf—aº`¡n–ɵĺ¡ºí ë:§žz*Ë–-cüøñf—Ó”R¤§§3jԨ×vèB6oÞL[[[Ìo•R†Arr2£Fê<@ "D›Öm‡1ÖiY)pØBSÔÉA|!†<ý‡4 ¯ ŒX9 A¬žE8󟕚CžAKÉi)¾b§ë­fEàöŽÌÛ‘Vh‘å³}[¸sõcT6ÖÆÆ‰-Ji`œ–?•ïí:éüÍM”¾%Ý;¸Š2ȹx—ÃìRbÚOßʼ‘'š]†è ÝškÁ‚Œ1‚9sæ0wîÜžwo,­„O¿ÜâÄqIèöp €ßéfÃ$t{¸ôôtî¸ã¦M›fv)1---1cƶ,,G9ñÄÙ½{7{÷ë­R ›ÍFaa!)))f—#ºb³Âô1PZ_ì …p£¹ë­¦Bñ ²¡`è2ź,®SqOÜJËþÒRò£ÐÂh¤) š5øá¿Äê]lv5Bˆˆ pdމͻ„¦]×·w½ …ó£S(™aM˜C|Þ/Ñ…Ýn!ÂoJöh^ûÆñôšWyö_¯ z¶ëšNR¼›ïÌ¿š¹…rÐEA(Œzç°d \}¨ë­¦Eoç[¥BAâ9sà—¿„BÙ¿F$¯fO…Űc?(#ºÕ)Ànƒqùždv5Bˆ£ë:^¯÷°®¯ƒ¡®®®3à:»ìv\744àóù:¯ÚÚÚhllì|ŒêêƒOikkèÜ®CGðè ÷f½Xb‰·¡ÇÛ°$HÐ1ÜtM¾[S Ïäá¤Ì*".ËÓý¢ßâ­ò]ˆž¨««ãÉ'ŸäÉ'Ÿ`̘1Ì™3§ó’––Öõ†=èz/™œóŒ¸¸ ˜lr-bè?~<\p³fÍêÑ ¢ï”R]þÃvú½¦iŒ1‚ŒŒ ¶mÛFuuug·Øh¡i†a••  IDATň#äI 2’ Õ;÷Áöâв(zÎuvÌð¸`|>$Ä›]‘"Â(-ž¸a÷cO¹’¦]ÿ¯îm:ÑÓ™O)ìi×7씞`vIBˆ£9F’0ú-|5¯Ñ´ëºößÑó:„N,°$—ócl)—›]è†ÃjçÆS/âÜñ³yà­§ùx÷Ft¥0¢'fÕ-ƒA.š|+g],_ò!Ž6r$¼õ¼ö\w”•A Êö¯ $%…:÷^.û׈§kP” ÃÒ`㨨=xì+Z¨öã‡gÀ¨\ù‚WÕÜn7À ‡{BGøP---477w»í‘ÁÞcéèܻロµk×v»Þ¡ 8}Ñ™¼oý {š»WÛŠcÕ‡nWzK‚ïô¤œ2kbœÙå )ñ69#DO¤§ÞtóæÍlÞ¼™'žx€üü|æÏŸÏüùó™;wîÁæxùLf:5tOjI®VÙ&×"†‹Å©§žÊ\ÀøñãÍ.gÈ8VèV&¥^+++Ù½{7õõõ¾Õ4`0HFF¹¹¹ÄÅɇ‘¨ÔÔ Þî;ºÁϹÎ.-‰ P”©‘ÐK|5¯Ó²ï?ñ7~ÊFwæÓ¬`ØS–ãÈúš½À슄QÀ6ÒZö­%cª1Œ ‘œÔPJGé^ìYwcO»¥9Í.IôÁ;?ã‰^dSé,šŽ?¹¡4‹¦c‹ÆÎâš“–㑉¬„=ÐØO<? ÕÕ¡®·‘|ÜD×Á녻JåÕ°}/Ô4<©T{ëžai¡™¨â%j×í¡~[ÍÅUøë[0 =Ά#ͳ ï´ßCÃŽrš‹«ñ7´ 4…î´ãHOÄ5*ï yèñ¶Aø zÇüqùCŒÎav)¢† Fii)~¯ÃV¬XÁSO=Õ£u•RL˜09sæ0÷¤™œ¦%’䊜“Z>Ø´Žå|ŸÝå¥Ìž0…<ôx·ýdžϘ{÷½¯ä¹ÿ#ÛÜÛ2Ìÿõ>/~ñ©iã÷Ö@ì_'ÛlÜÜÖÆÅ„ºÜší`9°˜ ü£U üxØ ”N Èægcû1Fín_°M¬¢çª««yûí·ûõ^¯—… ²xñâƒ'˜hýúõ<ðÀ”••1iÒ$}ôÑm÷á‡rÏ=÷ôyÜ¿þõ¯¦d65M#''‡¼¼¼Ã–›vJ_rr2ÉÉÉÔÖÖ²gϪªª".|«Ú¦¦¦¦2|øp ÛF»xL,„‘9ðÕ~ØSZISÙuLç˜è„Âa&ÓÓ !zÇê9«çüõÒ²ÿ~|µC)+†á3»´v*ÆÀ–tqÙß“°­¢W”æÄ‘y;ŽŒU´Uþæ}÷lÝ h)]¾•Œ š5Gæ]ØÒV 4ù,ÍfåOaVþ>Û·…'?~…¾Z‡UÓñEHøVz{ØöÌÑ3ù™HØVÑ;N'Ü~;¬ZøÜ{/ìÝ:N)Ýo-–Ð1“ôt¸ë.X±äX]tKó†.Õu¡åÔ€¦"çX"Ô±Ç0 +%tLQ¶B!Ž#11‘%K–p饗rúé§Ö ÉAÝ.@²¿m¤ü[útÒ‹¯¶™òw¾ ê“þ£g„ñ×·ÐPßBÃŽrÊßù‚´¹cH_0îàI,&9s/\þ©5ôÆ·¾õ-z¨÷õjšÆé§ŸÎªU«ømål­Ø3ÕõNŸsÍûk(ç ê6cñ~Ñ@°¦ _Mõ_–PöÖF²ÏŸŽgRÎ@”ßgÍûª“•Ãá ++‹ÌÌL¼^ï1o6ŒÄ( ÅŠÕ«W³hѢÖudDžzê)®¹æ3Ê’ÒÒÒz¼®a¬_¿žõë×óØc¡)‰# ˜;q*s'žÀS¦ã°…?xïóûùîsOó£G0ŠfLë¯ ç ¿w·ÙeôØ@ì_~ç;¨ ¡ºÞñß~ô÷øY+Pl^k_ö×Ï1û*øã“OÂðá&UÐ;k×®eÚ´i}Ú¶¨¨ˆë¯¿ž¯}ík”•• šûÚâ÷ûùÕ¯~Åþð‡°×b±X°X̉¹†Ñe§[ÓçÑHLLd„ Ô××SVVFYY~¿ß´nǸñññdff’––†Í„²Dqv; †…ºÞ—ACóÁÀk¸iíû€ßßþ¼3!€ÛüƒÜtÈJ»<ç„B„¨#‚£v»³Ï>›Ë.»ŒsÎ9‡£ëp­'.2:¬¶–×±ç÷Ó¼¯:t‚I”¼ö95ëBAN=ÞFÚécqÍÂæub­åuT­ÙAåÇ;BaË·7á«ofØÓð7é½$gl^v¹\\rÉ%ÜrË-Œ=€Õ/®GU( g‰ˆçÀ®_½¯¶€¸l/ióÆâ‘‚gÃW×LËþÊÿ±…¦]š}ìùÝGXâf“P”1@¿Iïùë[hiiaçÎìܹ³Ûm:3 )))¤¤¤vûÈe òr,\¸0¢šÂÅ¢`0Hee%TVVvÞ®¨¨àÀË6nÜØ÷1Œ ë¾ÚNfR2çLŸiJàvKñn.ûÑòéŽ/z¯ól±ýy¹«ý+?ÿ9˜ºÝ\|J¿v¯@èÛ´+ÿm¬Ë€+€)€ ؼB([ÓϱL²yÝ›RŠyóæ…Þ ¢”¢´´”ÒÒRSëÚ³g÷ß?[·n5åunöìÙXM:&lF—c›ºíàr¹p¹\PUUEii)UUUƒA4M´„tÇÁ0 ìv;©©©dddà”iébŸÝ ùY¡K]c(€»¿ZÛžm;`o² ÛÕu¼×4Hõ„¦¦Kóš~–¯"öèÎiÄ;§Ÿû|5oÐVñk|µo`›AÙÚ¸ƒp@Aéí@³ebKº[Êåèñ~,!ÄЦlØ’–aKZ†á?@[åó´UüÓç¡÷rš‚mƒ3¶fƒ ”Â?[ÊØ’/FYRg<1Æeä3.#ŸÛæ,矻ÖñÿÁ‡;×ÑhÃªëøƒÁA9`¯k0‚¤8=œ=æT;¢ÔÜK1ÄÙl°lYèrà<ÿ|(€ûyûþÕj…¶AÚ¿Úlà í_™2®¸"N•ýkÌKL]ÆäÁj(.u¿ ·nÇá8°ÛB]m‡¥+~pÆBõ4Mãä“OfÙ²e\vÙe=šâÕi‹#Ñᤶ¥1 v­ò£í”¬þœ /€wZŽ47%YßçÇÓ¬:×ÍÅ‘éé\¦"³—N#.;‰â? jÍN'æà2)iÕ-ä'e›2ö`+((`åÊ•\uÕU¸Ý‡O¯>")›µ{7ã øM©m ŸsŽŒD n˜‡f=ØmÌæubó:qÍb׳R·y?PöÖ&SC·¯‡eË–QVVÆ(++£ªªê¸Û444°mÛ6¶mÛÖ£1)))$''“––Fjj*)))$%%‘””„×ë=ìÒ±LšŽ‰¾0 £Ëmee%åååG-ë¸=˜l6—ÎYÀm‹/b|^þ Žu,¿|ýenúqšÛZ¹bÞד›ÇÝÏ>ѯÇ|â†;¸îìó¨ÂA¤)HˆÍYˆŽ·eôhxï½Á;6Ö_·̈́±c€þô¾—PàÖüXvÄÏóÛ€éÀ%YÐ;))ƒ'8N®¸â V­ZŨQ£ûY\\œ©'‰¼úê«<ñÄ´¶¶rÖYg‘››Ë“O>ÙçÇ[¾|9W_}u·ëùý~.ºè"*++YºtiŸÇññG'Œ˜Ðm¥ÉÉÉ$''cõõõTWWS]]M]]]ç“èаloûÐm¬V+^¯ǃÇã!N¦¤ºÜÎÐeL4µ@E-TÖBE øÚ?ˆ*u°³KOuLC×±RàuAŠ’Á“ A[!Dx(Vï¹X½ç‚áÇßø/üuÇWû&†5F[çzÊbÐóƒpJÙ0Œ ´o£Y’°$žÅ=«k.š£p~!!„8š²¤bO_…=}†¿ý{øêÞÅ_ûW-Û  ÐPÊŠaøèy7\ ¥Y1‚Û(tÇH,‰gauŸŽÅ=¥{º{ƒ¬º…9'0§àÁJwð¯=ùh×z6–là «®4 ÁûáqjÞQ}è6‰ŽN>ÃÇ3=w¹ó¾4B 1©©°jUèR]úRáÝwᯅíÛÛOrÑBA\Ÿ¯çÇMŽÜF)9Î: N?fÏì_‡$MAzRèbPÓpð8]uýÁ“ä5-4ÃAo¾sÐÚ÷¼^«%tB|rbè9S !„ˆL·Ür ¿þõ¯ÉÌÌìõ¶yIÙ¬Û¿uªêž¿¾…}¯¬Åžâ"û‚i$¤Qµ¦û®›Ç“:{ôaÛ#%ÍȧjÍšö†B†ÕŸ|eZè6hä%õþß,’rÊ)ÜtÓM,Y²ä˜Síæy³L h Æs 뼩‡n£)³ŠB¡[ ©¸êø_Ù¤éSùõO¾wز¶¶¶ÎniiéQ·ËËË)))éìêóæÂ––Š‹‹)..îUmN§³Ë0nÇ%11‘„„\.—ËÕy¿cÙëæêëëihh ¡¡ºº:jkk;ó8555Ý^jkk¥.»ÝŽËåêU@×ívså•WrÇw0louè³¢ J«+¹á—?adVÿ³òNæNœÊÓ{Í”ZLc¡Ûžì_5jøõN)p0ø`.ðt?o#ðãöÛ7stàöP§'ÿîÇx¢£ãpŒÈÌÌdÅŠ¬\¹’äctðí*ð.UUUüìg?cذaÜvÛmL™2…×_=,c¿ûî»TVVRTTĸqãÂ2æ±t•)¸Ðí¡”R¸ÝnÜn7ÇÇ0 š››ijj:ìÚï÷ã÷û ƒ‚Á º®£iº®£ë:v»øøxâââ:¯ív»Ù¿¢ˆDñÈu„¦hiƒÆæÐ¥¡ýºÅ?ø Àè×Û/V X,¡7N8ãB·ã²B˜OY°$ÌÄ’0GÖ=`ø´î$ؼ™@ËV‚-[ ´|‰¨ÁðՀшlÁ6£ô”æDéN”–€²f¡ÅFwŒBw¡9F¡Ùb³c€"º(‹«÷<¬Þó€G1uZ¾ ½Æ5o&Øò%ÁÖ]ƒõhÀÔ…º€J‹CénÐ]hZš=Í1½ýõNs…~.Ä!tMgrV“³ŠXqÒRüÁÅ5eì¬ÚÇîªvW—°«º„ú–&ê[hnk¥Õï£5ÐF¼ÍAœÅNœÍN¼5ŽÔ#’²ÉKÊd¸7‹¼¤LÒ’Ìþ…¼^8ï¼ÐåÑG¡®¾ü¶n…Í›C·wí‚úzhhý¼9´%.Ünp¹BÝ(òòBÉG}YQTú¹‡ê8Ýë‚Âa¡/µšZ£kl¿í÷‡NœC—`,:h:XèpX£s¶_ÒáK!DïœuÖY}Þ¶0%‡M¥;ðqBfXhŠ´ycIŸ?e9F`±—\£ºкFgv†n[J'¬Õ`€‹¦S˜œsÔr›ÍFvv6ÙÙ=û¥¦¦†òòòÎΡáÜŽû—òòrêêêzô¸466ö:¬{¨„„„Î ®Ûí&11—Ë…Ãá 11±3Ÿát:±Ùl$&&b±Xzü³¡¨©©‰ÖÖVjkkñûýÔÖÖÒÖÖFcccÖÒÒBCC555Û†††A ÌÉjµvv_NNN&%%¥³ó¡Ë’““;—»\.¶lÙ˜1cº}ü¼¼<®»î:®½öZ<'êV·†NÖ4!iÑ-|ç¢+¸çâ+q ÅÒA#ôÙ:ÊõvÿʨQ¡“ÈM`¾Ü ÄiÃ?|í{GÖÿdÆì«Æ7»Šq '°jÕ*.½ôÒc¼ÛY­Vt]'ÿû:]×Y¾|9Ë—/ïw§|·ÛÍøñãIOOïÑú/½ô€é]nu]Çjµµ<¢C·GRJ?dßd “8l¡Kr¢Ù•!ÄàPÖöÐì(Ž~« „±Aén,Îéàœnv)bˆ°h:yIYä%Eÿ—zBqLn7LŸºJ Ì !„QfDR–iY,N;gMÇÊýúÉä~ýä­«ÇübÞð÷b&ÉA0<Š;ݦ¥¥qÕUW±råʇ5SI äs`Ì=çöh½Ö²ƒ¡ÓøÜdÓºÜ*¥¤»rÇŒ½EEE=Z¿­­ŠŠ ªªª¨®®î¼>òvW÷ýþžÏ€t†9KKKûò«u«'a]·Û®‡BÝ^¯‹Å‚Ëå8*À›ÐehæÈíêëëù÷ðù|444tÞï¼¹]uu5@€ºººÎ€lcc#mmm]†g#‰®ëÏ?ÇÓ9ƒ´×ëí2DÛ®u÷ñdÚ´´´ãþ|Ú´iÜvÛm\pÁG‡ÓâBÿ×MÚ§¸¹ÿòá8’Dñçó¾î_5jðŠêF pÿ=V-ðJûí™ÀñÿF¥Lýû÷—Õjå¼óÎãæ›ofæÌ™½Ú6..î°ýO¸$&&rõÕWÈcMœ8‘ǼGënܸ‘-[¶˜˜È¼yódü¾:VN5ªB·B!„B!„B!„BmF§åáô.Ôí|µÍ·YÓêHt$šà5mü¾š4i×_=Ë—/ïrJÛ’p9œÔ·DV˜o°šÛ({sJSúí-_ÀÏè´¼°k³ÙÈÊÊ"+«÷뺺ºÎn]]õõõRkkk;C¶õõõÔÔÔP__߹ΡË|Ôý±# Ú^=cµZIHHÀãñtv#v:x½ÞÃî{<žÃºw„j;N^¯›ÍF[[[ç2¥óæÍcÕªU,Z´èØ»¡Ž«1æ­Ï>á¿ÿò oÙĺâí2¼IÌ3‹O›Ï‚©'š]bhè(œA¦¿ûW† jj¾¸0ú;ÐÒ~{jûõVàqà  °9Àà:Àô³mm0eŠÙUôZbb"W\q·ß~;99GwÂï ·ÛMSSÁ ¹'²…Ë‹/¾À¢E‹ŽyÂL8(¥Ž¹O”ЭB!„B!„B!„B ¢‰YEXuŸ Óš¥¹ä`Å;-Ï”t¥sòð‰(³ZžöÑ­·Þʃ>دÇP(fäŽçïÛ>!hÄ`@À`›Ÿ¶Êê·•QñÁV|µMèñ6†-›Ž3?Õ´Òl+³Fš6~_¸ÝnÜn7Çï÷c577ÓÒÒrØuuuõQËúó³ÚÚÚ¨ 9âââ»öz½G-ëÏÏ::þF¥iiic·Ûùú׿έ·ÞÊØ±c»ßØë u¿4b'xû“WžgûþâÖÕú¨mlàËâ=<ûÖëÌ7‰çïúO²“Mz­Ó¤šwBM_ Äþ¥`Þ=z4%%%} xÂãñ°ÿþª*²•——óÁ içžÛ³ÙÓ±ö­ºB!„B!„B!„BˆAd·X™9’ÏŠ¿Ä0cì0k«j¤a[ñy)¸Çô¾óæÀ08qø8“Æî»ÔÔ QÍÈÏ»Ûÿmʴ냩jÍNŠ_üä°eñy)¤žV„wúô8ó:/*“2GbÓÍëÊf¶¸¸¸°@ëêê´Þ::âêêêhkk£±ñ`§çŽÐî±Ô´w­ôxŽ$ì¸vp:Øl¡ç›ÛíF×uà`@Çb±„½kl´9r$K—.åÎ;ï$;;»çjZ(x[]3ÁÛíû‹9±h,·Ÿ ³ÆM"É妲®Ž¿¯[Ëþð,_ïáƒMë˜qË7ùägÏ™”þ" ’Ã?n? Ôþ•yóàϘÇ2ÉÆCnÿð>ü8Hö/?M„ºá†R0sf(xEœNç€<ÎñöM±æÕW_%0{ölÒÒÒL­Å0 »~­“ЭB!„B!„B!„B ²“‡Odýþíøƒ~³KtåooÃ@³[ȹм)°FsMŸ Ù4'æŽ#ŒÞ.|½Ñ¼· ¥)t§ïÔá¡pŽ ,šÎÉyM{¨q»Ý·£µÃ«8èí·ßFÓúØ?3Å5õ1s‚ÁugŸÇ/®¿ Mü{d&%sÙÜ,>igÜsoÙľÊ|ý‘ÿä>þ" ¢2t;`N?üÑý~®òÛï ÀÀèC–çw3¹@ø°8#,UÂj…3Â>jİZ­ÄÇÇÓÔÔdv)ƒª¥¥…Õ«W°téR“«øøx¬Ö®O¤’ЭB!„B!„B!„B ²‡çÿü£Ùe ºÚ ÅT}ò(EÎE3°§š×å1Åé!Ç“nÚøfËKÊ")ÞMUSÙ¥ ¨¤ù$ÍÈÇñ×·ÐøÕ*>ÜFãÎ4î<@åÇ;È»òT,ÎðwÃóýC:è-D_õ9p ¡ðçÖ=WŒIæL˜‚ñú?»NB\¿¹õ^Æ\{)A#Èß×­åýŸsÚøÉaª²ÝñŽî׋U£FAZ”—›]IŸÕqÿvÜjpðtûý‡0!tÛÖ ;aIII477cÄHW﮼ùæ›ÔÕÕQPPÀ¤I“L­EÓ4’“ÝI¼{-!„B!„B!„B!„=11s$)ñ‰1Ó‰¯+»*ØûüÇd/™Jâ„a¦ÕbÕ-œ3öTÓÆ_} V-6{q)]Ãê‰Ç3e8…7Î#ù¤švU°ë™÷1Á°×”’àa\FAØÇbHó¸Àf3»Š°)ÊÎaö„ƒ!ÛW>z?¼(Ù)á3]rIT?ï|GÜ¿¨›õ/=äö{@ÃÀ–Ó½¬,˜>=Ü£F”ÔÔԘܼüòË@dt¹ ƒ¤¦¦óçºB!„B!„B!„BˆA¦)Źæ`Ñu³K_`×3ïôÈ^rÉ'šZ/àgáØÓL­!,7 _0º§Àî¥ÈZr¶$'M{«¨þä«°–`Õ,,?M©°Ž+ħ€œTІÎÿ½£ÆuÞþdÛæðnÞ1#Ñòå¡î«QêÐyì@Q7ëO=ävX?à‡ÍW]ýéˆÜn7Gìv˜þä“Oصk.—‹ùóç›]‡—ëØ3v íg£B!„B!„B!„B„É¢±§áÌ.cÀ5î<ÀWO½G°ÍOÎE3HžinàVyÞLŠRsM­#ŒMÏgDRŠØ£)Má™|ðß¼æóðN7ï ú9g쬰Ž)„h—ÁØîy¨ oRçíòšêðîŒW|xÇŒD'œ£G‡:ÿF!Ï·» &ÖCîW xEÇÑÖ_ÿz8GŒX¨(}Îu祗^àœsÎÁn·›Z‹¦iddd0Õ"„B!„B!„B!„CZ~r6£Ò†ÇT²ñ« ¾zæ}Œ@œKOÆ{BžÙ%¡k:K'Î3»Œˆ±xüô!ÒΞæî¼ÝRZ¶q•RŒNÁˆ¤¬°)„8DB¸Q€ì­à!ṠõWV rÒÃ8`„»ê*ˆÒ Fr»§ýðµ‡íi§L™ 8 ÒÓÓ1ŒØ;Á`ïÞ½¬Y³MÓ8ï¼óÌ.‡`0Hzúñ_ë†Æ;k!„B!„B!„B!„ˆN^3ªšvWòÕ3ïa‚ ¿ü<“rÌ. å3ޓޣ;-jshûÿï3Ößñ ;Ê{´¾²Œ@Ûz#ê?…â¢Ég„maý/д»2,uYtó'Î'9>1,ãEƒ§‡ó&œŽU·˜]JŸµìëÙôém ·-®ã‡%’7ÞÅ¢±§…m<¦ºÝ666òÆopþùç›\ †A^^^·ëIèV!„B!„B!„B!ÂĦ[¹zÆb”½_Õ6ï«æ«§þ4È»ú4\£"§» 4¸rú"³Ëˆ8WŸ¸ø°éÈ£MÕÚ]‡ÏkÝàfݞλÎá鯍k×ÌXÕ¡f!b‚¦Aþ°0Î{?ðþ±þ3öU8î:[÷íåýë:ïŸwr˜ÿŸž±¢É·¾Q¸ÍÎ<äþsݬÿûCn/$¼tºßþ6Ølá-jx<ÜnwÌt»]½z5ÍÍÍ >œ©S§šZ‹R —ËEbb÷'ïEï'9!„B!„B!„B!„ˆBLœ‡Ó×}ˆ0µì¯aç“ÿF|s6 a›`¸[MgѸÓÈt§˜]JÄÉt§pÎØS°jz÷+G –ý5”þmÃq×)ùëZJjCw$ŸT†Ê ÁÏùOËXBˆn䦃%zðm~ß|ì!|~—?ohn抟þ€ `îÄ©œ6~òà¦)ÈIƒ8ûàmrsá²Ë¢2ú0Ðñ¿åI`Í1Öûx¶ý¶øî ×Õ)1¾ùÍpUb¥Ûm0ä•W^`éÒ¥&WÓó.·pðÿŽB!„B!„B!„Bˆ0pXí\3ã<ýày‚Á ÙåôXKi-;Ÿü¦6vüâ“+:œ\sҳˈX×ÌXÂë›>4»Œ>+ç š‹«I9u$q9Ièq6‚->šöTràƒ­4l-í\7mîâó?|­)ož´»%úÂVBÄ$]ƒ‚lørwTžØð×ÌÌÛ¯å[^Îiã'ãq&PQWÃ;Ÿ¯åþ?üš-Å»ÈNNå¹;î OQ¡¿«èÚ·¿ Ïu×+6òŒ~Ü ø€À÷€e@:PüøhßæçÀ„p§ëðï@\\8F‹:III$$$ÐØØÕáÛ?üÒÒRœN' ,0µ¥ $%%õl}#šÿòB!„B!„B!„B…|?K};ûjÊ ƒ¼=ðÞJV¯ë~EÀ‘å¡è–3»üÙ¶Gߤ¹¸ºO5Þ8ŸøáÉ}Ú¶'t¥sÕ‰‹X9ëâA#üìýßó¿ÿ~0ÐýÊý0PÏ9c+ÞÝLåGÛ úº¯Y³[È8s)³ŠzUo_hJ#ÓÂ«ßø 6Ý:èã !z(hÀŸASË oyù÷ÜñÌ/z´î¤…|þøoºü™ÏïçÙ·_ç‡/ü–Ýå¥]®s¨Ù¦ð»;î#;9µWõö‰RŸ £r¬hv×]ðÓŸÂ1º”G€;z¸î$àó¬÷sàn é8ë$ÿ „å]–®‡:oÞ vé®|,õõõ|öÙgƒº}á…xâ‰'z´nAAÏ<óLû¦›nbݺu\pÁÜxã}-qÀL:—ËÕ£u¥Ó­B!„B!„B!„B„™U·pß‚\óÂÌ.¥Ç _dvåÕ”"Éé–.·=pÝÉçóúPÑXC0 úsYœv2N&ýŒñÔn(¦~[)ÍÅÕøë› ¶úQVK‚Gf"®‘éx&GO×Ù ä{g^+[!"¦`B!|¼ÑìJzÌj±°â¬Å\sæ"Þù|-¯ü¿÷øxË&v••PßÜ„+.žìäTfŽ™Àųç3wâÔ0U¦Àn…BérÛ­ûîƒßýJJ Šf1X œ < ¼ìê/08 XÑ~?,xúi ÜvÃår‘‘‘AiiiTv»Ý¾};ëÖ­C)Å’%澇WJ‘™™ÙãÀ-H§[!„B!„B!„B!„0ͯ=Ê»Ûþ…o;Æ2…â¿Î»¹…ÓÌ.%*¼³í_Üöçÿˆֹ×#€E×™?òD^x“Ù¥!Žå³­PVê|+úî„ÑÞ³éÖ‡¼—_†óÏ7»Šèfµ†þ†Ï?ov%QÁï÷³fÍüƒÜa9ÖY,f̘ÅÒóþµÚ Ö#„B!„B!„B!„â8îœ{9v‹ ¥”Ù¥D%‹ná´ü)¸í…y#Odfþ$,ºnv)QI)…C·qÇÜ+Ì.Eqµ !„B!„B!„B!„Én>íRF¦Ç¢Ig´žPJ±jÖÅLÎev)Qkê°ÑÜpÊ…hÐè‹®3:-•³.6»!DoŒÎ·4y­ë±Q¹à•ZúlÖ,øÁ@N¦ê«¦L0»’¨¥ë:ãÆ3»Œ¨3~üxô>v¥–ÿÝB!„B!„B!„Ba2«nág‹o#Á.ÁÛãÒ5y#§s剋Ì.%ê}cÆbN+˜*Ϲn蚎۞ÀOß*Áx!¢¦`êèP'M ÞŸ¦ # ò³Í®$úÝ}7,\zÞ‰c³XÀë…—^ …oEŸ9NFmvQcôèÑ8Î>o/¡[!„B!„B!„B!„ˆ™îž¹è»8,Vé>z Mgbf!?<{%Jæ ï7M)Yt S³GK˜ô4¥°éVþ{Ù·Ép%›]Ž¢/âì0c\¨ó¨ì_»¦4ð¸`rߦZGÐ4øÓŸàÔS%Lz,ºv;¼ù&ää˜]MLHKK£°°Ðì2"^AAiiiýz Ý !„B!„B!„B!D„(LÉáçKïB×4”’¯seÑt†'eñ‹óïÆn‘Ë@±ê~vÞmäz3°èÒ‘ïP …®é¦œœ† ÖïÇ‘WJ!„B!„B!„B!„ˆ ' ÃÝŒ(éÈ„·i I<¹ì;8mqf—sìñ¿ý¾7ƒßú¼û¿( `Äv8HSŠrÆòðÂUüÿöî?6΃¾ãøûùqgßÅml7㸉CÚÌý1 4d]“²Š"Ú)À¦UãÔj0iHl“ƘPÑ$„DˆiR'­µ *ª@*kKù£´ëµeZɚдNêæ—ã8Žï’Ø>ûîž{öÇc·´ªØ÷#ï—ôèÎgŸŸÏÉž{¬û<ßç’âêfÇ‘´Rª5øå(L•›d,¾ŸnÝïÞøú—Zai wß Ÿû\öuÒá'T…!Üx#üà00Ðì4¬³gϲoß>ªÕjÇÿ/¹\Ž‘‘V¯^žc:K·’$I’$I’$I’ÔRR¾óÜCüÛStl 2#z ñ/ûÞ³þòfÇð¿G÷óÿ+§+39i9 Hþnç'¸cûn[hÒ…éà1xåpv¿«TaM}ïÐwQ³ÓàÉ'á¶Û`zjµf§9ÿ–¦(õ«ðùσ'Q5]­V㥗^¢T*5;ʲêïïgÛ¶märËw¥ K·’$I’$I’$I’ÔF^8¶ŸzänNÍž&éd„¦ì~/_¾åoè-Xj%åÊY¾øè¿óôØ/! c&¤EAÈšž>¾¾ûï-yK‚ÒxáX¨Bgìæ²i¶)°¶®Ù ù¸Ù‰ô릦àöÛáÑG³’j£CN¨ŠcX·x®¿¾ÙiôGŽall èœcº`±Ô½yóf6nܸüë³t+I’$I’$I’$Iíe¾¶À½Ïý˜ï<ÿ0POêÍŽôŽEaDñb¾ø¡Oñ'ï~³ãèwxbôÜõø·™ž;ÓÖ…ï8Œø«íãÓ;>NWœor"I-#iÀÁ£pp‚m\« €®<\9 ýÍN£ßåÁ᳟…ÉI¨·ï1¹\6)ú _€;ï„B¡Ù‰ô[T*FGG)•JAÐöåÛ¾¾>¶nÝJa…¶9K·’$I’$I’$I’Ô¦—'øÊc÷ò졽DAH’¶Ï„´\Óh4øËk?ÌßîüÅ\w³#éÌ×øö/â¾g @½Ê·QÒh¤\»aÿ|ó§¾dC³#IjUsó°÷ L~cZl»‚,ï¦upÅDQ³é\ÌÍÁ×¾wÝ•ý kµf':wqœ•…wí‚{î‘‘f'Ò9:u꣣£T«Õ¶+ÞA@ÇlÙ²…•]·¥[I’$I’$I’$IjoO¾ú÷<ó#öM$£–.BÆaD ìÙɧwü{WöCr‡ËÜûÜðÈÞŸ„õ¤u·¹¥Bú•Ã|æocçðµÍŽ$©]L–àÀ(Ï,–Y[¸fµxyu6¬…-ï‚¢'³´¥²âíý÷C¶vù6Š IàºëàK_‚[omv"½I’0>>Α#G¨×ëmQ¾Íår 188HÔ„ ,ÝJ’$I’$I’$IR‡xáØ~¾õìƒ<3¶‡\Qk‘òmD‹eÛl»žÏ\ÿ–m;Äñ3S|÷áG{~F#Mi4’–™‹rÔ’Wnå¯w|œ·¼¯Ù‘$µ«Ò8pN–!  Ñ"{º¬ ¼~ lÝhÙ¶S> ßø|ó›ÐhdåÖV©ùuuÁÂìØwÞ »w7;‘΃4M™œœdllŒ……‚ h™îR–|>ÿzÙ6 Ãæå±t+I’$I’$I’$IeßÄ«<¼ïç<ò«'™Y˜#Š¢¦L"]*þnî_ÏŸ_s:²“KŠ«W<‡–ßÔl™Ÿüê)|ñ?›>F>Š©&õÏ…Fª®»GvñÑ«ndd`xÅsHêP§gàèI? õz6]¶Ü¥âïª ÀúK¡+·ò9´ü&&à{߃ûîƒýû!Ÿ‡juåsÄqVü½øbøä'áŽ;à}žÌÒ‰Ò4ejjЉ‰ J¥Ò›_iÁâï¾¾>Ö­[Çš5k^¬™,ÝJ’$I’$I’$IR‡ª%už~míý/žzu I•\Qo4–åƒó( !…$m°fU/·þÁ ì¾r—_:tÞ×¥ÖõòÉC<¼ï¿ùéKO35[& ²IdIÚ8ïë ‚€8 ©% ]Qž†ÿ^õAnØüâpå/7,éÑHád ŽNfÓo倻Ô1K®|6ÕvÃZ¸¨¸<ëSkڳÿý¬Œ-¾Ï-ljUA¹\Vð-à–[àöÛ³Ûœï E­Vcrr’'N033Cš¦Ë:wéwA@OO¬]»–\‹ms–n%I’$I’$I’$é4^œ8Èó‡÷òÌkÿÇÞã£Ô’ÈE4%i¼¹´‘òFÏç­rQü¦ç¬îîaÇeWóˮ⺡+ê]·¬¯Gíápy‚çïã¹C/òì¡93? @FA@í·LÃýMÛ^FDA@5I€”\qõàVþhÓ5lºŠ«×m!²h+i¥¥)”gàÔi˜*Célö@BÚÈvjç*\Üû-xs1\Ú —¬Î–b÷y¯6uà<ñ<þ8<ö,M$ãl»û}¦á¾õ9ù<ìØ7ß 7ÝÛ·g?£ Z½^§\.S.—™žž¦R©¼þ½wRÄ}ës …ýýýôööÒÛÛKÜÂÛœ¥[I’$I’$I’$IºÕ GË'xuú‡¦s¨tœ×JÇ9;?ÇÙ…*Õê5’*Å|7…¸‹B¾‹b®À¥=½lî›ú¹¬o=›úYÛÓßì—¤6pâìt¶­MgKiœ“3eæj*Õ*õæªótEyºâÅ|7=]«¸¨»È¦¾A.ëdSÿz6÷¯gCï€Ól%µž4…¹y˜©Àlfï×ëP«CÒÈ–FâÂ⢺s°ª=…ìvUºóÍ~EjGÂ+¯ÀË/¿±ŒÃÌ ÌÎfËÌL6µ¶»V­‚ÞÞl¹â ¸üòìvÛ6vš­ÞV’$ÌÍÍQ©T˜¥R©0??O½^'I’$¡ÑÈ®r†!QÇ1QÑÝÝM±X¤X,R((‹DQûÓYº•$I’$I’$I’$I’$I’ÞFØì’$I’$I’$I’$I’$IR«³t+I’$I’$I’$I’$I’$½ K·’$I’$I’$I’$I’$IÒÛˆ6;„$I’$I’$I’$I’$I’ÔÊþ u£tŒIEND®B`‚abPOA-1.4.1/python/000077500000000000000000000000001425041320700137565ustar00rootroot00000000000000abPOA-1.4.1/python/README.md000066400000000000000000000102451425041320700152370ustar00rootroot00000000000000# pyabpoa: abPOA Python interface ## Introduction pyabpoa provides an easy-to-use interface to [abPOA](https://github.com/yangao07/abPOA), it contains all the APIs that can be used to perform MSA for a set of sequences and consensus calling from the final alignment graph. ## Installation ### Install pyabpoa with pip pyabpoa can be installed with pip: ``` pip install pyabpoa ``` ### Install pyabpoa from source Alternatively, you can install pyabpoa from source (cython is required): ``` git clone --recursive https://github.com/yangao07/abPOA.git cd abPOA make install_py ``` ## Examples The following code illustrates how to use pyabpoa. ``` import pyabpoa as pa a = pa.msa_aligner() seqs=[ 'CCGAAGA', 'CCGAACTCGA', 'CCCGGAAGA', 'CCGAAGA' ] res=a.msa(seqs, out_cons=True, out_msa=True, out_pog='pog.png', incr_fn='') # perform multiple sequence alignment # generate a figure of alignment graph to pog.png for seq in res.cons_seq: print(seq) # print consensus sequence res.print_msa() # print row-column multiple sequence alignment in PIR format ``` You can also try the example script provided in the source folder: ``` python ./python/example.py ``` ## APIs ### Class pyabpoa.msa_aligner ``` pyabpoa.msa_aligner(aln_mode='g', ...) ``` This constructs a multiple sequence alignment handler of pyabpoa, it accepts the following arguments: * **aln_mode**: alignment mode. 'g': global, 'l': local, 'e': extension; default: **'g'** * **is_aa**: input is amino acid sequence; default: **False** * **match**: match score; default: **2** * **mismatch**: match penaty; default: **4** * **score_matrix**: scoring matrix file, **match** and **mismatch** are not used when **score_matrix** is used; default: **''** * **gap_open1**: first gap opening penalty; default: **4** * **gap_ext1**: first gap extension penalty; default: **2** * **gap_open2**: second gap opening penalty; default: **24** * **gap_ext2**: second gap extension penalty; default: **1** * **extra_b**: first adaptive banding paremeter; set as < 0 to disable adaptive banded DP; default: **10** * **extra_f**: second adaptive banding paremete; the number of extra bases added on both sites of the band is *b+f\*L*, where *L* is the length of the aligned sequence; default : **0.01** The `msa_aligner` handler provides one method which performs multiple sequence alignment and takes four arguments: ``` pyabpoa.msa_aligner.msa(seqs, out_cons, out_msa, out_pog='', incr_fn='') ``` * **seqs**: a list variable containing a set of input sequences; **positional** * **out_cons**: a bool variable to ask pyabpoa to generate consensus sequence; **positional** * **out_msa**: a bool variable to ask pyabpoa to generate RC-MSA; **positional** * **max_n_cons**: maximum number of consensus sequence to generate; default: **1** * **min_freq**: minimum frequency of each consensus to output (effective when **max_n_cons** > 1); default: **0.3** * **out_pog**: name of a file (`.png` or `.pdf`) to store the plot of the final alignment graph; default: **''** * **incr_fn**: name of an existing graph (GFA) or MSA (FASTA) file, incrementally align sequence to this graph/MSA; default: **''** ### Class pyabpoa.msa_result ``` pyabpoa.msa_result(seq_n, cons_n, cons_len, ...) ``` This class describes the information of the generated consensus sequence and the RC-MSA. The returned result of `pyabpoa.msa_aligner.msa()` is an object of this class that has the following properties: * **n_seq**: number of input aligned sequences * **n_cons**: number of generated consensus sequences (generally 1, could be 2 or more if **max_n_cons** is set as > 1) * **clu_n_seq**: an array of sequence cluster size * **cons_len**: an array of consensus sequence length(s) * **cons_seq**: an array of consensus sequence(s) * **cons_cov**: an array of consensus sequence coverage for each base * **msa_len**: size of each row in the RC-MSA * **msa_seq**: an array containing `n_seq`+`n_cons` strings that demonstrates the RC-MSA, each consisting of one input sequence and several `-` indicating the alignment gaps. `pyabpoa.msa_result()` has a function of `print_msa` which prints the RC-MSA to screen. ``` pyabpoa.msa_result().print_msa() ``` abPOA-1.4.1/python/cabpoa.pxd000066400000000000000000000155051425041320700157260ustar00rootroot00000000000000from libc.stdint cimport int8_t, uint8_t, int32_t, int64_t, uint32_t, uint64_t from libc.stdio cimport FILE cdef extern from "simd_instruction.h": int simd_check() cdef extern from "abpoa.h": cdef int ABPOA_GLOBAL_MODE "ABPOA_GLOBAL_MODE" cdef int ABPOA_LOCAL_MODE "ABPOA_LOCAL_MODE" cdef int ABPOA_EXTEND_MODE "ABPOA_EXTEND_MODE" # gap mode cdef int ABPOA_LINEAR_GAP "ABPOA_LINEAR_GAP" cdef int ABPOA_AFFINE_GAP "ABPOA_AFFINE_GAP" cdef int ABPOA_CONVEX_GAP "ABPOA_CONVEX_GAP" cdef int ABPOA_EXTRA_B "ABPOA_EXTRA_B" cdef float ABPOA_EXTRA_F "ABPOA_EXTRA_F" cdef char *ABPOA_CIGAR_STR "ABPOA_CIGAR_STR" cdef int ABPOA_CMATCH "ABPOA_CMATCH" cdef int ABPOA_CINS "ABPOA_CINS" cdef int ABPOA_CDEL "ABPOA_CDEL" cdef int ABPOA_CDIFF "ABPOA_CDIFF" cdef int ABPOA_CSOFT_CLIP "ABPOA_CSOFT_CLIP" cdef int ABPOA_CHARD_CLIP "ABPOA_CHARD_CLIP" cdef int ABPOA_SRC_NODE_ID "ABPOA_SRC_NODE_ID" cdef int ABPOA_SINK_NODE_ID "ABPOA_SINK_NODE_ID" cdef int ABPOA_OUT_CONS "ABPOA_OUT_CONS" cdef int ABPOA_OUT_MSA "ABPOA_OUT_MSA" cdef int ABPOA_OUT_CONS_MSA "ABPOA_OUT_CONS_MSA" cdef int ABPOA_OUT_GFA "ABPOA_OUT_GFA" cdef int ABPOA_OUT_CONS_GFA "ABPOA_OUT_CONS_GFA" cdef int ABPOA_HB "ABPOA_HB" cdef int ABPOA_HC "ABPOA_HC" cdef int ABPOA_MF "ABPOA_MF" ctypedef struct abpoa_res_t: int n_cigar, m_cigar uint64_t *graph_cigar int node_s, node_e, query_s, query_e # for local and extension mode int n_aln_bases, n_matched_bases uint32_t best_score ctypedef struct abpoa_para_t: int m int *mat # score matrix char *mat_fn int use_score_matrix int match, max_mat, mismatch, min_mis, gap_open1, gap_open2, gap_ext1, gap_ext2 int inf_min int k, w, min_w int wb # 1st part of extra band width float wf # 2nd part of extra band width. w=wb+wf*L (L is sequence length) int zdrop, end_bonus # from minimap2 # int simd_flag # available SIMD instruction # alignment mode uint8_t ret_cigar, rev_cigar, out_msa, out_cons, out_gfa, out_fq, use_read_ids, amb_strand # mode: 0: global, 1: local, 2: extend uint8_t use_qv, disable_seeding, progressive_poa char *incr_fn char *out_pog int align_mode, gap_mode, max_n_cons double min_freq # for diploid data int verbose ctypedef struct abpoa_node_t: int node_id int in_edge_n, in_edge_m int *in_id int out_edge_n, out_edge_m int *out_id int *out_weight int *read_weight int n_read, m_read uint64_t **read_ids int read_ids_n # for diploid int aligned_node_n, aligned_node_m int *aligned_node_id # mismatch; aligned node will have same rank uint8_t base # 0~m ctypedef struct abpoa_graph_t: abpoa_node_t *node int node_n, node_m, index_rank_m int *index_to_node_id int *node_id_to_index int *node_id_to_max_pos_left int *node_id_to_max_pos_right int *node_id_to_max_remain int *node_id_to_msa_rank uint8_t is_topological_sorted, is_called_cons, is_set_msa_rank ctypedef struct abpoa_cons_t: int n_cons, n_seq, msa_len int *clu_n_seq int **clu_read_ids int *cons_len int **cons_node_ids uint8_t **cons_base uint8_t **msa_base int **cons_cov int **cons_phred_score; ctypedef struct abpoa_str_t: int l, m char *s ctypedef struct abpoa_seq_t: int n_seq, m_seq abpoa_str_t *seq abpoa_str_t *name abpoa_str_t *comment abpoa_str_t *qual uint8_t *is_rc ctypedef struct abpoa_simd_matrix_t: pass ctypedef struct abpoa_t: abpoa_graph_t *abg abpoa_seq_t *abs abpoa_simd_matrix_t *abm abpoa_cons_t *abc # init for abpoa parameters abpoa_para_t *abpoa_init_para() void abpoa_set_mat_from_file(abpoa_para_t *abpt, char *mtx_fn) void abpoa_post_set_para(abpoa_para_t *abpt) void abpoa_free_para(abpoa_para_t *abpt) # init for alignment abpoa_t *abpoa_init() void abpoa_free(abpoa_t *ab) # do msa for a set of input sequences int abpoa_msa(abpoa_t *ab, abpoa_para_t *abpt, int n_seqs, char **seq_names, int *seq_lens, uint8_t **seqs, int ** qual_weights, FILE *out_fp) int abpoa_msa1(abpoa_t *ab, abpoa_para_t *abpt, char *read_fn, FILE *out_fp) # clean alignment graph void abpoa_reset(abpoa_t *ab, abpoa_para_t *abpt, int qlen) # restore graph from GFA/MSA file abpoa_t *abpoa_restore_graph(abpoa_t *ab, abpoa_para_t *abpt) # align a sequence to a graph int abpoa_align_sequence_to_graph(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int qlen, abpoa_res_t *res) # align to sub-graph void abpoa_subgraph_nodes(abpoa_t *ab, abpoa_para_t *abpt, int inc_beg, int inc_end, int *exc_beg, int *exc_end) int abpoa_align_sequence_to_subgraph(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int qlen, abpoa_res_t *res) # add an alignment to a graph int abpoa_add_graph_node(abpoa_graph_t *abg, uint8_t base) void abpoa_add_graph_edge(abpoa_graph_t *abg, int from_id, int to_id, int check_edge, int w, uint8_t add_read_id, uint8_t add_read_weight, int read_id, int read_ids_n, int tot_read_n) int abpoa_add_graph_alignment(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int *weight, int qlen, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends) int abpoa_add_subgraph_alignment(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int *weight, int qlen, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends) void abpoa_BFS_set_node_index(abpoa_graph_t *abg, int src_id, int sink_id) void abpoa_BFS_set_node_remain(abpoa_graph_t *abg, int src_id, int sink_id) void abpoa_topological_sort(abpoa_graph_t *abg, abpoa_para_t *abpt) # generate consensus sequence from graph # para: # out_fp: consensus sequence output in FASTA format, set as NULL to disable void abpoa_generate_consensus(abpoa_t *ab, abpoa_para_t *abpt) void abpoa_output_fx_consensus(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) # generate column multiple sequence alignment from graph void abpoa_generate_rc_msa(abpoa_t *ab, abpoa_para_t *abpt) void abpoa_output_rc_msa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) # generate full graph in GFA format void abpoa_generate_gfa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) # output to out_fp void abpoa_output(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) # generate DOT graph plot void abpoa_dump_pog(abpoa_t *ab, abpoa_para_t *abpt) abPOA-1.4.1/python/example.py000066400000000000000000000065371425041320700157760ustar00rootroot00000000000000import pyabpoa as pa #@parameters of msa_aligner: # aln_mode='g' # g: global, l: local, e: extension # is_aa=False # set as True if input is amino acid sequence # score_matrix='' # file of score matrix, e.g. HOXD70.mtx/BLOSUM62.mtx # match=2 # mismatch=4 # gap_open1=4 # gap_open2=24 # gap_ext1=2 # gap_ext2=1 # extra_b = 10 # 1st part of extra band, -1 to disable banded DP # extra_f = 0.01 # 2nd part of eatra band. w = extra_b+extra_f*L (L is sequence length) # max_n_cons=1 # to output at most N cons, set max_n_cons as N # min_freq=0.3 # minimum frequence of each consensus to output for diploid data # construct msa aligner a = pa.msa_aligner() print("==== First exmaple: 2 consensus sequences ====\n") # for multiple consensus seqs=[ 'CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATAAAAAAAAAAAAAAAAAAACGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT', 'CGATCGATCGATCGATGCATGCATCGATGCATCGATCGATGCATGCAT' ] #@parameters of msa #seqs: multiple sequences out_cons=True # generate consensus sequence, set as False to disable out_msa=True # generate row-column multiple sequence alignment, set as False to disable out_pog="example1.png" # generate plot of alignment graph, set None to disable max_n_cons = 2 # multiple sequence alignment for 'seqs' res=a.msa(seqs, out_cons=out_cons, out_msa=out_msa, max_n_cons=max_n_cons, out_pog=out_pog) # output result if out_cons: for i in range(res.n_cons): print(">Consensus_sequence_{}".format(i+1)) print(res.cons_seq[i]) if out_msa: res.print_msa() print("\n\n==== Second exmaple: 1 consensus sequence ====\n") seqs=[ 'CGTCAATCTATCGAAGCATACGCGGGCAGAGCCGAAGACCTCGGCAATCCA', 'CCACGTCAATCTATCGAAGCATACGCGGCAGCCGAACTCGACCTCGGCAATCAC', 'CGTCAATCTATCGAAGCATACGCGGCAGAGCCCGGAAGACCTCGGCAATCAC', 'CGTCAATGCTAGTCGAAGCAGCTGCGGCAGAGCCGAAGACCTCGGCAATCAC', 'CGTCAATCTATCGAAGCATTCTACGCGGCAGAGCCGACCTCGGCAATCAC', 'CGTCAATCTAGAAGCATACGCGGCAAGAGCCGAAGACCTCGGCCAATCAC', 'CGTCAATCTATCGGTAAAGCATACGCTCTGTAGCCGAAGACCTCGGCAATCAC', 'CGTCAATCTATCTTCAAGCATACGCGGCAGAGCCGAAGACCTCGGCAATC', 'CGTCAATGGATCGAGTACGCGGCAGAGCCGAAGACCTCGGCAATCAC', 'CGTCAATCTAATCGAAGCATACGCGGCAGAGCCGTCTACCTCGGCAATCACGT' ] #@parameters of msa #seqs: multiple sequences out_cons=True # generate consensus sequence, set as False to disable out_msa=True # generate row-column multiple sequence alignment, set as False to disable out_pog="example2.png" # generate plot of alignment graph, set None to disable max_n_cons = 1 # multiple sequence alignment for 'seqs' res=a.msa(seqs, out_cons=out_cons, out_msa=out_msa, max_n_cons=max_n_cons, out_pog=out_pog) # output result if out_cons: for i in range(res.n_cons): print(">Consensus_sequence_{}".format(i+1)) print(res.cons_seq[i]) if out_msa: for i in range(res.n_seq): print(">Seq_{}".format(i+1)) print(res.msa_seq[i]) for i in range(res.n_cons): print(">Consensus_sequence_{}".format(i+1)) print(res.msa_seq[res.n_seq+i]) abPOA-1.4.1/python/pyabpoa.pyx000066400000000000000000000170441425041320700161610ustar00rootroot00000000000000import re, sys, os from libc.stdlib cimport malloc, free from libc.stdint cimport uint8_t from collections import defaultdict as dd cimport cython from cabpoa cimport * cdef class msa_result: cdef int n_seq cdef int n_cons cdef clu_n_seq, clu_read_ids, cons_len, cons_seq, cons_cov # _cons_len:[int], _cons_seq:[''] cdef int msa_len cdef msa_seq # _msa_seq:[''] def __cinit__(self, n_seq, n_cons, clu_n_seq, clu_read_ids, cons_len, cons_seq, cons_cov, msa_len, msa_seq): self.n_seq = n_seq self.n_cons = n_cons self.clu_n_seq = clu_n_seq self.clu_read_ids = clu_read_ids self.cons_len = cons_len self.cons_seq = cons_seq self.cons_cov = cons_cov self.msa_len = msa_len self.msa_seq = msa_seq @property def n_seq(self): return self.n_seq @property def n_cons(self): return self.n_cons @property def clu_n_seq(self): return self.clu_n_seq @property def clu_read_ids(self): return self.clu_read_ids @property def cons_len(self): return self.cons_len @property def cons_seq(self): return self.cons_seq @property def cons_cov(self): return self.cons_cov @property def msa_len(self): return self.msa_len @property def msa_seq(self): return self.msa_seq def print_msa(self): if not self.msa_seq: return for i, s in enumerate(self.msa_seq): if i < self.n_seq: print('>Seq_{}'.format(i+1)) else: if self.n_cons > 1: cons_id = '_{} {}'.format(i-self.n_seq+1, ','.join(list(map(str, self.clu_read_ids[i-self.n_seq])))) else: cons_id = '' print('>Consensus_sequence{}'.format(cons_id)) print(s) return def set_seq_int_dict(m): if m == 5: # ACGTN ==> 01234, U ==> 4 seqs = 'ACGUTN' ints = [0, 1, 2, 3, 3, 4] elif m == 27: # ACGTN ==> 01234, BDEFH... ==> 56789... seqs = 'ACGTNBDEFHIJKLMOPQRSUVWXYZ*' ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] else: raise Exception('Unexpected m: {}'.format(m)) seq2int_dict = dd(lambda: m-1) int2seq_dict = dd(lambda: '-') for s, i in zip(seqs, ints): seq2int_dict[s] = i seq2int_dict[s.lower()] = i int2seq_dict[i] = s return seq2int_dict, int2seq_dict cdef class msa_aligner: cdef abpoa_t *ab cdef abpoa_para_t abpt cdef seq2int_dict, int2seq_dict def __cinit__(self, aln_mode='g', is_aa=False, match=2, mismatch=4, score_matrix=b'', gap_open1=4, gap_open2=24, gap_ext1=2, gap_ext2=1, extra_b=10, extra_f=0.01): self.ab = abpoa_init() if aln_mode == 'g': self.abpt.align_mode = ABPOA_GLOBAL_MODE elif aln_mode == 'l': self.abpt.align_mode = ABPOA_LOCAL_MODE elif aln_mode == 'e': self.abpt.align_mode = ABPOA_EXTEND_MODE else: raise Exception('Unknown align mode: {}'.format(aln_mode)) if is_aa: self.abpt.m = 27 self.abpt.mat = malloc(27 * 27 * cython.sizeof(int)) else: self.abpt.m = 5 self.abpt.mat = malloc(25 * cython.sizeof(int)) self.abpt.match = match self.abpt.mismatch = mismatch if score_matrix: if isinstance(score_matrix, str): score_matrix = bytes(score_matrix, 'utf-8') if os.path.exists(score_matrix.decode('utf-8')): abpoa_set_mat_from_file(&self.abpt, score_matrix) else: raise Exception('Matrix file not exist: {}'.format(score_matrix.decode('utf-8'))) self.abpt.gap_open1 = gap_open1 self.abpt.gap_open2 = gap_open2 self.abpt.gap_ext1 = gap_ext1 self.abpt.gap_ext2 = gap_ext2 self.abpt.ret_cigar = 1 self.abpt.wb = extra_b self.abpt.wf = extra_f self.abpt.use_qv = 0 self.abpt.end_bonus = -1 # disable end_bonus/zdrop self.abpt.zdrop = -1 self.abpt.disable_seeding = 1 self.abpt.progressive_poa = 0 self.seq2int_dict, self.int2seq_dict = set_seq_int_dict(self.abpt.m) def __dealloc__(self): free(self.abpt.mat) abpoa_free(self.ab) def __bool__(self): return self.ab != NULL def msa(self, seqs, out_cons, out_msa, max_n_cons=1, min_freq=0.25, out_pog=b'', incr_fn=b''): cdef int seq_n = len(seqs) cdef int exist_n = 0 cdef int tot_n = seq_n cdef uint8_t *bseq cdef abpoa_res_t res cdef abpoa_cons_t abc if out_cons: self.abpt.out_cons = 1 else: self.abpt.out_cons = 0 if out_msa: self.abpt.out_msa = 1 else: self.abpt.out_msa = 0 self.abpt.max_n_cons = max_n_cons self.abpt.min_freq = min_freq if out_pog: if isinstance(out_pog, str): out_pog = bytes(out_pog, 'utf-8') self.abpt.out_pog = out_pog else: self.abpt.out_pog = NULL abpoa_post_set_para(&self.abpt) abpoa_reset(self.ab, &self.abpt, len(seqs[0])) if incr_fn: if isinstance(incr_fn, str): incr_fn = bytes(incr_fn, 'utf-8') self.abpt.incr_fn = incr_fn abpoa_restore_graph(self.ab, &self.abpt) exist_n = self.ab[0].abs[0].n_seq tot_n += exist_n else: self.abpt.incr_fn = NULL self.ab[0].abs[0].n_seq += seq_n for read_i, seq in enumerate(seqs): seq_l = len(seq) bseq = malloc(seq_l * cython.sizeof(uint8_t)) for i in range(seq_l): bseq[i] = self.seq2int_dict[seq[i]] res.n_cigar = 0 abpoa_align_sequence_to_graph(self.ab, &self.abpt, bseq, seq_l, &res) abpoa_add_graph_alignment(self.ab, &self.abpt, bseq, NULL, seq_l, NULL, res, exist_n+read_i, tot_n, 1) free(bseq) if res.n_cigar: free(res.graph_cigar) if self.abpt.out_msa: abpoa_generate_rc_msa(self.ab, &self.abpt) elif self.abpt.out_cons: abpoa_generate_consensus(self.ab, &self.abpt) abc = self.ab[0].abc[0] n_cons, clu_n_seq, clu_read_ids, cons_len, cons_seq, cons_cov, msa_len, msa_seq = 0, [], [], [], [], [], 0, [] n_cons = abc.n_cons for i in range(n_cons): clu_n_seq.append(abc.clu_n_seq[i]) cons_len.append(abc.cons_len[i]) clu_read_ids1, cons_seq1, cons_cov1 = [], '', [] for j in range(abc.clu_n_seq[i]): clu_read_ids1.append(abc.clu_read_ids[i][j]) clu_read_ids.append(clu_read_ids1) for j in range(abc.cons_len[i]): c = abc.cons_base[i][j] if isinstance(c, bytes): c = ord(c) cons_seq1 += self.int2seq_dict[c] cons_cov1.append(abc.cons_cov[i][j]) cons_seq.append(cons_seq1) cons_cov.append(cons_cov1) msa_len = abc.msa_len if msa_len > 0: for i in range(abc.n_seq + n_cons): msa_seq1 = '' for c in abc.msa_base[i][:msa_len]: if isinstance(c, bytes): c = ord(c) msa_seq1 += self.int2seq_dict[c] msa_seq.append(msa_seq1) if self.abpt.out_pog: abpoa_dump_pog(self.ab, &self.abpt) return msa_result(tot_n, n_cons, clu_n_seq, clu_read_ids, cons_len, cons_seq, cons_cov, msa_len, msa_seq) abPOA-1.4.1/setup.py000066400000000000000000000052531425041320700141540ustar00rootroot00000000000000import os, platform, sys try: from setuptools import setup, Extension except ImportError: from distutils.core import setup from distutils.extension import Extension cmdclass = {} try: from Cython.Build import build_ext except ImportError: # without Cython #module_src = 'python/pyabpoa.c' sys.stderr.write('Error: \'cython\' is required to install pyabpoa\n') sys.exit(0) else: # with Cython module_src = 'python/pyabpoa.pyx' cmdclass['build_ext'] = build_ext simde = '-DUSE_SIMDE -DSIMDE_ENABLE_NATIVE_ALIASES' sys.path.append('python') if platform.machine() in ["aarch64", "arm64"]: simd_flag = '-march=armv8-a+simd -D__AVX2__' elif platform.machine() in ["aarch32"]: simd_flag = '-march=armv8-a+simd -mfput=auto -D__AVX2__' else: simd_flag='-march=native' if os.getenv('SSE4', False): simd_flag='-msse4.1' elif os.getenv('SSE2', False): simd_flag='-msse2' elif os.getenv('AVX2', False): simd_flag='-mavx2' #elif os.getenv('AVX512F', False): # simd_flag='-mavx512f' #elif os.getenv('AVX512BW', False): # simd_flag='-mavx512bw' src_dir='src/' inc_dir='include/' src=[module_src, src_dir+'abpoa_align.c', src_dir+'abpoa_graph.c', src_dir+'abpoa_output.c', src_dir+'abpoa_plot.c', src_dir+'abpoa_seed.c', src_dir+'abpoa_seq.c', src_dir+'kalloc.c', src_dir+'kstring.c', src_dir+'simd_abpoa_align.c', src_dir+'simd_check.c', src_dir+'utils.c'] long_description = open('python/README.md').read() setup( # Information name = "pyabpoa", description = "pyabpoa: SIMD-based partial order alignment using adaptive band", long_description = long_description, long_description_content_type="text/markdown", version = "1.4.1", url = "https://github.com/yangao07/abPOA", author = "Yan Gao", author_email = "gaoy1@chop.edu", license = "MIT", keywords = "multiple-sequence-alignment partial-order-graph-alignment", # Build instructions ext_modules = [Extension("pyabpoa", sources=src, include_dirs=[inc_dir], depends=[src_dir+'abpoa.h', src_dir+'abpoa_align.h', src_dir+'abpoa_graph.h', src_dir+'abpoa_output.h', src_dir+'abpoa_seed.h', src_dir+'abpoa_seq.h', src_dir+'kalloc.h', src_dir+'khash.h', src_dir+'kdq.h', src_dir+'kseq.h', src_dir+'ksort.h', src_dir+'kstring.h', src_dir+'kvec.h', src_dir+'simd_abpoa_align.h', src_dir+'simd_instruction.h', src_dir+'utils.h', 'python/cabpoa.pxd'], libraries = ['z', 'm', 'pthread'], extra_compile_args=['-O3', '-Wno-error=declaration-after-statement', simde, simd_flag])], install_requires=['cython'], cmdclass = cmdclass ) abPOA-1.4.1/src/000077500000000000000000000000001425041320700132245ustar00rootroot00000000000000abPOA-1.4.1/src/abpoa.c000066400000000000000000000264331425041320700144620ustar00rootroot00000000000000#include #include #include #include #include "abpoa.h" #include "abpoa_graph.h" #include "abpoa_align.h" #include "abpoa_seq.h" #include "utils.h" char NAME[20] = "abPOA"; char PROG[20] = "abpoa"; #define _ba BOLD UNDERLINE "a" NONE #define _bb BOLD UNDERLINE "b" NONE #define _bP BOLD UNDERLINE "P" NONE #define _bO BOLD UNDERLINE "O" NONE #define _bA BOLD UNDERLINE "A" NONE char DESCRIPTION[100] = _ba "daptive " _bb "anded " _bP "artial " _bO "rder " _bA "lignment"; char VERSION[20] = "1.4.1"; char CONTACT[30] = "gaoy1@chop.edu"; const struct option abpoa_long_opt [] = { { "align-mode", 1, NULL, 'm' }, { "match", 1, NULL, 'M' }, { "mismatch", 1, NULL, 'X' }, { "matrix", 1, NULL, 't' }, { "gap-open", 1, NULL, 'O' }, { "gap-ext", 1, NULL, 'E' }, { "extra-b", 1, NULL, 'b' }, { "extra-f", 1, NULL, 'f' }, { "zdrop", 1, NULL, 'z' }, { "bouns", 1, NULL, 'e' }, { "seeding", 0, NULL, 'S'}, { "k-mer", 1, NULL, 'k' }, { "window", 1, NULL, 'w' }, { "min-poa-win", 1, NULL, 'n' }, { "progressive", 0, NULL, 'p'}, { "use-qual-weight", 0, NULL, 'Q'}, { "amino-acid", 0, NULL, 'c'}, { "in-list", 0, NULL, 'l' }, { "increment", 1, NULL, 'i' }, { "amb-strand", 0, NULL, 's' }, { "output", 1, NULL, 'o' }, { "result", 1, NULL, 'r' }, { "out-pog", 1, NULL, 'g' }, { "max-num-cons", 1, NULL, 'd', }, { "min-freq", 1, NULL, 'q', }, { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'v' }, { 0, 0, 0, 0} }; int abpoa_usage(void) { err_printf("\n"); err_printf("%s: %s \n\n", PROG, DESCRIPTION); err_printf("Version: %s\t", VERSION); err_printf("Contact: %s\n\n", CONTACT); err_printf("Usage: %s [options] > cons.fa/msa.out/abpoa.gfa\n\n", PROG); err_printf("Options:\n"); err_printf(" Alignment:\n"); err_printf(" -m --aln-mode INT alignment mode [%d]\n", ABPOA_GLOBAL_MODE); err_printf(" %d: global, %d: local, %d: extension\n", ABPOA_GLOBAL_MODE, ABPOA_LOCAL_MODE, ABPOA_EXTEND_MODE); err_printf(" -M --match INT match score [%d]\n", ABPOA_MATCH); err_printf(" -X --mismatch INT mismatch penalty [%d]\n", ABPOA_MISMATCH); err_printf(" -t --matrix FILE scoring matrix file, \'-M\' and \'-X\' are not used when \'-t\' is used [Null]\n"); err_printf(" e.g., \'HOXD70.mtx, BLOSUM62.mtx\'\n"); err_printf(" -O --gap-open INT(,INT) gap opening penalty (O1,O2) [%d,%d]\n", ABPOA_GAP_OPEN1, ABPOA_GAP_OPEN2); err_printf(" -E --gap-ext INT(,INT) gap extension penalty (E1,E2) [%d,%d]\n", ABPOA_GAP_EXT1, ABPOA_GAP_EXT2); err_printf(" %s provides three gap penalty modes, cost of a g-long gap:\n", NAME); err_printf(" - convex (default): min{O1+g*E1, O2+g*E2}\n"); err_printf(" - affine (set O2 as 0): O1+g*E1\n"); err_printf(" - linear (set O1 as 0): g*E1\n"); err_printf(" -s --amb-strand ambiguous strand mode [False]\n"); err_printf(" for each input sequence, try the reverse complement if the current\n"); err_printf(" alignment score is too low, and pick the strand with a higher score\n"); err_printf(" Adaptive banded DP:\n"); err_printf(" -b --extra-b INT first adaptive banding parameter [%d]\n", ABPOA_EXTRA_B); err_printf(" set b as < 0 to disable adaptive banded DP\n"); err_printf(" -f --extra-f FLOAT second adaptive banding parameter [%.2f]\n", ABPOA_EXTRA_F); err_printf(" the number of extra bases added on both sites of the band is\n"); err_printf(" b+f*L, where L is the length of the aligned sequence\n"); // err_printf(" -z --zdrop INT Z-drop score in extension alignment [-1]\n"); // err_printf(" set as <= 0 to disable Z-drop extension\n"); // err_printf(" -e --bonus INT end bonus score in extension alignment [-1]\n"); // err_printf(" set as <= 0 to disable end bounus\n"); err_printf(" Minimizer-based seeding and partition (only effective in global alignment mode):\n"); err_printf(" -S --seeding enable minimizer-based seeding and anchoring [False]\n"); err_printf(" -k --k-mer INT minimizer k-mer size [%d]\n", ABPOA_MMK); err_printf(" -w --window INT minimizer window size [%d]\n", ABPOA_MMW); err_printf(" -n --min-poa-win INT min. size of window to perform POA [%d]\n", ABPOA_MIN_POA_WIN); err_printf(" -p --progressive build guide tree and perform progressive partial order alignment [False]\n"); // err_printf(" -n --par-size minimal partition size [%d]\n", ABPOA_W); err_printf(" Input/Output:\n"); err_printf(" -Q --use-qual-weight take base quality score from FASTQ input file as graph edge weight [False]\n"); err_printf(" -c --amino-acid input sequences are amino acid (default is nucleotide) [False]\n"); err_printf(" -l --in-list input file is a list of sequence file names [False]\n"); err_printf(" each line is one sequence file containing a set of sequences\n"); err_printf(" which will be aligned by abPOA to generate a consensus sequence\n"); err_printf(" -i --incrmnt FILE incrementally align sequences to an existing graph/MSA [Null]\n"); err_printf(" graph could be in GFA or MSA format generated by abPOA\n"); err_printf(" -o --output FILE ouput to FILE [stdout]\n"); err_printf(" -r --result INT output result mode [%d]\n", ABPOA_OUT_CONS); err_printf(" - %d: consensus in FASTA format\n", ABPOA_OUT_CONS); err_printf(" - %d: MSA in PIR format\n", ABPOA_OUT_MSA); err_printf(" - %d: both 0 & 1\n", ABPOA_OUT_CONS_MSA); err_printf(" - %d: graph in GFA format\n", ABPOA_OUT_GFA); err_printf(" - %d: graph with consensus path in GFA format\n", ABPOA_OUT_CONS_GFA); err_printf(" - %d: consensus in FASTQ format\n", ABPOA_OUT_CONS_FQ); err_printf(" -d --maxnum-cons INT max. number of consensus sequence to generate [1]\n"); err_printf(" -q --min-freq FLOAT min. frequency of each consensus sequence (only effective when -d/--num-cons > 1) [%.2f]\n", MULTIP_MIN_FREQ); err_printf(" -g --out-pog FILE dump final alignment graph to FILE (.pdf/.png) [Null]\n\n"); err_printf(" -h --help print this help usage information\n"); err_printf(" -v --version show version number\n"); err_printf("\n"); return 1; } int abpoa_main(char *file_fn, int is_list, abpoa_para_t *abpt){ double realtime0 = realtime(); // TODO abpoa_init for each input file ??? abpoa_t *ab = abpoa_init(); if (is_list) { // input file list FILE *list_fp = fopen(file_fn, "r"); char read_fn[1024]; while (fgets(read_fn, sizeof(read_fn), list_fp)) { read_fn[strlen(read_fn)-1] = '\0'; abpoa_msa1(ab, abpt, read_fn, stdout); } fclose(list_fp); } else // input file abpoa_msa1(ab, abpt, file_fn, stdout); abpoa_free(ab); err_func_printf(__func__, "Real time: %.3f sec; CPU: %.3f sec; Peak RSS: %.3f GB.", realtime() - realtime0, cputime(), peakrss() / 1024.0 / 1024.0 / 1024.0); return 0; } int main(int argc, char **argv) { int c, m, in_list=0; char *s; abpoa_para_t *abpt = abpoa_init_para(); while ((c = getopt_long(argc, argv, "m:M:X:t:O:E:b:f:z:e:QSk:w:n:i:clpso:r:g:d:q:hvV:", abpoa_long_opt, NULL)) >= 0) { switch(c) { case 'm': m = atoi(optarg); if (m != ABPOA_GLOBAL_MODE && m != ABPOA_EXTEND_MODE && m != ABPOA_LOCAL_MODE) { err_printf("Unknown alignment mode: %d.\n", m); return abpoa_usage(); } abpt->align_mode=m; break; case 'M': abpt->match = atoi(optarg); break; case 'X': abpt->mismatch = atoi(optarg); break; case 't': abpt->use_score_matrix = 1; abpt->mat_fn = strdup(optarg); break; case 'O': abpt->gap_open1 = strtol(optarg, &s, 10); if (*s == ',') abpt->gap_open2 = strtol(s+1, &s, 10); break; case 'E': abpt->gap_ext1 = strtol(optarg, &s, 10); if (*s == ',') abpt->gap_ext2 = strtol(s+1, &s, 10); break; case 'b': abpt->wb = atoi(optarg); break; case 'f': abpt->wf = atof(optarg); break; case 'z': abpt->zdrop = atoi(optarg); break; case 'e': abpt->end_bonus= atoi(optarg); break; case 'Q': abpt->use_qv = 1; break; case 'S': abpt->disable_seeding = 0; break; case 'k': abpt->k = atoi(optarg); break; case 'w': abpt->w = atoi(optarg); break; case 'n': abpt->min_w = atoi(optarg); break; case 'c': abpt->m = 27; abpt->mat = (int*)_err_realloc(abpt->mat, abpt->m * abpt->m * sizeof(int)); break; case 'i': abpt->incr_fn = strdup(optarg); break; case 'l': in_list = 1; break; case 'p': abpt->progressive_poa = 1; break; case 's': abpt->amb_strand = 1; break; case 'o': if (strcmp(optarg, "-") != 0) { if (freopen(optarg, "wb", stdout) == NULL) err_fatal(__func__, "Failed to open the output file %s", optarg); } break; case 'r': if (atoi(optarg) == ABPOA_OUT_CONS) abpt->out_cons = 1, abpt->out_msa = 0; else if (atoi(optarg) == ABPOA_OUT_MSA) abpt->out_cons = 0, abpt->out_msa = 1; else if (atoi(optarg) == ABPOA_OUT_CONS_MSA) abpt->out_cons = abpt->out_msa = 1; else if (atoi(optarg) == ABPOA_OUT_GFA) abpt->out_cons = 0, abpt->out_gfa = 1; else if (atoi(optarg) == ABPOA_OUT_CONS_GFA) abpt->out_cons = 1, abpt->out_gfa = 1; else if (atoi(optarg) == ABPOA_OUT_CONS_FQ) abpt->out_cons = 1, abpt->out_fq = 1; else err_printf("Error: unknown output result mode: %s.\n", optarg); break; case 'g': abpt->out_pog= strdup(optarg); break; case 'd': abpt->max_n_cons = atoi(optarg); break; case 'q': abpt->min_freq = atof(optarg); break; case 'h': return abpoa_usage(); case 'V': abpt->verbose = atoi(optarg); break; case 'v': printf("%s\n", VERSION); goto End; break; default: err_printf("Error: unknown option: %s.\n", optarg); return abpoa_usage(); break; } } if (argc - optind != 1) return abpoa_usage(); abpoa_post_set_para(abpt); fprintf(stderr, "[%s] CMD: ", __func__); for (c = 0; c < argc; ++c) fprintf(stderr, " %s", argv[c]); fprintf(stderr, "\n"); abpoa_main(argv[optind], in_list, abpt); End: abpoa_free_para(abpt); return 0; } abPOA-1.4.1/src/abpoa.h000066400000000000000000000207561425041320700144710ustar00rootroot00000000000000#ifndef ABPOA_H #define ABPOA_H #include #include "simd_instruction.h" #define ABPOA_GLOBAL_MODE 0 #define ABPOA_LOCAL_MODE 1 #define ABPOA_EXTEND_MODE 2 //#define ABPOA_SEMI_MODE 3 // gap mode #define ABPOA_LINEAR_GAP 0 #define ABPOA_AFFINE_GAP 1 #define ABPOA_CONVEX_GAP 2 #define ABPOA_EXTRA_B 10 #define ABPOA_EXTRA_F 0.01 #define ABPOA_CIGAR_STR "MIDXSH" #define ABPOA_CMATCH 0 #define ABPOA_CINS 1 #define ABPOA_CDEL 2 #define ABPOA_CDIFF 3 #define ABPOA_CSOFT_CLIP 4 #define ABPOA_CHARD_CLIP 5 #define ABPOA_SRC_NODE_ID 0 #define ABPOA_SINK_NODE_ID 1 #define ABPOA_OUT_CONS 0 #define ABPOA_OUT_MSA 1 #define ABPOA_OUT_CONS_MSA 2 #define ABPOA_OUT_GFA 3 #define ABPOA_OUT_CONS_GFA 4 #define ABPOA_OUT_CONS_FQ 5 #define ABPOA_HB 0 #define ABPOA_HC 1 // NOTE: upper boundary of in_edge_n is pow(2,30) // for MATCH/MISMATCH: node_id << 34 | query_id << 4 | op // for INSERTION: query_id << 34 | op_len << 4 | op // for DELETION: node_id << 34 | op_len << 4 | op // op_len is always equal to 1 // for CLIP query_id << 34 | op_len << 4 | op #define abpoa_cigar_t uint64_t #ifdef __cplusplus extern "C" { #endif typedef struct { int n_cigar, m_cigar; abpoa_cigar_t *graph_cigar; int node_s, node_e, query_s, query_e; // for local and extension mode int n_aln_bases, n_matched_bases; int32_t best_score; // uint8_t is_rc:1; // is_rc: best_score is from the reverse complement // now is_rc is determined based on minimizer-based seeding and chaining } abpoa_res_t; typedef struct { int m; int *mat; char *mat_fn; // score matrix int use_score_matrix; // set _mat_ based on score matrix file, then _match_/_mismatch_ is not used. int match, max_mat, mismatch, min_mis, gap_open1, gap_open2, gap_ext1, gap_ext2; int inf_min; // minimizer seeding parameter int k, w, min_w; int wb; float wf; // extra band width int zdrop, end_bonus; // from minimap2 // int simd_flag; // available SIMD instruction // alignment mode uint8_t ret_cigar:1, rev_cigar:1, out_msa:1, out_cons:1, out_gfa:1, out_fq:1, use_read_ids:1, amb_strand:1; uint8_t use_qv:1, disable_seeding:1, progressive_poa:1; char *incr_fn, *out_pog; int align_mode, gap_mode, max_n_cons; double min_freq; // for multiploid data int verbose; // to control output msg // char LogTable65536[65536]; // char bit_table16[65536]; } abpoa_para_t; typedef struct { int node_id; int in_edge_n, in_edge_m, *in_id; int out_edge_n, out_edge_m, *out_id; int *out_weight; int *read_weight, n_read, m_read; // weight of each read, valid when use_qv=1 uint64_t **read_ids; int read_ids_n; // for each edge int aligned_node_n, aligned_node_m, *aligned_node_id; // mismatch; aligned node will have same rank // int heaviest_weight, heaviest_out_id; // for consensus uint8_t base; // 0~m // ID, pos ??? } abpoa_node_t; typedef struct { abpoa_node_t *node; int node_n, node_m, index_rank_m; int *index_to_node_id; int *node_id_to_index, *node_id_to_max_pos_left, *node_id_to_max_pos_right, *node_id_to_max_remain, *node_id_to_msa_rank; uint8_t is_topological_sorted:1, is_called_cons:1, is_set_msa_rank:1; } abpoa_graph_t; typedef struct { int n_cons, n_seq, msa_len; // # cons, # of total seq, length of row-column MSA (including gaps) int *clu_n_seq; // # of reads in each read cluster/group, size: n_cons int **clu_read_ids; // read ids for each cluster/group, size: n_cons * clu_n_seq[i] int *cons_len; // length of each consensus sequence, size: n_cons int **cons_node_ids; // node id of each consensus, size: n_cons * cons_len[i] uint8_t **cons_base; // sequence base of each consensus, size: n_cons * cons_len[i] uint8_t **msa_base; // sequence base of RC-MSA, size: (n_seq + n_cons) * msa_len int **cons_cov; // coverage of each consensus base, size: n_cons * cons_len[i] int **cons_phred_score; // phred score for each consensus base, size: n_cons * cons_len[i] } abpoa_cons_t; typedef struct { int l, m; char *s; } abpoa_str_t; typedef struct { int n_seq, m_seq; abpoa_str_t *seq, *name, *comment, *qual; uint8_t *is_rc; } abpoa_seq_t; typedef struct { SIMDi *s_mem; uint64_t s_msize; // qp, DP_HE, dp_f OR qp, DP_H, dp_f : based on (qlen, num_of_value, m, node_n) int *dp_beg, *dp_end, *dp_beg_sn, *dp_end_sn, rang_m; // if band : based on (node_m) } abpoa_simd_matrix_t; typedef struct { abpoa_graph_t *abg; abpoa_seq_t *abs; abpoa_simd_matrix_t *abm; abpoa_cons_t *abc; } abpoa_t; // init for abpoa parameters abpoa_para_t *abpoa_init_para(void); void abpoa_set_mat_from_file(abpoa_para_t *abpt, char *mat_fn); void abpoa_post_set_para(abpoa_para_t *abpt); void abpoa_free_para(abpoa_para_t *abpt); // init for alignment abpoa_t *abpoa_init(void); void abpoa_free(abpoa_t *ab); // perform msa int abpoa_msa(abpoa_t *ab, abpoa_para_t *abpt, int n_seqs, char **seq_names, int *seq_lens, uint8_t **seqs, int **qual_weights, FILE *out_fp); int abpoa_msa1(abpoa_t *ab, abpoa_para_t *abpt, char *read_fn, FILE *out_fp); // clean alignment graph void abpoa_reset(abpoa_t *ab, abpoa_para_t *abpt, int qlen); // restore graph from GFA/FASTA file abpoa_t *abpoa_restore_graph(abpoa_t *ab, abpoa_para_t *abpt); // for development: // align a sequence to a graph int abpoa_align_sequence_to_graph(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int qlen, abpoa_res_t *res); // align a sequence to a graph between beg_node_id and end_node_id (both are excluded) void abpoa_subgraph_nodes(abpoa_t *ab, abpoa_para_t *abpt, int inc_beg, int inc_end, int *exc_beg, int *exc_end); int abpoa_align_sequence_to_subgraph(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int qlen, abpoa_res_t *res); // add a node to a graph // para: // base: 0123 for ACGT int abpoa_add_graph_node(abpoa_graph_t *abg, uint8_t base); // add an edge to a graph // para: // from_id/to_id: ids of from and to nodes // check_edge: set as 1 if this edge maybe alread exist and only need to update weight, set as 0 if the edge is new // add_read_id: set as 1 if read_id is used (to use row-column algorithm/generate MSA result/multiple consensus) // read_id: is of sequence // read_ids_n: size of read_id array, each one is 64-bit (1+(tot_read_n-1)/64) int abpoa_add_graph_edge(abpoa_graph_t *abg, int from_id, int to_id, int check_edge, int w, uint8_t add_read_id, uint8_t add_read_weight, int read_id, int read_ids_n, int tot_read_n); // add an alignment to a graph // para: // query: 0123 for ACGT // qlen: query length // n_cigar/abpoa_cigar: from alignment result (abpoa_res_t) // read_id: id of sequence // tot_read_n: total number of sequence int abpoa_add_graph_alignment(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int *weight, int qlen, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends); int abpoa_add_subgraph_alignment(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int *weight, int qlen, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends); void abpoa_BFS_set_node_index(abpoa_graph_t *abg, int src_id, int sink_id); void abpoa_BFS_set_node_remain(abpoa_graph_t *abg, int src_id, int sink_id); // topological sortting of graph void abpoa_topological_sort(abpoa_graph_t *abg, abpoa_para_t *abpt); // generate consensus sequence from graph // para: // out_fp: consensus sequence output in FASTA format, set as NULL to disable // cons_seq, cons_l, cons_n: store consensus sequences in variables, set cons_n as NULL to disable. // cons_seq: store consensus sequences // cons_l: store consensus sequences length // cons_n: store number of consensus sequences // Note: cons_seq and cons_l need to be freed by user. void abpoa_generate_consensus(abpoa_t *ab, abpoa_para_t *abpt); void abpoa_output_fx_consensus(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // generate column multiple sequence alignment from graph void abpoa_generate_rc_msa(abpoa_t *ab, abpoa_para_t *abpt); void abpoa_output_rc_msa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // generate graph in GFA format to _out_fp_ void abpoa_generate_gfa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // output cons/msa void abpoa_output(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp); // generate DOT graph plot and dump graph into PDF/PNG format file void abpoa_dump_pog(abpoa_t *ab, abpoa_para_t *abpt); #ifdef __cplusplus } #endif #endif abPOA-1.4.1/src/abpoa_align.c000066400000000000000000000532021425041320700156260ustar00rootroot00000000000000#include #include #include #include "abpoa.h" #include "simd_abpoa_align.h" #include "abpoa_align.h" #include "abpoa_seq.h" #include "abpoa_output.h" #include "utils.h" #include "abpoa_seed.h" void gen_simple_mat(abpoa_para_t *abpt) { int m = abpt->m, i, j; int match = abpt->match < 0 ? -abpt->match : abpt->match; int mismatch = abpt->mismatch > 0? -abpt->mismatch : abpt->mismatch; for (i = 0; i < m - 1; ++i) { for (j = 0; j < m - 1; ++j) abpt->mat[i * m + j] = i == j ? match : mismatch; abpt->mat[i * m + m - 1] = 0; } for (j = 0; j < m; ++j) abpt->mat[(m - 1) * m + j] = 0; abpt->max_mat = match; abpt->min_mis = -mismatch; } extern char ab_nt4_table[256]; extern char ab_nt256_table[256]; extern char ab_aa26_table[256]; extern char ab_aa256_table[256]; extern char ab_char26_table[256]; extern char ab_char256_table[256]; void parse_mat_first_line(char *l, int *order) { int i, n; for (i = n = 0; l[i]; ++i) { if (isspace(l[i])) continue; order[n++] = ab_char26_table[(int)l[i]]; } } void parse_mat_score_line(char *l, int *order, int m, int *mat) { int n, is_base=1, _i=-1; long s; char *str = l, *pEnd=NULL; for (n = 0; *str; ++str) { if (!isalpha(*str) && !isdigit(*str) && *str != '+' && *str != '-') continue; if (is_base) { // get base _i = ab_char26_table[(int)*str]; if (_i >= m) err_fatal(__func__, "Unknown base: \"%c\" (%d).\n", *str, _i); is_base = 0; } else { // get score if (n == m) err_fatal_simple("Too many scores in matrix.\n"); s = strtol(str, &pEnd, 10); str = pEnd; mat[_i *m + order[n]] = s; n++; } } } void abpoa_set_mat_from_file(abpoa_para_t *abpt, char *mat_fn) { char *l = (char*)_err_malloc(1024 * sizeof(char)); FILE *fp; if ((fp = fopen(mat_fn, "r")) == NULL) err_fatal(__func__, "Unable to open scoring matrix file: \"%s\"\n", mat_fn); int first_line = 1; int *order = (int*)_err_malloc(abpt->m * sizeof(int)); while (fgets(l, 1024, fp) != NULL) { if (l[0] == '#') continue; if (first_line) { first_line = 0; // get A/C/G/T/N bases parse_mat_first_line(l, order); } else { // get match/mismatch scores parse_mat_score_line(l, order, abpt->m, abpt->mat); } } int i; abpt->min_mis = 0, abpt->max_mat = 0; for (i = 0; i < abpt->m * abpt->m; ++i) { if (abpt->mat[i] > abpt->max_mat) abpt->max_mat = abpt->mat[i]; if (-abpt->mat[i] > abpt->min_mis) abpt->min_mis = -abpt->mat[i]; } free(l); free(order); fclose(fp); } void abpoa_set_gap_mode(abpoa_para_t *abpt) { if (abpt->gap_open1 == 0) abpt->gap_mode = ABPOA_LINEAR_GAP; else if (abpt->gap_open1 > 0 && abpt->gap_open2 == 0) abpt->gap_mode = ABPOA_AFFINE_GAP; else abpt->gap_mode = ABPOA_CONVEX_GAP; } abpoa_para_t *abpoa_init_para(void) { abpoa_para_t *abpt = (abpoa_para_t*)_err_malloc(sizeof(abpoa_para_t)); abpt->align_mode = ABPOA_GLOBAL_MODE; abpt->gap_mode = ABPOA_CONVEX_GAP; abpt->zdrop = -1; // disable zdrop abpt->end_bonus = -1; // disable end bouns abpt->wb = ABPOA_EXTRA_B; // extra bandwidth abpt->wf = ABPOA_EXTRA_F; // extra bandwidth abpt->amb_strand = 0; // ambiguous strand abpt->ret_cigar = 1; // return cigar abpt->rev_cigar = 0; // reverse cigar abpt->out_cons = 1; // output consensus sequence in fasta abpt->out_fq = 0; // output consensus sequence in fastq abpt->out_gfa = 0; // out graph in GFA format abpt->out_msa = 0; // output msa abpt->max_n_cons = 1; // number of max. generated consensus sequence abpt->min_freq = MULTIP_MIN_FREQ; abpt->use_read_ids = 0; abpt->incr_fn = NULL; // incrementally align seq to an existing graph abpt->out_pog = NULL; // dump partial order graph to file // number of residue types abpt->m = 5; // nucleotide abpt->mat = (int*)_err_malloc(abpt->m * abpt->m * sizeof(int)); // score matrix abpt->use_score_matrix = 0; abpt->mat_fn = NULL; abpt->match = ABPOA_MATCH; abpt->mismatch = ABPOA_MISMATCH; abpt->gap_open1 = ABPOA_GAP_OPEN1; abpt->gap_open2 = ABPOA_GAP_OPEN2; abpt->gap_ext1 = ABPOA_GAP_EXT1; abpt->gap_ext2 = ABPOA_GAP_EXT2; abpt->use_qv = 0; abpt->disable_seeding = 1; // no seeding by default abpt->k = ABPOA_MMK; abpt->w = ABPOA_MMW; abpt->min_w = ABPOA_MIN_POA_WIN; abpt->progressive_poa = 0; // progressive partial order alignment abpt->verbose = 0; // abpt->simd_flag = simd_check(); return abpt; } void abpoa_post_set_para(abpoa_para_t *abpt) { abpoa_set_gap_mode(abpt); if (abpt->out_msa || abpt->out_gfa || abpt->max_n_cons > 1) { abpt->use_read_ids = 1; set_65536_table(); if (abpt->max_n_cons > 1) set_bit_table16(); } if (abpt->align_mode == ABPOA_LOCAL_MODE) abpt->wb = -1; int i; if (abpt->m > 5) { // for aa sequence for (i = 0; i < 256; ++i) { ab_char26_table[i] = ab_aa26_table[i]; ab_char256_table[i] = ab_aa256_table[i]; } if (abpt->k > 11) { abpt->k = 7, abpt->w = 4; } } else { for (i = 0; i < 256; ++i) { ab_char26_table[i] = ab_nt4_table[i]; ab_char256_table[i] = ab_nt256_table[i]; } } if (abpt->use_score_matrix == 0) gen_simple_mat(abpt); else abpoa_set_mat_from_file(abpt, abpt->mat_fn); } void abpoa_free_para(abpoa_para_t *abpt) { if (abpt->mat != NULL) free(abpt->mat); if (abpt->mat_fn != NULL) free(abpt->mat_fn); if (abpt->out_pog != NULL) free(abpt->out_pog); if (abpt->incr_fn != NULL) free(abpt->incr_fn); free(abpt); } int abpoa_align_sequence_to_subgraph(abpoa_t *ab, abpoa_para_t *abpt, int exc_beg_node_id, int exc_end_node_id, uint8_t *query, int qlen, abpoa_res_t *res) { if (ab->abg->node_n <= 2) return -1; if (ab->abg->is_topological_sorted == 0) abpoa_topological_sort(ab->abg, abpt); simd_abpoa_align_sequence_to_subgraph(ab, abpt, exc_beg_node_id, exc_end_node_id, query, qlen, res); return 0; } int abpoa_align_sequence_to_graph(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int qlen, abpoa_res_t *res) { if (ab->abg->node_n <= 2) return -1; if (ab->abg->is_topological_sorted == 0) abpoa_topological_sort(ab->abg, abpt); simd_abpoa_align_sequence_to_graph(ab, abpt, query, qlen, res); return 0; } int abpoa_anchor_poa(abpoa_t *ab, abpoa_para_t *abpt, uint8_t **seqs, int **weights, int *seq_lens, ab_u64_v par_anchors, int *par_c, int *tpos_to_node_id, int *qpos_to_node_id, int *read_id_map, int exist_n_seq, int n_seq) { // err_func_format_printf(__func__, "Performing POA between anchors ..."); abpoa_res_t res; int read_id, last_read_id = -1, m_c = 0, k = abpt->k, qlen; abpoa_seq_t *abs = ab->abs; int *tmp; int i, _i, ai, j, tot_n_seq = exist_n_seq + n_seq; uint8_t *qseq; int *weight; abpoa_res_t whole_res; // uint8_t *seq1; for (_i = 0; _i < n_seq; ++_i) { i = read_id_map[_i]; read_id = exist_n_seq + i; qlen = seq_lens[i]; whole_res.n_cigar = 0, whole_res.m_cigar = 0, whole_res.graph_cigar = 0; #ifdef __DEBUG__ fprintf(stderr, "seq: # %d\n", i); #endif // seq-to-graph alignment and add alignment within each split window if (_i == 0) ai = 0; else ai = par_c[_i-1]; int beg_id = ABPOA_SRC_NODE_ID, beg_qpos = 0, end_id=-1, end_tpos=-1, end_qpos=-1; if (ai < par_c[_i]) { abs->is_rc[read_id] = (abs->is_rc[last_read_id] ^ (par_anchors.a[ai] >> 63)); // construct rc qseq if (abs->is_rc[read_id]) { qseq = (uint8_t*)_err_malloc(qlen * sizeof(uint8_t)); weight = (int*)_err_malloc(qlen * sizeof(int)); for (j = 0; j < qlen; ++j) { if (seqs[i][qlen-j-1] < 4) qseq[j] = 3 - seqs[i][qlen-j-1]; else qseq[j] = 4; weight[j] = weights[i][qlen-j-1]; } if (abs->is_rc[last_read_id]) { // reset tpos/qpos in par_anchors int last_qlen = seq_lens[read_id_map[_i-1]]; for (j = ai; j < par_c[_i]; ++j) { end_tpos = ((par_anchors.a[j] >> 32) & 0x7fffffff); end_qpos = (uint32_t)par_anchors.a[j]; par_anchors.a[j] = (par_anchors.a[j] >> 63) << 63 | (uint64_t)(last_qlen-end_tpos+k) << 32 | (qlen-end_qpos+k); } for (j = 0; j < (par_c[_i]-ai)/2; ++j) { uint64_t tmp = par_anchors.a[ai+j]; par_anchors.a[ai+j] = par_anchors.a[par_c[_i]-1-j]; par_anchors.a[par_c[_i]-1-j] = tmp; } } } else { qseq = seqs[i]; weight = weights[i]; if (abs->is_rc[last_read_id]) { // reset tpos/qpos in par_anchors int last_qlen = seq_lens[read_id_map[_i-1]]; for (j = ai; j < par_c[_i]; ++j) { end_tpos = ((par_anchors.a[j] >> 32) & 0x7fffffff); end_qpos = (uint32_t)par_anchors.a[j]; par_anchors.a[j] = (par_anchors.a[j] >> 63) << 63 | (uint64_t)(last_qlen-end_tpos+k) << 32 | (qlen-end_qpos+k); } for (j = 0; j < (par_c[_i]-ai)/2; ++j) { uint64_t tmp = par_anchors.a[ai+j]; par_anchors.a[ai+j] = par_anchors.a[par_c[_i]-1-j]; par_anchors.a[par_c[_i]-1-j] = tmp; } } } } else { abs->is_rc[read_id] = 0, qseq = seqs[i]; weight = weights[i]; } for (; ai < par_c[_i]; ++ai) { end_tpos = ((par_anchors.a[ai] >> 32) & 0x7fffffff) - k + 1; end_id = tpos_to_node_id[end_tpos]; end_qpos = (uint32_t)par_anchors.a[ai] - k + 1; #ifdef __DEBUG__ fprintf(stderr, "\tanchor: t: %d (id: %d), q: %d\n", end_tpos, end_id, end_qpos); #endif res.graph_cigar = 0; res.n_cigar = 0; abpoa_align_sequence_to_subgraph(ab, abpt, beg_id, end_id, qseq+beg_qpos, end_qpos-beg_qpos, &res); abpoa_push_whole_cigar(&whole_res.n_cigar, &whole_res.m_cigar, &whole_res.graph_cigar, res.n_cigar, res.graph_cigar); if (res.n_cigar) free(res.graph_cigar); // abpoa_add_subgraph_alignment(ab, abpt, beg_id, end_id, qseq+beg_qpos, end_qpos-beg_qpos, qpos_to_node_id+beg_qpos, res, read_id, tot_n_seq, 1); // add alignment for anchors res.graph_cigar = (abpoa_cigar_t*)_err_malloc((k) * sizeof(abpoa_cigar_t)); res.n_cigar = 0; m_c = k; for (j = 0; j < k; ++j) res.graph_cigar = abpoa_push_cigar(&(res.n_cigar), &m_c, res.graph_cigar, ABPOA_CMATCH, 1, tpos_to_node_id[end_tpos+j], j); // for (j = 0; j < k; ++j) qpos_to_node_id[end_qpos+j] = tpos_to_node_id[end_tpos+j]; // abpoa_add_subgraph_alignment(ab, abpt, end_id, tpos_to_node_id[end_tpos+k-1], qseq+end_qpos, k, NULL, res, read_id, tot_n_seq, 1); abpoa_push_whole_cigar(&whole_res.n_cigar, &whole_res.m_cigar, &whole_res.graph_cigar, res.n_cigar, res.graph_cigar); if (res.n_cigar) free(res.graph_cigar); // for next anchor beg_id = tpos_to_node_id[end_tpos+k-1]; beg_qpos = end_qpos+k; } end_id = ABPOA_SINK_NODE_ID; end_qpos = seq_lens[i]; #ifdef __DEBUG__ fprintf(stderr, "\tanchor: t: %d (id: %d), q: %d\n", end_tpos, end_id, end_qpos); #endif res.graph_cigar = 0; res.n_cigar = 0; abpoa_align_sequence_to_subgraph(ab, abpt, beg_id, end_id, qseq+beg_qpos, end_qpos-beg_qpos, &res); abpoa_push_whole_cigar(&whole_res.n_cigar, &whole_res.m_cigar, &whole_res.graph_cigar, res.n_cigar, res.graph_cigar); if (res.n_cigar) free(res.graph_cigar); abpoa_add_subgraph_alignment(ab, abpt, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID, qseq, weight, qlen, qpos_to_node_id, whole_res, read_id, tot_n_seq, 1); if (abs->is_rc[read_id]) { free(qseq); free(weight); } if (whole_res.n_cigar) free(whole_res.graph_cigar); tmp = qpos_to_node_id; qpos_to_node_id = tpos_to_node_id; tpos_to_node_id = tmp; last_read_id = read_id; } // err_func_format_printf(__func__, "Performing POA between anchors done."); return 0; } // simply partial order alignment, no seeding-based anchor or progressive tree int abpoa_poa(abpoa_t *ab, abpoa_para_t *abpt, uint8_t **seqs, int **weights, int *seq_lens, int exist_n_seq, int n_seq) { // err_func_format_printf(__func__, "Performing POA ..."); abpoa_seq_t *abs = ab->abs; abpoa_res_t res; int i, j, read_id, qlen, tot_n_seq = exist_n_seq + n_seq; uint8_t *qseq, *rc_qseq; int *weight, *rc_weight; // uint8_t *seq1; for (i = 0; i < n_seq; ++i) { qlen = seq_lens[i]; qseq = seqs[i]; weight = weights[i]; read_id = exist_n_seq + i; #ifdef __DEBUG__ fprintf(stderr, "seq: # %d\n", i); #endif res.graph_cigar = 0; res.n_cigar = 0; if (abpoa_align_sequence_to_graph(ab, abpt, qseq, qlen, &res) >= 0) { if (abpt->amb_strand && (res.best_score < MIN_OF_TWO(qlen, ab->abg->node_n-2) * abpt->max_mat * .3333)) { // TODO .3333 rc_qseq = (uint8_t*)_err_malloc(sizeof(uint8_t) * qlen); for (j = 0; j < qlen; ++j) { if (qseq[qlen-j-1] < 4) rc_qseq[j] = 3 - qseq[qlen-j-1]; else rc_qseq[j] = 4; } rc_weight = (int*)_err_malloc(sizeof(int) * qlen); for (j = 0; j < qlen; ++j) { rc_weight[j] = weight[qlen-j-1]; } abpoa_res_t rc_res; rc_res.n_cigar = 0, rc_res.graph_cigar = 0; simd_abpoa_align_sequence_to_graph(ab, abpt, rc_qseq, qlen, &rc_res); if (rc_res.best_score > res.best_score) { abpoa_res_copy(&res, &rc_res); qseq = rc_qseq; weight = rc_weight; abs->is_rc[read_id] = 1; } else { free(rc_qseq); free(rc_weight); } if (rc_res.n_cigar) free(rc_res.graph_cigar); } } abpoa_add_graph_alignment(ab, abpt, qseq, weight, qlen, NULL, res, read_id, tot_n_seq, 1); if (abs->is_rc[read_id]) { free(qseq); free(weight); } if (res.n_cigar) free(res.graph_cigar); } // err_func_format_printf(__func__, "Performing POA ... done."); return 0; } void abpoa_output(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) { // generate & output GFA if (abpt->out_gfa) abpoa_generate_gfa(ab, abpt, out_fp); else { // generate rc-msa/cons if (abpt->out_msa) abpoa_generate_rc_msa(ab, abpt); if (abpt->out_cons) { abpoa_generate_consensus(ab, abpt); if (ab->abg->is_called_cons == 0) err_printf("Warning: no consensus sequence generated.\n"); } // output cons/rc-msa if (abpt->out_msa) abpoa_output_rc_msa(ab, abpt, out_fp); else if (abpt->out_cons) abpoa_output_fx_consensus(ab, abpt, out_fp); } // plot partial-order graph using dot if (abpt->out_pog) abpoa_dump_pog(ab, abpt); } // do msa for a set of input sequences // @function: // generate consensus sequence // generate rc-msa (row column multiple sequence alignment) // @para: // ab/abpt: abpoa related variable and parameter // n_seq: number of input sequences // seq_len: array of input sequence length, size: seq_n // seqs: array of input sequences, 0123 for ACGT, size: seq_n * seq_len[] int abpoa_msa(abpoa_t *ab, abpoa_para_t *abpt, int n_seq, char **seq_names, int *seq_lens, uint8_t **seqs, int **qual_weights, FILE *out_fp) { if ((!abpt->out_msa && !abpt->out_cons && !abpt->out_gfa) || n_seq <= 0) return 0; abpoa_reset(ab, abpt, 1024); if (abpt->incr_fn) abpoa_restore_graph(ab, abpt); // restore existing graph abpoa_seq_t *abs = ab->abs; int i, exist_n_seq = abs->n_seq; // set ab->abs, name abs->n_seq += n_seq; abpoa_realloc_seq(abs); if (seq_names) { for (i = 0; i < n_seq; ++i) { abpoa_cpy_str(abs->name+exist_n_seq+i, seq_names[i], strlen(seq_names[i])); } } else { for (i = 0; i < n_seq; ++i) { abs->name[exist_n_seq+i].l = 0; abs->name[exist_n_seq+i].m = 0; } } // always reset graph before perform POA int max_len = 0; for (i = 0; i < n_seq; ++i) { if (seq_lens[i] > max_len) max_len = seq_lens[i]; } int j, **weights = (int**)_err_malloc(n_seq * sizeof(int*)); for (i = 0; i < n_seq; ++i) { weights[i] = (int*)_err_malloc(seq_lens[i] * sizeof(int)); if (abpt->use_qv && qual_weights != NULL && qual_weights[i] != NULL) { for (j = 0; j < seq_lens[i]; ++j) weights[i][j] = (int)qual_weights[i][j]; } else { for (j = 0; j < seq_lens[i]; ++j) weights[i][j] = 1; } } if ((abpt->disable_seeding && abpt->progressive_poa==0) || abpt->align_mode != ABPOA_GLOBAL_MODE) { abpoa_poa(ab, abpt, seqs, weights, seq_lens, exist_n_seq, n_seq); } else { // sequence pos to node id int *tpos_to_node_id = (int*)_err_calloc(max_len, sizeof(int)), *qpos_to_node_id = (int*)_err_calloc(max_len, sizeof(int)); // seeding, build guide tree, and partition into small windows int *read_id_map = (int*)_err_malloc(sizeof(int) * n_seq); // guide tree order -> input order ab_u64_v par_anchors = {0, 0, 0}; int *par_c = (int*)_err_calloc(n_seq, sizeof(int)); abpoa_build_guide_tree_partition(seqs, seq_lens, n_seq, abpt, read_id_map, &par_anchors, par_c); if (abpt->incr_fn) { // collect anchors between last one path and first seq // anchors // new_par_anchors // push anchors // free(par_anchors.a); // par_anchors = new_par_anchors; // collect tpos_to_node_id for last one path } // perform partial order alignment abpoa_anchor_poa(ab, abpt, seqs, weights, seq_lens, par_anchors, par_c, tpos_to_node_id, qpos_to_node_id, read_id_map, exist_n_seq, n_seq); free(read_id_map); free(tpos_to_node_id); free(qpos_to_node_id); free(par_c); if (par_anchors.m > 0) free(par_anchors.a); } // output abpoa_output(ab, abpt, out_fp); for (i = 0; i < n_seq; ++i) free(weights[i]); free(weights); return 0; } int abpoa_msa1(abpoa_t *ab, abpoa_para_t *abpt, char *read_fn, FILE *out_fp) { if (!abpt->out_msa && !abpt->out_cons && !abpt->out_gfa) return 0; abpoa_reset(ab, abpt, 1024); if (abpt->incr_fn) abpoa_restore_graph(ab, abpt); // restore existing graph abpoa_seq_t *abs = ab->abs; int exist_n_seq = abs->n_seq; // read seq from read_fn gzFile readfp = xzopen(read_fn, "r"); kseq_t *ks = kseq_init(readfp); int i, j, n_seq = abpoa_read_seq(abs, ks); // always reset graph before perform POA int max_len = 0; for (i = 0; i < abs->n_seq; ++i) { if (abs->seq[i].l > max_len) max_len = abs->seq[i].l; } // set seqs, seq_lens extern char ab_char26_table[256]; uint8_t **seqs = (uint8_t**)_err_malloc(n_seq * sizeof(uint8_t*)); int *seq_lens = (int*)_err_malloc(n_seq * sizeof(int)); int **weights = (int**)_err_malloc(n_seq * sizeof(int*)); for (i = 0; i < n_seq; ++i) { seq_lens[i] = abs->seq[exist_n_seq+i].l; seqs[i] = (uint8_t*)_err_malloc(sizeof(uint8_t) * seq_lens[i]); weights[i] = (int*)_err_malloc(sizeof(int) * seq_lens[i]); for (j = 0; j < seq_lens[i]; ++j) seqs[i][j] = ab_char26_table[(int)abs->seq[exist_n_seq+i].s[j]]; if (abpt->use_qv && abs->qual[exist_n_seq+i].l > 0) { for (j = 0; j < seq_lens[i]; ++j) weights[i][j] = (int)abs->qual[exist_n_seq+i].s[j]-32; } else { for (j = 0; j < seq_lens[i]; ++j) weights[i][j] = 1; } } if ((abpt->disable_seeding && abpt->progressive_poa==0) || abpt->align_mode != ABPOA_GLOBAL_MODE) { abpoa_poa(ab, abpt, seqs, weights, seq_lens, exist_n_seq, n_seq); } else { // sequence pos to node id int *tpos_to_node_id = (int*)_err_calloc(max_len, sizeof(int)), *qpos_to_node_id = (int*)_err_calloc(max_len, sizeof(int)); // seeding, build guide tree, and partition into small windows int *read_id_map = (int*)_err_malloc(sizeof(int) * n_seq); // guide tree order -> input order ab_u64_v par_anchors = {0, 0, 0}; int *par_c = (int*)_err_calloc(n_seq, sizeof(int)); abpoa_build_guide_tree_partition(seqs, seq_lens, n_seq, abpt, read_id_map, &par_anchors, par_c); if (abpt->incr_fn) { // TODO collect anchors between last one path and first seq // anchors // new_par_anchors // push anchors // free(par_anchors.a); // par_anchors = new_par_anchors; // collect tpos_to_node_id for last one path // set tpos_to_node_id // } abpoa_anchor_poa(ab, abpt, seqs, weights, seq_lens, par_anchors, par_c, tpos_to_node_id, qpos_to_node_id, read_id_map, exist_n_seq, n_seq); free(read_id_map); free(tpos_to_node_id); free(qpos_to_node_id); free(par_c); if (par_anchors.m > 0) free(par_anchors.a); } // output abpoa_output(ab, abpt, out_fp); kseq_destroy(ks); gzclose(readfp); for (i = 0; i < n_seq; ++i) { free(seqs[i]); free(weights[i]); } free(seqs); free(weights); free(seq_lens); return 0; } abPOA-1.4.1/src/abpoa_align.h000066400000000000000000000116601425041320700156350ustar00rootroot00000000000000#ifndef ABPOA_ALIGN_H #define ABPOA_ALIGN_H #include "abpoa.h" #include "abpoa_graph.h" #define CHUNK_READ_N 1024 #define ABPOA_MATCH 2 #define ABPOA_MISMATCH 4 #define ABPOA_GAP_OPEN1 4 #define ABPOA_GAP_OPEN2 24 #define ABPOA_GAP_EXT1 2 #define ABPOA_GAP_EXT2 1 #define ABPOA_MMK 19 #define ABPOA_MMW 10 #define ABPOA_MIN_POA_WIN 500 #define ABPOA_M_OP 0x1 #define ABPOA_E1_OP 0x2 #define ABPOA_E2_OP 0x4 #define ABPOA_E_OP 0x6 #define ABPOA_F1_OP 0x8 #define ABPOA_F2_OP 0x10 #define ABPOA_F_OP 0x18 #define ABPOA_ALL_OP 0x1f #define MULTIP_MIN_FREQ 0.25 // start and end of each band: // range: (min_of_two(max_left, qlen-remain), max_of_two(max_right, qlen-remain)) // with extra band width: (range_min-w, range_max+w) #define GET_AD_DP_BEGIN(graph, w, id, end_id, qlen) MAX_OF_TWO(0, MIN_OF_TWO(abpoa_graph_node_id_to_max_pos_left(graph, id), qlen-(abpoa_graph_node_id_to_max_remain(graph,id)-abpoa_graph_node_id_to_max_remain(graph,end_id)-1)) - w) #define GET_AD_DP_END(graph, w, id, end_id, qlen) MIN_OF_TWO(qlen, MAX_OF_TWO(abpoa_graph_node_id_to_max_pos_right(graph, id), qlen-(abpoa_graph_node_id_to_max_remain(graph,id)-abpoa_graph_node_id_to_max_remain(graph,end_id)-1)) + w) #ifdef __cplusplus extern "C" { #endif static inline void abpoa_res_copy(abpoa_res_t *dest, abpoa_res_t *src) { int i; if (dest->n_cigar) free(dest->graph_cigar); dest->n_cigar = src->n_cigar; dest->graph_cigar = (abpoa_cigar_t*)_err_malloc(src->n_cigar * sizeof(abpoa_cigar_t)); for (i = 0; i < src->n_cigar; ++i) dest->graph_cigar[i] = src->graph_cigar[i]; dest->node_s = src->node_s, dest->node_e = src->node_e; dest->query_s = src->query_s, dest->query_e = src->query_e; dest->n_aln_bases = src->n_aln_bases, dest->n_matched_bases = src->n_matched_bases; dest->best_score = src->best_score; // dest->is_rc = src->is_rc; } static inline abpoa_cigar_t *abpoa_push_cigar(int *n_cigar, int *m_cigar, abpoa_cigar_t *cigar, int op, int len, int32_t node_id, int32_t query_id) { abpoa_cigar_t l = len; if (*n_cigar == 0 || (op != ABPOA_CINS && op != ABPOA_CSOFT_CLIP && op != ABPOA_CHARD_CLIP) || op != (cigar[(*n_cigar)-1] & 0xf)) { if (*n_cigar == *m_cigar) { *m_cigar = *m_cigar? (*m_cigar)<<1 : 4; cigar = (abpoa_cigar_t*)_err_realloc(cigar, (*m_cigar) * sizeof(abpoa_cigar_t)); } abpoa_cigar_t n_id = node_id, q_id = query_id; if (op == ABPOA_CMATCH || op == ABPOA_CDIFF) cigar[(*n_cigar)++] = n_id << 34 | q_id << 4 | op; else if (op == ABPOA_CINS || op == ABPOA_CSOFT_CLIP || op == ABPOA_CHARD_CLIP) cigar[(*n_cigar)++] = q_id << 34 | l << 4 | op; else if (op == ABPOA_CDEL) cigar[(*n_cigar)++] = n_id << 34 | l << 4 | op; else err_fatal(__func__, "Unknown cigar operation: %s\n", op); } else cigar[(*n_cigar)-1] += l << 4; return cigar; } static inline int abpoa_push_whole_cigar(int *dest_n_cigar, int *dest_m_cigar, abpoa_cigar_t **dest_cigar, int src_n_cigar, abpoa_cigar_t *src_cigar) { int i, dest_n_c = *dest_n_cigar; *dest_n_cigar += src_n_cigar; if (*dest_n_cigar > *dest_m_cigar) { *dest_m_cigar = MAX_OF_TWO((*dest_m_cigar) << 1, *dest_n_cigar); *dest_cigar = (abpoa_cigar_t*)_err_realloc(*dest_cigar, *dest_m_cigar * sizeof(abpoa_cigar_t)); } for (i = 0; i < src_n_cigar; ++i) { (*dest_cigar)[dest_n_c+i] = src_cigar[i]; } return 0; } static inline abpoa_cigar_t *abpoa_reverse_cigar(int n_cigar, abpoa_cigar_t *cigar) { int i; abpoa_cigar_t tmp; for (i = 0; i < n_cigar >> 1; ++i) { tmp = cigar[i]; cigar[i] = cigar[n_cigar-1-i]; cigar[n_cigar-1-i] = tmp; } return cigar; } static inline void abpoa_print_cigar(int n_cigar, abpoa_cigar_t *cigar, abpoa_graph_t *graph) { int i, node_id, query_id, index_i; int op, len; int n[6] = {0, 0, 0, 0, 0, 0}; for (i = 0; i < n_cigar; ++i) { op = cigar[i] & 0xf; node_id = (int)(cigar[i] >> 34); len = query_id = (int)(cigar[i] >> 4) & 0x3fffffff; if (op == ABPOA_CMATCH || op == ABPOA_CDIFF) { index_i = abpoa_graph_node_id_to_index(graph, node_id); printf("1%c:%d,%d\t", ABPOA_CIGAR_STR[op], index_i, query_id); n[op] += 1; } else if (op == ABPOA_CDEL) { index_i = abpoa_graph_node_id_to_index(graph, node_id); printf("%d%c:%d\t", len, ABPOA_CIGAR_STR[op], index_i); n[op] += len; } else if (op == ABPOA_CINS || op == ABPOA_CSOFT_CLIP || op == ABPOA_CHARD_CLIP) { query_id = node_id; printf("%d%c:%d\t", len, ABPOA_CIGAR_STR[op], query_id); n[op] += len; } else { err_fatal(__func__, "Unknown cigar operation: %s\n", op); } } printf("\n"); for (i = 0; i < 6; ++i) printf("%d%c ", n[i], ABPOA_CIGAR_STR[i]); printf("\n"); } #ifdef __cplusplus } #endif #endif abPOA-1.4.1/src/abpoa_graph.c000066400000000000000000000763531425041320700156510ustar00rootroot00000000000000#include #include #include #include "abpoa_align.h" #include "abpoa_seq.h" #include "simd_abpoa_align.h" #include "kdq.h" KDQ_INIT(int) #define kdq_int_t kdq_t(int) abpoa_node_t *abpoa_init_node(int n) { abpoa_node_t *node = (abpoa_node_t*)_err_calloc(n, sizeof(abpoa_node_t)); return node; } void abpoa_set_graph_node(abpoa_graph_t *abg, int node_i) { abg->node[node_i].node_id = node_i; abg->node[node_i].in_edge_n = 0; abg->node[node_i].in_edge_m = 0; abg->node[node_i].out_edge_n = 0; abg->node[node_i].out_edge_m = 0; abg->node[node_i].aligned_node_n = 0; abg->node[node_i].aligned_node_m = 0; abg->node[node_i].n_read = 0; abg->node[node_i].m_read = 0; abg->node[node_i].read_weight = NULL; abg->node[node_i].read_ids_n = 0; } void abpoa_free_node(abpoa_node_t *node, int n) { int i, j; for (i = 0; i < n; ++i) { if (node[i].in_edge_m > 0) free(node[i].in_id); if (node[i].out_edge_m > 0) { free(node[i].out_id); free(node[i].out_weight); if (node[i].read_ids_n > 0) { for (j = 0; j < node[i].out_edge_m; ++j) { free(node[i].read_ids[j]); } free(node[i].read_ids); } } if (node[i].m_read > 0) free(node[i].read_weight); if (node[i].aligned_node_m > 0) free(node[i].aligned_node_id); } free(node); } // 0: in_edge, 1: out_edge abpoa_graph_t *abpoa_realloc_graph_edge(abpoa_graph_t *abg, int io, int id, int use_read_ids) { if (io == 0) { _uni_realloc(abg->node[id].in_id, abg->node[id].in_edge_n, abg->node[id].in_edge_m, int); } else { int edge_m = abg->node[id].out_edge_m; if (edge_m <= 0) { abg->node[id].out_edge_m = MAX_OF_TWO(abg->node[id].out_edge_n, 1); abg->node[id].out_id = (int*)_err_malloc(abg->node[id].out_edge_m * sizeof(int)); abg->node[id].out_weight = (int*)_err_malloc(abg->node[id].out_edge_m * sizeof(int)); if (use_read_ids || abg->node[id].read_ids_n > 0) { abg->node[id].read_ids = (uint64_t**)_err_malloc(abg->node[id].out_edge_m * sizeof(uint64_t*)); if (abg->node[id].read_ids_n > 0) { int i; for (i = 0; i < abg->node[id].out_edge_m; ++i) { abg->node[id].read_ids[i] = (uint64_t*)_err_calloc(abg->node[id].read_ids_n, sizeof(uint64_t)); } } } } else if (abg->node[id].out_edge_n >= edge_m) { abg->node[id].out_edge_m = abg->node[id].out_edge_n+1; kroundup32(abg->node[id].out_edge_m); abg->node[id].out_id = (int*)_err_realloc(abg->node[id].out_id, abg->node[id].out_edge_m * sizeof(int)); abg->node[id].out_weight = (int*)_err_realloc(abg->node[id].out_weight, abg->node[id].out_edge_m * sizeof(int)); if (use_read_ids || abg->node[id].read_ids_n > 0) { abg->node[id].read_ids = (uint64_t**)_err_realloc(abg->node[id].read_ids, abg->node[id].out_edge_m * sizeof(uint64_t*)); if (abg->node[id].read_ids_n > 0) { int i; for (i = edge_m; i < abg->node[id].out_edge_m; ++i) { abg->node[id].read_ids[i] = (uint64_t*)_err_calloc(abg->node[id].read_ids_n, sizeof(uint64_t)); } } } } } return abg; } abpoa_graph_t *abpoa_realloc_graph_node(abpoa_graph_t *abg) { if (abg->node_m <= 0) { abg->node_m = 1; abg->node = (abpoa_node_t*)_err_calloc(1, sizeof(abpoa_node_t)); } if (abg->node_n == abg->node_m) { int i; abg->node_m <<= 1; abg->node = (abpoa_node_t*)_err_realloc(abg->node, abg->node_m * sizeof(abpoa_node_t)); for (i = abg->node_m >> 1; i < abg->node_m; ++i) { abpoa_set_graph_node(abg, i); } } return abg; } abpoa_graph_t *abpoa_init_graph(void) { abpoa_graph_t *abg = (abpoa_graph_t*)_err_malloc(sizeof(abpoa_graph_t)); abg->node_n = 2, abg->node_m = 2, abg->index_rank_m = 0; abg->node = abpoa_init_node(2); abg->node[0].node_id = 0; abg->node[1].node_id = 1; abg->node[0].read_ids_n = 0; abg->node[1].read_ids_n = 0; abg->is_topological_sorted = abg->is_called_cons = 0; abg->node_id_to_index = NULL; abg->index_to_node_id = NULL; abg->node_id_to_msa_rank = NULL; abg->node_id_to_max_pos_left = NULL; abg->node_id_to_max_pos_right = NULL; abg->node_id_to_max_remain = NULL; return abg; } void abpoa_free_graph(abpoa_graph_t *abg) { if (abg->node_m > 0) abpoa_free_node(abg->node, abg->node_m); if (abg->node_n > 0) { free(abg->index_to_node_id); free(abg->node_id_to_index); if (abg->node_id_to_msa_rank) free(abg->node_id_to_msa_rank); if (abg->node_id_to_max_pos_left) free(abg->node_id_to_max_pos_left); if (abg->node_id_to_max_pos_right) free(abg->node_id_to_max_pos_right); if (abg->node_id_to_max_remain) free(abg->node_id_to_max_remain); } free(abg); } abpoa_cons_t *abpoa_init_cons(void) { abpoa_cons_t *abc = (abpoa_cons_t*)_err_malloc(sizeof(abpoa_cons_t)); abc->n_cons = 0; abc->msa_len = 0; abc->clu_n_seq = NULL; abc->cons_len = NULL; abc->cons_node_ids = NULL; abc->cons_base = NULL; abc->msa_base = NULL; abc->cons_cov = NULL; abc->clu_read_ids = NULL; abc->cons_phred_score = NULL; return abc; } void abpoa_free_cons(abpoa_cons_t *abc) { int i; if (abc->n_cons > 0) { if (abc->clu_n_seq != NULL) free(abc->clu_n_seq); if (abc->cons_len != NULL) free(abc->cons_len); if (abc->cons_node_ids != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_node_ids[i]); free(abc->cons_node_ids); } if (abc->cons_base != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_base[i]); free(abc->cons_base); } if (abc->cons_cov != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_cov[i]); free(abc->cons_cov); } if (abc->clu_read_ids != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->clu_read_ids[i]); free(abc->clu_read_ids); } if (abc->cons_phred_score != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_phred_score[i]); free(abc->cons_phred_score); } } if (abc->msa_len > 0) { if (abc->msa_base != NULL) { for (i = 0; i < abc->n_seq+abc->n_cons; ++i) free(abc->msa_base[i]); free(abc->msa_base); } } free(abc); } abpoa_t *abpoa_init(void) { abpoa_t *ab = (abpoa_t*)_err_malloc(sizeof(abpoa_t)); ab->abg = abpoa_init_graph(); ab->abs = abpoa_init_seq(); ab->abm = abpoa_init_simd_matrix(); ab->abc = abpoa_init_cons(); return ab; } void abpoa_free(abpoa_t *ab) { abpoa_free_graph(ab->abg); abpoa_free_seq(ab->abs); abpoa_free_simd_matrix(ab->abm); abpoa_free_cons(ab->abc); free(ab); } void abpoa_BFS_set_node_index(abpoa_graph_t *abg, int src_id, int sink_id) { int *id, cur_id, out_id, aligned_id; int index = 0, q_size, new_q_size; int *in_degree = (int*)_err_malloc(abg->node_n * sizeof(int)); int i, j; for (i = 0; i < abg->node_n; ++i) in_degree[i] = abg->node[i].in_edge_n; kdq_int_t *q = kdq_init_int(); // Breadth-First-Search kdq_push_int(q, src_id); q_size = 1; new_q_size = 0; // node[q.id].in_degree equals 0 while (q_size > 0) { if ((id = kdq_shift_int(q)) == 0) err_fatal_simple("Error in queue."); cur_id = *id; abg->index_to_node_id[index] = cur_id; abg->node_id_to_index[cur_id] = index++; if (cur_id == sink_id) { kdq_destroy_int(q); free(in_degree); return; } for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; if (--in_degree[out_id] == 0) { for (j = 0; j < abg->node[out_id].aligned_node_n; ++j) { aligned_id = abg->node[out_id].aligned_node_id[j]; if (in_degree[aligned_id] != 0) goto next_out_node; } kdq_push_int(q, out_id); ++new_q_size; for (j = 0; j < abg->node[out_id].aligned_node_n; ++j) { aligned_id = abg->node[out_id].aligned_node_id[j]; kdq_push_int(q, aligned_id); ++new_q_size; } } next_out_node:; } if (--q_size == 0) { q_size = new_q_size; new_q_size = 0; } } err_fatal_simple("Failed to set node index."); } void abpoa_BFS_set_node_remain(abpoa_graph_t *abg, int src_id, int sink_id) { int *id, cur_id, i, out_id, in_id; int *out_degree = (int*)_err_malloc(abg->node_n * sizeof(int)); for (i = 0; i < abg->node_n; ++i) { out_degree[i] = abg->node[i].out_edge_n; abg->node_id_to_max_remain[i] = 0; } kdq_int_t *q = kdq_init_int(); // Breadth-First-Search kdq_push_int(q, sink_id); // node[q.id].in_degree equals 0 abg->node_id_to_max_remain[sink_id] = -1; // XXX not 0 while ((id = kdq_shift_int(q)) != 0) { cur_id = *id; // all out_id of cur_id have beed visited // max weight out_id if (cur_id != sink_id) { int max_w=-1, max_id=-1; for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; if (abg->node[cur_id].out_weight[i] > max_w) { max_w = abg->node[cur_id].out_weight[i]; max_id = out_id; } } abg->node_id_to_max_remain[cur_id] = abg->node_id_to_max_remain[max_id] + 1; // fprintf(stderr, "%d -> %d\n", abg->node_id_to_index[cur_id], abg->node_id_to_max_remain[cur_id]); } if (cur_id == src_id) { kdq_destroy_int(q); free(out_degree); return; } for (i = 0; i < abg->node[cur_id].in_edge_n; ++i) { in_id = abg->node[cur_id].in_id[i]; if (--out_degree[in_id] == 0) kdq_push_int(q, in_id); } } err_fatal_simple("Failed to set node remain."); } // 1. index_to_node_id // 2. node_id_to_index // 3. node_id_to_rank void abpoa_topological_sort(abpoa_graph_t *abg, abpoa_para_t *abpt) { if (abg->node_n <= 0) { err_func_format_printf(__func__, "Empty graph.\n"); return; } int node_n = abg->node_n; if (node_n > abg->index_rank_m) { abg->index_rank_m = node_n; kroundup32(abg->index_rank_m); // fprintf(stderr, "node_n: %d, index_rank_m: %d\n", node_n, abg->index_rank_m); abg->index_to_node_id = (int*)_err_realloc(abg->index_to_node_id, abg->index_rank_m * sizeof(int)); abg->node_id_to_index = (int*)_err_realloc(abg->node_id_to_index, abg->index_rank_m * sizeof(int)); if (abpt->out_msa || abpt->max_n_cons > 1) abg->node_id_to_msa_rank = (int*)_err_realloc(abg->node_id_to_msa_rank, abg->index_rank_m * sizeof(int)); if (abpt->wb >= 0) { abg->node_id_to_max_pos_left = (int*)_err_realloc(abg->node_id_to_max_pos_left, abg->index_rank_m * sizeof(int)); abg->node_id_to_max_pos_right = (int*)_err_realloc(abg->node_id_to_max_pos_right, abg->index_rank_m * sizeof(int)); abg->node_id_to_max_remain = (int*)_err_realloc(abg->node_id_to_max_remain, abg->index_rank_m * sizeof(int)); } else if (abpt->zdrop > 0) { abg->node_id_to_max_remain = (int*)_err_realloc(abg->node_id_to_max_remain, abg->index_rank_m * sizeof(int)); } } // start from ABPOA_SRC_NODE_ID to ABPOA_SINK_NODE_ID abpoa_BFS_set_node_index(abg, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID); // init min/max rank if (abpt->wb >= 0) { int i; for (i = 0; i < node_n; ++i) { abg->node_id_to_max_pos_right[i] = 0; abg->node_id_to_max_pos_left[i] = node_n; } abpoa_BFS_set_node_remain(abg, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID); } else if (abpt->zdrop > 0) abpoa_BFS_set_node_remain(abg, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID); abg->is_topological_sorted = 1; } void abpoa_DFS_set_msa_rank(abpoa_graph_t *abg, int src_id, int sink_id, int *in_degree) { // fprintf(stderr, "node_n: %d, m: %d\n", abg->node_n, abg->index_rank_m); if (abg->node_n > abg->index_rank_m) { int m = abg->node_n; kroundup32(m); abg->node_id_to_msa_rank = (int*)_err_realloc(abg->node_id_to_msa_rank, m * sizeof(int)); } int *id, cur_id, i, j, out_id, aligned_id; int msa_rank = 0; kdq_int_t *q = kdq_init_int(); // Depth-First-Search kdq_push_int(q, src_id); // node[q.id].in_degree equals 0 abg->node_id_to_msa_rank[src_id] = -1; // printf("tot_node_n: %d, node_m: %d\n", abg->node_n, abg->node_m); while((id = kdq_pop_int(q)) != 0) { cur_id = *id; if (abg->node_id_to_msa_rank[cur_id] < 0) { abg->node_id_to_msa_rank[cur_id] = msa_rank; for (i = 0; i < abg->node[cur_id].aligned_node_n; ++i) { aligned_id = abg->node[cur_id].aligned_node_id[i]; abg->node_id_to_msa_rank[aligned_id] = msa_rank; } msa_rank++; } if (cur_id == sink_id) { kdq_destroy_int(q); abg->is_set_msa_rank = 1; return; } for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; if (--in_degree[out_id] == 0) { for (j = 0; j < abg->node[out_id].aligned_node_n; ++j) { aligned_id = abg->node[out_id].aligned_node_id[j]; if (in_degree[aligned_id] != 0) goto next_out_node; } kdq_push_int(q, out_id); abg->node_id_to_msa_rank[out_id] = -1; for (j = 0; j < abg->node[out_id].aligned_node_n; ++j) { aligned_id = abg->node[out_id].aligned_node_id[j]; kdq_push_int(q, aligned_id); // printf("aln_id: %d\n", aligned_id); abg->node_id_to_msa_rank[aligned_id] = -1; } } next_out_node:; } } err_fatal_simple("Error in set_msa_rank.\n"); } void abpoa_set_msa_rank(abpoa_graph_t *abg, int src_id, int sink_id) { if (abg->is_set_msa_rank == 0) { int i, *in_degree = (int*)_err_malloc(abg->node_n * sizeof(int)); for (i = 0; i < abg->node_n; ++i) in_degree[i] = abg->node[i].in_edge_n; abpoa_DFS_set_msa_rank(abg, src_id, sink_id, in_degree); free(in_degree); } } int abpoa_get_aligned_id(abpoa_graph_t *abg, int node_id, uint8_t base) { int i, aln_id; abpoa_node_t *node = abg->node; for (i = 0; i < node[node_id].aligned_node_n; ++i) { aln_id = node[node_id].aligned_node_id[i]; if (node[aln_id].base == base) return aln_id; } return -1; } void abpoa_add_graph_aligned_node1(abpoa_node_t *node, int aligned_id) { _uni_realloc(node->aligned_node_id, node->aligned_node_n, node->aligned_node_m, int); node->aligned_node_id[node->aligned_node_n++] = aligned_id; } void abpoa_add_graph_aligned_node(abpoa_graph_t *abg, int node_id, int aligned_id) { int i; abpoa_node_t *node = abg->node; for (i = 0; i < node[node_id].aligned_node_n; ++i) { abpoa_add_graph_aligned_node1(node + node[node_id].aligned_node_id[i], aligned_id); abpoa_add_graph_aligned_node1(node + aligned_id, node[node_id].aligned_node_id[i]); } abpoa_add_graph_aligned_node1(abg->node + node_id, aligned_id); abpoa_add_graph_aligned_node1(abg->node + aligned_id, node_id); } void abpoa_set_read_id(uint64_t *read_ids, int read_id) { int n = read_id / 64; uint64_t one = 1; int b = read_id & 0x3f; read_ids[n] |= (one << b); } int abpoa_add_graph_node(abpoa_graph_t *abg, uint8_t base) { int node_id = abg->node_n; abpoa_realloc_graph_node(abg); // add node abg->node[node_id].base = base; ++abg->node_n; return node_id; } int abpoa_add_graph_edge(abpoa_graph_t *abg, int from_id, int to_id, int check_edge, int w, uint8_t add_read_id, uint8_t add_read_weight, int read_id, int read_ids_n, int tot_read_n) { int ret = 1; if (from_id < 0 || from_id >= abg->node_n || to_id < 0 || to_id >= abg->node_n) err_fatal(__func__, "node_n: %d\tfrom_id: %d\tto_id: %d.", abg->node_n, from_id, to_id); // fprintf(stderr, "weigth: %d\n", w); int out_edge_n = abg->node[from_id].out_edge_n; int edge_exist = 0; int out_edge_i = -1; if (check_edge) { int i; for (i = 0; i < out_edge_n; ++i) { if (abg->node[from_id].out_id[i] == to_id) { // edge exists abg->node[from_id].out_weight[i] += w; // update weight on existing edge // update label id edge_exist = 1; out_edge_i = i; break; } } } // add edge if (edge_exist == 0) { /// in edge abpoa_realloc_graph_edge(abg, 0, to_id, 0); abg->node[to_id].in_id[abg->node[to_id].in_edge_n] = from_id; ++abg->node[to_id].in_edge_n; /// out edge abpoa_realloc_graph_edge(abg, 1, from_id, add_read_id); abg->node[from_id].out_id[out_edge_n] = to_id; abg->node[from_id].out_weight[out_edge_n] = w; // initial weight for new edge out_edge_i = out_edge_n; ++abg->node[from_id].out_edge_n; } // add read_id to out edge if (add_read_id) { if (out_edge_i < 0) err_fatal_simple("No edge found."); if (read_ids_n <= 0) err_fatal(__func__, "Unexpected read_ids_n: %d.", read_ids_n); int i, j; abpoa_node_t *from_node = abg->node + from_id; if (from_node->read_ids_n == 0) { for (i = 0; i < from_node->out_edge_m; ++i) { from_node->read_ids[i] = (uint64_t*)_err_calloc(read_ids_n, sizeof(uint64_t)); } from_node->read_ids_n = read_ids_n; } else if (from_node->read_ids_n < read_ids_n) { // reallocate from_node->read_ids for (i = 0; i < from_node->out_edge_m; ++i) { from_node->read_ids[i] = (uint64_t*)_err_realloc(from_node->read_ids[i], read_ids_n * sizeof(uint64_t)); for (j = from_node->read_ids_n; j < read_ids_n; ++j) from_node->read_ids[i][j] = 0; } from_node->read_ids_n = read_ids_n; } abpoa_set_read_id(from_node->read_ids[out_edge_i], read_id); } abg->node[from_id].n_read += 1; if (add_read_weight) { if (tot_read_n > abg->node[from_id].m_read) { abg->node[from_id].read_weight = (int*)_err_realloc(abg->node[from_id].read_weight, tot_read_n * sizeof(int)); int i; for (i = abg->node[from_id].m_read; i < tot_read_n; ++i) abg->node[from_id].read_weight[i] = 0; abg->node[from_id].m_read = tot_read_n; } abg->node[from_id].read_weight[read_id] = w; } return ret; } void abpoa_add_graph_sequence(abpoa_graph_t *abg, uint8_t *seq, int *weight, int seq_l, int *qpos_to_node_id, int start, int end, uint8_t add_read_id, uint8_t add_read_weight, int read_id, int read_ids_n, int tot_read_n) { if (start >= seq_l || end <= start) err_fatal(__func__, "seq_l: %d\tstart: %d\tend: %d.", seq_l, start, end); if (end > seq_l) end = seq_l; int i, last_node_id, cur_node_id; last_node_id = ABPOA_SRC_NODE_ID; for (i = start; i < end; ++i) { cur_node_id = abpoa_add_graph_node(abg, seq[i]); if (qpos_to_node_id) qpos_to_node_id[i] = cur_node_id; abpoa_add_graph_edge(abg, last_node_id, cur_node_id, 0, weight[i], add_read_id, add_read_weight, read_id, read_ids_n, tot_read_n); last_node_id = cur_node_id; } abpoa_add_graph_edge(abg, last_node_id, ABPOA_SINK_NODE_ID, 0, weight[seq_l-1], add_read_id, add_read_weight, read_id, read_ids_n, tot_read_n); abg->is_called_cons = abg->is_set_msa_rank = abg->is_topological_sorted = 0; // abpoa_topological_sort(abg, abpt); } int is_full_upstream_subgraph(abpoa_graph_t *abg, int up_index, int down_index) { int i, j, id, in_id; for (i = up_index+1; i <= down_index; ++i) { id = abg->index_to_node_id[i]; for (j = 0; j < abg->node[id].in_edge_n; ++j) { in_id = abg->node[id].in_id[j]; if (abg->node_id_to_index[in_id] < up_index) return 0; } } return 1; } int abpoa_upstream_index(abpoa_graph_t *abg, int beg_index, int end_index) { int min_index, in_index, i, j, node_id, in_id; while (1) { min_index = beg_index; for (i = beg_index; i <= end_index; ++i) { node_id = abg->index_to_node_id[i]; for (j = 0; j < abg->node[node_id].in_edge_n; ++j) { in_id = abg->node[node_id].in_id[j]; in_index = abg->node_id_to_index[in_id]; min_index = MIN_OF_TWO(min_index, in_index); } } if (is_full_upstream_subgraph(abg, min_index, beg_index)) { return min_index; } else { end_index = beg_index; beg_index = min_index; } } } int is_full_downstream_subgraph(abpoa_graph_t *abg, int up_index, int down_index) { int i, j, id, out_id; for (i = up_index; i < down_index; ++i) { id = abg->index_to_node_id[i]; for (j = 0; j < abg->node[id].out_edge_n; ++j) { out_id = abg->node[id].out_id[j]; if (abg->node_id_to_index[out_id] > down_index) return 0; } } return 1; } int abpoa_downstream_index(abpoa_graph_t *abg, int beg_index, int end_index) { int max_index, out_index, i, j, node_id, out_id; while (1) { max_index = end_index; for (i = beg_index; i <= end_index; ++i) { node_id = abg->index_to_node_id[i]; for (j = 0; j < abg->node[node_id].out_edge_n; ++j) { out_id = abg->node[node_id].out_id[j]; out_index = abg->node_id_to_index[out_id]; max_index = MAX_OF_TWO(max_index, out_index); } } if (is_full_upstream_subgraph(abg, end_index, max_index)) { return max_index; } else { beg_index = end_index; end_index = max_index; } } } // exc_beg | inc_beg ... inc_end | exc_end void abpoa_subgraph_nodes(abpoa_t *ab, abpoa_para_t *abpt, int exc_beg0, int exc_end0, int *exc_beg, int *exc_end) { abpoa_graph_t *abg = ab->abg; if (ab->abg->is_topological_sorted == 0) abpoa_topological_sort(abg, abpt); int inc_beg_index = abg->node_id_to_index[exc_beg0], inc_end_index = abg->node_id_to_index[exc_end0]; int exc_beg_index = abpoa_upstream_index(abg, inc_beg_index, inc_end_index); int exc_end_index = abpoa_downstream_index(abg, inc_beg_index, inc_end_index); if (exc_beg_index < 0 || exc_end_index >= abg->node_n) err_fatal_simple("Error in subgraph_nodes"); *exc_beg = abg->index_to_node_id[exc_beg_index]; *exc_end = abg->index_to_node_id[exc_end_index]; } // fusion stratergy : // 1. Match: merge to one node // 2. Mismatch: check if B is identical to A' aligned nodes, then merge to node; if not, add node // 3. Insertion: add node // 4. Deletion: nothing // 5. Clipping: add node // 6. For all first/last node, link to virtual start/end node // inc_both_ends: set as 1 to add weight for edge between beg_node_id/end_node_id and internal node int abpoa_add_subgraph_alignment(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *seq, int *_weight, int seq_l, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends) { abpoa_graph_t *abg = ab->abg; int n_cigar = res.n_cigar; abpoa_cigar_t *abpoa_cigar = res.graph_cigar; int read_ids_n = 1 + ((tot_read_n-1) >> 6); uint8_t add_read_id = abpt->use_read_ids, add_read_weight = abpt->use_qv & (abpt->max_n_cons>1), add; int i, *weight; if (_weight == NULL) { weight = (int*)_err_malloc(seq_l * sizeof(int)); for (i = 0; i < seq_l; ++i) weight[i] = 1; } else weight = _weight; if (abg->node_n == 2) { // empty graph abpoa_add_graph_sequence(abg, seq, weight, seq_l, qpos_to_node_id, 0, seq_l, add_read_id, add_read_weight, read_id, read_ids_n, tot_read_n); if (_weight == NULL) free(weight); return 0; } else { if (abg->node_n < 2) { err_fatal(__func__, "Graph node: %d.", abg->node_n); } else if (n_cigar == 0) { if (_weight == NULL) free(weight); return 0; //err_fatal(__func__, "Empty graph cigar."); } } // normal graph, normal graph_cigar int j; int op, len; int node_id, query_id=-1, last_new = 0, last_id = beg_node_id, new_id, aligned_id; for (i = 0; i < n_cigar; ++i) { op = abpoa_cigar[i] & 0xf; if (op == ABPOA_CMATCH) { node_id = (abpoa_cigar[i] >> 34) & 0x3fffffff; query_id++; // = (abpoa_cigar[i] >> 4) & 0x3fffffff; if (abg->node[node_id].base != seq[query_id]) { // mismatch // check if query base is identical to node_id's aligned node if ((aligned_id = abpoa_get_aligned_id(abg, node_id, seq[query_id])) != -1) { if (last_id != beg_node_id || inc_both_ends) add = 1; else add = 0; abpoa_add_graph_edge(abg, last_id, aligned_id, 1-last_new, weight[query_id], add_read_id&add, add_read_weight, read_id, read_ids_n, tot_read_n); last_id = aligned_id; last_new = 0; } else { new_id = abpoa_add_graph_node(abg, seq[query_id]); if (last_id != beg_node_id || inc_both_ends) add = 1; else add = 0; abpoa_add_graph_edge(abg, last_id, new_id, 0, weight[query_id], add_read_id&add, add_read_weight, read_id, read_ids_n, tot_read_n); last_id = new_id; last_new = 1; // add new_id to node_id's aligned node abpoa_add_graph_aligned_node(abg, node_id, new_id); } } else { // match if (last_id != beg_node_id || inc_both_ends) add = 1; else add = 0; abpoa_add_graph_edge(abg, last_id, node_id, 1-last_new, weight[query_id], add_read_id&add, add_read_weight, read_id, read_ids_n, tot_read_n); last_id = node_id; last_new = 0; } if (qpos_to_node_id) qpos_to_node_id[query_id] = last_id; } else if (op == ABPOA_CINS || op == ABPOA_CSOFT_CLIP || op == ABPOA_CHARD_CLIP) { len = (abpoa_cigar[i] >> 4) & 0x3fffffff; query_id+=len; // = (abpoa_cigar[i] >> 34) & 0x3fffffff; for (j = len-1; j >= 0; --j) { // XXX use dynamic id, instead of static query_id new_id = abpoa_add_graph_node(abg, seq[query_id-j]); if (last_id != beg_node_id || inc_both_ends) add = 1; else add = 0; abpoa_add_graph_edge(abg, last_id, new_id, 0, weight[query_id-j], add_read_id&add, add_read_weight, read_id, read_ids_n, tot_read_n); last_id = new_id; last_new = 1; if (qpos_to_node_id) qpos_to_node_id[query_id-j] = last_id; } } else if (op == ABPOA_CDEL) { // nothing; continue; } } // if (inc_both_ends) add = 1; else add = 0; XXX end_node_id is always excluded when adding weight // abpoa_add_graph_edge(abg, last_id, end_node_id, 1-last_new, w, add_read_id&add, read_id, read_ids_n); abpoa_add_graph_edge(abg, last_id, end_node_id, 1-last_new, weight[seq_l-1], add_read_id, add_read_weight, read_id, read_ids_n, tot_read_n); abg->is_called_cons = abg->is_topological_sorted = 0; // abpoa_topological_sort(abg, abpt); if (_weight == NULL) free(weight); return 0; } int abpoa_add_graph_alignment(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *seq, int *weight, int seq_l, int *qpos_to_node_id, abpoa_res_t res, int read_id, int tot_read_n, int inc_both_ends) { return abpoa_add_subgraph_alignment(ab, abpt, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID, seq, weight, seq_l, qpos_to_node_id, res, read_id, tot_read_n, inc_both_ends); } // reset allocated memery everytime init the graph // * node // * index_to_node_id/node_id_to_index/node_id_to_max_remain, max_pos_left/right void abpoa_reset(abpoa_t *ab, abpoa_para_t *abpt, int qlen) { abpoa_graph_t *abg = ab->abg; int i, j, k, node_m; abg->is_topological_sorted = abg->is_called_cons = 0; for (i = 0; i < abg->node_n; ++i) { for (j = 0; j < abg->node[i].out_edge_n; ++j) { for (k = 0; k < abg->node[i].read_ids_n; ++k) abg->node[i].read_ids[j][k] = 0; } abg->node[i].in_edge_n = abg->node[i].out_edge_n = abg->node[i].aligned_node_n = 0; abg->node[i].n_read = 0; } abg->node_n = 2; if (qlen+2 > abg->node_m) { node_m = qlen+2; kroundup32(node_m); abg->node = (abpoa_node_t*)_err_realloc(abg->node, node_m * sizeof(abpoa_node_t)); for (i = abg->node_m; i < node_m; ++i) abpoa_set_graph_node(abg, i); abg->node_m = abg->index_rank_m = node_m; abg->index_to_node_id = (int*)_err_realloc(abg->index_to_node_id, node_m * sizeof(int)); abg->node_id_to_index = (int*)_err_realloc(abg->node_id_to_index, node_m * sizeof(int)); if (abpt->out_msa || abpt->max_n_cons > 1) abg->node_id_to_msa_rank = (int*)_err_realloc(abg->node_id_to_msa_rank, node_m * sizeof(int)); if (abpt->wb >= 0) { abg->node_id_to_max_pos_left = (int*)_err_realloc(abg->node_id_to_max_pos_left, node_m * sizeof(int)); abg->node_id_to_max_pos_right = (int*)_err_realloc(abg->node_id_to_max_pos_right, node_m * sizeof(int)); abg->node_id_to_max_remain = (int*)_err_realloc(abg->node_id_to_max_remain, node_m * sizeof(int)); } else if (abpt->zdrop > 0) { abg->node_id_to_max_remain = (int*)_err_realloc(abg->node_id_to_max_remain, node_m * sizeof(int)); } } // fprintf(stderr, "qlen: %d, node_n: %d, node_m: %d\n", qlen, abg->node_n, abg->node_m); // reset abs ab->abs->n_seq = 0; // reset cons abpoa_cons_t *abc = ab->abc; if (abc->n_cons > 0) { if (abc->clu_n_seq != NULL) free(abc->clu_n_seq); if (abc->cons_len != NULL) free(abc->cons_len); if (abc->cons_node_ids != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_node_ids[i]); free(abc->cons_node_ids); } if (abc->cons_base != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_base[i]); free(abc->cons_base); } if (abc->cons_cov != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_cov[i]); free(abc->cons_cov); } if (abc->clu_read_ids != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->clu_read_ids[i]); free(abc->clu_read_ids); } if (abc->cons_phred_score != NULL) { for (i = 0; i < abc->n_cons; ++i) free(abc->cons_phred_score[i]); free(abc->cons_phred_score); } } if (abc->msa_len > 0) { if (abc->msa_base != NULL) { for (i = 0; i < abc->n_seq+abc->n_cons; ++i) free(abc->msa_base[i]); free(abc->msa_base); } } abc->n_seq = abc->n_cons = abc->msa_len = 0; } abPOA-1.4.1/src/abpoa_graph.h000066400000000000000000000042331425041320700156420ustar00rootroot00000000000000#ifndef ABPOA_GRAPH_H #define ABPOA_GRAPH_H #include #include "abpoa.h" #include "utils.h" //#define CIGAR_STR "MIDNSHP=XB" //#define ABPOA_GRAPH_CIGAR_STR "=XIDNSH" //#define ABPOA_GRAPH_CEQUAL 0 //#define ABPOA_GRAPH_CMISMATCH 1 //#define ABPOA_GRAPH_CINS 2 //#define ABPOA_GRAPH_CDEL 3 //#define ABPOA_GRAPH_CREF_SKIP 4 //#define ABPOA_GRAPH_CCLIP 5 #ifdef __cplusplus extern "C" { #endif int abpoa_get_aligned_id(abpoa_graph_t *abg, int node_id, uint8_t base); void abpoa_add_graph_aligned_node(abpoa_graph_t *abg, int node_id, int aligned_id); void abpoa_set_msa_rank(abpoa_graph_t *abg, int src_id, int sink_id); abpoa_graph_t *abpoa_init_graph(void); void abpoa_free_graph(abpoa_graph_t *graph); static inline int abpoa_graph_node_id_to_index(abpoa_graph_t *graph, int node_id) { if (node_id < 0 || node_id >= graph->node_n) err_fatal(__func__, "Wrong node id: %d\n", node_id); return graph->node_id_to_index[node_id]; } static inline int abpoa_graph_node_id_to_max_pos_right(abpoa_graph_t *graph, int node_id) { if (node_id < 0 || node_id >= graph->node_n) err_fatal(__func__, "Wrong node id: %d\n", node_id); return graph->node_id_to_max_pos_right[node_id]; } static inline int abpoa_graph_node_id_to_max_pos_left(abpoa_graph_t *graph, int node_id) { if (node_id < 0 || node_id >= graph->node_n) err_fatal(__func__, "Wrong node id: %d\n", node_id); return graph->node_id_to_max_pos_left[node_id]; } static inline int abpoa_graph_node_id_to_max_remain(abpoa_graph_t *graph, int node_id) { if (node_id < 0 || node_id >= graph->node_n) err_fatal(__func__, "Wrong node id: %d\n", node_id); return graph->node_id_to_max_remain[node_id]; } static inline int abpoa_graph_index_to_node_id(abpoa_graph_t *graph, int index_i) { if (index_i < 0 || index_i >= graph->node_n) err_fatal(__func__, "Wrong index: %d\n", index_i); return graph->index_to_node_id[index_i]; } static inline int abpoa_graph_node_id_to_msa_rank(abpoa_graph_t *graph, int node_id) { if (node_id < 0 || node_id >= graph->node_n) err_fatal(__func__, "Wrong node id: %d\n", node_id); return graph->node_id_to_msa_rank[node_id]; } #ifdef __cplusplus } #endif #endif abPOA-1.4.1/src/abpoa_output.c000066400000000000000000001111751425041320700161000ustar00rootroot00000000000000#include #include #include #include "abpoa.h" #include "abpoa_graph.h" #include "utils.h" #include "abpoa_seq.h" #include "kdq.h" extern char ab_char256_table[256]; char ab_LogTable65536[65536]; char ab_bit_table16[65536]; #define NAT_E 2.718281828459045 static const char ab_LogTable256[256] = { #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) }; static inline int ilog2_32(uint32_t v) { uint32_t t, tt; if ((tt = v>>16)) return (t = tt>>8) ? 24 + ab_LogTable256[t] : 16 + ab_LogTable256[tt]; return (t = v>>8) ? 8 + ab_LogTable256[t] : ab_LogTable256[v]; } void set_65536_table(void) { int i; for (i = 0; i < 65536; ++i) { ab_LogTable65536[i] = ilog2_32(i); } } void set_bit_table16(void) { int i; ab_bit_table16[0] = 0; for (i = 0; i != 65536; ++i) ab_bit_table16[i] = (i&1) + ab_bit_table16[i>>1]; } #define get_bit_cnt4(table, b) (table[(b)&0xffff] + table[(b)>>16&0xffff] + table[(b)>>32&0xffff] + table[(b)>>48&0xffff]) static inline int ilog2_64(uint64_t v) { uint64_t t, tt; if ((tt = v >> 32)) return (t = tt >> 16) ? 48 + ab_LogTable65536[t] : 32 + ab_LogTable65536[tt]; return (t = v>>16) ? 16 + ab_LogTable65536[t] : ab_LogTable65536[v]; } KDQ_INIT(int) #define kdq_int_t kdq_t(int) static inline int get_read_cnt(uint64_t *read_ids, int read_ids_n) { int i, c; for (i = c =0; i < read_ids_n; ++i) { c += get_bit_cnt4(ab_bit_table16, read_ids[i]); } return c; } abpoa_cons_t *abpoa_allocate_rc_msa(abpoa_cons_t *abc, int msa_len, int n_seq, int n_cons) { int i; abc->n_seq = n_seq; abc->msa_len = msa_len; abc->msa_base = (uint8_t**)_err_malloc((n_seq+n_cons) * sizeof(uint8_t*)); for (i = 0; i < n_seq+n_cons; ++i) { abc->msa_base[i] = (uint8_t*)_err_malloc(msa_len * sizeof(uint8_t)); } return abc; } void abpoa_output_rc_msa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) { if (out_fp == NULL) return; int i, j; abpoa_seq_t *abs = ab->abs; abpoa_cons_t *abc = ab->abc; if (abc->msa_len <= 0) return; for (i = 0; i < abs->n_seq; ++i) { if (abs->name[i].l > 0) { if (abs->is_rc[i]) fprintf(out_fp, ">%s_reverse_complement\n", abs->name[i].s); else fprintf(out_fp, ">%s\n", abs->name[i].s); } else { fprintf(out_fp, ">Seq_%d\n", i+1); } for (j = 0; j < abc->msa_len; ++j) fprintf(out_fp, "%c", ab_char256_table[abc->msa_base[i][j]]); fprintf(out_fp, "\n"); } if (abpt->out_cons) { // RC-MSA for consensus sequence int cons_i; for (cons_i = 0; cons_i < abc->n_cons; cons_i++) { fprintf(out_fp, ">Consensus_sequence"); if (abc->n_cons > 1) { fprintf(out_fp, "_%d ", cons_i+1); for (j = 0; j < abc->clu_n_seq[cons_i]; ++j) { // cluter read_id if (j != 0) fprintf(out_fp, ","); fprintf(out_fp, "%d", abc->clu_read_ids[cons_i][j]); } } fprintf(out_fp, "\n"); for (i = 0; i < abc->msa_len; ++i) fprintf(out_fp, "%c", ab_char256_table[abc->msa_base[abc->n_seq+cons_i][i]]); fprintf(out_fp, "\n"); } } } void abpoa_set_msa_seq(abpoa_node_t node, int rank, uint8_t **msa_base) { int i, j, b, read_id; uint8_t base = node.base; uint64_t num, tmp; b = 0; for (i = 0; i < node.read_ids_n; ++i) { for (j = 0; j < node.out_edge_n; ++j) { num = node.read_ids[j][i]; while (num) { tmp = num & -num; read_id = ilog2_64(tmp); msa_base[b+read_id][rank-1] = base; num ^= tmp; } } b += 64; } } // only generate rc-msa, output in separated func void abpoa_generate_rc_msa(abpoa_t *ab, abpoa_para_t *abpt) { abpoa_graph_t *abg = ab->abg; if (abg->node_n <= 2) return; abpoa_set_msa_rank(abg, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID); if (abpt->out_cons) abpoa_generate_consensus(ab, abpt); abpoa_seq_t *abs = ab->abs; abpoa_cons_t *abc = ab->abc; int i, j, aligned_id, n_seq = abs->n_seq; int msa_len = abg->node_id_to_msa_rank[ABPOA_SINK_NODE_ID]-1; abpoa_allocate_rc_msa(abc, msa_len, n_seq, abc->n_cons); for (i = 0; i < n_seq; ++i) { for (j = 0; j < abc->msa_len; ++j) abc->msa_base[i][j] = abpt->m; } int rank; // if (out_fp && abpt->out_msa_header == 0) fprintf(out_fp, ">Multiple_sequence_alignment\n"); for (i = 2; i < abg->node_n; ++i) { // get msa rank rank = abpoa_graph_node_id_to_msa_rank(abg, i); for (j = 0; j < abg->node[i].aligned_node_n; ++j) { aligned_id = abg->node[i].aligned_node_id[j]; rank = MAX_OF_TWO(rank, abpoa_graph_node_id_to_msa_rank(abg, aligned_id)); } // assign seq abpoa_set_msa_seq(abg->node[i], rank, abc->msa_base); } if (abpt->out_cons) { int cons_i, cur_id; for (cons_i = 0; cons_i < abc->n_cons; cons_i++) { for (i = 0; i < msa_len; ++i) abc->msa_base[n_seq+cons_i][i] = abpt->m; for (i = 0; i < abc->cons_len[cons_i]; ++i) { cur_id = abc->cons_node_ids[cons_i][i]; rank = abpoa_graph_node_id_to_msa_rank(abg, cur_id); for (j = 0; j < abg->node[cur_id].aligned_node_n; ++j) { aligned_id = abg->node[cur_id].aligned_node_id[j]; rank = MAX_OF_TWO(rank, abpoa_graph_node_id_to_msa_rank(abg, aligned_id)); } abc->msa_base[n_seq+cons_i][rank-1] = abc->cons_base[cons_i][i]; } } } } // generate & output gfa void abpoa_generate_gfa(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) { if (out_fp == NULL) return; abpoa_seq_t *abs = ab->abs; abpoa_graph_t *abg = ab->abg; if (abg->node_n <= 2) return; // traverse graph int *in_degree = (int*)_err_malloc(abg->node_n * sizeof(int)); int n_seq = abs->n_seq; int **read_paths = (int**)_err_malloc(n_seq * sizeof(int*)), *read_path_i = (int*)_err_calloc(n_seq, sizeof(int)); int i, j, cur_id, pre_id, out_id, *id; for (i = 0; i < abg->node_n; ++i) in_degree[i] = abg->node[i].in_edge_n; for (i = 0; i < n_seq; ++i) read_paths[i] = (int*)_err_malloc(abg->node_n * sizeof(int)); // output comment and header int nl = 0; for (i = 2; i < abg->node_n; ++i) nl += abg->node[i].in_edge_n; fprintf(out_fp, "H\tVN:Z:1.0\tNS:i:%d\tNL:i:%d\tNP:i:%d\n", abg->node_n-2, nl - abg->node[ABPOA_SRC_NODE_ID].out_edge_n, n_seq + abpt->out_cons); kdq_int_t *q = kdq_init_int(); // Breadth-First-Search kdq_push_int(q, ABPOA_SRC_NODE_ID); while ((id = kdq_shift_int(q)) != 0) { cur_id = *id; if (cur_id == ABPOA_SINK_NODE_ID) { kdq_destroy_int(q); break; } else { if (cur_id != ABPOA_SRC_NODE_ID) { // output node fprintf(out_fp, "S\t%d\t%c\n", cur_id-1, ab_char256_table[abg->node[cur_id].base]); // output all links based pre_ids for (i = 0; i < abg->node[cur_id].in_edge_n; ++i) { pre_id = abg->node[cur_id].in_id[i]; if (pre_id != ABPOA_SRC_NODE_ID) fprintf(out_fp, "L\t%d\t+\t%d\t+\t0M\n", pre_id-1, cur_id-1); } // add node id to read path int b, read_id; uint64_t num, tmp; b = 0; for (i = 0; i < abg->node[cur_id].read_ids_n; ++i) { for (j = 0; j < abg->node[cur_id].out_edge_n; ++j) { num = abg->node[cur_id].read_ids[j][i]; while (num) { tmp = num & -num; read_id = ilog2_64(tmp); read_paths[b+read_id][read_path_i[b+read_id]++] = cur_id-1; num ^= tmp; } } b += 64; } } for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; if (--in_degree[out_id] == 0) { kdq_push_int(q, out_id); } } } } // output read paths for (i = 0; i < n_seq; ++i) { if (abs->name[i].l > 0) fprintf(out_fp, "P\t%s\t", abs->name[i].s); else fprintf(out_fp, "P\t%d\t", i+1); if (abs->is_rc[i]) { for (j = read_path_i[i]-1; j >= 0; --j) { fprintf(out_fp, "%d-", read_paths[i][j]); if (j != 0) fprintf(out_fp, ","); else fprintf(out_fp, "\t*\n"); } } else { for (j = 0; j < read_path_i[i]; ++j) { fprintf(out_fp, "%d+", read_paths[i][j]); if (j != read_path_i[i]-1) fprintf(out_fp, ","); else fprintf(out_fp, "\t*\n"); } } } if (abpt->out_cons) { abpoa_generate_consensus(ab, abpt); abpoa_cons_t *abc = ab->abc; int cons_i; for (cons_i = 0; cons_i < abc->n_cons; ++cons_i) { fprintf(out_fp, "P\tConsensus_sequence"); if (abc->n_cons > 1) fprintf(out_fp, "_%d", cons_i+1); fprintf(out_fp, "\t"); for (i = 0; i < abc->cons_len[cons_i]; ++i) { cur_id = abc->cons_node_ids[cons_i][i]; fprintf(out_fp, "%d+", cur_id-1); if (i != abc->cons_len[cons_i]-1) fprintf(out_fp, ","); else fprintf(out_fp, "\t*\n"); } } } free(in_degree); for (i = 0; i < n_seq; ++i) free(read_paths[i]); free(read_paths); free(read_path_i); } int abpoa_cons_phred_score(int n_cov, int n_seq) { if (n_cov > n_seq) err_fatal(__func__, "Error: unexpected n_cov/n_seq (%d/%d).", n_cov, n_seq); double x, p; x = 13.8 * (1.25 * n_cov / n_seq - 0.25); p = 1 - 1.0 / (1.0 + pow(NAT_E, -1 * x)); return (33 + (int)(-10 * log10(p) + 0.499)); } int get_read_ids_clu_count(uint64_t *cur_read_ids, int read_ids_n, uint64_t *clu_read_ids) { int n = 0, i; uint64_t b; for (i = 0; i < read_ids_n; ++i) { b = cur_read_ids[i] & clu_read_ids[i]; n += get_bit_cnt4(ab_bit_table16, b); } return n; } int get_read_ids_clu_weight(uint64_t *cur_read_ids, int read_ids_n, uint64_t *clu_read_ids, uint8_t use_qv, int *read_weight, int m_read) { if (use_qv == 0) return get_read_ids_clu_count(cur_read_ids, read_ids_n, clu_read_ids); int w = 0, i; uint64_t b; for (i = 0; i < read_ids_n; ++i) { b = cur_read_ids[i] & clu_read_ids[i]; w += get_bit_cnt4(ab_bit_table16, b); } uint64_t one = 1; for (i = 0; i < m_read; ++i) { if (read_weight[i] > 0) { int n = i / 64, b = i & 0x3f; if ((cur_read_ids[n] & clu_read_ids[n] & (one << b)) > 0) w += read_weight[i]; } } return w; } int abpoa_consensus_cov(abpoa_graph_t *abg, int id, uint64_t *clu_read_ids) { int i, j, in_id, left_n, right_n; // for each id: get max{left_weigth, right_weight} left_n = right_n = 0; for (i = 0; i < abg->node[id].in_edge_n; ++i) { in_id = abg->node[id].in_id[i]; for (j = 0; j < abg->node[in_id].out_edge_n; ++j) { if (abg->node[in_id].out_id[j] == id) { left_n += get_read_ids_clu_count(abg->node[in_id].read_ids[j], abg->node[in_id].read_ids_n, clu_read_ids); break; } } } for (i = 0; i < abg->node[id].out_edge_n; ++i) { right_n += get_read_ids_clu_count(abg->node[id].read_ids[i], abg->node[id].read_ids_n, clu_read_ids); } return MAX_OF_TWO(left_n, right_n); } void abpoa_set_hb_cons(abpoa_graph_t *abg, int **max_out_id, int n_cons, uint64_t **clu_read_ids, int src_id, int sink_id, abpoa_cons_t *abc) { abc->n_cons = n_cons; int i, j, cur_id; for (i = 0; i < n_cons; ++i) { cur_id = max_out_id[i][src_id]; j = 0; while (cur_id != sink_id) { abc->cons_node_ids[i][j] = cur_id; abc->cons_base[i][j] = abg->node[cur_id].base; abc->cons_cov[i][j] = abpoa_consensus_cov(abg, cur_id, clu_read_ids[i]); abc->cons_phred_score[i][j] = abpoa_cons_phred_score(abc->cons_cov[i][j], abc->clu_n_seq[i]); ++j; cur_id = max_out_id[i][cur_id]; } abc->cons_len[i] = j; } } void abpoa_set_hb_cons1(abpoa_graph_t *abg, int *max_out_id, int src_id, int sink_id, abpoa_cons_t *abc) { int i = 0, cur_id; abc->n_cons = 1; cur_id = max_out_id[src_id]; while (cur_id != sink_id) { abc->cons_node_ids[0][i] = cur_id; abc->cons_base[0][i] = abg->node[cur_id].base; abc->cons_cov[0][i] = abg->node[cur_id].n_read; abc->cons_phred_score[0][i] = abpoa_cons_phred_score(abc->cons_cov[0][i], abc->n_seq); cur_id = max_out_id[cur_id]; ++i; } abc->cons_len[0] = i; } // heaviest_bundling // 1. argmax{cur->weight} // 2. argmax{out_node->weight} void abpoa_heaviest_bundling(abpoa_graph_t *abg, int src_id, int sink_id, int *out_degree, abpoa_cons_t *abc) { int *id, i, cur_id, in_id, out_id, max_id; int max_w, out_w; int *score = (int*)_err_malloc(abg->node_n * sizeof(int)); int *max_out_id = (int*)_err_malloc(abg->node_n * sizeof(int)); kdq_int_t *q = kdq_init_int(); kdq_push_int(q, sink_id); // reverse Breadth-First-Search while ((id = kdq_shift_int(q)) != 0) { cur_id = *id; if (cur_id == sink_id) { max_out_id[cur_id] = -1; score[cur_id] = 0; } else { max_id = -1; if (cur_id == src_id) { int path_score = -1, path_max_w = -1; for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; out_w = abg->node[cur_id].out_weight[i]; if (out_w > path_max_w || (out_w == path_max_w && score[out_id] > path_score)) { max_id = out_id; path_score = score[out_id]; path_max_w = out_w; } } max_out_id[cur_id] = max_id; kdq_destroy_int(q); break; } else { max_w = INT32_MIN; for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; out_w = abg->node[cur_id].out_weight[i]; if (max_w < out_w) { max_w = out_w; max_id = out_id; } else if (max_w == out_w && score[max_id] <= score[out_id]) { max_id = out_id; } } score[cur_id] = max_w + score[max_id]; max_out_id[cur_id] = max_id; } } for (i = 0; i < abg->node[cur_id].in_edge_n; ++i) { in_id = abg->node[cur_id].in_id[i]; if (--out_degree[in_id] == 0) kdq_push_int(q, in_id); } } abc->clu_n_seq[0] = abc->n_seq; // set cons read ids for (i = 0; i < abc->n_seq; ++i) abc->clu_read_ids[0][i] = i; abpoa_set_hb_cons1(abg, max_out_id, src_id, sink_id, abc); free(score); free(max_out_id); } void set_clu_read_ids(abpoa_cons_t *abc, uint64_t **read_ids, int cons_i, int n_seq) { int n, i, j; uint64_t b, one = 1; for (i = n = 0; i < n_seq; ++i) { j = i / 64; b = i & 0x3f; if (read_ids[cons_i][j] & (one << b)) { abc->clu_read_ids[cons_i][n++] = i; } } if (n != abc->clu_n_seq[cons_i]) err_fatal(__func__, "Error in set cluster read ids. (%d, %d)", n, abc->clu_n_seq[cons_i]); } void abpoa_multip_heaviest_bundling(abpoa_graph_t *abg, abpoa_para_t *abpt, int src_id, int sink_id, int *out_degree, int n_clu, int read_ids_n, uint64_t **clu_read_ids, abpoa_cons_t *abc) { int *id, cons_i, i, cur_id, in_id, out_id, max_id; int max_w, out_w; int *_out_degree = (int*)_err_malloc(abg->node_n * sizeof(int)); int *score = (int*)_err_malloc(abg->node_n * sizeof(int)); int **max_out_id = (int**)_err_malloc(n_clu * sizeof(int*)); for (i = 0; i < n_clu; ++i) max_out_id[i] = (int*)_err_malloc(abg->node_n * sizeof(int)); for (cons_i = 0; cons_i < n_clu; cons_i++) { for (i = 0; i < abg->node_n; ++i) _out_degree[i] = out_degree[i]; abc->clu_n_seq[cons_i] = get_read_cnt(clu_read_ids[cons_i], read_ids_n); set_clu_read_ids(abc, clu_read_ids, cons_i, abc->n_seq); kdq_int_t *q = kdq_init_int(); kdq_push_int(q, sink_id); // reverse Breadth-First-Search while ((id = kdq_shift_int(q)) != 0) { cur_id = *id; if (cur_id == sink_id) { max_out_id[cons_i][cur_id] = -1; score[cur_id] = 0; } else { max_id = -1; if (cur_id == src_id) { int path_score = -1, path_max_w = -1; for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; out_w = get_read_ids_clu_weight(abg->node[cur_id].read_ids[i], abg->node[cur_id].read_ids_n, clu_read_ids[cons_i], abpt->use_qv, abg->node[cur_id].read_weight, abg->node[cur_id].m_read); // out_w = abg->node[cur_id].out_weight[i]; if (out_w > path_max_w || (out_w == path_max_w && score[out_id] > path_score)) { max_id = out_id; path_score = score[out_id]; path_max_w = out_w; } } max_out_id[cons_i][cur_id] = max_id; kdq_destroy_int(q); break; } else { max_w = INT32_MIN; for (i = 0; i < abg->node[cur_id].out_edge_n; ++i) { out_id = abg->node[cur_id].out_id[i]; out_w = get_read_ids_clu_weight(abg->node[cur_id].read_ids[i], abg->node[cur_id].read_ids_n, clu_read_ids[cons_i], abpt->use_qv, abg->node[cur_id].read_weight, abg->node[cur_id].m_read); if (max_w < out_w) { max_w = out_w; max_id = out_id; } else if (max_w == out_w && score[max_id] <= score[out_id]) { max_id = out_id; } } score[cur_id] = max_w + score[max_id]; max_out_id[cons_i][cur_id] = max_id; } } for (i = 0; i < abg->node[cur_id].in_edge_n; ++i) { in_id = abg->node[cur_id].in_id[i]; if (--_out_degree[in_id] == 0) kdq_push_int(q, in_id); } } } abpoa_set_hb_cons(abg, max_out_id, n_clu, clu_read_ids, src_id, sink_id, abc); free(score); free(_out_degree); for (i = 0; i < n_clu; ++i) free(max_out_id[i]); free(max_out_id); } void abpoa_output_fx_consensus(abpoa_t *ab, abpoa_para_t *abpt, FILE *out_fp) { if (out_fp == NULL) return; int cons_i, j; abpoa_cons_t *abc = ab->abc; for (cons_i = 0; cons_i < abc->n_cons; ++cons_i) { if (abpt->out_fq) fprintf(out_fp, "@Consensus_sequence"); else fprintf(out_fp, ">Consensus_sequence"); if (abc->n_cons > 1) { fprintf(out_fp, "_%d ", cons_i+1); // cons_id for (j = 0; j < abc->clu_n_seq[cons_i]; ++j) { // cluter read_id if (j != 0) fprintf(out_fp, ","); fprintf(out_fp, "%d", abc->clu_read_ids[cons_i][j]); } } fprintf(out_fp, "\n"); for (j = 0; j < abc->cons_len[cons_i]; ++j) { fprintf(out_fp, "%c", ab_char256_table[abc->cons_base[cons_i][j]]); } fprintf(out_fp, "\n"); if (abpt->out_fq) { fprintf(out_fp, "+Consensus_sequence"); if (abc->n_cons > 1) { fprintf(out_fp, "_%d ", cons_i+1); // cons_id for (j = 0; j < abc->clu_n_seq[cons_i]; ++j) { // cluter read_id if (j != 0) fprintf(out_fp, ","); fprintf(out_fp, "%d", abc->clu_read_ids[cons_i][j]); } } fprintf(out_fp, "\n"); for (j = 0; j < abc->cons_len[cons_i]; ++j) { fprintf(out_fp, "%c", abc->cons_phred_score[cons_i][j]); } fprintf(out_fp, "\n"); } } } abpoa_cons_t *abpoa_allocate_cons(abpoa_cons_t *abc, int n_node, int n_seq, int n_cons) { int i; abc->n_cons = n_cons, abc->n_seq = n_seq; abc->clu_n_seq = (int*)_err_calloc(n_cons, sizeof(int)); abc->cons_len = (int*)_err_calloc(n_cons, sizeof(int)); abc->cons_node_ids = (int**)_err_malloc(n_cons * sizeof(int*)); abc->cons_base = (uint8_t**)_err_malloc(n_cons * sizeof(uint8_t*)); abc->cons_cov = (int**)_err_malloc(n_cons * sizeof(int*)); abc->clu_read_ids = (int**)_err_malloc(n_cons * sizeof(int*)); abc->cons_phred_score = (int**)_err_malloc(n_cons * sizeof(int*)); for (i = 0; i < n_cons; ++i) { abc->cons_node_ids[i] = (int*)_err_malloc(n_node * sizeof(int)); abc->cons_base[i] = (uint8_t*)_err_malloc(n_node * sizeof(uint8_t)); abc->cons_cov[i] = (int*)_err_malloc(n_node * sizeof(int)); abc->clu_read_ids[i] = (int*)_err_malloc(n_seq * sizeof(int)); abc->cons_phred_score[i] = (int*)_err_malloc(n_node * sizeof(int)); } return abc; } int abpoa_check_iden_read_ids(int **rc_weight, uint64_t ***read_ids, int m, int read_ids_n, int pos1, int pos2) { int i, j, k, iden = 1; uint8_t *map = (uint8_t*)_err_calloc(m, sizeof(uint8_t)); for (i = 0; i < m ; ++i) { if (rc_weight[pos1][i] == 0) continue; int found_iden = 0; for (j = 0; j < m; ++j) { // find from 0~m that is identical to i'th read_ids if (map[j] == 1 || rc_weight[pos1][i] != rc_weight[pos2][j]) continue; // iden rc_weight int diff = 0; for (k = 0; k < read_ids_n; ++k) { if (read_ids[pos1][i][k] != read_ids[pos2][j][k]) { diff = 1; break; } } if (diff == 0) { // i is identical to j found_iden = 1; map[j] = 1; break; } } if (found_iden == 0) { // no iden for i'th base iden = 0; break; } } free(map); return iden; } // return: 1 if redundent else 0 int check_redundent_hap(int **clu_haps, int *clu_size, uint64_t **clu_read_ids, int n_clu, int new_clu_i, int n_het_pos, int read_id_i, uint64_t read_id) { int i, j, redundent = 0; for (i = n_clu-1; i >= 0; --i) { int iden = 1; for (j = 0; j < n_het_pos; ++j) { if (clu_haps[i][j] != clu_haps[new_clu_i][j]) { iden = 0; break; } } if (iden == 1) { clu_size[i] += 1; clu_read_ids[i][read_id_i] |= read_id; redundent = 1; break; } } if (redundent == 0) { clu_size[new_clu_i] += 1; clu_read_ids[new_clu_i][read_id_i] |= read_id; } return redundent; } int reassign_hap_by_min_w(int **clu_haps, int *clu_size, uint64_t **clu_read_ids, int read_ids_n, int n_clu, int min_w, int n_het_pos) { int i, j, k, n_reassign = 0; for (i = 0; i < n_clu; ++i) { if (clu_size[i] >= min_w || clu_size[i] == 0) continue; int reassign_i = -1, max_iden_pos = 0; for (j = 0; j < n_clu; ++j) { int n_iden_pos = 0; if (clu_size[j] < min_w) continue; // i < min_w, j >= min_w for (k = 0; k < n_het_pos; ++k) { if (clu_haps[i][k] == clu_haps[j][k]) n_iden_pos++; } if (n_iden_pos > max_iden_pos) { max_iden_pos = n_iden_pos; reassign_i = j; } } if (reassign_i >= 0) { for (j = 0; j < read_ids_n; ++j) { clu_read_ids[reassign_i][j] |= clu_read_ids[i][j]; clu_read_ids[i][j] = 0; } clu_size[reassign_i] += clu_size[i]; clu_size[i] = 0; n_reassign += 1; } } return n_clu - n_reassign; } int reassign_max_n_hap1(int **clu_haps, int *clu_size, uint64_t **clu_read_ids, int read_ids_n, int n_clu, int *clu_poss, int max_n_cons, int n_het_pos) { int i, j, k, n_reassign = 0; for (i = 0; i < n_clu; ++i) { int is_clu = 0; if (clu_size[i] == 0) continue; for (j = 0; j < max_n_cons; ++j) { if (i == clu_poss[j]) { is_clu = 1; break; } } if (is_clu) continue; int reassign_i = -1, max_iden_pos = 0; for (j = 0; j < max_n_cons; ++j) { int clu_i = clu_poss[j], n_iden_pos = 0; // i < min_w, clu_i >= min_w for (k = 0; k < n_het_pos; ++k) { if (clu_haps[i][k] == clu_haps[clu_i][k]) n_iden_pos++; } if (n_iden_pos > max_iden_pos) { max_iden_pos = n_iden_pos; reassign_i = clu_i; } } if (reassign_i >= 0) { for (j = 0; j < read_ids_n; ++j) { clu_read_ids[reassign_i][j] |= clu_read_ids[i][j]; clu_read_ids[i][j] = 0; } clu_size[reassign_i] += clu_size[i]; clu_size[i] = 0; n_reassign += 1; } else { clu_size[i] = 0; } } return n_clu - n_reassign; } typedef struct { int size, pos; } clu_hap_tuple_t; // descending order int tup_cmpfunc (const void * a, const void * b) { return -(((clu_hap_tuple_t*)a)->size - ((clu_hap_tuple_t*)b)->size); } int reassign_max_n_hap(int **clu_haps, int *clu_size, uint64_t **clu_read_ids, int read_ids_n, int n_clu, int n_het_pos, int max_n_cons) { int i; clu_hap_tuple_t *tup = (clu_hap_tuple_t*)_err_malloc(n_clu * sizeof(clu_hap_tuple_t)); int *clu_poss = (int*)_err_malloc(max_n_cons * sizeof(int)); while (n_clu > max_n_cons) { for (i = 0; i < n_clu; ++i) { tup[i].size = clu_size[i]; tup[i].pos = i; } qsort(tup, n_clu, sizeof(clu_hap_tuple_t), tup_cmpfunc); // new min_w for (i = 0; i < max_n_cons; ++i) clu_poss[i] = tup[i].pos; int new_n_clu = reassign_max_n_hap1(clu_haps, clu_size, clu_read_ids, read_ids_n, n_clu, clu_poss, max_n_cons, n_het_pos); if (new_n_clu == n_clu) { // no further reassignment, but still have more than _max_n_cons_ clus err_func_printf(__func__, "%d small clusters of sequences remain un-assigned.", n_clu-max_n_cons); break; } n_clu = new_n_clu; } free(tup); free(clu_poss); return n_clu; } int reassign_hap(int **clu_haps, int *clu_size, uint64_t **clu_read_ids, int read_ids_n, int n_clu, int min_w, int max_n_cons, int n_het_pos) { // assign haplotype with reads < min_w to haplotype with reads >= min_w int new_n_clu = reassign_hap_by_min_w(clu_haps, clu_size, clu_read_ids, read_ids_n, n_clu, min_w, n_het_pos); if (new_n_clu > max_n_cons) // keep at most _max_n_cons_ new_n_clu = reassign_max_n_hap(clu_haps, clu_size, clu_read_ids, read_ids_n, n_clu, n_het_pos, max_n_cons); // move max_n_cons to the front int i, j, pos_i; for (i = pos_i = 0; i < n_clu; ++i) { if (clu_size[i] == 0) continue; if (i == pos_i) { pos_i++; continue; } // move i to pos_i for (j = 0; j < read_ids_n; ++j) { clu_read_ids[pos_i][j] = clu_read_ids[i][j]; clu_size[pos_i] = clu_size[i]; } pos_i++; } if (pos_i > max_n_cons) err_fatal_core(__func__, "Error: collected %d clusters.", pos_i); return pos_i; } // read_weight is NOT used here, no matter use_qv is set or not. // collect minimized set of het bases int abpoa_set_het_row_column_ids_weight(abpoa_graph_t *abg, uint64_t ***read_ids, int *het_poss, int **rc_weight, int msa_l, int n_seq, int m, int min_w, int read_ids_n) { int i, j, k, n, rank; uint64_t b, one = 1, *whole_read_ids = (uint64_t*)_err_calloc(read_ids_n, sizeof(uint64_t)); for (i = 0; i < n_seq; ++i) { j = i / 64; b = i & 0x3f; whole_read_ids[j] |= (one << b); } for (i = 0; i < msa_l; ++i) { for (j = 0; j < read_ids_n; ++j) { read_ids[i][m-1][j] = whole_read_ids[j]; } } free(whole_read_ids); uint8_t *node_map = (uint8_t*)_err_calloc(abg->node_n, sizeof(uint8_t)); int *n_branch = (int*)_err_calloc(msa_l, sizeof(int)), n_het_pos = 0; for (i = 2; i < abg->node_n; ++i) { if (abg->node[i].out_edge_n < 2) continue; for (j = 0; j < abg->node[i].out_edge_n; ++j) { int out_id = abg->node[i].out_id[j]; if (node_map[out_id]) continue; else node_map[out_id] = 1; int sum_out_w = 0; for (k = 0; k < abg->node[out_id].out_edge_n; ++k) sum_out_w += abg->node[out_id].n_read; if (sum_out_w < min_w || sum_out_w > n_seq-min_w) continue; rank = abpoa_graph_node_id_to_msa_rank(abg, out_id); n_branch[rank-1] += 1; // assign seq for (n = 0; n < abg->node[out_id].out_edge_n; ++n) { for (k = 0; k < abg->node[out_id].read_ids_n; ++k) { b = abg->node[out_id].read_ids[n][k]; rc_weight[rank-1][abg->node[out_id].base] += get_bit_cnt4(ab_bit_table16, b); read_ids[rank-1][abg->node[out_id].base][k] |= b; read_ids[rank-1][m-1][k] ^= b; } } rc_weight[rank-1][m-1] -= rc_weight[rank-1][abg->node[out_id].base]; } } for (rank = 0; rank < msa_l; ++rank) { if (rc_weight[rank][m-1] >= min_w && rc_weight[rank][m-1] <= n_seq-min_w) n_branch[rank]++; if (n_branch[rank] > 1) { // filter out identical read_ids int iden = 0; for (i = n_het_pos-1; i >= 0; i--) { int het_pos = het_poss[i]; // remove het bases that share the identical read groups iden = abpoa_check_iden_read_ids(rc_weight, read_ids, m, read_ids_n, rank, het_pos); if (iden == 1) break; } if (iden == 1) continue; het_poss[n_het_pos++] = rank; #ifdef __DEBUG__ fprintf(stderr, "%d\t", rank); for (j = 0; j < m; ++j) { fprintf(stderr, "%c: %d\t", "ACGT-"[j], rc_weight[rank][j]); } fprintf(stderr, "\n"); #endif } } free(n_branch); free(node_map); return n_het_pos; } // group read into clusters based on all het bases // initial cluster size could be > max_n_cons int abpoa_collect_clu_hap_read_ids(int *het_poss, int n_het_pos, uint64_t ***read_ids, int read_ids_n, int n_seq, int m, int min_w, int max_n_cons, uint64_t ***clu_read_ids, int *_m_clu) { if (n_het_pos == 0) return 1; int i, j, k, n_clu = 0, m_clu = 2; int **clu_haps = (int**)_err_malloc(2 * sizeof(int*)); int *clu_size = (int*)_err_calloc(2, sizeof(int)); *clu_read_ids = (uint64_t**)_err_malloc(2 * sizeof(uint64_t**)); for (i = 0; i < 2; ++i) { clu_haps[i] = (int*)_err_calloc(n_het_pos, sizeof(int)); (*clu_read_ids)[i] = (uint64_t*)_err_calloc(read_ids_n, sizeof(uint64_t)); } for (i = 0; i < n_seq; ++i) { // collect haplotype for each sequence int read_id_i = i / 64; uint64_t read_id = 1ULL << (i & 0x3f); for (j = 0; j < n_het_pos; ++j) { int het_pos = het_poss[j]; for (k = 0; k < m; ++k) { if (read_ids[het_pos][k][read_id_i] & read_id) { clu_haps[n_clu][j] = k; break; } } } if (check_redundent_hap(clu_haps, clu_size, *clu_read_ids, n_clu, n_clu, n_het_pos, read_id_i, read_id) == 0) { if (++n_clu == m_clu) { m_clu <<= 1; clu_haps = (int**)_err_realloc(clu_haps, m_clu * sizeof(int*)); clu_size = (int*)_err_realloc(clu_size, m_clu * sizeof(int)); (*clu_read_ids) = (uint64_t**)_err_realloc(*clu_read_ids, m_clu * sizeof(uint64_t**)); for (j = n_clu; j < m_clu; ++j) { clu_haps[j] = (int*)_err_calloc(n_het_pos, sizeof(int)); clu_size[j] = 0; (*clu_read_ids)[j] = (uint64_t*)_err_calloc(read_ids_n, sizeof(uint64_t)); // mem may lost } } } } if (n_clu < 2) err_fatal(__func__, "# haplotypes: %d\n", n_clu); #ifdef __DEBUG__ fprintf(stderr, "n_clu: %d\n", n_clu); for (i = 0; i < n_clu; ++i) { for (j = 0; j < n_het_pos; ++j) { fprintf(stderr, "%d\t", clu_haps[i][j]); } fprintf(stderr, "\tsize: %d\n", clu_size[i]); } #endif // assign haplotype with reads < min_w to haplotype with reads >= min_w // keep at most _max_n_cons_ haps and read ids, weight need to >= min_w n_clu = reassign_hap(clu_haps, clu_size, *clu_read_ids, read_ids_n, n_clu, min_w, max_n_cons, n_het_pos); #ifdef __DEBUG__ fprintf(stderr, "After re-assign: n_clu: %d\n", n_clu); for (i = 0; i < n_clu; ++i) { fprintf(stderr, "%d:\tsize: %d\n", i, clu_size[i]); } #endif for (i = 0; i < m_clu; ++i) free(clu_haps[i]); free(clu_haps); free(clu_size); *_m_clu = m_clu; return n_clu; } // read_weight is NOT used here // cluster reads into _n_clu_ groups based on heterogeneous bases int abpoa_multip_read_clu(abpoa_graph_t *abg, int src_id, int sink_id, int n_seq, int m, int max_n_cons, double min_freq, uint64_t ***clu_read_ids, int *_m_clu) { abpoa_set_msa_rank(abg, src_id, sink_id); int i, j, n_clu, m_clu, read_ids_n = (n_seq-1)/64+1; int msa_l = abg->node_id_to_msa_rank[sink_id]-1, min_w = MAX_OF_TWO(1, n_seq * min_freq); // TODO fastq-qual weight // read_ids: support reads for each base (A/C/G/T) at each position uint64_t ***read_ids = (uint64_t***)_err_malloc(sizeof(uint64_t**) * msa_l); for (i = 0; i < msa_l; ++i) { read_ids[i] = (uint64_t**)_err_malloc(sizeof(uint64_t*) * m); for (j = 0; j < m; ++j) read_ids[i][j] = (uint64_t*)_err_calloc(read_ids_n, sizeof(uint64_t)); } // is rc_weight necessary? int **rc_weight = (int**)_err_malloc(msa_l * sizeof(int*)); for (i = 0; i < msa_l; ++i) { rc_weight[i] = (int*)_err_calloc(m, sizeof(int)); // ACGT rc_weight[i][m-1] = n_seq; } // find min set of het nodes int *het_poss = (int*)_err_calloc(msa_l, sizeof(int)); int n_het_pos = abpoa_set_het_row_column_ids_weight(abg, read_ids, het_poss, rc_weight, msa_l, n_seq, m, min_w, read_ids_n); if (n_het_pos < 1) n_clu = 1; // collect at most _max_n_cons_ haplotypes and corresponding read ids else n_clu = abpoa_collect_clu_hap_read_ids(het_poss, n_het_pos, read_ids, read_ids_n, n_seq, m, min_w, max_n_cons, clu_read_ids, &m_clu); for (i = 0; i < msa_l; ++i) { for (j = 0; j < m; ++j) free(read_ids[i][j]); free(read_ids[i]); free(rc_weight[i]); } free(read_ids); free(rc_weight); free(het_poss); *_m_clu = m_clu; return n_clu; } // should always do topological sort first, then generate consensus void abpoa_generate_consensus(abpoa_t *ab, abpoa_para_t *abpt) { if (ab->abg->is_called_cons == 1) return; abpoa_graph_t *abg = ab->abg; if (abg->node_n <= 2) return; int i, *out_degree = (int*)_err_malloc(abg->node_n * sizeof(int)); for (i = 0; i < abg->node_n; ++i) { out_degree[i] = abg->node[i].out_edge_n; } int n_clu, m_clu, n_seq = ab->abs->n_seq; uint64_t **clu_read_ids; int read_ids_n = (n_seq-1)/64+1; if (abpt->max_n_cons > 1) n_clu = abpoa_multip_read_clu(abg, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID, n_seq, abpt->m, abpt->max_n_cons, abpt->min_freq, &clu_read_ids, &m_clu); else n_clu = 1; abpoa_cons_t *abc = ab->abc; abpoa_allocate_cons(abc, abg->node_n, ab->abs->n_seq, n_clu); if (n_clu > 1) { abpoa_multip_heaviest_bundling(abg, abpt, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID, out_degree, n_clu, read_ids_n, clu_read_ids, abc); for (i = 0; i < m_clu; ++i) free(clu_read_ids[i]); free(clu_read_ids); } else { abpoa_heaviest_bundling(abg, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID, out_degree, abc); } abg->is_called_cons = 1; free(out_degree); } abPOA-1.4.1/src/abpoa_output.h000066400000000000000000000002631425041320700161000ustar00rootroot00000000000000#ifndef ABPOA_OUTPUT_H #define ABPOA_OUTPUT_H #ifdef __cplusplus extern "C" { #endif void set_65536_table(void); void set_bit_table16(void); #ifdef __cplusplus } #endif #endifabPOA-1.4.1/src/abpoa_plot.c000066400000000000000000000123361425041320700155150ustar00rootroot00000000000000#include #include #include "abpoa.h" #include "abpoa_graph.h" #include "utils.h" /* example of dot file for graphviz */ /* digraph test1 { a -> b -> c; a -> {x y}; b [shape=box]; c [label="hello\nworld",color=blue,fontsize=24, fontname="Palatino-Italic",fontcolor=red,style=filled]; a -> z [label="hi", weight=100]; x -> z [label="multi-line\nlabel"]; edge [style=dashed,color=red]; b -> x; {rank=same; b x} } graph test2 { a -- b -- c [style=dashed]; a -- {x y}; x -- c [w=10.0]; x -- y [w=5.0,len=3]; } */ extern char ab_nt256_table[256]; // base (index, rank, node_id) // A (1, 1, 2) A: base 1: index 1: rank 2: node_id void abpoa_dump_pog(abpoa_t *ab, abpoa_para_t *abpt) { char PROG[20] = "abpoa"; int font_size=24; abpoa_graph_t *abg = ab->abg; if (abg->is_topological_sorted == 0) abpoa_topological_sort(abg, abpt); // all settings // char node_color[5][10] = {"purple3", "red3", "seagreen4", "gold2", "gray"}; // ACGTN char node_color[5][10] = {"pink1", "red1", "gold2", "seagreen4", "gray"}; // ACGTN // float dpi_size = 3000, graph_width = 100, graph_height = 6; float node_width=1; char rankdir[5] = "LR", node_style[10]="filled", node_fixedsize[10]="true", node_shape[10]="circle"; int show_aligned_mismatch = 1; int i, j, id, index, out_id; char base; char **node_label = (char**)_err_malloc(abg->node_n * sizeof(char*)); for (i = 0; i < abg->node_n; ++i) node_label[i] = (char*)_err_malloc(sizeof(char) * 128); char *dot_fn = (char*)malloc(strlen(abpt->out_pog) + 10); strcpy(dot_fn, abpt->out_pog); FILE *fp = xopen(strcat(dot_fn, ".dot"), "w"); fprintf(fp, "// %s graph dot file.\n// %d nodes.\n", PROG, abg->node_n); // fprintf(fp, "digraph ABPOA_graph {\n\tgraph [dpi=%f]; size=\"%f,%f\";\n\trankdir=\"%s\";\n\tnode [width=%f, style=%s, fixedsize=%s, shape=%s];\n", dpi_size, graph_width, graph_height, rankdir, node_width, node_style, node_fixedsize, node_shape); fprintf(fp, "digraph ABPOA_graph {\n\tgraph [rankdir=\"%s\"];\n\tnode [width=%f, style=%s, fixedsize=%s, shape=%s];\n", rankdir, node_width, node_style, node_fixedsize, node_shape); for (i = 0; i < abg->node_n; ++i) { id = abpoa_graph_index_to_node_id(abg, i); index = i; if (id == ABPOA_SRC_NODE_ID) { base = 'S'; //sprintf(node_label[id], "\"%c\n(%d,%d,%d)\"", base, index, rank, id); // only show seq sprintf(node_label[id], "\"%c\n%d\"", base,index); fprintf(fp, "%s [color=%s, fontsize=%d]\n", node_label[id], node_color[4], font_size); } else if (id == ABPOA_SINK_NODE_ID) { base = 'E'; //sprintf(node_label[id], "\"%c\n(%d,%d,%d)\"", base, index, rank, id); // only show seq sprintf(node_label[id], "\"%c\n%d\"", base,index); fprintf(fp, "%s [color=%s, fontsize=%d]\n", node_label[id], node_color[4], font_size); } else { base = ab_nt256_table[abg->node[id].base]; //sprintf(node_label[id], "\"%c\n(%d,%d,%d)\"", base, index, rank, id); // only show seq sprintf(node_label[id], "\"%c\n%d\"", base,index); fprintf(fp, "%s [color=%s, fontsize=%d]\n", node_label[id], node_color[abg->node[id].base], font_size); } } int x_index = -1; for (i = 0; i < abg->node_n; ++i) { id = abpoa_graph_index_to_node_id(abg, i); // out_edge for (j = 0; j < abg->node[id].out_edge_n; ++j) { out_id = abg->node[id].out_id[j]; fprintf(fp, "\t%s -> %s [label=\"%d\", penwidth=%d]\n", node_label[id], node_label[out_id], abg->node[id].out_weight[j], abg->node[id].out_weight[j]+1); } if (abg->node[id].aligned_node_n > 0) { fprintf(fp, "\t{rank=same; %s ", node_label[id]); for (j = 0; j < abg->node[id].aligned_node_n; ++j) fprintf(fp, "%s ", node_label[abg->node[id].aligned_node_id[j]]); fprintf(fp, "};\n"); if (show_aligned_mismatch) { if (i > x_index) { x_index = i; // mismatch dashed line fprintf(fp, "\t{ edge [style=dashed, arrowhead=none]; %s ", node_label[id]); for (j = 0; j < abg->node[id].aligned_node_n; ++j) { fprintf(fp, "-> %s ", node_label[abg->node[id].aligned_node_id[j]]); index = abpoa_graph_node_id_to_index(abg, abg->node[id].aligned_node_id[j]); x_index = index > x_index ? index : x_index; } fprintf(fp, "}\n"); } } } } fprintf(fp, "}\n"); for (i = 0; i < abg->node_n; ++i) free(node_label[i]); free(node_label); err_fclose(fp); char cmd[1024]; char *type = strrchr(abpt->out_pog, '.'); if (strcmp(type+1, "pdf") != 0 && strcmp(type+1, "png") != 0) err_fatal_simple("POG can only be dump to .pdf/.png file"); sprintf(cmd, "dot %s -T%s > %s", dot_fn, type+1, abpt->out_pog); free(dot_fn); if (system(cmd) != 0) err_fatal(__func__, "Fail to plot %s DAG.", PROG); } abPOA-1.4.1/src/abpoa_seed.c000066400000000000000000000756451425041320700154730ustar00rootroot00000000000000#include #include #include #include #include "abpoa.h" #include "abpoa_seed.h" #include "utils.h" #include "kvec.h" #include "ksort.h" const char LogTable256[256] = { #define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) }; static inline int ilog2_32(uint32_t v) { uint32_t t, tt; if ((tt = v>>16)) return (t = tt>>8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]; return (t = v>>8) ? 8 + LogTable256[t] : LogTable256[v]; } #define ab_sort_key_128x(a) ((a).x) KRADIX_SORT_INIT(ab_128x, ab_u128_t, ab_sort_key_128x, 8) #define ab_sort_key_128y(a) ((a).y) KRADIX_SORT_INIT(ab_128y, ab_u128_t, ab_sort_key_128y, 8) #define ab_sort_key_64(a) (a) KRADIX_SORT_INIT(64, uint64_t, ab_sort_key_64, 8) /* from lh3/minimap2/sketch.c */ /********** start *************/ static inline uint64_t hash64(uint64_t key, uint64_t mask) { key = (~key + (key << 21)) & mask; // key = (key << 21) - key - 1; key = key ^ key >> 24; key = ((key + (key << 3)) + (key << 8)) & mask; // key * 265 key = key ^ key >> 14; key = ((key + (key << 2)) + (key << 4)) & mask; // key * 21 key = key ^ key >> 28; key = (key + (key << 31)) & mask; return key; } typedef struct { // a simplified version of kdq int front, count; int a[32]; } tiny_queue_t; static inline void tq_push(tiny_queue_t *q, int x) { q->a[((q->count++) + q->front) & 0x1f] = x; } static inline int tq_shift(tiny_queue_t *q) { int x; if (q->count == 0) return -1; x = q->a[q->front++]; q->front &= 0x1f; --q->count; return x; } /** * Find symmetric (w,k)-minimizers on a DNA sequence * * @param km thread-local memory pool; using NULL falls back to malloc() * @param str DNA sequence * @param len length of $str * @param w find a minimizer for every $w consecutive k-mers * @param k k-mer size * @param rid reference ID; will be copied to the output $p array * @param is_hpc homopolymer-compressed or not * @param p minimizers * p->a[i].x = kMer<<8 | kmerSpan * p->a[i].y = rid<<32 | lastPos<<1 | strand * where lastPos is the position of the last base of the i-th minimizer, * and strand indicates whether the minimizer comes from the top or the bottom strand. * Callers may want to set "p->n = 0"; otherwise results are appended to p */ void mm_sketch(void *km, const uint8_t *str, int len, int w, int k, uint32_t rid, int is_hpc, int both_strand, ab_u128_v *p) { uint64_t shift1 = 2 * (k - 1), mask = (1ULL<<2*k) - 1, kmer[2] = {0,0}; int i, j, l, buf_pos, min_pos, kmer_span = 0; ab_u128_t buf[256], min = { UINT64_MAX, UINT64_MAX }; tiny_queue_t tq; assert(len > 0 && (w > 0 && w < 256) && (k > 0 && k <= 28)); // 56 bits for k-mer; could use long k-mers, but 28 enough in practice memset(buf, 0xff, w * 16); memset(&tq, 0, sizeof(tiny_queue_t)); kv_resize(ab_u128_t, km, *p, p->n + len/w); for (i = l = buf_pos = min_pos = 0; i < len; ++i) { int c = str[i]; ab_u128_t info = { UINT64_MAX, UINT64_MAX }; if (c < 4) { // not an ambiguous base uint32_t z; if (is_hpc) { int skip_len = 1; if (i + 1 < len && str[i + 1] == c) { for (skip_len = 2; i + skip_len < len; ++skip_len) if (str[i + skip_len] != c) break; i += skip_len - 1; // put $i at the end of the current homopolymer run } tq_push(&tq, skip_len); kmer_span += skip_len; if (tq.count > k) kmer_span -= tq_shift(&tq); } else kmer_span = l + 1 < k? l + 1 : k; if (both_strand) { kmer[0] = (kmer[0] << 2 | c) & mask; // forward k-mer kmer[1] = (kmer[1] >> 2) | (3ULL^c) << shift1; // reverse k-mer if (kmer[0] == kmer[1]) continue; // skip "symmetric k-mers" as we don't know it strand z = kmer[0] < kmer[1]? 0 : 1; // strand } else { kmer[0] = (kmer[0] << 2 | c) & mask; // forward k-mer z = 0; } ++l; if (l >= k && kmer_span < 256) { info.x = hash64(kmer[z], mask) << 8 | kmer_span; info.y = (uint64_t)rid<<32 | (uint32_t)i<<1 | z; } } else l = 0, tq.count = tq.front = 0, kmer_span = 0; buf[buf_pos] = info; // need to do this here as appropriate buf_pos and buf[buf_pos] are needed below if (l == w + k - 1 && min.x != UINT64_MAX) { // special case for the first window - because identical k-mers are not stored yet for (j = buf_pos + 1; j < w; ++j) if (min.x == buf[j].x && buf[j].y != min.y) kv_push(ab_u128_t, km, *p, buf[j]); for (j = 0; j < buf_pos; ++j) if (min.x == buf[j].x && buf[j].y != min.y) kv_push(ab_u128_t, km, *p, buf[j]); } if (info.x <= min.x) { // a new minimum; then write the old min if (l >= w + k && min.x != UINT64_MAX) kv_push(ab_u128_t, km, *p, min); min = info, min_pos = buf_pos; } else if (buf_pos == min_pos) { // old min has moved outside the window if (l >= w + k - 1 && min.x != UINT64_MAX) kv_push(ab_u128_t, km, *p, min); for (j = buf_pos + 1, min.x = UINT64_MAX; j < w; ++j) // the two loops are necessary when there are identical k-mers if (min.x >= buf[j].x) min = buf[j], min_pos = j; // >= is important s.t. min is always the closest k-mer for (j = 0; j <= buf_pos; ++j) if (min.x >= buf[j].x) min = buf[j], min_pos = j; if (l >= w + k - 1 && min.x != UINT64_MAX) { // write identical k-mers for (j = buf_pos + 1; j < w; ++j) // these two loops make sure the output is sorted if (min.x == buf[j].x && min.y != buf[j].y) kv_push(ab_u128_t, km, *p, buf[j]); for (j = 0; j <= buf_pos; ++j) if (min.x == buf[j].x && min.y != buf[j].y) kv_push(ab_u128_t, km, *p, buf[j]); } } if (++buf_pos == w) buf_pos = 0; } if (min.x != UINT64_MAX) kv_push(ab_u128_t, km, *p, min); } // For amino acid sequence void mm_aa_sketch(void *km, const uint8_t *str, int len, int w, int k, uint32_t rid, int is_hpc, ab_u128_v *p) { uint64_t mask = (1ULL<<5*k) - 1, kmer[2] = {0,0}; int i, j, l, buf_pos, min_pos, kmer_span = 0; ab_u128_t buf[256], min = { UINT64_MAX, UINT64_MAX }; tiny_queue_t tq; assert(len > 0 && (w > 0 && w < 256) && (k > 0 && k <= 11)); // 56 / 5 == 11 memset(buf, 0xff, w * 16); memset(&tq, 0, sizeof(tiny_queue_t)); kv_resize(ab_u128_t, km, *p, p->n + len/w); for (i = l = buf_pos = min_pos = 0; i < len; ++i) { int c = str[i]; ab_u128_t info = { UINT64_MAX, UINT64_MAX }; if (c < 26) { // not an ambiguous base uint32_t z; if (is_hpc) { int skip_len = 1; if (i + 1 < len && str[i + 1] == c) { for (skip_len = 2; i + skip_len < len; ++skip_len) if (str[i + skip_len] != c) break; i += skip_len - 1; // put $i at the end of the current homopolymer run } tq_push(&tq, skip_len); kmer_span += skip_len; if (tq.count > k) kmer_span -= tq_shift(&tq); } else kmer_span = l + 1 < k? l + 1 : k; kmer[0] = (kmer[0] << 5 | c) & mask; // only forward k-mer for aa seq z = 0; ++l; if (l >= k && kmer_span < 256) { info.x = hash64(kmer[z], mask) << 8 | kmer_span; info.y = (uint64_t)rid<<32 | (uint32_t)i<<1 | z; } } else l = 0, tq.count = tq.front = 0, kmer_span = 0; buf[buf_pos] = info; // need to do this here as appropriate buf_pos and buf[buf_pos] are needed below if (l == w + k - 1 && min.x != UINT64_MAX) { // special case for the first window - because identical k-mers are not stored yet for (j = buf_pos + 1; j < w; ++j) if (min.x == buf[j].x && buf[j].y != min.y) kv_push(ab_u128_t, km, *p, buf[j]); for (j = 0; j < buf_pos; ++j) if (min.x == buf[j].x && buf[j].y != min.y) kv_push(ab_u128_t, km, *p, buf[j]); } if (info.x <= min.x) { // a new minimum; then write the old min if (l >= w + k && min.x != UINT64_MAX) kv_push(ab_u128_t, km, *p, min); min = info, min_pos = buf_pos; } else if (buf_pos == min_pos) { // old min has moved outside the window if (l >= w + k - 1 && min.x != UINT64_MAX) kv_push(ab_u128_t, km, *p, min); for (j = buf_pos + 1, min.x = UINT64_MAX; j < w; ++j) // the two loops are necessary when there are identical k-mers if (min.x >= buf[j].x) min = buf[j], min_pos = j; // >= is important s.t. min is always the closest k-mer for (j = 0; j <= buf_pos; ++j) if (min.x >= buf[j].x) min = buf[j], min_pos = j; if (l >= w + k - 1 && min.x != UINT64_MAX) { // write identical k-mers for (j = buf_pos + 1; j < w; ++j) // these two loops make sure the output is sorted if (min.x == buf[j].x && min.y != buf[j].y) kv_push(ab_u128_t, km, *p, buf[j]); for (j = 0; j <= buf_pos; ++j) if (min.x == buf[j].x && min.y != buf[j].y) kv_push(ab_u128_t, km, *p, buf[j]); } } if (++buf_pos == w) buf_pos = 0; } if (min.x != UINT64_MAX) kv_push(ab_u128_t, km, *p, min); } /************ end *************/ // tree_id_map: guide tree node id -> original input order id /* mm: is unsorted * a.x = kMer<<8 | kmerSpan * a.y = rid<<32 | strand<<31 | lastPos */ int abpoa_build_guide_tree(abpoa_para_t *abpt, int n_seq, ab_u128_v *mm, int *tree_id_map) { if (mm->n == 0) return 0; if (abpt->verbose > 0) fprintf(stderr, "[%s] Building progressive guide tree ... ", __func__); size_t i, _i, j; int rid1, rid2; // mm_hit_n: mimizer hits between each two sequences // 0: 0 // 1: 0 1 // 2: 0 1 2 int *mm_hit_n = (int*)_err_calloc((n_seq * (n_seq+1)) >> 1, sizeof(int)); // ... // n: 0 1 ... n-1 n // // # total mimizers of i: mm_hit_n[(i*(i+1))/2+i] // # total hits for i and j (i>j): mm_hit_n[(i*(i+1)/2)+j] radix_sort_ab_128x(mm->a, mm->a + mm->n); // sort mm by k-mer hash values uint64_t last_x = mm->a[0].x; int *mm_cnt = (int*)_err_malloc(n_seq * sizeof(int)); for (_i = 0, i = 1; i < mm->n; ++i) { // collect mm hits if (mm->a[i].x != last_x) { // now [_i, i-1] have the same minimizer k-mer memset(mm_cnt, 0, n_seq * sizeof(int)); for (j = _i; j < i; ++j) { // count mm->a[j] rid1 = mm->a[j].y >> 32; ++mm_cnt[rid1]; ++mm_hit_n[((rid1 * (rid1+1)) >> 1) + rid1]; } for (rid1 = 0; rid1 < n_seq-1; ++rid1) { for (rid2 = rid1+1; rid2 < n_seq; ++rid2) { mm_hit_n[((rid2 * (rid2 + 1)) >> 1) + rid1] += MIN_OF_TWO(mm_cnt[rid1], mm_cnt[rid2]); } } // next minimizer last_x = mm->a[i].x, _i = i; } } // now [_i, i-1] have the same minimizer k-mer memset(mm_cnt, 0, n_seq * sizeof(int)); for (j = _i; j < i; ++j) { // count mm->a[j] rid1 = mm->a[j].y >> 32; ++mm_cnt[rid1]; ++mm_hit_n[((rid1 * (rid1+1)) >> 1) + rid1]; } for (rid1 = 0; rid1 < n_seq-1; ++rid1) { for (rid2 = rid1+1; rid2 < n_seq; ++rid2) { mm_hit_n[((rid2 * (rid2 + 1)) >> 1) + rid1] += MIN_OF_TWO(mm_cnt[rid1], mm_cnt[rid2]); } } free(mm_cnt); // calculate jaccard similarity between each two sequences double *jac_sim = (double*)_err_calloc((n_seq * (n_seq-1)) >> 1, sizeof(double)); double max_jac = -1.0, jac; int max_i=-1, max_j=-1; for (i = 1; i < (size_t)n_seq; ++i) { for (j = 0; j < i; ++j) { int tot_n = mm_hit_n[((i*(i+1))>>1)+i] + mm_hit_n[((j*(j+1))>>1)+j] - mm_hit_n[((i*(i+1))>>1)+j]; if (tot_n == 0) jac = 0; else if (tot_n < 0) err_fatal(__func__, "Bug in progressive tree building. (1)"); else jac = (0.0+mm_hit_n[((i*(i+1))>>1)+j]) / tot_n; jac_sim[((i * (i-1)) >> 1) + j] = jac; // jac_sim[rid2*(rid2-1)/2 + rid1] if (jac > max_jac) { max_jac = jac; max_i = i, max_j = j; } } } // build guide tree // first pick two with the biggest jac (max_i, max_j) int n_in_map = 2; tree_id_map[0] = max_j, tree_id_map[1] = max_i; // then, pick one with biggest jac sum with existing sequence in tree_id_map while (n_in_map < n_seq) { max_jac = -1.0, max_i = n_seq; for (rid1 = 0; rid1 < n_seq; ++rid1) { jac = 0.0; for (i = 0; i < (size_t)n_in_map; ++i) { rid2 = tree_id_map[i]; if (rid1 == rid2) { jac = -1.0; break; } else if (rid1 > rid2) jac += jac_sim[((rid1 * (rid1-1)) >> 1) + rid2]; else jac += jac_sim[((rid2 * (rid2-1)) >> 1) + rid1]; } if (jac > max_jac) { max_jac = jac; max_i = rid1; } } if (max_i == n_seq) err_fatal(__func__, "Bug in progressive tree building. (2)"); tree_id_map[n_in_map++] = max_i; } free(mm_hit_n); free(jac_sim); if (abpt->verbose > 0) fprintf(stderr, "done!\n"); return 0; } // t's mm: is sorted, q's mm is unsorted // | r1's minimizers | r2's minimizers | ... | rn's minimizers | // mm_c: | 0 | n_r1_mm | n_r1..2_mm | ... | n_r1..n-1_mm | n_r1..n_mm | // t is already in the graph, q is query sequence // merge sort for t and q's minimizer buckets int collect_anchors1(void *km, ab_u64_v *anchors, ab_u128_v mm, int *mm_c, int tid, int qid, int qlen, int k) { int i, j, _i, _j; uint64_t xi, xj, _xi, _xj, _yi, _yj, a; i = mm_c[tid], j = mm_c[qid]; // t's mm is already sorted XXX radix_sort_ab_128x(mm.a + j, mm.a + mm_c[qid+1]); while (i < mm_c[tid+1] && j < mm_c[qid+1]) { xi = mm.a[i].x, xj = mm.a[j].x; if (xi == xj) { for (_i = i; _i < mm_c[tid+1]; ++_i) { _xi = mm.a[_i].x; if (_xi != xi) break; _yi = mm.a[_i].y; for (_j = j; _j < mm_c[qid+1]; ++_j) { _xj = mm.a[_j].x; if (_xj != xj) break; _yj = mm.a[_j].y; // t_strand<<63 | t_lastPos<<32 | q_lastPos if ((_yi & 1) == (_yj & 1)) { // same strand a = (uint64_t)((uint32_t)_yi>>1)<<32 | ((uint32_t)_yj>>1); } else { // different strand a = 1ULL<<63 | (uint64_t)((uint32_t)_yi>>1)<<32 | (qlen - (((uint32_t)_yj>>1)+1-k) - 1); // XXX qlen < pow(2,28) } kv_push(uint64_t, km, *anchors, a); } } i = _i, j = _j; } else if (xi < xj) ++i; else if (xi > xj) ++j; } // sort by tpos radix_sort_64(anchors->a, anchors->a + anchors->n); return anchors->n; } int get_local_chain_score(int j_end_tpos, int j_end_qpos, int i_end_anchor_i, ab_u64_v *anchors, int *pre_id, int *score) { int i = i_end_anchor_i, chain_score = 0; int i_tpos, i_qpos; do { i_tpos = (anchors->a[i] >> 32) & 0x7fffffff, i_qpos = (int32_t)anchors->a[i]; if (i_tpos <= j_end_tpos && i_qpos <= j_end_qpos) break; i = pre_id[i]; } while (i != -1); if (i == -1) chain_score = score[i_end_anchor_i]; else chain_score = score[i_end_anchor_i] - score[i]; return chain_score; } // local chains: // x: strand | end_tpos | end_qpos // y: end_anchor_i | start_anchor_i int abpoa_dp_chaining_of_local_chains(void *km, ab_u128_t *local_chains, int n_local_chains, ab_u64_v *anchors, int *score, int *pre_id, ab_u64_v *par_anchors, int min_w, int tlen, int qlen) { int i, j, st, score1, global_max_score=INT32_MIN, global_max_i=-1; int *chain_score = (int*)kmalloc(km, n_local_chains * 4), *pre_chain_id = (int*)kmalloc(km, n_local_chains * 4); size_t _n = par_anchors->n; for (i = st = 0; i < n_local_chains; ++i) { uint64_t ix = local_chains[i].x, iy = local_chains[i].y; int istrand = ix >> 63, i_end_qpos = (int32_t)ix, i_end_anchor_i = iy >> 32, i_start_anchor_i = (int32_t)iy; int i_start_tpos = (anchors->a[i_start_anchor_i] >> 32) & 0x7fffffff, i_start_qpos = (int32_t)anchors->a[i_start_anchor_i]; int max_j = -1, max_score = score[i_end_anchor_i]; while (st < i) { if ((local_chains[st].x) >> 63 != istrand) ++st; else break; } for (j = i-1; j >= st; --j) { uint64_t jx = local_chains[j].x; int j_end_tpos = (jx >> 32) & 0x7fffffff, j_end_qpos = (int32_t)jx; //, j_end_anchor_i = iy >> 32; if (j_end_qpos >= i_end_qpos) continue; if (i_start_tpos > j_end_tpos && i_start_qpos > j_end_qpos) score1 = chain_score[j] + score[i_end_anchor_i]; else score1 = chain_score[j] + get_local_chain_score(j_end_tpos, j_end_qpos, i_end_anchor_i, anchors, pre_id, score); if (score1 > max_score) { max_score = score1; max_j = j; } } chain_score[i] = max_score; pre_chain_id[i] = max_j; if (max_score > global_max_score) { global_max_score = max_score; global_max_i = i; } } if (global_max_i < 0) return 0; // collect anchors based on global_max_i int cur_i = global_max_i, pre_i = pre_chain_id[global_max_i]; uint64_t cur_y = local_chains[cur_i].y, pre_x, pre_y; int last_tpos=tlen, last_qpos=qlen; while (pre_i != -1) { // collect valid anchors in local_chains[cur_i], constrained by local_chains[pre_i] pre_x = local_chains[pre_i].x, pre_y = local_chains[pre_i].y; int pre_end_tpos = (pre_x >> 32) & 0x7fffffff, pre_end_qpos = (int32_t)pre_x; i = cur_y >> 32; while (i != -1) { int cur_tpos = (anchors->a[i] >> 32) & 0x7fffffff, cur_qpos = (int32_t)anchors->a[i]; if (cur_tpos > pre_end_tpos && cur_qpos > pre_end_qpos) { if (last_tpos - cur_tpos >= min_w && last_qpos - cur_qpos >= min_w) { kv_push(uint64_t, 0, *par_anchors, anchors->a[i]); last_tpos = cur_tpos, last_qpos = cur_qpos; } } else break; i = pre_id[i]; } cur_i = pre_i, pre_i = pre_chain_id[pre_i], cur_y = pre_y; } // collect anchors of last chain: local_chains[cur_i] i = cur_y >> 32; while (i != -1) { int cur_tpos = (anchors->a[i] >> 32) & 0x7fffffff, cur_qpos = (int32_t)anchors->a[i]; if (last_tpos - cur_tpos >= min_w && last_qpos - cur_qpos >= min_w) { kv_push(uint64_t, 0, *par_anchors, anchors->a[i]); last_tpos = cur_tpos, last_qpos = cur_qpos; } i = pre_id[i]; } // reverse order of par_anchors for (i = 0; i < (int)(par_anchors->n-_n) >> 1; ++i) { uint64_t tmp = par_anchors->a[_n+i]; par_anchors->a[_n+i] = par_anchors->a[par_anchors->n-i-1]; par_anchors->a[par_anchors->n-i-1] = tmp; } #ifdef __DEBUG__ for (i = _n; i < par_anchors->n; ++i) { uint64_t ia = par_anchors->a[i]; // strand, rpos, qpos fprintf(stderr, "%c\t%ld\t%d\n", "+-"[ia >> 63], (ia>>32) & 0x7fffffff, ((uint32_t)ia)); } #endif kfree(km, chain_score), kfree(km, pre_chain_id); return 0; } // for DP chaining static int get_chain_score(int max_bw, int *score, int i_qpos, int i_tpos, int j_qpos, int j_tpos, int k) { int delta_q, delta_t, delta_tq, min_d; delta_q = i_qpos - j_qpos; delta_t = i_tpos - j_tpos; min_d = MIN_OF_THREE(delta_q, delta_t, k); *score = min_d; if (delta_q >= delta_t) { if ((delta_tq = delta_q - delta_t) > max_bw) return 0; } else { if ((delta_tq = delta_t - delta_q) > max_bw) return 0; } *score -= ((ilog2_32(delta_tq) >> 1) + delta_tq * 0.01 * k); return 1; } // Dynamic Programming-based Chaining for global alignment mode // anchors: // strand<<63 | tpos<<32 | qpos int abpoa_dp_chaining(void *km, ab_u64_v *anchors, ab_u64_v *par_anchors, abpoa_para_t *abpt, int tlen, int qlen) { int i, j, st, n_a = anchors->n; int *score = (int32_t*)kmalloc(km, n_a * 4), *pre_id = (int32_t*)kmalloc(km, n_a * 4), *end_pos = (int32_t*)kmalloc(km, n_a * 4); memset(end_pos, 0, n_a * 4); int max_bw = 100, max_dis = 100, max_skip_anchors = 25, max_non_best_anchors = 50, min_local_chain_score = 100; int min_w = abpt->min_w+abpt->k; int i_qpos, i_tpos, i_tstrand, j_qpos, j_tpos; for (i = st = 0; i < (int)anchors->n; ++i) { uint64_t ia = anchors->a[i]; i_qpos = (int32_t)ia, i_tpos = (ia >> 32) & 0x7fffffff, i_tstrand = ia >> 63; int max_j = -1, n_skip=0, non_best_iter_n = 0, max_score=abpt->k, _score; while (st < i) { uint64_t st_a = anchors->a[st]; if ((st_a >> 63) != i_tstrand || (int)((st_a >> 32) & 0x7fffffff) + max_dis < i_tpos) ++st; else break; } for (j = i-1; j >= st; --j) { // check if j is i's optimal pre anchor uint64_t ja = anchors->a[j]; j_qpos = (uint32_t)ja; j_tpos = (ja >> 32) & 0x7fffffff; if (j_qpos >= i_qpos || j_qpos + max_dis < i_qpos) continue; if (!get_chain_score(max_bw, &_score, i_qpos, i_tpos, j_qpos, j_tpos, abpt->k)) continue; _score += score[j]; if (_score > max_score) { max_score = _score; max_j = j; non_best_iter_n = 0; if (n_skip > 0) --n_skip; } else if (end_pos[j] == i) { if (++n_skip > max_skip_anchors) break; } else if (++non_best_iter_n > max_non_best_anchors) break; if (pre_id[j] >= 0) end_pos[pre_id[j]] = i; } #ifdef __DEBUG__ fprintf(stderr, "%d pre_id: %d, score: %d, tpos: %d, qpos: %d\n", i, max_j, max_score, i_tpos, i_qpos); #endif score[i] = max_score, pre_id[i] = max_j; } memset(end_pos, 0, n_a * 4); int n_local_chains = 0; for (i = n_a-1; i >= 0; --i) { if (pre_id[i] >= 0) end_pos[pre_id[i]] = 1; if (end_pos[i] == 0 && score[i] >= min_local_chain_score) { end_pos[i] = 2; ++n_local_chains; } } // collect local chains // x: score // y: e_a_i ab_u128_t *local_chains = (ab_u128_t*)kmalloc(km, n_local_chains * sizeof(ab_u128_t)); for (i = n_local_chains = 0; i < n_a; ++i) { if (end_pos[i] == 2) { local_chains[n_local_chains].x = score[i]; local_chains[n_local_chains++].y = i; } } radix_sort_ab_128x(local_chains, local_chains + n_local_chains); // collect local chains // x: strand | endpos | score // y: s_a_i | e_a_i int32_t *anchor_map = end_pos; memset(anchor_map, 0, n_a * 4); int start_id, end_id, tot_chain_i; uint64_t strand, tpos, qpos; for (i = tot_chain_i = n_local_chains-1; i >=0; --i) { j = local_chains[i].y; end_id = j; strand = anchors->a[i] >> 63; tpos = (anchors->a[j] >> 32) & 0x7fffffff, qpos = (int32_t)anchors->a[j]; do { start_id = j; anchor_map[j] = 1; j = pre_id[j]; } while (j >= 0 && anchor_map[j] == 0); if (j < 0) { // reach the start of the chain local_chains[tot_chain_i].x = strand << 63 | tpos << 32 | qpos; local_chains[tot_chain_i--].y = (uint64_t)end_id << 32 | start_id; } // not keep branched chains /*else if ((int32_t)local_chains[i].x - score[j] >= min_local_chain_score) { // anchor_map == 1, anchor was already used in other chain local_chains[tot_chain_i].x = strand << 63 | tpos << 32 | qpos; local_chains[tot_chain_i--].y = (uint64_t)end_id << 32 | ((int32_t)local_chains[i].x - score[j]); pre_id[start_id] = -1; }*/ } radix_sort_ab_128x(local_chains+tot_chain_i+1, local_chains + n_local_chains); abpoa_dp_chaining_of_local_chains(km, local_chains+tot_chain_i+1, n_local_chains-1-tot_chain_i, anchors, score, pre_id, par_anchors, min_w, tlen, qlen); kfree(km, score); kfree(km, pre_id); kfree(km, end_pos); kfree(km, local_chains); return 0; } int bin_search_min_larger(int *lis, int left, int right, int key) { int mid; while (right - left > 1) { mid = ((right - left) >> 1) + left; if (lis[mid] >= key) right = mid; else left = mid; } return right; } // rank: qpos<<32 | tpos_rank int LIS(void *km, int tot_n, uint64_t *rank, int n) { int *pre_rank = (int*)kcalloc(km, tot_n+1, sizeof(int)); int *lis = (int*)kmalloc(km, n * sizeof(int)); int i, n_lis, irank, idx; lis[0] = (uint32_t)rank[0]; n_lis = 1; // calculate LIS length for (i = 1; i < n; ++i) { irank = (uint32_t)rank[i]; if (irank < lis[0]) { lis[0] = irank; } else if (irank > lis[n_lis-1]) { lis[n_lis] = irank; pre_rank[irank] = lis[n_lis-1]; ++n_lis; } else { idx = bin_search_min_larger(lis, -1, n_lis-1, irank); lis[idx] = irank; if (idx > 0) pre_rank[irank] = lis[idx-1]; } } // collect LIS, store ids in rank int r = lis[n_lis-1]; i = n_lis-1; while (r != 0) { if (i < 0) err_fatal_simple("Error in LIS."); rank[i--] = r; r = pre_rank[r]; } kfree(km, pre_rank); kfree(km, lis); return n_lis; } // XXX TODO use dp-based chaining // XXX TODO remove q_span // Longest Increasing Subsequence-based Chaining (only works for global alignment mode) // input: // anchors: (sorted by tpos) // strand<<63 | tpos<<32 | qpos // output: // anchor list size: n // list of anchors: anchors int LIS_chaining(void *km, ab_u64_v *anchors, ab_u64_v *par_anchors, int min_w) { size_t i, j, n_a = anchors->n, n_for, n_rev; uint64_t *for_rank = (uint64_t*)kmalloc(km, sizeof(uint64_t) * n_a); uint64_t *rev_rank = (uint64_t*)kmalloc(km, sizeof(uint64_t) * n_a); uint64_t qpos; n_for = 0, n_rev = 0; for (i = 0; i < n_a; ++i) { uint64_t ia = anchors->a[i]; qpos = (uint32_t)ia; if (ia >> 63) { // reverse rev_rank[n_rev++] = qpos << 32 | (i+1); } else { // forward for_rank[n_for++] = qpos << 32 | (i+1); } } if (n_for > 0) { radix_sort_64(for_rank, for_rank + n_for); n_for = LIS(km, n_a, for_rank, n_for); } if (n_rev > 0) { radix_sort_64(rev_rank, rev_rank + n_rev); n_rev = LIS(km, n_a, rev_rank, n_rev); } size_t n; uint64_t *rank; if (n_for > n_rev) { n = n_for; rank = for_rank; kfree(km, rev_rank); } else { n = n_rev; rank = rev_rank; kfree(km, for_rank); } // filter anchors int last_tpos = -1, last_qpos = -1, cur_tpos, cur_qpos; #ifdef __DEBUG__ size_t _n = par_anchors->n; #endif for (i = 0; i < n; ++i) { j = (int)rank[i]-1; cur_tpos = (anchors->a[j] >> 32) & 0x7fffffff; if (cur_tpos - last_tpos < min_w) continue; cur_qpos = (uint32_t)anchors->a[j]; if (cur_qpos - last_qpos < min_w) continue; kv_push(uint64_t, 0, *par_anchors, anchors->a[j]); // store LIS anchors into par_anchors last_tpos = cur_tpos; last_qpos = cur_qpos; } #ifdef __DEBUG__ for (i = _n; i < par_anchors->n; ++i) { uint64_t ia = par_anchors->a[i]; // strand, rpos, qpos fprintf(stderr, "%c\t%ld\t%d\n", "+-"[ia >> 63], (ia>>32) & 0x7fffffff, ((uint32_t)ia)); } #endif return 0; } int abpoa_collect_mm(void *km, uint8_t **seqs, int *seq_lens, int n_seq, abpoa_para_t *abpt, ab_u128_v *mm, int *mm_c) { if (abpt->verbose > 0) fprintf(stderr, "[%s] Collecting minimizers ... ", __func__); int i; mm_c[0] = 0; for (i = 0; i < n_seq; ++i) { // collect minimizers if (abpt->m > 5) mm_aa_sketch(km, seqs[i], seq_lens[i], abpt->w, abpt->k, i, 0, mm); else mm_sketch(km, seqs[i], seq_lens[i], abpt->w, abpt->k, i, 0, abpt->amb_strand, mm); mm_c[i+1] = mm->n; } if (abpt->verbose > 0) fprintf(stderr, "done!\n"); return mm->n; } // split guide tree and seeding and partition int abpoa_build_guide_tree_partition(uint8_t **seqs, int *seq_lens, int n_seq, abpoa_para_t *abpt, int *read_id_map, ab_u64_v *par_anchors, int *par_c) { int i; void *km = km_init(); for (i = 0; i < n_seq; ++i) read_id_map[i] = i; ab_u128_v mm1 = {0, 0, 0}; int *mm_c = (int*)_err_malloc((n_seq+1) * sizeof(int)); abpoa_collect_mm(km, seqs, seq_lens, n_seq, abpt, &mm1, mm_c); if (abpt->progressive_poa && n_seq > 2) { // copy mm1 to mm2 ab_u128_v mm2 = {0, 0, 0}; for (i = 0; i < (int)mm1.n; ++i) kv_push(ab_u128_t, km, mm2, mm1.a[i]); // use mm2 to build guide tree abpoa_build_guide_tree(abpt, n_seq, &mm2, read_id_map); kfree(km, mm2.a); } if (abpt->disable_seeding || n_seq < 2) { kfree(km, mm1.a); free(mm_c); km_destroy(km); return 0; // no anchor } // partition into small windows int qid, tid; tid = read_id_map[0]; radix_sort_ab_128x(mm1.a + mm_c[tid], mm1.a + mm_c[tid+1]); par_c[0] = 0; for (i = 1; i < n_seq; ++i) { tid = read_id_map[i-1]; qid = read_id_map[i]; ab_u64_v anchors = {0, 0, 0}; // collect minimizer hit anchors between t and q collect_anchors1(km, &anchors, mm1, mm_c, tid, qid, seq_lens[qid], abpt->k); // filtering and only keep LIS anchors #ifdef __DEBUG__ fprintf(stderr, "%d vs %d (tot_n: %ld)\n", tid, qid, anchors.n); #endif // alignment mode: different chaining result for global/local/extend abpoa_dp_chaining(km, &anchors, par_anchors, abpt, seq_lens[tid], seq_lens[qid]); par_c[i] = par_anchors->n; kfree(km, anchors.a); } kfree(km, mm1.a); free(mm_c); km_destroy(km); return 0; // par_anchors->n; } abPOA-1.4.1/src/abpoa_seed.h000066400000000000000000000010261425041320700154560ustar00rootroot00000000000000#ifndef _ABPOA_SEED_H #define _ABPOA_SEED_H #include #include #include "abpoa.h" // emulate 128-bit integers and arrays typedef struct { uint64_t x, y; } ab_u128_t; typedef struct { size_t n, m; ab_u128_t *a; } ab_u128_v; typedef struct { size_t n, m; uint64_t *a; } ab_u64_v; #ifdef __cplusplus extern "C" { #endif int abpoa_build_guide_tree_partition(uint8_t **seqs, int *seq_lens, int n_seq, abpoa_para_t *abpt, int *read_id_map, ab_u64_v *par_anchors, int *par_c); #ifdef __cplusplus } #endif #endif abPOA-1.4.1/src/abpoa_seq.c000066400000000000000000000665651425041320700153440ustar00rootroot00000000000000#include #include #include #include "abpoa_seq.h" #include "abpoa_align.h" #include "abpoa_graph.h" #include "utils.h" #include "kstring.h" #include "khash.h" KHASH_MAP_INIT_STR(abstr, uint32_t) // for nt // AaCcGgTtNn ==> 0,1,2,3,4 unsigned char ab_nt4_table[256] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 /*'-'*/, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 1, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; // 65,97=>A, 67,99=>C, 71,103=>G, 84,85,116,117=>T, else=>N const char ab_nt256_table[256] = { 'A', 'C', 'G', 'T', 'N', '-', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', '-', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'A', 'N', 'C', 'N', 'N', 'N', 'G', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'T', 'T', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'A', 'N', 'C', 'N', 'N', 'N', 'G', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'T', 'T', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N' }; // for aa // AaCcGgTtNn ... ==> 0,1,2,3,4 ... // BbDdEeFf ... ==> 5,6,7,8 ... unsigned char ab_aa26_table[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 5, 1, 6, 7, 8, 2, 9, 10, 11, 12, 13, 14, 4, 15, 16, 17, 18, 19, 3, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, 0, 5, 1, 6, 7, 8, 2, 9, 10, 11, 12, 13, 14, 4, 15, 16, 17, 18, 19, 3, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26 }; // 0/1/2/3/4=>ACGTN // 5/6/7/8=>BDEF ... const char ab_aa256_table[256] = { 'A', 'C', 'G', 'T', 'N', 'B', 'D', 'E', 'F', 'H', 'I', 'J', 'K', 'L', 'M', 'O', 'P', 'Q', 'R', 'S', 'U', 'V', 'W', 'X', 'Y', 'Z', '*', '-', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '*', '*', '*', '*', '*', '*', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*' }; char ab_char26_table[256]; char ab_char256_table[256]; abpoa_seq_t *abpoa_init_seq(void) { abpoa_seq_t *abs = (abpoa_seq_t*)_err_malloc(sizeof(abpoa_seq_t)); abs->n_seq = 0; abs->m_seq = CHUNK_READ_N; abs->seq = (abpoa_str_t*)_err_calloc(abs->m_seq, sizeof(abpoa_str_t)); abs->name = (abpoa_str_t*)_err_calloc(abs->m_seq, sizeof(abpoa_str_t)); abs->comment = (abpoa_str_t*)_err_calloc(abs->m_seq, sizeof(abpoa_str_t)); abs->qual = (abpoa_str_t*)_err_calloc(abs->m_seq, sizeof(abpoa_str_t)); abs->is_rc = (uint8_t*)_err_calloc(abs->m_seq, sizeof(uint8_t)); return abs; } void abpoa_free_seq(abpoa_seq_t *abs) { int i; for (i = 0; i < abs->m_seq; ++i) { if (abs->seq[i].m > 0) free(abs->seq[i].s); if (abs->name[i].m > 0) free(abs->name[i].s); if (abs->comment[i].m > 0) free(abs->comment[i].s); if (abs->qual[i].m > 0) free(abs->qual[i].s); } free(abs->seq); free(abs->name); free(abs->comment); free(abs->qual); free(abs->is_rc); free(abs); } void abpoa_cpy_str(abpoa_str_t *str, char *s, int l) { if (l > 0) { str->l = l; str->m = l + 1; str->s = (char*)_err_malloc(str->m * sizeof(char)); memcpy(str->s, s, l); str->s[str->l] = 0; } } void abpoa_cpy_seq(abpoa_seq_t *abs, int seq_i, kseq_t *kseq) { abpoa_cpy_str(abs->seq+seq_i, kseq->seq.s, kseq->seq.l); abpoa_cpy_str(abs->name+seq_i, kseq->name.s, kseq->name.l); abpoa_cpy_str(abs->comment+seq_i, kseq->comment.s, kseq->comment.l); abpoa_cpy_str(abs->qual+seq_i, kseq->qual.s, kseq->qual.l); } abpoa_seq_t *abpoa_realloc_seq(abpoa_seq_t *abs) { if (abs->n_seq >= abs->m_seq) { int m_seq = MAX_OF_TWO(abs->n_seq, abs->m_seq << 1); abs->seq = (abpoa_str_t*)_err_realloc(abs->seq, m_seq * sizeof(abpoa_str_t)); abs->name = (abpoa_str_t*)_err_realloc(abs->name, m_seq * sizeof(abpoa_str_t)); abs->comment = (abpoa_str_t*)_err_realloc(abs->comment, m_seq * sizeof(abpoa_str_t)); abs->qual = (abpoa_str_t*)_err_realloc(abs->qual, m_seq * sizeof(abpoa_str_t)); abs->is_rc = (uint8_t*)_err_realloc(abs->is_rc, m_seq * sizeof(uint8_t)); int i; for (i = abs->m_seq; i < m_seq; ++i) { abs->seq[i].l = abs->seq[i].m = 0; abs->name[i].l = abs->name[i].m = 0; abs->comment[i].l = abs->comment[i].m = 0; abs->qual[i].l = abs->qual[i].m = 0; abs->is_rc[i] = 0; } abs->m_seq = m_seq; } return abs; } int abpoa_read_nseq(abpoa_seq_t *abs, kseq_t *kseq, int chunk_read_n) { int n = 0; while (n < chunk_read_n && kseq_read(kseq) >= 0) { abpoa_realloc_seq(abs); // copy kseq to abs->seq abpoa_cpy_seq(abs, abs->n_seq, kseq); abs->n_seq++; n++; } return n; } int abpoa_read_seq(abpoa_seq_t *abs, kseq_t *kseq) { int n = 0; while (kseq_read(kseq) >= 0) { abpoa_realloc_seq(abs); // copy kseq to abs->seq abpoa_cpy_seq(abs, abs->n_seq, kseq); abs->n_seq++; n++; } return n; } static long int _strtol10(const char *str, char **endptr) { long int res = 0; unsigned d; char *s; for (s = (char*)str, d = s[0]-'0'; d < 10; ++s, d = s[0]-'0') res = res * 10 + d; if (endptr) *endptr = s; return res; } static unsigned long int _strtoul10(const char *str, char **endptr) { unsigned long int res = 0; unsigned d; char *s; for (s = (char*)str, d = s[0]-'0'; d < 10; ++s, d = s[0]-'0') res = res * 10 + d; if (endptr) *endptr = s; return res; } int gfa_aux_parse(char *s, uint8_t **data, int *max) { char *q, *p; kstring_t str; if (s == 0) return 0; str.l = 0, str.m = *max, str.s = (char*)*data; if (*s == '\t') ++s; for (p = q = s;; ++p) { if (*p == 0 || *p == '\t') { int c = *p; *p = 0; if (p - q >= 5 && q[2] == ':' && q[4] == ':' && (q[3] == 'I' || q[3] == 'A' || q[3] == 'i' || q[3] == 'f' || q[3] == 'Z' || q[3] == 'B')) { int type = q[3]; kputsn_(q, 2, &str); q += 5; if (type == 'A') { kputc_('A', &str); kputc_(*q, &str); } else if (type == 'I') { uint32_t x; // x = strtol(q, &q, 10); x = _strtoul10(q, &q); kputc_(type, &str); kputsn_((char*)&x, 4, &str); } else if (type == 'i') { int32_t x; // x = strtol(q, &q, 10); x = _strtol10(q, &q); kputc_(type, &str); kputsn_((char*)&x, 4, &str); } else if (type == 'f') { float x; x = strtod(q, &q); kputc_('f', &str); kputsn_(&x, 4, &str); } else if (type == 'Z') { kputc_('Z', &str); kputsn_(q, p - q + 1, &str); // note that this include the trailing NULL } else if (type == 'B') { type = *q++; // q points to the first ',' following the typing byte if (p - q >= 2 && (type == 'c' || type == 'C' || type == 's' || type == 'S' || type == 'i' || type == 'I' || type != 'f')) { int32_t n; char *r; for (r = q, n = 0; *r; ++r) if (*r == ',') ++n; kputc_('B', &str); kputc_(type, &str); kputsn_(&n, 4, &str); // TODO: to evaluate which is faster: a) aligned array and then memmove(); b) unaligned array; c) kputsn_() if (type == 'c') while (q + 1 < p) { int8_t x = strtol(q + 1, &q, 0); kputc_(x, &str); } else if (type == 'C') while (q + 1 < p) { uint8_t x = strtol(q + 1, &q, 0); kputc_(x, &str); } else if (type == 's') while (q + 1 < p) { int16_t x = strtol(q + 1, &q, 0); kputsn_(&x, 2, &str); } else if (type == 'S') while (q + 1 < p) { uint16_t x = strtol(q + 1, &q, 0); kputsn_(&x, 2, &str); } // else if (type == 'i') while (q + 1 < p) { int32_t x = strtol(q + 1, &q, 0); kputsn_(&x, 4, &str); } else if (type == 'i') while (q + 1 < p) { int32_t x = _strtol10(q + 1, &q); kputsn_(&x, 4, &str); } // else if (type == 'I') while (q + 1 < p) { uint32_t x = strtol(q + 1, &q, 0); kputsn_(&x, 4, &str); } else if (type == 'I') while (q + 1 < p) { uint32_t x = _strtoul10(q + 1, &q); kputsn_(&x, 4, &str); } else if (type == 'f') while (q + 1 < p) { float x = strtod(q + 1, &q); kputsn_(&x, 4, &str); } } } // should not be here, as we have tested all types } q = p + 1; if (c == 0) break; } } if (str.l > 0 && str.l == str.m) ks_resize(&str, str.l + 1); if (str.s) str.s[str.l] = 0; *max = str.m, *data = (uint8_t*)str.s; return str.l; } static inline int gfa_aux_type2size(int x) { if (x == 'C' || x == 'c' || x == 'A') return 1; else if (x == 'S' || x == 's') return 2; else if (x == 'I' || x == 'i' || x == 'f') return 4; else return 0; } #define __skip_tag(s) do { \ int type = toupper(*(s)); \ ++(s); \ if (type == 'Z') { while (*(s)) ++(s); ++(s); } \ else if (type == 'B') (s) += 5 + gfa_aux_type2size(*(s)) * (*(int32_t*)((s)+1)); \ else (s) += gfa_aux_type2size(type); \ } while(0) uint8_t *gfa_aux_get(int l_data, const uint8_t *data, const char tag[2]) { const uint8_t *s = data; int y = tag[0]<<8 | tag[1]; while (s < data + l_data) { int x = (int)s[0]<<8 | s[1]; s += 2; if (x == y) return (uint8_t*)s; __skip_tag(s); } return 0; } int gfa_aux_del(int l_data, uint8_t *data, uint8_t *s) { uint8_t *p; p = s - 2; __skip_tag(s); memmove(p, s, l_data - (s - data)); return l_data - (s - p); } int abpoa_gfa_parse_H(abpoa_graph_t *abg, int *n_s, int *n_l, int *n_p, char *s) { if (s[1] != '\t' || s[2] == '0') return -1; int l_aux, m_aux = 0; uint8_t *aux = 0, *info; l_aux = gfa_aux_parse(s + 2, &aux, &m_aux); info = gfa_aux_get(l_aux, aux, "NS"); if (info == 0 || info[0] != 'i') err_fatal_simple("Error: no \"NS\" tag in GFA header."); *n_s = *(int32_t*)(info+1); abg->node_m = *n_s + 2; abg->node = (abpoa_node_t*)_err_realloc(abg->node, abg->node_m * sizeof(abpoa_node_t*)); l_aux = gfa_aux_del(l_aux, aux, info); info = gfa_aux_get(l_aux, aux, "NL"); if (info == 0 || info[0] != 'i') err_fatal_simple("Error: no \"NL\" tag in GFA header."); *n_l = *(int32_t*)(info+1); l_aux = gfa_aux_del(l_aux, aux, info); info = gfa_aux_get(l_aux, aux, "NP"); if (info == 0 || info[0] != 'i') err_fatal_simple("Error: no \"NP\" tag in GFA header."); *n_p = *(int32_t*)(info+1); l_aux = gfa_aux_del(l_aux, aux, info); if (aux) free(aux); return 0; } typedef struct { int n, m; kstring_t *seq, *name; khash_t(abstr) *h; } seg_seq_t; seg_seq_t *seg_seq_init(void) { seg_seq_t *s = (seg_seq_t*)_err_malloc(sizeof(seg_seq_t)); s->n = s->m = 0; s->seq = 0, s->name = 0; s->h = kh_init(abstr); return s; } seg_seq_t *seg_seq_realloc(seg_seq_t *r) { if (r->n >= r->m) { int m; if (r->m == 0) m = 1; else m = MAX_OF_TWO(r->n, (r->m) << 1); r->seq = (kstring_t*)_err_realloc(r->seq, m * sizeof(kstring_t)); r->name = (kstring_t*)_err_realloc(r->name, m * sizeof(kstring_t)); int i; for (i = r->m; i < m; ++i) { r->seq[i] = (kstring_t){0,0,0}; r->name[i] = (kstring_t){0,0,0}; } r->m = m; } return r; } void seg_seq_free(seg_seq_t *s) { if (s->m > 0) { int i; for (i = 0; i < s->m; ++i) { if (s->seq[i].m) free(s->seq[i].s); if (s->name[i].m) free(s->name[i].s); } free(s->seq); free(s->name); } kh_destroy(abstr, s->h); free(s); } int abpoa_gfa_parse_S(seg_seq_t *segs, char *s) { if (s[1] != '\t' || s[2] == '\0') return -1; char *deli_s, *info_s, *seq = 0; int i, seq_len, seg_name_len, is_ok = 0; char *seg_name=0; for (i = 0, deli_s = info_s = s + 2;; ++deli_s) { if (*deli_s == 0 || *deli_s == '\t') { int c = *deli_s; *deli_s = 0; if (i == 0) { seg_name = info_s; seg_name_len = deli_s - info_s; } else if (i == 1) { seq = info_s; seq_len = deli_s - info_s; is_ok = 1; break; } if (c == 0) break; ++i, info_s = deli_s + 1; } } if (is_ok) { seg_seq_realloc(segs); kputsn(seg_name, seg_name_len, segs->name+segs->n); kputsn(seq, seq_len, segs->seq+segs->n); int absent; khint_t pos = kh_put(abstr, segs->h, segs->name[segs->n].s, &absent); if (absent) kh_val(segs->h, pos) = segs->n; else err_fatal(__func__, "Duplicated chromosome: \"%s\".", seg_name); ++segs->n; } else err_fatal(__func__, "Error: no seq in GFA segment line (%s).", seg_name); return 0; } /*int abpoa_gfa_parse_S(abpoa_graph_t *abg, char *s) { if (s[1] != '\t' || s[2] == '\0') return -1; char *deli_s, *info_s, *seq = 0; int i, seq_len, is_ok = 0; char *seg_name=0; for (i = 0, deli_s = info_s = s + 2;; ++deli_s) { if (*deli_s == 0 || *deli_s == '\t') { int c = *deli_s; *deli_s = 0; if (i == 0) { seg_name = info_s; abpoa_realloc_seq(seg_names); abpoa_cpy_str(seg_names->name+seg_names->n_seq, seg_name, deli_s - info_s); seg_names->n_seq++; } else if (i == 1) { seq = info_s; seq_len = deli_s - info_s; is_ok = 1; break; } if (c == 0) break; ++i, info_s = deli_s + 1; } } if (is_ok) { int seg_id, absent; for (i = 0; i < seq_len; ++i) { seg_id = abpoa_add_graph_node(abg, ab_char26_table[(int)(seq[i])]); if (i == 0) { khint_t pos = kh_put(str, seg_name2in_id, seg_names->name[seg_names->n_seq-1].s, &absent); if (absent) kh_val(seg_name2in_id, pos) = seg_id; else err_fatal(__func__, "Error: duplicated seg name (%s).", seg_name); } if (i == seq_len-1) { khint_t pos = kh_put(str, seg_name2out_id, seg_names->name[seg_names->n_seq-1].s, &absent); if (absent) kh_val(seg_name2out_id, pos) = seg_id; else err_fatal(__func__, "Error: duplicated seg name (%s).", seg_name); } } } else err_fatal(__func__, "Error: no seq in GFA segment line (%s).", seg_name); return 0; }*/ int abpoa_gfa_parse_P(abpoa_graph_t *abg, abpoa_seq_t *abs, seg_seq_t *segs, int add_read_id, int p_i, int p_n, khash_t(abstr) *seg_name2in_id, khash_t(abstr) *seg_name2out_id, char *s) { if (s[1] != '\t' || s[2] == '\0') return -1; char *deli_s, *info_s, *path = 0; int i, is_ok = 0, is_rc = -1; char *path_name=0; int path_name_len=0; kstring_t *seg_seq, *seg_name; int read_ids_n = 1 + ((p_n-1) >> 6); for (i = 0, deli_s = info_s = s + 2;; ++deli_s) { if (*deli_s == 0 || *deli_s == '\t') { int c = *deli_s; *deli_s = 0; if (i == 0) { path_name = info_s; path_name_len = deli_s - info_s; } else if (i == 1) { path = info_s; is_ok = 1; break; } if (c == 0) break; ++i, info_s = deli_s + 1; } } if (is_ok) { char *deli_s, *info_s, *_seg_name; khint_t pos, seg_pos; int absent; int id, in_id=-1, out_id=-1, last_id = ABPOA_SRC_NODE_ID, next_id = ABPOA_SINK_NODE_ID; for (deli_s = info_s = path; ; ++deli_s) { if (*deli_s == '+') { if (is_rc == 1) err_fatal(__func__, "Error: path has both \'+\' and \'-\' seg. (%s)", path_name); is_rc = 0; *deli_s = 0; _seg_name = info_s; seg_pos = kh_get(abstr, segs->h, _seg_name); if (seg_pos == kh_end(segs->h)) err_fatal(__func__, "Error: seg (%s) not exist.", info_s); seg_name = segs->name + kh_val(segs->h, seg_pos); seg_seq = segs->seq + kh_val(segs->h, seg_pos); // check if seg already exist pos = kh_put(abstr, seg_name2in_id, seg_name->s, &absent); if (absent) { // add node for seg_seq for (i = 0; i < (int)seg_seq->l; ++i) { id = abpoa_add_graph_node(abg, ab_char26_table[(int)(seg_seq->s[i])]); if (i == 0) in_id = id; if (i == (int)seg_seq->l-1) out_id = id; } kh_val(seg_name2in_id, pos) = in_id; pos = kh_put(abstr, seg_name2out_id, seg_name->s, &absent); kh_val(seg_name2out_id, pos) = out_id; } else { in_id = kh_val(seg_name2in_id, pos); pos = kh_put(abstr, seg_name2out_id, seg_name->s, &absent); out_id = kh_val(seg_name2out_id, pos); } // add edge abpoa_add_graph_edge(abg, last_id, in_id, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); if (in_id < out_id) { for (i = 0; i < out_id - in_id; ++i) abpoa_add_graph_edge(abg, in_id+i, in_id+i+1, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); } else if (in_id > out_id) err_fatal(__func__, "Error: in_id (%d) > out_id (%d).", in_id, out_id); last_id = out_id; info_s = deli_s + 2; } else if (*deli_s == '-') { if (is_rc == 0) err_fatal(__func__, "Error: path has both \'+\' and \'-\' seg. (%s)", path_name); is_rc = 1; *deli_s = 0; _seg_name = info_s; seg_pos = kh_get(abstr, segs->h, _seg_name); if (seg_pos == kh_end(segs->h)) err_fatal(__func__, "Error: seg (%s) not exist.", info_s); seg_name = segs->name + kh_val(segs->h, seg_pos); seg_seq = segs->seq + kh_val(segs->h, seg_pos); // check if seg exist pos = kh_put(abstr, seg_name2in_id, seg_name->s, &absent); if (absent) { // add node for seg_seq for (i = 0; i < (int)seg_seq->l; ++i) { id = abpoa_add_graph_node(abg, ab_char26_table[(int)(seg_seq->s[i])]); if (i == 0) in_id = id; if (i == (int)seg_seq->l-1) out_id = id; } kh_val(seg_name2in_id, pos) = in_id; pos = kh_put(abstr, seg_name2out_id, seg_name->s, &absent); kh_val(seg_name2out_id, pos) = out_id; } else { in_id = kh_val(seg_name2in_id, pos); out_id = kh_val(seg_name2out_id, pos); } // add edge abpoa_add_graph_edge(abg, out_id, next_id, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); if (in_id < out_id) { for (i = 0; i < out_id - in_id; ++i) abpoa_add_graph_edge(abg, in_id+i, in_id+i+1, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); } else if (in_id > out_id) err_fatal(__func__, "Error: in_id (%d) > out_id (%d).", in_id, out_id); next_id = in_id; info_s = deli_s + 2; } else if (*deli_s == 0 || *deli_s == '\t') break; } if (is_rc) abpoa_add_graph_edge(abg, ABPOA_SRC_NODE_ID, next_id, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); else abpoa_add_graph_edge(abg, last_id, ABPOA_SINK_NODE_ID, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); // set abs abpoa_realloc_seq(abs); abpoa_cpy_str(abs->name+abs->n_seq, path_name, path_name_len); abs->is_rc[abs->n_seq] = is_rc; abs->n_seq++; } else err_fatal(__func__, "Error: no path in GFA path line (%s).", path_name); return 0; } int abpoa_fa_parse_seq(abpoa_graph_t *abg, abpoa_seq_t *abs, kstring_t *seq, kstring_t *name, int add_read_id, int p_i, int p_n, int **rank2node_id) { if (*rank2node_id == 0) { *rank2node_id = (int*)_err_calloc(seq->l, sizeof(int)); } char *s = seq->s; int32_t read_ids_n = 1 + ((p_n-1) >> 6); int32_t i, rank, last_id = ABPOA_SRC_NODE_ID, cur_id, aln_id; uint8_t base; for (i = 0; s[i]; ++i) { if (s[i] == '-') continue; // gap else { base = ab_char26_table[(int)(s[i])]; rank = i; cur_id = (*rank2node_id)[rank]; if (cur_id == 0) { cur_id = abpoa_add_graph_node(abg, base); (*rank2node_id)[rank] = cur_id; } else { if (abg->node[cur_id].base != base) { aln_id = abpoa_get_aligned_id(abg, cur_id, base); if (aln_id == -1) { aln_id = abpoa_add_graph_node(abg, base); abpoa_add_graph_aligned_node(abg, cur_id, aln_id); } cur_id = aln_id; } } abpoa_add_graph_edge(abg, last_id, cur_id, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); last_id = cur_id; } } abpoa_add_graph_edge(abg, last_id, ABPOA_SINK_NODE_ID, 1, 1, add_read_id, 0, p_i, read_ids_n, p_n); abpoa_realloc_seq(abs); abpoa_cpy_str(abs->name + abs->n_seq, name->s, name->l); abs->n_seq++; return 0; } abpoa_t *abpoa_restore_graph(abpoa_t *ab, abpoa_para_t *abpt) { char *fn = abpt->incr_fn; if (fn == NULL) return ab; gzFile fp = fn && strcmp(fn, "-")? gzopen(fn, "r") : gzdopen(0, "r"); if (fp == 0) return NULL; kstream_t *ks = ks_init(fp); kstring_t s={0,0,0}; int dret, line_n=0, read_name_e; seg_seq_t *seqs = seg_seq_init(); khash_t(abstr) *seg_name2in_id = kh_init(abstr), *seg_name2out_id = kh_init(abstr); int add_read_id = abpt->use_read_ids; int p_i = -1, is_fa = 0, *rank2node_id=0; abpoa_graph_t *abg = ab->abg; abpoa_seq_t *abs = ab->abs; while (ks_getuntil(ks, KS_SEP_LINE, &s, &dret) >= 0) { line_n++; int ret = 0; if (is_fa) { if (s.l > 0 && s.s[0] == '>') { // name // parse_seq if (seqs->seq[seqs->n].l > 0) { ret = abpoa_fa_parse_seq(abg, abs, seqs->seq+seqs->n, seqs->name+seqs->n, add_read_id, p_i, p_i+1, &rank2node_id); seqs->n++; } // kputsn seqs->name read_name_e = 1; while (read_name_e < (int)s.l && !isspace(s.s[read_name_e])) read_name_e++; seg_seq_realloc(seqs); kputsn(s.s+1, read_name_e-1, seqs->name + seqs->n); p_i++; } else { // seq // kputsn seqs->seq kputsn(s.s, s.l, seqs->seq + seqs->n); } } else { if (s.l > 0 && s.s[0] == '>') { read_name_e = 1; while (read_name_e < (int)s.l && !isspace(s.s[read_name_e])) read_name_e++; seg_seq_realloc(seqs); kputsn(s.s+1, read_name_e-1, seqs->name + seqs->n); is_fa = 1; p_i++; } // else if (s.l < 2 || s.s[0] == '#') continue; // comment // else if (s.s[0] == 'H') ret = abpoa_gfa_parse_H(abg, &s_n, &l_n, &p_n, s.s); else if (s.s[0] == 'S') ret = abpoa_gfa_parse_S(seqs, s.s); // include Link information // else if (s.s[0] == 'L') ret = abpoa_gfa_parse_L(abg, seg_name, s.s); else if (s.s[0] == 'P') { p_i++; ret = abpoa_gfa_parse_P(abg, abs, seqs, add_read_id, p_i, p_i+1, seg_name2in_id, seg_name2out_id, s.s); } } if (ret < 0) err_fatal(__func__, "Error in %c-line at line %ld (error code %d)", s.s[0], (long)line_n, ret); } if (is_fa) { // last seq abpoa_fa_parse_seq(abg, abs, seqs->seq+seqs->n, seqs->name+seqs->n, add_read_id, p_i, p_i+1, &rank2node_id); seqs->n++; } if (s.m) free(s.s); ks_destroy(ks); gzclose(fp); seg_seq_free(seqs); kh_destroy(abstr, seg_name2in_id); kh_destroy(abstr, seg_name2out_id); if (rank2node_id) free(rank2node_id); if (abs->n_seq == 0) { err_func_printf(__func__, "Warning: no graph/sequence restored from file \'%s\'.\n", fn); abg->node_n = 2; } abg->is_called_cons = abg->is_set_msa_rank = abg->is_topological_sorted = 0; return ab; } abPOA-1.4.1/src/abpoa_seq.h000066400000000000000000000006601425041320700153310ustar00rootroot00000000000000#ifndef _ABPOA_SEQ_H #define _ABPOA_SEQ_H #include #include "abpoa.h" #include "kseq.h" KSEQ_INIT(gzFile, gzread) #ifdef __cplusplus extern "C" { #endif abpoa_seq_t *abpoa_realloc_seq(abpoa_seq_t *abs); void abpoa_cpy_str(abpoa_str_t *str, char *s, int l); abpoa_seq_t *abpoa_init_seq(void); void abpoa_free_seq(abpoa_seq_t *abs); int abpoa_read_seq(abpoa_seq_t *abs, kseq_t *kseq); #ifdef __cplusplus } #endif #endif abPOA-1.4.1/src/kalloc.c000066400000000000000000000165521425041320700146460ustar00rootroot00000000000000#include #include #include #include "kalloc.h" /* In kalloc, a *core* is a large chunk of contiguous memory. Each core is * associated with a master header, which keeps the size of the current core * and the pointer to next core. Kalloc allocates small *blocks* of memory from * the cores and organizes free memory blocks in a circular single-linked list. * * In the following diagram, "@" stands for the header of a free block (of type * header_t), "#" for the header of an allocated block (of type size_t), "-" * for free memory, and "+" for allocated memory. * * master This region is core 1. master This region is core 2. * | | * *@-------#++++++#++++++++++++@-------- *@----------#++++++++++++#+++++++@------------ * | | | | * p=p->ptr->ptr->ptr->ptr p->ptr p->ptr->ptr p->ptr->ptr->ptr */ typedef struct header_t { size_t size; struct header_t *ptr; } header_t; typedef struct { void *par; size_t min_core_size; header_t base, *loop_head, *core_head; /* base is a zero-sized block always kept in the loop */ } kmem_t; static void panic(const char *s) { fprintf(stderr, "%s\n", s); abort(); } void *km_init2(void *km_par, size_t min_core_size) { kmem_t *km; km = (kmem_t*)kcalloc(km_par, 1, sizeof(kmem_t)); km->par = km_par; km->min_core_size = min_core_size > 0? min_core_size : 0x80000; return (void*)km; } void *km_init(void) { return km_init2(0, 0); } void km_destroy(void *_km) { kmem_t *km = (kmem_t*)_km; void *km_par; header_t *p, *q; if (km == NULL) return; km_par = km->par; for (p = km->core_head; p != NULL;) { q = p->ptr; kfree(km_par, p); p = q; } kfree(km_par, km); } static header_t *morecore(kmem_t *km, size_t nu) { header_t *q; size_t bytes, *p; nu = (nu + 1 + (km->min_core_size - 1)) / km->min_core_size * km->min_core_size; /* the first +1 for core header */ bytes = nu * sizeof(header_t); q = (header_t*)kmalloc(km->par, bytes); if (!q) panic("[morecore] insufficient memory"); q->ptr = km->core_head, q->size = nu, km->core_head = q; p = (size_t*)(q + 1); *p = nu - 1; /* the size of the free block; -1 because the first unit is used for the core header */ kfree(km, p + 1); /* initialize the new "core"; NB: the core header is not looped. */ return km->loop_head; } void kfree(void *_km, void *ap) /* kfree() also adds a new core to the circular list */ { header_t *p, *q; kmem_t *km = (kmem_t*)_km; if (!ap) return; if (km == NULL) { free(ap); return; } p = (header_t*)((size_t*)ap - 1); p->size = *((size_t*)ap - 1); /* Find the pointer that points to the block to be freed. The following loop can stop on two conditions: * * a) "p>q && pptr": @------#++++++++#+++++++@------- @---------------#+++++++@------- * (can also be in | | | -> | | * two cores) q p q->ptr q q->ptr * * @-------- #+++++++++@-------- @-------- @------------------ * | | | -> | | * q p q->ptr q q->ptr * * b) "q>=q->ptr && (p>q || pptr)": @-------#+++++ @--------#+++++++ @-------#+++++ @---------------- * | | | -> | | * q->ptr q p q->ptr q * * #+++++++@----- #++++++++@------- @------------- #++++++++@------- * | | | -> | | * p q->ptr q q->ptr q */ for (q = km->loop_head; !(p > q && p < q->ptr); q = q->ptr) if (q >= q->ptr && (p > q || p < q->ptr)) break; if (p + p->size == q->ptr) { /* two adjacent blocks, merge p and q->ptr (the 2nd and 4th cases) */ p->size += q->ptr->size; p->ptr = q->ptr->ptr; } else if (p + p->size > q->ptr && q->ptr >= p) { panic("[kfree] The end of the allocated block enters a free block."); } else p->ptr = q->ptr; /* backup q->ptr */ if (q + q->size == p) { /* two adjacent blocks, merge q and p (the other two cases) */ q->size += p->size; q->ptr = p->ptr; km->loop_head = q; } else if (q + q->size > p && p >= q) { panic("[kfree] The end of a free block enters the allocated block."); } else km->loop_head = p, q->ptr = p; /* in two cores, cannot be merged; create a new block in the list */ } void *kmalloc(void *_km, size_t n_bytes) { kmem_t *km = (kmem_t*)_km; size_t n_units; header_t *p, *q; if (n_bytes == 0) return 0; if (km == NULL) return malloc(n_bytes); n_units = (n_bytes + sizeof(size_t) + sizeof(header_t) - 1) / sizeof(header_t); /* header+n_bytes requires at least this number of units */ if (!(q = km->loop_head)) /* the first time when kmalloc() is called, intialize it */ q = km->loop_head = km->base.ptr = &km->base; for (p = q->ptr;; q = p, p = p->ptr) { /* search for a suitable block */ if (p->size >= n_units) { /* p->size if the size of current block. This line means the current block is large enough. */ if (p->size == n_units) q->ptr = p->ptr; /* no need to split the block */ else { /* split the block. NB: memory is allocated at the end of the block! */ p->size -= n_units; /* reduce the size of the free block */ p += p->size; /* p points to the allocated block */ *(size_t*)p = n_units; /* set the size */ } km->loop_head = q; /* set the end of chain */ return (size_t*)p + 1; } if (p == km->loop_head) { /* then ask for more "cores" */ if ((p = morecore(km, n_units)) == 0) return 0; } } } void *kcalloc(void *_km, size_t count, size_t size) { kmem_t *km = (kmem_t*)_km; void *p; if (size == 0 || count == 0) return 0; if (km == NULL) return calloc(count, size); p = kmalloc(km, count * size); memset(p, 0, count * size); return p; } void *krealloc(void *_km, void *ap, size_t n_bytes) // TODO: this can be made more efficient in principle { kmem_t *km = (kmem_t*)_km; size_t cap, *p, *q; if (n_bytes == 0) { kfree(km, ap); return 0; } if (km == NULL) return realloc(ap, n_bytes); if (ap == NULL) return kmalloc(km, n_bytes); p = (size_t*)ap - 1; cap = (*p) * sizeof(header_t) - sizeof(size_t); if (cap >= n_bytes) return ap; /* TODO: this prevents shrinking */ q = (size_t*)kmalloc(km, n_bytes); memcpy(q, ap, cap); kfree(km, ap); return q; } void km_stat(const void *_km, km_stat_t *s) { kmem_t *km = (kmem_t*)_km; header_t *p; memset(s, 0, sizeof(km_stat_t)); if (km == NULL || km->loop_head == NULL) return; for (p = km->loop_head;; p = p->ptr) { s->available += p->size * sizeof(header_t); if (p->size != 0) ++s->n_blocks; /* &kmem_t::base is always one of the cores. It is zero-sized. */ if (p->ptr > p && p + p->size > p->ptr) panic("[km_stat] The end of a free block enters another free block."); if (p->ptr == km->loop_head) break; } for (p = km->core_head; p != NULL; p = p->ptr) { size_t size = p->size * sizeof(header_t); ++s->n_cores; s->capacity += size; s->largest = s->largest > size? s->largest : size; } } abPOA-1.4.1/src/kalloc.h000066400000000000000000000017161425041320700146470ustar00rootroot00000000000000#ifndef _KALLOC_H_ #define _KALLOC_H_ #include /* for size_t */ #ifdef __cplusplus extern "C" { #endif typedef struct { size_t capacity, available, n_blocks, n_cores, largest; } km_stat_t; void *kmalloc(void *km, size_t size); void *krealloc(void *km, void *ptr, size_t size); void *kcalloc(void *km, size_t count, size_t size); void kfree(void *km, void *ptr); void *km_init(void); void *km_init2(void *km_par, size_t min_core_size); void km_destroy(void *km); void km_stat(const void *_km, km_stat_t *s); #ifdef __cplusplus } #endif #define KMALLOC(km, ptr, len) ((ptr) = (__typeof__(ptr))kmalloc((km), (len) * sizeof(*(ptr)))) #define KCALLOC(km, ptr, len) ((ptr) = (__typeof__(ptr))kcalloc((km), (len), sizeof(*(ptr)))) #define KREALLOC(km, ptr, len) ((ptr) = (__typeof__(ptr))krealloc((km), (ptr), (len) * sizeof(*(ptr)))) #define KEXPAND(km, a, m) do { \ (m) = (m) >= 4? (m) + ((m)>>1) : 16; \ KREALLOC((km), (a), (m)); \ } while (0) #endif abPOA-1.4.1/src/kdq.h000066400000000000000000000102601425041320700141530ustar00rootroot00000000000000#ifndef __AC_KDQ_H #define __AC_KDQ_H #include #include #define __KDQ_TYPE(type) \ typedef struct { \ size_t front:58, bits:6, count, mask; \ type *a; \ } kdq_##type##_t; #define kdq_t(type) kdq_##type##_t #define kdq_size(q) ((q)->count) #define kdq_first(q) ((q)->a[(q)->front]) #define kdq_last(q) ((q)->a[((q)->front + (q)->count - 1) & (q)->mask]) #define kdq_at(q, i) ((q)->a[((q)->front + (i)) & (q)->mask]) #define __KDQ_IMPL(type, SCOPE) \ SCOPE kdq_##type##_t *kdq_init_##type() \ { \ kdq_##type##_t *q; \ q = (kdq_##type##_t*)calloc(1, sizeof(kdq_##type##_t)); \ q->bits = 2, q->mask = (1ULL<bits) - 1; \ q->a = (type*)malloc((1<bits) * sizeof(type)); \ return q; \ } \ SCOPE void kdq_destroy_##type(kdq_##type##_t *q) \ { \ if (q == 0) return; \ free(q->a); free(q); \ } \ SCOPE int kdq_resize_##type(kdq_##type##_t *q, int new_bits) \ { \ size_t new_size = 1ULL<bits; \ if (new_size < q->count) { /* not big enough */ \ int i; \ for (i = 0; i < 64; ++i) \ if (1ULL< q->count) break; \ new_bits = i, new_size = 1ULL<bits) return q->bits; /* unchanged */ \ if (new_bits > q->bits) q->a = (type*)realloc(q->a, (1ULL<front + q->count <= old_size) { /* unwrapped */ \ if (q->front + q->count > new_size) /* only happens for shrinking */ \ memmove(q->a, q->a + new_size, (q->front + q->count - new_size) * sizeof(type)); \ } else { /* wrapped */ \ memmove(q->a + (new_size - (old_size - q->front)), q->a + q->front, (old_size - q->front) * sizeof(type)); \ q->front = new_size - (old_size - q->front); \ } \ q->bits = new_bits, q->mask = (1ULL<bits) - 1; \ if (new_bits < q->bits) q->a = (type*)realloc(q->a, (1ULL<bits; \ } \ SCOPE type *kdq_pushp_##type(kdq_##type##_t *q) \ { \ if (q->count == 1ULL<bits) kdq_resize_##type(q, q->bits + 1); \ return &q->a[((q->count++) + q->front) & (q)->mask]; \ } \ SCOPE void kdq_push_##type(kdq_##type##_t *q, type v) \ { \ if (q->count == 1ULL<bits) kdq_resize_##type(q, q->bits + 1); \ q->a[((q->count++) + q->front) & (q)->mask] = v; \ } \ SCOPE type *kdq_unshiftp_##type(kdq_##type##_t *q) \ { \ if (q->count == 1ULL<bits) kdq_resize_##type(q, q->bits + 1); \ ++q->count; \ q->front = q->front? q->front - 1 : (1ULL<bits) - 1; \ return &q->a[q->front]; \ } \ SCOPE void kdq_unshift_##type(kdq_##type##_t *q, type v) \ { \ type *p; \ p = kdq_unshiftp_##type(q); \ *p = v; \ } \ SCOPE type *kdq_pop_##type(kdq_##type##_t *q) \ { \ return q->count? &q->a[((--q->count) + q->front) & q->mask] : 0; \ } \ SCOPE type *kdq_shift_##type(kdq_##type##_t *q) \ { \ type *d = 0; \ if (q->count == 0) return 0; \ d = &q->a[q->front++]; \ q->front &= q->mask; \ --q->count; \ return d; \ } #define KDQ_INIT2(type, SCOPE) \ __KDQ_TYPE(type) \ __KDQ_IMPL(type, SCOPE) #ifndef klib_unused #if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3) #define klib_unused __attribute__ ((__unused__)) #else #define klib_unused #endif #endif /* klib_unused */ #define KDQ_INIT(type) KDQ_INIT2(type, static inline klib_unused) #define KDQ_DECLARE(type) \ __KDQ_TYPE(type) \ kdq_##type##_t *kdq_init_##type(); \ void kdq_destroy_##type(kdq_##type##_t *q); \ int kdq_resize_##type(kdq_##type##_t *q, int new_bits); \ type *kdq_pushp_##type(kdq_##type##_t *q); \ void kdq_push_##type(kdq_##type##_t *q, type v); \ type *kdq_unshiftp_##type(kdq_##type##_t *q); \ void kdq_unshift_##type(kdq_##type##_t *q, type v); \ type *kdq_pop_##type(kdq_##type##_t *q); \ type *kdq_shift_##type(kdq_##type##_t *q); #define kdq_init(type) kdq_init_##type() #define kdq_destroy(type, q) kdq_destroy_##type(q) #define kdq_resize(type, q, new_bits) kdq_resize_##type(q, new_bits) #define kdq_pushp(type, q) kdq_pushp_##type(q) #define kdq_push(type, q, v) kdq_push_##type(q, v) #define kdq_pop(type, q) kdq_pop_##type(q) #define kdq_unshiftp(type, q) kdq_unshiftp_##type(q) #define kdq_unshift(type, q, v) kdq_unshift_##type(q, v) #define kdq_shift(type, q) kdq_shift_##type(q) #endif abPOA-1.4.1/src/khash.h000066400000000000000000000516051425041320700145020ustar00rootroot00000000000000/* The MIT License Copyright (c) 2008, 2009, 2011 by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* An example: #include "khash.h" KHASH_MAP_INIT_INT(32, char) int main() { int ret, is_missing; khiter_t k; khash_t(32) *h = kh_init(32); k = kh_put(32, h, 5, &ret); kh_value(h, k) = 10; k = kh_get(32, h, 10); is_missing = (k == kh_end(h)); k = kh_get(32, h, 5); kh_del(32, h, k); for (k = kh_begin(h); k != kh_end(h); ++k) if (kh_exist(h, k)) kh_value(h, k) = 1; kh_destroy(32, h); return 0; } */ /* 2013-05-02 (0.2.8): * Use quadratic probing. When the capacity is power of 2, stepping function i*(i+1)/2 guarantees to traverse each bucket. It is better than double hashing on cache performance and is more robust than linear probing. In theory, double hashing should be more robust than quadratic probing. However, my implementation is probably not for large hash tables, because the second hash function is closely tied to the first hash function, which reduce the effectiveness of double hashing. Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php 2011-12-29 (0.2.7): * Minor code clean up; no actual effect. 2011-09-16 (0.2.6): * The capacity is a power of 2. This seems to dramatically improve the speed for simple keys. Thank Zilong Tan for the suggestion. Reference: - http://code.google.com/p/ulib/ - http://nothings.org/computer/judy/ * Allow to optionally use linear probing which usually has better performance for random input. Double hashing is still the default as it is more robust to certain non-random input. * Added Wang's integer hash function (not used by default). This hash function is more robust to certain non-random input. 2011-02-14 (0.2.5): * Allow to declare global functions. 2009-09-26 (0.2.4): * Improve portability 2008-09-19 (0.2.3): * Corrected the example * Improved interfaces 2008-09-11 (0.2.2): * Improved speed a little in kh_put() 2008-09-10 (0.2.1): * Added kh_clear() * Fixed a compiling error 2008-09-02 (0.2.0): * Changed to token concatenation which increases flexibility. 2008-08-31 (0.1.2): * Fixed a bug in kh_get(), which has not been tested previously. 2008-08-31 (0.1.1): * Added destructor */ #ifndef __AC_KHASH_H #define __AC_KHASH_H /*! @header Generic hash table library. */ #define AC_VERSION_KHASH_H "0.2.8" #include #include #include #include "kalloc.h" /* compiler specific configuration */ #if UINT_MAX == 0xffffffffu typedef unsigned int khint32_t; #elif ULONG_MAX == 0xffffffffu typedef unsigned long khint32_t; #endif #if ULONG_MAX == ULLONG_MAX typedef unsigned long khint64_t; #else typedef unsigned long long khint64_t; #endif #ifndef kh_inline #ifdef _MSC_VER #define kh_inline __inline #else #define kh_inline inline #endif #endif /* kh_inline */ #ifndef klib_unused #if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3) #define klib_unused __attribute__ ((__unused__)) #else #define klib_unused #endif #endif /* klib_unused */ typedef khint32_t khint_t; typedef khint_t khiter_t; #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) #define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) #define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) #define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) #define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) #define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) #define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) #ifndef kroundup32 #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #endif static const double __ac_HASH_UPPER = 0.77; #define __KHASH_TYPE(name, khkey_t, khval_t) \ typedef struct kh_##name##_s { \ khint_t n_buckets, size, n_occupied, upper_bound; \ khint32_t *flags; \ khkey_t *keys; \ khval_t *vals; \ } kh_##name##_t; #define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ extern kh_##name##_t *kh_init_##name(void); \ extern void kh_destroy_##name(kh_##name##_t *h); \ extern void kh_clear_##name(kh_##name##_t *h); \ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ extern void kh_del_##name(kh_##name##_t *h, khint_t x); #define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ SCOPE kh_##name##_t *kh_init_##name(void) { \ return (kh_##name##_t*)kcalloc(0, 1, sizeof(kh_##name##_t)); \ } \ SCOPE void kh_destroy_##name(kh_##name##_t *h) \ { \ if (h) { \ kfree(0, (void *)h->keys); kfree(0, h->flags); \ kfree(0, (void *)h->vals); \ kfree(0, h); \ } \ } \ SCOPE void kh_clear_##name(kh_##name##_t *h) \ { \ if (h && h->flags) { \ memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ h->size = h->n_occupied = 0; \ } \ } \ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ { \ if (h->n_buckets) { \ khint_t k, i, last, mask, step = 0; \ mask = h->n_buckets - 1; \ k = __hash_func(key); i = k & mask; \ last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ i = (i + (++step)) & mask; \ if (i == last) return h->n_buckets; \ } \ return __ac_iseither(h->flags, i)? h->n_buckets : i; \ } else return 0; \ } \ SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ khint32_t *new_flags = 0; \ khint_t j = 1; \ { \ kroundup32(new_n_buckets); \ if (new_n_buckets < 4) new_n_buckets = 4; \ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ else { /* hash table size to be changed (shrink or expand); rehash */ \ new_flags = (khint32_t*)kmalloc(0, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ if (!new_flags) return -1; \ memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ if (h->n_buckets < new_n_buckets) { /* expand */ \ khkey_t *new_keys = (khkey_t*)krealloc(0, (void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ if (!new_keys) { kfree(0, new_flags); return -1; } \ h->keys = new_keys; \ if (kh_is_map) { \ khval_t *new_vals = (khval_t*)krealloc(0, (void *)h->vals, new_n_buckets * sizeof(khval_t)); \ if (!new_vals) { kfree(0, new_flags); return -1; } \ h->vals = new_vals; \ } \ } /* otherwise shrink */ \ } \ } \ if (j) { /* rehashing is needed */ \ for (j = 0; j != h->n_buckets; ++j) { \ if (__ac_iseither(h->flags, j) == 0) { \ khkey_t key = h->keys[j]; \ khval_t val; \ khint_t new_mask; \ new_mask = new_n_buckets - 1; \ if (kh_is_map) val = h->vals[j]; \ __ac_set_isdel_true(h->flags, j); \ while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ khint_t k, i, step = 0; \ k = __hash_func(key); \ i = k & new_mask; \ while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ __ac_set_isempty_false(new_flags, i); \ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ } else { /* write the element and jump out of the loop */ \ h->keys[i] = key; \ if (kh_is_map) h->vals[i] = val; \ break; \ } \ } \ } \ } \ if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ h->keys = (khkey_t*)krealloc(0, (void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ if (kh_is_map) h->vals = (khval_t*)krealloc(0, (void *)h->vals, new_n_buckets * sizeof(khval_t)); \ } \ kfree(0, h->flags); /* free the working space */ \ h->flags = new_flags; \ h->n_buckets = new_n_buckets; \ h->n_occupied = h->size; \ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ } \ return 0; \ } \ SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ { \ khint_t x; \ if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ if (h->n_buckets > (h->size<<1)) { \ if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ *ret = -1; return h->n_buckets; \ } \ } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ *ret = -1; return h->n_buckets; \ } \ } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ { \ khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ else { \ last = i; \ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ if (__ac_isdel(h->flags, i)) site = i; \ i = (i + (++step)) & mask; \ if (i == last) { x = site; break; } \ } \ if (x == h->n_buckets) { \ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ else x = i; \ } \ } \ } \ if (__ac_isempty(h->flags, x)) { /* not present at all */ \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; ++h->n_occupied; \ *ret = 1; \ } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ h->keys[x] = key; \ __ac_set_isboth_false(h->flags, x); \ ++h->size; \ *ret = 2; \ } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ return x; \ } \ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ { \ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ __ac_set_isdel_true(h->flags, x); \ --h->size; \ } \ } #define KHASH_DECLARE(name, khkey_t, khval_t) \ __KHASH_TYPE(name, khkey_t, khval_t) \ __KHASH_PROTOTYPES(name, khkey_t, khval_t) #define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ __KHASH_TYPE(name, khkey_t, khval_t) \ __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) /* --- BEGIN OF HASH FUNCTIONS --- */ /*! @function @abstract Integer hash function @param key The integer [khint32_t] @return The hash value [khint_t] */ #define kh_int_hash_func(key) (khint32_t)(key) /*! @function @abstract Integer comparison function */ #define kh_int_hash_equal(a, b) ((a) == (b)) /*! @function @abstract 64-bit integer hash function @param key The integer [khint64_t] @return The hash value [khint_t] */ #define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) /*! @function @abstract 64-bit integer comparison function */ #define kh_int64_hash_equal(a, b) ((a) == (b)) /*! @function @abstract const char* hash function @param s Pointer to a null terminated string @return The hash value */ static kh_inline khint_t __ac_X31_hash_string(const char *s) { khint_t h = (khint_t)*s; if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; return h; } /*! @function @abstract Another interface to const char* hash function @param key Pointer to a null terminated string [const char*] @return The hash value [khint_t] */ #define kh_str_hash_func(key) __ac_X31_hash_string(key) /*! @function @abstract Const char* comparison function */ #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) static kh_inline khint_t __ac_Wang_hash(khint_t key) { key += ~(key << 15); key ^= (key >> 10); key += (key << 3); key ^= (key >> 6); key += ~(key << 11); key ^= (key >> 16); return key; } #define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key) /* --- END OF HASH FUNCTIONS --- */ /* Other convenient macros... */ /*! @abstract Type of the hash table. @param name Name of the hash table [symbol] */ #define khash_t(name) kh_##name##_t /*! @function @abstract Initiate a hash table. @param name Name of the hash table [symbol] @return Pointer to the hash table [khash_t(name)*] */ #define kh_init(name) kh_init_##name() /*! @function @abstract Destroy a hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] */ #define kh_destroy(name, h) kh_destroy_##name(h) /*! @function @abstract Reset a hash table without deallocating memory. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] */ #define kh_clear(name, h) kh_clear_##name(h) /*! @function @abstract Resize a hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param s New size [khint_t] */ #define kh_resize(name, h, s) kh_resize_##name(h, s) /*! @function @abstract Insert a key to the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Key [type of keys] @param r Extra return code: -1 if the operation failed; 0 if the key is present in the hash table; 1 if the bucket is empty (never used); 2 if the element in the bucket has been deleted [int*] @return Iterator to the inserted element [khint_t] */ #define kh_put(name, h, k, r) kh_put_##name(h, k, r) /*! @function @abstract Retrieve a key from the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Key [type of keys] @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] */ #define kh_get(name, h, k) kh_get_##name(h, k) /*! @function @abstract Remove a key from the hash table. @param name Name of the hash table [symbol] @param h Pointer to the hash table [khash_t(name)*] @param k Iterator to the element to be deleted [khint_t] */ #define kh_del(name, h, k) kh_del_##name(h, k) /*! @function @abstract Test whether a bucket contains data. @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return 1 if containing data; 0 otherwise [int] */ #define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) /*! @function @abstract Get key given an iterator @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return Key [type of keys] */ #define kh_key(h, x) ((h)->keys[x]) /*! @function @abstract Get value given an iterator @param h Pointer to the hash table [khash_t(name)*] @param x Iterator to the bucket [khint_t] @return Value [type of values] @discussion For hash sets, calling this results in segfault. */ #define kh_val(h, x) ((h)->vals[x]) /*! @function @abstract Alias of kh_val() */ #define kh_value(h, x) ((h)->vals[x]) /*! @function @abstract Get the start iterator @param h Pointer to the hash table [khash_t(name)*] @return The start iterator [khint_t] */ #define kh_begin(h) (khint_t)(0) /*! @function @abstract Get the end iterator @param h Pointer to the hash table [khash_t(name)*] @return The end iterator [khint_t] */ #define kh_end(h) ((h)->n_buckets) /*! @function @abstract Get the number of elements in the hash table @param h Pointer to the hash table [khash_t(name)*] @return Number of elements in the hash table [khint_t] */ #define kh_size(h) ((h)->size) /*! @function @abstract Get the number of buckets in the hash table @param h Pointer to the hash table [khash_t(name)*] @return Number of buckets in the hash table [khint_t] */ #define kh_n_buckets(h) ((h)->n_buckets) /*! @function @abstract Iterate over the entries in the hash table @param h Pointer to the hash table [khash_t(name)*] @param kvar Variable to which key will be assigned @param vvar Variable to which value will be assigned @param code Block of code to execute */ #define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ if (!kh_exist(h,__i)) continue; \ (kvar) = kh_key(h,__i); \ (vvar) = kh_val(h,__i); \ code; \ } } /*! @function @abstract Iterate over the values in the hash table @param h Pointer to the hash table [khash_t(name)*] @param vvar Variable to which value will be assigned @param code Block of code to execute */ #define kh_foreach_value(h, vvar, code) { khint_t __i; \ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ if (!kh_exist(h,__i)) continue; \ (vvar) = kh_val(h,__i); \ code; \ } } /* More conenient interfaces */ /*! @function @abstract Instantiate a hash set containing integer keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_INT(name) \ KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) /*! @function @abstract Instantiate a hash map containing integer keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_INT(name, khval_t) \ KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) /*! @function @abstract Instantiate a hash map containing 64-bit integer keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_INT64(name) \ KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) /*! @function @abstract Instantiate a hash map containing 64-bit integer keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_INT64(name, khval_t) \ KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) typedef const char *kh_cstr_t; /*! @function @abstract Instantiate a hash map containing const char* keys @param name Name of the hash table [symbol] */ #define KHASH_SET_INIT_STR(name) \ KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) /*! @function @abstract Instantiate a hash map containing const char* keys @param name Name of the hash table [symbol] @param khval_t Type of values [type] */ #define KHASH_MAP_INIT_STR(name, khval_t) \ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) #endif /* __AC_KHASH_H */ abPOA-1.4.1/src/kseq.h000066400000000000000000000220201425041320700143340ustar00rootroot00000000000000/* The MIT License Copyright (c) 2008, 2009, 2011 Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Last Modified: 05MAR2012 */ #ifndef AC_KSEQ_H #define AC_KSEQ_H #include #include #include #ifdef USE_MALLOC_WRAPPERS # include "malloc_wrap.h" #endif #define KS_SEP_SPACE 0 // isspace(): \t, \n, \v, \f, \r #define KS_SEP_TAB 1 // isspace() && !' ' #define KS_SEP_LINE 2 // line separator: "\n" (Unix) or "\r\n" (Windows) #define KS_SEP_MAX 2 #define __KS_TYPE(type_t) \ typedef struct __kstream_t { \ unsigned char *buf; \ int begin, end, is_eof, last_char; \ type_t f; \ } kstream_t; #define ks_eof(ks) ((ks)->is_eof && (ks)->begin >= (ks)->end) #define ks_rewind(ks) ((ks)->is_eof = (ks)->begin = (ks)->end = 0) #define __KS_BASIC(type_t, __bufsize) \ static inline kstream_t *ks_init(type_t f) \ { \ kstream_t *ks = (kstream_t*)calloc(1, sizeof(kstream_t)); \ ks->f = f; \ ks->buf = (unsigned char*)malloc(__bufsize); \ return ks; \ } \ static inline void ks_destroy(kstream_t *ks) \ { \ if (ks) { \ free(ks->buf); \ free(ks); \ } \ } #define __KS_GETC(__read, __bufsize) \ static inline int ks_getc(kstream_t *ks) \ { \ if (ks->is_eof && ks->begin >= ks->end) return -1; \ if (ks->begin >= ks->end) { \ ks->begin = 0; \ ks->end = __read(ks->f, ks->buf, __bufsize); \ if (ks->end == 0) { ks->is_eof = 1; return -1;} \ } \ return (int)ks->buf[ks->begin++]; \ } #ifndef KSTRING_T #define KSTRING_T kstring_t typedef struct __kstring_t { size_t l, m; char *s; } kstring_t; #endif #ifndef kroundup32 #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #endif #define __KS_GETUNTIL(__read, __bufsize) \ static int ks_getuntil2(kstream_t *ks, int delimiter, kstring_t *str, int *dret, int append) \ { \ int gotany = 0; \ if (dret) *dret = 0; \ str->l = append? str->l : 0; \ for (;;) { \ int i; \ if (ks->begin >= ks->end) { \ if (!ks->is_eof) { \ ks->begin = 0; \ ks->end = __read(ks->f, ks->buf, __bufsize); \ if (ks->end == 0) { ks->is_eof = 1; break; } \ } else break; \ } \ if (delimiter == KS_SEP_LINE) { \ for (i = ks->begin; i < ks->end; ++i) \ if (ks->buf[i] == '\n') break; \ } else if (delimiter > KS_SEP_MAX) { \ for (i = ks->begin; i < ks->end; ++i) \ if (ks->buf[i] == delimiter) break; \ } else if (delimiter == KS_SEP_SPACE) { \ for (i = ks->begin; i < ks->end; ++i) \ if (isspace(ks->buf[i])) break; \ } else if (delimiter == KS_SEP_TAB) { \ for (i = ks->begin; i < ks->end; ++i) \ if (isspace(ks->buf[i]) && ks->buf[i] != ' ') break; \ } else i = 0; /* never come to here! */ \ if (str->m - str->l < (size_t)(i - ks->begin + 1)) { \ str->m = str->l + (i - ks->begin) + 1; \ kroundup32(str->m); \ str->s = (char*)realloc(str->s, str->m); \ } \ gotany = 1; \ memcpy(str->s + str->l, ks->buf + ks->begin, i - ks->begin); \ str->l = str->l + (i - ks->begin); \ ks->begin = i + 1; \ if (i < ks->end) { \ if (dret) *dret = ks->buf[i]; \ break; \ } \ } \ if (!gotany && ks_eof(ks)) return -1; \ if (str->s == 0) { \ str->m = 1; \ str->s = (char*)calloc(1, 1); \ } else if (delimiter == KS_SEP_LINE && str->l > 1 && str->s[str->l-1] == '\r') --str->l; \ str->s[str->l] = '\0'; \ return str->l; \ } \ static inline int ks_getuntil(kstream_t *ks, int delimiter, kstring_t *str, int *dret) \ { return ks_getuntil2(ks, delimiter, str, dret, 0); } #define KSTREAM_INIT(type_t, __read, __bufsize) \ __KS_TYPE(type_t) \ __KS_BASIC(type_t, __bufsize) \ __KS_GETC(__read, __bufsize) \ __KS_GETUNTIL(__read, __bufsize) #define kseq_rewind(ks) ((ks)->f->last_char = (ks)->f->is_eof = (ks)->f->begin = (ks)->f->end = 0) #define __KSEQ_BASIC(SCOPE, type_t) \ SCOPE kseq_t *kseq_init(type_t fd) \ { \ kseq_t *s = (kseq_t*)calloc(1, sizeof(kseq_t)); \ s->f = ks_init(fd); \ return s; \ } \ SCOPE void kseq_destroy(kseq_t *ks) \ { \ if (!ks) return; \ free(ks->name.s); free(ks->comment.s); free(ks->seq.s); free(ks->qual.s); \ ks_destroy(ks->f); \ free(ks); \ } /* Return value: >=0 length of the sequence (normal) -1 end-of-file -2 truncated quality string */ #define __KSEQ_READ(SCOPE) \ SCOPE int kseq_read(kseq_t *seq) \ { \ int c; \ kstream_t *ks = seq->f; \ if (ks->last_char == 0) { /* then jump to the next header line */ \ while ((c = ks_getc(ks)) != -1 && c != '>' && c != '@'); \ if (c == -1) return -1; /* end of file */ \ ks->last_char = c; \ } /* else: the first header char has been read in the previous call */ \ seq->comment.l = seq->seq.l = seq->qual.l = 0; /* reset all members */ \ if (ks_getuntil(ks, 0, &seq->name, &c) < 0) return -1; /* normal exit: EOF */ \ if (c != '\n') ks_getuntil(ks, KS_SEP_LINE, &seq->comment, 0); /* read FASTA/Q comment */ \ if (seq->seq.s == 0) { /* we can do this in the loop below, but that is slower */ \ seq->seq.m = 256; \ seq->seq.s = (char*)malloc(seq->seq.m); \ } \ while ((c = ks_getc(ks)) != -1 && c != '>' && c != '+' && c != '@') { \ if (c == '\n') continue; /* skip empty lines */ \ seq->seq.s[seq->seq.l++] = c; /* this is safe: we always have enough space for 1 char */ \ ks_getuntil2(ks, KS_SEP_LINE, &seq->seq, 0, 1); /* read the rest of the line */ \ } \ if (c == '>' || c == '@') ks->last_char = c; /* the first header char has been read */ \ if (seq->seq.l + 1 >= seq->seq.m) { /* seq->seq.s[seq->seq.l] below may be out of boundary */ \ seq->seq.m = seq->seq.l + 2; \ kroundup32(seq->seq.m); /* rounded to the next closest 2^k */ \ seq->seq.s = (char*)realloc(seq->seq.s, seq->seq.m); \ } \ seq->seq.s[seq->seq.l] = 0; /* null terminated string */ \ if (c != '+') return seq->seq.l; /* FASTA */ \ if (seq->qual.m < seq->seq.m) { /* allocate memory for qual in case insufficient */ \ seq->qual.m = seq->seq.m; \ seq->qual.s = (char*)realloc(seq->qual.s, seq->qual.m); \ } \ while ((c = ks_getc(ks)) != -1 && c != '\n'); /* skip the rest of '+' line */ \ if (c == -1) return -2; /* error: no quality string */ \ while (ks_getuntil2(ks, KS_SEP_LINE, &seq->qual, 0, 1) >= 0 && seq->qual.l < seq->seq.l); \ ks->last_char = 0; /* we have not come to the next header line */ \ if (seq->seq.l != seq->qual.l) return -2; /* error: qual string is of a different length */ \ return seq->seq.l; \ } #define __KSEQ_COPY(SCOPE) \ SCOPE void kseq_copy(kseq_t *seq, kseq_t kseq)\ { \ seq->name.s = strdup(kseq.name.s); \ seq->seq.s = strdup(kseq.seq.s); \ seq->seq.l = kseq.seq.l; \ } #define __KSEQ_TYPE(type_t) \ typedef struct { \ kstring_t name, comment, seq, qual; \ kstream_t *f; \ } kseq_t; #define KSEQ_INIT2(SCOPE, type_t, __read) \ KSTREAM_INIT(type_t, __read, 16384) \ __KSEQ_TYPE(type_t) \ __KSEQ_BASIC(SCOPE, type_t) \ __KSEQ_READ(SCOPE) \ __KSEQ_COPY(SCOPE) #define KSEQ_INIT(type_t, __read) KSEQ_INIT2(static, type_t, __read) #define KSEQ_DECLARE(type_t) \ __KS_TYPE(type_t) \ __KSEQ_TYPE(type_t) \ extern kseq_t *kseq_init(type_t fd); \ void kseq_destroy(kseq_t *ks); \ int kseq_read(kseq_t *seq); \ int kseq_copy(kseq_t *seq, kseq_t kseq); #endif abPOA-1.4.1/src/ksort.h000066400000000000000000000120311425041320700145340ustar00rootroot00000000000000/* The MIT License Copyright (c) 2008, 2011 Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // This is a simplified version of ksort.h #ifndef AC_KSORT_H #define AC_KSORT_H #include #include #include typedef struct { void *left, *right; int depth; } ks_isort_stack_t; #define KSORT_SWAP(type_t, a, b) { type_t t=(a); (a)=(b); (b)=t; } #define KSORT_INIT(name, type_t, __sort_lt) \ void ks_heapdown_##name(size_t i, size_t n, type_t l[]) \ { \ size_t k = i; \ type_t tmp = l[i]; \ while ((k = (k << 1) + 1) < n) { \ if (k != n - 1 && __sort_lt(l[k], l[k+1])) ++k; \ if (__sort_lt(l[k], tmp)) break; \ l[i] = l[k]; i = k; \ } \ l[i] = tmp; \ } \ void ks_heapmake_##name(size_t lsize, type_t l[]) \ { \ size_t i; \ for (i = (lsize >> 1) - 1; i != (size_t)(-1); --i) \ ks_heapdown_##name(i, lsize, l); \ } \ type_t ks_ksmall_##name(size_t n, type_t arr[], size_t kk) \ { \ type_t *low, *high, *k, *ll, *hh, *mid; \ low = arr; high = arr + n - 1; k = arr + kk; \ for (;;) { \ if (high <= low) return *k; \ if (high == low + 1) { \ if (__sort_lt(*high, *low)) KSORT_SWAP(type_t, *low, *high); \ return *k; \ } \ mid = low + (high - low) / 2; \ if (__sort_lt(*high, *mid)) KSORT_SWAP(type_t, *mid, *high); \ if (__sort_lt(*high, *low)) KSORT_SWAP(type_t, *low, *high); \ if (__sort_lt(*low, *mid)) KSORT_SWAP(type_t, *mid, *low); \ KSORT_SWAP(type_t, *mid, *(low+1)); \ ll = low + 1; hh = high; \ for (;;) { \ do ++ll; while (__sort_lt(*ll, *low)); \ do --hh; while (__sort_lt(*low, *hh)); \ if (hh < ll) break; \ KSORT_SWAP(type_t, *ll, *hh); \ } \ KSORT_SWAP(type_t, *low, *hh); \ if (hh <= k) low = ll; \ if (hh >= k) high = hh - 1; \ } \ } \ #define ks_ksmall(name, n, a, k) ks_ksmall_##name(n, a, k) #define ks_lt_generic(a, b) ((a) < (b)) #define ks_lt_str(a, b) (strcmp((a), (b)) < 0) typedef const char *ksstr_t; #define KSORT_INIT_GENERIC(type_t) KSORT_INIT(type_t, type_t, ks_lt_generic) #define KSORT_INIT_STR KSORT_INIT(str, ksstr_t, ks_lt_str) #define RS_MIN_SIZE 64 #define RS_MAX_BITS 8 #define KRADIX_SORT_INIT(name, rstype_t, rskey, sizeof_key) \ typedef struct { \ rstype_t *b, *e; \ } rsbucket_##name##_t; \ void rs_insertsort_##name(rstype_t *beg, rstype_t *end) \ { \ rstype_t *i; \ for (i = beg + 1; i < end; ++i) \ if (rskey(*i) < rskey(*(i - 1))) { \ rstype_t *j, tmp = *i; \ for (j = i; j > beg && rskey(tmp) < rskey(*(j-1)); --j) \ *j = *(j - 1); \ *j = tmp; \ } \ } \ void rs_sort_##name(rstype_t *beg, rstype_t *end, int n_bits, int s) \ { \ rstype_t *i; \ int size = 1<b = k->e = beg; \ for (i = beg; i != end; ++i) ++b[rskey(*i)>>s&m].e; \ for (k = b + 1; k != be; ++k) \ k->e += (k-1)->e - beg, k->b = (k-1)->e; \ for (k = b; k != be;) { \ if (k->b != k->e) { \ rsbucket_##name##_t *l; \ if ((l = b + (rskey(*k->b)>>s&m)) != k) { \ rstype_t tmp = *k->b, swap; \ do { \ swap = tmp; tmp = *l->b; *l->b++ = swap; \ l = b + (rskey(tmp)>>s&m); \ } while (l != k); \ *k->b++ = tmp; \ } else ++k->b; \ } else ++k; \ } \ for (b->b = beg, k = b + 1; k != be; ++k) k->b = (k-1)->e; \ if (s) { \ s = s > n_bits? s - n_bits : 0; \ for (k = b; k != be; ++k) \ if (k->e - k->b > RS_MIN_SIZE) rs_sort_##name(k->b, k->e, n_bits, s); \ else if (k->e - k->b > 1) rs_insertsort_##name(k->b, k->e); \ } \ } \ void radix_sort_##name(rstype_t *beg, rstype_t *end) \ { \ if (end - beg <= RS_MIN_SIZE) rs_insertsort_##name(beg, end); \ else rs_sort_##name(beg, end, RS_MAX_BITS, (sizeof_key - 1) * RS_MAX_BITS); \ } #endif abPOA-1.4.1/src/kstring.c000066400000000000000000000140511425041320700150520ustar00rootroot00000000000000#include #include #include #include #include #include "kstring.h" int kvsprintf(kstring_t *s, const char *fmt, va_list ap) { va_list args; int l; va_copy(args, ap); l = vsnprintf(s->s + s->l, s->m - s->l, fmt, args); // This line does not work with glibc 2.0. See `man snprintf'. va_end(args); if (l + 1 > s->m - s->l) { s->m = s->l + l + 2; kroundup32(s->m); s->s = (char*)realloc(s->s, s->m); va_copy(args, ap); l = vsnprintf(s->s + s->l, s->m - s->l, fmt, args); va_end(args); } s->l += l; return l; } int ksprintf(kstring_t *s, const char *fmt, ...) { va_list ap; int l; va_start(ap, fmt); l = kvsprintf(s, fmt, ap); va_end(ap); return l; } char *kstrtok(const char *str, const char *sep_in, ks_tokaux_t *aux) { const unsigned char *p, *start, *sep = (unsigned char *) sep_in; if (sep) { // set up the table if (str == 0 && aux->finished) return 0; // no need to set up if we have finished aux->finished = 0; if (sep[0] && sep[1]) { aux->sep = -1; aux->tab[0] = aux->tab[1] = aux->tab[2] = aux->tab[3] = 0; for (p = sep; *p; ++p) aux->tab[*p>>6] |= 1ull<<(*p&0x3f); } else aux->sep = sep[0]; } if (aux->finished) return 0; else if (str) start = (unsigned char *) str, aux->finished = 0; else start = (unsigned char *) aux->p + 1; if (aux->sep < 0) { for (p = start; *p; ++p) if (aux->tab[*p>>6]>>(*p&0x3f)&1) break; } else { for (p = start; *p; ++p) if (*p == aux->sep) break; } aux->p = (const char *) p; // end of token if (*p == 0) aux->finished = 1; // no more tokens return (char*)start; } // s MUST BE a null terminated string; l = strlen(s) int ksplit_core(char *s, int delimiter, int *_max, int **_offsets) { int i, n, max, last_char, last_start, *offsets, l; n = 0; max = *_max; offsets = *_offsets; l = strlen(s); #define __ksplit_aux do { \ if (_offsets) { \ s[i] = 0; \ if (n == max) { \ int *tmp; \ max = max? max<<1 : 2; \ if ((tmp = (int*)realloc(offsets, sizeof(int) * max))) { \ offsets = tmp; \ } else { \ free(offsets); \ *_offsets = NULL; \ return 0; \ } \ } \ offsets[n++] = last_start; \ } else ++n; \ } while (0) for (i = 0, last_char = last_start = 0; i <= l; ++i) { if (delimiter == 0) { if (isspace(s[i]) || s[i] == 0) { if (isgraph(last_char)) __ksplit_aux; // the end of a field } else { if (isspace(last_char) || last_char == 0) last_start = i; } } else { if (s[i] == delimiter || s[i] == 0) { if (last_char != 0 && last_char != delimiter) __ksplit_aux; // the end of a field } else { if (last_char == delimiter || last_char == 0) last_start = i; } } last_char = s[i]; } *_max = max; *_offsets = offsets; return n; } int kgetline(kstring_t *s, kgets_func *fgets_fn, void *fp) { size_t l0 = s->l; while (s->l == l0 || s->s[s->l-1] != '\n') { if (s->m - s->l < 200) ks_resize(s, s->m + 200); if (fgets_fn(s->s + s->l, s->m - s->l, fp) == NULL) break; s->l += strlen(s->s + s->l); } if (s->l == l0) return EOF; if (s->l > l0 && s->s[s->l-1] == '\n') { s->l--; if (s->l > l0 && s->s[s->l-1] == '\r') s->l--; } s->s[s->l] = '\0'; return 0; } /********************** * Boyer-Moore search * **********************/ typedef unsigned char ubyte_t; // reference: http://www-igm.univ-mlv.fr/~lecroq/string/node14.html static int *ksBM_prep(const ubyte_t *pat, int m) { int i, *suff, *prep, *bmGs, *bmBc; prep = (int*)calloc(m + 256, sizeof(int)); bmGs = prep; bmBc = prep + m; { // preBmBc() for (i = 0; i < 256; ++i) bmBc[i] = m; for (i = 0; i < m - 1; ++i) bmBc[pat[i]] = m - i - 1; } suff = (int*)calloc(m, sizeof(int)); { // suffixes() int f = 0, g; suff[m - 1] = m; g = m - 1; for (i = m - 2; i >= 0; --i) { if (i > g && suff[i + m - 1 - f] < i - g) suff[i] = suff[i + m - 1 - f]; else { if (i < g) g = i; f = i; while (g >= 0 && pat[g] == pat[g + m - 1 - f]) --g; suff[i] = f - g; } } } { // preBmGs() int j = 0; for (i = 0; i < m; ++i) bmGs[i] = m; for (i = m - 1; i >= 0; --i) if (suff[i] == i + 1) for (; j < m - 1 - i; ++j) if (bmGs[j] == m) bmGs[j] = m - 1 - i; for (i = 0; i <= m - 2; ++i) bmGs[m - 1 - suff[i]] = m - 1 - i; } free(suff); return prep; } void *kmemmem(const void *_str, int n, const void *_pat, int m, int **_prep) { int i, j, *prep = 0, *bmGs, *bmBc; const ubyte_t *str, *pat; str = (const ubyte_t*)_str; pat = (const ubyte_t*)_pat; prep = (_prep == 0 || *_prep == 0)? ksBM_prep(pat, m) : *_prep; if (_prep && *_prep == 0) *_prep = prep; bmGs = prep; bmBc = prep + m; j = 0; while (j <= n - m) { for (i = m - 1; i >= 0 && pat[i] == str[i+j]; --i); if (i >= 0) { int max = bmBc[str[i+j]] - m + 1 + i; if (max < bmGs[i]) max = bmGs[i]; j += max; } else return (void*)(str + j); } if (_prep == 0) free(prep); return 0; } char *kstrstr(const char *str, const char *pat, int **_prep) { return (char*)kmemmem(str, strlen(str), pat, strlen(pat), _prep); } char *kstrnstr(const char *str, const char *pat, int n, int **_prep) { return (char*)kmemmem(str, n, pat, strlen(pat), _prep); } /*********************** * The main() function * ***********************/ #ifdef KSTRING_MAIN #include int main() { kstring_t *s; int *fields, n, i; ks_tokaux_t aux; char *p; s = (kstring_t*)calloc(1, sizeof(kstring_t)); // test ksprintf() ksprintf(s, " abcdefg: %d ", 100); printf("'%s'\n", s->s); // test ksplit() fields = ksplit(s, 0, &n); for (i = 0; i < n; ++i) printf("field[%d] = '%s'\n", i, s->s + fields[i]); // test kstrtok() s->l = 0; for (p = kstrtok("ab:cde:fg/hij::k", ":/", &aux); p; p = kstrtok(0, 0, &aux)) { kputsn(p, aux.p - p, s); kputc('\n', s); } printf("%s", s->s); // free free(s->s); free(s); free(fields); { static char *str = "abcdefgcdgcagtcakcdcd"; static char *pat = "cd"; char *ret, *s = str; int *prep = 0; while ((ret = kstrstr(s, pat, &prep)) != 0) { printf("match: %s\n", ret); s = ret + prep[0]; } free(prep); } return 0; } #endif abPOA-1.4.1/src/kstring.h000066400000000000000000000156051425041320700150650ustar00rootroot00000000000000/* The MIT License Copyright (c) by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef KSTRING_H #define KSTRING_H #include #include #include #include #include #ifndef kroundup32 #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #endif #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) #define KS_ATTR_PRINTF(fmt, arg) __attribute__((__format__ (__printf__, fmt, arg))) #else #define KS_ATTR_PRINTF(fmt, arg) #endif /* kstring_t is a simple non-opaque type whose fields are likely to be * used directly by user code (but see also ks_str() and ks_len() below). * A kstring_t object is initialised by either of * kstring_t str = { 0, 0, NULL }; * kstring_t str; ...; str.l = str.m = 0; str.s = NULL; * and either ownership of the underlying buffer should be given away before * the object disappears (see ks_release() below) or the kstring_t should be * destroyed with free(str.s); */ #ifndef KSTRING_T #define KSTRING_T kstring_t typedef struct __kstring_t { size_t l, m; char *s; } kstring_t; #endif typedef struct { uint64_t tab[4]; int sep, finished; const char *p; // end of the current token } ks_tokaux_t; #ifdef __cplusplus extern "C" { #endif int kvsprintf(kstring_t *s, const char *fmt, va_list ap) KS_ATTR_PRINTF(2,0); int ksprintf(kstring_t *s, const char *fmt, ...) KS_ATTR_PRINTF(2,3); int ksplit_core(char *s, int delimiter, int *_max, int **_offsets); char *kstrstr(const char *str, const char *pat, int **_prep); char *kstrnstr(const char *str, const char *pat, int n, int **_prep); void *kmemmem(const void *_str, int n, const void *_pat, int m, int **_prep); /* kstrtok() is similar to strtok_r() except that str is not * modified and both str and sep can be NULL. For efficiency, it is * actually recommended to set both to NULL in the subsequent calls * if sep is not changed. */ char *kstrtok(const char *str, const char *sep, ks_tokaux_t *aux); /* kgetline() uses the supplied fgets()-like function to read a "\n"- * or "\r\n"-terminated line from fp. The line read is appended to the * kstring without its terminator and 0 is returned; EOF is returned at * EOF or on error (determined by querying fp, as per fgets()). */ typedef char *kgets_func(char *, int, void *); int kgetline(kstring_t *s, kgets_func *fgets, void *fp); #ifdef __cplusplus } #endif static inline int ks_resize(kstring_t *s, size_t size) { if (s->m < size) { char *tmp; s->m = size; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return -1; } return 0; } static inline char *ks_str(kstring_t *s) { return s->s; } static inline size_t ks_len(kstring_t *s) { return s->l; } // Give ownership of the underlying buffer away to something else (making // that something else responsible for freeing it), leaving the kstring_t // empty and ready to be used again, or ready to go out of scope without // needing free(str.s) to prevent a memory leak. static inline char *ks_release(kstring_t *s) { char *ss = s->s; s->l = s->m = 0; s->s = NULL; return ss; } static inline int kputsn(const char *p, int l, kstring_t *s) { if (s->l + l + 1 >= s->m) { char *tmp; s->m = s->l + l + 2; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return EOF; } memcpy(s->s + s->l, p, l); s->l += l; s->s[s->l] = 0; return l; } static inline int kputs(const char *p, kstring_t *s) { return kputsn(p, strlen(p), s); } static inline int kputc(int c, kstring_t *s) { if (s->l + 1 >= s->m) { char *tmp; s->m = s->l + 2; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return EOF; } s->s[s->l++] = c; s->s[s->l] = 0; return c; } static inline int kputc_(int c, kstring_t *s) { if (s->l + 1 > s->m) { char *tmp; s->m = s->l + 1; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return EOF; } s->s[s->l++] = c; return 1; } static inline int kputsn_(const void *p, int l, kstring_t *s) { if (s->l + l > s->m) { char *tmp; s->m = s->l + l; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return EOF; } memcpy(s->s + s->l, p, l); s->l += l; return l; } static inline int kputw(int c, kstring_t *s) { char buf[16]; int i, l = 0; unsigned int x = c; if (c < 0) x = -x; do { buf[l++] = x%10 + '0'; x /= 10; } while (x > 0); if (c < 0) buf[l++] = '-'; if (s->l + l + 1 >= s->m) { char *tmp; s->m = s->l + l + 2; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return EOF; } for (i = l - 1; i >= 0; --i) s->s[s->l++] = buf[i]; s->s[s->l] = 0; return 0; } static inline int kputuw(unsigned c, kstring_t *s) { char buf[16]; int l, i; unsigned x; if (c == 0) return kputc('0', s); for (l = 0, x = c; x > 0; x /= 10) buf[l++] = x%10 + '0'; if (s->l + l + 1 >= s->m) { char *tmp; s->m = s->l + l + 2; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return EOF; } for (i = l - 1; i >= 0; --i) s->s[s->l++] = buf[i]; s->s[s->l] = 0; return 0; } static inline int kputl(long c, kstring_t *s) { char buf[32]; int i, l = 0; unsigned long x = c; if (c < 0) x = -x; do { buf[l++] = x%10 + '0'; x /= 10; } while (x > 0); if (c < 0) buf[l++] = '-'; if (s->l + l + 1 >= s->m) { char *tmp; s->m = s->l + l + 2; kroundup32(s->m); if ((tmp = (char*)realloc(s->s, s->m))) s->s = tmp; else return EOF; } for (i = l - 1; i >= 0; --i) s->s[s->l++] = buf[i]; s->s[s->l] = 0; return 0; } /* * Returns 's' split by delimiter, with *n being the number of components; * NULL on failue. */ static inline int *ksplit(kstring_t *s, int delimiter, int *n) { int max = 0, *offsets = 0; *n = ksplit_core(s->s, delimiter, &max, &offsets); return offsets; } #endif abPOA-1.4.1/src/kvec.h000066400000000000000000000057251425041320700143360ustar00rootroot00000000000000/* The MIT License Copyright (c) 2008, by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* An example: #include "kvec.h" int main() { kvec_t(int) array; kv_init(array); kv_push(int, array, 10); // append kv_a(int, array, 20) = 5; // dynamic kv_A(array, 20) = 4; // static kv_destroy(array); return 0; } */ /* 2008-09-22 (0.1.0): * The initial version. */ #ifndef AC_KVEC_H #define AC_KVEC_H #include #include "kalloc.h" #define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #define kvec_t(type) struct { size_t n, m; type *a; } #define kv_init(v) ((v).n = (v).m = 0, (v).a = 0) #define kv_destroy(v) free((v).a) #define kv_A(v, i) ((v).a[(i)]) #define kv_pop(v) ((v).a[--(v).n]) #define kv_size(v) ((v).n) #define kv_max(v) ((v).m) #define kv_resize(type, km, v, s) do { \ if ((v).m < (s)) { \ (v).m = (s); \ kv_roundup32((v).m); \ (v).a = (type*)krealloc((km), (v).a, sizeof(type) * (v).m); \ } \ } while (0) #define kv_copy(type, km, v1, v0) do { \ if ((v1).m < (v0).n) kv_resize(type, (km), (v1), (v0).n); \ (v1).n = (v0).n; \ memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \ } while (0) \ #define kv_push(type, km, v, x) do { \ if ((v).n == (v).m) { \ (v).m = (v).m? (v).m<<1 : 2; \ (v).a = (type*)krealloc((km), (v).a, sizeof(type) * (v).m); \ } \ (v).a[(v).n++] = (x); \ } while (0) #define kv_pushp(type, km, v, p) do { \ if ((v).n == (v).m) { \ (v).m = (v).m? (v).m<<1 : 2; \ (v).a = (type*)krealloc((km), (v).a, sizeof(type) * (v).m); \ } \ *(p) = &(v).a[(v).n++]; \ } while (0) #define kv_reverse(type, v, start) do { \ if ((v).m > 0 && (v).n > (start)) { \ size_t __i, __end = (v).n - (start); \ type *__a = (v).a + (start); \ for (__i = 0; __i < __end>>1; ++__i) { \ type __t = __a[__end - 1 - __i]; \ __a[__end - 1 - __i] = __a[__i]; __a[__i] = __t; \ } \ } \ } while (0) #endif abPOA-1.4.1/src/simd_abpoa_align.c000066400000000000000000004461541425041320700166560ustar00rootroot00000000000000#include #include #include #include "abpoa_align.h" #include "simd_instruction.h" #include "utils.h" typedef struct { const int reg_n, bits_n, log_num, num_of_value, size; int inf_min; // based on penalty of mismatch and GAP_OE1 } SIMD_para_t; #define SIMDShiftOneNi8 1 #define SIMDShiftOneNi16 2 #define SIMDShiftOneNi32 4 #define SIMDShiftOneNi64 8 #ifdef __AVX512F__ SIMD_para_t _simd_p8 = {512, 8, 6, 64, 64, -1}; SIMD_para_t _simd_p16 = {512, 16, 5, 32, 64, -1}; SIMD_para_t _simd_p32 = {512, 32, 4, 16, 64, -1}; SIMD_para_t _simd_p64 = {512, 64, 3, 8, 64, -1}; #define SIMDTotalBytes 64 #elif defined(__AVX2__) SIMD_para_t _simd_p8 = {256, 8, 5, 32, 32, -1}; SIMD_para_t _simd_p16 = {256, 16, 4, 16, 32, -1}; SIMD_para_t _simd_p32 = {256, 32, 3, 8, 32, -1}; SIMD_para_t _simd_p64 = {256, 64, 2, 4, 32, -1}; #define SIMDTotalBytes 32 #else SIMD_para_t _simd_p8 = {128, 8, 4, 16, 16, -1}; SIMD_para_t _simd_p16 = {128, 16, 3, 8, 16, -1}; SIMD_para_t _simd_p32 = {128, 32, 2, 4, 16, -1}; SIMD_para_t _simd_p64 = {128, 64, 1, 2, 16, -1}; #define SIMDTotalBytes 16 #endif #define print_simd(s, str, score_t) { \ int _i; score_t *_a = (score_t*)(s); \ fprintf(stderr, "%s\t", str); \ for (_i = 0; _i < SIMDTotalBytes / (int)sizeof(score_t); ++_i) { \ fprintf(stderr, "%d\t", _a[_i]); \ } fprintf(stderr, "\n"); \ } #define simd_abpoa_print_lg_matrix(score_t, beg_index, end_index) { \ for (j = 0; j < end_index-beg_index; ++j) { \ fprintf(stderr, "index: %d\t", j); \ dp_h = DP_H + j * dp_sn; \ _dp_h = (score_t*)dp_h; \ for (i = dp_beg[j]; i <= dp_end[j]; ++i) { \ fprintf(stderr, "%d:(%d)\t", i, _dp_h[i]); \ } fprintf(stderr, "\n"); \ } \ } #define simd_abpoa_print_ag_matrix(score_t, beg_index, end_index) { \ for (j = beg_index; j < end_index; ++j) { \ fprintf(stderr, "index: %d\t", j); \ dp_h = DP_HEF + j * 3 * dp_sn; dp_e1 = dp_h + dp_sn; \ _dp_h = (score_t*)dp_h, _dp_e1 = (score_t*)dp_e1; \ for (i = dp_beg[j]; i <= dp_end[j]; ++i) { \ fprintf(stderr, "%d:(%d,%d)\t", i, _dp_h[i], _dp_e1[i]); \ } fprintf(stderr, "\n"); \ } \ } #define debug_simd_abpoa_print_cg_matrix_row(str, score_t, index_i) { \ score_t *_dp_h = (score_t*)dp_h, *_dp_e1 = (score_t*)dp_e1; \ score_t *_dp_e2 = (score_t*)dp_e2, *_dp_f1 = (score_t*)dp_f1, *_dp_f2 = (score_t*)dp_f2; \ fprintf(stderr, "%s\tindex: %d\t", str, index_i); \ for (i = dp_beg[index_i]; i <= (dp_end[index_i]/16+1)*16-1; ++i) { \ fprintf(stderr, "%d:(%d,%d,%d,%d,%d)\t", i, _dp_h[i], _dp_e1[i],_dp_e2[i], _dp_f1[i],_dp_f2[i]); \ } fprintf(stderr, "\n"); \ } #define simd_abpoa_print_cg_matrix(score_t, beg_index, end_index) { \ for (j = 0; j < end_index-beg_index; ++j) { \ fprintf(stderr, "index: %d\t", j); \ dp_h=DP_H2E2F+j*5*dp_sn; dp_e1=dp_h+dp_sn; dp_e2=dp_e1+dp_sn; dp_f1=dp_e2+dp_sn; dp_f2=dp_f1+dp_sn; \ score_t *_dp_h=(score_t*)dp_h, *_dp_e1=(score_t*)dp_e1, *_dp_e2=(score_t*)dp_e2; \ score_t *_dp_f1=(score_t*)dp_f1, *_dp_f2=(score_t*)dp_f2; \ for (i = dp_beg[j]; i <= dp_end[j]; ++i) { \ fprintf(stderr, "%d:(%d,%d,%d,%d,%d)\t", i, _dp_h[i], _dp_e1[i],_dp_e2[i], _dp_f1[i],_dp_f2[i]); \ } fprintf(stderr, "\n"); \ } \ } /* max_pos_left/right: left/right boundary of max column index for each row, based on the pre_nodes' DP score */ /* === workflow of alignment === */ /* a. global: * (1) alloc mem * (2) init for first row * (3) DP for each row * (3.2) if use_ada, update max_pos_left/right * (4) find best_i/j, backtrack * b. extend: * (1) alloc mem * (2) init for first row * (3) DP for each row * (3.2) find max of current row * (3.3) z-drop, set_max_score * (3.4) if use_ada, update max_pos_left/right */ // backtrack order: // Match/Mismatch, Deletion, Insertion #define simd_abpoa_lg_backtrack(score_t) { \ int i, j, k, pre_i, n_c = 0, s, is_match, m_c = 0, hit, id, _start_i, _start_j; \ SIMDi *dp_h; score_t *_dp_h=NULL, *_pre_dp_h; abpoa_cigar_t *cigar = 0; \ i = best_i, j = best_j, _start_i = best_i, _start_j = best_j; \ id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ if (best_j < qlen) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, qlen-j, -1, qlen-1); \ dp_h = DP_H + i * dp_sn; _dp_h = (score_t*)dp_h; \ int indel_first = 1; /* prefer to keep gaps at the end */ \ while (i > 0 && j > 0) { \ if (abpt->align_mode == ABPOA_LOCAL_MODE && _dp_h[j] == 0) break; \ _start_i = i, _start_j = j; \ int *pre_index_i = pre_index[i]; \ s = mat[m * graph->node[id].base + query[j-1]]; hit = 0; \ is_match = graph->node[id].base == query[j-1]; \ if (indel_first == 0) { /* match/mismatch */ \ for (k = 0; k < pre_n[i]; ++k) { /* match/mismatch */ \ pre_i = pre_index_i[k]; \ if (j-1 < dp_beg[pre_i] || j-1 > dp_end[pre_i]) continue; \ _pre_dp_h = (score_t*)(DP_H + pre_i * dp_sn); \ if (_pre_dp_h[j-1] + s == _dp_h[j]) { \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, j-1); \ i = pre_i; --j; hit = 1; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_H + i * dp_sn; _dp_h = (score_t*)dp_h; \ ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; \ break; \ } \ } \ } \ if (hit == 0) { /* deletion */ \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j < dp_beg[pre_i] || j > dp_end[pre_i]) continue; \ _pre_dp_h = (score_t*)( DP_H + pre_i * dp_sn); \ if (_pre_dp_h[j] - gap_ext1 == _dp_h[j]) { \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, j-1); \ i = pre_i; hit = 1; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_H + i * dp_sn; _dp_h = (score_t*)dp_h; \ break; \ } \ } \ } \ if (hit == 0) { /* insertion */ \ if (_dp_h[j-1] - gap_ext1 == _dp_h[j]) { \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, 1, id, j-1); j--; \ hit = 1; ++res->n_aln_bases; \ } \ } \ if (hit == 0 && indel_first == 1) { /* match/mismatch */ \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j-1 < dp_beg[pre_i] || j-1 > dp_end[pre_i]) continue; \ _pre_dp_h = (score_t*)(DP_H + pre_i * dp_sn); \ if (_pre_dp_h[j-1] + s == _dp_h[j]) { /* match/mismatch */ \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, j-1); \ i = pre_i; --j; hit = 1; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_H + i * dp_sn; _dp_h = (score_t*)dp_h; \ ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; \ indel_first = 0; \ break; \ } \ } \ } \ if (hit == 0) err_fatal_simple("Error in lg_backtrack."); \ /* fprintf(stderr, "%d, %d, (%d)\n", i, j, indel_first); */ \ } \ if (j > 0) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, j, -1, j-1); \ /* reverse cigar */ \ res->graph_cigar = abpt->rev_cigar ? cigar : abpoa_reverse_cigar(n_c, cigar); \ res->n_cigar = n_c; res->m_cigar = m_c; \ res->node_e = abpoa_graph_index_to_node_id(graph, best_i+beg_index), res->query_e=best_j-1; /*0-based*/ \ res->node_s = abpoa_graph_index_to_node_id(graph, _start_i+beg_index), res->query_s=_start_j-1; \ /*abpoa_print_cigar(n_c, *graph_cigar, graph);*/ \ } #define simd_abpoa_ag_backtrack(score_t) { \ int i, j, k, pre_i, n_c = 0, s, is_match, m_c = 0, id, hit, cur_op = ABPOA_ALL_OP, _start_i, _start_j; \ score_t *_dp_h, *_dp_e1, *_dp_f1, *_pre_dp_h, *_pre_dp_e1; abpoa_cigar_t *cigar = 0; \ i = best_i, j = best_j; _start_i = best_i, _start_j = best_j; \ id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ if (best_j < qlen) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, qlen-j, -1, qlen-1); \ SIMDi *dp_h = DP_HEF + dp_sn * i * 3; _dp_h = (score_t*)dp_h; \ int indel_first = 1; /* prefer to keep gaps at the end */ \ while (i > 0 && j > 0) { \ if (abpt->align_mode == ABPOA_LOCAL_MODE && _dp_h[j] == 0) break; \ _start_i = i, _start_j = j; \ int *pre_index_i = pre_index[i]; \ s = mat[m * graph->node[id].base + query[j-1]]; hit = 0; \ is_match = graph->node[id].base == query[j-1]; \ if (cur_op & ABPOA_M_OP && indel_first == 0) { /* match/mismatch */ \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j-1 < dp_beg[pre_i] || j-1 > dp_end[pre_i]) continue; \ _pre_dp_h = (score_t*)(DP_HEF + dp_sn * pre_i * 3); \ if (_pre_dp_h[j-1] + s == _dp_h[j]) { \ cur_op = ABPOA_ALL_OP; hit = 1; \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, j-1); \ i = pre_i; --j; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_HEF + dp_sn * i * 3; _dp_h = (score_t*)dp_h; \ ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; \ break; \ } \ } \ } \ if (hit == 0 && cur_op & ABPOA_E1_OP) { /* deletion */ \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j < dp_beg[pre_i] || j > dp_end[pre_i]) continue; \ _pre_dp_e1 = (score_t*)(DP_HEF + dp_sn * (pre_i * 3 + 1)); \ if (cur_op & ABPOA_M_OP) { \ if (_dp_h[j] == _pre_dp_e1[j]) { \ _pre_dp_h = (score_t*)(DP_HEF + dp_sn * pre_i * 3); \ if (_pre_dp_h[j] - gap_oe1 == _pre_dp_e1[j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; \ else cur_op = ABPOA_E1_OP; \ hit = 1; \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, j-1); \ i = pre_i; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_HEF + dp_sn * i * 3; _dp_h = (score_t*)dp_h; \ break; \ } \ } else { \ _dp_e1 = (score_t*)(dp_h + dp_sn); \ if (_dp_e1[j] == _pre_dp_e1[j] - gap_ext1) { \ _pre_dp_h = (score_t*)(DP_HEF + dp_sn * pre_i * 3); \ if (_pre_dp_h[j] - gap_oe1 == _pre_dp_e1[j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; \ else cur_op = ABPOA_E1_OP; \ hit = 1; \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, j-1); \ i = pre_i; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_HEF + dp_sn * i * 3; _dp_h = (score_t*)dp_h; \ break; \ } \ } \ } \ } \ if (hit == 0 && cur_op & ABPOA_F_OP) { /* insertion */ \ _dp_f1 = (score_t*)(dp_h + dp_sn * 2); \ if (cur_op & ABPOA_M_OP) { \ if (_dp_h[j] == _dp_f1[j]) { \ if (_dp_h[j-1] - gap_oe1 == _dp_f1[j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; \ else if (_dp_f1[j-1] - gap_ext1 == _dp_f1[j]) cur_op = ABPOA_F1_OP, hit = 1; \ } \ } else { \ if (_dp_h[j-1] - gap_oe1 == _dp_f1[j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; \ else if (_dp_f1[j-1] - gap_ext1 == _dp_f1[j]) cur_op = ABPOA_F1_OP, hit = 1; \ } \ if (hit == 1) { \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, 1, id, j-1); --j; \ ++res->n_aln_bases; \ } \ } \ if (hit == 0 && cur_op & ABPOA_M_OP && indel_first == 1) { \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j-1 < dp_beg[pre_i] || j-1 > dp_end[pre_i]) continue; \ _pre_dp_h = (score_t*)(DP_HEF + dp_sn * pre_i * 3); \ if (_pre_dp_h[j-1] + s == _dp_h[j]) { \ cur_op = ABPOA_ALL_OP; hit = 1; \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, j-1); \ i = pre_i; --j; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_HEF + dp_sn * i * 3; _dp_h = (score_t*)dp_h; \ ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; \ indel_first = 0; \ break; \ } \ } \ } \ if (hit == 0) err_fatal_simple("Error in ag_backtrack."); \ /* fprintf(stderr, "%d, %d, %d (indel_first: %d)\n", i, j, cur_op, indel_first); */ \ } \ if (j > 0) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, j, -1, j-1); \ /* reverse cigar */ \ res->graph_cigar = abpt->rev_cigar ? cigar : abpoa_reverse_cigar(n_c, cigar); \ res->n_cigar = n_c; res->m_cigar = m_c; \ res->node_e = abpoa_graph_index_to_node_id(graph, best_i+beg_index), res->query_e=best_j-1; /*0-based*/ \ res->node_s = abpoa_graph_index_to_node_id(graph, _start_i+beg_index), res->query_s=_start_j-1; \ /*abpoa_print_cigar(n_c, *graph_cigar, graph);*/ \ } #define simd_abpoa_cg_backtrack(score_t) { \ int i, j, k, pre_i, n_c = 0, s, is_match, m_c = 0, id, hit, cur_op = ABPOA_ALL_OP, _start_i, _start_j; \ score_t *_dp_h, *_dp_e1, *_dp_e2, *_dp_f1, *_dp_f2, *_pre_dp_h, *_pre_dp_e1, *_pre_dp_e2; \ abpoa_cigar_t *cigar = 0; \ i = best_i, j = best_j, _start_i = best_i, _start_j = best_j; \ id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ if (best_j < qlen) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, qlen-j, -1, qlen-1); \ SIMDi *dp_h = DP_H2E2F + dp_sn * i * 5; _dp_h = (score_t*)dp_h; \ int indel_first = 1; /* prefer to keep gaps at the end */ \ while (i > 0 && j > 0) { \ if (abpt->align_mode == ABPOA_LOCAL_MODE && _dp_h[j] == 0) break; \ _start_i = i, _start_j = j; \ int *pre_index_i = pre_index[i]; \ s = mat[m * graph->node[id].base + query[j-1]]; hit = 0; \ is_match = graph->node[id].base == query[j-1]; \ if (cur_op & ABPOA_M_OP && indel_first == 0) { /* match/mismatch */ \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j-1 < dp_beg[pre_i] || j-1 > dp_end[pre_i]) continue; \ _pre_dp_h = (score_t*)(DP_H2E2F + dp_sn * pre_i * 5); \ if (_pre_dp_h[j-1] + s == _dp_h[j]) { \ cur_op = ABPOA_ALL_OP; hit = 1; \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, j-1); \ i = pre_i; --j; id = abpoa_graph_index_to_node_id(graph, i+beg_index); hit = 1; \ dp_h = DP_H2E2F + dp_sn * i * 5; _dp_h = (score_t*)dp_h; \ ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; \ break; \ } \ } \ } \ if (hit == 0 && cur_op & ABPOA_E_OP) { /* deletion */ \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j < dp_beg[pre_i] || j > dp_end[pre_i]) continue; \ if (cur_op & ABPOA_E1_OP) { \ _pre_dp_e1 = (score_t*)(DP_H2E2F + dp_sn * (pre_i * 5 + 1)); \ if (cur_op & ABPOA_M_OP) { \ if (_dp_h[j] == _pre_dp_e1[j]) { \ _pre_dp_h = (score_t*)(DP_H2E2F + dp_sn * pre_i * 5); \ if (_pre_dp_h[j] - gap_oe1 == _pre_dp_e1[j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; \ else cur_op = ABPOA_E1_OP; \ hit = 1; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, j-1); \ i = pre_i; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_H2E2F + dp_sn * i * 5; _dp_h = (score_t*)dp_h; \ break; \ } \ } else { \ _dp_e1 = (score_t*)(dp_h + dp_sn); \ if (_dp_e1[j] == _pre_dp_e1[j] - gap_ext1) { \ _pre_dp_h = (score_t*)(DP_H2E2F + dp_sn * pre_i * 5); \ if (_pre_dp_h[j] - gap_oe1 == _pre_dp_e1[j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; \ else cur_op = ABPOA_E1_OP; \ hit = 1; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, j-1); \ i = pre_i; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_H2E2F + dp_sn * i * 5; _dp_h = (score_t*)dp_h; \ break; \ } \ } \ } \ if (cur_op & ABPOA_E2_OP) { \ _pre_dp_e2 = (score_t*)(DP_H2E2F + dp_sn * (pre_i * 5 + 2)); \ if (cur_op & ABPOA_M_OP) { \ if (_dp_h[j] == _pre_dp_e2[j]) { \ _pre_dp_h = (score_t*)(DP_H2E2F + dp_sn * pre_i * 5); \ if (_pre_dp_h[j] - gap_oe2 == _pre_dp_e2[j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; \ else cur_op = ABPOA_E2_OP; \ hit = 1; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, j-1); \ i = pre_i; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_H2E2F + dp_sn * i * 5; _dp_h = (score_t*)dp_h; \ break; \ } \ } else { \ _dp_e2 = (score_t*)(dp_h + dp_sn * 2); \ if (_dp_e2[j] == _pre_dp_e2[j] - gap_ext2) { \ _pre_dp_h = (score_t*)(DP_H2E2F + dp_sn * pre_i * 5); \ if (_pre_dp_h[j] - gap_oe2 == _pre_dp_e2[j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; \ else cur_op = ABPOA_E2_OP; \ hit = 1; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, j-1); \ i = pre_i; id = abpoa_graph_index_to_node_id(graph, i+beg_index); \ dp_h = DP_H2E2F + dp_sn * i * 5; _dp_h = (score_t*)dp_h; \ break; \ } \ } \ } \ } \ } \ if (hit == 0 && cur_op & ABPOA_F_OP) { /* insertion */ \ if (cur_op & ABPOA_F1_OP) { \ _dp_f1 = (score_t*)(dp_h + dp_sn * 3); \ if (cur_op & ABPOA_M_OP) { \ if (_dp_h[j] == _dp_f1[j]) { \ if (_dp_h[j-1] - gap_oe1 == _dp_f1[j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; \ else if (_dp_f1[j-1] - gap_ext1 == _dp_f1[j]) cur_op = ABPOA_F1_OP, hit = 1; \ } \ } else { \ if (_dp_h[j-1] - gap_oe1 == _dp_f1[j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; \ else if (_dp_f1[j-1] - gap_ext1 == _dp_f1[j]) cur_op = ABPOA_F1_OP, hit = 1; \ } \ } \ if (hit == 0 && cur_op & ABPOA_F2_OP) { \ _dp_f2 = (score_t*)(dp_h + dp_sn * 4); \ if (cur_op & ABPOA_M_OP) { \ if (_dp_h[j] == _dp_f2[j]) { \ if (_dp_h[j-1] - gap_oe2 == _dp_f2[j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; \ else if (_dp_f2[j-1] - gap_ext2 == _dp_f2[j]) cur_op = ABPOA_F2_OP, hit = 1; \ } \ } else { \ if (_dp_h[j-1] - gap_oe2 == _dp_f2[j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; \ else if (_dp_f2[j-1] - gap_ext2 == _dp_f2[j]) cur_op = ABPOA_F2_OP, hit = 1; \ } \ } \ if (hit == 1) { \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, 1, id, j-1); --j; \ ++res->n_aln_bases; \ } \ } \ if (hit == 0 && cur_op & ABPOA_M_OP && indel_first == 1) { /* match/mismatch */ \ for (k = 0; k < pre_n[i]; ++k) { \ pre_i = pre_index_i[k]; \ if (j-1 < dp_beg[pre_i] || j-1 > dp_end[pre_i]) continue; \ _pre_dp_h = (score_t*)(DP_H2E2F + dp_sn * pre_i * 5); \ if (_pre_dp_h[j-1] + s == _dp_h[j]) { \ cur_op = ABPOA_ALL_OP; hit = 1; \ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, j-1); \ i = pre_i; --j; id = abpoa_graph_index_to_node_id(graph, i+beg_index); hit = 1; \ dp_h = DP_H2E2F + dp_sn * i * 5; _dp_h = (score_t*)dp_h; \ ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; \ indel_first = 0; \ break; \ } \ } \ } \ if (hit == 0) err_fatal_simple("Error in cg_backtrack."); \ /* fprintf(stderr, "%d, %d, %d\n", i, j, cur_op); */ \ } \ if (j > 0) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, j, -1, j-1); \ /* reverse cigar */ \ res->graph_cigar = abpt->rev_cigar ? cigar : abpoa_reverse_cigar(n_c, cigar); \ res->n_cigar = n_c; res->m_cigar = m_c; \ res->node_e = abpoa_graph_index_to_node_id(graph, best_i+beg_index), res->query_e=best_j-1; /*0-based*/ \ res->node_s = abpoa_graph_index_to_node_id(graph, _start_i+beg_index), res->query_s=_start_j-1; \ /*abpoa_print_cigar(n_c, *graph_cigar, graph);*/ \ } // simd_abpoa_va // simd_abpoa_ag_only_var // sim_abpoa_init_var #define simd_abpoa_var(score_t, sp, SIMDSetOne, SIMDShiftOneN) \ /* int tot_dp_sn = 0; */ \ abpoa_graph_t *graph = ab->abg; abpoa_simd_matrix_t *abm = ab->abm; \ int matrix_row_n = end_index-beg_index+1, matrix_col_n = qlen + 1; \ int **pre_index, *pre_n, _pre_index, _pre_n, pre_i; \ int i, j, k, *dp_beg, *dp_beg_sn, *dp_end, *dp_end_sn, node_id, index_i, dp_i; \ int beg, end, beg_sn, end_sn, _beg_sn, _end_sn, pre_beg_sn, pre_end, sn_i; \ int pn, log_n, size, qp_sn, dp_sn; /* pn: value per SIMDi, qp_sn/dp_sn/d_sn: segmented length*/ \ SIMDi *dp_h, *pre_dp_h, *qp, *qi=NULL; \ score_t *_dp_h=NULL, *_qi, best_score = sp.inf_min, inf_min = sp.inf_min; \ int *mat = abpt->mat, m = abpt->m; score_t gap_ext1 = abpt->gap_ext1; \ int w = abpt->wb < 0 ? qlen : abpt->wb+(int)(abpt->wf*qlen); /* when w < 0, do whole global */ \ int best_i = 0, best_j = 0, best_id = 0, max, max_i=-1; \ SIMDi zero = SIMDSetZeroi(), SIMD_INF_MIN = SIMDSetOne(inf_min); \ pn = sp.num_of_value; qp_sn = dp_sn = (matrix_col_n + pn - 1) / pn; \ log_n = sp.log_num, size = sp.size; qp = abm->s_mem; \ int set_num; SIMDi *PRE_MASK, *SUF_MIN, *PRE_MIN; \ PRE_MASK = (SIMDi*)SIMDMalloc((pn+1) * size, size); \ SUF_MIN = (SIMDi*)SIMDMalloc((pn+1) * size, size); \ PRE_MIN = (SIMDi*)SIMDMalloc(pn * size, size); \ for (i = 0; i < pn; ++i) { \ score_t *pre_mask = (score_t*)(PRE_MASK+i); \ for (j = 0; j <= i; ++j) pre_mask[j] = -1; \ for (j = i+1; j < pn; ++j) pre_mask[j] = 0; \ } PRE_MASK[pn] = PRE_MASK[pn-1]; \ SUF_MIN[0] = SIMDShiftLeft(SIMD_INF_MIN, SIMDShiftOneN); \ for (i = 1; i < pn; ++i) \ SUF_MIN[i] = SIMDShiftLeft(SUF_MIN[i-1], SIMDShiftOneN); SUF_MIN[pn] = SUF_MIN[pn-1]; \ for (i = 1; i < pn; ++i) { \ score_t *pre_min = (score_t*)(PRE_MIN + i); \ for (j = 0; j < i; ++j) pre_min[j] = inf_min; \ for (j = i; j < pn; ++j) pre_min[j] = 0; \ } #define simd_abpoa_lg_only_var(score_t, SIMDSetOne, SIMDAdd) \ SIMDi *DP_H = qp + qp_sn * abpt->m; qi = DP_H + dp_sn * matrix_row_n; \ SIMDi GAP_E1 = SIMDSetOne(gap_ext1); \ SIMDi *GAP_E1S = (SIMDi*)SIMDMalloc(log_n * size, size); \ GAP_E1S[0] = GAP_E1; \ for (i = 1; i < log_n; ++i) { \ GAP_E1S[i] = SIMDAdd(GAP_E1S[i-1], GAP_E1S[i-1]); \ } #define simd_abpoa_ag_only_var(score_t, SIMDSetOne, SIMDAdd) \ score_t *_dp_e1, *_dp_f1, gap_open1 = abpt->gap_open1, gap_oe1 = abpt->gap_open1 + abpt->gap_ext1; \ SIMDi *DP_HEF, *dp_e1, *pre_dp_e1, *dp_f1; int pre_end_sn; \ DP_HEF = qp + qp_sn * abpt->m; qi = DP_HEF + dp_sn * matrix_row_n * 3; \ SIMDi GAP_O1 = SIMDSetOne(gap_open1), GAP_E1 = SIMDSetOne(gap_ext1), GAP_OE1 = SIMDSetOne(gap_oe1); \ SIMDi *GAP_E1S = (SIMDi*)SIMDMalloc(log_n * size, size); GAP_E1S[0] = GAP_E1; \ for (i = 1; i < log_n; ++i) { \ GAP_E1S[i] = SIMDAdd(GAP_E1S[i-1], GAP_E1S[i-1]); \ } #define simd_abpoa_cg_only_var(score_t, SIMDSetOne, SIMDAdd) \ score_t *_dp_e1, *_dp_e2, *_dp_f1, *_dp_f2, gap_open1 = abpt->gap_open1, gap_oe1 = gap_open1 + gap_ext1; \ score_t gap_open2 = abpt->gap_open2, gap_ext2 = abpt->gap_ext2, gap_oe2 = gap_open2 + gap_ext2; \ SIMDi *DP_H2E2F, *dp_e1, *dp_e2, *dp_f1, *dp_f2, *pre_dp_e1, *pre_dp_e2; int pre_end_sn; \ SIMDi GAP_O1 = SIMDSetOne(gap_open1), GAP_O2 = SIMDSetOne(gap_open2); \ SIMDi GAP_E1 = SIMDSetOne(gap_ext1), GAP_E2 = SIMDSetOne(gap_ext2); \ SIMDi GAP_OE1 = SIMDSetOne(gap_oe1), GAP_OE2 = SIMDSetOne(gap_oe2); \ DP_H2E2F = qp + qp_sn * abpt->m; qi = DP_H2E2F + dp_sn * matrix_row_n * 5; \ SIMDi *GAP_E1S = (SIMDi*)SIMDMalloc(log_n * size, size), *GAP_E2S = (SIMDi*)SIMDMalloc(log_n * size, size); \ GAP_E1S[0] = GAP_E1; GAP_E2S[0] = GAP_E2; \ for (i = 1; i < log_n; ++i) { \ GAP_E1S[i] = SIMDAdd(GAP_E1S[i-1], GAP_E1S[i-1]); \ GAP_E2S[i] = SIMDAdd(GAP_E2S[i-1], GAP_E2S[i-1]); \ } #define simd_abpoa_init_var(score_t) { \ /* generate the query profile */ \ for (i = 0; i < qp_sn * abpt->m; ++i) qp[i] = SIMD_INF_MIN; \ for (k = 0; k < abpt->m; ++k) { /* SIMD parallelization */ \ int *p = &mat[k * abpt->m]; \ score_t *_qp = (score_t*)(qp + k * qp_sn); _qp[0] = 0; \ for (j = 0; j < qlen; ++j) _qp[j+1] = (score_t)p[query[j]]; \ for (j = qlen+1; j < qp_sn * pn; ++j) _qp[j] = 0; \ } \ if (abpt->wb>=0 || abpt->align_mode==ABPOA_LOCAL_MODE || abpt->align_mode==ABPOA_EXTEND_MODE){ \ _qi = (score_t*)qi; /* query index */ \ for (i = 0; i <= qlen; ++i) _qi[i] = i; \ for (i = qlen+1; i < (qlen/pn+1) * pn; ++i) _qi[i] = -1; \ } \ /* for backtrack */ \ dp_beg=abm->dp_beg, dp_end=abm->dp_end, dp_beg_sn=abm->dp_beg_sn, dp_end_sn=abm->dp_end_sn; \ /* index of pre-node */ \ pre_index = (int**)_err_calloc(matrix_row_n, sizeof(int*)); \ pre_n = (int*)_err_calloc(matrix_row_n, sizeof(int)); \ for (index_i=beg_index+1, dp_i=1; index_i<=end_index; ++index_i, ++dp_i) { \ node_id = abpoa_graph_index_to_node_id(graph, index_i); \ pre_n[dp_i] = graph->node[node_id].in_edge_n; \ pre_index[dp_i] = (int*)_err_malloc(pre_n[dp_i] * sizeof(int)); \ for (j = _pre_n = 0; j < pre_n[dp_i]; ++j) { \ _pre_index = abpoa_graph_node_id_to_index(graph, graph->node[node_id].in_id[j]); \ if (index_map[_pre_index]) pre_index[dp_i][_pre_n++] = _pre_index-beg_index; \ } \ pre_n[dp_i] = _pre_n; \ } \ } #define simd_abpoa_free_var { \ for (i = 0; i < matrix_row_n; ++i) free(pre_index[i]); free(pre_index); free(pre_n); \ SIMDFree(PRE_MASK); SIMDFree(SUF_MIN); SIMDFree(PRE_MIN); \ } \ #define simd_abpoa_lg_var(score_t, sp, SIMDSetOne, SIMDShiftOneN, SIMDAdd) \ simd_abpoa_var(score_t, sp, SIMDSetOne, SIMDShiftOneN); \ simd_abpoa_lg_only_var(score_t, SIMDSetOne, SIMDAdd); \ simd_abpoa_init_var(score_t); #define simd_abpoa_ag_var(score_t, sp, SIMDSetOne, SIMDShiftOneN, SIMDAdd) \ simd_abpoa_var(score_t, sp, SIMDSetOne, SIMDShiftOneN); \ simd_abpoa_ag_only_var(score_t, SIMDSetOne, SIMDAdd); \ simd_abpoa_init_var(score_t); #define simd_abpoa_cg_var(score_t, sp, SIMDSetOne, SIMDShiftOneN, SIMDAdd) \ simd_abpoa_var(score_t, sp, SIMDSetOne, SIMDShiftOneN); \ simd_abpoa_cg_only_var(score_t, SIMDSetOne, SIMDAdd); \ simd_abpoa_init_var(score_t); #define simd_abpoa_lg_first_row { \ /* fill the first row */ \ if (abpt->wb >= 0) { \ graph->node_id_to_max_pos_left[beg_node_id] = graph->node_id_to_max_pos_right[beg_node_id] = 0; \ for (i = 0; i < graph->node[beg_node_id].out_edge_n; ++i) { /* set max pos for out_id */ \ int out_id = graph->node[beg_node_id].out_id[i]; \ if (index_map[abpoa_graph_node_id_to_index(graph, out_id)]) \ graph->node_id_to_max_pos_left[out_id] = graph->node_id_to_max_pos_right[out_id] = 1; \ } \ dp_beg[0] = 0, dp_end[0] = GET_AD_DP_END(graph, w, beg_node_id, end_node_id, qlen); \ } else { \ dp_beg[0] = 0, dp_end[0] = qlen; \ } \ dp_beg_sn[0] = (dp_beg[0])/pn; dp_end_sn[0] = (dp_end[0])/pn; \ dp_beg[0] = dp_beg_sn[0] * pn; dp_end[0] = (dp_end_sn[0]+1)*pn-1; \ dp_h = DP_H; _end_sn = MIN_OF_TWO(dp_end_sn[0]+1, dp_sn-1); \ } #define simd_abpoa_ag_first_row { \ /* fill the first row */ \ if (abpt->wb >= 0) { \ graph->node_id_to_max_pos_left[beg_node_id] = graph->node_id_to_max_pos_right[beg_node_id] = 0; \ for (i = 0; i < graph->node[beg_node_id].out_edge_n; ++i) { /* set max pos for out_id */ \ int out_id = graph->node[beg_node_id].out_id[i]; \ if (index_map[abpoa_graph_node_id_to_index(graph, out_id)]) \ graph->node_id_to_max_pos_left[out_id] = graph->node_id_to_max_pos_right[out_id] = 1; \ } \ dp_beg[0] = 0, dp_end[0] = GET_AD_DP_END(graph, w, beg_node_id, end_node_id, qlen); \ } else { \ dp_beg[0] = 0, dp_end[0] = qlen; \ } \ dp_beg_sn[0] = (dp_beg[0])/pn; dp_end_sn[0] = (dp_end[0])/pn; \ dp_beg[0] = dp_beg_sn[0] * pn; dp_end[0] = (dp_end_sn[0]+1)*pn-1; \ dp_h = DP_HEF; dp_e1 = dp_h + dp_sn; dp_f1 = dp_e1 + dp_sn; \ _end_sn = MIN_OF_TWO(dp_end_sn[0]+1, dp_sn-1); \ } #define simd_abpoa_cg_first_row { \ /* fill the first row */ \ if (abpt->wb >= 0) { \ graph->node_id_to_max_pos_left[beg_node_id] = graph->node_id_to_max_pos_right[beg_node_id] = 0; \ for (i = 0; i < graph->node[beg_node_id].out_edge_n; ++i) { /* set max pos for out_id */ \ int out_id = graph->node[beg_node_id].out_id[i]; \ if (index_map[abpoa_graph_node_id_to_index(graph, out_id)]) \ graph->node_id_to_max_pos_left[out_id] = graph->node_id_to_max_pos_right[out_id] = 1; \ } \ dp_beg[0] = 0, dp_end[0] = GET_AD_DP_END(graph, w, beg_node_id, end_node_id, qlen); \ } else { \ dp_beg[0] = 0, dp_end[0] = qlen; \ } \ dp_beg_sn[0] = (dp_beg[0])/pn; dp_end_sn[0] = (dp_end[0])/pn; \ dp_beg[0] = dp_beg_sn[0] * pn; dp_end[0] = (dp_end_sn[0]+1)*pn-1; \ dp_h = DP_H2E2F; dp_e1 = dp_h+dp_sn; dp_e2 = dp_e1+dp_sn; dp_f1 = dp_e2+dp_sn; dp_f2 = dp_f1+dp_sn; \ _end_sn = MIN_OF_TWO(dp_end_sn[0]+1, dp_sn-1); \ } #define simd_abpoa_lg_first_dp(score_t) { \ simd_abpoa_lg_first_row; \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ for (i = 0; i < _end_sn; ++i) \ dp_h[i] = zero; \ } else { \ for (i = 0; i <= _end_sn; ++i) { \ dp_h[i] = SIMD_INF_MIN; \ } \ _dp_h = (score_t*)dp_h; \ for (i = 0; i <= dp_end[0]; ++i) { /* no SIMD parallelization */ \ _dp_h[i] = -gap_ext1 * i; \ } \ } \ } #define simd_abpoa_ag_first_dp(score_t) { \ simd_abpoa_ag_first_row; \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ for (i = 0; i < _end_sn; ++i) \ dp_h[i] = dp_e1[i] = dp_f1[i] = zero; \ } else { \ for (i = 0; i <= _end_sn; ++i) { \ dp_h[i] = SIMD_INF_MIN; dp_e1[i] = SIMD_INF_MIN; \ } \ _dp_h=(score_t*)dp_h,_dp_e1=(score_t*)dp_e1,_dp_f1=(score_t*)dp_f1; \ _dp_h[0] = 0; _dp_e1[0] = -(gap_oe1), _dp_f1[0] = inf_min; \ for (i = 1; i <= dp_end[0]; ++i) { /* no SIMD parallelization */ \ _dp_f1[i] = -gap_open1 - gap_ext1 * i; \ _dp_h[i] = -gap_open1 - gap_ext1 * i; \ } \ } \ } #define simd_abpoa_cg_first_dp(score_t) { \ simd_abpoa_cg_first_row; \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ for (i = 0; i < _end_sn; ++i) \ dp_h[i] = dp_e1[i] = dp_e2[i] = dp_f1[i] = dp_f2[i] = zero; \ } else { \ for (i = 0; i <= _end_sn; ++i) { \ dp_h[i] = SIMD_INF_MIN; dp_e1[i] = SIMD_INF_MIN; dp_e2[i] = SIMD_INF_MIN; \ } \ _dp_h = (score_t*)dp_h, _dp_e1 = (score_t*)dp_e1, _dp_e2 = (score_t*)dp_e2; \ _dp_f1 = (score_t*)dp_f1, _dp_f2 = (score_t*)dp_f2; \ _dp_h[0] = 0; _dp_e1[0] = -(gap_oe1); _dp_e2[0] = -(gap_oe2); \ _dp_f1[0] = _dp_f2[0] = inf_min; \ for (i = 1; i <= dp_end[0]; ++i) { /* no SIMD parallelization */ \ _dp_f1[i] = -gap_open1 - gap_ext1 * i; \ _dp_f2[i] = -gap_open2 - gap_ext2 * i; \ _dp_h[i] = MAX_OF_TWO(_dp_f1[i], _dp_f2[i]); \ } \ } \ } // mask[pn], suf_min[pn], pre_min[logN] #define SIMD_SET_F(F, log_n, set_num, PRE_MIN, PRE_MASK, SUF_MIN, GAP_E1S, SIMDMax, SIMDAdd, SIMDSub, SIMDShiftOneN) { \ if (set_num == pn) { \ F = SIMDMax(F, SIMDOri(SIMDShiftLeft(SIMDSub(F, GAP_E1S[0]), SIMDShiftOneN), PRE_MIN[1])); \ if (log_n > 1) { \ F = SIMDMax(F, SIMDOri(SIMDShiftLeft(SIMDSub(F, GAP_E1S[1]), SIMDShiftOneN<<1), PRE_MIN[2])); \ } if (log_n > 2) { \ F = SIMDMax(F, SIMDOri(SIMDShiftLeft(SIMDSub(F, GAP_E1S[2]), SIMDShiftOneN<<2), PRE_MIN[4])); \ } if (log_n > 3) { \ F = SIMDMax(F, SIMDOri(SIMDShiftLeft(SIMDSub(F, GAP_E1S[3]), SIMDShiftOneN<<3), PRE_MIN[8])); \ } if (log_n > 4) { \ F = SIMDMax(F, SIMDOri(SIMDShiftLeft(SIMDSub(F, GAP_E1S[4]), SIMDShiftOneN<<4), PRE_MIN[16])); \ } if (log_n > 5) { \ F = SIMDMax(F, SIMDOri(SIMDShiftLeft(SIMDSub(F, GAP_E1S[5]), SIMDShiftOneN<<5), PRE_MIN[32])); \ } \ } else { /*suffix MIN_INF*/ \ int cov_bit = set_num; \ F = SIMDMax(F, SIMDOri(SIMDAndi(SIMDShiftLeft(SIMDSub(F, GAP_E1S[0]), SIMDShiftOneN), PRE_MASK[cov_bit]), SIMDOri(SUF_MIN[cov_bit], PRE_MIN[1]))); \ if (log_n > 1) { \ cov_bit += 2; \ F = SIMDMax(F, SIMDOri(SIMDAndi(SIMDShiftLeft(SIMDSub(F, GAP_E1S[1]), SIMDShiftOneN<<1), PRE_MASK[cov_bit]), SIMDOri(SUF_MIN[cov_bit], PRE_MIN[2]))); \ } if (log_n > 2) { \ cov_bit += 4; \ F = SIMDMax(F, SIMDOri(SIMDAndi(SIMDShiftLeft(SIMDSub(F, GAP_E1S[2]), SIMDShiftOneN<<2), PRE_MASK[cov_bit]), SIMDOri(SUF_MIN[cov_bit], PRE_MIN[4]))); \ } if (log_n > 3) { \ cov_bit += 8; \ F = SIMDMax(F, SIMDOri(SIMDAndi(SIMDShiftLeft(SIMDSub(F, GAP_E1S[3]), SIMDShiftOneN<<3), PRE_MASK[cov_bit]), SIMDOri(SUF_MIN[cov_bit], PRE_MIN[8]))); \ } if (log_n > 4) { \ cov_bit += 16; \ F = SIMDMax(F, SIMDOri(SIMDAndi(SIMDShiftLeft(SIMDSub(F, GAP_E1S[4]), SIMDShiftOneN<<4), PRE_MASK[cov_bit]), SIMDOri(SUF_MIN[cov_bit], PRE_MIN[16]))); \ } if (log_n > 5) { \ cov_bit += 32; \ F = SIMDMax(F, SIMDOri(SIMDAndi(SIMDShiftLeft(SIMDSub(F, GAP_E1S[5]), SIMDShiftOneN<<5), PRE_MASK[cov_bit]), SIMDOri(SUF_MIN[cov_bit], PRE_MIN[32]))); \ } \ } \ } #define simd_abpoa_lg_dp(score_t, SIMDShiftOneN, SIMDMax, SIMDAdd, SIMDSub) { \ node_id = abpoa_graph_index_to_node_id(graph, index_i); \ SIMDi *q = qp + graph->node[node_id].base * qp_sn, first, remain; \ dp_h = &DP_H[dp_i * dp_sn]; _dp_h = (score_t*)dp_h; \ int min_pre_beg_sn, max_pre_end_sn; \ if (abpt->wb < 0) { \ beg = dp_beg[dp_i] = 0, end = dp_end[dp_i] = qlen; \ beg_sn = dp_beg_sn[dp_i] = (dp_beg[dp_i])/pn; end_sn = dp_end_sn[dp_i] = (dp_end[dp_i])/pn; \ min_pre_beg_sn = 0, max_pre_end_sn = end_sn; \ } else { \ beg = GET_AD_DP_BEGIN(graph, w, node_id, end_node_id, qlen), end = GET_AD_DP_END(graph, w, node_id, end_node_id, qlen); \ beg_sn = beg / pn; min_pre_beg_sn = INT32_MAX, max_pre_end_sn = -1; \ for (i = 0; i < pre_n[dp_i]; ++i) { \ pre_i = pre_index[dp_i][i]; \ if (min_pre_beg_sn > dp_beg_sn[pre_i]) min_pre_beg_sn = dp_beg_sn[pre_i]; \ if (max_pre_end_sn < dp_end_sn[pre_i]) max_pre_end_sn = dp_end_sn[pre_i]; \ } if (beg_sn < min_pre_beg_sn) beg_sn = min_pre_beg_sn; \ dp_beg_sn[dp_i] = beg_sn; beg = dp_beg[dp_i] = dp_beg_sn[dp_i] * pn; \ end_sn = dp_end_sn[dp_i] = end/pn; end = dp_end[dp_i] = (dp_end_sn[dp_i]+1)*pn-1; \ } \ /* loop query */ \ /* first pre_node */ \ pre_i = pre_index[dp_i][0]; \ pre_dp_h = DP_H + pre_i * dp_sn; \ pre_end = dp_end[pre_i]; \ pre_beg_sn = dp_beg_sn[pre_i]; \ /* set M from (pre_i, q_i-1), E from (pre_i, q_i) */ \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ _beg_sn = 0, _end_sn = end_sn; first = SIMDShiftRight(zero, SIMDTotalBytes-SIMDShiftOneN); \ } else { \ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneN); \ else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneN); \ _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); \ for (i = beg_sn; i < _beg_sn; ++i) dp_h[i] = SIMD_INF_MIN; \ for (i = _end_sn+1; i <= MIN_OF_TWO(end_sn+1, dp_sn-1); ++i) dp_h[i] = SIMD_INF_MIN; \ } \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneN); \ dp_h[sn_i] = SIMDMax(SIMDAdd(SIMDOri(first, remain), q[sn_i]), SIMDSub(pre_dp_h[sn_i], GAP_E1)); \ first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneN); \ } \ /* get max m and e */ \ for (i = 1; i < pre_n[dp_i]; ++i) { \ pre_i = pre_index[dp_i][i]; \ pre_dp_h = DP_H + pre_i * dp_sn; \ pre_end = dp_end[pre_i]; \ pre_beg_sn = dp_beg_sn[pre_i]; \ /* set M from (pre_i, q_i-1), E from (pre_i, q_i) */ \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ first = SIMDShiftRight(zero, SIMDTotalBytes-SIMDShiftOneN); \ } else { \ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneN); \ else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneN); \ _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); \ } \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneN); \ dp_h[sn_i] = SIMDMax(SIMDAdd(SIMDOri(first, remain), q[sn_i]), SIMDMax(SIMDSub(pre_dp_h[sn_i], GAP_E1), dp_h[sn_i])); \ first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneN); \ } /* now we have max(h,e) stored at dp_h */ \ } \ /* new F start */ \ first = SIMDOri(SIMDAndi(dp_h[beg_sn], PRE_MASK[0]), SUF_MIN[0]); \ for (sn_i = beg_sn; sn_i <= end_sn; ++sn_i) { \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ set_num = pn; \ } else { \ if (sn_i < min_pre_beg_sn) { \ _err_fatal_simple(__func__, "sn_i < min_pre_beg_sn\n"); \ } else if (sn_i > max_pre_end_sn) { \ set_num = sn_i == max_pre_end_sn+1 ? 1 : 0; \ } else set_num = pn; \ } \ dp_h[sn_i] = SIMDMax(dp_h[sn_i], first); \ SIMD_SET_F(dp_h[sn_i], log_n, set_num, PRE_MIN, PRE_MASK, SUF_MIN, GAP_E1S, SIMDMax, SIMDAdd, SIMDSub, SIMDShiftOneN); \ first = SIMDOri(SIMDAndi(SIMDShiftRight(SIMDSub(dp_h[sn_i], GAP_E1), SIMDTotalBytes-SIMDShiftOneN), PRE_MASK[0]), SUF_MIN[0]); \ } \ if (abpt->align_mode == ABPOA_LOCAL_MODE) for (sn_i = 0; sn_i <= end_sn; ++sn_i) dp_h[sn_i] = SIMDMax(zero, dp_h[sn_i]); \ } #define simd_abpoa_ag_dp(score_t, SIMDShiftOneN, SIMDMax, SIMDAdd, SIMDSub, SIMDGetIfGreater, SIMDSetIfGreater, SIMDSetIfEqual) { \ node_id = abpoa_graph_index_to_node_id(graph, index_i); \ SIMDi *q = qp + graph->node[node_id].base * qp_sn, first, remain; \ dp_h = DP_HEF + dp_i * 3 * dp_sn; dp_e1 = dp_h + dp_sn; dp_f1 = dp_e1 + dp_sn; \ _dp_h = (score_t*)dp_h, _dp_e1 = (score_t*)dp_e1, _dp_f1 = (score_t*)dp_f1; \ int min_pre_beg_sn, max_pre_end_sn; \ if (abpt->wb < 0) { \ beg = dp_beg[dp_i] = 0, end = dp_end[dp_i] = qlen; \ beg_sn = dp_beg_sn[dp_i] = (dp_beg[dp_i])/pn; end_sn = dp_end_sn[dp_i] = (dp_end[dp_i])/pn; \ min_pre_beg_sn = 0, max_pre_end_sn = end_sn; \ } else { \ beg = GET_AD_DP_BEGIN(graph, w, node_id, end_node_id, qlen), end = GET_AD_DP_END(graph, w, node_id, end_node_id, qlen); \ beg_sn = beg / pn; min_pre_beg_sn = INT32_MAX, max_pre_end_sn = -1; \ for (i = 0; i < pre_n[dp_i]; ++i) { \ pre_i = pre_index[dp_i][i]; \ if (min_pre_beg_sn > dp_beg_sn[pre_i]) min_pre_beg_sn = dp_beg_sn[pre_i]; \ if (max_pre_end_sn < dp_end_sn[pre_i]) max_pre_end_sn = dp_end_sn[pre_i]; \ } if (beg_sn < min_pre_beg_sn) beg_sn = min_pre_beg_sn; \ dp_beg_sn[dp_i] = beg_sn; beg = dp_beg[dp_i] = dp_beg_sn[dp_i] * pn; \ end_sn = dp_end_sn[dp_i] = end/pn; end = dp_end[dp_i] = (dp_end_sn[dp_i]+1)*pn-1; \ } \ /* loop query */ \ /* first pre_node */ \ pre_i = pre_index[dp_i][0]; \ pre_dp_h = DP_HEF + pre_i * 3 * dp_sn; pre_dp_e1 = pre_dp_h + dp_sn; \ pre_end = dp_end[pre_i]; pre_beg_sn = dp_beg_sn[pre_i]; pre_end_sn = dp_end_sn[pre_i]; \ /* set M from (pre_i, q_i-1) */ \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ _beg_sn = 0, _end_sn = end_sn; first = SIMDShiftRight(zero, SIMDTotalBytes-SIMDShiftOneN); \ } else { \ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneN); \ else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneN); \ _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); \ for (i = beg_sn; i < _beg_sn; ++i) dp_h[i] = SIMD_INF_MIN; \ for (i = _end_sn+1; i <= MIN_OF_TWO(end_sn+1, dp_sn-1); ++i) dp_h[i] = SIMD_INF_MIN; \ } \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneN); \ dp_h[sn_i] = SIMDOri(first, remain); \ first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneN); \ } \ /* set E from (pre_i, q_i) */ \ if (abpt->align_mode != ABPOA_LOCAL_MODE) { \ _end_sn = MIN_OF_TWO(pre_end_sn, end_sn); \ for (i = beg_sn; i < _beg_sn; ++i) dp_e1[i] = SIMD_INF_MIN; \ for (i = _end_sn+1; i <= end_sn; ++i) dp_e1[i] = SIMD_INF_MIN; \ } \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) /* SIMD parallelization */ \ dp_e1[sn_i] = pre_dp_e1[sn_i]; \ /* get max m and e */ \ for (i = 1; i < pre_n[dp_i]; ++i) { \ pre_i = pre_index[dp_i][i]; \ pre_dp_h = DP_HEF + pre_i * 3 * dp_sn; pre_dp_e1 = pre_dp_h + dp_sn; \ pre_end = dp_end[pre_i]; pre_beg_sn = dp_beg_sn[pre_i]; pre_end_sn = dp_end_sn[pre_i]; \ /* set M from (pre_i, q_i-1) */ \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ first = SIMDShiftRight(zero, SIMDTotalBytes-SIMDShiftOneN); \ } else { \ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneN); \ else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneN); \ _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); \ } \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneN); \ dp_h[sn_i] = SIMDMax(SIMDOri(first, remain), dp_h[sn_i]); \ first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneN); \ } \ /* set E from (pre_i, q_i) */ \ _end_sn = MIN_OF_TWO(pre_end_sn, end_sn); \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) /* SIMD parallelization */ \ dp_e1[sn_i] = SIMDMax(pre_dp_e1[sn_i], dp_e1[sn_i]); \ } \ /* compare M, E, and F */ \ for (sn_i = beg_sn; sn_i <= end_sn; ++sn_i) { /* SIMD parallelization */ \ dp_h[sn_i] = SIMDAdd(dp_h[sn_i], q[sn_i]); \ } \ /* new F start */ \ first = SIMDShiftRight(SIMDShiftLeft(dp_h[beg_sn], SIMDTotalBytes-SIMDShiftOneN), SIMDTotalBytes-SIMDShiftOneN); \ for (sn_i = beg_sn; sn_i <= end_sn; ++sn_i) { \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ set_num = pn; \ } else { \ if (sn_i < min_pre_beg_sn) { \ _err_fatal_simple(__func__, "sn_i < min_pre_beg_sn\n"); \ } else if (sn_i > max_pre_end_sn) { \ set_num = sn_i == max_pre_end_sn+1 ? 2 : 1; \ } else set_num = pn; \ } \ /* F = (H << 1 | x) - OE */ \ dp_f1[sn_i] = SIMDSub(SIMDOri(SIMDShiftLeft(dp_h[sn_i], SIMDShiftOneN), first), GAP_OE1); \ /* F = max{F, (F-e)<<1}, F = max{F, (F-2e)<<2} ... */ \ SIMD_SET_F(dp_f1[sn_i], log_n, set_num, PRE_MIN, PRE_MASK, SUF_MIN, GAP_E1S, SIMDMax, SIMDAdd, SIMDSub, SIMDShiftOneN); \ /* x = max{H, F+o} */ \ first = SIMDShiftRight(SIMDMax(dp_h[sn_i], SIMDAdd(dp_f1[sn_i], GAP_O1)), SIMDTotalBytes-SIMDShiftOneN); \ /* H = max{H, F} */ \ dp_h[sn_i] = SIMDMax(dp_h[sn_i], dp_e1[sn_i]); SIMDi tmp = dp_h[sn_i]; \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ dp_h[sn_i] = SIMDMax(zero, SIMDMax(dp_h[sn_i], dp_f1[sn_i])); \ SIMDSetIfEqual(dp_e1[sn_i], dp_h[sn_i],tmp, SIMDMax(SIMDSub(dp_e1[sn_i],GAP_E1), SIMDSub(dp_h[sn_i],GAP_OE1)),zero); \ } else { \ dp_h[sn_i] = SIMDMax(dp_h[sn_i], dp_f1[sn_i]); \ SIMDSetIfEqual(dp_e1[sn_i], dp_h[sn_i],tmp, SIMDMax(SIMDSub(dp_e1[sn_i],GAP_E1), SIMDSub(dp_h[sn_i],GAP_OE1)),SIMD_INF_MIN); \ } \ } \ } #define simd_abpoa_cg_dp(score_t, SIMDShiftOneN, SIMDMax, SIMDAdd, SIMDSub, SIMDGetIfGreater, SIMDSetIfGreater, SIMDSetIfEqual) { \ node_id = abpoa_graph_index_to_node_id(graph, index_i); \ SIMDi *q = qp + graph->node[node_id].base * qp_sn, first, remain; \ dp_h = DP_H2E2F+dp_i*5*dp_sn; dp_e1 = dp_h+dp_sn; dp_e2 = dp_e1+dp_sn; dp_f1 = dp_e2+dp_sn; dp_f2 = dp_f1+dp_sn; \ _dp_h=(score_t*)dp_h, _dp_e1=(score_t*)dp_e1, _dp_e2=(score_t*)dp_e2, _dp_f1=(score_t*)dp_f1, _dp_f2=(score_t*)dp_f2; \ int min_pre_beg_sn, max_pre_end_sn; \ if (abpt->wb < 0) { \ beg = dp_beg[dp_i] = 0, end = dp_end[dp_i] = qlen; \ beg_sn = dp_beg_sn[dp_i] = beg/pn; end_sn = dp_end_sn[dp_i] = end/pn; \ min_pre_beg_sn = 0, max_pre_end_sn = end_sn; \ } else { \ beg = GET_AD_DP_BEGIN(graph, w, node_id, end_node_id, qlen), end = GET_AD_DP_END(graph, w, node_id, end_node_id, qlen); \ beg_sn = beg / pn; min_pre_beg_sn = INT32_MAX, max_pre_end_sn = -1; \ for (i = 0; i < pre_n[dp_i]; ++i) { \ pre_i = pre_index[dp_i][i]; \ if (min_pre_beg_sn > dp_beg_sn[pre_i]) min_pre_beg_sn = dp_beg_sn[pre_i]; \ if (max_pre_end_sn < dp_end_sn[pre_i]) max_pre_end_sn = dp_end_sn[pre_i]; \ } if (beg_sn < min_pre_beg_sn) beg_sn = min_pre_beg_sn; \ dp_beg_sn[dp_i] = beg_sn; beg = dp_beg[dp_i] = dp_beg_sn[dp_i] * pn; \ end_sn = dp_end_sn[dp_i] = end/pn; end = dp_end[dp_i] = (dp_end_sn[dp_i]+1)*pn-1; \ /* fprintf(stderr, "index: %d, beg: %d, end: %d, beg_sn: %d, end_sn: %d\n", index_i, beg, end, beg_sn, end_sn); */ \ } \ /* fprintf(stderr, "%d: beg, end: %d, %d\n", index_i, beg, end); */ \ /* tot_dp_sn += (end_sn - beg_sn + 1); */ \ /* loop query */ \ /* first pre_node */ \ pre_i = pre_index[dp_i][0]; \ pre_dp_h = DP_H2E2F + pre_i * 5 * dp_sn; pre_dp_e1 = pre_dp_h + dp_sn; pre_dp_e2 = pre_dp_e1 + dp_sn; \ pre_end = dp_end[pre_i]; pre_beg_sn = dp_beg_sn[pre_i]; pre_end_sn = dp_end_sn[pre_i]; \ /* set M from (pre_i, q_i-1) */ \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ _beg_sn = 0, _end_sn = end_sn; first = SIMDShiftRight(zero, SIMDTotalBytes-SIMDShiftOneN); \ } else { \ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneN); \ else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneN); \ _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); \ for (i = beg_sn; i < _beg_sn; ++i) dp_h[i] = SIMD_INF_MIN; \ for (i = _end_sn+1; i <= MIN_OF_TWO(end_sn+1, dp_sn-1); ++i) dp_h[i] = SIMD_INF_MIN; \ } \ /* fprintf(stderr, "1 index_i: %d, beg_sn: %d, end_sn: %d\n", index_i, _beg_sn, _end_sn); */ \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneN); \ dp_h[sn_i] = SIMDOri(first, remain); \ first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneN); \ } \ /* set E from (pre_i, q_i) */ \ if (abpt->align_mode != ABPOA_LOCAL_MODE) { \ _end_sn = MIN_OF_TWO(pre_end_sn, end_sn); \ for (i = beg_sn; i < _beg_sn; ++i) dp_e1[i] = SIMD_INF_MIN, dp_e2[i] = SIMD_INF_MIN; \ for (i = _end_sn+1; i <= end_sn; ++i) dp_e1[i] = SIMD_INF_MIN, dp_e2[i] = SIMD_INF_MIN; \ } \ /* fprintf(stderr, "2 index_i: %d, beg_sn: %d, end_sn: %d\n", index_i, _beg_sn, _end_sn); */ \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ dp_e1[sn_i] = pre_dp_e1[sn_i]; \ dp_e2[sn_i] = pre_dp_e2[sn_i]; \ } \ /* get max m and e */ \ for (i = 1; i < pre_n[dp_i]; ++i) { \ pre_i = pre_index[dp_i][i]; \ pre_dp_h = DP_H2E2F + (pre_i * 5) * dp_sn; pre_dp_e1 = pre_dp_h + dp_sn; pre_dp_e2 = pre_dp_e1 + dp_sn; \ pre_end = dp_end[pre_i]; pre_beg_sn = dp_beg_sn[pre_i]; pre_end_sn = dp_end_sn[pre_i]; \ /* set M from (pre_i, q_i-1) */ \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ first = SIMDShiftRight(zero, SIMDTotalBytes-SIMDShiftOneN); \ } else { \ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneN); \ else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneN); \ _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); \ } \ /* fprintf(stderr, "3 index_i: %d, beg_sn: %d, end_sn: %d\n", index_i, _beg_sn, _end_sn); */ \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneN); \ dp_h[sn_i] = SIMDMax(SIMDOri(first, remain), dp_h[sn_i]); \ first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneN); \ } \ /* set E from (pre_i, q_i) */ \ _end_sn = MIN_OF_TWO(pre_end_sn, end_sn); \ /* fprintf(stderr, "4 index_i: %d, beg_sn: %d, end_sn: %d\n", index_i, _beg_sn, _end_sn); */ \ for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ \ dp_e1[sn_i] = SIMDMax(pre_dp_e1[sn_i], dp_e1[sn_i]); \ dp_e2[sn_i] = SIMDMax(pre_dp_e2[sn_i], dp_e2[sn_i]); \ } \ } \ /* compare M, E, and F */ \ /* fprintf(stderr, "5 index_i: %d, beg_sn: %d, end_sn: %d\n", index_i, _beg_sn, _end_sn); */ \ for (sn_i = beg_sn; sn_i <= end_sn; ++sn_i) { /* SIMD parallelization */ \ dp_h[sn_i] = SIMDAdd(dp_h[sn_i], q[sn_i]); \ } \ /* new F start */ \ first = SIMDShiftRight(SIMDShiftLeft(dp_h[beg_sn], SIMDTotalBytes-SIMDShiftOneN), SIMDTotalBytes-SIMDShiftOneN); \ SIMDi first2 = first; \ for (sn_i = beg_sn; sn_i <= end_sn; ++sn_i) { \ if (abpt->align_mode == ABPOA_LOCAL_MODE) set_num = pn; \ else { \ if (sn_i < min_pre_beg_sn) { \ _err_fatal_simple(__func__, "sn_i < min_pre_beg_sn\n"); \ } else if (sn_i > max_pre_end_sn) { \ set_num = sn_i == max_pre_end_sn+1 ? 2 : 1; \ } else set_num = pn; \ } \ /* H = max{H, E} */ \ dp_h[sn_i] = SIMDMax(SIMDMax(dp_h[sn_i], dp_e1[sn_i]), dp_e2[sn_i]); \ /* F = (H << 1 | x) - OE */ \ dp_f1[sn_i] = SIMDSub(SIMDOri(SIMDShiftLeft(dp_h[sn_i], SIMDShiftOneN), first), GAP_OE1); \ dp_f2[sn_i] = SIMDSub(SIMDOri(SIMDShiftLeft(dp_h[sn_i], SIMDShiftOneN), first2), GAP_OE2); \ /* F = max{F, (F-e)<<1}, F = max{F, (F-2e)<<2} ... */ \ SIMD_SET_F(dp_f1[sn_i], log_n, set_num, PRE_MIN, PRE_MASK, SUF_MIN, GAP_E1S, SIMDMax, SIMDAdd, SIMDSub, SIMDShiftOneN); \ SIMD_SET_F(dp_f2[sn_i], log_n, set_num, PRE_MIN, PRE_MASK, SUF_MIN, GAP_E2S, SIMDMax, SIMDAdd, SIMDSub, SIMDShiftOneN); \ /* x = max{H, F+o} */ \ first = SIMDShiftRight(SIMDMax(dp_h[sn_i], SIMDAdd(dp_f1[sn_i], GAP_O1)), SIMDTotalBytes-SIMDShiftOneN); \ first2 = SIMDShiftRight(SIMDMax(dp_h[sn_i], SIMDAdd(dp_f2[sn_i], GAP_O2)), SIMDTotalBytes-SIMDShiftOneN); \ if (abpt->align_mode == ABPOA_LOCAL_MODE) { \ dp_h[sn_i] = SIMDMax(zero, SIMDMax(dp_h[sn_i], SIMDMax(dp_f1[sn_i], dp_f2[sn_i]))); \ dp_e1[sn_i] = SIMDMax(zero,SIMDMax(SIMDSub(dp_e1[sn_i],GAP_E1),SIMDSub(dp_h[sn_i],GAP_OE1))); \ dp_e2[sn_i] = SIMDMax(zero,SIMDMax(SIMDSub(dp_e2[sn_i],GAP_E2),SIMDSub(dp_h[sn_i],GAP_OE2))); \ } else { \ /* H = max{H, F} */ \ dp_h[sn_i] = SIMDMax(dp_h[sn_i], SIMDMax(dp_f1[sn_i], dp_f2[sn_i])); \ /* e for next cell */ \ dp_e1[sn_i] = SIMDMax(SIMDSub(dp_e1[sn_i],GAP_E1),SIMDSub(dp_h[sn_i],GAP_OE1)); \ dp_e2[sn_i] = SIMDMax(SIMDSub(dp_e2[sn_i],GAP_E2),SIMDSub(dp_h[sn_i],GAP_OE2)); \ } \ } \ } #define set_global_max_score(score, i, j) { \ if (score > best_score) { \ best_score = score; best_i = i; best_j = j; \ } \ } #define set_extend_max_score(score, i, j) { \ if (score > best_score) { \ best_score = score; best_i = i; best_j = j; best_id = node_id; \ } else if (abpt->zdrop > 0) { \ int delta_index = graph->node_id_to_max_remain[best_id] - graph->node_id_to_max_remain[node_id]; \ if (best_score - score > abpt->zdrop + gap_ext1 * abs(delta_index-(j-best_j))) \ break; \ } \ } #define simd_abpoa_global_get_max(score_t, DP_M, dp_sn) { \ int end, in_id, in_index, in_dp_i; \ for (i = 0; i < graph->node[end_node_id].in_edge_n; ++i) { \ in_id = graph->node[end_node_id].in_id[i]; \ in_index = abpoa_graph_node_id_to_index(graph, in_id); \ if (index_map[in_index] == 0) continue; \ in_dp_i = in_index - beg_index; \ dp_h = DP_M + in_dp_i * dp_sn; \ _dp_h = (score_t*)dp_h; \ if (qlen > dp_end[in_dp_i]) end = dp_end[in_dp_i]; \ else end = qlen; \ set_global_max_score(_dp_h[end], in_dp_i, end); \ } \ } #define simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater) { \ /* select max dp_h */ \ max = inf_min, max_i = -1; \ SIMDi a = dp_h[end_sn], b = qi[end_sn]; \ if (end_sn == qlen / pn) SIMDSetIfGreater(a, zero, b, SIMD_INF_MIN, a); \ for (i = beg_sn; i < end_sn; ++i) { \ SIMDGetIfGreater(b, a, dp_h[i], a, qi[i], b); \ } \ _dp_h = (score_t*)&a, _qi = (score_t*)&b; \ for (i = 0; i < pn; ++i) { \ if (_dp_h[i] > max) { \ max = _dp_h[i]; max_i = _qi[i]; \ } \ } \ } #define simd_abpoa_ada_max_i { \ /* set max_pos_left/right for next nodes */ \ int out_i = max_i + 1; \ for (i = 0; i < graph->node[node_id].out_edge_n; ++i) { \ int out_node_id = graph->node[node_id].out_id[i]; \ if (out_i > graph->node_id_to_max_pos_right[out_node_id]) graph->node_id_to_max_pos_right[out_node_id] = out_i; \ if (out_i < graph->node_id_to_max_pos_left[out_node_id]) graph->node_id_to_max_pos_left[out_node_id] = out_i; \ } \ } // TODO end_bonus for extension // linear gap penalty: gap_open1 == 0 #define simd_abpoa_lg_align_sequence_to_graph_core(score_t, sp, SIMDSetOne, SIMDMax, SIMDAdd, \ SIMDSub, SIMDShiftOneN, SIMDSetIfGreater, SIMDGetIfGreater) { \ simd_abpoa_lg_var(score_t, sp, SIMDSetOne, SIMDShiftOneN, SIMDAdd); \ simd_abpoa_lg_first_dp(score_t); \ for (index_i=beg_index+1, dp_i=1; index_ialign_mode == ABPOA_LOCAL_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ set_global_max_score(max, dp_i, max_i); \ } \ if (abpt->align_mode == ABPOA_EXTEND_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ set_extend_max_score(max, dp_i, max_i); \ } \ if (abpt->wb >= 0) { \ if (abpt->align_mode == ABPOA_GLOBAL_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ } \ simd_abpoa_ada_max_i; \ } \ } \ if (abpt->align_mode == ABPOA_GLOBAL_MODE) simd_abpoa_global_get_max(score_t, DP_H, dp_sn); \ res->best_score = best_score; \ /* simd_abpoa_print_lg_matrix(score_t, beg_index, end_index); printf("best_score: (%d, %d) -> %d\n", best_i, best_j, best_score); */ \ if (abpt->ret_cigar) simd_abpoa_lg_backtrack(score_t); \ simd_abpoa_free_var; SIMDFree(GAP_E1S); \ } // affine gap penalty: gap_open1 > 0 #define simd_abpoa_ag_align_sequence_to_graph_core(score_t, sp, SIMDSetOne, SIMDMax, SIMDAdd, \ SIMDSub, SIMDShiftOneN, SIMDSetIfGreater, SIMDGetIfGreater, SIMDSetIfEqual) { \ simd_abpoa_ag_var(score_t, sp, SIMDSetOne, SIMDShiftOneN, SIMDAdd); \ simd_abpoa_ag_first_dp(score_t); \ for (index_i=beg_index+1, dp_i=1; index_ialign_mode == ABPOA_LOCAL_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ set_global_max_score(max, dp_i, max_i); \ } else if (abpt->align_mode == ABPOA_EXTEND_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ set_extend_max_score(max, dp_i, max_i); \ } \ if (abpt->wb >= 0) { \ if (abpt->align_mode == ABPOA_GLOBAL_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ } \ simd_abpoa_ada_max_i; \ } \ } \ if (abpt->align_mode == ABPOA_GLOBAL_MODE) simd_abpoa_global_get_max(score_t, DP_HEF, 3*dp_sn); \ res->best_score = best_score; \ /* simd_abpoa_print_ag_matrix(score_t, beg_index, end_index); fprintf(stderr, "best_score: (%d, %d) -> %d\n", best_i, best_j, best_score); */ \ if (abpt->ret_cigar) simd_abpoa_ag_backtrack(score_t); \ simd_abpoa_free_var; SIMDFree(GAP_E1S); \ } // convex gap penalty: gap_open1 > 0 && gap_open2 > 0 #define simd_abpoa_cg_align_sequence_to_graph_core(score_t, sp, SIMDSetOne, SIMDMax, SIMDAdd, \ SIMDSub, SIMDShiftOneN, SIMDSetIfGreater, SIMDGetIfGreater, SIMDSetIfEqual) { \ simd_abpoa_cg_var(score_t, sp, SIMDSetOne, SIMDShiftOneN, SIMDAdd); \ simd_abpoa_cg_first_dp(score_t); \ for (index_i=beg_index+1, dp_i=1; index_ialign_mode == ABPOA_LOCAL_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ set_global_max_score(max, dp_i, max_i); \ } else if (abpt->align_mode == ABPOA_EXTEND_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ set_extend_max_score(max, dp_i, max_i); \ } \ if (abpt->wb >= 0) { \ if (abpt->align_mode == ABPOA_GLOBAL_MODE) { \ simd_abpoa_max_in_row(score_t, SIMDSetIfGreater, SIMDGetIfGreater); \ } \ simd_abpoa_ada_max_i; \ } \ } \ /* printf("dp_sn: %d\n", tot_dp_sn); */ \ if (abpt->align_mode == ABPOA_GLOBAL_MODE) simd_abpoa_global_get_max(score_t, DP_H2E2F, 5*dp_sn); \ res->best_score = best_score; \ /* simd_abpoa_print_cg_matrix(score_t, beg_index, end_index); fprintf(stderr,"best_score: (%d, %d) -> %d\n",best_i,best_j,best_score); */ \ if (abpt->ret_cigar) simd_abpoa_cg_backtrack(score_t); \ simd_abpoa_free_var; SIMDFree(GAP_E1S); SIMDFree(GAP_E2S); \ } abpoa_simd_matrix_t *abpoa_init_simd_matrix(void) { abpoa_simd_matrix_t *abm = (abpoa_simd_matrix_t*)_err_malloc(sizeof(abpoa_simd_matrix_t)); abm->s_msize = 0; abm->s_mem = NULL; abm->rang_m = 0; abm->dp_beg = NULL; abm->dp_end = NULL; abm->dp_beg_sn = NULL; abm->dp_end_sn = NULL; return abm; } void abpoa_free_simd_matrix(abpoa_simd_matrix_t *abm) { if (abm->s_mem) SIMDFree(abm->s_mem); if (abm->dp_beg) { free(abm->dp_beg); free(abm->dp_end); free(abm->dp_beg_sn); free(abm->dp_end_sn); } free(abm); } // realloc memory everytime the graph is updated (nodes are updated already) // * index_to_node_id/node_id_to_index/node_id_to_max_remain, max_pos_left/right // * qp, DP_HE/H (if ag/lg), dp_f, qi (if ada/extend) // * dp_beg/end, dp_beg/end_sn if band // * pre_n, pre_index int simd_abpoa_realloc(abpoa_t *ab, int gn, int qlen, abpoa_para_t *abpt, SIMD_para_t sp) { uint64_t pn = sp.num_of_value, size = sp.size, sn = (qlen + sp.num_of_value) / pn; uint64_t s_msize = sn * abpt->m * size; // qp if (abpt->gap_mode == ABPOA_LINEAR_GAP) s_msize += (sn * gn * size); // DP_H, linear else if (abpt->gap_mode == ABPOA_AFFINE_GAP) s_msize += (sn * gn * 3 * size); // DP_HEF, affine else s_msize += (sn * gn * 5 * size); // DP_H2E2F, convex if (abpt->wb >= 0 || abpt->align_mode == ABPOA_EXTEND_MODE) // qi s_msize += sn * size; // if (s_msize > UINT32_MAX) { // err_func_format_printf(__func__, "Warning: Graph is too large or query is too long.\n"); // return 1; // } // fprintf(stderr, "%lld, %lld, %lld\n", (long long)node_n, (long long)ab->abm->s_msize, (long long)s_msize); if (s_msize > ab->abm->s_msize) { if (ab->abm->s_mem) SIMDFree(ab->abm->s_mem); kroundup64(s_msize); ab->abm->s_msize = s_msize; ab->abm->s_mem = (SIMDi*)SIMDMalloc(ab->abm->s_msize, size); } if (gn > ab->abm->rang_m) { ab->abm->rang_m = gn; kroundup32(ab->abm->rang_m); ab->abm->dp_beg = (int*)_err_realloc(ab->abm->dp_beg, ab->abm->rang_m * sizeof(int)); ab->abm->dp_end = (int*)_err_realloc(ab->abm->dp_end, ab->abm->rang_m * sizeof(int)); ab->abm->dp_beg_sn = (int*)_err_realloc(ab->abm->dp_beg_sn, ab->abm->rang_m * sizeof(int)); ab->abm->dp_end_sn = (int*)_err_realloc(ab->abm->dp_end_sn, ab->abm->rang_m * sizeof(int)); } return 0; } void abpoa_init_var(abpoa_para_t *abpt, uint8_t *query, int qlen, SIMDi *qp, SIMDi *qi, int *mat, int qp_sn, int pn, SIMDi SIMD_INF_MIN) { int i, j, k; int32_t *_qi; /* generate the query profile */ for (i = 0; i < qp_sn * abpt->m; ++i) qp[i] = SIMD_INF_MIN; for (k = 0; k < abpt->m; ++k) { /* SIMD parallelization */ int *p = &mat[k * abpt->m]; int32_t *_qp = (int32_t*)(qp + k * qp_sn); _qp[0] = 0; for (j = 0; j < qlen; ++j) _qp[j+1] = (int32_t)p[query[j]]; for (j = qlen+1; j < qp_sn * pn; ++j) _qp[j] = 0; } if (abpt->wb >= 0 || abpt->align_mode == ABPOA_EXTEND_MODE) { /* query index */ _qi = (int32_t*)qi; for (i = 0; i <= qlen; ++i) _qi[i] = i; for (i = qlen+1; i < (qlen/pn+1) * pn; ++i) _qi[i] = -1; } } void abpoa_cg_first_dp(abpoa_para_t *abpt, abpoa_graph_t *graph, uint8_t *index_map, int beg_node_id, int end_node_id, int *dp_beg, int *dp_end, int *dp_beg_sn, int *dp_end_sn, int pn, int qlen, int w, int dp_sn, SIMDi *DP_H2E2F, SIMDi SIMD_INF_MIN, int32_t inf_min, int gap_open1, int gap_ext1, int gap_open2, int gap_ext2, int gap_oe1, int gap_oe2) { int i, _end_sn; if (abpt->wb >= 0) { graph->node_id_to_max_pos_left[beg_node_id] = graph->node_id_to_max_pos_right[beg_node_id] = 0; for (i = 0; i < graph->node[beg_node_id].out_edge_n; ++i) { /* set min/max rank for next_id */ int out_id = graph->node[beg_node_id].out_id[i]; if (index_map[abpoa_graph_node_id_to_index(graph, out_id)]) graph->node_id_to_max_pos_left[out_id] = graph->node_id_to_max_pos_right[out_id] = 1; } dp_beg[0] = GET_AD_DP_BEGIN(graph, w, beg_node_id, end_node_id, qlen), dp_end[0] = GET_AD_DP_END(graph, w, beg_node_id, end_node_id, qlen); } else { dp_beg[0] = 0, dp_end[0] = qlen; } dp_beg_sn[0] = (dp_beg[0])/pn; dp_end_sn[0] = (dp_end[0])/pn; dp_beg[0] = dp_beg_sn[0] * pn; dp_end[0] = (dp_end_sn[0]+1)*pn-1; SIMDi *dp_h = DP_H2E2F; SIMDi *dp_e1 = dp_h + dp_sn; SIMDi *dp_e2 = dp_e1 + dp_sn, *dp_f1 = dp_e2 + dp_sn, *dp_f2 = dp_f1 + dp_sn; _end_sn = MIN_OF_TWO(dp_end_sn[0]+1, dp_sn-1); for (i = 0; i <= _end_sn; ++i) { dp_h[i] = SIMD_INF_MIN; dp_e1[i] = SIMD_INF_MIN; dp_e2[i] = SIMD_INF_MIN; } int32_t *_dp_h = (int32_t*)dp_h, *_dp_e1 = (int32_t*)dp_e1, *_dp_e2 = (int32_t*)dp_e2, *_dp_f1 = (int32_t*)dp_f1, *_dp_f2 = (int32_t*)dp_f2; _dp_h[0] = 0; _dp_e1[0] = -(gap_oe1); _dp_e2[0] = -(gap_oe2); _dp_f1[0] = _dp_f2[0] = inf_min; for (i = 1; i <= dp_end[0]; ++i) { /* no SIMD parallelization */ _dp_f1[i] = -(gap_open1 + gap_ext1 * i); _dp_f2[i] = -(gap_open2 + gap_ext2 * i); _dp_h[i] = MAX_OF_TWO(_dp_f1[i], _dp_f2[i]); // -MIN_OF_TWO(gap_open1+gap_ext1*i, gap_open2+gap_ext2*i); } } int abpoa_max(SIMDi SIMD_INF_MIN, SIMDi zero, int inf_min, SIMDi *dp_h, SIMDi *qi, int qlen, int pn, int beg_sn, int end_sn) { /* select max dp_h */ int max = inf_min, max_i = -1, i; SIMDi a = dp_h[end_sn], b = qi[end_sn]; if (end_sn == qlen / pn) SIMDSetIfGreateri32(a, zero, b, SIMD_INF_MIN, a); for (i = beg_sn; i < end_sn; ++i) { SIMDGetIfGreateri32(b, a, dp_h[i], a, qi[i], b); } int32_t *_dp_h = (int32_t*)&a, *_qi = (int32_t*)&b; for (i = 0; i < pn; ++i) { if (_dp_h[i] > max) { max = _dp_h[i]; max_i = _qi[i]; } } return max_i; } void abpoa_ada_max_i(int max_i, abpoa_graph_t *graph, int node_id) { /* set max_pos_left/right for next nodes */ int out_i = max_i + 1; int i; for (i = 0; i < graph->node[node_id].out_edge_n; ++i) { int out_node_id = graph->node[node_id].out_id[i]; if (out_i > graph->node_id_to_max_pos_right[out_node_id]) graph->node_id_to_max_pos_right[out_node_id] = out_i; if (out_i < graph->node_id_to_max_pos_left[out_node_id]) graph->node_id_to_max_pos_left[out_node_id] = out_i; } } void abpoa_global_get_max(abpoa_graph_t *graph, int beg_index, int end_node_id, uint8_t *index_map, SIMDi *DP_H_HE, int dp_sn, int qlen, int *dp_end, int32_t *best_score, int *best_i, int *best_j) { int in_id, in_index, dp_i, i; for (i = 0; i < graph->node[end_node_id].in_edge_n; ++i) { in_id = graph->node[end_node_id].in_id[i]; in_index = abpoa_graph_node_id_to_index(graph, in_id); if (index_map[in_index] == 0) continue; dp_i = in_index - beg_index; SIMDi *dp_h = DP_H_HE + dp_i * dp_sn; int32_t *_dp_h = (int32_t*)dp_h; int end; if (qlen > dp_end[dp_i]) end = dp_end[dp_i]; else end = qlen; if (_dp_h[end] > *best_score) { *best_score = _dp_h[end]; *best_i = dp_i; *best_j = end; } } } int abpoa_cg_dp(SIMDi *q, SIMDi *dp_h, SIMDi *dp_e1, SIMDi *dp_e2, SIMDi *dp_f1, SIMDi *dp_f2, int **pre_index, int *pre_n, int index_i, int dp_i, abpoa_graph_t *graph, abpoa_para_t *abpt, int dp_sn, int pn, int qlen, int w, SIMDi *DP_H2E2F, SIMDi SIMD_INF_MIN, SIMDi GAP_O1, SIMDi GAP_O2, SIMDi GAP_E1, SIMDi GAP_E2, SIMDi GAP_OE1, SIMDi GAP_OE2, SIMDi* GAP_E1S, SIMDi* GAP_E2S, SIMDi *PRE_MIN, SIMDi *PRE_MASK, SIMDi *SUF_MIN, int log_n, int *dp_beg, int *dp_end, int *dp_beg_sn, int *dp_end_sn, int end_node_id) { int tot_dp_sn = 0, i, pre_i, node_id = abpoa_graph_index_to_node_id(graph, index_i); int min_pre_beg_sn, max_pre_end_sn, beg, end, beg_sn, end_sn, pre_end, pre_end_sn, pre_beg_sn, sn_i; if (abpt->wb < 0) { beg = dp_beg[dp_i] = 0, end = dp_end[dp_i] = qlen; beg_sn = dp_beg_sn[dp_i] = beg/pn; end_sn = dp_end_sn[dp_i] = end/pn; min_pre_beg_sn = 0, max_pre_end_sn = end_sn; } else { beg = GET_AD_DP_BEGIN(graph, w, node_id, end_node_id, qlen), end = GET_AD_DP_END(graph, w, node_id, end_node_id, qlen); beg_sn = beg / pn; min_pre_beg_sn = INT32_MAX, max_pre_end_sn = -1; for (i = 0; i < pre_n[dp_i]; ++i) { pre_i = pre_index[dp_i][i]; if (min_pre_beg_sn > dp_beg_sn[pre_i]) min_pre_beg_sn = dp_beg_sn[pre_i]; if (max_pre_end_sn < dp_end_sn[pre_i]) max_pre_end_sn = dp_end_sn[pre_i]; } if (beg_sn < min_pre_beg_sn) beg_sn = min_pre_beg_sn; dp_beg_sn[dp_i] = beg_sn; beg = dp_beg[dp_i] = dp_beg_sn[dp_i] * pn; end_sn = dp_end_sn[dp_i] = end/pn; end = dp_end[dp_i] = (dp_end_sn[dp_i]+1)*pn-1; #ifdef __DEBUG__ fprintf(stderr, "index: %d (node: %d): beg: %d, end: %d, beg_sn: %d, end_sn: %d\n", index_i, node_id, beg, end, beg_sn, end_sn); #endif } tot_dp_sn += (end_sn - beg_sn + 1); /* loop query */ // new init start int _beg_sn, _end_sn; // first pre_node pre_i = pre_index[dp_i][0]; SIMDi *pre_dp_h = DP_H2E2F + (pre_i * 5) * dp_sn; SIMDi *pre_dp_e1 = pre_dp_h + dp_sn; SIMDi *pre_dp_e2 = pre_dp_e1 + dp_sn; pre_end = dp_end[pre_i]; pre_beg_sn = dp_beg_sn[pre_i]; pre_end_sn = dp_end_sn[pre_i]; SIMDi first, remain; /* set M from (pre_i, q_i-1) */ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneNi32); else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneNi32); _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); for (i = beg_sn; i < _beg_sn; ++i) dp_h[i] = SIMD_INF_MIN; for (i = _end_sn+1; i <= MIN_OF_TWO(end_sn+1, dp_sn-1); ++i) dp_h[i] = SIMD_INF_MIN; for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneNi32); dp_h[sn_i] = SIMDOri(first, remain); first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneNi32); } /* set E from (pre_i, q_i) */ _end_sn = MIN_OF_TWO(pre_end_sn, end_sn); for (i = beg_sn; i < _beg_sn; ++i) dp_e1[i] = SIMD_INF_MIN, dp_e2[i] = SIMD_INF_MIN; for (i = _end_sn+1; i <= end_sn; ++i) dp_e1[i] = SIMD_INF_MIN, dp_e2[i] = SIMD_INF_MIN; for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ dp_e1[sn_i] = pre_dp_e1[sn_i]; dp_e2[sn_i] = pre_dp_e2[sn_i]; } // if (index_i == 13095) debug_simd_abpoa_print_cg_matrix_row("1", int32_t, index_i); // new init end /* get max m and e */ for (i = 1; i < pre_n[dp_i]; ++i) { pre_i = pre_index[dp_i][i]; pre_dp_h = DP_H2E2F + (pre_i * 5) * dp_sn; pre_dp_e1 = pre_dp_h + dp_sn; pre_dp_e2 = pre_dp_e1 + dp_sn; pre_end = dp_end[pre_i]; pre_beg_sn = dp_beg_sn[pre_i]; pre_end_sn = dp_end_sn[pre_i]; /* set M from (pre_i, q_i-1) */ if (pre_beg_sn < beg_sn) _beg_sn = beg_sn, first = SIMDShiftRight(pre_dp_h[beg_sn-1], SIMDTotalBytes-SIMDShiftOneNi32); else _beg_sn = pre_beg_sn, first = SIMDShiftRight(SIMD_INF_MIN, SIMDTotalBytes-SIMDShiftOneNi32); _end_sn = MIN_OF_THREE((pre_end+1)/pn, end_sn, dp_sn-1); for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ remain = SIMDShiftLeft(pre_dp_h[sn_i], SIMDShiftOneNi32); dp_h[sn_i] = SIMDMaxi32(SIMDOri(first, remain), dp_h[sn_i]); first = SIMDShiftRight(pre_dp_h[sn_i], SIMDTotalBytes-SIMDShiftOneNi32); } /* set E from (pre_i, q_i) */ _end_sn = MIN_OF_TWO(pre_end_sn, end_sn); for (sn_i = _beg_sn; sn_i <= _end_sn; ++sn_i) { /* SIMD parallelization */ dp_e1[sn_i] = SIMDMaxi32(pre_dp_e1[sn_i], dp_e1[sn_i]); dp_e2[sn_i] = SIMDMaxi32(pre_dp_e2[sn_i], dp_e2[sn_i]); } } // debug_simd_abpoa_print_cg_matrix_row("2", int32_t, index_i); /* compare M, E, and F */ for (sn_i = beg_sn; sn_i <= end_sn; ++sn_i) { /* SIMD parallelization */ dp_h[sn_i] = SIMDAddi32(dp_h[sn_i], q[sn_i]); } // debug_simd_abpoa_print_cg_matrix_row("3", int32_t, index_i); /* new F start */ first = SIMDShiftRight(SIMDShiftLeft(dp_h[beg_sn], SIMDTotalBytes-SIMDShiftOneNi32), SIMDTotalBytes-SIMDShiftOneNi32); int set_num; SIMDi first2 = first;//, tmp; for (sn_i = beg_sn; sn_i <= end_sn; ++sn_i) { if (sn_i < min_pre_beg_sn) { _err_fatal_simple(__func__, "sn_i < min_pre_beg_sn\n"); } else if (sn_i > max_pre_end_sn) { set_num = sn_i == max_pre_end_sn+1 ? 2 : 1; } else set_num = pn; /* H = max{H, E} */ dp_h[sn_i] = SIMDMaxi32(SIMDMaxi32(dp_h[sn_i], dp_e1[sn_i]), dp_e2[sn_i]); // tmp = dp_h[sn_i]; /* F = (H << 1 | x) - OE */ // if (sn_i==beg_sn) debug_simd_abpoa_print_cg_matrix_row("4.1", int32_t, index_i); dp_f1[sn_i] = SIMDSubi32(SIMDOri(SIMDShiftLeft(dp_h[sn_i], SIMDShiftOneNi32), first), GAP_OE1); dp_f2[sn_i] = SIMDSubi32(SIMDOri(SIMDShiftLeft(dp_h[sn_i], SIMDShiftOneNi32), first2), GAP_OE2); /* F = max{F, (F-e)<<1}, F = max{F, (F-2e)<<2} ... */ // if (sn_i==beg_sn) debug_simd_abpoa_print_cg_matrix_row("4.2", int32_t, index_i); SIMD_SET_F(dp_f1[sn_i], log_n, set_num, PRE_MIN, PRE_MASK, SUF_MIN, GAP_E1S, SIMDMaxi32, SIMDAddi32, SIMDSubi32, SIMDShiftOneNi32); SIMD_SET_F(dp_f2[sn_i], log_n, set_num, PRE_MIN, PRE_MASK, SUF_MIN, GAP_E2S, SIMDMaxi32, SIMDAddi32, SIMDSubi32, SIMDShiftOneNi32); /* x = max{H, F+o} */ // if (sn_i==beg_sn) debug_simd_abpoa_print_cg_matrix_row("4.3", int32_t, index_i); first = SIMDShiftRight(SIMDMaxi32(dp_h[sn_i], SIMDAddi32(dp_f1[sn_i], GAP_O1)), SIMDTotalBytes-SIMDShiftOneNi32); first2 = SIMDShiftRight(SIMDMaxi32(dp_h[sn_i], SIMDAddi32(dp_f2[sn_i], GAP_O2)), SIMDTotalBytes-SIMDShiftOneNi32); /* H = max{H, F} */ dp_h[sn_i] = SIMDMaxi32(SIMDMaxi32(dp_h[sn_i], dp_f1[sn_i]), dp_f2[sn_i]); // if (sn_i==beg_sn) debug_simd_abpoa_print_cg_matrix_row("4.4", int32_t, index_i); /* e for next cell */ // SIMDSetIfEquali32(dp_e1[sn_i], dp_h[sn_i], tmp, SIMDMaxi32(SIMDSubi32(dp_e1[sn_i], GAP_E1), SIMDSubi32(dp_h[sn_i], GAP_OE1)), SIMD_INF_MIN); dp_e1[sn_i] = SIMDMaxi32(SIMDSubi32(dp_e1[sn_i], GAP_E1), SIMDSubi32(dp_h[sn_i], GAP_OE1)); // SIMDSetIfEquali32(dp_e2[sn_i], dp_h[sn_i], tmp, SIMDMaxi32(SIMDSubi32(dp_e2[sn_i], GAP_E2), SIMDSubi32(dp_h[sn_i], GAP_OE2)), SIMD_INF_MIN); dp_e2[sn_i] = SIMDMaxi32(SIMDSubi32(dp_e2[sn_i], GAP_E2), SIMDSubi32(dp_h[sn_i], GAP_OE2)); } return tot_dp_sn; } void abpoa_cg_backtrack(SIMDi *DP_H2E2F, int **pre_index, int *pre_n, int *dp_beg, int *dp_end, int dp_sn, int m, int *mat, int gap_ext1, int gap_ext2, int gap_oe1, int gap_oe2, int beg_index, int best_dp_i, int best_dp_j, int qlen, abpoa_graph_t *graph, abpoa_para_t *abpt, uint8_t *query, abpoa_res_t *res) { int dp_i, dp_j, k, pre_i, n_c = 0, m_c = 0, id, hit, cur_op = ABPOA_ALL_OP, _start_i, _start_j; SIMDi *dp_h; int32_t s, is_match, *_dp_h=NULL, *_dp_e1, *_dp_e2, *_pre_dp_h, *_pre_dp_e1, *_pre_dp_e2, *_dp_f1, *_dp_f2; abpoa_cigar_t *cigar = 0; dp_i = best_dp_i, dp_j = best_dp_j, _start_i = best_dp_i, _start_j = best_dp_j; id = abpoa_graph_index_to_node_id(graph, dp_i+beg_index); if (best_dp_j < qlen) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, qlen-best_dp_j, -1, qlen-1); dp_h = DP_H2E2F + dp_sn * (dp_i * 5); _dp_h = (int32_t*)dp_h; int indel_first = 1; /* prefer to keep gaps at the end */ while (dp_i > 0 && dp_j > 0) { _start_i = dp_i, _start_j = dp_j; int *pre_index_i = pre_index[dp_i]; s = mat[m * graph->node[id].base + query[dp_j-1]]; hit = 0; is_match = graph->node[id].base == query[dp_j-1]; if ((cur_op & ABPOA_M_OP) && (indel_first == 0)) { for (k = 0; k < pre_n[dp_i]; ++k) { pre_i = pre_index_i[k]; if (dp_j-1 < dp_beg[pre_i] || dp_j-1 > dp_end[pre_i]) continue; _pre_dp_h = (int32_t*)(DP_H2E2F + dp_sn * (pre_i * 5)); if (_pre_dp_h[dp_j-1] + s == _dp_h[dp_j]) { /* match/mismatch */ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, dp_j-1); dp_i = pre_i; --dp_j; id = abpoa_graph_index_to_node_id(graph, dp_i+beg_index); hit = 1; dp_h = DP_H2E2F + dp_sn * (dp_i * 5); _dp_h = (int32_t*)dp_h; cur_op = ABPOA_ALL_OP; ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; break; } } } if (hit == 0 && cur_op & ABPOA_E_OP) { _dp_e1 = (int32_t*)(dp_h+dp_sn), _dp_e2 = (int32_t*)(dp_h+dp_sn*2); for (k = 0; k < pre_n[dp_i]; ++k) { pre_i = pre_index_i[k]; if (dp_j < dp_beg[pre_i] || dp_j > dp_end[pre_i]) continue; _pre_dp_h = (int32_t*)(DP_H2E2F + dp_sn * (pre_i * 5)); if (cur_op & ABPOA_E1_OP) { _pre_dp_e1 = (int32_t*)(DP_H2E2F + dp_sn * ((pre_i * 5) + 1)); if (cur_op & ABPOA_M_OP) { if (_dp_h[dp_j] == _pre_dp_e1[dp_j]) { if (_pre_dp_h[dp_j] - gap_oe1 == _pre_dp_e1[dp_j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; else cur_op = ABPOA_E1_OP; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, dp_j-1); dp_i = pre_i; id = abpoa_graph_index_to_node_id(graph, dp_i+beg_index); hit = 1; dp_h = DP_H2E2F + dp_sn * (dp_i * 5); _dp_h = (int32_t*)dp_h; break; } } else { if (_dp_e1[dp_j] == _pre_dp_e1[dp_j] - gap_ext1) { if (_pre_dp_h[dp_j] - gap_oe1 == _pre_dp_e1[dp_j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; else cur_op = ABPOA_E1_OP; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, dp_j-1); dp_i = pre_i; id = abpoa_graph_index_to_node_id(graph, dp_i+beg_index); hit = 1; dp_h = DP_H2E2F + dp_sn * (dp_i * 5); _dp_h = (int32_t*)dp_h; break; } } } if (cur_op & ABPOA_E2_OP) { _pre_dp_e2 = (int32_t*)(DP_H2E2F + dp_sn * ((pre_i * 5) + 2)); if (cur_op & ABPOA_M_OP) { if (_dp_h[dp_j] == _pre_dp_e2[dp_j]) { if (_pre_dp_h[dp_j] - gap_oe2 == _pre_dp_e2[dp_j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; else cur_op = ABPOA_E2_OP; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, dp_j-1); dp_i = pre_i; id = abpoa_graph_index_to_node_id(graph, dp_i+beg_index); hit = 1; dp_h = DP_H2E2F + dp_sn * (dp_i * 5); _dp_h = (int32_t*)dp_h; break; } } else { if (_dp_e2[dp_j] == _pre_dp_e2[dp_j] - gap_ext2) { if (_pre_dp_h[dp_j] - gap_oe2 == _pre_dp_e2[dp_j]) cur_op = ABPOA_M_OP | ABPOA_F_OP; else cur_op = ABPOA_E2_OP; cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CDEL, 1, id, dp_j-1); dp_i = pre_i; id = abpoa_graph_index_to_node_id(graph, dp_i+beg_index); hit = 1; dp_h = DP_H2E2F + dp_sn * (dp_i * 5); _dp_h = (int32_t*)dp_h; break; } } } } } if (hit == 0 && cur_op & ABPOA_F_OP) { if (cur_op & ABPOA_F1_OP) { _dp_f1 = (int32_t*)(dp_h + dp_sn * 3); if (cur_op & ABPOA_M_OP) { if (_dp_h[dp_j] == _dp_f1[dp_j]) { if (_dp_h[dp_j-1] - gap_oe1 == _dp_f1[dp_j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; else if (_dp_f1[dp_j-1] - gap_ext1 == _dp_f1[dp_j]) cur_op = ABPOA_F1_OP, hit = 1; } } else { if (_dp_h[dp_j-1] - gap_oe1 == _dp_f1[dp_j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; else if (_dp_f1[dp_j-1] - gap_ext1 == _dp_f1[dp_j]) cur_op = ABPOA_F1_OP, hit = 1; } } if (hit == 0 && cur_op & ABPOA_F2_OP) { _dp_f2 = (int32_t*)(dp_h + dp_sn * 4); if (cur_op & ABPOA_M_OP) { if (_dp_h[dp_j] == _dp_f2[dp_j]) { if (_dp_h[dp_j-1] - gap_oe2 == _dp_f2[dp_j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; else if (_dp_f2[dp_j-1] - gap_ext2 == _dp_f2[dp_j]) cur_op = ABPOA_F2_OP, hit = 1; } } else { if (_dp_h[dp_j-1] - gap_oe2 == _dp_f2[dp_j]) cur_op = ABPOA_M_OP | ABPOA_E_OP, hit = 1; else if (_dp_f2[dp_j-1] - gap_ext2 == _dp_f2[dp_j]) cur_op = ABPOA_F2_OP, hit = 1; } } if (hit == 1) { cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, 1, id, dp_j-1); --dp_j; ++res->n_aln_bases; } } if (hit == 0 && (cur_op & ABPOA_M_OP) && (indel_first == 1)) { for (k = 0; k < pre_n[dp_i]; ++k) { pre_i = pre_index_i[k]; if (dp_j-1 < dp_beg[pre_i] || dp_j-1 > dp_end[pre_i]) continue; _pre_dp_h = (int32_t*)(DP_H2E2F + dp_sn * (pre_i * 5)); if (_pre_dp_h[dp_j-1] + s == _dp_h[dp_j]) { /* match/mismatch */ cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CMATCH, 1, id, dp_j-1); dp_i = pre_i; --dp_j; id = abpoa_graph_index_to_node_id(graph, dp_i+beg_index); hit = 1; dp_h = DP_H2E2F + dp_sn * (dp_i * 5); _dp_h = (int32_t*)dp_h; cur_op = ABPOA_ALL_OP; ++res->n_aln_bases; res->n_matched_bases += is_match ? 1 : 0; indel_first = 0; break; } } } if (hit == 0) exit(1); #ifdef __DEBUG__ fprintf(stderr, "%d, %d, %d\n", dp_i, dp_j, cur_op); #endif } if (dp_j > 0) cigar = abpoa_push_cigar(&n_c, &m_c, cigar, ABPOA_CINS, dp_j, -1, dp_j-1); /* reverse cigar */ res->graph_cigar = abpt->rev_cigar ? cigar : abpoa_reverse_cigar(n_c, cigar); res->n_cigar = n_c; res->m_cigar = m_c; res->node_e = abpoa_graph_index_to_node_id(graph, best_dp_i+beg_index), res->query_e = best_dp_j-1; /* 0-based */ res->node_s = abpoa_graph_index_to_node_id(graph, _start_i+beg_index), res->query_s = _start_j-1; /*abpoa_print_cigar(n_c, *graph_cigar, graph);*/ } int abpoa_cg_global_align_sequence_to_graph_core(abpoa_t *ab, int beg_node_id, int beg_index, int end_node_id, int end_index, uint8_t *index_map, int qlen, uint8_t *query, abpoa_para_t *abpt, SIMD_para_t sp, abpoa_res_t *res) { int tot_dp_sn = 0; abpoa_graph_t *graph = ab->abg; abpoa_simd_matrix_t *abm = ab->abm; int matrix_row_n = end_index-beg_index+1, matrix_col_n = qlen + 1; int **pre_index, *pre_n, _pre_index, _pre_n; int i, j, *dp_beg, *dp_beg_sn, *dp_end, *dp_end_sn, node_id, index_i, dp_i; int beg_sn, end_sn; int pn, log_n, size, qp_sn, dp_sn; /* pn: value per SIMDi, qp_sn/dp_sn/d_sn: segmented length*/ SIMDi *dp_h, *qp, *qi; int32_t best_score = sp.inf_min, inf_min = sp.inf_min; int *mat = abpt->mat, best_i = 0, best_j = 0; int32_t gap_ext1 = abpt->gap_ext1; int w = abpt->wb < 0 ? qlen : abpt->wb + (int)(abpt->wf * qlen); /* when w < 0, do whole global */ SIMDi zero = SIMDSetZeroi(), SIMD_INF_MIN = SIMDSetOnei32(inf_min); pn = sp.num_of_value; qp_sn = dp_sn = (matrix_col_n + pn - 1) / pn, log_n = sp.log_num, size = sp.size; qp = abm->s_mem; int32_t gap_open1 = abpt->gap_open1, gap_oe1 = gap_open1 + gap_ext1; int32_t gap_open2 = abpt->gap_open2, gap_ext2 = abpt->gap_ext2, gap_oe2 = gap_open2 + gap_ext2; SIMDi *DP_H2E2F, *dp_e1, *dp_e2, *dp_f2, *dp_f1; SIMDi GAP_O1 = SIMDSetOnei32(gap_open1), GAP_O2 = SIMDSetOnei32(gap_open2), GAP_E1 = SIMDSetOnei32(gap_ext1), GAP_E2 = SIMDSetOnei32(gap_ext2), GAP_OE1 = SIMDSetOnei32(gap_oe1), GAP_OE2 = SIMDSetOnei32(gap_oe2); DP_H2E2F = qp + qp_sn * abpt->m; qi = DP_H2E2F + dp_sn * matrix_row_n * 5; // for SET_F mask[pn], suf_min[pn], pre_min[logN] SIMDi *PRE_MASK, *SUF_MIN, *PRE_MIN, *GAP_E1S, *GAP_E2S; PRE_MASK = (SIMDi*)SIMDMalloc((pn+1) * size, size), SUF_MIN = (SIMDi*)SIMDMalloc((pn+1) * size, size), PRE_MIN = (SIMDi*)SIMDMalloc(pn * size, size), GAP_E1S = (SIMDi*)SIMDMalloc(log_n * size, size), GAP_E2S = (SIMDi*)SIMDMalloc(log_n * size, size); for (i = 0; i < pn; ++i) { int32_t *pre_mask = (int32_t*)(PRE_MASK + i); for (j = 0; j <= i; ++j) pre_mask[j] = -1; for (j = i+1; j < pn; ++j) pre_mask[j] = 0; } PRE_MASK[pn] = PRE_MASK[pn-1]; SUF_MIN[0] = SIMDShiftLeft(SIMD_INF_MIN, SIMDShiftOneNi32); for (i = 1; i < pn; ++i) SUF_MIN[i] = SIMDShiftLeft(SUF_MIN[i-1], SIMDShiftOneNi32); SUF_MIN[pn] = SUF_MIN[pn-1]; for (i = 1; i < pn; ++i) { int32_t *pre_min = (int32_t*)(PRE_MIN + i); for (j = 0; j < i; ++j) pre_min[j] = inf_min; for (j = i; j < pn; ++j) pre_min[j] = 0; } GAP_E1S[0] = GAP_E1; GAP_E2S[0] = GAP_E2; for (i = 1; i < log_n; ++i) { GAP_E1S[i] = SIMDAddi32(GAP_E1S[i-1], GAP_E1S[i-1]); GAP_E2S[i] = SIMDAddi32(GAP_E2S[i-1], GAP_E2S[i-1]); } abpoa_init_var(abpt, query, qlen, qp, qi, mat, qp_sn, pn, SIMD_INF_MIN); dp_beg = abm->dp_beg, dp_end = abm->dp_end, dp_beg_sn = abm->dp_beg_sn, dp_end_sn = abm->dp_end_sn; /* index of pre-node */ pre_index = (int**)_err_calloc(matrix_row_n, sizeof(int*)); pre_n = (int*)_err_calloc(matrix_row_n, sizeof(int)); for (index_i = beg_index+1, dp_i = 1; index_i <= end_index; ++index_i, ++dp_i) { node_id = abpoa_graph_index_to_node_id(graph, index_i); pre_n[dp_i] = graph->node[node_id].in_edge_n; pre_index[dp_i] = (int*)_err_malloc(pre_n[dp_i] * sizeof(int)); _pre_n = 0; for (j = 0; j < pre_n[dp_i]; ++j) { _pre_index = abpoa_graph_node_id_to_index(graph, graph->node[node_id].in_id[j]); if (index_map[_pre_index]) pre_index[dp_i][_pre_n++] = _pre_index-beg_index; } pre_n[dp_i] = _pre_n; } abpoa_cg_first_dp(abpt, graph, index_map, beg_node_id, end_node_id, dp_beg, dp_end, dp_beg_sn, dp_end_sn, pn, qlen, w, dp_sn, DP_H2E2F, SIMD_INF_MIN, inf_min, gap_open1, gap_ext1, gap_open2, gap_ext2, gap_oe1, gap_oe2); for (index_i=beg_index+1, dp_i=1; index_inode[node_id].base * qp_sn; dp_h = DP_H2E2F + (dp_i*5) * dp_sn; dp_e1 = dp_h + dp_sn; dp_e2 = dp_e1 + dp_sn; dp_f1 = dp_e2 + dp_sn; dp_f2 = dp_f1 + dp_sn; tot_dp_sn += abpoa_cg_dp(q, dp_h, dp_e1, dp_e2, dp_f1, dp_f2, pre_index, pre_n, index_i, dp_i, graph, abpt, dp_sn, pn, qlen, w, DP_H2E2F, SIMD_INF_MIN, GAP_O1, GAP_O2, GAP_E1, GAP_E2, GAP_OE1, GAP_OE2, GAP_E1S, GAP_E2S, PRE_MIN, PRE_MASK, SUF_MIN, log_n, dp_beg, dp_end, dp_beg_sn, dp_end_sn, end_node_id); if (abpt->wb >= 0) { beg_sn = dp_beg_sn[dp_i], end_sn = dp_end_sn[dp_i]; int max_i = abpoa_max(SIMD_INF_MIN, zero, inf_min, dp_h, qi, qlen, pn, beg_sn, end_sn); abpoa_ada_max_i(max_i, graph, node_id); } } // printf("dp_sn: %d\n", tot_dp_sn); // printf("dp_sn: %d, node_n: %d, seq_n: %d\n", tot_dp_sn, graph->node_n, qlen); abpoa_global_get_max(graph, beg_index, end_node_id, index_map, DP_H2E2F, 5*dp_sn, qlen, dp_end, &best_score, &best_i, &best_j); #ifdef __DEBUG__ simd_abpoa_print_cg_matrix(int32_t, beg_index, end_index); fprintf(stderr, "best_score: (%d, %d) -> %d\n", best_i, best_j, best_score); #endif res->best_score = best_score; abpoa_cg_backtrack(DP_H2E2F, pre_index, pre_n, dp_beg, dp_end, dp_sn, abpt->m, mat, gap_ext1, gap_ext2, gap_oe1, gap_oe2, beg_index, best_i, best_j, qlen, graph, abpt, query, res); for (i = 0; i < matrix_row_n; ++i) free(pre_index[i]); free(pre_index); free(pre_n); SIMDFree(PRE_MASK); SIMDFree(SUF_MIN); SIMDFree(PRE_MIN); SIMDFree(GAP_E1S); SIMDFree(GAP_E2S); return best_score; } // align query to subgraph between beg_node_id and end_node_id (both are excluded) // generally: beg/end are the SRC/SINK_node int simd_abpoa_align_sequence_to_subgraph(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int qlen, abpoa_res_t *res) { // if (abpt->simd_flag == 0) err_fatal_simple("No SIMD instruction available."); int i, j, beg_index = ab->abg->node_id_to_index[beg_node_id], end_index = ab->abg->node_id_to_index[end_node_id]; int gn = end_index - beg_index + 1; uint8_t *index_map = (uint8_t*)_err_calloc(ab->abg->node_n, sizeof(uint8_t)); index_map[beg_index] = index_map[end_index] = 1; for (i = beg_index; i < end_index-1; ++i) { if (index_map[i] == 0) continue; int node_id = abpoa_graph_index_to_node_id(ab->abg, i); int out_n = ab->abg->node[node_id].out_edge_n; for (j = 0; j < out_n; ++j) { int out_id = ab->abg->node[node_id].out_id[j]; index_map[abpoa_graph_node_id_to_index(ab->abg, out_id)] = 1; } } #ifdef __DEBUG__ _simd_p32.inf_min = MAX_OF_TWO(abpt->gap_ext1, abpt->gap_ext2) * 31 +MAX_OF_THREE(INT32_MIN + abpt->min_mis, INT32_MIN + abpt->gap_open1 + abpt->gap_ext1, INT32_MIN + abpt->gap_open2 + abpt->gap_ext2); if (simd_abpoa_realloc(ab, gn, qlen, abpt, _simd_p32)) return 0; if (abpt->gap_mode == ABPOA_CONVEX_GAP) abpoa_cg_global_align_sequence_to_graph_core(ab, beg_node_id, beg_index, end_node_id, end_index, index_map, qlen, query, abpt, _simd_p32, res); #else int32_t max_score, bits, mem_ret=0, gap_ext1 = abpt->gap_ext1, gap_ext2 = abpt->gap_ext2; int32_t gap_oe1 = abpt->gap_open1+gap_ext1, gap_oe2 = abpt->gap_open2+gap_ext2; // if (abpt->simd_flag & SIMD_AVX512F && !(abpt->simd_flag & SIMD_AVX512BW)) // max_score = INT16_MAX + 1; // AVX512F has no 8/16 bits operations // else { int len = qlen > gn ? qlen : gn; max_score = MAX_OF_TWO(qlen * abpt->max_mat, len * abpt->gap_ext1 + abpt->gap_open1); // } if (max_score <= INT16_MAX - abpt->min_mis - gap_oe1 - gap_oe2) { _simd_p16.inf_min = MAX_OF_THREE(INT16_MIN + abpt->min_mis, INT16_MIN + gap_oe1, INT16_MIN + gap_oe2) + 31 * MAX_OF_TWO(gap_ext1, gap_ext2); mem_ret = simd_abpoa_realloc(ab, gn, qlen, abpt, _simd_p16); bits = 16; } else { _simd_p32.inf_min = MAX_OF_THREE(INT32_MIN + abpt->min_mis, INT32_MIN + gap_oe1, INT32_MIN + gap_oe2) + 31 * MAX_OF_TWO(gap_ext1, gap_ext2); mem_ret = simd_abpoa_realloc(ab, gn, qlen, abpt, _simd_p32); bits = 32; } if (mem_ret) return 0; if (bits == 16) { if (abpt->gap_mode == ABPOA_LINEAR_GAP) { simd_abpoa_lg_align_sequence_to_graph_core(int16_t, _simd_p16, SIMDSetOnei16, SIMDMaxi16, \ SIMDAddi16, SIMDSubi16, SIMDShiftOneNi16, SIMDSetIfGreateri16, SIMDGetIfGreateri16); } else if (abpt->gap_mode == ABPOA_AFFINE_GAP) { simd_abpoa_ag_align_sequence_to_graph_core(int16_t, _simd_p16, SIMDSetOnei16, SIMDMaxi16, \ SIMDAddi16, SIMDSubi16, SIMDShiftOneNi16, SIMDSetIfGreateri16, SIMDGetIfGreateri16, SIMDSetIfEquali16); } else if (abpt->gap_mode == ABPOA_CONVEX_GAP) { simd_abpoa_cg_align_sequence_to_graph_core(int16_t, _simd_p16, SIMDSetOnei16, SIMDMaxi16, \ SIMDAddi16, SIMDSubi16, SIMDShiftOneNi16, SIMDSetIfGreateri16, SIMDGetIfGreateri16, SIMDSetIfEquali16); } } else { // 2147483647, DP_H/E/F: 32 bits if (abpt->gap_mode == ABPOA_LINEAR_GAP) { simd_abpoa_lg_align_sequence_to_graph_core(int32_t, _simd_p32, SIMDSetOnei32, SIMDMaxi32, \ SIMDAddi32, SIMDSubi32, SIMDShiftOneNi32, SIMDSetIfGreateri32, SIMDGetIfGreateri32); } else if (abpt->gap_mode == ABPOA_AFFINE_GAP) { simd_abpoa_ag_align_sequence_to_graph_core(int32_t, _simd_p32, SIMDSetOnei32, SIMDMaxi32, \ SIMDAddi32, SIMDSubi32, SIMDShiftOneNi32, SIMDSetIfGreateri32, SIMDGetIfGreateri32, SIMDSetIfEquali32); } else if (abpt->gap_mode == ABPOA_CONVEX_GAP) { simd_abpoa_cg_align_sequence_to_graph_core(int32_t, _simd_p32, SIMDSetOnei32, SIMDMaxi32, \ SIMDAddi32, SIMDSubi32, SIMDShiftOneNi32, SIMDSetIfGreateri32, SIMDGetIfGreateri32, SIMDSetIfEquali32); } } #endif free(index_map); return 0; } int simd_abpoa_align_sequence_to_graph(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int qlen, abpoa_res_t *res) { return simd_abpoa_align_sequence_to_subgraph(ab, abpt, ABPOA_SRC_NODE_ID, ABPOA_SINK_NODE_ID, query, qlen, res); } abPOA-1.4.1/src/simd_abpoa_align.h000066400000000000000000000010521425041320700166430ustar00rootroot00000000000000#ifndef SIMD_ABPOA_ALIGN_H #define SIMD_ABPOA_ALIGN_H #include "abpoa.h" #include "abpoa_graph.h" #ifdef __cplusplus extern "C" { #endif int simd_abpoa_align_sequence_to_graph(abpoa_t *ab, abpoa_para_t *abpt, uint8_t *query, int qlen, abpoa_res_t *res); int simd_abpoa_align_sequence_to_subgraph(abpoa_t *ab, abpoa_para_t *abpt, int beg_node_id, int end_node_id, uint8_t *query, int qlen, abpoa_res_t *res); abpoa_simd_matrix_t *abpoa_init_simd_matrix(void); void abpoa_free_simd_matrix(abpoa_simd_matrix_t *abm); #ifdef __cplusplus } #endif #endif abPOA-1.4.1/src/simd_check.c000066400000000000000000000053141425041320700154640ustar00rootroot00000000000000#include "simd_instruction.h" //https://stackoverflow.com/questions/152016/detecting-cpu-architecture-compile-time #if MSVC #ifdef _M_X86 #define ARCH_X86 #endif #endif #if GCC #ifdef __i386__ #define ARCH_X86 #endif #endif #ifndef ARCH_X86 int simd_check(void) { return SIMD_AVX2; } #else #ifndef _MSC_VER // adapted from https://github.com/01org/linux-sgx/blob/master/common/inc/internal/linux/cpuid_gnu.h void __cpuidex(int cpuid[4], int func_id, int subfunc_id) { #if defined(__x86_64__) __asm__ volatile ("cpuid" : "=a" (cpuid[0]), "=b" (cpuid[1]), "=c" (cpuid[2]), "=d" (cpuid[3]) : "0" (func_id), "2" (subfunc_id)); #else // on 32bit, ebx can NOT be used as PIC code __asm__ volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" : "=a" (cpuid[0]), "=r" (cpuid[1]), "=c" (cpuid[2]), "=d" (cpuid[3]) : "0" (func_id), "2" (subfunc_id)); #endif } #endif int simd_check(void) { int flag = 0, cpuid[4], max_id; __cpuidex(cpuid, 0, 0); // int i; // for (i = 0 ; i < 4; ++i) printf("%d\t", cpuid[i]); printf("\n"); max_id = cpuid[0]; if (max_id == 0) return 0; __cpuidex(cpuid, 1, 0); // for (i = 0 ; i < 4; ++i) printf("%d\t", cpuid[i]); printf("\n"); if (cpuid[3]>>25&1) flag |= SIMD_SSE; if (cpuid[3]>>26&1) flag |= SIMD_SSE2; if (cpuid[2]>>0 &1) flag |= SIMD_SSE3; if (cpuid[2]>>9 &1) flag |= SIMD_SSSE3; if (cpuid[2]>>19&1) flag |= SIMD_SSE41; if (cpuid[2]>>20&1) flag |= SIMD_SSE42; if (cpuid[2]>>28&1) flag |= SIMD_AVX; if (max_id >= 7) { __cpuidex(cpuid, 7, 0); // for (i = 0 ; i < 4; ++i) printf("%d\t", cpuid[i]); printf("\n"); if (cpuid[1]>>5 &1) flag |= SIMD_AVX2; if (cpuid[1]>>16&1) flag |= SIMD_AVX512F; if (cpuid[1]>>30&1) flag |= SIMD_AVX512BW; } return flag; } #ifdef __CHECK_SIMD_MAIN__ int main(void) { char simd_label[6][20] = {"No SIMD", "SSE2 (128 bits)", "SSE4.1 (128 bits)", "AVX2 (256 bits)", "AVX512F (512 bits)", "AVX512BW (512 bits)"}; int simd_flag = simd_check(), t=0; if (simd_flag & SIMD_AVX512BW) printf("__AVX512BW__\n"), t = 5; else if (simd_flag & SIMD_AVX512F) printf("__AVX512F__\n"), t = 4; else if (simd_flag & SIMD_AVX2) printf("__AVX2__\n"), t = 3; else if (simd_flag & SIMD_SSE41) printf("__SSE4_1__\n"), t = 2; else if (simd_flag & SIMD_SSE2) printf("__SSE2__\n"), t = 1; else printf("NO SIMD\n"), t = 0; char msg[100], i; fprintf(stderr, "\n"); sprintf(msg, "==== %s will be used. ====", simd_label[t]); for (i = 0; msg[i]; ++i) fprintf(stderr, "="); fprintf(stderr, "\n"); fprintf(stderr, "%s\n",msg); for (i = 0; msg[i]; ++i) fprintf(stderr, "="); fprintf(stderr, "\n"); fprintf(stderr, "\n"); return simd_flag; } #endif #endif abPOA-1.4.1/src/simd_instruction.h000066400000000000000000000764601425041320700170070ustar00rootroot00000000000000// A header file to get you set going with Intel SIMD instrinsic programming. // is inlucded for SSE2, SSE41, AVX2 and AVX512F, AVX512BW // SSE4.1: floor and blend is available) // AVX2: double speed // do not support AVX512F/AVX512BW 12/20/2021 - Yan Gao // AVX512F: quardruple speed // AVX512BW: byte and word operation #include #include #pragma once #ifndef SIMD_INSTRUCTION_H #define SIMD_INSTRUCTION_H #undef __AVX512F__ #undef __AVX512BW__ #ifndef USE_SIMDE #include #else // use SIMDE #ifdef __AVX512F__ #include "simde/simde/x86/avx512.h" #else #ifdef __AVX2__ #include "simde/simde/x86/avx2.h" #else #ifdef __SSE4_1__ #include "simde/simde/x86/sse4.1.h" #else #include "simde/simde/x86/sse2.h" #endif // end of sse41 #endif // end of AVX2 #endif // end of 512F #endif // end of USE_SIMDE #include #include #include #include #define SIMD_SSE 0x1 #define SIMD_SSE2 0x2 #define SIMD_SSE3 0x4 #define SIMD_SSSE3 0x8 #define SIMD_SSE41 0x10 #define SIMD_SSE42 0x20 #define SIMD_AVX 0x40 #define SIMD_AVX2 0x80 #define SIMD_AVX512F 0x100 #define SIMD_AVX512BW 0x200 // #define SIMDFree(x) _mm_free(x) // posix_memalign and free #define SIMDFree(x) free(x) // Shift, Blend, ... for 8/16 and 32/64 #ifdef __AVX512BW__ // start of AVX512BW typedef __m512 SIMDf; typedef __m512i SIMDi; #define SIMDStore(x,y) _mm512_store_ps(x,y) #define SIMDStorei(x,y) _mm512_store_si512(x,y) #define SIMDLoad(x) _mm512_load_ps(x) #define SIMDLoadi(x) _mm512_load_si512(x) #define SIMDZero _mm512_setzero_si512() #define SIMDSetZero() _mm512_setzero_ps() #define SIMDSetZeroi() _mm512_setzero_si512() #define SIMDSetOne(x) _mm512_set1_ps(x) #define SIMDSetOnei8(x) _mm512_set1_epi8(x) #define SIMDSetOnei16(x) _mm512_set1_epi16(x) #define SIMDSetOnei32(x) _mm512_set1_epi32(x) #define SIMDSetOnei64(x) _mm512_set1_epi64(x) #define SIMDAdd(x,y) _mm512_add_ps(x,y) #define SIMDAddi8(x,y) _mm512_add_epi8(x,y) #define SIMDAddi16(x,y) _mm512_add_epi16(x,y) #define SIMDAddi32(x,y) _mm512_add_epi32(x,y) #define SIMDAddi64(x,y) _mm512_add_epi64(x,y) #define SIMDSub(x,y) _mm512_sub_ps(x,y) #define SIMDSubi8(x,y) _mm512_sub_epi8(x,y) #define SIMDSubi16(x,y) _mm512_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm512_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm512_sub_epi64(x,y) #define SIMDMul(x,y) _mm512_mul_ps(x,y) #define SIMDMuli32(x,y) _mm512_mul_epi32(x,y) #define SIMDAnd(x,y) _mm512_and_ps(x,y) #define SIMDAndi(x,y) _mm512_and_si512(x,y) #define SIMDAndNot(x,y) _mm512_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm512_andnot_si512(x,y) #define SIMDOr(x,y) _mm512_or_ps(x,y) #define SIMDOri(x,y) _mm512_or_si512(x,y) #define SIMDShiftLeft(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(x, _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), (16-(n))) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), (32-(n))) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), (48-(n))) : \ _mm512_bslli_epi128(_mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), ((n)-48)))) /* static inline SIMDi SIMDShiftLeft(SIMDi x, const int n) { // x=a|b|c|d SIMDi tmp1,tmp2; if (n < 16) { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)); // tmp1=0|0|c|d tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(2,1,0,2)); // tmp2=b|c|d|0 return _mm512_alignr_epi8(x, tmp2, 16 - n); } else if (n < 32) { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)); // tmp1=0|0|c|d tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(2,1,0,2)); // tmp2=b|c|d|0 tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)); // tmp1=c|d|0|0 return _mm512_alignr_epi8(tmp2, tmp1, 32 - n); } else if (n < 48) { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)); // tmp1=c|d|0|0 tmp2 = _mm512_shuffle_i64x2(SIMDZero, tmp1, _MM_SHUFFLE(2,0,0,0)); // tmp2=d|0|0|0 return _mm512_alignr_epi8(tmp1, tmp2, 48 - n); } else { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)); // tmp1=c|d|0|0 tmp2 = _mm512_shuffle_i64x2(SIMDZero, tmp1, _MM_SHUFFLE(2,0,0,0)); // tmp2=d|0|0|0 return _mm512_bslli_epi128(tmp2, n - 48); } }*/ #define SIMDShiftRight(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2( _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), x, (n)) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), ((n)-16)) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), ((n)-32)) : \ _mm512_bsrli_epi128(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), ((n)-48)))) /* static inline SIMDi SIMDShiftRight(SIMDi x, int n) { // x=a|b|c|d SIMDi tmp1, tmp2; if (n < 16) { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)); // tmp1=a|b|0|0 tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(0,3,2,1)); // tmp2=0|a|b|c return _mm512_alignr_epi8(tmp2, x, n); } else if (n < 32) { tmp1 = _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)); // tmp1=a|b|0|0 tmp2 = _mm512_shuffle_i64x2(tmp1, x, _MM_SHUFFLE(0,3,2,1)); // tmp2=0|a|b|c tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)); // tmp1=0|0|a|b return _mm512_alignr_epi8(tmp1, tmp2, n-16); } else if (n < 48) { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)); // tmp1=0|0|a|b tmp2 = _mm512_shuffle_i64x2(tmp1, SIMDZero, _MM_SHUFFLE(0,0,2,1)); // tmp2=0|0|0|a return _mm512_alignr_epi8(tmp2, tmp1, n-32); } else { tmp1 = _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)); // tmp1=0|0|a|b tmp2 = _mm512_shuffle_i64x2(tmp1, SIMDZero, _MM_SHUFFLE(0,0,2,1)); // tmp2=0|0|0|a return _mm512_bsrli_epi128(tmp2, n - 48); } }*/ #define SIMDShiftLeftOnei16(x,y) _mm512_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm512_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm512_slli_epi64(x,y) #define SIMDShiftRightOnei16(x,y) _mm512_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm512_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm512_srli_epi64(x,y) #define SIMDEqualM(x,y) _mm512_cmpeq_ps_mask(x,y) #define SIMDEquali8M(x,y) _mm512_cmpeq_epi8_mask(x,y) #define SIMDEquali16M(x,y) _mm512_cmpeq_epi16_mask(x,y) #define SIMDEquali32M(x,y) _mm512_cmpeq_epi32_mask(x,y) #define SIMDEquali64M(x,y) _mm512_cmpeq_epi64_mask(x,y) #define SIMDNotEqualM(x,y) _mm512_cmpneq_ps_mask(x,y) #define SIMDNotEquali8M(x,y) _mm512_cmpneq_epi8_mask(x,y) #define SIMDNotEquali16M(x,y) _mm512_cmpneq_epi16_mask(x,y) #define SIMDNotEquali32M(x,y) _mm512_cmpneq_epi32_mask(x,y) #define SIMDNotEquali64M(x,y) _mm512_cmpneq_epi64_mask(x,y) #define SIMDGreaterThani8M(x,y) _mm512_cmpgt_epi8_mask(x,y) #define SIMDGreaterThani16M(x,y) _mm512_cmpgt_epi16_mask(x,y) #define SIMDGreaterThani32M(x,y) _mm512_cmpgt_epi32_mask(x,y) #define SIMDGreaterThani64M(x,y) _mm512_cmpgt_epi64_mask(x,y) #define SIMDGreaterThanOrEquali8M(x,y) _mm512_cmpge_epi8_mask(x,y) #define SIMDGreaterThanOrEquali16M(x,y) _mm512_cmpge_epi16_mask(x,y) #define SIMDGreaterThanOrEquali32M(x,y) _mm512_cmpge_epi32_mask(x,y) #define SIMDGreaterThanOrEquali64M(x,y) _mm512_cmpge_epi64_mask(x,y) #define SIMDLessThanM(x,y) _mm512_cmplt_ps_mask(x,y) #define SIMDLessThani8M(x,y) _mm512_cmplt_epi8_mask(x,y) #define SIMDLessThani16M(x,y) _mm512_cmplt_epi16_mask(x,y) #define SIMDLessThani32M(x,y) _mm512_cmplt_epi32_mask(x,y) #define SIMDLessThani64M(x,y) _mm512_cmplt_epi64_mask(x,y) #define SIMDLessThanOrEqualM(x,y) _mm512_cmple_ps_mask(x,y) #define SIMDLessThanOrEquali8M(x,y) _mm512_cmple_epi8_mask(x,y) #define SIMDLessThanOrEquali16M(x,y) _mm512_cmple_epi16_mask(x,y) #define SIMDLessThanOrEquali32M(x,y) _mm512_cmple_epi32_mask(x,y) #define SIMDLessThanOrEquali64M(x,y) _mm512_cmple_epi64_mask(x,y) #define SIMDMax(x,y) _mm512_max_ps(x,y) #define SIMDMaxi8(x,y) _mm512_max_epi8(x,y) #define SIMDMaxi16(x,y) _mm512_max_epi16(x,y) #define SIMDMaxi32(x,y) _mm512_max_epi32(x,y) #define SIMDMaxi64(x,y) _mm512_max_epi64(x,y) #define SIMDMin(x,y) _mm512_min_ps(x,y) #define SIMDMini8(x,y) _mm512_min_epi8(x,y) #define SIMDMini16(x,y) _mm512_min_epi16(x,y) #define SIMDMini32(x,y) _mm512_min_epi32(x,y) #define SIMDMini64(x,y) _mm512_min_epi64(x,y) #define SIMDBlend(x,y,z) _mm512_mask_blend_ps(z, x, y) #define SIMDBlendi8(x,y,z) _mm512_mask_blend_epi8(z, x, y) #define SIMDBlendi16(x,y,z) _mm512_mask_blend_epi16(z, x, y) #define SIMDBlendi32(x,y,z) _mm512_mask_blend_epi32(z, x, y) #define SIMDBlendi64(x,y,z) _mm512_mask_blend_epi64(z, x, y) // with AVX512BW #define Maski8 __mmask64 #define Maski16 __mmask32 #define Maski32 __mmask16 #define Maski64 __mmask8 /* x = a == b ? c : d */ #define SIMDSetIfEquali8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDEquali8M(a,b)); } #define SIMDSetIfEquali16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDEquali16M(a,b)); } #define SIMDSetIfEquali32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDEquali32M(a,b)); } #define SIMDSetIfEquali64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDEquali64M(a,b)); } /* x = a > b ? c : d */ #define SIMDSetIfGreateri8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDGreaterThani8M(a,b)); } #define SIMDSetIfGreateri16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16M(a,b)); } #define SIMDSetIfGreateri32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32M(a,b)); } #define SIMDSetIfGreateri64(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani64M(a,b)); } /* x = a < b ? c : d */ #define SIMDSetIfLessi8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDGreaterThani8M(b,a)); } #define SIMDSetIfLessi16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16M(b,a)); } #define SIMDSetIfLessi32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32M(b,a)); } #define SIMDSetIfLessi64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani64M(b,a)); } /* x = a > b ? c : d, y = a > b ? a : b */ #define SIMDGetIfGreateri8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(a,b); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } #define SIMDGetIfGreateri16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(a,b); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfGreateri32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(a,b); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfGreateri64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(a,b); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } /* x = a < b ? c : d, y = a < b ? a : b */ #define SIMDGetIfLessi8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(b,a); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } #define SIMDGetIfLessi16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(b,a); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfLessi32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(b,a); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfLessi64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(b,a); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } // end of AVX512BW #else #ifdef __AVX512F__ // start of AVX512F // XXX AVX512F has no following instructions (AVX512BW HAS), so AVX512F is not working for 8/16 bits tasks // addi8/16, subi8/16, alignri8, bslli_epi128, bslrli_epi128, // comeqi8/16, cmpneqi8/16, cmpgti8/16, cmpgei8/16, cmplti8/16, cmplei8 // maxi8/16, blendi8/i16, slli_epi16,srli_epi16 typedef __m512 SIMDf; typedef __m512i SIMDi; #define SIMDStore(x,y) _mm512_store_ps(x,y) #define SIMDStorei(x,y) _mm512_store_si512(x,y) #define SIMDLoad(x) _mm512_load_ps(x) #define SIMDLoadi(x) _mm512_load_si512(x) #define SIMDZero _mm512_setzero_si512() #define SIMDSetZero() _mm512_setzero_ps() #define SIMDSetZeroi() _mm512_setzero_si512() #define SIMDSetOne(x) _mm512_set1_ps(x) #define SIMDSetOnei8(x) _mm512_set1_epi8(x) #define SIMDSetOnei16(x) _mm512_set1_epi16(x) #define SIMDSetOnei32(x) _mm512_set1_epi32(x) #define SIMDSetOnei64(x) _mm512_set1_epi64(x) #define SIMDAdd(x,y) _mm512_add_ps(x,y) //#define SIMDAddi8(x,y) _mm512_add_epi8(x,y) //#define SIMDAddi16(x,y) _mm512_add_epi16(x,y) #define SIMDAddi32(x,y) _mm512_add_epi32(x,y) #define SIMDAddi64(x,y) _mm512_add_epi64(x,y) #define SIMDSub(x,y) _mm512_sub_ps(x,y) //#define SIMDSubi8(x,y) _mm512_sub_epi8(x,y) //#define SIMDSubi16(x,y) _mm512_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm512_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm512_sub_epi64(x,y) #define SIMDMul(x,y) _mm512_mul_ps(x,y) #define SIMDMuli32(x,y) _mm512_mul_epi32(x,y) #define SIMDAnd(x,y) _mm512_and_ps(x,y) #define SIMDAndi(x,y) _mm512_and_si512(x,y) #define SIMDAndNot(x,y) _mm512_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm512_andnot_si512(x,y) #define SIMDOr(x,y) _mm512_or_ps(x,y) #define SIMDOri(x,y) _mm512_or_si512(x,y) /*#define SIMDShiftLeft(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(x, _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), (16-(n))) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,1,0)), x, _MM_SHUFFLE(2,1,0,2)), _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), (32-(n))) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), (48-(n))) : \ _mm512_bslli_epi128(_mm512_shuffle_i64x2(SIMDZero, _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(1,0,0,0)), _MM_SHUFFLE(2,0,0,0)), ((n)-48)))) #define SIMDShiftRight(x,n) \ (n) < 16 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2( _mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), x, (n)) : \ ((n) < 32 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), _mm512_shuffle_i64x2(_mm512_shuffle_i64x2(SIMDZero, x, _MM_SHUFFLE(3,2,0,0)), x, _MM_SHUFFLE(0,3,2,1)), ((n)-16)) : \ ((n) < 48 ? \ _mm512_alignr_epi8(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), _mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), ((n)-32)) : \ _mm512_bsrli_epi128(_mm512_shuffle_i64x2(_mm512_shuffle_i64x2(x, SIMDZero, _MM_SHUFFLE(0,0,3,2)), SIMDZero, _MM_SHUFFLE(0,0,2,1)), ((n)-48))))*/ //#define SIMDShiftLeftOnei16(x,y) _mm512_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm512_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm512_slli_epi64(x,y) //#define SIMDShiftRightOnei16(x,y) _mm512_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm512_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm512_srli_epi64(x,y) #define SIMDEqualM(x,y) _mm512_cmpeq_ps_mask(x,y) //#define SIMDEquali8M(x,y) _mm512_cmpeq_epi8_mask(x,y) //#define SIMDEquali16M(x,y) _mm512_cmpeq_epi16_mask(x,y) #define SIMDEquali32M(x,y) _mm512_cmpeq_epi32_mask(x,y) #define SIMDEquali64M(x,y) _mm512_cmpeq_epi64_mask(x,y) #define SIMDNotEqualM(x,y) _mm512_cmpneq_ps_mask(x,y) //#define SIMDNotEquali8M(x,y) _mm512_cmpneq_epi8_mask(x,y) //#define SIMDNotEquali16M(x,y) _mm512_cmpneq_epi16_mask(x,y) #define SIMDNotEquali32M(x,y) _mm512_cmpneq_epi32_mask(x,y) #define SIMDNotEquali64M(x,y) _mm512_cmpneq_epi64_mask(x,y) //#define SIMDGreaterThani8M(x,y) _mm512_cmpgt_epi8_mask(x,y) //#define SIMDGreaterThani16M(x,y) _mm512_cmpgt_epi16_mask(x,y) #define SIMDGreaterThani32M(x,y) _mm512_cmpgt_epi32_mask(x,y) #define SIMDGreaterThani64M(x,y) _mm512_cmpgt_epi64_mask(x,y) //#define SIMDGreaterThanOrEquali8M(x,y) _mm512_cmpge_epi8_mask(x,y) //#define SIMDGreaterThanOrEquali16M(x,y) _mm512_cmpge_epi16_mask(x,y) #define SIMDGreaterThanOrEquali32M(x,y) _mm512_cmpge_epi32_mask(x,y) #define SIMDGreaterThanOrEquali64M(x,y) _mm512_cmpge_epi64_mask(x,y) #define SIMDLessThanM(x,y) _mm512_cmplt_ps_mask(x,y) //#define SIMDLessThani8M(x,y) _mm512_cmplt_epi8_mask(x,y) //#define SIMDLessThani16M(x,y) _mm512_cmplt_epi16_mask(x,y) #define SIMDLessThani32M(x,y) _mm512_cmplt_epi32_mask(x,y) #define SIMDLessThani64M(x,y) _mm512_cmplt_epi64_mask(x,y) #define SIMDLessThanOrEqualM(x,y) _mm512_cmple_ps_mask(x,y) //#define SIMDLessThanOrEquali8M(x,y) _mm512_cmple_epi8_mask(x,y) //#define SIMDLessThanOrEquali16M(x,y) _mm512_cmple_epi16_mask(x,y) #define SIMDLessThanOrEquali32M(x,y) _mm512_cmple_epi32_mask(x,y) #define SIMDLessThanOrEquali64M(x,y) _mm512_cmple_epi64_mask(x,y) #define SIMDMax(x,y) _mm512_max_ps(x,y) //#define SIMDMaxi8(x,y) _mm512_max_epi8(x,y) //#define SIMDMaxi16(x,y) _mm512_max_epi16(x,y) #define SIMDMaxi32(x,y) _mm512_max_epi32(x,y) #define SIMDMaxi64(x,y) _mm512_max_epi64(x,y) #define SIMDMin(x,y) _mm512_min_ps(x,y) //#define SIMDMini8(x,y) _mm512_min_epi8(x,y) //#define SIMDMini16(x,y) _mm512_min_epi16(x,y) #define SIMDMini32(x,y) _mm512_min_epi32(x,y) #define SIMDMini64(x,y) _mm512_min_epi64(x,y) #define SIMDBlend(x,y,z) _mm512_mask_blend_ps(z, x, y) //#define SIMDBlendi8(x,y,z) _mm512_mask_blend_epi8(z, x, y) //#define SIMDBlendi16(x,y,z) _mm512_mask_blend_epi16(z, x, y) #define SIMDBlendi32(x,y,z) _mm512_mask_blend_epi32(z, x, y) #define SIMDBlendi64(x,y,z) _mm512_mask_blend_epi64(z, x, y) // with AVX512F //#define Maski8 __mmask64 //#define Maski16 __mmask32 #define Maski32 __mmask16 #define Maski64 __mmask8 /* x = a == b ? c : d */ //#define SIMDSetIfEquali8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDEquali8M(a,b)); } //#define SIMDSetIfEquali16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDEquali16M(a,b)); } #define SIMDSetIfEquali32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDEquali32M(a,b)); } #define SIMDSetIfEquali64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDEquali64M(a,b)); } /* x = a > b ? c : d */ //#define SIMDSetIfGreateri8(x,a,b,c,d) { x = SIMDBlendi8(d, c, SIMDGreaterThani8M(a,b)); } //#define SIMDSetIfGreateri16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16M(a,b)); } #define SIMDSetIfGreateri32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32M(a,b)); } #define SIMDSetIfGreateri64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani64M(a,b)); } /* x = a < b ? c : d */ //#define SIMDSetIfLessi8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDGreaterThani8M(b,a)); } //#define SIMDSetIfLessi16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani8M(b,a)); } #define SIMDSetIfLessi32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani8M(b,a)); } #define SIMDSetIfLessi64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani8M(b,a)); } /* x = a > b ? c : d, y = a > b ? a : b */ //#define SIMDGetIfGreateri8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(a,b); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } //#define SIMDGetIfGreateri16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(a,b); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfGreateri32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(a,b); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfGreateri64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(a,b); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } /* x = a < b ? c : d, y = a < b ? a : b */ //#define SIMDGetIfLessi8(x,y,a,b,c,d) { Maski8 cmp = SIMDGreaterThani8M(b,a); x = SIMDBlendi8(d, c, cmp); y = SIMDBlendi8(b, a, cmp); } //#define SIMDGetIfLessi16(x,y,a,b,c,d) { Maski16 cmp = SIMDGreaterThani16M(b,a); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfLessi32(x,y,a,b,c,d) { Maski32 cmp = SIMDGreaterThani32M(b,a); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfLessi64(x,y,a,b,c,d) { Maski64 cmp = SIMDGreaterThani64M(b,a); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } // end of AVX512F #else // AVX2 SSE4.1 SSE2 #ifdef __AVX2__ // start of AVX2 // m256 will be our base type typedef __m256 SIMDf; //for floats typedef __m256i SIMDi; //for integers //intrinsic functions #define SIMDStore(x,y) _mm256_store_ps(x,y) #define SIMDLoad(x) _mm256_load_ps(x) #define SIMDStorei(x,y) _mm256_store_si256(x,y) #define SIMDLoadi(x) _mm256_load_si256(x) #define SIMDSet(x,y,z,w,a,b,c,d) _mm256_set_ps(x,y,z,w,a,b,c,d) #define SIMDSeti8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32) __mm256_set_epi8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28,x29,x30,x31,x32) #define SIMDSeti16(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) __mm256_set_epi16(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) #define SIMDSeti32(x1,x2,x3,x4,x5,x6,x7,x8) __mm256_set_epi32(x1,x2,x3,x4,x5,x6,x7,x8) #define SIMDSeti64(x1,x2,x3,x4) __mm256_set_epi64x(x1,x2,x3,x4) #define SIMDSeti128(x,y) __mm256_set_m128(x,y) #define SIMDSetZero() _mm256_setzero_ps() #define SIMDSetZeroi() _mm256_setzero_si256() #define SIMDSetOne(x) _mm256_set1_ps(x) #define SIMDSetOnei8(x) _mm256_set1_epi8(x) #define SIMDSetOnei16(x) _mm256_set1_epi16(x) #define SIMDSetOnei32(x) _mm256_set1_epi32(x) #define SIMDSetOnei64(x) _mm256_set1_epi64x(x) #define SIMDAdd(x,y) _mm256_add_ps(x,y) #define SIMDAddi8(x,y) _mm256_add_epi8(x,y) #define SIMDAddi16(x,y) _mm256_add_epi16(x,y) #define SIMDAddi32(x,y) _mm256_add_epi32(x,y) #define SIMDAddi64(x,y) _mm256_add_epi64(x,y) #define SIMDSub(x,y) _mm256_sub_ps(x,y) #define SIMDSubi8(x,y) _mm256_sub_epi8(x,y) #define SIMDSubi16(x,y) _mm256_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm256_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm256_sub_epi64(x,y) #define SIMDMul(x,y) _mm256_mul_ps(x,y) #define SIMDMuli(x,y) _mm256_mul_epi32(x,y) #define SIMDAnd(x,y) _mm256_and_ps(x,y) #define SIMDAndi(x,y) _mm256_and_si256(x,y) #define SIMDAndNot(x,y) _mm256_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm256_andnot_si256(x,y) #define SIMDOr(x,y) _mm256_or_ps(x,y) #define SIMDOri(x,y) _mm256_or_si256(x,y) #define SIMDShiftLeft(a, n) (n) < 16 ? \ _mm256_alignr_epi8(a, _mm256_permute2x128_si256(a, a, _MM_SHUFFLE(0, 0, 2, 0)), (16-(n))) : \ _mm256_slli_si256(_mm256_permute2x128_si256(a, a, _MM_SHUFFLE(0, 0, 2, 0)), ((n)-16)) #define SIMDShiftRight(a, n) (n) < 16 ? \ _mm256_alignr_epi8(a, _mm256_permute2x128_si256(a, a, _MM_SHUFFLE(2, 0, 0, 1)), (n)) : \ _mm256_srli_si256(_mm256_permute2x128_si256(a, a, _MM_SHUFFLE(2, 0, 0, 1)), ((n)-16)) #define SIMDShiftLeftOnei16(x,y) _mm256_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm256_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm256_slli_epi64(x,y) #define SIMDShiftRightOnei16(x,y) _mm256_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm256_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm256_srli_epi64(x,y) #define SIMDEqual(x,y) _mm256_cmp_ps(x,y,_CMP_EQ_OQ) #define SIMDEquali16(x,y) _mm256_cmpeq_epi16(x,y) #define SIMDEquali8(x,y) _mm256_cmpeq_epi8(x,y) #define SIMDEquali32(x,y) _mm256_cmpeq_epi32(x,y) #define SIMDEquali64(x,y) _mm256_cmpeq_epi64(x,y) #define SIMDGreaterThan(x,y) _mm256_cmp_ps(x,y,_CMP_GT_OQ) #define SIMDGreaterThani16(x,y) _mm256_cmpgt_epi16(x,y) #define SIMDGreaterThani8(x,y) _mm256_cmpgt_epi8(x,y) #define SIMDGreaterThani32(x,y) _mm256_cmpgt_epi32(x,y) #define SIMDGreaterThani64(x,y) _mm256_cmpgt_epi64(x,y) #define SIMDFloor(x) _mm256_floor_ps(x) #define SIMDMax(x,y) _mm256_max_ps(x,y) #define SIMDMaxi8(x,y) _mm256_max_epi8(x,y) #define SIMDMaxi16(x,y) _mm256_max_epi16(x,y) #define SIMDMaxi32(x,y) _mm256_max_epi32(x,y) #define SIMDMaxi64(x,y) _mm256_max_epi64(x,y) #define SIMDMin(x,y) _mm256_min_ps(x,y) #define SIMDMini8(x,y) _mm256_min_epi8(x,y) #define SIMDMini16(x,y) _mm256_min_epi16(x,y) #define SIMDMini32(x,y) _mm256_min_epi32(x,y) #define SIMDBlendV(x,y,z) _mm256_blendv_ps(x,y,z) #define SIMDBlendVi8(x,y,z) _mm256_blendv_epi8(x,y,z) // end of AVX2 only #else // SSE4.1 SSE2 // start of SSE4.1 and SSE2 // m128 will be our base type typedef __m128 SIMDf; //for floats typedef __m128i SIMDi; //for integers #define SIMDStore(x,y) _mm_store_ps(x,y) #define SIMDLoad(x) _mm_load_ps(x) #define SIMDStorei(x,y) _mm_store_si128(x,y) #define SIMDLoadi(x) _mm_load_si128(x) #define SIMDSeti8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) __mm_set_epi8(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16) #define SIMDSeti16(x1,x2,x3,x4,x5,x6,x7,x8) __mm_set_epi16(x1,x2,x3,x4,x5,x6,x7,x8) #define SIMDSeti32(x1,x2,x3,x4) __mm_set_epi32(x1,x2,x3,x4) #define SIMDSeti64(x,y) __mm_set_epi64(x,y) #define SIMDSetOne(x) _mm_set1_ps(x) #define SIMDSetZero() _mm_setzero_ps() #define SIMDSetOnei8(x) _mm_set1_epi8(x) #define SIMDSetOnei16(x) _mm_set1_epi16(x) #define SIMDSetOnei32(x) _mm_set1_epi32(x) #define SIMDSetOnei64(x) _mm_set1_epi64(x) #define SIMDSetZeroi() _mm_setzero_si128() #define SIMDAdd(x,y) _mm_add_ps(x,y) #define SIMDAddi8(x,y) _mm_add_epi8(x,y) #define SIMDAddi16(x,y) _mm_add_epi16(x,y) #define SIMDAddi32(x,y) _mm_add_epi32(x,y) #define SIMDAddi64(x,y) _mm_add_epi64(x,y) #define SIMDSub(x,y) _mm_sub_ps(x,y) #define SIMDSubi8(x,y) _mm_sub_epi8(x,y) #define SIMDSubi16(x,y) _mm_sub_epi16(x,y) #define SIMDSubi32(x,y) _mm_sub_epi32(x,y) #define SIMDSubi64(x,y) _mm_sub_epi64(x,y) #define SIMDMul(x,y) _mm_mul_ps(x,y) #define SIMDMuli(x,y) _mm_mul_epi32(x,y) #define SIMDAnd(x,y) _mm_and_ps(x,y) #define SIMDAndi(x,y) _mm_and_si128(x,y) #define SIMDAndNot(x,y) _mm_andnot_ps(x,y) #define SIMDAndNoti(x,y) _mm_andnot_si128(x,y) #define SIMDOr(x,y) _mm_or_ps(x,y) #define SIMDOri(x,y) _mm_or_si128(x,y) #define SIMDShiftLeft(x,y) _mm_slli_si128(x,y) // shift whole x by y bits #define SIMDShiftRight(x,y) _mm_srli_si128(x,y) #define SIMDShiftLeftOnei16(x,y) _mm_slli_epi16(x,y) #define SIMDShiftLeftOnei32(x,y) _mm_slli_epi32(x,y) #define SIMDShiftLeftOnei64(x,y) _mm_slli_epi64(x,y) #define SIMDShiftRightOnei16(x,y) _mm_srli_epi16(x,y) #define SIMDShiftRightOnei32(x,y) _mm_srli_epi32(x,y) #define SIMDShiftRightOnei64(x,y) _mm_srli_epi64(x,y) #define SIMDEqual(x,y) _mm_cmpeq_ps(x,y) #define SIMDEquali8(x,y) _mm_cmpeq_epi8(x,y) #define SIMDEquali16(x,y) _mm_cmpeq_epi16(x,y) #define SIMDEquali32(x,y) _mm_cmpeq_epi32(x,y) #define SIMDGreaterThan(x,y) _mm_cmpgt_ps(x,y) #define SIMDGreaterThani8(x,y) _mm_cmpgt_epi8(x,y) #define SIMDGreaterThani16(x,y) _mm_cmpgt_epi16(x,y) #define SIMDGreaterThani32(x,y) _mm_cmpgt_epi32(x,y) #define SIMDLessThan(x,y) _mm_cmplt_ps(x,y) #define SIMDLessThani8(x,y) _mm_cmplt_epi8(x,y) #define SIMDLessThani16(x,y) _mm_cmplt_epi16(x,y) #define SIMDLessThani32(x,y) _mm_cmplt_epi32(x,y) #define SIMDMax(x,y) _mm_max_ps(x,y) #define SIMDMaxi16(x,y) _mm_max_epi16(x,y) #define SIMDMin(x,y) _mm_min_ps(x,y) #define SIMDMini16(x,y) _mm_min_epi16(x,y) #define Maski16 __mmask8 #define Maski32 __mmask8 #ifdef __SSE4_1__ // start of SSE4.1 only #define SIMDBlendV(x,y,z) _mm_blendv_ps(x,y,z) // z is __mask #define SIMDBlendVi8(x,y,z) _mm_blendv_epi8(x,y,z) #define SIMDEquali64(x,y) _mm_cmpeq_epi64(x,y) #define SIMDFloor(x) _mm_floor_ps(x) #define SIMDMaxi8(x,y) _mm_max_epi8(x,y) #define SIMDMini8(x,y) _mm_min_epi8(x,y) #define SIMDMaxi32(x,y) _mm_max_epi32(x,y) #define SIMDMini32(x,y) _mm_min_epi32(x,y) // end of SSE4.1 only #else // SSE2 // start of SSE2 only #define SIMDBlendV(x,y,z) SIMDOr(SIMDAndNot(z,x), SIMDAnd(z,y)) //if we don't have sse4 #define SIMDBlendVi8(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) //if we don't have sse4 #define SIMDMaxi8(x,y) SIMDBlendVi8(y, x, SIMDGreaterThani8(x,y)) #define SIMDMini8(x,y) SIMDBlendVi8(x, y, SIMDGreaterThani8(x,y)) #define SIMDMaxi32(x,y) SIMDBlendi32(y, x, SIMDGreaterThani32(x,y)) #define SIMDMini32(x,y) SIMDBlendi32(x, y, SIMDGreaterThani32((x,y)) // end of SSE2 only // end of SSE4.1 and SSE2 #endif // SSE4.1 #endif // AVX2 // start of no AVX512F (AVX2/SSE4.1/SSE2) #define SIMDBlendi16(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) #define SIMDBlendi32(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) #define SIMDBlendi64(x,y,z) SIMDOri(SIMDAndNoti(z,x), SIMDAndi(z,y)) /* x = a == b ? c : d */ #define SIMDSetIfEquali8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDEquali8(a,b)); } #define SIMDSetIfEquali16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDEquali16(a,b)); } #define SIMDSetIfEquali32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDEquali32(a,b)); } #define SIMDSetIfEquali64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDEquali64(a,b)); } /* x = a > b ? c : d */ #define SIMDSetIfGreateri8(x,a,b,c,d) { x = SIMDBlendVi8(d, c, SIMDGreaterThani8(a,b)); } #define SIMDSetIfGreateri16(x,a,b,c,d) { x = SIMDBlendi16(d, c, SIMDGreaterThani16(a,b)); } #define SIMDSetIfGreateri32(x,a,b,c,d) { x = SIMDBlendi32(d, c, SIMDGreaterThani32(a,b)); } #define SIMDSetIfGreateri64(x,a,b,c,d) { x = SIMDBlendi64(d, c, SIMDGreaterThani64(a,b)); } /* x = a < b ? c : d */ #define SIMDSetIfLessi8(x,a,b,c,d) { x = BlendVi8(d, c, SIMDGreaterThani8(b,a)); } #define SIMDSetIfLessi16(x,a,b,c,d) { x = Blendi16(d, c, SIMDGreaterThani16(b,a)); } #define SIMDSetIfLessi32(x,a,b,c,d) { x = Blendi32(d, c, SIMDGreaterThani32(b,a)); } #define SIMDSetIfLessi64(x,a,b,c,d) { x = Blendi64(d, c, SIMDGreaterThani64(b,a)); } /* x = a > b ? c : d, y = a > b ? a : b */ #define SIMDGetIfGreateri8(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani8(a,b); x = SIMDBlendVi8(d, c, cmp); y = SIMDBlendVi8(b, a, cmp); } #define SIMDGetIfGreateri16(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani16(a,b); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfGreateri32(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani32(a,b); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfGreateri64(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani64(a,b); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } /* x = a < b ? c : d, y = a < b ? a : b */ #define SIMDGetIfLessi8(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani8(b,a); x = SIMDBlendVi8(d, c, cmp); y = SIMDBlendVi8(b, a, cmp); } #define SIMDGetIfLessi16(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani16(b,a); x = SIMDBlendi16(d, c, cmp); y = SIMDBlendi16(b, a, cmp); } #define SIMDGetIfLessi32(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani32(b,a); x = SIMDBlendi32(d, c, cmp); y = SIMDBlendi32(b, a, cmp); } #define SIMDGetIfLessi64(x,y,a,b,c,d) { SIMDi cmp = SIMDGreaterThani64(b,a); x = SIMDBlendi64(d, c, cmp); y = SIMDBlendi64(b, a, cmp); } // end of no AVX512F (AVX2/SSE4.1/SSE2) #endif // AVX512F #endif // AVX512BW #ifdef __cplusplus extern "C" { #endif // int simd_check(void); /* static void *SIMDMalloc(size_t size, size_t align) { void *ret = (void*)_mm_malloc(size, align); if (ret == NULL) { fprintf(stderr, "[%s] mm_Malloc fail!\nSize: %ld\n", __func__, size); exit(1); } else return ret; }*/ // use posix_memalign static void *SIMDMalloc(size_t size, size_t align) { void *ret; int res; res = posix_memalign(&ret, align, size); if (res != 0) { char error[10]; if (res == EINVAL) strcpy(error, "EINVAR"); else if (res == ENOMEM) strcpy(error, "ENOMEM"); else strcpy(error, "Unknown"); fprintf(stderr, "[%s] posix_memalign fail!\nSize: %ld, Error: %s\n", __func__, size, error); exit(1); } else return ret; } #ifdef __cplusplus } #endif #endif // SIMD_INSTRUCTION_H abPOA-1.4.1/src/utils.c000066400000000000000000000225011425041320700145300ustar00rootroot00000000000000/* The MIT License Copyright (c) 2008 Genome Research Ltd (GRL). Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Contact: Heng Li */ #define FSYNC_ON_FLUSH #include #include #include #include #include #include #ifdef FSYNC_ON_FLUSH #include #include #include #endif #include #include #include #include "utils.h" #include "ksort.h" #define pair64_lt(a, b) ((a).x < (b).x || ((a).x == (b).x && (a).y < (b).y)) KSORT_INIT(128, pair64_t, pair64_lt) KSORT_INIT(64, uint64_t, ks_lt_generic) #include "kseq.h" KSEQ_INIT2(, gzFile, err_gzread) /******************** * System utilities * ********************/ FILE *err_xopen_core(const char *func, const char *fn, const char *mode) { FILE *fp = 0; if (strcmp(fn, "-") == 0) return (strstr(mode, "r"))? stdin : stdout; if ((fp = fopen(fn, mode)) == 0) { err_fatal(func, "fail to open file '%s' : %s", fn, strerror(errno)); } return fp; } FILE *err_xreopen_core(const char *func, const char *fn, const char *mode, FILE *fp) { if (freopen(fn, mode, fp) == 0) { err_fatal(func, "fail to open file '%s' : %s", fn, strerror(errno)); } return fp; } gzFile err_xzopen_core(const char *func, const char *fn, const char *mode) { gzFile fp; if (strcmp(fn, "-") == 0) { fp = gzdopen(fileno((strstr(mode, "r"))? stdin : stdout), mode); /* According to zlib.h, this is the only reason gzdopen can fail */ if (!fp) err_fatal(func, "Out of memory"); return fp; } if ((fp = gzopen(fn, mode)) == 0) { err_fatal(func, "fail to open file '%s' : %s", fn, errno ? strerror(errno) : "Out of memory"); } return fp; } void err_fatal(const char *header, const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "[%s] ", header); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(EXIT_FAILURE); } void err_fatal_core(const char *header, const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "[%s] ", header); vfprintf(stderr, fmt, args); fprintf(stderr, " Abort!\n"); va_end(args); abort(); } void _err_fatal_simple(const char *func, const char *msg) { fprintf(stderr, "[%s] %s\n", func, msg); exit(EXIT_FAILURE); } void _err_fatal_simple_core(const char *func, const char *msg) { fprintf(stderr, "[%s] %s Abort!\n", func, msg); abort(); } size_t err_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t ret = fwrite(ptr, size, nmemb, stream); if (ret != nmemb) _err_fatal_simple("fwrite", strerror(errno)); return ret; } size_t err_fread_noeof(void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t ret = fread(ptr, size, nmemb, stream); if (ret != nmemb) { _err_fatal_simple("fread", ferror(stream) ? strerror(errno) : "Unexpected end of file"); } return ret; } int err_gzread(gzFile file, void *ptr, unsigned int len) { int ret = gzread(file, ptr, len); if (ret < 0) { int errnum = 0; const char *msg = gzerror(file, &errnum); _err_fatal_simple("gzread", Z_ERRNO == errnum ? strerror(errno) : msg); } return ret; } int err_fseek(FILE *stream, long offset, int whence) { int ret = fseek(stream, offset, whence); if (0 != ret) { _err_fatal_simple("fseek", strerror(errno)); } return ret; } long err_ftell(FILE *stream) { long ret = ftell(stream); if (-1 == ret) { _err_fatal_simple("ftell", strerror(errno)); } return ret; } int err_func_printf(const char *func, const char *format, ...) { fprintf(stderr, "[%s] ", func); va_list arg; int done; va_start(arg, format); done = vfprintf(stderr, format, arg); fprintf(stderr, "\n"); int saveErrno = errno; va_end(arg); if (done < 0) _err_fatal_simple("vfprintf(stderr)", strerror(saveErrno)); return done; } int err_printf(const char *format, ...) { va_list arg; int done; va_start(arg, format); done = vfprintf(stderr, format, arg); int saveErrno = errno; va_end(arg); if (done < 0) _err_fatal_simple("vfprintf(stderr)", strerror(saveErrno)); return done; } int stdout_printf(const char *format, ...) { va_list arg; int done; va_start(arg, format); done = vfprintf(stdout, format, arg); int saveErrno = errno; va_end(arg); if (done < 0) _err_fatal_simple("vfprintf(stdout)", strerror(saveErrno)); return done; } int err_fprintf(FILE *stream, const char *format, ...) { va_list arg; int done; va_start(arg, format); done = vfprintf(stream, format, arg); int saveErrno = errno; va_end(arg); if (done < 0) _err_fatal_simple("vfprintf", strerror(saveErrno)); return done; } int err_fputc(int c, FILE *stream) { int ret = putc(c, stream); if (EOF == ret) { _err_fatal_simple("fputc", strerror(errno)); } return ret; } int err_fputs(const char *s, FILE *stream) { int ret = fputs(s, stream); if (EOF == ret) { _err_fatal_simple("fputs", strerror(errno)); } return ret; } void err_fgets(char *buff, size_t s, FILE *fp) { if (fgets(buff, s, fp) == NULL) { err_fatal_simple("fgets error.\n"); } } int err_puts(const char *s) { int ret = puts(s); if (EOF == ret) { _err_fatal_simple("puts", strerror(errno)); } return ret; } int err_fflush(FILE *stream) { int ret = fflush(stream); if (ret != 0) _err_fatal_simple("fflush", strerror(errno)); #ifdef FSYNC_ON_FLUSH /* Calling fflush() ensures that all the data has made it to the kernel buffers, but this may not be sufficient for remote filesystems (e.g. NFS, lustre) as an error may still occur while the kernel is copying the buffered data to the file server. To be sure of catching these errors, we need to call fsync() on the file descriptor, but only if it is a regular file. */ { struct stat sbuf; if (0 != fstat(fileno(stream), &sbuf)) _err_fatal_simple("fstat", strerror(errno)); if (S_ISREG(sbuf.st_mode)) { if (0 != fsync(fileno(stream))) _err_fatal_simple("fsync", strerror(errno)); } } #endif return ret; } int err_fclose(FILE *stream) { int ret = fclose(stream); if (ret != 0) _err_fatal_simple("fclose", strerror(errno)); return ret; } int err_gzclose(gzFile file) { int ret = gzclose(file); if (Z_OK != ret) { _err_fatal_simple("gzclose", Z_ERRNO == ret ? strerror(errno) : zError(ret)); } return ret; } /********* * alloc * *********/ void *err_malloc(const char *func, size_t s) { void *ret = (void*)malloc(s); if (ret == NULL) err_fatal_core(func, "Malloc fail!\nSize: %lld\n", s); else return ret; } void *err_calloc(const char *func, size_t n, size_t s) { void *ret = (void*)calloc(n, s); if (ret == NULL) err_fatal_core(func, "Calloc fail!\nN: %d\tSize: %lld\n", n, s); else return ret; } void *err_realloc(const char *func, void *p, size_t s) { void *ret = (void*)realloc(p, s); if (ret == NULL) err_fatal_core(func, "Realloc fail!\nSize: %lld\n", s); else return ret; } /********* * Timer * *********/ void usr_sys_cputime(double *usr_t, double *sys_t) { struct rusage r; getrusage(RUSAGE_SELF, &r); *usr_t = r.ru_utime.tv_sec + 1e-6 * r.ru_utime.tv_usec; *sys_t = r.ru_stime.tv_sec + 1e-6 * + r.ru_stime.tv_usec; } double cputime() { struct rusage r; getrusage(RUSAGE_SELF, &r); return r.ru_utime.tv_sec + r.ru_stime.tv_sec + 1e-6 * (r.ru_utime.tv_usec + r.ru_stime.tv_usec); } double realtime() { struct timeval tp; struct timezone tzp; gettimeofday(&tp, &tzp); return tp.tv_sec + tp.tv_usec * 1e-6; } long peakrss(void) { struct rusage r; getrusage(RUSAGE_SELF, &r); #ifdef __linux__ return r.ru_maxrss * 1024; #else return r.ru_maxrss; #endif } void get_cur_time(const char *prefix) { time_t now = time(0); struct tm ts; char buf[1024]; ts = *localtime(&now); err_printf("[%s] ", prefix); strftime(buf, sizeof(buf), "%Y-%m-%d-%s", &ts); } void print_format_time(FILE *out) { time_t rawtime; struct tm *info; char buffer[80]; time(&rawtime); info = localtime( &rawtime ); strftime(buffer,80,"%m-%d-%Y %X", info); fprintf(out, "== %s == ", buffer); } int err_func_format_printf(const char *func, const char *format, ...) { print_format_time(stderr); fprintf(stderr, "[%s] ", func); va_list arg; int done; va_start(arg, format); done = vfprintf(stderr, format, arg); fprintf(stderr, "\n"); int saveErrno = errno; va_end(arg); if (done < 0) _err_fatal_simple("vfprintf(stderr)", strerror(saveErrno)); return done; } abPOA-1.4.1/src/utils.h000066400000000000000000000232611425041320700145410ustar00rootroot00000000000000/* The MIT License Copyright (c) 2008 Genome Research Ltd (GRL). Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Contact: Heng Li */ #ifndef UTILS_H #define UTILS_H #include #include #include #include #include #ifndef kroundup32 #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) #endif #ifndef kroundup64 #define kroundup64(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, (x)|=(x)>>32, ++(x)) #endif #ifdef __GNUC__ // Tell GCC to validate printf format string and args #define ATTRIBUTE(list) __attribute__ (list) #else #define ATTRIBUTE(list) #endif #define err_fatal_simple(msg) _err_fatal_simple(__func__, msg) #define err_fatal_simple_core(msg) _err_fatal_simple_core(__func__, msg) #define xopen(fn, mode) err_xopen_core(__func__, fn, mode) #define xreopen(fn, mode, fp) err_xreopen_core(__func__, fn, mode, fp) #define xzopen(fn, mode) err_xzopen_core(__func__, fn, mode) #define xassert(cond, msg) if ((cond) == 0) _err_fatal_simple_core(__func__, msg) #define _err_simple_func_printf(msg) err_func_printf(__func__, msg) typedef struct { uint64_t x, y; } pair64_t; typedef struct { size_t n, m; uint64_t *a; } uint64_v; typedef struct { size_t n, m; pair64_t *a; } pair64_v; #ifdef __cplusplus extern "C" { #endif void err_fatal(const char *header, const char *fmt, ...) ATTRIBUTE((noreturn)); void err_fatal_core(const char *header, const char *fmt, ...) ATTRIBUTE((noreturn)); void _err_fatal_simple(const char *func, const char *msg) ATTRIBUTE((noreturn)); void _err_fatal_simple_core(const char *func, const char *msg) ATTRIBUTE((noreturn)); FILE *err_xopen_core(const char *func, const char *fn, const char *mode); FILE *err_xreopen_core(const char *func, const char *fn, const char *mode, FILE *fp); gzFile err_xzopen_core(const char *func, const char *fn, const char *mode); size_t err_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); size_t err_fread_noeof(void *ptr, size_t size, size_t nmemb, FILE *stream); int err_gzread(gzFile file, void *ptr, unsigned int len); int err_fseek(FILE *stream, long offset, int whence); #define err_rewind(FP) err_fseek((FP), 0, SEEK_SET) long err_ftell(FILE *stream); int err_fprintf(FILE *stream, const char *format, ...) ATTRIBUTE((format(printf, 2, 3))); int err_printf(const char *format, ...) ATTRIBUTE((format(printf, 1, 2))); int err_func_printf(const char *func, const char *format, ...) ATTRIBUTE((format(printf, 2, 3))); int stdout_printf(const char *format, ...) ATTRIBUTE((format(printf, 1, 2))); int err_fputc(int c, FILE *stream); #define err_putchar(C) err_fputc((C), stdout) int err_fputs(const char *s, FILE *stream); int err_puts(const char *s); void err_fgets(char *buff, size_t s, FILE *fp); int err_fflush(FILE *stream); int err_fclose(FILE *stream); int err_gzclose(gzFile file); #define _err_malloc(s) err_malloc(__func__, s) #define _err_calloc(n, s) err_calloc(__func__, n, s) #define _err_realloc(p, s) err_realloc(__func__, p, s) void *err_malloc(const char* func, size_t s); void *err_calloc(const char* func, size_t n, size_t s); void *err_realloc(const char* func, void *p, size_t s); void usr_sys_cputime(double *usr_t, double *sys_t); double cputime(); double realtime(); long peakrss(void); void print_format_time(FILE *out); int err_func_format_printf(const char *func, const char *format, ...); void ks_introsort_64 (size_t n, uint64_t *a); void ks_introsort_128(size_t n, pair64_t *a); #ifdef __cplusplus } #endif #define _uni_realloc(p, n, m, type) { \ if (m <= 0) { \ m = 1; \ m = MAX_OF_TWO(n, m); \ p = (type*)_err_malloc((m) * sizeof(type)); \ } else if (n >= m) { \ m = n + 1; kroundup32(m); \ p = (type*)_err_realloc(p, (m) * sizeof(type)); \ } \ } #define _realloc(p, m, type) {(m) <<= 1; p = (type*)_err_realloc(p, (m) * sizeof(type));} #define _sim_insert_abpoa_utils(v, p, n, m, type) { \ if (n == m) { \ _realloc(p, m, type) \ } \ p[n++] = v; \ } #define _insert_abpoa_utils(v, p, n, m, type) { \ int _i, _flag=0; \ for (_i = 0; _i < n; ++_i) { \ if (p[_i] == v) { \ _flag = 1; \ break; \ } \ } \ if (_flag == 0) { \ if (n == m) { \ _realloc(p, m, type) \ } \ p[n++] = v; \ } \ } #define _bin_insert_abpoa_utils_idx(v, p, n, m, type, flag, k_i) { \ flag=0, k_i=-1; \ int _left=0,_right=n-1,_mid; \ type _mid_v, _tmp_v; \ if (_right == -1) k_i = 0; \ else { \ while (_left <= _right) { \ _mid = (_left+_right) >> 1; \ _mid_v = p[_mid]; \ if (_mid_v == v) { \ k_i = _mid; \ flag = 1; break; \ } else if (_mid_v > v) { \ if (_mid != 0) { \ _tmp_v = p[_mid-1]; \ } \ if (_mid == 0 || v > _tmp_v) { \ k_i = _mid; \ break; \ } \ else _right = _mid-1; \ } else _left = _mid+1; \ } \ } \ if (k_i == -1) k_i = n; \ } #define _bin_insert_abpoa_utils(v, p, n, m, type) { \ int _k_i, _flag; \ _bin_insert_abpoa_utils_idx(v, p, n, m, type, _flag, _k_i) \ if (_flag == 0) { \ if (n == m) { \ _realloc(p, m, type) \ } \ if (_k_i <= n-1) \ memmove(p+_k_i+1, p+_k_i, (n-_k_i)*sizeof(type)); \ (p)[_k_i] = v; \ (n)++; \ } \ } #define _bin_search(v, p, n, type, hit, i) { \ int _left =0,_right=n-1,_mid; \ type _mid_v; \ hit = 0; \ if (_right == -1) hit=0; \ else { \ while (_left <= _right) { \ _mid = (_left+_right) >> 1; \ _mid_v = p[_mid]; \ if (_mid_v == v) { \ i = _mid; \ hit = 1; \ break; \ } else if (_mid_v > v) { \ _right = _mid-1; \ } else { \ _left = _mid+1; \ } \ } \ } \ } #define MIN_OF_TWO(a, b) ((a) < (b) ? (a) : (b)) #define MAX_OF_TWO(a, b) ((a) > (b) ? (a) : (b)) #define MIN_OF_THREE(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) #define MAX_OF_THREE(a, b, c) ((a) > (b) ? ((a) > (c) ? (a) : (c)) : ((b) > (c) ? (b) : (c))) #define AVG_OF_TWO(a, b) (((a)&(b)) + (((a)^(b)) >> 1)) static inline uint64_t hash_64(uint64_t key) { key += ~(key << 32); key ^= (key >> 22); key += ~(key << 13); key ^= (key >> 8); key += (key << 3); key ^= (key >> 15); key += ~(key << 27); key ^= (key >> 31); return key; } #ifndef _PRINT_FORMAT_H_ #define _PRINT_FORMAT_H_ #define NONE "\e[0m" //remove color/font #define BLACK "\e[0;30m" // black #define B_BLACK "\e[1;30m" // bold black #define RED "\e[0;31m" // read #define B_RED "\e[1;31m" // bold red #define GREEN "\e[0;32m" // green #define B_GREEN "\e[1;32m" // bold gren #define BROWN "\e[0;33m" // brown #define YELLOW "\e[1;33m" // yellow #define BLUE "\e[0;34m" // blue #define B_BLUE "\e[1;34m" // bold blue #define PURPLE "\e[0;35m" // purple #define B_PURPLE "\e[1;35m" // bold purple #define CYAN "\e[0;36m" // cyan #define B_CYAN "\e[1;36m" // bold cyan #define GRAY "\e[0;37m" // gray #define WHITE "\e[1;37m" // white, bold #define BOLD "\e[1m" // bold #define UNDERLINE "\e[4m" // underline #define BLINK "\e[5m" // blink #define REVERSE "\e[7m" // reverse background and foreground #define HIDE "\e[8m" // hide #define STRIKE "\e[9m" // strikethrough #define CLEAR "\e[2J" // clear #define CLRLINE "\r\e[K" // clear line // from https://blog.csdn.net/MoDa_Li/java/article/details/82156888 #endif #endif abPOA-1.4.1/sub_example.c000066400000000000000000000116421425041320700151110ustar00rootroot00000000000000/* sub_example.c libabpoa usage example To compile: gcc -g sub_example.c -I ./include -L ./lib -labpoa -lz -lm -o sub_example or: gcc -g sub_example.c -I ./include ./lib/libabpoa.a -lz -lm -o sub_example */ #include #include #include #include #include "include/abpoa.h" // AaCcGgTtNn ... ==> 0,1,2,3,4 ... // BbDdEeFf ... ==> 5,6,7,8 ... unsigned char _char26_table[256] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 5, 1, 6, 7, 8, 2, 9, 10, 11, 12, 13, 14, 4, 15, 16, 17, 18, 19, 3, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, 0, 5, 1, 6, 7, 8, 2, 9, 10, 11, 12, 13, 14, 4, 15, 16, 17, 18, 19, 3, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26 }; int main(void) { int i, j, n_seqs = 6; char seqs[100][1000] = { // 0 1 2 3 // 23456789012345678901234567890123 "CGTCAATCTATCGAAGCATACGCGGGCAGAGC", "CCACGTCAATCTATCGAAGCATACGCGGCAGC", "AATCTATCGAAGCATACG", "CAATGCTAGTCGAAGCAGCTGCGGCAG", "CGTCAATCTATCGAAGCATTCTACGCGGCAGAGC", "CGTCAATCTAGAAGCATACGCGGCAAGAGC", "CGTCAATCTATCGGTAAAGCATACGCTCTGTAGC", "CGTCAATCTATCTTCAAGCATACGCGGCAGAGC", "CGTCAATGGATCGAGTACGCGGCAGAGC", "CGTCAATCTAATCGAAGCATACGCGGCAGAGC" }; int beg_end_id[100][2] = { {0, 1}, {2, 33}, {6, 23}, {5, 30}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, //{2, 52}, //{2, 52}, //{2, 52}, //{2, 52}, //{2, 52}, //{2, 52}, //{2, 52}, //{2, 52}, //{2, 52} }; // initialize variables abpoa_t *ab = abpoa_init(); abpoa_para_t *abpt = abpoa_init_para(); // alignment parameters // abpt->align_mode = 0; // 0:global 1:local, 2:extension // abpt->match = 2; // match score // abpt->mismatch = 4; // mismatch penalty // abpt->gap_mode = ABPOA_CONVEX_GAP; // gap penalty mode // abpt->gap_open1 = 4; // gap open penalty #1 // abpt->gap_ext1 = 2; // gap extension penalty #1 // abpt->gap_open2 = 24; // gap open penalty #2 // abpt->gap_ext2 = 1; // gap extension penalty #2 // gap_penalty = min{gap_open1 + gap_len * gap_ext1, gap_open2 + gap_len * gap_ext2} // abpt->bw = 10; // extra band used in adaptive banded DP // abpt->bf = 0.01; // output options abpt->out_msa = 1; // generate Row-Column multiple sequence alignment(RC-MSA), set 0 to disable abpt->out_cons = 1; // generate consensus sequence, set 0 to disable abpoa_post_set_para(abpt); // collect sequence length, trasform ACGT to 0123 int *seq_lens = (int*)malloc(sizeof(int) * n_seqs); uint8_t **bseqs = (uint8_t**)malloc(sizeof(uint8_t*) * n_seqs); for (i = 0; i < n_seqs; ++i) { seq_lens[i] = strlen(seqs[i]); bseqs[i] = (uint8_t*)malloc(sizeof(uint8_t) * seq_lens[i]); for (j = 0; j < seq_lens[i]; ++j) bseqs[i][j] = _char26_table[(int)seqs[i][j]]; } // perform abpoa-msa ab->abs->n_seq = n_seqs; abpoa_res_t res; for (i = 0; i < n_seqs; ++i) { res.graph_cigar = 0, res.n_cigar = 0; int exc_beg, exc_end; if (i != 0) abpoa_subgraph_nodes(ab, abpt, beg_end_id[i][0], beg_end_id[i][1], &exc_beg, &exc_end); else exc_beg = 0, exc_end = 1; fprintf(stderr, "i: %d, beg: %d, end: %d\n", i, exc_beg, exc_end); abpoa_align_sequence_to_subgraph(ab, abpt, exc_beg, exc_end, bseqs[i], seq_lens[i], &res); abpoa_add_subgraph_alignment(ab, abpt, exc_beg, exc_end, bseqs[i], NULL, seq_lens[i], NULL, res, i, n_seqs, 0); if (res.n_cigar) free(res.graph_cigar); } abpoa_output(ab, abpt, stdout); /* generate DOT partial order graph plot */ abpt->out_pog = strdup("sub_example.png"); // dump parital order graph to file if (abpt->out_pog != NULL) abpoa_dump_pog(ab, abpt); for (i = 0; i < n_seqs; ++i) free(bseqs[i]); free(bseqs); free(seq_lens); abpoa_free(ab); abpoa_free_para(abpt); return 0; } abPOA-1.4.1/test_data/000077500000000000000000000000001425041320700144055ustar00rootroot00000000000000abPOA-1.4.1/test_data/heter.fa000066400000000000000000000252571425041320700160370ustar00rootroot00000000000000>m64062_200517_230654/46596725/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200517_230654/122620624/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCCACCATCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200604_115437/178193244/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200517_230654/73204047/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200604_115437/157813105/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200604_115437/141952744/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCAGTTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCCTCCACCAACATCCCCACCATCCCCACCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200604_115437/19531191/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCCCATTAACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACACCATTCTCACCATCTCCACCAACATCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCAATCCCCACCATCC >m64062_200604_115437/120652681/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200604_115437/28773203/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200604_115437/75628767/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTCCATCCATTCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCCATTACCATCCCACCACCATTTCCACCATCCCACCATCATCCCCACCACCATCCCCAGTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200517_230654/180226763/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200517_230654/29755012/ccs CCATTCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200517_230654/154468686/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATGCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCACCACCATCCTCACTACCATCCCACCACCATTCCACCATTCCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCACCACCATCTCCATTACCATCCCCACCACCATCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCATCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCATCCCCACCGCCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCACCCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATC >m64062_200604_115437/146211230/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC >m64062_200517_230654/85983296/ccs CCATTCCCACCATCCTTACCATCAACATCACCATCCCCACCATCCCCAACACCATTCCCACCATCCCTACCATCACCATCACCATCCCCACCAACATCCCCACCACCATCCTCACTACCATCCCCACCACCATTTCCACCATTCCCACCACAGTCACCATCACCCCCACCATCCCCATCATCATCCGCACCATCCCCACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCTCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATCCCCATTACCATCCCCACCACCATTTCCACCATTCCCACCATCATCCCCACCACCATCCTCGTTACCATCCCCACCACCTTTTCCACCATTCCCACCATCTCCAACACCTCCCCCACCATCATCCCCACCATCCCCACCACCTTCTCCACCATCATTCTCACCATCCCCACCACCATCTCCACCACCATTCTCACCATCTCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCAACATCCCCACCATCCCCACCCCCATGCCCACCATCATCCCCACCATCC abPOA-1.4.1/test_data/seq.fa000066400000000000000000000010511425041320700155020ustar00rootroot00000000000000>1 CGTCAATCTATCGAAGCATACGCGGGCAGAGCCGAAGACCTCGGCAATCCA >2 CCACGTCAATCTATCGAAGCATACGCGGCAGCCGAACTCGACCTCGGCAATCAC >3 CGTCAATCTATCGAAGCATACGCGGCAGAGCCCGGAAGACCTCGGCAATCAC >4 CGTCAATGCTAGTCGAAGCAGCTGCGGCAGAGCCGAAGACCTCGGCAATCAC >5 CGTCAATCTATCGAAGCATTCTACGCGGCAGAGCCGACCTCGGCAATCAC >6 CGTCAATCTAGAAGCATACGCGGCAAGAGCCGAAGACCTCGGCCAATCAC >7 CGTCAATCTATCGGTAAAGCATACGCTCTGTAGCCGAAGACCTCGGCAATCAC >8 CGTCAATCTATCTTCAAGCATACGCGGCAGAGCCGAAGACCTCGGCAATC >9 CGTCAATGGATCGAGTACGCGGCAGAGCCGAAGACCTCGGCAATCAC >10 CGTCAATCTAATCGAAGCATACGCGGCAGAGCCGTCTACCTCGGCAATCACGT abPOA-1.4.1/test_data/test.fa000066400000000000000000000001101425041320700156640ustar00rootroot00000000000000>1 ACGTGTACAGTTGAC >2 AGGTACACGTTAC >3 AGTGTCACGTTGAC >4 ACGTGTACATTGAC