1 /****************************************************************\
2 *                                                                *
3 *  Module of splice site and intron modelling                    *
4 *                                                                *
5 *  Guy St.C. Slater..   mailto:guy@ebi.ac.uk                     *
6 *  Copyright (C) 2000-2009.  All Rights Reserved.                *
7 *                                                                *
8 *  This source code is distributed under the terms of the        *
9 *  GNU General Public License, version 3. See the file COPYING   *
10 *  or http://www.gnu.org/licenses/gpl.txt for details            *
11 *                                                                *
12 *  If you use this code, please keep this notice intact.         *
13 *                                                                *
14 \****************************************************************/
15 
16 #include "intron.h"
17 #include "ungapped.h"
18 
Intron_ArgumentSet_create(Argument * arg)19 Intron_ArgumentSet *Intron_ArgumentSet_create(Argument *arg){
20     register ArgumentSet *as;
21     static Intron_ArgumentSet ias;
22     if(arg){
23         as = ArgumentSet_create("Intron Modelling Options");
24         ArgumentSet_add_option(as, 0, "minintron", "length",
25             "Minimum intron length", "30",
26             Argument_parse_int, &ias.min_intron);
27         ArgumentSet_add_option(as, 0, "maxintron", "length",
28             "Maximum intron length", "200000",
29             Argument_parse_int, &ias.max_intron);
30         ArgumentSet_add_option(as, 'i', "intronpenalty", "score",
31             "Intron Opening penalty", "-30",
32             Argument_parse_int, &ias.intron_open_penalty);
33         Argument_absorb_ArgumentSet(arg, as);
34         ias.sps = NULL;
35         if(ias.intron_open_penalty > 0)
36             g_warning("Intron open penalty should be negative [%d]",
37                       ias.intron_open_penalty);
38     } else {
39         if(!ias.sps)
40             ias.sps = SplicePredictorSet_create();
41             /* FIXME: this should be freed somewhere */
42         }
43     return &ias;
44     }
45 
46 /**/
47 
Intron_ChainData_get_seq_func(gint start,gint len,gpointer user_data)48 static gchar *Intron_ChainData_get_seq_func(gint start,
49                                             gint len,
50                                             gpointer user_data){
51     register Sequence *s = user_data;
52     return Sequence_get_substr(s, start, len);
53     }
54 
Intron_ChainData_init_splice_prediction(Intron_ChainData * icd,Sequence * s,SplicePredictorSet * sps,SpliceType type,gboolean use_single)55 void Intron_ChainData_init_splice_prediction(Intron_ChainData *icd,
56         Sequence *s, SplicePredictorSet *sps, SpliceType type,
57         gboolean use_single){
58     if(!icd->sps)
59         icd->sps = g_new0(SplicePrediction_Set, 1);
60     icd->sps = SplicePrediction_Set_add(icd->sps, sps, type, s->len,
61             Intron_ChainData_get_seq_func, use_single, s);
62     return;
63     }
64 
Intron_ChainData_init(Intron_ChainData * icd)65 static void Intron_ChainData_init(Intron_ChainData *icd){
66     icd->curr_intron_start = 0;
67     return;
68     }
69 
Intron_ChainData_create(void)70 static Intron_ChainData *Intron_ChainData_create(void){
71     register Intron_ChainData *icd = g_new0(Intron_ChainData, 1);
72     Intron_ChainData_init(icd);
73     return icd;
74     }
75 
Intron_ChainData_destroy(Intron_ChainData * icd)76 static void Intron_ChainData_destroy(Intron_ChainData *icd){
77     if(icd->sps)
78         SplicePrediction_Set_destroy(icd->sps);
79     g_free(icd);
80     return;
81     }
82 
83 /**/
84 
Intron_Data_create(void)85 Intron_Data *Intron_Data_create(void){
86     register Intron_Data *id = g_new0(Intron_Data, 1);
87     id->ias = Intron_ArgumentSet_create(NULL);
88     id->query_data = Intron_ChainData_create();
89     id->target_data = Intron_ChainData_create();
90     return id;
91     }
92 
Intron_Data_destroy(Intron_Data * id)93 void Intron_Data_destroy(Intron_Data *id){
94     Intron_ChainData_destroy(id->query_data);
95     Intron_ChainData_destroy(id->target_data);
96     g_free(id);
97     return;
98     }
99 
100 /**/
101 
intron_init_func(Region * region,gpointer user_data)102 static void intron_init_func(Region *region, gpointer user_data){
103     register Ungapped_Data *ud = user_data;
104     register Intron_Data *id = Ungapped_Data_get_Intron_Data(ud);
105     if(!id->curr_region){
106         id->curr_region = Region_share(region);
107         Intron_ChainData_init(id->query_data);
108         Intron_ChainData_init(id->target_data);
109         }
110     return;
111     }
112 
113 static gchar *intron_init_macro =
114     "if(!id->curr_region){\n"
115     "    id->curr_region = Region_share(region);\n"
116     "    Intron_ChainData_init(id->query_data);\n"
117     "    Intron_ChainData_init(id->target_data);\n"
118     "    }\n";
119 
intron_exit_func(Region * region,gpointer user_data)120 static void intron_exit_func(Region *region, gpointer user_data){
121     register Ungapped_Data *ud = user_data;
122     register Intron_Data *id = Ungapped_Data_get_Intron_Data(ud);
123     if(id->curr_region){
124         Region_destroy(id->curr_region);
125         id->curr_region = NULL;
126         }
127     return;
128     }
129 
130 static gchar *intron_exit_macro =
131     "if(id->curr_region){\n"
132     "    Region_destroy(id->curr_region);\n"
133     "    id->curr_region = NULL;\n"
134     "    }\n";
135 
136 /**/
137 
138 #define Intron_CalcFunc(site, chain, is_pre)                       \
139     static C4_Score Intron_calc_##site##_##chain(                  \
140         gint query_pos, gint target_pos, gpointer user_data){      \
141         register Ungapped_Data *ud = user_data;                    \
142         register Intron_Data *id                                   \
143                 = Ungapped_Data_get_Intron_Data(ud);               \
144         register C4_Score score = 0;                               \
145         register gint intron_length;                               \
146         g_assert(id->chain##_data->sps->site);                     \
147         g_assert(id->curr_region);                                 \
148         if(is_pre){                                                \
149             score = id->ias->intron_open_penalty;                  \
150         } else {                                                   \
151             intron_length = chain##_pos                            \
152                           - id->chain##_data->curr_intron_start    \
153                           + 2;                                     \
154             if((intron_length < id->ias->min_intron)               \
155             || (intron_length > id->ias->max_intron))              \
156                 return C4_IMPOSSIBLY_LOW_SCORE;                    \
157             }                                                      \
158         score += SplicePrediction_get(id->chain##_data->sps->site, \
159                                       chain##_pos);                \
160         return score;                                              \
161         }
162 
163 #define Intron_JointCalcFunc(site, is_pre)                          \
164     static C4_Score Intron_joint_calc_##site(                       \
165         gint query_pos, gint target_pos, gpointer user_data){       \
166         register Ungapped_Data *ud = user_data;                     \
167         register Intron_Data *id                                    \
168                 = Ungapped_Data_get_Intron_Data(ud);                \
169         register C4_Score score = 0;                                \
170         register gint intron_length;                                \
171         g_assert(id->query##_data->sps->site);                      \
172         g_assert(id->target##_data->sps->site);                     \
173         g_assert(id->curr_region);                                  \
174         if(is_pre){                                                 \
175             score = id->ias->intron_open_penalty;                   \
176         } else {                                                    \
177             intron_length = query_pos                               \
178                           - id->query_data->curr_intron_start       \
179                           + 2;                                      \
180             if((intron_length < id->ias->min_intron)                \
181             || (intron_length > id->ias->max_intron))               \
182                 return C4_IMPOSSIBLY_LOW_SCORE;                     \
183             intron_length = target_pos                              \
184                           - id->target_data->curr_intron_start      \
185                           + 2;                                      \
186             if((intron_length < id->ias->min_intron)                \
187             || (intron_length > id->ias->max_intron))               \
188                 return C4_IMPOSSIBLY_LOW_SCORE;                     \
189             }                                                       \
190         score += SplicePrediction_get(id->query##_data->sps->site,  \
191                                       query_pos);                   \
192         score += SplicePrediction_get(id->target##_data->sps->site, \
193                                       target_pos);                  \
194         return score;                                               \
195         }
196 
Intron_get_calc_macro_splice(gboolean on_query,gchar * site)197 static gchar *Intron_get_calc_macro_splice(gboolean on_query,
198                                            gchar *site){
199     register gchar *chain = on_query?"query":"target";
200     register gchar *chain_macro = on_query?"QP":"TP";
201     return g_strdup_printf("SplicePrediction_get(id->%s_data->sps->%s, %%%s)",
202             chain, site, chain_macro);
203     }
204 
Intron_get_calc_macro_length(gboolean on_query,gchar * input_macro)205 static gchar *Intron_get_calc_macro_length(gboolean on_query,
206                                            gchar *input_macro){
207     register gchar *chain = on_query?"query":"target";
208     register gchar *chain_macro = on_query?"QP":"TP";
209     register gchar *intron_length = g_strdup_printf(
210                     "(%%%s - id->%s_data->curr_intron_start + 2)",
211                      chain_macro, chain);
212     register gchar *macro = g_strdup_printf(
213                     "(((%s < id->ias->min_intron)"
214                     "||(%s > id->ias->max_intron))"
215                     "?C4_IMPOSSIBLY_LOW_SCORE:%s)",
216                     intron_length, intron_length, input_macro);
217     g_free(intron_length);
218     return macro;
219     }
220 
Intron_get_calc_macro(gboolean is_5_prime,gboolean is_forward,gboolean on_query,gboolean on_target,gboolean is_pre)221 static gchar *Intron_get_calc_macro(gboolean is_5_prime,
222                                     gboolean is_forward,
223                                     gboolean on_query,
224                                     gboolean on_target,
225                                     gboolean is_pre){
226     register gchar *macro = NULL;
227     register gchar *site = g_strdup_printf("ss%d_%s", is_5_prime?5:3,
228                                       is_forward?"forward":"reverse");
229     register gchar *splice_calc;
230     register gchar *a, *b;
231     if(on_query && on_target){ /* joint */
232         a = Intron_get_calc_macro_splice(TRUE, site);
233         b = Intron_get_calc_macro_splice(FALSE, site);
234         splice_calc = g_strdup_printf("(%s + %s)", a, b);
235         g_free(a);
236         g_free(b);
237     } else { /* single */
238         splice_calc = Intron_get_calc_macro_splice(on_query, site);
239         }
240     /**/
241     if(is_pre){
242         macro = g_strdup_printf("(%s + %s)",
243                      "id->ias->intron_open_penalty", splice_calc);
244     } else {
245         if(on_query && on_target){ /* joint */
246             a = Intron_get_calc_macro_length(TRUE, splice_calc);
247             macro = Intron_get_calc_macro_length(FALSE, a);
248             g_free(a);
249         } else { /* single */
250             macro = Intron_get_calc_macro_length(on_query, splice_calc);
251             }
252         }
253     g_free(splice_calc);
254     g_free(site);
255     g_assert(macro);
256     return macro;
257     }
258 
259 #define Intron_InitFunc(site, chain)                                         \
260     static void Intron_init_##site##_##chain(                                \
261                              Region *region, gpointer user_data){            \
262         register Ungapped_Data *ud = user_data;                              \
263         register Intron_Data *id = Ungapped_Data_get_Intron_Data(ud);        \
264         Intron_ChainData_init_splice_prediction(id->chain##_data, ud->chain, \
265                                            id->ias->sps,                     \
266                                            SpliceType_##site,                \
267                                            FALSE);                           \
268     return;                                                                  \
269     }
270 
271 #define Intron_JointInitFunc(site)                                 \
272     static void Intron_joint_init_##site(                          \
273                              Region *region, gpointer user_data){  \
274     Intron_init_##site##_query(region, user_data);                 \
275     Intron_init_##site##_target(region, user_data);                \
276     return;                                                        \
277     }
278 
Intron_get_strand_init_macro(gboolean is_5_prime,gboolean is_forward,gboolean on_query)279 static gchar *Intron_get_strand_init_macro(gboolean is_5_prime,
280                                            gboolean is_forward,
281                                            gboolean on_query){
282     register gchar *chain = on_query?"query":"target";
283     register gchar *site = g_strdup_printf("ss%d_%s",
284             is_5_prime?5:3, is_forward?"forward":"reverse");
285     register gchar *macro = g_strdup_printf(
286         "Intron_ChainData_init_splice_prediction(id->%s_data, ud->%s, id->ias->sps,"
287         "                                        SpliceType_%s,"
288         "        ((id->curr_region->%s_start == 0)"
289         "        && (id->curr_region->%s_length == ud->%s->len)));",
290         chain, chain, site, chain, chain, chain);
291     g_free(site);
292     return macro;
293     }
294 
Intron_get_init_macro(gboolean is_5_prime,gboolean is_forward,gboolean on_query,gboolean on_target)295 static gchar *Intron_get_init_macro(gboolean is_5_prime,
296                                     gboolean is_forward,
297                                     gboolean on_query,
298                                     gboolean on_target){
299     register gchar *qy_macro, *tg_macro, *macro;
300     if(on_query && on_target){
301         qy_macro = Intron_get_strand_init_macro(is_5_prime, is_forward,
302                                                 TRUE);
303         tg_macro = Intron_get_strand_init_macro(is_5_prime, is_forward,
304                                                 FALSE);
305         macro = g_strdup_printf("%s;%s", qy_macro, tg_macro);
306         g_free(qy_macro);
307         g_free(tg_macro);
308     } else {
309         macro = Intron_get_strand_init_macro(is_5_prime, is_forward,
310                                              on_query);
311         }
312     return macro;
313     }
314 
315 #if 0
316 #define Intron_InitMacro(site, chain, chainmacro)               \
317 static gchar *intron_## site ##_## chain ##_init_macro =        \
318     "if(!id->"#chain"_data->"#site"){\n"                        \
319     "    id->"#chain"_data->"#site" = SplicePrediction_create(" \
320     "          id->ias->sps->"# site ",\n"                      \
321     "          ud->"#chain"->seq,\n"                            \
322     "          ud->"#chain"->len,\n"                            \
323     "          %"#chainmacro"S, %"#chainmacro"L);\n"            \
324     "    }\n";
325 /* FIXME: do not give len when doing DP over unknown region
326  *        or give hint, eg: computing_full_dp
327  */
328 #endif /* 0 */
329 
330 #if 0
331 #define Intron_ExitFunc(site, chain)                                  \
332     static void Intron_exit_##site##_##chain(                         \
333                        Region *region, gpointer user_data){           \
334         register Ungapped_Data *ud = user_data;                       \
335         register Intron_Data *id = Ungapped_Data_get_Intron_Data(ud); \
336         if(id->chain##_data->site){                                   \
337             SplicePrediction_destroy(id->chain##_data->site);         \
338             id->chain##_data->site = NULL;                            \
339             }                                                         \
340         return;                                                       \
341         }
342 
343 #define Intron_JointExitFunc(site)                                    \
344     static void Intron_joint_exit_##site(                             \
345                        Region *region, gpointer user_data){           \
346         Intron_exit_##site##_query(region, user_data);                \
347         Intron_exit_##site##_target(region, user_data);               \
348         return;                                                       \
349         }
350 #endif /* 0 */
351 
352 #if 0
353 #define Intron_ExitMacro(site, chain)                                 \
354 static gchar *intron_##site##_##chain##_exit_macro =                  \
355         "if(id->"#chain"_data->"#site"){\n"                           \
356         "    SplicePrediction_destroy(id->"#chain"_data->"#site");\n" \
357         "    id->"#chain"_data->"#site" = NULL;"                      \
358         "    }\n";
359 #endif /* 0 */
360 
361 #if 0
362 static gchar *Intron_get_strand_exit_macro(gboolean is_5_prime,
363                                            gboolean is_forward,
364                                            gboolean on_query){
365     register gchar *chain = on_query?"query":"target";
366     register gchar *site = g_strdup_printf("ss%d_%s",
367             is_5_prime?5:3, is_forward?"forward":"reverse");
368     return g_strdup_printf(
369         "if(id->%s_data->%s){\n"
370         "    SplicePrediction_destroy(id->%s_data->%s);\n"
371         "    id->%s_data->%s = NULL;"
372         "    }\n",
373         chain, site, chain, site, chain, site);
374     }
375 
376 static gchar *Intron_get_exit_macro(gboolean is_5_prime,
377                                     gboolean is_forward,
378                                     gboolean on_query,
379                                     gboolean on_target){
380     register gchar *qy_macro, *tg_macro, *macro;
381     if(on_query && on_target){
382         qy_macro = Intron_get_strand_exit_macro(is_5_prime, is_forward,
383                                                 TRUE);
384         tg_macro = Intron_get_strand_exit_macro(is_5_prime, is_forward,
385                                                 FALSE);
386         macro = g_strdup_printf("%s;%s", qy_macro, tg_macro);
387         g_free(qy_macro);
388         g_free(tg_macro);
389     } else {
390         macro = Intron_get_strand_exit_macro(is_5_prime, is_forward,
391                                              on_query);
392         }
393     return macro;
394     }
395 #endif /* 0 */
396 
397 /**/
398 
399 #define Intron_CalcElements(site, chain, chainmacro, is_pre) \
400     Intron_CalcFunc(site, chain, is_pre)                     \
401     Intron_InitFunc(site, chain)
402 
403     /* Intron_ExitFunc(site, chain) */
404 
405 #define Intron_JointCalcElements(site, is_pre) \
406     Intron_JointCalcFunc(site, is_pre)         \
407     Intron_JointInitFunc(site)
408 
409 
410     /* Intron_JointExitFunc(site) */
411 
412 /* pre */
Intron_CalcElements(ss5_forward,query,Q,TRUE)413 Intron_CalcElements(ss5_forward, query, Q, TRUE)
414 Intron_CalcElements(ss5_forward, target, T, TRUE)
415 Intron_JointCalcElements(ss5_forward, TRUE)
416 
417 /* post */
418 Intron_CalcElements(ss3_forward, query, Q, FALSE)
419 Intron_CalcElements(ss3_forward, target, T, FALSE)
420 Intron_JointCalcElements(ss3_forward, FALSE)
421 
422 /* pre */
423 Intron_CalcElements(ss3_reverse, query, Q, TRUE)
424 Intron_CalcElements(ss3_reverse, target, T, TRUE)
425 Intron_JointCalcElements(ss3_reverse, FALSE)
426 
427 /* post */
428 Intron_CalcElements(ss5_reverse, query, Q, FALSE)
429 Intron_CalcElements(ss5_reverse, target, T, FALSE)
430 Intron_JointCalcElements(ss5_reverse, FALSE)
431 
432 #define Intron_assign_calc_elements(site, chain)       \
433     calc_func = Intron_calc_##site##_##chain;          \
434     init_func = Intron_init_##site##_##chain;
435 
436     /* exit_func = Intron_exit_##site##_##chain; */
437 
438 /**/
439 
440 #define Intron_assign_joint_calc_elements(site)    \
441     calc_func = Intron_joint_calc_##site;          \
442     init_func = Intron_joint_init_##site;
443 
444     /* exit_func = Intron_joint_exit_##site; */
445 
446 /**/
447 
448 /*
449 Functions and macros for intron length shadow:
450 
451 Intron_{query,target}_{start,end}_{func,macro}
452 */
453 
454 #define Intron_StartFunc(chain)                           \
455 static C4_Score Intron_##chain##_start_func(              \
456     gint query_pos, gint target_pos, gpointer user_data){ \
457     return (C4_Score)chain##_pos;                         \
458     }
459 
460 #define Intron_StartMacro(chain, chainmacro) \
461 static gchar *Intron_##chain##_start_macro   \
462     = "(C4_Score)(%"#chainmacro"P)";
463 
464 #define Intron_StartElements(chain, chainmacro) \
465     Intron_StartFunc(chain)                     \
466     Intron_StartMacro(chain, chainmacro)
467 
468 #define Intron_EndFunc(chain)                                     \
469 static void Intron_##chain##_end_func(C4_Score score,             \
470     gint query_pos, gint target_pos, gpointer user_data){         \
471     register Ungapped_Data *ud = user_data;                       \
472     register Intron_Data *id = Ungapped_Data_get_Intron_Data(ud); \
473     g_assert(id);                                                 \
474     id->chain##_data->curr_intron_start = (gint)score;            \
475     return;                                                       \
476     }
477 
478 #define Intron_EndMacro(chain)                            \
479 static gchar *Intron_##chain##_end_macro                  \
480     = "id->"#chain"_data->curr_intron_start = (gint)%SS";
481 
482 /**/
483 
484 #define Intron_EndElements(chain) \
485     Intron_EndFunc(chain)         \
486     Intron_EndMacro(chain)
487 
488 #define Intron_ShadowElements(chain, chainmacro) \
489     Intron_StartElements(chain, chainmacro)      \
490     Intron_EndElements(chain)
491 
492 Intron_ShadowElements(query, Q)
493 Intron_ShadowElements(target, T)
494 
495 /**/
496 
497 static C4_Calc *Intron_add_calc(C4_Model *model,
498                                 gchar *prefix, gchar *suffix,
499                                 gboolean use_pre,
500                                 gboolean is_forward,
501                                 gboolean on_query, gboolean on_target){
502     register gchar *name = g_strconcat(prefix, " ", suffix, NULL);
503     register Intron_ArgumentSet *ias
504            = Intron_ArgumentSet_create(NULL);
505     register SplicePredictor *sp;
506     register C4_Calc *calc;
507     register C4_CalcFunc calc_func;
508     register C4_PrepFunc init_func;
509     register gchar *calc_macro, *init_macro;
510     register C4_Score bound = 0;
511     register gboolean is_5_prime = is_forward?(use_pre?TRUE:FALSE)
512                                              :(use_pre?FALSE:TRUE);
513     if(is_forward){
514         if(use_pre){
515             sp = ias->sps->ss5_forward;
516             bound = ias->intron_open_penalty;
517             if(on_query){
518                 if(on_target){
519                     Intron_assign_joint_calc_elements(ss5_forward);
520                 } else {
521                     Intron_assign_calc_elements(ss5_forward, query);
522                     }
523             } else {
524                 Intron_assign_calc_elements(ss5_forward, target);
525                 }
526         } else {
527             sp = ias->sps->ss3_forward;
528             if(on_query){
529                 if(on_target){
530                     Intron_assign_joint_calc_elements(ss3_forward);
531                 } else {
532                     Intron_assign_calc_elements(ss3_forward, query);
533                     }
534             } else {
535                 Intron_assign_calc_elements(ss3_forward, target);
536                 }
537             }
538     } else {
539         if(use_pre){
540             sp = ias->sps->ss3_reverse;
541             bound = ias->intron_open_penalty;
542             if(on_query){
543                 if(on_target){
544                     Intron_assign_joint_calc_elements(ss3_reverse);
545                 } else {
546                     Intron_assign_calc_elements(ss3_reverse, query);
547                     }
548             } else {
549                 Intron_assign_calc_elements(ss3_reverse, target);
550                 }
551         } else {
552             sp = ias->sps->ss5_reverse;
553             if(on_query){
554                 if(on_target){
555                     Intron_assign_joint_calc_elements(ss5_reverse);
556                 } else {
557                     Intron_assign_calc_elements(ss5_reverse, query);
558                     }
559             } else {
560                 Intron_assign_calc_elements(ss5_reverse, target);
561                 }
562             }
563         }
564     bound += SplicePredictor_get_max_score(sp);
565     if(on_query && on_target) /* Double for joint intron */
566         bound += SplicePredictor_get_max_score(sp);
567     calc_macro = Intron_get_calc_macro(is_5_prime, is_forward,
568                                        on_query, on_target, use_pre);
569     init_macro = Intron_get_init_macro(is_5_prime, is_forward,
570                                        on_query, on_target);
571     /*
572     exit_macro = Intron_get_exit_macro(is_5_prime, is_forward,
573                                        on_query, on_target);
574     */
575     calc = C4_Model_add_calc(model, name, bound,
576                              calc_func, calc_macro,
577                              init_func, init_macro,
578                              NULL, NULL,
579                              C4_Protect_UNDERFLOW);
580     g_assert(calc_macro);
581     g_assert(init_macro);
582     g_free(calc_macro);
583     g_free(init_macro);
584     g_free(name);
585     return calc;
586     }
587 
Intron_create(gchar * suffix,gboolean on_query,gboolean on_target,gboolean is_forward)588 C4_Model *Intron_create(gchar *suffix, gboolean on_query,
589                         gboolean on_target, gboolean is_forward){
590     register gchar *name, *pre_name, *post_name;
591     register C4_Model *model;
592     register C4_State *intron_state;
593     register gint qy_splice_advance, tg_splice_advance;
594     register C4_Calc *pre_calc, *post_calc;
595     register C4_Label pre_label, post_label;
596     register Intron_ArgumentSet *ias
597            = Intron_ArgumentSet_create(NULL);
598     g_assert(on_query || on_target);
599     name = g_strconcat("intron ", suffix, NULL);
600     model = C4_Model_create(name);
601     g_free(name);
602     if(is_forward){
603         pre_name = "5'ss forward";
604         post_name = "3'ss forward";
605         pre_label = C4_Label_5SS;
606         post_label = C4_Label_3SS;
607     } else {
608         pre_name = "3'ss reverse";
609         post_name = "5'ss reverse";
610         pre_label = C4_Label_3SS;
611         post_label = C4_Label_5SS;
612         }
613     if(on_query){
614         if(on_target){ /* Joint intron */
615             qy_splice_advance = 2;
616             tg_splice_advance = 2;
617         } else {
618             qy_splice_advance = 2;
619             tg_splice_advance = 0;
620             }
621     } else { /* on target */
622         qy_splice_advance = 0;
623         tg_splice_advance = 2;
624         }
625     /* Add calcs */
626     pre_calc = Intron_add_calc(model, pre_name, suffix, TRUE,
627                     is_forward, on_query, on_target);
628     post_calc = Intron_add_calc(model, post_name, suffix, FALSE,
629                     is_forward, on_query, on_target);
630     /* Add intron state */
631     name = g_strconcat("intron ", suffix, NULL);
632     intron_state = C4_Model_add_state(model, name);
633     g_free(name);
634     /* Add transition START->intron */
635     name = g_strconcat("(START) to ", intron_state->name, NULL);
636     C4_Model_add_transition(model, name, NULL, intron_state,
637        qy_splice_advance, tg_splice_advance, pre_calc, pre_label, NULL);
638     g_free(name);
639     /* Add transition intron->intron */
640     if(on_query){
641         name = g_strconcat("query intron loop ", suffix, NULL);
642         C4_Model_add_transition(model, name, intron_state, intron_state,
643             1, 0, NULL, C4_Label_INTRON, NULL);
644         g_free(name);
645         }
646     if(on_target){
647         name = g_strconcat("target intron loop ", suffix, NULL);
648         C4_Model_add_transition(model, name, intron_state, intron_state,
649             0, 1, NULL, C4_Label_INTRON, NULL);
650         g_free(name);
651         }
652     /* Add transition intron->(END)*/
653     name = g_strconcat(intron_state->name, " to (END)", NULL);
654     C4_Model_add_transition(model, name,
655         intron_state, NULL, qy_splice_advance, tg_splice_advance,
656         post_calc, post_label, NULL);
657     g_free(name);
658     /* Add span state */
659     name = g_strconcat("intron span", suffix, NULL);
660     if(on_query){
661         if(on_target){
662             C4_Model_add_span(model, name, intron_state,
663                               ias->min_intron, ias->max_intron,
664                               ias->min_intron, ias->max_intron);
665         } else {
666             C4_Model_add_span(model, name, intron_state,
667                               ias->min_intron, ias->max_intron, 0, 0);
668             }
669     } else {
670         C4_Model_add_span(model, name, intron_state,
671                           0, 0, ias->min_intron, ias->max_intron);
672         }
673     g_free(name);
674     /* Add shadows */
675     if(on_query){
676         name = g_strconcat("query intron ", suffix, NULL);
677         C4_Model_add_shadow(model, name, NULL, NULL,
678                 Intron_query_start_func, Intron_query_start_macro,
679                 Intron_query_end_func, Intron_query_end_macro);
680         g_free(name);
681         }
682     if(on_target){
683         name = g_strconcat("target intron ", suffix, NULL);
684         C4_Model_add_shadow(model, name, NULL, NULL,
685                 Intron_target_start_func, Intron_target_start_macro,
686                 Intron_target_end_func, Intron_target_end_macro);
687         g_free(name);
688         }
689     /* Add DP init/exit funcs/macros */
690     C4_Model_configure_extra(model,
691         intron_init_func, intron_init_macro,
692         intron_exit_func, intron_exit_macro);
693     C4_Model_append_codegen(model, NULL,
694             "register Intron_Data *id = ud->intron_data;\n", NULL);
695     C4_Model_close(model);
696     return model;
697     }
698 
699