Browse code

Generate gantt diagram using relevant algorithm

Cinan Rakosnik authored on 16/04/2018 at 07:36:35
Showing 20 changed files
... ...
@@ -2,41 +2,97 @@ import React, { PureComponent } from 'react';
2 2
 import Form from './components/Form';
3 3
 import Gantt from './components/Gantt';
4 4
 import { normalizeData } from './ganttUtils';
5
+import algorithms from './algorithms';
6
+import { filter, flatten, identity, map, pluck, range, times } from 'rambda';
7
+import uuid from 'uuid/v4';
8
+import { selectAlgorithms, fastestResult, najneomeskanejsiResult } from './selectAlgorithm';
5 9
 
6
-const algoData = [
7
-  [
8
-    { startTime: 1, endTime: 3, processor: 1, name: 'UI dizajnér' },
9
-    { startTime: 4, endTime: 6, processor: 1, name: 'UX dizajnér' },
10
-    { startTime: 6, endTime: 10, processor: 1, name: 'UI dizajnér' },
11
-  ],
12
-  [
13
-    { startTime: 6, endTime: 10, processor: 2, name: 'UI dizajnér' },
14
-    { startTime: 22, endTime: 23, processor: 2, name: 'UI dizajnér' },
15
-    { startTime: 23, endTime: 24, processor: 2, name: 'UI dizajnér' },
16
-    { startTime: 24, endTime: 25, processor: 2, name: 'UI dizajnér' },
17
-  ],
18
-  [
19
-    { startTime: 10, endTime: 16, processor: 3, name: 'UI dizajnér' },
20
-  ],
21
-  [
22
-    { startTime: 10, endTime: 16, processor: 4, name: 'UI dizajnér' },
23
-  ],
24
-  [
25
-    { startTime: 10, endTime: 16, processor: 5, name: 'UI dizajnér' },
26
-  ],
27
-  [
28
-    { startTime: 10, endTime: 16, processor: 6, name: 'UI dizajnér' },
29
-  ]
30
-];
31
-
32
-const data = normalizeData(algoData);
10
+// const algoData = [
11
+//   [
12
+//     { startTime: 1, endTime: 3, processor: 1, name: 'UI dizajnér' },
13
+//     { startTime: 4, endTime: 6, processor: 1, name: 'UX dizajnér' },
14
+//     { startTime: 6, endTime: 10, processor: 1, name: 'UI dizajnér' },
15
+//   ]
16
+// ];
17
+//
18
+// const data = normalizeData(algoData);
33 19
 
34 20
 class App extends PureComponent {
21
+  state = { ganttData: null };
22
+
23
+  onFormSubmit = ({ preempt, rows }) => {
24
+    const algorithmNames = selectAlgorithms(rows, preempt);
25
+
26
+    if (!algorithmNames) {
27
+      alert('Pre zvolenú konfiguráciu sa nenašiel vhodný algoritmus');
28
+      return;
29
+    }
30
+
31
+    const results = map(algorithmName => {
32
+      const algorithm = algorithms[algorithmName];
33
+
34
+      let jobTimes;
35
+
36
+      if (algorithmName === 'johnson' || algorithmName === 'palmer' || algorithmName === 'grupt' || algorithmName === 'campbel') {
37
+        jobTimes = times(i => ({
38
+          jobId: uuid(),
39
+          operations: map(j => ({ t: rows[j].jobs[i].t }), range(0, rows.length))
40
+        }), rows[0].jobs.length);
41
+      } else if (algorithmName === 'mcnaught' || algorithmName === 'vahy') {
42
+        jobTimes = flatten(pluck('jobs', rows));
43
+        jobTimes = map(jobTime => ({
44
+          ...jobTime,
45
+          jobId: jobTime.id,
46
+          anc: map(anc => anc.split(':')[1], jobTime.anc || []) }), jobTimes);
47
+      } else {
48
+        jobTimes = map(job => ({
49
+          jobId: job.id,
50
+          t: +job.t,
51
+          d: +job.d,
52
+          w: +job.w,
53
+          name: job.name,
54
+          anc: map(anc => anc.split(':')[1], job.anc || [])
55
+        }), rows[0].jobs);
56
+      }
57
+
58
+      // console.log(jobTimes);
59
+      // return;
60
+
61
+      // console.log(rows[1].jobs);
62
+
63
+      const result = algorithm(jobTimes, rows.length);
64
+      return result;
65
+    }, algorithmNames);
66
+
67
+    const validResults = filter(identity, results);
68
+    const fastestIndex = rows.length === 1
69
+      ? najneomeskanejsiResult(validResults)
70
+      : fastestResult(validResults);
71
+
72
+    if (fastestIndex === -1) {
73
+      alert('Nepodarilo sa násť rozvrh pre zvolenú konfiguráciu');
74
+      return;
75
+    }
76
+
77
+    // console.log(fastestIndex, validResults);
78
+
79
+    const ganttData = normalizeData(validResults[fastestIndex]);
80
+
81
+    console.log(validResults[fastestIndex]);
82
+
83
+    this.setState({
84
+      ganttData
85
+    });
86
+  };
87
+
35 88
   render() {
89
+    const { ganttData } = this.state;
90
+
36 91
     return (
37 92
       <div style={{ padding: 20 }}>
38
-        <Form />
39
-        <Gantt data={data} />
93
+        <Form onSubmit={this.onFormSubmit} />
94
+        {ganttData && <Gantt data={ganttData}/>}
95
+        {/*<Gantt data={data} />*/}
40 96
       </div>
41 97
     );
42 98
   }
... ...
@@ -1,31 +1,16 @@
1 1
 import {
2 2
   addIndex,
3
-  findIndex,
4 3
   find,
5 4
   map,
6 5
   propEq,
7 6
   reduce,
8
-  sort,
9 7
   range,
10
-  concat,
11
-  filter,
12
-  reverse,
13
-  prop,
14
-  flatten,
15
-  uniq,
16
-  reject,
17
-  contains,
18
-  omit,
19
-  equals,
20
-  update,
21 8
   times,
22
-  add,
23
-  pluck,
24 9
   last
25 10
 } from 'rambda';
26 11
 import type { JobId } from '../../types';
27
-import { max, min, remove, slice } from 'ramda';
28 12
 import { createSchedule as johnson, updateCTime } from '../johnson';
13
+import { max } from 'ramda';
29 14
 
30 15
 type JobOperations = {
31 16
   jobId: JobId,
... ...
@@ -37,7 +22,26 @@ type JobOperationsWithC = {
37 37
   operations: Array<{ t: number, c: number }>
38 38
 };
39 39
 
40
+export const fillCorrupted = (jobsOperations: JobOperations[]) => {
41
+  const maxOperationsCount = reduce((acc, { operations }) => max(operations.length, acc), 0, jobsOperations);
42
+
43
+  return map(
44
+    ({ operations, ...rest }) => {
45
+      const fixedOperations = [...operations];
46
+      fixedOperations.length = maxOperationsCount;
47
+
48
+      return {
49
+        ...rest,
50
+        operations: fixedOperations.fill({ t: 0 }, operations.length, maxOperationsCount)
51
+      };
52
+    },
53
+    jobsOperations
54
+  )
55
+};
56
+
40 57
 export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => {
58
+  jobsOperations = fillCorrupted(jobsOperations);
59
+
41 60
   const allSchedules = map(k => {
42 61
     const jobsOperationsForK = map((jobsOperation: JobOperations) => {
43 62
       const t1 = reduce((acc, i) => acc + jobsOperation.operations[i].t, 0, range(0, k));
... ...
@@ -2,6 +2,7 @@ import { map, reduce, sort, range, omit, times } from 'rambda';
2 2
 import type { JobId } from '../../types';
3 3
 import { updateCTime } from '../johnson';
4 4
 import { min } from 'ramda';
5
+import { fillCorrupted } from '../campbel';
5 6
 
6 7
 type JobOperations = {
7 8
   jobId: JobId,
... ...
@@ -14,6 +15,8 @@ type JobOperationsWithC = {
14 14
 };
15 15
 
16 16
 export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => {
17
+  jobsOperations = fillCorrupted(jobsOperations);
18
+
17 19
   const jobsOperationsWithKoefs = map((jobsOperation: JobOperations) => {
18 20
     const ej = jobsOperation.operations[0].t < jobsOperation.operations[processorsCount - 1].t
19 21
       ? 1 : -1;
20 22
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+import campbel from './campbel';
1
+import grupt from './grupt';
2
+import johnson from './johnson';
3
+import lawler from './lawler';
4
+import mcnaught from './mcnaught';
5
+import moore from './moore';
6
+import palmer from './palmer';
7
+import smith from './smith';
8
+import vahy from './vahy';
9
+
10
+export default {
11
+  campbel,
12
+  grupt,
13
+  johnson,
14
+  lawler,
15
+  mcnaught,
16
+  moore,
17
+  palmer,
18
+  smith,
19
+  vahy
20
+};
0 21
\ No newline at end of file
... ...
@@ -1,18 +1,19 @@
1 1
 import {
2
-  addIndex, findIndex, find, map, propEq, reduce, sort, range, concat,
3
-  filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times
2
+  findIndex, map, reduce, sort, concat,
3
+  filter, reverse, equals, update, times
4 4
 } from 'rambda';
5 5
 import type { JobId } from '../../types';
6
-import { max, min, remove, slice } from 'ramda';
6
+import { remove } from 'ramda';
7
+import { fillCorrupted } from '../campbel';
7 8
 
8 9
 type JobOperations = {
9 10
   jobId: JobId,
10
-  operations: Array<{ t: number }>
11
+  operations: [{ t: number }, { t: number }]
11 12
 };
12 13
 
13 14
 type JobOperationsWithC = {
14 15
   jobId: JobId,
15
-  operations: Array<{ t: number, c: number }>
16
+  operations: [{ t: number, c: number }, { t: number, c: number }]
16 17
 };
17 18
 
18 19
 const findMinT = (operations) =>
... ...
@@ -72,6 +73,8 @@ export const updateCTime = (jobsOperations: JobOperations[], processorCount: num
72 72
 };
73 73
 
74 74
 export const createSchedule = (jobsOperations: JobOperations[]): JobOperationsWithC[] => {
75
+  jobsOperations = fillCorrupted(jobsOperations);
76
+
75 77
   let I = jobsOperations;
76 78
   let L = [[], []];
77 79
 
... ...
@@ -1,26 +1,18 @@
1 1
 import {
2
-  addIndex, findIndex, find, map, propEq, reduce, sort, range, concat,
3
-  filter, reverse, prop, flatten, uniq, reject, contains, omit
2
+  findIndex, map, propEq, reduce,
3
+  reverse, prop, flatten, uniq, reject, contains, omit
4 4
 } from 'rambda';
5 5
 import type { JobId } from '../../types';
6
-import { max, remove, slice } from 'ramda';
6
+import { max, remove } from 'ramda';
7 7
 
8 8
 type JobTime = {
9 9
   jobId: JobId,
10 10
   t: number,
11 11
   d: number,
12
-  c: number,
13 12
   w: number,
14 13
   anc: JobId[],
15 14
 };
16 15
 
17
-const findMaxTJob = (jobTimes: JobTime[]) =>
18
-  reduce(
19
-    (maxTJob: JobTime, jobTime: JobTime) => maxTJob.t >= jobTime.t ? maxTJob : jobTime,
20
-    { t: -Infinity },
21
-    jobTimes
22
-  );
23
-
24 16
 const findMinJob = (fi: number, jobTimes: JobTime[]) =>
25 17
   reduce(
26 18
     (minJob: JobTime, jobTime: JobTime) => {
... ...
@@ -45,7 +37,13 @@ const updateCTime = (jobTimes: JobTime[]) => {
45 45
   return cj;
46 46
 };
47 47
 
48
+const fillCorrupted = (jobs: JobTime[]) => map(
49
+  ({ d, w, anc, ...rest }) => ({ ...rest, d: d || 1000, w: w || 1, anc: anc || [] }),
50
+  jobs);
51
+
48 52
 export const createSchedule = (jobs: JobTime[]) => {
53
+  jobs = fillCorrupted(jobs);
54
+
49 55
   let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs);
50 56
   let jobsD = jobs;
51 57
   let R = [];
... ...
@@ -76,10 +74,13 @@ export const createSchedule = (jobs: JobTime[]) => {
76 76
 export default (jobs: JobTime[]) => {
77 77
   const schedule = createSchedule(jobs);
78 78
 
79
+  if (!schedule) return null;
80
+
79 81
   const normalizedSchedule = map((jobTime: JobTime) => ({
80 82
     processor: 1,
81 83
     startTime: jobTime.c - jobTime.t,
82 84
     endTime: jobTime.c,
85
+    delayed: max(0, jobTime.c - jobTime.d)
83 86
   }), schedule);
84 87
 
85 88
   return [normalizedSchedule];
... ...
@@ -50,15 +50,15 @@ test('create gantt', () => {
50 50
 
51 51
   expect(template.length).toBe(1);
52 52
   expect(template[0].length).toBe(11);
53
-  expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 });
54
-  expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 14 });
55
-  expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 19 });
56
-  expect(template[0][3]).toEqual({ processor: 1, startTime: 19, endTime: 24 });
57
-  expect(template[0][4]).toEqual({ processor: 1, startTime: 24, endTime: 27 });
58
-  expect(template[0][5]).toEqual({ processor: 1, startTime: 27, endTime: 36 });
59
-  expect(template[0][6]).toEqual({ processor: 1, startTime: 36, endTime: 45 });
60
-  expect(template[0][7]).toEqual({ processor: 1, startTime: 45, endTime: 50 });
61
-  expect(template[0][8]).toEqual({ processor: 1, startTime: 50, endTime: 58 });
62
-  expect(template[0][9]).toEqual({ processor: 1, startTime: 58, endTime: 63 });
63
-  expect(template[0][10]).toEqual({ processor: 1, startTime: 63, endTime: 68 });
53
+  expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6, delayed: 0 });
54
+  expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 14, delayed: 3 });
55
+  expect(template[0][2]).toEqual({ processor: 1, startTime: 14, endTime: 19, delayed: 5 });
56
+  expect(template[0][3]).toEqual({ processor: 1, startTime: 19, endTime: 24, delayed: 15 });
57
+  expect(template[0][4]).toEqual({ processor: 1, startTime: 24, endTime: 27, delayed: 13 });
58
+  expect(template[0][5]).toEqual({ processor: 1, startTime: 27, endTime: 36, delayed: 17 });
59
+  expect(template[0][6]).toEqual({ processor: 1, startTime: 36, endTime: 45, delayed: 9 });
60
+  expect(template[0][7]).toEqual({ processor: 1, startTime: 45, endTime: 50, delayed: 11 });
61
+  expect(template[0][8]).toEqual({ processor: 1, startTime: 50, endTime: 58, delayed: 27 });
62
+  expect(template[0][9]).toEqual({ processor: 1, startTime: 58, endTime: 63, delayed: 2 });
63
+  expect(template[0][10]).toEqual({ processor: 1, startTime: 63, endTime: 68, delayed: 9 });
64 64
 });
65 65
\ No newline at end of file
... ...
@@ -1,9 +1,6 @@
1
-import {
2
-  addIndex, findIndex, find, map, propEq, reduce, sort, range, concat,
3
-  filter, reverse, prop, flatten, uniq, reject, contains, omit, times
4
-} from 'rambda';
1
+import { map, reduce, times } from 'rambda';
5 2
 import type { JobId } from '../../types';
6
-import { groupBy, max, remove, slice } from 'ramda';
3
+import { groupBy, max } from 'ramda';
7 4
 
8 5
 type JobTime = {
9 6
   jobId: JobId,
... ...
@@ -46,8 +43,6 @@ export const createSchedule = (jobs: JobTime[], processorCount: number): JobTime
46 46
   let processorC = times(() => 0, processorCount + 1);
47 47
   let activeProcessor = 1;
48 48
 
49
-  let i = 0;
50
-
51 49
   while (G.length) {
52 50
     if (processorC[activeProcessor] + G[0].t <= K) {
53 51
       processorC[activeProcessor] += G[0].t;
... ...
@@ -65,8 +60,6 @@ export const createSchedule = (jobs: JobTime[], processorCount: number): JobTime
65 65
 
66 66
       activeProcessor++;
67 67
     }
68
-
69
-    i++;
70 68
   }
71 69
 
72 70
   const withC = updateCTime(R);
... ...
@@ -1,21 +1,13 @@
1
-import { addIndex, findIndex, find, map, propEq, reduce, sort, range, concat } from 'rambda';
1
+import { findIndex, find, map, propEq, reduce, sort, concat } from 'rambda';
2 2
 import type { JobId } from '../../types';
3
-import { remove, slice } from 'ramda';
3
+import { max, remove, slice } from 'ramda';
4 4
 
5 5
 type JobTime = {
6 6
   jobId: JobId,
7 7
   t: number,
8 8
   d: number,
9
-  c: number
10 9
 };
11 10
 
12
-const mapWithIndex = addIndex(map);
13
-
14
-// const byAsc = (a: JobTime, b: JobTime) => a.time - b.time;
15
-
16
-const findJobById = (jobId: JobId, jobTimes: JobTime[]): JobTime =>
17
-  find(propEq('jobId', jobId), jobTimes);
18
-
19 11
 const findLateJob = (jobTimes: JobTime[]) =>
20 12
   find(({ c, d }: JobTime) => c > d, jobTimes);
21 13
 
... ...
@@ -38,7 +30,13 @@ const updateCTime = (jobTimes: JobTime[]) => {
38 38
   return cj;
39 39
 };
40 40
 
41
+export const fillCorrupted = (jobs: JobTime[]) => map(
42
+  ({ d, ...rest }) => ({ ...rest, d: d || 1000 }),
43
+  jobs);
44
+
41 45
 export const createSchedule = (jobs: JobTime[]) => {
46
+  jobs = fillCorrupted(jobs);
47
+
42 48
   const F = sort((a: JobTime, b: JobTime) => a.d - b.d, jobs);
43 49
   const L = [];
44 50
 
... ...
@@ -70,6 +68,7 @@ export default (jobs: JobTime[]) => {
70 70
     processor: 1,
71 71
     startTime: jobTime.c - jobTime.t,
72 72
     endTime: jobTime.c,
73
+    delayed: max(0, jobTime.c - jobTime.d)
73 74
   }), schedule);
74 75
 
75 76
   return [normalizedSchedule];
... ...
@@ -44,13 +44,13 @@ test('create gantt', () => {
44 44
 
45 45
   expect(template.length).toBe(1);
46 46
   expect(template[0].length).toBe(9);
47
-  expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6 });
48
-  expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 12 });
49
-  expect(template[0][2]).toEqual({ processor: 1, startTime: 12, endTime: 16 });
50
-  expect(template[0][3]).toEqual({ processor: 1, startTime: 16, endTime: 21 });
51
-  expect(template[0][4]).toEqual({ processor: 1, startTime: 21, endTime: 24 });
52
-  expect(template[0][5]).toEqual({ processor: 1, startTime: 24, endTime: 28 });
53
-  expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 34 });
54
-  expect(template[0][7]).toEqual({ processor: 1, startTime: 34, endTime: 41 });
55
-  expect(template[0][8]).toEqual({ processor: 1, startTime: 41, endTime: 49 });
47
+  expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 6, delayed: 0 });
48
+  expect(template[0][1]).toEqual({ processor: 1, startTime: 6, endTime: 12, delayed: 0 });
49
+  expect(template[0][2]).toEqual({ processor: 1, startTime: 12, endTime: 16, delayed: 0 });
50
+  expect(template[0][3]).toEqual({ processor: 1, startTime: 16, endTime: 21, delayed: 0 });
51
+  expect(template[0][4]).toEqual({ processor: 1, startTime: 21, endTime: 24, delayed: 0 });
52
+  expect(template[0][5]).toEqual({ processor: 1, startTime: 24, endTime: 28, delayed: 0 });
53
+  expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 34, delayed: 0 });
54
+  expect(template[0][7]).toEqual({ processor: 1, startTime: 34, endTime: 41, delayed: 21 });
55
+  expect(template[0][8]).toEqual({ processor: 1, startTime: 41, endTime: 49, delayed: 24 });
56 56
 });
57 57
\ No newline at end of file
... ...
@@ -1,9 +1,9 @@
1 1
 import {
2
-  addIndex, findIndex, find, map, propEq, reduce, sort, range, concat,
3
-  filter, reverse, prop, flatten, uniq, reject, contains, omit, equals, update, times
2
+  map, reduce, sort, range, omit, times
4 3
 } from 'rambda';
5 4
 import type { JobId } from '../../types';
6 5
 import { updateCTime } from '../johnson';
6
+import { fillCorrupted } from '../campbel';
7 7
 
8 8
 type JobOperations = {
9 9
   jobId: JobId,
... ...
@@ -16,6 +16,8 @@ type JobOperationsWithC = {
16 16
 };
17 17
 
18 18
 export const createSchedule = (jobsOperations: JobOperations[], processorsCount: number): JobOperationsWithC[] => {
19
+  jobsOperations = fillCorrupted(jobsOperations);
20
+
19 21
   const jobsOperationsWithKoefs = map(jobsOperation => {
20 22
     const koef = reduce(
21 23
       (acc, i) => acc + Math.abs(processorsCount - 2 * i + 1) * jobsOperation.operations[i-1].t,
... ...
@@ -1,15 +1,14 @@
1 1
 import {
2
-  addIndex, findIndex, find, map, propEq, reduce, sort, range, concat,
3
-  filter, reverse
2
+  findIndex, map, propEq, reduce, filter, reverse
4 3
 } from 'rambda';
5 4
 import type { JobId } from '../../types';
6
-import { remove, slice } from 'ramda';
5
+import { max, remove } from 'ramda';
6
+import { fillCorrupted } from '../moore';
7 7
 
8 8
 type JobTime = {
9 9
   jobId: JobId,
10 10
   t: number,
11 11
   d: number,
12
-  c: number
13 12
 };
14 13
 
15 14
 const findMaxTJob = (jobTimes: JobTime[]) =>
... ...
@@ -32,6 +31,8 @@ const updateCTime = (jobTimes: JobTime[]) => {
32 32
 };
33 33
 
34 34
 export const createSchedule = (jobs: JobTime[]) => {
35
+  jobs = fillCorrupted(jobs);
36
+
35 37
   let fi = reduce((sum, job: JobTime) => sum + job.t, 0, jobs);
36 38
   let jobsD = jobs;
37 39
   let R = [];
... ...
@@ -58,10 +59,13 @@ export const createSchedule = (jobs: JobTime[]) => {
58 58
 export default (jobs: JobTime[]) => {
59 59
   const schedule = createSchedule(jobs);
60 60
 
61
+  if (!schedule) return null;
62
+
61 63
   const normalizedSchedule = map((jobTime: JobTime) => ({
62 64
     processor: 1,
63 65
     startTime: jobTime.c - jobTime.t,
64 66
     endTime: jobTime.c,
67
+    delayed: max(0, jobTime.c - jobTime.d)
65 68
   }), schedule);
66 69
 
67 70
   return [normalizedSchedule];
... ...
@@ -50,15 +50,15 @@ test('create gantt', () => {
50 50
 
51 51
   expect(template.length).toBe(1);
52 52
   expect(template[0].length).toBe(11);
53
-  expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 1 });
54
-  expect(template[0][1]).toEqual({ processor: 1, startTime: 1, endTime: 5 });
55
-  expect(template[0][2]).toEqual({ processor: 1, startTime: 5, endTime: 9 });
56
-  expect(template[0][3]).toEqual({ processor: 1, startTime: 9, endTime: 15 });
57
-  expect(template[0][4]).toEqual({ processor: 1, startTime: 15, endTime: 21 });
58
-  expect(template[0][5]).toEqual({ processor: 1, startTime: 21, endTime: 28 });
59
-  expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 30 });
60
-  expect(template[0][7]).toEqual({ processor: 1, startTime: 30, endTime: 33 });
61
-  expect(template[0][8]).toEqual({ processor: 1, startTime: 33, endTime: 40 });
62
-  expect(template[0][9]).toEqual({ processor: 1, startTime: 40, endTime: 46 });
63
-  expect(template[0][10]).toEqual({ processor: 1, startTime: 46, endTime: 50 });
53
+  expect(template[0][0]).toEqual({ processor: 1, startTime: 0, endTime: 1, delayed: 0 });
54
+  expect(template[0][1]).toEqual({ processor: 1, startTime: 1, endTime: 5, delayed: 0 });
55
+  expect(template[0][2]).toEqual({ processor: 1, startTime: 5, endTime: 9, delayed: 0 });
56
+  expect(template[0][3]).toEqual({ processor: 1, startTime: 9, endTime: 15, delayed: 0 });
57
+  expect(template[0][4]).toEqual({ processor: 1, startTime: 15, endTime: 21, delayed: 0 });
58
+  expect(template[0][5]).toEqual({ processor: 1, startTime: 21, endTime: 28, delayed: 0 });
59
+  expect(template[0][6]).toEqual({ processor: 1, startTime: 28, endTime: 30, delayed: 0 });
60
+  expect(template[0][7]).toEqual({ processor: 1, startTime: 30, endTime: 33, delayed: 0 });
61
+  expect(template[0][8]).toEqual({ processor: 1, startTime: 33, endTime: 40, delayed: 0 });
62
+  expect(template[0][9]).toEqual({ processor: 1, startTime: 40, endTime: 46, delayed: 0 });
63
+  expect(template[0][10]).toEqual({ processor: 1, startTime: 46, endTime: 50, delayed: 0 });
64 64
 });
65 65
\ No newline at end of file
... ...
@@ -1,30 +1,17 @@
1 1
 import {
2
-  addIndex,
3
-  findIndex,
4 2
   find,
5 3
   map,
6
-  propEq,
7 4
   reduce,
8 5
   sort,
9
-  range,
10
-  concat,
11 6
   filter,
12
-  reverse,
13
-  prop,
14 7
   flatten,
15
-  uniq,
16 8
   reject,
17 9
   contains,
18 10
   omit,
19
-  equals,
20
-  update,
21
-  times,
22
-  add,
23
-  pluck,
24
-  last, forEach, all
11
+  forEach, all
25 12
 } from 'rambda';
26 13
 import type { JobId } from '../../types';
27
-import { groupBy, max, min, remove, slice } from 'ramda';
14
+import { groupBy } from 'ramda';
28 15
 import { updateCTime } from '../mcnaught';
29 16
 
30 17
 type JobTime = {
... ...
@@ -41,7 +28,13 @@ const noSuccJobs = (jobTimes: JobTime[]) => {
41 41
 const successors = (id: JobId, jobTimes: JobTime[]) =>
42 42
   filter(({ anc }) => contains(id, anc), jobTimes);
43 43
 
44
+const fillCorrupted = (jobs: JobTime[]) => map(
45
+  ({ anc, ...rest }) => ({ ...rest, anc: anc || [] }),
46
+  jobs);
47
+
44 48
 export const createSchedule = (jobTimes: JobTime[], m: number) => {
49
+  jobTimes = fillCorrupted(jobTimes);
50
+
45 51
   let T = reduce((acc, jobTime) => acc + jobTime.t, 0, jobTimes);
46 52
 
47 53
   let noSucc = noSuccJobs(jobTimes);
... ...
@@ -124,6 +117,8 @@ export const createSchedule = (jobTimes: JobTime[], m: number) => {
124 124
 export default (jobTimes: JobTime[], processorsCount: number) => {
125 125
   const schedule = createSchedule(jobTimes, processorsCount);
126 126
 
127
+  if (!schedule) return null;
128
+
127 129
   const grouped = Object.values(groupBy(jobTime => jobTime.processor, schedule));
128 130
 
129 131
   return map(map(job => ({
... ...
@@ -187,6 +187,7 @@ class Box extends React.PureComponent {
187 187
       as = 'div',
188 188
       className,
189 189
       style,
190
+      nativeRef,
190 191
       ...props
191 192
     } = this.props;
192 193
 
... ...
@@ -204,7 +205,7 @@ class Box extends React.PureComponent {
204 204
       className
205 205
     };
206 206
 
207
-    return React.createElement(as, { ...restProps, ...styles });
207
+    return React.createElement(as, { ...restProps, ...styles, ref: nativeRef });
208 208
   }
209 209
 }
210 210
 
... ...
@@ -12,116 +12,130 @@ import PropTypes from 'proptypes';
12 12
 import Select from 'react-select';
13 13
 import 'react-select/dist/react-select.css';
14 14
 import {
15
-  filter, find, findIndex, flatten, forEach, identity, map, prop, propEq, reject, uniq, update
15
+  filter, find, flatten, identity, map, prop, propEq, reject, uniq, update
16 16
 } from 'rambda';
17 17
 import { insert, remove } from 'ramda';
18 18
 import theme from '../theme';
19 19
 
20
-const allJobs = {
21
-  whatever: 'Vševediaci',
22
-
23
-  marketResearch: 'Prieskum trhu',
24
-
25
-  designUi: 'Dizajnér – UI',
26
-  designUx: 'Dizajnér – UX',
27
-
28
-  architect: 'Softwarový architekt',
29
-
30
-  devFrontend: 'Developer – frontend',
31
-  devBackend: 'Developer – backend',
32
-  devDb: 'Developer – databázy',
33
-
34
-  admin: 'Serverový administrátor',
35
-
36
-  tester: 'Tester',
37
-
38
-  analyst: 'Analytik',
39
-
40
-  qa: 'Quality assurance',
41
-
42
-  deployManager: 'Deploy'
43
-};
44
-
45 20
 const nextWorkerName = (rows) =>
46 21
   `${rows.length + 1}. pracovník`;
47 22
 
48 23
 class Form extends React.PureComponent {
49 24
   state = {
25
+    allJobs: [],
50 26
     rows: [
51 27
       // {
52 28
       //   id: uuid(),
53 29
       //   name: 'pracovnik 1',
54
-      //   preempt: false,
55
-      //   linkedTo: null,
56 30
       //   jobs: [
57
-      //     { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
58
-      //     { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
31
+      //     { id: uuid(), name: 'frontend', t: '', d: '', w: '', anc: [] },
32
+      //     { id: uuid(), name: 'backend', t: '', d: '', w: '', anc: [] }
59 33
       //   ]
60 34
       // },
61 35
       // {
62 36
       //   id: uuid(),
63 37
       //   name: 'pracovnik 2',
64
-      //   preempt: false,
65
-      //   linkedTo: null,
66 38
       //   jobs: [
67
-      //     { id: uuid(), name: 'frontend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 },
68
-      //     { id: uuid(), name: 'backend', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0 }
39
+      //     { id: uuid(), name: 'frontend', t: '', d: '', w: '', anc: [] },
40
+      //     { id: uuid(), name: 'backend', t: '', d: '', w: '', anc: [] }
69 41
       //   ]
70 42
       // }
71
-    ]
43
+    ],
44
+    preempt: false,
72 45
   };
73 46
 
74 47
   onSubmit = (values) => {
75
-    console.log(values);
48
+    if (!values || !values.rows.length) return;
49
+
50
+    const { onSubmit } = this.props;
51
+    onSubmit(values);
76 52
   };
77 53
 
78
-  addWorker = (setFieldValue, rows) => {
54
+  addWorker = (setFieldValue, rows, allJobs) => {
79 55
     const jobs = [{
80
-      id: uuid(), name: 'whatever', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0
56
+      id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: []
81 57
     }];
82 58
 
83 59
     const updatedRows = [...rows, { id: uuid(), name: nextWorkerName(rows), jobs } ];
84 60
     setFieldValue('rows', updatedRows);
85 61
   };
86 62
 
63
+  addTemplateWorker = (setFieldValue, allJobs) => (e) => {
64
+    if (e.keyCode !== undefined && e.keyCode !== 13) return;
65
+
66
+    e.preventDefault();
67
+
68
+    const val = this.new.value.replace(/:/g, '');
69
+    const updatedJobs = [...allJobs, val ];
70
+    setFieldValue('allJobs', updatedJobs);
71
+
72
+    this.new.value = null;
73
+  };
74
+
87 75
   render() {
88 76
     return (
89
-      <Formik initialValues={this.state} onSubmit={this.onSubmit} render={({ setFieldValue, values: { rows } }) => (
77
+      <Formik initialValues={this.state} onSubmit={this.onSubmit} render={({ setFieldValue, values: { rows, allJobs } }) => (
90 78
         <FormikForm>
91
-          <h1>Konfigurácia projektu</h1>
92
-
93
-          {rows.length ?
94
-          <Box flexDirection="row" marginBottom={10}>
95
-            <Box flexBasis="15%" flexShrink={0}><strong>Pracovník</strong></Box>
96
-            <Box flexBasis="63%" flexShrink={0}><strong>Úlohy</strong></Box>
97
-            <Box flexBasis="12%" flexShrink={0} align="center"><strong>Prerušenie</strong></Box>
98
-          </Box> : null
99
-          }
100
-
101
-          <FieldArray name="rows" render={arrayHelpers => (
102
-            <React.Fragment>
103
-              {rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers} prefix={`rows.${i}`} index={i} />)}
104
-            </React.Fragment>
105
-          )} />
106
-
107
-          <Box flexDirection="row" marginBottom={10}>
108
-            <Box
109
-              flexShrink={0}
110
-              onClick={() => this.addWorker(setFieldValue, rows)}
111
-              {...theme.buttons.primary}
112
-            >
113
-              + Nový pracovník
114
-            </Box>
79
+          <h1>1/3 Vytvorenie pracovníkov</h1>
80
+          <Box flexWrap="wrap" flexDirection="row">
81
+            {allJobs.map((_, key) => (
82
+              <Box key={key} width={300} flexDirection="row" marginBottom={10}>
83
+                <Box as={Field} name={`allJobs.${key}`} flex={1} marginRight={5} onChange={({ target: { value } }) => setFieldValue(`allJobs.${key}`, value)} />
84
+                <Box cursor="pointer" onClick={() => setFieldValue(`allJobs`, remove(key, 1, allJobs))} color="red" marginRight={20}>✕</Box>
85
+              </Box>
86
+            ))}
115 87
           </Box>
116 88
 
117
-          <Box as="button" margin="auto" width="20rem" alignItems="center" padding={5}
118
-               {...theme.buttons.primary} backgroundColor="#C89C93">
119
-            <Box fontSize="1rem">Cieľ:</Box>
120
-            <Box as="select" marginTop={5}>
121
-              <option>Minimalizovať počet oneskorených úloh</option>
122
-              <option>2...</option>
123
-            </Box>
89
+          <Box width={300} flexDirection="row" marginBottom={10}>
90
+            <Box as="input" placeholder="Nový pracovník" onKeyDown={this.addTemplateWorker(setFieldValue, allJobs)} nativeRef={c => this.new = c} marginRight={5} flex={1} />
91
+            <Box cursor="pointer" onClick={this.addTemplateWorker(setFieldValue, allJobs)} color="green" fontSize="1rem">+</Box>
124 92
           </Box>
93
+
94
+          {allJobs.length ? (
95
+            <div>
96
+              <h1>2/3 Konfigurácia projektu</h1>
97
+
98
+              {rows.length ?
99
+                <Box flexDirection="row" marginBottom={10}>
100
+                  <Box flexBasis="20%" flexShrink={0}><strong>Pracovníci</strong></Box>
101
+                  <Box flexBasis="63%" flexShrink={0}><strong>Úlohy</strong></Box>
102
+                </Box> : null
103
+              }
104
+
105
+              <FieldArray name="rows" render={arrayHelpers => (
106
+                <React.Fragment>
107
+                  {rows.map((row, i) => <Row key={i} {...row} arrayHelpers={arrayHelpers}
108
+                                             prefix={`rows.${i}`} index={i}/>)}
109
+                </React.Fragment>
110
+              )}/>
111
+
112
+              <Box flexDirection="row" marginBottom={10}>
113
+                <Box
114
+                  flexShrink={0}
115
+                  onClick={() => this.addWorker(setFieldValue, rows, allJobs)}
116
+                  {...theme.buttons.primary}
117
+                >
118
+                  + Nový pracovník
119
+                </Box>
120
+              </Box>
121
+
122
+              {rows.length ?
123
+                <div>
124
+                  <Box as="label" justifyContent="center" flexDirection="row">
125
+                    <Field type="checkbox" name="preempt"/>
126
+                    <Box as="em" color="gray" textAlign="center" fontSize="0.9em">
127
+                      Úlohy pracovníkov možno prerušiť
128
+                    </Box>
129
+                  </Box>
130
+
131
+                  <Box as="button" margin="auto" marginTop={10} width="20rem" alignItems="center"
132
+                       padding={5} {...theme.buttons.primary} backgroundColor="#C89C93" fontSize="1rem">
133
+                    Vytvoriť rozvrh
134
+                  </Box>
135
+                </div>
136
+                : null}
137
+            </div>
138
+          ) : null}
125 139
         </FormikForm>
126 140
       )}>
127 141
       </Formik>
... ...
@@ -159,8 +173,8 @@ const SortableJob = SortableElement(
159 159
     };
160 160
 
161 161
     render() {
162
-      const { myIndex, rowIndex, prefix, job, existingJobs: _existingJobs } = this.props;
163
-      const { formik: { values: { rows } } } = this.context;
162
+      const { prefix, job, existingJobs: _existingJobs } = this.props;
163
+      const { formik: { values: { rows, allJobs } } } = this.context;
164 164
 
165 165
       const existingJobs = map(
166 166
         value => {
... ...
@@ -168,7 +182,7 @@ const SortableJob = SortableElement(
168 168
           const row = find(propEq('id', idRow), rows);
169 169
           const jobName = find(propEq('id', idJob), row.jobs).name;
170 170
 
171
-          return ({ value, label: `${row.name} – ${allJobs[jobName]}` });
171
+          return ({ value, label: `${row.name} – ${jobName}` });
172 172
         },
173 173
         reject(
174 174
           value => {
... ...
@@ -187,55 +201,50 @@ const SortableJob = SortableElement(
187 187
           className="sortable-inactive"
188 188
         >
189 189
           <Box marginRight={10} fontSize="1.5rem" width={20}>
190
-            {!rows[rowIndex].linkedTo &&
191 190
             <DragHandle/>
192
-            }
193 191
           </Box>
194 192
 
195 193
           <Box flex={1} padding={1}>
196 194
             <Box width={200} marginBottom={10} flexDirection="row">
197
-              <Field component="select" name={`${prefix}.name`} disabled={rows[rowIndex].linkedTo}>
198
-               {Object.keys(allJobs).map(key => (
199
-                  <option value={key} key={key}>{allJobs[key]}</option>
195
+              <Field component="select" name={`${prefix}.name`}>
196
+               {allJobs.map((job, key) => (
197
+                  <option value={job} key={key}>{job}</option>
200 198
                 ))}
201 199
               </Field>
202
-              {!rows[rowIndex].linkedTo &&
203 200
               <Box cursor="pointer" onClick={this.deleteJob} marginLeft={5} color="red">✕</Box>
204
-              }
205 201
             </Box>
206 202
 
207 203
             <Box flexDirection="row" justifyContent="space-between" marginBottom={10}>
208 204
               <Box flexBasis="30%">
209 205
                 <label>
210 206
                   Trvanie: <br />
211
-                  <Field type="number" step={1} name={`${prefix}.t`} style={{ width: '100%' }} />
207
+                  <Field type="number" min={0} step={1} name={`${prefix}.t`} style={{ width: '100%' }} required />
212 208
                 </label>
213 209
               </Box>
214 210
               <Box flexBasis="30%">
215 211
                 <label>
216 212
                   Deadline: <br />
217
-                  <Field type="number" step={1} name={`${prefix}.d`} style={{ width: '100%' }} />
213
+                  <Field type="number" min={0} step={1} name={`${prefix}.d`} style={{ width: '100%' }} disabled={rows.length > 1} />
218 214
                 </label>
219 215
               </Box>
220 216
               <Box flexBasis="30%">
221 217
                 <label>
222
-                  Váha: <br />
223
-                  <Field type="number" min={0} max={1} step={0.1} name={`${prefix}.w`} style={{ width: '100%' }} />
218
+                  Váha: (1-10) <br />
219
+                  <Field type="number" min={1} max={10} step={1} name={`${prefix}.w`} style={{ width: '100%' }} />
224 220
                 </label>
225 221
               </Box>
226 222
             </Box>
227 223
 
228 224
             <Box>
229
-              <Box as="label" flexDirection="row" marginBottom={5}>
230
-                <Field type="radio" name={`${prefix}.succOrAnc`} value={0} checked={job.succOrAnc == '0'} disabled={rows[rowIndex].linkedTo}/> Bez nadväznosti
231
-              </Box>
225
+              {/*<Box as="label" flexDirection="row" marginBottom={5}>*/}
226
+                {/*<Field type="radio" name={`${prefix}.succOrAnc`} value={0} checked={job.succOrAnc == '0'} disabled={rows[rowIndex].linkedTo}/> Bez nadväznosti*/}
227
+              {/*</Box>*/}
232 228
               <Box marginBottom={5}>
233 229
                 <Box as="label" flexDirection="row">
234
-                  <Field type="radio" name={`${prefix}.succOrAnc`} value={1} checked={job.succOrAnc == '1'} disabled={rows[rowIndex].linkedTo} /> Predchodcovia
230
+                  Predchodcovia
235 231
                 </Box>
236
-                <Box flex={1} marginLeft={10} marginTop={5}>
232
+                <Box flex={1} marginTop={5}>
237 233
                   <Select
238
-                    disabled={job.succOrAnc != '1' || rows[rowIndex].linkedTo}
239 234
                     multi
240 235
                     onChange={e => this.handleSelectChange('anc', e)}
241 236
                     options={existingJobs}
... ...
@@ -244,21 +253,6 @@ const SortableJob = SortableElement(
244 244
                   />
245 245
                 </Box>
246 246
               </Box>
247
-              <Box>
248
-                <Box as="label" flexDirection="row">
249
-                  <Field type="radio" name={`${prefix}.succOrAnc`} value={2} checked={job.succOrAnc == '2'} disabled={rows[rowIndex].linkedTo} /> Nasledovníci
250
-                </Box>
251
-                <Box flex={1} marginLeft={10} marginTop={5}>
252
-                  <Select
253
-                    disabled={job.succOrAnc != '2' || rows[rowIndex].linkedTo}
254
-                    multi
255
-                    onChange={e => this.handleSelectChange('succ', e)}
256
-                    options={existingJobs}
257
-                    removeSelected
258
-                    value={job.succ}
259
-                  />
260
-                </Box>
261
-              </Box>
262 247
             </Box>
263 248
           </Box>
264 249
         </Box>
... ...
@@ -322,9 +316,9 @@ class Row extends React.PureComponent {
322 322
 
323 323
   addJob = () => {
324 324
     const { index } = this.props;
325
-    const { formik: { setFieldValue, values: { rows } } } = this.context;
325
+    const { formik: { setFieldValue, values: { rows, allJobs } } } = this.context;
326 326
     const newJob = {
327
-      id: uuid(), name: 'whatever', t: '', d: '', w: '', succ: [], anc: [], succOrAnc: 0
327
+      id: uuid(), name: allJobs[0], t: '', d: '', w: '', anc: []
328 328
     };
329 329
 
330 330
     const jobs = [...rows[index].jobs, newJob];
... ...
@@ -333,27 +327,20 @@ class Row extends React.PureComponent {
333 333
   };
334 334
 
335 335
   deleteRow = () => {
336
-    const { index, id } = this.props;
336
+    const { index } = this.props;
337 337
     const { formik: { setFieldValue, values: { rows } } } = this.context;
338 338
 
339 339
     let newRows = remove(index, 1, rows);
340 340
 
341
-    const alsoDelete = filter(propEq('linkedTo', id), rows);
342
-
343
-    forEach(row => {
344
-      const index = findIndex(propEq('id', row.id), newRows);
345
-      newRows = remove(index, 1, newRows);
346
-    }, alsoDelete);
347
-
348 341
     setFieldValue('rows', newRows);
349 342
   };
350 343
 
351 344
   duplicate = () => {
352
-    const { id, index, name, preempt, jobs } = this.props;
345
+    const { index, name, preempt, jobs } = this.props;
353 346
     const { formik: { setFieldValue, values: { rows } } } = this.context;
354 347
 
355 348
     const jobsCopy = map(job => ({ ...job }), jobs);
356
-    const newRow = { id: uuid(), name: `${name} – kópia`, preempt, linkedTo: id, jobs: jobsCopy };
349
+    const newRow = { id: uuid(), name: `${name} – kópia`, preempt, jobs: jobsCopy };
357 350
 
358 351
     const updatedRows = insert(index + 1, newRow, rows);
359 352
 
... ...
@@ -361,16 +348,16 @@ class Row extends React.PureComponent {
361 361
   };
362 362
 
363 363
   render() {
364
-    const { index, prefix, name, preempt, linkedTo, jobs } = this.props;
364
+    const { index, prefix, name, jobs } = this.props;
365 365
     const { draggingIndex } = this.state;
366 366
 
367 367
     const existingJobs = this.findExistingJobs();
368 368
 
369 369
     return (
370 370
       <Box flexDirection="row" marginBottom={30} padding={10} className="row">
371
-        <Box flexBasis="15%" flexShrink={0} paddingRight={10}>
371
+        <Box flexBasis="20%" flexShrink={0} paddingRight={10} flexDirection="row">
372 372
           <strong>{name}</strong>
373
-          <Box cursor="pointer" onClick={this.deleteRow} color="red">✕</Box>
373
+          <Box cursor="pointer" onClick={this.deleteRow} color="red" marginLeft={5}>✕</Box>
374 374
         </Box>
375 375
         <Box flexBasis="63%" flexShrink={0}>
376 376
           <FieldArray render={() => (
... ...
@@ -386,22 +373,11 @@ class Row extends React.PureComponent {
386 386
               draggingIndex={draggingIndex}
387 387
             />
388 388
           )} />
389
-          {!linkedTo &&
390 389
           <Box onClick={this.addJob} {...theme.buttons.secondary} alignSelf="flex-start">
391 390
             + Nová úloha
392 391
           </Box>
393
-          }
394
-        </Box>
395
-        <Box flexBasis="12%" flexShrink={0}>
396
-          <Box as="label" alignItems="center" paddingTop={10}>
397
-            <Field type="checkbox" name={`${prefix}.preempt`} disabled={linkedTo} />
398
-            <Box as="em" color="gray" textAlign="center" padding={10} fontSize="0.9em">
399
-              Úlohy pracovníka možno prerušiť
400
-            </Box>
401
-          </Box>
402 392
         </Box>
403
-        <Box alignItems="flex-end">
404
-          {!linkedTo &&
393
+        <Box alignItems="center" flex={1}>
405 394
           <Box
406 395
             onClick={this.duplicate}
407 396
             {...theme.buttons.tertiary}
... ...
@@ -413,7 +389,6 @@ class Row extends React.PureComponent {
413 413
           >
414 414
             <span style={{ fontSize: '1.8em', paddingRight: '0.2em' }}>⩇</span> Duplikovať
415 415
           </Box>
416
-          }
417 416
         </Box>
418 417
       </Box>
419 418
     )
... ...
@@ -61,7 +61,7 @@ class Gannt extends React.PureComponent {
61 61
 
62 62
     return (
63 63
       <div ref={measureRef}>
64
-        <h1>Ganttov diagram</h1>
64
+        <h1>3/3 Ganttov diagram</h1>
65 65
 
66 66
         <ScatterChart width={width} height={height} data={data[0]} margin={theme.gantt.margin}>
67 67
           <CartesianGrid
... ...
@@ -48,13 +48,17 @@ export const normalizeData = data =>
48 48
 
49 49
     forEach(startTime => {
50 50
       if (!dataObj[startTime]) {
51
-        dataObj[startTime] = { startTime };
51
+        dataObj[startTime] = { startTime, processor: last(processorData).processor };
52 52
       }
53 53
     }, rangeTime);
54 54
 
55 55
     const filledData = Object.values(dataObj);
56 56
 
57
-    filledData.push({ startTime: last(processorData).endTime });
57
+    filledData.push({
58
+      startTime: last(processorData).endTime,
59
+      endTime: last(processorData).endTime + step,
60
+      processor: last(processorData).processor
61
+    });
58 62
 
59 63
     return filledData;
60 64
   }, data);
61 65
\ No newline at end of file
62 66
new file mode 100644
... ...
@@ -0,0 +1,71 @@
0
+import { find, indexOf, last, map, prop, reduce, sort } from 'rambda';
1
+import { max } from 'ramda';
2
+
3
+export const selectAlgorithms = (rows, preempt) => {
4
+  const processorsCount = rows.length;
5
+
6
+  if (!processorsCount) return null;
7
+
8
+  if (processorsCount === 1) {
9
+    const existD = !!find(prop('d'), rows[0].jobs);
10
+    const existAnc = !!find(({ anc }) => Array.isArray(anc) && anc.length, rows[0].jobs);
11
+
12
+    if (existAnc) {
13
+      return ['lawler'];
14
+    }
15
+
16
+    if (existD) {
17
+      return ['moore', 'smith'];
18
+    }
19
+
20
+    return ['moore'];
21
+  }
22
+
23
+  const existAnc = find(
24
+    ({ jobs }) => !!find(({ anc }) => Array.isArray(anc) && anc.length, jobs),
25
+    rows);
26
+
27
+  if (existAnc) {
28
+    return ['vahy'];
29
+  }
30
+
31
+  let multi = ['campbel', 'grupt', 'palmer'];
32
+
33
+  if (processorsCount === 2) {
34
+    multi.push('johnson');
35
+  }
36
+
37
+  if (preempt) {
38
+    multi.push('mcnaught');
39
+  }
40
+
41
+  return multi;
42
+};
43
+
44
+export const najneomeskanejsiResult = (algoResults: []) => {
45
+  if (!algoResults.length) return -1;
46
+
47
+  const delaysSum = reduce((acc, processorJobs) => {
48
+    const delays = reduce((acc, { delayed }) => acc + delayed, 0, processorJobs);
49
+    return acc + delays;
50
+  }, 0);
51
+
52
+  const delaysTimes = map(delaysSum, algoResults);
53
+  const sortedDelaysTimes = sort((a, b) => a - b, delaysTimes);
54
+
55
+  return indexOf(sortedDelaysTimes[0], delaysTimes);
56
+};
57
+
58
+export const fastestResult = (algoResults: []) => {
59
+  if (!algoResults.length) return -1;
60
+
61
+  const slowestProcessorEndTime = reduce((endTime, processorJobs) => {
62
+    const lastEndTime = last(processorJobs).endTime;
63
+    return max(lastEndTime, endTime);
64
+  }, 0);
65
+
66
+  const endTimes = map(slowestProcessorEndTime, algoResults);
67
+  const sortedEndTimes = sort((a, b) => a - b, endTimes);
68
+
69
+  return indexOf(sortedEndTimes[0], endTimes);
70
+};
0 71
\ No newline at end of file
1 72
new file mode 100644
... ...
@@ -0,0 +1,52 @@
0
+import { fastestResult, najneomeskanejsiResult } from './selectAlgorithm';
1
+
2
+test('find fastest result', () => {
3
+  const fastest = fastestResult([
4
+    [
5
+      [
6
+        { processor: 1, endTime: 10 },
7
+        { processor: 1, endTime: 11 }
8
+      ],
9
+      [
10
+        { processor: 2, endTime: 7 },
11
+        { processor: 2, endTime: 8 }
12
+      ]
13
+    ],
14
+
15
+    [
16
+      [
17
+        { processor: 1, endTime: 20 },
18
+      ],
19
+      [
20
+        { processor: 2, endTime: 19 }
21
+      ]
22
+    ],
23
+  ]);
24
+
25
+  expect(fastest).toBe(0);
26
+});
27
+
28
+test('find najneomeskanejsi result', () => {
29
+  const best = najneomeskanejsiResult([
30
+    [
31
+      [
32
+        { processor: 1, endTime: 10, delayed: 10 },
33
+        { processor: 1, endTime: 11, delayed: 20 }
34
+      ],
35
+    ],
36
+    [
37
+      [
38
+        { processor: 1, endTime: 10, delayed: 0 },
39
+        { processor: 1, endTime: 11, delayed: 5 }
40
+      ],
41
+    ],
42
+    [
43
+      [
44
+        { processor: 1, endTime: 10, delayed: 10 },
45
+        { processor: 1, endTime: 11, delayed: 10 }
46
+      ],
47
+    ]
48
+  ]);
49
+
50
+  expect(best).toBe(1);
51
+});
0 52
\ No newline at end of file