source: experiments/frams/foraminifera/data/scripts/foraminifera.expdef @ 435

Last change on this file since 435 was 435, checked in by oriona, 9 years ago

stepToNearest call corrected.

File size: 18.6 KB
Line 
1expdef:
2name:Reproduction of benthic foraminifera
3info:~
4Basic information about this simulation:
5www.framsticks.com/foraminifera
6
7Technical information:
8Genes and parameter values which control reproduction are stored in user1 and user2 fields.
9
10user1:
11genes which are not encoded in Ff genotype:
12min_repro_energy - Minimum energy necessary for reproduction
13hibernation - Defines foram behavior in the case of no nutrients
14
15user2:
16Physiological parameters of foraminifera:
17max_energy_level - maximum energy level reached so far
18gen - generation: 0 haploid, 1 diploid
19species - species: 0 not hibernating 1 hibernating
20hibernated - 0/1 foram is/isn't hibernated
21reproduce - 0/1 foram isn't/is ready for reproduction
22~
23code:~
24
25
26global nutrientenergywaiting;
27global reprocounter;
28global colors;
29global chambers;
30global o;
31global max_chamber_energ;
32global dir_change;
33
34@include "foraminifera.inc"
35
36// -------------------------------- experiment begin --------------------------------
37
38function onExpDefLoad()
39{
40        // define genotype and creature groups
41        GenePools.clear();
42        Populations.clear();
43        GenePools[0].name = "Unused";
44
45        var pop = Populations[0];
46        pop.name = "Forams";
47        pop.en_assim = 0;
48        pop.nnsim = 0;
49        pop.enableperf = 1;
50        pop.death = 1;
51        pop.energy = 1;
52        pop.selfmask = 0x10001;
53        pop.othermask = 0x20001;
54        //pop.selfmask = 0x20002; pop.othermask = 0x10002;
55        pop.perfperiod = 25;
56
57        pop = Populations.addGroup("Nutrients");
58        pop.nnsim = 0;
59        pop.enableperf = 0;
60        pop.death = 1;
61        pop.energy = 1;
62        pop.selfmask = 0x20002;
63        pop.othermask = 0x10000;
64        //pop.othermask = 0x10002;
65
66        //ExpParams.showRet = 1; //uncomment to show reticulopodia
67
68        pop = Populations.addGroup("Reticulopodia");
69        pop.nnsim = 0;
70        pop.enableperf = 0;
71        pop.death = 0;
72        pop.energy = 0;
73        pop.selfmask = 0x20002;
74        pop.othermask = 0x10000;
75       
76        //world
77        SignalView.mode = 1;
78        World.wrldwat = 200;
79        World.wrldsiz = scale(40000);
80        World.wrldbnd = 1;
81        ExpParams.stress = 1;
82        ExpParams.creath = -0.99; //just above the bottom
83        ExpParams.autorestart = 0;
84
85        //ExpParams.logging = 1; //uncomment to enable logging simulation parameters to log files
86
87        //reproduction
88        ExpParams.foramPop = 10;       
89        ExpParams.crossprob = 0.4;
90        ExpParams.mutationprob = 0.2;
91        ExpParams.repro_time = 20;
92        reprocounter = 0;
93
94        //morphology
95        dir_change = 500;
96        ExpParams.zone1_range = scale(5000);
97        ExpParams.zone2_range = scale(8000);
98        init_chambers();
99        ExpParams.chamber_proculus_haplo = scale(50);
100        ExpParams.chamber_difference_haplo = 0.0;
101        ExpParams.chamber_proculus_diplo = scale(20);
102        ExpParams.chamber_difference_diplo = 0.2;
103        max_chamber_energ = [Vector.new(), Vector.new()];
104        for (var j = 0; j < 2; j++)
105        {
106                for (var i = 0; i < chambers[0].size; i++)
107                {
108                        max_chamber_energ[j].add(((Math.pow(getProperty(j, "chamber_proculus"),3) + Math.pow(getProperty(j, "chamber_proculus") + (i) * getProperty(j, "chamber_difference"),3))*(i+1))/2);
109                }                                 
110        }
111
112        //energetics
113        ExpParams.min_repro_energ_haplo = 4;
114        ExpParams.min_repro_energ_diplo = 6;
115
116        ExpParams.e_meta = 0.0002;
117        ExpParams.energy_hib = 0.0001;
118        ExpParams.energy_move = 0.00025;
119
120        ExpParams.energies0_haplo = max_chamber_energ[0][0] - 0.001*max_chamber_energ[0][0];
121        ExpParams.energies0_diplo = max_chamber_energ[1][0] - 0.001*max_chamber_energ[1][0];
122        ExpParams.feedtrans = 0.5;
123        ExpParams.e_repro_cost_haplo = 0.7;
124        ExpParams.e_repro_cost_diplo = 0.3;
125
126        //nutrients
127        ExpParams.nutrientsize = scale(10);
128        ExpParams.energy_nut = 100 * Math.pow(ExpParams.nutrientsize, 3);
129        ExpParams.nutrientPop = 1;
130        ExpParams.feedrate = 0.1;
131        nutrientenergywaiting = ExpParams.energy_nut;
132        ExpState.totaltestedcr = 0;
133        ExpState.nutrient = "";
134}
135
136@include "standard_placement.inc"
137
138function scale(x)
139{
140        return x*0.025;
141}
142
143function getProperty(gen, prop_id)
144{
145        var ploid = "haplo";
146        if (gen == 1) ploid = "diplo";
147        return ExpParams.[prop_id + "_" + ploid];
148}
149
150function addInitialForam(species, i)
151{
152        var geno = createForamGenotype(0, species, 0);
153        var cr = Populations[0].add(geno);
154        cr.name = "Initial creature" + species + "_" + i;
155        placeCreatureRandomly(cr, 0, 0);
156        cr.energy0 = getProperty(0, "energies0");
157        cr.energy = cr.energy0;
158        setGenotype({"opt" : 0, "cr" : cr, "species" : species});
159}
160
161function onExpInit()
162{
163        Populations[0].clear();
164        Populations[1].clear();
165        Populations[2].clear();
166
167        for (var i = 0; i < ExpParams.foramPop; i++)
168        {
169                addInitialForam(0, i); 
170                addInitialForam(1, i);
171        }
172        o = Populations[0][0].getMechPart(0).orient.clone();
173        ExpState.totaltestedcr = 0;
174        nutrientenergywaiting = ExpParams.energy_nut;
175}
176
177function onExpLoad()
178{
179        for (var pop in Populations)
180                pop.clear();
181
182        Loader.addClass(sim_params.*);
183        Loader.setBreakLabel(Loader.BeforeUnknown, "onExpLoad_Unknown");
184        Loader.run();
185
186        Simulator.print("Loaded " + Populations[0].size + " Forams and " + Populations[1].size + " nutrient objects");
187}
188
189function onExpLoad_Unknown()
190{
191        if (Loader.objectName == "org") // saved by the old expdef
192        {
193                var g = Genotype.newFromString("");
194                Loader.currentObject = g;
195                Interface.makeFrom(g).setAllDefault();
196                Loader.loadObject();
197                var cr = Populations[0].add(g);
198                if (cr != null)
199                {
200                        //cr.rotate(0,0,Math.rnd01*Math.twopi);
201                        if ((typeof(g.user1) == "Vector") && (g.user1.size >= 3))
202                        {
203                                // [x,y,energy]
204                                cr.move(g.user1[0] - cr.center_x, g.user1[1] - cr.center_y, 0);
205                                cr.energy = g.user1[2];
206                        }
207                        else
208                        {
209                                cr.move(Math.rnd01 * World.wrldsiz - cr.center_x, Math.rnd01 * World.wrldsiz - cr.center_y, 0);
210                        }
211                }
212        }
213        else if (Loader.objectName == "Creature")
214        {
215                Loader.currentObject = CreatureSnapshot.new();
216                Loader.loadObject();
217                Populations[0].add(Loader.currentObject);
218        }
219}
220
221function onExpSave()
222{
223        File.writeComment("saved by '%s.expdef'" % Simulator.expdef);
224
225        var tmpvec = [], i;
226
227        for(var cr in Populations[1])
228                tmpvec.add([cr.center_x, cr.center_y, cr.energy]);
229
230        ExpState.nutrient = tmpvec;
231        File.writeObject(sim_params.*);
232        ExpState.nutrient = null; //vectors are only created for saving and then discarded
233
234        for (var cr in Populations[0])
235                File.writeObject(cr);
236}
237
238// -------------------------------- experiment end --------------------------------
239
240// -------------------------------- foram begin -----------------------------------
241
242function setForamMeta(cr, gen)
243{
244        cr.idleen = ExpParams.e_meta * max_chamber_energ[gen][Math.min(lastChamberNum(cr), max_chamber_energ[gen].size-1)];
245}
246
247function lastChamberNum(cr)
248{
249        return cr.numparts-1;
250}
251
252function onForamsBorn(cr)
253{
254        setForamMeta(cr, 1);
255        if (ExpParams.showRet == 1)
256        {
257                var ret = Populations[2].add("//0\np:sh=3,sx=0.01,sy="+ExpParams.zone1_range+",sz="+ExpParams.zone1_range+",ry=1.57");
258                cr.user3 = ret;
259        }
260}
261
262function placeRandomlyNotColliding(cr)
263{
264        var retry = 100; //try 100 times
265        while (retry--)
266        {
267                placeCreatureRandomly(cr, 0, 0);
268                if (!cr.boundingBoxCollisions(0))
269                        return cr;
270        }
271
272        Populations[0].delete(cr);
273}
274
275function foramGrow(cr, chamber_num)
276{
277        var geno = createForamGenotype(cr.user2["gen"], cr.user2["species"], chamber_num+1);
278        var cr2 = Populations[0].add(geno);
279
280        cr2.energy0 = cr.energy;
281        cr2.energy = cr2.energy0;
282
283        setGenotype({"cr" : cr2, "parent_user1" : cr.user1, "parent_user2" : cr.user2, "opt" : 2});
284        cr2.moveAbs(cr.center_x - cr2.size_x / 2, cr.center_y - cr2.size_y / 2, cr.pos_z);
285        setForamMeta(cr2, cr2.user2["gen"]);
286
287        if (ExpParams.showRet == 1)
288        {
289                Populations[2].delete(cr.user3);
290        }
291        Populations[0].delete(cr);
292}
293
294function stepToNearest(cr)
295{
296        var p = cr.getMechPart(0);
297        var n = cr.signals.receiveSet("nutrient", ExpParams.zone2_range);
298
299        //if signals are received find the source of the nearest
300        if (n.size > 0)
301        {
302                var i;
303                var mp;
304                var distvec = XYZ.new(0, 0, 0);
305                var dist;
306                var mindist = 100000000000;
307                var mindistvec = null;
308                var eating = 0;
309
310                for (i = 0; i < n.size; i++)
311                {
312                        mp = n[i].value.getMechPart(0);
313                        distvec.set(mp.pos);
314                        distvec.sub(p.pos);
315                        dist = distvec.length;
316                        if (dist < ExpParams.zone1_range)
317                        {
318                                if (n[i].value != null)
319                                {
320                                        energyTransfer(cr, n[i].value);
321                                        eating = 1;
322                                }
323                        }
324                        else if (eating == 0 && cr.user2["hibernated"] == 0 && dist < mindist)
325                        {
326                                mindist = dist;
327                                mindistvec = distvec.clone();
328                        }
329                }
330
331                if (!eating && cr.user2["hibernated"] == 0)
332                {
333                        mindistvec.normalize();
334                        mindistvec.scale(-0.08);
335                        cr.localDrive = mindistvec;
336                        moveEnergyDec(cr);
337                }
338
339                return 1;
340        }
341       
342        else
343                return 0;
344}
345
346function moveEnergyDec(cr)
347{
348        if (cr.user2["hibernated"] == 0)
349        {
350                cr.energy_m += ExpParams.energy_move * max_chamber_energ[cr.user2["gen"]][Math.min(lastChamberNum(cr), (max_chamber_energ[cr.user2["gen"]].size)-1)];
351        }
352}
353
354function foramMove(cr)
355{
356        //TODO moving inside sediment?
357
358        //adjustment in z axis
359        cr.moveAbs(cr.pos_x, cr.pos_y, 0);
360
361        //are there any nutrients in zone 1 or 2?
362        {
363                var moved = stepToNearest(cr); //TODO weighted sum of distance and energy
364                if (moved==1)
365                {
366                        return;
367                }
368        }
369
370        //no nutrients in zone 2
371        var hibernation = 0;
372        if (cr.user2["gen"] == 0) hibernation =  cr.user1["hibernation"];
373        else hibernation =  cr.user1[0]["hibernation"];
374        //hibernation
375        if (hibernation == 1)
376        {
377                reverseHib(cr);
378                cr.localDrive = XYZ.new(0,0,0);
379        }
380        //random move
381        else if (cr.lifespan%dir_change == 0)
382        {
383                var dir = (Math.rndUni(-ExpParams.zone2_range, ExpParams.zone2_range), Math.rndUni(-ExpParams.zone2_range, ExpParams.zone2_range), 0); 
384                dir.normalize();
385                dir.scale(-0.08);
386                cr.localDrive = dir;
387                moveEnergyDec(cr);
388        }
389}
390
391function energyTransfer(cr1, cr2)
392{
393        cr1.localDrive = XYZ.new(0,0,0);
394        var e = cr2.getPart(0).ing * ExpParams.feedtrans; //TODO efficiency dependent on age
395        e = Math.min(cr2.energy, e) + 0.0000001;
396        //Simulator.print("transferring "+e+" to "+cr1.name+" from "+cr2.name+" ("+cr2.energy+")");
397        cr2.energy_m = cr2.energy_m + e;
398        cr1.energy_p = cr1.energy_p + e;
399        if (cr1.user2["hibernated"] == 1)
400        {
401                reverseHib(cr1);
402        }
403}
404
405function reverseHib(cr)
406{
407        if (cr.user2["hibernated"] == 1)
408        {
409                setForamMeta(cr, cr.user2["gen"]); //unhibernate
410        }
411        else
412        {
413                cr.idleen = ExpParams.energy_hib * max_chamber_energ[cr.user2["gen"]][Math.min(lastChamberNum(cr), (max_chamber_energ[cr.user2["gen"]].size)-1)]; //hibernate
414        }
415        cr.user2["hibernated"] = 1 - cr.user2["hibernated"];
416}
417
418function onForamsStep(cr)
419{
420        cr.getMechPart(0).orient.set(o);
421        if (ExpParams.showRet == 1)
422                cr.user3.moveAbs(cr.center_x-ExpParams.zone1_range, cr.center_y-ExpParams.zone1_range, cr.center_z-ExpParams.zone1_range-getProperty(cr.user2["gen"], "chamber_proculus"));
423
424        if (deathConditions(cr) == 1)
425        {
426                Populations[0].kill(cr);
427                return;
428        }
429
430        foramMove(cr);
431
432        var repro = foramReproduce(cr);
433        if (repro == 1)
434        {
435                return;
436        }
437
438        cr.user2["max_energy_level"] = Math.max(cr.energy, cr.user2["max_energy_level"]);
439        if  (lastChamberNum(cr) <= chambers[0].size-1)
440        {
441                if ((cr.user2["max_energy_level"] >= max_chamber_energ[cr.user2["gen"]][lastChamberNum(cr)]))   
442                {
443                        foramGrow(cr, lastChamberNum(cr));
444                }       
445        }       
446}
447
448function deathConditions(cr)
449{
450        if ((cr.energy <= ExpParams.e_death_level) || (Math.rnd01 < ExpParams.hunted_prob))
451                return 1;
452        else
453                return 0;
454}
455
456function onForamsDied(cr)
457{
458        if (ExpParams.showRet == 1)
459        {
460                Populations[2].delete(cr.user3);
461        }
462        //fossilization
463        var geno = GenePools[0].add(cr.genotype);
464        geno.user1 = cr.user1;
465        geno.user2 = cr.user2;
466        if (ExpParams.logging == 1) Simulator.print("\"" + cr.name + "\" died...");
467        ExpState.totaltestedcr++;
468}
469
470// --------------------------------foram end -------------------------------------
471
472// -------------------------------- nutrient begin --------------------------------
473
474function createNutrientGenotype(nutrientsize, zone1_range)
475{
476        return "//0\np:sh=3,sx="+(nutrientsize+25)+",sy="+nutrientsize+",sz="+nutrientsize+",ry=1.5,vr=0.0,1.0,0.0";
477}
478
479function onNutrientsStep(cr)
480{
481        cr.moveAbs(cr.pos_x % World.wrldsiz, cr.pos_y % World.wrldsiz, -ExpParams.zone1_range+0.5);
482}
483
484function addNutrient()
485{
486        var cr = Populations[1].add(createNutrientGenotype(ExpParams.nutrientsize, ExpParams.zone1_range));
487
488        cr.name = "Nutrients";
489        cr.idleen = 0;
490        cr.energy0 = ExpParams.energy_nut;
491        cr.energy = cr.energy0;
492        cr.signals.add("nutrient");
493
494        cr.signals[0].value = cr;
495
496        placeCreatureRandomly(cr, 0, 0);
497}
498
499function nutrientGrowth()
500{
501        nutrientenergywaiting = nutrientenergywaiting + ExpParams.feedrate;
502        if (nutrientenergywaiting > ExpParams.energy_nut)
503        {
504                for (var i = 0; i < ExpParams.nutrientPop; i++)
505                {   
506                        addNutrient();
507                }
508
509                nutrientenergywaiting = 0.0;
510                Simulator.checkpoint();
511        }
512}
513
514// -------------------------------- nutrient end --------------------------------
515
516// -------------------------------- step begin --------------------------------
517
518function onStep()
519{
520
521        nutrientGrowth();
522        if (ExpParams.logging == 1)
523        {
524                createStatistics();
525        }
526
527        //reproduction --------------------------------------------
528        reprocounter += 1;
529        if (reprocounter > ExpParams.repro_time)
530        {
531                reprocounter = 0;
532                reproduce_parents(0);
533                reproduce_parents(1);
534        }
535
536        //check for extinction -----------------------------------------------
537        if (Populations[0].size == 0)
538        {
539                if (ExpParams.autorestart)
540                {
541                        Simulator.print("no more creatures, restarting...");
542                        onExpInit();
543                }
544                else
545                {
546                        Simulator.print("no more creatures, stopped.");
547                        Simulator.stop();
548                }
549        }
550        if (ExpParams.maxSteps > 0)
551        {
552                if (Simulator.stepNumber >= ExpParams.maxSteps)
553                        Simulator.stop();
554        }
555}
556
557function createStatistics()
558{       
559        var number = [[0, 0],[0,0]]; // [species][gen]
560        var e_inc = [[0, 0],[0,0]];
561        var e_nut = 0.0;
562
563        for (var i = 0; i < Populations[0].size; i++)
564        {
565                var cr = Populations[0].get(i);
566                var gen = cr.user2["gen"];
567                var species = cr.user2["species"];
568
569                number[species][gen] = number[species][gen] + 1;
570                e_inc[species][gen] = e_inc[species][gen] + cr.energy;
571        }
572
573        for (var i = 0; i < Populations[1].size; i++)
574        {
575                var cr = Populations[1].get(i);
576                e_nut += cr.energy;
577        }
578
579        var log_numbers = [number[1][0], number[1][1], number[0][0], number[0][1], Populations[1].size];
580        var log_energies = [e_inc[1][0], e_inc[1][1], e_inc[0][0], e_inc[0][1], e_nut];
581
582        log(log_numbers, "forams_log.txt");
583        log(log_energies,  "energies_log.txt");
584}
585
586function log(tolog, fname)
587{
588        var f = File.appendDirect(fname, "forams data");
589        f.writeString("" + Simulator.stepNumber);
590        for (var  i = 0; i < tolog.size; i++)
591        {
592                f.writeString(";" + tolog[i]);
593        }
594        f.writeString("\n");
595        f.close();
596}
597
598// -------------------------------- step end --------------------------------
599
600@include "standard_events.inc"
601
602~
603
604prop:
605id:showRet
606name:Show reticulopodia
607type:d 0 1 0
608group:Foraminifera
609
610prop:
611id:maxSteps
612name:Stop after the given number of simulation steps
613type:d 0 1000000 0
614
615prop:
616id:e_repro_cost_haplo
617name:Cost of reproduction
618type:f 0.1 0.9 0.5
619group:Foraminifera
620
621prop:
622id:e_repro_cost_diplo
623name:Cost of reproduction
624type:f 0.1 0.9 0.3
625group:Foraminifera
626
627prop:
628id:chamber_proculus_haplo
629name:Size of proculus
630type:f
631group:Foraminifera
632
633prop:
634id:chamber_proculus_diplo
635name:Size of proculus
636type:f
637group:Foraminifera
638
639prop:
640id:chamber_difference_haplo
641name:Difference in size between subsequent chambers
642type:f
643group:Foraminifera
644
645prop:
646id:chamber_difference_diplo
647name:Difference in size between subsequent chambers
648type:f
649group:Foraminifera
650
651prop:
652id:hunted_prob
653name:Probability of being hunted
654type:f 0 1 0
655group:Forminifera
656
657prop:
658id:zone1_range
659name:Zone 1 range
660type:f 0 200
661group:Foraminifera
662
663prop:
664id:zone2_range
665name:Zone 2 range
666type:f 0 3000
667group:Foraminifera
668
669prop:
670id:colors
671name:Haploid and diploid colors
672type:x
673group:Foraminifera
674
675prop:
676id:min_repro_energ_haplo
677name:Min reproduction energy of forams
678type:f
679group:Foraminifera
680
681prop:
682id:min_repro_energ_diplo
683name:Min reproduction energy of forams
684type:f
685group:Foraminifera
686
687prop:
688id:repro_prob
689name:Probability of reproduction
690type:f 0 1 0.8
691group:Foraminifera
692
693prop:
694id:energies0_haplo
695name:Energy of offspring from diploid forams
696type:f
697group:Foraminifera
698
699prop:
700id:energies0_diplo
701name:Energy of offspring from diploid forams
702type:f
703group:Foraminifera
704
705prop:
706id:energy_hib
707name:Energy used for hibernation during one step
708type:f 0 1 0.001
709group:Foraminifera
710
711prop:
712id:energy_move
713name:Energy used for movement during one step
714type:f 0 20 0.001
715group:Foraminifera
716
717prop:
718id:min_vol
719name:Minimal volume for reproduction
720type:f 100 900 100
721group:Foraminifera
722
723prop:
724id:max_size
725name:Maximal size
726type:d 1 10 5
727group:Foraminifera
728
729prop:
730id:foramPop
731name:Initial forams population size
732type:d 1 1000 100
733group:Foraminifera
734
735prop:
736id:crossprob
737name:Crossover probability
738type:f 0 1 0
739group:Foraminifera
740
741prop:
742id:mutationprob
743name:Mutation probability
744type:f 0 1 0
745group:Foraminifera
746
747prop:
748id:e_death_level
749name:Minimal level of energy to sustain life
750type:f 0 20 0
751group:Foraminifera
752
753prop:
754id:e_meta
755name:Idle metabolism
756type:f 0 1
757group:Energy
758help:Each stick consumes this amount of energy in one time step
759
760prop:
761id:feedrate
762name:Feeding rate
763type:f 0 100
764group:Energy
765help:How fast energy is created in the world
766
767prop:
768id:energy_nut
769name:Nutrient energy
770type:f 0 1000
771group:Energy
772
773prop:
774id:feedtrans
775name:Ingestion multiplier
776type:f 0 100
777group:Energy
778
779prop:
780id:nutrientsize
781name:Nutrient size
782type:f 0.1 0.9 0.1
783group:Energy
784
785prop:
786id:nutrientPop
787name:Nutrient population size
788group:Energy
789type:d 1 1000 10
790
791prop:
792id:stress
793name:Environmental stress
794type:d 0 1 1
795group:World
796
797prop:
798id:repro_trigger
799name:Reproduction trigger
800type:d 0 1 1
801group:World
802
803prop:
804id:repro_time
805name:Time before reproduction
806type:d 0 1000
807
808prop:
809id:creath
810name:Creation height
811type:f -1 50
812help:~
813Vertical position (above the surface) where new Forams are revived.
814Negative values are only used in the water area:
815  0   = at the surface
816-0.5 = half depth
817-1   = just above the bottom~
818
819state:
820id:nutrient
821name:Nutrient locations
822help:vector of vectors [x,y,energy]
823type:x
824flags:32
825
826prop:
827id:autorestart
828name:Restart after extinction
829help:Restart automatically this experiment after the last creature has died?
830type:d 0 1
831
832state:
833id:notes
834name:Notes
835type:s 1
836help:~
837You can write anything here
838(it will be saved to the experiment file)~
839
840state:
841id:totaltestedcr
842name:Evaluated Forams
843help:Total number of the Forams evaluated in the experiment
844type:d
845flags:16
846
847prop:
848id:logging
849name:Log statistics to file
850type:d 0 1 0
Note: See TracBrowser for help on using the repository browser.