Δευτέρα, 10 Απριλίου 2017

Prime Numbers - Using C from M2000

Ένα πρόγραμμα με χρήση dll με κώδικα σε C που φτιάχνουμε άμεσα, και συνδέουμε μια συνάρτηση της Μ2000 με μια συνάρτηση της C. Παρέχουμε πίνακα σε Διάρθρωση (μνήμης) ή Buffer και λαμβάνουμε σε αυτόν τα αποτελέσματα. Για το παράδειγμα αυτό τα αποτελέσματα τα γράφουμε σε ένα αρχείο, καθαρίζουμε την διάρθρωση και την ξαναφορτώνουμε για να εμφανίσουμε τα αποτελέσματα (με αυτόν τον τρόπο σώνουμε και φορτώνουμε δυαδικά αρχεία).
Αν θέλουμε να αλλάξουμε το κώδικα της C ενώ έχει ήδη γίνει σύνδεση με το dll, έχω προγραμματίσει το F1 και από την κονσόλα δίνει πρώτα εντολή για να σώσουμε το κώδικα, μετά εκτελεί την εντολή Νέο (New) η οποία εκτός από το ότι σβήνει το κώδικα απελευθερώνει και όποια dll έχουμε φορτώσει, και αμέσως φορτώνει τον κώδικα. Την πρώτη φορά το πρόγραμμα θα δείξει τον κώδικα της C, αλλά κάθε επόμενη φορά που θα το τρέξουμε δεν θα την δείχνει, και για το λόγο αυτό χρησιμοποιούμε μια στατική μεταβλητή (η οποία την πρώτη μόνο φορά παίρνει τιμή 0).


Ο χρωματισμός της c έγινε αυτόματα βάσει του χρωματισμού κώδικα της Μ2000, επειδή υπάρχουν κοινά ονόματα με τη C καθώς και οι αγκύλες. Ο κώδικας μπήκε σε μια συνάρτηση μόνο και μόνο για να φαίνεται χρωματισμένος. Το κώδικα τον αντιγράφουμε από την συνάρτηση χρησιμοποιόντας την αναφορά για τη συνάρτηση (οι αναφορές είναι αλφαριθμητικά στην Μ2000) και ειδικά στις συναρτήσεις είναι ο κώδικας της συνάρτησης σε αγκύλες. Πχ το "{=100}" είναι αλφαρθμητικό που αν είναι στο σωρό τιμών μια Διάβασε &Α() θα σχηματίσει την συνάρτηση Α{ = 100} και το Α() θα γυρνάει το 100. Οι συναρτήσεις των ομάδων γράφουν στο τέλος και την ισχνή αναφορά της ομάδας οπότε γνωρίζει ο διερμηνευτής ότι η αναφορά συνάρτησης είναι αναφορά από συγκεκριμένη ομάδα. Αυτά γίνονται γιατί ο κώδικας της Μ2000 δεν γίνεται γλώσσα μηχανής για να έχουμε την διεύθυνση μιας συνάρτησης (και έτσι η αναφορά της να ήταν απλά η διεύθυνσή της).
Για την σύνδεση με το dll, η Μ2000 φτιάχνει αντικείμενο και μια συνάρτηση που καλεί αυτό το αντικείμενο και του παρέχει το σωρό τιμών. Το αντικείμενο από την Όρισε (ή Declare εδώ) γνωρίζει τι θα σηκώσει από τον σωρό τιμών, και κατόπιν αφού πάρει τις τιμές καλεί τη συνάρτηση. Εδώ η συνάρτηση στη C διαβάζει έναν πίνακα, δηλαδή ένα δείκτη σε πίνακα, τον οποίο μας παρέχει η διάρθρωση (το Α(0) είναι η διεύθυνση του πρώτου στοιχείου, το Α(1) του δεύτερου, υπολογίζοντας το πλάτος της δομής, εδώ έχουμε απλή δομή, τον μακρύ ή Long, με 4 Bytes). Διαβάζουμε από τη διάρθρωση τον μακρύ ή Long  και άμεσα γίνεται double ως αριθμός χωρίς πρόσημο. Έτσι αν είχαμε Ψηφία (Bytes) θα είχαμε τιμές από 0 έως 255. Υπάρχει η συνάρτηση Δυαδικό.Ακέραιο() όπου παίρνει ως όρισμα έναν Long με πρόσημο και δίνει έναν unsign, χωρίς πρόσημο, με ίδια ακριβώς bits.Ομοίως η Ακέραιο.Δυαδικό() παίρνει όρισμα έναν unsign και δίνει έναν με πρόσημο με ίδια ακριβώς bits. Με αυτές τις δυο συναρτήσεις μπορούμε να βάζουμε τιμές με πρόσημα (επειδή εμείς λογαριάζουμε ότι είναι τιμές με πρόσημο). Στο κώδικα της C όντως οι τιμές του πίνακα φαίνονται ως τιμές με πρόσημο, επειδή ο πίνακας είναι τέτοιος. Στις διαρθρώσεις μπορούμε να διαβάσουμε όπως θέλουμε, ξεφεύγοντας από τη δομή αρκεί να δώσουμε το σημείο που θέλουμε και το τρόπο που θα το διαβάσουμε.

Στο VirtualBox Windows 7 που χρησιμοποιώ έβγαλα 664579 πρώτους (γράφτηκαν σε αρχείο) σε 386,548 χιλιοστά του δευτερολέπτου, ούτε μισό δευτερόλεπτο, δίνοντας όριο τα 10 εκατομμύρια.
Αν δεν δώσουμε όριο αυτόματα μπαίνει το 100 χιλιάδες.
Η εντολή Read ή Διάβασε δεν διαβάζει από το πληκτρολόγιο αλλά από το σωρό τιμών. Αν το παρακάτω το γράψουμε σε ένα τμήμα Α τότε το Α 100 σημαίνει ότι θα κληθεί η Α αφού περαστεί στο σωρό τιμών το 100 και η Διάβασε ? Ν αν δει ότι υπάρχει αριθμός τότε θα τον δώσει στον Ν αλλιώς τον αφήνει ως έχει. Αν δει όμως κάτι άλλο θα βγάλει λάθος. Είναι θέμα του προγραμματιστεί να διατηρεί τον σωρό σωστό! Αυτός ο σωρός δεν έχει να κάνει με το πως επιστρέφουμε από τμήματα. Οι συναρτήσεις έχουν δικό τους σωρό, οπότε ο γενικός σωρός πάει μόνο στα τμήματα που καλούμε από την γραμμή εντολών, σε όποιο βάθος κλήσεων (μεταβιβάζεται και σε τμήματα που καλούμε από τμήματα) και η χρησιμότητά του είναι για να δίνουμε και να παίρνουμε τιμές. Οι συναρτήσεις δεν γυρνούν κάτι στο σωρό γιατί ο σωρός τους χάνεται. Όμως αν καλέσουν τμήματα τότε μεταβιβάζουν τον σωρό τους. Μια χρησιμότητα του σωρού είναι ότι "σηκώνουμε" τιμές όχι άμεσα με την εκκίνηση του τμήματος ή της συνάρτησης αλλά μπορούμε να επεξεργαστούμε το σωρό και ανάλογα να πράξουμε. Σε άλλες γλώσσες υποχρεωτικά θα δηλώσουμε που θα πάνε οι τιμές που παρέχουμε σε μια συνάρτηση κατά την εκκίνηση αυτής. Αυτό συμβαίνει στην Μ2000 όταν καλούμε εξωτερικές συναρτήσεις, όπως εδώ με την συνάρτηση σε C. Στο σωρό τιμών βάζουμε οτιδήποτε εκτός από τμήματα! Βάζουμε με αναφορά τιμές (αφήνουμε ισχνές αναφορές) αλλά βάζουμε και αντικείμενα. Ακόμα και η διάρθρωση (ή Buffer) μπορεί να μπει στο σωρό γιατί είναι αντικείμενο παρόλο που συνδέεται με περιοχή μνήμης. Θα μπορούσε μια συνάρτηση να γυρίσει την διάρθρωση. Η Μ2000 με την πρώτη εκχώρηση δημιουργεί τον τύπο του αναγνωριστικού, εκτός από περιπτώσεις που δηλώνονται χωριστά όπως το Έγγραφο (Document) το οποίο ως επιστροφή έχει αλφαριθμητικό (μόνο εντολές για Έγγραφα βλέπουν ότι είναι αντικείμενο, ενώ πολλές εντολές όπως και σε εκφράσεις φαίνεται ως αλφαριθμητικό). Επειδή ο διερμηνευτής και το περιβάλλον της Μ2000 έχει γραφτεί σε Visual Basic 6 τα αντικείμενά της παρέχουν τον τύπο και άλλες πληροφορίες, εκτός από το ίδιο πρόγραμμα και σε εξωτερικά χρησιμοποιόντας ένα σύστημα που έχουν τα Windows, το λεγόμενο iDispatch Interface. Εκτός από τη C που καλούμε με διαρθρώσεις, μπορούμε να καλέσουμε VBScript και να περνάμε πίνακες, και να καλέσουμε Javascript και να περνάμε αντικείμενα - π.χ. μπορούμε να περάσουμε ένα αντικείμενο Κατάσταση και να το χειριστούμε από την Javascript, με χρήση του αναφερθέντος Interface. Η Μ2000 δεν έχει την ταχύτητα της C αλλά δεν έχει πρόβλημα να την χρησιμοποιήσει! Το ίδιο κάνουν και άλλες γλώσσες.


Στην αναθεώρηση 2 του προγράμματος μπήκαν τα Put και Get τα οποία στέλνουν ή παίρνουν έναν Buffer σε ένα αρχείο, ως έχει σε στοιχεία. Το Input$() που χρησιμοποιήθηκε στη προηγούμενη έκδοση, δεν μπορεί να χρησιμοποιηθεί πια για δυαδικά δεδομένα, παρά μόνο για UTF16LE και για το σκοπό αυτό γίνεται έλεγχος αν τα δεδομένα είναι με αυτήν τη κωδικοποίηση και αν δεν είναι βγαίνει λάθος. Αυτό είχε συμβεί με την προηγούμενη έκδοση. Τώρα έχουν αντικατασταθεί οι εντολές για να τρέχει το πρόγραμμα


\\ Revision 2

Static DisplayOnce=0
N=100000
Read ? N
Form 60
Pen 14
Background { Cls 5}
Cls 5
\\ use f1 do unload lib - because only New statemend unload it
FKEY 1,"save ctst1:new:load ctst1"
\\ We use a function as string container, because c code can easy color decorated in M2000.
Function ccode {
      long primes(long a[], long b)
      {          
            long k=2;
            long k2,d=2, l, i;
            k2=k*k;
            if (b>2)
            {
                  if (k2<b)
                  {
                      do {
                              for (l=k2; l<=b; l+=k)
                                    a[l]--;
                              k++;
                              while (a[k])
                                    k++;
                              k2=k*k;
                        } while (k2<=b);
                  }
                   for (i=2;i<=b;i++)
                   {
                        if (a[i]==0)
                        {
                              a[d]=i ; d++ ;
                        }
                   }
            }
            else {
                        if (b>1)
                           {
                                if (b>2)
                                 {
                                       d=2; a[0]=2; a[1]=3 ;
                                 }
                                 else {
                                       d=1; a[0]=2;
                                 }
                              }    
            }
            a[b+1]=d;
            return 0;
      }
}
\\ extract code. &functionname() is a string with the code inside "{ }"
\\ a reference to function actual is code of function in m2000
\\ using Document object we have an easy way to drop paragraphs
document code$=Mid$(&ccode(), 2, len(&ccode())-2)
\\ remove 1st line two times \\ one line for an edit information from interpreter
\\ paragraph$(code$, 1) export paragraph 1st,using  third parameter -1 means delete after export.
drop$=paragraph$(code$,1,-1)+paragraph$(code$,1,-1)
If DisplayOnce Else {
      Report 2, "c code for primes"
      Report code$ \\ report stop after 3/4 of screen lines use. Press spacebar or mouse button to continue
      DisplayOnce++
}
If not exist("c:\MyName.dll") then {
      Report 2, "Now we have to make a dll"
      Rem : Load Make \\ we can use a Make.gsb in current folder - this is the user folder for now
      Module MAKE ( fname$, code$, timeout ) {
            if timeout<1000 then timeout=1000
            If left$(fname$,2)="My" Else Error "Not proper name - use 'My' as first two letters"
            Dos "del  c:\"+fname$+".*", timeout;
            Open "c:\"+fname$+".c" for output as F \\ use of non unicode output
            Print #F, code$
            Close #F
            \\ use these two lines for opening dos console and return to M2000 command line
            rem : Dos "cd c:\ && gcc -c -DBUILD_DLL "+fname$+".c"
            rem : Error "Check for errors"
            \\ by default we give a time to process dos command and then continue
            dos "cd c:\ && gcc -c -DBUILD_DLL "+fname$+".c" , timeout;
            if exist("c:\"+fname$+".o") then {
                  dos "cd c:\ && gcc -shared -o "+fname$+".dll "+fname$+".o -Wl,--out-implib,libmessage.a", timeout;
            } else Error "No object file - Error"
            if not exist("c:\"+fname$+".dll") then Error "No dll - Error"
      }
      Make "MyName", code$, 1000
}
Declare primes lib c "c:\MyName.primes" {long c, long d} \\ c after lib mean CDecl call
\\ So now we can check error
\\ make a Buffer (add two more longs for any purpose)
Buffer Clear A as Long*(N+2) \\ so A(0) is base address, of an array of 1002 long (unsign for M2000).
\\ profiler enable a timecount
profiler
Call primes(A(0), N)
m=timecount
total=Eval(A,N+1)
\\ redim buffer preserving data
Buffer A as Long*total
\\ movw to 0 or A(0)
Return A, 0:=Eval$(A, 2,total*4-2*4)
\\ resize A, preserve data
Buffer A as Long*(total-2)
primesdat$="primes1.dat"
Open primesdat$ for wide output as f
      ' put data to f
      Put #f, A,1
Close #f
\\ So now we read from file
total=filelen(primesdat$)/4
\\ now we clear the buffer
Buffer Clear A as Long*total
Open primesdat$ for Input as f
      Get #F, A,1
Close #f
Clear Yes, No
Print "Press Y or N to display or not the primes"
Repeat {
      Yes=keypress(89) : No=Keypress(78)
      wait 10
} Until Yes or No
If Yes then {
      Form 80,50
      Refresh
      For i=0 to total-1
            Print Eval(A,i),
      next i
      Print
}
Print format$("Compute {0} primes in range 1 to {1}, in msec:{2:3}", total, N, m)