Active object: Difference between revisions

From Rosetta Code
Content added Content deleted
(C)
(→‎{{header|C}}: result with higher sampling frequency)
Line 186: Line 186:
double sint;
double sint;
integrator = create_integrator(10);
integrator = create_integrator(10.0);
integrator_setinput(integrator, func1);
integrator_setinput(integrator, func1);
sleep(2);
sleep(2);
Line 204: Line 204:
S = -0.015451
S = -0.015451
</pre>
</pre>

By increasing the ''precision'' of the integrator by increasing the "sampling frequency" you can take a better result; e.g. with <tt>create_integrator(100.0)</tt> I've obtained S = -0.000157.


=={{header|E}}==
=={{header|E}}==

Revision as of 22:54, 17 December 2008

Task
Active object
You are encouraged to solve this task according to the task description, using any language you may know.

In object-oriented programming an object is active when its state depends on clock. Usually an active object encapsulates a task that updates the object's state. To the outer world the object looks like a normal object with methods that can be called from outside. Implementation of such methods must have a certain synchronization mechanism with the encapsulated task in order to prevent object's state corruption.

A typical instance of an active object is an animation widget. The widget state changes with the time, while as an object it has all properties of a normal widget.

The task

Implement an active integrator object. The object has an input and output. The input can be set using the method Input. The input is a function of time. The output can be queried using the method Output. The object integrates its input over the time and the result becomes the object's output. So if the input is K(t) and the output is S, the object state S is changed to S + (K(t1) + K(t0)) * (t1 - t0) / 2, i.e. it integrates K using the trapeze method. Initially K is constant 0 and S is 0.

In order to test the object:

  1. set its input to sin (2pi f t), where the frequency f=0.5Hz. The phase is irrelevant.
  2. wait 2s
  3. set the input to constant 0
  4. wait 0.5s

Verify that now the object's output is approximately 0s (the sine has the period of 2s). The accuracy of the result will depend on the OS scheduler time slicing and the accuracy of the clock.

Ada

<ada> with Ada.Calendar; use Ada.Calendar; with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; with Ada.Text_IO; use Ada.Text_IO;

procedure Test_Integrator is

  type Func is access function (T : Time) return Float;
  function Zero (T : Time) return Float is
  begin
     return 0.0;
  end Zero;
  Epoch : constant Time := Clock;
  function Sine (T : Time) return Float is
  begin
     return Sin (Pi * Float (T - Epoch));
  end Sine;
  task type Integrator is
     entry Input  (Value : Func);
     entry Output (Value : out Float);
     entry Shut_Down;
  end Integrator;

  task body Integrator is
     K  : Func  := Zero'Access;
     S  : Float := 0.0;
     F0 : Float := 0.0;
     F1 : Float;
     T0 : Time  := Clock;
     T1 : Time;
  begin
     loop
        select
           accept Input (Value : Func) do
              K := Value;
           end Input;
        or accept Output (Value : out Float) do
              Value := S;
           end Output;
        or accept Shut_Down;
           exit;
        else
           T1 := Clock;
           F1 := K (T1);
           S  := S + 0.5 * (F1 + F0) * Float (T1 - T0);
           T0 := T1;
           F0 := F1;
        end select;
     end loop;
  end Integrator;

  I : Integrator;
  S : Float;

begin

  I.Input (Sine'Access);
  delay 2.0;
  I.Input (Zero'Access);
  delay 0.5;
  I.Output (S);
  Put_Line ("Integrated" & Float'Image (S) & "s");
  I.Shut_Down;

end Test_Integrator; </ada> Sample output:

Integrated-5.34100E-05s

C

<c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <unistd.h>
  3. include <math.h>
  4. include <pthread.h>

double nullfunc(double t) {

 return 0.0;

}

double func1(double t) {

  return sin(2.0*M_PI*0.5*t);

}

typedef double (*objfunc)(double); struct objhandle_s {

   int terminate;
   pthread_mutex_t access_S;
   pthread_mutex_t access_I;
   pthread_mutex_t access_t;
   objfunc I;
   pthread_t th;
   double S;
   double t;
   double freq;

}; typedef struct objhandle_s objhandle_t; typedef struct objhandle_s *objhandle;

void *the_integrator(void *n) {

   objhandle oh = (objhandle)n;
   double ts = 1.0/oh->freq;
   while(oh->terminate == 0)
   {
     usleep((useconds_t)(ts*1000000.0));
     pthread_mutex_lock(&oh->access_S);
     pthread_mutex_lock(&oh->access_t);
     pthread_mutex_lock(&oh->access_I);
     oh->S += ( oh->I(oh->t+ts) - oh->I(oh->t) )*ts/2.0;
     oh->t += ts;
     pthread_mutex_unlock(&oh->access_I);
     pthread_mutex_unlock(&oh->access_t);
     pthread_mutex_unlock(&oh->access_S);
   }

}

void destroy_integrator(objhandle integrator) {

  integrator->terminate = 1;
  pthread_join(integrator->th, NULL);
  free(integrator);

}

double integrator_getsum(objhandle integrator) {

  return integrator->S;

}

void integrator_setinput(objhandle integrator, objfunc nif) {

  pthread_mutex_lock(&integrator->access_I);
  integrator->I = nif;
  pthread_mutex_unlock(&integrator->access_I);

}

objhandle create_integrator(double freq) {

   objhandle r;
   
   r = malloc(sizeof(objhandle_t));
   if ( r != NULL )
   {
      pthread_mutex_init(&r->access_S, NULL);
      pthread_mutex_init(&r->access_I, NULL);
      pthread_mutex_init(&r->access_t, NULL);
      r->terminate = 0;
      r->t = 0.0;
      r->S = 0.0;
      r->I = nullfunc;
      r->freq = freq;
      pthread_create(&r->th, NULL, the_integrator, (void *)r);
   }
   return r;

}


int main() {

  objhandle integrator;
  double sint;
  
  integrator = create_integrator(10.0);
  integrator_setinput(integrator, func1);
  sleep(2);
  integrator_setinput(integrator, nullfunc);
  usleep(500000);
  sint = integrator_getsum(integrator);
  printf("S = %lf\n", sint);
  destroy_integrator(integrator);
  return 0;

}</c>

Mutex for S and t (time) exists in case one wants to implement functions to reset/set S (sum) and time (t) i.e. to reset or set the integrator to a particular state.

Output:

S = -0.015451

By increasing the precision of the integrator by increasing the "sampling frequency" you can take a better result; e.g. with create_integrator(100.0) I've obtained S = -0.000157.

E

def makeIntegrator() {
    var value := 0.0
    var input := fn { 0.0 }
    
    var input1 := input()
    var t1 := timer.now()
    
    def update() {
        def t2 := timer.now()
        def input2 :float64 := input()
        def dt := (t2 - t1) / 1000
        
        value += (input1 + input2) * dt / 2
        
        t1 := t2
        input1 := input2
    }
    
    var task() {
        update <- ()
        task <- ()
    }
    task()
    
    def integrator {
        to input(new) :void  { input := new }
        to output() :float64 { return value }
        to shutdown()        { task := fn {} }
    }
    return integrator
}
def test() {
    def result
    
    def pi := (-1.0).acos()
    def freq := pi / 1000
    
    def base := timer.now()
    def i := makeIntegrator()
    
    i.input(fn { (freq * timer.now()).sin() })
    timer.whenPast(base + 2000, fn {
        i.input(fn {0})
    })
    timer.whenPast(base + 2500, fn {
        bind result := i.output()
        i.shutdown()
    })
    return result
}