1 
2 module osqp;
3 
4 nothrow @nogc extern(C):
5 
6 /* Includes */
7 import glob_opts;
8 version(EMBEDDED) {
9   import osqp_configure; // for c_free and others  
10 }
11 import types;
12 import constants;
13 import util; // Needed for osqp_set_default_settings functions
14 
15 // Library to deal with sparse matrices enabled only if embedded not defined
16 version(EMBEDDED) {}
17 else {
18 import cs;
19 } // ifndef EMBEDDED
20 
21 import auxil;
22 import lin_alg;
23 import scaling;
24 import error;
25 
26 version(EMBEDDED){}
27 else {
28 import polish;
29 }/* ifndef EMBEDDED */
30 
31 version(CTRLC){
32 import ctrlc;
33 } /* ifdef CTRLC */
34 
35 //version(EMBEDDED){}
36 //else {
37 import lin_sys;
38 //} /* ifndef EMBEDDED */
39 
40 /**********************
41 * Main API Functions *
42 **********************/
43 void osqp_set_default_settings(OSQPSettings *settings) {
44 
45   settings.rho           = cast(c_float)RHO;            /* ADMM step */
46   settings.sigma         = cast(c_float)SIGMA;          /* ADMM step */
47   settings.scaling = SCALING;                       /* heuristic problem scaling */
48 version(EMBEDDED_1){}
49 else {
50   settings.adaptive_rho           = ADAPTIVE_RHO;
51   settings.adaptive_rho_interval  = ADAPTIVE_RHO_INTERVAL;
52   settings.adaptive_rho_tolerance = cast(c_float)ADAPTIVE_RHO_TOLERANCE;
53 
54 version(PROFILING){
55   settings.adaptive_rho_fraction = cast(c_float)ADAPTIVE_RHO_FRACTION;
56 } /* ifdef PROFILING */
57   }  /* if EMBEDDED != 1 */
58 
59   settings.max_iter      = MAX_ITER;                /* maximum iterations to
60                                                         take */
61   settings.eps_abs       = cast(c_float)EPS_ABS;        /* absolute convergence
62                                                         tolerance */
63   settings.eps_rel       = cast(c_float)EPS_REL;        /* relative convergence
64                                                         tolerance */
65   settings.eps_prim_inf  = cast(c_float)EPS_PRIM_INF;   /* primal infeasibility
66                                                         tolerance */
67   settings.eps_dual_inf  = cast(c_float)EPS_DUAL_INF;   /* dual infeasibility
68                                                         tolerance */
69   settings.alpha         = cast(c_float)ALPHA;          /* relaxation parameter */
70   settings.linsys_solver = cast(linsys_solver_type)LINSYS_SOLVER;           /* relaxation parameter */
71 
72 version(EMBEDDED){}
73 else {
74   settings.delta              = DELTA;              /* regularization parameter
75                                                         for polish */
76   settings.polish             = POLISH;             /* ADMM solution polish: 1
77                                                       */
78   settings.polish_refine_iter = POLISH_REFINE_ITER; /* iterative refinement
79                                                         steps in polish */
80   settings.verbose            = VERBOSE;            /* print output */
81 } /* ifndef EMBEDDED */
82 
83   settings.scaled_termination = SCALED_TERMINATION; /* Evaluate scaled
84                                                         termination criteria*/
85   settings.check_termination  = CHECK_TERMINATION;  /* Interval for evaluating
86                                                         termination criteria */
87   settings.warm_start         = WARM_START;         /* warm starting */
88 
89 version(PROFILING){
90   settings.time_limit = TIME_LIMIT;
91 } /* ifdef PROFILING */
92 }
93 
94 version(EMBEDDED){}
95 else {
96 
97 c_int osqp_setup(OSQPWorkspace** workp, const OSQPData *data, const OSQPSettings *settings) {
98   c_int exitflag;
99 
100   OSQPWorkspace * work;
101 
102   // Validate data
103   if (validate_data(data)) return osqp_error(cast(osqp_error_type)OSQP_DATA_VALIDATION_ERROR, __FUNCTION__);
104 
105   // Validate settings
106   if (validate_settings(settings)) return osqp_error(cast(osqp_error_type)OSQP_SETTINGS_VALIDATION_ERROR, __FUNCTION__);
107 
108   // Allocate empty workspace
109   work = cast(OSQPWorkspace*)c_calloc(1, (OSQPWorkspace.sizeof));
110   if (!(work)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
111   *workp = work;
112 
113   // Start and allocate directly timer
114 version(PROFILING){
115   work.timer = cast(OSQPTimer*)c_malloc((OSQPTimer.sizeof));
116   if (!(work.timer)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
117   osqp_tic(work.timer);
118 } /* ifdef PROFILING */
119 
120   // Copy problem data into workspace
121   work.data = cast(OSQPData*)c_malloc((OSQPData.sizeof));
122   if (!(work.data)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
123   work.data.n = data.n;
124   work.data.m = data.m;
125 
126   // Cost function
127   work.data.P = copy_csc_mat(data.P);
128   work.data.q = vec_copy(cast(c_float*)data.q, cast(c_int)data.n);
129   if (!(work.data.P) || !(work.data.q)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
130 
131   // Constraints
132   work.data.A = copy_csc_mat(data.A);
133   if (!(work.data.A)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
134   work.data.l = vec_copy(cast(c_float*)data.l, cast(c_int)data.m);
135   work.data.u = vec_copy(cast(c_float*)data.u, cast(c_int)data.m);
136   if ( data.m && (!(work.data.l) || !(work.data.u)) )
137     return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
138 
139   // Vectorized rho parameter
140   work.rho_vec     = cast(c_float*)c_malloc(data.m * (c_float.sizeof));
141   work.rho_inv_vec = cast(c_float*)c_malloc(data.m * (c_float.sizeof));
142   if ( data.m && (!(work.rho_vec) || !(work.rho_inv_vec)) )
143     return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
144 
145   // Type of constraints
146   work.constr_type = cast(c_int*)c_calloc(data.m, (c_int.sizeof));
147   if (data.m && !(work.constr_type)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
148 
149   // Allocate internal solver variables (ADMM steps)
150   work.x        = cast(c_float*)c_calloc(data.n, (c_float.sizeof));
151   work.z        = cast(c_float*)c_calloc(data.m, (c_float.sizeof));
152   work.xz_tilde = cast(c_float*)c_calloc(data.n + data.m, (c_float.sizeof));
153   work.x_prev   = cast(c_float*)c_calloc(data.n, (c_float.sizeof));
154   work.z_prev   = cast(c_float*)c_calloc(data.m, (c_float.sizeof));
155   work.y        = cast(c_float*)c_calloc(data.m, (c_float.sizeof));
156   if (!(work.x) || !(work.xz_tilde) || !(work.x_prev))
157     return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
158   if ( data.m && (!(work.z) || !(work.z_prev) || !(work.y)) )
159     return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
160 
161   // Initialize variables x, y, z to 0
162   cold_start(work);
163 
164   // Primal and dual residuals variables
165   work.Ax  = cast(c_float*)c_calloc(data.m, (c_float.sizeof));
166   work.Px  = cast(c_float*)c_calloc(data.n, (c_float.sizeof));
167   work.Aty = cast(c_float*)c_calloc(data.n, (c_float.sizeof));
168 
169   // Primal infeasibility variables
170   work.delta_y   = cast(c_float*)c_calloc(data.m, (c_float.sizeof));
171   work.Atdelta_y = cast(c_float*)c_calloc(data.n, (c_float.sizeof));
172 
173   // Dual infeasibility variables
174   work.delta_x  = cast(c_float*)c_calloc(data.n, (c_float.sizeof));
175   work.Pdelta_x = cast(c_float*)c_calloc(data.n, (c_float.sizeof));
176   work.Adelta_x = cast(c_float*)c_calloc(data.m, (c_float.sizeof));
177 
178   if (!(work.Px) || !(work.Aty) || !(work.Atdelta_y) ||
179       !(work.delta_x) || !(work.Pdelta_x))
180     return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
181   if ( data.m && (!(work.Ax) || !(work.delta_y) || !(work.Adelta_x)) )
182     return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
183 
184   // Copy settings
185   work.settings = copy_settings(settings);
186   if (!(work.settings)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
187 
188   // Perform scaling
189   if (settings.scaling) {
190     // Allocate scaling structure
191     work.scaling = cast(OSQPScaling*)c_malloc((OSQPScaling.sizeof));
192     if (!(work.scaling)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
193     work.scaling.D    = cast(c_float*)c_malloc(data.n * (c_float.sizeof));
194     work.scaling.Dinv = cast(c_float*)c_malloc(data.n * (c_float.sizeof));
195     work.scaling.E    = cast(c_float*)c_malloc(data.m * (c_float.sizeof));
196     work.scaling.Einv = cast(c_float*)c_malloc(data.m * (c_float.sizeof));
197     if (!(work.scaling.D) || !(work.scaling.Dinv))
198       return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
199     if ( data.m && (!(work.scaling.E) || !(work.scaling.Einv)) )
200       return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
201 
202 
203     // Allocate workspace variables used in scaling
204     work.D_temp   = cast(c_float*)c_malloc(data.n * (c_float.sizeof));
205     work.D_temp_A = cast(c_float*)c_malloc(data.n * (c_float.sizeof));
206     work.E_temp   = cast(c_float*)c_malloc(data.m * (c_float.sizeof));
207     // if (!(work.D_temp) || !(work.D_temp_A) || !(work.E_temp))
208     //   return osqp_error(OSQP_MEM_ALLOC_ERROR);
209     if (!(work.D_temp) || !(work.D_temp_A)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
210     if (data.m && !(work.E_temp))           return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
211 
212     // Scale data
213     scale_data(work);
214   } else {
215     work.scaling  = OSQP_NULL;
216     work.D_temp   = OSQP_NULL;
217     work.D_temp_A = OSQP_NULL;
218     work.E_temp   = OSQP_NULL;
219   }
220 
221   // Set type of constraints
222   set_rho_vec(work);
223 
224   // Load linear system solver
225   if (load_linsys_solver(work.settings.linsys_solver)) return osqp_error(cast(osqp_error_type)OSQP_LINSYS_SOLVER_LOAD_ERROR, __FUNCTION__);
226 
227   // Initialize linear system solver structure
228   exitflag = init_linsys_solver(&(work.linsys_solver), work.data.P, work.data.A,
229                                 work.settings.sigma, work.rho_vec,
230                                 work.settings.linsys_solver, 0);
231 
232   if (exitflag) {
233     return osqp_error(cast(osqp_error_type)exitflag, __FUNCTION__);
234   }
235 
236   // Initialize active constraints structure
237   work.pol = cast(OSQPPolish*)c_malloc(OSQPPolish.sizeof);
238   if (!(work.pol)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
239   work.pol.Alow_to_A = cast(c_int*)c_malloc(data.m * (c_int.sizeof));
240   work.pol.Aupp_to_A = cast(c_int*)c_malloc(data.m * (c_int.sizeof));
241   work.pol.A_to_Alow = cast(c_int*)c_malloc(data.m * (c_int.sizeof));
242   work.pol.A_to_Aupp = cast(c_int*)c_malloc(data.m * (c_int.sizeof));
243   work.pol.x         = cast(c_float*)c_malloc(data.n * (c_float.sizeof));
244   work.pol.z         = cast(c_float*)c_malloc(data.m * (c_float.sizeof));
245   work.pol.y         = cast(c_float*)c_malloc(data.m * (c_float.sizeof));
246   if (!(work.pol.x)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
247   if ( data.m && (!(work.pol.Alow_to_A) || !(work.pol.Aupp_to_A) ||
248       !(work.pol.A_to_Alow) || !(work.pol.A_to_Aupp) ||
249       !(work.pol.z) || !(work.pol.y)) )
250     return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
251 
252   // Allocate solution
253   work.solution = cast(OSQPSolution*)c_calloc(1, (OSQPSolution.sizeof));
254   if (!(work.solution)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
255   work.solution.x = cast(c_float*)c_calloc(1, data.n * (c_float.sizeof));
256   work.solution.y = cast(c_float*)c_calloc(1, data.m * (c_float.sizeof));
257   if (!(work.solution.x))            return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
258   if (data.m && !(work.solution.y)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
259 
260   // Allocate and initialize information
261   work.info = cast(OSQPInfo*)c_calloc(1, (OSQPInfo.sizeof));
262   if (!(work.info)) return osqp_error(cast(osqp_error_type)OSQP_MEM_ALLOC_ERROR, __FUNCTION__);
263   work.info.status_polish = 0;              // Polishing not performed
264   update_status(work.info, OSQP_UNSOLVED);
265 version(PROFILING){
266   work.info.solve_time  = 0.0;                   // Solve time to zero
267   work.info.update_time = 0.0;                   // Update time to zero
268   work.info.polish_time = 0.0;                   // Polish time to zero
269   work.info.run_time    = 0.0;                   // Total run time to zero
270   work.info.setup_time  = osqp_toc(work.timer); // Update timer information
271 
272   work.first_run         = 1;
273   work.clear_update_time = 0;
274   work.rho_update_from_solve = 0;
275 } /* ifdef PROFILING */
276   work.info.rho_updates  = 0;                    // Rho updates set to 0
277   work.info.rho_estimate = work.settings.rho;  // Best rho estimate
278 
279   // Print header
280 version(PRINTING){
281   if (work.settings.verbose) print_setup_header(work);
282   work.summary_printed = 0; // Initialize last summary  to not printed
283 } /* ifdef PRINTING */
284 
285 
286   // If adaptive rho and automatic interval, but profiling disabled, we need to
287   // set the interval to a default value
288 version(PROFILING){}
289 else {
290   if (work.settings.adaptive_rho && !work.settings.adaptive_rho_interval) {
291     if (work.settings.check_termination) {
292       // If check_termination is enabled, we set it to a multiple of the check
293       // termination interval
294       work.settings.adaptive_rho_interval = ADAPTIVE_RHO_MULTIPLE_TERMINATION *
295                                               work.settings.check_termination;
296     } else {
297       // If check_termination is disabled we set it to a predefined fix number
298       work.settings.adaptive_rho_interval = ADAPTIVE_RHO_FIXED;
299     }
300   }
301 } /* ifndef PROFILING */
302 
303   // Return exit flag
304   return 0;
305 }
306 
307 } // #ifndef EMBEDDED
308 
309 
310 c_int osqp_solve(OSQPWorkspace *work) {
311 
312   c_int exitflag;
313   c_int iter;
314   c_int compute_cost_function; // Boolean: compute the cost function in the loop or not
315   c_int can_check_termination; // Boolean: check termination or not
316 
317 version(PROFILING){
318   c_float temp_run_time;       // Temporary variable to store current run time
319 } /* ifdef PROFILING */
320 
321 version(PRINTING){
322   c_int can_print;             // Boolean whether you can print
323 } /* ifdef PRINTING */
324 
325   // Check if workspace has been initialized
326   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
327 
328 version(PROFILING){
329   if (work.clear_update_time == 1)
330     work.info.update_time = 0.0;
331   work.rho_update_from_solve = 1;
332 } /* ifdef PROFILING */
333 
334   // Initialize variables
335   exitflag              = 0;
336   can_check_termination = 0;
337 version(PRINTING){
338   can_print = work.settings.verbose;
339 } /* ifdef PRINTING */
340 version(PRINTING){
341   compute_cost_function = work.settings.verbose; // Compute cost function only
342                                                    // if verbose is on
343 } else {/* ifdef PRINTING */
344   compute_cost_function = 0;                       // Never compute cost
345                                                    // function during the
346                                                    // iterations if no printing
347                                                    // enabled
348 } /* ifdef PRINTING */
349 
350 
351 
352 version(PROFILING){
353   osqp_tic(work.timer); // Start timer
354  } /* ifdef PROFILING */
355 
356 
357 version(PRINTING){
358 
359   if (work.settings.verbose) {
360     // Print Header for every column
361     print_header();
362   }
363 } /* ifdef PRINTING */
364 
365 version(CTRLC){
366 
367   // initialize Ctrl-C support
368   osqp_start_interrupt_listener();
369 } /* ifdef CTRLC */
370 
371   // Initialize variables (cold start or warm start depending on settings)
372   if (!work.settings.warm_start) cold_start(work);  // If not warm start .
373                                                       // set x, z, y to zero
374 
375   // Main ADMM algorithm
376   for (iter = 1; iter <= work.settings.max_iter; iter++) {
377     // Update x_prev, z_prev (preallocated, no malloc)
378     swap_vectors(&(work.x), &(work.x_prev));
379     swap_vectors(&(work.z), &(work.z_prev));
380 
381     /* ADMM STEPS */
382     /* Compute \tilde{x}^{k+1}, \tilde{z}^{k+1} */
383     update_xz_tilde(work);
384 
385     /* Compute x^{k+1} */
386     update_x(work);
387 
388     /* Compute z^{k+1} */
389     update_z(work);
390 
391     /* Compute y^{k+1} */
392     update_y(work);
393 
394     /* End of ADMM Steps */
395 
396 version(CTRLC){
397 
398     // Check the interrupt signal
399     if (osqp_is_interrupted()) {
400       update_status(work.info, OSQP_SIGINT);
401 version(PRINTING){
402       c_print("Solver interrupted\n");
403 } /* ifdef PRINTING */
404       exitflag = 1;
405       goto exit;
406     }
407 } /* ifdef CTRLC */
408 
409 version(PROFILING){
410 
411     // Check if solver time_limit is enabled. In case, check if the current
412     // run time is more than the time_limit option.
413     if (work.first_run) {
414       temp_run_time = work.info.setup_time + osqp_toc(work.timer);
415     }
416     else {
417       temp_run_time = work.info.update_time + osqp_toc(work.timer);
418     }
419 
420     if (work.settings.time_limit &&
421         (temp_run_time >= work.settings.time_limit)) {
422       update_status(work.info, OSQP_TIME_LIMIT_REACHED);
423 version(PRINTING){
424       if (work.settings.verbose) c_print("run time limit reached\n");
425       can_print = 0;  // Not printing at this iteration
426 } /* ifdef PRINTING */
427       break;
428     }
429 } /* ifdef PROFILING */
430 
431 
432     // Can we check for termination ?
433     can_check_termination = work.settings.check_termination &&
434                             (iter % work.settings.check_termination == 0);
435 
436 version(PRINTING){
437 
438     // Can we print ?
439     can_print = work.settings.verbose &&
440                 ((iter % PRINT_INTERVAL == 0) || (iter == 1));
441 
442     if (can_check_termination || can_print) { // Update status in either of
443                                               // these cases
444       // Update information
445       update_info(work, iter, compute_cost_function, 0);
446 
447       if (can_print) {
448         // Print summary
449         print_summary(work);
450       }
451 
452       if (can_check_termination) {
453         // Check algorithm termination
454         if (check_termination(work, 0)) {
455           // Terminate algorithm
456           break;
457         }
458       }
459     }
460 }else{ /* ifdef PRINTING */
461 
462     if (can_check_termination) {
463       // Update information and compute also objective value
464       update_info(work, iter, compute_cost_function, 0);
465 
466       // Check algorithm termination
467       if (check_termination(work, 0)) {
468         // Terminate algorithm
469         break;
470       }
471     }
472 } /* ifdef PRINTING */
473 
474 
475 
476 version(EMBEDDED_1){}
477 else{
478 version(PROFILING){
479 
480     // If adaptive rho with automatic interval, check if the solve time is a
481     // certain fraction
482     // of the setup time.
483     if (work.settings.adaptive_rho && !work.settings.adaptive_rho_interval) {
484       // Check time
485       if (osqp_toc(work.timer) >
486           work.settings.adaptive_rho_fraction * work.info.setup_time) {
487         // Enough time has passed. We now get the number of iterations between
488         // the updates.
489         if (work.settings.check_termination) {
490           // If check_termination is enabled, we round the number of iterations
491           // between
492           // rho updates to the closest multiple of check_termination
493           work.settings.adaptive_rho_interval = cast(c_int)c_roundmultiple(iter,
494                                                                          work.settings.check_termination);
495         } else {
496           // If check_termination is disabled, we round the number of iterations
497           // between
498           // updates to the closest multiple of the default check_termination
499           // interval.
500           work.settings.adaptive_rho_interval = cast(c_int)c_roundmultiple(iter,
501                                                                          CHECK_TERMINATION);
502         }
503 
504         // Make sure the interval is not 0 and at least check_termination times
505         work.settings.adaptive_rho_interval = c_max(
506           work.settings.adaptive_rho_interval,
507           work.settings.check_termination);
508       } // If time condition is met
509     }   // If adaptive rho enabled and interval set to auto
510 } // PROFILING
511 
512     // Adapt rho
513     if (work.settings.adaptive_rho &&
514         work.settings.adaptive_rho_interval &&
515         (iter % work.settings.adaptive_rho_interval == 0)) {
516       // Update info with the residuals if it hasn't been done before
517 version(PRINTING){
518 
519       if (!can_check_termination && !can_print) {
520         // Information has not been computed neither for termination or printing
521         // reasons
522         update_info(work, iter, compute_cost_function, 0);
523       }
524 } else {/* ifdef PRINTING */
525 
526       if (!can_check_termination) {
527         // Information has not been computed before for termination check
528         update_info(work, iter, compute_cost_function, 0);
529       }
530 }  /* ifdef PRINTING */
531 
532       // Actually update rho
533       if (adapt_rho(work)) {
534 version(PRINTING){
535         c_eprint("ERROR in %s: Failed rho update\n", __FUNCTION__.ptr);
536 } // PRINTING
537         exitflag = 1;
538         goto exit;
539       }
540     }
541 } // EMBEDDED_1 != 1
542 
543   }        // End of ADMM for loop
544 
545 
546   // Update information and check termination condition if it hasn't been done
547   // during last iteration (max_iter reached or check_termination disabled)
548   if (!can_check_termination) {
549     /* Update information */
550 version(PRINTING){
551 
552     if (!can_print) {
553       // Update info only if it hasn't been updated before for printing
554       // reasons
555       update_info(work, iter - 1, compute_cost_function, 0);
556     }
557 }else {/* ifdef PRINTING */
558 
559     // If no printing is enabled, update info directly
560     update_info(work, iter - 1, compute_cost_function, 0);
561 } /* ifdef PRINTING */
562 
563 version(PRINTING){
564 
565     /* Print summary */
566     if (work.settings.verbose && !work.summary_printed) print_summary(work);
567 } /* ifdef PRINTING */
568 
569     /* Check whether a termination criterion is triggered */
570     check_termination(work, 0);
571   }
572 
573   // Compute objective value in case it was not
574   // computed during the iterations
575   if (!compute_cost_function && has_solution(work.info)){
576     work.info.obj_val = compute_obj_val(work, work.x);
577   }
578 
579 
580 version(PRINTING){
581   /* Print summary for last iteration */
582   if (work.settings.verbose && !work.summary_printed) {
583     print_summary(work);
584   }
585 } /* ifdef PRINTING */
586 
587   /* if max iterations reached, change status accordingly */
588   if (work.info.status_val == OSQP_UNSOLVED) {
589     if (!check_termination(work, 1)) { // Try to check for approximate
590       update_status(work.info, OSQP_MAX_ITER_REACHED);
591     }
592   }
593 
594 version(PROFILING){
595   /* if time-limit reached check termination and update status accordingly */
596  if (work.info.status_val == OSQP_TIME_LIMIT_REACHED) {
597     if (!check_termination(work, 1)) { // Try for approximate solutions
598       update_status(work.info, OSQP_TIME_LIMIT_REACHED); /* Change update status back to OSQP_TIME_LIMIT_REACHED */
599     }
600   }
601 } /* ifdef PROFILING */
602 
603 
604 version(EMBEDDED_1) {}
605 else {
606   /* Update rho estimate */
607   work.info.rho_estimate = compute_rho_estimate(work);
608 } /* if EMBEDDED_1 != 1 */
609 
610   /* Update solve time */
611 version(PROFILING){
612   work.info.solve_time = osqp_toc(work.timer);
613 } /* ifdef PROFILING */
614 
615 
616 version(EMBEDDED) {}
617 else {
618   // Polish the obtained solution
619   if (work.settings.polish && (work.info.status_val == OSQP_SOLVED)){
620     polish.polish(work);
621   }
622 } /* ifndef EMBEDDED */
623 
624 version(PROFILING){
625   /* Update total time */
626   if (work.first_run) {
627     // total time: setup + solve + polish
628     work.info.run_time = work.info.setup_time +
629                            work.info.solve_time +
630                            work.info.polish_time;
631   } else {
632     // total time: update + solve + polish
633     work.info.run_time = work.info.update_time +
634                            work.info.solve_time +
635                            work.info.polish_time;
636   }
637 
638   // Indicate that the solve function has already been executed
639   if (work.first_run) work.first_run = 0;
640 
641   // Indicate that the update_time should be set to zero
642   work.clear_update_time = 1;
643 
644   // Indicate that osqp_update_rho is not called from osqp_solve
645   work.rho_update_from_solve = 0;
646 } /* ifdef PROFILING */
647 
648 version(PRINTING){
649   /* Print final footer */
650   if (work.settings.verbose) print_footer(work.info, work.settings.polish);
651 } /* ifdef PRINTING */
652 
653   // Store solution
654   store_solution(work);
655 
656 // Define exit flag for quitting function
657 version(PROFILING){
658   exit:
659 } else {
660   version(CTRLC){
661     exit:
662   }else {
663     version(EMBEDDED_1){}
664     else {
665       exit:
666     }
667   }
668 }
669 //#if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1
670 //exit:
671 //#endif /* if defined(PROFILING) || defined(CTRLC) || EMBEDDED != 1 */
672 
673 version(CTRLC){
674   // Restore previous signal handler
675   osqp_end_interrupt_listener();
676 } /* ifdef CTRLC */
677 
678   return exitflag;
679 }
680 
681 
682 c_int osqp_cleanup(OSQPWorkspace *work) {
683   c_int exitflag = 0;
684 
685   if (work) { // If workspace has been allocated
686     // Free Data
687     if (work.data) {
688 version(EMBEDDED){}   // todo : useless here
689 else {
690       if (work.data.P) csc_spfree(work.data.P);   // todo : test it
691       if (work.data.A) csc_spfree(work.data.A);
692 } // !EMBEDDED
693       if (work.data.q) c_free(work.data.q);
694       if (work.data.l) c_free(work.data.l);
695       if (work.data.u) c_free(work.data.u);
696       c_free(work.data);
697     }
698 
699     // Free scaling variables
700     if (work.scaling){
701       if (work.scaling.D)    c_free(work.scaling.D);
702       if (work.scaling.Dinv) c_free(work.scaling.Dinv);
703       if (work.scaling.E)    c_free(work.scaling.E);
704       if (work.scaling.Einv) c_free(work.scaling.Einv);
705       c_free(work.scaling);
706     }
707 
708     // Free temp workspace variables for scaling
709     if (work.D_temp)   c_free(work.D_temp);
710     if (work.D_temp_A) c_free(work.D_temp_A);
711     if (work.E_temp)   c_free(work.E_temp);
712 
713 version(EMBEDDED){}
714 else {
715     // Free linear system solver structure
716     if (work.linsys_solver) {
717       if (work.linsys_solver.free) {
718         work.linsys_solver.free(work.linsys_solver);
719       }
720     }
721 } // !EMBEDDED
722 
723     // Unload linear system solver after free
724     if (work.settings) {
725       exitflag = unload_linsys_solver(work.settings.linsys_solver);
726     }
727 
728 version(EMBEDDED){}   // todo : useless here
729 else {
730     // Free active constraints structure
731     if (work.pol) {
732       if (work.pol.Alow_to_A) c_free(work.pol.Alow_to_A);
733       if (work.pol.Aupp_to_A) c_free(work.pol.Aupp_to_A);
734       if (work.pol.A_to_Alow) c_free(work.pol.A_to_Alow);
735       if (work.pol.A_to_Aupp) c_free(work.pol.A_to_Aupp);
736       if (work.pol.x)         c_free(work.pol.x);
737       if (work.pol.z)         c_free(work.pol.z);
738       if (work.pol.y)         c_free(work.pol.y);
739       c_free(work.pol);
740     }
741 } /* ifndef EMBEDDED */
742 
743     // Free other Variables
744     if (work.rho_vec)     c_free(work.rho_vec);
745     if (work.rho_inv_vec) c_free(work.rho_inv_vec);
746 version(EMBEDDED_1){}
747 else {
748     if (work.constr_type) c_free(work.constr_type);
749 }
750     if (work.x)           c_free(work.x);
751     if (work.z)           c_free(work.z);
752     if (work.xz_tilde)    c_free(work.xz_tilde);
753     if (work.x_prev)      c_free(work.x_prev);
754     if (work.z_prev)      c_free(work.z_prev);
755     if (work.y)           c_free(work.y);
756     if (work.Ax)          c_free(work.Ax);
757     if (work.Px)          c_free(work.Px);
758     if (work.Aty)         c_free(work.Aty);
759     if (work.delta_y)     c_free(work.delta_y);
760     if (work.Atdelta_y)   c_free(work.Atdelta_y);
761     if (work.delta_x)     c_free(work.delta_x);
762     if (work.Pdelta_x)    c_free(work.Pdelta_x);
763     if (work.Adelta_x)    c_free(work.Adelta_x);
764     // Free Settings
765     if (work.settings) c_free(work.settings);
766 
767     // Free solution
768     if (work.solution) {
769       if (work.solution.x) c_free(work.solution.x);
770       if (work.solution.y) c_free(work.solution.y);
771       c_free(work.solution);
772     }
773 
774     // Free information
775     if (work.info) c_free(work.info);
776 
777 version(PROFILING){
778     // Free timer
779     if (work.timer) c_free(work.timer);
780 } /* ifdef PROFILING */
781 
782     // Free work
783     c_free(work);
784   }
785   return exitflag;
786 }
787 
788 //} // #ifndef EMBEDDED
789 
790 
791 /************************
792 * Update problem data  *
793 ************************/
794 c_int osqp_update_lin_cost(OSQPWorkspace *work, const c_float *q_new) {
795 
796   // Check if workspace has been initialized
797   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
798 
799 version(PROFILING){
800   if (work.clear_update_time == 1) {
801     work.clear_update_time = 0;
802     work.info.update_time = 0.0;
803   }
804   osqp_tic(work.timer); // Start timer
805 } /* ifdef PROFILING */
806 
807   // Replace q by the new vector
808   prea_vec_copy(q_new, work.data.q, work.data.n);
809 
810   // Scaling
811   if (work.settings.scaling) {
812     vec_ew_prod(work.scaling.D, work.data.q, work.data.q, work.data.n);
813     vec_mult_scalar(work.data.q, work.scaling.c, work.data.n);
814   }
815 
816   // Reset solver information
817   reset_info(work.info);
818 
819 version(PROFILING){
820   work.info.update_time += osqp_toc(work.timer);
821 } /* ifdef PROFILING */
822 
823   return 0;
824 }
825 
826 c_int osqp_update_bounds(OSQPWorkspace *work,
827                          const c_float *l_new,
828                          const c_float *u_new) {
829   c_int i, exitflag = 0;
830 
831   // Check if workspace has been initialized
832   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
833 
834 version(PROFILING){
835   if (work.clear_update_time == 1) {
836     work.clear_update_time = 0;
837     work.info.update_time = 0.0;
838   }
839   osqp_tic(work.timer); // Start timer
840 } /* ifdef PROFILING */
841 
842   // Check if lower bound is smaller than upper bound
843   for (i = 0; i < work.data.m; i++) {
844     if (l_new[i] > u_new[i]) {
845 version(PRINTING){
846       c_eprint("ERROR in %s: lower bound must be lower than or equal to upper bound\n", __FUNCTION__.ptr);
847 } /* ifdef PRINTING */
848       return 1;
849     }
850   }
851 
852   // Replace l and u by the new vectors
853   prea_vec_copy(l_new, work.data.l, work.data.m);
854   prea_vec_copy(u_new, work.data.u, work.data.m);
855 
856   // Scaling
857   if (work.settings.scaling) {
858     vec_ew_prod(work.scaling.E, work.data.l, work.data.l, work.data.m);
859     vec_ew_prod(work.scaling.E, work.data.u, work.data.u, work.data.m);
860   }
861 
862   // Reset solver information
863   reset_info(work.info);
864 
865 version(EMBEDDED_1){}
866 else {
867   // Update rho_vec and refactor if constraints type changes
868   exitflag = update_rho_vec(work);
869 } // EMBEDDED != 1
870 
871 version(PROFILING){
872   work.info.update_time += osqp_toc(work.timer);
873  } /* ifdef PROFILING */
874 
875   return exitflag;
876 }
877 
878 c_int osqp_update_lower_bound(OSQPWorkspace *work, const c_float *l_new) {
879   c_int i, exitflag = 0;
880 
881   // Check if workspace has been initialized
882   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
883 
884 version(PROFILING){
885   if (work.clear_update_time == 1) {
886     work.clear_update_time = 0;
887     work.info.update_time = 0.0;
888   }
889   osqp_tic(work.timer); // Start timer
890 } /* ifdef PROFILING */
891 
892   // Replace l by the new vector
893   prea_vec_copy(l_new, work.data.l, work.data.m);
894 
895   // Scaling
896   if (work.settings.scaling) {
897     vec_ew_prod(work.scaling.E, work.data.l, work.data.l, work.data.m);
898   }
899 
900   // Check if lower bound is smaller than upper bound
901   for (i = 0; i < work.data.m; i++) {
902     if (work.data.l[i] > work.data.u[i]) {
903 version(PRINTING){
904       c_eprint("ERROR in %s: upper bound must be greater than or equal to lower bound\n", __FUNCTION__.ptr);
905  } /* ifdef PRINTING */
906       return 1;
907     }
908   }
909 
910   // Reset solver information
911   reset_info(work.info);
912 
913 version(EMBEDDED_1){}
914 else {
915   // Update rho_vec and refactor if constraints type changes
916   exitflag = update_rho_vec(work);
917 } // EMBEDDED ! =1
918 
919 version(PROFILING){
920   work.info.update_time += osqp_toc(work.timer);
921 } /* ifdef PROFILING */
922 
923   return exitflag;
924 }
925 
926 c_int osqp_update_upper_bound(OSQPWorkspace *work, const c_float *u_new) {
927   c_int i, exitflag = 0;
928 
929   // Check if workspace has been initialized
930   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
931 
932 version(PROFILING){
933   if (work.clear_update_time == 1) {
934     work.clear_update_time = 0;
935     work.info.update_time = 0.0;
936   }
937   osqp_tic(work.timer); // Start timer
938  } /* ifdef PROFILING */
939 
940   // Replace u by the new vector
941   prea_vec_copy(u_new, work.data.u, work.data.m);
942 
943   // Scaling
944   if (work.settings.scaling) {
945     vec_ew_prod(work.scaling.E, work.data.u, work.data.u, work.data.m);
946   }
947 
948   // Check if upper bound is greater than lower bound
949   for (i = 0; i < work.data.m; i++) {
950     if (work.data.u[i] < work.data.l[i]) {
951 version(PRINTING){
952       c_eprint("ERROR in %s: lower bound must be lower than or equal to upper bound\n", __FUNCTION__.ptr);
953  } /* ifdef PRINTING */
954       return 1;
955     }
956   }
957 
958   // Reset solver information
959   reset_info(work.info);
960 
961 version(EMBEDDED_1){}
962 else {
963   // Update rho_vec and refactor if constraints type changes
964   exitflag = update_rho_vec(work);
965 } // EMBEDDED != 1
966 
967 version(PROFILING){
968   work.info.update_time += osqp_toc(work.timer);
969  } /* ifdef PROFILING */
970 
971   return exitflag;
972 }
973 
974 c_int osqp_warm_start(OSQPWorkspace *work, const c_float *x, const c_float *y) {
975 
976   // Check if workspace has been initialized
977   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
978 
979   // Update warm_start setting to true
980   if (!work.settings.warm_start) work.settings.warm_start = 1;
981 
982   // Copy primal and dual variables into the iterates
983   prea_vec_copy(x, work.x, work.data.n);
984   prea_vec_copy(y, work.y, work.data.m);
985 
986   // Scale iterates
987   if (work.settings.scaling) {
988     vec_ew_prod(work.scaling.Dinv, work.x, work.x, work.data.n);
989     vec_ew_prod(work.scaling.Einv, work.y, work.y, work.data.m);
990     vec_mult_scalar(work.y, work.scaling.c, work.data.m);
991   }
992 
993   // Compute Ax = z and store it in z
994   mat_vec(work.data.A, work.x, work.z, 0);
995 
996   return 0;
997 }
998 
999 c_int osqp_warm_start_x(OSQPWorkspace *work, const c_float *x) {
1000 
1001   // Check if workspace has been initialized
1002   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1003 
1004   // Update warm_start setting to true
1005   if (!work.settings.warm_start) work.settings.warm_start = 1;
1006 
1007   // Copy primal variable into the iterate x
1008   prea_vec_copy(x, work.x, work.data.n);
1009 
1010   // Scale iterate
1011   if (work.settings.scaling) {
1012     vec_ew_prod(work.scaling.Dinv, work.x, work.x, work.data.n);
1013   }
1014 
1015   // Compute Ax = z and store it in z
1016   mat_vec(work.data.A, work.x, work.z, 0);
1017 
1018   return 0;
1019 }
1020 
1021 c_int osqp_warm_start_y(OSQPWorkspace *work, const c_float *y) {
1022 
1023   // Check if workspace has been initialized
1024   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1025 
1026   // Update warm_start setting to true
1027   if (!work.settings.warm_start) work.settings.warm_start = 1;
1028 
1029   // Copy primal variable into the iterate y
1030   prea_vec_copy(y, work.y, work.data.m);
1031 
1032   // Scale iterate
1033   if (work.settings.scaling) {
1034     vec_ew_prod(work.scaling.Einv, work.y, work.y, work.data.m);
1035     vec_mult_scalar(work.y, work.scaling.c, work.data.m);
1036   }
1037 
1038   return 0;
1039 }
1040 
1041 
1042 version(EMBEDDED_1){}
1043 else {
1044 
1045 c_int osqp_update_P(OSQPWorkspace *work,
1046                     const c_float *Px_new,
1047                     const c_int   *Px_new_idx,
1048                     c_int          P_new_n) {
1049   c_int i;        // For indexing
1050   c_int exitflag; // Exit flag
1051   c_int nnzP;     // Number of nonzeros in P
1052 
1053   // Check if workspace has been initialized
1054   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1055 
1056 version(PROFILING){
1057   if (work.clear_update_time == 1) {
1058     work.clear_update_time = 0;
1059     work.info.update_time = 0.0;
1060   }
1061   osqp_tic(work.timer); // Start timer
1062 } /* ifdef PROFILING */
1063 
1064   nnzP = work.data.P.p[work.data.P.n];
1065 
1066   if (Px_new_idx) { // Passing the index of elements changed
1067     // Check if number of elements is less or equal than the total number of
1068     // nonzeros in P
1069     if (P_new_n > nnzP) {
1070 version(PRINTING){
1071       c_eprint("ERROR in %s: new number of elements (%i) greater than elements in P (%i)\n",
1072                __FUNCTION__.ptr,
1073                cast(c_int)P_new_n,
1074                cast(c_int)nnzP);
1075 } /* ifdef PRINTING */
1076       return 1;
1077     }
1078   }
1079 
1080   if (work.settings.scaling) {
1081     // Unscale data
1082     unscale_data(work);
1083   }
1084 
1085   // Update P elements
1086   if (Px_new_idx) { // Change only Px_new_idx
1087     for (i = 0; i < P_new_n; i++) {
1088       work.data.P.x[Px_new_idx[i]] = Px_new[i];
1089     }
1090   }
1091   else // Change whole P
1092   {
1093     for (i = 0; i < nnzP; i++) {
1094       work.data.P.x[i] = Px_new[i];
1095     }
1096   }
1097 
1098   if (work.settings.scaling) {
1099     // Scale data
1100     scale_data(work);
1101   }
1102 
1103   // Update linear system structure with new data
1104   exitflag = work.linsys_solver.update_matrices(work.linsys_solver,
1105                                                   work.data.P,
1106                                                   work.data.A);
1107 
1108   // Reset solver information
1109   reset_info(work.info);
1110 
1111 version(PRINTING){
1112 
1113   if (exitflag < 0) {
1114     c_eprint("ERROR in %s: new KKT matrix is not quasidefinite\n", __FUNCTION__.ptr);
1115   }
1116 } /* ifdef PRINTING */
1117 
1118 version(PROFILING){
1119   work.info.update_time += osqp_toc(work.timer);
1120 } /* ifdef PROFILING */
1121 
1122   return exitflag;
1123 }
1124 
1125 
1126 c_int osqp_update_A(OSQPWorkspace *work,
1127                     const c_float *Ax_new,
1128                     const c_int   *Ax_new_idx,
1129                     c_int          A_new_n) {
1130   c_int i;        // For indexing
1131   c_int exitflag; // Exit flag
1132   c_int nnzA;     // Number of nonzeros in A
1133 
1134   // Check if workspace has been initialized
1135   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1136 
1137 version(PROFILING){
1138   if (work.clear_update_time == 1) {
1139     work.clear_update_time = 0;
1140     work.info.update_time = 0.0;
1141   }
1142   osqp_tic(work.timer); // Start timer
1143 } /* ifdef PROFILING */
1144 
1145   nnzA = work.data.A.p[work.data.A.n];
1146 
1147   if (Ax_new_idx) { // Passing the index of elements changed
1148     // Check if number of elements is less or equal than the total number of
1149     // nonzeros in A
1150     if (A_new_n > nnzA) {
1151 version(PRINTING){
1152       c_eprint("ERROR in %s: new number of elements (%i) greater than elements in A (%i)\n",
1153                __FUNCTION__.ptr,
1154                cast(c_int)A_new_n,
1155                cast(c_int)nnzA);
1156 } /* ifdef PRINTING */
1157       return 1;
1158     }
1159   }
1160 
1161   if (work.settings.scaling) {
1162     // Unscale data
1163     unscale_data(work);
1164   }
1165 
1166   // Update A elements
1167   if (Ax_new_idx) { // Change only Ax_new_idx
1168     for (i = 0; i < A_new_n; i++) {
1169       work.data.A.x[Ax_new_idx[i]] = Ax_new[i];
1170     }
1171   }
1172   else { // Change whole A
1173     for (i = 0; i < nnzA; i++) {
1174       work.data.A.x[i] = Ax_new[i];
1175     }
1176   }
1177 
1178   if (work.settings.scaling) {
1179     // Scale data
1180     scale_data(work);
1181   }
1182 
1183   // Update linear system structure with new data
1184   exitflag = work.linsys_solver.update_matrices(work.linsys_solver,
1185                                                   work.data.P,
1186                                                   work.data.A);
1187 
1188   // Reset solver information
1189   reset_info(work.info);
1190 
1191 version(PRINTING){
1192 
1193   if (exitflag < 0) {
1194     c_eprint("ERROR in %s: new KKT matrix is not quasidefinite\n", __FUNCTION__.ptr);
1195   }
1196 } /* ifdef PRINTING */
1197 
1198 version(PROFILING){
1199   work.info.update_time += osqp_toc(work.timer);
1200  } /* ifdef PROFILING */
1201 
1202   return exitflag;
1203 }
1204 
1205 
1206 c_int osqp_update_P_A(OSQPWorkspace *work,
1207                       const c_float *Px_new,
1208                       const c_int   *Px_new_idx,
1209                       c_int          P_new_n,
1210                       const c_float *Ax_new,
1211                       const c_int   *Ax_new_idx,
1212                       c_int          A_new_n) {
1213   c_int i;          // For indexing
1214   c_int exitflag;   // Exit flag
1215   c_int nnzP, nnzA; // Number of nonzeros in P and A
1216 
1217   // Check if workspace has been initialized
1218   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1219 
1220 version(PROFILING){
1221   if (work.clear_update_time == 1) {
1222     work.clear_update_time = 0;
1223     work.info.update_time = 0.0;
1224   }
1225   osqp_tic(work.timer); // Start timer
1226 } /* ifdef PROFILING */
1227 
1228   nnzP = work.data.P.p[work.data.P.n];
1229   nnzA = work.data.A.p[work.data.A.n];
1230 
1231 
1232   if (Px_new_idx) { // Passing the index of elements changed
1233     // Check if number of elements is less or equal than the total number of
1234     // nonzeros in P
1235     if (P_new_n > nnzP) {
1236 version(PRINTING){
1237       c_eprint("ERROR in %s: new number of elements (%i) greater than elements in P (%i)\n",
1238                __FUNCTION__.ptr,
1239                cast(c_int)P_new_n,
1240                cast(c_int)nnzP);
1241 } /* ifdef PRINTING */
1242       return 1;
1243     }
1244   }
1245 
1246 
1247   if (Ax_new_idx) { // Passing the index of elements changed
1248     // Check if number of elements is less or equal than the total number of
1249     // nonzeros in A
1250     if (A_new_n > nnzA) {
1251 version(PRINTING){
1252       c_eprint("ERROR in %s: new number of elements (%i) greater than elements in A (%i)\n",
1253                __FUNCTION__.ptr,
1254                cast(c_int)A_new_n,
1255                cast(c_int)nnzA);
1256 } /* ifdef PRINTING */
1257       return 2;
1258     }
1259   }
1260 
1261   if (work.settings.scaling) {
1262     // Unscale data
1263     unscale_data(work);
1264   }
1265 
1266   // Update P elements
1267   if (Px_new_idx) { // Change only Px_new_idx
1268     for (i = 0; i < P_new_n; i++) {
1269       work.data.P.x[Px_new_idx[i]] = Px_new[i];
1270     }
1271   }
1272   else // Change whole P
1273   {
1274     for (i = 0; i < nnzP; i++) {
1275       work.data.P.x[i] = Px_new[i];
1276     }
1277   }
1278 
1279   // Update A elements
1280   if (Ax_new_idx) { // Change only Ax_new_idx
1281     for (i = 0; i < A_new_n; i++) {
1282       work.data.A.x[Ax_new_idx[i]] = Ax_new[i];
1283     }
1284   }
1285   else { // Change whole A
1286     for (i = 0; i < nnzA; i++) {
1287       work.data.A.x[i] = Ax_new[i];
1288     }
1289   }
1290 
1291   if (work.settings.scaling) {
1292     // Scale data
1293     scale_data(work);
1294   }
1295 
1296   // Update linear system structure with new data
1297   exitflag = work.linsys_solver.update_matrices(work.linsys_solver,
1298                                                   work.data.P,
1299                                                   work.data.A);
1300 
1301   // Reset solver information
1302   reset_info(work.info);
1303 
1304 version(PRINTING){
1305 
1306   if (exitflag < 0) {
1307     c_eprint("ERROR in %s: new KKT matrix is not quasidefinite\n", __FUNCTION__.ptr);
1308   }
1309  } /* ifdef PRINTING */
1310 
1311 version(PROFILING){
1312   work.info.update_time += osqp_toc(work.timer);
1313 } /* ifdef PROFILING */
1314 
1315   return exitflag;
1316 }
1317 
1318 c_int osqp_update_rho(OSQPWorkspace *work, c_float rho_new) {
1319   c_int exitflag, i;
1320 
1321   // Check if workspace has been initialized
1322   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1323 
1324   // Check value of rho
1325   if (rho_new <= 0) {
1326 version(PRINTING){
1327     c_eprint("ERROR in %s: rho must be positive\n", __FUNCTION__.ptr);
1328 } /* ifdef PRINTING */
1329     return 1;
1330   }
1331 
1332 version(PROFILING){
1333   if (work.rho_update_from_solve == 0) {
1334     if (work.clear_update_time == 1) {
1335       work.clear_update_time = 0;
1336       work.info.update_time = 0.0;
1337     }
1338     osqp_tic(work.timer); // Start timer
1339   }
1340 } /* ifdef PROFILING */
1341 
1342   // Update rho in settings
1343   work.settings.rho = c_min(c_max(rho_new, RHO_MIN), RHO_MAX);
1344 
1345   // Update rho_vec and rho_inv_vec
1346   for (i = 0; i < work.data.m; i++) {
1347     if (work.constr_type[i] == 0) {
1348       // Inequalities
1349       work.rho_vec[i]     = work.settings.rho;
1350       work.rho_inv_vec[i] = 1. / work.settings.rho;
1351     }
1352     else if (work.constr_type[i] == 1) {
1353       // Equalities
1354       work.rho_vec[i]     = RHO_EQ_OVER_RHO_INEQ * work.settings.rho;
1355       work.rho_inv_vec[i] = 1. / work.rho_vec[i];
1356     }
1357   }
1358 
1359   // Update rho_vec in KKT matrix
1360   exitflag = work.linsys_solver.update_rho_vec(work.linsys_solver,
1361                                                  work.rho_vec);
1362 
1363 version(PROFILING){
1364   if (work.rho_update_from_solve == 0)
1365     work.info.update_time += osqp_toc(work.timer);
1366 } /* ifdef PROFILING */
1367 
1368   return exitflag;
1369 }
1370 
1371 } // EMBEDDED != 1
1372 
1373 /****************************
1374 * Update problem settings  *
1375 ****************************/
1376 c_int osqp_update_max_iter(OSQPWorkspace *work, c_int max_iter_new) {
1377 
1378   // Check if workspace has been initialized
1379   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1380 
1381   // Check that max_iter is positive
1382   if (max_iter_new <= 0) {
1383 version(PRINTING){
1384     c_eprint("ERROR in %s: max_iter must be positive\n", __FUNCTION__.ptr);
1385 } /* ifdef PRINTING */
1386     return 1;
1387   }
1388 
1389   // Update max_iter
1390   work.settings.max_iter = max_iter_new;
1391 
1392   return 0;
1393 }
1394 
1395 c_int osqp_update_eps_abs(OSQPWorkspace *work, c_float eps_abs_new) {
1396 
1397   // Check if workspace has been initialized
1398   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1399 
1400   // Check that eps_abs is positive
1401   if (eps_abs_new < 0.) {
1402 version(PRINTING){
1403     c_eprint("ERROR in %s: eps_abs must be nonnegative\n", __FUNCTION__.ptr);
1404 } /* ifdef PRINTING */
1405     return 1;
1406   }
1407 
1408   // Update eps_abs
1409   work.settings.eps_abs = eps_abs_new;
1410 
1411   return 0;
1412 }
1413 
1414 c_int osqp_update_eps_rel(OSQPWorkspace *work, c_float eps_rel_new) {
1415 
1416   // Check if workspace has been initialized
1417   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1418 
1419   // Check that eps_rel is positive
1420   if (eps_rel_new < 0.) {
1421 version(PRINTING){
1422     c_eprint("ERROR in %s: eps_rel must be nonnegative\n", __FUNCTION__.ptr);
1423 } /* ifdef PRINTING */
1424     return 1;
1425   }
1426 
1427   // Update eps_rel
1428   work.settings.eps_rel = eps_rel_new;
1429 
1430   return 0;
1431 }
1432 
1433 c_int osqp_update_eps_prim_inf(OSQPWorkspace *work, c_float eps_prim_inf_new) {
1434 
1435   // Check if workspace has been initialized
1436   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1437 
1438   // Check that eps_prim_inf is positive
1439   if (eps_prim_inf_new < 0.) {
1440 version(PRINTING){
1441     c_eprint("ERROR in %s: eps_prim_inf must be nonnegative\n", __FUNCTION__.ptr);
1442 } /* ifdef PRINTING */
1443     return 1;
1444   }
1445 
1446   // Update eps_prim_inf
1447   work.settings.eps_prim_inf = eps_prim_inf_new;
1448 
1449   return 0;
1450 }
1451 
1452 c_int osqp_update_eps_dual_inf(OSQPWorkspace *work, c_float eps_dual_inf_new) {
1453 
1454   // Check if workspace has been initialized
1455   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1456 
1457   // Check that eps_dual_inf is positive
1458   if (eps_dual_inf_new < 0.) {
1459 version(PRINTING){
1460     c_eprint("ERROR in %s: eps_dual_inf must be nonnegative\n", __FUNCTION__.ptr);
1461 } /* ifdef PRINTING */
1462     return 1;
1463   }
1464 
1465   // Update eps_dual_inf
1466   work.settings.eps_dual_inf = eps_dual_inf_new;
1467 
1468 
1469   return 0;
1470 }
1471 
1472 c_int osqp_update_alpha(OSQPWorkspace *work, c_float alpha_new) {
1473 
1474   // Check if workspace has been initialized
1475   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1476 
1477   // Check that alpha is between 0 and 2
1478   if ((alpha_new <= 0.) || (alpha_new >= 2.)) {
1479 version(PRINTING){
1480     c_eprint("ERROR in %s: alpha must be between 0 and 2\n", __FUNCTION__.ptr);
1481 } /* ifdef PRINTING */
1482     return 1;
1483   }
1484 
1485   // Update alpha
1486   work.settings.alpha = alpha_new;
1487 
1488   return 0;
1489 }
1490 
1491 c_int osqp_update_warm_start(OSQPWorkspace *work, c_int warm_start_new) {
1492 
1493   // Check if workspace has been initialized
1494   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1495 
1496   // Check that warm_start is either 0 or 1
1497   if ((warm_start_new != 0) && (warm_start_new != 1)) {
1498 version(PRINTING){
1499     c_eprint("ERROR in %s: warm_start should be either 0 or 1\n", __FUNCTION__.ptr);
1500 } /* ifdef PRINTING */
1501     return 1;
1502   }
1503 
1504   // Update warm_start
1505   work.settings.warm_start = warm_start_new;
1506 
1507   return 0;
1508 }
1509 
1510 c_int osqp_update_scaled_termination(OSQPWorkspace *work, c_int scaled_termination_new) {
1511 
1512   // Check if workspace has been initialized
1513   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1514 
1515   // Check that scaled_termination is either 0 or 1
1516   if ((scaled_termination_new != 0) && (scaled_termination_new != 1)) {
1517 version(PRINTING){
1518     c_eprint("ERROR in %s: scaled_termination should be either 0 or 1\n", __FUNCTION__.ptr);
1519 } /* ifdef PRINTING */
1520     return 1;
1521   }
1522 
1523   // Update scaled_termination
1524   work.settings.scaled_termination = scaled_termination_new;
1525 
1526   return 0;
1527 }
1528 
1529 c_int osqp_update_check_termination(OSQPWorkspace *work, c_int check_termination_new) {
1530 
1531   // Check if workspace has been initialized
1532   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1533 
1534   // Check that check_termination is nonnegative
1535   if (check_termination_new < 0) {
1536 version(PRINTING){
1537     c_eprint("ERROR in %s: check_termination should be nonnegative\n", __FUNCTION__.ptr);
1538 } /* ifdef PRINTING */
1539     return 1;
1540   }
1541 
1542   // Update check_termination
1543   work.settings.check_termination = check_termination_new;
1544 
1545   return 0;
1546 }
1547 
1548 version(EMBEDDED){}
1549 else{
1550 
1551 c_int osqp_update_delta(OSQPWorkspace *work, c_float delta_new) {
1552 
1553   // Check if workspace has been initialized
1554   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1555 
1556   // Check that delta is positive
1557   if (delta_new <= 0.) {
1558 version(PRINTING){
1559     c_eprint("ERROR in %s: delta must be positive\n", __FUNCTION__.ptr);
1560 } /* ifdef PRINTING */
1561     return 1;
1562   }
1563 
1564   // Update delta
1565   work.settings.delta = delta_new;
1566 
1567   return 0;
1568 }
1569 
1570 c_int osqp_update_polish(OSQPWorkspace *work, c_int polish_new) {
1571 
1572   // Check if workspace has been initialized
1573   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1574 
1575   // Check that polish is either 0 or 1
1576   if ((polish_new != 0) && (polish_new != 1)) {
1577 version(PRINTING){
1578     c_eprint("ERROR in %s: polish should be either 0 or 1\n", __FUNCTION__.ptr);
1579 } /* ifdef PRINTING */
1580     return 1;
1581   }
1582 
1583   // Update polish
1584   work.settings.polish = polish_new;
1585 
1586 version(PROFILING){
1587 
1588   // Reset polish time to zero
1589   work.info.polish_time = 0.0;
1590 } /* ifdef PROFILING */
1591 
1592   return 0;
1593 }
1594 
1595 c_int osqp_update_polish_refine_iter(OSQPWorkspace *work, c_int polish_refine_iter_new) {
1596 
1597   // Check if workspace has been initialized
1598   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1599 
1600   // Check that polish_refine_iter is nonnegative
1601   if (polish_refine_iter_new < 0) {
1602 version(PRINTING){
1603     c_eprint("ERROR in %s: polish_refine_iter must be nonnegative\n", __FUNCTION__.ptr);
1604 } /* ifdef PRINTING */
1605     return 1;
1606   }
1607 
1608   // Update polish_refine_iter
1609   work.settings.polish_refine_iter = polish_refine_iter_new;
1610 
1611   return 0;
1612 }
1613 
1614 c_int osqp_update_verbose(OSQPWorkspace *work, c_int verbose_new) {
1615 
1616   // Check if workspace has been initialized
1617   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1618 
1619   // Check that verbose is either 0 or 1
1620   if ((verbose_new != 0) && (verbose_new != 1)) {
1621 
1622 version(PRINTING){
1623     c_eprint("ERROR in %s: verbose should be either 0 or 1\n", __FUNCTION__.ptr);
1624 } /* ifdef PRINTING */
1625     return 1;
1626   }
1627 
1628   // Update verbose
1629   work.settings.verbose = verbose_new;
1630 
1631   return 0;
1632 }
1633 
1634 } // EMBEDDED
1635 
1636 version(PROFILING){
1637 
1638 c_int osqp_update_time_limit(OSQPWorkspace *work, c_float time_limit_new) {
1639 
1640   // Check if workspace has been initialized
1641   if (!work) return osqp_error(cast(osqp_error_type)OSQP_WORKSPACE_NOT_INIT_ERROR, __FUNCTION__);
1642 
1643   // Check that time_limit is nonnegative
1644   if (time_limit_new < 0.) {
1645 version(PRINTING){
1646     c_print("time_limit must be nonnegative\n");
1647 } /* ifdef PRINTING */
1648     return 1;
1649   }
1650 
1651   // Update time_limit
1652   work.settings.time_limit = time_limit_new;
1653 
1654   return 0;
1655 }
1656 } /* ifdef PROFILING */