/*NNPRNGRP.LIB*/
/*NPRNGRPH.LIB with modified savegraph() and getgraph()*/
/*new getgraph() able to get only savefile saved with new savegraph()*/
/*NPRNGRPH.LIB*/
/*PRNGRPH.LIB with modified run_initgrph() and run_line_parameters() including
print option of the parameters*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <alloc.h>
/*Codes*/
extern char IBMSpacing[10], Epsonspacing[9], spacing0[4], spacing1[5],
spacing2[5], spacing3[4], spacing4[4], Head[12]; extern int *Header;
extern char *spacing[5];
/*General Variables*/
extern char *Printline; extern char *Pline; extern int *Pcolumn;
extern unsigned char *Charac; extern unsigned long Pointcounter;
extern unsigned long Maxpoint; extern long lastcolumn; extern int ac;
extern int closeflag;
/*Graph Parameters*/
extern int Lines, Width, Pc, DDc; extern char *Graphpar;
/*Line Parameters*/
extern int full, doble; extern double maxX, maxY, minX, minY;
extern int Ptobell;
extern FILE *savefile;
/*Prototypes*/
void line_parameters(int full_line, int double_line, double max_X,
double min_X, double max_Y, double min_Y, int point_to_bell_ratio);
void initprngraph(unsigned lines, unsigned columns, unsigned margin,
int density);
void savegraph(char *filename);
void getgraph(char *filename);
void closegraph(void);
int redata(void);
void run_initgraph();
void run_line_parameters();
int Potencia(int basis, int exponent);
void printgraph(void);
void recpoint(double x, double y);
void newline(void);
void axis(double x, double y, double Xmark, double Ymark, int Type);
void initprngraph(unsigned lines, unsigned columns, unsigned margin, int density)
{
unsigned intmaxpoint;
/*Cancel previous initiation*/
if(closeflag != 0 ) closegraph();
if(lines > 80) {fputs("Lines has to be a positive integer less than 81\n",stdout);
_exit(0);}
if(columns>80) {fputs("Columns has to be a positive integer less than 81\n",stdout);
_exit(0);}
if(margin>80-columns) {fputs("Margin has to be a positive integer less than (81 - columns)\n",stdout);
_exit(0);}
if(density!=1&&density!=2&&density!=4) {fputs("Density = 1, 2 or 4 are the only allowed values\n",stdout);
_exit(0);}
Lines = (int) lines;
Width = (int) columns;
Pc = (int) margin;
DDc = density;
Printline = calloc(Width*6*DDc, 1);
if(Printline == NULL) {fputs("Insufficient Data Memory",stdout);_exit(0);}
intmaxpoint = (coreleft() - 1024)/4*4;
Pline = calloc( intmaxpoint, 1);
if(Pline == NULL) {fputs("Insufficient Data Memory",stdout);_exit(0);}
Pcolumn = (int *) (Pline + ( intmaxpoint/=4));
Charac = Pline + 3 * intmaxpoint;
Maxpoint = (long) intmaxpoint;
closeflag = 1;/*Allocated memory*/
}
void line_parameters(int full_line, int double_line, double max_X, double min_X,
double max_Y, double min_Y, int point_to_bell_ratio)
{
if(full_line!=0&&full_line!=1&&full_line!=2){
fputs("full_line = 0, 1 or 2 are the only allowed values\n",stdout); _exit(0);}
if(double_line!=0&&double_line!=1&&double_line!=2){
fputs("double_line = 0, 1 or 2 are the only allowed values\n",stdout); _exit(0);}
if(max_X<=min_X){
fputs("min_X < max_X is expected\n",stdout); _exit(0);}
/*1*/ if(min_Y == 0)minY = max_Y-(max_X-min_X)*Lines/Width*8.0/7.2;
else
/*2*/ {
/*3*/ if(max_Y<=min_Y){
/*3*/ fputs("min_Y < max_y is expected\n",stdout); _exit(0);}
/*2*/ else minY = min_Y;}
/*1*/
if(point_to_bell_ratio<0){
fputs("min_Y < a non-negative value is expected for point_to_bell_ratio\n",stdout);
_exit(0);}
full = full_line;
doble = double_line;
maxX = max_X;
minX = min_X;
maxY = max_Y;
}
void savegraph(char *filename){unsigned long i; int j;
savefile = fopen(filename, "wb");
if(savefile == NULL){fputs("I can't open the file",stdout); _exit(0);}
/*Save Graph Parameters*/
for(j=0;j<=8;j++) putc(*(Graphpar+j), savefile);
/*Save Graph Points*/
for(i=0;i<=4*Pointcounter;i++) putc(*(Pline+i),savefile);
fclose(savefile);
}
void getgraph(char *filename){unsigned long i; char a;
savefile = fopen(filename, "rb");
if(savefile == NULL){fputs("Unable to open the file\n",stdout);
_exit(0);}
Pointcounter = filelength(fileno(savefile))/4 - 2;
/*Read Graph Parameters*/
for(i=0;i<=8;i++){ *(Graphpar+i) = getc(savefile);}
Printline = calloc(Width*6*DDc, 1);
Pline = (char *) calloc(4*(int) Pointcounter, 1);
Pcolumn = (int *) (Pline +(int) Pointcounter);
Charac = Pline + 3*(int)Pointcounter;
for(i=0;i<=4*Pointcounter;i++) *(Pline + i) = getc(savefile);
closeflag = 1;/*Allocated memory*/
fclose(savefile);}
void closegraph(void){
free((void *) Pline);
free((void *) Printline);
lastcolumn = -1; Pointcounter = 0; closeflag = 0;}
int redata(void){void *psave;
psave = (void *) (Pline + (int) Pointcounter);
memcpy(psave,(void *) Pcolumn, 2* (int) Pointcounter);
psave=(void *)(Pline + 3* (int) Pointcounter);
memcpy(psave,(void *) Charac, (int) Pointcounter);
Pcolumn = (int *) (Pline + (int) Pointcounter);
Charac = Pline + 3* (int) Pointcounter;
free((void *) Pline);
if(malloc(4* (int) Pointcounter)!=Pline){
fputs("Allocation of permanent memory is forbiden between initprngraph and redata",stdout);_exit(0);}
closeflag = 1; }
void run_initgraph(){
char buf[8]; unsigned intmaxpoint; int saveDDc;
/*Cancels previous initiation*/
if(closeflag!=0) closegraph();
fputs("Graph Parameters\n",stdout);
fputs("<ENTER> to confirm present value\n",stdout);
fputs("<r> <ENTER> to return to the anterior parameter\n",stdout);
fputs("Lines = 1 to EXIT\n\n",stdout);
acx: fputs("Lines (Graphic's height - 1 Line = 1/9\") = ",stdout);
fputs(itoa(Lines,buf,10),stdout); fputs("\n",stdout);
fputs("Lines = ",stdout);if(*gets(buf)=='r')goto acx;
if(buf[0] != '')Lines = atoi(buf);if(Lines==1) _exit(0);
if(Lines>90 || Lines <=0) {fputs("It has to be a positive integer <= 90 \n",stdout);
goto acx;}
ac1: fputs("Width (Graphic's width in 1/10\") = ",stdout);
fputs(itoa(Width,buf,10),stdout); fputs("\n",stdout);
fputs("Width = ",stdout);if(*gets(buf)=='r')goto acx;
if(buf[0] != '')Width = atoi(buf);
if(Width>80 || Width<=0) {fputs("It has to be a positive integer <= 80 \n",stdout);
goto ac1;}
ac2: fputs("Margin (Graphic's left margin in 1/10\") = ",stdout);
fputs(itoa((Pc=(80-Width)/2),buf,10),stdout);fputs("\n",stdout);
fputs("Margin = ",stdout);if(*gets(buf)=='r')goto ac1;
if(buf[0] != '')Pc = atoi(buf);
if(Pc>80-Width||Pc<0) {fputs("It has to be a non-negative integer <= 80 - Width\n",stdout);
goto ac2;}
ac7: fputs("Density (number of points per surface's unit) (1,2 or 4) = ",stdout);
fputs(itoa(DDc,buf,10),stdout);fputs("\n",stdout);
fputs("Density = ",stdout);if(*gets(buf)=='r')goto ac2;
if(buf[0] != '')DDc = atoi(buf);fputs("\n",stdout);
if(DDc!=1&&DDc!=2&&DDc!=4) {fputs("1,2 and 4 are the only allowed values\n",stdout);goto ac7;}
/*Print Graph Parameters*/
fputs("\nLines = ",stdout); fputs(itoa(Lines,buf,10), stdout);
fputs(" Width = ",stdout); fputs(itoa(Width,buf,10), stdout);
fputs(" Margin = ",stdout); fputs(itoa(Pc,buf,10), stdout);
fputs(" Density = ",stdout); fputs(itoa(DDc,buf,10), stdout); fputs("\n",stdout);
Okey:
fputs("Okey?(y/n)\n",stdout); gets(buf); if(buf[0]=='n') goto acx;
if(buf[0]!='y') {fputs("y or n are tke unique accepted letters\n",stdout); goto Okey;}
Do:
fputs("\nShould I print these graph parameters? (y/n)",stdout);gets(buf);
if(buf[0]=='n') goto np1;
if(buf[0]!='y') {fputs("y or n are tke unique accepted letters\n",stdout); goto Do;}
fputs("Lines = ",stdprn); fputs(itoa(Lines,buf,10), stdprn);
fputs(" Width = ",stdprn); fputs(itoa(Width,buf,10), stdprn);
fputs(" Margin = ",stdprn); fputs(itoa(Pc,buf,10), stdprn);
fputs(" Density = ",stdprn); fputs(itoa(DDc,buf,10), stdprn); fputs("\n",stdprn);
np1:
/*Initgraph*/
Printline = calloc(Width*6*DDc, 1);
if(Printline == NULL) {fputs("Insufficient Data Memory",stdout);_exit(0);}
intmaxpoint = (coreleft() - 1024)/4*4;
Pline = calloc( intmaxpoint, 1);
if(Pline == NULL) {fputs("Insufficient Data Memory",stdout);_exit(0);}
Pcolumn = (int *) (Pline + ( intmaxpoint/=4));
Charac = Pline + 3 * intmaxpoint;
Maxpoint = (long) intmaxpoint;
closeflag = 1;/*Allocated memory*/
}
void run_line_parameters(){ char buf[8]; double save;
fputs("<ENTER> to confirm present value\n",stdout);
fputs("<r> <ENTER> to return to the anterior parameter\n\n",stdout);
goto ac0;
acc0: closegraph(); run_initgraph();
ac0: fputs("Maximum X (Value of X at the extreme right) = ",stdout);
fputs(gcvt(maxX,6,buf),stdout); fputs("\n",stdout);
fputs("Maximum X = ",stdout);
if(*gets(buf)=='r') goto acc0;
if(buf[0] != '')maxX = atof(buf);
ac4: fputs("Minimum X (Value of X at the extreme left) = ",stdout);
fputs(gcvt(minX,6,buf),stdout); fputs("\n",stdout);
fputs("Minimum X = ",stdout);if(*gets(buf)=='r')goto ac0;
if(buf[0] != '')minX = atof(buf);
if(minX>=maxX){fputs("A value smaller than Maximum X is expected\n",stdout);
goto ac4;}
ac5: fputs("Maximum Y (Value of Y at the top) = ",stdout);
fputs(gcvt(maxY,6,buf),stdout); fputs("\n",stdout);
fputs("Maximum Y = ",stdout);if(*gets(buf)=='r')goto ac4;
if(buf[0] != '') maxY = atof(buf);
ac6: fputs("Minimum Y (Value of Y at the bottom) = ",stdout);
minY = maxY-(maxX-minX)*Lines/Width*8.0/7.2;
fputs(gcvt(minY,6,buf),stdout); fputs("\n",stdout);
fputs("This value of Minimum Y corresponds to Y/X = 1\n",stdout);
fputs("Minimum Y = ",stdout);if(*gets(buf)=='r')goto ac5;
if(buf[0] != '') {save = minY; minY = atof(buf);}
if(minY>=maxY){fputs("A value smaller than Maximum Y is expected\n",stdout);
minY = save; goto ac6;}
ac8: fputs("Full line (Consecutive points joined by a straight line - 0, 1 or 2) = ",stdout);
fputs(itoa(full,buf,10),stdout);fputs("\n",stdout);
fputs("Full line = ",stdout);if(*gets(buf)=='r')goto ac6;
if(buf[0] != '')full = atoi(buf);
if(full!=1&&full!=0&&full!=2) {fputs("0, 1 and 2 are the only allowed values\n",stdout);
goto ac8;}
ac9: fputs("Line Width (0, 1 or 2) = ",stdout);
fputs(itoa(doble,buf,10),stdout);fputs("\n",stdout);
fputs("Line Width = ",stdout);if(*gets(buf)=='r')goto ac8;
if(buf[0] != '')doble = atoi(buf);
if(doble!=1&&doble!=0&&doble!=2) {fputs("0,1 and 2 are the only allowed values\n",stdout);
goto ac9;}
ac10: fputs("Ptobell (One bell for each group of Ptobell out-of-graph points) = ",stdout);
fputs(itoa(Ptobell,buf,10),stdout);fputs("\n",stdout);
fputs("0 for no bell\n",stdout);
fputs("Ptobell = ",stdout);if(*gets(buf)=='r')goto ac9;
if(buf[0] != '') Ptobell = atoi(buf);
/*Print Line Parameters*/
fputs("\nfull = ",stdout); fputs(itoa(full,buf,10), stdout);
fputs(" double = ",stdout); fputs(itoa(doble,buf,10), stdout);
fputs(" maxX = ",stdout); fputs(gcvt(maxX,10,buf), stdout);
fputs(" minX = ",stdout); fputs(gcvt(minX,10,buf), stdout); fputs("\n",stdout);
fputs("maxY = ",stdout); fputs(gcvt(maxY,10,buf), stdout);
fputs(" minY = ",stdout); fputs(gcvt(minY,10,buf), stdout);
fputs(" Ptobell = ",stdout); fputs(itoa(Ptobell,buf,10), stdout); fputs("\n",stdout);
Okey:
fputs("Okey?(y/n)\n",stdout); gets(buf); if(buf[0]=='n') goto ac0;
if(buf[0]!='y') {fputs("y or n are tke unique accepted letters\n",stdout); goto Okey;}
Do:
fputs("\nShould I print these line parameters? (y/n)",stdout);gets(buf);
if(buf[0]=='n') goto np2;
if(buf[0]!='y') {fputs("y or n are tke unique accepted letters\n",stdout); goto Do;}
fputs("\nfull = ",stdprn); fputs(itoa(full,buf,10), stdprn);
fputs(" double = ",stdprn); fputs(itoa(doble,buf,10), stdprn);
fputs(" maxX = ",stdprn); fputs(gcvt(maxX,10,buf), stdprn);
fputs(" minX = ",stdprn); fputs(gcvt(minX,10,buf), stdprn); fputs("\n",stdprn);
fputs("maxY = ",stdprn); fputs(gcvt(maxY,10,buf), stdprn);
fputs(" minY = ",stdprn); fputs(gcvt(minY,10,buf), stdprn);
fputs(" Ptobell = ",stdprn); fputs(itoa(Ptobell,buf,10), stdprn); fputs("\n",stdprn);
np2:;
}
int Potencia(int basis, int exponent){int j; int z=1;
for(j=1;j<=exponent;j++) z*=basis; return z;}
void printgraph(void){int i,j,k;int WIDTHH, PCC;
/* fputs (Epsonspacing,stdprn);*/
putc('\xD',stdprn);
WIDTHH =Width*6*DDc;
PCC = Pc*6;
for(k=Lines*(DDc - DDc/4)-1;k>=0;k--){
for(i=0;i<Pointcounter;i++)
{
if(*(Pline + i) == k){
*(Printline + *(Pcolumn+i)) |= *(Charac + i);
if(*(Printline + *(Pcolumn+i)) == '\x1A')
*(Printline + *(Pcolumn+i))-=2;
}
}
if(k%(DDc - DDc/4)!=0)fputs(spacing[DDc-1],stdprn);
else fputs(spacing[DDc - 1/DDc],stdprn);
/*Pass over the margin*/
Header[1] = Pc*6;
for(i=0;i<4;i++) putc(Head[i],stdprn);
for(i=0;i<PCC;i++) putc(0,stdprn);
/*Print the line*/
*(Header+(2+DDc/2*4)/2) = WIDTHH;
for(i=0;i<4;i++) putc(Head[i+DDc/2*4],stdprn);
for(i=0;i<WIDTHH;i++) {
putc(*(Printline + i),stdprn);
*(Printline + i) = 0;
}
putc('\xA',stdprn); putc('\xD',stdprn);
}
putc('\x1B',stdprn); putc('\x40',stdprn);
}
void recpoint(double x, double y){long columna=0, line=0; int subline=0, z=0;
long newcolumn=0, Tl=0;char Flag = 0;
long maxdif, mindif, absmaxdif, newTl, dx, dy, j; int i;
long *L1; long *L2; extern unsigned long Maxpoint;
static long lastTl;
int DDc4, DDc8, DOBLEE, DOBLEL, FULLE, colDOBLEE; double MAXXMINX;
MAXXMINX = (maxX-minX)/(Width*6*DDc-1);
DDc4 = DDc - DDc/4;
if(x>maxX || x<minX ) {lastcolumn = -1;
if(Ptobell!=0 && ac++%Ptobell == 0) putc(7,stdout);}
else
/*-2*/ {if(y>maxY || y<minY) {lastcolumn = -1;if(Ptobell!=0 && ac++%Ptobell == 0) putc(7,stdout);}
else
/*-1*/ {
x-=minX; y-=minY;
columna = (long)(x/MAXXMINX+0.5);
line = (Tl=(long)(y/(maxY-minY)*(Lines*8*DDc4-1)+0.5))/8/DDc4*DDc4;/*Number of line multiple of DDc */
subline = (int) Tl%(8*DDc4); line += subline%DDc4;
subline/=DDc4;
if(Pointcounter > Maxpoint-1){fputs("Too much points",stdout);_exit(0);}
*(Pline + (int) Pointcounter) = line;
*(Pcolumn + (int) Pointcounter) = (int) columna;
*(Charac + (int) Pointcounter++) += (z=Potencia(2,subline));
newcolumn = columna;/*for full line*/
newTl = Tl;/* Total number of sublines*/
/*Double line*/
if(doble > 0)
/*0*/ {
DOBLEE = doble/2+DDc/4 +1;
DOBLEL = doble/2*DDc/4 +1;
/*1*/ if(columna >= DOBLEE) {
colDOBLEE = columna - DOBLEE;
if(Pointcounter > Maxpoint-1){fputs("Too much points",stdout);_exit(0);}
*(Pline + (int) Pointcounter) = line;
*(Pcolumn + (int) Pointcounter) = (int) columna - DOBLEE;
*(Charac + (int) Pointcounter++) += z;
/*1*/ } else {if(Ptobell!=0 && ac++%Ptobell == 0) putc(7,stdout);}
/*5*/ if(Tl >= DOBLEL){
line = (Tl - DOBLEL)/8/DDc4*DDc4;/*Number of line multiple of DDc */
subline = ( (int) Tl - DOBLEL)%(8*DDc4); line += subline%DDc4;
subline/=DDc4;
if(Pointcounter > Maxpoint-1){fputs("Too much points",stdout);_exit(0);}
*(Pline + (int) Pointcounter) = line;
*(Pcolumn + (int) Pointcounter) = (int) columna;
*(Charac + (int) Pointcounter++) = Potencia(2,subline);
/*5*/ } else {if(Ptobell!=0 && ac++%Ptobell == 0) putc(7,stdout);}
/*0*/ }
/*End of double line*/
/*Full line*/
/* FULLE = 1 + DDc/4 + doble + doble * (DDc/4);*/
FULLE = 1 + DDc/4;
if(full == 2) FULLE += DDc*2;
if(full>0 && lastcolumn >= 0)
/*00*/ { dx = newcolumn - lastcolumn; dy = newTl - lastTl;
if(labs(dx)>FULLE||labs(dy)>FULLE)
/*01*/ {
columna = lastcolumn; Tl = lastTl;
maxdif = dx; mindif = dy; L1 = &Tl; L2 = &columna;
if(labs(dy)>labs(dx))
{
maxdif = dy; mindif = dx; L1 = &columna; L2 = &Tl; }
absmaxdif = labs(maxdif);
for(j=FULLE;j < absmaxdif; j+=FULLE)
/*02*/ {
columna = lastcolumn; Tl = lastTl; Flag = 0;
*L1+= mindif*j/absmaxdif;
*L2+=maxdif/absmaxdif*j;
line = Tl/8/DDc4*DDc4;/*Multiple of DDc number of line*/
subline = (int) Tl%(8*DDc4); line += subline%DDc4;
subline/=DDc4;
if(Pointcounter > Maxpoint-1){fputs("Too much points",stdout);_exit(0);}
*(Pline + (int) Pointcounter) =(char) line;
*(Pcolumn + (int) Pointcounter) = (int) columna;
*(Charac + (int) Pointcounter++) +=(z= Potencia(2,subline));
/*Double-full line*/
/*0*/ if(doble > 0){
/*1*/ if(columna >=DOBLEE) {
colDOBLEE = columna - DOBLEE;
if(Pointcounter > Maxpoint-1){fputs("Too much points",stdout);_exit(0);}
*(Pline + (int) Pointcounter) = line;
*(Pcolumn + (int) Pointcounter) = (int) columna-DOBLEE;
*(Charac + (int) Pointcounter++) += z;
/*1 Taken from there*/} else {if(Ptobell!=0 && ac++%Ptobell == 0) putc(7,stdout);}
/*5*/ if(Tl>=DOBLEL){
line = (Tl-DOBLEL)/8/DDc4*DDc4;/*Multiple of DDc number of line*/
subline = ( (int) Tl-DOBLEL)%(8*DDc4); line += subline%DDc4;
subline/=DDc4;
if(Pointcounter > Maxpoint-1){fputs("Too much points",stdout);_exit(0);}
*(Pline + (int) Pointcounter) = line;
*(Pcolumn + (int) Pointcounter) = (int) columna;
*(Charac + (int) Pointcounter++) += Potencia(2,subline);
/*5*/ } else {if(Ptobell!=0 && ac++%Ptobell == 0) putc(7,stdout);}
/*1 } else {if(Ptobell!=0 && ac++%Ptobell == 0) putc(7,stdout);}*//*Taken from here*/
/*0*/ }
/*02*/ }
/*01*/ }
/*00*/ }
lastcolumn = newcolumn; lastTl = newTl;
/*-1*/ }
/*-2*/ }
}
/*End of recpoint */
void newline(void){ ac = Ptobell -1; recpoint(maxX+1, 0);}
void axis(double x, double y, double Xmark, double Ymark, int Type){double save1, save2, save3, save4;
double u, v, w, wFULLE, xmarke, ymarke;
int WIDTHH, FULLE, FULLEY, LINESS, savefull, save5, i;
save5 = doble; savefull=full;
save1 = maxX; save2 = maxY; save3 = minX; save4 = minY;
full = 0;
doble = 0;
WIDTHH = Width*6*DDc;
LINESS = Lines*8*(DDc-DDc/4);
FULLE = 1 + DDc/4;
FULLEY = 1 + DDc/4*DDc/2;
if (Type == 0){/*x, y and respective marks are coordinates*/
x-=minX; y-=minY;
v = (int)(x/(maxX-minX)*(WIDTHH-1)+0.5);
w = (int)(y/(maxY-minY)*(LINESS-1)+0.5);
xmarke =(int)(Xmark/(maxX-minX)*(WIDTHH-1)+0.5);
ymarke = (int)(Ymark/(maxY-minY)*(LINESS-1)+0.5);}
else {/* x and y are column and line*/
v = x; w = y; xmarke = Xmark; ymarke = Ymark;}
maxX =WIDTHH - 1; maxY =LINESS-1; minX = 0; minY = 0;
/*Axis X*/
u = 0.0;
wFULLE = w + FULLE;
for(i=0;i<WIDTHH;i+=FULLE){
recpoint(u,wFULLE);
recpoint(u,w);
if(xmarke!=0 && abs((int) (u-v)) % (int) xmarke < FULLE)
{ recpoint(u, wFULLE + FULLE); recpoint(u, w - FULLE); }
u+=FULLE;}
/*axis Y*/
newline();
u = 0.0;
for(i=0;i<LINESS;i+=FULLE){
recpoint(v+FULLEY,u);
recpoint(v,u);
if(ymarke!=0 && abs((int) (u-w)) % (int) ymarke < FULLE)
{ recpoint(v + 2 * FULLEY , u); recpoint(v - FULLEY, u); }
u+=FULLE;}
maxX = save1; maxY = save2; minX = save3; minY = save4;
full = savefull; doble = save5;}
/*End of NPRNGRPH.LIB*/
כתיבת תגובה