OGGUtilities.Mod 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  1. (** AUTHOR "Christian Wassmer, chwassme@student.ethz.ch";
  2. PURPOSE "utility functions and classes for OGGVorbisPlayer.Mod";
  3. DATE "Avril 2004" *)
  4. MODULE OGGUtilities;
  5. IMPORT
  6. SYSTEM, Strings, Files, KernelLog, SoundDevices, BIT;
  7. CONST
  8. (* debugging set *)
  9. Trace = 1;
  10. Error = 2;
  11. Debug = {Error};
  12. MaxCodewordLength* = 33;
  13. MaxBlocksize* = 8192;
  14. NoHuffmanLeaf = -1;
  15. (* HuffmanNode directions *)
  16. GoLeft = 0; GoRight = 1;
  17. BaseVectorSize = 256;
  18. (* fraction of fix-point numbers; must be even *)
  19. Nx* = 20;
  20. VAR
  21. f: Files.File;
  22. w*: Files.Writer;
  23. Ny: LONGINT;
  24. ScaleFactor: HUGEINT;
  25. TYPE
  26. (** object for holding PCM buffer data (may also be used for floor-data (24 bits)) *)
  27. PCMBuffer* = OBJECT
  28. VAR data*: ARRAY MaxBlocksize OF HUGEINT;
  29. PROCEDURE Print*(name: ARRAY OF CHAR; len: LONGINT);
  30. BEGIN
  31. ArrayHugeLen(name, data, len)
  32. END Print;
  33. PROCEDURE PrintShort(name: ARRAY OF CHAR; len: LONGINT);
  34. VAR
  35. tmp: POINTER TO ARRAY OF LONGINT;
  36. i: LONGINT;
  37. BEGIN
  38. NEW(tmp,len);
  39. FOR i := 0 TO len - 1 DO
  40. tmp[i] := SHORT(data[i])
  41. END;
  42. ArrayLen(name, tmp^, len);
  43. END PrintShort;
  44. (** set whole buffer to 0 *)
  45. PROCEDURE ZeroBuffer*;
  46. VAR i: LONGINT;
  47. BEGIN
  48. IF (Trace IN Debug) THEN String("@VorbisCodec::PCMBuffer::ZeroBuffer()") END;
  49. FOR i := 0 TO LEN(data) - 1 DO
  50. data[i] := 0
  51. END
  52. END ZeroBuffer;
  53. END PCMBuffer;
  54. (** entry-point for a HuffmanTree *)
  55. HuffmanTree* = OBJECT
  56. VAR start-: HuffmanNode;
  57. PROCEDURE &Init*;
  58. BEGIN
  59. NEW(start, NoHuffmanLeaf, FALSE, FALSE)
  60. END Init;
  61. (** insert the HuffmanNode correspond to the codeword with the value *)
  62. PROCEDURE AddNode(value, length, codeword: LONGINT);
  63. BEGIN
  64. IF (Trace IN Debug) THEN
  65. String("@MyUtilities::HuffmanTree::AddNode()")
  66. END;
  67. start.AddNode(value, length, codeword)
  68. END AddNode;
  69. (** check if the length list represents a valid huffman-tree *)
  70. PROCEDURE IsValidLengthList*(VAR list: IntList; unused: LONGINT): BOOLEAN;
  71. VAR limit, sum: HUGEINT;
  72. BEGIN
  73. IF (Trace IN Debug) THEN
  74. String("@MyUtilities::HuffmanTree::IsValidLengthList()")
  75. END;
  76. GetSumAndLimit(list, sum, limit, unused);
  77. (* both under- and overspecified huffman-trees are not allowed *)
  78. RETURN (sum = limit)
  79. END IsValidLengthList;
  80. (** check if the list of length represents an overspecified huffman-tree *)
  81. PROCEDURE IsOverspecified*(VAR list: IntList; unused: LONGINT): BOOLEAN;
  82. VAR limit, sum: HUGEINT;
  83. BEGIN
  84. GetSumAndLimit(list, sum, limit, unused);
  85. RETURN (sum > limit)
  86. END IsOverspecified;
  87. (** check if the list of length represents an underspecified huffman-tree *)
  88. PROCEDURE IsUnderspecified*(VAR list: IntList; unused: LONGINT): BOOLEAN;
  89. VAR limit, sum: HUGEINT;
  90. BEGIN
  91. GetSumAndLimit(list, sum, limit, unused);
  92. RETURN (sum > limit)
  93. END IsUnderspecified;
  94. (* background: Kraft-McMillan's Theorem - sum(1,n, 1 / (2^l[i])) <= 1 <=> l[i] represents a valid HuffmanTree *)
  95. PROCEDURE GetSumAndLimit(VAR list: IntList; VAR sum, limit: HUGEINT; unused: LONGINT);
  96. VAR
  97. i: HUGEINT;
  98. cur: ListElement;
  99. BEGIN
  100. limit := PowerH(2, MaxCodewordLength);
  101. sum := 0;
  102. cur := list.start;
  103. FOR i := 0 TO list.length - 1 DO
  104. IF (cur(IntElement).long # unused) THEN (* is it valid length *)
  105. sum := sum + PowerH(2, MaxCodewordLength - cur(IntElement).long);
  106. END;
  107. cur := cur.next
  108. END
  109. END GetSumAndLimit;
  110. (* check wether the list has any used entries *)
  111. PROCEDURE HasUsedEntries*(VAR list: IntList; unused: LONGINT): BOOLEAN;
  112. VAR
  113. i: HUGEINT;
  114. cur: ListElement;
  115. BEGIN
  116. cur := list.start;
  117. FOR i := 0 TO list.length - 1 DO
  118. IF (cur(IntElement).long # unused) THEN RETURN TRUE END
  119. END;
  120. RETURN FALSE (* list has only unused codebook entries *)
  121. END HasUsedEntries;
  122. (** build a HuffmanTree from a IntList, return FALSE if not possible, else TRUE *)
  123. PROCEDURE BuildTree*(VAR lengths: IntList; VAR codes: IntList; unused: LONGINT);
  124. VAR
  125. value: LONGINT;
  126. curLen, curCode: IntElement;
  127. BEGIN
  128. IF (Trace IN Debug) THEN String("@MyUtilities::HuffmanTree::BuildTree()") END;
  129. curLen := lengths.start(IntElement);
  130. curCode := codes.start(IntElement);
  131. value := 0;
  132. WHILE (curLen # NIL) DO
  133. (* if it's a used codeword-entry *)
  134. IF (curLen.long # unused) THEN
  135. AddNode(value, curLen.long, curCode.long);
  136. IF (curCode.next # NIL) THEN curCode := curCode.next(IntElement) END
  137. END; (* else do nothing special*)
  138. INC(value);
  139. IF (curLen.next # NIL) THEN curLen := curLen.next(IntElement) ELSE curLen := NIL END
  140. END
  141. END BuildTree;
  142. (** get left or rigth son (as a VAR-parameter) depending on the input (0/1) during Hufman-decode-process
  143. (it's up to the clients to decide wether a codeword/leaf has been found or not *)
  144. PROCEDURE GoLeftOrRight*(VAR node: HuffmanNode; bit: LONGINT);
  145. BEGIN
  146. IF ((bit # 0) & (bit # 1)) THEN
  147. KernelLog.String("ASSERTION failed - bit is not 0 or 1"); KernelLog.Ln
  148. END;
  149. ASSERT((bit = 0) OR (bit = 1));
  150. IF (bit = GoLeft) THEN
  151. node := node.left
  152. ELSIF (bit = GoRight) THEN
  153. node := node.right
  154. END
  155. END GoLeftOrRight;
  156. PROCEDURE Print;
  157. BEGIN
  158. String("(inorder, *: isFull)");
  159. IF ~(start = NIL) THEN start.Print() END;
  160. String("")
  161. END Print;
  162. END HuffmanTree;
  163. (* node containing references to theirs sons and a value representing the decoded codeword *)
  164. HuffmanNode* = OBJECT
  165. VAR
  166. left, right: HuffmanNode;
  167. value: LONGINT;
  168. isLeaf, isFull: BOOLEAN; (* isLeaf only used for Print() *)
  169. PROCEDURE &Init*(value: LONGINT; isLeaf, isFull: BOOLEAN);
  170. BEGIN
  171. left := NIL; right := NIL;
  172. SELF.value := value;
  173. SELF.isFull := isFull;
  174. SELF.isLeaf := isLeaf
  175. END Init;
  176. (** get the value *)
  177. PROCEDURE GetValue*(): LONGINT;
  178. BEGIN
  179. RETURN value
  180. END GetValue;
  181. (** is this node a leaf <=> valid codeword *)
  182. PROCEDURE IsLeaf*(): BOOLEAN;
  183. BEGIN
  184. RETURN isLeaf
  185. END IsLeaf;
  186. (** print tree to console in inorder *)
  187. PROCEDURE Print;
  188. BEGIN
  189. IF (left # NIL) THEN left.Print() END;
  190. w.String("[");
  191. IF isLeaf THEN w.Int(value, 1) ELSE w.String(".") END;
  192. IF isFull THEN w.String("*") END;
  193. w.String("]");
  194. IF (right # NIL) THEN right.Print() END
  195. END Print;
  196. PROCEDURE AddNode(value, length, restCodeword: LONGINT);
  197. VAR
  198. digit: LONGINT;
  199. BEGIN
  200. IF (Trace IN Debug) THEN
  201. String("@MyUtilities::HuffmanNode::AddNode()")
  202. END;
  203. (* we read our codewords from MSb to LSb *)
  204. digit := LSH(restCodeword, -(length - 1)); (* we need the length-rightmost bits: right shift of length-1 *)
  205. restCodeword := restCodeword MOD Power(2, length - 1); (* only the (length-1)rightmost bits are used *)
  206. IF (length = 1) THEN
  207. (* recursion base *)
  208. IF (digit = GoLeft) THEN
  209. NEW(left, value, TRUE, TRUE)
  210. ELSIF (digit = GoRight) THEN
  211. NEW(right, value, TRUE, TRUE)
  212. END
  213. ELSE
  214. (* recursion step *)
  215. IF (digit = GoLeft) THEN
  216. IF (left = NIL) THEN NEW(left, NoHuffmanLeaf, FALSE, FALSE) END;
  217. left.AddNode(value, length - 1, restCodeword)
  218. ELSIF (digit = GoRight) THEN
  219. IF (right = NIL) THEN NEW(right, NoHuffmanLeaf, FALSE, FALSE) END;
  220. right.AddNode(value, length - 1, restCodeword)
  221. END
  222. END
  223. END AddNode;
  224. END HuffmanNode;
  225. (** data-structure for holding a number of (BaseVectorSize) longint data *)
  226. Vector* = OBJECT
  227. VAR
  228. capacity, len: LONGINT;
  229. data: ARRAY BaseVectorSize OF HUGEINT;
  230. PROCEDURE &Init*;
  231. BEGIN
  232. len := 0;
  233. capacity := BaseVectorSize
  234. END Init;
  235. PROCEDURE Print*;
  236. BEGIN
  237. ArrayHugeLen("vector", data, len);
  238. END Print;
  239. (** add a value at the end *)
  240. PROCEDURE Add*(value: HUGEINT);
  241. BEGIN
  242. IF (len >= capacity) THEN
  243. KernelLog.String("ASSERTION failed - vector exceeds size"); KernelLog.Ln
  244. END;
  245. ASSERT(len < capacity);
  246. data[len] := value;
  247. INC(len)
  248. END Add;
  249. (** increase each value by inc *)
  250. PROCEDURE Increase(inc: HUGEINT);
  251. VAR i: LONGINT;
  252. BEGIN
  253. FOR i := 0 TO len - 1 DO
  254. INC(data[i], inc)
  255. END
  256. END Increase;
  257. (** get the length *)
  258. PROCEDURE GetLen(): LONGINT;
  259. BEGIN
  260. RETURN len
  261. END GetLen;
  262. (** get the last element *)
  263. PROCEDURE GetLast(): HUGEINT;
  264. BEGIN
  265. RETURN data[len]
  266. END GetLast;
  267. (** get value at a certain position *)
  268. PROCEDURE GetValueAt*(idx: LONGINT): HUGEINT;
  269. BEGIN
  270. IF (idx >= len) THEN
  271. KernelLog.String("ASSERTION failed - access to invalid vector element"); KernelLog.Ln
  272. END;
  273. ASSERT(idx < len);
  274. RETURN data[idx]
  275. END GetValueAt;
  276. PROCEDURE IncValueAt(inc: HUGEINT; idx: LONGINT);
  277. BEGIN
  278. SetValueAt(inc + GetValueAt(idx), idx)
  279. END IncValueAt;
  280. PROCEDURE SetValueAt(val: HUGEINT; idx: LONGINT);
  281. BEGIN
  282. IF (idx >= len) THEN
  283. KernelLog.String("ASSERTION failed - access to invalid vector element"); KernelLog.Ln
  284. END;
  285. ASSERT(idx < len);
  286. data[idx] := val
  287. END SetValueAt;
  288. (** concetenate another vector *)
  289. PROCEDURE Concatenate(VAR v: Vector);
  290. VAR i: LONGINT;
  291. BEGIN
  292. FOR i := 0 TO v.GetLen() - 1 DO
  293. SELF.Add(v.data[i])
  294. END
  295. END Concatenate;
  296. END Vector;
  297. List* = OBJECT
  298. VAR
  299. length*: HUGEINT;
  300. start-, last-: ListElement;
  301. PROCEDURE &Init*(first: ListElement);
  302. VAR
  303. BEGIN
  304. IF (first = NIL) THEN length := 0 ELSE length := 1 END;
  305. SELF.start := first;
  306. SELF.last := first
  307. END Init;
  308. PROCEDURE Append*(node: ListElement);
  309. BEGIN
  310. IF (last = NIL) THEN
  311. start := node; last := node
  312. ELSE
  313. last.next := node;
  314. last := node
  315. END;
  316. INC(length);
  317. node.next := NIL
  318. END Append;
  319. (** print the list *)
  320. PROCEDURE Print*;
  321. VAR
  322. i, split: LONGINT;
  323. cur: ListElement;
  324. BEGIN
  325. i := 0; split := 16; (* every 'split' elements make a line break *)
  326. cur := SELF.start;
  327. String("-> List <-");
  328. Var("list-length", SYSTEM.VAL(LONGINT,length));
  329. WHILE (cur # NIL) DO
  330. IF (~(i = 0) & (i MOD split = 0)) THEN w.Ln END;
  331. cur.Print;
  332. cur := cur.next;
  333. INC(i)
  334. END;
  335. w.Ln; String("-> END (List) <-")
  336. END Print;
  337. END List;
  338. IntList* = OBJECT(List)
  339. (** no additional fields needed *)
  340. END IntList;
  341. ListElement* = OBJECT
  342. VAR
  343. next-: ListElement;
  344. PROCEDURE &Init*;
  345. BEGIN
  346. SELF.next := NIL
  347. END Init;
  348. PROCEDURE Print;
  349. (* do nothing *)
  350. END Print;
  351. END ListElement;
  352. IntElement* = OBJECT(ListElement)
  353. VAR
  354. long-: LONGINT;
  355. PROCEDURE &InitInt*(long: LONGINT);
  356. BEGIN
  357. SELF.long := long;
  358. SELF.next := NIL
  359. END InitInt;
  360. PROCEDURE Print;
  361. BEGIN
  362. w.Int(long, 3)
  363. END Print;
  364. END IntElement;
  365. BufferPool* = OBJECT
  366. VAR head, num: LONGINT; buffer: POINTER TO ARRAY OF SoundDevices.Buffer;
  367. PROCEDURE Append*(x: SoundDevices.Buffer);
  368. BEGIN {EXCLUSIVE}
  369. AWAIT(num # LEN(buffer));
  370. buffer[(head+num) MOD LEN(buffer)] := x;
  371. INC(num)
  372. END Append;
  373. PROCEDURE Remove*(): SoundDevices.Buffer;
  374. VAR x: SoundDevices.Buffer;
  375. BEGIN {EXCLUSIVE}
  376. AWAIT(num # 0);
  377. x := buffer[head];
  378. head := (head+1) MOD LEN(buffer);
  379. DEC(num);
  380. RETURN x
  381. END Remove;
  382. PROCEDURE &Init*(n: LONGINT);
  383. BEGIN
  384. head := 0; num := 0; NEW(buffer, n)
  385. END Init;
  386. END BufferPool;
  387. (** these procedures are used in multiple places by the OGGVorbisPlayer *)
  388. (** get the position number of the lowest set bit of x *)
  389. PROCEDURE ILog*(x: LONGINT): LONGINT;
  390. VAR ret: LONGINT;
  391. BEGIN
  392. ret := 0;
  393. LOOP
  394. IF (x > 0) THEN
  395. INC(ret);
  396. x := LSH(x, -1)
  397. ELSE
  398. EXIT
  399. END
  400. END;
  401. RETURN ret
  402. END ILog;
  403. (** translate the packed binary representation of a Vorbis codebook float value into the
  404. representation used by the decoder for floating point numbers *)
  405. PROCEDURE Float32Unpack*(val: LONGINT): HUGEINT;
  406. VAR
  407. mantissa, sign, exponent, one: LONGINT;
  408. res: REAL;
  409. BEGIN
  410. mantissa := BIT.LAND(val, 1FFFFFH);
  411. sign := BIT.LAND(val, LONGINT(80000000H));
  412. exponent := BIT.LAND(val, 7FE00000H) DIV 1FFFFFH;
  413. IF (sign # 0) THEN
  414. mantissa := -1 * mantissa
  415. END;
  416. one := 1;
  417. res := mantissa / LSH(one, -1*(exponent - 788));
  418. RETURN ScaleUpHuge(res)
  419. END Float32Unpack;
  420. (** compute the correct length of the value index for a codebook VQ lookup table of lookup type 1 *)
  421. PROCEDURE Lookup1Values*(cbEntries, cbDimensions: LONGINT): LONGINT;
  422. VAR res: LONGINT;
  423. BEGIN
  424. res := 0;
  425. WHILE (Power(res, cbDimensions) <= cbEntries) DO
  426. INC(res);
  427. END;
  428. DEC(res); (* find the highest ... that is smaller than ... *)
  429. RETURN res
  430. END Lookup1Values;
  431. (** find the position n in vector v of the GREATEST value scalar element for which n is less than x and v[n] is less than v[x] *)
  432. PROCEDURE LowNeighbor*(v: ARRAY OF LONGINT; x: LONGINT): LONGINT;
  433. VAR i, n: LONGINT;
  434. BEGIN
  435. n := 0;
  436. FOR i := 1 TO x - 1 DO (* n is less than x *)
  437. IF (v[i] < v[x]) THEN (* v[n] is less than v[x] *)
  438. IF ((v[i] > v[n]) OR (n = 0)) THEN (* the greatest value OR if its first value less than v[x]*)
  439. n := i
  440. END
  441. END
  442. END;
  443. RETURN n
  444. END LowNeighbor;
  445. (** find the position n in vector v of the LOWEST value scalar element for which n is less than x and v[n] is greater than v[x] *)
  446. PROCEDURE HighNeighbor*(v: ARRAY OF LONGINT; x: LONGINT): LONGINT;
  447. VAR i, n: LONGINT;
  448. BEGIN
  449. n := 0;
  450. FOR i := 0 TO x - 1 DO (* n is less than x *)
  451. IF (v[i] > v[x]) THEN (* v[n] is greater than v[x] *)
  452. IF ((v[i] < v[n]) OR (n = 0)) THEN (* the lowest value OR if its first value greater than v[x] *)
  453. n := i
  454. END
  455. END
  456. END;
  457. RETURN n
  458. END HighNeighbor;
  459. (** find the y-coordinate at point x along the line specified by x0, x1, y0 and y1 *)
  460. PROCEDURE RenderPoint*(x0,y0,x1,y1,x: LONGINT): LONGINT;
  461. VAR
  462. dy, adx, ady, err, off: LONGINT;
  463. BEGIN
  464. dy := y1 - y0;
  465. adx := x1 - x0;
  466. ady := ABS(dy);
  467. err := ady * (x - x0);
  468. off := err DIV adx;
  469. IF dy < 0 THEN
  470. RETURN (y0 - off)
  471. ELSE
  472. RETURN (y0 + off)
  473. END
  474. END RenderPoint;
  475. (** construct an integer floor curve for contiguous piecewise line segments *)
  476. PROCEDURE RenderLine*(x0, y0, x1, y1: LONGINT; VAR v: ARRAY OF HUGEINT);
  477. VAR dy, adx, ady, x, y, err, base, sy: LONGINT;
  478. BEGIN
  479. dy := y1 - y0;
  480. adx := x1 - x0;
  481. ady := ABS(dy);
  482. (* adx is always positive, dy negative or positive *)
  483. IF (dy < 0) THEN
  484. base := -1 * ABS(dy) DIV adx (* must round towards zero, DIV-operator does not *)
  485. ELSE
  486. base := dy DIV adx
  487. END;
  488. x := x0;
  489. y := y0;
  490. err := 0;
  491. IF (dy < 0) THEN
  492. sy := base - 1
  493. ELSE
  494. sy := base + 1
  495. END;
  496. ady := ady - ABS(base) * adx;
  497. v[x] := y;
  498. FOR x := x0 + 1 TO x1 - 1 DO
  499. INC(err, ady);
  500. IF (err >= adx) THEN
  501. DEC(err, adx);
  502. INC(y, sy)
  503. ELSE
  504. INC(y, base)
  505. END;
  506. v[x] := y
  507. END
  508. END RenderLine;
  509. (** sort an array, storing the position changes *)
  510. PROCEDURE EasySortRemember*(VAR data, sortptr: ARRAY OF LONGINT; n: LONGINT);
  511. VAR tmp, j, k: LONGINT;
  512. BEGIN
  513. IF ((n > LEN(data)) OR (n > LEN(sortptr))) THEN
  514. KernelLog.String("ASSERTION failed - array too short to sort")
  515. END;
  516. ASSERT((n <= LEN(data)) & (n <= LEN(sortptr)));
  517. FOR j := 0 TO n - 1 DO sortptr[j] := j END; (* no position changes yet *)
  518. FOR j := 0 TO n - 2 DO
  519. FOR k := j TO n - 1 DO
  520. IF (data[sortptr[j]] > data[sortptr[k]]) THEN
  521. (* remember swapping positions *)
  522. tmp := sortptr[k];
  523. sortptr[k] := sortptr[j];
  524. sortptr[j] := tmp
  525. END
  526. END
  527. END;
  528. END EasySortRemember;
  529. (** some mathematical procedures *)
  530. (** return x to the power of n (ASSERT(n>=0)) *)
  531. PROCEDURE Power*(x,n: LONGINT): LONGINT;
  532. VAR res: LONGINT;
  533. BEGIN
  534. IF (n < 0) THEN
  535. KernelLog.String("ASSERTION failed - power of negative numbers are not allowed"); KernelLog.Ln
  536. END;
  537. ASSERT(n >= 0);
  538. IF (n = 0) THEN
  539. RETURN 1
  540. ELSE
  541. res := x;
  542. WHILE (n > 1) DO
  543. res := x * res;
  544. DEC(n)
  545. END
  546. END;
  547. RETURN res;
  548. END Power;
  549. (** return x to the power of n (ASSERT(n>=0)) *)
  550. PROCEDURE PowerH(x: HUGEINT; n: LONGINT): HUGEINT;
  551. VAR res: HUGEINT;
  552. BEGIN
  553. IF (n < 0) THEN
  554. KernelLog.String("ASSERTION failed - power of negative numbers are not allowed"); KernelLog.Ln
  555. END;
  556. ASSERT(n >= 0);
  557. IF (n = 0) THEN
  558. RETURN 1
  559. ELSE
  560. res := x;
  561. WHILE (n > 1) DO
  562. res := x * res;
  563. DEC(n)
  564. END
  565. END;
  566. RETURN res;
  567. END PowerH;
  568. (** return x to the power of n (ASSERT(n>=0)) *)
  569. PROCEDURE PowerR*(x: REAL; n: LONGINT): REAL;
  570. VAR res: REAL;
  571. BEGIN
  572. IF (n < 0) THEN
  573. KernelLog.String("ASSERTION failed - power of negative numbers are not allowed"); KernelLog.Ln
  574. END;
  575. ASSERT(n >= 0);
  576. IF (n = 0) THEN
  577. RETURN 1
  578. ELSE
  579. res := x;
  580. WHILE (n > 1) DO
  581. res := res * x;
  582. DEC(n)
  583. END
  584. END;
  585. RETURN res;
  586. END PowerR;
  587. (** returns the logarithm to the base 2 *)
  588. PROCEDURE Log2n*(n: LONGINT): LONGINT;
  589. VAR res: LONGINT;
  590. BEGIN
  591. ASSERT(n > 0);
  592. res := 0;
  593. WHILE (n # 1) DO
  594. n := n DIV 2;
  595. INC(res);
  596. END;
  597. RETURN res
  598. END Log2n;
  599. (*
  600. PROCEDURE HighestBit(h: HUGEINT): LONGINT;
  601. VAR res, i: LONGINT;
  602. BEGIN
  603. IF h < 0 THEN h := -1 * h END;
  604. res := 0;
  605. FOR i := 0 TO 63 DO
  606. IF LONGINT(h) MOD 2 = 1 THEN
  607. res := i
  608. END;
  609. h := LSH(h, -1)
  610. END;
  611. RETURN res
  612. END HighestBit;
  613. *)
  614. (** fix-point arithmetic procedures *)
  615. (** fixed-point multiplication, result same format as input with fraction n*)
  616. PROCEDURE MultHugeFP*(a, b: HUGEINT): HUGEINT;
  617. VAR sign, res: HUGEINT;
  618. BEGIN
  619. IF ((a = 0) OR (b = 0)) THEN
  620. RETURN 0
  621. ELSE
  622. res := a * b;
  623. IF res < 0 THEN
  624. sign := -1;
  625. (* eliminate sign for shifting*)
  626. res := sign * res;
  627. ELSE
  628. sign := 1
  629. END;
  630. res := LSH(res, -Ny);
  631. (* rebuild sign *)
  632. res := sign * res;
  633. RETURN res
  634. END
  635. END MultHugeFP;
  636. (** fixed-point multiplication for "dot product" of residue and floor *)
  637. PROCEDURE MultDotProductFP*(residue, floor: HUGEINT): HUGEINT;
  638. VAR sign: HUGEINT;
  639. BEGIN
  640. IF (residue = 0) OR (floor = 0) THEN
  641. RETURN 0
  642. ELSE
  643. IF residue < 0 THEN
  644. sign := -1;
  645. residue := sign * residue
  646. ELSE
  647. sign := 1
  648. END;
  649. (* scale down residue-value which is an integer *)
  650. residue := ScaleDownHuge(residue);
  651. residue := sign * residue;
  652. (* leave floor-value *)
  653. RETURN residue * floor
  654. END
  655. END MultDotProductFP;
  656. (** fixed-point multiplication, result same format as input with constant fraction Nx*)
  657. PROCEDURE MultFP*(a, b: HUGEINT): HUGEINT;
  658. VAR divident, n: LONGINT;
  659. BEGIN
  660. IF (a = 0) OR (b = 0) THEN
  661. RETURN 0
  662. ELSE
  663. n := Ny DIV 2;
  664. divident := LSH(LONG(LONG(1)), n);
  665. a := a DIV divident;
  666. b := b DIV divident;
  667. RETURN a * b
  668. END
  669. END MultFP;
  670. (** fixed-point division, result same format as input with constant fraction Nx*)
  671. PROCEDURE DivFP*(a, b: LONGINT): LONGINT;
  672. BEGIN
  673. RETURN ScaleUpInt(a) DIV b;
  674. END DivFP;
  675. (** scales a float up for fix-point representation with fraction Ny *)
  676. PROCEDURE ScaleUpHuge*(r: REAL): HUGEINT;
  677. VAR tmp: HUGEINT;
  678. BEGIN
  679. tmp := ENTIERH(0.5 + r * ScaleFactor);
  680. RETURN tmp
  681. END ScaleUpHuge;
  682. (** scales a float up for fix-point representation with fraction Ny *)
  683. PROCEDURE ScaleUp*(r: LONGREAL): LONGINT;
  684. VAR tmp: LONGINT;
  685. BEGIN
  686. tmp := ENTIER(0.5 + r * ScaleFactor);
  687. RETURN tmp
  688. END ScaleUp;
  689. (** scales a float up for fix-point representation with fraction Ny *)
  690. PROCEDURE ScaleUpRealToHuge*(r: LONGREAL): HUGEINT;
  691. VAR tmp: HUGEINT;
  692. BEGIN
  693. tmp := ENTIERH(0.5 + r * ScaleFactor);
  694. RETURN tmp
  695. END ScaleUpRealToHuge;
  696. (** scales an integer up for fix-point representation with constant fraction Ny *)
  697. PROCEDURE ScaleUpInt*(i: LONGINT): LONGINT;
  698. VAR tmp: LONGINT;
  699. BEGIN
  700. tmp := i * SHORT(ScaleFactor);
  701. RETURN tmp
  702. END ScaleUpInt;
  703. (** scales an huge integer up for fix-point representation with constant fraction Ny *)
  704. PROCEDURE ScaleUpHugeInt*(i: HUGEINT): HUGEINT;
  705. VAR tmp: HUGEINT;
  706. BEGIN
  707. tmp := i * ScaleFactor;
  708. RETURN tmp
  709. END ScaleUpHugeInt;
  710. (** scales a hugeint down for fix-point representation (rounded) with constant fraction Ny*)
  711. PROCEDURE ScaleDownRoundedHuge*(i: HUGEINT): LONGINT;
  712. BEGIN
  713. RETURN SHORT((i + ScaleFactor DIV 2) DIV ScaleFactor)
  714. END ScaleDownRoundedHuge;
  715. (** scales a hugeint down for fix-point representation with constant fraction Ny*)
  716. PROCEDURE ScaleDownHuge(i: HUGEINT): HUGEINT;
  717. BEGIN
  718. RETURN i DIV ScaleFactor
  719. END ScaleDownHuge;
  720. (** procedures for debugging output to a log-file *)
  721. PROCEDURE GetFilename*(VAR name: ARRAY OF CHAR);
  722. BEGIN
  723. f.GetName(name)
  724. END GetFilename;
  725. PROCEDURE Array2D*(name: ARRAY OF CHAR; VAR a: ARRAY OF ARRAY OF LONGINT);
  726. VAR i: LONGINT;
  727. BEGIN
  728. w.String("[ "); w.String(name); w.String(" ]"); w.Ln;
  729. FOR i := 0 TO LEN(a) - 1 DO
  730. Array("---", a[i]);
  731. w.Ln;
  732. END;
  733. w.Ln; w.String("[ END ("); w.String(name); w.String(") ]"); w.Ln;
  734. w.Update
  735. END Array2D;
  736. PROCEDURE ArrayBool*(name: ARRAY OF CHAR; VAR a: ARRAY OF BOOLEAN);
  737. VAR i: LONGINT;
  738. BEGIN
  739. w.String("[ "); w.String(name); w.String(" ]"); w.Ln;
  740. FOR i := 0 TO LEN(a) - 1 DO
  741. IF (~(i = 0) & (i MOD 16 = 0)) THEN
  742. w.Ln; w.String(" ")
  743. END;
  744. IF (a[i]) THEN
  745. w.String("1")
  746. ELSE
  747. w.String("0")
  748. END;
  749. IF (i # LEN(a)-1) THEN w.String(", ") END
  750. END;
  751. w.Ln; w.String("[ END ("); w.String(name); w.String(") ]"); w.Ln;
  752. w.Update
  753. END ArrayBool;
  754. (** print an array of longint *)
  755. PROCEDURE Array*(name: ARRAY OF CHAR; VAR a: ARRAY OF LONGINT);
  756. BEGIN
  757. ArrayLen(name, a, LEN(a))
  758. END Array;
  759. PROCEDURE ArrayHuge*(name: ARRAY OF CHAR; VAR a: ARRAY OF HUGEINT);
  760. BEGIN
  761. ArrayHugeLen(name, a, LEN(a))
  762. END ArrayHuge;
  763. PROCEDURE ArrayHugeLen*(name: ARRAY OF CHAR; VAR a: ARRAY OF HUGEINT; n: LONGINT);
  764. VAR i: LONGINT;
  765. BEGIN
  766. IF (n > LEN(a)) THEN
  767. KernelLog.String("ASSERTION failed - array too short")
  768. END;
  769. ASSERT(n <= LEN(a));
  770. w.String("[ "); w.String(name); w.String(" ] "); w.Ln;
  771. FOR i := 0 TO n - 1 DO
  772. w.Int(i, 0); w.String(";");
  773. w.Int(SHORT(a[i]), 0);
  774. w.Ln;
  775. END;
  776. w.Ln; w.String("[ END ("); w.String(name); w.String(") ]"); w.Ln;
  777. w.Update
  778. END ArrayHugeLen;
  779. (** print an array of longint of a given length *)
  780. PROCEDURE ArrayLen*(name: ARRAY OF CHAR; VAR a: ARRAY OF LONGINT; n: LONGINT);
  781. VAR i: LONGINT;
  782. BEGIN
  783. IF (n > LEN(a)) THEN
  784. KernelLog.String("ASSERTION failed - array too short")
  785. END;
  786. ASSERT(n <= LEN(a));
  787. w.String("[ "); w.String(name); w.String(" ]"); w.Ln;
  788. FOR i := 0 TO n - 1 DO
  789. w.Int(i, 0); w.String(";"); w.Int(a[i], 0); w.Ln;
  790. END;
  791. w.Ln; w.String("[ END ("); w.String(name); w.String(") ]"); w.Ln;
  792. w.Update
  793. END ArrayLen;
  794. (** write a string to the log-file *)
  795. PROCEDURE String*(str: ARRAY OF CHAR);
  796. BEGIN
  797. w.String(str); w.Ln; w.Update
  798. END String;
  799. (** write some bytes from a buffer *)
  800. PROCEDURE Buffer*(VAR buf: ARRAY OF CHAR; ofs, len: LONGINT);
  801. VAR i, line: LONGINT;
  802. hex: ARRAY 3 OF CHAR;
  803. BEGIN
  804. line := 16;
  805. FOR i := 0 TO len - 1 DO
  806. IF ((i # 0) & (i MOD line = 0)) THEN w.Ln END;
  807. CharToHex(buf[ofs + i], hex);
  808. w.String(hex); w.String(" ")
  809. END;
  810. w.Ln; w.Update
  811. END Buffer;
  812. PROCEDURE CharToHex(ch: CHAR; VAR hex: ARRAY OF CHAR);
  813. VAR ord, low, high: LONGINT;
  814. BEGIN
  815. ord := ORD(ch);
  816. low := ord MOD 16;
  817. high := ord DIV 16;
  818. hex[0] := GetHexDigit(high);
  819. hex[1] := GetHexDigit(low);
  820. END CharToHex;
  821. PROCEDURE GetHexDigit(val: LONGINT): CHAR;
  822. BEGIN
  823. IF (val <= 9) THEN
  824. RETURN CHR(48 + val)
  825. ELSE
  826. RETURN CHR(65 + val - 10)
  827. END
  828. END GetHexDigit;
  829. (** print a varname with its value, type must be a longint *)
  830. PROCEDURE Var*(name: ARRAY OF CHAR; value: LONGINT);
  831. BEGIN
  832. w.String(name); w.String(": "); w.Int(value,5); w.Ln; w.Update
  833. END Var;
  834. (** print a varname with its value, type must be a hugeint *)
  835. PROCEDURE VarH*(name: ARRAY OF CHAR; value: HUGEINT);
  836. BEGIN
  837. w.String(name); w.String(": ");
  838. VarH2(value);
  839. w.Ln; w.Update
  840. END VarH;
  841. (** print a hugeint *)
  842. PROCEDURE VarH2*(value: HUGEINT);
  843. VAR sign: LONGINT;
  844. BEGIN
  845. IF (value < 0) THEN sign := -1; value := -1 * value ELSE sign := 1 END;
  846. IF (sign = -1) THEN w.String("-") ELSE w.String(" ") END;
  847. PrintHex(SHORT (ASH(value, -32)));
  848. PrintHex(SHORT (value));
  849. w.Update
  850. END VarH2;
  851. PROCEDURE PrintHex(x: LONGINT);
  852. VAR i, j: LONGINT;
  853. buf: ARRAY 10 OF CHAR;
  854. BEGIN
  855. j := 8;
  856. FOR i := j-1 TO 0 BY -1 DO
  857. buf[i] := CHR(x MOD 10H + 48);
  858. IF buf[i] > "9" THEN
  859. buf[i] := CHR(ORD(buf[i]) - 48 + 65 - 10)
  860. END;
  861. x := x DIV 10H
  862. END;
  863. buf[j] := 0X;
  864. w.String(buf)
  865. END PrintHex;
  866. (** print a varname with its value, type must be a REAL *)
  867. PROCEDURE VarReal*(name: ARRAY OF CHAR; value: LONGREAL);
  868. VAR tmpStr: ARRAY 32 OF CHAR;
  869. BEGIN
  870. Strings.FloatToStr(value, 5, 4, 0, tmpStr);
  871. w.String(name); w.String(": "); w.String(tmpStr); w.Ln; w.Update
  872. END VarReal;
  873. PROCEDURE InitLogger*;
  874. BEGIN
  875. f := Files.New("LogFile.csv");
  876. Files.Register(f);
  877. Files.OpenWriter(w, f, 0);
  878. END InitLogger;
  879. BEGIN
  880. Ny := Nx; (* necessary for some reason *)
  881. ScaleFactor := LSH(LONG(LONG(1)), Ny DIV 2);
  882. ScaleFactor := ScaleFactor * ScaleFactor;
  883. END OGGUtilities.