#pragma once
/******************************************************** 
	* @file    : public_define.h
	* @brief   : 公用的定义属性
	* @details : 
	* @author  : qqm
	* @date    : 2021-2-4
*********************************************************/


#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>

#if defined(_MSC_VER) || defined(_WIN32) || defined(_WIN64)
#define dll_export __declspec(dllexport)
#define dll_import __declspec(dllimport)
#else //  __linux__
#define dll_export __attribute__ ((visibility("default")))
#include <memory>
#endif


/// dll return value
enum RETURN_VALUE {
	RT_OK					= 0,	/// 正常
	RT_ERROR				= 1,	/// 内部error
	RT_UUID_EMPTY			= 2,	/// 批次号异常
	RT_SAVE_PATH_EMPTY		= 3,	/// 保存路径异常
	RT_TEMPLATE_EMPTY		= 4,	/// 模板为空
	RT_HIMGS_LIST_EMPTY		= 5,	/// 图像列表异常
	RT_HIMGS_LIST_NOT_DIV	= 6,	/// 图像列表不是模板整数被

	/// 异常算法新增的异常类型
	RT_ERROR_IMAGES_NOT_EQUAL_TPS	= 7,	/// 传入的答题卡个数跟模板个数不一致
	RT_CHECK_ERROR					= 8,	/// 单份答题卡安全校验未通过
	RT_CHECK_LCPTS_NOT_EQUAL_TPS	= 9,	/// 异常接口2传入的模板定位信息、答题卡定位信息、模板size不相等
	RT_CHECK_LCPTINFO_ERROR			= 10,	/// 传入的定位信息不能满足矫正需求，成对数量<3
	RT_CHECK_ONLINECARD_LCPT_ERROR	= 11,	/// 在线答题卡传入的定位点数量<3
	RT_CHECK_LCPTINFO_XS_ERROR		= 12,	/// 传入的定位信息不能满足矫正需求，分布象限<3

	/// 新增模板坐标僬侥
	RT_TEMPLATE_BOX_ERROR	= 20,	/// 模板内的坐标越界或者无效
};

/// debug log leave 主要调式使用
enum LOG_LEAVE{
	LOG_AUTO		= 0, /// log默认输出，正常接口默认值
	LOG_FUN			= 1, /// log所有流程中调用的函数  fun_xxx() in! ... fun_xxx() return!
	LOG_KEYSTEP		= 2, /// log所有的关键点步骤信息 
	LOG_DETAIL		= 3, /// log关键点的具体值各个细节
};

/// 接口参数类型
enum API_PARAM{
	TYPE_ONLINE_CARD = 1,	/// 在线答题卡
	TYPE_THRID_CARD  = 2,	/// 第三方答题卡
	NEED_OCR_SOCRE   = 4,	/// 需要识别分数
	NO_NEED_OCR_SCORE= 8,	/// 不需要识别分数
	ERROR_CARD		 = 16,	/// 异常答题卡 api_processing_error_images_hand 接口使用
};

/// 答题卡类型
enum CARD_TYPE {
	OLWS	= TYPE_ONLINE_CARD | NEED_OCR_SOCRE,	/// 在线 识别分数
	OLNS	= TYPE_ONLINE_CARD | NO_NEED_OCR_SCORE,	/// 在线 不识别分数
	TDWS	= TYPE_THRID_CARD | NEED_OCR_SOCRE,		/// 第三方 识别分数
	TDNS	= TYPE_THRID_CARD | NO_NEED_OCR_SCORE,	/// 第三方 不识别分数
	OLWSE	= OLWS | ERROR_CARD,					/// api_processing_error_images_hand接口类型
	OLNSE	= OLNS | ERROR_CARD,
	TDWSE	= TDWS | ERROR_CARD,
	TDNSE	= TDNS | ERROR_CARD,
};

/// 答题卡可能的状态信息  !!!有待完善设计
enum PAPER_STATUS {
	PAPER_SUCC					= 0, /// SUCC
	MISSED_LOCATION_POINT		= 1, /// 定位点缺失
	MISSED_LOCATION_AREA		= 2, /// 定位区缺失
	MISSED_LOCATION_CROSSPOINT	= 4, /// 交叉点缺失
	MATCH_FAILED				= 8, /// 分页失败 模板不匹配
	PROGRAM_ERROR				= 16,/// 程序异常导致的失败
	PAGEDISMISS					= 32,/// 本份缺页
	PAGEDATAERROR				= 64,/// 本份图像异常
};

namespace preinfo {
	/// 图像方向 逆时针方向
	enum IMG_ROTATION { 
		ROTATION_0		= 1,
		ROTATION_90		= 2,
		ROTATION_180	= 4,
		ROTATION_270	= 8,
	};

	/// 图像&试题类型
	enum BOX_TYPE
	{
		BOX_NONE,				/// UNKNOW
		BOX_ABSENCE_MARK,		/// 缺考标记
		BOX_BREAK_PRECEDENT,	/// 违纪标记
		BOX_EXAM_TT_NUMBER,		/// 填涂考号
		BOX_EXAM_QRCODE,		/// 试卷二维码
		BOX_EXAM_BARCODE,		/// 试卷条码
		BOX_QUESTION_CHOICE_S,	///	单项选择题
		BOX_QUESTION_CHOICE_M,	///	不定项选择题
		BOX_QUESTION_COMPLETION,/// 填空题
		BOX_QUESETION_TF,		/// 判断题
		BOX_SUBJECTIVE_NORMAL,  /// 普通主观题
		BOX_SCORING_BOX,		/// 线下阅卷的打分区
		BOX_SUBJECTIVE_CHOOSE,			/// 选做题
		BOX_SUBJECTIVE_CHOOSE_COMBIN,	/// 选做题-合并填涂
		BOX_SUBJECTIVE_CHOOSE_SPLITE,	/// 选做题-分离填涂
		BOX_WRITE_CHINESE_COMPOSITION,	/// 写作-语文作文
		BOX_WRITE_ENGLISH_WRITING,		/// 写作-英语写作

		BOX_LOCATION_POINT,		/// 定位点
		BOX_LOCATION_AREA,		/// 定位区
		BOX_LOCATION_CROSS,		/// 定位交叉点
	};

	/// 方向
	enum DIR_FLAG {
		DIR_UNKNOW = -1,
		DIR_UP = 1,
		DIR_DOWN = 2,
		DIR_LEFT = 4,
		DIR_RIGHT = 8
	};

	/// 填涂区域的方向
	enum TT_DIR_FLAG { 
		TT_Y = 0, /// 垂直
		TT_X = 1, /// 水平
	};

	template<typename T> struct TtSize{ /// 
		T width;
		T height;
		TtSize() :width(T(0)), height(T(0)) {};
	};
	template<typename T> struct PaperRect {
		T centerx;
		T centery;
		T width;
		T height;
		T getX() const { return centerx - width / 2.0;}
		T getY() const { return centery - height/ 2.0; }
		PaperRect() :centerx(T(-1)), centery(T(-1)), width(T(0)), height(T(0)) {};
	};
	struct Field :PaperRect<double> {
		int id;		///	一个唯一标识符 不可能有重复的
		std::vector<int> vecTiHao;	/// 整数题号 可以有重复
		Field() :id(-1) {};
	};

	/// 定位点
	struct LocationPoint :Field {
		BOX_TYPE type;
		LocationPoint() :type(BOX_LOCATION_POINT) {};
	};

	/// 定位区
	struct LocationArea :Field {
		BOX_TYPE type;
		bool head;	/// 是否是试卷名称区域
		LocationArea() :head(false), type(BOX_LOCATION_AREA) {};
	};

	/// 交叉点
	struct LocationCrossPoint :Field {
		BOX_TYPE type;
		int sign;		/// 交叉点开口方向
		double angle;
		LocationCrossPoint() :sign(DIR_UNKNOW), angle(0.0), type(BOX_LOCATION_CROSS) {};
	};

	/// 条码/二维码
	struct CodeInfo :Field {
		BOX_TYPE type; /// BOX_EXAM_QRCODE or BOX_EXAM_BARCODE
		TT_DIR_FLAG way;
		CodeInfo() :type(BOX_EXAM_BARCODE), way(TT_X) {};
	};

	/// 缺考标记
	struct AbsenceMark:Field {
		BOX_TYPE type;
		AbsenceMark() :type(BOX_ABSENCE_MARK) {};
	};

	/// 填涂考号
	struct FillStuNumber:Field {
		bool			useable;	/// 是否可用
		TT_DIR_FLAG		way;		/// 水平/垂直
		int				cols;		/// 列数
		int				rows;		/// 行数
		TtSize<double>  itemSize;	/// 单个小项宽高
		double			disx;		/// 横向相邻的两个小项的x坐标的距离差
		double			disy;		/// 纵向的........
		FillStuNumber() :useable(false), way(TT_Y), cols(-1), rows(-1), disx(0.0), disy(0.0) {};
		void init_dis() {
			disx = (cols == 1) ? 0.0 : (width - itemSize.width) * 1.0 / (cols - 1) * 1.0;
			disy = (rows == 1) ? 0.0 : (height - itemSize.height) * 1.0 / (rows - 1) * 1.0;
		}
	};

	
	/// 填涂考号 拆解到小项坐标
	/// 填涂小项结构信息
	struct TtItem :PaperRect<double> {
		std::string optName; /// 0 ~ 9 A B C D .... 填涂
	};

	/// 新逻辑 - 填涂考号
	struct _FillStuNumber{
		bool		useable;	/// 是否可用
		int			cols;		/// 列数
		int			rows;		/// 行数
		std::map<int,std::vector<TtItem>> groups; /// pair<列序号(0开始),小项合集(从上到下)>
		_FillStuNumber() : useable(false), cols(-1), rows(-1) { groups.clear(); };
	};

	/// 新逻辑 - 填涂选做题 or 选择题
	struct _QuestionChoice:Field{
		BOX_TYPE				type;		/// BOX_QUESTION_CHOICE_S / M
		int						number;		/// 填涂个数
		TT_DIR_FLAG				way;		/// 方向
		TtSize<double>			itemSize;	/// 单个小项宽高
		std::vector<TtItem>		groups;		/// 组内的各个小项
		_QuestionChoice():number(0),way(TT_X) {};
	};

	/// 判断题
	struct QuesetionTF:Field{ 
		;
	};

	
	/// 单个选择题
	struct QuestionChoice:Field{ 
		BOX_TYPE		type;		/// BOX_QUESTION_CHOICE_S / M
		TT_DIR_FLAG		way;		/// 水平/垂直
		TtSize<double>  itemSize;	/// 单个小项宽高
		int				number;		/// 填涂项个数
		std::vector<TtItem>		groups;		/// 组内的各个小项
		QuestionChoice() :type(BOX_QUESTION_CHOICE_M), way(TT_X), number(0) {};
	};

	/// 主观题的区域信息
	struct SubjectiveField:Field{ 
		BOX_TYPE type;
		bool	multiple;		/// 是否多块 
		int		orderNumber;	/// 如果是多块 本块序号 从1开始
		bool	needConbin;		/// 是否需要合并一起
		int		marktype;		/// 如果等于4或者10则需要保留小图			2021.6.11 确认如果试题 如此切图
		TT_DIR_FLAG way;		/// 左右拼接 or 上下拼接
		std::string name;		/// 落地保存的文件名 e: "17" "22"
		SubjectiveField() :type(BOX_SUBJECTIVE_NORMAL), multiple(false), orderNumber(1), needConbin(true), way(TT_Y), name("") {};
	};

	/// 打分区
	enum SCORE_TYPE	{
		NONE   = 0, /// 
		NORMAL = 1, /// 一般的
		COMBIN = 2, /// 组合型 个位&十位
		QtCom  = 3, /// 填空题
	};
	struct SocringArea:PaperRect<double> { /// 关联在 boxInfo下面 多块的只有ordernumber=0需要识别分数
		bool		ocr;		/// 是否需要识别分数
		int			cols;		/// 列数
		SCORE_TYPE	type;	
		bool		withHalf;	/// 是否含有.5分
		int			limit;		/// 上限分值
		double      maxSocre;   /// 小问最大分值
		std::vector<double> vecSocreValue;	/// 每个格子对应的数据 小于零的项不可用（个位&十位格子）
		SocringArea() :cols(0), type(NONE),withHalf(false),ocr(false),limit(-1), maxSocre(-1.0){};
	};

	/// 单个填空题
	struct QuestionCompletion :SubjectiveField {
		SocringArea scoreBox;
	};

	/// 其他类型的box 只需要切割区域不需要额外操作的
	struct BoxInfo :SubjectiveField { 
		SocringArea scoreBox;
		BoxInfo() {};
	};

	/// 选做题的标识符 每个选做题&选做题填涂 都有唯一的Key 
	struct SubjectiveChooseKey	{ 
		std::vector<int> vecIds;			/// 选做题的id列表
		std::vector<std::string> vecNames;	/// 别名列表
		int idx;	/// 本块的序号 0 1 2 3 ...多选多的N序号
		bool hbtt;	/// 是否是合并填涂
		int m;		/// m选n
		int n;		/// m选n
		bool operator == (const SubjectiveChooseKey & _k) {
			return (_k.vecIds == vecIds) && (_k.idx == idx);
		}
		SubjectiveChooseKey() :idx(-1),hbtt(true) {};
	};

	/// 选做题的区域信息 
	struct SubjectiveChooseField:SubjectiveField{ 
		SubjectiveChooseKey key;
		SocringArea scoreBox;
	};

	/// 选做题的填涂判断信息
	struct SubjectiveChooseTt:QuestionChoice{ 
		SubjectiveChooseKey key;
	};

	/// 新逻辑的填涂信息
	struct _SubjectiveChooseTt : _QuestionChoice {
		SubjectiveChooseKey key;
	};

	/// 待定... 
	struct SubjectiveChoose	{ /// 选做题 分离&合并填涂  M选N 
		int		m;		/// 总数
		int		n;		/// 选做个数
		std::vector<int>	ids;		/// 题号列表
		std::vector<SubjectiveChooseField>	vecBoxes;	/// 图像区域列表
		std::vector<SubjectiveChooseTt>		vecTt;		/// 填涂区域列表
		
		virtual void push_back(const SubjectiveField & sjtField) = 0;
	};
	struct Combin:public SubjectiveChoose{ /// 合并填涂

	};
	struct Splite:public SubjectiveChoose{ /// 分离填涂
		Splite(const int & _m, const int & _n) {
			;
		}
	protected:
		 
	};

	/// 答题卡模板
	typedef struct PaperTemplateInfo {
		int index;	/// 模板页的序号 从0开始
		int width;	/// 
		int height;	/// 

		std::string						strJsonPath;		/// 模板json文件绝对路径
		std::string						strImgPath;			/// 本张模板图像路径/URL
		std::vector<BoxInfo>			vecBoxes;			/// 本张模板内主观题题目信息
		std::vector<LocationPoint>		vecLocaltionPoints;	/// 定位点信息
		std::vector<LocationArea>		vecLocaltionAreas;	/// 定位区信息
		std::vector<LocationCrossPoint> vecLocationCross;	/// 交叉点信息
		std::vector<QuestionChoice>		vecQtChoices;		/// 选择题
		std::vector<_QuestionChoice>	_vecQtChoices;		/// _选择题
		std::vector<QuesetionTF>		vecQtTf;			/// 判断题
		std::vector<QuestionCompletion> vecQtCompletion;	/// 填空题
		std::vector<AbsenceMark>		vecAbsenceMark;		/// 缺考&违纪
		std::vector<CodeInfo>			vecCodeBQ;			/// 条码/ 二维码
		std::vector<FillStuNumber>		vecFillNumber;		/// 填涂考号
		std::vector<_FillStuNumber>		_vecFillNumber;		/// _填涂考号
		std::vector<SubjectiveChooseField>  vecXzBoxes;		/// 选做题题目信息
		std::vector<SubjectiveChooseTt>		vecXzTtBoxes;	/// 选做题填涂信息
		std::vector<_SubjectiveChooseTt>	_vecXzTtBoxes;	/// 选做题填涂信息
		void * ptrAreaFeatures;	/// 定位区特征
	} ptinfo;
	typedef std::vector<PaperTemplateInfo> templatesInfo;

	/// 选做题信息
	struct XX {
		std::vector<int> vecTiHao; ///! 必须是按照题号 升序排列
		int m;
		int n;
		/**
			2022.1.19 题组实现逻辑
			问题：题组的结构是数量的N倍，与现有的逻辑冲突，但是如果把题组的题号拆分成两个
			选做题就可以实现，但是问题就是 第二块没有填涂区域，如何解决这个问题 ，
			方案：
			设定一个标识 判断是否使用其他题号的填涂判断结果 
			stealVecRes = true 则本题偷取 vecStealTiHao 的填涂结果
		*/
		bool stealVecRes;
		std::vector<int> vecStealTiHao;
		XX() :m(0), n(0), stealVecRes(false) { vecStealTiHao.clear(); };
	};

	struct SubjChiInfo	{
		bool hasSplitTT;
		bool hasCombinTT;
		std::vector<XX> vecSptt;
		std::vector<XX> vecCbtt;
		SubjChiInfo() :hasSplitTT(false), hasCombinTT(false) {};
	};

	struct QtNumInfo {
		std::vector<int> qtIds;
		int              qtNum;
	};
}

namespace analysis {

	/// 记录定位点 定位区 交叉点的查找情况
	struct LocationResult
	{
		std::vector<preinfo::LocationPoint> vecLcpts;		/// 图像找到的定位点
		std::vector<preinfo::LocationArea> vecAreas;		/// 图像找到的上的定位区
		std::vector<preinfo::LocationCrossPoint> vecCrosses;/// 图像找到的上的交叉点
		
		LocationResult() { vecLcpts.clear(); vecAreas.clear(); vecCrosses.clear(); }
	};

	/// 每张答题卡的匹配结果
	struct MatchResult {
		bool matched;				/// 是否匹配到了
		PAPER_STATUS matchedStatus;	/// 匹配过程中的状态记录
		int matchedCount;			/// 匹配到点数
		int totalCount;				/// 参与匹配的总数
		int schemaIndex;			/// 匹配到模板ID 从0开始
		double scale;				/// 与模板的比例系数
		int dir;					/// 方向
		LocationResult lcRes;		/// 定位信息结果
		double transfrom[6];		/// 第三方的3*2矫正举证		模板-图像
		double transfrom_[6];		/// 在线&第三方的3*2举证    图像-模板
		bool nineUse;				/// 是否优先使用3*3矩阵
		double olctransfrom_[9];	/// 在线答题卡的3*3映射矩阵 图像-模板
		MatchResult() { nineUse = false;  matched = false; matchedStatus = PAPER_SUCC; matchedCount = 0; totalCount = 0; schemaIndex = -1; scale = -1; dir = 0; }
	};
	struct PageRes {
		int idx;
		preinfo::DIR_FLAG dir;
		PageRes() { idx = -1; dir = preinfo::DIR_UNKNOW; }
	};
}

namespace result {
	/**
	  !所有填涂判断的起始值都是 -1 标识未填涂
	*/
	
	enum ABSENT_MARK {
		MARK_NORMAL = 0,			/// 正常
		MARK_ABSENCE = 1,			/// 缺考
		MARK_BREAK_PRECEDENT =2,	/// 违纪
		CHOICE_TOO_MUCH = 4,		/// 单选题 填涂了全部
		CHOICE_NONE		= 8,		/// 本题未填涂
	};

	/// 填涂题目结果
	struct ChoiceResult	{
		struct element	{
			int		index;		/// 按照从左到右/从上到下排列小项的下标 从-1开始 标识未填涂
			double	confidence;	/// 置信度
			element() :index(-1), confidence(0.0) {};
		};
		int id;					/// 与模板的ID对应
		int tiHao;				/// 题号
		ABSENT_MARK status;		/// 本题的状态
		int number;				/// 本题小项个数
		int mostCredible;		/// 单选情况下 最可信的ID下标
		std::vector<std::string> vecOpts;	/// 本题结果选项是哪一个
		preinfo::BOX_TYPE type;	/// 多选还是单选
		std::vector<bool> vecRes;/// _看需求 是否启用 true:填涂了 false:未填涂
		std::vector<element> vecIdx;/// 备选方案 
		ChoiceResult() :id(-1),tiHao(-1), status(MARK_NORMAL), number(0), type(preinfo::BOX_QUESTION_CHOICE_M) {};
	};

	/// 判断题
	struct QTFRes {
		int		id;
		bool	fill;
		QTFRes() :id(-1), fill(false) {};
	};

	/// 主观题题目
	struct SubjectiveResult	{
		struct IMG_INFO {
			std::vector<unsigned char> imgData;
			bool vaild;
			int cols;
			int rows;
			int channel;
			IMG_INFO() :vaild(false), cols(0), rows(0), channel(0) {};
		};
		int		id;				/// 不是题号 是一个UUID雷士的东西
		std::vector<int> uuids;	/// 多块合并的图像，是可能有多个UUID的 2022.1.19 新增
		int		th;				/// 本题的题号
		std::vector<int> tiHao;	/// 选做题适用
		bool	multiple;		/// 是否多块 
		int		orderNumber;	/// 如果是多块 本块序号  默认从1开始 如果是合并得到图则 ordernumber=-1
		double	score;			/// 分数
		preinfo::BOX_TYPE	type;	/// BOX TYPE
		int					marktype;	/// 是否保存分块的小图 只有4和10保留
		int					idx;	/// 只有type=选做题时候有效
		IMG_INFO			img;	/// 线下阅卷问题 预留方案
		/*
			2022.1.19 改动 多个小块的图像有可能有多个打分框 所以要做支持
			改动记录：
			由 vector<int> 改动到 unordered_map<int, std::vector<int>> ,int = uuid 作为唯一关联key值
		**/
		std::vector<int>    sr;		/// 分数判断结果 内容：红色批改的框的红色值 个数：所有小框数量和 
		std::unordered_map<int, std::vector<int>> mapSr; /// 记录每个uuid对应的打分框的识别结果

		std::string			path;	/// 绝对路径
		std::string			name;	/// 落地文件名 
		int					width;	/// 2021.4.24 需求需要提供主观题的宽高信息
		int					height;	/// ...
		SubjectiveResult() :id(-1), multiple(false), orderNumber(1),marktype(0),
			score(0.0),idx(-1), type(preinfo::BOX_NONE), path(""), name(""), width(0),height(0) {};
	};

	/// 选做题
	struct SubjectiveChooseResult{
		preinfo::SubjectiveChooseKey key;
		preinfo::BOX_TYPE	type;
		int id;
		int tIdx;	/// 选中的下标 0：可能是未填涂默认值  也有可能是填涂了第一个
		std::string			path;	/// 绝对路径
		std::string			name;	/// 文件名
		double				score;	/// 分数
		SubjectiveChooseResult() :type(preinfo::BOX_SUBJECTIVE_CHOOSE), id(-1), tIdx(-1), path(""), name(""), score(0.0) {};
	};

	/// 选做题结果简单记录
	struct SubjChResRecord {
		std::vector<int> vecTiHaos;
		int m; int n;
		preinfo::BOX_TYPE	type;
		std::vector<int> vecRes;
	};

	/// 识别的单份答题卡结果
	typedef struct SinglePaperInfo {
		int				orderNumber;		/// 本份的序号			备注：orderNumber = totalNumbers/mb.size()
		bool			last;				/// 是否本批次最后一份
		int				idx;				/// 如果状态是异常,标明那一张答题卡出现了异常,下标0开始 备注：首先判断status的状态
		int				numbers;			/// 本份页数			备注：理论上等于模板的张数
		int				choiceNumbers;		/// 所有填涂题目个数	备注：所有选择题总数量
		PAPER_STATUS	status;				/// 本份试卷状态标志	备注：见枚举定义
		ABSENT_MARK		absent;				/// 缺考 or 违纪		备注：见枚举定义
		std::string		barCode;			/// 条码识别结果		备注：条形码识别结果 仅仅是条形码结果
		std::string		qrCode;				/// 二维码识别结果		备注：二维码识别结果 空值只能标明未识别到或者没有条码 条码也适用
		std::string		fillNumber;			/// 填涂考号			备注：长度=模板设定考号列数，未填涂位置用' '空格填充 !!!重要
		std::string		strSavePath;		/// 本份的保存路径		备注：备份的保存路径下面会有 xx.jpg xx_.jpg等等图像
		std::vector<std::string>			vecUrlPath;			/// 图像的本地路径					备注：客户端需求添加
		std::vector<std::string>			vecUrlAPath;		/// 矫正后的原图路径				备注：矫正后的图，顺序为 img_1.jpg img_2.jpg ... 不超过模板张数
		std::vector<QTFRes>					tfRes;				/// 判断题							备注：该类型未实现
		std::vector<ChoiceResult>			choiceRes;			/// 所有填涂题目结果				备注：使用tiHao（int) 和 vecOpts（判断结果）即可 ，单选题vecOpts可能是一个或者全部数量（对应全部填涂错误）多选择是真实个数
		std::shared_ptr<ChoiceResult>		ptrChoiceRes;		/// 所有填涂题目结果				备注：未启用 请忽略
		std::vector<SubjectiveResult>		subjectiveRes;		/// 主观题							备注：所有主观题（填空，解答，选做等等）都在这里面 th标识本题最终输出结果 
		std::shared_ptr<SubjectiveResult>	ptrSubjective;		/// ......							备注：未启用 请忽略
		std::vector<SubjChResRecord>		subjectiveCRes;		/// 选做题							备注：存放选择题判断结果 内部使用请忽略
		std::shared_ptr<SubjChResRecord>	ptrSubjectiveC;		///									备注：未启用 请忽略
		std::vector<analysis::LocationResult>	vecLcRes;		///  定位信息的结果保存				备注：与 vecUrlPath 一一对应
		SinglePaperInfo() :orderNumber(-1), last(false), idx(-1), numbers(0), choiceNumbers(0), status(PAPER_SUCC), absent(MARK_NORMAL),
			barCode(""), qrCode(""), fillNumber(""), strSavePath("") {};
	}spinfo;



}

